diff --git a/docs/en_US/getting_started.rst b/docs/en_US/getting_started.rst index 5b09c5b4c..2595505d2 100644 --- a/docs/en_US/getting_started.rst +++ b/docs/en_US/getting_started.rst @@ -64,6 +64,8 @@ display. To open the *Preferences* dialog, select *Preferences* from the *File* for the dialog. You can access additional Postgres help by navigating through the *Help* menu, and selecting the name of the resource that you wish to open. +You can search for objects in the database using the :ref:`Search objects ` + .. toctree:: :maxdepth: 2 @@ -74,6 +76,7 @@ the *Help* menu, and selecting the name of the resource that you wish to open. tree_control preferences keyboard_shortcuts + search_objects 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* diff --git a/docs/en_US/images/search_objects.png b/docs/en_US/images/search_objects.png new file mode 100644 index 0000000000000000000000000000000000000000..1c5b4be267ab0240dc207c486057e236e18507a8 GIT binary patch literal 143936 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 literal 0 HcmV?d00001 diff --git a/docs/en_US/images/toolbar.png b/docs/en_US/images/toolbar.png index 19c4c574a905fb30ea1828b4825e85af7cdb0c0e..aac16b967b93b44576bf2a042b989aa296251648 100644 GIT binary patch literal 42931 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%0D-NlnK%gelqO9|`%poZ~XyuF**dKg(*_% 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

k6m3s9%Y>P*~% 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. You can double +click on a result row to select the object in the +:ref:`browser `. If the object is grey, this means that you +have not enabled those object types in the :ref:`preferences `, +so you can't click on it. + +You can filter based on a particular object type by selecting one from the +object type dropdown. An object type will not be visible in the dropdown +if the database server does not support it or if it is not enabled from the +:ref:`preferences `. \ No newline at end of file diff --git a/docs/en_US/toolbar.rst b/docs/en_US/toolbar.rst index 12dc8621a..f4c685f24 100644 --- a/docs/en_US/toolbar.rst +++ b/docs/en_US/toolbar.rst @@ -18,4 +18,6 @@ the selected browser node. * Use the :ref:`View Data ` button to view/edit the data stored in a selected table. * Use the :ref:`Filtered Rows ` button to access the Data Filter popup - to apply a filter to a set of data for viewing/editing. \ No newline at end of file + to apply a filter to a set of data for viewing/editing. +* Use the :ref:`Search objects ` button to access the search objects + dialog. It helps you search any database object. \ No newline at end of file diff --git a/web/pgadmin/browser/register_browser_preferences.py b/web/pgadmin/browser/register_browser_preferences.py index 09f472795..5f57f9fa1 100644 --- a/web/pgadmin/browser/register_browser_preferences.py +++ b/web/pgadmin/browser/register_browser_preferences.py @@ -249,6 +249,21 @@ def register_browser_preferences(self): fields=fields ) + self.preference.register( + 'keyboard_shortcuts', + 'sub_menu_search_objects', + gettext('Search objects'), + 'keyboardshortcut', + { + 'alt': True, + 'shift': True, + 'control': False, + 'key': {'key_code': 83, 'char': 's'} + }, + category_label=gettext('Keyboard shortcuts'), + fields=fields + ) + self.preference.register( 'keyboard_shortcuts', 'sub_menu_create', diff --git a/web/pgadmin/browser/server_groups/servers/databases/extensions/static/js/extension.js b/web/pgadmin/browser/server_groups/servers/databases/extensions/static/js/extension.js index 7724ef4c4..b2127c4b0 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/extensions/static/js/extension.js +++ b/web/pgadmin/browser/server_groups/servers/databases/extensions/static/js/extension.js @@ -24,7 +24,7 @@ define('pgadmin.node.extension', [ pgAdmin.Browser.Nodes['coll-extension'] = pgAdmin.Browser.Collection.extend({ node: 'extension', - label: gettext('Extension'), + label: gettext('Extensions'), type: 'coll-extension', columns: ['name', 'owner', 'comment'], }); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/packages/ppas/12_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/packages/ppas/12_plus/nodes.sql new file mode 100644 index 000000000..8b11b594a --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/packages/templates/packages/ppas/12_plus/nodes.sql @@ -0,0 +1,11 @@ +SELECT + nsp.oid, nspname AS name +FROM + pg_namespace nsp +WHERE nspparent = {{scid}}::oid +{% if pkgid %} +AND nsp.oid = {{pkgid}}::oid +{% endif %} +AND nspobjecttype = 0 +AND nspcompoundtrigger = false +ORDER BY nspname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py index 2984a58e4..4b0a0918c 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/__init__.py @@ -54,8 +54,8 @@ class IndexConstraintModule(ConstraintTypeModule): initialized. """ - NODE_TYPE = 'Index constraint' - COLLECTION_LABEL = _('index_constraint') + NODE_TYPE = 'index_constraint' + COLLECTION_LABEL = _('Index constraint') def __init__(self, *args, **kwargs): """ diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py index e455b8adf..9ffbe742c 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/__init__.py @@ -29,8 +29,11 @@ from pgadmin.tools.schema_diff.compare import SchemaDiffObjectCompare def backend_supported(module, manager, **kwargs): - if 'tid' in kwargs and CollectionNodeModule.BackendSupported( - module, manager, **kwargs): + + if CollectionNodeModule.BackendSupported(module, manager, **kwargs): + if 'tid' not in kwargs: + return True + conn = manager.connection(did=kwargs['did']) template_path = 'partitions/sql/{0}/#{0}#{1}#'.format( diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/tests/test_backend_supported.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/tests/test_backend_supported.py index bf92e5f01..1de4de825 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/tests/test_backend_supported.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/partitions/tests/test_backend_supported.py @@ -21,19 +21,19 @@ else: class TestBackendSupport(BaseTestGenerator): scenarios = [ - ('when tid is not present in arguments, should return None and no ' - 'query should be done', + ('when tid is not present in arguments, but server version' + 'is supported then return True', dict( manager=dict( - server_type="", - version="" + server_type="pg", + version="100000" ), input_arguments=dict(did=432), collection_node_active=True, connection_execution_return_value=[], - expected_return_value=None, + expected_return_value=True, expect_error_response=False, expected_number_calls_on_render_template=0 )), diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/macros/catalogs.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/macros/catalogs.sql index 5f9bf9532..f1645d070 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/macros/catalogs.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/pg/macros/catalogs.sql @@ -9,6 +9,9 @@ (SELECT 1 FROM pg_class WHERE relname = 'tables' AND relnamespace = {{ tbl }}.oid LIMIT 1)) {%- endmacro %} +{% macro IS_CATALOG_SCHEMA(schema_col_name) -%} + {{ schema_col_name }} IN ('pg_catalog', 'pgagent', 'information_schema') +{%- endmacro %} {% macro LABELS(tbl, _) -%} CASE {{ tbl }}.nspname WHEN 'pg_catalog' THEN '{{ _( 'PostgreSQL Catalog' ) }} (pg_catalog)' @@ -17,9 +20,24 @@ ELSE {{ tbl }}.nspname END AS name {%- endmacro %} +{% macro LABELS_SCHEMACOL(schema_col_name, _) -%} + CASE {{ schema_col_name }} + WHEN 'pg_catalog' THEN '{{ _( 'PostgreSQL Catalog' ) }} (pg_catalog)' + WHEN 'pgagent' THEN '{{ _( 'pgAgent Job Scheduler' ) }} (pgagent)' + WHEN 'information_schema' THEN '{{ _( 'ANSI' ) }} (information_schema)' + ELSE {{ schema_col_name }} + END +{%- endmacro %} {% macro DB_SUPPORT(tbl) -%} CASE WHEN {{ tbl }}.nspname = ANY('{information_schema}') THEN false ELSE true END {%- endmacro %} +{% macro DB_SUPPORT_SCHEMACOL(schema_col_name) -%} + CASE + WHEN {{ schema_col_name }} = ANY('{information_schema}') + THEN false + ELSE true END +{%- endmacro %} + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/macros/catalogs.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/macros/catalogs.sql index 37fb594ed..b9fc276ef 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/macros/catalogs.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/templates/catalog/ppas/macros/catalogs.sql @@ -13,6 +13,9 @@ (SELECT 1 FROM pg_proc WHERE pronamespace = {{ tbl }}.oid and proname = 'run_job' LIMIT 1)) {%- endmacro %} +{% macro IS_CATALOG_SCHEMA(schema_col_name) -%} + {{ schema_col_name }} IN ('pg_catalog', 'pgagent', 'information_schema', 'dbo', 'sys', 'dbms_job_procedure') +{%- endmacro %} {% macro LABELS(tbl, _) -%} CASE {{ tbl }}.nspname WHEN 'pg_catalog' THEN '{{ _( 'PostgreSQL Catalog' ) }} (pg_catalog)' @@ -23,9 +26,25 @@ ELSE {{ tbl }}.nspname END AS name {%- endmacro %} -{% macro DB_SUPPORT(tbl) -%} +{% macro LABELS_SCHEMACOL(schema_col_name, _) -%} + CASE {{ schema_col_name }} + WHEN 'pg_catalog' THEN '{{ _( 'PostgreSQL Catalog' ) }} (pg_catalog)' + WHEN 'pgagent' THEN '{{ _( 'pgAgent Job Scheduler' ) }} (pgagent)' + WHEN 'information_schema' THEN '{{ _( 'ANSI' ) }} (information_schema)' + WHEN 'dbo' THEN 'Redmond (dbo)' + WHEN 'sys' THEN 'Redwood (sys)' + ELSE {{ schema_col_name }} + END +{%- endmacro %} +{% macro DB_SUPPORT(tbl, schema_col_name) -%} CASE WHEN {{ tbl }}.nspname = ANY('{information_schema,sys,dbo}') THEN false ELSE true END {%- endmacro %} +{% macro DB_SUPPORT_SCHEMACOL(schema_col_name) -%} + CASE + WHEN {{ schema_col_name }} = ANY('{information_schema,sys,dbo}') + THEN false + ELSE true END +{%- endmacro %} diff --git a/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js b/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js index 47500caf7..c7bb247ec 100644 --- a/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js +++ b/web/pgadmin/browser/server_groups/servers/roles/static/js/role.js @@ -18,6 +18,7 @@ define('pgadmin.node.role', [ pgAdmin.Browser.Nodes['coll-role'] = pgAdmin.Browser.Collection.extend({ node: 'role', + label: gettext('Login/Group Roles'), type: 'coll-role', columns: [ 'rolname', 'rolvaliduntil', 'rolconnlimit', 'rolcanlogin', diff --git a/web/pgadmin/browser/static/js/collection.js b/web/pgadmin/browser/static/js/collection.js index 2ec4fe1b5..a3c9ad96e 100644 --- a/web/pgadmin/browser/static/js/collection.js +++ b/web/pgadmin/browser/static/js/collection.js @@ -51,14 +51,23 @@ define([ }]); // show query tool only in context menu of supported nodes. - if (pgAdmin.DataGrid && pgAdmin.unsupported_nodes) { - if (_.indexOf(pgAdmin.unsupported_nodes, this.type) == -1) { + + if (pgAdmin.unsupported_nodes && _.indexOf(pgAdmin.unsupported_nodes, this.type) == -1) { + if ((this.type == 'database' && this.allowConn) || this.type != 'database') { pgAdmin.Browser.add_menus([{ - name: 'show_query_tool', node: this.type, module: this, + name: 'show_query_tool', node: this.type, module: pgAdmin.DataGrid, applies: ['context'], callback: 'show_query_tool', priority: 998, label: gettext('Query Tool...'), icon: 'pg-font-icon icon-query-tool', }]); + + // show search objects same as query tool + pgAdmin.Browser.add_menus([{ + name: 'search_objects', node: this.type, module: pgAdmin.SearchObjects, + applies: ['context'], callback: 'show_search_objects', + priority: 997, label: gettext('Search Objects...'), + icon: 'fa fa-search', + }]); } } }, diff --git a/web/pgadmin/browser/static/js/keyboard.js b/web/pgadmin/browser/static/js/keyboard.js index 5cd00e6a3..8705e14be 100644 --- a/web/pgadmin/browser/static/js/keyboard.js +++ b/web/pgadmin/browser/static/js/keyboard.js @@ -34,6 +34,7 @@ _.extend(pgBrowser.keyboardNavigation, { 'tabbed_panel_forward': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'tabbed_panel_forward').value), 'sub_menu_query_tool': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_query_tool').value), 'sub_menu_view_data': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_view_data').value), + 'sub_menu_search_objects': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_search_objects').value), 'sub_menu_properties': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_properties').value), 'sub_menu_create': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_create').value), 'sub_menu_delete': commonUtils.parseShortcutValue(pgBrowser.get_preference('browser', 'sub_menu_delete').value), @@ -55,6 +56,7 @@ _.extend(pgBrowser.keyboardNavigation, { 'bindLeftTree': {'shortcuts': this.keyboardShortcut.left_tree_shortcut}, // Main menu, 'bindSubMenuQueryTool': {'shortcuts': this.keyboardShortcut.sub_menu_query_tool}, // Sub menu - Open Query Tool, 'bindSubMenuViewData': {'shortcuts': this.keyboardShortcut.sub_menu_view_data}, // Sub menu - Open View Data, + 'bindSubMenuSearchObjects': {'shortcuts': this.keyboardShortcut.sub_menu_search_objects}, // Sub menu - Open search objects, 'bindSubMenuProperties': {'shortcuts': this.keyboardShortcut.sub_menu_properties}, // Sub menu - Edit Properties, 'bindSubMenuCreate': {'shortcuts': this.keyboardShortcut.sub_menu_create}, // Sub menu - Create Object, 'bindSubMenuDelete': {'shortcuts': this.keyboardShortcut.sub_menu_delete}, // Sub menu - Delete object, @@ -261,6 +263,15 @@ _.extend(pgBrowser.keyboardNavigation, { // Call data grid method to render view data pgAdmin.DataGrid.show_data_grid({'mnuid': 1}, tree.i); }, + bindSubMenuSearchObjects: function() { + const tree = this.getTreeDetails(); + + if (!tree.d) + return; + + // Call data grid method to render view data + pgAdmin.SearchObjects.show_search_objects('', tree.i); + }, bindSubMenuProperties: function() { const tree = this.getTreeDetails(); diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js index 029a29817..9ee0b4bf5 100644 --- a/web/pgadmin/browser/static/js/node.js +++ b/web/pgadmin/browser/static/js/node.js @@ -177,6 +177,14 @@ define('pgadmin.browser.node', [ // Show query tool only in context menu of supported nodes. if (_.indexOf(pgAdmin.unsupported_nodes, self.type) == -1) { + let enable = function(itemData) { + if (itemData._type == 'database' && itemData.allowConn) + return true; + else if (itemData._type != 'database') + return true; + else + return false; + }; pgAdmin.Browser.add_menus([{ name: 'show_query_tool', node: self.type, @@ -186,14 +194,15 @@ define('pgadmin.browser.node', [ priority: 998, label: gettext('Query Tool...'), icon: 'pg-font-icon icon-query-tool', - enable: function(itemData) { - if (itemData._type == 'database' && itemData.allowConn) - return true; - else if (itemData._type != 'database') - return true; - else - return false; - }, + enable: enable, + }]); + + // show search objects same as query tool + pgAdmin.Browser.add_menus([{ + name: 'search_objects', node: self.type, module: pgAdmin.SearchObjects, + applies: ['context'], callback: 'show_search_objects', + priority: 997, label: gettext('Search Objects...'), + icon: 'fa fa-search', enable: enable, }]); } diff --git a/web/pgadmin/browser/static/js/toolbar.js b/web/pgadmin/browser/static/js/toolbar.js index 955fe6f32..6eb1f39f4 100644 --- a/web/pgadmin/browser/static/js/toolbar.js +++ b/web/pgadmin/browser/static/js/toolbar.js @@ -46,6 +46,16 @@ let _defaultToolBarButtons = [ parentClass: 'pg-toolbar-btn btn-secondary', enabled: false, }, + { + label: gettext('Search objects'), + ariaLabel: gettext('Search objects'), + btnClass: 'fa fa-search', + text: '', + toggled: false, + toggleClass: '', + parentClass: 'pg-toolbar-btn btn-secondary', + enabled: false, + }, ]; // Place holder for non default tool bar buttons. @@ -92,6 +102,8 @@ export function initializeToolbar(panel, wcDocker) { pgAdmin.DataGrid.show_data_grid({mnuid: 3}, pgAdmin.Browser.tree.selected()); else if ('name' in data && data.name === gettext('Filtered Rows')) pgAdmin.DataGrid.show_filtered_row({mnuid: 4}, pgAdmin.Browser.tree.selected()); + else if ('name' in data && data.name === gettext('Search objects')) + pgAdmin.SearchObjects.show_search_objects('', pgAdmin.Browser.tree.selected()); }); } diff --git a/web/pgadmin/browser/templates/browser/index.html b/web/pgadmin/browser/templates/browser/index.html index 682c23d65..67a10ba89 100644 --- a/web/pgadmin/browser/templates/browser/index.html +++ b/web/pgadmin/browser/templates/browser/index.html @@ -11,7 +11,7 @@ {% block init_script %} try { require( -['sources/generated/app.bundle', 'sources/generated/codemirror', 'sources/generated/browser_nodes'], +['sources/generated/app.bundle', 'sources/generated/codemirror', 'sources/generated/browser_nodes', 'sources/generated/slickgrid'], function() { }, function() { diff --git a/web/pgadmin/static/bundle/slickgrid.js b/web/pgadmin/static/bundle/slickgrid.js index 4b0ab410b..96ad3c330 100644 --- a/web/pgadmin/static/bundle/slickgrid.js +++ b/web/pgadmin/static/bundle/slickgrid.js @@ -8,7 +8,6 @@ ////////////////////////////////////////////////////////////// import 'slickgrid/lib/jquery.event.drag-2.3.0'; -import 'slickgrid/lib/jquery-ui-1.11.3'; import 'slickgrid/slick.core'; import 'slickgrid/slick.grid'; import 'slickgrid/slick.dataview'; @@ -21,5 +20,6 @@ import 'slickgrid/plugins/slick.cellrangeselector'; import 'slickgrid/plugins/slick.checkboxselectcolumn'; import 'slickgrid/plugins/slick.rowselectionmodel'; import 'sources/slickgrid/custom_header_buttons'; +import 'sources/slickgrid/plugins/slick.autocolumnsize'; export default window.Slick; diff --git a/web/pgadmin/static/css/style.css b/web/pgadmin/static/css/style.css index 6c0f84460..55e477ee1 100644 --- a/web/pgadmin/static/css/style.css +++ b/web/pgadmin/static/css/style.css @@ -5,19 +5,18 @@ @import '~tempusdominus-bootstrap-4/build/css/tempusdominus-bootstrap-4.css'; @import '~bootstrap4-toggle/css/bootstrap4-toggle.css'; @import '~backgrid-filter/backgrid-filter.css'; -@import '~slickgrid/css/select2.css'; @import '~jquery-contextmenu/dist/jquery.contextMenu.css'; @import '~webcabin-docker/Build/wcDocker.css'; @import '~acitree/css/aciTree.css'; @import '~spectrum-colorpicker/spectrum.css'; @import '~leaflet/dist/leaflet.css'; +@import '../../../node_modules/select2/dist/css/select2.css'; @import '~codemirror/lib/codemirror.css'; @import '~codemirror/addon/dialog/dialog.css'; @import '~codemirror/addon/scroll/simplescrollbars.css'; @import '~slickgrid/slick.grid.css'; -@import '~slickgrid/slick-default-theme.css'; @import '~slickgrid/css/smoothness/jquery-ui-1.11.3.custom.css'; @import '../vendor/backgrid/backgrid.css'; diff --git a/web/pgadmin/static/js/alertify.pgadmin.defaults.js b/web/pgadmin/static/js/alertify.pgadmin.defaults.js index 84dfc12fe..daa60f696 100644 --- a/web/pgadmin/static/js/alertify.pgadmin.defaults.js +++ b/web/pgadmin/static/js/alertify.pgadmin.defaults.js @@ -271,7 +271,8 @@ define([ let container = $(self.elements.footer); commonUtils.findAndSetFocus(container.find('button:not([disabled]):last')); } - }); }); + }); + }); this.set('onresize', alertifyDialogStartResizing.bind(this, true)); this.set('onresized', alertifyDialogResized.bind(this, true)); this.set('onmaximized', alertifyDialogResized); diff --git a/web/pgadmin/static/js/alertify/dialog.js b/web/pgadmin/static/js/alertify/dialog.js index 6f4933720..8e5bdb738 100644 --- a/web/pgadmin/static/js/alertify/dialog.js +++ b/web/pgadmin/static/js/alertify/dialog.js @@ -80,6 +80,39 @@ export class Dialog { return serverInformation; } + retrieveAncestorOfTypeDatabase(item) { + let databaseInfo = null; + let aciTreeItem = item || this.pgBrowser.treeMenu.selected(); + let treeNode = this.pgBrowser.treeMenu.findNodeByDomElement(aciTreeItem); + + if (treeNode) { + if(treeNode.getData()._type === 'database') { + databaseInfo = treeNode.getData(); + } else { + let nodeData = null; + treeNode.ancestorNode( + (node) => { + nodeData = node.getData(); + if(nodeData._type === 'database') { + databaseInfo = nodeData; + return true; + } + return false; + } + ); + } + } + + if (databaseInfo === null) { + this.alertify.alert( + gettext(this.errorAlertTitle), + gettext('Please select a database or its child node from the browser.') + ); + } + + return databaseInfo; + } + hasBinariesConfiguration(serverInformation) { const module = 'paths'; let preference_name = 'pg_bin_dir'; diff --git a/web/pgadmin/static/js/alertify/dialog_factory.js b/web/pgadmin/static/js/alertify/dialog_factory.js index cb0ae1552..760fde679 100644 --- a/web/pgadmin/static/js/alertify/dialog_factory.js +++ b/web/pgadmin/static/js/alertify/dialog_factory.js @@ -9,6 +9,7 @@ import * as BackupDialog from '../../../tools/backup/static/js/backup_dialog_wrapper'; import {RestoreDialogWrapper} from '../../../tools/restore/static/js/restore_dialog_wrapper'; +import SearchObjectsDialogWrapper from '../../../tools/search_objects/static/js/search_objects_dialog_wrapper'; export class DialogFactory { constructor(pgBrowser, $, @@ -25,6 +26,8 @@ export class DialogFactory { create(dialogTitle, typeOfDialog) { if (typeOfDialog === 'restore') { return this.createRestoreDialog(dialogTitle, typeOfDialog); + } else if (typeOfDialog === 'search_objects') { + return this.createSearchObjectsDialog(dialogTitle, typeOfDialog); } else { return this.createBackupDialog(dialogTitle, typeOfDialog); } @@ -49,4 +52,14 @@ export class DialogFactory { this.dialogModel, this.backform); } + + createSearchObjectsDialog(dialogTitle, typeOfDialog) { + return new SearchObjectsDialogWrapper( + this.dialogContainerSelector, dialogTitle, typeOfDialog, + this.jquery, + this.pgBrowser, + this.alertify, + this.dialogModel, + this.backform); + } } diff --git a/web/pgadmin/static/js/alertify/dialog_wrapper.js b/web/pgadmin/static/js/alertify/dialog_wrapper.js index e4c275f3a..32d9a7a0b 100644 --- a/web/pgadmin/static/js/alertify/dialog_wrapper.js +++ b/web/pgadmin/static/js/alertify/dialog_wrapper.js @@ -58,7 +58,11 @@ export class DialogWrapper { let backform_tab = $(alertifyDialog.elements.body).find('.backform-tab'); backform_tab.attr('tabindex', -1); this.pgBrowser.keyboardNavigation.getDialogTabNavigator($(alertifyDialog.elements.dialog)); - const container = backform_tab.find('.tab-content:first > .tab-pane.active:first'); + let container = backform_tab.find('.tab-content:first > .tab-pane.active:first'); + + if(container.length === 0 && alertifyDialog.elements.content.innerHTML) { + container = $(alertifyDialog.elements.content); + } commonUtils.findAndSetFocus(container); } diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index a57f70ebc..3cdc13d4d 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -2132,6 +2132,7 @@ define([ defaults: _.extend({}, Backform.SelectControl.prototype.defaults, { select2: { first_empty: true, + multiple: false, emptyOptions: false, preserveSelectionOrder: false, diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.autocolumnsize.js b/web/pgadmin/static/js/slickgrid/plugins/slick.autocolumnsize.js index 40e537e1b..85b339139 100644 --- a/web/pgadmin/static/js/slickgrid/plugins/slick.autocolumnsize.js +++ b/web/pgadmin/static/js/slickgrid/plugins/slick.autocolumnsize.js @@ -98,7 +98,7 @@ function getTemplateWidth(rowEl, template) { var cell = $(rowEl.find('.slick-cell')); cell.append(template); - $(cell).find('*').css('position', 'relative'); + cell.find('*').css('position', 'relative'); return cell.outerWidth() + 1; } @@ -128,7 +128,7 @@ 'text-overflow': 'initial', 'white-space': 'nowrap', }); - var gridCanvas = $container.find('.grid-canvas'); + var gridCanvas = $container.find('.grid-canvas').first(); $(gridCanvas).append(rowEl); return rowEl; } diff --git a/web/pgadmin/static/js/tree/tree.js b/web/pgadmin/static/js/tree/tree.js index 57b6207c8..984d247d3 100644 --- a/web/pgadmin/static/js/tree/tree.js +++ b/web/pgadmin/static/js/tree/tree.js @@ -50,17 +50,50 @@ export class TreeNode { } reload(tree) { - this.unload(tree); - tree.aciTreeApi.setInode(this.domNode); - tree.aciTreeApi.deselect(this.domNode); - setTimeout(() => { - tree.selectNode(this.domNode); - }, 0); + return new Promise((resolve)=>{ + this.unload(tree) + .then(()=>{ + tree.aciTreeApi.setInode(this.domNode); + tree.aciTreeApi.deselect(this.domNode); + setTimeout(() => { + tree.selectNode(this.domNode); + }, 0); + resolve(); + }); + }); } unload(tree) { - this.children = []; - tree.aciTreeApi.unload(this.domNode); + return new Promise((resolve, reject)=>{ + this.children = []; + tree.aciTreeApi.unload(this.domNode, { + success: ()=>{ + resolve(true); + }, + fail: ()=>{ + reject(); + }, + }); + }); + } + + open(tree, suppressNoDom) { + return new Promise((resolve, reject)=>{ + if(suppressNoDom && (this.domNode == null || typeof(this.domNode) === 'undefined')) { + resolve(true); + } else if(tree.aciTreeApi.isOpen(this.domNode)) { + resolve(true); + } else { + tree.aciTreeApi.open(this.domNode, { + success: ()=>{ + resolve(true); + }, + fail: ()=>{ + reject(true); + }, + }); + } + }); } /* @@ -202,6 +235,47 @@ export class Tree { return findInTree(this.rootNode, path.join('.')); } + findNodeWithToggle(path) { + let tree = this; + path = path.join('.'); + + let onCorrectPath = function(matchPath) { + return (matchPath !== undefined && path !== undefined + && (path.startsWith(matchPath + '.') || path === matchPath)); + }; + + return (function findInNode(currentNode) { + return new Promise((resolve, reject)=>{ + if (path === null || path === undefined || path.length === 0) { + resolve(null); + } + /* No point in checking the children if + * the path for currentNode itself is not matching + */ + if (currentNode.path !== undefined && !onCorrectPath(currentNode.path)) { + reject(null); + } else if (currentNode.path === path) { + resolve(currentNode); + } else { + currentNode.open(tree, true) + .then(()=>{ + for (let i = 0, length = currentNode.children.length; i < length; i++) { + let childNode = currentNode.children[i]; + if(onCorrectPath(childNode.path)) { + resolve(findInNode(childNode)); + return; + } + } + reject(null); + }) + .catch(()=>{ + reject(null); + }); + } + }); + })(this.rootNode); + } + findNodeByDomElement(domElement) { const path = this.translateTreeNodeIdFromACITree(domElement); if(!path || !path[0]) { @@ -215,8 +289,19 @@ export class Tree { return this.aciTreeApi.selected(); } - selectNode(aciTreeIdentifier) { + /* scrollIntoView will scroll only to top and bottom + * Logic can be added for scroll to middle + */ + scrollTo(domElement) { + domElement.scrollIntoView(); + } + + selectNode(aciTreeIdentifier, scrollOnSelect) { this.aciTreeApi.select(aciTreeIdentifier); + + if(scrollOnSelect) { + this.scrollTo(aciTreeIdentifier[0]); + } } createOrUpdateNode(id, data, parent, domNode) { @@ -227,6 +312,7 @@ export class Tree { const oldNode = this.findNode(oldNodePath); if (oldNode !== null) { oldNode.data = data; + oldNode.domNode = domNode; return oldNode; } @@ -238,6 +324,18 @@ export class Tree { return node; } + unloadNode(id, data, domNode, parentPath) { + let oldNodePath = [id]; + const parent = this.findNode(parentPath); + if(parent !== null && parent !== undefined) { + oldNodePath = [parent.path, id]; + } + const oldNode = this.findNode(oldNodePath); + if(oldNode) { + oldNode.children = []; + } + } + /** * Given the JQuery object that contains the ACI Tree * this method is responsible for registering this tree class @@ -252,16 +350,20 @@ export class Tree { $treeJQuery.on('acitree', function (event, api, item, eventName) { if (api.isItem(item)) { /* If the id of node is changed, the path should also be changed */ - if (eventName === 'added' || eventName === 'idset') { + if (['added', 'idset', 'beforeunload'].indexOf(eventName) != -1) { const id = api.getId(item); const data = api.itemData(item); + const parentId = this.translateTreeNodeIdFromACITree(api.parent(item)); - if(eventName === 'added') { - this.prepareDraggable(data, item); - } + if(eventName === 'beforeunload') { + this.unloadNode(id, data, item, parentId); + } else { + if(eventName === 'added') { + this.prepareDraggable(data, item); + } - const parentId = this.translateTreeNodeIdFromACITree(api.parent(item)); - this.addNewNode(id, data, item, parentId); + this.addNewNode(id, data, item, parentId); + } if(data.errmsg) { Alertify.error(data.errmsg); } diff --git a/web/pgadmin/static/js/utils.js b/web/pgadmin/static/js/utils.js index 86dae698e..7bdddba85 100644 --- a/web/pgadmin/static/js/utils.js +++ b/web/pgadmin/static/js/utils.js @@ -32,11 +32,12 @@ export function findAndSetFocus(container) { * browser. For eg, in safari focus() works only when element has * tabindex="0", whereas in Chrome it works in any case */ + if (first_el.length == 0) { first_el = container .find(` - .pgadmin-controls:first input:enabled, .pgadmin-controls:first .btn:not(.toggle), + .pgadmin-controls:first, .ajs-commands:first, .CodeMirror-scroll`) .find('*[tabindex]:not([tabindex="-1"])'); diff --git a/web/pgadmin/static/scss/_alert.scss b/web/pgadmin/static/scss/_alert.scss index 3f2e561e3..0e7e234d1 100644 --- a/web/pgadmin/static/scss/_alert.scss +++ b/web/pgadmin/static/scss/_alert.scss @@ -119,21 +119,18 @@ } .success-in-footer { - border-radius: 5px; - border: 1px solid transparent; - - .alert-text { - border-color: $color-success-light; - } + border-radius: $border-radius; + border: 1px solid $color-success-light; + background: $color-success-light; } .info-in-footer { + border-radius: $border-radius; border: 1px solid $color-primary; - border-radius: 4px; - height: 35px; + background: $color-primary-light; - .alert-text { - border: none; + .fa { + font-size: 1rem; } } } diff --git a/web/pgadmin/static/scss/_webcabin.pgadmin.scss b/web/pgadmin/static/scss/_webcabin.pgadmin.scss index 4846a8031..ca806785a 100644 --- a/web/pgadmin/static/scss/_webcabin.pgadmin.scss +++ b/web/pgadmin/static/scss/_webcabin.pgadmin.scss @@ -166,7 +166,7 @@ .wcTabIcon { background-position: center; - padding: 0px 10px; + padding: 0rem 0.75rem; &.fa, &.pg-font-icon{ padding: 0rem 0.25rem 0rem 0rem diff --git a/web/pgadmin/tools/search_objects/__init__.py b/web/pgadmin/tools/search_objects/__init__.py new file mode 100644 index 000000000..e8bd59141 --- /dev/null +++ b/web/pgadmin/tools/search_objects/__init__.py @@ -0,0 +1,87 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2020, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Implements Search Object feature""" + +from flask import request +from flask_babelex import gettext +from flask_security import login_required + +from pgadmin.utils import PgAdminModule +from pgadmin.utils.ajax import make_json_response, bad_request,\ + internal_server_error +from pgadmin.utils.preferences import Preferences +from pgadmin.tools.search_objects.utils import SearchObjectsHelper + +MODULE_NAME = 'search_objects' + + +class SearchObjectsModule(PgAdminModule): + LABEL = gettext('Search objects') + + def get_exposed_url_endpoints(self): + """ + Returns: + list: URL endpoints for search_object module + """ + return ['search_objects.search', 'search_objects.types'] + + def show_system_objects(self): + """ + return system preference objects + """ + return self.pref_show_system_objects.get() + + def register_preferences(self): + """ + Get show_system_objects preference + """ + browser_preference = Preferences.module('browser') + self.pref_show_system_objects =\ + browser_preference.preference('show_system_objects') + + +# Create blueprint for BackupModule class +blueprint = SearchObjectsModule( + MODULE_NAME, __name__, static_url_path='' +) + + +@blueprint.route("/", endpoint='index') +@login_required +def index(): + return bad_request(errormsg=_("This URL cannot be called directly.")) + + +@blueprint.route("types//", endpoint='types') +@login_required +def types(sid, did): + so_obj = SearchObjectsHelper(sid, did, blueprint.show_system_objects()) + return make_json_response(data=so_obj.get_supported_types()) + + +@blueprint.route("search//", endpoint='search') +@login_required +def search(sid, did): + """ + URL args: + text : search text + type : type of object to be searched. + """ + text = request.args.get('text', None) + obj_type = request.args.get('type', None) + + so_obj = SearchObjectsHelper(sid, did, blueprint.show_system_objects()) + + status, res = so_obj.search(text, obj_type) + + if not status: + return internal_server_error(errormsg=res) + + return make_json_response(data=res) diff --git a/web/pgadmin/tools/search_objects/static/js/search_objects.js b/web/pgadmin/tools/search_objects/static/js/search_objects.js new file mode 100644 index 000000000..84e40afd1 --- /dev/null +++ b/web/pgadmin/tools/search_objects/static/js/search_objects.js @@ -0,0 +1,90 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2020, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +define([ + 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'pgadmin.alertifyjs', + 'sources/pgadmin', 'sources/csrf', 'pgadmin.browser.toolbar', + 'pgadmin.search_objects/search_objects_dialog', +], function( + gettext, url_for, $, _, alertify, pgAdmin, csrfToken, toolBar, SearchObjectsDialog +) { + + var pgBrowser = pgAdmin.Browser; + if (pgAdmin.SearchObjects) + return pgAdmin.SearchObjects; + + pgAdmin.SearchObjects = { + init: function() { + if (this.initialized) + return; + + this.initialized = true; + csrfToken.setPGCSRFToken(pgAdmin.csrf_token_header, pgAdmin.csrf_token); + + // Define the nodes on which the menus to be appear + var menus = [{ + name: 'search_objects', + module: this, + applies: ['tools'], + callback: 'show_search_objects', + enable: this.search_objects_enabled, + priority: 1, + label: gettext('Search objects'), + }, { + name: 'search_objects', + module: this, + applies: ['context'], + callback: 'show_search_objects', + enable: this.search_objects_enabled, + priority: 1, + label: gettext('Search objects'), + }]; + + pgBrowser.add_menus(menus); + return this; + }, + + search_objects_enabled: function(obj) { + /* Same as query tool */ + var isEnabled = (() => { + if (!_.isUndefined(obj) && !_.isNull(obj)) { + if (_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) { + if (obj._type == 'database' && obj.allowConn) { + return true; + } else if (obj._type != 'database') { + return true; + } else { + return false; + } + } else { + return false; + } + } else { + return false; + } + })(); + + toolBar.enable(gettext('Search objects'), isEnabled); + return isEnabled; + }, + + // Callback to show the dialog + show_search_objects: function(action, item) { + let dialog = new SearchObjectsDialog.default( + pgBrowser, + $, + alertify, + {}, + ); + dialog.draw(action, item, {}, pgBrowser.stdW.md, pgBrowser.stdH.md); + }, + }; + + return pgAdmin.SearchObjects; +}); diff --git a/web/pgadmin/tools/search_objects/static/js/search_objects_dialog.js b/web/pgadmin/tools/search_objects/static/js/search_objects_dialog.js new file mode 100644 index 000000000..c2e24940d --- /dev/null +++ b/web/pgadmin/tools/search_objects/static/js/search_objects_dialog.js @@ -0,0 +1,40 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2020, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import gettext from 'sources/gettext'; +import {Dialog} from 'sources/alertify/dialog'; +import {getPanelTitle} from 'tools/datagrid/static/js/datagrid_panel_title'; + +export default class SearchObjectsDialog extends Dialog { + constructor(pgBrowser, $, alertify, BackupModel, backform = null) { + super('Search Objects Error', + '

', + pgBrowser, $, alertify, BackupModel, backform + ); + } + + dialogName() { + return 'search_objects'; + } + + draw(action, aciTreeItem, params, width=0, height=0) { + let dbInfo = this.retrieveAncestorOfTypeDatabase(aciTreeItem); + if (!dbInfo) { + return; + } + + let dialogTitle = getPanelTitle(this.pgBrowser, aciTreeItem); + dialogTitle = gettext('Search Objects - ') + dialogTitle; + const dialog = this.createOrGetDialog( + gettext('Search Objects...'), + 'search_objects' + ); + dialog(dialogTitle).resizeTo(width, height); + } +} diff --git a/web/pgadmin/tools/search_objects/static/js/search_objects_dialog_wrapper.js b/web/pgadmin/tools/search_objects/static/js/search_objects_dialog_wrapper.js new file mode 100644 index 000000000..926c5cf43 --- /dev/null +++ b/web/pgadmin/tools/search_objects/static/js/search_objects_dialog_wrapper.js @@ -0,0 +1,614 @@ +import {getTreeNodeHierarchyFromElement} from 'sources/tree/pgadmin_tree_node'; +import axios from 'axios/index'; +import gettext from 'sources/gettext'; +import url_for from 'sources/url_for'; +import 'select2'; +import {DialogWrapper} from 'sources/alertify/dialog_wrapper'; +import Slick from 'sources/../bundle/slickgrid'; +import pgAdmin from 'sources/pgadmin'; + + +export default class SearchObjectsDialogWrapper extends DialogWrapper { + constructor(dialogContainerSelector, dialogTitle, typeOfDialog, + jquery, pgBrowser, alertify, dialogModel, backform) { + super(dialogContainerSelector, dialogTitle, jquery, + pgBrowser, alertify, dialogModel, backform); + + this.grid = null; + this.gridContainer = null; + } + + showMessage(text, is_error, call_after_show=()=>{}) { + if(text == '' || text == null) { + this.statusBar.classList.add('d-none'); + } else { + if(is_error) { + this.statusBar.innerHTML = ` + + `; + + this.statusBar.querySelector('.close-error').addEventListener('click', ()=>{ + this.showMessage(null); + }); + } else { + this.statusBar.innerHTML = ` + + `; + } + this.statusBar.classList.remove('d-none'); + call_after_show(this.statusBar); + } + } + + createDialogDOM(dialogContainer) { + dialogContainer.innerHTML = ` +
+
+
+
+
+
+ +
+ +
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `; + + return dialogContainer; + } + + updateDimOfSearchResult() { + let dim = this.searchResultContainer.getBoundingClientRect(); + this.searchResult.style.height = dim.height + 'px'; + this.searchResult.style.width = dim.width + 'px'; + } + + setLoading(text) { + if(text != null) { + this.loader.classList.remove('d-none'); + this.loader.querySelector('.pg-sp-text').innerHTML = text; + } else { + this.loader.classList.add('d-none'); + } + } + + searchBtnEnabled(enabled) { + if(typeof(enabled) != 'undefined') { + this.searchBtn.disabled = !enabled; + } else { + return !this.searchBtn.disabled; + } + } + + searchBoxVal(val) { + if(typeof(val) != 'undefined') { + this.searchBox.value = val; + } else { + return this.searchBox.value.trim(); + } + } + + typesVal(val) { + if(typeof(val) != 'undefined') { + this.typesSelect.value = val; + } else { + return this.typesSelect.value; + } + } + + setTypes(data, enabled=true) { + this.jquery(this.typesSelect).empty().select2({ + data: data, + }); + + this.typesSelect.disabled = !enabled; + } + + showOtherInfo(rowno) { + let data = this.grid.getData(); + let rowData = data[rowno]; + rowData.name += ` (${rowData.other_info})`; + rowData.other_info = null; + data[rowno] = rowData; + this.setGridData(data); + } + + setGridData(data) { + data.getItemMetadata = (row)=>{ + if(data[row] && !data[row].show_node){ + return { + cssClasses: 'object-muted', + }; + } + return null; + }; + this.grid.setData(data); + this.grid.resizeCanvas(); + } + + prepareGrid() { + this.grid = new Slick.Grid( + this.searchResult, + [], + [ + { id: 'name', name: 'Object name', field: 'name', sortable: true, + formatter: (row, cell, value, columnDef, dataContext) => { + let ret_el = `${value}`; + + if(dataContext.other_info != null && dataContext.other_info != '') { + ret_el += ' (...)'; + } + + return ret_el; + }, + width: 50, + }, + { id: 'type', name: 'Type', field: 'type_label', sortable: true, width: 35 }, + { id: 'path', name: 'Path', field: 'path', sortable: false }, + ], + { + enableCellNavigation: true, + enableColumnReorder: false, + multiColumnSort: true, + explicitInitialization: true, + } + ); + + this.grid.registerPlugin(new Slick.AutoColumnSize()); + + this.grid.setSelectionModel(new Slick.RowSelectionModel({selectActiveRow: true})); + + this.grid.onKeyDown.subscribe((event) => { + let activeRow = this.grid.getActiveCell(); + if(activeRow && !event.ctrlKey && !event.altKey && !event.metaKey && event.keyCode == 9) { + event.preventDefault(); + event.stopImmediatePropagation(); + + if(event.shiftKey) { + this.prevToGrid.focus(); + } else { + this.nextToGrid.focus(); + } + } + }); + + this.grid.onClick.subscribe((event, args) => { + if(event.target.classList.contains('object-other-info')) { + this.showOtherInfo(args.row); + } + }); + + this.grid.onDblClick.subscribe((event, args) => { + let rowData = this.grid.getDataItem(args.row); + let treeMenu = this.pgBrowser.treeMenu; + + if(!rowData.show_node) { + this.showMessage( + gettext('%s objects are disabled in the browser.', rowData.type) + ' ' + + gettext('You can enable them in the') + ' ' + gettext('preferences dialog') + '', + true, + (statusBar)=>{ + statusBar.querySelector('.pref-dialog-link').addEventListener('click', ()=>{ + if(pgAdmin.Preferences) { + pgAdmin.Preferences.show(); + } + }); + } + ); + return false; + } + this.showMessage(gettext('Locating...')); + treeMenu.findNodeWithToggle(rowData.id_path) + .then((treeItem)=>{ + treeMenu.selectNode(treeItem.domNode, true); + this.showMessage(null); + }) + .catch((args)=>{ + this.showMessage(gettext('Unable to locate this object in the browser.'), true); + console.warn(args); + }); + }); + + this.grid.onSort.subscribe((event, args) => { + let cols = args.sortCols; + let data = this.grid.getData(); + + data.sort(function (dataRow1, dataRow2) { + for (var i = 0, l = cols.length; i < l; i++) { + var field = cols[i].sortCol.field; + var sign = cols[i].sortAsc ? 1 : -1; + var value1 = dataRow1[field], value2 = dataRow2[field]; + var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign; + if (result != 0) { + return result; + } + } + return false; + }); + this.setGridData(data); + }); + } + + onDialogResize() { + this.updateDimOfSearchResult(); + + if(this.grid) { + this.grid.resizeCanvas(); + this.grid.autosizeColumns(); + } + } + + onDialogShow() { + this.focusOnDialog(this); + + setTimeout(()=>{ + if(!this.grid) { + this.prepareGrid(); + } + this.updateDimOfSearchResult(); + this.grid.init(); + this.setGridData([]); + this.onDialogResize(); + }, 500); + } + + getBaseUrl(endpoint) { + return url_for('search_objects.'+endpoint, { + sid: this.treeInfo.server._id, + did: this.treeInfo.database._id, + }); + } + + getCollNode(node_type) { + if('coll-'+node_type in this.pgBrowser.Nodes) { + return this.pgBrowser.Nodes['coll-'+node_type]; + } else if(node_type in this.pgBrowser.Nodes && + typeof(this.pgBrowser.Nodes[node_type].collection_type) === 'string') { + return this.pgBrowser.Nodes[this.pgBrowser.Nodes[node_type].collection_type]; + } + + return null; + } + + getSelectedNode() { + const tree = this.pgBrowser.treeMenu; + const selectedNode = tree.selected(); + if (selectedNode) { + return tree.findNodeByDomElement(selectedNode); + } else { + return undefined; + } + } + + finaliseData(datum) { + datum.icon = 'icon-' + datum.type; + /* finalise path */ + [datum.path, datum.id_path] = this.translateSearchObjectsPath(datum.path, datum.catalog_level); + return datum; + } + + /* This function will translate the path given by search objects API into two parts + * 1. The display path on the UI + * 2. The tree search path to locate the object on the tree. + * + * Sample path returned by search objects API + * :schema.11:/pg_catalog/:table.2604:/pg_attrdef + * + * Sample path required by tree locator + * Normal object - server_group/1.server/3.coll-database/3.database/13258.coll-schema/13258.schema/2200.coll-table/2200.table/41773 + * pg_catalog schema - server_group/1.server/3.coll-database/3.database/13258.coll-catalog/13258.catalog/11.coll-table/11.table/2600 + * Information Schema, dbo, sys - server_group/1.server/3.coll-database/3.database/13258.coll-catalog/13258.catalog/12967.coll-catalog_object/12967.catalog_object/13204 + * + * Column catalog_level has values as + * N - Not a catalog schema + * D - Catalog schema with DB support - pg_catalog + * O - Catalog schema with object support only - info schema, dbo, sys + */ + translateSearchObjectsPath(path, catalog_level) { + if (path === null) { + return path; + } + + catalog_level = catalog_level || 'N'; + + /* path required by tree locator */ + /* the path received from the backend is after the DB node, initial path setup */ + let id_path = [ + this.treeInfo.server_group.id, + this.treeInfo.server.id, + this.getCollNode('database').type + '/' + this.treeInfo.server._id, + this.treeInfo.database.id, + ]; + + let prev_node_id = this.treeInfo.database._id; + + /* add the slash to match regex, remove it from display path later */ + path = '/' + path; + /* the below regex will match all /:server_group.1:/ */ + let new_path = path.replace(/\/:[a-zA-Z_]+\.[0-9]+:\//g, (token)=>{ + let orig_token = token; + /* remove the slash and colon */ + token = token.slice(2, -2); + let [node_type, node_oid, others] = token.split('.'); + if(typeof(others) !== 'undefined') { + return token; + } + + /* schema type is "catalog" for catalog schemas */ + node_type = (['D', 'O'].indexOf(catalog_level) != -1 && node_type == 'schema') ? 'catalog' : node_type; + + /* catalog like info schema will only have views and tables AKA catalog_object except for pg_catalog */ + node_type = (catalog_level === 'O' && ['view', 'table'].indexOf(node_type) != -1) ? 'catalog_object' : node_type; + + /* If collection node present then add it */ + let coll_node = this.getCollNode(node_type); + if(coll_node) { + /* Add coll node to the path */ + if(prev_node_id != null) id_path.push(`${coll_node.type}/${prev_node_id}`); + + /* Add the node to the path */ + id_path.push(`${node_type}/${node_oid}`); + + /* This will be needed for coll node */ + prev_node_id = node_oid; + + /* This will be displayed in the grid */ + return `/${coll_node.label}/`; + } else if(node_type in this.pgBrowser.Nodes) { + /* Add the node to the path */ + id_path.push(`${node_type}/${node_oid}`); + + /* This will be need for coll node id path */ + prev_node_id = node_oid; + + /* Remove the token and replace with slash. This will be displayed in the grid */ + return '/'; + } + prev_node_id = null; + return orig_token; + }); + + /* Remove the slash we had added */ + new_path = new_path.substring(1); + return [new_path, id_path]; + } + + prepareDialog() { + this.showMessage(null); + if(this.grid) { + this.grid.destroy(); + this.grid = null; + } + + /* Load types */ + this.setTypes([{ + id: -1, + text: gettext('Loading...'), + value: null, + }], false); + + axios.get( + this.getBaseUrl('types') + ).then((res)=>{ + let types = [{ + id: 'all', + text: 'All types', + }]; + + for (const key of Object.keys(res.data.data).sort()) { + types.push({ + id: key, + text: res.data.data[key], + }); + } + this.setTypes(types); + }).catch(()=>{ + this.setTypes([{ + id: -1, + text: gettext('Failed'), + value: null, + }], false); + }); + } + + main(title) { + this.set('title', title); + } + + setup() { + return { + buttons: [{ + text: '', + key: 112, + className: 'btn btn-secondary pull-left fa fa-question pg-alertify-icon-button', + attrs: { + name: 'dialog_help', + type: 'button', + label: gettext('Help'), + 'aria-label': gettext('Help'), + url: url_for('help.static', { + 'filename': 'search_objects.html', + }), + }, + }, { + text: gettext('Close'), + key: 27, + className: 'btn btn-secondary fa fa-lg fa-times pg-alertify-button', + 'data-btn-name': 'cancel', + }], + // Set options for dialog + options: { + title: this.dialogTitle, + //disable both padding and overflow control. + padding: !1, + overflow: !1, + model: 0, + resizable: true, + maximizable: true, + pinnable: false, + closableByDimmer: false, + modal: false, + }, + }; + } + + build() { + let tmpEle = document.createElement('div'); + tmpEle.innerHTML = this.dialogContainerSelector; + let dialogContainer = tmpEle.firstChild; + + // Append the container + this.elements.content.innerHTML = ''; + this.elements.content.appendChild(dialogContainer); + + this.createDialogDOM(dialogContainer); + this.alertify.pgDialogBuild.apply(this); + + this.loader = dialogContainer.getElementsByClassName('pg-sp-container')[0]; + + this.searchBox = dialogContainer.querySelector('#txtGridSearch'); + this.searchBtn = dialogContainer.querySelector('.btn-search'); + this.typesSelect = dialogContainer.querySelector('.node-types'); + this.searchResultContainer = dialogContainer.querySelector('.search-result-container'); + this.searchResult = dialogContainer.querySelector('.search-result'); + this.statusBar = dialogContainer.querySelector('.pg-prop-status-bar'); + + /* These two values are required to come out of grid when tab is + * pressed in the grid. Slickgrid does not allow any way to come out + */ + this.nextToGrid = this.elements.footer.querySelector('.ajs-button'); + this.prevToGrid = this.typesSelect; + + /* init select2 */ + this.setTypes([{ + id: -1, + text: gettext('Loading...'), + value: null, + }], false); + + /* on search box change */ + this.searchBox.addEventListener('input', ()=>{ + if(this.searchBoxVal().length >= 3) { + this.searchBtnEnabled(true); + } else { + this.searchBtnEnabled(false); + } + }); + + /* on enter key press */ + this.searchBox.addEventListener('keypress', (e)=>{ + if(e.keyCode == 13) { + e.stopPropagation(); + if(this.searchBtnEnabled()) { + this.searchBtn.dispatchEvent(new Event('click')); + } + } + }); + + /* on search button click */ + this.searchBtn.addEventListener('click', ()=>{ + this.searchBtnEnabled(false); + this.setGridData([]); + this.showMessage(null); + + this.setLoading(gettext('Searching....')); + axios.get(this.getBaseUrl('search'), { + params: { + text: this.searchBoxVal(), + type: this.typesVal(), + }, + }).then((res)=>{ + let grid_data = res.data.data.map((row)=>{ + return this.finaliseData(row); + }); + + this.setGridData(grid_data); + }).catch((error)=>{ + let errmsg = ''; + + if (error.response) { + errmsg = error.response.statusText; + } else if (error.request) { + errmsg = gettext('No response received'); + } else { + errmsg = error.message; + } + this.showMessage(gettext('An unexpected occurred: %s', errmsg), true); + console.warn(error); + }).finally(()=>{ + this.setLoading(null); + this.searchBtnEnabled(true); + }); + }); + + this.set({ + 'onresized': this.onDialogResize.bind(this), + 'onmaximized': this.onDialogResize.bind(this), + 'onrestored': this.onDialogResize.bind(this), + 'onshow': this.onDialogShow.bind(this), + }); + } + + prepare() { + let selectedTreeNode = this.getSelectedNode(); + if (!this.getSelectedNodeData(selectedTreeNode)) { + return; + } + + this.treeInfo = getTreeNodeHierarchyFromElement(this.pgBrowser, selectedTreeNode); + this.prepareDialog(); + this.focusOnDialog(this); + } + + callback(event) { + if (this.wasHelpButtonPressed(event)) { + event.cancel = true; + this.pgBrowser.showHelp( + event.button.element.name, + event.button.element.getAttribute('url'), + null, + null, + ); + return; + } + } +} diff --git a/web/pgadmin/tools/search_objects/static/scss/_search_objects.scss b/web/pgadmin/tools/search_objects/static/scss/_search_objects.scss new file mode 100644 index 000000000..920c53832 --- /dev/null +++ b/web/pgadmin/tools/search_objects/static/scss/_search_objects.scss @@ -0,0 +1,118 @@ +.search_objects_dialog { + height: 100%; + + .object-other-info { + &:hover { + font-weight: bold; + } + } + + .pref-dialog-link { + color: $color-fg !important; + text-decoration: underline !important; + cursor: pointer; + } + + .search-result-container { + width: 100%; + height: 100%; + min-height: 0; + } + + .node-types ~ .select2-container { + min-width: 100%; + } + + .ui-widget { + font-family: $font-family-primary; + font-size: $font-size-base; + + .slick-header.ui-state-default { + border: $table-border-width solid $table-border-color; + .slick-header-columns { + background: $table-bg; + color: $color-fg; + border-bottom: $panel-border; + + .slick-header-column-sorted { + font-style: unset; + } + + .ui-state-default { + background: $table-bg !important; + color: $color-fg !important; + padding: $table-header-cell-padding $table-cell-padding; + border-right: $table-border-width solid $table-border-color; + + .slick-column-name { + font-weight: bold; + } + + .slick-sort-indicator { + float: unset; + } + } + + .slick-header-sortable { + cursor: pointer !important; + + .slick-sort-indicator-asc { + background: none; + border-top: none; + border-right: 0.25rem solid transparent; + border-bottom: 0.25rem solid $color-fg; + border-left: 0.25rem solid transparent; + } + + .slick-sort-indicator-desc { + background: none; + border-top: 0.25rem solid $color-fg; + border-right: 0.25rem solid transparent; + border-bottom: none; + border-left: 0.25rem solid transparent; + } + } + } + } + .ui-widget-content { + color: $color-fg; + &.slick-row { + &.object-muted { + &.active, &.active:hover, &:hover, & { + .slick-cell { + color: $text-muted !important; + cursor: default !important; + } + } + } + + &.active, &.active:hover { + .slick-cell { + border-top: $table-border-width solid transparent !important; + background-color: $tree-bg-selected !important; + color: $tree-fg-selected !important; + } + } + + &:hover { + cursor: pointer; + .slick-cell { + border-top: $table-border-width solid transparent !important; + border-bottom: $table-border-width solid transparent !important; + background-color: $tree-bg-hover !important; + color: $tree-fg-hover !important; + cursor: pointer !important; + } + } + } + } + } + + + .pg-prop-status-bar { + position: absolute; + bottom: 0; + right: 0; + left: 0; + } +} diff --git a/web/pgadmin/tools/search_objects/templates/search_objects/sql/pg/10_plus/search.sql b/web/pgadmin/tools/search_objects/templates/search_objects/sql/pg/10_plus/search.sql new file mode 100644 index 000000000..d00ca0d11 --- /dev/null +++ b/web/pgadmin/tools/search_objects/templates/search_objects/sql/pg/10_plus/search.sql @@ -0,0 +1,434 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +{% set all_obj = false %} +{% if obj_type == 'all' or obj_type is none %} +{% set all_obj = true %} +{% endif %} +SELECT obj_type, obj_name, + REPLACE(obj_path, '/'||sn.schema_name||'/', '/'||{{ CATALOGS.LABELS_SCHEMACOL('sn.schema_name', _) }}||'/') AS obj_path, + schema_name, show_node, other_info, + CASE + WHEN {{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }} THEN + CASE WHEN {{ CATALOGS.DB_SUPPORT_SCHEMACOL('sn.schema_name') }} THEN 'D' ELSE 'O' END + ELSE 'N' + END AS catalog_level +FROM ( +{% if all_obj or obj_type in ['sequence', 'view', 'mview'] %} + SELECT + CASE + WHEN c.relkind = 'S' THEN 'sequence' + WHEN c.relkind = 'v' THEN 'view' + WHEN c.relkind = 'm' THEN 'mview' + ELSE 'should not happen' + END::text AS obj_type, c.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || + CASE + WHEN c.relkind = 'S' THEN ':sequence.' + WHEN c.relkind = 'v' THEN ':view.' + WHEN c.relkind = 'm' THEN ':mview.' + ELSE 'should not happen' + END || c.oid ||':/' || c.relname AS obj_path, n.nspname AS schema_name, + CASE + WHEN c.relkind = 'S' THEN {{ show_node_prefs['sequence'] }} + WHEN c.relkind = 'v' THEN {{ show_node_prefs['view'] }} + WHEN c.relkind = 'm' THEN {{ show_node_prefs['mview'] }} + ELSE False + END AS show_node, NULL AS other_info + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + {% if all_obj %} + WHERE c.relkind in ('S','v','m') + {% elif obj_type == 'sequence' %} + WHERE c.relkind = 'S' + {% elif obj_type == 'view' %} + WHERE c.relkind = 'v' + {% elif obj_type == 'mview' %} + WHERE c.relkind = 'm' + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['table', 'partition'] %} + SELECT CASE WHEN c.relispartition THEN 'partition' ELSE 'table' END::text AS obj_type, c.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || ( + WITH RECURSIVE table_path_data as ( + select c.oid as oid, 0 as height, + CASE c.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || c.oid || ':/' || c.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) obj_path, n.nspname AS schema_name, + CASE WHEN c.relispartition THEN {{ show_node_prefs['partition'] }} + ELSE {{ show_node_prefs['table'] }} END AS show_node, + NULL AS other_info + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relkind in ('p','r') + {% if obj_type == 'table' %} + AND NOT c.relispartition + {% elif obj_type == 'partition' %} + AND c.relispartition + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['index'] %} + SELECT 'index'::text AS obj_type, cls.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/:table.'|| tab.oid ||':/' || tab.relname || '/:index.'|| cls.oid ||':/' || cls.relname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info + FROM pg_index idx + JOIN pg_class cls ON cls.oid=indexrelid + JOIN pg_class tab ON tab.oid=indrelid + JOIN pg_namespace n ON n.oid=tab.relnamespace + LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i') + LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) + LEFT OUTER JOIN pg_description des ON des.objoid=cls.oid + LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) + WHERE contype IS NULL +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger_function', 'function'] %} + SELECT + CASE + WHEN t.typname IN ('trigger', 'event_trigger') THEN 'trigger_function' + ELSE 'function' END::text AS obj_type, p.proname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || case when t.typname = 'trigger' then ':trigger_function.' else ':function.' end || p.oid ||':/' || p.proname AS obj_path, n.nspname AS schema_name, + CASE WHEN t.typname IN ('trigger', 'event_trigger') THEN {{ show_node_prefs['trigger_function'] }} ELSE {{ show_node_prefs['function'] }} END AS show_node, + pg_catalog.pg_get_function_identity_arguments(p.oid) AS other_info + from pg_proc p + left join pg_namespace n on p.pronamespace = n.oid + left join pg_type t on p.prorettype = t.oid + WHERE ({{ CATALOGS.DB_SUPPORT('n') }}) +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['event_trigger'] %} + select 'event_trigger'::text AS obj_type, evtname AS obj_name, ':event_trigger.'||oid||':/' || evtname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info from pg_event_trigger +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['schema'] %} + select 'schema'::text AS obj_type, n.nspname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname as obj_path, n.nspname AS schema_name, + {{ show_node_prefs['schema'] }} AS show_node, NULL AS other_info from pg_namespace n + where {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['column'] %} + select 'column'::text AS obj_type, a.attname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname || '/' || + case + WHEN t.relkind = 'r' THEN ':table.' + WHEN t.relkind = 'v' THEN ':view.' + WHEN t.relkind = 'm' THEN ':mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname || '/:column.'|| a.attnum ||':/' || a.attname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['column'] }} AS show_node, NULL AS other_info + from pg_attribute a + inner join pg_class t on a.attrelid = t.oid and t.relkind in ('r','v','m') + left join pg_namespace n on t.relnamespace = n.oid where a.attnum > 0 +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['constraints', 'check_constraint', 'foreign_key', 'primary_key', 'unique_constraint', 'exclusion_constraint'] %} + SELECT + CASE + WHEN c.contype = 'c' THEN 'check_constraint' + WHEN c.contype = 'f' THEN 'foreign_key' + WHEN c.contype = 'p' THEN 'primary_key' + WHEN c.contype = 'u' THEN 'unique_constraint' + WHEN c.contype = 'x' THEN 'exclusion_constraint' + END::text AS obj_type, + case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_name, + ':schema.'||n.oid||':/' || n.nspname||'/'|| + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) || + CASE + WHEN c.contype = 'c' THEN '/:check_constraint.' ||c.oid + WHEN c.contype = 'f' THEN '/:foreign_key.' ||c.conindid + WHEN c.contype = 'p' THEN '/:primary_key.' ||c.conindid + WHEN c.contype = 'u' THEN '/:unique_constraint.' ||c.conindid + WHEN c.contype = 'x' THEN '/:exclusion_constraint.' ||c.conindid + END ||':/'|| case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['constraints'] }} AS show_node, NULL AS other_info + from pg_constraint c + left join pg_class t on c.conrelid = t.oid + left join pg_class tf on c.confrelid = tf.oid + left join pg_namespace n on t.relnamespace = n.oid + where c.contypid = 0 + {% if obj_type == 'check_constraint' %} + AND c.contype = 'c' + {% elif obj_type == 'foreign_key' %} + AND c.contype = 'f' + {% elif obj_type == 'primary_key' %} + AND c.contype = 'p' + {% elif obj_type == 'unique_constraint' %} + AND c.contype = 'u' + {% elif obj_type == 'exclusion_constraint' %} + AND c.contype = 'x' + {% else %} + AND c.contype IN ('c', 'f', 'p', 'u', 'x') + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['rule'] %} + select 'rule'::text AS obj_type, r.rulename AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| '/' || + case + when t.relkind = 'v' then ':view.' + when t.relkind = 'm' then ':mview.' + WHEN t.relkind in ('r', 'p') THEN + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) + end + ||'/:rule.'||r.oid||':/'|| r.rulename AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['rule'] }} AS show_node, NULL AS other_info + from pg_rewrite r + left join pg_class t on r.ev_class = t.oid + left join pg_namespace n on t.relnamespace = n.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger'] %} + select 'trigger'::text AS obj_type, tr.tgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| '/' || + case + when t.relkind = 'v' then ':view.' + when t.relkind = 'm' then ':mview.' + WHEN t.relkind in ('r', 'p') THEN + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) + end || '/:trigger.'|| tr.oid || ':/' || tr.tgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['trigger'] }} AS show_node, NULL AS other_info + from pg_trigger tr + left join pg_class t on tr.tgrelid = t.oid + left join pg_namespace n on t.relnamespace = n.oid + where tr.tgisinternal = false + and {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['type'] %} + SELECT 'type'::text AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || + '/:type.'|| t.oid ||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['type'] }} AS show_node, NULL AS other_info + FROM pg_type t + LEFT OUTER JOIN pg_type e ON e.oid=t.typelem + LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_namespace n on t.typnamespace = n.oid + WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' + {% if not show_system_objects %} + AND ct.oid is NULL + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['cast'] %} + SELECT 'cast'::text AS obj_type, format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_name, + ':cast.'||ca.oid||':/' || format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['cast'] }} AS show_node, NULL AS other_info + FROM pg_cast ca + JOIN pg_type st ON st.oid=castsource + JOIN pg_type tt ON tt.oid=casttarget + {% if not show_system_objects %} + WHERE ca.oid > {{last_system_oid}}::OID + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['language'] %} + SELECT 'language'::text AS obj_type, lanname AS obj_name, ':language.'||lan.oid||':/' || lanname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['language'] }} AS show_node, NULL AS other_info + FROM pg_language lan + WHERE lanispl IS TRUE +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_configuration'] %} + SELECT 'fts_configuration'::text AS obj_type, cfg.cfgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:fts_configuration.'||cfg.oid||':/' || cfg.cfgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['fts_configuration'] }} AS show_node, NULL AS other_info + FROM pg_ts_config cfg + left join pg_namespace n on cfg.cfgnamespace = n.oid + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_dictionary'] %} + SELECT 'fts_dictionary' AS obj_type, dict.dictname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_dictionary.'||dict.oid||':/' || dict.dictname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_dictionary'] }} AS show_node, NULL AS other_info + FROM pg_ts_dict dict + left join pg_namespace ns on dict.dictnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_parser'] %} + SELECT 'fts_parser' AS obj_type, prs.prsname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_parser.'||prs.oid||':/' || prs.prsname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_parser'] }} AS show_node, NULL AS other_info + FROM pg_ts_parser prs + left join pg_namespace ns on prs.prsnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_template'] %} + SELECT 'fts_template' AS obj_type, tmpl.tmplname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_template.'||tmpl.oid||':/' || tmpl.tmplname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_template'] }} AS show_node, NULL AS other_info + FROM pg_ts_template tmpl + left join pg_namespace ns on tmpl.tmplnamespace = ns.oid + AND {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain'] %} + select 'domain' AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['domain'] }} AS show_node, NULL AS other_info + from pg_type t + inner join pg_namespace n on t.typnamespace = n.oid + where t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain_constraints'] %} + SELECT 'domain_constraints' AS obj_type, + c.conname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname || '/:domain_constraints.'||c.oid||':/' || c.conname AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['domain_constraints'] }} AS show_node, NULL AS other_info + FROM pg_constraint c JOIN pg_type t + ON t.oid=contypid JOIN pg_namespace n + ON n.oid=t.typnamespace + WHERE t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_data_wrapper'] %} + select 'foreign_data_wrapper' AS obj_type, fdwname AS obj_name, ':foreign_data_wrapper.'||oid||':/' || fdwname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_data_wrapper'] }} AS show_node, NULL AS other_info + from pg_foreign_data_wrapper +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_server'] %} + select 'foreign_server' AS obj_type, sr.srvname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_server'] }} AS show_node, NULL AS other_info + from pg_foreign_server sr + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['user_mapping'] %} + select 'user_mapping' AS obj_type, ro.rolname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname || '/:user_mapping.'||ro.oid||':/' || ro.rolname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['user_mapping'] }} AS show_node, NULL AS other_info + from pg_user_mapping um + inner join pg_roles ro on um.umuser = ro.oid + inner join pg_foreign_server sr on um.umserver = sr.oid + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_table'] %} + select 'foreign_table' AS obj_type, c.relname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:foreign_table.'||c.oid||':/' || c.relname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['foreign_table'] }} AS show_node, NULL AS other_info + from pg_foreign_table ft + inner join pg_class c on ft.ftrelid = c.oid + inner join pg_namespace ns on c.relnamespace = ns.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['extension'] %} + select 'extension' AS obj_type, x.extname AS obj_name, ':extension.'||x.oid||':/' || x.extname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['extension'] }} AS show_node, NULL AS other_info + FROM pg_extension x + JOIN pg_namespace n on x.extnamespace=n.oid + join pg_available_extensions() e(name, default_version, comment) ON x.extname=e.name +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['collation'] %} + SELECT 'collation' AS obj_type, c.collname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:collation.'||c.oid||':/' || c.collname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['collation'] }} AS show_node, NULL AS other_info + FROM pg_collation c + JOIN pg_namespace n ON n.oid=c.collnamespace + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} + +) sn +where lower(sn.obj_name) like '%{{ search_text }}%' +{% if not show_system_objects %} +AND NOT ({{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }}) +AND (sn.schema_name IS NOT NULL AND sn.schema_name NOT LIKE 'pg\_%') +{% endif %} +ORDER BY 1, 2, 3 diff --git a/web/pgadmin/tools/search_objects/templates/search_objects/sql/pg/11_plus/search.sql b/web/pgadmin/tools/search_objects/templates/search_objects/sql/pg/11_plus/search.sql new file mode 100644 index 000000000..bd2f94d9c --- /dev/null +++ b/web/pgadmin/tools/search_objects/templates/search_objects/sql/pg/11_plus/search.sql @@ -0,0 +1,451 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +{% set all_obj = false %} +{% if obj_type == 'all' or obj_type is none %} +{% set all_obj = true %} +{% endif %} +SELECT obj_type, obj_name, + REPLACE(obj_path, '/'||sn.schema_name||'/', '/'||{{ CATALOGS.LABELS_SCHEMACOL('sn.schema_name', _) }}||'/') AS obj_path, + schema_name, show_node, other_info, + CASE + WHEN {{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }} THEN + CASE WHEN {{ CATALOGS.DB_SUPPORT_SCHEMACOL('sn.schema_name') }} THEN 'D' ELSE 'O' END + ELSE 'N' + END AS catalog_level +FROM ( +{% if all_obj or obj_type in ['sequence', 'view', 'mview'] %} + SELECT + CASE + WHEN c.relkind = 'S' THEN 'sequence' + WHEN c.relkind = 'v' THEN 'view' + WHEN c.relkind = 'm' THEN 'mview' + ELSE 'should not happen' + END::text AS obj_type, c.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || + CASE + WHEN c.relkind = 'S' THEN ':sequence.' + WHEN c.relkind = 'v' THEN ':view.' + WHEN c.relkind = 'm' THEN ':mview.' + ELSE 'should not happen' + END || c.oid ||':/' || c.relname AS obj_path, n.nspname AS schema_name, + CASE + WHEN c.relkind = 'S' THEN {{ show_node_prefs['sequence'] }} + WHEN c.relkind = 'v' THEN {{ show_node_prefs['view'] }} + WHEN c.relkind = 'm' THEN {{ show_node_prefs['mview'] }} + ELSE False + END AS show_node, NULL AS other_info + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + {% if all_obj %} + WHERE c.relkind in ('S','v','m') + {% elif obj_type == 'sequence' %} + WHERE c.relkind = 'S' + {% elif obj_type == 'view' %} + WHERE c.relkind = 'v' + {% elif obj_type == 'mview' %} + WHERE c.relkind = 'm' + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['table', 'partition'] %} + SELECT CASE WHEN c.relispartition THEN 'partition' ELSE 'table' END::text AS obj_type, c.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || ( + WITH RECURSIVE table_path_data as ( + select c.oid as oid, 0 as height, + CASE c.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || c.oid || ':/' || c.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) obj_path, n.nspname AS schema_name, + CASE WHEN c.relispartition THEN {{ show_node_prefs['partition'] }} + ELSE {{ show_node_prefs['table'] }} END AS show_node, + NULL AS other_info + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relkind in ('p','r') + {% if obj_type == 'table' %} + AND NOT c.relispartition + {% elif obj_type == 'partition' %} + AND c.relispartition + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['index'] %} + SELECT 'index'::text AS obj_type, cls.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/:table.'|| tab.oid ||':/' || tab.relname || '/:index.'|| cls.oid ||':/' || cls.relname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info + FROM pg_index idx + JOIN pg_class cls ON cls.oid=indexrelid + JOIN pg_class tab ON tab.oid=indrelid + JOIN pg_namespace n ON n.oid=tab.relnamespace + LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i') + LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) + LEFT OUTER JOIN pg_description des ON des.objoid=cls.oid + LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) + WHERE contype IS NULL +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger_function', 'function', 'procedure'] %} + SELECT + CASE + WHEN t.typname IN ('trigger', 'event_trigger') THEN 'trigger_function' + WHEN p.prokind = 'p' THEN 'procedure' + ELSE 'function' + END::text AS obj_type, p.proname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || + CASE + WHEN t.typname IN ('trigger', 'event_trigger') THEN ':trigger_function.' + WHEN p.prokind = 'p' THEN ':procedure.' + ELSE ':function.' + END || p.oid ||':/' || p.proname AS obj_path, n.nspname AS schema_name, + CASE + WHEN t.typname IN ('trigger', 'event_trigger') THEN {{ show_node_prefs['trigger_function'] }} + WHEN p.prokind = 'p' THEN {{ show_node_prefs['procedure'] }} + ELSE {{ show_node_prefs['function'] }} + END AS show_node, + pg_catalog.pg_get_function_identity_arguments(p.oid) AS other_info + from pg_proc p join pg_namespace n + on p.pronamespace = n.oid join pg_type t + on p.prorettype = t.oid join pg_language lng + ON lng.oid=p.prolang + WHERE p.prokind IN ('f', 'w', 'p') + AND CASE + WHEN t.typname IN ('trigger', 'event_trigger') THEN lng.lanname NOT IN ('edbspl', 'sql', 'internal') + ELSE true + END + AND ({{ CATALOGS.DB_SUPPORT('n') }}) +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['event_trigger'] %} + select 'event_trigger'::text AS obj_type, evtname AS obj_name, ':event_trigger.'||oid||':/' || evtname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info from pg_event_trigger +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['schema'] %} + select 'schema'::text AS obj_type, n.nspname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname as obj_path, n.nspname AS schema_name, + {{ show_node_prefs['schema'] }} AS show_node, NULL AS other_info from pg_namespace n + where {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['column'] %} + select 'column'::text AS obj_type, a.attname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname || '/' || + case + WHEN t.relkind = 'r' THEN ':table.' + WHEN t.relkind = 'v' THEN ':view.' + WHEN t.relkind = 'm' THEN ':mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname || '/:column.'|| a.attnum ||':/' || a.attname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['column'] }} AS show_node, NULL AS other_info + from pg_attribute a + inner join pg_class t on a.attrelid = t.oid and t.relkind in ('r','v','m') + left join pg_namespace n on t.relnamespace = n.oid where a.attnum > 0 +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['constraints', 'check_constraint', 'foreign_key', 'primary_key', 'unique_constraint', 'exclusion_constraint'] %} + SELECT + CASE + WHEN c.contype = 'c' THEN 'check_constraint' + WHEN c.contype = 'f' THEN 'foreign_key' + WHEN c.contype = 'p' THEN 'primary_key' + WHEN c.contype = 'u' THEN 'unique_constraint' + WHEN c.contype = 'x' THEN 'exclusion_constraint' + END::text AS obj_type, + case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_name, + ':schema.'||n.oid||':/' || n.nspname||'/'|| + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) || + CASE + WHEN c.contype = 'c' THEN '/:check_constraint.' ||c.oid + WHEN c.contype = 'f' THEN '/:foreign_key.' ||c.conindid + WHEN c.contype = 'p' THEN '/:primary_key.' ||c.conindid + WHEN c.contype = 'u' THEN '/:unique_constraint.' ||c.conindid + WHEN c.contype = 'x' THEN '/:exclusion_constraint.' ||c.conindid + END ||':/'|| case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['constraints'] }} AS show_node, NULL AS other_info + from pg_constraint c + left join pg_class t on c.conrelid = t.oid + left join pg_class tf on c.confrelid = tf.oid + left join pg_namespace n on t.relnamespace = n.oid + where c.contypid = 0 + {% if obj_type == 'check_constraint' %} + AND c.contype = 'c' + {% elif obj_type == 'foreign_key' %} + AND c.contype = 'f' + {% elif obj_type == 'primary_key' %} + AND c.contype = 'p' + {% elif obj_type == 'unique_constraint' %} + AND c.contype = 'u' + {% elif obj_type == 'exclusion_constraint' %} + AND c.contype = 'x' + {% else %} + AND c.contype IN ('c', 'f', 'p', 'u', 'x') + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['rule'] %} + select 'rule'::text AS obj_type, r.rulename AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| '/' || + case + when t.relkind = 'v' then ':view.' + when t.relkind = 'm' then ':mview.' + WHEN t.relkind in ('r', 'p') THEN + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) + end + ||'/:rule.'||r.oid||':/'|| r.rulename AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['rule'] }} AS show_node, NULL AS other_info + from pg_rewrite r + left join pg_class t on r.ev_class = t.oid + left join pg_namespace n on t.relnamespace = n.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger'] %} + select 'trigger'::text AS obj_type, tr.tgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| '/' || + case + when t.relkind = 'v' then ':view.' + when t.relkind = 'm' then ':mview.' + WHEN t.relkind in ('r', 'p') THEN + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) + end || '/:trigger.'|| tr.oid || ':/' || tr.tgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['trigger'] }} AS show_node, NULL AS other_info + from pg_trigger tr + left join pg_class t on tr.tgrelid = t.oid + left join pg_namespace n on t.relnamespace = n.oid + where tr.tgisinternal = false + and {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['type'] %} + SELECT 'type'::text AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || + '/:type.'|| t.oid ||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['type'] }} AS show_node, NULL AS other_info + FROM pg_type t + LEFT OUTER JOIN pg_type e ON e.oid=t.typelem + LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_namespace n on t.typnamespace = n.oid + WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' + {% if not show_system_objects %} + AND ct.oid is NULL + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['cast'] %} + SELECT 'cast'::text AS obj_type, format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_name, + ':cast.'||ca.oid||':/' || format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['cast'] }} AS show_node, NULL AS other_info + FROM pg_cast ca + JOIN pg_type st ON st.oid=castsource + JOIN pg_type tt ON tt.oid=casttarget + {% if not show_system_objects %} + WHERE ca.oid > {{last_system_oid}}::OID + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['language'] %} + SELECT 'language'::text AS obj_type, lanname AS obj_name, ':language.'||lan.oid||':/' || lanname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['language'] }} AS show_node, NULL AS other_info + FROM pg_language lan + WHERE lanispl IS TRUE +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_configuration'] %} + SELECT 'fts_configuration'::text AS obj_type, cfg.cfgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:fts_configuration.'||cfg.oid||':/' || cfg.cfgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['fts_configuration'] }} AS show_node, NULL AS other_info + FROM pg_ts_config cfg + left join pg_namespace n on cfg.cfgnamespace = n.oid + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_dictionary'] %} + SELECT 'fts_dictionary' AS obj_type, dict.dictname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_dictionary.'||dict.oid||':/' || dict.dictname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_dictionary'] }} AS show_node, NULL AS other_info + FROM pg_ts_dict dict + left join pg_namespace ns on dict.dictnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_parser'] %} + SELECT 'fts_parser' AS obj_type, prs.prsname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_parser.'||prs.oid||':/' || prs.prsname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_parser'] }} AS show_node, NULL AS other_info + FROM pg_ts_parser prs + left join pg_namespace ns on prs.prsnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_template'] %} + SELECT 'fts_template' AS obj_type, tmpl.tmplname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_template.'||tmpl.oid||':/' || tmpl.tmplname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_template'] }} AS show_node, NULL AS other_info + FROM pg_ts_template tmpl + left join pg_namespace ns on tmpl.tmplnamespace = ns.oid + AND {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain'] %} + select 'domain' AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['domain'] }} AS show_node, NULL AS other_info + from pg_type t + inner join pg_namespace n on t.typnamespace = n.oid + where t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain_constraints'] %} + SELECT 'domain_constraints' AS obj_type, + c.conname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname || '/:domain_constraints.'||c.oid||':/' || c.conname AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['domain_constraints'] }} AS show_node, NULL AS other_info + FROM pg_constraint c JOIN pg_type t + ON t.oid=contypid JOIN pg_namespace n + ON n.oid=t.typnamespace + WHERE t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_data_wrapper'] %} + select 'foreign_data_wrapper' AS obj_type, fdwname AS obj_name, ':foreign_data_wrapper.'||oid||':/' || fdwname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_data_wrapper'] }} AS show_node, NULL AS other_info + from pg_foreign_data_wrapper +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_server'] %} + select 'foreign_server' AS obj_type, sr.srvname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_server'] }} AS show_node, NULL AS other_info + from pg_foreign_server sr + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['user_mapping'] %} + select 'user_mapping' AS obj_type, ro.rolname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname || '/:user_mapping.'||ro.oid||':/' || ro.rolname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['user_mapping'] }} AS show_node, NULL AS other_info + from pg_user_mapping um + inner join pg_roles ro on um.umuser = ro.oid + inner join pg_foreign_server sr on um.umserver = sr.oid + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_table'] %} + select 'foreign_table' AS obj_type, c.relname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:foreign_table.'||c.oid||':/' || c.relname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['foreign_table'] }} AS show_node, NULL AS other_info + from pg_foreign_table ft + inner join pg_class c on ft.ftrelid = c.oid + inner join pg_namespace ns on c.relnamespace = ns.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['extension'] %} + select 'extension' AS obj_type, x.extname AS obj_name, ':extension.'||x.oid||':/' || x.extname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['extension'] }} AS show_node, NULL AS other_info + FROM pg_extension x + JOIN pg_namespace n on x.extnamespace=n.oid + join pg_available_extensions() e(name, default_version, comment) ON x.extname=e.name +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['collation'] %} + SELECT 'collation' AS obj_type, c.collname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:collation.'||c.oid||':/' || c.collname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['collation'] }} AS show_node, NULL AS other_info + FROM pg_collation c + JOIN pg_namespace n ON n.oid=c.collnamespace + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} + +) sn +where lower(sn.obj_name) like '%{{ search_text }}%' +{% if not show_system_objects %} +AND NOT ({{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }}) +AND (sn.schema_name IS NOT NULL AND sn.schema_name NOT LIKE 'pg\_%') +{% endif %} +ORDER BY 1, 2, 3 diff --git a/web/pgadmin/tools/search_objects/templates/search_objects/sql/pg/default/search.sql b/web/pgadmin/tools/search_objects/templates/search_objects/sql/pg/default/search.sql new file mode 100644 index 000000000..b384b316d --- /dev/null +++ b/web/pgadmin/tools/search_objects/templates/search_objects/sql/pg/default/search.sql @@ -0,0 +1,367 @@ +{% import 'catalog/pg/macros/catalogs.sql' as CATALOGS %} +{% set all_obj = false %} +{% if obj_type == 'all' or obj_type is none %} +{% set all_obj = true %} +{% endif %} +SELECT obj_type, obj_name, + REPLACE(obj_path, '/'||sn.schema_name||'/', '/'||{{ CATALOGS.LABELS_SCHEMACOL('sn.schema_name', _) }}||'/') AS obj_path, + schema_name, show_node, other_info, + CASE + WHEN {{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }} THEN + CASE WHEN {{ CATALOGS.DB_SUPPORT_SCHEMACOL('sn.schema_name') }} THEN 'D' ELSE 'O' END + ELSE 'N' + END AS catalog_level +FROM ( +{% if all_obj or obj_type in ['table', 'sequence', 'view', 'mview'] %} + SELECT + CASE + WHEN c.relkind = 'r' THEN 'table' + WHEN c.relkind = 'S' THEN 'sequence' + WHEN c.relkind = 'v' THEN 'view' + WHEN c.relkind = 'm' THEN 'mview' + ELSE 'should not happen' + END::text AS obj_type, c.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || + CASE + WHEN c.relkind = 'r' THEN ':table.' + WHEN c.relkind = 'S' THEN ':sequence.' + WHEN c.relkind = 'v' THEN ':view.' + WHEN c.relkind = 'm' THEN ':mview.' + ELSE 'should not happen' + END || c.oid ||':/' || c.relname AS obj_path, n.nspname AS schema_name, + CASE + WHEN c.relkind = 'r' THEN {{ show_node_prefs['table'] }} + WHEN c.relkind = 'S' THEN {{ show_node_prefs['sequence'] }} + WHEN c.relkind = 'v' THEN {{ show_node_prefs['view'] }} + WHEN c.relkind = 'm' THEN {{ show_node_prefs['mview'] }} + ELSE False + END AS show_node, NULL AS other_info + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + {% if all_obj %} + WHERE c.relkind in ('r','S','v','m') + {% elif obj_type == 'table' %} + WHERE c.relkind = 'r' + {% elif obj_type == 'sequence' %} + WHERE c.relkind = 'S' + AND {{ CATALOGS.DB_SUPPORT('n') }} + {% elif obj_type == 'view' %} + WHERE c.relkind = 'v' + {% elif obj_type == 'mview' %} + WHERE c.relkind = 'm' + AND {{ CATALOGS.DB_SUPPORT('n') }} + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['index'] %} + SELECT 'index'::text AS obj_type, cls.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/:table.'|| tab.oid ||':/' || tab.relname || '/:index.'|| cls.oid ||':/' || cls.relname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info + FROM pg_index idx + JOIN pg_class cls ON cls.oid=indexrelid + JOIN pg_class tab ON tab.oid=indrelid + JOIN pg_namespace n ON n.oid=tab.relnamespace + LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i') + LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) + LEFT OUTER JOIN pg_description des ON des.objoid=cls.oid + LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) + WHERE contype IS NULL +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger_function', 'function'] %} + SELECT + CASE + WHEN t.typname IN ('trigger', 'event_trigger') THEN 'trigger_function' + ELSE 'function' END::text AS obj_type, p.proname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || case when t.typname = 'trigger' then ':trigger_function.' else ':function.' end || p.oid ||':/' || p.proname AS obj_path, n.nspname AS schema_name, + CASE WHEN t.typname IN ('trigger', 'event_trigger') THEN {{ show_node_prefs['trigger_function'] }} ELSE {{ show_node_prefs['function'] }} END AS show_node, + pg_catalog.pg_get_function_identity_arguments(p.oid) AS other_info + from pg_proc p + left join pg_namespace n on p.pronamespace = n.oid + left join pg_type t on p.prorettype = t.oid + WHERE ({{ CATALOGS.DB_SUPPORT('n') }}) +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['event_trigger'] %} + select 'event_trigger'::text AS obj_type, evtname AS obj_name, ':event_trigger.'||oid||':/' || evtname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info from pg_event_trigger +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['schema'] %} + select 'schema'::text AS obj_type, n.nspname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname as obj_path, n.nspname AS schema_name, + {{ show_node_prefs['schema'] }} AS show_node, NULL AS other_info from pg_namespace n + where {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['column'] %} + select 'column'::text AS obj_type, a.attname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname || '/' || + case + WHEN t.relkind = 'r' THEN ':table.' + WHEN t.relkind = 'v' THEN ':view.' + WHEN t.relkind = 'm' THEN ':mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname || '/:column.'|| a.attnum ||':/' || a.attname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['column'] }} AS show_node, NULL AS other_info + from pg_attribute a + inner join pg_class t on a.attrelid = t.oid and t.relkind in ('r','v','m') + left join pg_namespace n on t.relnamespace = n.oid where a.attnum > 0 +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['constraints', 'check_constraint', 'foreign_key', 'primary_key', 'unique_constraint', 'exclusion_constraint'] %} + SELECT + CASE + WHEN c.contype = 'c' THEN 'check_constraint' + WHEN c.contype = 'f' THEN 'foreign_key' + WHEN c.contype = 'p' THEN 'primary_key' + WHEN c.contype = 'u' THEN 'unique_constraint' + WHEN c.contype = 'x' THEN 'exclusion_constraint' + END::text AS obj_type, + case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_name, + ':schema.'||n.oid||':/' || n.nspname||'/:table.'|| t.oid || ':/'||t.relname|| + CASE + WHEN c.contype = 'c' THEN '/:check_constraint.' ||c.oid + WHEN c.contype = 'f' THEN '/:foreign_key.' ||c.conindid + WHEN c.contype = 'p' THEN '/:primary_key.' ||c.conindid + WHEN c.contype = 'u' THEN '/:unique_constraint.' ||c.conindid + WHEN c.contype = 'x' THEN '/:exclusion_constraint.' ||c.conindid + END ||':/'|| case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['constraints'] }} AS show_node, NULL AS other_info + from pg_constraint c + left join pg_class t on c.conrelid = t.oid + left join pg_class tf on c.confrelid = tf.oid + left join pg_namespace n on t.relnamespace = n.oid + where c.contypid = 0 + {% if obj_type == 'check_constraint' %} + AND c.contype = 'c' + {% elif obj_type == 'foreign_key' %} + AND c.contype = 'f' + {% elif obj_type == 'primary_key' %} + AND c.contype = 'p' + {% elif obj_type == 'unique_constraint' %} + AND c.contype = 'u' + {% elif obj_type == 'exclusion_constraint' %} + AND c.contype = 'x' + {% else %} + AND c.contype IN ('c', 'f', 'p', 'u', 'x') + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['rule'] %} + select 'rule'::text AS obj_type, r.rulename AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| + case + WHEN t.relkind = 'r' THEN '/:table.' + when t.relkind = 'v' then '/:view.' + when t.relkind = 'm' then '/:mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname ||'/:rule.'||r.oid||':/'|| r.rulename AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['rule'] }} AS show_node, NULL AS other_info + from pg_rewrite r + left join pg_class t on r.ev_class = t.oid + left join pg_namespace n on t.relnamespace = n.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger'] %} + select 'trigger'::text AS obj_type, tr.tgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| + case + WHEN t.relkind = 'r' THEN '/:table.' + when t.relkind = 'v' then '/:view.' + when t.relkind = 'm' then '/:mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname || '/:trigger.'|| tr.oid || ':/' || tr.tgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['trigger'] }} AS show_node, NULL AS other_info + from pg_trigger tr + left join pg_class t on tr.tgrelid = t.oid + left join pg_namespace n on t.relnamespace = n.oid + where tr.tgisinternal = false + and {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['type'] %} + SELECT 'type'::text AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || + '/:type.'|| t.oid ||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['type'] }} AS show_node, NULL AS other_info + FROM pg_type t + LEFT OUTER JOIN pg_type e ON e.oid=t.typelem + LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_namespace n on t.typnamespace = n.oid + WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' + {% if not show_system_objects %} + AND ct.oid is NULL + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['cast'] %} + SELECT 'cast'::text AS obj_type, format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_name, + ':cast.'||ca.oid||':/' || format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['cast'] }} AS show_node, NULL AS other_info + FROM pg_cast ca + JOIN pg_type st ON st.oid=castsource + JOIN pg_type tt ON tt.oid=casttarget + {% if not show_system_objects %} + WHERE ca.oid > {{last_system_oid}}::OID + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['language'] %} + SELECT 'language'::text AS obj_type, lanname AS obj_name, ':language.'||lan.oid||':/' || lanname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['language'] }} AS show_node, NULL AS other_info + FROM pg_language lan + WHERE lanispl IS TRUE +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_configuration'] %} + SELECT 'fts_configuration'::text AS obj_type, cfg.cfgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:fts_configuration.'||cfg.oid||':/' || cfg.cfgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['fts_configuration'] }} AS show_node, NULL AS other_info + FROM pg_ts_config cfg + left join pg_namespace n on cfg.cfgnamespace = n.oid + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_dictionary'] %} + SELECT 'fts_dictionary'::text AS obj_type, dict.dictname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_dictionary.'||dict.oid||':/' || dict.dictname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_dictionary'] }} AS show_node, NULL AS other_info + FROM pg_ts_dict dict + left join pg_namespace ns on dict.dictnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_parser'] %} + SELECT 'fts_parser'::text AS obj_type, prs.prsname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_parser.'||prs.oid||':/' || prs.prsname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_parser'] }} AS show_node, NULL AS other_info + FROM pg_ts_parser prs + left join pg_namespace ns on prs.prsnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_template'] %} + SELECT 'fts_template'::text AS obj_type, tmpl.tmplname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_template.'||tmpl.oid||':/' || tmpl.tmplname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_template'] }} AS show_node, NULL AS other_info + FROM pg_ts_template tmpl + left join pg_namespace ns on tmpl.tmplnamespace = ns.oid + AND {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain'] %} + select 'domain'::text AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['domain'] }} AS show_node, NULL AS other_info + from pg_type t + inner join pg_namespace n on t.typnamespace = n.oid + where t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain_constraints'] %} + SELECT 'domain_constraints'::text AS obj_type, + c.conname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname || '/:domain_constraints.'||c.oid||':/' || c.conname AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['domain_constraints'] }} AS show_node, NULL AS other_info + FROM pg_constraint c JOIN pg_type t + ON t.oid=contypid JOIN pg_namespace n + ON n.oid=t.typnamespace + WHERE t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_data_wrapper'] %} + select 'foreign_data_wrapper'::text AS obj_type, fdwname AS obj_name, ':foreign_data_wrapper.'||oid||':/' || fdwname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_data_wrapper'] }} AS show_node, NULL AS other_info + from pg_foreign_data_wrapper +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_server'] %} + select 'foreign_server'::text AS obj_type, sr.srvname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_server'] }} AS show_node, NULL AS other_info + from pg_foreign_server sr + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['user_mapping'] %} + select 'user_mapping'::text AS obj_type, ro.rolname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname || '/:user_mapping.'||ro.oid||':/' || ro.rolname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['user_mapping'] }} AS show_node, NULL AS other_info + from pg_user_mapping um + inner join pg_roles ro on um.umuser = ro.oid + inner join pg_foreign_server sr on um.umserver = sr.oid + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_table'] %} + select 'foreign_table'::text AS obj_type, c.relname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:foreign_table.'||c.oid||':/' || c.relname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['foreign_table'] }} AS show_node, NULL AS other_info + from pg_foreign_table ft + inner join pg_class c on ft.ftrelid = c.oid + inner join pg_namespace ns on c.relnamespace = ns.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['extension'] %} + select 'extension'::text AS obj_type, x.extname AS obj_name, ':extension.'||x.oid||':/' || x.extname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['extension'] }} AS show_node, NULL AS other_info + FROM pg_extension x + JOIN pg_namespace n on x.extnamespace=n.oid + join pg_available_extensions() e(name, default_version, comment) ON x.extname=e.name +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['collation'] %} + SELECT 'collation'::text AS obj_type, c.collname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:collation.'||c.oid||':/' || c.collname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['collation'] }} AS show_node, NULL AS other_info + FROM pg_collation c + JOIN pg_namespace n ON n.oid=c.collnamespace + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} + +) sn +where lower(sn.obj_name) like '%{{ search_text }}%' +{% if not show_system_objects %} +AND NOT ({{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }}) +AND (sn.schema_name IS NOT NULL AND sn.schema_name NOT LIKE 'pg\_%') +{% endif %} +ORDER BY 1, 2, 3 diff --git a/web/pgadmin/tools/search_objects/templates/search_objects/sql/ppas/10_plus/search.sql b/web/pgadmin/tools/search_objects/templates/search_objects/sql/ppas/10_plus/search.sql new file mode 100644 index 000000000..76d3f01ba --- /dev/null +++ b/web/pgadmin/tools/search_objects/templates/search_objects/sql/ppas/10_plus/search.sql @@ -0,0 +1,494 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +{% set all_obj = false %} +{% if obj_type == 'all' or obj_type is none %} +{% set all_obj = true %} +{% endif %} +SELECT obj_type, obj_name, + REPLACE(obj_path, '/'||sn.schema_name||'/', '/'||{{ CATALOGS.LABELS_SCHEMACOL('sn.schema_name', _) }}||'/') AS obj_path, + schema_name, show_node, other_info, + CASE + WHEN {{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }} THEN + CASE WHEN {{ CATALOGS.DB_SUPPORT_SCHEMACOL('sn.schema_name') }} THEN 'D' ELSE 'O' END + ELSE 'N' + END AS catalog_level +FROM ( +{% if all_obj or obj_type in ['sequence', 'view', 'mview'] %} + SELECT + CASE + WHEN c.relkind = 'S' THEN 'sequence' + WHEN c.relkind = 'v' THEN 'view' + WHEN c.relkind = 'm' THEN 'mview' + ELSE 'should not happen' + END::text AS obj_type, c.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || + CASE + WHEN c.relkind = 'S' THEN ':sequence.' + WHEN c.relkind = 'v' THEN ':view.' + WHEN c.relkind = 'm' THEN ':mview.' + ELSE 'should not happen' + END || c.oid ||':/' || c.relname AS obj_path, n.nspname AS schema_name, + CASE + WHEN c.relkind = 'S' THEN {{ show_node_prefs['sequence'] }} + WHEN c.relkind = 'v' THEN {{ show_node_prefs['view'] }} + WHEN c.relkind = 'm' THEN {{ show_node_prefs['mview'] }} + ELSE False + END AS show_node, NULL AS other_info + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + {% if all_obj %} + WHERE c.relkind in ('S','v','m') + {% elif obj_type == 'sequence' %} + WHERE c.relkind = 'S' + {% elif obj_type == 'view' %} + WHERE c.relkind = 'v' + {% elif obj_type == 'mview' %} + WHERE c.relkind = 'm' + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['table', 'partition'] %} + SELECT CASE WHEN c.relispartition THEN 'partition' ELSE 'table' END::text AS obj_type, c.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || ( + WITH RECURSIVE table_path_data as ( + select c.oid as oid, 0 as height, + CASE c.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || c.oid || ':/' || c.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) obj_path, n.nspname AS schema_name, + CASE WHEN c.relispartition THEN {{ show_node_prefs['partition'] }} + ELSE {{ show_node_prefs['table'] }} END AS show_node, + NULL AS other_info + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relkind in ('p','r') + {% if obj_type == 'table' %} + AND NOT c.relispartition + {% elif obj_type == 'partition' %} + AND c.relispartition + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['index'] %} + SELECT 'index'::text AS obj_type, cls.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/:table.'|| tab.oid ||':/' || tab.relname || '/:index.'|| cls.oid ||':/' || cls.relname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info + FROM pg_index idx + JOIN pg_class cls ON cls.oid=indexrelid + JOIN pg_class tab ON tab.oid=indrelid + JOIN pg_namespace n ON n.oid=tab.relnamespace + LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i') + LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) + LEFT OUTER JOIN pg_description des ON des.objoid=cls.oid + LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) + WHERE contype IS NULL +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger_function', 'function', 'procedure', 'edbfunc', 'edbproc'] %} + SELECT fd.obj_type, fd.obj_name, + CASE + WHEN fd.obj_type = 'function' THEN + ':schema.'|| fd.schema_oid || ':/' || fd.schema_name || '/:function.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'procedure' THEN + ':schema.'|| fd.schema_oid || ':/' || fd.schema_name || '/:procedure.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'trigger_function' THEN + ':schema.'|| fd.schema_oid || ':/' || fd.schema_name || '/:trigger_function.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'edbfunc' THEN + ':schema.'|| fd.next_schema_oid || ':/' || fd.next_schema_name || '/:package.'|| fd.schema_oid || ':/' || fd.schema_name || '/:edbfunc.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'edbproc' THEN + ':schema.'|| fd.next_schema_oid || ':/' || fd.next_schema_name || '/:package.'|| fd.schema_oid || ':/' || fd.schema_name || '/:edbproc.' || fd.obj_oid ||':/' || fd.obj_name + ELSE NULL + END AS obj_path, + CASE + WHEN fd.obj_type IN ('function', 'procedure', 'trigger_function') THEN fd.schema_name + WHEN fd.obj_type IN ('edbfunc', 'edbproc') THEN fd.next_schema_name + ELSE NULL + END AS schema_name, + CASE + WHEN fd.obj_type = 'function' THEN {{ show_node_prefs['function'] }} + WHEN fd.obj_type = 'procedure' THEN {{ show_node_prefs['procedure'] }} + WHEN fd.obj_type = 'trigger_function' THEN {{ show_node_prefs['trigger_function'] }} + WHEN fd.obj_type = 'edbfunc' THEN {{ show_node_prefs['edbfunc'] }} + WHEN fd.obj_type = 'edbproc' THEN {{ show_node_prefs['edbproc'] }} + ELSE NULL + END AS show_node, other_info + FROM ( + SELECT + CASE + WHEN t.typname IN ('trigger', 'event_trigger') THEN 'trigger_function' + WHEN pr.protype = '0'::char THEN + CASE WHEN np.oid IS NOT NULL THEN 'edbfunc' ELSE 'function' END + WHEN pr.protype = '1'::char THEN + CASE WHEN np.oid IS NOT NULL THEN 'edbproc' ELSE 'procedure' END + ELSE null + END::text AS obj_type, pr.proname AS obj_name, pr.oid AS obj_oid, n.oid AS schema_oid, n.nspname AS schema_name, np.oid next_schema_oid, np.nspname next_schema_name, + pg_catalog.pg_get_function_identity_arguments(pr.oid) AS other_info + FROM pg_proc pr left join pg_namespace n + ON pr.pronamespace = n.oid left JOIN pg_namespace np + ON np.oid=n.nspparent left JOIN pg_type t + ON t.oid = pr.prorettype left JOIN pg_language l + ON l.oid = pr.prolang + WHERE NOT (t.typname = 'trigger' AND l.lanname = 'edbspl') + AND ({{ CATALOGS.DB_SUPPORT('n') }} AND {{ CATALOGS.DB_SUPPORT('np') }}) + ) fd + {% if not all_obj %} + WHERE fd.obj_type = '{{ obj_type }}' + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['event_trigger'] %} + select 'event_trigger'::text AS obj_type, evtname AS obj_name, ':event_trigger.'||oid||':/' || evtname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info from pg_event_trigger +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['schema'] %} + select 'schema'::text AS obj_type, n.nspname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname as obj_path, n.nspname AS schema_name, + {{ show_node_prefs['schema'] }} AS show_node, NULL AS other_info from pg_namespace n + where n.nspparent = 0 + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['column'] %} + select 'column'::text AS obj_type, a.attname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname || '/' || + case + WHEN t.relkind = 'r' THEN ':table.' + WHEN t.relkind = 'v' THEN ':view.' + WHEN t.relkind = 'm' THEN ':mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname || '/:column.'|| a.attnum ||':/' || a.attname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['column'] }} AS show_node, NULL AS other_info + from pg_attribute a + inner join pg_class t on a.attrelid = t.oid and t.relkind in ('r','v','m') + left join pg_namespace n on t.relnamespace = n.oid where a.attnum > 0 +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['constraints', 'check_constraint', 'foreign_key', 'primary_key', 'unique_constraint', 'exclusion_constraint'] %} + SELECT + CASE + WHEN c.contype = 'c' THEN 'check_constraint' + WHEN c.contype = 'f' THEN 'foreign_key' + WHEN c.contype = 'p' THEN 'primary_key' + WHEN c.contype = 'u' THEN 'unique_constraint' + WHEN c.contype = 'x' THEN 'exclusion_constraint' + END::text AS obj_type, + case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_name, + ':schema.'||n.oid||':/' || n.nspname||'/'|| + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) || + CASE + WHEN c.contype = 'c' THEN '/:check_constraint.' ||c.oid + WHEN c.contype = 'f' THEN '/:foreign_key.' ||c.conindid + WHEN c.contype = 'p' THEN '/:primary_key.' ||c.conindid + WHEN c.contype = 'u' THEN '/:unique_constraint.' ||c.conindid + WHEN c.contype = 'x' THEN '/:exclusion_constraint.' ||c.conindid + END ||':/'|| case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['constraints'] }} AS show_node, NULL AS other_info + from pg_constraint c + left join pg_class t on c.conrelid = t.oid + left join pg_class tf on c.confrelid = tf.oid + left join pg_namespace n on t.relnamespace = n.oid + where c.contypid = 0 + {% if obj_type == 'check_constraint' %} + AND c.contype = 'c' + {% elif obj_type == 'foreign_key' %} + AND c.contype = 'f' + {% elif obj_type == 'primary_key' %} + AND c.contype = 'p' + {% elif obj_type == 'unique_constraint' %} + AND c.contype = 'u' + {% elif obj_type == 'exclusion_constraint' %} + AND c.contype = 'x' + {% else %} + AND c.contype IN ('c', 'f', 'p', 'u', 'x') + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['rule'] %} + select 'rule'::text AS obj_type, r.rulename AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| '/' || + case + when t.relkind = 'v' then ':view.' + when t.relkind = 'm' then ':mview.' + WHEN t.relkind in ('r', 'p') THEN + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) + end + ||'/:rule.'||r.oid||':/'|| r.rulename AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['rule'] }} AS show_node, NULL AS other_info + from pg_rewrite r + left join pg_class t on r.ev_class = t.oid + left join pg_namespace n on t.relnamespace = n.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger'] %} + select 'trigger'::text AS obj_type, tr.tgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| + case + WHEN t.relkind = 'r' THEN '/:table.' + when t.relkind = 'v' then '/:view.' + when t.relkind = 'm' then '/:mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname || '/:trigger.'|| tr.oid || ':/' || tr.tgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['trigger'] }} AS show_node, NULL AS other_info + from pg_trigger tr + left join pg_class t on tr.tgrelid = t.oid + left join pg_namespace n on t.relnamespace = n.oid + where tr.tgisinternal = false + and {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['type'] %} + SELECT 'type'::text AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || + '/:type.'|| t.oid ||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['type'] }} AS show_node, NULL AS other_info + FROM pg_type t + LEFT OUTER JOIN pg_type e ON e.oid=t.typelem + LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_namespace n on t.typnamespace = n.oid + WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' + {% if not show_system_objects %} + AND ct.oid is NULL + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['cast'] %} + SELECT 'cast'::text AS obj_type, format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_name, + ':cast.'||ca.oid||':/' || format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['cast'] }} AS show_node, NULL AS other_info + FROM pg_cast ca + JOIN pg_type st ON st.oid=castsource + JOIN pg_type tt ON tt.oid=casttarget + {% if not show_system_objects %} + WHERE ca.oid > {{last_system_oid}}::OID + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['language'] %} + SELECT 'language'::text AS obj_type, lanname AS obj_name, ':language.'||lan.oid||':/' || lanname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['language'] }} AS show_node, NULL AS other_info + FROM pg_language lan + WHERE lanispl IS TRUE +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_configuration'] %} + SELECT 'fts_configuration'::text AS obj_type, cfg.cfgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:fts_configuration.'||cfg.oid||':/' || cfg.cfgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['fts_configuration'] }} AS show_node, NULL AS other_info + FROM pg_ts_config cfg + left join pg_namespace n on cfg.cfgnamespace = n.oid + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_dictionary'] %} + SELECT 'fts_dictionary' AS obj_type, dict.dictname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_dictionary.'||dict.oid||':/' || dict.dictname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_dictionary'] }} AS show_node, NULL AS other_info + FROM pg_ts_dict dict + left join pg_namespace ns on dict.dictnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_parser'] %} + SELECT 'fts_parser' AS obj_type, prs.prsname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_parser.'||prs.oid||':/' || prs.prsname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_parser'] }} AS show_node, NULL AS other_info + FROM pg_ts_parser prs + left join pg_namespace ns on prs.prsnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_template'] %} + SELECT 'fts_template' AS obj_type, tmpl.tmplname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_template.'||tmpl.oid||':/' || tmpl.tmplname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_template'] }} AS show_node, NULL AS other_info + FROM pg_ts_template tmpl + left join pg_namespace ns on tmpl.tmplnamespace = ns.oid + AND {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain'] %} + select 'domain' AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['domain'] }} AS show_node, NULL AS other_info + from pg_type t + inner join pg_namespace n on t.typnamespace = n.oid + where t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain_constraints'] %} + SELECT 'domain_constraints' AS obj_type, + c.conname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname || '/:domain_constraints.'||c.oid||':/' || c.conname AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['domain_constraints'] }} AS show_node, NULL AS other_info + FROM pg_constraint c JOIN pg_type t + ON t.oid=contypid JOIN pg_namespace n + ON n.oid=t.typnamespace + WHERE t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_data_wrapper'] %} + select 'foreign_data_wrapper' AS obj_type, fdwname AS obj_name, ':foreign_data_wrapper.'||oid||':/' || fdwname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_data_wrapper'] }} AS show_node, NULL AS other_info + from pg_foreign_data_wrapper +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_server'] %} + select 'foreign_server' AS obj_type, sr.srvname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_server'] }} AS show_node, NULL AS other_info + from pg_foreign_server sr + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['user_mapping'] %} + select 'user_mapping' AS obj_type, ro.rolname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname || '/:user_mapping.'||ro.oid||':/' || ro.rolname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['user_mapping'] }} AS show_node, NULL AS other_info + from pg_user_mapping um + inner join pg_roles ro on um.umuser = ro.oid + inner join pg_foreign_server sr on um.umserver = sr.oid + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_table'] %} + select 'foreign_table' AS obj_type, c.relname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:foreign_table.'||c.oid||':/' || c.relname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['foreign_table'] }} AS show_node, NULL AS other_info + from pg_foreign_table ft + inner join pg_class c on ft.ftrelid = c.oid + inner join pg_namespace ns on c.relnamespace = ns.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['extension'] %} + select 'extension' AS obj_type, x.extname AS obj_name, ':extension.'||x.oid||':/' || x.extname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['extension'] }} AS show_node, NULL AS other_info + FROM pg_extension x + JOIN pg_namespace n on x.extnamespace=n.oid + join pg_available_extensions() e(name, default_version, comment) ON x.extname=e.name +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['collation'] %} + SELECT 'collation' AS obj_type, c.collname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:collation.'||c.oid||':/' || c.collname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['collation'] }} AS show_node, NULL AS other_info + FROM pg_collation c + JOIN pg_namespace n ON n.oid=c.collnamespace + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['synonym'] %} + SELECT 'synonym' AS obj_type, s.synname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:synonym.'||s.oid||':/' || s.synname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['synonym'] }} AS show_node, NULL AS other_info + FROM pg_synonym s + JOIN pg_namespace n ON n.oid=s.synnamespace + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['package'] %} + SELECT 'package' AS obj_type, p.nspname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:package.'||p.oid||':/' || p.nspname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['package'] }} AS show_node, NULL AS other_info + FROM pg_namespace p + JOIN pg_namespace n ON n.oid=p.nspparent + WHERE p.nspcompoundtrigger = false + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['edbvar'] %} + SELECT 'edbvar' AS obj_type, v.varname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname || '/:package.'||p.oid||':/' || p.nspname || '/:edbvar.'||v.oid||':/' || v.varname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['edbvar'] }} AS show_node, NULL AS other_info + FROM edb_variable v JOIN pg_namespace p + ON v.varpackage = p.oid JOIN pg_namespace n + ON p.nspparent = n.oid + WHERE p.nspcompoundtrigger = false + AND {{ CATALOGS.DB_SUPPORT('p') }} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +) sn +where lower(sn.obj_name) like '%{{ search_text }}%' +{% if not show_system_objects %} +AND NOT ({{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }}) +AND (sn.schema_name IS NOT NULL AND sn.schema_name NOT LIKE 'pg\_%') +{% endif %} +ORDER BY 1, 2, 3 diff --git a/web/pgadmin/tools/search_objects/templates/search_objects/sql/ppas/12_plus/search.sql b/web/pgadmin/tools/search_objects/templates/search_objects/sql/ppas/12_plus/search.sql new file mode 100644 index 000000000..82eb9cf6f --- /dev/null +++ b/web/pgadmin/tools/search_objects/templates/search_objects/sql/ppas/12_plus/search.sql @@ -0,0 +1,515 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +{% set all_obj = false %} +{% if obj_type == 'all' or obj_type is none %} +{% set all_obj = true %} +{% endif %} +SELECT obj_type, obj_name, + REPLACE(obj_path, '/'||sn.schema_name||'/', '/'||{{ CATALOGS.LABELS_SCHEMACOL('sn.schema_name', _) }}||'/') AS obj_path, + schema_name, show_node, other_info, + CASE + WHEN {{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }} THEN + CASE WHEN {{ CATALOGS.DB_SUPPORT_SCHEMACOL('sn.schema_name') }} THEN 'D' ELSE 'O' END + ELSE 'N' + END AS catalog_level +FROM ( +{% if all_obj or obj_type in ['sequence', 'view', 'mview'] %} + SELECT + CASE + WHEN c.relkind = 'S' THEN 'sequence' + WHEN c.relkind = 'v' THEN 'view' + WHEN c.relkind = 'm' THEN 'mview' + ELSE 'should not happen' + END::text AS obj_type, c.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || + CASE + WHEN c.relkind = 'S' THEN ':sequence.' + WHEN c.relkind = 'v' THEN ':view.' + WHEN c.relkind = 'm' THEN ':mview.' + ELSE 'should not happen' + END || c.oid ||':/' || c.relname AS obj_path, n.nspname AS schema_name, + CASE + WHEN c.relkind = 'S' THEN {{ show_node_prefs['sequence'] }} + WHEN c.relkind = 'v' THEN {{ show_node_prefs['view'] }} + WHEN c.relkind = 'm' THEN {{ show_node_prefs['mview'] }} + ELSE False + END AS show_node, NULL AS other_info + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + {% if all_obj %} + WHERE c.relkind in ('S','v','m') + {% elif obj_type == 'sequence' %} + WHERE c.relkind = 'S' + {% elif obj_type == 'view' %} + WHERE c.relkind = 'v' + {% elif obj_type == 'mview' %} + WHERE c.relkind = 'm' + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['table', 'partition'] %} + SELECT CASE WHEN c.relispartition THEN 'partition' ELSE 'table' END::text AS obj_type, c.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || ( + WITH RECURSIVE table_path_data as ( + select c.oid as oid, 0 as height, + CASE c.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || c.oid || ':/' || c.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) obj_path, n.nspname AS schema_name, + CASE WHEN c.relispartition THEN {{ show_node_prefs['partition'] }} + ELSE {{ show_node_prefs['table'] }} END AS show_node, + NULL AS other_info + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE c.relkind in ('p','r') + {% if obj_type == 'table' %} + AND NOT c.relispartition + {% elif obj_type == 'partition' %} + AND c.relispartition + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['index'] %} + SELECT 'index'::text AS obj_type, cls.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/:table.'|| tab.oid ||':/' || tab.relname || '/:index.'|| cls.oid ||':/' || cls.relname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info + FROM pg_index idx + JOIN pg_class cls ON cls.oid=indexrelid + JOIN pg_class tab ON tab.oid=indrelid + JOIN pg_namespace n ON n.oid=tab.relnamespace + LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i') + LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) + LEFT OUTER JOIN pg_description des ON des.objoid=cls.oid + LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) + WHERE contype IS NULL +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger_function', 'function', 'procedure', 'edbfunc', 'edbproc'] %} + SELECT fd.obj_type, fd.obj_name, + CASE + WHEN fd.obj_type = 'function' THEN + ':schema.'|| fd.schema_oid || ':/' || fd.schema_name || '/:function.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'procedure' THEN + ':schema.'|| fd.schema_oid || ':/' || fd.schema_name || '/:procedure.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'trigger_function' THEN + ':schema.'|| fd.schema_oid || ':/' || fd.schema_name || '/:trigger_function.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'edbfunc' THEN + ':schema.'|| fd.next_schema_oid || ':/' || fd.next_schema_name || '/:package.'|| fd.schema_oid || ':/' || fd.schema_name || '/:edbfunc.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'edbproc' THEN + ':schema.'|| fd.next_schema_oid || ':/' || fd.next_schema_name || '/:package.'|| fd.schema_oid || ':/' || fd.schema_name || '/:edbproc.' || fd.obj_oid ||':/' || fd.obj_name + ELSE NULL + END AS obj_path, + CASE + WHEN fd.obj_type IN ('function', 'procedure', 'trigger_function') THEN fd.schema_name + WHEN fd.obj_type IN ('edbfunc', 'edbproc') THEN fd.next_schema_name + ELSE NULL + END AS schema_name, + CASE + WHEN fd.obj_type = 'function' THEN {{ show_node_prefs['function'] }} + WHEN fd.obj_type = 'procedure' THEN {{ show_node_prefs['procedure'] }} + WHEN fd.obj_type = 'trigger_function' THEN {{ show_node_prefs['trigger_function'] }} + WHEN fd.obj_type = 'edbfunc' THEN {{ show_node_prefs['edbfunc'] }} + WHEN fd.obj_type = 'edbproc' THEN {{ show_node_prefs['edbproc'] }} + ELSE NULL + END AS show_node, other_info + FROM ( + SELECT + CASE + WHEN t.typname IN ('trigger', 'event_trigger') THEN 'trigger_function' + WHEN pr.protype = '0'::char THEN + CASE WHEN np.oid IS NOT NULL THEN 'edbfunc' ELSE 'function' END + WHEN pr.protype = '1'::char THEN + CASE WHEN np.oid IS NOT NULL THEN 'edbproc' ELSE 'procedure' END + ELSE null + END::text AS obj_type, pr.proname AS obj_name, pr.oid AS obj_oid, n.oid AS schema_oid, n.nspname AS schema_name, np.oid next_schema_oid, np.nspname next_schema_name, + pg_catalog.pg_get_function_identity_arguments(pr.oid) AS other_info + FROM pg_proc pr left join pg_namespace n + ON pr.pronamespace = n.oid left JOIN pg_namespace np + ON np.oid=n.nspparent left JOIN pg_type t + ON t.oid = pr.prorettype left JOIN pg_language l + ON l.oid = pr.prolang + WHERE NOT (t.typname = 'trigger' AND l.lanname = 'edbspl') + AND ({{ CATALOGS.DB_SUPPORT('n') }} AND {{ CATALOGS.DB_SUPPORT('np') }}) + ) fd + {% if not all_obj %} + WHERE fd.obj_type = '{{ obj_type }}' + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['event_trigger'] %} + select 'event_trigger'::text AS obj_type, evtname AS obj_name, ':event_trigger.'||oid||':/' || evtname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info from pg_event_trigger +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['schema'] %} + select 'schema'::text AS obj_type, n.nspname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname as obj_path, n.nspname AS schema_name, + {{ show_node_prefs['schema'] }} AS show_node, NULL AS other_info from pg_namespace n + where n.nspparent = 0 + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['column'] %} + select 'column'::text AS obj_type, a.attname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname || '/' || + case + WHEN t.relkind = 'r' THEN ':table.' + WHEN t.relkind = 'v' THEN ':view.' + WHEN t.relkind = 'm' THEN ':mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname || '/:column.'|| a.attnum ||':/' || a.attname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['column'] }} AS show_node, NULL AS other_info + from pg_attribute a + inner join pg_class t on a.attrelid = t.oid and t.relkind in ('r','v','m') + left join pg_namespace n on t.relnamespace = n.oid where a.attnum > 0 +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['constraints', 'check_constraint', 'foreign_key', 'primary_key', 'unique_constraint', 'exclusion_constraint'] %} + SELECT + CASE + WHEN c.contype = 'c' THEN 'check_constraint' + WHEN c.contype = 'f' THEN 'foreign_key' + WHEN c.contype = 'p' THEN 'primary_key' + WHEN c.contype = 'u' THEN 'unique_constraint' + WHEN c.contype = 'x' THEN 'exclusion_constraint' + END::text AS obj_type, + case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_name, + ':schema.'||n.oid||':/' || n.nspname||'/'|| + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) || + CASE + WHEN c.contype = 'c' THEN '/:check_constraint.' ||c.oid + WHEN c.contype = 'f' THEN '/:foreign_key.' ||c.conindid + WHEN c.contype = 'p' THEN '/:primary_key.' ||c.conindid + WHEN c.contype = 'u' THEN '/:unique_constraint.' ||c.conindid + WHEN c.contype = 'x' THEN '/:exclusion_constraint.' ||c.conindid + END ||':/'|| case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['constraints'] }} AS show_node, NULL AS other_info + from pg_constraint c + left join pg_class t on c.conrelid = t.oid + left join pg_class tf on c.confrelid = tf.oid + left join pg_namespace n on t.relnamespace = n.oid + where c.contypid = 0 + {% if obj_type == 'check_constraint' %} + AND c.contype = 'c' + {% elif obj_type == 'foreign_key' %} + AND c.contype = 'f' + {% elif obj_type == 'primary_key' %} + AND c.contype = 'p' + {% elif obj_type == 'unique_constraint' %} + AND c.contype = 'u' + {% elif obj_type == 'exclusion_constraint' %} + AND c.contype = 'x' + {% else %} + AND c.contype IN ('c', 'f', 'p', 'u', 'x') + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['rule'] %} + select 'rule'::text AS obj_type, r.rulename AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| '/' || + case + when t.relkind = 'v' then ':view.' + when t.relkind = 'm' then ':mview.' + WHEN t.relkind in ('r', 'p') THEN + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) + end + ||'/:rule.'||r.oid||':/'|| r.rulename AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['rule'] }} AS show_node, NULL AS other_info + from pg_rewrite r + left join pg_class t on r.ev_class = t.oid + left join pg_namespace n on t.relnamespace = n.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger', 'compound_trigger'] %} + select + CASE WHEN tr.tgpackageoid != 0 THEN 'compound_trigger' ELSE 'trigger' END::text AS obj_type, tr.tgname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname|| '/' || + case + when t.relkind = 'v' then ':view.' + when t.relkind = 'm' then ':mview.' + WHEN t.relkind in ('r', 'p') THEN + ( + WITH RECURSIVE table_path_data as ( + select t.oid as oid, 0 as height, + CASE t.relispartition WHEN true THEN ':partition.' ELSE ':table.' END || t.oid || ':/' || t.relname as path + union + select rel.oid, pt.height+1 as height, + CASE rel.relispartition WHEN true THEN ':partition.' ELSE ':table.' END + || rel.oid || ':/' || rel.relname || '/' || pt.path as path + from pg_class rel JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid + join pg_inherits inh ON inh.inhparent = rel.oid + join table_path_data pt ON inh.inhrelid = pt.oid + ) + select path from table_path_data order by height desc limit 1 + ) + end || CASE WHEN tr.tgpackageoid != 0 THEN '/:compound_trigger.' ELSE '/:trigger.' END || tr.oid || ':/' || tr.tgname AS obj_path, n.nspname AS schema_name, + CASE WHEN tr.tgpackageoid != 0 THEN {{ show_node_prefs['compound_trigger'] }} ELSE {{ show_node_prefs['trigger'] }} END AS show_node, + NULL AS other_info + from pg_trigger tr + left join pg_class t on tr.tgrelid = t.oid + left join pg_namespace n on t.relnamespace = n.oid + where tr.tgisinternal = false + and {{ CATALOGS.DB_SUPPORT('n') }} + {% if obj_type == 'compound_trigger' %} + AND tr.tgpackageoid != 0 + {% elif obj_type == 'trigger' %} + AND tr.tgpackageoid = 0 + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['type'] %} + SELECT 'type'::text AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || + '/:type.'|| t.oid ||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['type'] }} AS show_node, NULL AS other_info + FROM pg_type t + LEFT OUTER JOIN pg_type e ON e.oid=t.typelem + LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_namespace n on t.typnamespace = n.oid + WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' + {% if not show_system_objects %} + AND ct.oid is NULL + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['cast'] %} + SELECT 'cast'::text AS obj_type, format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_name, + ':cast.'||ca.oid||':/' || format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['cast'] }} AS show_node, NULL AS other_info + FROM pg_cast ca + JOIN pg_type st ON st.oid=castsource + JOIN pg_type tt ON tt.oid=casttarget + {% if not show_system_objects %} + WHERE ca.oid > {{last_system_oid}}::OID + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['language'] %} + SELECT 'language'::text AS obj_type, lanname AS obj_name, ':language.'||lan.oid||':/' || lanname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['language'] }} AS show_node, NULL AS other_info + FROM pg_language lan + WHERE lanispl IS TRUE +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_configuration'] %} + SELECT 'fts_configuration'::text AS obj_type, cfg.cfgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:fts_configuration.'||cfg.oid||':/' || cfg.cfgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['fts_configuration'] }} AS show_node, NULL AS other_info + FROM pg_ts_config cfg + left join pg_namespace n on cfg.cfgnamespace = n.oid + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_dictionary'] %} + SELECT 'fts_dictionary'::text AS obj_type, dict.dictname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_dictionary.'||dict.oid||':/' || dict.dictname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_dictionary'] }} AS show_node, NULL AS other_info + FROM pg_ts_dict dict + left join pg_namespace ns on dict.dictnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_parser'] %} + SELECT 'fts_parser'::text AS obj_type, prs.prsname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_parser.'||prs.oid||':/' || prs.prsname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_parser'] }} AS show_node, NULL AS other_info + FROM pg_ts_parser prs + left join pg_namespace ns on prs.prsnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_template'] %} + SELECT 'fts_template'::text AS obj_type, tmpl.tmplname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_template.'||tmpl.oid||':/' || tmpl.tmplname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_template'] }} AS show_node, NULL AS other_info + FROM pg_ts_template tmpl + left join pg_namespace ns on tmpl.tmplnamespace = ns.oid + AND {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain'] %} + select 'domain'::text AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['domain'] }} AS show_node, NULL AS other_info + from pg_type t + inner join pg_namespace n on t.typnamespace = n.oid + where t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain_constraints'] %} + SELECT 'domain_constraints'::text AS obj_type, + c.conname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname || '/:domain_constraints.'||c.oid||':/' || c.conname AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['domain_constraints'] }} AS show_node, NULL AS other_info + FROM pg_constraint c JOIN pg_type t + ON t.oid=contypid JOIN pg_namespace n + ON n.oid=t.typnamespace + WHERE t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_data_wrapper'] %} + select 'foreign_data_wrapper'::text AS obj_type, fdwname AS obj_name, ':foreign_data_wrapper.'||oid||':/' || fdwname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_data_wrapper'] }} AS show_node, NULL AS other_info + from pg_foreign_data_wrapper +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_server'] %} + select 'foreign_server' AS obj_type, sr.srvname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_server'] }} AS show_node, NULL AS other_info + from pg_foreign_server sr + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['user_mapping'] %} + select 'user_mapping' AS obj_type, ro.rolname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname || '/:user_mapping.'||ro.oid||':/' || ro.rolname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['user_mapping'] }} AS show_node, NULL AS other_info + from pg_user_mapping um + inner join pg_roles ro on um.umuser = ro.oid + inner join pg_foreign_server sr on um.umserver = sr.oid + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_table'] %} + select 'foreign_table' AS obj_type, c.relname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:foreign_table.'||c.oid||':/' || c.relname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['foreign_table'] }} AS show_node, NULL AS other_info + from pg_foreign_table ft + inner join pg_class c on ft.ftrelid = c.oid + inner join pg_namespace ns on c.relnamespace = ns.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['extension'] %} + select 'extension' AS obj_type, x.extname AS obj_name, ':extension.'||x.oid||':/' || x.extname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['extension'] }} AS show_node, NULL AS other_info + FROM pg_extension x + JOIN pg_namespace n on x.extnamespace=n.oid + join pg_available_extensions() e(name, default_version, comment) ON x.extname=e.name +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['collation'] %} + SELECT 'collation' AS obj_type, c.collname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:collation.'||c.oid||':/' || c.collname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['collation'] }} AS show_node, NULL AS other_info + FROM pg_collation c + JOIN pg_namespace n ON n.oid=c.collnamespace + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['synonym'] %} + SELECT 'synonym' AS obj_type, s.synname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:synonym.'||s.oid||':/' || s.synname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['synonym'] }} AS show_node, NULL AS other_info + FROM pg_synonym s + JOIN pg_namespace n ON n.oid=s.synnamespace + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['package'] %} + SELECT 'package' AS obj_type, p.nspname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:package.'||p.oid||':/' || p.nspname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['package'] }} AS show_node, NULL AS other_info + FROM pg_namespace p + JOIN pg_namespace n ON n.oid=p.nspparent + WHERE p.nspcompoundtrigger = false + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['edbvar'] %} + SELECT 'edbvar' AS obj_type, v.varname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname || '/:package.'||p.oid||':/' || p.nspname || '/:edbvar.'||v.oid||':/' || v.varname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['edbvar'] }} AS show_node, NULL AS other_info + FROM edb_variable v JOIN pg_namespace p + ON v.varpackage = p.oid JOIN pg_namespace n + ON p.nspparent = n.oid + WHERE p.nspcompoundtrigger = false + AND {{ CATALOGS.DB_SUPPORT('p') }} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +) sn +where lower(sn.obj_name) like '%{{ search_text }}%' +{% if not show_system_objects %} +AND NOT ({{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }}) +AND (sn.schema_name IS NOT NULL AND sn.schema_name NOT LIKE 'pg\_%') +{% endif %} +ORDER BY 1, 2, 3 diff --git a/web/pgadmin/tools/search_objects/templates/search_objects/sql/ppas/default/search.sql b/web/pgadmin/tools/search_objects/templates/search_objects/sql/ppas/default/search.sql new file mode 100644 index 000000000..7bb126e32 --- /dev/null +++ b/web/pgadmin/tools/search_objects/templates/search_objects/sql/ppas/default/search.sql @@ -0,0 +1,437 @@ +{% import 'catalog/ppas/macros/catalogs.sql' as CATALOGS %} +{% set all_obj = false %} +{% if obj_type == 'all' or obj_type is none %} +{% set all_obj = true %} +{% endif %} +SELECT obj_type, obj_name, + REPLACE(obj_path, '/'||sn.schema_name||'/', '/'||{{ CATALOGS.LABELS_SCHEMACOL('sn.schema_name', _) }}||'/') AS obj_path, + schema_name, show_node, other_info, + CASE + WHEN {{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }} THEN + CASE WHEN {{ CATALOGS.DB_SUPPORT_SCHEMACOL('sn.schema_name') }} THEN 'D' ELSE 'O' END + ELSE 'N' + END AS catalog_level +FROM ( +{% if all_obj or obj_type in ['table', 'sequence', 'view', 'mview'] %} + SELECT + CASE + WHEN c.relkind = 'r' THEN 'table' + WHEN c.relkind = 'S' THEN 'sequence' + WHEN c.relkind = 'v' THEN 'view' + WHEN c.relkind = 'm' THEN 'mview' + ELSE 'should not happen' + END::text::text AS obj_type, c.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/' || + CASE + WHEN c.relkind = 'r' THEN ':table.' + WHEN c.relkind = 'S' THEN ':sequence.' + WHEN c.relkind = 'v' THEN ':view.' + WHEN c.relkind = 'm' THEN ':mview.' + ELSE 'should not happen' + END || c.oid ||':/' || c.relname AS obj_path, n.nspname AS schema_name, + CASE + WHEN c.relkind = 'r' THEN {{ show_node_prefs['table'] }} + WHEN c.relkind = 'S' THEN {{ show_node_prefs['sequence'] }} + WHEN c.relkind = 'v' THEN {{ show_node_prefs['view'] }} + WHEN c.relkind = 'm' THEN {{ show_node_prefs['mview'] }} + ELSE False + END AS show_node, NULL AS other_info + FROM pg_class c + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + {% if all_obj %} + WHERE c.relkind in ('r','S','v','m') + {% elif obj_type == 'table' %} + WHERE c.relkind = 'r' + {% elif obj_type == 'sequence' %} + WHERE c.relkind = 'S' + {% elif obj_type == 'view' %} + WHERE c.relkind = 'v' + {% elif obj_type == 'mview' %} + WHERE c.relkind = 'm' + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['index'] %} + SELECT 'index'::text::text AS obj_type, cls.relname AS obj_name, + ':schema.'|| n.oid || ':/' || n.nspname || '/:table.'|| tab.oid ||':/' || tab.relname || '/:index.'|| cls.oid ||':/' || cls.relname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info + FROM pg_index idx + JOIN pg_class cls ON cls.oid=indexrelid + JOIN pg_class tab ON tab.oid=indrelid + JOIN pg_namespace n ON n.oid=tab.relnamespace + LEFT JOIN pg_depend dep ON (dep.classid = cls.tableoid AND dep.objid = cls.oid AND dep.refobjsubid = '0' AND dep.refclassid=(SELECT oid FROM pg_class WHERE relname='pg_constraint') AND dep.deptype='i') + LEFT OUTER JOIN pg_constraint con ON (con.tableoid = dep.refclassid AND con.oid = dep.refobjid) + LEFT OUTER JOIN pg_description des ON des.objoid=cls.oid + LEFT OUTER JOIN pg_description desp ON (desp.objoid=con.oid AND desp.objsubid = 0) + WHERE contype IS NULL +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger_function', 'function', 'procedure', 'edbfunc', 'edbproc'] %} + SELECT fd.obj_type, fd.obj_name, + CASE + WHEN fd.obj_type = 'function' THEN + ':schema.'|| fd.schema_oid || ':/' || fd.schema_name || '/:function.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'procedure' THEN + ':schema.'|| fd.schema_oid || ':/' || fd.schema_name || '/:procedure.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'trigger_function' THEN + ':schema.'|| fd.schema_oid || ':/' || fd.schema_name || '/:trigger_function.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'edbfunc' THEN + ':schema.'|| fd.next_schema_oid || ':/' || fd.next_schema_name || '/:package.'|| fd.schema_oid || ':/' || fd.schema_name || '/:edbfunc.' || fd.obj_oid ||':/' || fd.obj_name + WHEN fd.obj_type = 'edbproc' THEN + ':schema.'|| fd.next_schema_oid || ':/' || fd.next_schema_name || '/:package.'|| fd.schema_oid || ':/' || fd.schema_name || '/:edbproc.' || fd.obj_oid ||':/' || fd.obj_name + ELSE NULL + END AS obj_path, + CASE + WHEN fd.obj_type IN ('function', 'procedure', 'trigger_function') THEN fd.schema_name + WHEN fd.obj_type IN ('edbfunc', 'edbproc') THEN fd.next_schema_name + ELSE NULL + END AS schema_name, + CASE + WHEN fd.obj_type = 'function' THEN {{ show_node_prefs['function'] }} + WHEN fd.obj_type = 'procedure' THEN {{ show_node_prefs['procedure'] }} + WHEN fd.obj_type = 'trigger_function' THEN {{ show_node_prefs['trigger_function'] }} + WHEN fd.obj_type = 'edbfunc' THEN {{ show_node_prefs['edbfunc'] }} + WHEN fd.obj_type = 'edbproc' THEN {{ show_node_prefs['edbproc'] }} + ELSE NULL + END AS show_node, other_info + FROM ( + SELECT + CASE + WHEN t.typname IN ('trigger', 'event_trigger') THEN 'trigger_function' + WHEN pr.protype = '0'::char THEN + CASE WHEN np.oid IS NOT NULL THEN 'edbfunc' ELSE 'function' END + WHEN pr.protype = '1'::char THEN + CASE WHEN np.oid IS NOT NULL THEN 'edbproc' ELSE 'procedure' END + ELSE null + END::text::text AS obj_type, pr.proname AS obj_name, pr.oid AS obj_oid, n.oid AS schema_oid, n.nspname AS schema_name, np.oid next_schema_oid, np.nspname next_schema_name, + pg_catalog.pg_get_function_identity_arguments(pr.oid) AS other_info + FROM pg_proc pr left join pg_namespace n + ON pr.pronamespace = n.oid left JOIN pg_namespace np + ON np.oid=n.nspparent left JOIN pg_type t + ON t.oid = pr.prorettype left JOIN pg_language l + ON l.oid = pr.prolang + WHERE NOT (t.typname = 'trigger' AND l.lanname = 'edbspl') + AND ({{ CATALOGS.DB_SUPPORT('n') }} AND {{ CATALOGS.DB_SUPPORT('np') }}) + ) fd + {% if not all_obj %} + WHERE fd.obj_type = '{{ obj_type }}' + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['event_trigger'] %} + select 'event_trigger'::text::text AS obj_type, evtname AS obj_name, ':event_trigger.'||oid||':/' || evtname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['index'] }} AS show_node, NULL AS other_info from pg_event_trigger +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['schema'] %} + select 'schema'::text::text AS obj_type, n.nspname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname as obj_path, n.nspname AS schema_name, + {{ show_node_prefs['schema'] }} AS show_node, NULL AS other_info from pg_namespace n + where n.nspparent = 0 + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['column'] %} + select 'column'::text::text AS obj_type, a.attname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname || '/' || + case + WHEN t.relkind = 'r' THEN ':table.' + WHEN t.relkind = 'v' THEN ':view.' + WHEN t.relkind = 'm' THEN ':mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname || '/:column.'|| a.attnum ||':/' || a.attname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['column'] }} AS show_node, NULL AS other_info + from pg_attribute a + inner join pg_class t on a.attrelid = t.oid and t.relkind in ('r','v','m') + left join pg_namespace n on t.relnamespace = n.oid where a.attnum > 0 +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['constraints', 'check_constraint', 'foreign_key', 'primary_key', 'unique_constraint', 'exclusion_constraint'] %} + SELECT + CASE + WHEN c.contype = 'c' THEN 'check_constraint' + WHEN c.contype = 'f' THEN 'foreign_key' + WHEN c.contype = 'p' THEN 'primary_key' + WHEN c.contype = 'u' THEN 'unique_constraint' + WHEN c.contype = 'x' THEN 'exclusion_constraint' + END::text::text AS obj_type, + case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_name, + ':schema.'||n.oid||':/' || n.nspname||'/:table.'|| t.oid || ':/'||t.relname|| + CASE + WHEN c.contype = 'c' THEN '/:check_constraint.' ||c.oid + WHEN c.contype = 'f' THEN '/:foreign_key.' ||c.conindid + WHEN c.contype = 'p' THEN '/:primary_key.' ||c.conindid + WHEN c.contype = 'u' THEN '/:unique_constraint.' ||c.conindid + WHEN c.contype = 'x' THEN '/:exclusion_constraint.' ||c.conindid + END ||':/'|| case when tf.relname is null then c.conname else c.conname || ' -> ' || tf.relname end AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['constraints'] }} AS show_node, NULL AS other_info + from pg_constraint c + left join pg_class t on c.conrelid = t.oid + left join pg_class tf on c.confrelid = tf.oid + left join pg_namespace n on t.relnamespace = n.oid + where c.contypid = 0 + {% if obj_type == 'check_constraint' %} + AND c.contype = 'c' + {% elif obj_type == 'foreign_key' %} + AND c.contype = 'f' + {% elif obj_type == 'primary_key' %} + AND c.contype = 'p' + {% elif obj_type == 'unique_constraint' %} + AND c.contype = 'u' + {% elif obj_type == 'exclusion_constraint' %} + AND c.contype = 'x' + {% else %} + AND c.contype IN ('c', 'f', 'p', 'u', 'x') + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['rule'] %} + select 'rule'::text::text AS obj_type, r.rulename AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| + case + WHEN t.relkind = 'r' THEN '/:table.' + when t.relkind = 'v' then '/:view.' + when t.relkind = 'm' then '/:mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname ||'/:rule.'||r.oid||':/'|| r.rulename AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['rule'] }} AS show_node, NULL AS other_info + from pg_rewrite r + left join pg_class t on r.ev_class = t.oid + left join pg_namespace n on t.relnamespace = n.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['trigger'] %} + select 'trigger'::text::text AS obj_type, tr.tgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname|| + case + WHEN t.relkind = 'r' THEN '/:table.' + when t.relkind = 'v' then '/:view.' + when t.relkind = 'm' then '/:mview.' + else 'should not happen' + end || t.oid || ':/' || t.relname || '/:trigger.'|| tr.oid || ':/' || tr.tgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['trigger'] }} AS show_node, NULL AS other_info + from pg_trigger tr + left join pg_class t on tr.tgrelid = t.oid + left join pg_namespace n on t.relnamespace = n.oid + where tr.tgisinternal = false + and {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['type'] %} + SELECT 'type'::text::text AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || + '/:type.'|| t.oid ||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['type'] }} AS show_node, NULL AS other_info + FROM pg_type t + LEFT OUTER JOIN pg_type e ON e.oid=t.typelem + LEFT OUTER JOIN pg_class ct ON ct.oid=t.typrelid AND ct.relkind <> 'c' + LEFT OUTER JOIN pg_namespace n on t.typnamespace = n.oid + WHERE t.typtype != 'd' AND t.typname NOT LIKE E'\\_%' + {% if not show_system_objects %} + AND ct.oid is NULL + {% endif %} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['cast'] %} + SELECT 'cast'::text::text AS obj_type, format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_name, + ':cast.'||ca.oid||':/' || format_type(st.oid,NULL) ||'->'|| format_type(tt.oid,tt.typtypmod) AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['cast'] }} AS show_node, NULL AS other_info + FROM pg_cast ca + JOIN pg_type st ON st.oid=castsource + JOIN pg_type tt ON tt.oid=casttarget + {% if not show_system_objects %} + WHERE ca.oid > {{last_system_oid}}::OID + {% endif %} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['language'] %} + SELECT 'language'::text::text AS obj_type, lanname AS obj_name, ':language.'||lan.oid||':/' || lanname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['language'] }} AS show_node, NULL AS other_info + FROM pg_language lan + WHERE lanispl IS TRUE +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_configuration'] %} + SELECT 'fts_configuration'::text::text AS obj_type, cfg.cfgname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:fts_configuration.'||cfg.oid||':/' || cfg.cfgname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['fts_configuration'] }} AS show_node, NULL AS other_info + FROM pg_ts_config cfg + left join pg_namespace n on cfg.cfgnamespace = n.oid + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_dictionary'] %} + SELECT 'fts_dictionary'::text::text AS obj_type, dict.dictname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_dictionary.'||dict.oid||':/' || dict.dictname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_dictionary'] }} AS show_node, NULL AS other_info + FROM pg_ts_dict dict + left join pg_namespace ns on dict.dictnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_parser'] %} + SELECT 'fts_parser'::text::text AS obj_type, prs.prsname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_parser.'||prs.oid||':/' || prs.prsname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_parser'] }} AS show_node, NULL AS other_info + FROM pg_ts_parser prs + left join pg_namespace ns on prs.prsnamespace = ns.oid + WHERE {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['fts_template'] %} + SELECT 'fts_template'::text::text AS obj_type, tmpl.tmplname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:fts_template.'||tmpl.oid||':/' || tmpl.tmplname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['fts_template'] }} AS show_node, NULL AS other_info + FROM pg_ts_template tmpl + left join pg_namespace ns on tmpl.tmplnamespace = ns.oid + AND {{ CATALOGS.DB_SUPPORT('ns') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain'] %} + select 'domain'::text::text AS obj_type, t.typname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['domain'] }} AS show_node, NULL AS other_info + from pg_type t + inner join pg_namespace n on t.typnamespace = n.oid + where t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['domain_constraints'] %} + SELECT 'domain_constraints'::text::text AS obj_type, + c.conname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:domain.'||t.oid||':/' || t.typname || '/:domain_constraints.'||c.oid||':/' || c.conname AS obj_path, + n.nspname AS schema_name, + {{ show_node_prefs['domain_constraints'] }} AS show_node, NULL AS other_info + FROM pg_constraint c JOIN pg_type t + ON t.oid=contypid JOIN pg_namespace n + ON n.oid=t.typnamespace + WHERE t.typtype = 'd' + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_data_wrapper'] %} + select 'foreign_data_wrapper'::text AS obj_type, fdwname AS obj_name, ':foreign_data_wrapper.'||oid||':/' || fdwname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_data_wrapper'] }} AS show_node, NULL AS other_info + from pg_foreign_data_wrapper +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_server'] %} + select 'foreign_server'::text AS obj_type, sr.srvname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['foreign_server'] }} AS show_node, NULL AS other_info + from pg_foreign_server sr + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['user_mapping'] %} + select 'user_mapping'::text AS obj_type, ro.rolname AS obj_name, ':foreign_data_wrapper.'||fdw.oid||':/' || fdw.fdwname || '/:foreign_server.'||sr.oid||':/' || sr.srvname || '/:user_mapping.'||ro.oid||':/' || ro.rolname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['user_mapping'] }} AS show_node, NULL AS other_info + from pg_user_mapping um + inner join pg_roles ro on um.umuser = ro.oid + inner join pg_foreign_server sr on um.umserver = sr.oid + inner join pg_foreign_data_wrapper fdw on sr.srvfdw = fdw.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['foreign_table'] %} + select 'foreign_table'::text AS obj_type, c.relname AS obj_name, ':schema.'||ns.oid||':/' || ns.nspname || '/:foreign_table.'||c.oid||':/' || c.relname AS obj_path, ns.nspname AS schema_name, + {{ show_node_prefs['foreign_table'] }} AS show_node, NULL AS other_info + from pg_foreign_table ft + inner join pg_class c on ft.ftrelid = c.oid + inner join pg_namespace ns on c.relnamespace = ns.oid +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['extension'] %} + select 'extension'::text AS obj_type, x.extname AS obj_name, ':extension.'||x.oid||':/' || x.extname AS obj_path, ''::text AS schema_name, + {{ show_node_prefs['extension'] }} AS show_node, NULL AS other_info + FROM pg_extension x + JOIN pg_namespace n on x.extnamespace=n.oid + join pg_available_extensions() e(name, default_version, comment) ON x.extname=e.name +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['collation'] %} + SELECT 'collation'::text AS obj_type, c.collname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:collation.'||c.oid||':/' || c.collname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['collation'] }} AS show_node, NULL AS other_info + FROM pg_collation c + JOIN pg_namespace n ON n.oid=c.collnamespace + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['synonym'] %} + SELECT 'synonym'::text AS obj_type, s.synname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:synonym.'||s.oid||':/' || s.synname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['synonym'] }} AS show_node, NULL AS other_info + FROM pg_synonym s + JOIN pg_namespace n ON n.oid=s.synnamespace + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['package'] %} + SELECT 'package'::text AS obj_type, p.nspname AS obj_name, ':schema.'||n.oid||':/' || n.nspname || '/:package.'||p.oid||':/' || p.nspname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['package'] }} AS show_node, NULL AS other_info + FROM pg_namespace p + JOIN pg_namespace n ON n.oid=p.nspparent + WHERE {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +{% if all_obj %} + UNION +{% endif %} +{% if all_obj or obj_type in ['edbvar'] %} + SELECT 'edbvar'::text AS obj_type, v.varname AS obj_name, + ':schema.'||n.oid||':/' || n.nspname || '/:package.'||p.oid||':/' || p.nspname || '/:edbvar.'||v.oid||':/' || v.varname AS obj_path, n.nspname AS schema_name, + {{ show_node_prefs['edbvar'] }} AS show_node, NULL AS other_info + FROM edb_variable v JOIN pg_namespace p + ON v.varpackage = p.oid JOIN pg_namespace n + ON p.nspparent = n.oid + WHERE {{ CATALOGS.DB_SUPPORT('p') }} + AND {{ CATALOGS.DB_SUPPORT('n') }} +{% endif %} +) sn +where lower(sn.obj_name) like '%{{ search_text }}%' +{% if not show_system_objects %} +AND NOT ({{ CATALOGS.IS_CATALOG_SCHEMA('sn.schema_name') }}) +AND (sn.schema_name IS NOT NULL AND sn.schema_name NOT LIKE 'pg\_%') +{% endif %} +ORDER BY 1, 2, 3 diff --git a/web/pgadmin/tools/search_objects/tests/__init__.py b/web/pgadmin/tools/search_objects/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/web/pgadmin/tools/search_objects/tests/test_api_search.py b/web/pgadmin/tools/search_objects/tests/test_api_search.py new file mode 100644 index 000000000..3952006d2 --- /dev/null +++ b/web/pgadmin/tools/search_objects/tests/test_api_search.py @@ -0,0 +1,75 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2020, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +from __future__ import print_function +import sys +import json + +from pgadmin.utils.route import BaseTestGenerator +from regression import parent_node_dict +from pgadmin.browser.server_groups.servers.databases.tests import utils as \ + database_utils +from regression.python_test_utils import test_utils as utils + +try: + from urllib import urlencode +except Exception as e: + from urllib.parse import urlencode + + +class SearchObjectsApiSearch(BaseTestGenerator): + """ This class will test search API of search objects. """ + scenarios = [ + ('Search with all types', dict(text='emp', type='all', singles=False)), + ('Search with None types', dict(text='emp', type=None, singles=False)), + ('Search for all single types', + dict(text='emp', type=None, singles=True)), + ] + + def runFor(self, text=None, type=None): + url_params = dict( + text=text + ) + if type is not None: + url_params['type'] = type + + url_params = urlencode(url_params) + response = self.tester.get(self.base_url + '?' + url_params) + + self.assertEquals(response.status_code, 200) + + def runTest(self): + database_info = parent_node_dict["database"][-1] + server_id = database_info["server_id"] + db_id = database_info["db_id"] + + db_con = database_utils.connect_database(self, + utils.SERVER_GROUP, + server_id, + db_id) + if not db_con["info"] == "Database connected.": + raise Exception("Could not connect to database to add the schema.") + + self.base_url = '/search_objects/search/' \ + + str(server_id) + '/' + str(db_id) + + if not self.singles: + self.runFor(text=self.text, type=self.type) + else: + # test for all the node types individually + types_url = '/search_objects/types/' +\ + str(server_id) + '/' + str(db_id) + response = self.tester.get(types_url) + self.assertEquals(response.status_code, 200) + types_data = json.loads(response.data.decode('utf-8'))['data'] + + for a_type in types_data: + print('Running search for type {0}'.format(a_type), + file=sys.stderr) + self.runFor(text=self.text, type=a_type) diff --git a/web/pgadmin/tools/search_objects/tests/test_api_types.py b/web/pgadmin/tools/search_objects/tests/test_api_types.py new file mode 100644 index 000000000..aed5571ad --- /dev/null +++ b/web/pgadmin/tools/search_objects/tests/test_api_types.py @@ -0,0 +1,47 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2020, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +import json +from pgadmin.utils.route import BaseTestGenerator +from regression import parent_node_dict +from pgadmin.browser.server_groups.servers.databases.tests import utils as \ + database_utils +from regression.python_test_utils import test_utils as utils + + +class SearchObjectsApiTypes(BaseTestGenerator): + """ This class will test types API of search objects. """ + scenarios = [ + # Fetching default URL for schema node. + ('Types API URL', dict(url='/search_objects/types')) + ] + + def runTest(self): + database_info = parent_node_dict["database"][-1] + server_id = database_info["server_id"] + + db_id = database_info["db_id"] + db_con = database_utils.connect_database(self, + utils.SERVER_GROUP, + server_id, + db_id) + if not db_con["info"] == "Database connected.": + raise Exception("Could not connect to database to add the schema.") + + url = self.url + '/' + str(server_id) + '/' + str(db_id) + response = self.tester.get(url) + self.assertEquals(response.status_code, 200) + + # repsonse data should be dict + response_data = json.loads(response.data.decode('utf-8'))['data'] + self.assertEquals(type(response_data), dict) + + # response data key values should not be None + for key, value in response_data.items(): + self.assertIsNotNone(value, 'Key {0} has value None'.format(key)) diff --git a/web/pgadmin/tools/search_objects/tests/test_search_objects_helper.py b/web/pgadmin/tools/search_objects/tests/test_search_objects_helper.py new file mode 100644 index 000000000..ec9c369b9 --- /dev/null +++ b/web/pgadmin/tools/search_objects/tests/test_search_objects_helper.py @@ -0,0 +1,117 @@ +import sys + +from pgadmin.tools.search_objects.utils import SearchObjectsHelper, current_app +from pgadmin.utils.route import BaseTestGenerator + +if sys.version_info < (3, 3): + from mock import patch, MagicMock +else: + from unittest.mock import patch, MagicMock + + +class SearchObjectsHelperTest(BaseTestGenerator): + scenarios = [ + ('scenario', dict( + node_blueprints=[ + dict(node_type='table', coll_label='Tables', + backend_supported=True), + dict(node_type='view', coll_label='Views', + backend_supported=False), + dict(node_type='index', coll_label='Indexes', + backend_supported=True), + dict(node_type='role', coll_label='Roles', + backend_supported=True) + ], + all_node_types=['table', 'view', 'index'], + expected_show_node_prefs=dict(table=True, view=False, index=True), + expected_supported_types=dict(table='Tables', index='Indexes'), + expected_supported_types_skip=dict(table='Tables', view='Views', + index='Indexes'), + execute_dict_return_value=( + True, dict(rows=[ + dict(obj_name='name1', obj_type='table', + obj_path='some/path', show_node=True, + other_info=None, catalog_level='N'), + dict(obj_name='name2', obj_type='view', + obj_path='some1/path', show_node=True, + other_info=None, catalog_level='D'), + dict(obj_name='name3', obj_type='index', + obj_path='some2/path1', show_node=True, + other_info='oid', catalog_level='O'), + ])), + expected_search_op=( + True, [ + dict(name='name1', type='table', type_label='Tables', + path='some/path', + show_node=True, other_info=None, catalog_level='N'), + dict(name='name2', type='view', type_label='Views', + path='some1/path', + show_node=True, other_info=None, catalog_level='D'), + dict(name='name3', type='index', type_label='Indexes', + path='some2/path1', + show_node=True, other_info='oid', catalog_level='O'), + ] + ) + )) + ] + + def __create_manager(self): + connection = MagicMock( + execute_dict=MagicMock(), + db='somedb' + ) + connection.execute_dict.return_value = self.execute_dict_return_value + + def connection_function(did): + return connection + + return MagicMock( + connection=connection_function + ) + + @patch('pgadmin.tools.search_objects.utils.get_node_blueprint') + @patch('pgadmin.tools.search_objects.utils.get_driver') + def runTest(self, get_driver_mock, get_node_blueprint_mock): + manager = self.__create_manager() + + get_driver_mock.return_value = MagicMock( + connection_manager=lambda session_id: manager) + + def __get_node_blueprint_mock(node_type): + blueprints = self.node_blueprints + blueprint = None + for data in blueprints: + if node_type == data['node_type']: + blueprint = MagicMock( + BackendSupported=MagicMock( + return_value=data['backend_supported']), + collection_label=data['coll_label'], + show_node=data['backend_supported'], + ) + return blueprint + + get_node_blueprint_mock.side_effect = __get_node_blueprint_mock + + with self.app.app_context(): + + so_obj = SearchObjectsHelper(2, 18456, + node_types=self.all_node_types) + so_obj.get_sql = MagicMock(return_value='dummy query') + + # test template path + manager.server_type = 'pg' + manager.version = 906000 + self.assertEquals(so_obj.get_template_path(), + 'search_objects/sql/pg/#906000#') + + self.assertEquals(so_obj.get_show_node_prefs(), + self.expected_show_node_prefs) + + self.assertEquals(so_obj.get_supported_types(), + self.expected_supported_types) + + self.assertEquals(so_obj.get_supported_types(skip_check=True), + self.expected_supported_types_skip) + + self.assertEquals(so_obj.search('searchtext', 'all'), + self.expected_search_op) diff --git a/web/pgadmin/tools/search_objects/utils.py b/web/pgadmin/tools/search_objects/utils.py new file mode 100644 index 000000000..cf858d6e2 --- /dev/null +++ b/web/pgadmin/tools/search_objects/utils.py @@ -0,0 +1,131 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2020, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +from flask import current_app, render_template +from flask_babelex import gettext + +from pgadmin.utils.driver import get_driver +from config import PG_DEFAULT_DRIVER + + +def get_node_blueprint(node_type): + blueprint = None + node_type = 'NODE-' + node_type + if node_type in current_app.blueprints: + blueprint = current_app.blueprints[node_type] + + return blueprint + + +class SearchObjectsHelper: + def __init__(self, sid, did, show_system_objects=False, node_types=None): + self.sid = sid + self.did = did + self.show_system_objects = show_system_objects + self.manager = get_driver( + PG_DEFAULT_DRIVER + ).connection_manager(sid) + + self._all_node_types = [ + 'cast', 'fts_dictionary', 'check_constraint', + 'exclusion_constraint', 'foreign_key', + 'primary_key', 'unique_constraint', 'constraints', 'trigger', + 'table', 'compound_trigger', 'rule', 'column', 'partition', + 'index', 'type', 'domain', 'domain_constraints', 'schema', + 'synonym', 'sequence', 'edbvar', 'edbfunc', 'edbproc', 'package', + 'foreign_table', 'fts_parser', 'function', 'procedure', + 'trigger_function', 'fts_template', 'collation', 'view', 'mview', + 'fts_configuration', 'extension', 'language', + 'event_trigger', 'foreign_server', 'user_mapping', + 'foreign_data_wrapper' + ] if node_types is None else node_types + + @property + def all_node_types(self): + return self._all_node_types + + def get_template_path(self): + return 'search_objects/sql/{0}/#{1}#'.format( + self.manager.server_type, self.manager.version) + + def get_show_node_prefs(self): + return_types = {} + for node_type in self.all_node_types: + blueprint = get_node_blueprint(node_type) + if blueprint is None: + continue + + return_types[node_type] = blueprint.show_node + return return_types + + def get_supported_types(self, skip_check=False): + return_types = {} + for node_type in self.all_node_types: + blueprint = get_node_blueprint(node_type) + if blueprint is None: + continue + + if blueprint.BackendSupported(self.manager, is_catalog=False, + did=self.did) or skip_check: + if node_type in ['edbfunc', 'edbproc']: + return_types[node_type] =\ + gettext('Package {0}').format( + blueprint.collection_label) + else: + return_types[node_type] = blueprint.collection_label + + return return_types + + def get_sql(self, sql_file, **kwargs): + return render_template( + "/".join([self.get_template_path(), sql_file]), + **kwargs + ) + + def finalize_id_path(self, path, base_path): + if base_path is not None: + path = '{0}/{1}'.format(base_path, path) + + return path + + def search(self, text, obj_type=None): + conn = self.manager.connection(did=self.did) + last_system_oid = (self.manager.db_info[self.did])['datlastsysoid'] \ + if self.manager.db_info is not None and self.did in \ + self.manager.db_info else 0 + + show_node_prefs = self.get_show_node_prefs() + node_labels = self.get_supported_types(skip_check=True) + # Column catalog_level has values as + # N - Not a catalog schema + # D - Catalog schema with DB support - pg_catalog + # O - Catalog schema with object support only - info schema, dbo, sys + status, res = conn.execute_dict( + self.get_sql('search.sql', search_text=text, obj_type=obj_type, + show_system_objects=self.show_system_objects, + show_node_prefs=show_node_prefs, _=gettext, + last_system_oid=last_system_oid) + ) + + if not status: + return status, res + + ret_val = [ + { + 'name': row['obj_name'], + 'type': row['obj_type'], + 'type_label': node_labels[row['obj_type']], + 'path': row['obj_path'], + 'show_node': row['show_node'], + 'other_info': row['other_info'], + 'catalog_level': row['catalog_level'], + } + for row in res['rows'] + ] + return True, ret_val diff --git a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css index 52b821d8b..f9d15b8da 100644 --- a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css +++ b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css @@ -112,7 +112,7 @@ li { font-size: 9pt; } -.slick-header-column.ui-state-default { +#datagrid .slick-header-column.ui-state-default { height: 32px !important; } diff --git a/web/regression/javascript/fake_endpoints.js b/web/regression/javascript/fake_endpoints.js index fa01b3f89..157a34863 100644 --- a/web/regression/javascript/fake_endpoints.js +++ b/web/regression/javascript/fake_endpoints.js @@ -19,5 +19,7 @@ define(function () { 'datagrid.initialize_query_tool_with_did': '/initialize/query_tool///', 'restore.create_job': '/restore/job/', 'datagrid.panel': '/panel/', + 'search_objects.types': '/search_objects/types//', + 'search_objects.search': '/search_objects/search//', }; }); diff --git a/web/regression/javascript/search_objects/search_objects_dialog_spec.js b/web/regression/javascript/search_objects/search_objects_dialog_spec.js new file mode 100644 index 000000000..039db1970 --- /dev/null +++ b/web/regression/javascript/search_objects/search_objects_dialog_spec.js @@ -0,0 +1,155 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2020, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// +import SearchObjectsDialog from 'tools/search_objects/static/js/search_objects_dialog'; +import {TreeFake} from '../tree/tree_fake'; +import MockAdapter from 'axios-mock-adapter'; +import axios from 'axios/index'; + +const context = describe; + +describe('SearchObjectsDialog', () => { + let soDialog; + let pgBrowser; + let jquerySpy; + let alertifySpy; + + beforeEach(() => { + pgBrowser = { + treeMenu: new TreeFake(), + Nodes: { + server: { + hasId: true, + label: 'server', + getTreeNodeHierarchy: jasmine.createSpy('server.getTreeNodeHierarchy'), + }, + database: { + hasId: true, + label: 'database', + getTreeNodeHierarchy: jasmine.createSpy('db.getTreeNodeHierarchy'), + }, + schema: { + hasId: true, + label: 'schema', + getTreeNodeHierarchy: jasmine.createSpy('db.getTreeNodeHierarchy'), + }, + }, + stdW: { + sm: 500, + md: 700, + lg: 900, + default: 500, + }, + stdH: { + sm: 200, + md: 400, + lg: 550, + default: 550, + }, + }; + pgBrowser.Nodes.server.hasId = true; + pgBrowser.Nodes.database.hasId = true; + jquerySpy = jasmine.createSpy('jquerySpy'); + + const hierarchy = { + children: [ + { + id: 'root', + children: [ + { + id: 'serverTreeNode', + data: { + _id: 10, + _type: 'server', + user: {name: 'username'}, + label: 'theserver', + }, + children: [ + { + id: 'some_database', + data: { + _type: 'database', + _id: 11, + label: 'thedatabase', + }, + }, + ], + }, + { + id: 'ppasServer', + data: { + _type: 'server', + server_type: 'ppas', + children: [ + {id: 'someNodeUnderneathPPASServer'}, + ], + }, + }, + ], + }, + ], + }; + + pgBrowser.treeMenu = TreeFake.build(hierarchy); + }); + + describe('#draw', () => { + let networkMock; + beforeEach(() => { + networkMock = new MockAdapter(axios); + alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']); + alertifySpy['search_objects'] = jasmine.createSpy('search_objects'); + soDialog = new SearchObjectsDialog( + pgBrowser, + jquerySpy, + alertifySpy, + null + ); + + pgBrowser.get_preference = jasmine.createSpy('get_preferences'); + }); + + afterEach(() => { + networkMock.restore(); + }); + + context('there are no ancestors of the type database', () => { + it('does not create a dialog', () => { + pgBrowser.treeMenu.selectNode([{id: 'serverTreeNode'}]); + soDialog.draw(null, null, null); + expect(alertifySpy['search_objects']).not.toHaveBeenCalled(); + }); + + it('display an alert with a Backup Error', () => { + soDialog.draw(null, [{id: 'serverTreeNode'}], null); + expect(alertifySpy.alert).toHaveBeenCalledWith( + 'Search Objects Error', + 'Please select a database or its child node from the browser.' + ); + }); + }); + + context('there is an ancestor of the type database', () => { + let soDialogResizeToSpy; + beforeEach(() => { + soDialogResizeToSpy = jasmine.createSpyObj('soDialogResizeToSpy', ['resizeTo']); + alertifySpy['search_objects'].and + .returnValue(soDialogResizeToSpy); + }); + + it('displays the dialog when database node selected', (done) => { + soDialog.draw(null, [{id: 'some_database'}], null, pgBrowser.stdW.md, pgBrowser.stdH.md); + setTimeout(() => { + expect(alertifySpy['search_objects']).toHaveBeenCalledWith('Search Objects - thedatabase/username@theserver'); + expect(soDialogResizeToSpy.resizeTo).toHaveBeenCalledWith(pgBrowser.stdW.md, pgBrowser.stdH.md); + done(); + }, 0); + }); + }); + }); +}); diff --git a/web/regression/javascript/search_objects/search_objects_dialog_wrapper_spec.js b/web/regression/javascript/search_objects/search_objects_dialog_wrapper_spec.js new file mode 100644 index 000000000..4131e2d3a --- /dev/null +++ b/web/regression/javascript/search_objects/search_objects_dialog_wrapper_spec.js @@ -0,0 +1,526 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2020, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import {TreeFake} from '../tree/tree_fake'; +import SearchObjectsDialogWrapper from 'tools/search_objects/static/js/search_objects_dialog_wrapper'; +import axios from 'axios/index'; +import MockAdapter from 'axios-mock-adapter'; +import {TreeNode} from '../../../pgadmin/static/js/tree/tree'; + +let context = describe; + +describe('SearchObjectsDialogWrapper', () => { + let jquerySpy; + let pgBrowser; + let alertifySpy; + let dialogModelKlassSpy = null; + let backform; + let soDialogWrapper; + let noDataNode; + let serverTreeNode; + let databaseTreeNode; + let viewSchema; + let soJQueryContainerSpy; + let soNodeChildNodeSpy; + let soNode; + + beforeEach(() => { + pgBrowser = { + treeMenu: new TreeFake(), + Nodes: { + server: { + hasId: true, + getTreeNodeHierarchy: jasmine.createSpy('getTreeNodeHierarchy'), + }, + database: { + hasId: true, + getTreeNodeHierarchy: jasmine.createSpy('getTreeNodeHierarchy'), + }, + 'coll-sometype': { + type: 'coll-sometype', + hasId: false, + label: 'Some types coll', + }, + sometype: { + type: 'sometype', + hasId: true, + }, + someothertype: { + type: 'someothertype', + hasId: true, + collection_type: 'coll-sometype', + }, + 'coll-edbfunc': { + type: 'coll-edbfunc', + hasId: true, + label: 'Functions', + }, + 'coll-edbproc': { + type: 'coll-edbfunc', + hasId: true, + label: 'Procedures', + }, + 'coll-edbvar': { + type: 'coll-edbfunc', + hasId: true, + label: 'Variables', + }, + }, + keyboardNavigation: jasmine.createSpyObj('keyboardNavigation', ['getDialogTabNavigator']), + }; + noDataNode = pgBrowser.treeMenu.addNewNode('level1.1', undefined, [{id: 'level1'}]); + serverTreeNode = pgBrowser.treeMenu.addNewNode('level2.1', { + _type: 'server', + _id: 10, + label: 'some-tree-label', + }, [{id: 'level2.1'}]); + databaseTreeNode = new TreeNode('database-tree-node', { + _type: 'database', + _id: 123, + _label: 'some-database-label', + }, [{id: 'database-tree-node'}]); + pgBrowser.treeMenu.addChild(serverTreeNode, databaseTreeNode); + + jquerySpy = jasmine.createSpy('jquerySpy'); + soNode = { + __internal: { + buttons: [{}, {}, {}, { + element: { + disabled: false, + }, + }], + }, + elements: { + body: { + childNodes: [ + {}, + ], + }, + content: jasmine.createSpyObj('content', ['appendChild', 'attr']), + }, + }; + + soJQueryContainerSpy = jasmine.createSpyObj('soJQueryContainer', ['get', 'attr']); + soJQueryContainerSpy.get.and.returnValue(soJQueryContainerSpy); + + viewSchema = {}; + backform = jasmine.createSpyObj('backform', ['generateViewSchema', 'Dialog']); + backform.generateViewSchema.and.returnValue(viewSchema); + + soNodeChildNodeSpy = jasmine.createSpyObj('something', ['addClass']); + jquerySpy.and.callFake((selector) => { + if (selector === '
') { + return soJQueryContainerSpy; + } else if (selector === soNode.elements.body.childNodes[0]) { + return soNodeChildNodeSpy; + } + }); + alertifySpy = jasmine.createSpyObj('alertify', ['alert', 'dialog']); + + }); + + describe('#prepare', () => { + beforeEach(() => { + soDialogWrapper = new SearchObjectsDialogWrapper( + '
', + 'soDialogTitle', + 'search_objects', + jquerySpy, + pgBrowser, + alertifySpy, + dialogModelKlassSpy, + backform + ); + soDialogWrapper = Object.assign(soDialogWrapper, soNode); + spyOn(soDialogWrapper, 'prepareDialog').and.callThrough(); + spyOn(soDialogWrapper, 'setTypes'); + }); + + context('no tree element is selected', () => { + it('does not prepare dialog', () => { + spyOn(soDialogWrapper, 'prepareDialog'); + soDialogWrapper.prepare(); + expect(soDialogWrapper.prepareDialog).not.toHaveBeenCalled(); + }); + }); + + context('selected tree node has no data', () => { + beforeEach(() => { + pgBrowser.treeMenu.selectNode(noDataNode.domNode); + }); + + it('does not prepare the dialog', () => { + spyOn(soDialogWrapper, 'prepareDialog'); + soDialogWrapper.prepare(); + expect(soDialogWrapper.prepareDialog).not.toHaveBeenCalled(); + }); + }); + + context('tree element is selected', () => { + let gridDestroySpy; + let networkMock; + + beforeEach(() => { + pgBrowser.treeMenu.selectNode(databaseTreeNode.domNode); + soDialogWrapper.grid = jasmine.createSpyObj('grid', ['destroy']); + spyOn(soDialogWrapper, 'showMessage'); + gridDestroySpy = spyOn(soDialogWrapper.grid, 'destroy'); + + networkMock = new MockAdapter(axios); + + }); + + afterEach(() => { + networkMock.restore(); + }); + + it('creates dialog and displays it', () => { + soDialogWrapper.prepare(); + expect(soDialogWrapper.prepareDialog).toHaveBeenCalled(); + expect(soDialogWrapper.showMessage).toHaveBeenCalledWith(null); + }); + + + it('if grid set then destroy it', () => { + soDialogWrapper.prepare(); + expect(gridDestroySpy).toHaveBeenCalled(); + expect(soDialogWrapper.grid).toBe(null); + }); + + it('setTypes called after the ajax success', (done) => { + networkMock.onGet('/search_objects/types/10/123').reply(200, { + 'data': { + 'type1': 'Type Label 1', + 'type2': 'Type Label 2', + }, + }); + + soDialogWrapper.prepare(); + + expect(soDialogWrapper.setTypes.calls.argsFor(0)).toEqual([ + [{ id: -1, text: 'Loading...', value: null }], false, + ]); + + setTimeout(()=>{ + expect(soDialogWrapper.setTypes.calls.argsFor(1)).toEqual([ + [{id: 'all', text: 'All types'}, + {id: 'type1', text: 'Type Label 1'}, + {id: 'type2', text: 'Type Label 2'}], + ]); + done(); + }, 0); + }); + + it('setTypes called after the ajax fail', (done) => { + networkMock.onGet('/search_objects/types/10/123').reply(500); + + soDialogWrapper.prepare(); + + expect(soDialogWrapper.setTypes.calls.argsFor(0)).toEqual([ + [{ id: -1, text: 'Loading...', value: null }], false, + ]); + + setTimeout(()=>{ + expect(soDialogWrapper.setTypes.calls.argsFor(1)).toEqual([ + [{id: -1, text: 'Failed', value: null }], false, + ]); + done(); + }, 0); + }); + }); + }); + + describe('showMessage', () => { + beforeEach(() => { + soDialogWrapper = new SearchObjectsDialogWrapper( + '
', + 'soDialogTitle', + 'search_objects', + jquerySpy, + pgBrowser, + alertifySpy, + dialogModelKlassSpy, + backform + ); + soDialogWrapper.statusBar = document.createElement('div'); + soDialogWrapper.statusBar.classList.add('d-none'); + document.body.appendChild(soDialogWrapper.statusBar); + }); + + afterEach(() => { + document.body.removeChild(soDialogWrapper.statusBar); + }); + it('when info message', ()=>{ + soDialogWrapper.showMessage('locating', false); + expect(soDialogWrapper.statusBar.classList.contains('d-none')).toBe(false); + expect(soDialogWrapper.statusBar.querySelector('.error-in-footer')).toBe(null); + expect(soDialogWrapper.statusBar.querySelector('.info-in-footer')).not.toBe(null); + expect(soDialogWrapper.statusBar.querySelector('.alert-text').innerHTML).toEqual('locating'); + }); + + it('when error message', ()=>{ + soDialogWrapper.showMessage('some error', true); + expect(soDialogWrapper.statusBar.classList.contains('d-none')).toBe(false); + expect(soDialogWrapper.statusBar.querySelector('.error-in-footer')).not.toBe(null); + expect(soDialogWrapper.statusBar.querySelector('.info-in-footer')).toBe(null); + expect(soDialogWrapper.statusBar.querySelector('.alert-text').innerHTML).toEqual('some error'); + }); + + it('when no message', ()=>{ + soDialogWrapper.showMessage(null); + expect(soDialogWrapper.statusBar.classList.contains('d-none')).toBe(true); + }); + }); + + describe('function', () => { + beforeEach(() => { + soDialogWrapper = new SearchObjectsDialogWrapper( + '
', + 'soDialogTitle', + 'search_objects', + jquerySpy, + pgBrowser, + alertifySpy, + dialogModelKlassSpy, + backform + ); + }); + + it('updateDimOfSearchResult', ()=>{ + soDialogWrapper.searchResultContainer = document.createElement('div'); + soDialogWrapper.searchResult = document.createElement('div'); + spyOn(soDialogWrapper.searchResultContainer, 'getBoundingClientRect').and.returnValue({height:100, width: 50}); + + soDialogWrapper.updateDimOfSearchResult(); + expect(soDialogWrapper.searchResult.style.height).toEqual('100px'); + expect(soDialogWrapper.searchResult.style.width).toEqual('50px'); + }); + + it('setLoading', ()=>{ + soDialogWrapper.loader = document.createElement('div'); + soDialogWrapper.loader.innerHTML = ` +
+ `; + + soDialogWrapper.setLoading('loading'); + expect(soDialogWrapper.loader.classList.contains('d-none')).toBe(false); + expect(soDialogWrapper.loader.querySelector('.pg-sp-text').innerHTML).toEqual('loading'); + + soDialogWrapper.setLoading(null); + expect(soDialogWrapper.loader.classList.contains('d-none')).toBe(true); + }); + + it('searchBtnEnabled', ()=>{ + soDialogWrapper.searchBtn = document.createElement('button'); + + soDialogWrapper.searchBtnEnabled(true); + expect(soDialogWrapper.searchBtn.disabled).toEqual(false); + expect(soDialogWrapper.searchBtnEnabled()).toEqual(true); + + soDialogWrapper.searchBtnEnabled(false); + expect(soDialogWrapper.searchBtn.disabled).toEqual(true); + expect(soDialogWrapper.searchBtnEnabled()).toEqual(false); + }); + + it('searchBoxVal', ()=>{ + soDialogWrapper.searchBox = document.createElement('input'); + soDialogWrapper.searchBoxVal('abc'); + expect(soDialogWrapper.searchBox.value).toEqual('abc'); + expect(soDialogWrapper.searchBoxVal()).toEqual('abc'); + }); + + it('typesVal', ()=>{ + soDialogWrapper.typesSelect = document.createElement('select'); + let opt = document.createElement('option'); + opt.appendChild( document.createTextNode('Some type') ); + opt.value = 'sometype'; + soDialogWrapper.typesSelect.appendChild(opt); + + soDialogWrapper.typesVal('sometype'); + expect(soDialogWrapper.typesSelect.value).toEqual('sometype'); + expect(soDialogWrapper.typesVal()).toEqual('sometype'); + }); + + it('setGridData', ()=>{ + soDialogWrapper.grid = jasmine.createSpyObj('grid', ['setData', 'resizeCanvas']); + soDialogWrapper.setGridData([{id:'somedata'}]); + expect(soDialogWrapper.grid.setData).toHaveBeenCalled(); + expect(soDialogWrapper.grid.resizeCanvas).toHaveBeenCalled(); + }); + + it('onDialogResize', ()=>{ + soDialogWrapper.grid = jasmine.createSpyObj('grid', ['autosizeColumns', 'resizeCanvas']); + spyOn(soDialogWrapper, 'updateDimOfSearchResult'); + + soDialogWrapper.onDialogResize(); + expect(soDialogWrapper.updateDimOfSearchResult).toHaveBeenCalled(); + expect(soDialogWrapper.grid.resizeCanvas).toHaveBeenCalled(); + expect(soDialogWrapper.grid.autosizeColumns).toHaveBeenCalled(); + }); + + it('onDialogShow', (done)=>{ + spyOn(soDialogWrapper, 'prepareGrid').and.callFake(function() { + this.grid = jasmine.createSpyObj('grid', ['init']); + }); + + spyOn(soDialogWrapper, 'focusOnDialog'); + spyOn(soDialogWrapper, 'updateDimOfSearchResult'); + spyOn(soDialogWrapper, 'setGridData'); + spyOn(soDialogWrapper, 'onDialogResize'); + + + soDialogWrapper.onDialogShow(); + setTimeout(()=>{ + expect(soDialogWrapper.prepareGrid).toHaveBeenCalled(); + expect(soDialogWrapper.focusOnDialog).toHaveBeenCalled(); + expect(soDialogWrapper.setGridData).toHaveBeenCalledWith([]); + expect(soDialogWrapper.onDialogResize).toHaveBeenCalled(); + done(); + }, 750); + }); + + context('getCollNode', ()=>{ + it('type have same coll node', ()=>{ + let collNode = soDialogWrapper.getCollNode('sometype'); + expect(collNode.type).toEqual('coll-sometype'); + }); + + it('type does not same coll node', ()=>{ + let collNode = soDialogWrapper.getCollNode('someothertype'); + expect(collNode.type).toEqual('coll-sometype'); + }); + + it('type does not have coll node at all', ()=>{ + let collNode = soDialogWrapper.getCollNode('database'); + expect(collNode).toBe(null); + }); + }); + + it('finaliseData', ()=>{ + spyOn(soDialogWrapper, 'translateSearchObjectsPath').and.returnValue(['disp/path', 'id/path']); + let data = soDialogWrapper.finaliseData({ + name: 'objname', + type: 'sometype', + type_label: 'Some types coll', + path: ':some.123:/path', + show_node: true, + }); + expect(data).toEqual({ + icon: 'icon-sometype', + name: 'objname', + type: 'sometype', + type_label: 'Some types coll', + path: 'disp/path', + id_path: 'id/path', + show_node: true, + }); + }); + + context('translateSearchObjectsPath', ()=>{ + let path = null, catalog_level = null; + beforeEach(()=>{ + pgBrowser.Nodes = { + 'server_group': { + type:'server_group', + label: 'Server group', + }, + 'server': { + type:'server', + label: 'Server', + }, + 'coll-database': { + type:'coll-database', + label: 'Databases', + }, + 'database': { + type:'database', + label: 'Database', + }, + 'coll-schema': { + type:'coll-schema', + label: 'Schemas', + }, + 'schema': { + type:'schema', + label: 'Schema', + }, + 'coll-table': { + type:'coll-table', + label: 'Tables', + }, + 'table': { + type:'table', + label: 'Table', + }, + 'sometype': { + type:'sometype', + label: 'Some type', + collection_type: 'coll-table', + }, + 'coll-catalog': { + type:'coll-catalog', + label: 'Catalogs', + }, + 'catalog': { + type:'catalog', + label: 'Catalog', + }, + 'coll-catalog_object': { + type:'coll-catalog_object', + label: 'Catalog Objects', + }, + 'catalog_object': { + type:'catalog_object', + label: 'catalog object', + }, + }; + + soDialogWrapper.treeInfo = { + 'server_group': {'id': 'server_group/1', '_id': 1}, + 'server': {'id': 'server/3', '_id': 3}, + 'database': {'id': 'database/18456', '_id': 18456}, + }; + }); + it('regular schema', ()=>{ + path = ':schema.2200:/test_db/:table.2604:/sampletab'; + catalog_level = 'N'; + + let retVal = soDialogWrapper.translateSearchObjectsPath(path, catalog_level); + expect(retVal).toEqual([ + 'Schemas/test_db/Tables/sampletab', + ['server_group/1','server/3','coll-database/3','database/18456','coll-schema/18456','schema/2200','coll-table/2200','table/2604'], + ]); + }); + + context('catalog schema', ()=>{ + it('with db support', ()=>{ + path = ':schema.11:/PostgreSQL Catalog (pg_catalog)/:table.2604:/pg_class'; + catalog_level = 'D'; + + let retVal = soDialogWrapper.translateSearchObjectsPath(path, catalog_level); + expect(retVal).toEqual([ + 'Catalogs/PostgreSQL Catalog (pg_catalog)/Tables/pg_class', + ['server_group/1','server/3','coll-database/3','database/18456','coll-catalog/18456','catalog/11','coll-table/11','table/2604'], + ]); + }); + + it('with object support only', ()=>{ + path = ':schema.11:/ANSI (information_schema)/:table.2604:/attributes'; + catalog_level = 'O'; + + let retVal = soDialogWrapper.translateSearchObjectsPath(path, catalog_level); + expect(retVal).toEqual([ + 'Catalogs/ANSI (information_schema)/Catalog Objects/attributes', + ['server_group/1','server/3','coll-database/3','database/18456','coll-catalog/18456','catalog/11','coll-catalog_object/11','catalog_object/2604'], + ]); + }); + }); + }); + }); +}); diff --git a/web/regression/javascript/tree/tree_fake.js b/web/regression/javascript/tree/tree_fake.js index c0c17796a..f476bb272 100644 --- a/web/regression/javascript/tree/tree_fake.js +++ b/web/regression/javascript/tree/tree_fake.js @@ -41,6 +41,9 @@ export class TreeFake extends Tree { this.aciTreeToOurTreeTranslator = {}; this.aciTreeApi = jasmine.createSpyObj( 'ACITreeApi', ['setInode', 'unload', 'deselect', 'select']); + this.aciTreeApi.unload.and.callFake(function(domNode, config) { + config.success(); + }); } addNewNode(id, data, domNode, path) { diff --git a/web/regression/javascript/tree/tree_spec.js b/web/regression/javascript/tree/tree_spec.js index bb607796c..038fcc24c 100644 --- a/web/regression/javascript/tree/tree_spec.js +++ b/web/regression/javascript/tree/tree_spec.js @@ -246,39 +246,70 @@ describe('tree tests', () => { tree.aciTreeApi = jasmine.createSpyObj( 'ACITreeApi', ['setInode', 'unload', 'deselect', 'select']); + tree.aciTreeApi.unload.and.callFake((domNode, config) => { + config.success(); + }); }); - it('reloads the node and its children', () => { - level2.reload(tree); - expect(tree.findNodeByDomElement([{id: 'level2'}])).toEqual(level2); + it('reloads the node and its children', (done) => { + level2.reload(tree) + .then(()=>{ + expect(tree.findNodeByDomElement([{id: 'level2'}])).toEqual(level2); + done(); + }) + .catch((error)=>{ + fail(error); + }); }); - it('does not reload the children of node', () => { - level2.reload(tree); - expect(tree.findNodeByDomElement([{id: 'level3'}])).toBeNull(); + it('does not reload the children of node', (done) => { + level2.reload(tree) + .then(()=>{ + expect(tree.findNodeByDomElement([{id: 'level3'}])).toBeNull(); + done(); + }) + .catch((error)=>{ + fail(error); + }); }); it('select the node', (done) => { - level2.reload(tree); - setTimeout(() => { - expect(tree.selected()).toEqual([{id: 'level2'}]); - done(); - }, 20); + level2.reload(tree) + .then(()=>{ + setTimeout(() => { + expect(tree.selected()).toEqual([{id: 'level2'}]); + done(); + }, 20); + }) + .catch((error)=>{ + fail(error); + }); }); describe('ACITree specific', () => { - it('sets the current node as a Inode, changing the Icon back to +', () => { - level2.reload(tree); - expect(tree.aciTreeApi.setInode).toHaveBeenCalledWith([{id: 'level2'}]); + it('sets the current node as a Inode, changing the Icon back to +', (done) => { + level2.reload(tree) + .then(()=>{ + expect(tree.aciTreeApi.setInode).toHaveBeenCalledWith([{id: 'level2'}]); + done(); + }) + .catch((error)=>{ + fail(error); + }); }); it('deselect the node and selects it again to trigger ACI tree' + ' events', (done) => { - level2.reload(tree); - setTimeout(() => { - expect(tree.aciTreeApi.deselect).toHaveBeenCalledWith([{id: 'level2'}]); - done(); - }, 20); + level2.reload(tree) + .then(()=>{ + setTimeout(() => { + expect(tree.aciTreeApi.deselect).toHaveBeenCalledWith([{id: 'level2'}]); + done(); + }, 20); + }) + .catch((error)=>{ + fail(error); + }); }); }); }); @@ -292,17 +323,32 @@ describe('tree tests', () => { level2 = tree.addNewNode('level2', {data: 'data'}, ['
  • level2
  • '], ['level1']); tree.addNewNode('level3', {data: 'more data'}, ['
  • level3
  • '], ['level1', 'level2']); tree.aciTreeApi = jasmine.createSpyObj('ACITreeApi', ['unload']); + tree.aciTreeApi.unload.and.callFake((domNode, config) => { + config.success(); + }); }); - it('unloads the children of the current node', () => { - level2.unload(tree); - expect(tree.findNodeByDomElement([{id: 'level2'}])).toEqual(level2); - expect(tree.findNodeByDomElement([{id: 'level3'}])).toBeNull(); + it('unloads the children of the current node', (done) => { + level2.unload(tree) + .then(()=>{ + expect(tree.findNodeByDomElement([{id: 'level2'}])).toEqual(level2); + expect(tree.findNodeByDomElement([{id: 'level3'}])).toBeNull(); + done(); + }) + .catch((error)=>{ + fail(error); + }); }); - it('calls unload on the ACI Tree', () => { - level2.unload(tree); - expect(tree.aciTreeApi.unload).toHaveBeenCalledWith(['
  • level2
  • ']); + it('calls unload on the ACI Tree', (done) => { + level2.unload(tree) + .then(()=>{ + expect(tree.aciTreeApi.unload).toHaveBeenCalledWith(['
  • level2
  • '], jasmine.any(Object)); + done(); + }) + .catch((error)=>{ + fail(error); + }); }); }); }); diff --git a/web/webpack.config.js b/web/webpack.config.js index 9fd0a32e9..29811388a 100644 --- a/web/webpack.config.js +++ b/web/webpack.config.js @@ -494,7 +494,8 @@ module.exports = [{ ',pgadmin.tools.debugger.controller' + ',pgadmin.tools.debugger.direct' + ',pgadmin.node.pga_job' + - ',pgadmin.tools.schema_diff', + ',pgadmin.tools.schema_diff' + + ',pgadmin.tools.search_objects', }, }, { test: require.resolve('snapsvg'), diff --git a/web/webpack.shim.js b/web/webpack.shim.js index a5c73805a..0587d0498 100644 --- a/web/webpack.shim.js +++ b/web/webpack.shim.js @@ -281,6 +281,8 @@ var webpackShimConfig = { 'pgadmin.tools.restore': path.join(__dirname, './pgadmin/tools/restore/static/js/restore'), 'pgadmin.tools.schema_diff': path.join(__dirname, './pgadmin/tools/schema_diff/static/js/schema_diff'), 'pgadmin.tools.schema_diff_ui': path.join(__dirname, './pgadmin/tools/schema_diff/static/js/schema_diff_ui'), + 'pgadmin.tools.search_objects': path.join(__dirname, './pgadmin/tools/search_objects/static/js/search_objects'), + 'pgadmin.search_objects': path.join(__dirname, './pgadmin/tools/search_objects/static/js'), 'pgadmin.tools.user_management': path.join(__dirname, './pgadmin/tools/user_management/static/js/user_management'), 'pgadmin.user_management.current_user': '/user_management/current_user', 'slick.pgadmin.editors': path.join(__dirname, './pgadmin/tools/../static/js/slickgrid/editors'), diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js index 70db29125..cfa8dda02 100644 --- a/web/webpack.test.config.js +++ b/web/webpack.test.config.js @@ -105,6 +105,7 @@ module.exports = { 'pgadmin.browser.preferences': path.join(__dirname, './pgadmin/browser/static/js/preferences'), 'pgadmin.browser.activity': path.join(__dirname, './pgadmin/browser/static/js/activity'), 'bundled_codemirror': path.join(__dirname, './pgadmin/static/bundle/codemirror'), + 'tools': path.join(__dirname, './pgadmin/tools/'), }, }, };