Skip to content

Commit 4d12f00

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 loading variables to executed query. plan
1 parent 9f24652 commit 4d12f00

File tree

12 files changed

+214
-6
lines changed

12 files changed

+214
-6
lines changed

doc/src/sgml/parallel.sgml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,12 @@ EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%';
524524
Plan nodes that reference a correlated <literal>SubPlan</literal>.
525525
</para>
526526
</listitem>
527+
528+
<listitem>
529+
<para>
530+
Usage of a session variable.
531+
</para>
532+
</listitem>
527533
</itemizedlist>
528534

529535
<sect2 id="parallel-labeling">

src/backend/catalog/dependency.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,6 +1901,16 @@ find_expr_references_walker(Node *node,
19011901
{
19021902
Param *param = (Param *) node;
19031903

1904+
/*
1905+
* catalog less session variable variable cannot be used in persistent
1906+
* catalog based object.
1907+
*/
1908+
if (param->paramkind == PARAM_VARIABLE)
1909+
ereport(ERROR,
1910+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1911+
errmsg("session variable \"%s\" cannot be referenced in a catalog object",
1912+
param->paramvarname)));
1913+
19041914
/* A parameter must depend on the parameter's datatype */
19051915
add_object_address(TypeRelationId, param->paramtype, 0,
19061916
context->addrs);

src/backend/commands/session_variable.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,45 @@ get_session_variable_type_typmod_collid(char *varname,
127127
*collid = svar->varcollation;
128128
}
129129

130+
/*
131+
* Returns a copy of the value of the session variable (in the current memory
132+
* context).
133+
*/
134+
Datum
135+
GetSessionVariableWithTypecheck(char *varname,
136+
Oid typid, int32 typmod,
137+
bool *isnull)
138+
{
139+
SVariable svar;
140+
Datum result;
141+
142+
svar = search_variable(varname);
143+
144+
if (svar->vartype != typid || svar->vartypmod != typmod)
145+
ereport(ERROR,
146+
(errcode(ERRCODE_DATATYPE_MISMATCH),
147+
errmsg("session variable %s is not of a type %s but type %s",
148+
varname,
149+
format_type_with_typemod(typid, typmod),
150+
format_type_with_typemod(svar->vartype, svar->vartypmod))));
151+
152+
/* only owner can get content of variable */
153+
if (svar->varowner != GetUserId() && !superuser())
154+
ereport(ERROR,
155+
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
156+
errmsg("permission denied for session variable %s",
157+
varname)));
158+
159+
if (!svar->isnull)
160+
result = datumCopy(svar->value, svar->typbyval, svar->typlen);
161+
else
162+
result = (Datum) 0;
163+
164+
*isnull = svar->isnull;
165+
166+
return result;
167+
}
168+
130169
/*
131170
* Creates a new variable - does new entry in sessionvars
132171
*

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: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ 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);
213215

214216

215217
/*****************************************************************************
@@ -1341,6 +1343,50 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
13411343
return plan;
13421344
}
13431345

1346+
/*
1347+
* Search usage of session variables in subqueries
1348+
*/
1349+
void
1350+
pull_up_has_session_variables(PlannerInfo *root)
1351+
{
1352+
Query *query = root->parse;
1353+
1354+
if (query->hasSessionVariables)
1355+
{
1356+
root->hasSessionVariables = true;
1357+
}
1358+
else
1359+
{
1360+
(void) query_tree_walker(query,
1361+
pull_up_has_session_variables_walker,
1362+
(void *) root, 0);
1363+
}
1364+
}
1365+
1366+
static bool
1367+
pull_up_has_session_variables_walker(Node *node, PlannerInfo *root)
1368+
{
1369+
if (node == NULL)
1370+
return false;
1371+
if (IsA(node, Query))
1372+
{
1373+
Query *query = (Query *) node;
1374+
1375+
if (query->hasSessionVariables)
1376+
{
1377+
root->hasSessionVariables = true;
1378+
return false;
1379+
}
1380+
1381+
/* recurse into subselects */
1382+
return query_tree_walker((Query *) node,
1383+
pull_up_has_session_variables_walker,
1384+
(void *) root, 0);
1385+
}
1386+
return expression_tree_walker(node, pull_up_has_session_variables_walker,
1387+
(void *) root);
1388+
}
1389+
13441390
/*
13451391
* set_indexonlyscan_references
13461392
* Do set_plan_references processing on an IndexOnlyScan
@@ -2141,6 +2187,10 @@ fix_expr_common(PlannerInfo *root, Node *node)
21412187
* If it's a PARAM_MULTIEXPR, replace it with the appropriate Param from
21422188
* root->multiexpr_params; otherwise no change is needed.
21432189
* Just for paranoia's sake, we make a copy of the node in either case.
2190+
*
2191+
* If it's a PARAM_VARIABLE, then we collect used session variables in
2192+
* the list root->glob->sessionVariable. Also, assign the parameter's
2193+
* "paramid" to the parameter's position in that list.
21442194
*/
21452195
static Node *
21462196
fix_param_node(PlannerInfo *root, Param *p)
@@ -2159,6 +2209,43 @@ fix_param_node(PlannerInfo *root, Param *p)
21592209
elog(ERROR, "unexpected PARAM_MULTIEXPR ID: %d", p->paramid);
21602210
return copyObject(list_nth(params, colno - 1));
21612211
}
2212+
2213+
if (p->paramkind == PARAM_VARIABLE)
2214+
{
2215+
int n = 0;
2216+
2217+
/* we will modify object */
2218+
p = (Param *) copyObject(p);
2219+
2220+
/*
2221+
* Now, we can actualize list of session variables, and we can
2222+
* complete paramid parameter.
2223+
*/
2224+
foreach_node(Param, paramvar, root->glob->sessionVariables)
2225+
{
2226+
if (strcmp(paramvar->paramvarname, p->paramvarname) == 0)
2227+
{
2228+
p->paramid = paramvar->paramid;
2229+
2230+
return (Node *) p;
2231+
}
2232+
2233+
n += 1;
2234+
}
2235+
2236+
p->paramid = n;
2237+
2238+
/*
2239+
* Because session variables are catalogless, we cannot to use plan
2240+
* invalidation. Then we need to check type, typmod, collid any time,
2241+
* when we load values of session variables to parameter's buffer.
2242+
* For this purpose it is more easy to save complete Param node.
2243+
*/
2244+
root->glob->sessionVariables = lappend(root->glob->sessionVariables, p);
2245+
2246+
return (Node *) p;
2247+
}
2248+
21622249
return (Node *) copyObject(p);
21632250
}
21642251

