Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 6b652e6

Browse files
committed
Set query ID for inner queries of CREATE TABLE AS and DECLARE
Some utility statements contain queries that can be planned and executed: CREATE TABLE AS and DECLARE CURSOR. This commit adds query ID computation for the inner queries executed by these two utility commands, with and without EXPLAIN. This change leads to four new callers of JumbleQuery() and post_parse_analyze_hook() so as extensions can decide what to do with this new data. Previously, extensions relying on the query ID, like pg_stat_statements, were not able to track these nested queries as the query_id was 0. For pg_stat_statements, this commit leads to additions under !toplevel when pg_stat_statements.track is set to "all", as shown in its regression tests. The output of EXPLAIN for these two utilities gains a "Query Identifier" if compute_query_id is enabled. Author: Anthonin Bonnefoy Reviewed-by: Michael Paquier, Jian He Discussion: https://postgr.es/m/CAO6_XqqM6S9bQ2qd=75W+yKATwoazxSNhv5sjW06fjGAtHbTUA@mail.gmail.com
1 parent 33b2fbe commit 6b652e6

File tree

9 files changed

+89
-35
lines changed

9 files changed

+89
-35
lines changed

contrib/pg_stat_statements/expected/level_tracking.out

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -924,8 +924,9 @@ SELECT toplevel, calls, query FROM pg_stat_statements
924924
| | DECLARE foocur CURSOR FOR SELECT * FROM stats_track_tab
925925
t | 1 | EXPLAIN (ANALYZE, COSTS OFF, SUMMARY OFF, TIMING OFF) SELECT $1
926926
f | 1 | SELECT $1
927+
f | 1 | SELECT * FROM stats_track_tab
927928
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
928-
(4 rows)
929+
(5 rows)
929930

930931
-- Explain analyze, top tracking.
931932
SET pg_stat_statements.track = 'top';
@@ -1047,9 +1048,10 @@ SELECT toplevel, calls, query FROM pg_stat_statements
10471048
----------+-------+-----------------------------------------------------------------
10481049
t | 1 | CREATE TEMPORARY TABLE pgss_ctas_1 AS SELECT $1
10491050
t | 1 | CREATE TEMPORARY TABLE pgss_ctas_2 AS EXECUTE test_prepare_pgss
1051+
f | 1 | SELECT $1
10501052
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
10511053
f | 1 | select generate_series($1, $2)
1052-
(4 rows)
1054+
(5 rows)
10531055

10541056
-- CREATE TABLE AS, top-level tracking.
10551057
SET pg_stat_statements.track = 'top';
@@ -1089,8 +1091,9 @@ SELECT toplevel, calls, query FROM pg_stat_statements
10891091
toplevel | calls | query
10901092
----------+-------+---------------------------------------------------------------------------
10911093
t | 1 | EXPLAIN (COSTS OFF) CREATE TEMPORARY TABLE pgss_explain_ctas AS SELECT $1
1094+
f | 1 | SELECT $1
10921095
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1093-
(2 rows)
1096+
(3 rows)
10941097

10951098
-- EXPLAIN with CREATE TABLE AS - top-level tracking.
10961099
SET pg_stat_statements.track = 'top';
@@ -1140,8 +1143,9 @@ SELECT toplevel, calls, query FROM pg_stat_statements
11401143
t | 1 | COMMIT
11411144
t | 1 | DECLARE FOOCUR CURSOR FOR SELECT * from stats_track_tab
11421145
t | 1 | FETCH FORWARD 1 FROM foocur
1146+
f | 1 | SELECT * from stats_track_tab
11431147
t | 1 | SELECT pg_stat_statements_reset() IS NOT NULL AS t
1144-
(6 rows)
1148+
(7 rows)
11451149

11461150
-- DECLARE CURSOR, top-level tracking.
11471151
SET pg_stat_statements.track = 'top';

