Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
527e084ef7 | ||
|
|
b6842e88ac | ||
|
|
f0327f5219 | ||
|
|
3cfc3366d7 | ||
|
|
05e067035f | ||
|
|
8b5abdfa8f | ||
|
|
782b7419f6 | ||
|
|
bbb8433af3 | ||
|
|
ef68cae1f1 | ||
|
|
9b0889842d | ||
|
|
eae18f6f14 | ||
|
|
29ed7ec881 | ||
|
|
220fdf583e | ||
|
|
628f0d254e | ||
|
|
7dde5d52d0 | ||
|
|
95cda78bdb | ||
|
|
e59eaedfd0 | ||
|
|
f17979141c | ||
|
|
3359a0de7a | ||
|
|
6e03775f05 | ||
|
|
b58f288eec | ||
|
|
140372a5cf | ||
|
|
d97431cb4f | ||
|
|
1e14b0d068 | ||
|
|
4ff0fcdd82 | ||
|
|
696fbfb8b9 | ||
|
|
be47390baf | ||
|
|
0d5a6eeeff | ||
|
|
6ddab02769 | ||
|
|
091aa62084 | ||
|
|
2058cdf662 | ||
|
|
6bc6bc7f60 | ||
|
|
370df47042 | ||
|
|
9ae49cbf31 | ||
|
|
822d384746 | ||
|
|
ce31726af5 | ||
|
|
494a8ac3fe | ||
|
|
50738db2b8 | ||
|
|
6fa6939f52 | ||
|
|
ef0e53e8fc | ||
|
|
fd2c2c4ff7 | ||
|
|
d6c4911135 | ||
|
|
6991c06937 | ||
|
|
3f16a0c52c | ||
|
|
4a6426d3e7 | ||
|
|
235a75202d | ||
|
|
55cf0cf2f9 | ||
|
|
0565f8dd78 | ||
|
|
bdf90e67a7 | ||
|
|
7144db7f5a | ||
|
|
c66840bc8e | ||
|
|
26ad6d4c5d | ||
|
|
87b64cf3c0 | ||
|
|
0f1651686b | ||
|
|
528ea88dec | ||
|
|
44ef501283 | ||
|
|
eb27fa29b1 | ||
|
|
a89ab88097 | ||
|
|
c092495b7a | ||
|
|
8e2ede21a0 | ||
|
|
15fe26a710 | ||
|
|
530017809a | ||
|
|
6c2c18106f | ||
|
|
1b20831338 | ||
|
|
6a46e43567 | ||
|
|
003889f15e | ||
|
|
a2258c83e9 | ||
|
|
370d9c72ec | ||
|
|
026969e3d1 | ||
|
|
0ec3224212 | ||
|
|
38ddea038a | ||
|
|
d6c64e8f11 | ||
|
|
4010dc80a9 | ||
|
|
9c5e42c7a6 | ||
|
|
532cbe216a | ||
|
|
1850a3a995 | ||
|
|
22776d2e14 | ||
|
|
7f1b588c69 | ||
|
|
8f88443e15 | ||
|
|
a74b9c96c1 | ||
|
|
0e59be237b | ||
|
|
6a4689599d | ||
|
|
6e54a13ba2 | ||
|
|
fa1cf7b7a3 | ||
|
|
82c9cca683 |
No files matched your search
@@ -27,7 +27,7 @@ Building the Runtime
|
||||
To build the runtime, the following packages must be installed:
|
||||
|
||||
- QT 4.6 or above
|
||||
- Python 2.6, 2.7 or 3.3+ (2.7 only on Windows)
|
||||
- Python 2.6, 2.7 or 3.4+
|
||||
|
||||
Assuming both qmake and python-config are in the path:
|
||||
|
||||
|
||||
@@ -113,6 +113,8 @@ When you’ve specified the details that will be incorporated into the pg_dump c
|
||||
.. image:: images/backup_messages.png
|
||||
:alt: Backup success notification popup
|
||||
|
||||
Use the **Stop Process** button to stop the Backup process.
|
||||
|
||||
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
||||
|
||||
.. image:: images/backup_process_watcher.png
|
||||
|
||||
@@ -24,6 +24,8 @@ Click the *Backup* button to build and execute a command based on your selection
|
||||
.. image:: images/backup_globals_messages.png
|
||||
:alt: Backup globals success notification popup
|
||||
|
||||
Use the **Stop Process** button to stop the Backup process.
|
||||
|
||||
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
||||
|
||||
.. image:: images/backup_globals_process_watcher.png
|
||||
|
||||
@@ -78,6 +78,8 @@ Click the *Backup* button to build and execute a command based on your selection
|
||||
.. image:: images/backup_server_messages.png
|
||||
:alt: Backup server success notification popup
|
||||
|
||||
Use the **Stop Process** button to stop the Backup process.
|
||||
|
||||
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
||||
|
||||
.. image:: images/backup_server_process_watcher.png
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
`Connecting to a Server`:index:
|
||||
*******************************
|
||||
|
||||
Before you can use the pgAdmin client to manage the objects that reside on your Postgres server, you must define a connection to the server. You can (optionally) use the *Server Group* dialog to create server groups to organize the server connections within the tree control for easier management. To open the *Server Group* dialog, right-click on the *Servers* node of the tree control, and select *Server Group* from the *Create* menu.
|
||||
Before you can use the pgAdmin client to manage the objects that reside on your
|
||||
Postgres server, you must define a connection to the server. You can
|
||||
(optionally) use the *Server Group* dialog to create server groups to organize
|
||||
the server connections within the tree control for easier management. To open
|
||||
the *Server Group* dialog, right-click on the *Servers* node of the tree
|
||||
control, and select *Server Group* from the *Create* menu.
|
||||
|
||||
Contents:
|
||||
|
||||
@@ -12,7 +17,10 @@ Contents:
|
||||
|
||||
server_group_dialog
|
||||
|
||||
Use the fields on the *Server* dialog to define the connection properties for each new server that you wish to manage with pgAdmin. To open the *Server* dialog, right-click on the *Servers* node of the tree control, and select *Server* from the *Create* menu.
|
||||
Use the fields on the *Server* dialog to define the connection properties for
|
||||
each new server that you wish to manage with pgAdmin. To open the *Server*
|
||||
dialog, right-click on the *Servers* node of the tree control, and select
|
||||
*Server* from the *Create* menu.
|
||||
|
||||
Contents:
|
||||
|
||||
@@ -20,11 +28,21 @@ Contents:
|
||||
|
||||
server_dialog
|
||||
|
||||
After defining a server connection, right-click on the server name, and select *Connect to server* to authenticate with the server, and start using pgAdmin to manage objects that reside on the server.
|
||||
After defining a server connection, right-click on the server name, and select
|
||||
*Connect to server* to authenticate with the server, and start using pgAdmin to
|
||||
manage objects that reside on the server.
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
|
||||
connect_to_server
|
||||
connect_error
|
||||
connect_error
|
||||
|
||||
Server definitions (and their groups) can be exported to a JSON file and
|
||||
re-imported to the same or a different system to enable easy pre-configuration
|
||||
of pgAdmin.
|
||||
|
||||
.. toctree::
|
||||
|
||||
export_import_servers
|
||||
@@ -0,0 +1,117 @@
|
||||
.. _export_import_servers:
|
||||
|
||||
****************************************
|
||||
`Exporting and importing Servers`:index:
|
||||
****************************************
|
||||
|
||||
Server definitions (and their groups) can be exported to a JSON file and
|
||||
re-imported to the same or a different system to enable easy pre-configuration
|
||||
of pgAdmin. The ``setup.py`` script is used for this purpose.
|
||||
|
||||
.. note:: To export or import servers, you must use the Python interpreter that
|
||||
is normally used to run pgAdmin to ensure that the required Python
|
||||
packages are available. In most packages, this can be found in the
|
||||
Python Virtual Environment that can be found in the installation
|
||||
directory. When using platform-native packages, the system installation
|
||||
of Python may be the one used by pgAdmin.
|
||||
|
||||
**Exporting Servers**
|
||||
|
||||
To export the servers defined in an installation, simply invoke ``setup.py`` with
|
||||
the ``--dump-servers`` command line option, followed by the name (and if required,
|
||||
path) to the desired output file. By default, servers owned by the desktop mode
|
||||
user will be dumped (pgadmin4@pgadmin.org by default - see the DESKTOP_USER
|
||||
setting in ``config.py``). This can be overridden with the ``--user`` command
|
||||
line option. For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
/path/to/python /path/to/setup.py --dump-servers output_file.json
|
||||
|
||||
# or, to specify a non-default user name:
|
||||
|
||||
/path/to/python /path/to/setup.py --dump-servers output_file.json --user user@example.com
|
||||
|
||||
To export only certain servers, use the ``--servers`` option and list one or
|
||||
more server IDs. For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
/path/to/python /path/to/setup.py --dump-servers output_file.json --server 1 2 5
|
||||
|
||||
**Importing Servers**
|
||||
|
||||
To import the servers defined in a JSON file, simply invoke ``setup.py`` with
|
||||
the ``--load-servers`` command line option, followed by the name (and if required,
|
||||
path) of the JSON file containing the server definitions. Servers will be owned
|
||||
by the desktop mode user (pgadmin4@pgadmin.org by default - see the DESKTOP_USER
|
||||
setting in ``config.py``). This can be overridden with the ``--user`` command
|
||||
line option. For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
/path/to/python /path/to/setup.py --load-servers input_file.json
|
||||
|
||||
# or, to specify a non-default user name to own the new servers:
|
||||
|
||||
/path/to/python /path/to/setup.py --load-servers input_file.json --user user@example.com
|
||||
|
||||
If any Servers are defined with a Server Group that is not already present in
|
||||
the configuration database, the required Group will be created.
|
||||
|
||||
**JSON format**
|
||||
|
||||
The JSON file format used when importing or exporting servers is quite
|
||||
straightforward and simply contains a list of servers, with a number of
|
||||
attributes. The following attributes are required to be present in every server
|
||||
definition: Name, Group, Port, Username, SSLMode, MaintenanceDB and one of Host,
|
||||
HostAddr or Service.
|
||||
|
||||
Password fields cannot be imported or exported.
|
||||
|
||||
The following example shows both a minimally defined and a fully defined server:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
{
|
||||
"Servers": {
|
||||
"1": {
|
||||
"Name": "Minimally Defined Server",
|
||||
"Group": "Server Group 1",
|
||||
"Port": 5432,
|
||||
"Username": "postgres",
|
||||
"Host": "localhost",
|
||||
"SSLMode": "prefer",
|
||||
"MaintenanceDB": "postgres"
|
||||
},
|
||||
"2": {
|
||||
"Name: "Fully Defined Server",
|
||||
"Group": "Server Group 2",
|
||||
"Host": "host.domain.com",
|
||||
"HostAddr": "192.168.1.2",
|
||||
"Port": 5432,
|
||||
"MaintenanceDB": "postgres",
|
||||
"Username": "postgres",
|
||||
"Role": "my_role_name",
|
||||
"SSLMode": "require",
|
||||
"Comment": "This server has every option configured in the JSON",
|
||||
"DBRestriction": "live_db test_db",
|
||||
"PassFile": "/path/to/pgpassfile",
|
||||
"SSLCert": "/path/to/sslcert.crt",
|
||||
"SSLKey": "/path/to/sslcert.key",
|
||||
"SSLRootCert": "/path/to/sslroot.crt",
|
||||
"SSLCrl": "/path/to/sslcrl.crl",
|
||||
"SSLCompression": 1,
|
||||
"BGColor": "#ff9900",
|
||||
"FGColor": "#000000",
|
||||
"Service": "postgresql-10",
|
||||
"Timeout": 60,
|
||||
"UseSSHTunnel": 1,
|
||||
"TunnelHost": "192.168.1.253",
|
||||
"TunnelPort": 22,
|
||||
"TunnelUsername": "username",
|
||||
"TunnelAuthentication": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,16 +74,12 @@ Contents:
|
||||
|
||||
browser
|
||||
pgadmin_menu_bar
|
||||
pgadmin_toolbar
|
||||
pgadmin_tabbed_browser
|
||||
pgadmin_tree_control
|
||||
preferences
|
||||
keyboard_shortcuts
|
||||
|
||||
Before using pgAdmin to manage objects that reside on a server, you must define a
|
||||
connection to the server; for more information please see *Connecting to a Server*:
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
|
||||
connecting
|
||||
connection to the server; for more information please see *Connecting to a Server*
|
||||
in the next section.
|
||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 340 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 204 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 322 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 228 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 180 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 233 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 259 KiB |
@@ -53,6 +53,8 @@ After completing the *Import/Export data* dialog, click the *OK* button to perfo
|
||||
.. image:: images/import_export_complete.png
|
||||
:alt: Import Export data completion notification
|
||||
|
||||
Use the **Stop Process** button to stop the Import/Export process.
|
||||
|
||||
Use the *Click here for details* link on the notification to open the *Process Watcher* and review detailed information about the execution of the command that performed the import or export:
|
||||
|
||||
.. image:: images/import_export_pw.png
|
||||
|
||||
@@ -34,6 +34,8 @@ pgAdmin will inform you when the background process completes:
|
||||
.. image:: images/maintenance_complete.png
|
||||
:alt: Maintenance completion notification
|
||||
|
||||
Use the **Stop Process** button to stop the Maintenance process.
|
||||
|
||||
Use the *Click here for details* link on the notification to open the *Process Watcher* and review detailed information about the execution of the command that performed the import or export:
|
||||
|
||||
.. image:: images/maintenance_pw.png
|
||||
|
||||
@@ -31,11 +31,17 @@ You can use icons in the *Sessions* table to review or control the state of a se
|
||||
.. image:: images/main_properties_table.png
|
||||
:alt: Properties panel
|
||||
|
||||
The *Properties* tab displays information about the object selected. Click the *Edit* icon in the toolbar under the browser tabs to launch the *Properties* dialog for the selected object.
|
||||
The *Properties* tab displays information about the object selected.
|
||||
|
||||
Click the *Delete* icon in the toolbar under the browser tab to delete the selected objects in the Properties panel.
|
||||
|
||||
Click the *Drop Cascade* icon in the toolbar under the browser tab to delete the selected objects and all dependent objects in the Properties panel.
|
||||
|
||||
.. image:: images/main_properties_icons.png
|
||||
:alt: Object editor icon
|
||||
|
||||
Click the *Edit* icon in the toolbar under the browser tabs to launch the *Properties* dialog for the selected object.
|
||||
|
||||
To preserve any changes to the *Properties* dialog, click the *Save* icon; your modifications will be displayed in the updated *Properties* tab.
|
||||
|
||||
.. image:: images/main_properties_edit.png
|
||||
@@ -136,4 +142,4 @@ The *Dependents* tab displays a table of objects that depend on the object curre
|
||||
|
||||
Additional tabs open when you access the extended functionality offered by pgAdmin tools (such as the Query tool, Debugger, or SQL editor). Use the close icon (X) located in the upper-right corner of each tab to close the tab when you are finished using the tool. Like permanent tabs, these tabs may be repositioned in the pgAdmin client window.
|
||||
|
||||
By default, each time you open a tool, pgAdmin will open a new browser tab. You can control this behavior by modifying the *Display* node of the *Preferences* dialog for each tool. To open the *Preferences* dialog, select *Preferences* from the *File* menu.
|
||||
By default, each time you open a tool, pgAdmin will open a new browser tab. You can control this behavior by modifying the *Display* node of the *Preferences* dialog for each tool. To open the *Preferences* dialog, select *Preferences* from the *File* menu.
|
||||
@@ -0,0 +1,16 @@
|
||||
.. _pgadmin_toolbar:
|
||||
|
||||
*************************
|
||||
`pgAdmin Toolbar`:index:
|
||||
*************************
|
||||
|
||||
The pgAdmin toolbar provides shortcut buttons for frequently used features like View Data and the Query Tool
|
||||
which are most frequently used in pgAdmin.
|
||||
This toolbar is visible on the Browser panel. Buttons get enabled/disabled based on the selected browser node.
|
||||
|
||||
.. image:: /images/pgadmin_toolbar.png
|
||||
:alt: pgAdmin Toolbar
|
||||
|
||||
* Use the :ref:`Query Tool <query_tool>` button to open the Query Tool in the current database context.
|
||||
* Use the :ref:`View Data <editgrid>` button to view/edit the data stored in a selected table.
|
||||
* Use the :ref:`Filtered Rows <editgrid>` button to access the Data Filter popup to apply a filter to a set of data for viewing/editing.
|
||||
@@ -69,9 +69,13 @@ Use the fields on the *Graphs* panel to specify your display preferences for the
|
||||
.. image:: images/preferences_dashboard_display.png
|
||||
:alt: Preferences dialog dashboard display options
|
||||
|
||||
* When the *Show activity?* switch is set to *True*, activity tables will be displayed on dashboards.
|
||||
|
||||
* When the *Show graph data points?* switch is set to *True*, data points will be visible on graph lines.
|
||||
|
||||
* When the *Show graphs?* switch is set to *True*, graphs will be displayed on dashboards.
|
||||
|
||||
* When the *Show activity?* switch is set to *True*, activity tables will be displayed on dashboards.
|
||||
* When the *Show mouse hover tooltip?* switch is set to *True*, a tooltip will appear on mouse hover on the graph lines giving the data point details.
|
||||
|
||||
|
||||
**The Debugger Node**
|
||||
@@ -123,9 +127,9 @@ Use the fields on the *Help* panel to specify the location of help files.
|
||||
|
||||
Please note: the default help paths include the *VERSION* placeholder; the $VERSION$ placeholder will be replaced by the current database version.
|
||||
|
||||
**The SQL Editor Node**
|
||||
**The Query Tool Node**
|
||||
|
||||
Expand the *SQL Editor* node to access panels that allow you to specify your preferences for the SQL Editor tool.
|
||||
Expand the *Query Tool* node to access panels that allow you to specify your preferences for the Query Editor tool.
|
||||
|
||||
.. image:: images/preferences_sql_auto_completion.png
|
||||
:alt: Preferences dialog sqleditor auto completion option
|
||||
@@ -146,13 +150,13 @@ Use the fields on the *CSV Output* panel to control the CSV output.
|
||||
.. image:: images/preferences_sql_display.png
|
||||
:alt: Preferences dialog sqleditor display options
|
||||
|
||||
Use the fields on the *Display* panel to specify your preferences for the SQL Editor display.
|
||||
Use the fields on the *Display* panel to specify your preferences for the Query Tool display.
|
||||
|
||||
* When the *Connection status* switch is set to *True*, each new instance of the SQL Editor will display connection and transaction status.
|
||||
* When the *Connection status* switch is set to *True*, each new instance of the Query Tool will display connection and transaction status.
|
||||
|
||||
* Use the *Connection status refresh rate* field to specify the number of seconds between connection/transaction status updates.
|
||||
|
||||
* When the *Open in new browser tab* switch is set to *True*, each new instance of the SQL Editor will open in a new browser tab.
|
||||
* When the *Open in new browser tab* switch is set to *True*, each new instance of the Query Tool will open in a new browser tab.
|
||||
|
||||
* Use the *Query info notifier timeout* field to control the behaviour of the notifier that is displayed when query execution completes. A value of *-1* will disable the notifier, and a value of 0 will display it until clicked. If a positive value above zero is specified, the notifier will be displayed for the specified number of seconds. The default is *5*.
|
||||
|
||||
@@ -203,7 +207,7 @@ Use the fields on the *Results grid* panel to specify your formatting preference
|
||||
* Use the *Result copy quote character* drop-down listbox to select the quote character for copied data.
|
||||
* Use the *Result copy quoting* drop-down listbox to select which type of fields require quoting; select *All*, *None*, or *Strings*.
|
||||
|
||||
Use the fields on the *Keyboard shortcuts* panel to configure shortcuts for the sql editor window navigation:
|
||||
Use the fields on the *Keyboard shortcuts* panel to configure shortcuts for the query tool window navigation:
|
||||
|
||||
.. image:: images/preferences_sql_keyboard_shortcuts.png
|
||||
:alt: Preferences dialog sql keyboard shortcuts section
|
||||
|
||||
@@ -9,6 +9,9 @@ for it.
|
||||
|
||||
.. toctree::
|
||||
|
||||
release_notes_3_6
|
||||
release_notes_3_5
|
||||
release_notes_3_4
|
||||
release_notes_3_3
|
||||
release_notes_3_2
|
||||
release_notes_3_1
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
***********
|
||||
Version 3.4
|
||||
***********
|
||||
|
||||
Release date: 2018-10-04
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 3.3
|
||||
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #2927 <https://redmine.postgresql.org/issues/2927>`_ - Move all CSS into SCSS files for consistency and ease of colour maintenance etc.
|
||||
| `Feature #3514 <https://redmine.postgresql.org/issues/3514>`_ - Add optional data point markers and mouse-over tooltips to display values on graphs.
|
||||
| `Feature #3564 <https://redmine.postgresql.org/issues/3564>`_ - Add shortcuts for View Data and the Query tool to the Browser header bar.
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #3464 <https://redmine.postgresql.org/issues/3464>`_ - Ensure the runtime can startup properly if there are wide characters in the logfile path on Windows.
|
||||
| `Bug #3551 <https://redmine.postgresql.org/issues/3551>`_ - Fix handling of backslashes in the edit grid.
|
||||
| `Bug #3576 <https://redmine.postgresql.org/issues/3576>`_ - Ensure queries are no longer executed when dashboards are closed.
|
||||
| `Bug #3596 <https://redmine.postgresql.org/issues/3596>`_ - Fix support for the CLOB datatype in EPAS.
|
||||
| `Bug #3607 <https://redmine.postgresql.org/issues/3607>`_ - Fix logic around validation and highlighting of Sort/Filter in the Query Tool.
|
||||
| `Bug #3630 <https://redmine.postgresql.org/issues/3630>`_ - Ensure auto-complete works for objects in schemas other than public and pg_catalog.
|
||||
| `Bug #3657 <https://redmine.postgresql.org/issues/3657>`_ - Ensure changes to Query Tool settings from the Preferences dialogue are applied before executing queries.
|
||||
| `Bug #3658 <https://redmine.postgresql.org/issues/3658>`_ - Swap the Schema and Schemas icons and Catalog and Catalogs icons that had been used the wrong way around.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
***********
|
||||
Version 3.5
|
||||
***********
|
||||
|
||||
Release date: 2018-11-01
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 3.4
|
||||
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #1253 <https://redmine.postgresql.org/issues/1253>`_ - Save the treeview state periodically, and restore it automatically when reconnecting.
|
||||
| `Feature #3562 <https://redmine.postgresql.org/issues/3562>`_ - Migrate from Bootstrap 3 to Bootstrap 4.
|
||||
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #3232 <https://redmine.postgresql.org/issues/3232>`_ - Ensure that Utilities(Backup/Restore/Maintenence/Import-Export) should not be started if binary path is wrong and also added 'Stop Process' button to cancel the process.
|
||||
| `Bug #3638 <https://redmine.postgresql.org/issues/3638>`_ - Fix syntax error when creating new pgAgent schedules with a start date/time and exception.
|
||||
| `Bug #3674 <https://redmine.postgresql.org/issues/3674>`_ - Cleanup session files periodically.
|
||||
| `Bug #3660 <https://redmine.postgresql.org/issues/3660>`_ - Rename the 'SQL Editor' section of the Preferences to 'Query Tool' as it applies to the whole tool, not just the editor.
|
||||
| `Bug #3676 <https://redmine.postgresql.org/issues/3676>`_ - Fix CREATE Script functionality for EDB-Wrapped functions.
|
||||
| `Bug #3700 <https://redmine.postgresql.org/issues/3700>`_ - Fix connection garbage collector.
|
||||
| `Bug #3703 <https://redmine.postgresql.org/issues/3703>`_ - Purge connections from the cache on logout.
|
||||
| `Bug #3722 <https://redmine.postgresql.org/issues/3722>`_ - Ensure that utility existence check should work for schema and other child objects while taking Backup/Restore.
|
||||
| `Bug #3730 <https://redmine.postgresql.org/issues/3730>`_ - Fixed fatal error while launching the pgAdmin4 3.5. Update the version of the Flask to 0.12.4 for release.
|
||||
@@ -0,0 +1,27 @@
|
||||
***********
|
||||
Version 3.6
|
||||
***********
|
||||
|
||||
Release date: 2018-11-29
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 3.5
|
||||
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #1513 <https://redmine.postgresql.org/issues/1513>`_ - Add support for dropping multiple objects at once from the collection Properties panel.
|
||||
| `Feature #3772 <https://redmine.postgresql.org/issues/3772>`_ - Add the ability to import and export server definitions from a config database.
|
||||
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #3016 <https://redmine.postgresql.org/issues/3016>`_ - Ensure previous notices are not removed from the Messages tab in the Query Tool if an error occurs during query execution.
|
||||
| `Bug #3029 <https://redmine.postgresql.org/issues/3029>`_ - Allow the selection order to be preserved in the Select2 control to fix column ordering in data Import/Export.
|
||||
| `Bug #3629 <https://redmine.postgresql.org/issues/3629>`_ - Allow use of 0 (integer) and empty strings as parameters in the debugger.
|
||||
| `Bug #3723 <https://redmine.postgresql.org/issues/3723>`_ - Properly report errors when debugging cannot be started.
|
||||
| `Bug #3734 <https://redmine.postgresql.org/issues/3734>`_ - Prevent the debugger controls being pressed again before previous processing is complete.
|
||||
| `Bug #3736 <https://redmine.postgresql.org/issues/3736>`_ - Fix toggle breakpoints buttons in the debugger.
|
||||
| `Bug #3742 <https://redmine.postgresql.org/issues/3742>`_ - Fix changes to the NOT NULL and default value options in the Table Dialogue.
|
||||
| `Bug #3746 <https://redmine.postgresql.org/issues/3746>`_ - Fix dropping of multiple functions/procedures at once.
|
||||
@@ -84,6 +84,8 @@ When you’ve specified the details that will be incorporated into the pg_restor
|
||||
.. image:: images/restore_messages.png
|
||||
:alt: Restore dialog notifications
|
||||
|
||||
Use the **Stop Process** button to stop the Restore process.
|
||||
|
||||
Click *Click here for details* on the popup to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the restore, and provides additional information for troubleshooting should the restore command encounter problems.
|
||||
|
||||
.. image:: images/restore_process_watcher.png
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#########################################################################
|
||||
|
||||
# First of all, build frontend with NodeJS in a separate builder container
|
||||
# Node-6 with ABI v48 is supported by all needed C++ packages
|
||||
FROM node:6 AS node-builder
|
||||
# Node-8 is supported by all needed C++ packages
|
||||
FROM node:8 AS node-builder
|
||||
|
||||
COPY ./pgadmin4/web/ /pgadmin4/web/
|
||||
WORKDIR /pgadmin4/web
|
||||
@@ -45,7 +45,8 @@ ENV PYTHONPATH=/pgadmin4
|
||||
# so that deps do not increase the size of resulting image by remaining in layers
|
||||
COPY ./pgadmin4/requirements.txt /pgadmin4
|
||||
RUN set -ex && \
|
||||
apk add --no-cache --virtual build-deps build-base postgresql-dev libffi-dev && \
|
||||
apk add --no-cache --virtual build-deps build-base postgresql-dev libffi-dev linux-headers && \
|
||||
pip install --upgrade pip && \
|
||||
pip install --no-cache-dir -r requirements.txt && \
|
||||
apk del --no-cache build-deps
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
blinker==1.4
|
||||
extras==1.0.0
|
||||
fixtures==3.0.0
|
||||
Flask==0.12.2
|
||||
Flask==0.12.4
|
||||
Flask-Gravatar==0.5.0
|
||||
Flask-Login==0.3.2
|
||||
Flask-Mail==0.9.1
|
||||
@@ -28,6 +28,7 @@ speaklater==1.3
|
||||
sqlparse==0.2.4
|
||||
WTForms==2.1
|
||||
Flask-Paranoid==0.2.0
|
||||
psutil==5.4.7
|
||||
|
||||
################################################################
|
||||
# Modules specifically requires for Python2.7 or greater version
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>pgAdmin 4</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.3.0</string>
|
||||
<string>3.6.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.3.0</string>
|
||||
<string>3.6.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.10</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// Logger.cpp - Logger Utility
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "Logger.h"
|
||||
Logger* Logger::m_pThis = NULL;
|
||||
QString Logger::m_sFileName = "";
|
||||
QFile* Logger::m_Logfile = NULL;
|
||||
|
||||
Logger::Logger()
|
||||
{
|
||||
}
|
||||
Logger::~Logger()
|
||||
{
|
||||
}
|
||||
|
||||
Logger* Logger::GetLogger()
|
||||
{
|
||||
if (m_pThis == NULL)
|
||||
{
|
||||
m_pThis = new Logger();
|
||||
m_sFileName = QDir::homePath() + (QString("/.%1.startup.log").arg(PGA_APP_NAME)).remove(" ");
|
||||
m_Logfile = new QFile;
|
||||
m_Logfile->setFileName(m_sFileName);
|
||||
m_Logfile->open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
m_Logfile->setPermissions(QFile::ReadOwner|QFile::WriteOwner);
|
||||
}
|
||||
|
||||
return m_pThis;
|
||||
}
|
||||
|
||||
void Logger::Log(const QString& sMessage)
|
||||
{
|
||||
QString text = QDateTime::currentDateTime().toString("dd.MM.yyyy hh:mm:ss ") + sMessage + "\n";
|
||||
if (m_Logfile != NULL)
|
||||
{
|
||||
QTextStream out(m_Logfile);
|
||||
out << text;
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::ReleaseLogger()
|
||||
{
|
||||
if (m_pThis != NULL)
|
||||
{
|
||||
if(m_Logfile != NULL)
|
||||
m_Logfile->close();
|
||||
delete m_pThis;
|
||||
m_pThis = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
// Logger.h - Logger Utility
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class Logger : public QObject
|
||||
{
|
||||
public:
|
||||
static Logger* GetLogger();
|
||||
static void ReleaseLogger();
|
||||
void Log(const QString& sMessage);
|
||||
|
||||
private:
|
||||
Logger();
|
||||
virtual ~Logger();
|
||||
|
||||
private:
|
||||
static Logger* m_pThis;
|
||||
static QString m_sFileName;
|
||||
static QFile *m_Logfile;
|
||||
};
|
||||
|
||||
#endif // LOGGER_H
|
||||
@@ -10,6 +10,7 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "pgAdmin4.h"
|
||||
#include "Logger.h"
|
||||
|
||||
// Must be before QT
|
||||
#include <Python.h>
|
||||
@@ -175,6 +176,10 @@ Server::Server(quint16 port, QString key, QString logFileName)
|
||||
|
||||
qDebug() << "Python path: " << python_path
|
||||
<< "\nPython Home: " << pythonHome;
|
||||
|
||||
Logger::GetLogger()->Log(QString("Python Path: %1").arg(python_path));
|
||||
Logger::GetLogger()->Log(QString("Python Home: %1").arg(pythonHome));
|
||||
|
||||
if (!pythonHome.isEmpty())
|
||||
{
|
||||
pythonHome_utf8 = pythonHome.toUtf8();
|
||||
@@ -190,36 +195,80 @@ Server::Server(quint16 port, QString key, QString logFileName)
|
||||
#endif
|
||||
}
|
||||
|
||||
Logger::GetLogger()->Log("Initializing Python...");
|
||||
Py_Initialize();
|
||||
Logger::GetLogger()->Log("Python initialized.");
|
||||
|
||||
// Get the current path
|
||||
PyObject* sysPath = PySys_GetObject((char*)"path");
|
||||
|
||||
// Add new additional path elements
|
||||
for (i = path_list.size() - 1; i >= 0 ; --i)
|
||||
if (sysPath != NULL)
|
||||
{
|
||||
// Add new additional path elements
|
||||
Logger::GetLogger()->Log("Adding new additional path elements");
|
||||
for (i = path_list.size() - 1; i >= 0 ; --i)
|
||||
{
|
||||
#ifdef PYTHON2
|
||||
PyList_Append(sysPath, PyString_FromString(path_list.at(i).toUtf8().data()));
|
||||
PyList_Append(sysPath, PyString_FromString(path_list.at(i).toUtf8().data()));
|
||||
#else
|
||||
#if PY_MINOR_VERSION > 2
|
||||
PyList_Append(sysPath, PyUnicode_DecodeFSDefault(path_list.at(i).toUtf8().data()));
|
||||
PyList_Append(sysPath, PyUnicode_DecodeFSDefault(path_list.at(i).toUtf8().data()));
|
||||
#else
|
||||
PyList_Append(sysPath, PyBytes_FromString(path_list.at(i).toUtf8().data()));
|
||||
PyList_Append(sysPath, PyBytes_FromString(path_list.at(i).toUtf8().data()));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
Logger::GetLogger()->Log("Unable to get the current path.");
|
||||
|
||||
// Redirect stderr
|
||||
Logger::GetLogger()->Log("Redirecting stderr...");
|
||||
PyObject *sys = PyImport_ImportModule("sys");
|
||||
if (sys != NULL)
|
||||
{
|
||||
PyObject *err = NULL;
|
||||
#ifdef PYTHON2
|
||||
PyObject *err = PyFile_FromString(m_logFileName.toUtf8().data(), (char *)"w");
|
||||
err = PyFile_FromString(m_logFileName.toUtf8().data(), (char *)"w");
|
||||
#else
|
||||
FILE *log = fopen(m_logFileName.toUtf8().data(), (char *)"w");
|
||||
int fd = fileno(log);
|
||||
PyObject *err = PyFile_FromFd(fd, NULL, (char *)"w", -1, NULL, NULL, NULL, 0);
|
||||
FILE *log = NULL;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
char *logFile = m_logFileName.toUtf8().data();
|
||||
size_t fileSize = strlen(logFile) + 1;
|
||||
wchar_t * wcLogFileName = new wchar_t[fileSize];
|
||||
mbstowcs (wcLogFileName, logFile, fileSize);
|
||||
|
||||
log = _wfopen(wcLogFileName, (wchar_t *)"w");
|
||||
#else
|
||||
log = fopen(m_logFileName.toUtf8().data(), (char *)"w");
|
||||
#endif
|
||||
QFile(m_logFileName).setPermissions(QFile::ReadOwner|QFile::WriteOwner);
|
||||
PyObject_SetAttrString(sys, "stderr", err);
|
||||
if (log != NULL)
|
||||
{
|
||||
int fd = fileno(log);
|
||||
err = PyFile_FromFd(fd, NULL, (char *)"w", -1, NULL, NULL, NULL, 0);
|
||||
}
|
||||
else
|
||||
Logger::GetLogger()->Log(QString("Failed to open log file: %1").arg(m_logFileName));
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
if (wcLogFileName != NULL)
|
||||
{
|
||||
delete wcLogFileName;
|
||||
wcLogFileName = NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
QFile(m_logFileName).setPermissions(QFile::ReadOwner|QFile::WriteOwner);
|
||||
if (err != NULL)
|
||||
{
|
||||
PyObject_SetAttrString(sys, "stderr", err);
|
||||
Logger::GetLogger()->Log("stderr redirected successfully.");
|
||||
}
|
||||
else
|
||||
Logger::GetLogger()->Log(QString("Failed to get the file pointer of: %1 ").arg(m_logFileName));
|
||||
}
|
||||
else
|
||||
Logger::GetLogger()->Log("Failed to import 'sys' module.");
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
@@ -263,12 +312,14 @@ bool Server::Init()
|
||||
if (QFile::exists(m_appfile))
|
||||
{
|
||||
qDebug() << "Webapp path: " << m_appfile;
|
||||
Logger::GetLogger()->Log(QString("Webapp Path: %1").arg(m_appfile));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!QFile::exists(m_appfile))
|
||||
{
|
||||
Logger::GetLogger()->Log("Failed to locate pgAdmin4.py, terminating server thread.");
|
||||
setError(tr("Failed to locate pgAdmin4.py, terminating server thread."));
|
||||
return false;
|
||||
}
|
||||
@@ -279,14 +330,17 @@ bool Server::Init()
|
||||
void Server::run()
|
||||
{
|
||||
// Open the application code and run it.
|
||||
Logger::GetLogger()->Log("Open the application code and run it.");
|
||||
FILE *cp = fopen(m_appfile.toUtf8().data(), "r");
|
||||
if (!cp)
|
||||
{
|
||||
Logger::GetLogger()->Log(QString(tr("Failed to open the application file: %1, server thread exiting.")).arg(m_appfile));
|
||||
setError(QString(tr("Failed to open the application file: %1, server thread exiting.")).arg(m_appfile));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the port number and key, and force SERVER_MODE off.
|
||||
Logger::GetLogger()->Log("Set the port number, key and force SERVER_MODE off");
|
||||
PyRun_SimpleString(QString("PGADMIN_PORT = %1").arg(m_port).toLatin1());
|
||||
PyRun_SimpleString(QString("PGADMIN_KEY = '%1'").arg(m_key).toLatin1());
|
||||
PyRun_SimpleString(QString("SERVER_MODE = False").toLatin1());
|
||||
@@ -303,10 +357,14 @@ void Server::run()
|
||||
char* n_argv[] = { m_appfile_utf8.data() };
|
||||
PySys_SetArgv(1, n_argv);
|
||||
|
||||
Logger::GetLogger()->Log("PyRun_SimpleFile launching application server...");
|
||||
PyObject* PyFileObject = PyFile_FromString(m_appfile_utf8.data(), (char *)"r");
|
||||
int ret = PyRun_SimpleFile(PyFile_AsFile(PyFileObject), m_appfile_utf8.data());
|
||||
if (ret != 0)
|
||||
{
|
||||
Logger::GetLogger()->Log("Failed to launch the application server, server thread exiting.");
|
||||
setError(tr("Failed to launch the application server, server thread exiting."));
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* Untrusted search path vulnerability in the PySys_SetArgv API function in Python 2.6 and earlier, and possibly later
|
||||
@@ -321,8 +379,12 @@ void Server::run()
|
||||
wchar_t* n_argv[] = { wcAppName };
|
||||
PySys_SetArgv(1, n_argv);
|
||||
|
||||
Logger::GetLogger()->Log("PyRun_SimpleFile launching application server...");
|
||||
if (PyRun_SimpleFile(cp, m_appfile_utf8.data()) != 0)
|
||||
{
|
||||
Logger::GetLogger()->Log("Failed to launch the application server, server thread exiting.");
|
||||
setError(tr("Failed to launch the application server, server thread exiting."));
|
||||
}
|
||||
#endif
|
||||
|
||||
fclose(cp);
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "TrayIcon.h"
|
||||
#include "MenuActions.h"
|
||||
#include "FloatingWindow.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include <QTime>
|
||||
|
||||
@@ -217,6 +218,7 @@ int main(int argc, char * argv[])
|
||||
menuActions->setLogFile(logFileName);
|
||||
|
||||
splash->showMessage(QString(QWidget::tr("Checking for system tray...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("Checking for system tray...");
|
||||
|
||||
// Check system tray is available or not. If not then create one floating window.
|
||||
FloatingWindow *floatingWindow = NULL;
|
||||
@@ -235,13 +237,15 @@ int main(int argc, char * argv[])
|
||||
else
|
||||
{
|
||||
splash->showMessage(QString(QWidget::tr("System tray not found, creating floating window...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("System tray not found, creating floating window...");
|
||||
// Unable to find tray icon, so creating floting window
|
||||
floatingWindow = new FloatingWindow();
|
||||
if (floatingWindow == NULL)
|
||||
{
|
||||
QString error = QString(QWidget::tr("Unable to initialize either a tray icon or control window."));
|
||||
QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -256,10 +260,14 @@ int main(int argc, char * argv[])
|
||||
bool done = false;
|
||||
|
||||
splash->showMessage(QString(QWidget::tr("Starting pgAdmin4 server...")), Qt::AlignBottom | Qt::AlignCenter);
|
||||
Logger::GetLogger()->Log("Starting pgAdmin4 server...");
|
||||
while (done != true)
|
||||
{
|
||||
QString msg = QString(QWidget::tr("Creating server object, port:%1, key:%2, logfile:%3")).arg(port).arg(key).arg(logFileName);
|
||||
Logger::GetLogger()->Log(msg);
|
||||
server = new Server(port, key, logFileName);
|
||||
|
||||
Logger::GetLogger()->Log("Initializing server...");
|
||||
if (!server->Init())
|
||||
{
|
||||
splash->finish(NULL);
|
||||
@@ -269,9 +277,14 @@ int main(int argc, char * argv[])
|
||||
QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError());
|
||||
QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Logger::GetLogger()->Log("Server initialized.");
|
||||
Logger::GetLogger()->Log("Starting Server Thread...");
|
||||
server->start();
|
||||
|
||||
// This is a hack to give the server a chance to start and potentially fail. As
|
||||
@@ -290,6 +303,7 @@ int main(int argc, char * argv[])
|
||||
|
||||
QString error = QString(QWidget::tr("An error occurred initialising the application server:\n\n%1")).arg(server->getError());
|
||||
QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);
|
||||
Logger::GetLogger()->Log(error);
|
||||
|
||||
// Allow the user to tweak the Python Path if needed
|
||||
bool ok;
|
||||
@@ -315,6 +329,7 @@ int main(int argc, char * argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -329,6 +344,7 @@ int main(int argc, char * argv[])
|
||||
|
||||
// Generate the app server URL
|
||||
QString appServerUrl = QString("http://127.0.0.1:%1/?key=%2").arg(port).arg(key);
|
||||
Logger::GetLogger()->Log(QString(QWidget::tr("Application Server URL: %1")).arg(appServerUrl));
|
||||
|
||||
// Read the server connection timeout from the registry or set the default timeout.
|
||||
int timeout = settings.value("ConnectionTimeout", 30).toInt();
|
||||
@@ -339,6 +355,7 @@ int main(int argc, char * argv[])
|
||||
QTime endTime = QTime::currentTime().addSecs(timeout);
|
||||
bool alive = false;
|
||||
|
||||
Logger::GetLogger()->Log("The server should be up, we'll attempt to connect and get a response. Ping the server");
|
||||
while(QTime::currentTime() <= endTime)
|
||||
{
|
||||
alive = PingServer(QUrl(appServerUrl));
|
||||
@@ -352,12 +369,14 @@ int main(int argc, char * argv[])
|
||||
}
|
||||
|
||||
// Attempt to connect one more time in case of a long network timeout while looping
|
||||
Logger::GetLogger()->Log("Attempt to connect one more time in case of a long network timeout while looping");
|
||||
if (!alive && !PingServer(QUrl(appServerUrl)))
|
||||
{
|
||||
splash->finish(NULL);
|
||||
QString error(QWidget::tr("The application server could not be contacted."));
|
||||
QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -392,6 +411,8 @@ int main(int argc, char * argv[])
|
||||
QString error(QWidget::tr("Failed to open the system default web browser. Is one installed?."));
|
||||
QMessageBox::critical(NULL, QString(QWidget::tr("Fatal Error")), error);
|
||||
|
||||
Logger::GetLogger()->Log(error);
|
||||
Logger::ReleaseLogger();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -402,6 +423,8 @@ int main(int argc, char * argv[])
|
||||
if (floatingWindow != NULL)
|
||||
floatingWindow->show();
|
||||
|
||||
Logger::GetLogger()->Log("Everything works fine, successfully started pgAdmin4.");
|
||||
Logger::ReleaseLogger();
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION = 3.3.0.0
|
||||
VERSION = 3.6.0.0
|
||||
QMAKE_TARGET_COMPANY = "The pgAdmin Development Team"
|
||||
QMAKE_TARGET_PRODUCT = "pgAdmin 4"
|
||||
QMAKE_TARGET_DESCRIPTION = "pgAdmin 4 Desktop Runtime"
|
||||
@@ -93,14 +93,16 @@ HEADERS = Server.h \
|
||||
TrayIcon.h \
|
||||
LogWindow.h \
|
||||
MenuActions.h \
|
||||
FloatingWindow.h
|
||||
FloatingWindow.h \
|
||||
Logger.h
|
||||
SOURCES = pgAdmin4.cpp \
|
||||
Server.cpp \
|
||||
ConfigWindow.cpp \
|
||||
TrayIcon.cpp \
|
||||
LogWindow.cpp \
|
||||
MenuActions.cpp \
|
||||
FloatingWindow.cpp
|
||||
FloatingWindow.cpp \
|
||||
Logger.cpp
|
||||
FORMS = ConfigWindow.ui \
|
||||
LogWindow.ui \
|
||||
FloatingWindow.ui
|
||||
|
||||
@@ -50,7 +50,7 @@ APP_ICON = 'pg-icon'
|
||||
|
||||
# Application version number components
|
||||
APP_RELEASE = 3
|
||||
APP_REVISION = 3
|
||||
APP_REVISION = 6
|
||||
|
||||
# Application version suffix, e.g. 'beta1', 'dev'. Usually an empty string
|
||||
# for GA releases.
|
||||
@@ -59,7 +59,7 @@ APP_SUFFIX = ''
|
||||
# Numeric application version for upgrade checks. Should be in the format:
|
||||
# [X]XYYZZ, where X is the release version, Y is the revision, with a leading
|
||||
# zero if needed, and Z represents the suffix, with a leading zero if needed
|
||||
APP_VERSION_INT = 30300
|
||||
APP_VERSION_INT = 30600
|
||||
|
||||
# DO NOT CHANGE!
|
||||
# The application version string, constructed from the components
|
||||
@@ -118,7 +118,7 @@ NODE_BLACKLIST = []
|
||||
# from it, notably various paths such as LOG_FILE and anything
|
||||
# using DATA_DIR.
|
||||
|
||||
if builtins.SERVER_MODE is None:
|
||||
if (not hasattr(builtins, 'SERVER_MODE')) or builtins.SERVER_MODE is None:
|
||||
SERVER_MODE = True
|
||||
else:
|
||||
SERVER_MODE = builtins.SERVER_MODE
|
||||
@@ -387,6 +387,17 @@ SESSION_SKIP_PATHS = [
|
||||
'/misc/ping'
|
||||
]
|
||||
|
||||
##########################################################################
|
||||
# Session expiration support
|
||||
##########################################################################
|
||||
# SESSION_EXPIRATION_TIME is the interval in Days. Session will be
|
||||
# expire after the specified number of *days*.
|
||||
SESSION_EXPIRATION_TIME = 1
|
||||
|
||||
# CHECK_SESSION_FILES_INTERVAL is interval in Hours. Application will check
|
||||
# the session files for cleanup after specified number of *hours*.
|
||||
CHECK_SESSION_FILES_INTERVAL = 24
|
||||
|
||||
##########################################################################
|
||||
# SSH Tunneling supports only for Python 2.7 and 3.4+
|
||||
##########################################################################
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""Added state of the utility process
|
||||
|
||||
Revision ID: b5b87fdfcb30
|
||||
Revises: ece2e76bf60e
|
||||
Create Date: 2018-10-24 12:37:59.487969
|
||||
|
||||
"""
|
||||
from pgadmin.model import db
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b5b87fdfcb30'
|
||||
down_revision = 'ece2e76bf60e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
db.engine.execute(
|
||||
'ALTER TABLE process ADD COLUMN process_state INTEGER DEFAULT 0'
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -0,0 +1,34 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
""" Added utility pid to stop process
|
||||
|
||||
Revision ID: ece2e76bf60e
|
||||
Revises: ca00ec32581b
|
||||
Create Date: 2018-10-18 14:45:13.483068
|
||||
|
||||
"""
|
||||
|
||||
from pgadmin.model import db
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ece2e76bf60e'
|
||||
down_revision = 'ca00ec32581b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
db.engine.execute(
|
||||
'ALTER TABLE process ADD COLUMN utility_pid INTEGER'
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -4,6 +4,7 @@
|
||||
"axios-mock-adapter": "^1.14.1",
|
||||
"babel-core": "~6.24.0",
|
||||
"babel-loader": "~7.1.2",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-preset-airbnb": "^2.4.0",
|
||||
"babel-preset-es2015": "~6.24.0",
|
||||
"babel-preset-react": "~6.23.0",
|
||||
@@ -32,8 +33,10 @@
|
||||
"karma-webpack": "~2.0.13",
|
||||
"node-sass": "^4.5.3",
|
||||
"optimize-css-assets-webpack-plugin": "^2.0.0",
|
||||
"popper.js": "^1.14.4",
|
||||
"raw-loader": "^0.5.1",
|
||||
"sass-loader": "^6.0.6",
|
||||
"sass-loader": "^7.1.0",
|
||||
"sass-resources-loader": "^1.3.3",
|
||||
"style-loader": "^0.18.2",
|
||||
"uglifyjs-webpack-plugin": "^0.4.6",
|
||||
"url-loader": "^0.5.9",
|
||||
@@ -54,7 +57,7 @@
|
||||
"backgrid-select-all": "^0.3.5",
|
||||
"backgrid-sizeable-columns": "^0.1.1",
|
||||
"bignumber.js": "^6.0.0",
|
||||
"bootstrap": "^3.3.7",
|
||||
"bootstrap": "^4.1.3",
|
||||
"bootstrap-datepicker": "^1.7.0",
|
||||
"bootstrap-switch": "3.3.4",
|
||||
"bowser": "1.6.1",
|
||||
@@ -63,11 +66,10 @@
|
||||
"css-loader": "0.14.0",
|
||||
"cssnano": "^3.10.0",
|
||||
"dropzone": "^5.1.1",
|
||||
"eonasdan-bootstrap-datetimepicker": "^4.17.47",
|
||||
"exports-loader": "~0.6.4",
|
||||
"flotr2": "^0.1.0",
|
||||
"flotr2": "git+https://github.com/EnterpriseDB/Flotr2.git",
|
||||
"font-awesome": "^4.7.0",
|
||||
"hard-source-webpack-plugin": "^0.4.9",
|
||||
"hard-source-webpack-plugin": "0.12.0",
|
||||
"immutability-helper": "^2.2.0",
|
||||
"imports-loader": "^0.7.1",
|
||||
"ip-address": "^5.8.9",
|
||||
@@ -76,6 +78,7 @@
|
||||
"jquery-ui": "^1.12.1",
|
||||
"leaflet": "^1.3.3",
|
||||
"moment": "^2.20.1",
|
||||
"moment-timezone": "^0.5.21",
|
||||
"mousetrap": "^1.6.1",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^16.2.0",
|
||||
@@ -89,10 +92,12 @@
|
||||
"spectrum-colorpicker": "^1.8.0",
|
||||
"sprintf-js": "^1.1.1",
|
||||
"tablesorter": "^2.30.6",
|
||||
"tempusdominus-bootstrap-4": "^5.1.2",
|
||||
"tempusdominus-core": "^5.0.3",
|
||||
"underscore": "^1.8.3",
|
||||
"underscore.string": "^3.3.4",
|
||||
"watchify": "~3.9.0",
|
||||
"webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker",
|
||||
"webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker/#5fbdf05a6757464d2fa2c2b017322c97688b2105",
|
||||
"wkx": "^0.4.5"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -32,6 +32,7 @@ from pgadmin.utils import PgAdminModule, driver
|
||||
from pgadmin.utils.preferences import Preferences
|
||||
from pgadmin.utils.session import create_session_interface, pga_unauthorised
|
||||
from pgadmin.utils.versioned_template_loader import VersionedTemplateLoader
|
||||
from datetime import timedelta
|
||||
|
||||
# If script is running under python3, it will not have the xrange function
|
||||
# defined
|
||||
@@ -354,6 +355,10 @@ def create_app(app_name=None):
|
||||
# register custom unauthorised handler.
|
||||
app.login_manager.unauthorized_handler(pga_unauthorised)
|
||||
|
||||
# Set the permanent session lifetime to the specified value in config file.
|
||||
app.permanent_session_lifetime = timedelta(
|
||||
days=config.SESSION_EXPIRATION_TIME)
|
||||
|
||||
app.session_interface = create_session_interface(
|
||||
app, config.SESSION_SKIP_PATHS
|
||||
)
|
||||
@@ -545,6 +550,13 @@ def create_app(app_name=None):
|
||||
def force_session_write(app, user):
|
||||
session.force_write = True
|
||||
|
||||
@user_logged_out.connect_via(app)
|
||||
def clear_current_user_connections(app, user):
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.utils.driver import get_driver
|
||||
_driver = get_driver(PG_DEFAULT_DRIVER)
|
||||
_driver.gc_own()
|
||||
|
||||
##########################################################################
|
||||
# Load plugin modules
|
||||
##########################################################################
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="col-sm-9">{{ info.current_user }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-right"><img
|
||||
<div class="col-12 text-right"><img
|
||||
src="{{ url_for('static', filename='img/logo-right-128.png') }}"
|
||||
alt="{{ config.APP_NAME }} {{ _('logo') }}"
|
||||
></div>
|
||||
|
||||
@@ -67,11 +67,9 @@ class BrowserModule(PgAdminModule):
|
||||
('static', 'vendor/codemirror/addon/dialog/dialog.css'),
|
||||
('static', context_menu_file),
|
||||
('static', wcdocker_file),
|
||||
('browser.static', 'css/browser.css'),
|
||||
('browser.static', 'vendor/aciTree/css/aciTree.css')
|
||||
]:
|
||||
stylesheets.append(url_for(endpoint, filename=filename))
|
||||
stylesheets.append(url_for('browser.browser_css'))
|
||||
return stylesheets
|
||||
|
||||
def get_own_javascripts(self):
|
||||
|
||||
@@ -28,6 +28,16 @@ def register_browser_preferences(self):
|
||||
True, category_label=gettext('Display')
|
||||
)
|
||||
|
||||
self.preference.register(
|
||||
'display', 'browser_tree_state_save_interval',
|
||||
gettext("Browser tree state saving interval"), 'integer',
|
||||
30, category_label=gettext('Display'),
|
||||
help_str=gettext(
|
||||
'Browser tree state saving interval in seconds.'
|
||||
'Use -1 to disable the tree saving mechanism.'
|
||||
)
|
||||
)
|
||||
|
||||
self.table_row_count_threshold = self.preference.register(
|
||||
'properties', 'table_row_count_threshold',
|
||||
gettext("Count rows if estimated less than"), 'integer', 2000,
|
||||
@@ -232,6 +242,36 @@ def register_browser_preferences(self):
|
||||
fields=fields
|
||||
)
|
||||
|
||||
self.preference.register(
|
||||
'keyboard_shortcuts',
|
||||
'grid_menu_drop_multiple',
|
||||
gettext('Delete/Drop multiple objects'),
|
||||
'keyboardshortcut',
|
||||
{
|
||||
'alt': True,
|
||||
'shift': True,
|
||||
'control': False,
|
||||
'key': {'key_code': 77, 'char': 'm'}
|
||||
},
|
||||
category_label=gettext('Keyboard shortcuts'),
|
||||
fields=fields
|
||||
)
|
||||
|
||||
self.preference.register(
|
||||
'keyboard_shortcuts',
|
||||
'grid_menu_drop_cascade_multiple',
|
||||
gettext('Drop Cascade multiple objects'),
|
||||
'keyboardshortcut',
|
||||
{
|
||||
'alt': True,
|
||||
'shift': True,
|
||||
'control': False,
|
||||
'key': {'key_code': 85, 'char': 'u'}
|
||||
},
|
||||
category_label=gettext('Keyboard shortcuts'),
|
||||
fields=fields
|
||||
)
|
||||
|
||||
self.preference.register(
|
||||
'keyboard_shortcuts',
|
||||
'context_menu',
|
||||
|
||||
@@ -585,7 +585,8 @@ class ServerNode(PGChildNodeView):
|
||||
server_icon_and_background(connected, manager, server),
|
||||
True,
|
||||
self.node_type,
|
||||
connected=False,
|
||||
connected=connected,
|
||||
user=manager.user_info if connected else None,
|
||||
server_type='pg' # default server type
|
||||
)
|
||||
)
|
||||
|
||||
@@ -104,7 +104,7 @@ class DatabaseView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'nodes': [
|
||||
{'get': 'node'},
|
||||
@@ -222,6 +222,15 @@ class DatabaseView(PGChildNodeView):
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
for row in res['rows']:
|
||||
if self.manager.db == row['name']:
|
||||
connected = True
|
||||
row['canDrop'] = False
|
||||
else:
|
||||
conn = self.manager.connection(row['name'], did=row['did'])
|
||||
connected = conn.connected()
|
||||
row['canDrop'] = True
|
||||
|
||||
return ajax_response(
|
||||
response=res['rows'],
|
||||
status=200
|
||||
@@ -733,44 +742,54 @@ class DatabaseView(PGChildNodeView):
|
||||
)
|
||||
|
||||
@check_precondition(action="drop")
|
||||
def delete(self, gid, sid, did):
|
||||
def delete(self, gid, sid, did=None):
|
||||
"""Delete the database."""
|
||||
default_conn = self.manager.connection()
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
did=did, conn=self.conn
|
||||
)
|
||||
status, res = default_conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if res is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=_(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=_(
|
||||
'The specified database could not be found.\n'
|
||||
)
|
||||
if did is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [did]}
|
||||
|
||||
status = self.manager.release(did=did)
|
||||
|
||||
for did in data['ids']:
|
||||
default_conn = self.manager.connection()
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
datname=res, conn=self.conn
|
||||
did=did, conn=self.conn
|
||||
)
|
||||
|
||||
status, msg = default_conn.execute_scalar(SQL)
|
||||
status, res = default_conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
# reconnect if database drop failed.
|
||||
conn = self.manager.connection(did=did, auto_reconnect=True)
|
||||
status, errmsg = conn.connect()
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return internal_server_error(errormsg=msg)
|
||||
if res is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=_(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=_(
|
||||
'The specified database could not be found.\n'
|
||||
)
|
||||
)
|
||||
else:
|
||||
|
||||
status = self.manager.release(did=did)
|
||||
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
datname=res, conn=self.conn
|
||||
)
|
||||
|
||||
status, msg = default_conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
# reconnect if database drop failed.
|
||||
conn = self.manager.connection(did=did,
|
||||
auto_reconnect=True)
|
||||
status, errmsg = conn.connect()
|
||||
|
||||
return internal_server_error(errormsg=msg)
|
||||
|
||||
return make_json_response(success=1)
|
||||
|
||||
|
||||
@@ -164,12 +164,12 @@ class CastView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'children': [{
|
||||
'get': 'children'
|
||||
}],
|
||||
'delete': [{'delete': 'delete'}],
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||
@@ -443,7 +443,7 @@ class CastView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, cid):
|
||||
def delete(self, gid, sid, did, cid=None):
|
||||
"""
|
||||
This function will drop the cast object
|
||||
:param cid: cast id
|
||||
@@ -459,50 +459,54 @@ class CastView(PGChildNodeView):
|
||||
else:
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name for cast from cid
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||
cid=cid)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified cast object could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
# drop cast
|
||||
result = res['rows'][0]
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||
castsource=result['castsource'],
|
||||
casttarget=result['casttarget'],
|
||||
cascade=cascade
|
||||
)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Cast dropped"),
|
||||
data={
|
||||
'id': cid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did
|
||||
}
|
||||
if cid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [cid]}
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
for cid in data['ids']:
|
||||
try:
|
||||
# Get name for cast from cid
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
cid=cid)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified cast object could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
# drop cast
|
||||
result = res['rows'][0]
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
castsource=result['castsource'],
|
||||
casttarget=result['casttarget'],
|
||||
cascade=cascade
|
||||
)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Cast dropped")
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, cid=None):
|
||||
|
||||
@@ -58,6 +58,7 @@ define('pgadmin.node.cast', [
|
||||
|
||||
// Define the backform model for cast node
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
idAttribute: 'oid',
|
||||
defaults: {
|
||||
name: undefined, // Name of the cast
|
||||
encoding: 'UTF8',
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
{% else %}
|
||||
SELECT
|
||||
ca.oid,
|
||||
ca.oid as id,
|
||||
CASE
|
||||
WHEN {{datlastsysoid}}::OID > ca.oid then True ELSE False
|
||||
END AS syscast,
|
||||
@@ -56,4 +57,4 @@
|
||||
ca.oid > {{datlastsysoid}}::OID
|
||||
{% endif %}
|
||||
ORDER BY st.typname, tt.typname
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,73 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
import json
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as cast_utils
|
||||
|
||||
|
||||
class CastsMultipleDeleteTestCase(BaseTestGenerator):
|
||||
""" This class will delete the cast node added under database node. """
|
||||
skip_on_database = ['gpdb']
|
||||
scenarios = [
|
||||
# Fetching default URL for cast node.
|
||||
('Check Cast Node', dict(url='/browser/cast/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(CastsMultipleDeleteTestCase, self).setUp()
|
||||
self.default_db = self.server["db"]
|
||||
self.database_info = parent_node_dict['database'][-1]
|
||||
self.db_name = self.database_info['db_name']
|
||||
self.server["db"] = self.db_name
|
||||
self.source_type = 'money'
|
||||
self.target_type = 'bigint'
|
||||
self.cast_id = cast_utils.create_cast(self.server, self.source_type,
|
||||
self.target_type)
|
||||
|
||||
def runTest(self):
|
||||
""" This function will delete added cast."""
|
||||
self.server_id = self.database_info["server_id"]
|
||||
self.db_id = self.database_info['db_id']
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if not db_con["info"] == "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
connection = utils.get_db_connection(self.server['db'],
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'],
|
||||
self.server['sslmode'])
|
||||
response = cast_utils.verify_cast(connection, self.source_type,
|
||||
self.target_type)
|
||||
if len(response) == 0:
|
||||
raise Exception("Could not find cast.")
|
||||
delete_response = self.tester.delete(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(self.server_id) + '/' + str(self.db_id) +
|
||||
'/',
|
||||
data=json.dumps({'ids': [self.cast_id]}),
|
||||
content_type='html/json',
|
||||
follow_redirects=True)
|
||||
self.assertEquals(delete_response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function will disconnect test database."""
|
||||
database_utils.disconnect_database(self, self.server_id,
|
||||
self.db_id)
|
||||
self.server['db'] = self.default_db
|
||||
@@ -164,7 +164,7 @@ class EventTriggerView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'children': [{'get': 'children'}],
|
||||
@@ -478,7 +478,7 @@ class EventTriggerView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, etid):
|
||||
def delete(self, gid, sid, did, etid=None):
|
||||
"""
|
||||
This function will delete an existing event trigger object.
|
||||
|
||||
@@ -497,48 +497,51 @@ class EventTriggerView(PGChildNodeView):
|
||||
cascade = True
|
||||
else:
|
||||
cascade = False
|
||||
|
||||
if etid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [etid]}
|
||||
|
||||
try:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
etid=etid
|
||||
)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified event trigger could not be found.\n'
|
||||
)
|
||||
for etid in data['ids']:
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
etid=etid
|
||||
)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
name=name, cascade=cascade
|
||||
)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified event trigger could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
name=name, cascade=cascade
|
||||
)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Event trigger dropped"),
|
||||
data={
|
||||
'id': etid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did
|
||||
}
|
||||
info=gettext("Event trigger dropped")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, etid=None):
|
||||
|
||||
@@ -12,6 +12,8 @@ define('pgadmin.node.event_trigger', [
|
||||
label: gettext('Event Trigger'),
|
||||
type: 'coll-event_trigger',
|
||||
columns: ['name', 'eventowner', 'comment'],
|
||||
canDrop: true,
|
||||
canDropCascade: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,6 +57,7 @@ define('pgadmin.node.event_trigger', [
|
||||
},
|
||||
// Define the model for event trigger node
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
idAttribute: 'oid',
|
||||
defaults: {
|
||||
oid: undefined,
|
||||
name: undefined,
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.utils import server_utils as server_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression import trigger_funcs_utils
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as event_trigger_utils
|
||||
|
||||
|
||||
class EventTriggerMultipleDeleteTestCase(BaseTestGenerator):
|
||||
""" This class will delete added event trigger under test database. """
|
||||
scenarios = [
|
||||
# Fetching default URL for event trigger node.
|
||||
('Fetch Event Trigger Node URL',
|
||||
dict(url='/browser/event_trigger/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
self.schema_data = parent_node_dict['schema'][-1]
|
||||
self.server_id = self.schema_data['server_id']
|
||||
self.db_id = self.schema_data['db_id']
|
||||
self.schema_name = self.schema_data['schema_name']
|
||||
self.schema_id = self.schema_data['schema_id']
|
||||
self.extension_name = "postgres_fdw"
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
self.db_user = self.server["username"]
|
||||
self.func_name = "trigger_func_%s" % str(uuid.uuid4())[1:8]
|
||||
self.trigger_names = ["event_trigger_delete_%s" % (
|
||||
str(uuid.uuid4())[1:8]), "event_trigger_delete_%s" % (
|
||||
str(uuid.uuid4())[1:8])]
|
||||
server_con = server_utils.connect_server(self, self.server_id)
|
||||
if not server_con["info"] == "Server connected.":
|
||||
raise Exception("Could not connect to server to add resource "
|
||||
"groups.")
|
||||
server_version = 0
|
||||
if "type" in server_con["data"]:
|
||||
if server_con["data"]["version"] < 90300:
|
||||
message = "Event triggers are not supported by PG9.2 " \
|
||||
"and PPAS9.2 and below."
|
||||
self.skipTest(message)
|
||||
self.function_info = trigger_funcs_utils.create_trigger_function(
|
||||
self.server, self.db_name, self.schema_name, self.func_name,
|
||||
server_version)
|
||||
self.event_trigger_ids = []
|
||||
self.event_trigger_ids.append(event_trigger_utils.create_event_trigger(
|
||||
self.server, self.db_name, self.schema_name, self.func_name,
|
||||
self.trigger_names[0]))
|
||||
self.event_trigger_ids.append(event_trigger_utils.create_event_trigger(
|
||||
self.server, self.db_name, self.schema_name, self.func_name,
|
||||
self.trigger_names[1]))
|
||||
|
||||
def runTest(self):
|
||||
""" This function will delete event trigger under test database. """
|
||||
db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
|
||||
self.server_id, self.db_id)
|
||||
if not db_con['data']["connected"]:
|
||||
raise Exception("Could not connect to database.")
|
||||
schema_response = schema_utils.verify_schemas(self.server,
|
||||
self.db_name,
|
||||
self.schema_name)
|
||||
if not schema_response:
|
||||
raise Exception("Could not find the schema.")
|
||||
func_name = self.function_info[1]
|
||||
func_response = trigger_funcs_utils.verify_trigger_function(
|
||||
self.server,
|
||||
self.db_name,
|
||||
func_name)
|
||||
if not func_response:
|
||||
raise Exception("Could not find the trigger function.")
|
||||
trigger_response = event_trigger_utils.verify_event_trigger(
|
||||
self.server, self.db_name,
|
||||
self.trigger_names[0])
|
||||
if not trigger_response:
|
||||
raise Exception("Could not find event trigger.")
|
||||
trigger_response = event_trigger_utils.verify_event_trigger(
|
||||
self.server, self.db_name,
|
||||
self.trigger_names[1])
|
||||
if not trigger_response:
|
||||
raise Exception("Could not find event trigger.")
|
||||
data = {'ids': self.event_trigger_ids}
|
||||
del_response = self.tester.delete(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(self.server_id) + '/' +
|
||||
str(self.db_id) + '/',
|
||||
follow_redirects=True,
|
||||
data=json.dumps(data),
|
||||
content_type='html/json')
|
||||
self.assertEquals(del_response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
# Disconnect the database
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
||||
@@ -123,9 +123,9 @@ class ExtensionView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'delete': [{'delete': 'delete'}],
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||
@@ -327,49 +327,53 @@ class ExtensionView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, eid):
|
||||
def delete(self, gid, sid, did, eid=None):
|
||||
"""
|
||||
This function will drop/drop cascade a extension object
|
||||
"""
|
||||
|
||||
if eid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [eid]}
|
||||
|
||||
cascade = True if self.cmd == 'delete' else False
|
||||
|
||||
try:
|
||||
# check if extension with eid exists
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'delete.sql']), eid=eid)
|
||||
status, name = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
for eid in data['ids']:
|
||||
# check if extension with eid exists
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'delete.sql']), eid=eid)
|
||||
status, name = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified extension could not be found.\n'
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified extension could not be found.\n'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# drop extension
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'delete.sql']
|
||||
), name=name, cascade=cascade)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
# drop extension
|
||||
SQL = render_template("/".join(
|
||||
[self.template_path, 'delete.sql']
|
||||
), name=name, cascade=cascade)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Extension dropped"),
|
||||
data={
|
||||
'id': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
info=gettext("Extension dropped")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ define('pgadmin.node.extension', [
|
||||
* of the model in schema.
|
||||
*/
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
idAttribute: 'eid',
|
||||
schema: [
|
||||
{
|
||||
id: 'name', label: gettext('Name'), first_empty: true,
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
import json
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as extension_utils
|
||||
|
||||
|
||||
class ExtensionsDeleteMultipleTestCase(BaseTestGenerator):
|
||||
skip_on_database = ['gpdb']
|
||||
scenarios = [
|
||||
# Fetching default URL for extension node.
|
||||
('Check Extension Node', dict(url='/browser/extension/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
""" This function will create extensions."""
|
||||
super(ExtensionsDeleteMultipleTestCase, self).setUp()
|
||||
self.schema_data = parent_node_dict['schema'][-1]
|
||||
self.server_id = self.schema_data['server_id']
|
||||
self.db_id = self.schema_data['db_id']
|
||||
self.schema_name = self.schema_data['schema_name']
|
||||
|
||||
self.extension_names = ["dblink", "hstore"]
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
self.extension_ids = []
|
||||
self.extension_ids.append(extension_utils.create_extension(
|
||||
self.server, self.db_name, self.extension_names[0],
|
||||
self.schema_name))
|
||||
self.extension_ids.append(extension_utils.create_extension(
|
||||
self.server, self.db_name, self.extension_names[1],
|
||||
self.schema_name))
|
||||
|
||||
def runTest(self):
|
||||
""" This function will delete extensions added test database. """
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if not db_con["info"] == "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
response = extension_utils.verify_extension(self.server, self.db_name,
|
||||
self.extension_names[0])
|
||||
if not response:
|
||||
raise Exception("Could not find extension.")
|
||||
response = extension_utils.verify_extension(self.server, self.db_name,
|
||||
self.extension_names[1])
|
||||
if not response:
|
||||
raise Exception("Could not find extension.")
|
||||
data = {'ids': self.extension_ids}
|
||||
delete_response = self.tester.delete(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(self.server_id) + '/' + str(self.db_id) + '/',
|
||||
follow_redirects=True,
|
||||
data=json.dumps(data),
|
||||
content_type='html/json'
|
||||
)
|
||||
self.assertEquals(delete_response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function disconnect the test database. """
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
||||
@@ -186,11 +186,9 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'delete': [{
|
||||
'delete': 'delete'
|
||||
}],
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'children': [{'get': 'children'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
@@ -478,7 +476,7 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, fid):
|
||||
def delete(self, gid, sid, did, fid=None):
|
||||
"""
|
||||
This function will delete the selected foreign data wrapper node.
|
||||
|
||||
@@ -488,56 +486,59 @@ class ForeignDataWrapperView(PGChildNodeView):
|
||||
did: Database ID
|
||||
fid: foreign data wrapper ID
|
||||
"""
|
||||
if fid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [fid]}
|
||||
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
cascade = True
|
||||
else:
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name of foreign data wrapper from fid
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||
fid=fid, conn=self.conn
|
||||
)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
for fid in data['ids']:
|
||||
try:
|
||||
# Get name of foreign data wrapper from fid
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
fid=fid, conn=self.conn
|
||||
)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign data'
|
||||
' wrapper could not be found.\n'
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign data'
|
||||
' wrapper could not be found.\n'
|
||||
)
|
||||
)
|
||||
)
|
||||
# drop foreign data wrapper node
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=name,
|
||||
cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
# drop foreign data wrapper node
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=name,
|
||||
cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Foreign Data Wrapper dropped"),
|
||||
data={
|
||||
'id': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Foreign Data Wrapper dropped")
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, fid=None):
|
||||
|
||||
@@ -183,11 +183,9 @@ class ForeignServerView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'delete': [{
|
||||
'delete': 'delete'
|
||||
}],
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'children': [{'get': 'children'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
@@ -498,7 +496,7 @@ class ForeignServerView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, fid, fsid):
|
||||
def delete(self, gid, sid, did, fid, fsid=None):
|
||||
"""
|
||||
This function will delete the selected foreign server node.
|
||||
|
||||
@@ -510,6 +508,13 @@ class ForeignServerView(PGChildNodeView):
|
||||
fsid: foreign server ID
|
||||
"""
|
||||
|
||||
if fsid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [fsid]}
|
||||
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
cascade = True
|
||||
@@ -517,43 +522,40 @@ class ForeignServerView(PGChildNodeView):
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name of foreign data wrapper from fid
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||
fsid=fsid, conn=self.conn)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
for fsid in data['ids']:
|
||||
# Get name of foreign data wrapper from fid
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
fsid=fsid, conn=self.conn)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign server could not be found.\n'
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign server '
|
||||
'could not be found.\n'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# drop foreign server
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||
name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
# drop foreign server
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Foreign Server dropped"),
|
||||
data={
|
||||
'id': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
info=gettext("Foreign Server dropped")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -92,6 +92,7 @@ define('pgadmin.node.foreign_server', [
|
||||
|
||||
// Defining model for foreign server node
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
idAttribute: 'fsrvid',
|
||||
defaults: {
|
||||
name: undefined,
|
||||
fsrvtype: undefined,
|
||||
|
||||
@@ -11,8 +11,6 @@ from __future__ import print_function
|
||||
|
||||
import uuid
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import \
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.\
|
||||
tests import utils as fdw_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
@@ -39,11 +37,9 @@ class ForeignServerDeleteTestCase(BaseTestGenerator):
|
||||
self.db_id = self.schema_data['db_id']
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
self.schema_name = self.schema_data['schema_name']
|
||||
self.extension_name = "cube"
|
||||
self.fdw_name = "test_fdw_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.fsrv_name = "test_fsrv_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.extension_id = extension_utils.create_extension(
|
||||
self.server, self.db_name, self.extension_name, self.schema_name)
|
||||
|
||||
self.fdw_id = fdw_utils.create_fdw(self.server, self.db_name,
|
||||
self.fdw_name)
|
||||
self.fsrv_id = fsrv_utils.create_fsrv(self.server, self.db_name,
|
||||
@@ -75,8 +71,9 @@ class ForeignServerDeleteTestCase(BaseTestGenerator):
|
||||
self.assertEquals(delete_response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function disconnect the test database and drop added extension
|
||||
and dependant objects."""
|
||||
extension_utils.drop_extension(self.server, self.db_name,
|
||||
self.extension_name)
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
||||
"""This function disconnect the test database and drop
|
||||
added foreign data server and dependant objects."""
|
||||
fdw_utils.delete_fdw(self.server, self.db_name,
|
||||
self.fdw_name)
|
||||
database_utils.disconnect_database(self, self.server_id,
|
||||
self.db_id)
|
||||
@@ -0,0 +1,90 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.\
|
||||
tests import utils as fdw_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as fsrv_utils
|
||||
|
||||
|
||||
class ForeignServerDeleteMultipleTestCase(BaseTestGenerator):
|
||||
"""This class will add foreign server under FDW node."""
|
||||
skip_on_database = ['gpdb']
|
||||
scenarios = [
|
||||
# Fetching default URL for foreign server node.
|
||||
('Check FSRV Node', dict(url='/browser/foreign_server/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
""" This function will create extension and foreign data wrapper."""
|
||||
super(ForeignServerDeleteMultipleTestCase, self).setUp()
|
||||
self.schema_data = parent_node_dict['schema'][-1]
|
||||
self.server_id = self.schema_data['server_id']
|
||||
self.db_id = self.schema_data['db_id']
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
self.schema_name = self.schema_data['schema_name']
|
||||
self.fdw_name = "test_fdw_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.fsrv_names = ["test_fsrv_%s" % (str(uuid.uuid4())[1:8]),
|
||||
"test_fsrv_%s" % (str(uuid.uuid4())[1:8])]
|
||||
|
||||
self.fdw_id = fdw_utils.create_fdw(self.server, self.db_name,
|
||||
self.fdw_name)
|
||||
self.fsrv_ids = [fsrv_utils.create_fsrv(self.server, self.db_name,
|
||||
self.fsrv_names[0],
|
||||
self.fdw_name),
|
||||
fsrv_utils.create_fsrv(self.server, self.db_name,
|
||||
self.fsrv_names[1],
|
||||
self.fdw_name)]
|
||||
|
||||
def runTest(self):
|
||||
"""This function will fetch foreign server present under test
|
||||
database."""
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if not db_con["info"] == "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
fdw_response = fdw_utils.verify_fdw(self.server, self.db_name,
|
||||
self.fdw_name)
|
||||
if not fdw_response:
|
||||
raise Exception("Could not find FDW.")
|
||||
fsrv_response = fsrv_utils.verify_fsrv(self.server, self.db_name,
|
||||
self.fsrv_names[0])
|
||||
if not fsrv_response:
|
||||
raise Exception("Could not find FSRV.")
|
||||
fsrv_response = fsrv_utils.verify_fsrv(self.server, self.db_name,
|
||||
self.fsrv_names[1])
|
||||
if not fsrv_response:
|
||||
raise Exception("Could not find FSRV.")
|
||||
data = {'ids': self.fsrv_ids}
|
||||
delete_response = self.tester.delete(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(self.server_id) + '/' + str(self.db_id) +
|
||||
'/' + str(self.fdw_id) + "/",
|
||||
data=json.dumps(data),
|
||||
content_type='html/json',
|
||||
follow_redirects=True)
|
||||
self.assertEquals(delete_response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function disconnect the test database and drop added
|
||||
foreign data server and dependant objects."""
|
||||
fdw_utils.delete_fdw(self.server, self.db_name,
|
||||
self.fdw_name)
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
||||
@@ -200,11 +200,9 @@ class UserMappingView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'delete': [{
|
||||
'delete': 'delete'
|
||||
}],
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'children': [{'get': 'children'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
@@ -506,7 +504,7 @@ class UserMappingView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, fid, fsid, umid):
|
||||
def delete(self, gid, sid, did, fid, fsid, umid=None):
|
||||
"""
|
||||
This function will delete the selected user mapping node.
|
||||
|
||||
@@ -518,6 +516,12 @@ class UserMappingView(PGChildNodeView):
|
||||
fsid: foreign server ID
|
||||
umid: User mapping ID
|
||||
"""
|
||||
if umid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [umid]}
|
||||
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
@@ -526,64 +530,59 @@ class UserMappingView(PGChildNodeView):
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name of foreign server from fsid
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||
fsid=fsid, conn=self.conn)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
for umid in data['ids']:
|
||||
# Get name of foreign server from fsid
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
fsid=fsid, conn=self.conn)
|
||||
status, name = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign server could not be found.\n'
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign server '
|
||||
'could not be found.\n'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'properties.sql']),
|
||||
umid=umid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'properties.sql']),
|
||||
umid=umid, conn=self.conn)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'The specified user mapping could not be found.\n'
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'The specified user mapping could not be found.\n'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
data = res['rows'][0]
|
||||
data = res['rows'][0]
|
||||
|
||||
# drop user mapping
|
||||
sql = render_template("/".join([self.template_path, 'delete.sql']),
|
||||
data=data, name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
# drop user mapping
|
||||
sql = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
data=data, name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("User Mapping dropped"),
|
||||
data={
|
||||
'id': umid,
|
||||
'fsid': fsid,
|
||||
'fid': fid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
info=gettext("User Mapping dropped")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ define('pgadmin.node.user_mapping', [
|
||||
|
||||
// Defining model for user mapping node
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
idAttribute: 'um_oid',
|
||||
defaults: {
|
||||
name: undefined,
|
||||
um_options: [],
|
||||
|
||||
@@ -41,11 +41,8 @@ class UserMappingDeleteTestCase(BaseTestGenerator):
|
||||
self.db_id = self.schema_data['db_id']
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
self.schema_name = self.schema_data['schema_name']
|
||||
self.extension_name = "cube"
|
||||
self.fdw_name = "fdw_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.fsrv_name = "fsrv_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.extension_id = extension_utils.create_extension(
|
||||
self.server, self.db_name, self.extension_name, self.schema_name)
|
||||
self.fdw_id = fdw_utils.create_fdw(self.server, self.db_name,
|
||||
self.fdw_name)
|
||||
self.fsrv_id = fsrv_utils.create_fsrv(self.server, self.db_name,
|
||||
@@ -83,8 +80,9 @@ class UserMappingDeleteTestCase(BaseTestGenerator):
|
||||
self.assertEquals(delete_response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function disconnect the test database and drop added extension
|
||||
and dependant objects."""
|
||||
extension_utils.drop_extension(self.server, self.db_name,
|
||||
self.extension_name)
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
||||
"""This function disconnect the test database and drop
|
||||
foreign data wrapper and dependant objects."""
|
||||
fdw_utils.delete_fdw(self.server, self.db_name,
|
||||
self.fdw_name)
|
||||
database_utils.disconnect_database(self, self.server_id,
|
||||
self.db_id)
|
||||
@@ -0,0 +1,91 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.extensions.tests import \
|
||||
utils as extension_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers. \
|
||||
foreign_servers.tests import utils as fsrv_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers.\
|
||||
tests import utils as fdw_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as um_utils
|
||||
|
||||
|
||||
class UserMappingDeleteMultipleTestCase(BaseTestGenerator):
|
||||
"""This class will delete user mapping under foreign server node."""
|
||||
skip_on_database = ['gpdb']
|
||||
scenarios = [
|
||||
# Fetching default URL for user mapping node.
|
||||
('Check user mapping Node', dict(url='/browser/user_mapping/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
""" This function will create extension and foreign data wrapper."""
|
||||
super(UserMappingDeleteMultipleTestCase, self).setUp()
|
||||
self.schema_data = parent_node_dict['schema'][-1]
|
||||
self.server_id = self.schema_data['server_id']
|
||||
self.db_id = self.schema_data['db_id']
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
self.schema_name = self.schema_data['schema_name']
|
||||
self.fdw_name = "fdw_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.fsrv_name = "fsrv_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.fdw_id = fdw_utils.create_fdw(self.server, self.db_name,
|
||||
self.fdw_name)
|
||||
self.fsrv_id = fsrv_utils.create_fsrv(self.server, self.db_name,
|
||||
self.fsrv_name, self.fdw_name)
|
||||
self.um_id = um_utils.create_user_mapping(self.server, self.db_name,
|
||||
self.fsrv_name)
|
||||
|
||||
def runTest(self):
|
||||
"""This function will delete user mapping present under test
|
||||
database. """
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if not db_con["info"] == "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
fdw_response = fdw_utils.verify_fdw(self.server, self.db_name,
|
||||
self.fdw_name)
|
||||
if not fdw_response:
|
||||
raise Exception("Could not find FDW.")
|
||||
fsrv_response = fsrv_utils.verify_fsrv(self.server, self.db_name,
|
||||
self.fsrv_name)
|
||||
if not fsrv_response:
|
||||
raise Exception("Could not find FSRV.")
|
||||
um_response = um_utils.verify_user_mapping(self.server, self.db_name,
|
||||
self.fsrv_name)
|
||||
if not um_response:
|
||||
raise Exception("Could not find user mapping.")
|
||||
data = {'ids': [self.um_id]}
|
||||
delete_response = self.tester.delete(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(self.server_id) + '/' + str(self.db_id) +
|
||||
'/' + str(self.fdw_id) + '/' +
|
||||
str(self.fsrv_id) + "/",
|
||||
follow_redirects=True,
|
||||
data=json.dumps(data),
|
||||
content_type='html/json')
|
||||
self.assertEquals(delete_response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function disconnect the test database and drop
|
||||
foreign data wrapper and dependant objects."""
|
||||
fdw_utils.delete_fdw(self.server, self.db_name, self.fdw_name)
|
||||
database_utils.disconnect_database(self, self.server_id,
|
||||
self.db_id)
|
||||
@@ -95,6 +95,7 @@ define('pgadmin.node.foreign_data_wrapper', [
|
||||
|
||||
// Defining model for foreign data wrapper node
|
||||
model: pgBrowser.Node.Model.extend({
|
||||
idAttribute: 'fdwoid',
|
||||
defaults: {
|
||||
name: undefined,
|
||||
fdwowner: undefined,
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as fdw_utils
|
||||
|
||||
|
||||
class FDWDDeleteMultipleTestCase(BaseTestGenerator):
|
||||
"""This class will delete foreign data wrappers under test database."""
|
||||
skip_on_database = ['gpdb']
|
||||
scenarios = [ # Fetching default URL for foreign_data_wrapper node.
|
||||
('Check FDW Node',
|
||||
dict(url='/browser/foreign_data_wrapper/obj/'))]
|
||||
|
||||
def setUp(self):
|
||||
""" This function will create extension and foreign data wrapper."""
|
||||
super(FDWDDeleteMultipleTestCase, self).setUp()
|
||||
self.schema_data = parent_node_dict['schema'][-1]
|
||||
self.server_id = self.schema_data['server_id']
|
||||
self.db_id = self.schema_data['db_id']
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
self.schema_name = self.schema_data['schema_name']
|
||||
self.fdw_names = ["fdw_{0}".format(str(uuid.uuid4())[1:8]),
|
||||
"fdw_{0}".format(str(uuid.uuid4())[1:8])]
|
||||
self.fdw_ids = [fdw_utils.create_fdw(self.server, self.db_name,
|
||||
self.fdw_names[0]),
|
||||
fdw_utils.create_fdw(self.server, self.db_name,
|
||||
self.fdw_names[1])]
|
||||
|
||||
def runTest(self):
|
||||
"""This function will fetch foreign data wrapper present under test
|
||||
database."""
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if not db_con["info"] == "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
fdw_response = fdw_utils.verify_fdw(self.server, self.db_name,
|
||||
self.fdw_names[0])
|
||||
if not fdw_response:
|
||||
raise Exception("Could not find FDW.")
|
||||
fdw_response = fdw_utils.verify_fdw(self.server, self.db_name,
|
||||
self.fdw_names[1])
|
||||
if not fdw_response:
|
||||
raise Exception("Could not find FDW.")
|
||||
data = {'ids': self.fdw_ids}
|
||||
delete_response = self.tester.delete(self.url +
|
||||
str(utils.SERVER_GROUP) +
|
||||
'/' + str(self.server_id) + '/' +
|
||||
str(self.db_id) + '/',
|
||||
follow_redirects=True,
|
||||
data=json.dumps(data),
|
||||
content_type='html/json')
|
||||
self.assertEquals(delete_response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function disconnect the test database and drop added extension
|
||||
and dependant objects."""
|
||||
database_utils.disconnect_database(self, self.server_id,
|
||||
self.db_id)
|
||||
@@ -128,6 +128,6 @@ def delete_fdw(server, db_name, fdw_name):
|
||||
server['port'],
|
||||
server['sslmode'])
|
||||
pg_cursor = connection.cursor()
|
||||
pg_cursor.execute("DROP FOREIGN DATA WRAPPER %s" % fdw_name)
|
||||
pg_cursor.execute("DROP FOREIGN DATA WRAPPER %s CASCADE" % fdw_name)
|
||||
connection.commit()
|
||||
connection.close()
|
||||
@@ -188,7 +188,7 @@ class LanguageView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
@@ -198,7 +198,7 @@ class LanguageView(PGChildNodeView):
|
||||
'dependent': [{'get': 'dependents'}],
|
||||
'get_functions': [{}, {'get': 'get_functions'}],
|
||||
'get_templates': [{}, {'get': 'get_templates'}],
|
||||
'delete': [{'delete': 'delete'}]
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}]
|
||||
})
|
||||
|
||||
def _init_(self, **kwargs):
|
||||
@@ -490,7 +490,7 @@ class LanguageView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, lid):
|
||||
def delete(self, gid, sid, did, lid=None):
|
||||
"""
|
||||
This function will drop the language object
|
||||
|
||||
@@ -500,6 +500,13 @@ class LanguageView(PGChildNodeView):
|
||||
did: Database ID
|
||||
lid: Language ID
|
||||
"""
|
||||
if lid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [lid]}
|
||||
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
cascade = True
|
||||
@@ -507,35 +514,30 @@ class LanguageView(PGChildNodeView):
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name for language from lid
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
lid=lid, conn=self.conn
|
||||
)
|
||||
status, lname = self.conn.execute_scalar(sql)
|
||||
for lid in data['ids']:
|
||||
# Get name for language from lid
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
lid=lid, conn=self.conn
|
||||
)
|
||||
status, lname = self.conn.execute_scalar(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=lname)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=lname)
|
||||
|
||||
# drop language
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
lname=lname, cascade=cascade, conn=self.conn
|
||||
)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
# drop language
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
lname=lname, cascade=cascade, conn=self.conn
|
||||
)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Language dropped"),
|
||||
data={
|
||||
'id': lid,
|
||||
'did': did,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
}
|
||||
info=gettext("Language dropped")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -59,6 +59,7 @@ define('pgadmin.node.language', [
|
||||
|
||||
// Define the model for language node
|
||||
model: pgBrowser.Node.Model.extend({
|
||||
idAttribute: 'oid',
|
||||
defaults: {
|
||||
name: undefined,
|
||||
lanowner: undefined,
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as language_utils
|
||||
|
||||
|
||||
class LanguagesDeleteMultipleTestCase(BaseTestGenerator):
|
||||
scenarios = [
|
||||
('Language delete test case', dict(url='/browser/language/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
self.server_data = parent_node_dict["database"][-1]
|
||||
self.server_id = self.server_data["server_id"]
|
||||
self.db_id = self.server_data['db_id']
|
||||
self.db_name = self.server_data["db_name"]
|
||||
self.lang_names = ["language_%s" % str(uuid.uuid4())[1:8],
|
||||
"language_%s" % str(uuid.uuid4())[1:8]]
|
||||
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
if not db_con["info"] == "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
self.language_ids = [language_utils.create_language(
|
||||
self.server,
|
||||
self.db_name,
|
||||
self.lang_names[0]
|
||||
),
|
||||
language_utils.create_language(
|
||||
self.server,
|
||||
self.db_name,
|
||||
self.lang_names[1])
|
||||
]
|
||||
|
||||
def runTest(self):
|
||||
"""This function will delete languages under test database."""
|
||||
data = {'ids': self.language_ids}
|
||||
response = self.tester.delete("{0}{1}/{2}/{3}/".format(
|
||||
self.url, utils.SERVER_GROUP, self.server_id, self.db_id),
|
||||
follow_redirects=True,
|
||||
data=json.dumps(data),
|
||||
content_type='html/json'
|
||||
)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
"""This function disconnect the test database."""
|
||||
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
||||
@@ -214,7 +214,7 @@ class SchemaView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'children': [{'get': 'children'}],
|
||||
'nodes': [{'get': 'nodes'}, {'get': 'nodes'}],
|
||||
@@ -223,7 +223,8 @@ class SchemaView(PGChildNodeView):
|
||||
'stats': [{'get': 'statistics'}],
|
||||
'dependency': [{'get': 'dependencies'}],
|
||||
'dependent': [{'get': 'dependents'}],
|
||||
'delete': [{'delete': 'delete'}]
|
||||
'delete': [{'delete': 'delete'},
|
||||
{'delete': 'delete'}]
|
||||
})
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -645,7 +646,7 @@ It may have been removed by another user.
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, scid):
|
||||
def delete(self, gid, sid, did, scid=None):
|
||||
"""
|
||||
This function will delete an existing schema object
|
||||
|
||||
@@ -656,54 +657,56 @@ It may have been removed by another user.
|
||||
scid: Schema ID
|
||||
"""
|
||||
|
||||
try:
|
||||
# Get name for schema from did
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'sql/get_name.sql']),
|
||||
_=gettext,
|
||||
scid=scid
|
||||
if scid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [scid]}
|
||||
|
||||
status, name = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified schema could not be found.\n'
|
||||
)
|
||||
for scid in data['ids']:
|
||||
try:
|
||||
# Get name for schema from did
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'sql/get_name.sql']),
|
||||
_=gettext,
|
||||
scid=scid
|
||||
)
|
||||
|
||||
# drop schema
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'sql/delete.sql']),
|
||||
_=gettext, name=name, conn=self.conn,
|
||||
cascade=True if self.cmd == 'delete' else False
|
||||
)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
status, name = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Schema dropped"),
|
||||
data={
|
||||
'id': scid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did
|
||||
}
|
||||
)
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified schema could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return internal_server_error(errormsg=str(e))
|
||||
# drop schema
|
||||
SQL = render_template(
|
||||
"/".join([self.template_path, 'sql/delete.sql']),
|
||||
_=gettext, name=name, conn=self.conn,
|
||||
cascade=True if self.cmd == 'delete' else False
|
||||
)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Schema dropped")
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def msql(self, gid, sid, did, scid=None):
|
||||
|
||||
@@ -10,6 +10,8 @@ define('pgadmin.node.catalog_object_column', [
|
||||
label: gettext('catalog_object_column'),
|
||||
type: 'coll-catalog_object_column',
|
||||
columns: ['attname', 'attnum', 'cltype', 'description'],
|
||||
canDrop: false,
|
||||
canDropCascade: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ define('pgadmin.node.catalog_object', [
|
||||
label: gettext('Catalog Objects'),
|
||||
type: 'coll-catalog_object',
|
||||
columns: ['name', 'owner', 'description'],
|
||||
canDrop: false,
|
||||
canDropCascade: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -161,9 +161,9 @@ class CollationView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'delete': [{'delete': 'delete'}],
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}],
|
||||
'children': [{'get': 'children'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
@@ -482,7 +482,7 @@ class CollationView(PGChildNodeView):
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, scid, coid):
|
||||
def delete(self, gid, sid, did, scid, coid=None):
|
||||
"""
|
||||
This function will delete existing the collation object
|
||||
|
||||
@@ -493,6 +493,12 @@ class CollationView(PGChildNodeView):
|
||||
scid: Schema ID
|
||||
coid: Collation ID
|
||||
"""
|
||||
if coid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [coid]}
|
||||
|
||||
# Below will decide if it's simple drop or drop with cascade call
|
||||
if self.cmd == 'delete':
|
||||
@@ -502,40 +508,36 @@ class CollationView(PGChildNodeView):
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'get_name.sql']),
|
||||
scid=scid, coid=coid)
|
||||
status, name = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
for coid in data['ids']:
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'get_name.sql']),
|
||||
scid=scid, coid=coid)
|
||||
status, name = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=name)
|
||||
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified collation could not be found.\n'
|
||||
if name is None:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified collation could not be found.\n'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=name, cascade=cascade,
|
||||
conn=self.conn)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Collation dropped"),
|
||||
data={
|
||||
'id': coid,
|
||||
'scid': scid,
|
||||
'did': did
|
||||
}
|
||||
info=gettext("Collation dropped")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -54,6 +54,7 @@ define('pgadmin.node.collation', [
|
||||
|
||||
},
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
idAttribute: 'oid',
|
||||
defaults: {
|
||||
name: undefined,
|
||||
oid: undefined,
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||
database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as collation_utils
|
||||
|
||||
|
||||
class CollationDeleteMultipleTestCase(BaseTestGenerator):
|
||||
""" This class will delete added collations under schema node. """
|
||||
skip_on_database = ['gpdb']
|
||||
scenarios = [
|
||||
# Fetching default URL for collation node.
|
||||
('Fetch collation Node URL', dict(url='/browser/collation/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(CollationDeleteMultipleTestCase, self).setUp()
|
||||
self.schema_info = parent_node_dict["schema"][-1]
|
||||
self.schema_name = self.schema_info["schema_name"]
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
coll_names = ["collation_get_%s" % str(uuid.uuid4())[1:8],
|
||||
"collation_get_%s" % str(uuid.uuid4())[1:8]]
|
||||
|
||||
self.collations = [collation_utils.create_collation(self.server,
|
||||
self.schema_name,
|
||||
coll_names[0],
|
||||
self.db_name),
|
||||
collation_utils.create_collation(self.server,
|
||||
self.schema_name,
|
||||
coll_names[1],
|
||||
self.db_name)
|
||||
]
|
||||
|
||||
def runTest(self):
|
||||
""" This function will delete collations under schema node. """
|
||||
server_id = self.schema_info["server_id"]
|
||||
db_id = self.schema_info["db_id"]
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
server_id,
|
||||
db_id)
|
||||
if not db_con['data']["connected"]:
|
||||
raise Exception("Could not connect to database.")
|
||||
|
||||
schema_response = schema_utils.verify_schemas(self.server,
|
||||
self.db_name,
|
||||
self.schema_name)
|
||||
if not schema_response:
|
||||
raise Exception("Could not find the schema.")
|
||||
data = {'ids': [self.collations[0][0], self.collations[1][0]]}
|
||||
schema_id = self.schema_info["schema_id"]
|
||||
get_response = self.tester.delete(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' + str(
|
||||
server_id) + '/' +
|
||||
str(db_id) + '/' + str(schema_id) + '/',
|
||||
content_type='html/json',
|
||||
data=json.dumps(data),
|
||||
follow_redirects=True,
|
||||
)
|
||||
self.assertEquals(get_response.status_code, 200)
|
||||
# Disconnect database to delete it
|
||||
database_utils.disconnect_database(self, server_id, db_id)
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
@@ -158,9 +158,9 @@ class DomainView(PGChildNodeView, DataTypeReader):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'delete': [{'delete': 'delete'}],
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}],
|
||||
'children': [{'get': 'children'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
@@ -581,7 +581,7 @@ AND relkind != 'c'))"""
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, scid, doid):
|
||||
def delete(self, gid, sid, did, scid, doid=None):
|
||||
"""
|
||||
Drops the Domain object.
|
||||
|
||||
@@ -592,6 +592,12 @@ AND relkind != 'c'))"""
|
||||
scid: Schema Id
|
||||
doid: Domain Id
|
||||
"""
|
||||
if doid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [doid]}
|
||||
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
@@ -599,45 +605,39 @@ AND relkind != 'c'))"""
|
||||
else:
|
||||
cascade = False
|
||||
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
scid=scid, doid=doid)
|
||||
status, res = self.conn.execute_2darray(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
for doid in data['ids']:
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
scid=scid, doid=doid)
|
||||
status, res = self.conn.execute_2darray(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified domain could not be found.\n'
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
status=410,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified domain could not be found.\n'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
name = res['rows'][0]['name']
|
||||
basensp = res['rows'][0]['basensp']
|
||||
name = res['rows'][0]['name']
|
||||
basensp = res['rows'][0]['basensp']
|
||||
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=name, basensp=basensp, cascade=cascade)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=name, basensp=basensp, cascade=cascade)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Domain dropped"),
|
||||
data={
|
||||
'id': doid,
|
||||
'scid': scid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did
|
||||
}
|
||||
info=gettext("Domain dropped")
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
|
||||
@@ -167,9 +167,8 @@ class DomainConstraintView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'delete': [{'delete': 'delete'}],
|
||||
'children': [{'get': 'children'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
@@ -450,7 +449,7 @@ class DomainConstraintView(PGChildNodeView):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, scid, doid, coid):
|
||||
def delete(self, gid, sid, did, scid, doid, coid=None):
|
||||
"""
|
||||
Drops the Domain Constraint object.
|
||||
|
||||
@@ -462,45 +461,47 @@ class DomainConstraintView(PGChildNodeView):
|
||||
doid: Domain Id
|
||||
coid: Domain Constraint Id
|
||||
"""
|
||||
if coid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [coid]}
|
||||
|
||||
try:
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'properties.sql']),
|
||||
doid=doid, coid=coid)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
for coid in data['ids']:
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'properties.sql']),
|
||||
doid=doid, coid=coid)
|
||||
status, res = self.conn.execute_dict(SQL)
|
||||
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified domain constraint could not be found.\n'
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified domain constraint '
|
||||
'could not be found.\n'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
data = res['rows'][0]
|
||||
data = res['rows'][0]
|
||||
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
data=data)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
data=data)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Domain Constraint dropped"),
|
||||
data={
|
||||
'id': doid,
|
||||
'scid': scid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did
|
||||
}
|
||||
info=gettext("Domain Constraint dropped")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -13,6 +13,8 @@ define('pgadmin.node.domain_constraints', [
|
||||
label: gettext('Domain Constraints'),
|
||||
type: 'coll-domain_constraints',
|
||||
columns: ['name', 'description'],
|
||||
canDrop: true,
|
||||
canDropCascade: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,6 +61,7 @@ define('pgadmin.node.domain_constraints', [
|
||||
},
|
||||
canDrop: schemaChildTreeNode.isTreeItemOfChildOfSchema,
|
||||
model: pgAdmin.Browser.Node.Model.extend({
|
||||
idAttribute: 'oid',
|
||||
defaults: {
|
||||
name: undefined,
|
||||
oid: undefined,
|
||||
|
||||
@@ -120,6 +120,7 @@ define('pgadmin.node.domain', [
|
||||
},
|
||||
// Domain Node Model
|
||||
model: pgBrowser.Node.Model.extend({
|
||||
idAttribute: 'oid',
|
||||
initialize: function(attrs, args) {
|
||||
var isNew = (_.size(attrs) === 0);
|
||||
if (isNew) {
|
||||
|
||||
@@ -23,7 +23,7 @@ class DomainDeleteTestCase(BaseTestGenerator):
|
||||
""" This class will delete new domain under schema node. """
|
||||
scenarios = [
|
||||
# Fetching default URL for domain node.
|
||||
('Fetch domain Node URL', dict(url='/browser/domain/delete/'))
|
||||
('Fetch domain Node URL', dict(url='/browser/domain/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
@@ -61,8 +61,12 @@ class DomainDeleteTestCase(BaseTestGenerator):
|
||||
str(db_id) + '/' +
|
||||
str(self.schema_id) + '/' +
|
||||
str(domain_id),
|
||||
content_type='html/json')
|
||||
content_type='html/json',
|
||||
follow_redirects=True
|
||||
)
|
||||
|
||||
self.assertEquals(get_response.status_code, 200)
|
||||
|
||||
# Disconnect the database
|
||||
database_utils.disconnect_database(self, server_id, db_id)
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
|
||||
utils as schema_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||
database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as domain_utils
|
||||
|
||||
|
||||
class DomainDeleteMultipleTestCase(BaseTestGenerator):
|
||||
""" This class will delete new domains under schema node. """
|
||||
scenarios = [
|
||||
# Fetching default URL for domain node.
|
||||
('Fetch domain Node URL', dict(url='/browser/domain/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(DomainDeleteMultipleTestCase, self).setUp()
|
||||
self.database_info = parent_node_dict["database"][-1]
|
||||
self.db_name = self.database_info["db_name"]
|
||||
self.schema_info = parent_node_dict["schema"][-1]
|
||||
self.schema_name = self.schema_info["schema_name"]
|
||||
self.schema_id = self.schema_info["schema_id"]
|
||||
self.domain_names = ["domain_delete_%s" % (str(uuid.uuid4())[1:8]),
|
||||
"domain_delete_%s" % (str(uuid.uuid4())[1:8])]
|
||||
self.domain_infos = [domain_utils.create_domain(self.server,
|
||||
self.db_name,
|
||||
self.schema_name,
|
||||
self.schema_id,
|
||||
self.domain_names[0]),
|
||||
domain_utils.create_domain(self.server,
|
||||
self.db_name,
|
||||
self.schema_name,
|
||||
self.schema_id,
|
||||
self.domain_names[1])]
|
||||
|
||||
def runTest(self):
|
||||
""" This function will add domain under schema node. """
|
||||
db_id = self.database_info["db_id"]
|
||||
server_id = self.database_info["server_id"]
|
||||
db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
|
||||
server_id, db_id)
|
||||
if not db_con['data']["connected"]:
|
||||
raise Exception("Could not connect to database to get the domain.")
|
||||
db_name = self.database_info["db_name"]
|
||||
schema_response = schema_utils.verify_schemas(self.server,
|
||||
db_name,
|
||||
self.schema_name)
|
||||
if not schema_response:
|
||||
raise Exception("Could not find the schema to get the domain.")
|
||||
data = {'ids': [self.domain_infos[0][0], self.domain_infos[1][0]]}
|
||||
url = self.url + str(utils.SERVER_GROUP) + '/' + str(
|
||||
server_id) + '/' + str(db_id) + '/' + str(self.schema_id) + "/"
|
||||
# Call GET API to verify the domain
|
||||
get_response = self.tester.delete(
|
||||
url,
|
||||
content_type='html/json',
|
||||
follow_redirects=True,
|
||||
data=json.dumps(data))
|
||||
|
||||
self.assertEquals(get_response.status_code, 200)
|
||||
# Disconnect the database
|
||||
database_utils.disconnect_database(self, server_id, db_id)
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
@@ -194,9 +194,9 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'delete': [{'delete': 'delete'}],
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}],
|
||||
'children': [{'get': 'children'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
@@ -708,7 +708,7 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, scid, foid):
|
||||
def delete(self, gid, sid, did, scid, foid=None):
|
||||
"""
|
||||
Drops the Foreign Table.
|
||||
|
||||
@@ -719,6 +719,13 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
|
||||
scid: Schema Id
|
||||
foid: Foreign Table Id
|
||||
"""
|
||||
if foid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [foid]}
|
||||
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
cascade = True
|
||||
@@ -726,47 +733,41 @@ class ForeignTableView(PGChildNodeView, DataTypeReader):
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Fetch Name and Schema Name to delete the foreign table.
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']), scid=scid,
|
||||
foid=foid)
|
||||
status, res = self.conn.execute_2darray(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
for foid in data['ids']:
|
||||
# Fetch Name and Schema Name to delete the foreign table.
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']), scid=scid,
|
||||
foid=foid)
|
||||
status, res = self.conn.execute_2darray(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign table could not be found.\n'
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=gettext(
|
||||
'The specified foreign table could not be found.\n'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
name = res['rows'][0]['name']
|
||||
basensp = res['rows'][0]['basensp']
|
||||
name = res['rows'][0]['name']
|
||||
basensp = res['rows'][0]['basensp']
|
||||
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=name,
|
||||
basensp=basensp,
|
||||
cascade=cascade)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
SQL = render_template("/".join([self.template_path,
|
||||
'delete.sql']),
|
||||
name=name,
|
||||
basensp=basensp,
|
||||
cascade=cascade)
|
||||
status, res = self.conn.execute_scalar(SQL)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=gettext("Foreign Table dropped"),
|
||||
data={
|
||||
'id': foid,
|
||||
'scid': scid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did
|
||||
}
|
||||
info=gettext("Foreign Table dropped")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers. \
|
||||
foreign_servers.tests import utils as fsrv_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.foreign_data_wrappers. \
|
||||
tests import utils as fdw_utils
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import \
|
||||
utils as database_utils
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as ft_utils
|
||||
|
||||
|
||||
class ForeignTableDeleteMultipleTestCase(BaseTestGenerator):
|
||||
"""
|
||||
This class will delete foreign table under database node.
|
||||
"""
|
||||
skip_on_database = ['gpdb']
|
||||
|
||||
scenarios = [
|
||||
# Fetching default URL for foreign table node.
|
||||
('Check foreign table Node', dict(url='/browser/foreign_table/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
""" This function will create foreign data wrapper, foreign server
|
||||
and foreign table. """
|
||||
super(ForeignTableDeleteMultipleTestCase, self).setUp()
|
||||
|
||||
self.schema_data = parent_node_dict['schema'][-1]
|
||||
self.server_id = self.schema_data['server_id']
|
||||
self.db_id = self.schema_data['db_id']
|
||||
self.db_name = parent_node_dict["database"][-1]["db_name"]
|
||||
self.schema_name = self.schema_data['schema_name']
|
||||
self.schema_id = self.schema_data['schema_id']
|
||||
self.fdw_name = "fdw_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.fsrv_name = "fsrv_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.ft_name = "ft_%s" % (str(uuid.uuid4())[1:8])
|
||||
self.fdw_id = fdw_utils.create_fdw(self.server, self.db_name,
|
||||
self.fdw_name)
|
||||
self.fsrv_id = fsrv_utils.create_fsrv(self.server, self.db_name,
|
||||
self.fsrv_name, self.fdw_name)
|
||||
self.ft_ids = [ft_utils.create_foreign_table(
|
||||
self.server, self.db_name,
|
||||
self.schema_name, self.fsrv_name,
|
||||
"ft_%s" % (str(uuid.uuid4())[1:8])),
|
||||
ft_utils.create_foreign_table(
|
||||
self.server, self.db_name,
|
||||
self.schema_name, self.fsrv_name,
|
||||
"ft_%s" % (str(uuid.uuid4())[1:8]))
|
||||
]
|
||||
|
||||
def runTest(self):
|
||||
"""This function will delete foreign table under test database."""
|
||||
|
||||
db_con = database_utils.connect_database(self,
|
||||
utils.SERVER_GROUP,
|
||||
self.server_id,
|
||||
self.db_id)
|
||||
|
||||
if not db_con["info"] == "Database connected.":
|
||||
raise Exception("Could not connect to database.")
|
||||
|
||||
fsrv_response = fsrv_utils.verify_fsrv(self.server, self.db_name,
|
||||
self.fsrv_name)
|
||||
|
||||
if not fsrv_response:
|
||||
raise Exception("Could not find Foreign Server.")
|
||||
|
||||
data = {'ids': self.ft_ids}
|
||||
|
||||
delete_response = self.tester.delete(
|
||||
self.url + str(utils.SERVER_GROUP) + '/' +
|
||||
str(self.server_id) + '/' +
|
||||
str(self.db_id) + '/' +
|
||||
str(self.schema_id) + '/',
|
||||
content_type='html/json',
|
||||
follow_redirects=True,
|
||||
data=json.dumps(data))
|
||||
|
||||
self.assertEquals(delete_response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
""" This function disconnect the test database. """
|
||||
|
||||
database_utils.disconnect_database(self, self.server_id, self.db_id)
|
||||
@@ -188,12 +188,12 @@ class FtsConfigurationView(PGChildNodeView):
|
||||
operations = dict({
|
||||
'obj': [
|
||||
{'get': 'properties', 'delete': 'delete', 'put': 'update'},
|
||||
{'get': 'list', 'post': 'create'}
|
||||
{'get': 'list', 'post': 'create', 'delete': 'delete'}
|
||||
],
|
||||
'children': [{
|
||||
'get': 'children'
|
||||
}],
|
||||
'delete': [{'delete': 'delete'}],
|
||||
'delete': [{'delete': 'delete'}, {'delete': 'delete'}],
|
||||
'nodes': [{'get': 'node'}, {'get': 'nodes'}],
|
||||
'sql': [{'get': 'sql'}],
|
||||
'msql': [{'get': 'msql'}, {'get': 'msql'}],
|
||||
@@ -531,7 +531,7 @@ class FtsConfigurationView(PGChildNodeView):
|
||||
)
|
||||
|
||||
@check_precondition
|
||||
def delete(self, gid, sid, did, scid, cfgid):
|
||||
def delete(self, gid, sid, did, scid, cfgid=None):
|
||||
"""
|
||||
This function will drop the FTS Configuration object
|
||||
:param gid: group id
|
||||
@@ -540,6 +540,13 @@ class FtsConfigurationView(PGChildNodeView):
|
||||
:param scid: schema id
|
||||
:param cfgid: FTS Configuration id
|
||||
"""
|
||||
if cfgid is None:
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
else:
|
||||
data = {'ids': [cfgid]}
|
||||
|
||||
# Below will decide if it's simple drop or drop with cascade call
|
||||
if self.cmd == 'delete':
|
||||
# This is a cascade operation
|
||||
@@ -548,49 +555,44 @@ class FtsConfigurationView(PGChildNodeView):
|
||||
cascade = False
|
||||
|
||||
try:
|
||||
# Get name for FTS Configuration from cfgid
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_name.sql']),
|
||||
cfgid=cfgid
|
||||
)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
for cfgid in data['ids']:
|
||||
# Get name for FTS Configuration from cfgid
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'get_name.sql']),
|
||||
cfgid=cfgid
|
||||
)
|
||||
status, res = self.conn.execute_dict(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=_(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=_(
|
||||
'The specified FTS configuration could not be found.\n'
|
||||
if not res['rows']:
|
||||
return make_json_response(
|
||||
success=0,
|
||||
errormsg=_(
|
||||
'Error: Object not found.'
|
||||
),
|
||||
info=_(
|
||||
'The specified FTS configuration '
|
||||
'could not be found.\n'
|
||||
)
|
||||
)
|
||||
|
||||
# Drop FTS Configuration
|
||||
result = res['rows'][0]
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
name=result['name'],
|
||||
schema=result['schema'],
|
||||
cascade=cascade
|
||||
)
|
||||
|
||||
# Drop FTS Configuration
|
||||
result = res['rows'][0]
|
||||
sql = render_template(
|
||||
"/".join([self.template_path, 'delete.sql']),
|
||||
name=result['name'],
|
||||
schema=result['schema'],
|
||||
cascade=cascade
|
||||
)
|
||||
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
status, res = self.conn.execute_scalar(sql)
|
||||
if not status:
|
||||
return internal_server_error(errormsg=res)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=_("FTS Configuration dropped"),
|
||||
data={
|
||||
'id': cfgid,
|
||||
'sid': sid,
|
||||
'gid': gid,
|
||||
'did': did,
|
||||
'scid': scid
|
||||
}
|
||||
info=_("FTS Configuration dropped")
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
|
||||