From 7f3bdca9d1a31cbe296c76d35976947b44f0748b Mon Sep 17 00:00:00 2001 From: "okbob@github.com" Date: Sun, 28 Jan 2024 21:32:49 +0100 Subject: [PATCH 06/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 a4b6564bdb..177dc2a697 100644 --- a/doc/src/sgml/ref/explain.sgml +++ b/doc/src/sgml/ref/explain.sgml @@ -97,7 +97,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 a9d5056af4..3a6655de97 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 "nodes/extensible.h" @@ -457,8 +458,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); } @@ -542,6 +544,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) @@ -565,8 +586,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) @@ -610,6 +631,8 @@ ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into, ExplainState *es, */ if (into) dest = CreateIntoRelDestReceiver(into); + else if (OidIsValid(targetvar)) + dest = CreateVariableDestReceiver(targetvar); else dest = None_Receiver; 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 3609f8c0f4..5908bb75b8 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -12031,7 +12031,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 cf195f1359..53f866375b 100644 --- a/src/include/commands/explain.h +++ b/src/include/commands/explain.h @@ -93,7 +93,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 3748e1439b..c824e6ae2f 100644 --- a/src/test/regress/expected/session_variables.out +++ b/src/test/regress/expected/session_variables.out @@ -1223,3 +1223,43 @@ SELECT var1; (1 row) DROP VARIABLE var1, var2; +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 (returns $0) + -> 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 (returns $0) + -> 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 4643cf138a..423c4ef367 100644 --- a/src/test/regress/sql/session_variables.sql +++ b/src/test/regress/sql/session_variables.sql @@ -820,3 +820,24 @@ BEGIN; DROP VARIABLE var1; ROLLBACK; SELECT var1; DROP VARIABLE var1, var2; + +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.44.0