src/backend/commands/createas.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
#include "commands/view.h"
3838
#include "nodes/makefuncs.h"
3939
#include "nodes/nodeFuncs.h"
40+
#include "nodes/queryjumble.h"
41+
#include "parser/analyze.h"
4042
#include "rewrite/rewriteHandler.h"
4143
#include "tcop/tcopprot.h"
4244
#include "utils/builtins.h"
@@ -222,6 +224,7 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
222224
{
223225
Query *query = castNode(Query, stmt->query);
224226
IntoClause *into = stmt->into;
227+
JumbleState *jstate = NULL;
225228
bool is_matview = (into->viewQuery != NULL);
226229
bool do_refresh = false;
227230
DestReceiver *dest;
@@ -236,6 +239,13 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
236239
*/
237240
dest = CreateIntoRelDestReceiver(into);
238241

242+
/* Query contained by CTAS needs to be jumbled if requested */
243+
if (IsQueryIdEnabled())
244+
jstate = JumbleQuery(query);
245+
246+
if (post_parse_analyze_hook)
247+
(*post_parse_analyze_hook) (pstate, query, jstate);
248+
239249
/*
240250
* The contained Query could be a SELECT, or an EXECUTE utility command.
241251
* If the latter, we just pass it off to ExecuteQuery.

src/backend/commands/explain.c

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ typedef struct SerializeMetrics
7171

7272
static void ExplainOneQuery(Query *query, int cursorOptions,
7373
IntoClause *into, ExplainState *es,
74-
const char *queryString, ParamListInfo params,
75-
QueryEnvironment *queryEnv);
74+
ParseState *pstate, ParamListInfo params);
7675
static void ExplainPrintJIT(ExplainState *es, int jit_flags,
7776
JitInstrumentation *ji);
7877
static void ExplainPrintSerialize(ExplainState *es,
@@ -350,7 +349,7 @@ ExplainQuery(ParseState *pstate, ExplainStmt *stmt,
350349
{
351350
ExplainOneQuery(lfirst_node(Query, l),
352351
CURSOR_OPT_PARALLEL_OK, NULL, es,
353-
pstate->p_sourcetext, params, pstate->p_queryEnv);
352+
pstate, params);
354353

355354
/* Separate plans with an appropriate separator */
356355
if (lnext(rewritten, l) != NULL)
@@ -436,24 +435,22 @@ ExplainResultDesc(ExplainStmt *stmt)
436435
static void
437436
ExplainOneQuery(Query *query, int cursorOptions,
438437
IntoClause *into, ExplainState *es,
439-
const char *queryString, ParamListInfo params,
440-
QueryEnvironment *queryEnv)
438+
ParseState *pstate, ParamListInfo params)
441439
{
442440
/* planner will not cope with utility statements */
443441
if (query->commandType == CMD_UTILITY)
444442
{
445-
ExplainOneUtility(query->utilityStmt, into, es, queryString, params,
446-
queryEnv);
443+
ExplainOneUtility(query->utilityStmt, into, es, pstate, params);
447444
return;
448445
}
449446

450447
/* if an advisor plugin is present, let it manage things */
451448
if (ExplainOneQuery_hook)
452449
(*ExplainOneQuery_hook) (query, cursorOptions, into, es,
453-
queryString, params, queryEnv);
450+
pstate->p_sourcetext, params, pstate->p_queryEnv);
454451
else
455452
standard_ExplainOneQuery(query, cursorOptions, into, es,
456-
queryString, params, queryEnv);
453+
pstate->p_sourcetext, params, pstate->p_queryEnv);
457454
}
458455

