Compare commits

..
84 Commits
Author SHA1 Message Date
Akshay Joshi 99c688f894 Update version for release 8.6 2024-04-29 13:42:28 +05:30
Khushboo Vashi f4761f55f7 Fixed Multi-Factor Authentication bypass vulnerability (CVE-2024-4215). #7425 2024-04-29 13:41:02 +05:30
Pravesh Sharma e18a8bf620 Fixed issue found during testing of #7163. 2024-04-29 13:12:21 +05:30
Pravesh Sharma 7c6fdcb12e Disabled auto-fill on password fields. #2410 2024-04-29 11:44:17 +05:30
Khushboo Vashi f88c89f3eb Fixed an issue when closing the materialized view data second tab; it raises the error that the 'MViewCommand' object has no attribute 'auto_commit'. 2024-04-26 15:53:15 +05:30
Domenico Sgarbossa 5949696ded Italian translation updated for v8.6 2024-04-26 14:43:17 +05:30
Akshay Joshi f724578463 Fixed API test cases for exec/call procedure. 2024-04-26 13:01:35 +05:30
Khushboo Vashi 2324337149 Trigger the browser tree destroyed event so the consequences of destroying the tree are handled properly. 2024-04-26 11:59:53 +05:30
Akshay Joshi 2dd009eb7c Update message catalogs for version 8.6 2024-04-25 18:03:28 +05:30
Akshay Joshi 92bb931b0a Fixed an issue where incorrect select/exec scripts were generated for functions/procedures. #7334 2024-04-25 17:24:02 +05:30
Khushboo Vashi eeee45e57c For some Debian platforms, the python3-dbus package is available instead of python-dbus. 2024-04-25 15:02:50 +05:30
Khushboo Vashi 10ba029980 Fixed an issue where connection to the database is not automatically re-established after connectivity drop. #7372 2024-04-24 14:37:33 +05:30
Pravesh Sharma fd65f1b644 Fixed all input boxes in pgAdmin to show browser auto-fill only where it is relevant. #2410 2024-04-24 11:27:08 +05:30
Akshay Joshi 9f6ddd3cf9 Fixed violates check constraint issue when creating a pgAgent schedule. #7390 2024-04-23 17:27:11 +05:30
Yogesh Mahajan 673b5ea5a1 Fix the date-time picker broken after MUI5 upgrade. 2024-04-23 17:03:34 +05:30
Khushboo Vashi 84c024f6fa Fixed an issue when closing the view data second tab; it raises the error that the 'ViewCommand' object has no attribute 'auto_commit'. #7384 2024-04-23 14:45:15 +05:30
Akshay Joshi 3e0ade5a59 Fixed an issue where pressing backspace should remove the spaces and not the entire tab width, on enabling 'Use spaces?' in the preferences. #7317 2024-04-22 12:33:38 +05:30
dependabot[bot] db23330939 Python dependency: Bump selenium in /web/regression (#7325)
Bumps [selenium](https://github.com/SeleniumHQ/Selenium) from 4.10.0 to 4.19.0.
- [Release notes](https://github.com/SeleniumHQ/Selenium/releases)
- [Commits](https://github.com/SeleniumHQ/Selenium/compare/selenium-4.10.0...selenium-4.19.0)

---
updated-dependencies:
- dependency-name: selenium
  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>
2024-04-18 14:03:28 +05:30
Akshay Joshi bb24c445e6 Update yarn.lock in runtime 2024-04-18 13:03:10 +05:30
dependabot[bot] b44b06591b Javascript dependency: Bump eslint from 8.52.0 to 9.0.0 in /runtime (#7361)
Bumps [eslint](https://github.com/eslint/eslint) from 8.52.0 to 9.0.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.52.0...v9.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-18 12:48:45 +05:30
Akshay Joshi b475be93fa Update yarn.lock file 2024-04-18 12:46:00 +05:30
dependabot[bot] 3e47abed8f Python dependency: Bump sphinx from 7.2.6 to 7.3.5 in /tools (#7387)
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 7.2.6 to 7.3.5.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES.rst)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v7.2.6...v7.3.5)

---
updated-dependencies:
- dependency-name: sphinx
  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>
2024-04-18 12:15:30 +05:30
Yogesh Mahajan f0185fbe7f Fix issues reported while testing MUI5 library update. 2024-04-17 17:31:55 +05:30
Khushboo Vashi 5f689a56dd Install dbus-python, an in-direct dependency of the Keyring package as a system package for Debian platforms. #7173 2024-04-17 16:21:50 +05:30
Pravesh Sharma 0d1e3f36e1 Revert "Fixed an issue where function arguments were getting enclosed in double quotes if written in upper case. #7222"
This reverts commit fc4b419987.
2024-04-17 11:33:25 +05:30
Aditya Toshniwal 29ccdb3242 Fixed an issue where debugger was not scrolling automatically on stepping. #7275 2024-04-16 15:33:42 +05:30
Akshay Joshi 0546e79aed Made adjustments to ensure the correct version of Authlib is used. #7362 2024-04-16 12:56:42 +05:30
Pravesh Sharma 5ee403fec7 Upgraded Flask, Flask-Security-Too, Werkzeug, and other dependencies, ensuring compatibility with Python 3.7. #7362 2024-04-16 12:23:03 +05:30
Neville Li 5a3fa59741 Fix id_token KeyError on OAuth2 logout. 2024-04-16 12:19:06 +05:30
Akshay Joshi 60497df04b Updated release note. 2024-04-15 11:54:31 +05:30
Pravesh Sharma f143aa05a3 Added support for excluding multiple tables while taking Backup. #7163 2024-04-15 11:50:08 +05:30
Aditya Toshniwal ee0687ecd3 Add a function getQueryAt to get the SQL query at given cursor position from a CodeMirror input 2024-04-13 15:47:18 +05:30
Aditya Toshniwal e612140473 Update release notes. 2024-04-10 18:00:57 +05:30
Yogesh Mahajan e384c9665a Fixed a XSS vulnerability in the /settings/store endpoint. #7282 2024-04-10 17:58:51 +05:30
Akshay Joshi 30d2d1b23e Fixed the following code smells:
1) useState call is not destructured into value + setter pair.
2) A fragment with only one child is redundant.
3) Unnecessary '.apply()' and '.call()'.
4) Expected the Promise rejection reason to be an Error.
2024-04-09 19:18:56 +05:30
Aditya Toshniwal ed2a73f7ff Fix artifact archive error for EPAS tests on github workflows 2024-04-09 09:52:39 +05:30
Aditya Toshniwal b5e5f8ac4c Update release notes. 2024-04-09 08:37:41 +05:30
Yogesh Mahajan 102e0a9839 - Update MUI v4 to v5
- Remove the SCSS dependency completely and use MUI for theming.
- Update - date-fns, @date-io, notistack. Remove - popper.js, sass-loader.
- Cleanup webpack config.
- Port PSQL tool to use MUI themes instead of SCSS theme.
- Theme change will reflect realtime without refreshing pgAdmin.
2024-04-09 08:21:14 +05:30
Akshay Joshi edec9adbfb Fixed SonarQube code smell 'Unnecessary use of boolean literals in conditional expression'. 2024-04-08 17:19:51 +05:30
Aditya Toshniwal c9345ad618 Fixed an issue where double dollar quoted code is treated as string in syntax highlighter. #7294 2024-04-08 15:32:01 +05:30
Pravesh Sharma fc4b419987 Fixed an issue where function arguments were getting enclosed in double quotes if written in upper case. #7222 2024-04-04 16:48:00 +05:30
Akshay Joshi 475e3689f7 Added the new columns last_seq_scan and last_idx_scan from the pg_stat_all_tables and pg_stat_all_indexes tables respectively to the Statistics tab. #6728 2024-04-04 16:39:12 +05:30
Akshay Joshi c90508cece Update version for release 8.5 2024-04-01 17:01:55 +05:30
Pravesh Sharma a85d907351 Fixed an issue where user was unable to share a newly registered server. #7111
* Fixed an issue where the clear SSH tunnel password option was disabled in desktop mode.
2024-04-01 16:57:47 +05:30
Akshay Joshi 9f31ec115a Fixed Schema Diff issues while comparing Materialized View. #7271 2024-04-01 16:56:18 +05:30
Khushboo Vashi fbbbfe22dd Fixed a remote code execution issue in the validate binary path (CVE-2024-3116). #7326 2024-04-01 11:34:01 +05:30
Libor M 26b279cc8a Czech translation for version 8.5 2024-04-01 11:09:48 +05:30
Domenico Sgarbossa 3ac271e702 Italian translation update for v8.5 2024-03-29 11:41:44 +05:30
JyotiEdb 4ae9132b59 Fixed an issue with empty locale provider while editing collation. #5611 2024-03-29 11:40:44 +05:30
Akshay Joshi 3fa48f26b8 Update message catalogs for v8.5 2024-03-28 17:58:24 +05:30
Akshay Joshi 00e17ef152 Fixed an issue where the 'Save Password' option for SSH tunneling password was consistently disabled. #7139 2024-03-28 13:58:03 +05:30
Aditya ToshniwalandAkshay Joshi ace73ebb60 Added support for viewing Log Based Clusters. #7216
Co-authored-by: Akshay Joshi <akshay.joshi@enterprisedb.com>
2024-03-28 12:19:34 +05:30
JyotiEdb 5931162556 Fixed an issue where maximize icon is missing from query tool panel. #7218 2024-03-28 11:53:56 +05:30
JyotiEdb 053b1e3d69 Fixed issues in create collation. #5611 2024-03-27 17:30:16 +05:30
Pravesh Sharma 082fb165d3 Implemented shortcut mapping for the edit menu in the query tool. 2024-03-27 16:41:08 +05:30
Anil Sahoo a02ebe192b Fixed rollback and commit button activation on execute button click. #7248 2024-03-27 16:38:42 +05:30
Akshay Joshi 8ab78dc40d Fixed an issue where serial, bigserial, and smallserial columns were always shown as different in the schema diff. 2024-03-27 16:11:40 +05:30
Khushboo Vashi 6ecd7b20f7 Update release notes. 2024-03-27 11:50:31 +05:30
Benjamin Blattberg 48a86f6c38 Fix the issue where the update-user CLI command doesn't change the password. #7304 2024-03-27 11:47:58 +05:30
Pravesh Sharma b742487dc3 Fixed an issue where table properties were not updating from properties dialog. #7210 2024-03-27 11:41:01 +05:30
Akshay Joshi 089f890f2e Fixed an issue where Triggers, Rules, Indexes were absent from the Schema Diff when comparing views. #7271
Fixed an issue in Schema Diff where Columns with sequences get altered unnecessarily. #4413
2024-03-27 11:40:23 +05:30
Khushboo Vashi 1f0a2fd150 Update release notes. 2024-03-26 18:24:44 +05:30
István Lantos 715839d295 Expose UNIX socket in entrypoint.sh for Docker implementation. #7221 2024-03-26 18:22:29 +05:30
Pravesh Sharma 1bcf343ceb Fixed an issue where taking backup of a shared server was using server owner's user name. #7255 2024-03-26 11:28:20 +05:30
Anil Sahoo e99fc02f9e Fixed issue related to email authentication of Two-factor authentication. #7308 2024-03-26 11:27:35 +05:30
Aditya Toshniwal c0a1429664 Fix failing feature tests 2024-03-26 11:20:04 +05:30
Aditya Toshniwal cc1ce09b73 More CodeMirror fixes. #7268
Fix query tool autocomplete results when cursor is in between the SQL query. #7299
Fix an issue in query tool where custom keyboard shortcuts are not working for some. #7305
2024-03-23 18:22:06 +05:30
JyotiEdb 25074e46b4 Added support for provider, deterministic, version and RULES parameter while creating collation. #5611 2024-03-19 16:52:22 +05:30
Anil Sahoo cee0945a78 Revert the changes made to include a separate 'ALTER TABLE ...' query for setting defaults, as per the previous fix for issue #7165. 2024-03-19 15:12:04 +05:30
Aditya Toshniwal 24b0c2e919 Update axios to the latest version 2024-03-18 16:23:54 +05:30
Akshay Joshi c56aef335b Fixed RESQL test cases for DBMS Schedule 2024-03-18 13:44:07 +05:30
Akshay Joshi 097b630738 Added support for EDB Job Scheduler. #7098 2024-03-18 11:53:59 +05:30
Aditya Toshniwal f351b10ed0 Fix an issue in query tool where toggle case of selected text loses selection. #7277
Also make changes to give pgAdmin shortcuts higher priority over CodeMirror default shortcuts.
2024-03-14 18:18:53 +05:30
Pravesh Sharma 1a02d13a28 Bug fixes and improvements in pgAdmin CLI. #7116 2024-03-14 18:12:28 +05:30
Anil Sahoo bece59d4b3 Fixed schema diff wrong query generation for table, foreign table and sequence. #7165 2024-03-14 11:21:19 +05:30
Akshay Joshi 30398219f4 Replace actions/upload-artifact@v3 with actions/upload-artifact@v4 for all the github actions. 2024-03-12 16:04:15 +05:30
Akshay Joshi 8545d846c0 Replace actions/checkout@v3 with actions/checkout@v4 for all the github actions. 2024-03-12 15:26:31 +05:30
Akshay Joshi 6d8c4bdb82 1) Checking actions/checkout@v4 working or not.
2) Added edb job scheduler command for EPAS 16 only.
2024-03-12 14:45:44 +05:30
Anthony DeBarros 496acf5598 Polish up the system_stats not installed message. 2024-03-12 10:50:09 +05:30
Aditya Toshniwal 36228fece7 Add a workaround in pgAdmin code to make Flask-Migrat>=4.0.6 work 2024-03-11 17:17:40 +05:30
Aditya Toshniwal 794c7cb335 Fixed multiple editor related issues:
1. Replace option in query tool edit menu is not working on non-Mac OS. #7262
2. Format SQL shortcut and multiline selection are not working. #7268
3. "Use Spaces?" Preference of Editor is not working. #7269
2024-03-11 16:52:56 +05:30
Akshay Joshi 9c9766ca0a Update release note. 2024-03-08 11:49:36 +05:30
Aditya Toshniwal 7374997425 Fix an issue in table dialog where changing column name was not syncing table constraints appropriately. #7229 2024-03-08 11:45:32 +05:30
Florian 134e651989 Added support to exclude multiple tables while taking backup. #7163 2024-03-08 11:44:29 +05:30
549 changed files with 54586 additions and 40604 deletions

No files matched your search

