Merge branch 'master' into master

pull/9122/head
Koren Peretz 2025-10-04 08:44:18 +03:00 committed by GitHub
commit 1bb665ce37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
73 changed files with 3038 additions and 594 deletions

View File

@ -20,7 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
pgver: [13, 14, 15, 16, 17]
pgver: [13, 14, 15, 16, 17, 18]
runs-on: ubuntu-22.04

View File

@ -105,8 +105,13 @@ jobs:
run: |
# Note: we use a custom port for PostgreSQL as the runner may already have a version of PostgreSQL installed
sudo su -c "echo local all all trust > /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host all all 127.0.0.1/32 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host all all ::1/128 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host replication postgres 127.0.0.1/32 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host replication postgres ::1/128 trust >> /etc/edb-as/${{ matrix.pgver }}/main/pg_hba.conf"
sudo sed -i "s/port = 544[0-9]/port = 58${{ matrix.pgver }}/g" /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
sudo sed -i "s/shared_preload_libraries = '/shared_preload_libraries = '\$libdir\/plugin_debugger,/g" /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
echo "wal_level = logical" | sudo tee -a /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
sudo su - enterprisedb -c "mkdir -p /var/run/edb-as/${{ matrix.pgver }}-main.epas_stat_tmp"
sudo systemctl restart edb-as@${{ matrix.pgver }}-main
@ -115,6 +120,16 @@ jobs:
sleep 2
done
- name: Start PostgreSQL on Windows
if: ${{ matrix.os == 'windows-latest' }}
run: |
echo host replication postgres 127.0.0.1/32 trust >> "C:\EPAS\${{ matrix.pgver }}\data\pg_hba.conf"
echo host replication postgres ::1/128 trust >> "C:\EPAS\${{ matrix.pgver }}\data\pg_hba.conf"
echo wal_level = logical >> "C:\EPAS\${{ matrix.pgver }}\data\postgresql.conf"
net stop epas-${{ matrix.pgver }}
net start epas-${{ matrix.pgver }}
shell: cmd
- name: Create pgagent extension on Linux
if: ${{ matrix.os == 'ubuntu-22.04' && matrix.pgver <= 16 }}
run: psql -U enterprisedb -d postgres -p 58${{ matrix.pgver }} -c 'CREATE EXTENSION IF NOT EXISTS pgagent;'

View File

@ -21,7 +21,7 @@ jobs:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-22.04, windows-latest]
pgver: [13, 14, 15, 16, 17]
pgver: [13, 14, 15, 16, 17, 18]
runs-on: ${{ matrix.os }}
@ -116,8 +116,13 @@ jobs:
if: ${{ matrix.os == 'ubuntu-22.04' }}
run: |
sudo su -c "echo local all all trust > /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host all all 127.0.0.1/32 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host all all ::1/128 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host replication postgres 127.0.0.1/32 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
sudo su -c "echo host replication postgres ::1/128 trust >> /etc/postgresql/${{ matrix.pgver }}/main/pg_hba.conf"
sudo sed -i "s/port = 543[0-9]/port = 59${{ matrix.pgver }}/g" /etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf
sudo sed -i "s/#shared_preload_libraries = ''/shared_preload_libraries = '\$libdir\/plugin_debugger'/g" /etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf
echo "wal_level = logical" | sudo tee -a /etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf
sudo su - postgres -c "/usr/lib/postgresql/${{ matrix.pgver }}/bin/postgres -D /var/lib/postgresql/${{ matrix.pgver }}/main -c config_file=/etc/postgresql/${{ matrix.pgver }}/main/postgresql.conf &"
until sudo runuser -l postgres -c "pg_isready -p 59${{ matrix.pgver }}" 2>/dev/null; do
@ -133,7 +138,12 @@ jobs:
if: ${{ matrix.os == 'macos-latest' }}
run: |
echo local all all trust > /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
echo 'host all all 127.0.0.1/32 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
echo 'host all all ::1/128 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
echo 'host replication postgres 127.0.0.1/32 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
echo 'host replication postgres ::1/128 trust' >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/pg_hba.conf
sed -i '' "s/#port = 543[0-9]/port = 59${{ matrix.pgver }}/g" /opt/homebrew/var/postgresql@${{ matrix.pgver }}/postgresql.conf
echo "wal_level = logical" >> /opt/homebrew/var/postgresql@${{ matrix.pgver }}/postgresql.conf
brew services restart postgresql@${{ matrix.pgver }}
until /opt/homebrew/opt/postgresql@${{ matrix.pgver }}/bin/pg_isready -p 59${{ matrix.pgver }} 2>/dev/null; do
@ -143,6 +153,16 @@ jobs:
psql postgres -p 59${{ matrix.pgver }} -c 'CREATE ROLE postgres SUPERUSER LOGIN;'
- name: Start PostgreSQL on Windows
if: ${{ matrix.os == 'windows-latest' }}
run: |
echo host replication postgres 127.0.0.1/32 trust >> "C:\PostgreSQL\${{ matrix.pgver }}\data\pg_hba.conf"
echo host replication postgres ::1/128 trust >> "C:\PostgreSQL\${{ matrix.pgver }}\data\pg_hba.conf"
echo wal_level = logical >> "C:\PostgreSQL\${{ matrix.pgver }}\data\postgresql.conf"
net stop postgresql-x64-${{ matrix.pgver }}
net start postgresql-x64-${{ matrix.pgver }}
shell: cmd
- name: Install Python dependencies on Linux and macOS
if: ${{ matrix.os == 'macos-latest' || matrix.os == 'ubuntu-22.04' }}
run: make install-python-testing

View File