459456
/*
@@ -534,8 +531,7 @@ standard_ExplainOneQuery(Query *query, int cursorOptions,
534531
*/
535532
void
536533
ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
537-
const char *queryString, ParamListInfo params,
538-
QueryEnvironment *queryEnv)
534+
ParseState *pstate, ParamListInfo params)
539535
{
540536
if (utilityStmt == NULL)
541537
return;
@@ -547,7 +543,9 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
547543
* ExplainOneQuery. Copy to be safe in the EXPLAIN EXECUTE case.
548544
*/
549545
CreateTableAsStmt *ctas = (CreateTableAsStmt *) utilityStmt;
546+
Query *ctas_query;
550547
List *rewritten;
548+
JumbleState *jstate = NULL;
551549

552550
/*
553551
* Check if the relation exists or not. This is done at this stage to
@@ -565,11 +563,16 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
565563
return;
566564
}
567565

568-
rewritten = QueryRewrite(castNode(Query, copyObject(ctas->query)));
566+
ctas_query = castNode(Query, copyObject(ctas->query));
567+
if (IsQueryIdEnabled())
568+
jstate = JumbleQuery(ctas_query);
569+
if (post_parse_analyze_hook)
570+
(*post_parse_analyze_hook) (pstate, ctas_query, jstate);
571+
rewritten = QueryRewrite(ctas_query);
569572
Assert(list_length(rewritten) == 1);
570573
ExplainOneQuery(linitial_node(Query, rewritten),
571574
CURSOR_OPT_PARALLEL_OK, ctas->into, es,
572-
queryString, params, queryEnv);
575+
pstate, params);
573576
}
574577
else if (IsA(utilityStmt, DeclareCursorStmt))
575578
{
@@ -582,17 +585,25 @@ ExplainOneUtility(Node *utilityStmt, IntoClause *into, ExplainState *es,
582585
* be created, however.
583586
*/
584587
DeclareCursorStmt *dcs = (DeclareCursorStmt *) utilityStmt;
588+
Query *dcs_query;
585589
List *rewritten;
590+
JumbleState *jstate = NULL;
586591

587-
rewritten = QueryRewrite(castNode(Query, copyObject(dcs->query)));
592+
dcs_query = castNode(Query, copyObject(dcs->query));
593+
if (IsQueryIdEnabled())
594+
jstate = JumbleQuery(dcs_query);
595+
if (post_parse_analyze_hook)
596+
(*post_parse_analyze_hook) (pstate, dcs_query, jstate);
597+
598+
rewritten = QueryRewrite(dcs_query);
588599
Assert(list_length(rewritten) == 1);
589600
ExplainOneQuery(linitial_node(Query, rewritten),
590601
dcs->options, NULL, es,
591-
queryString, params, queryEnv);
602+
pstate, params);
592603
}
593604
else if (IsA(utilityStmt, ExecuteStmt))
594605
ExplainExecuteQuery((ExecuteStmt *) utilityStmt, into, es,
595-
queryString, params, queryEnv);
606+
pstate, params);
596607
else if (IsA(utilityStmt, NotifyStmt))
597608
{
598609
if (es->format == EXPLAIN_FORMAT_TEXT)

src/backend/commands/portalcmds.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include "executor/executor.h"
2929
#include "executor/tstoreReceiver.h"
3030
#include "miscadmin.h"
31+
#include "nodes/queryjumble.h"
32+
#include "parser/analyze.h"
3133
#include "rewrite/rewriteHandler.h"
3234
#include "tcop/pquery.h"
3335
#include "tcop/tcopprot.h"
@@ -44,6 +46,7 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
4446
bool isTopLevel)
4547
{
4648
Query *query = castNode(Query, cstmt->query);
49+
JumbleState *jstate = NULL;
4750
List *rewritten;
4851
PlannedStmt *plan;
4952
Portal portal;
@@ -71,6 +74,13 @@ PerformCursorOpen(ParseState *pstate, DeclareCursorStmt *cstmt, ParamListInfo pa
7174
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
7275
errmsg("cannot create a cursor WITH HOLD within security-restricted operation")));
7376

