Skip to content

Commit b159005

Browse files
okbob@github.comCommitfest Bot
authored andcommitted
collect session variables used in plan and assign paramid
In the plan stage we need to collect used session variables. On the order of this list, the param nodes gets paramid (fix_param_node). This number is used (later) as index to buffer of values of the used session variables. The buffer is prepared and filled by executor. Some unsupported optimizations are disabled: * parallel execution * simple expression execution in PL/pgSQL * SQL functions inlining Before execution of query with session variables we need to collect used session variables. This list is used for
1 parent a080430 commit b159005

File tree

11 files changed

+199
-9
lines changed

11 files changed

+199
-9
lines changed

doc/src/sgml/parallel.sgml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,12 @@ EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%';
515515
Plan nodes that reference a correlated <literal>SubPlan</literal>.
516516
</para>
517517
</listitem>
518+
519+
<listitem>
520+
<para>
521+
Plan nodes that use a session variable.
522+
</para>
523+
</listitem>
518524
</itemizedlist>
519525

520526
<sect2 id="parallel-labeling">

src/backend/catalog/dependency.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,11 @@ find_expr_references_walker(Node *node,
18751875
{
18761876
Param *param = (Param *) node;
18771877

1878+
/* a variable parameter depends on the session variable */
1879+
if (param->paramkind == PARAM_VARIABLE)
1880+
add_object_address(VariableRelationId, param->paramvarid, 0,
1881+
context->addrs);
1882+
18781883
/* A parameter must depend on the parameter's datatype */
18791884
add_object_address(TypeRelationId, param->paramtype, 0,
18801885
context->addrs);

src/backend/optimizer/plan/planner.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
374374
glob->dependsOnRole = false;
375375
glob->partition_directory = NULL;
376376
glob->rel_notnullatts_hash = NULL;
377+
glob->sessionVariables = NIL;
377378

378379
/*
379380
* Assess whether it's feasible to use parallel mode for this query. We
@@ -617,6 +618,9 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
617618
result->paramExecTypes = glob->paramExecTypes;
618619
/* utilityStmt should be null, but we might as well copy it */
619620
result->utilityStmt = parse->utilityStmt;
621+
622+
result->sessionVariables = glob->sessionVariables;
623+
620624
result->stmt_location = parse->stmt_location;
621625
result->stmt_len = parse->stmt_len;
622626

@@ -805,6 +809,13 @@ subquery_planner(PlannerGlobal *glob, Query *parse, char *plan_name,
805809
*/
806810
pull_up_subqueries(root);
807811

812+
/*
813+
* Check if some subquery uses a session variable. The flag
814+
* hasSessionVariables should be true if the query or some subquery uses a
815+
* session variable.
816+
*/
817+
pull_up_has_session_variables(root);
818+
808819
/*
809820
* If this is a simple UNION ALL query, flatten it into an appendrel. We
810821
* do this now because it requires applying pull_up_subqueries to the leaf

src/backend/optimizer/plan/setrefs.c

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ static List *set_returning_clause_references(PlannerInfo *root,
210210
static List *set_windowagg_runcondition_references(PlannerInfo *root,
211211
List *runcondition,
212212
Plan *plan);
213+
static bool pull_up_has_session_variables_walker(Node *node,
214+
PlannerInfo *root);
215+
static void record_plan_variable_dependency(PlannerInfo *root, Oid varid);
213216

214217

215218
/*****************************************************************************
@@ -1341,6 +1344,50 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
13411344
return plan;
13421345
}
13431346

1347+
/*
1348+
* Search usage of session variables in subqueries
1349+
*/
1350+
void
1351+
pull_up_has_session_variables(PlannerInfo *root)
1352+
{
1353+
Query *query = root->parse;
1354+
1355+
if (query->hasSessionVariables)
1356+
{
1357+
root->hasSessionVariables = true;
1358+
}
1359+
else
1360+
{
1361+
(void) query_tree_walker(query,
1362+
pull_up_has_session_variables_walker,
1363+
(void *) root, 0);
1364+
}
1365+
}
1366+
1367+
static bool
1368+
pull_up_has_session_variables_walker(Node *node, PlannerInfo *root)
1369+
{
1370+
if (node == NULL)
1371+
return false;
1372+
if (IsA(node, Query))
1373+
{
1374+
Query *query = (Query *) node;
1375+
1376+
if (query->hasSessionVariables)
1377+
{
1378+
root->hasSessionVariables = true;
1379+
return false;
1380+
}
1381+
1382+
/* recurse into subselects */
1383+
return query_tree_walker((Query *) node,
1384+
pull_up_has_session_variables_walker,
1385+
(void *) root, 0);
1386+
}
1387+
return expression_tree_walker(node, pull_up_has_session_variables_walker,
1388+
(void *) root);
1389+
}
1390+
13441391
/*
13451392
* set_indexonlyscan_references
13461393
* Do set_plan_references processing on an IndexOnlyScan
@@ -2041,8 +2088,9 @@ copyVar(Var *var)
20412088
* This is code that is common to all variants of expression-fixing.
20422089
* We must look up operator opcode info for OpExpr and related nodes,
20432090
* add OIDs from regclass Const nodes into root->glob->relationOids, and
2044-
* add PlanInvalItems for user-defined functions into root->glob->invalItems.
2045-
* We also fill in column index lists for GROUPING() expressions.
2091+
* add PlanInvalItems for user-defined functions and session variables into
2092+
* root->glob->invalItems. We also fill in column index lists for GROUPING()
2093+
* expressions.
20462094
*
20472095
* We assume it's okay to update opcode info in-place. So this could possibly
20482096
* scribble on the planner's input data structures, but it's OK.
@@ -2132,6 +2180,13 @@ fix_expr_common(PlannerInfo *root, Node *node)
21322180
g->cols = cols;
21332181
}
21342182
}
2183+
else if (IsA(node, Param))
2184+
{
2185+
Param *p = (Param *) node;
2186+
2187+
if (p->paramkind == PARAM_VARIABLE)
2188+
record_plan_variable_dependency(root, p->paramvarid);
2189+
}
21352190
}
21362191

21372192
/*
@@ -2141,6 +2196,10 @@ fix_expr_common(PlannerInfo *root, Node *node)
21412196
* If it's a PARAM_MULTIEXPR, replace it with the appropriate Param from
21422197
* root->multiexpr_params; otherwise no change is needed.
21432198
* Just for paranoia's sake, we make a copy of the node in either case.
2199+
*
2200+
* If it's a PARAM_VARIABLE, then we collect used session variables in
2201+
* the list root->glob->sessionVariable. Also, assign the parameter's
2202+
* "paramid" to the parameter's position in that list.
21442203
*/
21452204
static Node *
21462205
fix_param_node(PlannerInfo *root, Param *p)
@@ -2159,6 +2218,40 @@ fix_param_node(PlannerInfo *root, Param *p)
21592218
elog(ERROR, "unexpected PARAM_MULTIEXPR ID: %d", p->paramid);
21602219
return copyObject(list_nth(params, colno - 1));
21612220
}
2221+
2222+
if (p->paramkind == PARAM_VARIABLE)
2223+
{
2224+
int n = 0;
2225+
bool found = false;
2226+
2227+
/* we will modify object */
2228+
p = (Param *) copyObject(p);
2229+
2230+
/*
2231+
* Now, we can actualize list of session variables, and we can
2232+
* complete paramid parameter.
2233+
*/
2234+
foreach_oid(varid, root->glob->sessionVariables)
2235+
{
2236+
if (varid == p->paramvarid)
2237+
{
2238+
p->paramid = n;
2239+
found = true;
2240+
break;
2241+
}
2242+
n += 1;
2243+
}
2244+
2245+
if (!found)
2246+
{
2247+
root->glob->sessionVariables = lappend_oid(root->glob->sessionVariables,
2248+
p->paramvarid);
2249+
p->paramid = n;
2250+
}
2251+
2252+
return (Node *) p;
2253+
}
2254+
21622255
return (Node *) copyObject(p);
21632256
}
21642257

@@ -2220,7 +2313,10 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
22202313
* replacing Aggref nodes that should be replaced by initplan output Params,
22212314
* choosing the best implementation for AlternativeSubPlans,
22222315
* looking up operator opcode info for OpExpr and related nodes,
2223-
* and adding OIDs from regclass Const nodes into root->glob->relationOids.
2316+
* adding OIDs from regclass Const nodes into root->glob->relationOids,
2317+
* assigning paramvarid to PARAM_VARIABLE params, and collecting the
2318+
* OIDs of session variables in the root->glob->sessionVariables list
2319+
* (paramvarid is the position of the session variable in this list).
22242320
*
22252321
* 'node': the expression to be modified
22262322
* 'rtoffset': how much to increment varnos by
@@ -2242,7 +2338,8 @@ fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
22422338
root->multiexpr_params != NIL ||
22432339
root->glob->lastPHId != 0 ||
22442340
root->minmax_aggs != NIL ||
2245-
root->hasAlternativeSubPlans)
2341+
root->hasAlternativeSubPlans ||
2342+
root->hasSessionVariables)
22462343
{
22472344
return fix_scan_expr_mutator(node, &context);
22482345
}
@@ -3635,6 +3732,25 @@ record_plan_type_dependency(PlannerInfo *root, Oid typid)
36353732
}
36363733
}
36373734