+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Build the container
run: docker build .
+4 -4
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install platform dependencies
run: |
@@ -33,7 +33,7 @@ jobs:
make docs
- name: Archive HTML
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: pgadmin4-html-build-output
path: docs/en_US/_build/html/*
@@ -44,7 +44,7 @@ jobs:
make docs-epub
- name: Archive ePub
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: pgadmin4-epub-build-output
path: docs/en_US/_build/epub/pgAdmin4.epub
@@ -55,7 +55,7 @@ jobs:
make docs-pdf
- name: Archive PDF
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: pgadmin4-pdf-build-output
path: docs/en_US/_build/latex/pgadmin4.pdf
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Upgrade yarn
run: |
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install platform dependencies
run: |
@@ -33,7 +33,7 @@ jobs:
make pip
- name: Archive Python wheel
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: pgadmin4-python-build-output
path: dist/*
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Python dependencies
run: |
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install platform dependencies
run: |
@@ -33,7 +33,7 @@ jobs:
make src
- name: Archive source tarball
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: pgadmin4-tarball-build-output
path: dist/*
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install platform dependencies
run: |
+4 -4
View File
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup the EDB APT repo on Linux
run: |
@@ -157,21 +157,21 @@ jobs:
- name: Archive server log
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: server-log-pg${{ matrix.pgver }}
path: var/pgadmin4.log
- name: Archive regression log
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: regression-log-pg${{ matrix.pgver }}
path: web/regression/regression.log
- name: Archive screenshots
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: screenshots-pg${{ matrix.pgver }}
path: web/regression/screenshots
+4 -4
View File
@@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup the PGDG APT repo
run: |
@@ -160,21 +160,21 @@ jobs:
- name: Archive server log
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: server-log-pg${{ matrix.pgver }}
path: var/pgadmin4.log
- name: Archive regression log
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: regression-log-pg${{ matrix.pgver }}
path: web/regression/regression.log
- name: Archive screenshots
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: screenshots-pg${{ matrix.pgver }}
path: web/regression/screenshots
+3 -3
View File
@@ -22,11 +22,11 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: '18.x'
node-version: '20.x'
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Upgrade yarn
run: |
+11 -5
View File
@@ -37,7 +37,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup the EDB APT repo on Linux
if: ${{ matrix.os == 'ubuntu-22.04' }}
@@ -78,6 +78,12 @@ jobs:
icacls "C:\EPAS\${{ matrix.pgver }}\tablespaces" /grant "NETWORK SERVICE":(OI)(CI)F /T
shell: cmd
- name: Add edb_job_scheduler into config file.
if: ${{ matrix.os == 'ubuntu-22.04' && matrix.pgver == 16 }}
run: |
sudo sed -i "s/shared_preload_libraries = '/shared_preload_libraries = '\$libdir\/edb_job_scheduler,/g" /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf
sudo su -c "echo \"edb_job_scheduler.database_list = 'test_dbms_job_scheduler'\" >> /etc/edb-as/${{ matrix.pgver }}/main/postgresql.conf"
- name: Start PostgreSQL on Linux
if: ${{ matrix.os == 'ubuntu-22.04' }}
run: |
@@ -272,14 +278,14 @@ jobs:
- name: Archive server log
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: server-log-epas${{ matrix.pgver }}
name: server-log-epas-${{ matrix.os }}-pg${{ matrix.pgver }}
path: var/pgadmin4.log
- name: Archive regression log
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: regression-log-epas${{ matrix.pgver }}
name: regression-log-epas-${{ matrix.os }}-pg${{ matrix.pgver }}
path: web/regression/regression.log
+3 -3
View File
@@ -26,7 +26,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup the PGDG APT repo on Linux
if: ${{ matrix.os == 'ubuntu-latest' }}
@@ -291,14 +291,14 @@ jobs:
- name: Archive server log
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: server-log-${{ matrix.os }}-pg${{ matrix.pgver }}
path: var/pgadmin4.log
- name: Archive regression log
if: success() || failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: regression-log-${{ matrix.os }}-pg${{ matrix.pgver }}
path: web/regression/regression.log
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
+2 -2
View File
@@ -200,8 +200,8 @@ RUN apk add \
/venv/bin/python3 -m pip install --no-cache-dir gunicorn==20.1.0 && \
find / -type d -name '__pycache__' -exec rm -rf {} + && \
useradd -r -u 5050 -g root -s /sbin/nologin pgadmin && \
mkdir -p /var/lib/pgadmin && \
chown pgadmin:root /var/lib/pgadmin && \
mkdir -p /run/pgadmin /var/lib/pgadmin && \
chown pgadmin:root /run/pgadmin /var/lib/pgadmin && \
chmod g=u /var/lib/pgadmin && \
touch /pgadmin4/config_distro.py && \
chown pgadmin:root /pgadmin4/config_distro.py && \
+22 -4
View File
@@ -200,11 +200,29 @@ tab related to tables that should be included in the backup.
contents of the table that they have access to. This option is relevant
only when dumping the contents of a table which has row security.
* Move the switch next to *With OIDs* towards right position to include object
identifiers as part of the table data for each table.
* Use the *Include table(s) and Children* field to dump the tables and any partitions
or inheritance child tables of the tables matching the pattern.
**Note:** This option is visible only for database server greater than or
equal to 16.
* The Exclude patterns field is utilized to exclude patterns in the following manner:
* Use the *Exclude table data* field to not dump data for any tables
matching the table pattern.
* Use the *Table(s)* field to not dump the tables matching the
pattern. Multiple patterns can be given separated by space.
* Use the *Table(s) data* field to not dump data for any tables
matching the pattern. Multiple patterns can be given separated by
spaces.
* Use the *Table(s) and children* field to not dump the tables and any
partitions or inheritance child tables of the tables matching the pattern.
**Note:** This option is visible only for database server greater than or
equal to 16.
* Use the *Table(s) data and children* field to not dump data for the
tables and any partitions or inheritance child tables of the tables matching
the table pattern. **Note:** This option is visible only for database server
greater than or equal to 16.
Click the *Options* tab to continue. Use the fields in the *Options*
tab to provide other backup options.
-3
View File
@@ -136,9 +136,6 @@ tab related to tables that should be included in the backup.
partition itself. **Note:** This option is visible only for database server
greater than or equal to 11.
* Move the switch next to *With OIDs* towards right position to include object
identifiers as part of the table data for each table.
Click the *Options* tab to continue. Use the fields in the *Options*
tab to provide other backup options.
+10
View File
@@ -50,6 +50,16 @@ settings:
* Use the *LC_CTYPE* field to specify a locale with specified character
classification. The locale must be applicable to the current database encoding.
(See CREATE DATABASE for details.)
* Use the drop-down listbox next to *Locale Provider* to select a locale services associated
with the collation. Possible values are: icu, libc. libc is the default.
* Move the switch next to *Deterministic* to *YES* to specify whether the collation should use
deterministic comparisons. By default, this option is set to true. In a
deterministic comparison, strings that are not byte-wise equal are considered
unequal, even if they are considered logically equal in the comparison.
* Use the *Rules* field to specify a rules for customizing the behavior of the collation.
It includes considerations such as character ordering, case sensitivity, and accent
sensitivity.
* Use the *Version* field to specify version string to store with the collation object.
Click the *SQL* tab to continue.
+108
View File
@@ -0,0 +1,108 @@
.. _dbms_job:
*****************
`DBMS Job`:index:
*****************
Use the *DBMS Job* dialog to create a DBMS Job.
.. image:: images/dbms_job_general.png
:alt: DBMS Job dialog general tab
:align: center
Use the fields in the *General* tab to create job:
* Use the *Name* field to add a descriptive name for the job. The name will
be displayed in the *pgAdmin* object explorer.
* Use the *Enabled?* switch to indicate that job should be enabled or disabled.
* Use the *Job Type* field to select the type of the job. Type could be SELF-CONTAINED or PRE-DEFINED.
If the Job Type is Self-Contained you need to specify the action and repeat interval in the Action and Repeat tabs respectively.
If the Job Type is Pre-Defined you need to specify the existing Program and Schedule names in the Pre-Defined tab.
* Store notes about the job in the *Comment* field.
Click the *Action* tab to continue.
.. image:: images/dbms_job_action.png
:alt: DBMS Job dialog action tab
:align: center
Use the *Action* tab to select the action for the job. This tab is only enabled when the job type is 'SELF-CONTAINED'.
* Use the *Type* field to select the type of the job. Type could be PLSQL BLOCK or STORED PROCEDURE.
* Use the *Procedure* field to select an existing procedure that executes when the job is invoked.
* *Number of Arguments* field is read-only and indicates the quantity of arguments necessary for the chosen procedure.
Click the *Code* tab to continue.
.. image:: images/dbms_job_code.png
:alt: DBMS Job dialog code tab
:align: center
* Use the *Code* field to write the code that executes when the job is invoked.
This tab is only enabled when the job type is 'SELF-CONTAINED' and type of the action is set to 'PLSQL BLOCK'.
Click the *Arguments* tab to continue.
.. image:: images/dbms_job_arguments.png
:alt: DBMS Job dialog arguments tab
:align: center
* *Arguments* tab outlines the arguments required by the selected procedure in the 'Action' tab. This tab is only enabled when the job type is 'SELF-CONTAINED'.
Click the *Repeat* tab to continue.
.. image:: images/dbms_job_repeat.png
:alt: DBMS Job dialog repeat tab
:align: center
Use the *Repeat* tab to select the repeat interval for the job. This tab is only enabled when the job type is 'SELF-CONTAINED'.
* Use the calendar selector in the *Start* field to specify the starting date
and time for the job.
* Use the calendar selector in the *End* field to specify the ending date and
time for the job.
* Use the *Frequency* field to select the frequency. Frequency is one of the following:
YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY.
* Use the *Date* field to select the date on which job will execute.Date is YYYYMMDD.
* Use the *Months* field to select the months in which the job will execute.
* Use the *Week Days* field to select the days on which the job will execute.
* Use the *Month Days* field to select the numeric days on which the job will
execute.
* Use the *Hours* field to select the hour at which the job will execute.
* Use the *Minutes* field to select the minute at which the job will execute.
Click the *Pre-Defined* tab to continue.
.. image:: images/dbms_job_predefined.png
:alt: DBMS Job dialog predefined tab
:align: center
Use the *Pre-Defined* tab to select the existing program and schedule to create the job.
This tab is only enabled when the job type is 'PRE-DEFINED'.
* Use the *Program Name* field to select the existing program.
* Use the *Schedule Name* field to select an existing schedule.
Click the *SQL* tab to continue.
Your entries in the *DBMS Job* dialog generate a SQL command (see an example below).
Use the *SQL* tab for review; revisit or switch tabs to make any changes to the
SQL command.
**Example**
The following is an example of the sql command generated by user selections in
the *DBMS Job* dialog:
.. image:: images/dbms_job_sql.png
:alt: DBMS Job dialog sql tab
:align: center
* Click the *Info* button (i) to access online help.
* Click the *Help* button (?) to access dialog help.
* Click the *Save* button to save work.
* Click the *Close* button to exit without saving work.
* Click the *Reset* button to restore configuration parameters.
+45
View File
@@ -0,0 +1,45 @@
.. _dbms_job_scheduler:
********************************
`Using EDB Job Scheduler`:index:
********************************
In the past versions of EPAS, DBMS_SCHEDULER or DBMS_JOBS required the configuration
of pgAgent, an essential service for their functionality. Maintaining pgAgent in a
production environment is cumbersome. The need for correct configuration, regular updates,
and ensuring the service’s health added complexity.
EPAS 16 revolutionizes job scheduling by eliminating the need for the pgAgent component.
The new version introduces EDB Job Scheduler which is an extension that runs the job scheduler
as a background process for the DBMS_SCHEDULER and DBMS_JOB packages.
The EDB Job Scheduler has a scheduler process that starts when the database cluster starts.
To start the scheduler process, load the EDB Job Scheduler extension using the **shared_preload_libraries**
parameter. After you load the extension, create the extension **'edb_job_scheduler'** and **'dbms_scheduler'** using
the CREATE EXTENSION command. The database in which you're creating the extension must be listed in the
**edb_job_scheduler.database_list** parameter.
Instructions for configuring the EDB Job Scheduler can be found in the
`Configuring EDB Job Scheduler <https://www.enterprisedb.com/docs/pg_extensions/edb_job_scheduler/configuring/>`_.
.. image:: images/dbms_job_scheduler.png
:alt: DBMS Job Scheduler Object Browser
:align: center
Check the status of all the jobs
********************************
To check the running status of all the jobs select the 'DBMS Job Scheduler' collection node from the object
explorer and select the Properties tab.
.. image:: images/dbms_job_details.png
:alt: DBMS Job Details
:align: center
.. toctree::
:maxdepth: 1
dbms_job
dbms_program
dbms_schedule
+71
View File
@@ -0,0 +1,71 @@
.. _dbms_program:
*********************
`DBMS Program`:index:
*********************
Use the *DBMS Program* dialog to create a DBMS Program.
.. image:: images/dbms_program_general.png
:alt: DBMS Program dialog general tab
:align: center
Use the fields in the *General* tab to create program:
* Use the *Name* field to add a descriptive name for the program. The name will
be displayed in the *pgAdmin* object explorer.
* Use the *Enabled?* switch to indicate that program should be enabled or disabled.
* Store notes about the program in the *Comment* field.
Click the *Action* tab to continue.
.. image:: images/dbms_program_action.png
:alt: DBMS Program dialog action tab
:align: center
Use the *Action* tab to select the action for the program:
* Use the *Type* field to select the type of the program. Type could be PLSQL BLOCK or STORED PROCEDURE.
* Use the *Procedure* field to select an existing procedure that executes when the program is invoked.
* *Number of Arguments* field is read-only and indicates the quantity of arguments necessary for the chosen procedure.
Click the *Code* tab to continue.
.. image:: images/dbms_program_code.png
:alt: DBMS Program dialog code tab
:align: center
* Use the *Code* field to write the code that executes when the program is invoked.
This tab is only enabled when the type of the program is set to 'PLSQL BLOCK'.
Click the *Arguments* tab to continue.
.. image:: images/dbms_program_arguments.png
:alt: DBMS Program dialog arguments tab
:align: center
* *Arguments* tab is a read-only section that outlines the arguments required by the selected procedure in the 'Action' tab.
Click the *SQL* tab to continue.
Your entries in the *DBMS Program* dialog generate a SQL command (see an example below).
Use the *SQL* tab for review; revisit or switch tabs to make any changes to the
SQL command.
**Example**
The following is an example of the sql command generated by user selections in
the *DBMS Program* dialog:
.. image:: images/dbms_program_sql.png
:alt: DBMS Program dialog sql tab
:align: center
* Click the *Info* button (i) to access online help.
* Click the *Help* button (?) to access dialog help.
* Click the *Save* button to save work.
* Click the *Close* button to exit without saving work.
* Click the *Reset* button to restore configuration parameters.
+61
View File
@@ -0,0 +1,61 @@
.. _dbms_schedule:
**********************
`DBMS Schedule`:index:
**********************
Use the *DBMS Schedule* dialog to create a DBMS Schedule.
.. image:: images/dbms_schedule_general.png
:alt: DBMS Schedule dialog general tab
:align: center
Use the fields in the *General* tab to create schedule:
* Use the *Name* field to add a descriptive name for the schedule. The name will
be displayed in the *pgAdmin* object explorer.
* Store notes about the schedule in the *Comment* field.
Click the *Repeat* tab to continue.
.. image:: images/dbms_schedule_repeat.png
:alt: DBMS Schedule dialog repeat tab
:align: center
Use the *Repeat* tab to select the repeat interval for the schedule:
* Use the calendar selector in the *Start* field to specify the starting date
and time for the schedule.
* Use the calendar selector in the *End* field to specify the ending date and
time for the schedule.
* Use the *Frequency* field to select the frequency. Frequency is one of the following:
YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY.
* Use the *Date* field to select the date on which schedule will execute.Date is YYYYMMDD.
* Use the *Months* field to select the months in which the schedule will execute.
* Use the *Week Days* field to select the days on which the schedule will execute.
* Use the *Month Days* field to select the numeric days on which the schedule will
execute.
* Use the *Hours* field to select the hour at which the schedule will execute.
* Use the *Minutes* field to select the minute at which the schedule will execute.
Click the *SQL* tab to continue.
Your entries in the *DBMS Schedule* dialog generate a SQL command (see an example below).
Use the *SQL* tab for review; revisit or switch tabs to make any changes to the
SQL command.
**Example**
The following is an example of the sql command generated by user selections in
the *DBMS Schedule* dialog:
.. image:: images/dbms_schedule_sql.png
:alt: DBMS Schedule dialog sql tab
:align: center
* Click the *Info* button (i) to access online help.
* Click the *Help* button (?) to access dialog help.
* Click the *Save* button to save work.
* Click the *Close* button to exit without saving work.
* Click the *Reset* button to restore configuration parameters.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

+1
View File
@@ -18,4 +18,5 @@ database, right-click on the *Databases* node, and select *Create Database...*
resource_group_dialog
role_dialog
tablespace_dialog
replica_nodes_dialog
role_reassign_dialog
+1
View File
@@ -18,6 +18,7 @@ node, and select *Create Cast...*
:maxdepth: 1
cast_dialog
dbms_job_scheduler
collation_dialog
domain_dialog
domain_constraint_dialog
+2
View File
@@ -12,6 +12,8 @@ notes for it.
:maxdepth: 1
release_notes_8_6
release_notes_8_5
release_notes_8_4
release_notes_8_3
release_notes_8_2
+54
View File
@@ -0,0 +1,54 @@
***********
Version 8.5
***********
Release date: 2024-04-04
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v8.5.
Supported Database Servers
**************************
**PostgreSQL**: 12, 13, 14, 15, and 16
**EDB Advanced Server**: 12, 13, 14, 15, and 16
Bundled PostgreSQL Utilities
****************************
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 16.1
New features
************
| `Issue #5611 <https://github.com/pgadmin-org/pgadmin4/issues/5611>`_ - Added support for provider, deterministic, version and RULES parameter while creating collation.
| `Issue #7098 <https://github.com/pgadmin-org/pgadmin4/issues/7098>`_ - Added support for EDB Job Scheduler.
| `Issue #7216 <https://github.com/pgadmin-org/pgadmin4/issues/7216>`_ - Added support for viewing Log Streaming replication Based Clusters.
| `Issue #7221 <https://github.com/pgadmin-org/pgadmin4/issues/7221>`_ - Added support for UNIX socket in entrypoint.sh for Docker implementation.
Housekeeping
************
Bug fixes
*********
| `Issue #4413 <https://github.com/pgadmin-org/pgadmin4/issues/4413>`_ - Fixed an issue in Schema Diff where Columns with sequences get altered unnecessarily.
| `Issue #7111 <https://github.com/pgadmin-org/pgadmin4/issues/7111>`_ - Fixed an issue where user was unable to share a newly registered server.
| `Issue #7116 <https://github.com/pgadmin-org/pgadmin4/issues/7116>`_ - Bug fixes and improvements in pgAdmin CLI.
| `Issue #7139 <https://github.com/pgadmin-org/pgadmin4/issues/7139>`_ - Fixed an issue where the 'Save Password' option for SSH tunneling password was consistently disabled.
| `Issue #7165 <https://github.com/pgadmin-org/pgadmin4/issues/7165>`_ - Fixed schema diff wrong query generation for table, foreign table and sequence.
| `Issue #7210 <https://github.com/pgadmin-org/pgadmin4/issues/7210>`_ - Fixed an issue where table properties were not updating from properties dialog.
| `Issue #7218 <https://github.com/pgadmin-org/pgadmin4/issues/7218>`_ - Fixed an issue where maximize icon is missing from query tool panel.
| `Issue #7229 <https://github.com/pgadmin-org/pgadmin4/issues/7229>`_ - Fix an issue in table dialog where changing column name was not syncing table constraints appropriately.
| `Issue #7248 <https://github.com/pgadmin-org/pgadmin4/issues/7248>`_ - Fixed rollback and commit button activation on execute button click.
| `Issue #7255 <https://github.com/pgadmin-org/pgadmin4/issues/7255>`_ - Fixed an issue where taking backup of a shared server was using server owner's user name.
| `Issue #7262 <https://github.com/pgadmin-org/pgadmin4/issues/7262>`_ - Fix an issue in editor where replace option in query tool edit menu is not working on non-Mac OS.
| `Issue #7268 <https://github.com/pgadmin-org/pgadmin4/issues/7268>`_ - Fix an issue in editor where Format SQL shortcut and multiline selection are not working.
| `Issue #7269 <https://github.com/pgadmin-org/pgadmin4/issues/7269>`_ - Fix an issue in editor where "Use Spaces?" Preference of Editor is not working.
| `Issue #7271 <https://github.com/pgadmin-org/pgadmin4/issues/7271>`_ - Fixed an issue where Triggers, Rules, Indexes were absent from the Schema Diff when comparing views.
| `Issue #7277 <https://github.com/pgadmin-org/pgadmin4/issues/7277>`_ - Fix an issue in query tool where toggle case of selected text loses selection.
| `Issue #7299 <https://github.com/pgadmin-org/pgadmin4/issues/7299>`_ - Fix query tool autocomplete results when cursor is in between the SQL query.
| `Issue #7305 <https://github.com/pgadmin-org/pgadmin4/issues/7305>`_ - Fix an issue in query tool where custom keyboard shortcuts are not working for some.
| `Issue #7304 <https://github.com/pgadmin-org/pgadmin4/issues/7304>`_ - Fixed the issue where the update-user CLI command doesn't change the password.
| `Issue #7308 <https://github.com/pgadmin-org/pgadmin4/issues/7308>`_ - Fixed issue related to email authentication of Two-factor authentication.
| `Issue #7326 <https://github.com/pgadmin-org/pgadmin4/issues/7326>`_ - Fixed a remote code execution issue in the validate binary path (CVE-2024-3116).
+45
View File
@@ -0,0 +1,45 @@
***********
Version 8.6
***********
Release date: 2024-05-02
This release contains a number of bug fixes and new features since the release of pgAdmin 4 v8.5.
Supported Database Servers
**************************
**PostgreSQL**: 12, 13, 14, 15, and 16
**EDB Advanced Server**: 12, 13, 14, 15, and 16
Bundled PostgreSQL Utilities
****************************
**psql**, **pg_dump**, **pg_dumpall**, **pg_restore**: 16.1
New features
************
| `Issue #6728 <https://github.com/pgadmin-org/pgadmin4/issues/6728>`_ - Added the new columns "last_seq_scan" and "last_idx_scan" from the pg_stat_all_tables and pg_stat_all_indexes tables respectively to the Statistics tab.
| `Issue #7163 <https://github.com/pgadmin-org/pgadmin4/issues/7163>`_ - Added support for excluding multiple tables while taking Backup.
Housekeeping
************
| `Issue #7213 <https://github.com/pgadmin-org/pgadmin4/issues/7213>`_ - Update UI library MUI from v4 to v5, more details `here <https://github.com/pgadmin-org/pgadmin4/commit/102e0a983956be57fdb63abb356b5b8fcb8b74ba>`_
| `Issue #7362 <https://github.com/pgadmin-org/pgadmin4/issues/7362>`_ - Upgraded Flask, Flask-Security-Too, Werkzeug, and other dependencies, ensuring compatibility with Python 3.7.
Bug fixes
*********
| `Issue #2410 <https://github.com/pgadmin-org/pgadmin4/issues/2410>`_ - Fixed all input boxes in pgAdmin to show browser auto-fill only where it is relevant.
| `Issue #7173 <https://github.com/pgadmin-org/pgadmin4/issues/7173>`_ - Install dbus-python, an in-direct dependency of the Keyring package as a system package for Debian platforms.
| `Issue #7275 <https://github.com/pgadmin-org/pgadmin4/issues/7275>`_ - Fixed an issue where debugger was not scrolling automatically on stepping.
| `Issue #7282 <https://github.com/pgadmin-org/pgadmin4/issues/7282>`_ - Fixed an XSS vulnerability in the /settings/store endpoint (CVE-2024-4216).
| `Issue #7294 <https://github.com/pgadmin-org/pgadmin4/issues/7294>`_ - Fixed an issue where double dollar quoted code is treated as string in syntax highlighter.
| `Issue #7317 <https://github.com/pgadmin-org/pgadmin4/issues/7317>`_ - Fixed an issue where pressing backspace should remove the spaces and not the entire tab width, on enabling 'Use spaces?' in the preferences.
| `Issue #7334 <https://github.com/pgadmin-org/pgadmin4/issues/7334>`_ - Fixed an issue where incorrect select/exec scripts were generated for functions/procedures.
| `Issue #7372 <https://github.com/pgadmin-org/pgadmin4/issues/7372>`_ - Fixed an issue where connection to the database is not automatically re-established after connectivity drop.
| `Issue #7384 <https://github.com/pgadmin-org/pgadmin4/issues/7384>`_ - Fixed an issue when closing the view data second tab; it raises the error that the 'ViewCommand' object has no attribute 'auto_commit'.
| `Issue #7390 <https://github.com/pgadmin-org/pgadmin4/issues/7390>`_ - Fixed violates check constraint issue when creating a pgAgent schedule.
| `Issue #7425 <https://github.com/pgadmin-org/pgadmin4/issues/7425>`_ - Fixed Multi-Factor Authentication bypass vulnerability (CVE-2024-4215).
+46
View File
@@ -0,0 +1,46 @@
.. _replica_nodes_dialog:
****************************
`Replica Node Dialog`:index:
****************************
Use The *Replica Node* dialog to view a standby instance being replicated
using log based streaming replication. Streaming replication allows a standby
server to stay more up-to-date than is possible with file-based log shipping.
The standby connects to the primary, which streams WAL records to the standby as
they're generated, without waiting for the WAL file to be filled.
The *Replica Node* dialog organizes the information through the following tabs:
*General*, *Replication Slot*
.. image:: images/replica_nodes_general.png
:alt: Replica Node dialog general tab
:align: center
* The *PID* field is the process ID of a WAL sender process.
* The *Username* field is the name of the user logged into this WAL sender process.
* The *App Name* field is the name of the application that is connected to this WAL sender.
* The *Client Address* field is the IP address of the client connected to this WAL sender.
If this field is null, it indicates that the client is connected via a Unix socket on the server machine.
* The *Client Hostname* field is the host name of the connected client, as reported by a reverse DNS lookup
of client_addr.This field will only be non-null for IP connections, and only when log_hostname is enabled.
* The *Client Port* field is the TCP port number that the client is using for communication with
this WAL sender, or -1 if a Unix socket is used.
* The *State* field is the current WAL sender state.
Click the *Replication Slot* tab to continue.
.. image:: images/replica_nodes_replication.png
:alt: Replica Node dialog replication slot tab
:align: center
* The *Slot Name* field is a unique, cluster-wide identifier for the replication slot.
* The *Slot Type* field is the slot type - physical or logical
* The *Active* field is True if this slot is currently actively being used.
Other buttons:
* Click the *Info* button (i) to access online help.
* Click the *Save* button to save work.
* Click the *Close* button to exit without saving work.
* Click the *Reset* button to restore configuration parameters.
+2 -2
View File
@@ -50,7 +50,7 @@ Version: ${APP_LONG_VERSION}
Architecture: ${OS_ARCH}
Section: database
Priority: optional
Depends: ${PYTHON_BINARY}, libpq5 (>= 11.0), libgssapi-krb5-2
Depends: ${PYTHON_BINARY}, libpq5 (>= 11.0), libgssapi-krb5-2, python-dbus | python3-dbus
Recommends: postgresql-client | postgresql-client-15 | postgresql-client-14 | postgresql-client-13 | postgresql-client-12 | postgresql-client-11 | postgresql-client-10
Maintainer: pgAdmin Development Team <pgadmin-hackers@postgresql.org>
Description: The core server package for pgAdmin. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
@@ -73,7 +73,7 @@ Version: ${APP_LONG_VERSION}
Architecture: ${OS_ARCH}
Section: database
Priority: optional
Depends: ${APP_NAME}-server (= ${APP_LONG_VERSION}), libatomic1, xdg-utils
Depends: ${APP_NAME}-server (= ${APP_LONG_VERSION}), libatomic1, xdg-utils, python-dbus | python3-dbus
Maintainer: pgAdmin Development Team <pgadmin-hackers@postgresql.org>
Description: The desktop user interface for pgAdmin. pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
EOF
+13 -3
View File
@@ -101,8 +101,18 @@ TIMEOUT=$(cd /pgadmin4 && /venv/bin/python3 -c 'import config; print(config.SESS
# NOTE: currently pgadmin can run only with 1 worker due to sessions implementation
# Using --threads to have multi-threaded single-process worker
if [ -n "${PGADMIN_ENABLE_TLS}" ]; then
exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-443}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app
if [ -n "${PGADMIN_ENABLE_SOCK}" ]; then
BIND_ADDRESS="unix:/run/pgadmin/pgadmin.sock"
else
exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-80}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" -c gunicorn_config.py run_pgadmin:app
if [ -n "${PGADMIN_ENABLE_TLS}" ]; then
BIND_ADDRESS="${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-443}"
else
BIND_ADDRESS="${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-80}"
fi
fi
if [ -n "${PGADMIN_ENABLE_TLS}" ]; then
exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app
else
exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" -c gunicorn_config.py run_pgadmin:app
fi
+9 -8
View File
@@ -8,9 +8,8 @@
#
###############################################################################
Flask==2.3.*; python_version > '3.7'
Flask==3.0.*; python_version > '3.7'
Flask==2.2.*; python_version <= '3.7'
Flask-Gravatar==0.*
Flask-Login==0.*
Flask-Mail==0.*
Flask-Migrate==4.*
@@ -23,13 +22,13 @@ Flask-Compress==1.*
Flask-Paranoid==0.*
Flask-Babel==4.0.*; python_version > '3.7'
Flask-Babel==3.1.*; python_version <= '3.7'
Flask-Security-Too==5.3.*; python_version > '3.7'
Flask-Security-Too==5.4.*; python_version > '3.7'
Flask-Security-Too==5.1.*; python_version <= '3.7'
Flask-SocketIO==5.3.*
WTForms==3.1.*; python_version > '3.7'
WTForms==3.0.*; python_version <= '3.7'
passlib==1.*
pytz==2023.*
pytz==2024.*
speaklater3==1.*
sqlparse==0.*
psutil==5.9.*
@@ -45,7 +44,8 @@ eventlet==0.34.2
httpagentparser==1.9.*
user-agents==2.2.0
pywinpty==2.0.*; sys_platform=="win32"
Authlib==1.2.*
Authlib==1.3.*; python_version > '3.7'
Authlib==1.2.*; python_version <= '3.7'
pyotp==2.*
qrcode==7.*
boto3==1.33.*
@@ -55,11 +55,12 @@ azure-mgmt-resource==23.0.1
azure-mgmt-subscription==3.1.1
azure-identity==1.15.0
google-api-python-client==2.*
google-auth-oauthlib==1.1.0
google-auth-oauthlib==1.2.0
keyring==24.*; python_version > '3.7'
keyring==23.*; python_version <= '3.7'
Werkzeug==2.3.*; python_version > '3.7'
Werkzeug==3.0.*; python_version > '3.7'
Werkzeug==2.2.3; python_version <= '3.7'
typer[all]==0.9.*
typer[all]==0.12.*
setuptools==69.*; python_version >= '3.12'
jsonformatter~=0.3.2
libgravatar==1.0.*
+4 -3
View File
@@ -23,14 +23,15 @@
"256": "assets/pgAdmin4.png"
},
"dependencies": {
"axios": "^1.5.1",
"axios": "^1.6.8",
"bootstrap": "^4.5.3"
},
"devDependencies": {
"eslint": "^8.50.0",
"eslint": "^9.0.0",
"nw": "0.77.0"
},
"scripts": {
"linter": "yarn eslint --no-eslintrc -c .eslintrc.js --ext .js ."
}
},
"packageManager": "yarn@3.6.4"
}
+95 -129
View File
@@ -30,38 +30,38 @@ __metadata:
languageName: node
linkType: hard
"@eslint/eslintrc@npm:^2.1.2":
version: 2.1.2
resolution: "@eslint/eslintrc@npm:2.1.2"
"@eslint/eslintrc@npm:^3.0.2":
version: 3.0.2
resolution: "@eslint/eslintrc@npm:3.0.2"
dependencies:
ajv: ^6.12.4
debug: ^4.3.2
espree: ^9.6.0
globals: ^13.19.0
espree: ^10.0.1
globals: ^14.0.0
ignore: ^5.2.0
import-fresh: ^3.2.1
js-yaml: ^4.1.0
minimatch: ^3.1.2
strip-json-comments: ^3.1.1
checksum: bc742a1e3b361f06fedb4afb6bf32cbd27171292ef7924f61c62f2aed73048367bcc7ac68f98c06d4245cd3fabc43270f844e3c1699936d4734b3ac5398814a7
checksum: 05bf516b60fbb1c1bdc264e081118b2172c5feb071cd665976482c5614b8e7950991175fea3ca6b1f482ced7cb0d0aa34ceab3a508d6bf1ff17b4efc0911e293
languageName: node
linkType: hard
"@eslint/js@npm:8.52.0":
version: 8.52.0
resolution: "@eslint/js@npm:8.52.0"
checksum: 490893b8091a66415f4ac98b963d23eb287264ea3bd6af7ec788f0570705cf64fd6ab84b717785980f55e39d08ff5c7fde6d8e4391ccb507169370ce3a6d091a
"@eslint/js@npm:9.0.0":
version: 9.0.0
resolution: "@eslint/js@npm:9.0.0"
checksum: bd8692d68fc79306ad44999fdec9c1ab39c72c3947034f5a251a9148bc175f1d831b54cbbe522d69d88d9d4f1b9e85a1495513553b8528fd8b107d77eadb713f
languageName: node
linkType: hard
"@humanwhocodes/config-array@npm:^0.11.13":
version: 0.11.13
resolution: "@humanwhocodes/config-array@npm:0.11.13"
"@humanwhocodes/config-array@npm:^0.12.3":
version: 0.12.3
resolution: "@humanwhocodes/config-array@npm:0.12.3"
dependencies:
"@humanwhocodes/object-schema": ^2.0.1
debug: ^4.1.1
"@humanwhocodes/object-schema": ^2.0.3
debug: ^4.3.1
minimatch: ^3.0.5
checksum: f8ea57b0d7ed7f2d64cd3944654976829d9da91c04d9c860e18804729a33f7681f78166ef4c761850b8c324d362f7d53f14c5c44907a6b38b32c703ff85e4805
checksum: 4212cf12567538675f45f09dae9715978fac805fbc6f194ce31e62dbbe01ca286b87478463c042883bf4451998be737012dd0882530f55ad0c0e901d963b5123
languageName: node
linkType: hard
@@ -72,10 +72,10 @@ __metadata:
languageName: node
linkType: hard
"@humanwhocodes/object-schema@npm:^2.0.1":
version: 2.0.1
resolution: "@humanwhocodes/object-schema@npm:2.0.1"
checksum: 24929487b1ed48795d2f08346a0116cc5ee4634848bce64161fb947109352c562310fd159fc64dda0e8b853307f5794605191a9547f7341158559ca3c8262a45
"@humanwhocodes/object-schema@npm:^2.0.3":
version: 2.0.3
resolution: "@humanwhocodes/object-schema@npm:2.0.3"
checksum: d3b78f6c5831888c6ecc899df0d03bcc25d46f3ad26a11d7ea52944dc36a35ef543fad965322174238d677a43d5c694434f6607532cff7077062513ad7022631
languageName: node
linkType: hard
@@ -116,27 +116,20 @@ __metadata:
linkType: hard
"@types/node@npm:*":
version: 20.8.10
resolution: "@types/node@npm:20.8.10"
version: 20.12.7
resolution: "@types/node@npm:20.12.7"
dependencies:
undici-types: ~5.26.4
checksum: 7c61190e43e8074a1b571e52ff14c880bc67a0447f2fe5ed0e1a023eb8a23d5f815658edb98890f7578afe0f090433c4a635c7c87311762544e20dd78723e515
checksum: 7cc979f7e2ca9a339ec71318c3901b9978555257929ef3666987f3e447123bc6dc92afcc89f6347e09e07d602fde7d51bcddea626c23aa2bb74aeaacfd1e1686
languageName: node
linkType: hard
"@types/responselike@npm:^1.0.0":
version: 1.0.2
resolution: "@types/responselike@npm:1.0.2"
version: 1.0.3
resolution: "@types/responselike@npm:1.0.3"
dependencies:
"@types/node": "*"
checksum: ff1767e947eb7d49849e4566040453efcd894888e85b398f7f8cb731552f303f26aceda573b680a142b77ec5fb6c79535d9c6d047d9f936c386dbf3863d2ae17
languageName: node
linkType: hard
"@ungap/structured-clone@npm:^1.2.0":
version: 1.2.0
resolution: "@ungap/structured-clone@npm:1.2.0"
checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524
checksum: 6ac4b35723429b11b117e813c7acc42c3af8b5554caaf1fc750404c1ae59f9b7376bc69b9e9e194a5a97357a597c2228b7173d317320f0360d617b6425212f58
languageName: node
linkType: hard
@@ -149,12 +142,12 @@ __metadata:
languageName: node
linkType: hard
"acorn@npm:^8.9.0":
version: 8.11.2
resolution: "acorn@npm:8.11.2"
"acorn@npm:^8.11.3":
version: 8.11.3
resolution: "acorn@npm:8.11.3"
bin:
acorn: bin/acorn
checksum: 818450408684da89423e3daae24e4dc9b68692db8ab49ea4569c7c5abb7a3f23669438bf129cc81dfdada95e1c9b944ee1bfca2c57a05a4dc73834a612fbf6a7
checksum: 76d8e7d559512566b43ab4aadc374f11f563f0a9e21626dd59cb2888444e9445923ae9f3699972767f18af61df89cd89f5eaaf772d1327b055b45cb829b4a88c
languageName: node
linkType: hard
@@ -214,14 +207,14 @@ __metadata:
languageName: node
linkType: hard
"axios@npm:^1.5.1":
version: 1.6.0
resolution: "axios@npm:1.6.0"
"axios@npm:^1.6.8":
version: 1.6.8
resolution: "axios@npm:1.6.8"
dependencies:
follow-redirects: ^1.15.0
follow-redirects: ^1.15.6
form-data: ^4.0.0
proxy-from-env: ^1.1.0
checksum: c7c9f2ae9e0b9bad7d6f9a4dff030930b12ee667dedf54c3c776714f91681feb743c509ac0796ae5c01e12c4ab4a2bee74905068dd200fbc1ab86f9814578fb0
checksum: bf007fa4b207d102459300698620b3b0873503c6d47bf5a8f6e43c0c64c90035a4f698b55027ca1958f61ab43723df2781c38a99711848d232cad7accbcdfcdd
languageName: node
linkType: hard
@@ -460,7 +453,7 @@ __metadata:
languageName: node
linkType: hard
"debug@npm:^4.1.1, debug@npm:^4.3.2":
"debug@npm:^4.3.1, debug@npm:^4.3.2":
version: 4.3.4
resolution: "debug@npm:4.3.4"
dependencies:
@@ -556,15 +549,6 @@ __metadata:
languageName: node
linkType: hard
"doctrine@npm:^3.0.0":
version: 3.0.0
resolution: "doctrine@npm:3.0.0"
dependencies:
esutils: ^2.0.2
checksum: fd7673ca77fe26cd5cba38d816bc72d641f500f1f9b25b83e8ce28827fe2da7ad583a8da26ab6af85f834138cf8dae9f69b0cd6ab925f52ddab1754db44d99ce
languageName: node
linkType: hard
"download@npm:^5.0.3":
version: 5.0.3
resolution: "download@npm:5.0.3"
@@ -610,57 +594,60 @@ __metadata:
languageName: node
linkType: hard
"eslint-scope@npm:^7.2.2":
version: 7.2.2
resolution: "eslint-scope@npm:7.2.2"
"eslint-scope@npm:^8.0.1":
version: 8.0.1
resolution: "eslint-scope@npm:8.0.1"
dependencies:
esrecurse: ^4.3.0
estraverse: ^5.2.0
checksum: ec97dbf5fb04b94e8f4c5a91a7f0a6dd3c55e46bfc7bbcd0e3138c3a76977570e02ed89a1810c778dcd72072ff0e9621ba1379b4babe53921d71e2e4486fda3e
checksum: 67a5a39312dadb8c9a677df0f2e8add8daf15280b08bfe07f898d5347ee2d7cd2a1f5c2760f34e46e8f5f13f7192f47c2c10abe676bfa4173ae5539365551940
languageName: node
linkType: hard
"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3":
"eslint-visitor-keys@npm:^3.3.0":
version: 3.4.3
resolution: "eslint-visitor-keys@npm:3.4.3"
checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60
languageName: node
linkType: hard
"eslint@npm:^8.50.0":
version: 8.52.0
resolution: "eslint@npm:8.52.0"
"eslint-visitor-keys@npm:^4.0.0":
version: 4.0.0
resolution: "eslint-visitor-keys@npm:4.0.0"
checksum: 5c09f89cf29d87cdbfbac38802a880d3c2e65f8cb61c689888346758f1e24a4c7f6caefeac9474dfa52058a99920623599bdb00516976a30134abeba91275aa2
languageName: node
linkType: hard
"eslint@npm:^9.0.0":
version: 9.0.0
resolution: "eslint@npm:9.0.0"
dependencies:
"@eslint-community/eslint-utils": ^4.2.0
"@eslint-community/regexpp": ^4.6.1
"@eslint/eslintrc": ^2.1.2
"@eslint/js": 8.52.0
"@humanwhocodes/config-array": ^0.11.13
"@eslint/eslintrc": ^3.0.2
"@eslint/js": 9.0.0
"@humanwhocodes/config-array": ^0.12.3
"@humanwhocodes/module-importer": ^1.0.1
"@nodelib/fs.walk": ^1.2.8
"@ungap/structured-clone": ^1.2.0
ajv: ^6.12.4
chalk: ^4.0.0
cross-spawn: ^7.0.2
debug: ^4.3.2
doctrine: ^3.0.0
escape-string-regexp: ^4.0.0
eslint-scope: ^7.2.2
eslint-visitor-keys: ^3.4.3
espree: ^9.6.1
eslint-scope: ^8.0.1
eslint-visitor-keys: ^4.0.0
espree: ^10.0.1
esquery: ^1.4.2
esutils: ^2.0.2
fast-deep-equal: ^3.1.3
file-entry-cache: ^6.0.1
file-entry-cache: ^8.0.0
find-up: ^5.0.0
glob-parent: ^6.0.2
globals: ^13.19.0
graphemer: ^1.4.0
ignore: ^5.2.0
imurmurhash: ^0.1.4
is-glob: ^4.0.0
is-path-inside: ^3.0.3
js-yaml: ^4.1.0
json-stable-stringify-without-jsonify: ^1.0.1
levn: ^0.4.1
lodash.merge: ^4.6.2
@@ -671,18 +658,18 @@ __metadata:
text-table: ^0.2.0
bin:
eslint: bin/eslint.js
checksum: fd22d1e9bd7090e31b00cbc7a3b98f3b76020a4c4641f987ae7d0c8f52e1b88c3b268bdfdabac2e1a93513e5d11339b718ff45cbff48a44c35d7e52feba510ed
checksum: 3f20468cfd0f5affadf0f307e3af5b8f9ed19feaabccc3b4ed7548d40727d3f5dea4138db1ec287ca5a6152e164a051450604d47186b5fb28bb158a40937cf60
languageName: node
linkType: hard
"espree@npm:^9.6.0, espree@npm:^9.6.1":
version: 9.6.1
resolution: "espree@npm:9.6.1"
"espree@npm:^10.0.1":
version: 10.0.1
resolution: "espree@npm:10.0.1"
dependencies:
acorn: ^8.9.0
acorn: ^8.11.3
acorn-jsx: ^5.3.2
eslint-visitor-keys: ^3.4.1
checksum: eb8c149c7a2a77b3f33a5af80c10875c3abd65450f60b8af6db1bfcfa8f101e21c1e56a561c6dc13b848e18148d43469e7cd208506238554fb5395a9ea5a1ab9
eslint-visitor-keys: ^4.0.0
checksum: 62c9242a84c6741cebd35ede6574131d0419be7e5559566403e384087d99c4ddb2ced44e32acd44a4c3d8a8a84997cf8d78810c4e46b3fe25a804f1a92dc6b9d
languageName: node
linkType: hard
@@ -740,11 +727,11 @@ __metadata:
linkType: hard
"fastq@npm:^1.6.0":
version: 1.15.0
resolution: "fastq@npm:1.15.0"
version: 1.17.1
resolution: "fastq@npm:1.17.1"
dependencies:
reusify: ^1.0.4
checksum: 0170e6bfcd5d57a70412440b8ef600da6de3b2a6c5966aeaf0a852d542daff506a0ee92d6de7679d1de82e644bce69d7a574a6c93f0b03964b5337eed75ada1a
checksum: a8c5b26788d5a1763f88bae56a8ddeee579f935a831c5fe7a8268cea5b0a91fbfe705f612209e02d639b881d7b48e461a50da4a10cfaa40da5ca7cc9da098d88
languageName: node
linkType: hard
@@ -757,12 +744,12 @@ __metadata:
languageName: node
linkType: hard
"file-entry-cache@npm:^6.0.1":
version: 6.0.1
resolution: "file-entry-cache@npm:6.0.1"
"file-entry-cache@npm:^8.0.0":
version: 8.0.0
resolution: "file-entry-cache@npm:8.0.0"
dependencies:
flat-cache: ^3.0.4
checksum: f49701feaa6314c8127c3c2f6173cfefff17612f5ed2daaafc6da13b5c91fd43e3b2a58fd0d63f9f94478a501b167615931e7200e31485e320f74a33885a9c74
flat-cache: ^4.0.0
checksum: f67802d3334809048c69b3d458f672e1b6d26daefda701761c81f203b80149c35dea04d78ea4238969dd617678e530876722a0634c43031a0957f10cc3ed190f
languageName: node
linkType: hard
@@ -822,31 +809,30 @@ __metadata:
languageName: node
linkType: hard
"flat-cache@npm:^3.0.4":
version: 3.1.1
resolution: "flat-cache@npm:3.1.1"
"flat-cache@npm:^4.0.0":
version: 4.0.1
resolution: "flat-cache@npm:4.0.1"
dependencies:
flatted: ^3.2.9
keyv: ^4.5.3
rimraf: ^3.0.2
checksum: 4958cfe0f46acf84953d4e16676ef5f0d38eab3a92d532a1e8d5f88f11eea8b36d5d598070ff2aeae15f1fde18f8d7d089eefaf9db10b5a587cc1c9072325c7a
keyv: ^4.5.4
checksum: 899fc86bf6df093547d76e7bfaeb900824b869d7d457d02e9b8aae24836f0a99fbad79328cfd6415ee8908f180699bf259dc7614f793447cb14f707caf5996f6
languageName: node
linkType: hard
"flatted@npm:^3.2.9":
version: 3.2.9
resolution: "flatted@npm:3.2.9"
checksum: f14167fbe26a9d20f6fca8d998e8f1f41df72c8e81f9f2c9d61ed2bea058248f5e1cbd05e7f88c0e5087a6a0b822a1e5e2b446e879f3cfbe0b07ba2d7f80b026
version: 3.3.1
resolution: "flatted@npm:3.3.1"
checksum: 85ae7181650bb728c221e7644cbc9f4bf28bc556f2fc89bb21266962bdf0ce1029cc7acc44bb646cd469d9baac7c317f64e841c4c4c00516afa97320cdac7f94
languageName: node
linkType: hard
"follow-redirects@npm:^1.15.0":
version: 1.15.3
resolution: "follow-redirects@npm:1.15.3"
"follow-redirects@npm:^1.15.6":
version: 1.15.6
resolution: "follow-redirects@npm:1.15.6"
peerDependenciesMeta:
debug:
optional: true
checksum: 584da22ec5420c837bd096559ebfb8fe69d82512d5585004e36a3b4a6ef6d5905780e0c74508c7b72f907d1fa2b7bd339e613859e9c304d0dc96af2027fd0231
checksum: a62c378dfc8c00f60b9c80cab158ba54e99ba0239a5dd7c81245e5a5b39d10f0c35e249c3379eae719ff0285fff88c365dd446fab19dee771f1d76252df1bbf5
languageName: node
linkType: hard
@@ -924,12 +910,10 @@ __metadata:
languageName: node
linkType: hard
"globals@npm:^13.19.0":
version: 13.23.0
resolution: "globals@npm:13.23.0"
dependencies:
type-fest: ^0.20.2
checksum: 194c97cf8d1ef6ba59417234c2386549c4103b6e5f24b1ff1952de61a4753e5d2069435ba629de711a6480b1b1d114a98e2ab27f85e966d5a10c319c3bbd3dc3
"globals@npm:^14.0.0":
version: 14.0.0
resolution: "globals@npm:14.0.0"
checksum: 534b8216736a5425737f59f6e6a5c7f386254560c9f41d24a9227d60ee3ad4a9e82c5b85def0e212e9d92162f83a92544be4c7fd4c902cb913736c10e08237ac
languageName: node
linkType: hard
@@ -1006,9 +990,9 @@ __metadata:
linkType: hard
"ignore@npm:^5.2.0":
version: 5.2.4
resolution: "ignore@npm:5.2.4"
checksum: 3d4c309c6006e2621659311783eaea7ebcd41fe4ca1d78c91c473157ad6666a57a2df790fe0d07a12300d9aac2888204d7be8d59f9aaf665b1c7fcdb432517ef
version: 5.3.1
resolution: "ignore@npm:5.3.1"
checksum: 71d7bb4c1dbe020f915fd881108cbe85a0db3d636a0ea3ba911393c53946711d13a9b1143c7e70db06d571a5822c0a324a6bcde5c9904e7ca5047f01f1bf8cd3
languageName: node
linkType: hard
@@ -1183,7 +1167,7 @@ __metadata:
languageName: node
linkType: hard
"keyv@npm:^4.5.3":
"keyv@npm:^4.5.4":
version: 4.5.4
resolution: "keyv@npm:4.5.4"
dependencies:
@@ -1441,9 +1425,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "pgadmin4@workspace:."
dependencies:
axios: ^1.5.1
axios: ^1.6.8
bootstrap: ^4.5.3
eslint: ^8.50.0
eslint: ^9.0.0
nw: 0.77.0
languageName: unknown
linkType: soft
@@ -1574,17 +1558,6 @@ __metadata:
languageName: node
linkType: hard
"rimraf@npm:^3.0.2":
version: 3.0.2
resolution: "rimraf@npm:3.0.2"
dependencies:
glob: ^7.1.3
bin:
rimraf: bin.js
checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0
languageName: node
linkType: hard
"run-parallel@npm:^1.1.9":
version: 1.2.0
resolution: "run-parallel@npm:1.2.0"
@@ -1794,13 +1767,6 @@ __metadata:
languageName: node
linkType: hard
"type-fest@npm:^0.20.2":
version: 0.20.2
resolution: "type-fest@npm:0.20.2"
checksum: 4fb3272df21ad1c552486f8a2f8e115c09a521ad7a8db3d56d53718d0c907b62c6e9141ba5f584af3f6830d0872c521357e512381f24f7c44acae583ad517d73
languageName: node
linkType: hard
"unbzip2-stream@npm:^1.0.9":
version: 1.4.3
resolution: "unbzip2-stream@npm:1.4.3"
+1 -1
View File
@@ -1,5 +1,5 @@
requests>=2.21.0
requests[security]>=2.21.0
safety>=1.9.0
Sphinx==7.2.6
Sphinx==7.3.5
sphinxcontrib-youtube==1.4.1
+20
View File
@@ -469,6 +469,26 @@ DEFAULT_BINARY_PATHS = {
"ppas-16": ""
}
##########################################################################
# Admin can specify fixed binary paths to prevent users from changing.
# It will take precedence over DEFAULT_BINARY_PATHS.
FIXED_BINARY_PATHS = {
"pg": "",
"pg-12": "",
"pg-13": "",
"pg-14": "",
"pg-15": "",
"pg-16": "",
"ppas": "",
"ppas-12": "",
"ppas-13": "",
"ppas-14": "",
"ppas-15": "",
"ppas-16": ""
}
##########################################################################
# Test settings - used primarily by the regression suite, not for users
##########################################################################
+16 -19
View File
@@ -17,7 +17,7 @@
"@babel/preset-typescript": "^7.22.5",
"@emotion/core": "^10.0.14",
"@emotion/memoize": "^0.7.5",
"@emotion/react": "^11.1.5",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@emotion/utils": "^1.0.0",
"@svgr/webpack": "^8.1.0",
@@ -56,16 +56,13 @@
"process": "^0.11.10",
"prop-types": "^15.7.2",
"resize-observer-polyfill": "^1.5.1",
"sass": "^1.24.4",
"sass-loader": "^11.0.0",
"sass-resources-loader": "^2.2.1",
"shim-loader": "^1.0.1",
"style-loader": "^3.3.2",
"stylis": "^4.0.7",
"svgo": "^2.7.0",
"svgo-loader": "^2.2.0",
"terser-webpack-plugin": "^5.1.1",
"typescript": "^3.2.2",
"typescript": "^4.9.5",
"url-loader": "^4.1.1",
"webfonts-loader": "^8.0.1",
"webpack": "^5.88.2",
@@ -76,25 +73,26 @@
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.10.4",
"@babel/preset-react": "^7.12.13",
"@codemirror/lang-sql": "^6.5.5",
"@date-io/core": "^1.3.6",
"@date-io/date-fns": "1.x",
"@codemirror/lang-sql": "^6.6.3",
"@date-io/core": "^3.0.0",
"@date-io/date-fns": "3.x",
"@emotion/sheet": "^1.0.1",
"@fortawesome/fontawesome-free": "latest",
"@material-ui/core": "4.12.4",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "4.0.0-alpha.61",
"@material-ui/pickers": "3.3.10",
"@mui/icons-material": "^5.15.10",
"@mui/lab": "^5.0.0-alpha.165",
"@mui/material": "^5.15.10",
"@mui/styles": "^5.15.10",
"@mui/x-date-pickers": "^6.19.7",
"@projectstorm/react-diagrams": "^6.6.1",
"@simonwep/pickr": "^1.5.1",
"@szhsin/react-menu": "^2.2.0",
"@types/classnames": "^2.2.6",
"@types/react": "^16.7.18",
"@types/react-dom": "^17.0.11",
"@types/react": "^17.0.80",
"@types/react-dom": "^17.0.25",
"ajv": "^8.8.2",
"anti-trojan-source": "^1.4.0",
"aspen-decorations": "^1.0.2",
"axios": "^1.4.0",
"axios": "^1.6.8",
"babelify": "~10.0.0",
"bignumber.js": "^9.0.1",
"brace": "^0.11.1",
@@ -107,7 +105,7 @@
"convert-units": "^2.3.4",
"cssnano": "^5.0.2",
"dagre": "^0.8.4",
"date-fns": "^2.24.0",
"date-fns": "^3.6.0",
"diff-arrays-of-objects": "^1.1.8",
"hotkeys-js": "^3.13.3",
"html-to-image": "^1.11.11",
@@ -122,11 +120,10 @@
"moment": "^2.29.4",
"moment-timezone": "^0.5.34",
"notificar": "^1.0.1",
"notistack": "^1.0.10",
"notistack": "^2.0.8",
"path-fx": "^2.0.0",
"pathfinding": "^0.4.18",
"paths-js": "^0.4.9",
"popper.js": "^1.16.1",
"postcss": "^8.4.31",
"raf": "^3.4.1",
"rc-dock": "^3.2.9",
@@ -172,7 +169,7 @@
"bundle:watch": "yarn run linter && yarn run webpacker:watch",
"bundle:dev": "yarn run linter && yarn run webpacker",
"bundle:analyze": "cross-env NODE_ENV=production ANALYZE=true yarn run bundle:dev",
"bundle": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=2048 yarn run bundle:dev",
"bundle": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=3072 yarn run bundle:dev",
"test:js-once": "yarn run linter && yarn run jest --maxWorkers=50%",
"test:js": "yarn run test:js-once --watch",
"test:js-file": "yarn run test:js-once -t",
-7
View File
@@ -1,7 +0,0 @@
{
"dark": {
"disp_name": "dark_(Beta)",
"cssfile": "pgadmin.theme.dark",
"preview_img": "dark_preview.png"
}
}
+9 -5
View File
@@ -505,7 +505,9 @@ def create_app(app_name=None):
'WTF_CSRF_TIME_LIMIT': getattr(config, 'CSRF_TIME_LIMIT', None),
'WTF_CSRF_METHODS': ['GET', 'POST', 'PUT', 'DELETE'],
# Disable deliverable check for email addresss
'SECURITY_EMAIL_VALIDATOR_ARGS': config.SECURITY_EMAIL_VALIDATOR_ARGS
'SECURITY_EMAIL_VALIDATOR_ARGS': config.SECURITY_EMAIL_VALIDATOR_ARGS,
# Disable CSRF for unauthenticated endpoints
'SECURITY_CSRF_IGNORE_UNAUTH_ENDPOINTS': True
}))
app.config.update(dict({
@@ -519,7 +521,12 @@ def create_app(app_name=None):
security.init_app(app, user_datastore)
# register custom unauthorised handler.
app.login_manager.unauthorized_handler(pga_unauthorised)
if sys.version_info < (3, 8):
app.login_manager.unauthorized_handler(pga_unauthorised)
else:
# Flask-Security-Too > 5.4.* requires custom unauth handeler
# to be registeres with it.
security.unauthn_handler(pga_unauthorised)
# Set the permanent session lifetime to the specified value in config file.
app.permanent_session_lifetime = timedelta(
@@ -890,9 +897,6 @@ def create_app(app_name=None):
from flask_compress import Compress
Compress(app)
from pgadmin.misc.themes import themes
themes(app)
@app.context_processor
def inject_blueprint():
"""
+4 -3
View File
@@ -11,7 +11,8 @@
from flask import Response, render_template, request
from flask_babel import gettext
from flask_security import current_user, login_required
from flask_security import current_user
from pgadmin.user_login_check import pga_login_required
from pgadmin.utils import PgAdminModule
from pgadmin.utils.menu import MenuItem
from pgadmin.utils.constants import MIMETYPE_APP_JS
@@ -53,7 +54,7 @@ blueprint = AboutModule(MODULE_NAME, __name__, static_url_path='')
# A test page
##########################################################################
@blueprint.route("/", endpoint='index')
@login_required
@pga_login_required
def index():
"""Render the about box."""
info = {}
@@ -138,7 +139,7 @@ def detect_browser(request):
@blueprint.route("/about.js")
@login_required
@pga_login_required
def script():
"""render the required javascript"""
return Response(
@@ -10,9 +10,9 @@
import gettext from 'sources/gettext';
import url_for from 'sources/url_for';
import React, { useEffect, useState, useRef } from 'react';
import { Box, Grid, InputLabel } from '@material-ui/core';
import { Box, Grid, InputLabel } from '@mui/material';
import { DefaultButton } from '../../../static/js/components/Buttons';
import { makeStyles } from '@material-ui/styles';
import { makeStyles } from '@mui/styles';
import { InputText } from '../../../static/js/components/FormComponents';
import getApiInstance from '../../../static/js/api_instance';
import { copyToClipboard } from '../../../static/js/clipboard';
@@ -141,11 +141,11 @@ export default function AboutComponent() {
}}>{copyText}</DefaultButton>
</Box>
<Box flexGrow="1" paddingTop="1px">
<InputText style={{height: '100%'}} controlProps={{multiline: true}} inputStyle={{resize: 'none'}}
<InputText style={{height: '100%'}} controlProps={{multiline: true, rows: 8}} inputStyle={{resize: 'none'}}
value={aboutData.settings}/>
</Box>
</Box>
}
</Box>
);
}
}
+6 -8
View File
@@ -23,12 +23,10 @@ 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, \
get_safe_post_logout_redirect
from pgadmin.utils.constants import KERBEROS, INTERNAL, OAUTH2, LDAP,\
MessageType
import pgadmin.utils as pga_utils
from pgadmin.authenticate.registry import AuthSourceRegistry
MODULE_NAME = 'authenticate'
@@ -84,7 +82,7 @@ def socket_login_required(f):
return wrapped
class AuthenticateModule(PgAdminModule):
class AuthenticateModule(pga_utils.PgAdminModule):
def get_exposed_url_endpoints(self):
return ['authenticate.login']
@@ -135,7 +133,7 @@ def _login():
'Administrator.'),
MessageType.WARNING)
logout_user()
return redirect(get_safe_post_logout_redirect())
return redirect(pga_utils.get_safe_post_logout_redirect())
# Validate the user
if not auth_obj.validate():
@@ -161,7 +159,7 @@ def _login():
flash_login_attempt_error = None
flash(error, MessageType.WARNING)
return redirect(get_safe_post_logout_redirect())
return redirect(pga_utils.get_safe_post_logout_redirect())
# Authenticate the user
status, msg = auth_obj.authenticate()
@@ -177,7 +175,7 @@ def _login():
'authenticate.kerberos_login'), url_for('browser.index')))
flash(msg, MessageType.ERROR)
return redirect(get_safe_post_logout_redirect())
return redirect(pga_utils.get_safe_post_logout_redirect())
session['auth_source_manager'] = current_auth_obj
@@ -187,7 +185,7 @@ def _login():
if 'auth_obj' in session:
session.pop('auth_obj')
return redirect(get_safe_post_login_redirect())
return redirect(pga_utils.get_safe_post_login_redirect())
elif isinstance(msg, Response):
return msg
+3 -3
View File
@@ -18,7 +18,7 @@ from flask import request, Response, session,\
current_app, render_template, flash, url_for
from flask_security.views import _security
from flask_security.utils import logout_user
from flask_security import login_required
from pgadmin.user_login_check import pga_login_required
import config
from pgadmin.model import User
@@ -97,7 +97,7 @@ def init_app(app):
@blueprint.route("/update_ticket",
endpoint="update_ticket", methods=["GET"])
@pgCSRFProtect.exempt
@login_required
@pga_login_required
def kerberos_update_ticket():
"""
Update the kerberos ticket.
@@ -127,7 +127,7 @@ def init_app(app):
@blueprint.route("/validate_ticket",
endpoint="validate_ticket", methods=["GET"])
@pgCSRFProtect.exempt
@login_required
@pga_login_required
def kerberos_validate_ticket():
"""
Return the kerberos ticket lifetime left after getting the
+3 -2
View File
@@ -227,7 +227,7 @@ class EmailAuthentication(BaseMFAuth):
def _registration_view_after_code_sent(self, _form_data):
session['mfa_email_id'] = _form_data.get('send_to', None)
success, _, message = _send_code_to_email(
success, http_code, message = _send_code_to_email(
session['mfa_email_id']
)
@@ -239,7 +239,8 @@ class EmailAuthentication(BaseMFAuth):
label=email_authentication_label(),
auth_method=EMAIL_AUTH_METHOD,
message=message,
otp_placeholder=_("Enter code here")
otp_placeholder=_("Enter code here"),
http_code=http_code,
)
def registration_view(self, _form_data):
@@ -53,7 +53,6 @@ def init_dummy_auth_class():
def test_create_dummy_app(name=__name__):
import os
import pgadmin
from pgadmin.misc.themes import themes
def index():
return Response("<html><body>logged in</body></html>")
@@ -88,8 +87,6 @@ def test_create_dummy_app(name=__name__):
app.register_logout_hook = types.MethodType(__dummy_logout_hook, app)
themes(app)
return app
+3 -3
View File
@@ -13,7 +13,7 @@ from functools import wraps
from flask import url_for, session, request, redirect
from flask_login.utils import login_url
from flask_security import current_user
from flask_security import current_user, login_required
import config
from pgadmin.model import UserMFA, db
@@ -279,7 +279,7 @@ def mfa_required(wrapped):
registration_url = url_for('mfa.register')
if next_url.startswith(registration_url):
return url('browser.index')
return url_for('browser.index')
return next_url
@@ -290,8 +290,8 @@ def mfa_required(wrapped):
return redirect(login_url("mfa.register", next_url=get_next_url()))
@wraps(wrapped)
@login_required
def inner(*args, **kwargs):
def execute_func():
session['mfa_authenticated'] = True
return wrapped(*args, **kwargs)
+1 -1
View File
@@ -69,7 +69,6 @@ def init_app(app):
methods=['GET', 'POST'])
@pgCSRFProtect.exempt
def oauth_logout():
id_token = session['oauth2_token']['id_token']
logout_url = None
if 'oauth2_logout_url' in session:
logout_url = session['oauth2_logout_url']
@@ -81,6 +80,7 @@ def init_app(app):
logout_user()
if logout_url:
id_token = session['oauth2_token'].get('id_token')
return redirect(logout_url.format(
redirect_uri=request.url_root,
id_token=id_token))
@@ -43,7 +43,7 @@ function fetch_ticket_lifetime () {
if (ticket_lifetime > 0) {
return Promise.resolve(ticket_lifetime);
} else {
return Promise.reject();
return Promise.reject(new Error(null));
}
});
+30 -22
View File
@@ -27,8 +27,8 @@ import keyring
from flask import current_app, render_template, url_for, make_response, \
flash, Response, request, after_this_request, redirect, session
from flask_babel import gettext
from flask_gravatar import Gravatar
from flask_login import current_user, login_required
from libgravatar import Gravatar
from flask_security import current_user
from flask_login.utils import login_url
from flask_security.changeable import send_password_changed_notice
from flask_security.decorators import anonymous_user_required
@@ -64,6 +64,8 @@ from pgadmin.utils.constants import MIMETYPE_APP_JS, PGADMIN_NODE,\
from pgadmin.authenticate import AuthSourceManager
from pgadmin.utils.exception import CryptKeyMissing
from pgadmin.user_login_check import pga_login_required
try:
from flask_security.views import default_render_json
except ImportError as e:
@@ -360,24 +362,26 @@ def _get_supported_browser():
return browser_name, browser_known, version
@blueprint.add_app_template_filter
def gravatar(username):
"""
This function adds a template filter which
returns gravatar image for user.
:return: gravatar image
"""
g = Gravatar(username)
return g.get_image(
size=100,
rating='g',
default='retro'
)
@blueprint.route("/")
@pgCSRFProtect.exempt
@login_required
@mfa_required
@pga_login_required
def index():
"""Render and process the main browser window."""
# Register Gravatar module with the app only if required
if config.SHOW_GRAVATAR_IMAGE:
Gravatar(
current_app,
size=100,
rating='g',
default='retro',
force_default=False,
force_lower=True,
use_ssl=True,
base_url=None
)
# Check the browser is a supported version
# NOTE: If the checks here are updated, make sure the supported versions
@@ -468,16 +472,19 @@ def get_shared_storage_list():
@blueprint.route("/js/utils.js")
@pgCSRFProtect.exempt
@login_required
@pga_login_required
def utils():
layout = get_setting('Browser/Layout', default='')
snippets = []
prefs = Preferences.module('paths')
pg_help_path_pref = prefs.preference('pg_help_path')
pg_help_path = pg_help_path_pref.get()
# Added to have theme value available at app start page loading
prefs = Preferences.module('misc')
theme = prefs.preference('theme').get()
# Get sqleditor options
prefs = Preferences.module('sqleditor')
@@ -539,6 +546,7 @@ def utils():
render_template(
'browser/js/utils.js',
layout=layout,
theme=theme,
jssnippets=snippets,
pg_help_path=pg_help_path,
editor_tab_size=editor_tab_size,
@@ -585,7 +593,7 @@ def exposed_urls():
@blueprint.route("/js/error.js")
@pgCSRFProtect.exempt
@login_required
@pga_login_required
def error_js():
return make_response(
render_template('browser/js/error.js', _=gettext),
@@ -602,7 +610,7 @@ def messages_js():
@blueprint.route("/browser.css")
@pgCSRFProtect.exempt
@login_required
@pga_login_required
def browser_css():
"""Render and return CSS snippets from the nodes and modules."""
snippets = []
@@ -617,7 +625,7 @@ def browser_css():
@blueprint.route("/nodes/", endpoint="nodes")
@login_required
@pga_login_required
def get_nodes():
"""Build a list of treeview nodes from the child nodes."""
nodes = []
@@ -924,7 +932,7 @@ if hasattr(config, 'SECURITY_CHANGEABLE') and config.SECURITY_CHANGEABLE:
@blueprint.route("/change_password", endpoint="change_password",
methods=['GET', 'POST'])
@pgCSRFProtect.exempt
@login_required
@pga_login_required
def change_password():
"""View function which handles a change password request."""
+13 -12
View File
@@ -14,7 +14,8 @@ from abc import ABCMeta, abstractmethod
from flask import request, jsonify, render_template
from flask_babel import gettext
from flask_security import current_user, login_required
from flask_security import current_user
from pgadmin.user_login_check import pga_login_required
from pgadmin.browser import BrowserPluginModule
from pgadmin.browser.utils import NodeView
from pgadmin.utils.ajax import make_json_response, gone, \
@@ -162,7 +163,7 @@ class ServerGroupView(NodeView):
parent_ids = []
ids = [{'type': 'int', 'id': 'gid'}]
@login_required
@pga_login_required
def list(self):
res = []
@@ -176,7 +177,7 @@ class ServerGroupView(NodeView):
return ajax_response(response=res, status=200)
@login_required
@pga_login_required
def delete(self, gid):
"""Delete a server group node in the settings database"""
@@ -232,7 +233,7 @@ class ServerGroupView(NodeView):
return make_json_response(result=request.form)
@login_required
@pga_login_required
def update(self, gid):
"""Update the server-group properties"""
@@ -281,7 +282,7 @@ class ServerGroupView(NodeView):
)
)
@login_required
@pga_login_required
def properties(self, gid):
"""Update the server-group properties"""
@@ -299,7 +300,7 @@ class ServerGroupView(NodeView):
status=200
)
@login_required
@pga_login_required
def create(self):
"""Creates new server-group """
data = request.form if request.form else json.loads(
@@ -349,23 +350,23 @@ class ServerGroupView(NodeView):
success=0,
errormsg=gettext('No server group name was specified'))
@login_required
@pga_login_required
def sql(self, gid):
return make_json_response(status=422)
@login_required
@pga_login_required
def modified_sql(self, gid):
return make_json_response(status=422)
@login_required
@pga_login_required
def statistics(self, gid):
return make_json_response(status=422)
@login_required
@pga_login_required
def dependencies(self, gid):
return make_json_response(status=422)
@login_required
@pga_login_required
def dependents(self, gid):
return make_json_response(status=422)
@@ -394,7 +395,7 @@ class ServerGroupView(NodeView):
groups.append(group)
return groups
@login_required
@pga_login_required
def nodes(self, gid=None):
"""Return a JSON document listing the server groups for the user"""
nodes = []
@@ -13,8 +13,10 @@ import pgadmin.browser.server_groups as sg
from flask import render_template, request, make_response, jsonify, \
current_app, url_for, session
from flask_babel import gettext
from flask_security import current_user, login_required
from flask_security import current_user
from pgadmin.user_login_check import pga_login_required
from psycopg.conninfo import make_conninfo, conninfo_to_dict
from pgadmin.browser.server_groups.servers.types import ServerType
from pgadmin.browser.utils import PGChildNodeView
from pgadmin.utils.ajax import make_json_response, bad_request, forbidden, \
@@ -30,7 +32,8 @@ from pgadmin.utils.driver import get_driver
from pgadmin.utils.master_password import get_crypt_key
from pgadmin.utils.exception import CryptKeyMissing
from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
from pgadmin.browser.server_groups.servers.utils import is_valid_ipaddress
from pgadmin.browser.server_groups.servers.utils import \
is_valid_ipaddress, get_replication_type
from pgadmin.utils.constants import UNAUTH_REQ, MIMETYPE_APP_JS, \
SERVER_CONNECTION_CLOSED
from sqlalchemy import or_
@@ -216,7 +219,7 @@ class ServerModule(sg.ServerGroupPluginModule):
return servers
@login_required
@pga_login_required
def get_nodes(self, gid):
"""Return a JSON document listing the server groups for the user"""
@@ -343,6 +346,9 @@ class ServerModule(sg.ServerGroupPluginModule):
from .tablespaces import blueprint as module
self.submodules.append(module)
from .replica_nodes import blueprint as module
self.submodules.append(module)
super().register(app, options)
# We do not have any preferences for server node.
@@ -469,7 +475,7 @@ class ServerNode(PGChildNodeView):
}],
'check_pgpass': [{'get': 'check_pgpass'}],
'clear_saved_password': [{'put': 'clear_saved_password'}],
'clear_sshtunnel_password': [{'put': 'clear_sshtunnel_password'}]
'clear_sshtunnel_password': [{'put': 'clear_sshtunnel_password'}],
})
SSL_MODES = ['prefer', 'require', 'verify-ca', 'verify-full']
@@ -559,7 +565,7 @@ class ServerNode(PGChildNodeView):
data['connection_params'] = existing_conn_params
@login_required
@pga_login_required
def nodes(self, gid):
res = []
"""
@@ -645,7 +651,7 @@ class ServerNode(PGChildNodeView):
return make_json_response(result=res)
@login_required
@pga_login_required
def node(self, gid, sid):
"""Return a JSON document listing the server groups for the user"""
server = Server.query.filter_by(id=sid).first()
@@ -728,7 +734,7 @@ class ServerNode(PGChildNodeView):
success=0,
errormsg=e.message)
@login_required
@pga_login_required
def delete(self, gid, sid):
"""Delete a server node in the settings database."""
servers = Server.query.filter_by(user_id=current_user.id, id=sid)
@@ -778,7 +784,7 @@ class ServerNode(PGChildNodeView):
return make_json_response(success=1,
info=gettext("Server deleted"))
@login_required
@pga_login_required
def update(self, gid, sid):
"""Update the server settings"""
server = Server.query.filter_by(id=sid).first()
@@ -983,7 +989,7 @@ class ServerNode(PGChildNodeView):
).format(disp_lbl[arg])
)
@login_required
@pga_login_required
def list(self, gid):
"""
Return list of attributes of all servers.
@@ -1030,7 +1036,7 @@ class ServerNode(PGChildNodeView):
response=res
)
@login_required
@pga_login_required
def properties(self, gid, sid):
"""Return list of attributes of a server"""
@@ -1144,12 +1150,12 @@ class ServerNode(PGChildNodeView):
con_info_ord = OrderedDict([('host', server.host),
('port', server.port),
('dbname', db_name),
('user', server.username)])
('user', manager.user)])
con_info_ord.update(con_info)
display_conn_string = make_conninfo(**con_info_ord)
return display_conn_string
@login_required
@pga_login_required
def create(self, gid):
"""Add a server node to the settings database"""
required_args = ['name', 'db']
@@ -1247,6 +1253,8 @@ class ServerNode(PGChildNodeView):
connected = False
user = None
manager = None
replication_type = None
tunnel_password_saved = False
if 'connect_now' in data and data['connect_now']:
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(
@@ -1323,7 +1331,10 @@ class ServerNode(PGChildNodeView):
KEY_RING_TUNNEL_FORMAT.format(server.name,
server.id),
tunnel_password)
tunnel_password_saved = True
replication_type = get_replication_type(conn,
manager.version)
user = manager.user_info
connected = True
@@ -1337,6 +1348,7 @@ class ServerNode(PGChildNodeView):
username=server.username,
user=user,
connected=connected,
replication_type=replication_type,
shared=server.shared,
server_type=manager.server_type
if manager and manager.server_type
@@ -1348,7 +1360,8 @@ class ServerNode(PGChildNodeView):
gss_authenticated=manager.gss_authenticated if
manager and manager.gss_authenticated else False,
is_password_saved=bool(server.save_password),
is_tunnel_password_saved=bool(server.tunnel_password)
is_tunnel_password_saved=tunnel_password_saved,
user_id=server.user_id
)
)
@@ -1364,15 +1377,15 @@ class ServerNode(PGChildNodeView):
errormsg=str(e)
)
@login_required
@pga_login_required
def sql(self, gid, sid):
return make_json_response(data='')
@login_required
@pga_login_required
def modified_sql(self, gid, sid):
return make_json_response(data='')
@login_required
@pga_login_required
def statistics(self, gid, sid):
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection()
@@ -1396,11 +1409,11 @@ class ServerNode(PGChildNodeView):
)
)
@login_required
@pga_login_required
def dependencies(self, gid, sid):
return make_json_response(data='')
@login_required
@pga_login_required
def dependents(self, gid, sid):
return make_json_response(data='')
@@ -1427,6 +1440,7 @@ class ServerNode(PGChildNodeView):
in_recovery = None
wal_paused = None
errmsg = None
replication_type = None
if connected:
status, result, in_recovery, wal_paused =\
recovery_state(conn, manager.version)
@@ -1436,10 +1450,13 @@ class ServerNode(PGChildNodeView):
manager.release()
errmsg = "{0} : {1}".format(server.name, result)
replication_type = get_replication_type(conn, manager.version)
return make_json_response(
data={
'icon': server_icon_and_background(connected, manager, server),
'connected': connected,
'replication_type': replication_type,
'in_recovery': in_recovery,
'wal_pause': wal_paused,
'server_type': manager.server_type if connected else "pg",
@@ -1709,6 +1726,8 @@ class ServerNode(PGChildNodeView):
_, _, in_recovery, wal_paused =\
recovery_state(conn, manager.version)
replication_type = get_replication_type(conn, manager.version)
return make_json_response(
success=1,
info=gettext("Server connected."),
@@ -1716,6 +1735,7 @@ class ServerNode(PGChildNodeView):
'icon': server_icon_and_background(True, manager, server),
'connected': True,
'server_type': manager.server_type,
'replication_type': replication_type,
'type': manager.server_type,
'version': manager.version,
'db': manager.db,
@@ -2146,8 +2166,8 @@ class ServerNode(PGChildNodeView):
session['allow_save_password'] else False,
"allow_save_tunnel_password":
True if config.ALLOW_SAVE_TUNNEL_PASSWORD and
'allow_save_tunnel_password' in session and
session['allow_save_tunnel_password'] else False
'allow_save_password' in session and
session['allow_save_password'] else False
}
return make_json_response(
success=0,
@@ -128,6 +128,9 @@ class DatabaseModule(CollectionNodeModule):
from .subscriptions import blueprint as module
self.submodules.append(module)
from .dbms_job_scheduler import blueprint as module
self.submodules.append(module)
super().register(app, options)
@@ -76,11 +76,10 @@ define('pgadmin.node.cast', [
return new Promise((resolve, reject)=>{
const api = getApiInstance();
let _url = pgBrowser.Nodes['cast'].generate_url.apply(
pgBrowser.Nodes['cast'], [
null, 'get_functions', itemNodeData, false,
treeNodeInfo,
]);
let _url = pgBrowser.Nodes['cast'].generate_url(
null, 'get_functions', itemNodeData, false,
treeNodeInfo,
);
let data = {'srctyp' : srcTyp, 'trgtyp' : trgtyp};
if(srcTyp != undefined && srcTyp != '' &&
@@ -0,0 +1,280 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
""" Implements DBMS Job Scheduler objects Node."""
from functools import wraps
from flask import render_template
from flask_babel import gettext
from pgadmin.browser.collection import CollectionNodeModule
from pgadmin.browser.server_groups.servers import databases
from pgadmin.browser.utils import PGChildNodeView
from pgadmin.utils.ajax import (make_json_response, internal_server_error,
make_response as ajax_response)
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.utils.constants import DBMS_JOB_SCHEDULER_ID
class DBMSJobSchedulerModule(CollectionNodeModule):
"""
class DBMSJobSchedulerModule(CollectionNodeModule)
A module class for DBMS Job Scheduler objects node derived
from CollectionNodeModule.
Methods:
-------
* __init__(*args, **kwargs)
- Method is used to initialize the DBMS Job Scheduler objects, and it's
base module.
* get_nodes(gid, sid, did, scid, coid)
- Method is used to generate the browser collection node.
* script_load()
- Load the module script for DBMS Job Scheduler objects, when any of the
server node is initialized.
* backend_supported(manager, **kwargs)
* registert(self, app, options)
- Override the default register function to automagically register
sub-modules at once.
"""
_NODE_TYPE = 'dbms_job_scheduler'
_COLLECTION_LABEL = gettext("DBMS Job Scheduler")
def __init__(self, *args, **kwargs):
"""
Method is used to initialize the DBMSJobSchedulerModule, and it's base
module.
Args:
*args:
**kwargs:
"""
super().__init__(*args, **kwargs)
self.min_ver = None
self.max_ver = None
@property
def node_icon(self):
"""
icon to be displayed for the browser nodes
"""
return 'icon-coll-dbms_job_scheduler'
def get_nodes(self, gid, sid, did):
"""
Generate the collection node
"""
if self.show_node:
yield self.generate_browser_node(DBMS_JOB_SCHEDULER_ID, did,
self._COLLECTION_LABEL, None)
@property
def script_load(self):
"""
Load the module script for server, when any of the database node is
initialized.
"""
return databases.DatabaseModule.node_type
def backend_supported(self, manager, **kwargs):
"""
Function is used to check the pre-requisite for this node.
Args:
manager:
**kwargs:
Returns:
"""
if hasattr(self, 'show_node') and not self.show_node:
return False
# Get the connection for the respective database
conn = manager.connection(did=kwargs['did'])
# Checking whether both 'edb_job_scheduler' and 'dbms_scheduler'
# extensions are created or not.
status, res = conn.execute_scalar("""
SELECT COUNT(*) FROM pg_extension WHERE extname IN (
'edb_job_scheduler', 'dbms_scheduler') """)
if status and int(res) == 2:
# Get the list of databases specified for the edb_job_scheduler
status, res = conn.execute_scalar("""
SHOW edb_job_scheduler.database_list""")
# If database is available in the specified list than return True.
if status and res and conn.db in res:
return True
return False
@property
def module_use_template_javascript(self):
"""
Returns whether Jinja2 template is used for generating the javascript
module.
"""
return False
def register(self, app, options):
"""
Override the default register function to automagically register
sub-modules at once.
"""
from .dbms_jobs import blueprint as module
self.submodules.append(module)
from .dbms_programs import blueprint as module
self.submodules.append(module)
from .dbms_schedules import blueprint as module
self.submodules.append(module)
super().register(app, options)
blueprint = DBMSJobSchedulerModule(__name__)
class DBMSJobSchedulerView(PGChildNodeView):
"""
class DBMSJobSchedulerView(PGChildNodeView)
A view class for cast node derived from PGChildNodeView. This class is
responsible for all the stuff related to view like
create/update/delete cast, showing properties of cast node,
showing sql in sql pane.
Methods:
-------
* __init__(**kwargs)
- Method is used to initialize the DBMSJobSchedulerView, and it's
base view.
* check_precondition()
- This function will behave as a decorator which will checks
database connection before running view, it will also attach
manager,conn & template_path properties to self
* nodes()
- This function will use to create all the child node within that
collection. Here it will create all the scheduler nodes.
* properties(gid, sid, did, jsid)
- This function will show the properties of the selected job node
"""
node_type = blueprint.node_type
BASE_TEMPLATE_PATH = 'dbms_job_scheduler/ppas/#{0}#'
parent_ids = [
{'type': 'int', 'id': 'gid'},
{'type': 'int', 'id': 'sid'},
{'type': 'int', 'id': 'did'}
]
ids = [
{'type': 'int', 'id': 'jsid'}
]
operations = dict({
'obj': [
{'get': 'properties'},
{'get': 'list'}
],
'children': [{
'get': 'children'
}],
'nodes': [{'get': 'nodes'}, {'get': 'nodes'}]
})
def _init_(self, **kwargs):
self.conn = None
self.template_path = None
self.manager = None
super().__init__(**kwargs)
def check_precondition(f):
"""
This function will behave as a decorator which will check the
database connection before running view. It will also attach
manager, conn & template_path properties to self
"""
@wraps(f)
def wrap(*args, **kwargs):
self = args[0]
self.driver = get_driver(PG_DEFAULT_DRIVER)
self.manager = self.driver.connection_manager(kwargs['sid'])
self.conn = self.manager.connection(did=kwargs['did'])
# Set the template path for the SQL scripts
self.template_path = self.BASE_TEMPLATE_PATH.format(
self.manager.version)
# Here args[0] will hold self & kwargs will hold gid,sid,did
return f(*args, **kwargs)
return wrap
@check_precondition
def nodes(self, gid, sid, did):
"""
This function will use to create all the child nodes within the
collection.
"""
return make_json_response(
data=[],
status=200
)
@check_precondition
def list(self, gid, sid, did):
"""
This function will show the run details of all the jobs.
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
"""
try:
sql = render_template(
"/".join([self.template_path, 'get_job_run_details.sql']))
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
return ajax_response(
response=res['rows'],
status=200
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def properties(self, gid, sid, did, jsid):
"""
Args:
gid: Server Group ID
sid: Server ID
did: Database ID
jsid: Job Scheduler ID
"""
return make_json_response(
data=[],
status=200
)
DBMSJobSchedulerView.register_node_view(blueprint)
@@ -0,0 +1,785 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
""" Implements DBMS Job objects Node."""
import json
from functools import wraps
from flask import render_template, request, jsonify
from flask_babel import gettext
from pgadmin.browser.collection import CollectionNodeModule
from pgadmin.browser.server_groups.servers import databases
from pgadmin.browser.utils import PGChildNodeView
from pgadmin.utils.ajax import make_json_response, gone, \
make_response as ajax_response, internal_server_error, success_return
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.utils.constants import DBMS_JOB_SCHEDULER_ID
from pgadmin.browser.server_groups.servers.databases.schemas.functions.utils \
import format_arguments_from_db
from pgadmin.browser.server_groups.servers.databases.dbms_job_scheduler.utils \
import (resolve_calendar_string, create_calendar_string,
get_formatted_program_args)
class DBMSJobModule(CollectionNodeModule):
"""
class DBMSJobModule(CollectionNodeModule)
A module class for DBMS Job objects node derived
from CollectionNodeModule.
Methods:
-------
* get_nodes(gid, sid, did)
- Method is used to generate the browser collection node.
* script_load()
- Load the module script for DBMS Job objects, when any of
the server node is initialized.
"""
_NODE_TYPE = 'dbms_job'
_COLLECTION_LABEL = gettext("DBMS Jobs")
@property
def collection_icon(self):
"""
icon to be displayed for the browser collection node
"""
return 'icon-coll-pga_job'
@property
def node_icon(self):
"""
icon to be displayed for the browser nodes
"""
return 'icon-pga_job'
def get_nodes(self, gid, sid, did, jsid):
"""
Generate the collection node
"""
if self.show_node:
yield self.generate_browser_collection_node(did)
@property
def node_inode(self):
"""
Override this property to make the node a leaf node.
Returns: False as this is the leaf node
"""
return False
@property
def script_load(self):
"""
Load the module script for server, when any of the database node is
initialized.
"""
return databases.DatabaseModule.node_type
@property
def module_use_template_javascript(self):
"""
Returns whether Jinja2 template is used for generating the javascript
module.
"""
return False
blueprint = DBMSJobModule(__name__)
class DBMSJobView(PGChildNodeView):
"""
class DBMSJobView(PGChildNodeView)
A view class for DBMSJob node derived from PGChildNodeView.
This class is responsible for all the stuff related to view like
updating job node, showing properties, showing sql in sql pane.
Methods:
-------
* __init__(**kwargs)
- Method is used to initialize the DBMSJobView, and it's base view.
* check_precondition()
- This function will behave as a decorator which will checks
database connection before running view, it will also attaches
manager,conn & template_path properties to self
* list()
- This function is used to list all the job nodes within that
collection.
* nodes()
- This function will use to create all the child node within that
collection. Here it will create all the job node.
* properties(gid, sid, did, jsid, jsjobid)
- This function will show the properties of the selected job node
* create(gid, sid, did, jsid, jsjobid)
- This function will create the new job object
* msql(gid, sid, did, jsid, jsjobid)
- This function is used to return modified SQL for the
selected job node
* sql(gid, sid, did, jsid, jsjobid)
- Dummy response for sql panel
* delete(gid, sid, did, jsid, jsjobid)
- Drops job
"""
node_type = blueprint.node_type
BASE_TEMPLATE_PATH = 'dbms_jobs/ppas/#{0}#'
PROGRAM_TEMPLATE_PATH = 'dbms_programs/ppas/#{0}#'
SCHEDULE_TEMPLATE_PATH = 'dbms_schedules/ppas/#{0}#'
parent_ids = [
{'type': 'int', 'id': 'gid'},
{'type': 'int', 'id': 'sid'},
{'type': 'int', 'id': 'did'},
{'type': 'int', 'id': 'jsid'}
]
ids = [
{'type': 'int', 'id': 'jsjobid'}
]
operations = dict({
'obj': [
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
{'get': 'list', 'post': 'create', 'delete': 'delete'}
],
'nodes': [{'get': 'nodes'}, {'get': 'nodes'}],
'msql': [{'get': 'msql'}, {'get': 'msql'}],
'sql': [{'get': 'sql'}],
'get_procedures': [{}, {'get': 'get_procedures'}],
'enable_disable': [{'put': 'enable_disable'}],
'get_programs': [{}, {'get': 'get_programs'}],
'get_schedules': [{}, {'get': 'get_schedules'}],
'run_job': [{'put': 'run_job'}],
})
def _init_(self, **kwargs):
self.conn = None
self.template_path = None
self.pr_template_path = None
self.sch_template_path = None
self.manager = None
super().__init__(**kwargs)
def check_precondition(f):
"""
This function will behave as a decorator which will check the
database connection before running view. It will also attach
manager, conn & template_path properties to self
"""
@wraps(f)
def wrap(*args, **kwargs):
# Here args[0] will hold self & kwargs will hold gid,sid,did
self = args[0]
self.driver = get_driver(PG_DEFAULT_DRIVER)
self.manager = self.driver.connection_manager(kwargs['sid'])
self.conn = self.manager.connection(did=kwargs['did'])
# Set the template path for the SQL scripts
self.template_path = self.BASE_TEMPLATE_PATH.format(
self.manager.version)
self.pr_template_path = self.PROGRAM_TEMPLATE_PATH.format(
self.manager.version)
self.sch_template_path = self.SCHEDULE_TEMPLATE_PATH.format(
self.manager.version)
return f(*args, **kwargs)
return wrap
@check_precondition
def list(self, gid, sid, did, jsid):
"""
This function is used to list all the job nodes within
that collection.
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
"""
sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]))
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
return ajax_response(
response=res['rows'],
status=200
)
@check_precondition
def nodes(self, gid, sid, did, jsid, jsjobid=None):
"""
This function is used to create all the child nodes within
the collection. Here it will create all the job nodes.
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
"""
res = []
try:
sql = render_template(
"/".join([self.template_path, self._NODES_SQL]))
status, result = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=result)
if jsjobid is not None:
if len(result['rows']) == 0:
return gone(
errormsg=gettext("Could not find the specified job.")
)
row = result['rows'][0]
return make_json_response(
data=self.blueprint.generate_browser_node(
row['jsjobid'],
DBMS_JOB_SCHEDULER_ID,
row['jsjobname'],
is_enabled=row['jsjobenabled'],
icon="icon-pga_job" if row['jsjobenabled'] else
"icon-pga_job-disabled",
description=row['jsjobdesc']
)
)
for row in result['rows']:
res.append(
self.blueprint.generate_browser_node(
row['jsjobid'],
DBMS_JOB_SCHEDULER_ID,
row['jsjobname'],
is_enabled=row['jsjobenabled'],
icon="icon-pga_job" if row['jsjobenabled'] else
"icon-pga_job-disabled",
description=row['jsjobdesc']
)
)
return make_json_response(
data=res,
status=200
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def properties(self, gid, sid, did, jsid, jsjobid):
"""
This function will show the properties of the selected job node.
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
jsjobid: Job ID
"""
try:
status, data = self._fetch_properties(jsjobid)
if not status:
return data
return ajax_response(
response=data,
status=200
)
except Exception as e:
return internal_server_error(errormsg=str(e))
def _fetch_properties(self, jsjobid):
"""
This function is used to fetch the properties.
Args:
jsjobid:
"""
sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]),
jsjobid=jsjobid
)
status, res = self.conn.execute_dict(sql)
if not status:
return False, internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return False, gone(
errormsg=gettext("Could not find the specified job.")
)
data = res['rows'][0]
# If 'jsjobscname' and 'jsjobprname' in data then set the jsjobtype
if ('jsjobscname' in data and data['jsjobscname'] is not None and
'jsjobprname' in data and data['jsjobprname'] is not None):
data['jsjobtype'] = 'p'
else:
data['jsjobtype'] = 's'
# Resolve the repeat interval string
if 'jsscrepeatint' in data:
(freq, by_date, by_month, by_month_day, by_weekday, by_hour,
by_minute) = resolve_calendar_string(
data['jsscrepeatint'])
data['jsscfreq'] = freq
data['jsscdate'] = by_date
data['jsscmonths'] = by_month
data['jsscmonthdays'] = by_month_day
data['jsscweekdays'] = by_weekday
data['jsschours'] = by_hour
data['jsscminutes'] = by_minute
# Get Program's arguments
sql = render_template(
"/".join([self.pr_template_path, self._PROPERTIES_SQL]),
jsprid=data['program_id']
)
status, res_program = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res_program)
# Update the data dictionary.
if len(res_program['rows']) > 0:
# Get the formatted program args
get_formatted_program_args(self.pr_template_path, self.conn,
res_program['rows'][0])
# Get the job argument value
self.get_job_args_value(self.template_path, self.conn,
data['jsjobname'],
res_program['rows'][0])
data['jsprarguments'] = res_program['rows'][0]['jsprarguments'] \
if 'jsprarguments' in res_program['rows'][0] else []
return True, data
@check_precondition
def create(self, gid, sid, did, jsid):
"""
This function will create the job node.
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
"""
data = json.loads(request.data)
try:
# Get the SQL
sql, _, _ = self.get_sql(None, data)
status, res = self.conn.execute_void('BEGIN')
if not status:
return internal_server_error(errormsg=res)
status, res = self.conn.execute_scalar(sql)
if not status:
if self.conn.connected():
self.conn.execute_void('END')
return internal_server_error(errormsg=res)
self.conn.execute_void('END')
# Get the newly created job id
sql = render_template(
"/".join([self.template_path, 'get_job_id.sql']),
job_name=data['jsjobname'], conn=self.conn
)
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
errormsg=gettext("Job creation failed.")
)
row = res['rows'][0]
return jsonify(
node=self.blueprint.generate_browser_node(
row['jsjobid'],
DBMS_JOB_SCHEDULER_ID,
row['jsjobname'],
is_enabled=row['jsjobenabled'],
icon="icon-pga_job" if row['jsjobenabled'] else
"icon-pga_job-disabled"
)
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def update(self, gid, sid, did, jsid, jsjobid=None):
"""
This function will update job object
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
"""
data = request.form if request.form else json.loads(
request.data
)
try:
sql, jobname, jobenabled = self.get_sql(jsjobid, data)
status, res = self.conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
return jsonify(
node=self.blueprint.generate_browser_node(
jsjobid,
DBMS_JOB_SCHEDULER_ID,
jobname,
icon="icon-pga_job" if jobenabled else
"icon-pga_job-disabled"
)
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def delete(self, gid, sid, did, jsid, jsjobid=None):
"""Delete the Job."""
if jsjobid is None:
data = request.form if request.form else json.loads(
request.data
)
else:
data = {'ids': [jsjobid]}
try:
for jsjobid in data['ids']:
status, data = self._fetch_properties(jsjobid)
if not status:
return data
jsjobname = data['jsjobname']
status, res = self.conn.execute_void(
render_template(
"/".join([self.template_path, self._DELETE_SQL]),
job_name=jsjobname, conn=self.conn
)
)
if not status:
return internal_server_error(errormsg=res)
return make_json_response(success=1)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def msql(self, gid, sid, did, jsid, jsjobid=None):
"""
This function is used to return modified SQL for the
selected job node.
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
jsjobid: Job ID (optional)
"""
data = {}
for k, v in request.args.items():
try:
# comments should be taken as is because if user enters a
# json comment it is parsed by loads which should not happen
if k in ('jsjobdesc',):
data[k] = v
else:
data[k] = json.loads(v)
except ValueError:
data[k] = v
sql, _, _ = self.get_sql(jsjobid, data)
return make_json_response(
data=sql,
status=200
)
def get_sql(self, jsjobid, data):
"""
This function is used to get the SQL.
"""
sql = ''
name = ''
enabled = True
if jsjobid is None:
name = data['jsjobname']
enabled = data['jsjobenabled']
# Create calendar string for repeat interval
repeat_interval = create_calendar_string(
data['jsscfreq'], data['jsscdate'], data['jsscmonths'],
data['jsscmonthdays'], data['jsscweekdays'], data['jsschours'],
data['jsscminutes'])
sql = render_template(
"/".join([self.template_path, self._CREATE_SQL]),
job_name=data['jsjobname'],
internal_job_type=data['jsjobtype'],
job_type=data['jsprtype'],
job_action=data['jsprproc']
if data['jsprtype'] == 'STORED_PROCEDURE' else
data['jsprcode'],
enabled=data['jsjobenabled'],
comments=data['jsjobdesc'],
number_of_arguments=data['jsprnoofargs'],
start_date=data['jsscstart'],
repeat_interval=repeat_interval,
end_date=data['jsscend'],
program_name=data['jsjobprname'],
schedule_name=data['jsjobscname'],
arguments=data['jsprarguments'] if 'jsprarguments' in data
else [],
conn=self.conn
)
elif jsjobid is not None and 'jsprarguments' in data:
status, res = self._fetch_properties(jsjobid)
if not status:
return res
name = res['jsjobname']
enabled = res['jsjobenabled']
sql = render_template(
"/".join([self.template_path, self._UPDATE_SQL]),
job_name=res['jsjobname'],
changed_value=data['jsprarguments']['changed'],
conn=self.conn
)
return sql, name, enabled
@check_precondition
def sql(self, gid, sid, did, jsid, jsjobid):
"""
This function will generate sql for the sql panel
"""
try:
status, data = self._fetch_properties(jsjobid)
if not status:
return ''
SQL = render_template(
"/".join([self.template_path, self._CREATE_SQL]),
display_comments=True,
job_name=data['jsjobname'],
internal_job_type=data['jsjobtype'],
job_type=data['jsprtype'],
job_action=data['jsprproc']
if data['jsprtype'] == 'STORED_PROCEDURE' else
data['jsprcode'],
enabled=data['jsjobenabled'],
comments=data['jsjobdesc'],
number_of_arguments=data['jsprnoofargs'],
start_date=data['jsscstart'],
repeat_interval=data['jsscrepeatint'],
end_date=data['jsscend'],
program_name=data['jsjobprname'],
schedule_name=data['jsjobscname'],
arguments=data['jsprarguments'] if 'jsprarguments' in data
else [],
conn=self.conn
)
return ajax_response(response=SQL)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def get_procedures(self, gid, sid, did, jsid=None):
"""
This function will return procedure list
:param gid: group id
:param sid: server id
:param did: database id
:return:
"""
res = []
sql = render_template("/".join([self.pr_template_path,
'get_procedures.sql']),
datlastsysoid=self._DATABASE_LAST_SYSTEM_OID)
status, rset = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
# Get formatted Arguments
frmtd_params, _ = format_arguments_from_db(
self.template_path, self.conn, row)
res.append({'label': row['proc_name'],
'value': row['proc_name'],
'no_of_args': row['number_of_arguments'],
'arguments': frmtd_params['arguments']
})
return make_json_response(
data=res,
status=200
)
@check_precondition
def enable_disable(self, gid, sid, did, jsid, jsjobid=None):
"""
This function is used to enable/disable job.
"""
data = request.form if request.form else json.loads(
request.data
)
status, res = self.conn.execute_void(
render_template(
"/".join([self.pr_template_path, 'enable_disable.sql']),
name=data['job_name'],
is_enable=data['is_enable_job'], conn=self.conn
)
)
if not status:
return internal_server_error(errormsg=res)
return make_json_response(
success=1,
info=gettext("Job enabled") if data['is_enable_job'] else
gettext('Job disabled'),
data={
'sid': sid,
'did': did,
'jsid': jsid,
'jsjobid': jsjobid
}
)
@check_precondition
def get_programs(self, gid, sid, did, jsid=None):
"""
This function will return procedure list
:param gid: group id
:param sid: server id
:param did: database id
:return:
"""
res = []
sql = render_template("/".join([self.pr_template_path,
self._NODES_SQL]),
datlastsysoid=self._DATABASE_LAST_SYSTEM_OID)
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['jsprname'],
'value': row['jsprname']})
return make_json_response(
data=res,
status=200
)
@check_precondition
def get_schedules(self, gid, sid, did, jsid=None):
"""
This function will return procedure list
:param gid: group id
:param sid: server id
:param did: database id
:return:
"""
res = []
sql = render_template("/".join([self.sch_template_path,
self._NODES_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['jsscname'],
'value': row['jsscname']
})
return make_json_response(
data=res,
status=200
)
@check_precondition
def run_job(self, gid, sid, did, jsid, jsjobid=None):
"""
This function is used to run the job now.
"""
data = request.form if request.form else json.loads(
request.data
)
try:
status, res = self.conn.execute_void(
render_template(
"/".join([self.template_path, 'run_job.sql']),
job_name=data['job_name'], conn=self.conn
)
)
if not status:
return internal_server_error(errormsg=res)
return success_return(
message=gettext("Started the Job execution.")
)
except Exception as e:
return internal_server_error(errormsg=str(e))
def get_job_args_value(self, template_path, conn, jobname, data):
"""
This function is used to get the job arguments value.
Args:
template_path:
conn:
jobname:
data:
Returns:
"""
if 'jsprarguments' in data and len(data['jsprarguments']) > 0:
for args in data['jsprarguments']:
sql = render_template(
"/".join([template_path, 'get_job_args_value.sql']),
job_name=jobname,
arg_name=args['argname'], conn=self.conn)
status, res = conn.execute_scalar(sql)
if not status:
return internal_server_error(errormsg=res)
args['argval'] = res
DBMSJobView.register_node_view(blueprint)
@@ -0,0 +1,234 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import DBMSJobSchema from './dbms_job.ui';
import { getNodeAjaxOptions } from '../../../../../../../static/js/node_ajax';
import getApiInstance from '../../../../../../../../static/js/api_instance';
define('pgadmin.node.dbms_job', [
'sources/gettext', 'sources/url_for', 'sources/pgadmin',
'pgadmin.browser', 'pgadmin.browser.collection',
], function(gettext, url_for, pgAdmin, pgBrowser) {
if (!pgBrowser.Nodes['coll-dbms_job']) {
pgBrowser.Nodes['coll-dbms_job'] =
pgBrowser.Collection.extend({
node: 'dbms_job',
label: gettext('DBMS Jobs'),
type: 'coll-dbms_job',
columns: ['jsjobname', 'jsjobenabled', 'jsjobruncount', 'jsjobfailurecount', 'jsjobdesc'],
hasSQL: false,
hasDepends: false,
hasStatistics: false,
hasScriptTypes: [],
canDrop: true,
canDropCascade: false,
});
}
if (!pgBrowser.Nodes['dbms_job']) {
pgAdmin.Browser.Nodes['dbms_job'] = pgAdmin.Browser.Node.extend({
parent_type: 'dbms_job_scheduler',
type: 'dbms_job',
label: gettext('DBMS Job'),
node_image: 'icon-pga_job',
epasHelp: true,
epasURL: 'https://www.enterprisedb.com/docs/epas/$VERSION$/epas_compat_bip_guide/03_built-in_packages/15_dbms_scheduler/02_create_job/',
dialogHelp: url_for('help.static', {'filename': 'dbms_job.html'}),
canDrop: true,
hasSQL: true,
hasDepends: false,
hasStatistics: false,
Init: function() {
/* Avoid multiple registration of menus */
if (this.initialized)
return;
this.initialized = true;
pgBrowser.add_menus([{
name: 'create_dbms_job_on_coll', node: 'coll-dbms_job', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('DBMS Job...'),
data: {action: 'create'},
},{
name: 'create_dbms_job', node: 'dbms_job', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('DBMS Job...'),
data: {action: 'create'},
},{
name: 'create_dbms_job', node: 'dbms_job_scheduler', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('DBMS Job...'),
data: {action: 'create'},
}, {
name: 'enable_job', node: 'dbms_job', module: this,
applies: ['object', 'context'], callback: 'enable_job',
priority: 4, label: gettext('Enable Job'),
enable : 'is_enabled',data: {
data_disabled: gettext('Job is already enabled.'),
},
}, {
name: 'disable_job', node: 'dbms_job', module: this,
applies: ['object', 'context'], callback: 'disable_job',
priority: 4, label: gettext('Disable Job'),
enable : 'is_disabled',data: {
data_disabled: gettext('Job is already disabled.'),
},
}, {
name: 'run_job', node: 'dbms_job', module: this,
applies: ['object', 'context'], callback: 'run_job',
priority: 4, label: gettext('Run Job'),
enable : 'is_disabled', data: {
data_disabled: gettext('Job is already disabled.'),
}
}
]);
},
is_enabled: function(node) {
return !node?.is_enabled;
},
is_disabled: function(node) {
return node?.is_enabled;
},
callbacks: {
enable_job: function(args, notify) {
let input = args || {},
obj = this,
t = pgBrowser.tree,
i = 'item' in input ? input.item : t.selected(),
d = i ? t.itemData(i) : undefined;
if (d) {
notify = notify || _.isUndefined(notify) || _.isNull(notify);
let enable = function() {
let data = d;
getApiInstance().put(
obj.generate_url(i, 'enable_disable', d, true),
{'job_name': data.label, 'is_enable_job': true}
).then(({data: res})=> {
if (res.success == 1) {
pgAdmin.Browser.notifier.success(res.info);
t.removeIcon(i);
data.icon = 'icon-pga_jobstep';
data.is_enabled = true;
t.addIcon(i, {icon: data.icon});
t.updateAndReselectNode(i, data);
}
}).catch(function(error) {
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
t.refresh(i);
});
};
if (notify) {
pgAdmin.Browser.notifier.confirm(
gettext('Enable Job'),
gettext('Are you sure you want to enable the job %s?', d.label),
function() { enable(); },
function() { return true;},
);
} else {
enable();
}
}
return false;
},
disable_job: function(args, notify) {
let input = args || {},
obj = this,
t = pgBrowser.tree,
i = 'item' in input ? input.item : t.selected(),
d = i ? t.itemData(i) : undefined;
if (d) {
notify = notify || _.isUndefined(notify) || _.isNull(notify);
let disable = function() {
let data = d;
getApiInstance().put(
obj.generate_url(i, 'enable_disable', d, true),
{'job_name': data.label, 'is_enable_job': false}
).then(({data: res})=> {
if (res.success == 1) {
pgAdmin.Browser.notifier.success(res.info);
t.removeIcon(i);
data.icon = 'icon-pga_jobstep-disabled';
data.is_enabled = false;
t.addIcon(i, {icon: data.icon});
t.updateAndReselectNode(i, data);
}
}).catch(function(error) {
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
t.refresh(i);
});
};
if (notify) {
pgAdmin.Browser.notifier.confirm(
gettext('Disable Job'),
gettext('Are you sure you want to disable the job %s?', d.label),
function() { disable(); },
function() { return true;},
);
} else {
disable();
}
}
return false;
},
run_job: function(args, notify) {
let input = args || {},
obj = this,
t = pgBrowser.tree,
i = 'item' in input ? input.item : t.selected(),
d = i ? t.itemData(i) : undefined;
if (d) {
notify = notify || _.isUndefined(notify) || _.isNull(notify);
let run = function() {
let data = d;
getApiInstance().put(
obj.generate_url(i, 'run_job', d, true),
{'job_name': data.label}
).then(({data: res})=> {
if (res.success == 1) {
pgAdmin.Browser.notifier.success(res.info);
}
}).catch(function(error) {
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
});
};
if (notify) {
pgAdmin.Browser.notifier.confirm(
gettext('Run Job'),
gettext('Are you sure you want to run the job %s now?', d.label),
function() { run(); },
function() { return true;},
);
} else {
run();
}
}
return false;
}
},
getSchema: function(treeNodeInfo, itemNodeData) {
return new DBMSJobSchema(
{
procedures: ()=>getNodeAjaxOptions('get_procedures', this, treeNodeInfo, itemNodeData),
programs: ()=>getNodeAjaxOptions('get_programs', this, treeNodeInfo, itemNodeData),
schedules: ()=>getNodeAjaxOptions('get_schedules', this, treeNodeInfo, itemNodeData)
}
);
},
});
}
return pgBrowser.Nodes['dbms_job'];
});
@@ -0,0 +1,198 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import gettext from 'sources/gettext';
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
import { isEmptyString } from 'sources/validators';
import moment from 'moment';
import { getActionSchema, getRepeatSchema } from '../../../static/js/dbms_job_scheduler_common.ui';
export default class DBMSJobSchema extends BaseUISchema {
constructor(fieldOptions={}) {
super({
jsjobid: null,
jsjobname: '',
jsjobenabled: true,
jsjobdesc: '',
jsjobtype: 's',
jsjobruncount: 0,
jsjobfailurecount: 0,
// Program Args
jsjobprname: '',
jsprtype: 'PLSQL_BLOCK',
jsprenabled: true,
jsprnoofargs: 0,
jsprproc: null,
jsprcode: null,
jsprarguments: [],
// Schedule args
jsjobscname: '',
jsscstart: null,
jsscend: null,
jsscrepeatint: '',
jsscfreq: null,
jsscdate: null,
jsscweekdays: null,
jsscmonthdays: null,
jsscmonths: null,
jsschours: null,
jsscminutes: null,
});
this.fieldOptions = {
procedures: [],
programs: [],
schedules: [],
...fieldOptions,
};
}
get idAttribute() {
return 'jsjobid';
}
get baseFields() {
let obj = this;
return [
{
id: 'jsjobid', label: gettext('ID'), type: 'int', mode: ['properties'],
readonly: function(state) {return !obj.isNew(state); },
}, {
id: 'jsjobname', label: gettext('Name'), cell: 'text',
editable: false, type: 'text', noEmpty: true,
readonly: function(state) {return !obj.isNew(state); },
}, {
id: 'jsjobenabled', label: gettext('Enabled?'), type: 'switch', cell: 'switch',
readonly: function(state) {return !obj.isNew(state); },
}, {
id: 'jsjobtype', label: gettext('Job Type'),
type: ()=>{
let options = [
{'label': gettext('SELF-CONTAINED'), 'value': 's'},
{'label': gettext('PRE-DEFINED'), 'value': 'p'},
];
return {
type: 'toggle',
options: options,
};
},
readonly: function(state) {return !obj.isNew(state); },
helpMessage: gettext('If the Job Type is Self-Contained you need to specify the action and repeat interval in the Action and Repeat tabs respectively. If the Job Type is Pre-Defined you need to specify the existing Program and Schedule names in the Pre-Defined tab.'),
helpMessageMode: ['create'],
}, {
id: 'jsjobruncount', label: gettext('Run Count'), type: 'int',
readonly: true, mode: ['edit', 'properties']
}, {
id: 'jsjobfailurecount', label: gettext('Failure Count'), type: 'int',
readonly: true, mode: ['edit', 'properties']
}, {
id: 'jsjobdesc', label: gettext('Comment'), type: 'multiline',
readonly: function(state) {return !obj.isNew(state); },
},
// Add the Action Schema
...getActionSchema(obj, 'job'),
// Add the Repeat Schema.
...getRepeatSchema(obj, 'job'),
{
id: 'jsjobprname', label: gettext('Program Name'), type: 'select',
controlProps: { allowClear: false}, group: gettext('Pre-Defined'),
options: this.fieldOptions.programs,
readonly: function(state) {
return !obj.isNew(state) || state.jsjobtype == 's';
},
deps: ['jsjobtype'],
depChange: (state) => {
if (state.jsjobtype == 's') {
return { jsjobprname: null };
}
}
}, {
id: 'jsjobscname', label: gettext('Schedule Name'), type: 'select',
controlProps: { allowClear: false}, group: gettext('Pre-Defined'),
options: this.fieldOptions.schedules,
readonly: function(state) {
return !obj.isNew(state) || state.jsjobtype == 's';
},
deps: ['jsjobtype'],
depChange: (state) => {
if (state.jsjobtype == 's') {
return { jsjobscname: null };
}
}
},
];
}
validate(state, setError) {
if (state.jsjobtype == 's' ) {
if (isEmptyString(state.jsprtype)) {
setError('jsprtype', gettext('Job Type cannot be empty.'));
return true;
} else {
setError('jsprtype', null);
}
if (state.jsprtype == 'PLSQL_BLOCK' && isEmptyString(state.jsprcode)) {
setError('jsprcode', gettext('Code cannot be empty.'));
return true;
} else {
setError('jsprcode', null);
}
if (state.jsprtype == 'STORED_PROCEDURE' && isEmptyString(state.jsprproc)) {
setError('jsprproc', gettext('Procedure cannot be empty.'));
return true;
} else {
setError('jsprproc', null);
}
if (isEmptyString(state.jsscstart) && isEmptyString(state.jsscfreq) &&
isEmptyString(state.jsscmonths) && isEmptyString(state.jsscweekdays) &&
isEmptyString(state.jsscmonthdays) && isEmptyString(state.jsschours) &&
isEmptyString(state.jsscminutes) && isEmptyString(state.jsscdate)) {
setError('jsscstart', gettext('Either Start time or Repeat interval must be specified.'));
return true;
} else {
setError('jsscstart', null);
}
if (!isEmptyString(state.jsscend)) {
let start_time = state.jsscstart,
end_time = state.jsscend,
start_time_js = start_time.split(' '),
end_time_js = end_time.split(' ');
start_time_js = moment(start_time_js[0] + ' ' + start_time_js[1]);
end_time_js = moment(end_time_js[0] + ' ' + end_time_js[1]);
if(end_time_js.isBefore(start_time_js)) {
setError('jsscend', gettext('Start time must be less than end time'));
return true;
} else {
setError('jsscend', null);
}
} else {
state.jsscend = null;
}
} else if (state.jsjobtype == 'p') {
if (isEmptyString(state.jsjobprname)) {
setError('jsjobprname', gettext('Pre-Defined program name cannot be empty.'));
return true;
} else {
setError('jsjobprname', null);
}
if (isEmptyString(state.jsjobscname)) {
setError('jsjobscname', gettext('Pre-Defined schedule name cannot be empty.'));
return true;
} else {
setError('jsjobscname', null);
}
}
}
}
@@ -0,0 +1,61 @@
{% if display_comments %}
-- DBMS Job: '{{ job_name }}'
-- EXEC dbms_scheduler.DROP_JOB('{{ job_name }}');
{% endif %}
{% if internal_job_type is defined and internal_job_type == 's' %}
EXEC dbms_scheduler.CREATE_JOB(
job_name => {{ job_name|qtLiteral(conn) }},
job_type => {{ job_type|qtLiteral(conn) }},
job_action => {{ job_action|qtLiteral(conn) }},
repeat_interval => {{ repeat_interval|qtLiteral(conn) }}{% if start_date or end_date or number_of_arguments or enabled or comments %},{% endif %}
{% if start_date %}
start_date => {{ start_date|qtLiteral(conn) }}{% if end_date or number_of_arguments or enabled or comments %},{% endif %}
{% endif %}
{% if end_date %}
end_date => {{ end_date|qtLiteral(conn) }}{% if number_of_arguments or enabled or comments %},{% endif %}
{% endif %}
{% if number_of_arguments %}
number_of_arguments => {{ number_of_arguments }}{% if enabled or comments %},{% endif %}
{% endif %}
{% if enabled %}
enabled => {{ enabled }}{% if comments %},{% endif %}
{% endif %}
{% if comments %}
comments => {{ comments|qtLiteral(conn) }}
{% endif %}
);
{% elif internal_job_type is defined and internal_job_type == 'p' %}
EXEC dbms_scheduler.CREATE_JOB(
job_name => {{ job_name|qtLiteral(conn) }},
program_name => {{ program_name|qtLiteral(conn) }},
schedule_name => {{ schedule_name|qtLiteral(conn) }}{% if enabled or comments %},{% endif %}
{% if enabled %}
enabled => {{ enabled }}{% if comments %},{% endif %}
{% endif %}
{% if comments %}
comments => {{ comments|qtLiteral(conn) }}
{% endif %}
);
{% endif %}
{% for args_list_item in arguments %}
EXEC dbms_scheduler.SET_JOB_ARGUMENT_VALUE(
job_name => {{ job_name|qtLiteral(conn) }},
argument_name => {{ args_list_item.argname|qtLiteral(conn) }},
{% if args_list_item.argval is defined and args_list_item.argval != '' %}
argument_value => {{ args_list_item.argval|qtLiteral(conn) }}
{% elif args_list_item.argdefval is defined %}
argument_value => {{ args_list_item.argdefval|qtLiteral(conn) }}
{% endif %}
);
{% endfor %}
@@ -0,0 +1,3 @@
EXEC dbms_scheduler.DROP_JOB(
{{ job_name|qtLiteral(conn) }}
);
@@ -0,0 +1,3 @@
SELECT value
FROM dba_scheduler_job_args
WHERE job_name = {{ job_name|qtLiteral(conn) }} AND argument_name = {{ arg_name|qtLiteral(conn) }}
@@ -0,0 +1,5 @@
SELECT
dsj_job_id AS jsjobid, dsj_job_name AS jsjobname,
dsj_enabled AS jsjobenabled
FROM sys.scheduler_0400_job
WHERE dsj_job_name={{ job_name|qtLiteral(conn) }}
@@ -0,0 +1,4 @@
SELECT
dsj_job_id as jsjobid, dsj_job_name as jsjobname,
dsj_enabled as jsjobenabled, dsj_comments as jsjobdesc
FROM sys.scheduler_0400_job;
@@ -0,0 +1,14 @@
SELECT
job.dsj_job_id as jsjobid, job_name as jsjobname, program_name as jsjobprname, job_type as jsprtype,
CASE WHEN job_type = 'PLSQL_BLOCK' THEN job_action ELSE '' END AS jsprcode,
CASE WHEN job_type = 'STORED_PROCEDURE' THEN job_action ELSE '' END AS jsprproc,
job_action, number_of_arguments as jsprnoofargs, schedule_name as jsjobscname,
start_date as jsscstart, end_date as jsscend, repeat_interval as jsscrepeatint,
enabled as jsjobenabled, comments as jsjobdesc,
run_count as jsjobruncount, failure_count as jsjobfailurecount,
job.dsj_program_id as program_id, job.dsj_schedule_id as schedule_id
FROM sys.dba_scheduler_jobs jobv
LEFT JOIN sys.scheduler_0400_job job ON jobv.job_name = job.dsj_job_name
{% if jsjobid %}
WHERE job.dsj_job_id={{jsjobid}}::oid
{% endif %}
@@ -0,0 +1,3 @@
EXEC dbms_scheduler.RUN_JOB(
{{ job_name|qtLiteral(conn) }}
);
@@ -0,0 +1,12 @@
{% for chval in changed_value %}
EXEC dbms_scheduler.SET_JOB_ARGUMENT_VALUE(
job_name => {{ job_name|qtLiteral(conn) }},
argument_name => {{ chval.argname|qtLiteral(conn) }},
{% if chval.argval is defined and chval.argval != '' %}
argument_value => {{ chval.argval|qtLiteral(conn) }}
{% elif chval.argdefval is defined %}
argument_value => {{ chval.argdefval|qtLiteral(conn) }}
{% endif %}
);
{% endfor %}
@@ -0,0 +1,429 @@
{
"dbms_create_job": [
{
"name": "Create job when type is self contained and PLSQL_BLOCK",
"url": "/browser/dbms_job/obj/",
"is_positive_test": true,
"test_data": {
"jsjobid": null,
"jsjobname": "job_self_with_psql",
"jsjobenabled": true,
"jsjobdesc": "This is a self contained psql job.",
"jsjobtype": "s",
"jsprtype": "PLSQL_BLOCK",
"jsprnoofargs": 0,
"jsprarguments": [],
"jsprproc": null,
"jsprcode": "BEGIN PERFORM 1; END;",
"jsscstart": "2024-02-27 00:00:00 +05:30",
"jsscend": "2054-02-28 00:00:00 +05:30",
"jsscfreq": "YEARLY",
"jsscdate": null,
"jsscweekdays": ["7", "1", "2", "3", "4", "5", "6"],
"jsscmonthdays": ["2", "8", "31", "27"],
"jsscmonths": ["1", "5", "12"],
"jsschours": ["05", "18", "22"],
"jsscminutes": ["45", "37", "58"],
"jsjobprname": "",
"jsjobscname": ""
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
},
{
"name": "Create job when type is self contained and STORED_PROCEDURE without arguments",
"url": "/browser/dbms_job/obj/",
"is_positive_test": true,
"test_data": {
"jsjobid": null,
"jsjobname": "job_self_with_proc_noargs",
"jsjobenabled": true,
"jsjobdesc": "This is a self contained stored procedure job with no args.",
"jsjobtype": "s",
"jsprtype": "STORED_PROCEDURE",
"jsprnoofargs": 0,
"jsprarguments": [],
"jsprproc": "public.test_proc_without_args",
"jsprcode": null,
"jsscstart": "2024-02-27 00:00:00 +05:30",
"jsscend": "2054-02-28 00:00:00 +05:30",
"jsscfreq": "YEARLY",
"jsscdate": "20250113",
"jsscweekdays": [],
"jsscmonthdays": [],
"jsscmonths": [],
"jsschours": [],
"jsscminutes": [],
"jsjobprname": "",
"jsjobscname": ""
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
},
{
"name": "Create job when type is pre-defined and program is PLSQL",
"url": "/browser/dbms_job/obj/",
"is_positive_test": true,
"test_data": {
"jsjobid": null,
"jsjobname": "job_pre_with_psql",
"jsjobenabled": true,
"jsjobdesc": "This is a pre-defined job with PLSQL program.",
"jsjobtype": "p",
"jsjobprname": "prg_with_psql",
"jsjobscname": "yearly_sch",
"jsprtype": null,
"jsprnoofargs": 0,
"jsprarguments": [],
"jsprproc": null,
"jsprcode": null,
"jsscstart": null,
"jsscend": null,
"jsscfreq": null,
"jsscdate": null,
"jsscweekdays": null,
"jsscmonthdays": null,
"jsscmonths": null,
"jsschours": null,
"jsscminutes": null
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
},
{
"name": "Create job when type is pre-defined and program is Stored Procedure without args",
"url": "/browser/dbms_job/obj/",
"is_positive_test": true,
"test_data": {
"jsjobid": null,
"jsjobname": "job_pre_with_proc_noargs",
"jsjobenabled": true,
"jsjobdesc": "This is a pre-defined job with Stored Procedure without args",
"jsjobtype": "p",
"jsjobprname": "prg_with_proc_noargs",
"jsjobscname": "yearly_sch",
"jsprtype": null,
"jsprnoofargs": 0,
"jsprarguments": [],
"jsprproc": null,
"jsprcode": null,
"jsscstart": null,
"jsscend": null,
"jsscfreq": null,
"jsscdate": null,
"jsscweekdays": null,
"jsscmonthdays": null,
"jsscmonths": null,
"jsschours": null,
"jsscminutes": null
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
},
{
"name": "Create job when type is pre-defined and program is Stored Procedure with args",
"url": "/browser/dbms_job/obj/",
"is_positive_test": true,
"test_data": {
"jsjobid": null,
"jsjobname": "job_pre_with_proc_args",
"jsjobenabled": true,
"jsjobdesc": "This is a pre-defined job with program is Stored Procedure with args.",
"jsjobtype": "p",
"jsjobprname": "prg_with_proc_args",
"jsjobscname": "yearly_sch",
"jsprtype": null,
"jsprnoofargs": [],
"jsprarguments": [],
"jsprproc": null,
"jsprcode": null,
"jsscstart": null,
"jsscend": null,
"jsscfreq": null,
"jsscdate": null,
"jsscweekdays": null,
"jsscmonthdays": null,
"jsscmonths": null,
"jsschours": null,
"jsscminutes": null
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
},
{
"name": "Create job: while server is down",
"url": "/browser/dbms_job/obj/",
"is_positive_test": false,
"test_data": {
"jsjobid": null,
"jsjobname": "job_with_psql",
"jsjobenabled": true,
"jsjobdesc": "This is a self contained psql job.",
"jsjobtype": "s",
"jsprtype": "PLSQL_BLOCK",
"jsprnoofargs": 0,
"jsprarguments": [],
"jsprproc": null,
"jsprcode": "BEGIN PERFORM 1; END;",
"jsscstart": "2024-02-27 00:00:00 +05:30",
"jsscend": "2054-02-28 00:00:00 +05:30",
"jsscfreq": "YEARLY",
"jsscdate": null,
"jsscweekdays": ["7", "1", "2", "3", "4", "5", "6"],
"jsscmonthdays": ["2", "8", "31", "27"],
"jsscmonths": ["1", "5", "12"],
"jsschours": ["05", "18", "22"],
"jsscminutes": ["45", "37", "58"],
"jsjobprname": "",
"jsjobscname": ""
},
"mocking_required": true,
"mock_data": {
"function_name": "pgadmin.utils.driver.psycopg3.connection.Connection.execute_scalar",
"return_value": "[(False,'Mocked Internal Server Error')]"
},
"expected_data": {
"status_code": 500,
"error_msg": "Mocked Internal Server Error",
"test_result_data": {}
}
}
],
"dbms_update_job": [
{
"name": "Set job argument value",
"url": "/browser/dbms_job/obj/",
"is_positive_test": true,
"test_data": {
"jsjobname": "job_with_update_args",
"jsprarguments": {
"changed": [{"argid":0,"argtype":"bigint","argmode":"IN","argname":"salary","argdefval":"10000","argval":"5000"}]
}
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
}
],
"dbms_delete_job": [
{
"name": "Delete job: With existing DBMS job.",
"url": "/browser/dbms_job/obj/",
"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 jobs: With existing DBMS jobs.",
"url": "/browser/dbms_job/obj/",
"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
}
],
"dbms_get_job": [
{
"name": "Get job: With existing DBMS job.",
"url": "/browser/dbms_job/obj/",
"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 jobs: With multiple existing DBMS jobs.",
"url": "/browser/dbms_job/obj/",
"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": "Get job: while server down.",
"url": "/browser/dbms_job/obj/",
"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
}
],
"dbms_msql_job": [
{
"name": "Get job msql: For existing PLSQL job.",
"url": "/browser/dbms_job/msql/",
"is_positive_test": true,
"inventory_data": {},
"test_data": {
"jsjobid": null,
"jsjobname": "job_self_with_psql",
"jsjobenabled": true,
"jsjobdesc": "This is a self contained psql job.",
"jsjobtype": "s",
"jsprtype": "PLSQL_BLOCK",
"jsprnoofargs": 0,
"jsprarguments": [],
"jsprproc": null,
"jsprcode": "BEGIN PERFORM 1; END;",
"jsscstart": "2024-02-27 00:00:00 +05:30",
"jsscend": "2054-02-28 00:00:00 +05:30",
"jsscfreq": "YEARLY",
"jsscdate": null,
"jsscweekdays": ["7", "1", "2", "3", "4", "5", "6"],
"jsscmonthdays": ["2", "8", "31", "27"],
"jsscmonths": ["1", "5", "12"],
"jsschours": ["05", "18", "22"],
"jsscminutes": ["45", "37", "58"],
"jsjobprname": "",
"jsjobscname": ""
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
},
"is_list": false
},
{
"name": "Get job msql: For existing STORED_PROCEDURE job.",
"url": "/browser/dbms_job/msql/",
"is_positive_test": true,
"inventory_data": {},
"test_data": {
"jsjobid": null,
"jsjobname": "job_self_with_proc_noargs",
"jsjobenabled": true,
"jsjobdesc": "This is a self contained stored procedure job with no args.",
"jsjobtype": "s",
"jsprtype": "STORED_PROCEDURE",
"jsprnoofargs": 0,
"jsprarguments": [],
"jsprproc": "public.test_proc_without_args",
"jsprcode": null,
"jsscstart": "2024-02-27 00:00:00 +05:30",
"jsscend": "2054-02-28 00:00:00 +05:30",
"jsscfreq": "YEARLY",
"jsscdate": "20250113",
"jsscweekdays": [],
"jsscmonthdays": [],
"jsscmonths": [],
"jsschours": [],
"jsscminutes": [],
"jsjobprname": "",
"jsjobscname": ""
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
},
"is_list": false
}
],
"dbms_enable_job": [
{
"name": "Enable existing job",
"url": "/browser/dbms_job/enable_disable/",
"is_positive_test": true,
"test_data": {
"is_enable_job": true
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
}
],
"dbms_disable_job": [
{
"name": "Disable existing job",
"url": "/browser/dbms_job/enable_disable/",
"is_positive_test": true,
"test_data": {
"is_enable_job": false
},
"mocking_required": false,
"mock_data": {},
"expected_data": {
"status_code": 200,
"error_msg": null,
"test_result_data": {}
}
}
]
}
@@ -0,0 +1,94 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import os
import json
from unittest.mock import patch
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from ...tests import utils as job_scheduler_utils
from pgadmin.browser.server_groups.servers.databases.tests import \
utils as database_utils
# Load test data from json file.
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
test_cases = json.load(data_file)
class DBMSAddJobTestCase(BaseTestGenerator):
"""This class will test the add job in the DBMS Job API"""
scenarios = utils.generate_scenarios("dbms_create_job",
test_cases)
def setUp(self):
super().setUp()
# Load test data
self.data = self.test_data
if not job_scheduler_utils.is_supported_version(self):
self.skipTest(job_scheduler_utils.SKIP_MSG)
# Create db
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
self)
db_con = database_utils.connect_database(self,
utils.SERVER_GROUP,
self.server_id,
self.db_id)
if db_con["info"] != "Database connected.":
raise Exception("Could not connect to database.")
# Create extension required for job scheduler
job_scheduler_utils.create_job_scheduler_extensions(self)
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
# Create job schedule
job_scheduler_utils.create_dbms_schedule(self, 'yearly_sch')
job_scheduler_utils.create_dbms_program(self, 'prg_with_psql')
job_scheduler_utils.create_dbms_program(
self,'prg_with_proc_noargs', with_proc=True,
proc_name='public.test_proc_without_args()')
job_scheduler_utils.create_dbms_program(
self,'prg_with_proc_args', with_proc=True,
proc_name='public.test_proc_with_args(IN salary bigint DEFAULT '
'10000, IN name character varying)')
def runTest(self):
""" This function will add DBMS Job under test database. """
if self.is_positive_test:
response = job_scheduler_utils.api_create(self)
# Assert response
utils.assert_status_code(self, response)
# Verify in backend
response_data = json.loads(response.data)
self.jobs_id = response_data['node']['_id']
jobs_name = response_data['node']['label']
is_present = job_scheduler_utils.verify_dbms_job(
self, jobs_name)
self.assertTrue(
is_present,"DBMS job was not created successfully.")
else:
if self.mocking_required:
with patch(self.mock_data["function_name"],
side_effect=eval(self.mock_data["return_value"])):
response = job_scheduler_utils.api_create(self)
# Assert response
utils.assert_status_code(self, response)
utils.assert_error_message(self, response)
def tearDown(self):
"""This function will do the cleanup task."""
job_scheduler_utils.clean_up(self)
@@ -0,0 +1,98 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
import os
import json
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from ...tests import utils as job_scheduler_utils
from pgadmin.browser.server_groups.servers.databases.tests import \
utils as database_utils
# Load test data from json file.
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
test_cases = json.load(data_file)
class DBMSDeleteJobTestCase(BaseTestGenerator):
"""This class will test the delete job in the DBMS job API"""
scenarios = utils.generate_scenarios("dbms_delete_job",
test_cases)
def setUp(self):
super().setUp()
# Load test data
self.data = self.test_data
if not job_scheduler_utils.is_supported_version(self):
self.skipTest(job_scheduler_utils.SKIP_MSG)
# Create db
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
self)
db_con = database_utils.connect_database(self,
utils.SERVER_GROUP,
self.server_id,
self.db_id)
if db_con["info"] != "Database connected.":
raise Exception("Could not connect to database.")
# Create extension required for job scheduler
job_scheduler_utils.create_job_scheduler_extensions(self)
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
self.job_name = "test_job_delete%s" % str(uuid.uuid4())[1:8]
self.job_id = job_scheduler_utils.create_dbms_job(
self, self.job_name)
# multiple jobs
if self.is_list:
self.job_name2 = "test_job_delete%s" % str(uuid.uuid4())[1:8]
self.job_id_2 = job_scheduler_utils.create_dbms_job(
self, self.job_name2)
def runTest(self):
"""
This function will test delete DBMS job under test database.
"""
if self.is_list:
self.data['ids'] = [self.job_id, self.job_id_2]
response = job_scheduler_utils.api_delete(self, '')
# Assert response
utils.assert_status_code(self, response)
is_present = job_scheduler_utils.verify_dbms_job(
self, self.job_name)
self.assertFalse(
is_present, "DBMS job was not deleted successfully")
is_present = job_scheduler_utils.verify_dbms_job(
self, self.job_name2)
self.assertFalse(
is_present, "DBMS job was not deleted successfully")
else:
response = job_scheduler_utils.api_delete(self)
# Assert response
utils.assert_status_code(self, response)
is_present = job_scheduler_utils.verify_dbms_job(
self, self.job_name)
self.assertFalse(
is_present, "DBMS job was not deleted successfully")
def tearDown(self):
"""This function will do the cleanup task."""
job_scheduler_utils.clean_up(self)
@@ -0,0 +1,71 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
import os
import json
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from ...tests import utils as job_scheduler_utils
from pgadmin.browser.server_groups.servers.databases.tests import \
utils as database_utils
# Load test data from json file.
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
test_cases = json.load(data_file)
class DBMSDisableJobTestCase(BaseTestGenerator):
"""This class will test the add job in the DBMS job API"""
scenarios = utils.generate_scenarios("dbms_disable_job",
test_cases)
def setUp(self):
super().setUp()
# Load test data
self.data = self.test_data
if not job_scheduler_utils.is_supported_version(self):
self.skipTest(job_scheduler_utils.SKIP_MSG)
# Create db
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
self)
db_con = database_utils.connect_database(self,
utils.SERVER_GROUP,
self.server_id,
self.db_id)
if db_con["info"] != "Database connected.":
raise Exception("Could not connect to database.")
# Create extension required for job scheduler
job_scheduler_utils.create_job_scheduler_extensions(self)
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
self.job_name = "test_job_disable%s" % str(uuid.uuid4())[1:8]
self.data['job_name'] = self.job_name
self.job_id = job_scheduler_utils.create_dbms_job(
self, self.job_name)
def runTest(self):
""" This function will test DBMS job under test database."""
response = job_scheduler_utils.api_put(self, self.job_id)
# Assert response
utils.assert_status_code(self, response)
def tearDown(self):
"""This function will do the cleanup task."""
job_scheduler_utils.delete_dbms_job(self, self.job_name)
job_scheduler_utils.clean_up(self)
@@ -0,0 +1,71 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
import os
import json
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from ...tests import utils as job_scheduler_utils
from pgadmin.browser.server_groups.servers.databases.tests import \
utils as database_utils
# Load test data from json file.
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
test_cases = json.load(data_file)
class DBMSEnableJobTestCase(BaseTestGenerator):
"""This class will test the enable job in the DBMS job API"""
scenarios = utils.generate_scenarios("dbms_enable_job",
test_cases)
def setUp(self):
super().setUp()
# Load test data
self.data = self.test_data
if not job_scheduler_utils.is_supported_version(self):
self.skipTest(job_scheduler_utils.SKIP_MSG)
# Create db
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
self)
db_con = database_utils.connect_database(self,
utils.SERVER_GROUP,
self.server_id,
self.db_id)
if db_con["info"] != "Database connected.":
raise Exception("Could not connect to database.")
# Create extension required for job scheduler
job_scheduler_utils.create_job_scheduler_extensions(self)
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
self.job_name = "test_job_enable%s" % str(uuid.uuid4())[1:8]
self.data['job_name'] = self.job_name
self.job_id = job_scheduler_utils.create_dbms_job(
self, self.job_name, False)
def runTest(self):
""" This function will test DBMS job under test database."""
response = job_scheduler_utils.api_put(self, self.job_id)
# Assert response
utils.assert_status_code(self, response)
def tearDown(self):
"""This function will do the cleanup task."""
job_scheduler_utils.delete_dbms_job(self, self.job_name)
job_scheduler_utils.clean_up(self)
@@ -0,0 +1,92 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import uuid
import os
import json
from unittest.mock import patch
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from ...tests import utils as job_scheduler_utils
from pgadmin.browser.server_groups.servers.databases.tests import \
utils as database_utils
# Load test data from json file.
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
test_cases = json.load(data_file)
class DBMSGetJobTestCase(BaseTestGenerator):
"""This class will test the add job in the DBMS job API"""
scenarios = utils.generate_scenarios("dbms_get_job",
test_cases)
def setUp(self):
super().setUp()
# Load test data
self.data = self.test_data
if not job_scheduler_utils.is_supported_version(self):
self.skipTest(job_scheduler_utils.SKIP_MSG)
# Create db
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
self)
db_con = database_utils.connect_database(self,
utils.SERVER_GROUP,
self.server_id,
self.db_id)
if db_con["info"] != "Database connected.":
raise Exception("Could not connect to database.")
# Create extension required for job scheduler
job_scheduler_utils.create_job_scheduler_extensions(self)
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
self.job_name = "test_job_get%s" % str(uuid.uuid4())[1:8]
self.job_id = job_scheduler_utils.create_dbms_job(
self, self.job_name)
# multiple jobs
if self.is_list:
self.job_name2 = "test_job_get%s" % str(uuid.uuid4())[1:8]
self.job_id_2 = job_scheduler_utils.create_dbms_job(
self, self.job_name2,)
def runTest(self):
""" This function will test DBMS job under test database."""
if self.is_positive_test:
if self.is_list:
response = job_scheduler_utils.api_get(self, '')
else:
response = job_scheduler_utils.api_get(self)
# Assert response
utils.assert_status_code(self, response)
else:
if self.mocking_required:
with patch(self.mock_data["function_name"],
side_effect=[eval(self.mock_data["return_value"])]):
response = job_scheduler_utils.api_get(self)
# Assert response
utils.assert_status_code(self, response)
utils.assert_error_message(self, response)
def tearDown(self):
"""This function will do the cleanup task."""
job_scheduler_utils.delete_dbms_job(self, self.job_name)
if self.is_list:
job_scheduler_utils.delete_dbms_job(self, self.job_name2)
job_scheduler_utils.clean_up(self)
@@ -0,0 +1,65 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import os
import json
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from ...tests import utils as job_scheduler_utils
from pgadmin.browser.server_groups.servers.databases.tests import \
utils as database_utils
# Load test data from json file.
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
test_cases = json.load(data_file)
class DBMSGetMSQLJobTestCase(BaseTestGenerator):
"""This class will test the add job in the DBMS job API"""
scenarios = utils.generate_scenarios("dbms_msql_job",
test_cases)
def setUp(self):
super().setUp()
# Load test data
self.data = self.test_data
if not job_scheduler_utils.is_supported_version(self):
self.skipTest(job_scheduler_utils.SKIP_MSG)
# Create db
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
self)
db_con = database_utils.connect_database(self,
utils.SERVER_GROUP,
self.server_id,
self.db_id)
if db_con["info"] != "Database connected.":
raise Exception("Could not connect to database.")
# Create extension required for job scheduler
job_scheduler_utils.create_job_scheduler_extensions(self)
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
def runTest(self):
""" This function will add DBMS job under test database. """
url_encode_data = self.data
response = job_scheduler_utils.api_get_msql(self, url_encode_data)
# Assert response
utils.assert_status_code(self, response)
def tearDown(self):
"""This function will do the cleanup task."""
job_scheduler_utils.clean_up(self)
@@ -0,0 +1,73 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import os
import json
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from ...tests import utils as job_scheduler_utils
from pgadmin.browser.server_groups.servers.databases.tests import \
utils as database_utils
# Load test data from json file.
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
with open(CURRENT_PATH + "/dbms_jobs_test_data.json") as data_file:
test_cases = json.load(data_file)
class DBMSUpdateJobTestCase(BaseTestGenerator):
"""This class will test the add job in the DBMS Job API"""
scenarios = utils.generate_scenarios("dbms_update_job",
test_cases)
def setUp(self):
super().setUp()
# Load test data
self.data = self.test_data
if not job_scheduler_utils.is_supported_version(self):
self.skipTest(job_scheduler_utils.SKIP_MSG)
# Create db
self.db_name, self.db_id = job_scheduler_utils.create_test_database(
self)
db_con = database_utils.connect_database(self,
utils.SERVER_GROUP,
self.server_id,
self.db_id)
if db_con["info"] != "Database connected.":
raise Exception("Could not connect to database.")
# Create extension required for job scheduler
job_scheduler_utils.create_job_scheduler_extensions(self)
if not job_scheduler_utils.is_dbms_job_scheduler_present(self):
self.skipTest(job_scheduler_utils.SKIP_MSG_EXTENSION)
# Create job schedule
job_scheduler_utils.create_dbms_schedule(self, 'yearly_sch')
job_scheduler_utils.create_dbms_program(
self,'prg_with_proc_args', with_proc=True,
proc_name='public.test_proc_with_args()',
define_args=True)
self.job_id = job_scheduler_utils.create_dbms_job(
self, self.data['jsjobname'], True,
'prg_with_proc_args','yearly_sch')
def runTest(self):
""" This function will update DBMS Job under test database. """
response = job_scheduler_utils.api_put(self, self.job_id)
# Assert response
utils.assert_status_code(self, response)
def tearDown(self):
"""This function will do the cleanup task."""
job_scheduler_utils.clean_up(self)
@@ -0,0 +1,574 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
""" Implements DBMS Program objects Node."""
import json
from functools import wraps
from flask import render_template, request, jsonify
from flask_babel import gettext
from pgadmin.browser.collection import CollectionNodeModule
from pgadmin.browser.server_groups.servers import databases
from pgadmin.browser.utils import PGChildNodeView
from pgadmin.utils.ajax import make_json_response, gone, \
make_response as ajax_response, internal_server_error
from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER
from pgadmin.utils.constants import DBMS_JOB_SCHEDULER_ID
from pgadmin.browser.server_groups.servers.databases.schemas.functions.utils \
import format_arguments_from_db
from pgadmin.browser.server_groups.servers.databases.dbms_job_scheduler.utils \
import get_formatted_program_args
class DBMSProgramModule(CollectionNodeModule):
"""
class DBMSProgramModule(CollectionNodeModule)
A module class for DBMS Program objects node derived
from CollectionNodeModule.
Methods:
-------
* get_nodes(gid, sid, did)
- Method is used to generate the browser collection node.
* script_load()
- Load the module script for DBMS Program objects, when any of
the server node is initialized.
"""
_NODE_TYPE = 'dbms_program'
_COLLECTION_LABEL = gettext("DBMS Programs")
@property
def collection_icon(self):
"""
icon to be displayed for the browser collection node
"""
return 'icon-coll-pga_jobstep'
@property
def node_icon(self):
"""
icon to be displayed for the browser nodes
"""
return 'icon-pga_jobstep'
def get_nodes(self, gid, sid, did, jsid):
"""
Generate the collection node
"""
if self.show_node:
yield self.generate_browser_collection_node(did)
@property
def node_inode(self):
"""
Override this property to make the node a leaf node.
Returns: False as this is the leaf node
"""
return False
@property
def script_load(self):
"""
Load the module script for server, when any of the database node is
initialized.
"""
return databases.DatabaseModule.node_type
@property
def module_use_template_javascript(self):
"""
Returns whether Jinja2 template is used for generating the javascript
module.
"""
return False
blueprint = DBMSProgramModule(__name__)
class DBMSProgramView(PGChildNodeView):
"""
class DBMSProgramView(PGChildNodeView)
A view class for DBMSProgram node derived from PGChildNodeView.
This class is responsible for all the stuff related to view like
updating program node, showing properties, showing sql in sql pane.
Methods:
-------
* __init__(**kwargs)
- Method is used to initialize the DBMSProgramView, and it's base view.
* check_precondition()
- This function will behave as a decorator which will checks
database connection before running view, it will also attaches
manager,conn & template_path properties to self
* list()
- This function is used to list all the program nodes within that
collection.
* nodes()
- This function will use to create all the child node within that
collection. Here it will create all the program node.
* properties(gid, sid, did, jsid, jsprid)
- This function will show the properties of the selected program node
* create(gid, sid, did, jsid, jsprid)
- This function will create the new program object
* msql(gid, sid, did, jsid, jsprid)
- This function is used to return modified SQL for the
selected program node
* sql(gid, sid, did, jsid, jsprid)
- Dummy response for sql panel
* delete(gid, sid, did, jsid, jsprid)
- Drops job program
"""
node_type = blueprint.node_type
BASE_TEMPLATE_PATH = 'dbms_programs/ppas/#{0}#'
parent_ids = [
{'type': 'int', 'id': 'gid'},
{'type': 'int', 'id': 'sid'},
{'type': 'int', 'id': 'did'},
{'type': 'int', 'id': 'jsid'}
]
ids = [
{'type': 'int', 'id': 'jsprid'}
]
operations = dict({
'obj': [
{'get': 'properties', 'delete': 'delete'},
{'get': 'list', 'post': 'create', 'delete': 'delete'}
],
'nodes': [{'get': 'nodes'}, {'get': 'nodes'}],
'msql': [{'get': 'msql'}, {'get': 'msql'}],
'sql': [{'get': 'sql'}],
'get_procedures': [{}, {'get': 'get_procedures'}],
'enable_disable': [{'put': 'enable_disable'}],
})
def _init_(self, **kwargs):
self.conn = None
self.template_path = None
self.manager = None
super().__init__(**kwargs)
def check_precondition(f):
"""
This function will behave as a decorator which will check the
database connection before running view. It will also attach
manager, conn & template_path properties to self
"""
@wraps(f)
def wrap(*args, **kwargs):
# Here args[0] will hold self & kwargs will hold gid,sid,did
self = args[0]
self.driver = get_driver(PG_DEFAULT_DRIVER)
self.manager = self.driver.connection_manager(kwargs['sid'])
self.conn = self.manager.connection(did=kwargs['did'])
# Set the template path for the SQL scripts
self.template_path = self.BASE_TEMPLATE_PATH.format(
self.manager.version)
return f(*args, **kwargs)
return wrap
@check_precondition
def list(self, gid, sid, did, jsid):
"""
This function is used to list all the program nodes within
that collection.
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
"""
sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]))
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
return ajax_response(
response=res['rows'],
status=200
)
@check_precondition
def nodes(self, gid, sid, did, jsid, jsprid=None):
"""
This function is used to create all the child nodes within
the collection. Here it will create all the program nodes.
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
"""
res = []
try:
sql = render_template(
"/".join([self.template_path, self._NODES_SQL]))
status, result = self.conn.execute_2darray(sql)
if not status:
return internal_server_error(errormsg=result)
if jsprid is not None:
if len(result['rows']) == 0:
return gone(
errormsg=gettext("Could not find the specified "
"program."))
row = result['rows'][0]
return make_json_response(
data=self.blueprint.generate_browser_node(
row['jsprid'],
DBMS_JOB_SCHEDULER_ID,
row['jsprname'],
is_enabled=row['jsprenabled'],
icon="icon-pga_jobstep" if row['jsprenabled'] else
"icon-pga_jobstep-disabled",
description=row['jsprdesc']
)
)
for row in result['rows']:
res.append(
self.blueprint.generate_browser_node(
row['jsprid'],
DBMS_JOB_SCHEDULER_ID,
row['jsprname'],
is_enabled=row['jsprenabled'],
icon="icon-pga_jobstep" if row['jsprenabled'] else
"icon-pga_jobstep-disabled",
description=row['jsprdesc']
)
)
return make_json_response(
data=res,
status=200
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def properties(self, gid, sid, did, jsid, jsprid):
"""
This function will show the properties of the selected program node.
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
jsprid: Job program ID
"""
try:
sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]),
jsprid=jsprid
)
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
errormsg=gettext("Could not find the specified program.")
)
data = res['rows'][0]
# Get the formatted program args
get_formatted_program_args(self.template_path, self.conn, data)
return ajax_response(
response=data,
status=200
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def create(self, gid, sid, did, jsid):
"""
This function will update the data for the selected program node.
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
"""
data = json.loads(request.data)
try:
sql = render_template(
"/".join([self.template_path, self._CREATE_SQL]),
program_name=data['jsprname'],
program_type=data['jsprtype'],
program_action=data['jsprproc']
if data['jsprtype'] == 'STORED_PROCEDURE' else
data['jsprcode'],
number_of_arguments=data['jsprnoofargs'],
enabled=data['jsprenabled'],
comments=data['jsprdesc'],
arguments=data['jsprarguments'],
conn=self.conn
)
status, res = self.conn.execute_void('BEGIN')
if not status:
return internal_server_error(errormsg=res)
status, res = self.conn.execute_scalar(sql)
if not status:
if self.conn.connected():
self.conn.execute_void('END')
return internal_server_error(errormsg=res)
self.conn.execute_void('END')
# Get the newly created program id
sql = render_template(
"/".join([self.template_path, 'get_program_id.sql']),
jsprname=data['jsprname'], conn=self.conn
)
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
errormsg=gettext("Job program creation failed.")
)
row = res['rows'][0]
return jsonify(
node=self.blueprint.generate_browser_node(
row['jsprid'],
DBMS_JOB_SCHEDULER_ID,
row['jsprname'],
is_enabled=row['jsprenabled'],
icon="icon-pga_jobstep" if row['jsprenabled'] else
"icon-pga_jobstep-disabled"
)
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def delete(self, gid, sid, did, jsid, jsprid=None):
"""Delete the Job program."""
if jsprid is None:
data = request.form if request.form else json.loads(
request.data
)
else:
data = {'ids': [jsprid]}
try:
for jsprid in data['ids']:
sql = render_template(
"/".join([self.template_path, self._PROPERTIES_SQL]),
jsprid=jsprid
)
status, res = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=res)
jsprname = res['rows'][0]['jsprname']
status, res = self.conn.execute_void(
render_template(
"/".join([self.template_path, self._DELETE_SQL]),
program_name=jsprname, conn=self.conn
)
)
if not status:
return internal_server_error(errormsg=res)
return make_json_response(success=1)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def msql(self, gid, sid, did, jsid, jsprid=None):
"""
This function is used to return modified SQL for the
selected program node.
Args:
gid: Server Group ID
sid: Server ID
jsid: Job Scheduler ID
jsprid: Job program ID (optional)
"""
data = {}
for k, v in request.args.items():
try:
# comments should be taken as is because if user enters a
# json comment it is parsed by loads which should not happen
if k in ('jsprdesc',):
data[k] = v
else:
data[k] = json.loads(v)
except ValueError:
data[k] = v
try:
sql = render_template(
"/".join([self.template_path, self._CREATE_SQL]),
program_name=data['jsprname'],
program_type=data['jsprtype'],
program_action=data['jsprproc']
if data['jsprtype'] == 'STORED_PROCEDURE' else
data['jsprcode'],
number_of_arguments=data['jsprnoofargs'],
enabled=data['jsprenabled'],
comments=data['jsprdesc'],
arguments=data['jsprarguments'],
conn=self.conn
)
return make_json_response(
data=sql,
status=200
)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def sql(self, gid, sid, did, jsid, jsprid):
"""
This function will generate sql for the sql panel
"""
try:
SQL = render_template("/".join(
[self.template_path, self._PROPERTIES_SQL]
), jsprid=jsprid)
status, res = self.conn.execute_dict(SQL)
if not status:
return internal_server_error(errormsg=res)
if len(res['rows']) == 0:
return gone(
gettext("Could not find the DBMS Schedule.")
)
data = res['rows'][0]
# Get the formatted program args
get_formatted_program_args(self.template_path, self.conn, data)
SQL = render_template(
"/".join([self.template_path, self._CREATE_SQL]),
display_comments=True,
program_name=data['jsprname'],
program_type=data['jsprtype'],
program_action=data['jsprproc']
if data['jsprtype'] == 'STORED_PROCEDURE' else
data['jsprcode'],
number_of_arguments=data['jsprnoofargs'],
enabled=data['jsprenabled'],
comments=data['jsprdesc'],
arguments=data['jsprarguments'] if 'jsprarguments' in data
else [],
conn=self.conn
)
return ajax_response(response=SQL)
except Exception as e:
return internal_server_error(errormsg=str(e))
@check_precondition
def get_procedures(self, gid, sid, did, jsid=None):
"""
This function will return procedure list
:param gid: group id
:param sid: server id
:param did: database id
:return:
"""
res = []
sql = render_template("/".join([self.template_path,
'get_procedures.sql']),
datlastsysoid=self._DATABASE_LAST_SYSTEM_OID)
status, rset = self.conn.execute_dict(sql)
if not status:
return internal_server_error(errormsg=rset)
for row in rset['rows']:
# Get formatted Arguments
frmtd_params, _ = format_arguments_from_db(
self.template_path, self.conn, row)
res.append({'label': row['proc_name'],
'value': row['proc_name'],
'no_of_args': row['number_of_arguments'],
'arguments': frmtd_params['arguments']
})
return make_json_response(
data=res,
status=200
)
@check_precondition
def enable_disable(self, gid, sid, did, jsid, jsprid=None):
"""
This function is used to enable/disable program.
"""
data = request.form if request.form else json.loads(
request.data
)
status, res = self.conn.execute_void(
render_template(
"/".join([self.template_path, 'enable_disable.sql']),
name=data['program_name'],
is_enable=data['is_enable_program'], conn=self.conn
)
)
if not status:
return internal_server_error(errormsg=res)
return make_json_response(
success=1,
info=gettext("Program enabled") if data['is_enable_program'] else
gettext('Program disabled'),
data={
'sid': sid,
'did': did,
'jsid': jsid,
'jsprid': jsprid
}
)
DBMSProgramView.register_node_view(blueprint)
@@ -0,0 +1,189 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import DBMSProgramSchema from './dbms_program.ui';
import { getNodeAjaxOptions } from '../../../../../../../static/js/node_ajax';
import getApiInstance from '../../../../../../../../static/js/api_instance';
define('pgadmin.node.dbms_program', [
'sources/gettext', 'sources/url_for', 'sources/pgadmin',
'pgadmin.browser', 'pgadmin.browser.collection',
], function(gettext, url_for, pgAdmin, pgBrowser) {
if (!pgBrowser.Nodes['coll-dbms_program']) {
pgBrowser.Nodes['coll-dbms_program'] =
pgBrowser.Collection.extend({
node: 'dbms_program',
label: gettext('DBMS Programs'),
type: 'coll-dbms_program',
columns: ['jsprname', 'jsprtype', 'jsprenabled', 'jsprdesc'],
hasSQL: false,
hasDepends: false,
hasStatistics: false,
hasScriptTypes: [],
canDrop: true,
canDropCascade: false,
});
}
if (!pgBrowser.Nodes['dbms_program']) {
pgAdmin.Browser.Nodes['dbms_program'] = pgAdmin.Browser.Node.extend({
parent_type: 'dbms_job_scheduler',
type: 'dbms_program',
label: gettext('DBMS Program'),
node_image: 'icon-pga_jobstep',
epasHelp: true,
epasURL: 'https://www.enterprisedb.com/docs/epas/$VERSION$/epas_compat_bip_guide/03_built-in_packages/15_dbms_scheduler/03_create_program/',
dialogHelp: url_for('help.static', {'filename': 'dbms_program.html'}),
canDrop: true,
hasSQL: true,
hasDepends: false,
hasStatistics: false,
Init: function() {
/* Avoid multiple registration of menus */
if (this.initialized)
return;
this.initialized = true;
pgBrowser.add_menus([{
name: 'create_dbms_program_on_coll', node: 'coll-dbms_program', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('DBMS Program...'),
data: {action: 'create'},
}, {
name: 'create_dbms_program', node: 'dbms_program', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('DBMS Program...'),
data: {action: 'create'},
}, {
name: 'create_dbms_program', node: 'dbms_job_scheduler', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 4, label: gettext('DBMS Program...'),
data: {action: 'create'},
}, {
name: 'enable_program', node: 'dbms_program', module: this,
applies: ['object', 'context'], callback: 'enable_program',
priority: 4, label: gettext('Enable Program'),
enable : 'is_enabled',data: {
data_disabled: gettext('Program is already enabled.'),
},
}, {
name: 'disable_program', node: 'dbms_program', module: this,
applies: ['object', 'context'], callback: 'disable_program',
priority: 4, label: gettext('Disable Program'),
enable : 'is_disabled',data: {
data_disabled: gettext('Program is already disabled.'),
},
}
]);
},
is_enabled: function(node) {
return !node?.is_enabled;
},
is_disabled: function(node) {
return node?.is_enabled;
},
callbacks: {
enable_program: function(args, notify) {
let input = args || {},
obj = this,
t = pgBrowser.tree,
i = 'item' in input ? input.item : t.selected(),
d = i ? t.itemData(i) : undefined;
if (d) {
notify = notify || _.isUndefined(notify) || _.isNull(notify);
let enable = function() {
let data = d;
getApiInstance().put(
obj.generate_url(i, 'enable_disable', d, true),
{'program_name': data.label, 'is_enable_program': true}
).then(({data: res})=> {
if (res.success == 1) {
pgAdmin.Browser.notifier.success(res.info);
t.removeIcon(i);
data.icon = 'icon-pga_jobstep';
data.is_enabled = true;
t.addIcon(i, {icon: data.icon});
t.updateAndReselectNode(i, data);
}
}).catch(function(error) {
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
t.refresh(i);
});
};
if (notify) {
pgAdmin.Browser.notifier.confirm(
gettext('Enable Program'),
gettext('Are you sure you want to enable the program %s?', d.label),
function() { enable(); },
function() { return true;},
);
} else {
enable();
}
}
return false;
},
disable_program: function(args, notify) {
let input = args || {},
obj = this,
t = pgBrowser.tree,
i = 'item' in input ? input.item : t.selected(),
d = i ? t.itemData(i) : undefined;
if (d) {
notify = notify || _.isUndefined(notify) || _.isNull(notify);
let disable = function() {
let data = d;
getApiInstance().put(
obj.generate_url(i, 'enable_disable', d, true),
{'program_name': data.label, 'is_enable_program': false}
).then(({data: res})=> {
if (res.success == 1) {
pgAdmin.Browser.notifier.success(res.info);
t.removeIcon(i);
data.icon = 'icon-pga_jobstep-disabled';
data.is_enabled = false;
t.addIcon(i, {icon: data.icon});
t.updateAndReselectNode(i, data);
}
}).catch(function(error) {
pgAdmin.Browser.notifier.pgRespErrorNotify(error);
t.refresh(i);
});
};
if (notify) {
pgAdmin.Browser.notifier.confirm(
gettext('Disable Program'),
gettext('Are you sure you want to disable the program %s?', d.label),
function() { disable(); },
function() { return true;},
);
} else {
disable();
}
}
return false;
},
},
getSchema: function(treeNodeInfo, itemNodeData) {
return new DBMSProgramSchema(
{
procedures: ()=>getNodeAjaxOptions('get_procedures', this, treeNodeInfo, itemNodeData),
}
);
},
});
}
return pgBrowser.Nodes['dbms_program'];
});
@@ -0,0 +1,76 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2024, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
import gettext from 'sources/gettext';
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
import { isEmptyString } from 'sources/validators';
import { getActionSchema } from '../../../static/js/dbms_job_scheduler_common.ui';
export default class DBMSProgramSchema extends BaseUISchema {
constructor(fieldOptions={}) {
super({
jsprid: null,
jsprname: '',
jsprtype: 'PLSQL_BLOCK',
jsprenabled: true,
jsprnoofargs: 0,
jsprarguments: [],
jsprdesc: '',
jsprproc: null,
jsprcode: null,
});
this.fieldOptions = {
procedures: [],
...fieldOptions,
};
}
get idAttribute() {
return 'jsprid';
}
get baseFields() {
let obj = this;
return [
{
id: 'jsprid', label: gettext('ID'), type: 'int', mode: ['properties'],
readonly: function(state) {return !obj.isNew(state); },
}, {
id: 'jsprname', label: gettext('Name'), cell: 'text',
type: 'text', noEmpty: true,
readonly: function(state) {return !obj.isNew(state); },
}, {
id: 'jsprenabled', label: gettext('Enabled?'), type: 'switch', cell: 'switch',
readonly: function(state) {return !obj.isNew(state); },
},
// Add the Action Schema
...getActionSchema(obj, 'program'),
{
id: 'jsprdesc', label: gettext('Comment'), type: 'multiline',
readonly: function(state) {return !obj.isNew(state); },
}
];
}
validate(state, setError) {
/* code validation*/
if (state.jsprtype == 'PLSQL_BLOCK' && isEmptyString(state.jsprcode)) {
setError('jsprcode', gettext('Code cannot be empty.'));
return true;
} else {
setError('jsprcode', null);
}
if (state.jsprtype == 'STORED_PROCEDURE' && isEmptyString(state.jsprproc)) {
setError('jsprproc', gettext('Procedure cannot be empty.'));
return true;
} else {
setError('jsprproc', null);
}
}
}
@@ -0,0 +1,34 @@
{% if display_comments %}
-- DBMS Program: '{{ program_name }}'
-- EXEC dbms_scheduler.DROP_PROGRAM('{{ program_name }}');
{% endif %}
EXEC dbms_scheduler.CREATE_PROGRAM(
program_name => {{ program_name|qtLiteral(conn) }},
program_type => {{ program_type|qtLiteral(conn) }},
program_action => {{ program_action|qtLiteral(conn) }}{% if number_of_arguments or enabled or comments %},{% endif %}
{% if number_of_arguments %}
number_of_arguments => {{ number_of_arguments }}{% if enabled or comments %},{% endif %}
{% endif %}
{% if enabled %}
enabled => {{ enabled }}{% if comments %},{% endif %}
{% endif %}
{% if comments %}
comments => {{ comments|qtLiteral(conn) }}
{% endif %}
);
{% for args_list_item in arguments %}
EXEC dbms_scheduler.DEFINE_PROGRAM_ARGUMENT(
program_name => {{ program_name|qtLiteral(conn) }},
argument_position => {{ args_list_item['argid'] }},
argument_name => {{ args_list_item['argname']|qtLiteral(conn) }},
argument_type => {{ args_list_item['argtype']|qtLiteral(conn) }},
default_value => {{ args_list_item['argdefval']|qtLiteral(conn) }}
);
{% endfor %}
@@ -0,0 +1,3 @@
EXEC dbms_scheduler.DROP_PROGRAM(
{{ program_name|qtLiteral(conn) }}
);
Loaded 100 of 549 files, more files were not shown because too many files have changed in this diff. Show more