From f600f539f54d23dcd7183434dd82ecf02f98a09c Mon Sep 17 00:00:00 2001 From: "okbob@github.com" Date: Tue, 21 May 2024 18:28:07 +0200 Subject: [PATCH 08/19] EXPLAIN LET support Enhancing ExplainOnePlan is necessary to be EXPLAIN ANALYZE LET fully workable. In this case we want to be result of query or expression written to target variable. --- doc/src/sgml/ref/explain.sgml | 3 +- src/backend/commands/explain.c | 31 ++++++++++++-- src/backend/commands/prepare.c | 5 ++- src/backend/parser/gram.y | 3 +- src/include/commands/explain.h | 3 +- .../regress/expected/session_variables.out | 40 +++++++++++++++++++ src/test/regress/sql/session_variables.sql | 21 ++++++++++ 7 files changed, 97 insertions(+), 9 deletions(-) diff --git a/doc/src/sgml/ref/explain.sgml b/doc/src/sgml/ref/explain.sgml index db9d3a8549..caccc70658 100644 --- a/doc/src/sgml/ref/explain.sgml +++ b/doc/src/sgml/ref/explain.sgml @@ -98,7 +98,8 @@ EXPLAIN [ ( option [, ...] ) ] INSERT, UPDATE, DELETE, MERGE, CREATE TABLE AS, - or EXECUTE statement + EXECUTE, + or LET statement without letting the command affect your data, use this approach: BEGIN; diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 94511a5a02..e9cd2cfc5a 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -18,6 +18,7 @@ #include "commands/createas.h" #include "commands/defrem.h" #include "commands/prepare.h" +#include "executor/svariableReceiver.h" #include "foreign/fdwapi.h" #include "jit/jit.h" #include "libpq/pqformat.h" @@ -505,8 +506,9 @@ standard_ExplainOneQuery(Query *query, int cursorOptions, } /* run it (if needed) and produce output */ - ExplainOnePlan(plan, into, es, queryString, params, queryEnv, - &planduration, (es->buffers ? &bufusage : NULL), + ExplainOnePlan(plan, into, query->resultVariable, es, queryString, + params, queryEnv, &planduration, + (es->buffers ? &bufusage : NULL), es->memory ? &mem_counters : NULL); } @@ -590,6 +592,25 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, else ExplainDummyGroup("Notify", NULL, es); } + else if (IsA(utilityStmt, LetStmt)) + { + LetStmt *letstmt = (LetStmt *) utilityStmt; + List *rewritten; + Query *query; + + if (es->format == EXPLAIN_FORMAT_TEXT) + appendStringInfoString(es->str, "SET SESSION VARIABLE\n"); + else + ExplainDummyGroup("Set Session Variable", NULL, es); + + rewritten = QueryRewrite(castNode(Query, copyObject(letstmt->query))); + + Assert(list_length(rewritten) == 1); + query = linitial_node(Query, rewritten); + ExplainOneQuery(query, + CURSOR_OPT_PARALLEL_OK, NULL, es, + queryString, params, queryEnv); + } else { if (es->format == EXPLAIN_FORMAT_TEXT) @@ -613,8 +634,8 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, * to call it. */ void -ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, - const char *queryString, ParamListInfo params, +ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, Oid targetvar, + ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv, const instr_time *planduration, const BufferUsage *bufusage, const MemoryContextCounters *mem_counters) @@ -663,6 +684,8 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, */ if (into) dest = CreateIntoRelDestReceiver(into); + else if (OidIsValid(targetvar)) + dest = CreateVariableDestReceiver(targetvar); else if (es->serialize != EXPLAIN_SERIALIZE_NONE) dest = CreateExplainSerializeDestReceiver(es); else diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index 5e85585e9d..ae59f2de62 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -655,8 +655,9 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es, PlannedStmt *pstmt = lfirst_node(PlannedStmt, p); if (pstmt->commandType != CMD_UTILITY) - ExplainOnePlan(pstmt, into, es, query_string, paramLI, queryEnv, - &planduration, (es->buffers ? &bufusage : NULL), + ExplainOnePlan(pstmt, into, InvalidOid, es, query_string, paramLI, + queryEnv, &planduration, + (es->buffers ? &bufusage : NULL), es->memory ? &mem_counters : NULL); else ExplainOneUtility(pstmt->utilityStmt, into, es, query_string, diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 244b7e9681..7fd5443b1b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -12132,7 +12132,8 @@ ExplainableStmt: | CreateAsStmt | CreateMatViewStmt | RefreshMatViewStmt - | ExecuteStmt /* by default all are $$=$1 */ + | ExecuteStmt + | LetStmt /* by default all are $$=$1 */ ; /***************************************************************************** diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h index 9b8b351d9a..8965662506 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -101,7 +101,8 @@ extern void ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv); -extern void ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, +extern void ExplainOnePlan(PlannedStmt *plannedstmt, + IntoClause *into, Oid targetvar, ExplainState *es, const char *queryString, ParamListInfo params, QueryEnvironment *queryEnv, const instr_time *planduration, diff --git a/src/test/regress/expected/session_variables.out b/src/test/regress/expected/session_variables.out index 9a2d6b1c7b..5c90e6339d 100644 --- a/src/test/regress/expected/session_variables.out +++ b/src/test/regress/expected/session_variables.out @@ -1245,3 +1245,43 @@ SET session_variables_ambiguity_warning TO off; DROP TABLE public.xxtab; DROP SCHEMA xxtab CASCADE; NOTICE: drop cascades to session variable xxtab.avar +CREATE VARIABLE var1 bigint; +CREATE TABLE var_tab_test_table(a int); +INSERT INTO var_tab_test_table SELECT * FROM generate_series(1,10); +VACUUM ANALYZE var_tab_test_table; +EXPLAIN (COSTS OFF) LET var1 = (SELECT count(*) FROM var_tab_test_table); + QUERY PLAN +---------------------------------------------- + SET SESSION VARIABLE + Result + InitPlan 1 + -> Aggregate + -> Seq Scan on var_tab_test_table +(5 rows) + +-- should be NULL +SELECT var1; + var1 +------ + +(1 row) + +EXPLAIN (COSTS OFF, TIMING OFF, ANALYZE, SUMMARY OFF) LET var1 = (SELECT count(*) FROM var_tab_test_table); + QUERY PLAN +----------------------------------------------------------------------- + SET SESSION VARIABLE + Result (actual rows=1 loops=1) + InitPlan 1 + -> Aggregate (actual rows=1 loops=1) + -> Seq Scan on var_tab_test_table (actual rows=10 loops=1) +(5 rows) + +-- should be 10 +SELECT var1; + var1 +------ + 10 +(1 row) + +DROP VARIABLE var1; +DROP TABLE var_tab_test_table; diff --git a/src/test/regress/sql/session_variables.sql b/src/test/regress/sql/session_variables.sql index 66da92c220..3d146d2ec3 100644 --- a/src/test/regress/sql/session_variables.sql +++ b/src/test/regress/sql/session_variables.sql @@ -840,3 +840,24 @@ SET session_variables_ambiguity_warning TO off; DROP TABLE public.xxtab; DROP SCHEMA xxtab CASCADE; + +CREATE VARIABLE var1 bigint; + +CREATE TABLE var_tab_test_table(a int); + +INSERT INTO var_tab_test_table SELECT * FROM generate_series(1,10); + +VACUUM ANALYZE var_tab_test_table; + +EXPLAIN (COSTS OFF) LET var1 = (SELECT count(*) FROM var_tab_test_table); + +-- should be NULL +SELECT var1; + +EXPLAIN (COSTS OFF, TIMING OFF, ANALYZE, SUMMARY OFF) LET var1 = (SELECT count(*) FROM var_tab_test_table); + +-- should be 10 +SELECT var1; + +DROP VARIABLE var1; +DROP TABLE var_tab_test_table; -- 2.45.1