diff --git a/web/pgadmin/static/js/keyboard_shortcuts.js b/web/pgadmin/static/js/keyboard_shortcuts.js index c565b862..101aff5b 100644 --- a/web/pgadmin/static/js/keyboard_shortcuts.js +++ b/web/pgadmin/static/js/keyboard_shortcuts.js @@ -282,8 +282,9 @@ function keyboardShortcutsQueryTool( currLi = currLi.next(); } - /*do not focus on divider and disabled */ + /*do not focus on divider, disabled and d-none */ while(currLi.hasClass('dropdown-divider') + || currLi.hasClass('d-none') || currLi.find('.dropdown-item').first().hasClass('disabled')) { if(keyCode === UP_KEY) { currLi = currLi.prev(); diff --git a/web/pgadmin/static/js/sqleditor/query_tool_actions.js b/web/pgadmin/static/js/sqleditor/query_tool_actions.js index 7739e9b5..2c1be324 100644 --- a/web/pgadmin/static/js/sqleditor/query_tool_actions.js +++ b/web/pgadmin/static/js/sqleditor/query_tool_actions.js @@ -26,6 +26,14 @@ let queryToolActions = { return !$('.explain-timing').hasClass('visibility-hidden'); }, + _summary: function () { + return !$('.explain-summary').hasClass('visibility-hidden'); + }, + + _settings: function () { + return !$('.explain-settings').hasClass('visibility-hidden'); + }, + _clearMessageTab: function () { $('.sql-editor-message').html(''); }, @@ -41,36 +49,31 @@ let queryToolActions = { }, explainAnalyze: function (sqlEditorController) { - let costEnabled = this._costsEnabled(); - let verbose = this._verbose(); - let buffers = this._buffers(); - let timing = this._timing(); const explainObject = { format: 'json', analyze: true, - verbose: verbose, - costs: costEnabled, - buffers: buffers, - timing: timing, - summary: false, + verbose: this._verbose(), + costs: this._costsEnabled(), + buffers: this._buffers(), + timing: this._timing(), + summary: this._summary(), + settings: this._settings(), }; this._clearMessageTab(); sqlEditorController.execute(explainObject); }, explain: function (sqlEditorController) { - let costEnabled = this._costsEnabled(); - let verbose = this._verbose(); - // let explainQuery = `EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE ${verbose}, COSTS ${costEnabled}, BUFFERS OFF, TIMING OFF) `; const explainObject = { format: 'json', analyze: false, - verbose: verbose, - costs: costEnabled, + verbose: this._verbose(), + costs: this._costsEnabled(), buffers: false, timing: false, summary: false, + settings: false, }; this._clearMessageTab(); sqlEditorController.execute(explainObject); diff --git a/web/pgadmin/static/js/sqleditor/query_tool_preferences.js b/web/pgadmin/static/js/sqleditor/query_tool_preferences.js index c3906aaa..7fd19b40 100644 --- a/web/pgadmin/static/js/sqleditor/query_tool_preferences.js +++ b/web/pgadmin/static/js/sqleditor/query_tool_preferences.js @@ -134,6 +134,20 @@ function updateUIPreferences(sqlEditor) { $el.find('.explain-timing').addClass('visibility-hidden'); } + if (preferences.explain_summary) { + $el.find('.explain-summary').removeClass('visibility-hidden'); + } + else { + $el.find('.explain-summary').addClass('visibility-hidden'); + } + + if (preferences.explain_settings) { + $el.find('.explain-settings').removeClass('visibility-hidden'); + } + else { + $el.find('.explain-settings').addClass('visibility-hidden'); + } + /* Connection status check */ /* remove the status checker if present */ if(sqlEditor.connIntervalId != null) { diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py index fbc842fc..62db719d 100644 --- a/web/pgadmin/tools/datagrid/__init__.py +++ b/web/pgadmin/tools/datagrid/__init__.py @@ -234,6 +234,11 @@ def panel(trans_id, is_query_tool, editor_title): else: server_type = None + if request.args and request.args['server_ver'] != '': + server_ver = request.args['server_ver'] + else: + server_ver = 0 + # If title has slash(es) in it then replace it if request.args and request.args['fslashes'] != '': try: @@ -311,6 +316,7 @@ def panel(trans_id, is_query_tool, editor_title): is_desktop_mode=app.PGADMIN_RUNTIME, is_linux=is_linux_platform, server_type=server_type, + server_ver=server_ver, client_platform=user_agent.platform, bgcolor=bgcolor, fgcolor=fgcolor, @@ -408,7 +414,8 @@ def initialize_query_tool(sgid, sid, did=None): return make_json_response( data={ - 'gridTransId': trans_id + 'gridTransId': trans_id, + 'serverVersion': manager.version, } ) diff --git a/web/pgadmin/tools/datagrid/static/js/datagrid.js b/web/pgadmin/tools/datagrid/static/js/datagrid.js index cc095194..a405b7de 100644 --- a/web/pgadmin/tools/datagrid/static/js/datagrid.js +++ b/web/pgadmin/tools/datagrid/static/js/datagrid.js @@ -275,6 +275,7 @@ define('pgadmin.datagrid', [ baseUrl = url_for('datagrid.panel', url_params) + '?' + 'query_url=' + encodeURI(trans_obj.sURL) + '&server_type=' + encodeURIComponent(trans_obj.server_type) + + '&server_ver=' + trans_obj.serverVersion+ '&fslashes=' + titileForURLObj.slashLocations; if (self.preferences.new_browser_tab) { @@ -283,12 +284,6 @@ define('pgadmin.datagrid', [ // add a load listener to the window so that the title gets changed on page load newWin.addEventListener('load', function() { newWin.document.title = panel_title; - - /* Set the initial version of pref cache the new window is having - * This will be used by the poller to compare with window openers - * pref cache version - */ - //newWin.pgAdmin.Browser.preference_version(pgBrowser.preference_version()); }); } else { diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html index f138b9a4..65b6c4ec 100644 --- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html +++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html @@ -285,6 +285,18 @@ {{ _('Timing') }} +
  • + + + {{ _('Summary') }} + +
  • +
  • + + + {{ _('Settings') }} + +
  • @@ -412,7 +424,8 @@ script_type_url, "{{ server_type }}", {{ url_params|safe}}, - '{{ layout|safe }}' + '{{ layout|safe }}', + {{ server_ver }} ); }); {% endblock %} diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py index ac923a55..1d2796ec 100644 --- a/web/pgadmin/tools/sqleditor/__init__.py +++ b/web/pgadmin/tools/sqleditor/__init__.py @@ -95,7 +95,6 @@ class SqlEditorModule(PgAdminModule): return [ 'sqleditor.view_data_start', 'sqleditor.query_tool_start', - 'sqleditor.query_tool_preferences', 'sqleditor.poll', 'sqleditor.fetch', 'sqleditor.fetch_all', @@ -330,38 +329,6 @@ def extract_sql_from_network_parameters(request_data, request_arguments, return request_arguments or request_form_data -@blueprint.route( - '/query_tool/preferences/', - methods=["PUT"], endpoint='query_tool_preferences' -) -@login_required -def preferences(trans_id): - """ - This method is used to get/put explain options from/to preferences - - Args: - trans_id: unique transaction id - """ - - data = None - if request.data: - data = json.loads(request.data, encoding='utf-8') - else: - data = request.args or request.form - for k, v in data.items(): - v = bool(v) - if k == 'explain_verbose': - blueprint.explain_verbose.set(v) - elif k == 'explain_costs': - blueprint.explain_costs.set(v) - elif k == 'explain_buffers': - blueprint.explain_buffers.set(v) - elif k == 'explain_timing': - blueprint.explain_timing.set(v) - - return success_return() - - @blueprint.route('/poll/', methods=["GET"], endpoint='poll') @login_required def poll(trans_id): diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js index a8560574..4c27ae59 100644 --- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js @@ -81,6 +81,7 @@ define('tools.querytool', [ this.handler.preferences = this.preferences; this.connIntervalId = null; this.layout = opts.layout; + this.set_server_version(opts.server_ver); }, // Bind all the events @@ -121,6 +122,8 @@ define('tools.querytool', [ 'click #btn-explain-costs': 'on_explain_costs', 'click #btn-explain-buffers': 'on_explain_buffers', 'click #btn-explain-timing': 'on_explain_timing', + 'click #btn-explain-summary': 'on_explain_summary', + 'click #btn-explain-settings': 'on_explain_settings', 'change .limit': 'on_limit_change', 'keydown': 'keyAction', // Comment options @@ -166,6 +169,22 @@ define('tools.querytool', [ docker.addPanel('notifications', wcDocker.DOCK.STACKED, data_output_panel); }, + set_server_version: function(server_ver) { + let self = this; + self.server_ver = server_ver; + + this.$el.find('*[data-min-ver]').map(function() { + let minVer = 0, + ele = $(this); + minVer = parseInt(ele.attr('data-min-ver')); + if(minVer > self.server_ver) { + ele.addClass('d-none'); + } else { + ele.removeClass('d-none'); + } + }); + }, + // This function is used to render the template. render: function() { var self = this; @@ -1833,6 +1852,31 @@ define('tools.querytool', [ ); }, + on_explain_summary: function(ev) { + var self = this; + + this._stopEventPropogation(ev); + + self.handler.trigger( + 'pgadmin-sqleditor:button:explain-summary', + self, + self.handler + ); + }, + + on_explain_settings: function(ev) { + var self = this; + + this._stopEventPropogation(ev); + + self.handler.trigger( + 'pgadmin-sqleditor:button:explain-settings', + self, + self.handler + ); + }, + + do_not_close_menu: function(ev) { ev.stopPropagation(); }, @@ -2095,7 +2139,7 @@ define('tools.querytool', [ * header and loading icon and start execution of the sql query. */ start: function(transId, is_query_tool, editor_title, script_type_url, - server_type, url_params, layout + server_type, url_params, layout, server_ver ) { var self = this; @@ -2118,6 +2162,7 @@ define('tools.querytool', [ el: self.container, handler: self, layout: layout, + server_ver: server_ver, }); self.transId = self.gridView.transId = transId; @@ -2211,6 +2256,8 @@ define('tools.querytool', [ self.on('pgadmin-sqleditor:button:explain-costs', self._explain_costs, self); self.on('pgadmin-sqleditor:button:explain-buffers', self._explain_buffers, self); self.on('pgadmin-sqleditor:button:explain-timing', self._explain_timing, self); + self.on('pgadmin-sqleditor:button:explain-summary', self._explain_summary, self); + self.on('pgadmin-sqleditor:button:explain-settings', self._explain_settings, self); // Indentation related self.on('pgadmin-sqleditor:indent_selected_code', self._indent_selected_code, self); self.on('pgadmin-sqleditor:unindent_selected_code', self._unindent_selected_code, self); @@ -3866,108 +3913,37 @@ define('tools.querytool', [ }, - explainPreferenceUpdate: function(subItem, data, caller) { - let self = this; - $.ajax({ - url: url_for('sqleditor.query_tool_preferences', { - 'trans_id': self.transId, - }), - method: 'PUT', - contentType: 'application/json', - data: JSON.stringify(data), - }) - .done(function(res) { - if (res.success == undefined || !res.success) { - alertify.alert(gettext('Explain options error'), - gettext('Error occurred while setting %(subItem)s option in explain.', - {subItem : subItem}) - ); - } - else - self.call_cache_preferences(); - }) - .fail(function(e) { - let msg = httpErrorHandler.handleQueryToolAjaxError( - pgAdmin, self, e, caller, [], true - ); - alertify.alert(gettext('Explain options error'), msg); - }); + _toggle_explain_option: function(type) { + let selector = `.explain-${type}`; + $(selector).toggleClass('visibility-hidden'); }, // This function will toggle "verbose" option in explain _explain_verbose: function() { - var self = this; - let explain_verbose = false; - if ($('.explain-verbose').hasClass('visibility-hidden') === true) { - $('.explain-verbose').removeClass('visibility-hidden'); - explain_verbose = true; - } else { - $('.explain-verbose').addClass('visibility-hidden'); - explain_verbose = false; - } - - self.explainPreferenceUpdate( - 'verbose', { - 'explain_verbose': explain_verbose, - }, '_explain_verbose' - ); + this._toggle_explain_option('verbose'); }, // This function will toggle "costs" option in explain _explain_costs: function() { - var self = this; - let explain_costs = false; - if ($('.explain-costs').hasClass('visibility-hidden') === true) { - $('.explain-costs').removeClass('visibility-hidden'); - explain_costs = true; - } else { - $('.explain-costs').addClass('visibility-hidden'); - explain_costs = false; - } - - self.explainPreferenceUpdate( - 'costs', { - 'explain_costs': explain_costs, - }, '_explain_costs' - ); + this._toggle_explain_option('costs'); }, // This function will toggle "buffers" option in explain _explain_buffers: function() { - var self = this; - let explain_buffers = false; - if ($('.explain-buffers').hasClass('visibility-hidden') === true) { - $('.explain-buffers').removeClass('visibility-hidden'); - explain_buffers = true; - } else { - $('.explain-buffers').addClass('visibility-hidden'); - explain_buffers = false; - } - - self.explainPreferenceUpdate( - 'buffers', { - 'explain_buffers': explain_buffers, - }, '_explain_buffers' - ); + this._toggle_explain_option('buffers'); }, // This function will toggle "timing" option in explain _explain_timing: function() { - var self = this; - let explain_timing = false; - if ($('.explain-timing').hasClass('visibility-hidden') === true) { - $('.explain-timing').removeClass('visibility-hidden'); - explain_timing = true; - } else { - $('.explain-timing').addClass('visibility-hidden'); - explain_timing = false; - } + this._toggle_explain_option('timing'); + }, - self.explainPreferenceUpdate( - 'timing', { - 'explain_timing': explain_timing, - }, '_explain_timing' - ); + _explain_summary: function() { + this._toggle_explain_option('summary'); + }, + + _explain_settings: function() { + this._toggle_explain_option('settings'); }, /* diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/12_plus/explain_plan.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/12_plus/explain_plan.sql new file mode 100644 index 00000000..0bafcab5 --- /dev/null +++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/12_plus/explain_plan.sql @@ -0,0 +1,17 @@ +EXPLAIN ({% if format -%} + FORMAT {{ format.upper() }}, +{%- endif %}{% if analyze is defined -%} + ANALYZE {{ analyze }}, +{%- endif %}{% if verbose is defined -%} + VERBOSE {{ verbose }}, +{%- endif %}{% if costs is defined -%} + COSTS {{ costs }}, +{%- endif %}{% if timing is defined -%} + TIMING {{ timing }}, +{%- endif %}{% if summary is defined -%} + SUMMARY {{ summary }}, +{%- endif %}{% if settings is defined -%} + SETTINGS {{ settings }}, +{%- endif %}{% if buffers is defined -%} + BUFFERS {{ buffers }} +{%- endif %}) {{ sql }} diff --git a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py index ca09eaec..64c75754 100644 --- a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py +++ b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py @@ -62,6 +62,18 @@ def RegisterQueryToolPreferences(self): category_label=gettext('Explain') ) + self.explain_summary = self.preference.register( + 'Explain', 'explain_summary', + gettext("Show summary?"), 'boolean', False, + category_label=gettext('Explain') + ) + + self.explain_settings = self.preference.register( + 'Explain', 'explain_settings', + gettext("Show settings?"), 'boolean', False, + category_label=gettext('Explain') + ) + self.auto_commit = self.preference.register( 'Options', 'auto_commit', gettext("Auto commit?"), 'boolean', True, diff --git a/web/regression/javascript/sqleditor/query_tool_actions_spec.js b/web/regression/javascript/sqleditor/query_tool_actions_spec.js index b2b4e871..005e15e3 100644 --- a/web/regression/javascript/sqleditor/query_tool_actions_spec.js +++ b/web/regression/javascript/sqleditor/query_tool_actions_spec.js @@ -55,6 +55,8 @@ describe('queryToolActions', () => { spyOn(queryToolActions, '_costsEnabled').and.returnValue(false); spyOn(queryToolActions, '_buffers').and.returnValue(false); spyOn(queryToolActions, '_timing').and.returnValue(false); + spyOn(queryToolActions, '_summary').and.returnValue(false); + spyOn(queryToolActions, '_settings').and.returnValue(false); }); it('calls the execute function', () => { @@ -69,19 +71,22 @@ describe('queryToolActions', () => { buffers: false, timing: false, summary: false, + settings: false, }; expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject); }); }); - describe('when verbose and costs and buffers and timing are all selected', () => { + describe('when all options are selected', () => { beforeEach(() => { setUpSpies('', ''); spyOn(queryToolActions, '_verbose').and.returnValue(true); spyOn(queryToolActions, '_costsEnabled').and.returnValue(true); spyOn(queryToolActions, '_buffers').and.returnValue(true); spyOn(queryToolActions, '_timing').and.returnValue(true); + spyOn(queryToolActions, '_summary').and.returnValue(true); + spyOn(queryToolActions, '_settings').and.returnValue(true); }); it('calls the execute function', () => { queryToolActions.explainAnalyze(sqlEditorController); @@ -92,7 +97,8 @@ describe('queryToolActions', () => { costs: true, buffers: true, timing: true, - summary: false, + summary: true, + settings: true, }; expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject); }); @@ -105,6 +111,8 @@ describe('queryToolActions', () => { spyOn(queryToolActions, '_costsEnabled').and.returnValue(false); spyOn(queryToolActions, '_buffers').and.returnValue(true); spyOn(queryToolActions, '_timing').and.returnValue(false); + spyOn(queryToolActions, '_summary').and.returnValue(false); + spyOn(queryToolActions, '_settings').and.returnValue(false); }); it('calls the execute function', () => { queryToolActions.explainAnalyze(sqlEditorController); @@ -117,6 +125,7 @@ describe('queryToolActions', () => { buffers: true, timing: false, summary: false, + settings: false, }; expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject); @@ -130,6 +139,8 @@ describe('queryToolActions', () => { spyOn(queryToolActions, '_costsEnabled').and.returnValue(true); spyOn(queryToolActions, '_buffers').and.returnValue(false); spyOn(queryToolActions, '_timing').and.returnValue(true); + spyOn(queryToolActions, '_summary').and.returnValue(false); + spyOn(queryToolActions, '_settings').and.returnValue(false); }); it('calls the execute function', () => { queryToolActions.explainAnalyze(sqlEditorController); @@ -142,6 +153,35 @@ describe('queryToolActions', () => { buffers: false, timing: true, summary: false, + settings: false, + }; + + expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject); + }); + }); + + describe('when all are not selected except summary and settings', () => { + beforeEach(() => { + setUpSpies('', ''); + spyOn(queryToolActions, '_verbose').and.returnValue(false); + spyOn(queryToolActions, '_costsEnabled').and.returnValue(false); + spyOn(queryToolActions, '_buffers').and.returnValue(false); + spyOn(queryToolActions, '_timing').and.returnValue(false); + spyOn(queryToolActions, '_summary').and.returnValue(true); + spyOn(queryToolActions, '_settings').and.returnValue(true); + }); + it('calls the execute function', () => { + queryToolActions.explainAnalyze(sqlEditorController); + + const explainObject = { + format: 'json', + analyze: true, + verbose: false, + costs: false, + buffers: false, + timing: false, + summary: true, + settings: true, }; expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject); @@ -155,6 +195,10 @@ describe('queryToolActions', () => { setUpSpies('', ''); spyOn(queryToolActions, '_verbose').and.returnValue(true); spyOn(queryToolActions, '_costsEnabled').and.returnValue(true); + spyOn(queryToolActions, '_summary').and.returnValue(false); + spyOn(queryToolActions, '_settings').and.returnValue(false); + spyOn(queryToolActions, '_summary').and.returnValue(false); + spyOn(queryToolActions, '_settings').and.returnValue(false); }); it('calls the execute function', () => { @@ -167,6 +211,7 @@ describe('queryToolActions', () => { buffers: false, timing: false, summary: false, + settings: false, }; expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject); }); @@ -189,6 +234,7 @@ describe('queryToolActions', () => { buffers: false, timing: false, summary: false, + settings: false, }; expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject); @@ -212,6 +258,7 @@ describe('queryToolActions', () => { buffers: false, timing: false, summary: false, + settings: false, }; expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject); });