@ -120,21 +120,16 @@ RUN rm -rf /pgadmin4/docs/en_US/_build/html/_static/*.png
# Create additional builders to get all of the PostgreSQL utilities
#########################################################################
FROM postgres:12-alpine AS pg12-builder
FROM postgres:13-alpine AS pg13-builder
FROM postgres:14-alpine AS pg14-builder
FROM postgres:15-alpine AS pg15-builder
FROM postgres:16-alpine AS pg16-builder
FROM postgres:17-alpine AS pg17-builder
FROM postgres:18-alpine AS pg18-builder
FROM alpine:latest AS tool-builder
# Copy the PG binaries
COPY --from=pg12-builder /usr/local/bin/pg_dump /usr/local/pgsql/pgsql-12/
COPY --from=pg12-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-12/
COPY --from=pg12-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-12/
COPY --from=pg12-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-12/
COPY --from=pg13-builder /usr/local/bin/pg_dump /usr/local/pgsql/pgsql-13/
COPY --from=pg13-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-13/
COPY --from=pg13-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-13/
@ -160,6 +155,11 @@ COPY --from=pg17-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-17/
COPY --from=pg17-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-17/
COPY --from=pg17-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-17/
COPY --from=pg18-builder /usr/local/bin/pg_dump /usr/local/pgsql/pgsql-18/
COPY --from=pg18-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-18/
COPY --from=pg18-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-18/
COPY --from=pg18-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-18/
#########################################################################
# Assemble everything into the final container.
#########################################################################
@ -171,12 +171,12 @@ COPY --from=env-builder /venv /venv
# Copy in the tools
COPY --from=tool-builder /usr/local/pgsql /usr/local/
COPY --from=pg17-builder /usr/local/lib/libpq.so.5.17 /usr/lib/
COPY --from=pg17-builder /usr/lib/libzstd.so.1.5.7 /usr/lib/
COPY --from=pg17-builder /usr/lib/liblz4.so.1.10.0 /usr/lib/
COPY --from=pg18-builder /usr/local/lib/libpq.so.5.18 /usr/lib/
COPY --from=pg18-builder /usr/lib/libzstd.so.1.5.7 /usr/lib/
COPY --from=pg18-builder /usr/lib/liblz4.so.1.10.0 /usr/lib/
RUN ln -s libpq.so.5.17 /usr/lib/libpq.so.5 && \
ln -s libpq.so.5.17 /usr/lib/libpq.so && \
RUN ln -s libpq.so.5.18 /usr/lib/libpq.so.5 && \
ln -s libpq.so.5.18 /usr/lib/libpq.so && \
ln -s libzstd.so.1.5.7 /usr/lib/libzstd.so.1 && \
ln -s liblz4.so.1.10.0 /usr/lib/liblz4.so.1

View File

@ -53,6 +53,7 @@ The default binary paths set in the container are as follows:
.. code-block:: bash
DEFAULT_BINARY_PATHS = {
'pg-18': '/usr/local/pgsql-18',
'pg-17': '/usr/local/pgsql-17',
'pg-16': '/usr/local/pgsql-16',
'pg-15': '/usr/local/pgsql-15',

View File

@ -41,6 +41,9 @@ Use the fields in the *Definition* tab to define the function:
will change to an input text field.
* Use the drop-down listbox next to *Language* to select the implementation
language. The default is *sql*.
* Use the drop-down listbox next to *Depends on extensions* to select the extension that this function
depends on (for example, plpgsql). If set, dropping the extension will automatically drop the
function as well.
* Use the fields in the *Arguments* to define an argument. Click the *Add*
icon (+) to set parameters and values for the argument:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -22,6 +22,7 @@ New features
| `Issue #6394 <https://github.com/pgadmin-org/pgadmin4/issues/6394>`_ - Added "MULTIRANGE_TYPE_NAME" option while creating a Range Type.
| `Issue #6395 <https://github.com/pgadmin-org/pgadmin4/issues/6395>`_ - Added "SUBSCRIPT" option while creating a External Type.
| `Issue #8932 <https://github.com/pgadmin-org/pgadmin4/issues/8932>`_ - Added 'failover' and 'two_phase' parameter support in CREATE/ALTER SUBSCRIPTION for PostgreSQL v17+.
Housekeeping
************
@ -30,6 +31,7 @@ Housekeeping
Bug fixes
*********
| `Issue #9158 <https://github.com/pgadmin-org/pgadmin4/issues/9158>`_ - Fixed an issue where saving the newly changed preferences was not reflecting on the preferences tab.
| `Issue #9098 <https://github.com/pgadmin-org/pgadmin4/issues/9098>`_ - Fixed an issue where the query tool displayed 'default' instead of 'null' for null text data in the data output.
| `Issue #9125 <https://github.com/pgadmin-org/pgadmin4/issues/9125>`_ - Fixed an issue where the pgAdmin configuration database wasn't being created on a fresh install when an external database was used for the configuration.
| `Issue #9157 <https://github.com/pgadmin-org/pgadmin4/issues/9157>`_ - Fixed an issue where shortcuts are not working as expected on multiple keyboard layouts.
| `Issue #9158 <https://github.com/pgadmin-org/pgadmin4/issues/9158>`_ - Fixed an issue where saving the newly changed preferences was not reflecting on the preferences tab.

View File

@ -106,6 +106,7 @@ Use the *With* tab to define some parameters for a subscription:
* Move the *Run as owner?* switch to *true* position to specify all replication actions are performed as the subscription owner. If *false*, replication workers will perform actions on each table as the owner of that table. The default is *false*. This option is available only on PostgreSQL 16 and above.
* Use the *Password required?* to specify whether connections to the publisher made as a result of this subscription must use password authentication. This setting is ignored when the subscription is owned by a superuser. The default is true. Only superusers can set this value to *false*. This option is available only on PostgreSQL 16 and above.
* Use the *Origin* to specify whether the subscription will request the publisher to only send changes that don't have an origin or send changes regardless of origin. The default is *any*. This option is available only on PostgreSQL 16 and above.
* Use the *Failover* to specify whether the replication slots associated with the subscription are enabled to be synced to the standbys so that logical replication can be resumed from the new primary after failover. The default is false. This option is available only on PostgreSQL 17 and above.
Click the *SQL* tab to continue.

View File

@ -48,7 +48,8 @@ CA_FILE = '/etc/ssl/certs/ca-certificates.crt'
LOG_FILE = '/dev/null'
HELP_PATH = '../../docs'
DEFAULT_BINARY_PATHS = {
'pg': '/usr/local/pgsql-17',
'pg': '/usr/local/pgsql-18',
'pg-18': '/usr/local/pgsql-18',
'pg-17': '/usr/local/pgsql-17',
'pg-16': '/usr/local/pgsql-16',
'pg-15': '/usr/local/pgsql-15',

View File

@ -17,7 +17,7 @@ azure-identity==1.25.0
azure-mgmt-rdbms==10.1.0
azure-mgmt-resource==24.0.0
azure-mgmt-subscription==3.1.1
bcrypt==4.3.*
bcrypt==5.0.*
boto3==1.40.*
cryptography==46.0.*
Flask-Babel==4.0.*

View File

@ -12,13 +12,13 @@
},
"packageManager": "yarn@4.9.2",
"devDependencies": {
"electron": "38.1.2",
"electron": "38.2.0",
"eslint": "^9.36.0",
"eslint-plugin-unused-imports": "^4.2.0"
},
"dependencies": {
"axios": "^1.12.0",
"electron-context-menu": "^4.1.0",
"electron-store": "^10.1.0"
"electron-store": "^11.0.0"
}
}

View File

@ -200,20 +200,20 @@ __metadata:
linkType: hard
"@types/node@npm:*":
version: 24.5.2
resolution: "@types/node@npm:24.5.2"
version: 24.6.1
resolution: "@types/node@npm:24.6.1"
dependencies:
undici-types: "npm:~7.12.0"
checksum: 10c0/96baaca6564d39c6f7f6eddd73ce41e2a7594ef37225cd52df3be36fad31712af8ae178387a72d0b80f2e2799e7fd30c014bc0ae9eb9f962d9079b691be00c48
undici-types: "npm:~7.13.0"
checksum: 10c0/f2f8aea441d72139345cfa2e392af51bc27d12eb5f74b9b4d202046a2e82ab70d6da89c46a2ac7feea98854c2919e53070869d4af9d448e173a77249fcb7bca3
languageName: node
linkType: hard
"@types/node@npm:^22.7.7":
version: 22.18.6
resolution: "@types/node@npm:22.18.6"
version: 22.18.8
resolution: "@types/node@npm:22.18.8"
dependencies:
undici-types: "npm:~6.21.0"
checksum: 10c0/7ba190da2e64e56c59270661af8cd682c830a1375b6f965ab153be90baabfdaa867aa1d63f87b42de80956996d46dfe1cf93ecefe982d9a16e485b6756949f9a
checksum: 10c0/54473730e7417b923fec427f62ed3204259acbd8e450a7593bad8ae02a75effcfcc864b34bf02c108eeb9c04a404791687f42b801bafa5264a8761f4df9122fd
languageName: node
linkType: hard
@ -321,6 +321,20 @@ __metadata:
languageName: node
linkType: hard
"async-function@npm:^1.0.0":
version: 1.0.0
resolution: "async-function@npm:1.0.0"
checksum: 10c0/669a32c2cb7e45091330c680e92eaeb791bc1d4132d827591e499cd1f776ff5a873e77e5f92d0ce795a8d60f10761dec9ddfe7225a5de680f5d357f67b1aac73
languageName: node
linkType: hard
"async-generator-function@npm:^1.0.0":
version: 1.0.0
resolution: "async-generator-function@npm:1.0.0"
checksum: 10c0/2c50ef856c543ad500d8d8777d347e3c1ba623b93e99c9263ecc5f965c1b12d2a140e2ab6e43c3d0b85366110696f28114649411cbcd10b452a92a2318394186
languageName: node
linkType: hard
"asynckit@npm:^0.4.0":
version: 0.4.0
resolution: "asynckit@npm:0.4.0"
@ -480,20 +494,20 @@ __metadata:
languageName: node
linkType: hard
"conf@npm:^14.0.0":
version: 14.0.0
resolution: "conf@npm:14.0.0"
"conf@npm:^15.0.0":
version: 15.0.0
resolution: "conf@npm:15.0.0"
dependencies:
ajv: "npm:^8.17.1"
ajv-formats: "npm:^3.0.1"
atomically: "npm:^2.0.3"
debounce-fn: "npm:^6.0.0"
dot-prop: "npm:^9.0.0"
dot-prop: "npm:^10.0.0"
env-paths: "npm:^3.0.0"
json-schema-typed: "npm:^8.0.1"
semver: "npm:^7.7.2"
uint8array-extras: "npm:^1.4.0"
checksum: 10c0/d1472ea24dad78d0d33dee47a795dfefe92fcdf2633763a8b35563989f22b2658e768c9d338de8e8e5f9163525d22df9e5f8733094d822a8329f4571dedce208
uint8array-extras: "npm:^1.5.0"
checksum: 10c0/50d7c479276541540773ee31becdf2861b02338d257f219e17773c170419d7c24353dc564b0d392fb77090fa96e66a46d5110ad4f9b965a349e98b1392140693
languageName: node
linkType: hard
@ -588,12 +602,12 @@ __metadata:
languageName: node
linkType: hard
"dot-prop@npm:^9.0.0":
version: 9.0.0
resolution: "dot-prop@npm:9.0.0"
"dot-prop@npm:^10.0.0":
version: 10.0.0
resolution: "dot-prop@npm:10.0.0"
dependencies:
type-fest: "npm:^4.18.2"
checksum: 10c0/4bac49a2f559156811862ac92813906f70529c50da918eaab81b38dd869743c667d578e183607f5ae11e8ae2a02e43e98e32c8a37bc4cae76b04d5b576e3112f
type-fest: "npm:^5.0.0"
checksum: 10c0/549e1c7f8440dac358f934367047c6993797dd0d52bfb1617dcad43f1d6ebeb15a1a4658f2b268e105de47e3dd59e9fd4ff9b61bdbc6159723df0d96dbcd2226
languageName: node
linkType: hard
@ -637,26 +651,26 @@ __metadata:
languageName: node
linkType: hard
"electron-store@npm:^10.1.0":
version: 10.1.0
resolution: "electron-store@npm:10.1.0"
"electron-store@npm:^11.0.0":
version: 11.0.0
resolution: "electron-store@npm:11.0.0"
dependencies:
conf: "npm:^14.0.0"
type-fest: "npm:^4.41.0"
checksum: 10c0/a527a1957b09decbbb36828e299bb06c239ed9a7d406ceda07abe84d2a9dfd1920120c63859e42c9c7a0922b95b840c4f4e5d8db90256814445f22a8a48de07e
conf: "npm:^15.0.0"
type-fest: "npm:^5.0.1"
checksum: 10c0/9253ae08ec4e1b58518b47bcfdd016b1779a035b9b80956c6051e090af473f9fa4531818a68119b71211ad26964a67688e13a8a31ed55075423b9baf7bd51a8a
languageName: node
linkType: hard
"electron@npm:38.1.2":
version: 38.1.2
resolution: "electron@npm:38.1.2"
"electron@npm:38.2.0":
version: 38.2.0
resolution: "electron@npm:38.2.0"
dependencies:
"@electron/get": "npm:^2.0.0"
"@types/node": "npm:^22.7.7"
extract-zip: "npm:^2.0.1"
bin:
electron: cli.js
checksum: 10c0/63f768e8ac396221db17c280e483ac838067e881ee61eb78c5c8f587017e51c587cc1eb687f29672b5e63c0af8a5d818d721d49aef20bac082544a8cf2d323ea
checksum: 10c0/86c1a429b11d273c606d6827bdd114e6b0d57c8d12867e27c3f8542f1f5d85bda8da4330d17ad1556007860e4b2ac6aa71699a0648c9021bfdd83e4224e01c20
languageName: node
linkType: hard
@ -1033,6 +1047,13 @@ __metadata:
languageName: node
linkType: hard
"generator-function@npm:^2.0.0":
version: 2.0.1
resolution: "generator-function@npm:2.0.1"
checksum: 10c0/8a9f59df0f01cfefafdb3b451b80555e5cf6d76487095db91ac461a0e682e4ff7a9dbce15f4ecec191e53586d59eece01949e05a4b4492879600bbbe8e28d6b8
languageName: node
linkType: hard
"get-east-asian-width@npm:^1.0.0":
version: 1.4.0
resolution: "get-east-asian-width@npm:1.4.0"
@ -1041,20 +1062,23 @@ __metadata:
linkType: hard
"get-intrinsic@npm:^1.2.6":
version: 1.3.0
resolution: "get-intrinsic@npm:1.3.0"
version: 1.3.1
resolution: "get-intrinsic@npm:1.3.1"
dependencies:
async-function: "npm:^1.0.0"
async-generator-function: "npm:^1.0.0"
call-bind-apply-helpers: "npm:^1.0.2"
es-define-property: "npm:^1.0.1"
es-errors: "npm:^1.3.0"
es-object-atoms: "npm:^1.1.1"
function-bind: "npm:^1.1.2"
generator-function: "npm:^2.0.0"
get-proto: "npm:^1.0.1"
gopd: "npm:^1.2.0"
has-symbols: "npm:^1.1.0"
hasown: "npm:^2.0.2"
math-intrinsics: "npm:^1.1.0"
checksum: 10c0/52c81808af9a8130f581e6a6a83e1ba4a9f703359e7a438d1369a5267a25412322f03dcbd7c549edaef0b6214a0630a28511d7df0130c93cfd380f4fa0b5b66a
checksum: 10c0/9f4ab0cf7efe0fd2c8185f52e6f637e708f3a112610c88869f8f041bb9ecc2ce44bf285dfdbdc6f4f7c277a5b88d8e94a432374d97cca22f3de7fc63795deb5d
languageName: node
linkType: hard
@ -1563,9 +1587,9 @@ __metadata:
resolution: "pgadmin4@workspace:."
dependencies:
axios: "npm:^1.12.0"
electron: "npm:38.1.2"
electron: "npm:38.2.0"
electron-context-menu: "npm:^4.1.0"
electron-store: "npm:^10.1.0"
electron-store: "npm:^11.0.0"
eslint: "npm:^9.36.0"
eslint-plugin-unused-imports: "npm:^4.2.0"
languageName: unknown
@ -1806,6 +1830,13 @@ __metadata:
languageName: node
linkType: hard
"tagged-tag@npm:^1.0.0":
version: 1.0.0
resolution: "tagged-tag@npm:1.0.0"
checksum: 10c0/91d25c9ffb86a91f20522cefb2cbec9b64caa1febe27ad0df52f08993ff60888022d771e868e6416cf2e72dab68449d2139e8709ba009b74c6c7ecd4000048d1
languageName: node
linkType: hard
"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
version: 0.4.0
resolution: "type-check@npm:0.4.0"
@ -1822,14 +1853,16 @@ __metadata:
languageName: node
linkType: hard
"type-fest@npm:^4.18.2, type-fest@npm:^4.41.0":
version: 4.41.0
resolution: "type-fest@npm:4.41.0"
checksum: 10c0/f5ca697797ed5e88d33ac8f1fec21921839871f808dc59345c9cf67345bfb958ce41bd821165dbf3ae591cedec2bf6fe8882098dfdd8dc54320b859711a2c1e4
"type-fest@npm:^5.0.0, type-fest@npm:^5.0.1":
version: 5.0.1
resolution: "type-fest@npm:5.0.1"
dependencies:
tagged-tag: "npm:^1.0.0"
checksum: 10c0/8d248edd9705e0da3cc14f4f4ca7fb40a6c5b2c822fe4ba8fc226743578bc229f66fa4ff66e7d4070806ab8312adc64199ce0f0836d20ede13922ced2ff36118
languageName: node
linkType: hard
"uint8array-extras@npm:^1.4.0":
"uint8array-extras@npm:^1.5.0":
version: 1.5.0
resolution: "uint8array-extras@npm:1.5.0"
checksum: 10c0/0e74641ac7dadb02eadefc1ccdadba6010e007757bda824960de3c72bbe2b04e6d3af75648441f412148c4103261d54fcb60be45a2863beb76643a55fddba3bd
@ -1843,10 +1876,10 @@ __metadata:
languageName: node
linkType: hard
"undici-types@npm:~7.12.0":
version: 7.12.0
resolution: "undici-types@npm:7.12.0"
checksum: 10c0/326e455bbc0026db1d6b81c76a1cf10c63f7e2f9821db2e24fdc258f482814e5bfa8481f8910d07c68e305937c5c049610fdc441c5e8b7bb0daca7154fb8a306
"undici-types@npm:~7.13.0":
version: 7.13.0
resolution: "undici-types@npm:7.13.0"
checksum: 10c0/44bbb0935425291351bfd8039571f017295b5d6dc5727045d0a4fea8c6ffe73a6703b48ce010f9cb539b9041a75b463f8cfe1e7309cab7486452505fb0d66151
languageName: node
linkType: hard

View File

@ -43,8 +43,8 @@
"html-react-parser": "^5.2.0",
"image-minimizer-webpack-plugin": "^4.1.4",
"imports-loader": "^5.0.0",
"jest": "^30.1.3",
"jest-environment-jsdom": "^30.1.2",
"jest": "^30.2.0",
"jest-environment-jsdom": "^30.2.0",
"loader-utils": "^3.2.1",
"mini-css-extract-plugin": "^2.9.2",
"postcss-loader": "^8.1.1",
@ -81,7 +81,7 @@
"@fortawesome/fontawesome-free": "latest",
"@mui/icons-material": "^7.3.2",
"@mui/material": "^7.3.2",
"@mui/x-date-pickers": "^8.11.3",
"@mui/x-date-pickers": "^8.12.0",
"@nozbe/microfuzz": "^1.0.0",
"@projectstorm/react-diagrams": "^7.0.4",
"@simonwep/pickr": "^1.5.1",

View File

@ -260,7 +260,7 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
list_params = []
if request.method == 'GET':
list_params = ['arguments', 'variables', 'proacl',
'seclabels', 'acl', 'args']
'seclabels', 'acl', 'args', 'dependsonextensions']
if key in list_params and req[key] != '' and req[key] is not None:
# Coverts string into python list as expected.
@ -1171,6 +1171,10 @@ class FunctionView(PGChildNodeView, DataTypeReader, SchemaDiffObjectCompare):
old_data['proparallel'] = \
parallel_dict[old_data['proparallel']]
if self.node_type == 'function' and \
old_data['dependsonextensions'] is None:
old_data['dependsonextensions'] = []
# If any of the below argument is changed,
# then CREATE OR REPLACE SQL statement should be called
fun_change_args = ['lanname', 'prosrc', 'probin', 'prosrc_c',

View File

@ -76,6 +76,7 @@ define('pgadmin.node.function', [
},
getSchema: function(treeNodeInfo, itemNodeData) {
let nodeObj = pgBrowser.Nodes['extension'];
return new FunctionSchema(
(privileges)=>getNodePrivilegeRoleSchema(this, treeNodeInfo, itemNodeData, privileges),
()=>getNodeVariableSchema(this, treeNodeInfo, itemNodeData, false, false),
@ -85,6 +86,16 @@ define('pgadmin.node.function', [
cacheLevel: 'database'
}
),
extensionsList:()=>getNodeAjaxOptions('nodes', nodeObj, treeNodeInfo, itemNodeData, { cacheLevel: 'server'},
(data)=>{
let res = [];
if (data && _.isArray(data)) {
_.each(data, function(d) {
res.push({label: d.label, value: d.label, data: d});
});
}
return res;
}),
getTypes: ()=>getNodeAjaxOptions('get_types', this, treeNodeInfo, itemNodeData),
getLanguage: ()=>getNodeAjaxOptions('get_languages', this, treeNodeInfo, itemNodeData),
getSupportFunctions: ()=>getNodeAjaxOptions('get_support_functions', this, treeNodeInfo, itemNodeData, {

View File

@ -137,6 +137,7 @@ export default class FunctionSchema extends BaseUISchema {
role: [],
schema: [],
getTypes: [],
extensionsList: [],
...fieldOptions,
};
}
@ -274,8 +275,21 @@ export default class FunctionSchema extends BaseUISchema {
return this.node_info && 'catalog' in this.node_info;
}
},
{
},{
id: 'dependsonextensions',
label: gettext('Depends on extensions'),
group: gettext('Definition'),
type: 'select',
options: this.fieldOptions.extensionsList,
controlProps: {
multiple: true,
allowClear: true,
allowSelectAll: true,
placeholder: gettext('Select the Depends on extensions...'),
},
min_version: 130000,
mode: ['create', 'edit', 'properties'],
},{
id: 'probin', label: gettext('Object file'), cell: 'string',
type: 'text', group: gettext('Definition'), deps: ['lanname'], visible:
function(state) {

View File

@ -0,0 +1,82 @@
{% import 'macros/functions/security.macros' as SECLABEL %}
{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
{% import 'macros/functions/variable.macros' as VARIABLE %}
{% set is_columns = [] %}
{% set exclude_quoting = ['search_path'] %}
{% if data %}
{% if query_for == 'sql_panel' and func_def is defined %}
CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{func_def}}
{% else %}
CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({% if data.arguments %}
{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
{% if not loop.last %}, {% endif %}
{% endfor %}
{% endif -%}
)
{% endif %}
RETURNS{% if data.proretset and (data.prorettypename.startswith('SETOF ') or data.prorettypename.startswith('TABLE')) %} {{ data.prorettypename }} {% elif data.proretset %} SETOF {{ data.prorettypename }}{% else %} {{ data.prorettypename }}{% endif %}
LANGUAGE {{ data.lanname|qtLiteral(conn) }}
{% if data.procost %}
COST {{data.procost}}
{% endif %}
{% if data.provolatile %}{% if data.provolatile == 'i' %}IMMUTABLE{% elif data.provolatile == 's' %}STABLE{% else %}VOLATILE{% endif %} {% endif %}{% if data.proleakproof %}LEAKPROOF {% endif %}
{% if data.proisstrict %}STRICT {% endif %}
{% if data.prosecdef %}SECURITY DEFINER {% endif %}
{% if data.proiswindow %}WINDOW {% endif %}
{% if data.proparallel and (data.proparallel == 'r' or data.proparallel == 's' or data.proparallel == 'u') %}
{% if data.proparallel == 'r' %}PARALLEL RESTRICTED {% elif data.proparallel == 's' %}PARALLEL SAFE {% elif data.proparallel == 'u' %}PARALLEL UNSAFE{% endif %}{% endif %}
{% if data.prorows and (data.prorows | int) > 0 %}
ROWS {{data.prorows}}
{% endif %}
{% if data.prosupportfunc %}
SUPPORT {{ data.prosupportfunc }}
{% endif -%}
{% if data.variables %}{% for v in data.variables %}
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral(conn) }}{% endif %}{% endfor %}
{% endif %}
AS {% if data.lanname == 'c' %}
{{ data.probin|qtLiteral(conn) }}, {{ data.prosrc_c|qtLiteral(conn) }}
{% else %}
$BODY${{ data.prosrc }}$BODY${% endif -%};
{% if data.funcowner %}
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
OWNER TO {{ conn|qtIdent(data.funcowner) }};
{% endif -%}
{% if data.dependsonextensions %}
{% for ext in data.dependsonextensions %}
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% endfor %}
{% endif %}
{% if data.acl %}
{% for p in data.acl %}
{{ PRIVILEGE.SET(conn, "FUNCTION", p.grantee, data.name, p.without_grant, p.with_grant, data.pronamespace, data.func_args_without)}}
{% endfor %}{% endif %}
{% if data.revoke_all %}
{{ PRIVILEGE.UNSETALL(conn, "FUNCTION", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}}
{% endif %}
{% if data.description %}
COMMENT ON FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
IS {{ data.description|qtLiteral(conn) }};
{% endif -%}
{% if data.seclabels %}
{% for r in data.seclabels %}
{% if r.label and r.provider %}
{{ SECLABEL.SET(conn, 'FUNCTION', data.name, r.provider, r.label, data.pronamespace, data.func_args_without) }}
{% endif %}
{% endfor %}
{% endif -%}
{% endif %}

View File

@ -0,0 +1,49 @@
SELECT
pr.oid, pr.xmin,
CASE WHEN pr.prokind = 'w' THEN true ELSE false END AS proiswindow,
pr.prosrc, pr.prosrc AS prosrc_c, pr.pronamespace, pr.prolang, pr.procost, pr.prorows, pr.prokind,
pr.prosecdef, pr.proleakproof, pr.proisstrict, pr.proretset, pr.provolatile, pr.proparallel,
pr.pronargs, pr.prorettype, pr.proallargtypes, pr.proargmodes, pr.probin, pr.proacl,
pr.proname, pr.proname AS name, pg_catalog.pg_get_function_result(pr.oid) AS prorettypename,
typns.nspname AS typnsp, lanname, proargnames, pg_catalog.oidvectortypes(proargtypes) AS proargtypenames,
pg_catalog.pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
pr.pronargdefaults, proconfig, pg_catalog.pg_get_userbyid(proowner) AS funcowner, description,
(
SELECT array_agg(DISTINCT e.extname)
FROM pg_depend d
JOIN pg_extension e ON d.refobjid = e.oid
WHERE d.objid = pr.oid
) AS dependsonextensions,
CASE WHEN prosupport = 0::oid THEN ''
ELSE (
SELECT pg_catalog.quote_ident(nspname) || '.' || pg_catalog.quote_ident(proname) AS tfunctions
FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n
WHERE p.pronamespace = n.oid
AND p.oid = pr.prosupport::OID
) END AS prosupportfunc,
(SELECT
pg_catalog.array_agg(provider || '=' || label)
FROM
pg_catalog.pg_seclabel sl1
WHERE
sl1.objoid=pr.oid) AS seclabels
FROM
pg_catalog.pg_proc pr
JOIN
pg_catalog.pg_type typ ON typ.oid=prorettype
JOIN
pg_catalog.pg_namespace typns ON typns.oid=typ.typnamespace
JOIN
pg_catalog.pg_language lng ON lng.oid=prolang
LEFT OUTER JOIN
pg_catalog.pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass and des.objsubid = 0)
WHERE
pr.prokind IN ('f', 'w')
AND typname NOT IN ('trigger', 'event_trigger')
{% if fnid %}
AND pr.oid = {{fnid}}::oid
{% else %}
AND pronamespace = {{scid}}::oid
{% endif %}
ORDER BY
proname;

View File

@ -0,0 +1,146 @@
{% import 'macros/functions/security.macros' as SECLABEL %}
{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
{% import 'macros/functions/variable.macros' as VARIABLE %}{% if data %}
{% set name = o_data.name %}
{% set exclude_quoting = ['search_path'] %}
{% set set_variables = [] %}
{% if 'merged_variables' in data and data.merged_variables|length > 0 %}
{% set set_variables = data.merged_variables %}
{% elif 'variables' in o_data and o_data.variables|length > 0 %}
{% set set_variables = o_data.variables %}
{% endif %}
{% if data.name %}
{% if data.name != o_data.name %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}({{
o_data.proargtypenames }})
RENAME TO {{ conn|qtIdent(data.name) }};
{% set name = data.name %}
{% endif %}
{% endif -%}
{% if data.change_func %}
CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if data.arguments %}
{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
{% if not loop.last %},{% endif %}
{% endfor %}
{% endif -%}
)
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral(conn) }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral(conn) }}
{% endif %}{% if 'provolatile' in data and data.provolatile %}{{ data.provolatile }} {% elif 'provolatile' not in data and o_data.provolatile %}{{ o_data.provolatile }}{% endif %}
{% if ('proleakproof' in data and data.proleakproof) or ('proleakproof' not in data and o_data.proleakproof) %} LEAKPROOF{% elif 'proleakproof' in data and not data.proleakproof %} NOT LEAKPROOF{% endif %}
{% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %}
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %}
{% if data.prosupportfunc %}SUPPORT {{ data.prosupportfunc }}{% elif data.prosupportfunc is not defined and o_data.prosupportfunc %}SUPPORT {{ o_data.prosupportfunc }}{% endif -%}{% if set_variables and set_variables|length > 0 %}{% for v in set_variables %}
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral(conn) }}{% endif %}{% endfor -%}
{% endif %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral(conn) }}{% else %}{{ o_data.probin|qtLiteral(conn) }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral(conn) }}{% else %}{{ o_data.prosrc_c|qtLiteral(conn) }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral(conn) }}, {{ o_data.prosrc_c|qtLiteral(conn) }}{% else %}
$BODY${{ o_data.prosrc }}$BODY${% endif -%};
{% endif -%}
{% if data.funcowner %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
OWNER TO {{ conn|qtIdent(data.funcowner) }};
{% endif -%}
{# The SQL generated below will change priviledges #}
{% if data.acl %}
{% if 'deleted' in data.acl %}
{% for priv in data.acl.deleted %}
{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'changed' in data.acl %}
{% for priv in data.acl.changed %}
{% if priv.grantee != priv.old_grantee %}
{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.old_grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
{% else %}
{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
{% endif %}
{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'added' in data.acl %}
{% for priv in data.acl.added %}
{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}{% endif -%}
{% endif -%}
{% if data.change_func == False %}
{% if data.variables %}
{% if 'deleted' in data.variables and data.variables.deleted|length > 0 %}
{{ VARIABLE.UNSET(conn, 'FUNCTION', name, data.variables.deleted, o_data.pronamespace, o_data.proargtypenames) }}
{% endif -%}
{% if 'merged_variables' in data and data.merged_variables|length > 0 %}
{{ VARIABLE.SET(conn, 'FUNCTION', name, data.merged_variables, o_data.pronamespace, o_data.proargtypenames) }}
{% endif -%}
{% endif -%}
{% endif -%}
{% set seclabels = data.seclabels %}
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
{% for r in seclabels.deleted %}
{{ SECLABEL.UNSET(conn, 'FUNCTION', name, r.provider, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'added' in seclabels and seclabels.added|length > 0 %}
{% for r in seclabels.added %}
{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
{% for r in seclabels.changed %}
{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if data.description is defined and data.description != o_data.description%}
COMMENT ON FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
IS {{ data.description|qtLiteral(conn) }};
{% endif -%}
{% if data.pronamespace %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
{% endif -%}
{% set old_exts = (o_data.dependsonextensions or []) | list %}
{% set new_exts = data.dependsonextensions if 'dependsonextensions' in data else None %}
{% if new_exts is not none and old_exts != new_exts %}
{% for ext in (old_exts + new_exts) | unique %}
{% if ext in new_exts and ext not in old_exts %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }})
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% elif ext in old_exts and ext not in new_exts %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }})
NO DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% endif %}
{% endfor %}
{% endif %}
{% endif %}

View File

@ -50,6 +50,15 @@ $BODY${{ data.prosrc }}$BODY${% endif -%};
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
OWNER TO {{ conn|qtIdent(data.funcowner) }};
{% endif -%}
{% if data.dependsonextensions %}
{% for ext in data.dependsonextensions %}
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% endfor %}
{% endif %}
{% if data.acl %}
{% for p in data.acl %}

View File

@ -10,6 +10,12 @@ SELECT
pg_catalog.pg_get_function_sqlbody(pr.oid) AS prosrc_sql,
CASE WHEN pr.prosqlbody IS NOT NULL THEN true ELSE false END as is_pure_sql,
pr.pronargdefaults, proconfig, pg_catalog.pg_get_userbyid(proowner) AS funcowner, description,
(
SELECT array_agg(DISTINCT e.extname)
FROM pg_depend d
JOIN pg_extension e ON d.refobjid = e.oid
WHERE d.objid = pr.oid
) AS dependsonextensions,
CASE WHEN prosupport = 0::oid THEN ''
ELSE (
SELECT pg_catalog.quote_ident(nspname) || '.' || pg_catalog.quote_ident(proname) AS tfunctions

View File

@ -130,4 +130,20 @@ ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtype
SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
{% endif -%}
{% set old_exts = (o_data.dependsonextensions or []) | list %}
{% set new_exts = data.dependsonextensions if 'dependsonextensions' in data else None %}
{% if new_exts is not none and old_exts != new_exts %}
{% for ext in (old_exts + new_exts) | unique %}
{% if ext in new_exts and ext not in old_exts %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }})
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% elif ext in old_exts and ext not in new_exts %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }})
NO DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% endif %}
{% endfor %}
{% endif %}
{% endif %}

View File

@ -0,0 +1,83 @@
{% import 'macros/functions/security.macros' as SECLABEL %}
{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
{% import 'macros/functions/variable.macros' as VARIABLE %}
{% set is_columns = [] %}
{% set exclude_quoting = ['search_path'] %}
{% if data %}
{% if query_for == 'sql_panel' and func_def is defined %}
CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{func_def}}
{% else %}
CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({% if data.arguments %}
{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
{% if not loop.last %}, {% endif %}
{% endfor %}
{% endif -%}
)
{% endif %}
RETURNS{% if data.proretset and (data.prorettypename.startswith('SETOF ') or data.prorettypename.startswith('TABLE')) %} {{ data.prorettypename }} {% elif data.proretset %} SETOF {{ data.prorettypename }}{% else %} {{ data.prorettypename }}{% endif %}
LANGUAGE {{ data.lanname|qtLiteral(conn) }}
{% if data.procost %}
COST {{data.procost}}
{% endif %}
{% if data.provolatile %}{% if data.provolatile == 'i' %}IMMUTABLE{% elif data.provolatile == 's' %}STABLE{% else %}VOLATILE{% endif %} {% endif %}{% if data.proleakproof %}LEAKPROOF {% endif %}
{% if data.proisstrict %}STRICT {% endif %}
{% if data.prosecdef %}SECURITY DEFINER {% endif %}
{% if data.proiswindow %}WINDOW {% endif %}
{% if data.proparallel and (data.proparallel == 'r' or data.proparallel == 's' or data.proparallel == 'u') %}
{% if data.proparallel == 'r' %}PARALLEL RESTRICTED {% elif data.proparallel == 's' %}PARALLEL SAFE {% elif data.proparallel == 'u' %}PARALLEL UNSAFE{% endif %}{% endif %}
{% if data.prorows and (data.prorows | int) > 0 %}
ROWS {{data.prorows}}
{% endif %}
{% if data.prosupportfunc %}
SUPPORT {{ data.prosupportfunc }}
{% endif -%}
{% if data.variables %}{% for v in data.variables %}
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral(conn) }}{% endif %}{% endfor %}
{% endif %}
AS {% if data.lanname == 'c' %}
{{ data.probin|qtLiteral(conn) }}, {{ data.prosrc_c|qtLiteral(conn) }}
{% else %}
$BODY${{ data.prosrc }}$BODY${% endif -%};
{% if data.funcowner %}
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
OWNER TO {{ conn|qtIdent(data.funcowner) }};
{% endif -%}
{% if data.dependsonextensions %}
{% for ext in data.dependsonextensions %}
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% endfor %}
{% endif %}
{% if data.acl %}
{% for p in data.acl %}
{{ PRIVILEGE.SET(conn, "FUNCTION", p.grantee, data.name, p.without_grant, p.with_grant, data.pronamespace, data.func_args_without)}}
{% endfor %}{% endif %}
{% if data.revoke_all %}
{{ PRIVILEGE.UNSETALL(conn, "FUNCTION", "PUBLIC", data.name, data.pronamespace, data.func_args_without)}}
{% endif %}
{% if data.description %}
COMMENT ON FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
IS {{ data.description|qtLiteral(conn) }};
{% endif -%}
{% if data.seclabels %}
{% for r in data.seclabels %}
{% if r.label and r.provider %}
{{ SECLABEL.SET(conn, 'FUNCTION', data.name, r.provider, r.label, data.pronamespace, data.func_args_without) }}
{% endif %}
{% endfor %}
{% endif -%}
{% endif %}

View File

@ -0,0 +1,43 @@
SELECT
pr.oid, pr.xmin,
CASE WHEN pr.prokind = 'w' THEN true ELSE false END AS proiswindow,
pr.prosrc, pr.prosrc AS prosrc_c, pr.pronamespace, pr.prolang, pr.procost, pr.prorows, pr.prokind,
pr.prosecdef, pr.proleakproof, pr.proisstrict, pr.proretset, pr.provolatile, pr.proparallel,
pr.pronargs, pr.prorettype, pr.proallargtypes, pr.proargmodes, pr.probin, pr.proacl,
pr.proname, pr.proname AS name, pg_catalog.pg_get_function_result(pr.oid) AS prorettypename,
typns.nspname AS typnsp, lanname, proargnames, pg_catalog.oidvectortypes(proargtypes) AS proargtypenames,
pg_catalog.pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
pr.pronargdefaults, proconfig, pg_catalog.pg_get_userbyid(proowner) AS funcowner, description,
(
SELECT array_agg(DISTINCT e.extname)
FROM pg_depend d
JOIN pg_extension e ON d.refobjid = e.oid
WHERE d.objid = pr.oid
) AS dependsonextensions,
CASE WHEN prosupport = 0::oid THEN '' ELSE prosupport::text END AS prosupportfunc,
(SELECT
pg_catalog.array_agg(provider || '=' || label)
FROM
pg_catalog.pg_seclabel sl1
WHERE
sl1.objoid=pr.oid) AS seclabels
FROM
pg_catalog.pg_proc pr
JOIN
pg_catalog.pg_type typ ON typ.oid=prorettype
JOIN
pg_catalog.pg_namespace typns ON typns.oid=typ.typnamespace
JOIN
pg_catalog.pg_language lng ON lng.oid=prolang
LEFT OUTER JOIN
pg_catalog.pg_description des ON (des.objoid=pr.oid AND des.classoid='pg_proc'::regclass and des.objsubid = 0)
WHERE
pr.prokind IN ('f', 'w')
AND typname NOT IN ('trigger', 'event_trigger')
{% if fnid %}
AND pr.oid = {{fnid}}::oid
{% else %}
AND pronamespace = {{scid}}::oid
{% endif %}
ORDER BY
proname;

View File

@ -0,0 +1,146 @@
{% import 'macros/functions/security.macros' as SECLABEL %}
{% import 'macros/functions/privilege.macros' as PRIVILEGE %}
{% import 'macros/functions/variable.macros' as VARIABLE %}{% if data %}
{% set name = o_data.name %}
{% set exclude_quoting = ['search_path'] %}
{% set set_variables = [] %}
{% if 'merged_variables' in data and data.merged_variables|length > 0 %}
{% set set_variables = data.merged_variables %}
{% elif 'variables' in o_data and o_data.variables|length > 0 %}
{% set set_variables = o_data.variables %}
{% endif %}
{% if data.name %}
{% if data.name != o_data.name %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, o_data.name) }}({{
o_data.proargtypenames }})
RENAME TO {{ conn|qtIdent(data.name) }};
{% set name = data.name %}
{% endif %}
{% endif -%}
{% if data.change_func %}
CREATE OR REPLACE FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({% if data.arguments %}
{% for p in data.arguments %}{% if p.argmode %}{{p.argmode}} {% endif %}{% if p.argname %}{{ conn|qtIdent(p.argname) }} {% endif %}{% if p.argtype %}{{ p.argtype }}{% endif %}{% if p.argdefval %} DEFAULT {{p.argdefval}}{% endif %}
{% if not loop.last %},{% endif %}
{% endfor %}
{% endif -%}
)
RETURNS {% if 'prorettypename' in data %}{{ data.prorettypename }}{% else %}{{ o_data.prorettypename }}{% endif %}
{% if 'lanname' in data %}
LANGUAGE {{ data.lanname|qtLiteral(conn) }} {% else %}
LANGUAGE {{ o_data.lanname|qtLiteral(conn) }}
{% endif %}{% if 'provolatile' in data and data.provolatile %}{{ data.provolatile }} {% elif 'provolatile' not in data and o_data.provolatile %}{{ o_data.provolatile }}{% endif %}
{% if ('proleakproof' in data and data.proleakproof) or ('proleakproof' not in data and o_data.proleakproof) %} LEAKPROOF{% elif 'proleakproof' in data and not data.proleakproof %} NOT LEAKPROOF{% endif %}
{% if ('proisstrict' in data and data.proisstrict) or ('proisstrict' not in data and o_data.proisstrict) %} STRICT{% endif %}
{% if ('prosecdef' in data and data.prosecdef) or ('prosecdef' not in data and o_data.prosecdef) %} SECURITY DEFINER{% endif %}
{% if ('proiswindow' in data and data.proiswindow) or ('proiswindow' not in data and o_data.proiswindow) %} WINDOW{% endif %}
{% if 'proparallel' in data and data.proparallel %}PARALLEL {{ data.proparallel }}{% elif 'proparallel' not in data and o_data.proparallel %}PARALLEL {{ o_data.proparallel }}{% endif %}
{% if data.procost %}COST {{data.procost}}{% elif o_data.procost %}COST {{o_data.procost}}{% endif %}{% if data.prorows and data.prorows != '0' %}
ROWS {{data.prorows}}{% elif data.prorows is not defined and o_data.prorows and o_data.prorows != '0' %} ROWS {{o_data.prorows}} {%endif %}
{% if data.prosupportfunc %}SUPPORT {{ data.prosupportfunc }}{% elif data.prosupportfunc is not defined and o_data.prosupportfunc %}SUPPORT {{ o_data.prosupportfunc }}{% endif -%}{% if set_variables and set_variables|length > 0 %}{% for v in set_variables %}
SET {{ conn|qtIdent(v.name) }}={% if v.name in exclude_quoting %}{{ v.value }}{% else %}{{ v.value|qtLiteral(conn) }}{% endif %}{% endfor -%}
{% endif %}
AS {% if (data.lanname == 'c' or o_data.lanname == 'c') and ('probin' in data or 'prosrc_c' in data) %}
{% if 'probin' in data %}{{ data.probin|qtLiteral(conn) }}{% else %}{{ o_data.probin|qtLiteral(conn) }}{% endif %}, {% if 'prosrc_c' in data %}{{ data.prosrc_c|qtLiteral(conn) }}{% else %}{{ o_data.prosrc_c|qtLiteral(conn) }}{% endif %}{% elif 'prosrc' in data %}
$BODY${{ data.prosrc }}$BODY${% elif o_data.lanname == 'c' %}
{{ o_data.probin|qtLiteral(conn) }}, {{ o_data.prosrc_c|qtLiteral(conn) }}{% else %}
$BODY${{ o_data.prosrc }}$BODY${% endif -%};
{% endif -%}
{% if data.funcowner %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
OWNER TO {{ conn|qtIdent(data.funcowner) }};
{% endif -%}
{# The SQL generated below will change priviledges #}
{% if data.acl %}
{% if 'deleted' in data.acl %}
{% for priv in data.acl.deleted %}
{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'changed' in data.acl %}
{% for priv in data.acl.changed %}
{% if priv.grantee != priv.old_grantee %}
{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.old_grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
{% else %}
{{ PRIVILEGE.UNSETALL(conn, 'FUNCTION', priv.grantee, name, o_data.pronamespace, o_data.proargtypenames) }}
{% endif %}
{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'added' in data.acl %}
{% for priv in data.acl.added %}
{{ PRIVILEGE.SET(conn, 'FUNCTION', priv.grantee, name, priv.without_grant, priv.with_grant, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}{% endif -%}
{% endif -%}
{% if data.change_func == False %}
{% if data.variables %}
{% if 'deleted' in data.variables and data.variables.deleted|length > 0 %}
{{ VARIABLE.UNSET(conn, 'FUNCTION', name, data.variables.deleted, o_data.pronamespace, o_data.proargtypenames) }}
{% endif -%}
{% if 'merged_variables' in data and data.merged_variables|length > 0 %}
{{ VARIABLE.SET(conn, 'FUNCTION', name, data.merged_variables, o_data.pronamespace, o_data.proargtypenames) }}
{% endif -%}
{% endif -%}
{% endif -%}
{% set seclabels = data.seclabels %}
{% if 'deleted' in seclabels and seclabels.deleted|length > 0 %}
{% for r in seclabels.deleted %}
{{ SECLABEL.UNSET(conn, 'FUNCTION', name, r.provider, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'added' in seclabels and seclabels.added|length > 0 %}
{% for r in seclabels.added %}
{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if 'changed' in seclabels and seclabels.changed|length > 0 %}
{% for r in seclabels.changed %}
{{ SECLABEL.SET(conn, 'FUNCTION', name, r.provider, r.label, o_data.pronamespace, o_data.proargtypenames) }}
{% endfor %}
{% endif -%}
{% if data.description is defined and data.description != o_data.description%}
COMMENT ON FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
IS {{ data.description|qtLiteral(conn) }};
{% endif -%}
{% if data.pronamespace %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtypenames }})
SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
{% endif -%}
{% set old_exts = (o_data.dependsonextensions or []) | list %}
{% set new_exts = data.dependsonextensions if 'dependsonextensions' in data else None %}
{% if new_exts is not none and old_exts != new_exts %}
{% for ext in (old_exts + new_exts) | unique %}
{% if ext in new_exts and ext not in old_exts %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }})
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% elif ext in old_exts and ext not in new_exts %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }})
NO DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% endif %}
{% endfor %}
{% endif %}
{% endif %}

View File

@ -50,6 +50,15 @@ $BODY${{ data.prosrc }}$BODY${% endif -%};
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
OWNER TO {{ conn|qtIdent(data.funcowner) }};
{% endif -%}
{% if data.dependsonextensions %}
{% for ext in data.dependsonextensions %}
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% endfor %}
{% endif %}
{% if data.acl %}
{% for p in data.acl %}

View File

@ -8,6 +8,12 @@ SELECT
typns.nspname AS typnsp, lanname, proargnames, pg_catalog.oidvectortypes(proargtypes) AS proargtypenames,
pg_catalog.pg_get_expr(proargdefaults, 'pg_catalog.pg_class'::regclass) AS proargdefaultvals,
pr.pronargdefaults, proconfig, pg_catalog.pg_get_userbyid(proowner) AS funcowner, description,
(
SELECT array_agg(DISTINCT e.extname)
FROM pg_depend d
JOIN pg_extension e ON d.refobjid = e.oid
WHERE d.objid = pr.oid
) AS dependsonextensions,
pg_catalog.pg_get_function_sqlbody(pr.oid) AS prosrc_sql,
CASE WHEN pr.prosqlbody IS NOT NULL THEN true ELSE false END as is_pure_sql,
CASE WHEN prosupport = 0::oid THEN '' ELSE prosupport::text END AS prosupportfunc,

View File

@ -130,4 +130,20 @@ ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{o_data.proargtype
SET SCHEMA {{ conn|qtIdent(data.pronamespace) }};
{% endif -%}
{% set old_exts = (o_data.dependsonextensions or []) | list %}
{% set new_exts = data.dependsonextensions if 'dependsonextensions' in data else None %}
{% if new_exts is not none and old_exts != new_exts %}
{% for ext in (old_exts + new_exts) | unique %}
{% if ext in new_exts and ext not in old_exts %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }})
DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% elif ext in old_exts and ext not in new_exts %}
ALTER FUNCTION {{ conn|qtIdent(o_data.pronamespace, name) }}({{ o_data.proargtypenames }})
NO DEPENDS ON EXTENSION {{ conn|qtIdent(ext) }};
{% endif %}
{% endfor %}
{% endif %}
{% endif %}

View File

@ -0,0 +1,2 @@
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
NO DEPENDS ON EXTENSION postgres_fdw;

View File

@ -0,0 +1,22 @@
-- FUNCTION: public.Function1_$%{}[]()&*^!@"'`\/#(character varying)
-- DROP FUNCTION IF EXISTS public."Function1_$%{}[]()&*^!@""'`\/#"(character varying);
CREATE OR REPLACE FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(
param character varying DEFAULT '1'::character varying)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE LEAKPROOF STRICT SECURITY DEFINER WINDOW PARALLEL UNSAFE
SET enable_sort='true'
AS $BODY$
begin
select '1';
end
$BODY$;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
OWNER TO postgres;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
DEPENDS ON EXTENSION plpgsql;

View File

@ -0,0 +1,20 @@
CREATE FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(IN param character varying DEFAULT '1')
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE LEAKPROOF STRICT SECURITY DEFINER WINDOW PARALLEL UNSAFE
SET enable_sort=true
AS $BODY$
begin
select '1';
end
$BODY$;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
OWNER TO postgres;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
DEPENDS ON EXTENSION plpgsql;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
DEPENDS ON EXTENSION postgres_fdw;

View File

@ -0,0 +1,25 @@
-- FUNCTION: public.Function1_$%{}[]()&*^!@"'`\/#(character varying)
-- DROP FUNCTION IF EXISTS public."Function1_$%{}[]()&*^!@""'`\/#"(character varying);
CREATE OR REPLACE FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(
param character varying DEFAULT '1'::character varying)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE LEAKPROOF STRICT SECURITY DEFINER WINDOW PARALLEL UNSAFE
SET enable_sort='true'
AS $BODY$
begin
select '1';
end
$BODY$;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
OWNER TO postgres;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
DEPENDS ON EXTENSION plpgsql;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
DEPENDS ON EXTENSION postgres_fdw;

View File

@ -0,0 +1,434 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Extension",
"endpoint": "NODE-extension.obj",
"sql_endpoint": "NODE-extension.sql_id",
"data": {
"name": "postgres_fdw",
"version": "",
"relocatable": true
}
},
{
"type": "create",
"name": "Create function with extensions.",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function1_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "postgres",
"pronamespace": 2200,
"prorettypename": "character varying",
"lanname": "plpgsql",
"provolatile": "v",
"proretset": false,
"proisstrict": true,
"prosecdef": true,
"proiswindow": true,
"proparallel": "u",
"procost": "100",
"prorows": "0",
"proleakproof": true,
"arguments": [
{
"argtype": "character varying",
"argmode": "IN",
"argname": "param",
"argdefval": "'1'"
}
],
"prosrc": "begin\nselect '1';\nend",
"probin": "$libdir/",
"options": [],
"variables": [
{
"name": "enable_sort",
"value": true
}
],
"seclabels": [],
"acl": [],
"dependsonextensions": ["plpgsql", "postgres_fdw"],
"schema": "public"
},
"expected_sql_file": "create_function_on_depends.sql",
"expected_msql_file": "create_function_on_depends.msql"
},
{
"type": "alter",
"name": "Alter function with NO DEPENDS ON",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"dependsonextensions": ["plpgsql"]
},
"expected_sql_file": "alter_function_no_depends.sql",
"expected_msql_file": "alter_function_no_depends.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
},
{
"type": "create",
"name": "Create function with all options.",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function1_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "postgres",
"pronamespace": 2200,
"prorettypename": "character varying",
"lanname": "plpgsql",
"provolatile": "v",
"proretset": false,
"proisstrict": true,
"prosecdef": true,
"proiswindow": true,
"proparallel": "u",
"procost": "100",
"prorows": "0",
"proleakproof": true,
"arguments": [
{
"argtype": "character varying",
"argmode": "IN",
"argname": "param",
"argdefval": "'1'"
}
],
"prosrc": "begin\nselect '1';\nend",
"probin": "$libdir/",
"options": [],
"variables": [
{
"name": "enable_sort",
"value": true
}
],
"seclabels": [],
"acl": [],
"schema": "public"
},
"expected_sql_file": "create_function.sql",
"expected_msql_file": "create_function.msql"
},
{
"type": "alter",
"name": "Alter function comment",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"description": "Some comment"
},
"expected_sql_file": "alter_function_comment.sql",
"expected_msql_file": "alter_function_comment.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
},
{
"type": "create",
"name": "Create function for alter.",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function2_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "postgres",
"pronamespace": 2200,
"prorettypename": "character varying",
"lanname": "plpgsql",
"provolatile": "v",
"proparallel": "u",
"arguments": [],
"procost": "100",
"prosrc": "begin\nselect '1';\nend",
"probin": "$libdir/",
"options": [],
"variables": [
{
"name": "enable_sort",
"value": true
}
],
"seclabels": [],
"acl": [],
"schema": "public"
},
"expected_sql_file": "create_function_for_alter.sql",
"expected_msql_file": "create_function_for_alter.msql"
},
{
"type": "alter",
"name": "Alter function rename.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name":"Function3_$%{}[]()&*^!@\"'`\\/#"
},
"expected_sql_file": "alter_function_rename.sql",
"expected_msql_file": "alter_function_rename.msql"
},
{
"type": "alter",
"name": "Alter function code and add parameters.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"prosrc": "begin\nselect '2';\nend\n",
"variables": {
"added": [
{
"name": "application_name",
"value": "appname"
}
]
}
},
"expected_sql_file": "alter_function_add_parameter.sql",
"expected_msql_file": "alter_function_add_parameter.msql"
},
{
"type": "alter",
"name": "Alter function delete parameters.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"variables": {
"deleted": [
{
"name": "application_name",
"value": true
}
]
}
},
"expected_sql_file": "alter_function_delete_parameter.sql",
"expected_msql_file": "alter_function_delete_parameter.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
},
{
"type": "create",
"name": "Create function for acl.",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function2_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "postgres",
"pronamespace": 2200,
"prorettypename": "character varying",
"lanname": "plpgsql",
"provolatile": "v",
"proparallel": "u",
"arguments": [],
"procost": "100",
"prosrc": "begin\nselect '1';\nend",
"probin": "$libdir/",
"options": [],
"variables": [
{
"name": "enable_sort",
"value": true
}
],
"seclabels": [],
"acl": [],
"schema": "public"
},
"expected_sql_file": "create_function_for_alter.sql",
"expected_msql_file": "create_function_for_alter.msql"
},
{
"type": "alter",
"name": "Alter function add acl.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"acl": {
"added": [
{
"grantee": "postgres",
"grantor": "postgres",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
}
]
}
},
"expected_sql_file": "alter_function_add_acl.sql",
"expected_msql_file": "alter_function_add_acl.msql"
},
{
"type": "alter",
"name": "Alter function remove partial privileges.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"acl": {
"deleted": [
{
"grantee": "PUBLIC",
"grantor": "postgres",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
}
]
}
}
},
{
"type": "alter",
"name": "Alter function change grantee in privileges.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"acl": {
"changed": [
{
"grantee": "PUBLIC",
"grantor": "postgres",
"old_grantee": "postgres",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
}
]
}
},
"expected_sql_file": "alter_function_change_grantee_acl.sql",
"expected_msql_file": "alter_function_change_grantee_acl.msql"
},
{
"type": "alter",
"name": "Alter function delete acl.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"acl": {
"deleted": [
{
"grantee": "PUBLIC",
"grantor": "postgres",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
},
{
"grantee": "postgres",
"grantor": "postgres",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
}
]
}
},
"expected_sql_file": "alter_function_delete_acl.sql",
"expected_msql_file": "alter_function_delete_acl.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
},
{
"type": "create",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"name": "Create function with custom return type.",
"data": {
"name": "Function3_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "postgres",
"pronamespace": 2200,
"prorettypename": "table(val character varying)",
"lanname": "plpgsql",
"provolatile": "v",
"proretset": false,
"proisstrict": true,
"prosecdef": true,
"proiswindow": true,
"proparallel": "u",
"procost": "100",
"prorows": "0",
"proleakproof": true,
"arguments": [
{
"argtype": "character varying",
"argmode": "IN",
"argname": "param",
"argdefval": "'1'"
}
],
"prosrc": "begin\n return query select '1'::character varying;\nend",
"probin": "$libdir/",
"options": [],
"variables": [],
"seclabels": [],
"acl": [],
"schema": "public"
},
"expected_sql_file": "create_function_with_custom_return.sql",
"expected_msql_file": "create_function_with_custom_return.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
}
]
}

View File

@ -1,5 +1,81 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Extension",
"endpoint": "NODE-extension.obj",
"sql_endpoint": "NODE-extension.sql_id",
"data": {
"name": "postgres_fdw",
"version": "",
"relocatable": true
}
},
{
"type": "create",
"name": "Create function with extensions.",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function1_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "postgres",
"pronamespace": 2200,
"prorettypename": "character varying",
"lanname": "plpgsql",
"provolatile": "v",
"proretset": false,
"proisstrict": true,
"prosecdef": true,
"proiswindow": true,
"proparallel": "u",
"procost": "100",
"prorows": "0",
"proleakproof": true,
"arguments": [
{
"argtype": "character varying",
"argmode": "IN",
"argname": "param",
"argdefval": "'1'"
}
],
"prosrc": "begin\nselect '1';\nend",
"probin": "$libdir/",
"options": [],
"variables": [
{
"name": "enable_sort",
"value": true
}
],
"seclabels": [],
"acl": [],
"dependsonextensions": ["plpgsql", "postgres_fdw"],
"schema": "public"
},
"expected_sql_file": "create_function_on_depends.sql",
"expected_msql_file": "create_function_on_depends.msql"
},
{
"type": "alter",
"name": "Alter function with NO DEPENDS ON",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"dependsonextensions": ["plpgsql"]
},
"expected_sql_file": "alter_function_no_depends.sql",
"expected_msql_file": "alter_function_no_depends.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
},
{
"type": "create",
"name": "Create function with all options.",

View File

@ -0,0 +1,2 @@
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
NO DEPENDS ON EXTENSION postgres_fdw;

View File

@ -0,0 +1,22 @@
-- FUNCTION: public.Function1_$%{}[]()&*^!@"'`\/#(character varying)
-- DROP FUNCTION IF EXISTS public."Function1_$%{}[]()&*^!@""'`\/#"(character varying);
CREATE OR REPLACE FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(
param character varying DEFAULT '1'::character varying)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE LEAKPROOF STRICT SECURITY DEFINER WINDOW PARALLEL UNSAFE
SET enable_sort='true'
AS $BODY$
begin
select '1';
end
$BODY$;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
OWNER TO enterprisedb;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
DEPENDS ON EXTENSION plpgsql;

View File

@ -0,0 +1,20 @@
CREATE FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(IN param character varying DEFAULT '1')
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE LEAKPROOF STRICT SECURITY DEFINER WINDOW PARALLEL UNSAFE
SET enable_sort=true
AS $BODY$
begin
select '1';
end
$BODY$;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
OWNER TO enterprisedb;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
DEPENDS ON EXTENSION plpgsql;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
DEPENDS ON EXTENSION postgres_fdw;

View File

@ -0,0 +1,25 @@
-- FUNCTION: public.Function1_$%{}[]()&*^!@"'`\/#(character varying)
-- DROP FUNCTION IF EXISTS public."Function1_$%{}[]()&*^!@""'`\/#"(character varying);
CREATE OR REPLACE FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(
param character varying DEFAULT '1'::character varying)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE LEAKPROOF STRICT SECURITY DEFINER WINDOW PARALLEL UNSAFE
SET enable_sort='true'
AS $BODY$
begin
select '1';
end
$BODY$;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
OWNER TO enterprisedb;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
DEPENDS ON EXTENSION plpgsql;
ALTER FUNCTION public."Function1_$%{}[]()&*^!@""'`\/#"(character varying)
DEPENDS ON EXTENSION postgres_fdw;

View File

@ -0,0 +1,446 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Extension",
"endpoint": "NODE-extension.obj",
"sql_endpoint": "NODE-extension.sql_id",
"data": {
"name": "postgres_fdw",
"version": "",
"relocatable": true
}
},
{
"type": "create",
"name": "Create function with extensions.",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function1_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "enterprisedb",
"pronamespace": 2200,
"prorettypename": "character varying",
"lanname": "plpgsql",
"provolatile": "v",
"proretset": false,
"proisstrict": true,
"prosecdef": true,
"proiswindow": true,
"proparallel": "u",
"procost": "100",
"prorows": "0",
"proleakproof": true,
"arguments": [
{
"argtype": "character varying",
"argmode": "IN",
"argname": "param",
"argdefval": "'1'"
}
],
"prosrc": "begin\nselect '1';\nend",
"probin": "$libdir/",
"options": [],
"variables": [
{
"name": "enable_sort",
"value": true
}
],
"seclabels": [],
"acl": [],
"dependsonextensions": ["plpgsql", "postgres_fdw"],
"schema": "public"
},
"expected_sql_file": "create_function_on_depends.sql",
"expected_msql_file": "create_function_on_depends.msql"
},
{
"type": "alter",
"name": "Alter function with NO DEPENDS ON",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"dependsonextensions": ["plpgsql"]
},
"expected_sql_file": "alter_function_no_depends.sql",
"expected_msql_file": "alter_function_no_depends.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
},
{
"type": "create",
"name": "Create function with all options.",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function1_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "enterprisedb",
"pronamespace": 2200,
"prorettypename": "character varying",
"lanname": "plpgsql",
"provolatile": "v",
"proretset": false,
"proisstrict": true,
"prosecdef": true,
"proiswindow": true,
"proparallel": "u",
"procost": "100",
"prorows": "0",
"proleakproof": true,
"arguments": [
{
"argtype": "character varying",
"argmode": "IN",
"argname": "param",
"argdefval": "'1'"
}
],
"prosrc": "begin\nselect '1';\nend",
"probin": "$libdir/",
"options": [],
"variables": [
{
"name": "enable_sort",
"value": true
}
],
"seclabels": [],
"acl": [],
"schema": "public"
},
"expected_sql_file": "create_function.sql",
"expected_msql_file": "create_function.msql"
},
{
"type": "alter",
"name": "Alter function comment",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"description": "Some comment"
},
"expected_sql_file": "alter_function_comment.sql",
"expected_msql_file": "alter_function_comment.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
},
{
"type": "create",
"name": "Create function for alter.",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function2_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "enterprisedb",
"pronamespace": 2200,
"prorettypename": "character varying",
"lanname": "plpgsql",
"provolatile": "v",
"proparallel": "u",
"prosecdef": true,
"arguments": [],
"procost": "100",
"prosrc": "begin\nselect '1';\nend",
"probin": "$libdir/",
"options": [],
"variables": [
{
"name": "enable_sort",
"value": true
}
],
"seclabels": [],
"acl": [],
"schema": "public"
},
"expected_sql_file": "create_function_for_alter.sql",
"expected_msql_file": "create_function_for_alter.msql"
},
{
"type": "alter",
"name": "Alter function rename.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function3_$%{}[]()&*^!@\"'`\\/#"
},
"expected_sql_file": "alter_function_rename.sql",
"expected_msql_file": "alter_function_rename.msql"
},
{
"type": "alter",
"name": "Alter function code and add parameters.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"prosrc": "begin\nselect '2';\nend\n",
"variables": {
"added": [
{
"name": "application_name",
"value": "appname"
}
]
}
},
"expected_sql_file": "alter_function_add_parameter.sql"
},
{
"type": "alter",
"name": "Alter function delete parameters.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"variables": {
"deleted": [
{
"name": "application_name",
"value": true
}
]
}
},
"expected_sql_file": "alter_function_delete_parameter.sql",
"expected_msql_file": "alter_function_delete_parameter.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
},
{
"type": "create",
"name": "Create function for acl.",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function2_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "enterprisedb",
"pronamespace": 2200,
"prorettypename": "character varying",
"lanname": "plpgsql",
"provolatile": "v",
"proparallel": "u",
"prosecdef": true,
"arguments": [],
"procost": "100",
"prosrc": "begin\nselect '1';\nend",
"probin": "$libdir/",
"options": [],
"variables": [
{
"name": "enable_sort",
"value": true
}
],
"seclabels": [],
"acl": [],
"schema": "public"
},
"expected_sql_file": "create_function_for_alter.sql",
"expected_msql_file": "create_function_for_alter.msql"
},
{
"type": "alter",
"name": "Alter function add acl.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"acl": {
"added": [
{
"grantee": "PUBLIC",
"grantor": "enterprisedb",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
},
{
"grantee": "enterprisedb",
"grantor": "enterprisedb",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
}
]
}
},
"expected_sql_file": "alter_function_add_acl.sql",
"expected_msql_file": "alter_function_add_acl.msql"
},
{
"type": "alter",
"name": "Alter function remove partial privileges.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"acl": {
"deleted": [
{
"grantee": "PUBLIC",
"grantor": "enterprisedb",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
}
]
}
}
},
{
"type": "alter",
"name": "Alter function change grantee in privileges.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"acl": {
"changed": [
{
"grantee": "PUBLIC",
"grantor": "enterprisedb",
"old_grantee": "enterprisedb",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
}
]
}
},
"expected_sql_file": "alter_function_change_grantee_acl.sql",
"expected_msql_file": "alter_function_change_grantee_acl.msql"
},
{
"type": "alter",
"name": "Alter function delete acl.",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"acl": {
"deleted": [
{
"grantee": "PUBLIC",
"grantor": "enterprisedb",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
},
{
"grantee": "enterprisedb",
"grantor": "enterprisedb",
"privileges": [
{
"privilege_type": "X",
"privilege": true,
"with_grant": false
}
]
}
]
}
},
"expected_sql_file": "alter_function_delete_acl.sql",
"expected_msql_file": "alter_function_delete_acl.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
},
{
"type": "create",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"name": "Create function with custom return type.",
"data": {
"name": "Function3_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "enterprisedb",
"pronamespace": 2200,
"prorettypename": "table(val character varying)",
"lanname": "plpgsql",
"provolatile": "v",
"proretset": false,
"proisstrict": true,
"prosecdef": true,
"proiswindow": true,
"proparallel": "u",
"procost": "100",
"prorows": "0",
"proleakproof": true,
"arguments": [
{
"argtype": "character varying",
"argmode": "IN",
"argname": "param",
"argdefval": "'1'"
}
],
"prosrc": "begin\n return query select '1'::character varying;\nend",
"probin": "$libdir/",
"options": [],
"variables": [],
"seclabels": [],
"acl": [],
"schema": "public"
},
"expected_sql_file": "create_function_with_custom_return.sql",
"expected_msql_file": "create_function_with_custom_return.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
}
]
}

View File

@ -1,5 +1,81 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Extension",
"endpoint": "NODE-extension.obj",
"sql_endpoint": "NODE-extension.sql_id",
"data": {
"name": "postgres_fdw",
"version": "",
"relocatable": true
}
},
{
"type": "create",
"name": "Create function with extensions.",
"endpoint": "NODE-function.obj",
"msql_endpoint": "NODE-function.msql",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"name": "Function1_$%{}[]()&*^!@\"'`\\/#",
"funcowner": "enterprisedb",
"pronamespace": 2200,
"prorettypename": "character varying",
"lanname": "plpgsql",
"provolatile": "v",
"proretset": false,
"proisstrict": true,
"prosecdef": true,
"proiswindow": true,
"proparallel": "u",
"procost": "100",
"prorows": "0",
"proleakproof": true,
"arguments": [
{
"argtype": "character varying",
"argmode": "IN",
"argname": "param",
"argdefval": "'1'"
}
],
"prosrc": "begin\nselect '1';\nend",
"probin": "$libdir/",
"options": [],
"variables": [
{
"name": "enable_sort",
"value": true
}
],
"seclabels": [],
"acl": [],
"dependsonextensions": ["plpgsql", "postgres_fdw"],
"schema": "public"
},
"expected_sql_file": "create_function_on_depends.sql",
"expected_msql_file": "create_function_on_depends.msql"
},
{
"type": "alter",
"name": "Alter function with NO DEPENDS ON",
"endpoint": "NODE-function.obj_id",
"msql_endpoint": "NODE-function.msql_id",
"sql_endpoint": "NODE-function.sql_id",
"data": {
"dependsonextensions": ["plpgsql"]
},
"expected_sql_file": "alter_function_no_depends.sql",
"expected_msql_file": "alter_function_no_depends.msql"
},
{
"type": "delete",
"name": "Drop function",
"endpoint": "NODE-function.delete_id",
"data": {
}
},
{
"type": "create",
"name": "Create function with all options.",

View File

@ -76,6 +76,19 @@ class FunctionAddTestCase(BaseTestGenerator):
"status_code": 500
}
)),
('Create Function With Invalid Depends On Extension Fail', dict(
url='/browser/function/obj/',
is_positive_test=False,
mocking_required=True,
is_mock_function=True,
mock_data={
"function_name": "_get_sql",
"return_value": "(False, 'Invalid extension specified')"
},
expected_data={
"status_code": 500
}
))
]
def create_function(self, data):
@ -113,6 +126,7 @@ class FunctionAddTestCase(BaseTestGenerator):
"lanname": "sql",
"name": "test_function",
"options": [],
"dependsonextensions": ["plpgsql"],
"proleakproof": True,
"pronamespace": 2200,
"prorettypename": "integer",
@ -143,10 +157,13 @@ class FunctionAddTestCase(BaseTestGenerator):
)
data['prosupportfuc'] = support_function_name
if self.is_positive_test:
response = self.create_function(data)
else:
if "Invalid Depends On Extension" in getattr(
self, 'scenario_name', ''
):
data["dependsonextensions"] = ["non_existing_extensions"]
if hasattr(self, 'is_mock_function'):
def _get_sql(self, **kwargs):
return False, ''

View File

@ -35,6 +35,7 @@ class FunctionGetmsqlTestCase(BaseTestGenerator):
"funcowner": "",
"pronamespace": 2200,
"prorettypename": "character varying",
"dependsonextensions": [],
"lanname": "sql",
"arguments": [],
"prosrc": "select '1'",
@ -67,7 +68,8 @@ class FunctionGetmsqlTestCase(BaseTestGenerator):
"probin": "$libdir/",
"variables": [],
"seclabels": [],
"acl": []
"acl": [],
"dependsonextensions": ["non_existing_extension"]
},
mock_data={
"function_name": '_get_sql',
@ -178,7 +180,6 @@ class FunctionGetmsqlTestCase(BaseTestGenerator):
func_name = "test_function_delete_%s" % str(uuid.uuid4())[1:8]
function_info = funcs_utils.create_function(
self.server, self.db_name, self.schema_name, func_name)
func_id = function_info[0]
self.test_data['oid'] = func_id
self.test_data['name'] = func_name
@ -187,6 +188,7 @@ class FunctionGetmsqlTestCase(BaseTestGenerator):
self.schema_id) + '/' + str(func_id) + '?' + (
urlencode(self.test_data))
else:
self.test_data['dependsonextensions'] = json.dumps(["plpgsql"])
url = self.url + str(utils.SERVER_GROUP) + '/' + str(
self.server_id) + '/' + str(self.db_id) + '/' + str(
self.schema_id) + '/?' + (urlencode(self.test_data))

View File

@ -75,7 +75,7 @@ class FunctionPutTestCase(BaseTestGenerator):
}
)),
(
'Fetch Function update with arguments',
'Fetch Function update with arguments and depends on exxtensions.',
dict(
url='/browser/function/obj/',
is_positive_test=True,
@ -155,7 +155,8 @@ class FunctionPutTestCase(BaseTestGenerator):
data = {
"description": "This is a procedure update comment",
"id": func_id
"id": func_id,
"dependsonextensions": ["plpgsql"]
}
if hasattr(self, "is_add_argument") and self.is_add_argument:

View File

@ -13,7 +13,7 @@
add empty bracket with table name
#}
{% set empty_bracket = ""%}
{% if data.coll_inherits|length == 0 and data.columns|length == 0 and not data.typname and not data.like_relation and data.primary_key|length == 0 and data.unique_constraint|length == 0 and data.foreign_key|length == 0 and data.check_constraint|length == 0 and data.exclude_constraint|length == 0 %}
{% if not (data.coll_inherits or data.columns or data.typname or data.like_relation or data.primary_key or data.unique_constraint or data.foreign_key or data.check_constraint or data.exclude_constraint) %}
{% set empty_bracket = "\n(\n)"%}
{% endif %}
{% set with_clause = false%}

View File

@ -652,6 +652,14 @@ class SubscriptionView(PGChildNodeView, SchemaDiffObjectCompare):
if len(res['rows']) == 0:
return gone(self._NOT_FOUND_PUB_INFORMATION)
if self.manager.version >= 150000:
res['rows'][0]['two_phase'] = \
self.two_phase_mapping[res['rows'][0]['two_phase']]
if self.manager.version >= 160000:
res['rows'][0]['streaming'] = \
self.streaming_mapping[res['rows'][0]['streaming']]
old_data = res['rows'][0]
data, old_data = self.get_required_details(data, old_data)

View File

@ -26,10 +26,11 @@ export default class SubscriptionSchema extends BaseUISchema{
binary:false,
two_phase:false,
disable_on_error:false,
streaming:false,
streaming: (node_info?.node_info?.version >= 180000) ? 'parallel' : false,
password_required:true,
run_as_owner:false,
origin:'any',
failover:false,
copy_data_after_refresh:false,
sync:'off',
refresh_pub: false,
@ -72,13 +73,7 @@ export default class SubscriptionSchema extends BaseUISchema{
(this.node_info['node_info'].host == 'localhost' || this.node_info['node_info'].host == '127.0.0.1')){
host = this.node_info['node_info'].host;
}
if (host == this.node_info['node_info'].host && port == this.node_info['node_info'].port){
state.create_slot = false;
return true;
} else {
state.create_slot = true;
}
return false;
return (host == this.node_info['node_info'].host && port == this.node_info['node_info'].port);
}
isAllConnectionDataEnter(state){
let host = state.host,
@ -317,6 +312,14 @@ export default class SubscriptionSchema extends BaseUISchema{
readonly: obj.isConnect, deps :['connect', 'host', 'port'],
helpMessage: gettext('Specifies whether the command should create the replication slot on the publisher.This field will be disabled and set to false if subscription connects to same database.Otherwise, the CREATE SUBSCRIPTION call will hang.'),
helpMessageMode: ['edit', 'create'],
depChange: (state) => {
// Set create_slot to false if same DB, else true
if(obj.isSameDB(state)) {
state.create_slot = false;
} else {
state.create_slot = true;
}
},
},
{
id: 'enabled', label: gettext('Enabled?'),
@ -419,9 +422,18 @@ export default class SubscriptionSchema extends BaseUISchema{
helpMessageMode: ['edit', 'create'],
},
{
id: 'two_phase', label: gettext('Two phase?'),
type: 'switch', mode: ['create', 'properties'],
id: 'two_phase',
label: gettext('Two phase?'),
type: 'switch',
group: gettext('With'),
mode: (() => {
if (obj.version >= 180000) {
return ['create', 'edit', 'properties'];
} else if (obj.version >= 150000 && obj.version < 180000) {
return ['create', 'properties'];
}
return [];
})(),
min_version: 150000,
helpMessage: gettext('Specifies whether two-phase commit is enabled for this subscription.'),
helpMessageMode: ['edit', 'create'],
@ -465,6 +477,14 @@ export default class SubscriptionSchema extends BaseUISchema{
helpMessage: gettext('Specifies whether the subscription will request the publisher to only send changes that do not have an origin or send changes regardless of origin. Setting origin to none means that the subscription will request the publisher to only send changes that do not have an origin. Setting origin to any means that the publisher sends changes regardless of their origin.'),
helpMessageMode: ['edit', 'create'],
},
{
id: 'failover', label: gettext('Failover'),
type: 'switch', mode: ['create', 'edit', 'properties'],
group: gettext('With'),
min_version: 170000,
helpMessage: gettext('Specifies whether the replication slots associated with the subscription are enabled to be synced to the standbys so that logical replication can be resumed from the new primary after failover'),
helpMessageMode: ['edit', 'create'],
},
];
}

View File

@ -0,0 +1,24 @@
{% if data.copy_data is defined or data.create_slot is defined or data.slot_name is defined or data.sync is defined %}
{% set add_semicolon_after_enabled = 'enabled' %}
{% endif %}
{% if data.create_slot is defined or data.slot_name is defined %}
{% set add_semicolon_after_copy_data = 'copy_data' %}
{% endif %}
{% if data.slot_name is defined or data.sync is defined %}
{% set add_semicolon_after_create_slot = 'create_slot' %}
{% endif %}
{% if data.sync is defined %}
{% set add_semicolon_after_slot_name = 'slot_name' %}
{% endif %}
CREATE SUBSCRIPTION {{ conn|qtIdent(data.name) }}
{% if data.host or data.port or data.username or data.password or data.db or data.connect_timeout or data.passfile or data.sslmode or data.sslcompression or data.sslcert or data.sslkey or data.sslrootcert or data.sslcrl%}
CONNECTION '{% if data.host %}host={{data.host}}{% endif %}{% if data.port %} port={{ data.port }}{% endif %}{% if data.username %} user={{ data.username }}{% endif %}{% if data.db %} dbname={{ data.db }}{% endif %}{% if data.connect_timeout %} connect_timeout={{ data.connect_timeout }}{% endif %}{% if data.passfile %} passfile={{ data.passfile }}{% endif %}{% if data.password %} {% if dummy %}password=xxxxxx{% else %}password={{ data.password}}{% endif %}{% endif %}{% if data.sslmode %} sslmode={{ data.sslmode }}{% endif %}{% if data.sslcompression %} sslcompression={{ data.sslcompression }}{% endif %}{% if data.sslcert %} sslcert={{ data.sslcert }}{% endif %}{% if data.sslkey %} sslkey={{ data.sslkey }}{% endif %}{% if data.sslrootcert %} sslrootcert={{ data.sslrootcert }}{% endif %}{% if data.sslcrl %} sslcrl={{ data.sslcrl }}{% endif %}'
{% endif %}
{% if data.pub %}
PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %}
{% endif %}
WITH ({% if data.connect is defined %}connect = {{ data.connect|lower}}, {% endif %}enabled = {{ data.enabled|lower}}, {% if data.copy_data is defined %}copy_data = {{ data.copy_data|lower}}{% if add_semicolon_after_copy_data == 'copy_data' %}, {% endif %}{% endif %}
{% if data.create_slot is defined %}create_slot = {{ data.create_slot|lower }}{% if add_semicolon_after_create_slot == 'create_slot' %}, {% endif %}{% endif %}
{% if data.slot_name is defined and data.slot_name != ''%}slot_name = {{ data.slot_name }}{% if add_semicolon_after_slot_name == 'slot_name' %}, {% endif %}{% endif %}{% if data.sync %}synchronous_commit = '{{ data.sync }}', {% endif %}binary = {{ data.binary|lower}}, streaming = '{{ data.streaming}}', two_phase = {{ data.two_phase|lower}}, disable_on_error = {{ data.disable_on_error|lower}}, run_as_owner = {{ data.run_as_owner|lower}}, password_required = {{ data.password_required|lower}}, origin = '{{ data.origin}}', failover = {{ data.failover|lower}});

View File

@ -0,0 +1,35 @@
SELECT sub.oid as oid,
subname as name,
subpublications as pub,
subpublications as proppub,
sub.subsynccommit as sync,
pga.rolname as subowner,
subslotname as slot_name,
subenabled as enabled,
subbinary as binary,
substream as streaming,
subtwophasestate as two_phase,
subdisableonerr as disable_on_error,
subpasswordrequired as password_required,
subrunasowner as run_as_owner,
suborigin as origin,
subfailover as failover,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,' port',1), '=',2) as host,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'port=',2), ' ',1) as port,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'user=',2), ' ',1) as username,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'dbname=',2), ' ',1) as db,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'connect_timeout=',2), ' ',1) as connect_timeout,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'passfile=',2), ' ',1) as passfile,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslmode=',2), ' ',1) as sslmode,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslcompression=',2), ' ',1) as sslcompression,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslcert=',2), ' ',1) as sslcert,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslkey=',2), ' ',1) as sslkey,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslrootcert=',2), ' ',1) as sslrootcert,
pg_catalog.SPLIT_PART(pg_catalog.SPLIT_PART(subconninfo,'sslcrl=',2), ' ',1) as sslcrl
FROM pg_catalog.pg_subscription sub join pg_catalog.pg_roles pga on sub.subowner= pga.oid
WHERE
{% if subid %}
sub.oid = {{ subid }};
{% else %}
sub.subdbid = {{ did }};
{% endif %}

View File

@ -0,0 +1,80 @@
{% if data.sync is defined or data.failover is defined %}
{% set add_comma_after_slot_name = 'slot_name' %}
{% endif %}
{% if data.binary is defined or data.streaming is defined or data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_sync = 'sync' %}
{% endif %}
{% if data.streaming is defined or data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_binary = 'binary' %}
{% endif %}
{% if data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_streaming = 'streaming' %}
{% endif %}
{% if data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_disable_on_error = 'disable_on_error' %}
{% endif %}
{% if data.password_required is defined or data.origin is defined %}
{% set add_comma_after_run_as_owner = 'run_as_owner' %}
{% endif %}
{% if data.origin is defined %}
{% set add_comma_after_password_required = 'password_required' %}
{% endif %}
{% if data.failover is defined %}
{% set add_comma_after_origin = 'origin' %}
{% endif %}
{#####################################################}
{## Change owner of subscription ##}
{#####################################################}
{% if data.subowner and data.subowner != o_data.subowner %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
OWNER TO {{ data.subowner }};
{% endif %}
{### Disable subscription ###}
{% if data.enabled is defined and data.enabled != o_data.enabled %}
{% if not data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }} DISABLE;
{% endif %}
{% endif %}
{### Alter parameters of subscription ###}
{% if (data.slot_name is defined and data.slot_name != o_data.slot_name) or (data.sync is defined and data.sync != o_data.sync) or (data.binary is defined and data.binary!=o_data.binary) or (data.streaming is defined and data.streaming!=o_data.streaming) or (data.disable_on_error is defined and data.disable_on_error!=o_data.disable_on_error) or (data.run_as_owner is defined and data.run_as_owner!=o_data.run_as_owner) or (data.password_required is defined and data.password_required!=o_data.password_required) or (data.origin is defined and data.origin!=o_data.origin) or (data.failover is defined and data.failover!=o_data.failover) %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET ({% if data.slot_name is defined and data.slot_name != o_data.slot_name %}slot_name = {{ data.slot_name }}{% if add_comma_after_slot_name == 'slot_name' %}, {% endif %}{% endif %}{% if data.sync is defined and data.sync != o_data.sync %}synchronous_commit = '{{ data.sync }}'{% if add_comma_after_sync == 'sync' %}, {% endif %}{% endif %}{% if data.binary is defined and data.binary!=o_data.binary %}binary = {{ data.binary|lower}}{% if add_comma_after_binary == 'binary' %}, {% endif %}{% endif %}{% if data.streaming is defined and data.streaming!=o_data.streaming %}streaming = '{{ data.streaming }}'{% if add_comma_after_streaming == 'streaming' %}, {% endif %}{% endif %}{% if data.disable_on_error is defined and data.disable_on_error!=o_data.disable_on_error %}disable_on_error = {{ data.disable_on_error|lower}}{% if add_comma_after_disable_on_error == 'disable_on_error' %}, {% endif %}{% endif %}{% if data.run_as_owner is defined and data.run_as_owner!=o_data.run_as_owner %}run_as_owner = {{ data.run_as_owner|lower}}{% if add_comma_after_run_as_owner == 'run_as_owner' %}, {% endif %}{% endif %}{% if data.password_required is defined and data.password_required!=o_data.password_required %}password_required = {{ data.password_required|lower}}{% if add_comma_after_password_required == 'password_required' %}, {% endif %}{% endif %}{% if data.origin is defined and data.origin!=o_data.origin %}origin = '{{ data.origin }}'{% if add_comma_after_origin == 'origin' %}, {% endif %}{% endif %}{% if data.failover is defined and data.failover!=o_data.failover %}failover = {{ data.failover|lower}}{% endif %});
{% endif %}
{### Enable subscription ###}
{% if data.enabled is defined and data.enabled != o_data.enabled %}
{% if data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }} ENABLE;
{% endif %}
{% endif %}
{### Refresh publication ###}
{% if data.refresh_pub %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
REFRESH PUBLICATION{% if not data.copy_data_after_refresh %} WITH (copy_data = false){% else %} WITH (copy_data = true){% endif %};
{% endif %}
{### Alter publication of subscription ###}
{% if data.pub%}
{% if data.pub and not data.refresh_pub and not data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %} WITH (refresh = false);
{% else %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %};
{% endif %}
{% endif %}
{### Alter subscription connection info ###}
{% if data.host or data.port or data.username or data.db or data.connect_timeout or data.passfile or data.sslmode or data.sslcompression or data.sslcert or data.sslkey or data.sslrootcert or data.sslcrl %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
CONNECTION 'host={{ o_data.host}} port={{ o_data.port }} user={{ o_data.username }} dbname={{ o_data.db }} connect_timeout={{ o_data.connect_timeout }} {% if data.passfile %} passfile={{ o_data.passfile }}{% endif %} sslmode={{ o_data.sslmode }}{% if data.sslcompression %} sslcompression={{ data.sslcompression }}{% endif %}{% if data.sslcert %} sslcert={{ data.sslcert }}{% endif %}{% if data.sslkey %} sslkey={{ data.sslkey }}{% endif %}{% if data.sslrootcert %} sslrootcert={{ data.sslrootcert }}{% endif %}{% if data.sslcrl %} sslcrl={{ data.sslcrl }}{% endif %}';
{% endif %}
{### Alter subscription name ###}
{% if data.name and data.name != o_data.name %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}

View File

@ -0,0 +1,83 @@
{% if data.sync is defined or data.failover is defined %}
{% set add_comma_after_slot_name = 'slot_name' %}
{% endif %}
{% if data.binary is defined or data.streaming is defined or data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_sync = 'sync' %}
{% endif %}
{% if data.streaming is defined or data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_binary = 'binary' %}
{% endif %}
{% if data.disable_on_error is defined or data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_streaming = 'streaming' %}
{% endif %}
{% if data.run_as_owner is defined or data.password_required is defined or data.origin is defined %}
{% set add_comma_after_disable_on_error = 'disable_on_error' %}
{% endif %}
{% if data.password_required is defined or data.origin is defined %}
{% set add_comma_after_run_as_owner = 'run_as_owner' %}
{% endif %}
{% if data.origin is defined %}
{% set add_comma_after_password_required = 'password_required' %}
{% endif %}
{% if data.failover is defined %}
{% set add_comma_after_origin = 'origin' %}
{% endif %}
{% if data.two_phase is defined %}
{% set add_comma_after_failover = 'failover' %}
{% endif %}
{#####################################################}
{## Change owner of subscription ##}
{#####################################################}
{% if data.subowner and data.subowner != o_data.subowner %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
OWNER TO {{ data.subowner }};
{% endif %}
{### Disable subscription ###}
{% if data.enabled is defined and data.enabled != o_data.enabled %}
{% if not data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }} DISABLE;
{% endif %}
{% endif %}
{### Alter parameters of subscription ###}
{% if (data.slot_name is defined and data.slot_name != o_data.slot_name) or (data.sync is defined and data.sync != o_data.sync) or (data.binary is defined and data.binary!=o_data.binary) or (data.streaming is defined and data.streaming!=o_data.streaming) or (data.disable_on_error is defined and data.disable_on_error!=o_data.disable_on_error) or (data.run_as_owner is defined and data.run_as_owner!=o_data.run_as_owner) or (data.password_required is defined and data.password_required!=o_data.password_required) or (data.origin is defined and data.origin!=o_data.origin) or (data.failover is defined and data.failover!=o_data.failover) or (data.two_phase is defined and data.two_phase!=o_data.two_phase) %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET ({% if data.slot_name is defined and data.slot_name != o_data.slot_name %}slot_name = {{ data.slot_name }}{% if add_comma_after_slot_name == 'slot_name' %}, {% endif %}{% endif %}{% if data.sync is defined and data.sync != o_data.sync %}synchronous_commit = '{{ data.sync }}'{% if add_comma_after_sync == 'sync' %}, {% endif %}{% endif %}{% if data.binary is defined and data.binary!=o_data.binary %}binary = {{ data.binary|lower}}{% if add_comma_after_binary == 'binary' %}, {% endif %}{% endif %}{% if data.streaming is defined and data.streaming!=o_data.streaming %}streaming = '{{ data.streaming }}'{% if add_comma_after_streaming == 'streaming' %}, {% endif %}{% endif %}{% if data.disable_on_error is defined and data.disable_on_error!=o_data.disable_on_error %}disable_on_error = {{ data.disable_on_error|lower}}{% if add_comma_after_disable_on_error == 'disable_on_error' %}, {% endif %}{% endif %}{% if data.run_as_owner is defined and data.run_as_owner!=o_data.run_as_owner %}run_as_owner = {{ data.run_as_owner|lower}}{% if add_comma_after_run_as_owner == 'run_as_owner' %}, {% endif %}{% endif %}{% if data.password_required is defined and data.password_required!=o_data.password_required %}password_required = {{ data.password_required|lower}}{% if add_comma_after_password_required == 'password_required' %}, {% endif %}{% endif %}{% if data.origin is defined and data.origin!=o_data.origin %}origin = '{{ data.origin }}'{% if add_comma_after_origin == 'origin' %}, {% endif %}{% endif %}{% if data.failover is defined and data.failover!=o_data.failover %}failover = {{ data.failover|lower}}{% if add_comma_after_failover == 'failover' %}, {% endif %}{% endif %}{% if data.two_phase is defined and data.two_phase!=o_data.two_phase %}two_phase = {{ data.two_phase|lower}}{% endif %});
{% endif %}
{### Enable subscription ###}
{% if data.enabled is defined and data.enabled != o_data.enabled %}
{% if data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }} ENABLE;
{% endif %}
{% endif %}
{### Refresh publication ###}
{% if data.refresh_pub %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
REFRESH PUBLICATION{% if not data.copy_data_after_refresh %} WITH (copy_data = false){% else %} WITH (copy_data = true){% endif %};
{% endif %}
{### Alter publication of subscription ###}
{% if data.pub%}
{% if data.pub and not data.refresh_pub and not data.enabled %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %} WITH (refresh = false);
{% else %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
SET PUBLICATION {% for pub in data.pub %}{% if loop.index != 1 %},{% endif %}{{ conn|qtIdent(pub) }}{% endfor %};
{% endif %}
{% endif %}
{### Alter subscription connection info ###}
{% if data.host or data.port or data.username or data.db or data.connect_timeout or data.passfile or data.sslmode or data.sslcompression or data.sslcert or data.sslkey or data.sslrootcert or data.sslcrl %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
CONNECTION 'host={{ o_data.host}} port={{ o_data.port }} user={{ o_data.username }} dbname={{ o_data.db }} connect_timeout={{ o_data.connect_timeout }} {% if data.passfile %} passfile={{ o_data.passfile }}{% endif %} sslmode={{ o_data.sslmode }}{% if data.sslcompression %} sslcompression={{ data.sslcompression }}{% endif %}{% if data.sslcert %} sslcert={{ data.sslcert }}{% endif %}{% if data.sslkey %} sslkey={{ data.sslkey }}{% endif %}{% if data.sslrootcert %} sslrootcert={{ data.sslrootcert }}{% endif %}{% if data.sslcrl %} sslcrl={{ data.sslcrl }}{% endif %}';
{% endif %}
{### Alter subscription name ###}
{% if data.name and data.name != o_data.name %}
ALTER SUBSCRIPTION {{ conn|qtIdent(o_data.name) }}
RENAME TO {{ conn|qtIdent(data.name) }};
{% endif %}

View File

@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5917 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = true);

View File

@ -0,0 +1,2 @@
ALTER SUBSCRIPTION test_create_subscription
SET (failover = true);

View File

@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5917 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = true);

View File

@ -0,0 +1,2 @@
ALTER SUBSCRIPTION test_create_subscription
SET (slot_name = None);

View File

@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5917 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);

View File

@ -0,0 +1,4 @@
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5917 user=postgres dbname=postgres connect_timeout=10 password=xxxxxx sslmode=prefer'
PUBLICATION test_publication
WITH (connect = true, enabled = false, copy_data = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);

View File

@ -0,0 +1,124 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Table For Subscription",
"endpoint": "NODE-table.obj",
"sql_endpoint": "NODE-table.sql_id",
"data": {
"name": "test_table",
"columns": [
{
"name": "emp_id",
"cltype": "integer",
"is_primary_key": true
},
{
"name": "name",
"cltype": "text"
},
{
"name": "salary",
"cltype": "bigint"
}
],
"is_partitioned": false,
"schema": "public",
"spcname": "pg_default"
},
"store_object_id": true
},
{
"type": "create",
"name": "Create Publication for Subscription",
"endpoint": "NODE-publication.obj",
"sql_endpoint": "NODE-publication.sql_id",
"msql_endpoint": "NODE-publication.msql",
"precondition_sql": "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'test_create_subscription' UNION ALL SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'test_create_subscription') AND pg_create_logical_replication_slot('test_create_subscription', 'pgoutput', false) IS NOT NULL LIMIT 1;",
"data": {
"name": "test_publication",
"evnt_insert": true,
"evnt_update": true,
"evnt_delete": true,
"evnt_truncate": true,
"pubowner": "postgres",
"publish_via_partition_root": false,
"all_table": false,
"only_table": false,
"pubtable": [{
"table_name": "public.test_table"
}],
"pubschema": ""
}
},
{
"type": "create",
"name": "Create Subscription",
"endpoint": "NODE-subscription.obj",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql",
"data": {
"name": "test_create_subscription",
"subowner": "postgres",
"host": "localhost",
"port": 5917,
"db": "postgres",
"username": "postgres",
"password": "postgres",
"connect_timeout": 10,
"pub": ["test_publication"],
"sslmode": "prefer",
"copy_data": false,
"create_slot": false,
"enabled": false,
"connect": true,
"slot_name": "test_create_subscription",
"sync": "remote_apply",
"streaming": true,
"binary": true,
"two_phase": true,
"disable_on_error": true,
"run_as_owner": true,
"password_required": true,
"origin": "any",
"service": "",
"failover": false
},
"expected_sql_file": "create_subscription.sql",
"expected_msql_file": "create_subscription_msql.sql"
},
{
"type": "alter",
"name": "Alter failover parameter of subscription",
"endpoint": "NODE-subscription.obj_id",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql_id",
"data": {
"failover": true
},
"expected_sql_file": "alter_failover_parameter.sql",
"expected_msql_file": "alter_failover_parameter_msql.sql"
},
{
"type": "alter",
"name": "Alter slot_name parameter of subscription",
"endpoint": "NODE-subscription.obj_id",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql_id",
"data": {
"slot_name": "None"
},
"expected_sql_file": "alter_slot_name_parameter.sql",
"expected_msql_file": "alter_slot_name_parameter_msql.sql"
},
{
"type": "delete",
"name": "Drop subscription",
"endpoint": "NODE-subscription.delete_id",
"data": {
"name": "test_create_subscription"
},
"post_scenario_sql": "SELECT 1 WHERE EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'test_create_subscription') AND (SELECT pg_drop_replication_slot('test_create_subscription')) IS NOT NULL UNION ALL SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'test_create_subscription') LIMIT 1;"
}
]
}

View File

@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5434 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = None, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);

View File

@ -0,0 +1,2 @@
ALTER SUBSCRIPTION test_create_subscription
SET (slot_name = None);

View File

@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5434 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = true, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);

View File

@ -0,0 +1,2 @@
ALTER SUBSCRIPTION test_create_subscription
SET (two_phase = true);

View File

@ -0,0 +1,8 @@
-- Subscription: test_create_subscription
-- DROP SUBSCRIPTION IF EXISTS test_create_subscription;
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5434 user=postgres dbname=postgres connect_timeout=10 sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = false, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);

View File

@ -0,0 +1,4 @@
CREATE SUBSCRIPTION test_create_subscription
CONNECTION 'host=localhost port=5434 user=postgres dbname=postgres connect_timeout=10 password=xxxxxx sslmode=prefer'
PUBLICATION test_publication
WITH (connect = false, enabled = false, copy_data = false, create_slot = false, slot_name = test_create_subscription, synchronous_commit = 'remote_apply', binary = true, streaming = 'True', two_phase = false, disable_on_error = true, run_as_owner = true, password_required = true, origin = 'any', failover = false);

View File

@ -0,0 +1,74 @@
{
"scenarios": [
{
"type": "create",
"name": "Create Subscription",
"endpoint": "NODE-subscription.obj",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql",
"precondition_sql": "SELECT 1 FROM pg_replication_slots WHERE slot_name = 'test_create_subscription' UNION ALL SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'test_create_subscription') AND pg_create_logical_replication_slot('test_create_subscription', 'pgoutput', false) IS NOT NULL LIMIT 1;",
"data": {
"name": "test_create_subscription",
"subowner": "postgres",
"host": "localhost",
"port": 5434,
"db": "postgres",
"username": "postgres",
"password": "edb",
"connect_timeout": 10,
"pub": ["test_publication"],
"sslmode": "prefer",
"copy_data": false,
"create_slot": false,
"enabled": false,
"connect": false,
"slot_name": "test_create_subscription",
"sync": "remote_apply",
"streaming": true,
"binary": true,
"two_phase": false,
"disable_on_error": true,
"run_as_owner": true,
"password_required": true,
"origin": "any",
"service": "",
"failover": false
},
"expected_sql_file": "create_subscription.sql",
"expected_msql_file": "create_subscription_msql.sql"
},
{
"type": "alter",
"name": "Alter two_phase parameter of subscription",
"endpoint": "NODE-subscription.obj_id",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql_id",
"data": {
"two_phase": true
},
"expected_sql_file": "alter_two_phase_parameter.sql",
"expected_msql_file": "alter_two_phase_parameter_msql.sql"
},
{
"type": "alter",
"name": "Alter slot_name parameter of subscription",
"endpoint": "NODE-subscription.obj_id",
"sql_endpoint": "NODE-subscription.sql_id",
"msql_endpoint": "NODE-subscription.msql_id",
"data": {
"slot_name": "None"
},
"expected_sql_file": "alter_slot_name_parameter.sql",
"expected_msql_file": "alter_slot_name_parameter_msql.sql"
},
{
"type": "delete",
"name": "Drop subscription",
"endpoint": "NODE-subscription.delete_id",
"data": {
"name": "test_create_subscription"
},
"post_scenario_sql": "SELECT 1 WHERE EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'test_create_subscription') AND (SELECT pg_drop_replication_slot('test_create_subscription')) IS NOT NULL UNION ALL SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM pg_replication_slots WHERE slot_name = 'test_create_subscription') LIMIT 1;"
}
]
}

View File

@ -25,6 +25,7 @@
"streaming":false,
"password_required":true,
"run_as_owner":false,
"failover": false,
"origin":"any",
"pub": ["sample__1"]
},

View File

@ -428,7 +428,7 @@ export class ResultSetUtils {
async (formData) => {
try {
await connectServer(
this.api,
this.api,
this.queryToolCtx.modal,
this.queryToolCtx.params.sid,
this.queryToolCtx.params.user,
@ -669,8 +669,9 @@ export class ResultSetUtils {
return columnVal;
}
processRows(result, columns, fromClipboard=false, pasteSerials=false) {
processRows(result, columns, options={}) {
let retVal = [];
let {fromClipboard=false, pasteSerials=false, isNewRow=false} = options;
if(!_.isArray(result) || !_.size(result)) {
return retVal;
}
@ -685,8 +686,9 @@ export class ResultSetUtils {
// Convert 2darray to dict.
let rowObj = {};
for(const col of columns) {
// if column data is undefined and there is not default value then set it to null.
let columnVal = rec[col.pos] ?? (col.has_default_val ? undefined : null);
// if column data is not present for existing rows then use null
// for new rows, it should be undefined if there is default value.
let columnVal = rec[col.pos] ?? ((col.has_default_val && isNewRow) ? undefined : null);
/* If the source is clipboard, then it needs some extra handling */
if(fromClipboard) {
columnVal = this.processClipboardVal(columnVal, col, copiedRowsObjects[recIdx]?.[col.key], pasteSerials);
@ -1379,7 +1381,7 @@ export function ResultSet() {
}, [selectedRows, queryData, dataChangeStore, rows, allRowsSelect]);
useEffect(()=>{
const triggerAddRows = (_rows, fromClipboard, pasteSerials)=>{
const triggerAddRows = (_rows, options)=>{
let insPosn = 0;
if(selectedRows.size > 0) {
let selectedRowsSorted = Array.from(selectedRows);
@ -1396,7 +1398,7 @@ export function ResultSet() {
return x;
});
}
let newRows = rsu.current.processRows(_rows, columns, fromClipboard, pasteSerials);
let newRows = rsu.current.processRows(_rows, columns, options);
setRows((prev)=>[
...prev.slice(0, insPosn),
...newRows,

View File

@ -281,13 +281,13 @@ export function ResultSetToolbar({query, canEdit, totalRowCount, pagination, all
field_separator: queryToolPref.results_grid_field_separator,
});
let copiedRows = copyUtils.getCopiedRows();
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, copiedRows, true, checkedMenuItems['paste_with_serials']);
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, copiedRows, {fromClipboard: true, pasteSerials: checkedMenuItems['paste_with_serials']});
}, [queryToolPref, checkedMenuItems['paste_with_serials']]);
const copyData = ()=>{
eventBus.fireEvent(QUERY_TOOL_EVENTS.COPY_DATA, checkedMenuItems['copy_with_headers']);
};
const addRow = useCallback(()=>{
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, [[]]);
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, [[]], {isNewRow: true});
}, []);
const downloadResult = useCallback(()=>{
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SAVE_RESULTS);

View File

@ -76,7 +76,8 @@ def get_version_mapping_directories():
:param server_type:
:return:
"""
return ({'name': "17_plus", 'number': 170000},
return ({'name': "18_plus", 'number': 180000},
{'name': "17_plus", 'number': 170000},
{'name': "16_plus", 'number': 160000},
{'name': "15_plus", 'number': 150000},
{'name': "14_plus", 'number': 140000},

View File

@ -70,6 +70,7 @@ describe('FunctionSchema', ()=>{
getLanguage: [],
getTypes: [],
getSupportFunctions: [],
extensionsList: [],
},
{
node_info: {
@ -109,10 +110,6 @@ describe('FunctionSchema', ()=>{
let schemaObj = createSchemaObj();
let getInitData = ()=>Promise.resolve({});
beforeEach(()=>{
genericBeforeEach();
});
@ -133,6 +130,13 @@ describe('FunctionSchema', ()=>{
await getPropertiesView(createSchemaObj(), getInitData);
});
it('dependsonextensions field exists', ()=>{
let field = _.find(schemaObj.fields, (f)=>f.id=='dependsonextensions');
expect(field).toBeTruthy();
expect(field.type).toBe('select');
expect(field.controlProps.multiple).toBe(true);
});
it('proiswindow visible', async ()=>{
@ -449,7 +453,7 @@ describe('FunctionSchema', ()=>{
schemaObj.validate(state, setError);
expect(setError).toHaveBeenCalledWith('prosrc_c', null);
});
it('validate', ()=>{
let state = {prorettypename: 'char'};
let setError = jest.fn();

View File

@ -153,7 +153,6 @@ class ReverseEngineeredSQLTestCases(BaseTestGenerator):
# Check the final status of the test case
self.assertEqual(self.final_test_status, True)
def tearDown(self):
database_utils.disconnect_database(
self, self.server_information['server_id'],
self.server_information['db_id'])
@ -601,7 +600,10 @@ class ReverseEngineeredSQLTestCases(BaseTestGenerator):
try:
pg_cursor.execute(precondition_sql)
precondition_result = pg_cursor.fetchone()
if len(precondition_result) >= 1 and precondition_result[0] == '1':
if ((len(precondition_result) >= 1 and
precondition_result[0] == '1') or
(isinstance(precondition_result, tuple) and
precondition_result[0] == 1)):
precondition_flag = True
except Exception as e:
traceback.print_exc()

File diff suppressed because it is too large Load Diff