3735+
/*
3736+
* Record dependency on a session variable. The variable can be used as a
3737+
* session variable in an expression list.
3738+
*/
3739+
static void
3740+
record_plan_variable_dependency(PlannerInfo *root, Oid varid)
3741+
{
3742+
PlanInvalItem *inval_item = makeNode(PlanInvalItem);
3743+
3744+
/* paramid is still session variable id */
3745+
inval_item->cacheId = VARIABLEOID;
3746+
inval_item->hashValue = GetSysCacheHashValue1(VARIABLEOID,
3747+
ObjectIdGetDatum(varid));
3748+
3749+
/* append this variable to global, register dependency */
3750+
root->glob->invalItems = lappend(root->glob->invalItems,
3751+
inval_item);
3752+
}
3753+
36383754
/*
36393755
* extract_query_dependencies
36403756
* Given a rewritten, but not yet planned, query or queries

src/backend/optimizer/prep/prepjointree.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,6 +1647,9 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
16471647
/* If subquery had any RLS conditions, now main query does too */
16481648
parse->hasRowSecurity |= subquery->hasRowSecurity;
16491649

1650+
/* if the subquery had session variables, the main query does too */
1651+
parse->hasSessionVariables |= subquery->hasSessionVariables;
1652+
16501653
/*
16511654
* subquery won't be pulled up if it hasAggs, hasWindowFuncs, or
16521655
* hasTargetSRFs, so no work needed on those flags

src/backend/optimizer/util/clauses.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "catalog/pg_operator.h"
2626
#include "catalog/pg_proc.h"
2727
#include "catalog/pg_type.h"
28+
#include "commands/session_variable.h"
2829
#include "executor/executor.h"
2930
#include "executor/functions.h"
3031
#include "funcapi.h"
@@ -939,6 +940,13 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
939940
if (param->paramkind == PARAM_EXTERN)
940941
return false;
941942

943+
/* we don't support passing session variables to workers */
944+
if (param->paramkind == PARAM_VARIABLE)
945+
{
946+
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
947+
return true;
948+
}
949+
942950
if (param->paramkind != PARAM_EXEC ||
943951
!list_member_int(context->safe_param_ids, param->paramid))
944952
{
@@ -2397,6 +2405,7 @@ convert_saop_to_hashed_saop_walker(Node *node, void *context)
23972405
* value of the Param.
23982406
* 2. Fold stable, as well as immutable, functions to constants.
23992407
* 3. Reduce PlaceHolderVar nodes to their contained expressions.
2408+
* 4. Current value of session variable can be used for estimation too.
24002409
*--------------------
24012410
*/
24022411
Node *
@@ -2523,6 +2532,27 @@ eval_const_expressions_mutator(Node *node,
25232532
}
25242533
}
25252534
}
2535+
else if (param->paramkind == PARAM_VARIABLE &&
2536+
context->estimate)
2537+
{
2538+
int16 typLen;
2539+
bool typByVal;
2540+
Datum pval;
2541+
bool isnull;
2542+
2543+
get_typlenbyval(param->paramtype,
2544+
&typLen, &typByVal);
2545+
2546+
pval = GetSessionVariable(param->paramvarid, &isnull);
2547+
2548+
return (Node *) makeConst(param->paramtype,
2549+
param->paramtypmod,
2550+
param->paramcollid,
2551+
(int) typLen,
2552+
pval,
2553+
isnull,
2554+
typByVal);
2555+
}
25262556