@@ -2220,7 +2307,9 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
22202307
* replacing Aggref nodes that should be replaced by initplan output Params,
22212308
* choosing the best implementation for AlternativeSubPlans,
22222309
* looking up operator opcode info for OpExpr and related nodes,
2223-
* and adding OIDs from regclass Const nodes into root->glob->relationOids.
2310+
* adding OIDs from regclass Const nodes into root->glob->relationOids,
2311+
* assigning paramvarid to PARAM_VARIABLE params, and collecting the
2312+
* of session variables in the root->glob->sessionVariables list.
22242313
*
22252314
* 'node': the expression to be modified
22262315
* 'rtoffset': how much to increment varnos by
@@ -2242,7 +2331,8 @@ fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
22422331
root->multiexpr_params != NIL ||
22432332
root->glob->lastPHId != 0 ||
22442333
root->minmax_aggs != NIL ||
2245-
root->hasAlternativeSubPlans)
2334+
root->hasAlternativeSubPlans ||
2335+
root->hasSessionVariables)
22462336
{
22472337
return fix_scan_expr_mutator(node, &context);
22482338
}

src/backend/optimizer/prep/prepjointree.c

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

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

src/backend/optimizer/util/clauses.c

Lines changed: 34 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"
@@ -947,6 +948,13 @@ max_parallel_hazard_walker(Node *node, max_parallel_hazard_context *context)
947948
if (param->paramkind == PARAM_EXTERN)
948949
return false;
949950

951+
/* we don't support passing session variables to workers */
952+
if (param->paramkind == PARAM_VARIABLE)
953+
{
954+
if (max_parallel_hazard_test(PROPARALLEL_RESTRICTED, context))
955+
return true;
956+
}
957+
950958
if (param->paramkind != PARAM_EXEC ||
951959
!list_member_int(context->safe_param_ids, param->paramid))
952960
{
@@ -2405,6 +2413,7 @@ convert_saop_to_hashed_saop_walker(Node *node, void *context)
24052413
* value of the Param.
24062414
* 2. Fold stable, as well as immutable, functions to constants.
24072415
* 3. Reduce PlaceHolderVar nodes to their contained expressions.
2416+
* 4. Current value of session variable can be used for estimation too.
24082417
*--------------------
24092418
*/
24102419
Node *
@@ -2531,6 +2540,29 @@ eval_const_expressions_mutator(Node *node,
25312540
}
25322541
}
25332542
}
2543+
else if (param->paramkind == PARAM_VARIABLE &&
2544+
context->estimate)
2545+
{
2546+
int16 typLen;
2547+
bool typByVal;
2548+
Datum pval;
2549+
bool isnull;
2550+
2551+
get_typlenbyval(param->paramtype, &typLen, &typByVal);
2552+
2553+
pval = GetSessionVariableWithTypecheck(param->paramvarname,
2554+
param->paramtype,
2555+
param->paramtypmod,
2556+
&isnull);
2557+
2558+
return (Node *) makeConst(param->paramtype,
2559+
param->paramtypmod,
2560+
param->paramcollid,
2561+
(int) typLen,
2562+
pval,
2563+
isnull,
2564+
typByVal);
2565+
}
25342566

25352567
/*
25362568
* Not replaceable, so just copy the Param (no need to
@@ -4903,7 +4935,8 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
49034935
querytree->limitOffset ||
49044936
querytree->limitCount ||
49054937
querytree->setOperations ||
4906-
list_length(querytree->targetList) != 1)
4938+
(list_length(querytree->targetList) != 1) ||
4939+
querytree->hasSessionVariables)
49074940
goto fail;
49084941

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

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/commands/session_variable.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
extern void CreateVariable(ParseState *pstate, CreateSessionVarStmt *stmt);
2323
extern void DropVariableByName(char *varname);
2424

25+
extern Datum GetSessionVariableWithTypecheck(char *varname, Oid typid, int32 typmod, bool *isnull);
26+
2527
extern void get_session_variable_type_typmod_collid(char *varname,
2628
Oid *typid,
2729
int32 *typmod,

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

0 commit comments

Comments
 (0)