Added GENERIC_PLAN, MEMORY, SERIALIZE option to EXPLAIN/EXPLAIN ANALYZE command. #6456

pull/9017/head
Akshay Joshi 2025-08-01 12:40:07 +05:30 committed by GitHub
parent 1f7fbb91f7
commit 559c6cfa65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 127 additions and 23 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -140,20 +140,27 @@ Query Execution
| | | |
| | Navigate through the *Explain Options* menu to select options for the EXPLAIN command: | |
| | | |
| | * Select *Verbose* to display additional information regarding the query plan. | |
| | * Select *Buffers* to include information on buffer usage. | |
| | | |
| | * Select *Costs* to include information on the estimated startup and total cost of each | |
| | plan node, as well as the estimated number of rows and the estimated width of each | |
| | row. | |
| | | |
| | * Select *Buffers* to include information on buffer usage. | |
| | * Select *Generic Plan* to include the information on the Generic Plan. | |
| | | |
| | * Select *Memory* to include the information on memory consumption by the query planning phase. | |
| | | |
| | * Select *Serialize* to include information on the cost of serializing the query's output data, | |
| | that is converting it to text or binary format to send to the client. | |
| | | |
| | * Select *Settings* to include the information on the configuration parameters. | |
| | | |
| | * Select *Summary* to include the summary information about the query plan. | |
| | | |
| | * Select *Timing* to include information about the startup time and the amount of time | |
| | spent in each node of the query. | |
| | | |
| | * Select *Summary* to include the summary information about the query plan. | |
| | | |
| | * Select *Settings* to include the information on the configuration parameters. | |
| | * Select *Verbose* to display additional information regarding the query plan. | |
| | | |
| | * Select *Wal* to include the information on WAL record generation. | |
+----------------------+---------------------------------------------------------------------------------------------------+----------------+

View File

@ -20,6 +20,9 @@ Bundled PostgreSQL Utilities
New features
************
| `Issue #5766 <https://github.com/pgadmin-org/pgadmin4/issues/5766>`_ - Add support for automatic updates in the pgAdmin 4 Desktop application on macOS.
| `Issue #6456 <https://github.com/pgadmin-org/pgadmin4/issues/6456>`_ - Added GENERIC_PLAN, MEMORY, SERIALIZE option to EXPLAIN/EXPLAIN ANALYZE command.
| `Issue #8917 <https://github.com/pgadmin-org/pgadmin4/issues/8917>`_ - Add support for server tag-based filtering in the Object Explorer.
Housekeeping
************
@ -30,7 +33,11 @@ Housekeeping
Bug fixes
*********
| `Issue #8149 <https://github.com/pgadmin-org/pgadmin4/issues/8149>`_ - Fixed an issue where pgAdmin failed to update the server connection status when the server was disconnected in the background and a refresh was performed on that server.
| `Issue #8650 <https://github.com/pgadmin-org/pgadmin4/issues/8650>`_ - Make Dashboard tables to be vertically resizable.
| `Issue #8756 <https://github.com/pgadmin-org/pgadmin4/issues/8756>`_ - Fixed an issue in Firefox where the query window would shift to the left after opening the history tab or selecting a column header in the results grid.
| `Issue #8867 <https://github.com/pgadmin-org/pgadmin4/issues/8867>`_ - Ensure DB restriction type is preserved while import and export server.
| `Issue #8969 <https://github.com/pgadmin-org/pgadmin4/issues/8969>`_ - Fixed incorrect behaviour of the option deduplicate items after creating the index.
| `Issue #8969 <https://github.com/pgadmin-org/pgadmin4/issues/8969>`_ - Fixed incorrect behaviour of the option deduplicate items after creating the index.
| `Issue #8971 <https://github.com/pgadmin-org/pgadmin4/issues/8971>`_ - Added PKEY index in the index statistics summary.
| `Issue #9007 <https://github.com/pgadmin-org/pgadmin4/issues/9007>`_ - Ensure the scratch pad in the Query Tool is not restored after it is closed.
| `Issue #9008 <https://github.com/pgadmin-org/pgadmin4/issues/9008>`_ - Update the documentation for parameters that require file paths.

View File