25272557
/*
25282558
* Not replaceable, so just copy the Param (no need to
@@ -4822,7 +4852,8 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
48224852
querytree->limitOffset ||
48234853
querytree->limitCount ||
48244854
querytree->setOperations ||
4825-
list_length(querytree->targetList) != 1)
4855+
(list_length(querytree->targetList) != 1) ||
4856+
querytree->hasSessionVariables)
48264857
goto fail;
48274858

48284859
/* If the function result is composite, resolve it */

src/backend/utils/cache/plancache.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858

5959
#include "access/transam.h"
6060
#include "catalog/namespace.h"
61+
#include "catalog/pg_variable.h"
6162
#include "executor/executor.h"
6263
#include "miscadmin.h"
6364
#include "nodes/nodeFuncs.h"
@@ -153,6 +154,7 @@ InitPlanCache(void)
153154
CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
154155
CacheRegisterSyscacheCallback(FOREIGNSERVEROID, PlanCacheSysCallback, (Datum) 0);
155156
CacheRegisterSyscacheCallback(FOREIGNDATAWRAPPEROID, PlanCacheSysCallback, (Datum) 0);
157+
CacheRegisterSyscacheCallback(VARIABLEOID, PlanCacheObjectCallback, (Datum) 0);
156158
}
157159

