2019-01-02 04:24:12 -06:00
|
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// pgAdmin 4 - PostgreSQL Tools
|
|
|
|
//
|
2023-01-02 00:23:55 -06:00
|
|
|
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
2019-01-02 04:24:12 -06:00
|
|
|
// This software is released under the PostgreSQL Licence
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
2018-01-12 01:29:51 -06:00
|
|
|
define([], function() {
|
2022-09-08 07:38:58 -05:00
|
|
|
let pgAdmin = window.pgAdmin = window.pgAdmin || {};
|
2015-06-30 00:51:55 -05:00
|
|
|
|
2016-05-21 03:48:24 -05:00
|
|
|
// Reference:
|
|
|
|
// https://github.com/heygrady/Units/blob/master/Length.min.js
|
|
|
|
// Changed it to save the function in pgAdmin object.
|
2018-01-12 01:29:51 -06:00
|
|
|
(function(t, e, o) {
|
|
|
|
'use strict';
|
|
|
|
|
2020-07-14 05:15:01 -05:00
|
|
|
function r(_t, _e, _r, _p) {
|
|
|
|
_r = _r || 'width';
|
|
|
|
let _n, _l, _m, _c = (_e.match(s) || [])[2],
|
|
|
|
_f = 'px' === _c ? 1 : d[_c + 'toPx'],
|
|
|
|
_u = /r?em/i;
|
|
|
|
if (_f || _u.test(_c) && !_p){
|
2022-09-10 03:30:22 -05:00
|
|
|
if ('rem' === _c) {
|
|
|
|
_t = i;
|
|
|
|
}
|
|
|
|
_t = 'fontSize' === _r ? +t.parentNode || _t : _t;
|
|
|
|
// _t = _f ? _t : 'rem' === _c ? i : 'fontSize' === _r ? +t.parentNode || _t : _t;
|
2020-07-14 05:15:01 -05:00
|
|
|
_f = _f || parseFloat(a(_t, 'fontSize'));
|
|
|
|
_m = parseFloat(_e) * _f;
|
2020-06-18 05:47:55 -05:00
|
|
|
}
|
2018-01-12 01:29:51 -06:00
|
|
|
else {
|
2020-07-14 05:15:01 -05:00
|
|
|
_n = _t.style;
|
|
|
|
_l = _n[_r];
|
2018-01-12 01:29:51 -06:00
|
|
|
try {
|
2020-07-14 05:15:01 -05:00
|
|
|
_n[_r] = _e;
|
2018-01-12 01:29:51 -06:00
|
|
|
} catch (x) {
|
|
|
|
return 0;
|
|
|
|
}
|
2020-07-14 05:15:01 -05:00
|
|
|
_m = _n[_r] ? parseFloat(a(_t, _r)) : 0;
|
|
|
|
_n[_r] = _l !== o ? _l : null;
|
2018-01-12 01:29:51 -06:00
|
|
|
}
|
2020-07-14 05:15:01 -05:00
|
|
|
return _m;
|
2018-01-12 01:29:51 -06:00
|
|
|
}
|
|
|
|
|
2020-07-14 05:15:01 -05:00
|
|
|
function a(_t, _e) {
|
2022-09-10 03:34:28 -05:00
|
|
|
let _o, _n, _i, _l, _d, _c = /^(top|bottom)/,
|
2020-07-14 05:15:01 -05:00
|
|
|
_f = ['paddingTop', 'paddingBottom', 'borderTop', 'borderBottom'],
|
|
|
|
_u = 4;
|
2019-04-05 02:37:43 -05:00
|
|
|
|
2020-07-14 05:15:01 -05:00
|
|
|
_n = _t.style['pixel' + _e.charAt(0).toUpperCase() + _e.slice(1)];
|
2022-09-10 03:30:22 -05:00
|
|
|
if (m) {
|
|
|
|
_o = m(_t)[_e];
|
|
|
|
} else {
|
|
|
|
if (_n) {
|
|
|
|
_o = _n + 'px';
|
|
|
|
} else {
|
|
|
|
_o = 'fontSize' === _e ? r(_t, '1em', 'left', 1) + 'px' : _t.currentStyle[_e];
|
|
|
|
}
|
|
|
|
}
|
2020-07-14 05:15:01 -05:00
|
|
|
_i = (_o.match(s) || [])[2];
|
2019-04-05 02:37:43 -05:00
|
|
|
|
2020-07-14 05:15:01 -05:00
|
|
|
if ('%' === _i && p)
|
|
|
|
if (_c.test(_e)) {
|
|
|
|
for (_l = (_d = _t.parentNode || _t).offsetHeight; _u--;) _l -= parseFloat(a(_d, _f[_u]));
|
|
|
|
_o = parseFloat(_o) / 100 * _l + 'px';
|
|
|
|
} else _o = r(_t, _o);
|
|
|
|
else('auto' === _o || _i && 'px' !== _i) && m ? _o = 0 : _i && 'px' !== _i && !m && (_o = r(_t, _o) + 'px');
|
|
|
|
return _o;
|
2018-01-12 01:29:51 -06:00
|
|
|
}
|
2022-09-08 07:38:58 -05:00
|
|
|
let p, n = e.createElement('test'),
|
2018-01-12 01:29:51 -06:00
|
|
|
i = e.documentElement,
|
|
|
|
l = e.defaultView,
|
|
|
|
m = l && l.getComputedStyle,
|
|
|
|
s = /^(-?[\d+\.\-]+)([a-z]+|%)$/i,
|
|
|
|
d = {},
|
|
|
|
c = [1 / 25.4, 1 / 2.54, 1 / 72, 1 / 6],
|
|
|
|
f = ['mm', 'cm', 'pt', 'pc', 'in', 'mozmm'],
|
|
|
|
u = 6;
|
2020-06-26 02:42:07 -05:00
|
|
|
for (i.appendChild(n), m && ((n.style.marginTop = '1%'), (p = '1%' === m(n).marginTop)); u--;) d[f[u] + 'toPx'] = c[u] ? c[u] * d.inToPx : r(n, '1' + f[u]);
|
2020-06-18 05:47:55 -05:00
|
|
|
i.removeChild(n); t.toPx = r;
|
2018-01-12 01:29:51 -06:00
|
|
|
})(pgAdmin, window.document);
|
2016-05-21 03:48:24 -05:00
|
|
|
|
2016-08-29 09:36:48 -05:00
|
|
|
// Reference:
|
|
|
|
// https://github.com/javve/natural-sort/blob/master/index.js
|
|
|
|
// Changed it to save the function in pgAdmin object.
|
|
|
|
pgAdmin.natural_sort = function(a, b, options) {
|
2018-01-12 01:29:51 -06:00
|
|
|
options = options || {};
|
|
|
|
|
2022-09-10 03:18:14 -05:00
|
|
|
let re = /(^-?\d+(\.?\d*)[df]?e?\d?$|^0x[0-9a-f]+$|\d+)/gi,
|
2018-01-12 01:29:51 -06:00
|
|
|
sre = /(^[ ]*|[ ]*$)/g,
|
|
|
|
dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
|
|
|
|
hre = /^0x[0-9a-f]+$/i,
|
|
|
|
ore = /^0/,
|
|
|
|
i = function(s) {
|
|
|
|
return options.insensitive && ('' + s).toLowerCase() || '' + s;
|
|
|
|
},
|
|
|
|
// convert all to strings strip whitespace
|
|
|
|
x = i(a).replace(sre, '') || '',
|
|
|
|
y = i(b).replace(sre, '') || '',
|
|
|
|
// chunk/tokenize
|
|
|
|
xN = x.replace(re, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0'),
|
|
|
|
yN = y.replace(re, '\0$1\0').replace(/\0$/, '').replace(/^\0/, '').split('\0'),
|
|
|
|
// numeric, hex or date detection
|
|
|
|
xD = parseInt(x.match(hre)) || (xN.length !== 1 && x.match(dre) && Date.parse(x)),
|
|
|
|
yD = parseInt(y.match(hre)) || xD && y.match(dre) && Date.parse(y) || null,
|
|
|
|
oFxNcL, oFyNcL,
|
|
|
|
mult = options.desc ? -1 : 1;
|
2016-08-29 09:36:48 -05:00
|
|
|
|
|
|
|
// first try and sort Hex codes or Dates
|
|
|
|
if (yD)
|
2018-01-12 01:29:51 -06:00
|
|
|
if (xD < yD) return -1 * mult;
|
|
|
|
else if (xD > yD) return 1 * mult;
|
2016-08-29 09:36:48 -05:00
|
|
|
|
|
|
|
// natural sorting through split numeric strings and default strings
|
2022-09-08 07:38:58 -05:00
|
|
|
for (let cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
|
2016-08-29 09:36:48 -05:00
|
|
|
// find floats not starting with '0', string or 0 if not defined (Clint Priest)
|
|
|
|
oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc]) || xN[cLoc] || 0;
|
|
|
|
oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc]) || yN[cLoc] || 0;
|
|
|
|
// handle numeric vs string comparison - number < string - (Kyle Adams)
|
2018-01-12 01:29:51 -06:00
|
|
|
if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
|
|
|
|
return (isNaN(oFxNcL) ? 1 : -1) * mult;
|
|
|
|
}
|
2016-08-29 09:36:48 -05:00
|
|
|
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
|
|
|
|
else if (typeof oFxNcL !== typeof oFyNcL) {
|
|
|
|
oFxNcL += '';
|
|
|
|
oFyNcL += '';
|
|
|
|
}
|
|
|
|
if (oFxNcL < oFyNcL) return -1 * mult;
|
|
|
|
if (oFxNcL > oFyNcL) return 1 * mult;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2020-04-23 06:12:42 -05:00
|
|
|
pgAdmin.numeric_comparator = function(a, b) {
|
|
|
|
a = parseInt(a);
|
|
|
|
b = parseInt(b);
|
|
|
|
if (a < b)
|
2020-05-08 02:22:03 -05:00
|
|
|
return -1;
|
|
|
|
else if (a == b)
|
|
|
|
return 0;
|
2020-04-23 06:12:42 -05:00
|
|
|
else
|
2020-05-08 02:22:03 -05:00
|
|
|
return 1;
|
2020-04-23 06:12:42 -05:00
|
|
|
};
|
|
|
|
|
Fixes # 4778 - Implement the query plan analyzer
Look 'n' Feel and implementation logic are inspired from
'http://explain.depsez.com'.
It now creates three tabs under the 'Explain' panel when executing a
query using the Explain Analyze/Explain button from the toolbar of the
Query tool.
Graphical
---------
-> Graphical Explain Plan
Analysis
--------
-> Table to show details of the explain plan analyse.
-> Each row represents the statistics per Explain Plan Node
-> It may contains columns like node information, exclusive timing
(time spent for this explain node excluding the child nodes),
inclusive timing, actual rows, plan rows,
rowsx (misestimation between planned vs actual rows), loop.
-> Background color of exclusive, inclusive, rows changes based on
their values.
i.e.
If Percentage of exclusive, and inclusive timings of total query time
is:
> 90 - Red Color
> 50 - Orange (Between Red & Yellow Color)
> 10 - Yellow color
If planner misestimation for the rows is
> 1000 times - Red Color
> 100 times - Orange (Between Red & Yellow Color)
> 10 times - Yellow Color
Also - if actual rows <= planned rows then it shows up arrow, else it
shows down arrow.
Statistics
----------
-> It contains a HTML table for the statistics per Node Type, and
a HTML table for the statistics per table.
Reviewed by: Akshay Joshi
2019-10-08 06:03:25 -05:00
|
|
|
/**
|
|
|
|
* Decimal adjustment of a number.
|
|
|
|
*
|
|
|
|
* @param {String} type The type of adjustment.
|
|
|
|
* @param {Number} value The number.
|
|
|
|
* @param {Integer} exp The exponent (the 10 logarithm of the adjustment base).
|
|
|
|
* @returns {Number} The adjusted value.
|
|
|
|
*/
|
|
|
|
function decimalAdjust(type, value, exp) {
|
|
|
|
// If the exp is undefined or zero...
|
|
|
|
if (typeof exp === 'undefined' || +exp === 0) {
|
|
|
|
return Math[type](value);
|
|
|
|
}
|
|
|
|
value = +value;
|
|
|
|
exp = +exp;
|
|
|
|
// If the value is not a number or the exp is not an integer...
|
2020-06-18 00:44:56 -05:00
|
|
|
if (isNaN(value) || exp % 1 !== 0) {
|
Fixes # 4778 - Implement the query plan analyzer
Look 'n' Feel and implementation logic are inspired from
'http://explain.depsez.com'.
It now creates three tabs under the 'Explain' panel when executing a
query using the Explain Analyze/Explain button from the toolbar of the
Query tool.
Graphical
---------
-> Graphical Explain Plan
Analysis
--------
-> Table to show details of the explain plan analyse.
-> Each row represents the statistics per Explain Plan Node
-> It may contains columns like node information, exclusive timing
(time spent for this explain node excluding the child nodes),
inclusive timing, actual rows, plan rows,
rowsx (misestimation between planned vs actual rows), loop.
-> Background color of exclusive, inclusive, rows changes based on
their values.
i.e.
If Percentage of exclusive, and inclusive timings of total query time
is:
> 90 - Red Color
> 50 - Orange (Between Red & Yellow Color)
> 10 - Yellow color
If planner misestimation for the rows is
> 1000 times - Red Color
> 100 times - Orange (Between Red & Yellow Color)
> 10 times - Yellow Color
Also - if actual rows <= planned rows then it shows up arrow, else it
shows down arrow.
Statistics
----------
-> It contains a HTML table for the statistics per Node Type, and
a HTML table for the statistics per table.
Reviewed by: Akshay Joshi
2019-10-08 06:03:25 -05:00
|
|
|
return NaN;
|
|
|
|
}
|
|
|
|
// Shift
|
|
|
|
value = value.toString().split('e');
|
|
|
|
value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
|
|
|
|
// Shift back
|
|
|
|
value = value.toString().split('e');
|
|
|
|
return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decimal round
|
|
|
|
if (!Math.round10) {
|
|
|
|
Math.round10 = function(value, exp) {
|
|
|
|
return decimalAdjust('round', value, exp);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// Decimal floor
|
|
|
|
if (!Math.floor10) {
|
|
|
|
Math.floor10 = function(value, exp) {
|
|
|
|
return decimalAdjust('floor', value, exp);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
// Decimal ceil
|
|
|
|
if (!Math.ceil10) {
|
|
|
|
Math.ceil10 = function(value, exp) {
|
|
|
|
return decimalAdjust('ceil', value, exp);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-12-02 05:17:18 -06:00
|
|
|
pgAdmin.ui = {dialogs: {}};
|
|
|
|
|
2015-06-30 00:51:55 -05:00
|
|
|
return pgAdmin;
|
2018-01-12 01:29:51 -06:00
|
|
|
});
|