77+
/* Query contained by DeclareCursor needs to be jumbled if requested */
78+
if (IsQueryIdEnabled())
79+
jstate = JumbleQuery(query);
80+
81+
if (post_parse_analyze_hook)
82+
(*post_parse_analyze_hook) (pstate, query, jstate);
83+
7484
/*
7585
* Parse analysis was done already, but we still have to run the rule
7686
* rewriter. We do not do AcquireRewriteLocks: we assume the query either

src/backend/commands/prepare.c

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -561,13 +561,12 @@ DropAllPreparedStatements(void)
561561
* "into" is NULL unless we are doing EXPLAIN CREATE TABLE AS EXECUTE,
562562
* in which case executing the query should result in creating that table.
563563
*
564-
* Note: the passed-in queryString is that of the EXPLAIN EXECUTE,
564+
* Note: the passed-in pstate's queryString is that of the EXPLAIN EXECUTE,
565565
* not the original PREPARE; we get the latter string from the plancache.
566566
*/
567567
void
568568
ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
569-
const char *queryString, ParamListInfo params,
570-
QueryEnvironment *queryEnv)
569+
ParseState *pstate, ParamListInfo params)
571570
{
572571
PreparedStatement *entry;
573572
const char *query_string;
@@ -610,10 +609,10 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
610609
/* Evaluate parameters, if any */
611610
if (entry->plansource->num_params)
612611
{
613-
ParseState *pstate;
612+
ParseState *pstate_params;
614613

615-
pstate = make_parsestate(NULL);
616-
pstate->p_sourcetext = queryString;
614+
pstate_params = make_parsestate(NULL);
615+
pstate_params->p_sourcetext = pstate->p_sourcetext;
617616

618617
/*
619618
* Need an EState to evaluate parameters; must not delete it till end
@@ -624,12 +623,12 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
624623
estate = CreateExecutorState();
625624
estate->es_param_list_info = params;
626625

627-
paramLI = EvaluateParams(pstate, entry, execstmt->params, estate);
626+
paramLI = EvaluateParams(pstate_params, entry, execstmt->params, estate);
628627
}
629628

630629
/* Replan if needed, and acquire a transient refcount */
631630
cplan = GetCachedPlan(entry->plansource, paramLI,
632-
CurrentResourceOwner, queryEnv);
631+
CurrentResourceOwner, pstate->p_queryEnv);
633632

634633
INSTR_TIME_SET_CURRENT(planduration);
635634
INSTR_TIME_SUBTRACT(planduration, planstart);
@@ -655,12 +654,11 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
655654
PlannedStmt *pstmt = lfirst_node(PlannedStmt, p);
656655

657656
if (pstmt->commandType != CMD_UTILITY)
658-
ExplainOnePlan(pstmt, into, es, query_string, paramLI, queryEnv,
657+
ExplainOnePlan(pstmt, into, es, query_string, paramLI, pstate->p_queryEnv,
659658
&planduration, (es->buffers ? &bufusage : NULL),
660659
es->memory ? &mem_counters : NULL);
661660
else
662-
ExplainOneUtility(pstmt->utilityStmt, into, es, query_string,
663-
paramLI, queryEnv);
661+
ExplainOneUtility(pstmt->utilityStmt, into, es, pstate, paramLI);
664662

665663
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */
666664

src/include/commands/explain.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ extern ExplainState *NewExplainState(void);
100100
extern TupleDesc ExplainResultDesc(ExplainStmt *stmt);
101101

102102
extern void ExplainOneUtility(Node *utilityStmt, IntoClause *into,
103-
ExplainState *es, const char *queryString,
104-
ParamListInfo params, QueryEnvironment *queryEnv);
103+
ExplainState *es, ParseState *pstate,
104+
ParamListInfo params);
105105

106106
extern void ExplainOnePlan(PlannedStmt *plannedstmt, IntoClause *into,
107107
ExplainState *es, const char *queryString,

src/include/commands/prepare.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ extern void ExecuteQuery(ParseState *pstate,
4343
DestReceiver *dest, QueryCompletion *qc);
4444
extern void DeallocateQuery(DeallocateStmt *stmt);
4545
extern void ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into,
46-
ExplainState *es, const char *queryString,
47-
ParamListInfo params, QueryEnvironment *queryEnv);
46+
ExplainState *es, ParseState *pstate,
47+
ParamListInfo params);
4848

4949
/* Low-level access to stored prepared statements */
5050
extern void StorePreparedStatement(const char *stmt_name,

src/test/regress/expected/explain.out

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,23 @@ select explain_filter('explain (verbose) select * from int8_tbl i8');
662662
Query Identifier: N
663663
(3 rows)
664664

665+
-- Test compute_query_id with utility statements containing plannable query
666+
select explain_filter('explain (verbose) declare test_cur cursor for select * from int8_tbl');
667+
explain_filter
668+
-------------------------------------------------------------
669+
Seq Scan on public.int8_tbl (cost=N.N..N.N rows=N width=N)
670+
Output: q1, q2
671+
Query Identifier: N
672+
(3 rows)
673+
674+
select explain_filter('explain (verbose) create table test_ctas as select 1');
675+
explain_filter
676+
----------------------------------------
677+
Result (cost=N.N..N.N rows=N width=N)
678+
Output: N
679+
Query Identifier: N
680+
(3 rows)
681+
665682
-- Test SERIALIZE option
666683
select explain_filter('explain (analyze,serialize) select * from int8_tbl i8');
667684
explain_filter

src/test/regress/sql/explain.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ select explain_filter('explain (verbose) select * from t1 where pg_temp.mysin(f1
163163
set compute_query_id = on;
164164
select explain_filter('explain (verbose) select * from int8_tbl i8');
165165

166+
-- Test compute_query_id with utility statements containing plannable query
167+
select explain_filter('explain (verbose) declare test_cur cursor for select * from int8_tbl');
168+
select explain_filter('explain (verbose) create table test_ctas as select 1');
169+
166170
-- Test SERIALIZE option
167171
select explain_filter('explain (analyze,serialize) select * from int8_tbl i8');
168172
select explain_filter('explain (analyze,serialize text,buffers,timing off) select * from int8_tbl i8');

0 commit comments

Comments
 (0)