158160
/*
@@ -2196,7 +2198,9 @@ PlanCacheRelCallback(Datum arg, Oid relid)
21962198

21972199
/*
21982200
* PlanCacheObjectCallback
2199-
* Syscache inval callback function for PROCOID and TYPEOID caches
2201+
* Syscache inval callback function for TYPEOID, PROCOID, NAMESPACEOID,
2202+
* OPEROID, AMOPOPID, FOREIGNSERVEROID, FOREIGNDATAWRAPPEROID and
2203+
* VARIABLEOID caches.
22002204
*
22012205
* Invalidate all plans mentioning the object with the specified hash value,
22022206
* or all plans mentioning any member of this cache if hashvalue == 0.

src/backend/utils/fmgr/fmgr.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,9 +1991,13 @@ get_call_expr_arg_stable(Node *expr, int argnum)
19911991
*/
19921992
if (IsA(arg, Const))
19931993
return true;
1994-
if (IsA(arg, Param) &&
1995-
((Param *) arg)->paramkind == PARAM_EXTERN)
1996-
return true;
1994+
if (IsA(arg, Param))
1995+
{
1996+
Param *p = (Param *) arg;
1997+
1998+
if (p->paramkind == PARAM_EXTERN || p->paramkind == PARAM_VARIABLE)
1999+
return true;
2000+
}
19972001

19982002
return false;
19992003
}

src/include/nodes/pathnodes.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ typedef struct PlannerGlobal
189189
/* extension state */
190190
void **extension_state pg_node_attr(read_write_ignore);
191191
int extension_state_allocated;
192+
193+
/* list of used session variables */
194+
List *sessionVariables;
192195
} PlannerGlobal;
193196

194197
/* macro for fetching the Plan associated with a SubPlan node */
@@ -547,6 +550,8 @@ struct PlannerInfo
547550
bool hasRecursion;
548551
/* true if a planner extension may replan this subquery */
549552
bool assumeReplanning;
553+
/* true if session variables were used */
554+
bool hasSessionVariables;
550555

551556
/*
552557
* The rangetable index for the RTE_GROUP RTE, or 0 if there is no

src/include/nodes/plannodes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ typedef struct PlannedStmt
158158
*/
159159
List *extension_state;
160160

161+
/* OIDs for PARAM_VARIABLE Params */
162+
List *sessionVariables;
163+
161164
/* statement location in source string (copied from Query) */
162165
/* start location, or -1 if unknown */
163166
ParseLoc stmt_location;

0 commit comments

Comments
 (0)