@ -57,14 +57,18 @@ PgMenu.propTypes = {
menuButton: PropTypes.element,
};
export const PgSubMenu = (({label, ...props})=>{
export const PgSubMenu = (({label, alignCheck, ...props})=>{
if(alignCheck) {
label = <><CheckIcon style={{visibility: 'hidden', width: '1.3rem'}} data-label="CheckIcon"/>{label}</>;
}
return (
<SubMenu label={label} itemProps={{'data-label': label}} {...props} />
);
});
PgSubMenu.propTypes = {
label: PropTypes.string
label: PropTypes.string,
alignCheck: PropTypes.bool
};
export const PgMenuItem = (({hasCheck=false, checked=false, accesskey, shortcut, children, closeOnCheck=false, ...props})=>{

View File

@ -915,7 +915,7 @@ def start_view_data(trans_id):
if response is not None:
return response
status, msg = default_conn.connect()
status, _ = default_conn.connect()
if not status:
return service_unavailable(
gettext("Connection to the server has been lost."),
@ -1535,7 +1535,7 @@ def save(trans_id):
if response is not None:
return response
is_error, errmsg, conn = _check_and_connect(trans_obj)
is_error, _, conn = _check_and_connect(trans_obj)
if is_error:
return service_unavailable(
gettext("Connection to the server has been lost."),

View File

@ -21,9 +21,9 @@ import AssessmentRoundedIcon from '@mui/icons-material/AssessmentRounded';
import ExplicitRoundedIcon from '@mui/icons-material/ExplicitRounded';
import FormatListNumberedRoundedIcon from '@mui/icons-material/FormatListNumberedRounded';
import HelpIcon from '@mui/icons-material/HelpRounded';
import {QUERY_TOOL_EVENTS, CONNECTION_STATUS} from '../QueryToolConstants';
import {QUERY_TOOL_EVENTS, CONNECTION_STATUS, MODAL_DIALOGS} from '../QueryToolConstants';
import { QueryToolConnectionContext, QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
import { PgMenu, PgMenuDivider, PgMenuItem, usePgMenuGroup } from '../../../../../../static/js/components/Menu';
import { PgMenu, PgMenuDivider, PgMenuItem, usePgMenuGroup, PgSubMenu} from '../../../../../../static/js/components/Menu';
import gettext from 'sources/gettext';
import { useKeyboardShortcuts } from '../../../../../../static/js/custom_hooks';
import url_for from 'sources/url_for';
@ -34,7 +34,6 @@ import CustomPropTypes from '../../../../../../static/js/custom_prop_types';
import ConfirmTransactionContent from '../dialogs/ConfirmTransactionContent';
import { LayoutDocker } from '../../../../../../static/js/helpers/Layout';
import CloseRunningDialog from '../dialogs/CloseRunningDialog';
import { MODAL_DIALOGS } from '../QueryToolConstants';
const StyledBox = styled(Box)(({theme}) => ({
padding: '2px 4px',
@ -114,6 +113,10 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
summary: Boolean(checkedMenuItems['explain_summary']),
settings: Boolean(checkedMenuItems['explain_settings']),
wal: analyze ? Boolean(checkedMenuItems['explain_wal']) : false,
generic_plan: analyze ? false: Boolean(checkedMenuItems['explain_generic_plan']),
memory: Boolean(checkedMenuItems['explain_memory']),
serialize_binary: analyze ? Boolean(checkedMenuItems['explain_serialize_binary']) : false,
serialize_text: analyze ? Boolean(checkedMenuItems['explain_serialize_text']) : false,
});
}, [checkedMenuItems]);
@ -136,9 +139,18 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
});
});
}
let otherVars = {};
if (e.value === 'explain_serialize_binary' && newVal) {
otherVars = { 'explain_serialize_text': false };
} else if (e.value === 'explain_serialize_text' && newVal) {
otherVars = { 'explain_serialize_binary': false };
}
return {
...prev,
[e.value]: newVal,
...otherVars,
};
});
}, []);
@ -274,8 +286,8 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
};
useEffect(()=>{
if(isInTxn()) {
setDisableButton('commit', queryToolCtx.params.server_cursor && !queryToolCtx.params.is_query_tool ? true : false);
setDisableButton('rollback', queryToolCtx.params.server_cursor && !queryToolCtx.params.is_query_tool ? true : false);
setDisableButton('commit', queryToolCtx.params.server_cursor && !queryToolCtx.params.is_query_tool);
setDisableButton('rollback', queryToolCtx.params.server_cursor && !queryToolCtx.params.is_query_tool);
setDisableButton('execute-options', true);
} else {
setDisableButton('commit', true);
@ -347,6 +359,8 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
explain_summary: queryToolPref.explain_summary,
explain_settings: queryToolPref.explain_settings,
explain_wal: queryToolPref.explain_wal,
explain_generic_plan: queryToolPref.explain_generic_plan,
explain_memory: queryToolPref.explain_memory,
open_in_new_tab: queryToolPref.open_in_new_tab,
server_cursor: queryToolPref.server_cursor,
});
@ -645,18 +659,28 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT
onClose={onMenuClose}
label={gettext('Explain Options Menu')}
>
<PgMenuItem hasCheck value="explain_verbose" checked={checkedMenuItems['explain_verbose']}
onClick={checkMenuClick}>{gettext('Verbose')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_costs" checked={checkedMenuItems['explain_costs']}
onClick={checkMenuClick}>{gettext('Costs')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_buffers" checked={checkedMenuItems['explain_buffers']}
onClick={checkMenuClick}>{gettext('Buffers')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_timing" checked={checkedMenuItems['explain_timing']}
onClick={checkMenuClick}>{gettext('Timing')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_summary" checked={checkedMenuItems['explain_summary']}
onClick={checkMenuClick}>{gettext('Summary')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_costs" checked={checkedMenuItems['explain_costs']}
onClick={checkMenuClick}>{gettext('Costs')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_generic_plan" checked={checkedMenuItems['explain_generic_plan']}
onClick={checkMenuClick}>{gettext('Generic Plan')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_memory" checked={checkedMenuItems['explain_memory']}
onClick={checkMenuClick}>{gettext('Memory')}</PgMenuItem>
<PgSubMenu alignCheck key="SERIALIZE" label={gettext('Serialize')}>
<PgMenuItem hasCheck value="explain_serialize_text" checked={checkedMenuItems['explain_serialize_text']}
onClick={checkMenuClick}>{gettext('Text')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_serialize_binary" checked={checkedMenuItems['explain_serialize_binary']}
onClick={checkMenuClick}>{gettext('Binary')}</PgMenuItem>
</PgSubMenu>
<PgMenuItem hasCheck value="explain_settings" checked={checkedMenuItems['explain_settings']}
onClick={checkMenuClick}>{gettext('Settings')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_summary" checked={checkedMenuItems['explain_summary']}
onClick={checkMenuClick}>{gettext('Summary')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_timing" checked={checkedMenuItems['explain_timing']}
onClick={checkMenuClick}>{gettext('Timing')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_verbose" checked={checkedMenuItems['explain_verbose']}
onClick={checkMenuClick}>{gettext('Verbose')}</PgMenuItem>
<PgMenuItem hasCheck value="explain_wal" checked={checkedMenuItems['explain_wal']}
onClick={checkMenuClick}>{gettext('Wal')}</PgMenuItem>
</PgMenu>

View File

@ -0,0 +1,22 @@
{% 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 %}{% if wal is defined -%}
, WAL {{ UTILS.BOOL_TEXT(wal) }}
{%- endif %}{% if generic_plan is defined -%}
, GENERIC_PLAN {{ UTILS.BOOL_TEXT(generic_plan) }}
{%- endif %}) {{ sql }}

View File

@ -0,0 +1,28 @@
{% 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 %}{% if wal is defined -%}
, WAL {{ UTILS.BOOL_TEXT(wal) }}
{%- endif %}{% if generic_plan is defined -%}
, GENERIC_PLAN {{ UTILS.BOOL_TEXT(generic_plan) }}
{%- endif %}{% if memory is defined -%}
, MEMORY {{ UTILS.BOOL_TEXT(memory) }}
{%- endif %}{% if serialize_binary -%}
, SERIALIZE BINARY
{%- endif %}{% if serialize_text -%}
, SERIALIZE TEXT
{%- endif %}) {{ sql }}

View File

@ -60,6 +60,18 @@ def register_query_tool_preferences(self):
category_label=PREF_LABEL_EXPLAIN
)
self.explain_generic_plan = self.preference.register(
'Explain', 'explain_generic_plan',
gettext("Show generic plan?"), 'boolean', False,
category_label=PREF_LABEL_EXPLAIN
)
self.explain_memory = self.preference.register(
'Explain', 'explain_memory',
gettext("Show memory?"), 'boolean', False,
category_label=PREF_LABEL_EXPLAIN
)
self.auto_commit = self.preference.register(
'Options', 'auto_commit',
gettext("Auto commit?"), 'boolean', True,