mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2026-10-04 04:36:41 -05:00
Add EXPLAIN options for SETTINGS and SUMMARY. Fixes #4335
Prevent flickering of large tooltips on the Graphical EXPLAIN canvas. Fixes #4224 EXPLAIN options should be Query Tool instance-specific. Fixes #4395
This commit is contained in:
1 parent
15556f9f89
commit
0340b8fb28
25 files changed
+425
-249
No files matched your search
Executable → Regular
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 84 KiB |
Executable → Regular
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 122 KiB |
@@ -133,10 +133,12 @@ Use the *Explain* tab to view a graphical representation of a query:
|
|||||||
|
|
||||||
To generate a graphical explain diagram, open the *Explain* tab, and select
|
To generate a graphical explain diagram, open the *Explain* tab, and select
|
||||||
*Explain*, *Explain Analyze*, or one or more options from the *Explain options*
|
*Explain*, *Explain Analyze*, or one or more options from the *Explain options*
|
||||||
menu on the *Execute/Refresh* drop-down. Please note that *EXPLAIN VERBOSE*
|
drop-down. Please note that *EXPLAIN VERBOSE*
|
||||||
cannot be displayed graphically. Hover over an icon on the *Explain* tab to
|
cannot be displayed graphically. Hover over an icon on the *Explain* tab to
|
||||||
review information about that item; a popup window will display information
|
review information about that item; a popup window will display information
|
||||||
about the selected object:
|
about the selected object. For information on JIT statistics, triggers and a
|
||||||
|
summary, hover over the icon on top-right corner; a similar popup window will
|
||||||
|
be displayed when appropriate.
|
||||||
|
|
||||||
Use the download button on top left corner of the *Explain* canvas to download
|
Use the download button on top left corner of the *Explain* canvas to download
|
||||||
the plan as an SVG file.
|
the plan as an SVG file.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ notes for it.
|
|||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
|
release_notes_4_11
|
||||||
release_notes_4_10
|
release_notes_4_10
|
||||||
release_notes_4_9
|
release_notes_4_9
|
||||||
release_notes_4_8
|
release_notes_4_8
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
************
|
||||||
|
Version 4.10
|
||||||
|
************
|
||||||
|
|
||||||
|
Release date: 2019-07-25
|
||||||
|
|
||||||
|
This release contains a number of bug fixes and new features since the release of pgAdmin4 4.10.
|
||||||
|
|
||||||
|
New features
|
||||||
|
************
|
||||||
|
|
||||||
|
| `Feature #4335 <https://redmine.postgresql.org/issues/4335>`_ - Add EXPLAIN options for SETTINGS and SUMMARY.
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
*********
|
||||||
|
|
||||||
|
| `Bug #4224 <https://redmine.postgresql.org/issues/4224>`_ - Prevent flickering of large tooltips on the Graphical EXPLAIN canvas.
|
||||||
|
| `Bug #4395 <https://redmine.postgresql.org/issues/4395>`_ - EXPLAIN options should be Query Tool instance-specific.
|
||||||
@@ -30,7 +30,6 @@
|
|||||||
.pg-explain-stats-btn {
|
.pg-explain-stats-btn {
|
||||||
top: 5px;
|
top: 5px;
|
||||||
min-width: 25px;
|
min-width: 25px;
|
||||||
border: 1px solid transparent;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -427,14 +427,7 @@ define('pgadmin.misc.explain', [
|
|||||||
|
|
||||||
// Calculate co-ordinates for tooltip
|
// Calculate co-ordinates for tooltip
|
||||||
var toolTipX = ((currentXpos + pWIDTH) * zoomFactor - graphContainer.scrollLeft());
|
var toolTipX = ((currentXpos + pWIDTH) * zoomFactor - graphContainer.scrollLeft());
|
||||||
var toolTipY = ((currentYpos + pHEIGHT) * zoomFactor - graphContainer.scrollTop());
|
var toolTipY = ((currentYpos) * zoomFactor - graphContainer.scrollTop());
|
||||||
|
|
||||||
// Recalculate x.y if tooltip is going out of screen
|
|
||||||
if (graphContainer.width() < (toolTipX + toolTipContainer[0].clientWidth))
|
|
||||||
toolTipX -= (toolTipContainer[0].clientWidth + (pWIDTH * zoomFactor));
|
|
||||||
//if(document.children[0].clientHeight < (toolTipY + toolTipContainer[0].clientHeight))
|
|
||||||
if (graphContainer.height() < (toolTipY + toolTipContainer[0].clientHeight))
|
|
||||||
toolTipY -= (toolTipContainer[0].clientHeight + ((pHEIGHT / 2) * zoomFactor));
|
|
||||||
|
|
||||||
toolTipX = toolTipX < 0 ? 0 : (toolTipX);
|
toolTipX = toolTipX < 0 ? 0 : (toolTipX);
|
||||||
toolTipY = toolTipY < 0 ? 0 : (toolTipY);
|
toolTipY = toolTipY < 0 ? 0 : (toolTipY);
|
||||||
@@ -727,6 +720,19 @@ define('pgadmin.misc.explain', [
|
|||||||
delete data ['Triggers'];
|
delete data ['Triggers'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(data) {
|
||||||
|
let summKeys = ['Planning Time', 'Execution Time'],
|
||||||
|
summary = {};
|
||||||
|
|
||||||
|
summKeys.forEach((key)=>{
|
||||||
|
if (key in data) {
|
||||||
|
summary[key] = data[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
statistics.set('Summary', summary);
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
toJSON: function() {
|
toJSON: function() {
|
||||||
|
|||||||
@@ -15,81 +15,102 @@ let StatisticsModel = Backbone.Model.extend({
|
|||||||
defaults: {
|
defaults: {
|
||||||
JIT: [],
|
JIT: [],
|
||||||
Triggers: [],
|
Triggers: [],
|
||||||
|
Summary: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
set_statistics: function(toolTipContainer) {
|
set_statistics: function(toolTipContainer) {
|
||||||
var jit_stats = this.get('JIT'),
|
var jit_stats = this.get('JIT'),
|
||||||
triggers_stats = this.get('Triggers');
|
triggers_stats = this.get('Triggers'),
|
||||||
|
summary = this.get('Summary');
|
||||||
|
|
||||||
if (Object.keys(jit_stats).length > 0 ||
|
if (Object.keys(jit_stats).length > 0 ||
|
||||||
Object.keys(triggers_stats).length > 0) {
|
Object.keys(triggers_stats).length > 0 ||
|
||||||
|
Object.keys(summary).length > 0) {
|
||||||
$('.pg-explain-stats-area').removeClass('d-none');
|
$('.pg-explain-stats-area').removeClass('d-none');
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.pg-explain-stats-area').on('mouseover', () => {
|
var tooltip = $('<table></table>', {
|
||||||
|
class: 'pgadmin-tooltip-table',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Object.keys(jit_stats).length > 0){
|
||||||
|
tooltip.append('<tr><td class="label explain-tooltip">JIT:</td></tr>');
|
||||||
|
_.each(jit_stats, function(value, key) {
|
||||||
|
key = _.escape(key);
|
||||||
|
value = _.escape(value);
|
||||||
|
tooltip.append(`
|
||||||
|
<tr>
|
||||||
|
<td class="label explain-tooltip"> ${key}</td>
|
||||||
|
<td class="label explain-tooltip-val">${value}</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(triggers_stats).length > 0){
|
||||||
|
tooltip.append('<tr><td class="label explain-tooltip">Triggers:</td></tr>');
|
||||||
|
_.each(triggers_stats, function(triggers, key_id) {
|
||||||
|
if (triggers instanceof Object) {
|
||||||
|
_.each(triggers, function(value, key) {
|
||||||
|
if (key === 'Trigger Name') {
|
||||||
|
key = _.escape(key);
|
||||||
|
value = _.escape(value);
|
||||||
|
tooltip.append(`
|
||||||
|
<tr>
|
||||||
|
<td class="label explain-tooltip"> ${key}</td>
|
||||||
|
<td class="label explain-tooltip-val">${value}</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
key = _.escape(key);
|
||||||
|
value = _.escape(value);
|
||||||
|
tooltip.append(`
|
||||||
|
<tr>
|
||||||
|
<td class="label explain-tooltip"> ${key}</td>
|
||||||
|
<td class="label explain-tooltip-val">${value}</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
key_id = _.escape(key_id);
|
||||||
|
triggers = _.escape(triggers);
|
||||||
|
tooltip.append(`
|
||||||
|
<tr>
|
||||||
|
<td class="label explain-tooltip"> ${key_id}</td>
|
||||||
|
<td class="label explain-tooltip-val">${triggers}</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(summary).length > 0){
|
||||||
|
tooltip.append('<tr><td class="label explain-tooltip">Summary:</td></tr>');
|
||||||
|
_.each(summary, function(value, key) {
|
||||||
|
key = _.escape(key);
|
||||||
|
value = _.escape(value);
|
||||||
|
tooltip.append(`
|
||||||
|
<tr>
|
||||||
|
<td class="label explain-tooltip"> ${key}</td>
|
||||||
|
<td class="label explain-tooltip-val">${value}</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.pg-explain-stats-area').off('mouseover').on('mouseover', () => {
|
||||||
// Empty the tooltip content if it has any and add new data
|
// Empty the tooltip content if it has any and add new data
|
||||||
toolTipContainer.empty();
|
|
||||||
if (Object.keys(jit_stats).length == 0 &&
|
if (Object.keys(jit_stats).length == 0 &&
|
||||||
Object.keys(triggers_stats).length == 0) {
|
Object.keys(triggers_stats).length == 0 &&
|
||||||
|
Object.keys(summary).length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tooltip = $('<table></table>', {
|
toolTipContainer.empty();
|
||||||
class: 'pgadmin-tooltip-table',
|
toolTipContainer.append(tooltip);
|
||||||
}).appendTo(toolTipContainer);
|
|
||||||
|
|
||||||
if (Object.keys(jit_stats).length > 0){
|
|
||||||
tooltip.append('<tr><td class="label explain-tooltip">JIT:</td></tr>');
|
|
||||||
_.each(jit_stats, function(value, key) {
|
|
||||||
key = _.escape(key);
|
|
||||||
value = _.escape(value);
|
|
||||||
tooltip.append(`
|
|
||||||
<tr>
|
|
||||||
<td class="label explain-tooltip"> ${key}</td>
|
|
||||||
<td class="label explain-tooltip-val">${value}</td>
|
|
||||||
</tr>
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(triggers_stats).length > 0){
|
|
||||||
tooltip.append('<tr><td class="label explain-tooltip">Triggers:</td></tr>');
|
|
||||||
_.each(triggers_stats, function(triggers, key_id) {
|
|
||||||
if (triggers instanceof Object) {
|
|
||||||
_.each(triggers, function(value, key) {
|
|
||||||
if (key === 'Trigger Name') {
|
|
||||||
key = _.escape(key);
|
|
||||||
value = _.escape(value);
|
|
||||||
tooltip.append(`
|
|
||||||
<tr>
|
|
||||||
<td class="label explain-tooltip"> ${key}</td>
|
|
||||||
<td class="label explain-tooltip-val">${value}</td>
|
|
||||||
</tr>
|
|
||||||
`);
|
|
||||||
} else {
|
|
||||||
key = _.escape(key);
|
|
||||||
value = _.escape(value);
|
|
||||||
tooltip.append(`
|
|
||||||
<tr>
|
|
||||||
<td class="label explain-tooltip"> ${key}</td>
|
|
||||||
<td class="label explain-tooltip-val">${value}</td>
|
|
||||||
</tr>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
key_id = _.escape(key_id);
|
|
||||||
triggers = _.escape(triggers);
|
|
||||||
tooltip.append(`
|
|
||||||
<tr>
|
|
||||||
<td class="label explain-tooltip"> ${key_id}</td>
|
|
||||||
<td class="label explain-tooltip-val">${triggers}</td>
|
|
||||||
</tr>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show toolTip at respective x,y coordinates
|
// Show toolTip at respective x,y coordinates
|
||||||
toolTipContainer.css({
|
toolTipContainer.css({
|
||||||
@@ -104,12 +125,13 @@ let StatisticsModel = Backbone.Model.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Remove tooltip when mouse is out from node's area
|
// Remove tooltip when mouse is out from node's area
|
||||||
$('.pg-explain-stats-area').on('mouseout', () => {
|
$('.pg-explain-stats-area').off('mouseout').on('mouseout', () => {
|
||||||
toolTipContainer.empty();
|
toolTipContainer.empty();
|
||||||
toolTipContainer.css({
|
toolTipContainer.css({
|
||||||
'opacity': '0',
|
'opacity': '0',
|
||||||
'left': 0,
|
'left': 0,
|
||||||
'top': 0,
|
'top': 0,
|
||||||
|
'right': '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -282,8 +282,9 @@ function keyboardShortcutsQueryTool(
|
|||||||
currLi = currLi.next();
|
currLi = currLi.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*do not focus on divider and disabled */
|
/*do not focus on divider, disabled and d-none */
|
||||||
while(currLi.hasClass('dropdown-divider')
|
while(currLi.hasClass('dropdown-divider')
|
||||||
|
|| currLi.hasClass('d-none')
|
||||||
|| currLi.find('.dropdown-item').first().hasClass('disabled')) {
|
|| currLi.find('.dropdown-item').first().hasClass('disabled')) {
|
||||||
if(keyCode === UP_KEY) {
|
if(keyCode === UP_KEY) {
|
||||||
currLi = currLi.prev();
|
currLi = currLi.prev();
|
||||||
|
|||||||
@@ -26,6 +26,14 @@ let queryToolActions = {
|
|||||||
return !$('.explain-timing').hasClass('visibility-hidden');
|
return !$('.explain-timing').hasClass('visibility-hidden');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_summary: function () {
|
||||||
|
return !$('.explain-summary').hasClass('visibility-hidden');
|
||||||
|
},
|
||||||
|
|
||||||
|
_settings: function () {
|
||||||
|
return !$('.explain-settings').hasClass('visibility-hidden');
|
||||||
|
},
|
||||||
|
|
||||||
_clearMessageTab: function () {
|
_clearMessageTab: function () {
|
||||||
$('.sql-editor-message').html('');
|
$('.sql-editor-message').html('');
|
||||||
},
|
},
|
||||||
@@ -41,36 +49,31 @@ let queryToolActions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
explainAnalyze: function (sqlEditorController) {
|
explainAnalyze: function (sqlEditorController) {
|
||||||
let costEnabled = this._costsEnabled();
|
|
||||||
let verbose = this._verbose();
|
|
||||||
let buffers = this._buffers();
|
|
||||||
let timing = this._timing();
|
|
||||||
const explainObject = {
|
const explainObject = {
|
||||||
format: 'json',
|
format: 'json',
|
||||||
analyze: true,
|
analyze: true,
|
||||||
verbose: verbose,
|
verbose: this._verbose(),
|
||||||
costs: costEnabled,
|
costs: this._costsEnabled(),
|
||||||
buffers: buffers,
|
buffers: this._buffers(),
|
||||||
timing: timing,
|
timing: this._timing(),
|
||||||
summary: false,
|
summary: this._summary(),
|
||||||
|
settings: this._settings(),
|
||||||
};
|
};
|
||||||
this._clearMessageTab();
|
this._clearMessageTab();
|
||||||
sqlEditorController.execute(explainObject);
|
sqlEditorController.execute(explainObject);
|
||||||
},
|
},
|
||||||
|
|
||||||
explain: function (sqlEditorController) {
|
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) `;
|
// let explainQuery = `EXPLAIN (FORMAT JSON, ANALYZE OFF, VERBOSE ${verbose}, COSTS ${costEnabled}, BUFFERS OFF, TIMING OFF) `;
|
||||||
const explainObject = {
|
const explainObject = {
|
||||||
format: 'json',
|
format: 'json',
|
||||||
analyze: false,
|
analyze: false,
|
||||||
verbose: verbose,
|
verbose: this._verbose(),
|
||||||
costs: costEnabled,
|
costs: this._costsEnabled(),
|
||||||
buffers: false,
|
buffers: false,
|
||||||
timing: false,
|
timing: false,
|
||||||
summary: false,
|
summary: this._summary(),
|
||||||
|
settings: this._settings(),
|
||||||
};
|
};
|
||||||
this._clearMessageTab();
|
this._clearMessageTab();
|
||||||
sqlEditorController.execute(explainObject);
|
sqlEditorController.execute(explainObject);
|
||||||
|
|||||||
@@ -134,6 +134,20 @@ function updateUIPreferences(sqlEditor) {
|
|||||||
$el.find('.explain-timing').addClass('visibility-hidden');
|
$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 */
|
/* Connection status check */
|
||||||
/* remove the status checker if present */
|
/* remove the status checker if present */
|
||||||
if(sqlEditor.connIntervalId != null) {
|
if(sqlEditor.connIntervalId != null) {
|
||||||
|
|||||||
@@ -234,6 +234,11 @@ def panel(trans_id, is_query_tool, editor_title):
|
|||||||
else:
|
else:
|
||||||
server_type = None
|
server_type = None
|
||||||
|
|
||||||
|
if request.args and 'server_ver' in request.args:
|
||||||
|
server_ver = request.args['server_ver']
|
||||||
|
else:
|
||||||
|
server_ver = 0
|
||||||
|
|
||||||
# If title has slash(es) in it then replace it
|
# If title has slash(es) in it then replace it
|
||||||
if request.args and request.args['fslashes'] != '':
|
if request.args and request.args['fslashes'] != '':
|
||||||
try:
|
try:
|
||||||
@@ -311,6 +316,7 @@ def panel(trans_id, is_query_tool, editor_title):
|
|||||||
is_desktop_mode=app.PGADMIN_RUNTIME,
|
is_desktop_mode=app.PGADMIN_RUNTIME,
|
||||||
is_linux=is_linux_platform,
|
is_linux=is_linux_platform,
|
||||||
server_type=server_type,
|
server_type=server_type,
|
||||||
|
server_ver=server_ver,
|
||||||
client_platform=user_agent.platform,
|
client_platform=user_agent.platform,
|
||||||
bgcolor=bgcolor,
|
bgcolor=bgcolor,
|
||||||
fgcolor=fgcolor,
|
fgcolor=fgcolor,
|
||||||
@@ -408,7 +414,8 @@ def initialize_query_tool(sgid, sid, did=None):
|
|||||||
|
|
||||||
return make_json_response(
|
return make_json_response(
|
||||||
data={
|
data={
|
||||||
'gridTransId': trans_id
|
'gridTransId': trans_id,
|
||||||
|
'serverVersion': manager.version,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -275,6 +275,7 @@ define('pgadmin.datagrid', [
|
|||||||
baseUrl = url_for('datagrid.panel', url_params) +
|
baseUrl = url_for('datagrid.panel', url_params) +
|
||||||
'?' + 'query_url=' + encodeURI(trans_obj.sURL) +
|
'?' + 'query_url=' + encodeURI(trans_obj.sURL) +
|
||||||
'&server_type=' + encodeURIComponent(trans_obj.server_type) +
|
'&server_type=' + encodeURIComponent(trans_obj.server_type) +
|
||||||
|
'&server_ver=' + trans_obj.serverVersion+
|
||||||
'&fslashes=' + titileForURLObj.slashLocations;
|
'&fslashes=' + titileForURLObj.slashLocations;
|
||||||
|
|
||||||
if (self.preferences.new_browser_tab) {
|
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
|
// add a load listener to the window so that the title gets changed on page load
|
||||||
newWin.addEventListener('load', function() {
|
newWin.addEventListener('load', function() {
|
||||||
newWin.document.title = panel_title;
|
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 {
|
} else {
|
||||||
|
|||||||
@@ -285,6 +285,18 @@
|
|||||||
<span> {{ _('Timing') }} </span>
|
<span> {{ _('Timing') }} </span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li data-min-ver="100000">
|
||||||
|
<a class="dropdown-item" id="btn-explain-summary" href="#" tabindex="0">
|
||||||
|
<i class="explain-summary fa fa-check visibility-hidden" aria-hidden="true"></i>
|
||||||
|
<span> {{ _('Summary') }} </span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li data-min-ver="120000">
|
||||||
|
<a class="dropdown-item" id="btn-explain-settings" href="#" tabindex="0">
|
||||||
|
<i class="explain-settings fa fa-check visibility-hidden" aria-hidden="true"></i>
|
||||||
|
<span> {{ _('Settings') }} </span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group mr-1" role="group" aria-label="">
|
<div class="btn-group mr-1" role="group" aria-label="">
|
||||||
@@ -412,7 +424,8 @@
|
|||||||
script_type_url,
|
script_type_url,
|
||||||
"{{ server_type }}",
|
"{{ server_type }}",
|
||||||
{{ url_params|safe}},
|
{{ url_params|safe}},
|
||||||
'{{ layout|safe }}'
|
'{{ layout|safe }}',
|
||||||
|
{{ server_ver }}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -95,7 +95,6 @@ class SqlEditorModule(PgAdminModule):
|
|||||||
return [
|
return [
|
||||||
'sqleditor.view_data_start',
|
'sqleditor.view_data_start',
|
||||||
'sqleditor.query_tool_start',
|
'sqleditor.query_tool_start',
|
||||||
'sqleditor.query_tool_preferences',
|
|
||||||
'sqleditor.poll',
|
'sqleditor.poll',
|
||||||
'sqleditor.fetch',
|
'sqleditor.fetch',
|
||||||
'sqleditor.fetch_all',
|
'sqleditor.fetch_all',
|
||||||
@@ -330,38 +329,6 @@ def extract_sql_from_network_parameters(request_data, request_arguments,
|
|||||||
return request_arguments or request_form_data
|
return request_arguments or request_form_data
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route(
|
|
||||||
'/query_tool/preferences/<int:trans_id>',
|
|
||||||
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/<int:trans_id>', methods=["GET"], endpoint='poll')
|
@blueprint.route('/poll/<int:trans_id>', methods=["GET"], endpoint='poll')
|
||||||
@login_required
|
@login_required
|
||||||
def poll(trans_id):
|
def poll(trans_id):
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ define('tools.querytool', [
|
|||||||
this.handler.preferences = this.preferences;
|
this.handler.preferences = this.preferences;
|
||||||
this.connIntervalId = null;
|
this.connIntervalId = null;
|
||||||
this.layout = opts.layout;
|
this.layout = opts.layout;
|
||||||
|
this.set_server_version(opts.server_ver);
|
||||||
},
|
},
|
||||||
|
|
||||||
// Bind all the events
|
// Bind all the events
|
||||||
@@ -121,6 +122,8 @@ define('tools.querytool', [
|
|||||||
'click #btn-explain-costs': 'on_explain_costs',
|
'click #btn-explain-costs': 'on_explain_costs',
|
||||||
'click #btn-explain-buffers': 'on_explain_buffers',
|
'click #btn-explain-buffers': 'on_explain_buffers',
|
||||||
'click #btn-explain-timing': 'on_explain_timing',
|
'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',
|
'change .limit': 'on_limit_change',
|
||||||
'keydown': 'keyAction',
|
'keydown': 'keyAction',
|
||||||
// Comment options
|
// Comment options
|
||||||
@@ -166,6 +169,22 @@ define('tools.querytool', [
|
|||||||
docker.addPanel('notifications', wcDocker.DOCK.STACKED, data_output_panel);
|
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.
|
// This function is used to render the template.
|
||||||
render: function() {
|
render: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -1855,6 +1874,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) {
|
do_not_close_menu: function(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
},
|
},
|
||||||
@@ -2117,7 +2161,7 @@ define('tools.querytool', [
|
|||||||
* header and loading icon and start execution of the sql query.
|
* header and loading icon and start execution of the sql query.
|
||||||
*/
|
*/
|
||||||
start: function(transId, is_query_tool, editor_title, script_type_url,
|
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;
|
var self = this;
|
||||||
|
|
||||||
@@ -2140,6 +2184,7 @@ define('tools.querytool', [
|
|||||||
el: self.container,
|
el: self.container,
|
||||||
handler: self,
|
handler: self,
|
||||||
layout: layout,
|
layout: layout,
|
||||||
|
server_ver: server_ver,
|
||||||
});
|
});
|
||||||
self.transId = self.gridView.transId = transId;
|
self.transId = self.gridView.transId = transId;
|
||||||
|
|
||||||
@@ -2233,6 +2278,8 @@ define('tools.querytool', [
|
|||||||
self.on('pgadmin-sqleditor:button:explain-costs', self._explain_costs, self);
|
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-buffers', self._explain_buffers, self);
|
||||||
self.on('pgadmin-sqleditor:button:explain-timing', self._explain_timing, 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
|
// Indentation related
|
||||||
self.on('pgadmin-sqleditor:indent_selected_code', self._indent_selected_code, self);
|
self.on('pgadmin-sqleditor:indent_selected_code', self._indent_selected_code, self);
|
||||||
self.on('pgadmin-sqleditor:unindent_selected_code', self._unindent_selected_code, self);
|
self.on('pgadmin-sqleditor:unindent_selected_code', self._unindent_selected_code, self);
|
||||||
@@ -3888,108 +3935,37 @@ define('tools.querytool', [
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
explainPreferenceUpdate: function(subItem, data, caller) {
|
_toggle_explain_option: function(type) {
|
||||||
let self = this;
|
let selector = `.explain-${type}`;
|
||||||
$.ajax({
|
$(selector).toggleClass('visibility-hidden');
|
||||||
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);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// This function will toggle "verbose" option in explain
|
// This function will toggle "verbose" option in explain
|
||||||
_explain_verbose: function() {
|
_explain_verbose: function() {
|
||||||
var self = this;
|
this._toggle_explain_option('verbose');
|
||||||
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 function will toggle "costs" option in explain
|
// This function will toggle "costs" option in explain
|
||||||
_explain_costs: function() {
|
_explain_costs: function() {
|
||||||
var self = this;
|
this._toggle_explain_option('costs');
|
||||||
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 function will toggle "buffers" option in explain
|
// This function will toggle "buffers" option in explain
|
||||||
_explain_buffers: function() {
|
_explain_buffers: function() {
|
||||||
var self = this;
|
this._toggle_explain_option('buffers');
|
||||||
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 function will toggle "timing" option in explain
|
// This function will toggle "timing" option in explain
|
||||||
_explain_timing: function() {
|
_explain_timing: function() {
|
||||||
var self = this;
|
this._toggle_explain_option('timing');
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.explainPreferenceUpdate(
|
_explain_summary: function() {
|
||||||
'timing', {
|
this._toggle_explain_option('summary');
|
||||||
'explain_timing': explain_timing,
|
},
|
||||||
}, '_explain_timing'
|
|
||||||
);
|
_explain_settings: function() {
|
||||||
|
this._toggle_explain_option('settings');
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
|
{% import 'sql/macros/utils.macros' as UTILS %}
|
||||||
EXPLAIN ({% if format -%}
|
EXPLAIN ({% if format -%}
|
||||||
FORMAT {{ format.upper() }},
|
FORMAT {{ format.upper() }}
|
||||||
{%- endif %}{% if analyze is defined -%}
|
{%- endif %}{% if analyze is defined -%}
|
||||||
ANALYZE {{ analyze }},
|
, ANALYZE {{ UTILS.BOOL_TEXT(analyze) }}
|
||||||
{%- endif %}{% if verbose is defined -%}
|
{%- endif %}{% if verbose is defined -%}
|
||||||
VERBOSE {{ verbose }},
|
, VERBOSE {{ UTILS.BOOL_TEXT(verbose) }}
|
||||||
{%- endif %}{% if costs is defined -%}
|
{%- endif %}{% if costs is defined -%}
|
||||||
COSTS {{ costs }},
|
, COSTS {{ UTILS.BOOL_TEXT(costs) }}
|
||||||
{%- endif %}{% if timing is defined -%}
|
{%- endif %}{% if timing is defined -%}
|
||||||
TIMING {{ timing }},
|
, TIMING {{ UTILS.BOOL_TEXT(timing) }}
|
||||||
{%- endif %}{% if summary is defined -%}
|
|
||||||
SUMMARY {{ summary }},
|
|
||||||
{%- endif %}{% if buffers is defined -%}
|
{%- endif %}{% if buffers is defined -%}
|
||||||
BUFFERS {{ buffers }}
|
, BUFFERS {{ UTILS.BOOL_TEXT(buffers) }}
|
||||||
|
{%- endif %}{% if summary is defined -%}
|
||||||
|
, SUMMARY {{ UTILS.BOOL_TEXT(summary) }}
|
||||||
{%- endif %}) {{ sql }}
|
{%- endif %}) {{ sql }}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{% import 'sql/macros/utils.macros' as UTILS %}
|
||||||
|
EXPLAIN ({% if format -%}
|
||||||
|
FORMAT {{ format.upper() }}
|
||||||
|
{%- endif %}{% if analyze is defined -%}
|
||||||
|
, ANALYZE {{ UTILS.BOOL_TEXT(analyze) }}
|
||||||
|
{%- endif %}{% if verbose is defined -%}
|
||||||
|
, VERBOSE {{ UTILS.BOOL_TEXT(verbose) }}
|
||||||
|
{%- endif %}{% if costs is defined -%}
|
||||||
|
, COSTS {{ UTILS.BOOL_TEXT(costs) }}
|
||||||
|
{%- endif %}{% if timing is defined -%}
|
||||||
|
, TIMING {{ UTILS.BOOL_TEXT(timing) }}
|
||||||
|
{%- endif %}{% if buffers is defined -%}
|
||||||
|
, BUFFERS {{ UTILS.BOOL_TEXT(buffers) }}
|
||||||
|
{%- endif %}{% if summary is defined -%}
|
||||||
|
, SUMMARY {{ UTILS.BOOL_TEXT(summary) }}
|
||||||
|
{%- endif %}{% if settings is defined -%}
|
||||||
|
, SETTINGS {{ UTILS.BOOL_TEXT(settings) }}
|
||||||
|
{%- endif %}) {{ sql }}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
|
{% import 'sql/macros/utils.macros' as UTILS %}
|
||||||
EXPLAIN ({% if format -%}
|
EXPLAIN ({% if format -%}
|
||||||
FORMAT {{ format.upper() }},
|
FORMAT {{ format.upper() }}
|
||||||
{%- endif %}{% if analyze is defined -%}
|
{%- endif %}{% if analyze is defined -%}
|
||||||
ANALYZE {{ analyze }},
|
, ANALYZE {{ UTILS.BOOL_TEXT(analyze) }}
|
||||||
{%- endif %}{% if verbose is defined -%}
|
{%- endif %}{% if verbose is defined -%}
|
||||||
VERBOSE {{ verbose }},
|
, VERBOSE {{ UTILS.BOOL_TEXT(verbose) }}
|
||||||
{%- endif %}{% if costs is defined -%}
|
{%- endif %}{% if costs is defined -%}
|
||||||
COSTS {{ costs }},
|
, COSTS {{ UTILS.BOOL_TEXT(costs) }}
|
||||||
{%- endif %}{% if timing is defined -%}
|
{%- endif %}{% if timing is defined -%}
|
||||||
TIMING {{ timing }},
|
, TIMING {{ UTILS.BOOL_TEXT(timing) }}
|
||||||
{%- endif %}{% if buffers is defined -%}
|
{%- endif %}{% if buffers is defined -%}
|
||||||
BUFFERS {{ buffers }}
|
, BUFFERS {{ UTILS.BOOL_TEXT(buffers) }}
|
||||||
{%- endif %}) {{ sql }}
|
{%- endif %}) {{ sql }}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
|
{% import 'sql/macros/utils.macros' as UTILS %}
|
||||||
EXPLAIN ({% if format -%}
|
EXPLAIN ({% if format -%}
|
||||||
FORMAT {{ format.upper() }},
|
FORMAT {{ format.upper() }}
|
||||||
{%- endif %}{% if analyze is defined -%}
|
{%- endif %}{% if analyze is defined -%}
|
||||||
ANALYZE {{ analyze }},{%- endif %}{% if verbose is defined -%}
|
, ANALYZE {{ UTILS.BOOL_TEXT(analyze) }}
|
||||||
VERBOSE {{ verbose }},
|
{%- endif %}{% if verbose is defined -%}
|
||||||
|
, VERBOSE {{ UTILS.BOOL_TEXT(verbose) }}
|
||||||
{%- endif %}{% if costs is defined -%}
|
{%- endif %}{% if costs is defined -%}
|
||||||
COSTS {{ costs }},
|
, COSTS {{ UTILS.BOOL_TEXT(costs) }}
|
||||||
|
{%- endif %}{% if timing is defined -%}
|
||||||
|
, TIMING {{ UTILS.BOOL_TEXT(timing) }}
|
||||||
{%- endif %}{% if buffers is defined -%}
|
{%- endif %}{% if buffers is defined -%}
|
||||||
BUFFERS {{ buffers }}
|
, BUFFERS {{ UTILS.BOOL_TEXT(buffers) }}
|
||||||
{%- endif %}) {{ sql }}
|
{%- endif %}) {{ sql }}
|
||||||
@@ -10,10 +10,11 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from flask import Flask, render_template
|
from flask import Flask, render_template
|
||||||
from jinja2 import FileSystemLoader
|
from jinja2 import FileSystemLoader, ChoiceLoader
|
||||||
|
|
||||||
from pgadmin import VersionedTemplateLoader
|
from pgadmin import VersionedTemplateLoader
|
||||||
from pgadmin.utils.route import BaseTestGenerator
|
from pgadmin.utils.route import BaseTestGenerator
|
||||||
|
from pgadmin import tools
|
||||||
|
|
||||||
|
|
||||||
class TestExplainPlanTemplates(BaseTestGenerator):
|
class TestExplainPlanTemplates(BaseTestGenerator):
|
||||||
@@ -34,9 +35,9 @@ class TestExplainPlanTemplates(BaseTestGenerator):
|
|||||||
),
|
),
|
||||||
sql_statement='SELECT * FROM places',
|
sql_statement='SELECT * FROM places',
|
||||||
expected_return_value='EXPLAIN '
|
expected_return_value='EXPLAIN '
|
||||||
'(FORMAT XML,ANALYZE True,'
|
'(FORMAT XML, ANALYZE true, '
|
||||||
'VERBOSE True,COSTS False,'
|
'VERBOSE true, COSTS false, '
|
||||||
'BUFFERS True) SELECT * FROM places'
|
'BUFFERS true) SELECT * FROM places'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -52,7 +53,7 @@ class TestExplainPlanTemplates(BaseTestGenerator):
|
|||||||
),
|
),
|
||||||
sql_statement='SELECT * FROM places',
|
sql_statement='SELECT * FROM places',
|
||||||
expected_return_value='EXPLAIN '
|
expected_return_value='EXPLAIN '
|
||||||
'(FORMAT JSON,BUFFERS True) '
|
'(FORMAT JSON, BUFFERS true) '
|
||||||
'SELECT * FROM places'
|
'SELECT * FROM places'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -70,8 +71,8 @@ class TestExplainPlanTemplates(BaseTestGenerator):
|
|||||||
),
|
),
|
||||||
sql_statement='SELECT * FROM places',
|
sql_statement='SELECT * FROM places',
|
||||||
expected_return_value='EXPLAIN '
|
expected_return_value='EXPLAIN '
|
||||||
'(FORMAT JSON,TIMING False,'
|
'(FORMAT JSON, TIMING false, '
|
||||||
'BUFFERS True) SELECT * FROM places'
|
'BUFFERS true) SELECT * FROM places'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
@@ -89,8 +90,30 @@ class TestExplainPlanTemplates(BaseTestGenerator):
|
|||||||
),
|
),
|
||||||
sql_statement='SELECT * FROM places',
|
sql_statement='SELECT * FROM places',
|
||||||
expected_return_value='EXPLAIN '
|
expected_return_value='EXPLAIN '
|
||||||
'(FORMAT YAML,TIMING False,'
|
'(FORMAT YAML, TIMING false, '
|
||||||
'SUMMARY True,BUFFERS True) '
|
'BUFFERS true, SUMMARY true) '
|
||||||
|
'SELECT * FROM places'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'When rendering Postgres 12 template, '
|
||||||
|
'when settings is present,'
|
||||||
|
'it returns the explain plan with settings',
|
||||||
|
dict(
|
||||||
|
template_path='sqleditor/sql/12_plus/explain_plan.sql',
|
||||||
|
input_parameters=dict(
|
||||||
|
sql='SELECT * FROM places',
|
||||||
|
format='json',
|
||||||
|
buffers=False,
|
||||||
|
timing=False,
|
||||||
|
summary=False,
|
||||||
|
settings=True
|
||||||
|
),
|
||||||
|
sql_statement='SELECT * FROM places',
|
||||||
|
expected_return_value='EXPLAIN '
|
||||||
|
'(FORMAT JSON, TIMING false, '
|
||||||
|
'BUFFERS false, SUMMARY false, '
|
||||||
|
'SETTINGS true) '
|
||||||
'SELECT * FROM places'
|
'SELECT * FROM places'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@@ -153,6 +176,11 @@ class TestExplainPlanTemplates(BaseTestGenerator):
|
|||||||
class FakeApp(Flask):
|
class FakeApp(Flask):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(FakeApp, self).__init__("")
|
super(FakeApp, self).__init__("")
|
||||||
self.jinja_loader = FileSystemLoader(
|
self.jinja_loader = ChoiceLoader([
|
||||||
os.path.dirname(os.path.realpath(__file__)) + "/../templates"
|
FileSystemLoader(
|
||||||
)
|
os.path.dirname(os.path.realpath(__file__)) + "/../templates"
|
||||||
|
),
|
||||||
|
FileSystemLoader(
|
||||||
|
os.path.join(os.path.dirname(tools.__file__), 'templates')
|
||||||
|
)
|
||||||
|
])
|
||||||
@@ -62,6 +62,18 @@ def RegisterQueryToolPreferences(self):
|
|||||||
category_label=gettext('Explain')
|
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(
|
self.auto_commit = self.preference.register(
|
||||||
'Options', 'auto_commit',
|
'Options', 'auto_commit',
|
||||||
gettext("Auto commit?"), 'boolean', True,
|
gettext("Auto commit?"), 'boolean', True,
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{% macro BOOL_TEXT(bool_val) %}
|
||||||
|
{% if bool_val %}true{% else %}false{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
@@ -30,6 +30,7 @@ describe('ExplainStatistics', () => {
|
|||||||
|
|
||||||
statsModel.set('JIT', []);
|
statsModel.set('JIT', []);
|
||||||
statsModel.set('Triggers', []);
|
statsModel.set('Triggers', []);
|
||||||
|
statsModel.set('Summary', {});
|
||||||
statsModel.set_statistics(tooltipContainer);
|
statsModel.set_statistics(tooltipContainer);
|
||||||
|
|
||||||
expect($('.pg-explain-stats-area').hasClass('d-none')).toEqual(true);
|
expect($('.pg-explain-stats-area').hasClass('d-none')).toEqual(true);
|
||||||
@@ -93,4 +94,37 @@ describe('ExplainStatistics', () => {
|
|||||||
expect(tooltipContainer.css('opacity')).toEqual('0');
|
expect(tooltipContainer.css('opacity')).toEqual('0');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Summary', () => {
|
||||||
|
beforeEach(function() {
|
||||||
|
$('body').append(statsDiv);
|
||||||
|
statsModel.set('JIT', []);
|
||||||
|
statsModel.set('Triggers', []);
|
||||||
|
statsModel.set('Summary', {
|
||||||
|
'Planning Time': 0.12,
|
||||||
|
'Execution Time': 2.34,
|
||||||
|
});
|
||||||
|
statsModel.set_statistics(tooltipContainer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Statistics button should be visible', () => {
|
||||||
|
expect($('.pg-explain-stats-area').hasClass('d-none')).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Mouse over event should be trigger', () => {
|
||||||
|
// Trigger mouse over event
|
||||||
|
var hoverEvent = new $.Event('mouseover');
|
||||||
|
$('.pg-explain-stats-area').trigger(hoverEvent);
|
||||||
|
|
||||||
|
expect(tooltipContainer.css('opacity')).toEqual('0.8');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Mouse out event should be trigger', () => {
|
||||||
|
// Trigger mouse out event
|
||||||
|
var hoverEvent = new $.Event('mouseout');
|
||||||
|
$('.pg-explain-stats-area').trigger(hoverEvent);
|
||||||
|
|
||||||
|
expect(tooltipContainer.css('opacity')).toEqual('0');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -55,6 +55,8 @@ describe('queryToolActions', () => {
|
|||||||
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
|
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
|
||||||
spyOn(queryToolActions, '_buffers').and.returnValue(false);
|
spyOn(queryToolActions, '_buffers').and.returnValue(false);
|
||||||
spyOn(queryToolActions, '_timing').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', () => {
|
it('calls the execute function', () => {
|
||||||
@@ -69,19 +71,22 @@ describe('queryToolActions', () => {
|
|||||||
buffers: false,
|
buffers: false,
|
||||||
timing: false,
|
timing: false,
|
||||||
summary: false,
|
summary: false,
|
||||||
|
settings: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when verbose and costs and buffers and timing are all selected', () => {
|
describe('when all options are selected', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
setUpSpies('', '');
|
setUpSpies('', '');
|
||||||
spyOn(queryToolActions, '_verbose').and.returnValue(true);
|
spyOn(queryToolActions, '_verbose').and.returnValue(true);
|
||||||
spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
|
spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
|
||||||
spyOn(queryToolActions, '_buffers').and.returnValue(true);
|
spyOn(queryToolActions, '_buffers').and.returnValue(true);
|
||||||
spyOn(queryToolActions, '_timing').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', () => {
|
it('calls the execute function', () => {
|
||||||
queryToolActions.explainAnalyze(sqlEditorController);
|
queryToolActions.explainAnalyze(sqlEditorController);
|
||||||
@@ -92,7 +97,8 @@ describe('queryToolActions', () => {
|
|||||||
costs: true,
|
costs: true,
|
||||||
buffers: true,
|
buffers: true,
|
||||||
timing: true,
|
timing: true,
|
||||||
summary: false,
|
summary: true,
|
||||||
|
settings: true,
|
||||||
};
|
};
|
||||||
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
||||||
});
|
});
|
||||||
@@ -105,6 +111,8 @@ describe('queryToolActions', () => {
|
|||||||
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
|
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
|
||||||
spyOn(queryToolActions, '_buffers').and.returnValue(true);
|
spyOn(queryToolActions, '_buffers').and.returnValue(true);
|
||||||
spyOn(queryToolActions, '_timing').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', () => {
|
it('calls the execute function', () => {
|
||||||
queryToolActions.explainAnalyze(sqlEditorController);
|
queryToolActions.explainAnalyze(sqlEditorController);
|
||||||
@@ -117,6 +125,7 @@ describe('queryToolActions', () => {
|
|||||||
buffers: true,
|
buffers: true,
|
||||||
timing: false,
|
timing: false,
|
||||||
summary: false,
|
summary: false,
|
||||||
|
settings: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
||||||
@@ -130,6 +139,8 @@ describe('queryToolActions', () => {
|
|||||||
spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
|
spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
|
||||||
spyOn(queryToolActions, '_buffers').and.returnValue(false);
|
spyOn(queryToolActions, '_buffers').and.returnValue(false);
|
||||||
spyOn(queryToolActions, '_timing').and.returnValue(true);
|
spyOn(queryToolActions, '_timing').and.returnValue(true);
|
||||||
|
spyOn(queryToolActions, '_summary').and.returnValue(false);
|
||||||
|
spyOn(queryToolActions, '_settings').and.returnValue(false);
|
||||||
});
|
});
|
||||||
it('calls the execute function', () => {
|
it('calls the execute function', () => {
|
||||||
queryToolActions.explainAnalyze(sqlEditorController);
|
queryToolActions.explainAnalyze(sqlEditorController);
|
||||||
@@ -142,6 +153,35 @@ describe('queryToolActions', () => {
|
|||||||
buffers: false,
|
buffers: false,
|
||||||
timing: true,
|
timing: true,
|
||||||
summary: false,
|
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);
|
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
||||||
@@ -155,6 +195,10 @@ describe('queryToolActions', () => {
|
|||||||
setUpSpies('', '');
|
setUpSpies('', '');
|
||||||
spyOn(queryToolActions, '_verbose').and.returnValue(true);
|
spyOn(queryToolActions, '_verbose').and.returnValue(true);
|
||||||
spyOn(queryToolActions, '_costsEnabled').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', () => {
|
it('calls the execute function', () => {
|
||||||
@@ -167,6 +211,7 @@ describe('queryToolActions', () => {
|
|||||||
buffers: false,
|
buffers: false,
|
||||||
timing: false,
|
timing: false,
|
||||||
summary: false,
|
summary: false,
|
||||||
|
settings: false,
|
||||||
};
|
};
|
||||||
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
||||||
});
|
});
|
||||||
@@ -177,6 +222,8 @@ describe('queryToolActions', () => {
|
|||||||
setUpSpies('', '');
|
setUpSpies('', '');
|
||||||
spyOn(queryToolActions, '_verbose').and.returnValue(false);
|
spyOn(queryToolActions, '_verbose').and.returnValue(false);
|
||||||
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
|
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
|
||||||
|
spyOn(queryToolActions, '_summary').and.returnValue(false);
|
||||||
|
spyOn(queryToolActions, '_settings').and.returnValue(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls the execute function', () => {
|
it('calls the execute function', () => {
|
||||||
@@ -189,6 +236,7 @@ describe('queryToolActions', () => {
|
|||||||
buffers: false,
|
buffers: false,
|
||||||
timing: false,
|
timing: false,
|
||||||
summary: false,
|
summary: false,
|
||||||
|
settings: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
||||||
@@ -200,6 +248,8 @@ describe('queryToolActions', () => {
|
|||||||
setUpSpies('', '');
|
setUpSpies('', '');
|
||||||
spyOn(queryToolActions, '_verbose').and.returnValue(true);
|
spyOn(queryToolActions, '_verbose').and.returnValue(true);
|
||||||
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
|
spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
|
||||||
|
spyOn(queryToolActions, '_summary').and.returnValue(false);
|
||||||
|
spyOn(queryToolActions, '_settings').and.returnValue(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls the execute function', () => {
|
it('calls the execute function', () => {
|
||||||
@@ -212,6 +262,7 @@ describe('queryToolActions', () => {
|
|||||||
buffers: false,
|
buffers: false,
|
||||||
timing: false,
|
timing: false,
|
||||||
summary: false,
|
summary: false,
|
||||||
|
settings: false,
|
||||||
};
|
};
|
||||||
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
expect(sqlEditorController.execute).toHaveBeenCalledWith(explainObject);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user