Compare commits

...
92 Commits
Author SHA1 Message Date
Aditya Toshniwal 3416184236 Fix more UI issues related to System statistics dashboard 2023-10-18 13:31:43 +05:30
Aditya Toshniwal 6e3ee1c607 Fix an issue where servers are not listed in add new connection dialog. 2023-10-18 11:43:42 +05:30
Aditya Toshniwal b512404c85 Update release notes 2023-10-17 15:19:40 +05:30
Pravesh Sharma 5bb6e9133f Fixed query history slowness issue by storing query only for those having certain threshold max length. #6666 2023-10-17 15:17:53 +05:30
Aditya Toshniwal 344c236d72 Fix following issues in system stats:
1. Graphs rendering in opposite directions on tab change.
2. Y-axis label width should be dynamic.
3. Tooltip values should be formatted.
2023-10-17 15:01:52 +05:30
Khushboo Vashi b4b2a4ff67 Fix an issue where user login is not working if username/email contains single quote in server mode. #6865 2023-10-17 13:49:18 +05:30
Akshay Joshi 205a661922 Update version for v7.8 release 2023-10-16 15:11:27 +05:30
Pravesh Sharma eead9b77e7 Fixed issue where MSQL for delete column was not generating. #5749 2023-10-16 15:04:31 +05:30
Aditya Toshniwal 47e734dbf2 Fix an issue in graphical explain plan where query tool crashes when the plan has parallel workers details and sort node is clicked for details. #6858 2023-10-16 11:40:43 +05:30
Libor M ce372a73bf Czech translation for version 7.8 2023-10-16 11:25:29 +05:30
Akshay Joshi 10c5d25735 Update message catalogs 2023-10-12 17:59:09 +05:30
Akshay Joshi 81dcc91747 Added 'hostaddr' and used host string as it is while creating SSHTunnel. 2023-10-12 17:57:40 +05:30
Aditya Toshniwal 3a62fc5a2a Fix issues found during testing. #6229, #6836 2023-10-12 16:58:46 +05:30
Aditya Toshniwal 10adb6a11b Add support for setting prepare threshold in server connection. #6285 2023-10-12 16:54:06 +05:30
Khushboo Vashi 82fade7645 Fix System Stat dashboard UI issues. 2023-10-12 14:04:58 +05:30
Akshay Joshi 37dced23f0 Remove an extra space in --disable-components 2023-10-11 19:14:01 +05:30
Akshay Joshi 6f857bdbcb Replace the logic to download/install PostgreSQL on Windows in the GitHub actions. Use stack builder instead of Chocolatey. 2023-10-11 18:24:12 +05:30
Aditya Toshniwal 6ba9c192ce Fix failing JS test cases after scale related commit. #6851 2023-10-11 16:31:02 +05:30
Aditya Toshniwal 221af6caa6 Fix an issue where scale in columns is not allowed to have value as 0 or below. #6851 2023-10-11 15:06:57 +05:30
Aditya Toshniwal e22492ad51 Fixed an issue where non-super PostgreSQL users are not able to terminate their own connections from dashboard. #6836 2023-10-11 13:43:31 +05:30
Akshay Joshi 2b8efc6361 Update release note 2023-10-11 13:34:02 +05:30
Pravesh Sharma 73430a2062 Fixed an issue where user was not able to assign new/old columns as primary key once column with primary key is deleted. #5749 2023-10-11 13:29:19 +05:30
Aditya Toshniwal fc411bfc49 Allow setting custom username for shared servers, with default as username of server being shared. #6229 2023-10-11 12:56:06 +05:30
Yogesh Mahajan 4450145d31 Fixed issues reported while testing foreign table properties. #640 2023-10-11 11:53:14 +05:30
Sahil Harpal cd84200135 Fix system stat dashboard UI issues. 2023-10-11 11:27:21 +05:30
Nikhil Mohite 6997d0149e Fetch database objects after opening the backup dialog. #6799 2023-10-10 15:28:25 +05:30
Aditya Toshniwal 5981f4bd13 In GUI tests, do not try to set the binary path in preferences if it is blank 2023-10-09 12:42:52 +05:30
Anil Sahoo 5113fda7a6 Fix an issue where global/native keyboard shortcuts are not working when any cell of data output grid has focus. #6579 (#6834) 2023-10-06 13:28:16 +05:30
Yogesh Mahajan 7ce07f7dcf Fixed issues reported while testing foreign table node. #640 2023-10-06 12:28:40 +05:30
Aditya Toshniwal 078a959e3d Remove the Pillow dependency completely. 2023-10-05 12:40:29 +05:30
Dave Page b4f517f7ff Rename tasks in workflows to allow rule enforcement in Github. 2023-10-05 11:57:24 +05:30
dependabot[bot] 36dc2e9d2a JS dependency: Bump eslint from 8.47.0 to 8.50.0 in /runtime (#6789)
Bumps [eslint](https://github.com/eslint/eslint) from 8.47.0 to 8.50.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v8.47.0...v8.50.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-05 11:33:41 +05:30
Aditya Toshniwal 858c85adbe Upgrade Pillow to latest based on Python v3.7 compatibility. 2023-10-05 11:30:50 +05:30
dependabot[bot] 2cd86449d0 JS dependency: Bump webpack from 5.88.0 to 5.88.2 in /web (#6641)
Bumps [webpack](https://github.com/webpack/webpack) from 5.88.0 to 5.88.2.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.88.0...v5.88.2)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-04 18:37:38 +05:30
dependabot[bot] c37fb061bd JS dependency: Bump eslint-plugin-react in /web (#6688)
Bumps [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) from 7.32.2 to 7.33.2.
- [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases)
- [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.32.2...v7.33.2)

---
updated-dependencies:
- dependency-name: eslint-plugin-react
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-04 18:33:52 +05:30
dependabot[bot] dc9cc3730b Python dependency: Bump axios from 1.5.0 to 1.5.1 in /runtime (#6796)
Bumps [axios](https://github.com/axios/axios) from 1.5.0 to 1.5.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.5.0...v1.5.1)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-04 18:25:10 +05:30
dependabot[bot] 872b60de5c Python dependency: Bump postcss from 8.4.24 to 8.4.31 in /web (#6819)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.24 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.24...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-04 18:24:46 +05:30
Aditya Toshniwal 5d2ce142dd Fixed an issue in ERD tool where the downloaded images have a few links cut. #4995 2023-10-04 18:23:41 +05:30
Akshay Joshi 9666bd6c8d 1) Added 'load_balance_hosts' connection string parameter for PG 16 and above. #6802
2) Added EPAS 11 support back as its EOL is in November.
2023-10-04 17:55:37 +05:30
Khushboo Vashi 0b3c79865e Fix the issue where backend APIs are called even if System Stat extension is not installed for the database. 2023-10-04 15:04:33 +05:30
Nikhil Mohite 9c903f944c Fixed an issue where the user is unable to select objects on the backup dialog due to tree flickring #6799. 2023-10-04 12:13:21 +05:30
Nikhil Mohite 541d9dfcb4 Fix API test cases for backup dialog. 2023-09-29 14:49:11 +05:30
Aditya Toshniwal b9d3006915 Fix failing RE-SQL test cases for foreign tables on EPAS servers. 2023-09-29 13:38:22 +05:30
Khushboo Vashi 68b7ddb592 Fix UI issues for System Statistics panel. 2023-09-28 15:23:15 +05:30
Akshay Joshi 4309aa3ba7 Ensure that the backup works properly for PG 16 on the latest docker image. #6790 2023-09-27 17:09:49 +05:30
Yogesh Mahajan f9bbadfc6a Pin the cryptography version to fix PyO3 modules initialisation error. #6718 2023-09-27 16:45:48 +05:30
Khushboo Vashi c6f8918983 Update release notes. 2023-09-27 16:06:54 +05:30
Sahil Harpal 16c95d21a7 GUI representation of the system's activity using the system_stats extension. #6797 2023-09-27 16:04:48 +05:30
Akshay Joshi bae912fa40 Added PG version 16 and removed PG version 11 from github actions. 2023-09-27 13:26:21 +05:30
Akshay Joshi ca3ec76db6 Added version PG 16 and removed PG 11 2023-09-27 12:49:16 +05:30
Akshay Joshi 4050545f7c Fixed an issue where the wrong message 'Current database has been moved or renamed' is displayed when debugging any function. #6482 2023-09-26 18:07:25 +05:30
Khushboo Vashi 975bcd9fb0 Update release notes. 2023-09-26 15:14:55 +05:30
Yogesh Mahajan 3cd547f879 Implement column node and its features for foreign table. #640, #6373, #6674 2023-09-26 15:03:03 +05:30
Akshay Joshi 5736e173ea 1) Fixed an issue where PG 16 binaries not getting copied into the docker container.
2) Remove support for PostgreSQL 11.
2023-09-26 13:45:08 +05:30
Dave Page 5a26cad588 Use PG16 as the default in the container. 2023-09-21 15:35:44 +05:30
Aditya Toshniwal 3b616b2fb1 Handle a missing case in previous commit where a PG binary may not be present on some systems. #6763 2023-09-20 12:47:06 +05:30
Aditya Toshniwal 35f05e49b3 Fix the security issue of validate bin path to consider and fix more scenarios. #6763 2023-09-20 11:17:07 +05:30
Akshay Joshi 61fa8b16c9 Update version for release 7.7 2023-09-18 18:57:29 +05:30
Pravesh Sharma 3258851425 Access method for partitions not visible when opening table properties. #6378 2023-09-18 18:49:11 +05:30
Nikhil Mohite 0f5450c6d2 Fixed the following issues found in testing of #642
1. When all nodes are collapsed & you select the schema node, table node remains partially selected.
2. When paren node is expanded with above operation, Child selection is not visible
3. Prevent scrolling to top on selection of node
4. The checkbox state is NOT remembered on switching of the tab.
2023-09-18 16:23:20 +05:30
Aditya Toshniwal 07f18a1b05 Fix an issue where user get logged out if password in changed. #6274 2023-09-18 14:06:38 +05:30
Aditya Toshniwal 02ecc82e73 Fix a security related issue where an authenticated user can run remote command using validate binary path API (CVE-2023-5002). #6763 (#6764) 2023-09-18 14:01:11 +05:30
rlbxku1r 0d111e0fb6 Update Japanese translation for v7.7 2023-09-18 13:45:13 +05:30
Domenico Sgarbossa 7af597eca2 Updated Italian translations for v7.7 2023-09-18 12:13:49 +05:30
Akshay Joshi 64c71becc6 Update message catalogs for v7.7 2023-09-14 18:06:56 +05:30
Akshay Joshi ffec77d50b Added compression method option while creating a column. #6379 2023-09-14 11:56:00 +05:30
Pravesh Sharma d69b4282c8 Fixed issues reported while testing feature #6378 2023-09-13 16:19:07 +05:30
Nikhil Mohite aa973fc8ae Added support to select/deselect objects in the Backup dialog. #642 2023-09-13 11:07:28 +05:30
Pravesh Sharma 8c91d40932 Fixed RESQL test case failure for EPAS 15. #6400 2023-09-13 10:50:29 +05:30
Aditya Toshniwal 2f5fb6a996 Fix an issue where user is not able to change the password when SMTP is not configured. #6274.
Fix an issue where changing the password shows success but the new password is not working. #6730.
2023-09-12 15:16:09 +05:30
Pravesh Sharma cfc338c5f0 Fix an issue where login form doesn't appear if internal auth source is removed. #6378 2023-09-12 11:40:34 +05:30
Anil Sahoo 32403920fe Enhancement of properties tab for column's collection by adding Primary Key and Not NULL columns. #2411 2023-09-07 11:54:02 +05:30
Akshay Joshi e9384615dc Fixed linter error. 2023-09-06 11:43:55 +05:30
Akshay Joshi 07d965ea46 Ensure that Materialized view size fields in Statistics should be human-readable. #6712 2023-09-06 11:31:04 +05:30
Pravesh Sharma a43b70e360 1. Added USING method while creating the table. #6378
2. Added USING method while creating the materialized view. #6400
2023-09-06 11:22:30 +05:30
Khushboo Vashi 095e11247d Update release notes. 2023-09-05 18:18:36 +05:30
Akshay Joshi 88162c683e Unpin the 'python-socketio' as the issue is fixed in the latest version of Flask-SocketIO v5.3.6 2023-09-05 16:29:29 +05:30
Akshay Joshi cf3c696f7e Pin python-socketio to 5.8.0 as the latest version throwing error for the test client. 2023-09-05 11:54:43 +05:30
Everton Seiei Arakaki 02eaf787e9 Add support for additional ID token claim checks for OAuth 2 authentication. #6736 2023-09-05 11:28:18 +05:30
Dave Page e5c249e81c Remove version from SSL libs 2023-09-04 15:35:56 +01:00
Akshay Joshi c0b868b53b Added Strategy, Locale Provider, ICU Locale, ICU Rules, and OID options while creating a database. #6383 2023-09-04 16:03:06 +05:30
dependabot[bot] 39a14920e3 Python dependency: Bump axios from 1.4.0 to 1.5.0 in /runtime (#6725)
Bumps [axios](https://github.com/axios/axios) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-04 15:05:16 +05:30
sprayzcs 5cf9dea9a8 Change grep regex in the docker's entrypoint to find env variables starting with PGADMIN_CONFIG_ only. 2023-09-04 13:06:15 +05:30
Anil Sahoo 0b7eafaca0 Added the 'data type' column in the properties tab of the Columns collection node. #2411 2023-08-31 12:33:08 +05:30
Khushboo Vashi ed8aeed818 Remove backslash from the directory path to fix the SBOM generation issue on Windows. 2023-08-31 12:30:40 +05:30
Khushboo Vashi 0b91244f91 Another attempt to fix SBOM Windows issue. 2023-08-30 11:21:08 +05:30
Khushboo Vashi 46c67a2f4e Fix SBOM generation issue on Windows. 2023-08-28 15:42:39 +05:30
Akshay Joshi c87e925991 Update release note 2023-08-25 12:51:47 +05:30
Anil Sahoo 5fb80af90e Added all the new options of the 'WITH' clause in the subscription dialog. #4805 2023-08-25 12:48:31 +05:30
Akshay Joshi 77467209c2 1) Added release note for v7.7
2) Added exception handling while removing the test database file.
2023-08-25 11:14:54 +05:30
Yogesh Mahajan cd613ded0a Ensure user is redirected to login page after failed login. #6704 2023-08-25 10:38:50 +05:30
Aditya Toshniwal e8283173ba Remove imagemin-pngquant as we already have a PNG optimizer imagemin-optipng 2023-08-23 12:23:38 +05:30
409 changed files with 38273 additions and 16883 deletions

No files matched your search

+1 -1
View File
@@ -13,7 +13,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
build-container:
runs-on: ubuntu-latest
steps:
+1 -1
View File
@@ -13,7 +13,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
build-docs:
runs-on: ubuntu-latest
steps:
+1 -1
View File
@@ -13,7 +13,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
check-javascript-style:
runs-on: ubuntu-latest
steps:
+1 -1
View File
@@ -13,7 +13,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
build-python-package:
runs-on: ubuntu-latest
steps:
+1 -1
View File
@@ -13,7 +13,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
check-python-style:
runs-on: ubuntu-latest
steps:
+1 -1
View File
@@ -13,7 +13,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
build-tarball:
runs-on: ubuntu-latest
steps:
+1 -1
View File
@@ -13,7 +13,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
check-translations:
runs-on: ubuntu-latest
steps:
+1 -1
View File
@@ -13,7 +13,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
run-feature-tests-epas:
# Only run if the tests are enabled
# TODO: Figure out a way to test for the presence of the secrets instead
if: vars.ENABLE_EPAS_TESTS == 'true'
+2 -2
View File
@@ -16,11 +16,11 @@ concurrency:
cancel-in-progress: true
jobs:
build:
run-feature-tests-pg:
strategy:
fail-fast: false
matrix:
pgver: [11, 12, 13, 14, 15]
pgver: [12, 13, 14, 15, 16]
runs-on: ubuntu-latest
+2 -2
View File
@@ -13,7 +13,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
run-javascript-tests:
strategy:
fail-fast: false
matrix:
@@ -23,7 +23,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Upgrade yarn
run: |
yarn set version berry
+1 -1
View File
@@ -23,7 +23,7 @@ concurrency:
cancel-in-progress: true
jobs:
build:
run-python-tests-epas:
# Only run if the tests are enabled
# TODO: Figure out a way to test for the presence of the secrets instead
if: vars.ENABLE_EPAS_TESTS == 'true'
+5 -3
View File
@@ -16,12 +16,12 @@ concurrency:
cancel-in-progress: true
jobs:
build:
run-python-tests-pg:
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
pgver: [11, 12, 13, 14, 15]
pgver: [12, 13, 14, 15, 16]
runs-on: ${{ matrix.os }}
@@ -57,7 +57,9 @@ jobs:
- name: Install platform dependencies on Windows
if: ${{ matrix.os == 'windows-latest' }}
run: |
choco install -y postgresql${{ matrix.pgver }} --params "/Password:postgres /Port:59${{ matrix.pgver }}" --ia "--prefix C:\PostgreSQL\${{ matrix.pgver }} --datadir C:\PostgreSQL\${{ matrix.pgver }}\data --install_runtimes 0 --disable-components pgAdmin,stackbuilder --enable-components server,commandlinetools"
FOR /f "delims=" %%F IN ('python tools\get_sb_package.py "https://www.postgresql.org/applications-v2.xml" "postgresql_${{ matrix.pgver }}" "windows-x64"') DO SET INSTALLER_EXE=%%F
ECHO Running %INSTALLER_EXE%...
%INSTALLER_EXE% --prefix C:\PostgreSQL\${{ matrix.pgver }} --datadir C:\PostgreSQL\${{ matrix.pgver }}\data --serverport 59${{ matrix.pgver }} --superpassword postgres --install_runtimes 0 --mode unattended --unattendedmodeui none --disable-components pgAdmin,stackbuilder --enable-components server,commandlinetools"
choco install -y mitkerberos
REM Ignore error 3010 (reboot required)
+1 -1
View File
@@ -18,7 +18,7 @@ concurrency:
cancel-in-progress: false
jobs:
build:
run-sonarqube-scan:
# Only run if the project key is set
if: vars.SONARQUBE_PROJECT_KEY != null
+14 -9
View File
@@ -114,20 +114,15 @@ RUN rm -rf /pgadmin4/docs/en_US/_build/html/_static/*.png
# Create additional builders to get all of the PostgreSQL utilities
#########################################################################
FROM postgres:11-alpine AS pg11-builder
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 alpine:latest AS tool-builder
# Copy the PG binaries
COPY --from=pg11-builder /usr/local/bin/pg_dump /usr/local/pgsql/pgsql-11/
COPY --from=pg11-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-11/
COPY --from=pg11-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-11/
COPY --from=pg11-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-11/
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/
@@ -148,6 +143,11 @@ COPY --from=pg15-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-15/
COPY --from=pg15-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-15/
COPY --from=pg15-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-15/
COPY --from=pg16-builder /usr/local/bin/pg_dump /usr/local/pgsql/pgsql-16/
COPY --from=pg16-builder /usr/local/bin/pg_dumpall /usr/local/pgsql/pgsql-16/
COPY --from=pg16-builder /usr/local/bin/pg_restore /usr/local/pgsql/pgsql-16/
COPY --from=pg16-builder /usr/local/bin/psql /usr/local/pgsql/pgsql-16/
#########################################################################
# Assemble everything into the final container.
#########################################################################
@@ -159,9 +159,14 @@ COPY --from=env-builder /venv /venv
# Copy in the tools
COPY --from=tool-builder /usr/local/pgsql /usr/local/
COPY --from=pg15-builder /usr/local/lib/libpq.so.5.15 /usr/lib/
RUN ln -s libpq.so.5.15 /usr/lib/libpq.so.5 && \
ln -s libpq.so.5.15 /usr/lib/libpq.so
COPY --from=pg16-builder /usr/local/lib/libpq.so.5.16 /usr/lib/
COPY --from=pg16-builder /usr/lib/libzstd.so.1.5.5 /usr/lib/
COPY --from=pg16-builder /usr/lib/liblz4.so.1.9.4 /usr/lib/
RUN ln -s libpq.so.5.16 /usr/lib/libpq.so.5 && \
ln -s libpq.so.5.16 /usr/lib/libpq.so && \
ln -s libzstd.so.1.5.5 /usr/lib/libzstd.so.1 && \
ln -s liblz4.so.1.9.4 /usr/lib/liblz4.so.1
WORKDIR /pgadmin4
ENV PYTHONPATH=/pgadmin4
+4 -4
View File
@@ -26,8 +26,8 @@ CALL :CLEAN || EXIT /B 1
CALL :CREATE_VIRTUAL_ENV || EXIT /B 1
CALL :CREATE_PYTHON_ENV || EXIT /B 1
CALL :CREATE_RUNTIME_ENV || EXIT /B 1
CALL :CREATE_INSTALLER || EXIT /B 1
CALL :GENERATE_SBOM || EXIT /B 1
CALL :CREATE_INSTALLER || EXIT /B 1
CALL :SIGN_INSTALLER || EXIT /B 1
EXIT /B %ERRORLEVEL%
@@ -315,8 +315,8 @@ REM Main build sequence Ends
ECHO Staging PostgreSQL components...
COPY "%PGADMIN_POSTGRES_DIR%\bin\libpq.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
COPY "%PGADMIN_POSTGRES_DIR%\bin\libcrypto-1_1-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
COPY "%PGADMIN_POSTGRES_DIR%\bin\libssl-1_1-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
COPY "%PGADMIN_POSTGRES_DIR%\bin\libcrypto-*-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
COPY "%PGADMIN_POSTGRES_DIR%\bin\libssl-*-x64.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
IF EXIST "%PGADMIN_POSTGRES_DIR%\bin\libintl-*.dll" COPY "%PGADMIN_POSTGRES_DIR%\bin\libintl-*.dll" "%BUILDROOT%\runtime" > nul
IF EXIST "%PGADMIN_POSTGRES_DIR%\bin\libiconv-*.dll" COPY "%PGADMIN_POSTGRES_DIR%\bin\libiconv-*.dll" "%BUILDROOT%\runtime" > nul
COPY "%PGADMIN_POSTGRES_DIR%\bin\zlib.dll" "%BUILDROOT%\runtime" > nul || EXIT /B 1
@@ -364,7 +364,7 @@ REM Main build sequence Ends
:GENERATE_SBOM
ECHO Generating SBOM...
syft "%BUILDROOT%\" -o cyclonedx-json > "%BUILDROOT%\sbom.json"
CALL syft "%BUILDROOT%" -o cyclonedx-json > "%BUILDROOT%\sbom.json"
:SIGN_INSTALLER
ECHO Attempting to sign the installer...
+10
View File
@@ -251,6 +251,16 @@ tab to provide other backup options.
table locks at the beginning of the dump. Instead, fail if unable to lock a
table within the specified timeout.
Click the *Objects* tab to continue.
.. image:: images/backup_object_selection.png
:alt: Select objects in backup dialog
:align: center
* Select the objects from tree to take backup of selected objects only.
* If Schema is selected then it will take the backup of that selected schema only.
* If any Table, View, Materialized View, Sequences, or Foreign Table is selected then it will take the backup of those selected objects.
When you’ve specified the details that will be incorporated into the pg_dump
command:
+4
View File
@@ -39,6 +39,10 @@ are disabled if inapplicable.)
a text value.
* Use the drop-down listbox next to *Collation* to apply a collation setting to
the column.
* Use the drop-down listbox next to *Compression* to set the compression method
for the column. Compression is supported only for variable-width data types,
and is used only when the column's storage mode is main or extended. This
option is available from v14 and above.
Click the *Constraints* tab to continue.
+2 -2
View File
@@ -53,11 +53,11 @@ The default binary paths set in the container are as follows:
.. code-block:: bash
DEFAULT_BINARY_PATHS = {
'pg-16': '/usr/local/pgsql-16',
'pg-15': '/usr/local/pgsql-15',
'pg-14': '/usr/local/pgsql-14',
'pg-13': '/usr/local/pgsql-13',
'pg-12': '/usr/local/pgsql-12',
'pg-11': '/usr/local/pgsql-11'
'pg-12': '/usr/local/pgsql-12'
}
this may be changed in the :ref:`preferences`.
+14
View File
@@ -19,6 +19,9 @@ Use the fields in the *General* tab to identify the database:
* Use the *Database* field to add a descriptive name for the database. The name
will be displayed in the *pgAdmin* tree control.
* Use the *OID* field to specify the object identifier to be used for the new
database. Users can specify the value greater than 16383. This option is available
from v15 and above.
* Select the owner of the database from the drop-down listbox in the *Owner*
field.
* Store notes about the database in the *Comment* field.
@@ -38,11 +41,22 @@ Use the *Definition* tab to set properties for the database:
* Select a tablespace from the drop-down listbox in the *Tablespace* field. The
selected tablespace will be the default tablespace used to contain database
objects.
* Select the strategy from the drop-down listbox in the *Strategy* field while
creating a new database. This option is available from v15 and above.
* Select the locale provider from the drop-down listbox in the *Locale Provider*
field to set the default collation for this database. Possible values are: icu, libc.
This option is available from v15 and above.
* Select the collation order from the drop-down listbox in the *Collation* field.
* Select the character classification from the drop-down listbox in the
*Character Type* field. This affects the categorization of characters, e.g.
lower, upper and digit. The default, or a blank field, uses the character
classification of the template database.
* Select the icu locale from the drop-down listbox in the *ICU Locale* to
specifies the ICU locale ID if the ICU locale provider is used.
This option is available from v15 and above.
* Specify the icu rules in the *ICU Rules* field as additional collation
rules to customize the behavior of the default collation of this database.
This option is available from v16 and above.
* Specify a connection limit in the *Connection Limit* field to configure the
maximum number of connection requests. The default value (*-1*) allows
unlimited connections to the database.
Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 94 KiB

+14 -11
View File
@@ -31,17 +31,6 @@ Use the fields in the *General* tab to identify the materialized view:
Click the *Definition* tab to continue.
.. image:: images/materialized_view_definition.png
:alt: Materialized view dialog definition tab
:align: center
Use the text editor field in the *Definition* tab to provide the query that will
populate the materialized view. Please note that updating the definition of existing
materialized view would result in loss of Parameter(Table, Toast), Security(Privileges & Security labels),
Indexes and other dependent objects.
Click the *Storage* tab to continue.
.. image:: images/materialized_view_storage.png
:alt: Materialized view dialog storage tab
:align: center
@@ -52,10 +41,24 @@ Use the fields in the *Storage* tab to maintain the materialized view:
cannot be queried until you invoke REFRESH MATERIALIZED VIEW.
* Use the drop-down listbox next to *Tablespace* to select a location for the
materialized view.
* Use the drop-down list box next to Access Method to specify the table access method to use to store the contents
for the new materialized view; the method needs to be an access method of type TABLE. This field is optional.
This option is available from v12 and above.
* Use the *Fill Factor* field to specify a fill factor for the materialized
view. The fill factor for a table is a percentage between 10 and 100. 100
(complete packing) is the default.
Click the *Code* tab to continue.
.. image:: images/materialized_view_code.png
:alt: Materialized view dialog definition tab
:align: center
Use the text editor field in the *Code* tab to provide the query that will
populate the materialized view. Please note that updating the definition of existing
materialized view would result in loss of Parameter(Table, Toast), Security(Privileges & Security labels),
Indexes and other dependent objects.
Click the *Parameter* tab to continue.
.. image:: images/materialized_view_parameter.png
+7
View File
@@ -12,6 +12,10 @@ installed in Server mode. You can copy these settings from *config.py* file
and modify the values for the following parameters:
.. _AzureAD: https://learn.microsoft.com/en-us/security/zero-trust/develop/configure-tokens-group-claims-app-roles
.. _GitLab: https://docs.gitlab.com/ee/integration/openid_connect_provider.html#shared-information
.. csv-table::
:header: "**Parameter**", "**Description**"
:class: longtable
@@ -39,6 +43,9 @@ and modify the values for the following parameters:
"OAUTH2_AUTO_CREATE_USER", "Set the value to *True* if you want to automatically
create a pgAdmin user corresponding to a successfully authenticated Oauth2 user.
Please note that password is not stored in the pgAdmin database."
"OAUTH2_ADDITIONAL_CLAIMS", "If a dictionary is provided, pgAdmin will check for a matching key and value on the user profile.
In case the profile does not have any match with the provided config, the user will receive an authorization error.
Useful for checking AzureAD_ *wids* or *groups*, GitLab_ *owner*, *maintainer* and *reporter* claims."
Redirect URL
============
+2
View File
@@ -11,6 +11,8 @@ notes for it.
.. toctree::
:maxdepth: 1
release_notes_7_8
release_notes_7_7
release_notes_7_6
release_notes_7_5
release_notes_7_4
+44
View File
@@ -0,0 +1,44 @@
***********
Version 7.7
***********
Release date: 2023-09-21
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v7.6.
Supported Database Servers
**************************
**PostgreSQL**: 11, 12, 13, 14 and 15
**EDB Advanced Server**: 11, 12, 13, 14 and 15
Bundled PostgreSQL Utilities
****************************
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 15.4
New features
************
| `Issue #642 <https://github.com/pgadmin-org/pgadmin4/issues/642>`_ - Added support to select/deselect objects in the Backup dialog.
| `Issue #4805 <https://github.com/pgadmin-org/pgadmin4/issues/4805>`_ - Added all the new options of the 'WITH' clause in the subscription dialog.
| `Issue #6378 <https://github.com/pgadmin-org/pgadmin4/issues/6378>`_ - Added USING method while creating the table.
| `Issue #6379 <https://github.com/pgadmin-org/pgadmin4/issues/6379>`_ - Added compression method option while creating a column.
| `Issue #6383 <https://github.com/pgadmin-org/pgadmin4/issues/6383>`_ - Added Strategy, Locale Provider, ICU Locale, ICU Rules, and OID options while creating a database.
| `Issue #6400 <https://github.com/pgadmin-org/pgadmin4/issues/6400>`_ - Added USING method while creating the materialized view.
| `Issue #6736 <https://github.com/pgadmin-org/pgadmin4/issues/6736>`_ - Add support for additional ID token claim checks for OAuth 2 authentication.
Housekeeping
************
| `Issue #2411 <https://github.com/pgadmin-org/pgadmin4/issues/2411>`_ - Added the 'data type' column in the properties tab of the Columns collection node.
Bug fixes
*********
| `Issue #6274 <https://github.com/pgadmin-org/pgadmin4/issues/6274>`_ - Fix an issue where user is not able to change the password when SMTP is not configured.
| `Issue #6704 <https://github.com/pgadmin-org/pgadmin4/issues/6704>`_ - Ensure user is redirected to login page after failed login.
| `Issue #6712 <https://github.com/pgadmin-org/pgadmin4/issues/6712>`_ - Ensure that Materialized view size fields in "Statistics" should be human-readable.
| `Issue #6730 <https://github.com/pgadmin-org/pgadmin4/issues/6730>`_ - Fix an issue where changing the password shows success but the new password is not working.
| `Issue #6738 <https://github.com/pgadmin-org/pgadmin4/issues/6738>`_ - Fix an issue where login form doesn't appear if internal auth source is removed.
| `Issue #6764 <https://github.com/pgadmin-org/pgadmin4/issues/6764>`_ - Fix a security related issue where an authenticated user can run remote command using validate binary path API (CVE-2023-5002).
+51
View File
@@ -0,0 +1,51 @@
***********
Version 7.8
***********
Release date: 2023-10-19
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v7.7.
Supported Database Servers
**************************
**PostgreSQL**: 12, 13, 14, 15, and 16
**EDB Advanced Server**: 12, 13, 14 and 15
Bundled PostgreSQL Utilities
****************************
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 16.0
New features
************
| `Issue #640 <https://github.com/pgadmin-org/pgadmin4/issues/640>`_ - Add support for foreign table's new functionality for PG 11 and above.
| `Issue #6229 <https://github.com/pgadmin-org/pgadmin4/issues/6229>`_ - Allow setting custom username for shared servers, with default as username of server being shared.
| `Issue #6373 <https://github.com/pgadmin-org/pgadmin4/issues/6373>`_ - Add 'GENERATED ALWAYS AS..' option while creating column constraints for Foreign Table.
| `Issue #6797 <https://github.com/pgadmin-org/pgadmin4/issues/6797>`_ - GUI representation of the system's activity using the 'system_stats' extension.
| `Issue #6802 <https://github.com/pgadmin-org/pgadmin4/issues/6802>`_ - Added 'load_balance_hosts' connection string parameter for PG 16 and above.
Housekeeping
************
| `Issue #6782 <https://github.com/pgadmin-org/pgadmin4/issues/6782>`_ - Use PG16 as the default PostgreSQL version.
Bug fixes
*********
| `Issue #4995 <https://github.com/pgadmin-org/pgadmin4/issues/4995>`_ - Fixed an issue in ERD tool where the downloaded images have a few links cut.
| `Issue #5749 <https://github.com/pgadmin-org/pgadmin4/issues/5749>`_ - Fixed an issue where user was not able to assign new/old columns as primary key once column with primary key is deleted.
| `Issue #6285 <https://github.com/pgadmin-org/pgadmin4/issues/6285>`_ - Add support for setting prepare threshold in server connection.
| `Issue #6482 <https://github.com/pgadmin-org/pgadmin4/issues/6482>`_ - Fixed an issue where the wrong message "Current database has been moved or renamed" is displayed when debugging any function.
| `Issue #6538 <https://github.com/pgadmin-org/pgadmin4/issues/6538>`_ - Fixed an issue where Processes tab displays wrong server name in some scenario.
| `Issue #6579 <https://github.com/pgadmin-org/pgadmin4/issues/6579>`_ - Fix an issue where global/native keyboard shortcuts are not working when any cell of data output grid has focus.
| `Issue #6666 <https://github.com/pgadmin-org/pgadmin4/issues/6666>`_ - Fixed query history slowness issue by storing query only for those having certain threshold max length.
| `Issue #6674 <https://github.com/pgadmin-org/pgadmin4/issues/6674>`_ - Fix an issue where foreign table column name becomes "none" if the user changes any column data type.
| `Issue #6718 <https://github.com/pgadmin-org/pgadmin4/issues/6718>`_ - Pin the cryptography version to fix PyO3 modules initialisation error.
| `Issue #6790 <https://github.com/pgadmin-org/pgadmin4/issues/6790>`_ - Ensure that the backup works properly for PG 16 on the latest docker image.
| `Issue #6799 <https://github.com/pgadmin-org/pgadmin4/issues/6799>`_ - Fixed an issue where the user is unable to select objects on the backup dialog due to tree flickering.
| `Issue #6836 <https://github.com/pgadmin-org/pgadmin4/issues/6836>`_ - Fixed an issue where non-super PostgreSQL users are not able to terminate their own connections from dashboard.
| `Issue #6851 <https://github.com/pgadmin-org/pgadmin4/issues/6851>`_ - Fix an issue where scale in columns is not allowed to have value as 0 or below.
| `Issue #6858 <https://github.com/pgadmin-org/pgadmin4/issues/6858>`_ - Fix an issue in graphical explain plan where query tool crashes when the plan has parallel workers details and sort node is clicked for details.
| `Issue #6865 <https://github.com/pgadmin-org/pgadmin4/issues/6865>`_ - Fix an issue where user login is not working if username/email contains single quote in server mode.
+9
View File
@@ -44,6 +44,9 @@ Use the fields in the *General* tab to identify the server:
Please note that once the server is shared, it's icon is changed in the
object explorer.
* Use the *Shared Username* field to fill the username of the shared server
connection. By default, it will take the username of the server being shared.
* Provide a comment about the server in the *Comments* field.
Click the *Connection* tab to continue.
@@ -203,6 +206,12 @@ Use the fields in the *Advanced* tab to configure a connection:
the password will not expire until your pgAdmin session does.
Zero means the command will be executed for each new connection or reconnection that is made.
If the generated password is not valid indefinitely, set this value to slightly before it will expire.
* Use the *Prepare threshold* field to specify the number of times a query is
executed before it is prepared. If it is set to 0, every query is prepared
the first time it is executed. If it is set to blank, prepared statements are disabled
on the connection. This is particularly useful with external connection poolers,
such as PgBouncer, which is not compatible with prepared statements. Set this to
blank in such cases.
.. note:: The password file option is only supported when pgAdmin is using libpq
v10.0 or later to connect to the server.
+13 -2
View File
@@ -6,7 +6,7 @@
Use the *Subscription* dialog to create a subscription. A subscription defines the connection to another database and set of publications (one or more) to which it wants to subscribe.
The *subscription* dialog organizes the development of a subscription through the following dialog tabs: *General*, *Connection* and *With*. The *SQL* tab displays the SQL code generated by dialog selections.
The *subscription* dialog organizes the development of a subscription through the following dialog tabs: *General*, *Connection*, *SSL* and *With*. The *SQL* tab displays the SQL code generated by dialog selections.
.. image:: images/subscription_general.png
:alt: Subscription dialog general tab
@@ -83,7 +83,11 @@ icon that is located to the right of each of the following fields.
Click the *With* tab to continue.
.. image:: images/subscription_with.png
.. image:: images/subscription_with_1.png
:alt: Subscription dialog with tab
:align: center
.. image:: images/subscription_with_2.png
:alt: Subscription dialog with tab
:align: center
@@ -95,6 +99,13 @@ Use the *With* tab to define some parameters for a subscription:
* The *Connect?* specifies whether the CREATE SUBSCRIPTION should connect to the publisher at all. By default, it is set to *Yes*. Setting this to *No* will change default values of enabled, create_slot and copy_data to *No*.
* Use the *Slot Name* field to specify the name of the replication slot to use. By default, it uses the name of the subscription for the slot name.
* Use the *Synchronous commit* field to override the synchronous_commit setting. By default, it is set to *off*. It is safe to use off for logical replication: If the subscriber loses transactions because of missing synchronization, the data will be sent again from the publisher.
* Use the *Streaming* field to specify whether to enable streaming of in-progress transactions for this subscription. The default value is *off*, meaning all transactions are fully decoded on the publisher and only then sent to the subscriber as a whole. This option is available only on PostgreSQL 14 and above.
* Use the *Binary?* switch to specify whether the subscription will request the publisher to send the data in binary format (as opposed to text). The default is *false*. This option is available only on PostgreSQL 14 and above.
* Use the *Two phase?* switch to specify whether two-phase commit is enabled for this subscription. The default is *false*. This option is available only on PostgreSQL 15 and above.
* Use the *Disable on error?* switch to specify whether the subscription should be automatically disabled if any errors are detected by subscription workers during data replication from the publisher. The default is *false*. This option is available only on PostgreSQL 15 and above.
* 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.
Click the *SQL* tab to continue.
+3
View File
@@ -394,6 +394,9 @@ Use the fields in the *Advanced* tab to define advanced features for the table:
* Use the drop-down listbox next to *Of type* to copy the table structure from
the specified composite type. Please note that a typed table will be dropped
if the type is dropped (with DROP TYPE ... CASCADE).
* Use the drop-down list box next to Access Method to specify the table access method to use to store
the contents for the new table; the method needs to be an access method of type TABLE. This field is optional.
This option is available from v12 and above.
* Use the *Fill Factor* field to specify a fill factor for the table. The fill
factor for a table is a percentage between 10 and 100. 100 (complete packing)
is the default.
+4 -5
View File
@@ -19,19 +19,18 @@ CA_FILE = '/etc/ssl/certs/ca-certificates.crt'
LOG_FILE = '/dev/null'
HELP_PATH = '../../docs'
DEFAULT_BINARY_PATHS = {
'pg': '/usr/local/pgsql-15',
'pg': '/usr/local/pgsql-16',
'pg-16': '/usr/local/pgsql-16',
'pg-15': '/usr/local/pgsql-15',
'pg-14': '/usr/local/pgsql-14',
'pg-13': '/usr/local/pgsql-13',
'pg-12': '/usr/local/pgsql-12',
'pg-11': '/usr/local/pgsql-11',
'pg-10': '/usr/local/pgsql-10'
'pg-12': '/usr/local/pgsql-12'
}
EOF
# This is a bit kludgy, but necessary as the container uses BusyBox/ash as
# it's shell and not bash which would allow a much cleaner implementation
for var in $(env | grep PGADMIN_CONFIG_ | cut -d "=" -f 1); do
for var in $(env | grep "^PGADMIN_CONFIG_" | cut -d "=" -f 1); do
# shellcheck disable=SC2086
# shellcheck disable=SC2046
echo ${var#PGADMIN_CONFIG_} = $(eval "echo \$$var") >> /pgadmin4/config_distro.py
+4 -4
View File
@@ -27,11 +27,12 @@ pytz==2023.*
speaklater3==1.*
sqlparse==0.*
psutil==5.9.*
psycopg[c]==3.1.9
psycopg[c]==3.1.12
python-dateutil==2.*
SQLAlchemy==2.*
bcrypt==4.0.*
cryptography==41.0.*
# Pinned for #6718
cryptography==40.0.*
sshtunnel==0.*
ldap3==2.*
gssapi==1.8.*
@@ -42,7 +43,6 @@ pywinpty==2.0.*; sys_platform=="win32"
Authlib==1.2.*
pyotp==2.*
qrcode==7.*
Pillow==9.*
boto3==1.28.*
botocore==1.31.*
urllib3==1.26.*
@@ -53,4 +53,4 @@ azure-identity==1.13.0
google-api-python-client==2.*
google-auth-oauthlib==1.0.0
Werkzeug==2.2.3
keyring==23.*
keyring==23.*
+2 -2
View File
@@ -23,11 +23,11 @@
"256": "assets/pgAdmin4.png"
},
"dependencies": {
"axios": "^1.4.0",
"axios": "^1.5.1",
"bootstrap": "^4.5.3"
},
"devDependencies": {
"eslint": "^8.42.0",
"eslint": "^8.50.0",
"nw": "0.77.0"
},
"scripts": {
+20 -20
View File
@@ -47,21 +47,21 @@ __metadata:
languageName: node
linkType: hard
"@eslint/js@npm:^8.47.0":
version: 8.47.0
resolution: "@eslint/js@npm:8.47.0"
checksum: 0ef57fe27b6d4c305b33f3b2d2fee1ab397a619006f1d6f4ce5ee4746b8f03d11a4e098805a7d78601ca534cf72917d37f0ac19896c992a32e26299ecb9f9de1
"@eslint/js@npm:8.50.0":
version: 8.50.0
resolution: "@eslint/js@npm:8.50.0"
checksum: 302478f2acaaa7228729ec6a04f56641590185e1d8cd1c836a6db8a6b8009f80a57349341be9fbb9aa1721a7a569d1be3ffc598a33300d22816f11832095386c
languageName: node
linkType: hard
"@humanwhocodes/config-array@npm:^0.11.10":
version: 0.11.10
resolution: "@humanwhocodes/config-array@npm:0.11.10"
"@humanwhocodes/config-array@npm:^0.11.11":
version: 0.11.11
resolution: "@humanwhocodes/config-array@npm:0.11.11"
dependencies:
"@humanwhocodes/object-schema": ^1.2.1
debug: ^4.1.1
minimatch: ^3.0.5
checksum: 1b1302e2403d0e35bc43e66d67a2b36b0ad1119efc704b5faff68c41f791a052355b010fb2d27ef022670f550de24cd6d08d5ecf0821c16326b7dcd0ee5d5d8a
checksum: db84507375ab77b8ffdd24f498a5b49ad6b64391d30dd2ac56885501d03964d29637e05b1ed5aefa09d57ac667e28028bc22d2da872bfcd619652fbdb5f4ca19
languageName: node
linkType: hard
@@ -205,14 +205,14 @@ __metadata:
languageName: node
linkType: hard
"axios@npm:^1.4.0":
version: 1.4.0
resolution: "axios@npm:1.4.0"
"axios@npm:^1.5.1":
version: 1.5.1
resolution: "axios@npm:1.5.1"
dependencies:
follow-redirects: ^1.15.0
form-data: ^4.0.0
proxy-from-env: ^1.1.0
checksum: 7fb6a4313bae7f45e89d62c70a800913c303df653f19eafec88e56cea2e3821066b8409bc68be1930ecca80e861c52aa787659df0ffec6ad4d451c7816b9386b
checksum: 4444f06601f4ede154183767863d2b8e472b4a6bfc5253597ed6d21899887e1fd0ee2b3de792ac4f8459fe2e359d2aa07c216e45fd8b9e4e0688a6ebf48a5a8d
languageName: node
linkType: hard
@@ -618,15 +618,15 @@ __metadata:
languageName: node
linkType: hard
"eslint@npm:^8.42.0":
version: 8.47.0
resolution: "eslint@npm:8.47.0"
"eslint@npm:^8.50.0":
version: 8.50.0
resolution: "eslint@npm:8.50.0"
dependencies:
"@eslint-community/eslint-utils": ^4.2.0
"@eslint-community/regexpp": ^4.6.1
"@eslint/eslintrc": ^2.1.2
"@eslint/js": ^8.47.0
"@humanwhocodes/config-array": ^0.11.10
"@eslint/js": 8.50.0
"@humanwhocodes/config-array": ^0.11.11
"@humanwhocodes/module-importer": ^1.0.1
"@nodelib/fs.walk": ^1.2.8
ajv: ^6.12.4
@@ -661,7 +661,7 @@ __metadata:
text-table: ^0.2.0
bin:
eslint: bin/eslint.js
checksum: 1988617f703eadc5c7540468d54dc8e5171cf2bb9483f6172799cd1ff54a9a5e4470f003784e8cef92687eaa14de37172732787040e67817581a20bcb9c15970
checksum: 9ebfe5615dc84700000d218e32ddfdcfc227ca600f65f18e5541ec34f8902a00356a9a8804d9468fd6c8637a5ef6a3897291dad91ba6579d5b32ffeae5e31768
languageName: node
linkType: hard
@@ -1414,9 +1414,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "pgadmin4@workspace:."
dependencies:
axios: ^1.4.0
axios: ^1.5.1
bootstrap: ^4.5.3
eslint: ^8.42.0
eslint: ^8.50.0
nw: 0.77.0
languageName: unknown
linkType: soft
+26 -4
View File
@@ -50,7 +50,7 @@ APP_ICON = 'pg-icon'
# Application version number components
APP_RELEASE = 7
APP_REVISION = 6
APP_REVISION = 8
# Application version suffix, e.g. 'beta1', 'dev'. Usually an empty string
# for GA releases.
@@ -59,7 +59,7 @@ APP_SUFFIX = ''
# Numeric application version for upgrade checks. Should be in the format:
# [X]XYYZZ, where X is the release version, Y is the revision, with a leading
# zero if needed, and Z represents the suffix, with a leading zero if needed
APP_VERSION_INT = 70600
APP_VERSION_INT = 70800
# DO NOT CHANGE!
# The application version string, constructed from the components
@@ -477,14 +477,12 @@ STORAGE_DIR = os.path.join(DATA_DIR, 'storage')
##########################################################################
DEFAULT_BINARY_PATHS = {
"pg": "",
"pg-11": "",
"pg-12": "",
"pg-13": "",
"pg-14": "",
"pg-15": "",
"pg-16": "",
"ppas": "",
"ppas-11": "",
"ppas-12": "",
"ppas-13": "",
"ppas-14": "",
@@ -793,6 +791,30 @@ OAUTH2_CONFIG = [
'OAUTH2_ICON': None,
# UI button colour, ex: #0000ff
'OAUTH2_BUTTON_COLOR': None,
# The additional claims to check on user ID Token. This is useful to
# provide additional authorization checks before allowing access.
# Example for GitLab: allowing all maintainers teams, and a specific
# developers group to access pgadmin:
# 'OAUTH2_ADDITIONAL_CLAIMS': {
# 'https://gitlab.org/claims/groups/maintainer': [
# 'kuberheads/applications',
# 'kuberheads/dba',
# 'kuberheads/support'
# ],
# 'https://gitlab.org/claims/groups/developer': [
# 'kuberheads/applications/team01'
# ],
# }
# Example for AzureAD:
# 'OAUTH2_ADDITIONAL_CLAIMS': {
# 'groups': ["0760b6cf-170e-4a14-91b3-4b78e0739963"],
# 'wids': ["cf1c38e5-3621-4004-a7cb-879624dced7c"],
# }
# Example for any key value string check:
# 'OAUTH2_ADDITIONAL_CLAIMS': {
# 'group': "0760b6cf-170e-4a14-91b3-4b78e0739963",
# }
'OAUTH2_ADDITIONAL_CLAIMS': None,
}
]
+54
View File
@@ -0,0 +1,54 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
"""Add support for prepare_threshold param
https://www.psycopg.org/psycopg3/docs/advanced/prepare.html#prepared-statements
Revision ID: 44b9ce549393
Revises: 9426ad06a63b
Create Date: 2023-10-12 12:15:01.757931
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '44b9ce549393'
down_revision = '9426ad06a63b'
branch_labels = None
depends_on = None
def upgrade():
with op.batch_alter_table(
"server", table_kwargs={'sqlite_autoincrement': True}) as batch_op:
batch_op.add_column(sa.Column('prepare_threshold', sa.Integer(),
nullable=True))
with op.batch_alter_table("sharedserver") as batch_op:
batch_op.add_column(sa.Column('prepare_threshold', sa.Integer(),
nullable=True))
# get metadata from current connection
meta = sa.MetaData()
# define table representation
meta.reflect(op.get_bind(), only=('server', 'sharedserver'))
table = sa.Table('server', meta)
op.execute(
table.update().values(prepare_threshold=5)
)
table = sa.Table('sharedserver', meta)
op.execute(
table.update().values(prepare_threshold=5)
)
def downgrade():
# pgAdmin only upgrades, downgrade not implemented.
pass
+40
View File
@@ -0,0 +1,40 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
""" Add the new column for shared server username and change
autoincrement in server table
Revision ID: 9426ad06a63b
Revises: f656e56dfdc8
Create Date: 2023-10-09 15:09:50.773035
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '9426ad06a63b'
down_revision = 'f656e56dfdc8'
branch_labels = None
depends_on = None
def upgrade():
# Added sqlite_autoincrement to force sqlite use auto increment instead of
# last row id for next record. For future server table changes, we need to
# add table_kwargs={'sqlite_autoincrement': True} as a param to
# batch_alter_table
with op.batch_alter_table(
"server", table_kwargs={'sqlite_autoincrement': True}) as batch_op:
batch_op.alter_column('id', autoincrement=True)
batch_op.add_column(sa.Column('shared_username', sa.String(64), nullable=True))
def downgrade():
# pgAdmin only upgrades, downgrade not implemented.
pass
+5 -5
View File
@@ -35,7 +35,7 @@
"css-minimizer-webpack-plugin": "^5.0.0",
"enzyme": "^3.11.0",
"eslint": "^8.37.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.3.0",
"exports-loader": "^4.0.0",
"html-react-parser": "^4.2.0",
@@ -43,7 +43,6 @@
"imagemin": "^8.0.1",
"imagemin-mozjpeg": "^10.0.0",
"imagemin-optipng": "^8.0.0",
"imagemin-pngquant": "^9.0.2",
"imports-loader": "^4.0.1",
"is-docker": "^2.1.1",
"istanbul-instrumenter-loader": "^3.0.1",
@@ -77,7 +76,7 @@
"typescript": "^3.2.2",
"url-loader": "^4.1.1",
"webfonts-loader": "^8.0.1",
"webpack": "^5.76.3",
"webpack": "^5.88.2",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^4.5.0",
"yarn-audit-html": "4.0.0"
@@ -117,7 +116,7 @@
"dagre": "^0.8.4",
"date-fns": "^2.24.0",
"diff-arrays-of-objects": "^1.1.8",
"html2canvas": "^1.0.0-rc.7",
"html-to-image": "^1.11.11",
"immutability-helper": "^3.0.0",
"insert-if": "^1.1.0",
"ip-address": "^7.1.0",
@@ -138,10 +137,11 @@
"pathfinding": "^0.4.18",
"paths-js": "^0.4.9",
"popper.js": "^1.16.1",
"postcss": "^8.2.15",
"postcss": "^8.4.31",
"raf": "^3.4.1",
"rc-dock": "^3.2.9",
"react": "^17.0.1",
"react-arborist": "^3.2.0",
"react-aspen": "^1.1.0",
"react-checkbox-tree": "^1.7.2",
"react-data-grid": "https://github.com/pgadmin-org/react-data-grid.git#200d2f5e02de694e3e9ffbe177c279bc40240fb8",
+6 -6
View File
@@ -18,15 +18,15 @@ from flask import current_app, flash, Response, request, url_for, \
session, redirect, render_template
from flask_babel import gettext
from flask_security.views import _security, _ctx
from flask_security.utils import get_post_logout_redirect, logout_user,\
config_value
from flask_security.utils import logout_user, config_value
from flask_login import current_user
from flask_socketio import disconnect, ConnectionRefusedError
from pgadmin.model import db, User
from pgadmin.utils import PgAdminModule, get_safe_post_login_redirect
from pgadmin.utils import PgAdminModule, get_safe_post_login_redirect, \
get_safe_post_logout_redirect
from pgadmin.utils.constants import KERBEROS, INTERNAL, OAUTH2, LDAP,\
MessageType
from pgadmin.authenticate.registry import AuthSourceRegistry
@@ -135,7 +135,7 @@ def _login():
'Administrator.'),
MessageType.WARNING)
logout_user()
return redirect(get_post_logout_redirect())
return redirect(get_safe_post_logout_redirect())
# Validate the user
if not auth_obj.validate():
@@ -161,7 +161,7 @@ def _login():
flash_login_attempt_error = None
flash(error, MessageType.WARNING)
return redirect(get_post_logout_redirect())
return redirect(get_safe_post_logout_redirect())
# Authenticate the user
status, msg = auth_obj.authenticate()
@@ -177,7 +177,7 @@ def _login():
'authenticate.kerberos_login'), url_for('browser.index')))
flash(msg, MessageType.ERROR)
return redirect(get_post_logout_redirect())
return redirect(get_safe_post_logout_redirect())
session['auth_source_manager'] = current_auth_obj
@@ -154,7 +154,7 @@ class TOTPAuthenticator(BaseMFAuth):
img = qrcode.make(uri)
buffered = BytesIO()
img.save(buffered, format="JPEG")
img.save(buffered)
img_base64 = base64.b64encode(buffered.getvalue())
return dict(
+46 -4
View File
@@ -16,13 +16,14 @@ from flask import current_app, url_for, session, request,\
redirect, Flask, flash
from flask_babel import gettext
from flask_security import login_user, current_user
from flask_security.utils import get_post_logout_redirect, logout_user
from flask_security.utils import logout_user
from pgadmin.authenticate.internal import BaseAuthentication
from pgadmin.model import User
from pgadmin.tools.user_management import create_user
from pgadmin.utils.constants import OAUTH2, MessageType
from pgadmin.utils import PgAdminModule, get_safe_post_login_redirect
from pgadmin.utils import PgAdminModule, get_safe_post_login_redirect, \
get_safe_post_logout_redirect
from pgadmin.utils.csrf import pgCSRFProtect
from pgadmin.model import db
@@ -69,11 +70,11 @@ def init_app(app):
@pgCSRFProtect.exempt
def oauth_logout():
if not current_user.is_authenticated:
return redirect(get_post_logout_redirect())
return redirect(get_safe_post_logout_redirect())
for key in list(session.keys()):
session.pop(key)
logout_user()
return redirect(get_post_logout_redirect())
return redirect(get_safe_post_logout_redirect())
app.register_blueprint(blueprint)
app.login_manager.logout_view = OAUTH2_LOGOUT
@@ -150,6 +151,26 @@ class OAuth2Authentication(BaseAuthentication):
current_app.logger.exception(error_msg)
return False, gettext(error_msg)
additinal_claims = None
if 'OAUTH2_ADDITIONAL_CLAIMS' in self.oauth2_config[
self.oauth2_current_client]:
additinal_claims = self.oauth2_config[
self.oauth2_current_client
]['OAUTH2_ADDITIONAL_CLAIMS']
(valid, reason) = self.__is_additional_claims_valid(profile,
additinal_claims)
if not valid:
return_msg = "The user is not authorized to login" \
" based on the claims in the profile." \
" Please contact your administrator."
audit_msg = f"The authenticated user {username} is not" \
" authorized to access pgAdmin based on OAUTH2 config. " \
f"Reason: {reason}"
current_app.logger.warning(audit_msg)
return False, return_msg
user, msg = self.__auto_create_user(username, email)
if user:
user = db.session.query(User).filter_by(
@@ -203,3 +224,24 @@ class OAuth2Authentication(BaseAuthentication):
})
return True, {'username': username}
def __is_additional_claims_valid(self, profile, additional_claims):
if additional_claims is None:
reason = "Additional claim config is None, no check to do."
return (True, reason)
if not isinstance(additional_claims, dict):
reason = "Additional claim check config is not a dict."
return (False, reason)
if additional_claims.keys() is None:
reason = "Additional claim check config dict is empty."
return (False, reason)
for key in additional_claims.keys():
claim = profile.get(key)
if claim is None:
continue
authorized_claims = additional_claims.get(key)
if any(item in authorized_claims for item in claim):
reason = "Claim match found. Authorizing"
return (True, reason)
reason = f"Profile does not have any of given additional claims."
return (False, reason)
+19 -22
View File
@@ -30,13 +30,13 @@ from flask_babel import gettext
from flask_gravatar import Gravatar
from flask_login import current_user, login_required
from flask_login.utils import login_url
from flask_security.changeable import change_user_password
from flask_security.changeable import send_password_changed_notice
from flask_security.decorators import anonymous_user_required
from flask_security.recoverable import reset_password_token_status, \
generate_reset_password_token, update_password
from flask_security.signals import reset_password_instructions_sent
from flask_security.utils import config_value, do_flash, get_url, \
get_message, slash_url_suffix, login_user, send_mail, \
get_message, slash_url_suffix, login_user, send_mail, hash_password, \
get_post_logout_redirect
from flask_security.views import _security, view_commit, _ctx
from werkzeug.datastructures import MultiDict
@@ -668,7 +668,7 @@ def utils():
mfa_enabled=is_mfa_enabled(),
is_admin=current_user.has_role("Administrator"),
login_url=login_url,
username=current_user.username,
username=current_user.username.replace("'","\\'"),
auth_source=auth_source,
heartbeat_timeout=config.SERVER_HEARTBEAT_TIMEOUT,
password_length_min=config.PASSWORD_LENGTH_MIN,
@@ -1071,30 +1071,27 @@ if hasattr(config, 'SECURITY_CHANGEABLE') and config.SECURITY_CHANGEABLE:
}
elif req_json:
form = form_class(MultiDict(req_json))
if form.validate_on_submit():
if form.validate():
errormsg = None
# change_user_password from flask-security logs out the user
# this is undesirable, so change password on own
try:
change_user_password(current_user._get_current_object(),
form.new_password.data,
autologin=False)
except SOCKETErrorException as e:
# Handle socket errors which are not covered by
# SMTPExceptions.
logging.exception(str(e), exc_info=True)
errormsg = gettext(SMTP_SOCKET_ERROR).format(e)
except (SMTPConnectError, SMTPResponseException,
SMTPServerDisconnected, SMTPDataError, SMTPHeloError,
SMTPException, SMTPAuthenticationError,
SMTPSenderRefused, SMTPRecipientsRefused) as ex:
# Handle smtp specific exceptions.
logging.exception(str(ex), exc_info=True)
errormsg = gettext(SMTP_ERROR).format(ex)
user = User.query.filter(
User.fs_uniquifier == current_user.fs_uniquifier)\
.first()
user.password = hash_password(form.new_password.data)
try:
send_password_changed_notice(user)
except Exception as _:
# No need to throw error if failed in sending email
pass
except Exception as e:
# Handle other exceptions.
logging.exception(str(e), exc_info=True)
errormsg = gettext(PASS_ERROR).format(e)
if request.get_json(silent=True) is None and errormsg is None:
if errormsg is None:
old_key = get_crypt_key()[1]
set_crypt_key(form.new_password.data, False)
@@ -1103,12 +1100,12 @@ if hasattr(config, 'SECURITY_CHANGEABLE') and config.SECURITY_CHANGEABLE:
reencrpyt_server_passwords(
current_user.id, old_key, form.new_password.data)
return redirect(get_url(_security.post_change_view) or
get_url(_security.post_login_view))
db.session.commit()
elif errormsg is not None:
return internal_server_error(errormsg)
else:
return bad_request(list(form.errors.values())[0][0])
return make_json_response(
success=1,
info=gettext('pgAdmin user password changed successfully')
@@ -176,6 +176,7 @@ class ServerModule(sg.ServerGroupPluginModule):
server.username = sharedserver.username
server.server_owner = sharedserver.server_owner
server.password = sharedserver.password
server.prepare_threshold = sharedserver.prepare_threshold
return server
@@ -376,7 +377,7 @@ class ServerModule(sg.ServerGroupPluginModule):
host=data.host,
port=data.port,
maintenance_db=data.maintenance_db,
username=None,
username=data.shared_username,
save_password=0,
comment=None,
role=data.role,
@@ -390,7 +391,8 @@ class ServerModule(sg.ServerGroupPluginModule):
tunnel_authentication=0,
tunnel_identity_file=None,
shared=True,
connection_params=data.connection_params
connection_params=data.connection_params,
prepare_threshold=data.prepare_threshold
)
db.session.add(shared_server)
db.session.commit()
@@ -399,8 +401,7 @@ class ServerModule(sg.ServerGroupPluginModule):
db.session.delete(shared_server)
db.session.commit()
current_app.logger.exception(e)
return internal_server_error(errormsg=str(e))
raise e
@staticmethod
def get_shared_server(server, gid):
@@ -814,8 +815,10 @@ class ServerNode(PGChildNodeView):
'tunnel_authentication': 'tunnel_authentication',
'tunnel_identity_file': 'tunnel_identity_file',
'shared': 'shared',
'shared_username': 'shared_username',
'kerberos_conn': 'kerberos_conn',
'connection_params': 'connection_params'
'connection_params': 'connection_params',
'prepare_threshold': 'prepare_threshold'
}
disp_lbl = {
@@ -850,6 +853,10 @@ class ServerNode(PGChildNodeView):
errormsg=gettext('Not a valid Host address')
)
# remove the shared username if shared is updated to False
if 'shared' in data and data['shared'] is False:
data['shared_username'] = ''
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection()
connected = conn.connected()
@@ -1073,6 +1080,8 @@ class ServerNode(PGChildNodeView):
'port': server.port,
'db': server.maintenance_db,
'shared': server.shared if config.SERVER_MODE else None,
'shared_username': server.shared_username
if config.SERVER_MODE else None,
'username': server.username,
'gid': str(server.servergroup_id),
'group-name': sg.name if (sg and sg.name) else gettext('Servers'),
@@ -1102,7 +1111,8 @@ class ServerNode(PGChildNodeView):
'gss_encrypted': manager.gss_encrypted,
'cloud_status': server.cloud_status,
'connection_params': connection_params,
'connection_string': manager.display_connection_string
'connection_string': manager.display_connection_string,
'prepare_threshold': server.prepare_threshold
}
return ajax_response(response)
@@ -1192,10 +1202,12 @@ class ServerNode(PGChildNodeView):
False) else 0,
tunnel_identity_file=data.get('tunnel_identity_file', None),
shared=data.get('shared', None),
shared_username=data.get('shared_username', None),
passexec_cmd=data.get('passexec_cmd', None),
passexec_expiration=data.get('passexec_expiration', None),
kerberos_conn=1 if data.get('kerberos_conn', False) else 0,
connection_params=connection_params
connection_params=connection_params,
prepare_threshold=data.get('prepare_threshold', None)
)
db.session.add(server)
db.session.commit()
@@ -193,6 +193,10 @@ class DatabaseView(PGChildNodeView):
{'get': 'get_ctypes'},
{'get': 'get_ctypes'}
],
'get_icu_locale': [
{'get': 'get_icu_locale'},
{'get': 'get_icu_locale'}
],
'vopts': [
{}, {'get': 'variable_options'}
],
@@ -634,7 +638,7 @@ class DatabaseView(PGChildNodeView):
"""
This function to return list of available collation/character types
"""
res = [{'label': '', 'value': ''}]
res = []
default_list = ['C', 'POSIX']
for val in default_list:
res.append(
@@ -656,6 +660,28 @@ class DatabaseView(PGChildNodeView):
status=200
)
@check_precondition(action="get_icu_locale")
def get_icu_locale(self, gid, sid, did=None):
"""
This function is used to get the list of icu locale
"""
res = []
SQL = render_template(
"/".join([self.template_path, 'get_icu_locale.sql'])
)
status, rset = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
res.append(
{'label': row['colliculocale'], 'value': row['colliculocale']})
return make_json_response(
data=res,
status=200
)
@check_precondition(action="create")
def create(self, gid, sid):
"""Create the database."""
@@ -1230,6 +1256,12 @@ class DatabaseView(PGChildNodeView):
did=did, conn=conn, last_system_oid=0,
show_system_objects=False,
)
# try to connect the db if not connected
# don't delete the below code as it is needed for Database RESQL tests.
if not conn.connected():
conn.connect()
status, res = conn.execute_dict(SQL)
if not status:
@@ -10,11 +10,11 @@
"""Implements the Foreign Table Module."""
import sys
import traceback
import re
from functools import wraps
import json
from flask import render_template, make_response, request, jsonify, \
from flask import render_template, request, jsonify, \
current_app
from flask_babel import gettext
@@ -33,6 +33,10 @@ from pgadmin.utils.compile_template_name import compile_template_path
from pgadmin.utils.driver import get_driver
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
columns import utils as column_utils
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
triggers import utils as trigger_utils
class ForeignTableModule(SchemaChildModule):
@@ -88,6 +92,10 @@ class ForeignTableModule(SchemaChildModule):
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
constraints import blueprint as module
self.submodules.append(module)
from pgadmin.browser.server_groups.servers.databases.schemas. \
foreign_tables.foreign_table_columns import \
foreign_table_column_blueprint as module
self.submodules.append(module)
super().register(app, options)
@@ -184,6 +192,8 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
node_type = blueprint.node_type
node_label = "Foreign Table"
BASE_TEMPLATE_PATH = 'foreign_tables/sql/#{0}#'
double_newline = '\n\n'
pattern = '\n{2,}'
parent_ids = [
{'type': 'int', 'id': 'gid'},
@@ -216,6 +226,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
'get_foreign_servers': [{'get': 'get_foreign_servers'},
{'get': 'get_foreign_servers'}],
'get_tables': [{'get': 'get_tables'}, {'get': 'get_tables'}],
'set_trigger': [{'put': 'enable_disable_triggers'}],
'get_columns': [{'get': 'get_columns'}, {'get': 'get_columns'}],
'select_sql': [{'get': 'select_sql'}],
'insert_sql': [{'get': 'insert_sql'}],
@@ -398,6 +409,17 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
self.template_path = \
self.BASE_TEMPLATE_PATH.format(self.manager.version)
self.foreign_table_column_template_path = compile_template_path(
'foreign_table_columns/sql', self.manager.version)
self.column_template_path = compile_template_path(
'columns/sql', self.manager.version)
# Template for trigger node
self.trigger_template_path = \
'triggers/sql/{0}/#{1}#'.format(self.manager.server_type,
self.manager.version)
return f(*args, **kwargs)
return wrap
@@ -451,7 +473,9 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
scid,
row['name'],
icon="icon-foreign_table",
description=row['description']
description=row['description'],
tigger_count=row['triggercount'],
has_enable_triggers=row['has_enable_triggers'],
))
return make_json_response(
@@ -911,6 +935,11 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
SQL = sql_header + SQL
trigger_sql = self._get_resql_for_triggers(
foid, data['basensp'], data['name'])
SQL = SQL + trigger_sql
return ajax_response(response=SQL.strip('\n'))
@check_precondition
@@ -1005,9 +1034,9 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
:return:
"""
for c in data['columns']['changed']:
old_col_options = c['attfdwoptions'] = []
if 'attfdwoptions' in c and c['attfdwoptions']:
old_col_options = c['attfdwoptions']
old_col_options = []
if 'coloptions' in c and c['coloptions']:
old_col_options = c['coloptions']
old_col_frmt_options = {}
@@ -1082,7 +1111,13 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
data['is_schema_diff'] = True
old_data['columns_for_schema_diff'] = old_data['columns']
self._format_columns_data(data, old_data)
# If name is not present in request data
if 'name' not in data:
data['name'] = old_data['name']
# If name if not present
if 'schema' not in data:
data['schema'] = old_data['basensp']
# Parse Privileges
ForeignTableView._parse_privileges(data)
@@ -1102,6 +1137,37 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
"/".join([self.template_path, self._UPDATE_SQL]),
data=data, o_data=old_data, conn=self.conn
)
# Removes trailing new lines
if sql:
sql = sql.strip('\n') + self.double_newline
# Parse/Format columns & create sql
if 'columns' in data:
# Parse the data coming from client
data = column_utils.parse_format_columns(data, mode='edit')
columns = data['columns']
column_sql = '\n'
# If column(s) is/are deleted
column_sql = self._check_for_column_delete(columns, data,
column_sql)
# If column(s) is/are changed
column_sql = self._check_for_column_update(columns, data,
column_sql, foid)
# If column(s) is/are added
column_sql = self._check_for_column_add(columns, data,
column_sql)
# Combine all the SQL together
sql += column_sql.strip('\n')
sql = re.sub(self.pattern, self.double_newline, sql)
sql = sql.strip('\n')
return sql, data['name'] if 'name' in data else old_data['name']
else:
data['columns'] = self._format_columns(data['columns'])
@@ -1116,6 +1182,84 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
conn=self.conn)
return sql, data['name']
def _check_for_column_delete(self, columns, data, column_sql):
# If column(s) is/are deleted
if 'deleted' in columns:
for c in columns['deleted']:
c['schema'] = data['schema']
c['table'] = data['name']
# Sql for drop column
if 'inheritedfrom' not in c or \
('inheritedfrom' in c and c['inheritedfrom'] is None):
column_sql += render_template("/".join(
[self.foreign_table_column_template_path,
self._DELETE_SQL]),
data=c, conn=self.conn).strip('\n') + \
self.double_newline
return column_sql
def _check_for_column_update(self, columns, data, column_sql, tid):
# Here we will be needing previous properties of column
# so that we can compare & update it
if 'changed' in columns:
for c in columns['changed']:
c['schema'] = data['schema']
c['table'] = data['name']
properties_sql = render_template(
"/".join([self.column_template_path,
self._PROPERTIES_SQL]),
tid=tid,
clid=c['attnum'] if 'attnum' in c else None,
show_sys_objects=self.blueprint.show_system_objects
)
status, res = self.conn.execute_dict(properties_sql)
if not status:
return internal_server_error(errormsg=res)
old_col_data = res['rows'][0]
old_col_data['cltype'], \
old_col_data['hasSqrBracket'] = \
column_utils.type_formatter(old_col_data['cltype'])
old_col_data = \
column_utils.convert_length_precision_to_string(
old_col_data)
old_col_data = column_utils.fetch_length_precision(
old_col_data)
old_col_data['cltype'] = \
DataTypeReader.parse_type_name(
old_col_data['cltype'])
# Sql for alter column
if 'inheritedfrom' not in c and \
'inheritedfromtable' not in c:
column_sql += render_template("/".join(
[self.foreign_table_column_template_path,
self._UPDATE_SQL]),
data=c, o_data=old_col_data, conn=self.conn
).strip('\n') + self.double_newline
return column_sql
def _check_for_column_add(self, columns, data, column_sql):
# If column(s) is/are added
if 'added' in columns:
for c in columns['added']:
c['schema'] = data['schema']
c['table'] = data['name']
c = column_utils.convert_length_precision_to_string(c)
if 'inheritedfrom' not in c and \
'inheritedfromtable' not in c:
column_sql += render_template("/".join(
[self.foreign_table_column_template_path,
self._CREATE_SQL]),
data=c, conn=self.conn).strip('\n') + \
self.double_newline
return column_sql
@check_precondition
def dependents(self, gid, sid, did, scid, foid):
"""
@@ -1160,15 +1304,14 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
"""
cols = []
for c in columns:
if len(c) > 0:
if '[]' in c['datatype']:
c['datatype'] = c['datatype'].replace('[]', '')
if len(c) > 0 and 'cltype' in c:
if '[]' in c['cltype']:
c['cltype'] = c['cltype'].replace('[]', '')
c['isArrayType'] = True
else:
c['isArrayType'] = False
cols.append(c)
return cols
return cols if cols else columns
def _fetch_properties(self, gid, sid, did, scid, foid, inherits=False, ):
"""
@@ -1238,18 +1381,52 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
if not status:
return False, internal_server_error(errormsg=cols)
self._get_datatype_precision(cols)
# Fetch length and precision data
for col in cols['rows']:
column_utils.fetch_length_precision(col)
if 'attoptions' in col and col['attoptions'] != '':
col['attoptions'] = column_utils.parse_column_variables(
col['attoptions'])
if 'attfdwoptions' in col and col['attfdwoptions'] != '':
col['coloptions'] = column_utils.parse_options_for_column(
col['attfdwoptions'])
self._get_edit_types(cols['rows'])
if cols and 'rows' in cols:
data['columns'] = cols['rows']
# Get Inherited table names from their OID
is_error, errmsg = self._get_inherited_table_name(data, inherits)
if is_error:
return False, internal_server_error(errormsg=errmsg)
return True, data
def _get_edit_types(self, cols):
edit_types = {}
for col in cols:
edit_types[col['atttypid']] = []
if len(cols) > 0:
SQL = render_template("/".join([self.template_path,
'edit_mode_types_multi.sql']),
type_ids=",".join(map(lambda x: str(x),
edit_types.keys())))
status, res = self.conn.execute_2darray(SQL)
for row in res['rows']:
edit_types[row['main_oid']] = sorted(row['edit_types'])
for column in cols:
edit_type_list = edit_types[column['atttypid']]
edit_type_list.append(column['fulltype'])
column['edit_types'] = sorted(edit_type_list)
column['cltype'] = \
DataTypeReader.parse_type_name(column['cltype'])
def _get_datatype_precision(self, cols):
"""
The Length and the precision of the Datatype should be separated.
@@ -1262,11 +1439,11 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
substr = self.extract_type_length_precision(c)
typlen = substr.split(",")
if len(typlen) > 1:
c['typlen'] = self.convert_typlen_to_int(typlen)
c['precision'] = self.convert_precision_to_int(typlen)
c['attlen'] = self.convert_typlen_to_int(typlen)
c['attprecision'] = self.convert_precision_to_int(typlen)
else:
c['typlen'] = self.convert_typlen_to_int(typlen)
c['precision'] = None
c['attlen'] = self.convert_typlen_to_int(typlen)
c['attprecision'] = None
# Get formatted Column Options
if 'attfdwoptions' in c and c['attfdwoptions'] != '':
@@ -1393,7 +1570,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
columns = []
for c in data['columns']:
columns.append(self.qtIdent(self.conn, c['attname']))
columns.append(self.qtIdent(self.conn, c['name']))
if len(columns) > 0:
columns = ", ".join(columns)
@@ -1434,7 +1611,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
# Now we have all list of columns which we need
if 'columns' in data:
for c in data['columns']:
columns.append(self.qtIdent(self.conn, c['attname']))
columns.append(self.qtIdent(self.conn, c['name']))
values.append('?')
if len(columns) > 0:
@@ -1475,7 +1652,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
# Now we have all list of columns which we need
if 'columns' in data:
for c in data['columns']:
columns.append(self.qtIdent(self.conn, c['attname']))
columns.append(self.qtIdent(self.conn, c['name']))
if len(columns) > 0:
if len(columns) == 1:
@@ -1676,6 +1853,92 @@ class ForeignTableView(PGChildNodeView, DataTypeReader,
data['ftoptions'] = tmp_ftoptions
@check_precondition
def enable_disable_triggers(self, gid, sid, did, scid, foid):
"""
This function will enable/disable trigger(s) on the table object
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
scid: Schema ID
tid: Table ID
"""
# Below will decide if it's simple drop or drop with cascade call
data = request.form if request.form else json.loads(
request.data
)
# Convert str 'true' to boolean type
is_enable_trigger = data['is_enable_trigger']
try:
if foid is not None:
status, data = self._fetch_properties(
gid, sid, did, scid, foid, inherits=True)
if not status:
return data
elif not data:
return gone(self.not_found_error_msg())
SQL = render_template(
"/".join([self.template_path, 'enable_disable_trigger.sql']),
data=data, is_enable_trigger=is_enable_trigger
)
status, res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
SQL = render_template(
"/".join([self.template_path, 'get_enabled_triggers.sql']),
tid=foid
)
status, trigger_res = self.conn.execute_scalar(SQL)
if not status:
return internal_server_error(errormsg=res)
return make_json_response(
success=1,
info=gettext("Trigger(s) have been disabled")
if is_enable_trigger == 'D'
else gettext("Trigger(s) have been enabled"),
data={
'id': foid,
'scid': scid,
'has_enable_triggers': trigger_res
}
)
except Exception as e:
return internal_server_error(errormsg=str(e))
def _get_resql_for_triggers(self, tid, schema,
table):
"""
########################################
# Reverse engineered sql for TRIGGERS
########################################
"""
sql = render_template("/".join([self.trigger_template_path,
self._NODES_SQL]), tid=tid)
status, rset = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=rset)
trigger_sql = ''
for row in rset['rows']:
trigger_sql = trigger_utils.get_reverse_engineered_sql(
self.conn, schema=schema, table=table, tid=tid,
trid=row['oid'], datlastsysoid=self._DATABASE_LAST_SYSTEM_OID,
show_system_objects=self.blueprint.show_system_objects,
template_path=None)
trigger_sql = "\n" + trigger_sql
trigger_sql = re.sub(self.pattern, self.double_newline,
trigger_sql)
return trigger_sql
SchemaDiffRegistry(blueprint.node_type, ForeignTableView)
ForeignTableView.register_node_view(blueprint)
@@ -5,9 +5,10 @@ foreign tables.
Do not remove these imports as they will be automatically imported by the view
module as its children
"""
from pgadmin.browser.server_groups.servers.databases.schemas.tables.columns \
import blueprint as columns_module
from pgadmin.browser.server_groups.servers.databases.schemas.tables.triggers \
import blueprint as triggers_modules
from pgadmin.browser.server_groups.servers.databases.schemas.tables.\
constraints import blueprint as constraints_modules
from pgadmin.browser.server_groups.servers.databases.schemas.\
foreign_tables.foreign_table_columns import foreign_table_column_blueprint\
as foreign_tables_columns_modules
@@ -0,0 +1,97 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
""" Implements Column Node for foreign table """
import pgadmin.browser.server_groups.servers.databases as database
from flask_babel import gettext
from pgadmin.browser.collection import CollectionNodeModule
from pgadmin.browser.server_groups.servers.databases.schemas.tables.columns \
import ColumnsView
class ForeignTableColumnsModule(CollectionNodeModule):
"""
class ColumnsModule(CollectionNodeModule)
A module class for Column node derived from CollectionNodeModule.
Methods:
-------
* __init__(*args, **kwargs)
- Method is used to initialize the Column and it's base module.
* get_nodes(gid, sid, did, scid, tid)
- Method is used to generate the browser collection node.
* node_inode()
- Method is overridden from its base class to make the node as leaf node.
* script_load()
- Load the module script for schema, when any of the server node is
initialized.
"""
_NODE_TYPE = 'foreign_table_column'
_COLLECTION_LABEL = gettext("Columns")
def __init__(self, *args, **kwargs):
"""
Method is used to initialize the ColumnModule and it's base module.
Args:
*args:
**kwargs:
"""
self.min_ver = None
self.max_ver = None
super().__init__(*args, **kwargs)
def get_nodes(self, gid, sid, did, scid, foid, **kwargs):
"""
Generate the collection node
"""
if self.has_nodes(
sid, did, scid=scid, tid=foid,
base_template_path=ForeignTableColumnsView.BASE_TEMPLATE_PATH):
yield self.generate_browser_collection_node(foid)
@property
def script_load(self):
"""
Load the module script for server, when any of the server-group node is
initialized.
"""
return database.DatabaseModule.node_type
@property
def node_inode(self):
"""
Load the module node as a leaf node
"""
return False
@property
def module_use_template_javascript(self):
"""
Returns whether Jinja2 template is used for generating the javascript
module.
"""
return False
foreign_table_column_blueprint = ForeignTableColumnsModule(__name__)
class ForeignTableColumnsView(ColumnsView):
node_type = foreign_table_column_blueprint.node_type
node_label = "Column"
ForeignTableColumnsView.register_node_view(foreign_table_column_blueprint)
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#e1fff9;}.cls-2{fill:none;stroke:#aecec8;stroke-linejoin:round;stroke-width:0.75px;}.cls-3{fill:#16a085;}</style></defs><title>coll-column</title><g id="_2" data-name="2"><rect class="cls-1" x="4.15" y="1.95" width="5.5" height="9.9" rx="1" ry="1"/><line class="cls-2" x1="4.7" y1="6.9" x2="9.1" y2="6.9"/><line class="cls-2" x1="4.7" y1="9.1" x2="9.1" y2="9.1"/><path class="cls-3" d="M4.9,5.18l4,0v5.55a.35.35,0,0,1-.35.35H5.25a.35.35,0,0,1-.35-.35V5.18m-.75-.75v6.32a1.1,1.1,0,0,0,1.1,1.1h3.3a1.1,1.1,0,0,0,1.1-1.1V4.46l-5.5,0Z"/><path class="cls-3" d="M8.55,2.7A.35.35,0,0,1,8.9,3V4.5h-4V3a.35.35,0,0,1,.35-.35h3.3m0-.75H5.25A1.1,1.1,0,0,0,4.15,3v2.2h5.5V3A1.1,1.1,0,0,0,8.55,2Z"/><rect class="cls-1" x="6.35" y="4.15" width="5.5" height="9.9" rx="1" ry="1"/><line class="cls-2" x1="6.9" y1="9.1" x2="11.3" y2="9.1"/><line class="cls-2" x1="6.9" y1="11.3" x2="11.3" y2="11.3"/><path class="cls-3" d="M7.1,7.38l4,0V13a.35.35,0,0,1-.35.35H7.45A.35.35,0,0,1,7.1,13V7.38m-.75-.75V13A1.1,1.1,0,0,0,7.45,14h3.3a1.1,1.1,0,0,0,1.1-1.1V6.66l-5.5,0Z"/><path class="cls-3" d="M10.75,4.9a.35.35,0,0,1,.35.35V6.7h-4V5.25a.35.35,0,0,1,.35-.35h3.3m0-.75H7.45a1.1,1.1,0,0,0-1.1,1.1v2.2h5.5V5.25a1.1,1.1,0,0,0-1.1-1.1Z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="20" height="20"><defs><style>.cls-1{fill:#e1fff9;}.cls-2{fill:none;stroke:#aecec8;stroke-linejoin:round;stroke-width:0.75px;}.cls-3{fill:#16a085;}</style></defs><title>column</title><g id="_2" data-name="2"><rect class="cls-1" x="5.3" y="2.15" width="5.4" height="11.7" rx="1" ry="1"/><line class="cls-2" x1="5.75" y1="8" x2="10.25" y2="8"/><line class="cls-2" x1="5.75" y1="10.7" x2="10.25" y2="10.7"/><path class="cls-3" d="M6,5.83l3.9,0V13a.15.15,0,0,1-.15.15H6.2A.15.15,0,0,1,6,13V5.83M5.3,5.08V13a.9.9,0,0,0,.9.9H9.8a.9.9,0,0,0,.9-.9V5.1l-5.4,0Z"/><path class="cls-3" d="M9.8,2.9A.15.15,0,0,1,10,3V5H6V3A.15.15,0,0,1,6.2,2.9H9.8m0-.75H6.2a.9.9,0,0,0-.9.9v2.7h5.4V3a.9.9,0,0,0-.9-.9Z"/></g></svg>

After

Width:  |  Height:  |  Size: 768 B

@@ -0,0 +1,70 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import { getNodeColumnSchema } from '../../../static/js/foreign_table.ui';
define('pgadmin.node.foreign_table_column', [
'sources/gettext', 'sources/url_for', 'pgadmin.browser',
'pgadmin.node.schema.dir/schema_child_tree_node', 'pgadmin.browser.collection',
], function(
gettext, url_for, pgBrowser
) {
if (!pgBrowser.Nodes['coll-foreign_table_column']) {
pgBrowser.Nodes['coll-foreign_table_column'] =
pgBrowser.Collection.extend({
node: 'foreign_table_column',
label: gettext('Columns'),
type: 'coll-foreign_table_column',
columns: ['name', 'cltype', 'description']
});
}
if (!pgBrowser.Nodes['foreign_table_column']) {
pgBrowser.Nodes['foreign_table_column'] = pgBrowser.Node.extend({
// Foreign table is added in parent_type to support triggers on
// foreign table where we need column information.
parent_type: ['foreign_table'],
collection_type: ['coll-foreign_table'],
type: 'foreign_table_column',
label: gettext('Column'),
hasSQL: true,
sqlAlterHelp: 'sql-altertable.html',
sqlCreateHelp: 'sql-altertable.html',
dialogHelp: url_for('help.static', {'filename': 'column_dialog.html'}),
canDrop: true,
canDropCascade: false,
hasDepends: true,
hasStatistics: true,
Init: function() {
/* Avoid multiple registration of menus */
if (this.initialized)
return;
this.initialized = true;
pgBrowser.add_menus([{
name: 'create_column_on_coll', node: 'coll-foreign_table_column', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('Column...'),
data: {action: 'create'}, enable: 'canCreate',
},{
name: 'create_column_onTable', node: 'foreign_table', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('Column...'),
data: {action: 'create'}, enable: 'canCreate',
},
]);
},
getSchema: function(treeNodeInfo, itemNodeData) {
return getNodeColumnSchema(treeNodeInfo, itemNodeData, pgBrowser);
},
});
}
return pgBrowser.Nodes['foreign_table_column'];
});
@@ -0,0 +1,16 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
from pgadmin.utils.route import BaseTestGenerator
class ForeignTableGeneratorTestCase(BaseTestGenerator):
def runTest(self):
return []
@@ -0,0 +1,154 @@
{
"column_create": [
{
"name": "Create: Add FT column with valid data",
"is_positive_test": true,
"inventory_data": {},
"test_data": {
"name": "test_ft_column_add_",
"description": "Comment for create",
"cltype": "numeric",
"attlen":"10",
"attprecision":"5",
"coloptions":[],
"colconstype":"n",
"attnotnull": true,
"attoptions": [],
"seclabels": [],
"defval": "1"
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
},
{
"name": "Create: Add FT column with valid data while server down",
"is_positive_test": false,
"inventory_data": {},
"test_data": {
"name": "test_column_add_",
"cltype": "\"char\"",
"attacl": [],
"is_primary_key": false,
"attnotnull": false,
"attlen": null,
"attprecision": null,
"attoptions": [],
"seclabels": [],
"description": {
"comment": "jsoncomment"
}
},
"mocking_required": true,
"mock_data": {
"function_name": "pgadmin.utils.driver.psycopg3.connection.Connection.execute_scalar",
"return_value": "[(False, 'Mocked Internal Server Error'),(True,True)]"
},
"expected_data": {
"status_code": 500,
"error_msg": "Mocked Internal Server Error",
"test_result_data": {}
}
}
],
"column_delete": [
{
"name": "Delete: Existing FT column",
"is_positive_test": true,
"inventory_data": {},
"test_data": {},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
},
"is_list": false
},
{
"name": "Delete: Multiple existing FT column",
"is_positive_test": true,
"inventory_data": {},
"test_data": {},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
},
"is_list": true
},
{
"name": "Delete: Non-existing FT column",
"is_positive_test": false,
"inventory_data": {},
"test_data": {
"column_id": 9999999
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": "Error: Object not found.",
"test_result_data": {}
},
"is_list": false
},
{
"name": "Delete: Existing FT column while server down",
"is_positive_test": false,
"inventory_data": {},
"test_data": {},
"mocking_required": true,
"mock_data": {
"function_name": "pgadmin.utils.driver.psycopg3.connection.Connection.execute_dict",
"return_value": "[(False,'Mocked Internal Server Error')]"
},
"expected_data": {
"status_code": 500,
"error_msg": "Mocked Internal Server Error",
"test_result_data": {}
},
"is_list": false
}
],
"column_get": [
{
"name": "Get details: For existing FT column",
"is_positive_test": true,
"inventory_data": {},
"test_data": {},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
},
"is_list": false
},
{
"name": "Get details: For existing FT column while server down",
"is_positive_test": false,
"inventory_data": {},
"test_data": {},
"mocking_required": true,
"mock_data": {
"function_name": "pgadmin.utils.driver.psycopg3.connection.Connection.execute_dict",
"return_value": "[(False,'Mocked Internal Server Error')]"
},
"expected_data": {
"status_code": 500,
"error_msg": "Mocked Internal Server Error",
"test_result_data": {}
},
"is_list": false
}
]
}
@@ -0,0 +1,8 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_3_$%{}[]()&*^!@""'`\/#" TYPE time(10) with time zone ;
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_3_$%{}[]()&*^!@""'`\/#" DROP NOT NULL;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment modification';
@@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_3_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_3_$%{}[]()&*^!@""'`\/#" time(6) with time zone DEFAULT now();
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment modification';
@@ -0,0 +1,5 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#" OPTIONS (SET column_name 'test_options_update');
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=111);
@@ -0,0 +1,13 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_4_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_4_$%{}[]()&*^!@""'`\/#" bigint OPTIONS (column_name 'test_options_update') NOT NULL GENERATED ALWAYS AS ((1000 + 1)) STORED;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=111);
@@ -0,0 +1,5 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_3_$%{}[]()&*^!@""'`\/#" time with time zone NOT NULL DEFAULT now();
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
@@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_3_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_3_$%{}[]()&*^!@""'`\/#" time with time zone NOT NULL DEFAULT now();
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
@@ -0,0 +1,9 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_4_$%{}[]()&*^!@""'`\/#" bigint OPTIONS (column_name 'test_options') NOT NULL GENERATED ALWAYS AS (1000+1) STORED;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=1);
@@ -0,0 +1,13 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_4_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_4_$%{}[]()&*^!@""'`\/#" bigint OPTIONS (column_name 'test_options') NOT NULL GENERATED ALWAYS AS ((1000 + 1)) STORED;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=1);
@@ -0,0 +1,213 @@
{
"scenarios": [
{
"type": "create",
"name": "Create FDW for foreign table",
"endpoint": "NODE-foreign_data_wrapper.obj",
"sql_endpoint": "NODE-foreign_data_wrapper.sql_id",
"data": {
"name": "test_fdw_for_foreign_table_columns",
"fdwacl": [],
"fdwoptions": []
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create foreign server for foreign table",
"endpoint": "NODE-foreign_server.obj",
"sql_endpoint": "NODE-foreign_server.sql_id",
"data": {
"name":"test_fs_for_foreign_table_column"
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create Foreign Table for testing column node.",
"endpoint": "NODE-foreign_table.obj",
"sql_endpoint": "NODE-foreign_table.sql_id",
"data": {
"name": "foreign_table_2_$%{}[]()&*^!@\"'`\\/#",
"columns": [],
"schema": "public",
"basensp": "public",
"ftsrvname": "test_fs_for_foreign_table_column",
"owner": "postgres",
"ftoptions": [],
"inherits": [],
"relacl": [],
"seclabels": []
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_1_$%{}[]()&*^!@\"'`\\/#",
"description": "Comment for create",
"cltype": "numeric",
"attlen":"10",
"attprecision":"5",
"colconstype":"n",
"attnotnull": true,
"attoptions": [],
"seclabels": [],
"defval": "1"
},
"expected_sql_file": "create_column_numeric.sql",
"expected_msql_file": "create_column_numeric.msql"
},
{
"type": "alter",
"name": "Alter FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attnum": 1,
"name": "new_col_1_$%{}[]()&*^!@\"'`\\/#",
"description": "Comment for alter",
"cltype": "real",
"coloptions":{"added":[{"option":"column_name","value":"test"}]}
},
"expected_sql_file": "alter_column_numeric.sql",
"expected_msql_file": "alter_column_numeric.msql"
},
{
"type": "delete",
"name": "Drop FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "new_col_1_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create FT Column with text & default value",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_2_$%{}[]()&*^!@\"'`\\/#",
"cltype": "text",
"description": "test comment",
"attnotnull": true,
"colconstype": "n",
"defval": "'xyz'"
},
"expected_sql_file": "create_column_text_with_default_value.sql",
"expected_msql_file": "create_column_text_with_default_value.msql"
},
{
"type": "alter",
"name": "Alter FT Column with text & update default value",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attnum": 2,
"defval": "'changed default value'"
},
"expected_sql_file": "alter_column_text_with_default_value.sql",
"expected_msql_file": "alter_column_text_with_default_value.msql"
},
{
"type": "delete",
"name": "Drop FT column with text & update default value",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "col_2_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create FT Column with time with time zone & default value using function",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_3_$%{}[]()&*^!@\"'`\\/#",
"cltype": "time with time zone",
"attacl": [],
"description": "test comment",
"attnotnull": true,
"colconstype": "n",
"defval": "now()"
},
"expected_sql_file": "create_column_timestamp_with_default_value_using_function.sql",
"expected_msql_file": "create_column_timestamp_with_default_value_using_function.msql"
},
{
"type": "alter",
"name": "Alter FT Column with time with time zone & update length",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attlen": "10",
"attnotnull": false,
"attnum":3,
"description": "test comment modification"
},
"expected_sql_file": "alter_column_timestamp_with_default_value_using_function.sql",
"expected_msql_file": "alter_column_timestamp_with_default_value_using_function.msql"
},
{
"type": "delete",
"name": "Drop FT Column with time with time zone",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "col_3_$%{}[]()&*^!@\"'`\\/"
}
},
{
"type": "create",
"name": "Create FT Column with integer & generated always with expression, column option, variables",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {"name":"col_4_$%{}[]()&*^!@\"'`\\/#",
"description":"test comment",
"cltype":"bigint",
"attnotnull":true,
"coloptions":[{"option":"column_name","value":"test_options"}],
"colconstype":"g",
"is_tlength":false,
"is_precision":false,
"genexpr":"1000+1",
"attoptions":[{"name":"n_distinct","value":"1"}]},
"expected_sql_file": "create_column_with_integer_generated_always_column_option_variables.sql",
"expected_msql_file": "create_column_with_integer_generated_always_column_option_variables.msql"
},
{
"type": "alter",
"name": "Alter FT Column with integer & generated always with expression, column option, variables",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data":{
"attoptions":{
"changed":[{"name":"n_distinct","value":"111"}]},
"coloptions":{"changed":[{"option":"column_name","value":"test_options_update"}]},
"attnum":4},
"expected_sql_file": "alter_column_with_integer_generated_always_column_option_variables.sql",
"expected_msql_file": "alter_column_with_integer_generated_always_column_option_variables.msql"
},
{
"type": "delete",
"name": "Drop FT Column with time with time zone",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_columnn.sql_id",
"data": {
"name":"col_4_$%{}[]()&*^!@\"'`\\/#"
}
}
]}
@@ -0,0 +1,10 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
RENAME "col_1_$%{}[]()&*^!@""'`\/#" TO "new_col_1_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "new_col_1_$%{}[]()&*^!@""'`\/#" OPTIONS (ADD column_name 'test');
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "new_col_1_$%{}[]()&*^!@""'`\/#" TYPE real;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."new_col_1_$%{}[]()&*^!@""'`\/#"
IS 'Comment for alter';
@@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."new_col_1_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "new_col_1_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "new_col_1_$%{}[]()&*^!@""'`\/#" real OPTIONS (column_name 'test') NOT NULL DEFAULT 1;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."new_col_1_$%{}[]()&*^!@""'`\/#"
IS 'Comment for alter';
@@ -0,0 +1,2 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_2_$%{}[]()&*^!@""'`\/#" SET DEFAULT 'changed default value';
@@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_2_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_2_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_2_$%{}[]()&*^!@""'`\/#" text COLLATE pg_catalog."default" NOT NULL DEFAULT 'changed default value'::text;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_2_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
@@ -0,0 +1,5 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_1_$%{}[]()&*^!@""'`\/#" numeric(10, 5) NOT NULL DEFAULT 1;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_1_$%{}[]()&*^!@""'`\/#"
IS 'Comment for create';
@@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_1_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_1_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_1_$%{}[]()&*^!@""'`\/#" numeric(10,5) NOT NULL DEFAULT 1;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_1_$%{}[]()&*^!@""'`\/#"
IS 'Comment for create';
@@ -0,0 +1,5 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_2_$%{}[]()&*^!@""'`\/#" text NOT NULL DEFAULT 'xyz';
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_2_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
@@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_2_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_2_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_2_$%{}[]()&*^!@""'`\/#" text COLLATE pg_catalog."default" NOT NULL DEFAULT 'xyz'::text;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_2_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
@@ -0,0 +1,130 @@
{
"scenarios": [
{
"type": "create",
"name": "Create FDW for foreign table",
"endpoint": "NODE-foreign_data_wrapper.obj",
"sql_endpoint": "NODE-foreign_data_wrapper.sql_id",
"data": {
"name": "test_fdw_for_foreign_table_columns",
"fdwacl": [],
"fdwoptions": []
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create foreign server for foreign table",
"endpoint": "NODE-foreign_server.obj",
"sql_endpoint": "NODE-foreign_server.sql_id",
"data": {
"name":"test_fs_for_foreign_table_column"
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create Foreign Table for testing column node.",
"endpoint": "NODE-foreign_table.obj",
"sql_endpoint": "NODE-foreign_table.sql_id",
"data": {
"name": "foreign_table_2_$%{}[]()&*^!@\"'`\\/#",
"columns": [],
"schema": "public",
"basensp": "public",
"ftsrvname": "test_fs_for_foreign_table_column",
"owner": "postgres",
"ftoptions": [],
"inherits": [],
"relacl": [],
"seclabels": []
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_1_$%{}[]()&*^!@\"'`\\/#",
"description": "Comment for create",
"cltype": "numeric",
"attlen":"10",
"attprecision":"5",
"coloptions":[],
"colconstype":"n",
"attnotnull": true,
"attoptions": [],
"seclabels": [],
"defval": "1"
},
"expected_sql_file": "create_column_numeric.sql",
"expected_msql_file": "create_column_numeric.msql"
},
{
"type": "alter",
"name": "Alter FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attnum": 1,
"name": "new_col_1_$%{}[]()&*^!@\"'`\\/#",
"description": "Comment for alter",
"cltype": "real",
"coloptions":{"added":[{"option":"column_name","value":"test"}]}
},
"expected_sql_file": "alter_column_numeric.sql",
"expected_msql_file": "alter_column_numeric.msql"
},
{
"type": "delete",
"name": "Drop FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "new_col_1_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create FT Column with text & default value",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_2_$%{}[]()&*^!@\"'`\\/#",
"cltype": "text",
"description": "test comment",
"attnotnull": true,
"colconstype": "n",
"defval": "'xyz'"
},
"expected_sql_file": "create_column_text_with_default_value.sql",
"expected_msql_file": "create_column_text_with_default_value.msql"
},
{
"type": "alter",
"name": "Alter FT Column with text & update default value",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attnum": 2,
"defval": "'changed default value'"
},
"expected_sql_file": "alter_column_text_with_default_value.sql",
"expected_msql_file": "alter_column_text_with_default_value.msql"
},
{
"type": "delete",
"name": "Drop FT column with text & update default value",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "col_2_$%{}[]()&*^!@\"'`\\/#"
}
}
]}
@@ -0,0 +1,8 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_3_$%{}[]()&*^!@""'`\/#" TYPE time(10) with time zone ;
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_3_$%{}[]()&*^!@""'`\/#" DROP NOT NULL;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment modification';
@@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_3_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_3_$%{}[]()&*^!@""'`\/#" time(6) with time zone DEFAULT now();
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment modification';
@@ -0,0 +1,5 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#" OPTIONS (SET column_name 'test_options_update');
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=111);
@@ -0,0 +1,13 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_4_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_4_$%{}[]()&*^!@""'`\/#" bigint OPTIONS (column_name 'test_options_update') NOT NULL GENERATED ALWAYS AS ((1000 + 1)) STORED;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=111);
@@ -0,0 +1,5 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_3_$%{}[]()&*^!@""'`\/#" time with time zone NOT NULL DEFAULT now();
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
@@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_3_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_3_$%{}[]()&*^!@""'`\/#" time with time zone NOT NULL DEFAULT now();
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_3_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
@@ -0,0 +1,9 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_4_$%{}[]()&*^!@""'`\/#" bigint OPTIONS (column_name 'test_options') NOT NULL GENERATED ALWAYS AS (1000+1) STORED;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=1);
@@ -0,0 +1,13 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "col_4_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "col_4_$%{}[]()&*^!@""'`\/#" bigint OPTIONS (column_name 'test_options') NOT NULL GENERATED ALWAYS AS ((1000 + 1)) STORED;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."col_4_$%{}[]()&*^!@""'`\/#"
IS 'test comment';
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "col_4_$%{}[]()&*^!@""'`\/#"
SET (n_distinct=1);
@@ -0,0 +1,213 @@
{
"scenarios": [
{
"type": "create",
"name": "Create FDW for foreign table",
"endpoint": "NODE-foreign_data_wrapper.obj",
"sql_endpoint": "NODE-foreign_data_wrapper.sql_id",
"data": {
"name": "test_fdw_for_foreign_table_columns",
"fdwacl": [],
"fdwoptions": []
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create foreign server for foreign table",
"endpoint": "NODE-foreign_server.obj",
"sql_endpoint": "NODE-foreign_server.sql_id",
"data": {
"name":"test_fs_for_foreign_table_column"
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create Foreign Table for testing column node.",
"endpoint": "NODE-foreign_table.obj",
"sql_endpoint": "NODE-foreign_table.sql_id",
"data": {
"name": "foreign_table_2_$%{}[]()&*^!@\"'`\\/#",
"columns": [],
"schema": "public",
"basensp": "public",
"ftsrvname": "test_fs_for_foreign_table_column",
"owner": "enterprisedb",
"ftoptions": [],
"inherits": [],
"relacl": [],
"seclabels": []
},
"store_object_id": "True"
},
{
"type": "create",
"name": "Create FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_1_$%{}[]()&*^!@\"'`\\/#",
"description": "Comment for create",
"cltype": "numeric",
"attlen":"10",
"attprecision":"5",
"colconstype":"n",
"attnotnull": true,
"attoptions": [],
"seclabels": [],
"defval": "1"
},
"expected_sql_file": "create_column_numeric.sql",
"expected_msql_file": "create_column_numeric.msql"
},
{
"type": "alter",
"name": "Alter FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attnum": 1,
"name": "new_col_1_$%{}[]()&*^!@\"'`\\/#",
"description": "Comment for alter",
"cltype": "real",
"coloptions":{"added":[{"option":"column_name","value":"test"}]}
},
"expected_sql_file": "alter_column_numeric.sql",
"expected_msql_file": "alter_column_numeric.msql"
},
{
"type": "delete",
"name": "Drop FT Column (Integer/Numeric type)",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "new_col_1_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create FT Column with text & default value",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_2_$%{}[]()&*^!@\"'`\\/#",
"cltype": "text",
"description": "test comment",
"attnotnull": true,
"colconstype": "n",
"defval": "'xyz'"
},
"expected_sql_file": "create_column_text_with_default_value.sql",
"expected_msql_file": "create_column_text_with_default_value.msql"
},
{
"type": "alter",
"name": "Alter FT Column with text & update default value",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attnum": 2,
"defval": "'changed default value'"
},
"expected_sql_file": "alter_column_text_with_default_value.sql",
"expected_msql_file": "alter_column_text_with_default_value.msql"
},
{
"type": "delete",
"name": "Drop FT column with text & update default value",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "col_2_$%{}[]()&*^!@\"'`\\/#"
}
},
{
"type": "create",
"name": "Create FT Column with time with time zone & default value using function",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {
"name": "col_3_$%{}[]()&*^!@\"'`\\/#",
"cltype": "time with time zone",
"attacl": [],
"description": "test comment",
"attnotnull": true,
"colconstype": "n",
"defval": "now()"
},
"expected_sql_file": "create_column_timestamp_with_default_value_using_function.sql",
"expected_msql_file": "create_column_timestamp_with_default_value_using_function.msql"
},
{
"type": "alter",
"name": "Alter FT Column with time with time zone & update length",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data": {
"attlen": "10",
"attnotnull": false,
"attnum":3,
"description": "test comment modification"
},
"expected_sql_file": "alter_column_timestamp_with_default_value_using_function.sql",
"expected_msql_file": "alter_column_timestamp_with_default_value_using_function.msql"
},
{
"type": "delete",
"name": "Drop FT Column with time with time zone",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"data": {
"name": "col_3_$%{}[]()&*^!@\"'`\\/"
}
},
{
"type": "create",
"name": "Create FT Column with integer & generated always with expression, column option, variables",
"endpoint": "NODE-foreign_table_column.obj",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql",
"data": {"name":"col_4_$%{}[]()&*^!@\"'`\\/#",
"description":"test comment",
"cltype":"bigint",
"attnotnull":true,
"coloptions":[{"option":"column_name","value":"test_options"}],
"colconstype":"g",
"is_tlength":false,
"is_precision":false,
"genexpr":"1000+1",
"attoptions":[{"name":"n_distinct","value":"1"}]},
"expected_sql_file": "create_column_with_integer_generated_always_column_option_variables.sql",
"expected_msql_file": "create_column_with_integer_generated_always_column_option_variables.msql"
},
{
"type": "alter",
"name": "Alter FT Column with integer & generated always with expression, column option, variables",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_column.sql_id",
"msql_endpoint": "NODE-foreign_table_column.msql_id",
"data":{
"attoptions":{
"changed":[{"name":"n_distinct","value":"111"}]},
"coloptions":{"changed":[{"option":"column_name","value":"test_options_update"}]},
"attnum":4},
"expected_sql_file": "alter_column_with_integer_generated_always_column_option_variables.sql",
"expected_msql_file": "alter_column_with_integer_generated_always_column_option_variables.msql"
},
{
"type": "delete",
"name": "Drop FT Column with time with time zone",
"endpoint": "NODE-foreign_table_column.obj_id",
"sql_endpoint": "NODE-foreign_table_columnn.sql_id",
"data": {
"name":"col_4_$%{}[]()&*^!@\"'`\\/#"
}
}
]}
@@ -0,0 +1,10 @@
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
RENAME "col_1_$%{}[]()&*^!@""'`\/#" TO "new_col_1_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "new_col_1_$%{}[]()&*^!@""'`\/#" OPTIONS (ADD column_name 'test');
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ALTER COLUMN "new_col_1_$%{}[]()&*^!@""'`\/#" TYPE real;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."new_col_1_$%{}[]()&*^!@""'`\/#"
IS 'Comment for alter';
@@ -0,0 +1,9 @@
-- Column: public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."new_col_1_$%{}[]()&*^!@""'`\/#"
-- ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#" DROP COLUMN IF EXISTS "new_col_1_$%{}[]()&*^!@""'`\/#";
ALTER FOREIGN TABLE IF EXISTS public."foreign_table_2_$%{}[]()&*^!@""'`\/#"
ADD COLUMN "new_col_1_$%{}[]()&*^!@""'`\/#" real OPTIONS (column_name 'test') NOT NULL DEFAULT 1;
COMMENT ON COLUMN public."foreign_table_2_$%{}[]()&*^!@""'`\/#"."new_col_1_$%{}[]()&*^!@""'`\/#"
IS 'Comment for alter';
Loaded 100 of 409 files, more files were not shown because too many files have changed in this diff. Show more