Skip to content

Commit 9f24652

Browse files
okbob@github.comCommitfest Bot
authored andcommitted
parsing session variable fences
The session variables can be used in query only inside the variable fence. This is special syntax VARIABLE(varname), that eliminates a risk of collision between variable and column identifier. The session variables cannot be used as parameters of CALL or EXECUTE commands. These commands evaluates arguments by direct call of expression executor, and direct access to session variables from expression executor will be implemented later (in next step)
1 parent b914aca commit 9f24652

File tree

16 files changed

+228
-3
lines changed

16 files changed

+228
-3
lines changed

doc/src/sgml/ddl.sgml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5673,6 +5673,16 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
56735673
variable is stored in session memory and is private to each session. It is
56745674
automatically released when the session ends.
56755675
</para>
5676+
5677+
<para>
5678+
In a query, a session variable can only be referenced using the special
5679+
<literal>VARIABLE(varname)</literal> syntax. This avoids any risk of
5680+
collision between variable names and column names.
5681+
</para>
5682+
<programlisting>
5683+
SELECT VARIABLE(current_user_id);
5684+
</programlisting>
5685+
</para>
56765686
</sect1>
56775687

56785688
<sect1 id="ddl-others">

src/backend/commands/prepare.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,14 @@ EvaluateParams(ParseState *pstate, PreparedStatement *pstmt, List *params,
341341
i++;
342342
}
343343

344+
/*
345+
* The arguments of EXECUTE are evaluated by a direct expression executor
346+
* call. This mode doesn't support session variables yet. It will be
347+
* enabled later. This case should be blocked parser by
348+
* expr_kind_allows_session_variables, so only assertions is used here.
349+
*/
350+
Assert(!pstate->p_hasSessionVariables);
351+
344352
/* Prepare the expressions for execution */
345353
exprstates = ExecPrepareExprList(params, estate);
346354

src/backend/commands/session_variable.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,27 @@ search_variable(char *varname)
106106
return svar;
107107
}
108108

109+
/*
110+
* Returns the type, typmod and collid of the given session variable.
111+
*
112+
* Raises an error when the variable doesn't exists and *error is null.
113+
*/
114+
void
115+
get_session_variable_type_typmod_collid(char *varname,
116+
Oid *typid,
117+
int32 *typmod,
118+
Oid *collid)
119+
{
120+
SVariable svar;
121+
122+
svar = search_variable(varname);
123+
124+
/* only owner can set content of variable */
125+
*typid = svar->vartype;
126+
*typmod = svar->vartypmod;
127+
*collid = svar->varcollation;
128+
}
129+
109130
/*
110131
* Creates a new variable - does new entry in sessionvars
111132
*

src/backend/nodes/nodeFuncs.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,9 @@ exprLocation(const Node *expr)
16691669
case T_ParamRef:
16701670
loc = ((const ParamRef *) expr)->location;
16711671
break;
1672+
case T_VariableFence:
1673+
loc = ((const VariableFence *) expr)->location;
1674+
break;
16721675
case T_A_Const:
16731676
loc = ((const A_Const *) expr)->location;
16741677
break;
@@ -4701,6 +4704,9 @@ raw_expression_tree_walker_impl(Node *node,
47014704
return true;
47024705
}
47034706
break;
4707+
case T_VariableFence:
4708+
/* we assume the fields contain nothing interesting */
4709+
break;
47044710
default:
47054711
elog(ERROR, "unrecognized node type: %d",
47064712
(int) nodeTag(node));

src/backend/parser/analyze.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
619619
qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
620620
qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
621621
qry->hasAggs = pstate->p_hasAggs;
622+
qry->hasSessionVariables = pstate->p_hasSessionVariables;
622623

623624
assign_query_collations(pstate, qry);
624625

@@ -1044,6 +1045,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
10441045

10451046
qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
10461047
qry->hasSubLinks = pstate->p_hasSubLinks;
1048+
qry->hasSessionVariables = pstate->p_hasSessionVariables;
10471049

10481050
assign_query_collations(pstate, qry);
10491051

@@ -1527,6 +1529,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt,
15271529
qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
15281530
qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
15291531
qry->hasAggs = pstate->p_hasAggs;
1532+
qry->hasSessionVariables = pstate->p_hasSessionVariables;
15301533

15311534
foreach(l, stmt->lockingClause)
15321535
{
@@ -1753,6 +1756,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
17531756
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
17541757

17551758
qry->hasSubLinks = pstate->p_hasSubLinks;
1759+
qry->hasSessionVariables = pstate->p_hasSessionVariables;
17561760

17571761
assign_query_collations(pstate, qry);
17581762

@@ -2004,6 +2008,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
20042008
qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
20052009
qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
20062010
qry->hasAggs = pstate->p_hasAggs;
2011+
qry->hasSessionVariables = pstate->p_hasSessionVariables;
20072012

20082013
foreach(l, lockingClause)
20092014
{
@@ -2476,6 +2481,7 @@ transformReturnStmt(ParseState *pstate, ReturnStmt *stmt)
24762481
qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
24772482
qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
24782483
qry->hasAggs = pstate->p_hasAggs;
2484+
qry->hasSessionVariables = pstate->p_hasSessionVariables;
24792485

24802486
assign_query_collations(pstate, qry);
24812487

@@ -2543,6 +2549,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
25432549

25442550
qry->hasTargetSRFs = pstate->p_hasTargetSRFs;
25452551
qry->hasSubLinks = pstate->p_hasSubLinks;
2552+
qry->hasSessionVariables = pstate->p_hasSessionVariables;
25462553

25472554
assign_query_collations(pstate, qry);
25482555

src/backend/parser/gram.y

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
531531
%type <node> def_arg columnElem where_clause where_or_current_clause
532532
a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound
533533
columnref having_clause func_table xmltable array_expr
534-
OptWhereClause operator_def_arg
534+
OptWhereClause operator_def_arg variable_fence
535535
%type <list> opt_column_and_period_list
536536
%type <list> rowsfrom_item rowsfrom_list opt_col_def_list
537537
%type <boolean> opt_ordinality opt_without_overlaps
@@ -887,7 +887,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
887887
*/
888888
%nonassoc UNBOUNDED NESTED /* ideally would have same precedence as IDENT */
889889
%nonassoc IDENT PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
890-
SET KEYS OBJECT_P SCALAR VALUE_P WITH WITHOUT PATH
890+
SET KEYS OBJECT_P SCALAR VALUE_P WITH WITHOUT PATH VARIABLE
891891
%left Op OPERATOR /* multi-character ops and user-defined operators */
892892
%left '+' '-'
893893
%left '*' '/' '%'
@@ -15719,6 +15719,8 @@ c_expr: columnref { $$ = $1; }
1571915719
else
1572015720
$$ = $2;
1572115721
}
15722+
| variable_fence
15723+
{ $$ = $1; }
1572215724
| case_expr
1572315725
{ $$ = $1; }
1572415726
| func_expr
@@ -17121,6 +17123,17 @@ case_arg: a_expr { $$ = $1; }
1712117123
| /*EMPTY*/ { $$ = NULL; }
1712217124
;
1712317125

17126+
variable_fence:
17127+
VARIABLE '(' ColId ')'
17128+
{
17129+
VariableFence *vf = makeNode(VariableFence);
17130+
17131+
vf->varname = $3;
17132+
vf->location = @3;
17133+
$$ = (Node *) vf;
17134+
}
17135+
;
17136+
1712417137
columnref: ColId
1712517138
{
1712617139
$$ = makeColumnRef($1, NIL, @1, yyscanner);

src/backend/parser/parse_expr.c

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "access/htup_details.h"
1919
#include "catalog/pg_aggregate.h"
2020
#include "catalog/pg_type.h"
21+
#include "commands/session_variable.h"
2122
#include "miscadmin.h"
2223
#include "nodes/makefuncs.h"
2324
#include "nodes/nodeFuncs.h"
@@ -77,6 +78,7 @@ static Node *transformWholeRowRef(ParseState *pstate,
7778
static Node *transformIndirection(ParseState *pstate, A_Indirection *ind);
7879
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
7980
static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
81+
static Node *transformVariableFence(ParseState *pstate, VariableFence *vf);
8082
static Node *transformJsonObjectConstructor(ParseState *pstate,
8183
JsonObjectConstructor *ctor);
8284
static Node *transformJsonArrayConstructor(ParseState *pstate,
@@ -371,6 +373,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
371373
result = transformJsonFuncExpr(pstate, (JsonFuncExpr *) expr);
372374
break;
373375

376+
case T_VariableFence:
377+
result = transformVariableFence(pstate, (VariableFence *) expr);
378+
break;
379+
374380
default:
375381
/* should not reach here */
376382
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -904,6 +910,119 @@ transformParamRef(ParseState *pstate, ParamRef *pref)
904910
return result;
905911
}
906912

913+
/*
914+
* Returns true if the given expression kind is valid for session variables.
915+
* Session variables can be used everywhere where external parameters can be
916+
* used. Session variables are not allowed in DDL commands or in constraints.
917+
*
918+
* An identifier can be parsed as a session variable only for expression kinds
919+
* where session variables are allowed. This is the primary usage of this
920+
* function.
921+
*
922+
* The second usage of this function is to decide whether a "column does not
923+
* exist" or a "column or variable does not exist" error message should be
924+
* printed. When we are in an expression where session variables cannot be
925+
* used, we raise the first form of error message.
926+
*/
927+
static bool
928+
expr_kind_allows_session_variables(ParseExprKind p_expr_kind)
929+
{
930+
bool result = false;
931+
932+
switch (p_expr_kind)
933+
{
934+
case EXPR_KIND_NONE:
935+
Assert(false); /* can't happen */
936+
return false;
937+
938+
/* session variables allowed */
939+
case EXPR_KIND_OTHER:
940+
case EXPR_KIND_JOIN_ON:
941+
case EXPR_KIND_FROM_SUBSELECT:
942+
case EXPR_KIND_FROM_FUNCTION:
943+
case EXPR_KIND_WHERE:
944+
case EXPR_KIND_HAVING:
945+
case EXPR_KIND_FILTER:
946+
case EXPR_KIND_WINDOW_PARTITION:
947+
case EXPR_KIND_WINDOW_ORDER:
948+
case EXPR_KIND_WINDOW_FRAME_RANGE:
949+
case EXPR_KIND_WINDOW_FRAME_ROWS:
950+
case EXPR_KIND_WINDOW_FRAME_GROUPS:
951+
case EXPR_KIND_SELECT_TARGET:
952+
case EXPR_KIND_UPDATE_TARGET:
953+
case EXPR_KIND_UPDATE_SOURCE:
954+
case EXPR_KIND_MERGE_WHEN:
955+
case EXPR_KIND_MERGE_RETURNING:
956+
case EXPR_KIND_GROUP_BY:
957+
case EXPR_KIND_ORDER_BY:
958+
case EXPR_KIND_DISTINCT_ON:
959+
case EXPR_KIND_LIMIT:
960+
case EXPR_KIND_OFFSET:
961+
case EXPR_KIND_RETURNING:
962+
case EXPR_KIND_VALUES:
963+
case EXPR_KIND_VALUES_SINGLE:
964+
result = true;
965+
break;
966+
967+
/* session variables not allowed */
968+
case EXPR_KIND_INSERT_TARGET:
969+
case EXPR_KIND_EXECUTE_PARAMETER:
970+
case EXPR_KIND_CALL_ARGUMENT:
971+
case EXPR_KIND_CHECK_CONSTRAINT:
972+
case EXPR_KIND_DOMAIN_CHECK:
973+
case EXPR_KIND_COLUMN_DEFAULT:
974+
case EXPR_KIND_FUNCTION_DEFAULT:
975+
case EXPR_KIND_INDEX_EXPRESSION:
976+
case EXPR_KIND_INDEX_PREDICATE:
977+
case EXPR_KIND_STATS_EXPRESSION:
978+
case EXPR_KIND_TRIGGER_WHEN:
979+
case EXPR_KIND_PARTITION_BOUND:
980+
case EXPR_KIND_PARTITION_EXPRESSION:
981+
case EXPR_KIND_GENERATED_COLUMN:
982+
case EXPR_KIND_JOIN_USING:
983+
case EXPR_KIND_CYCLE_MARK:
984+
case EXPR_KIND_ALTER_COL_TRANSFORM:
985+
case EXPR_KIND_POLICY:
986+
case EXPR_KIND_COPY_WHERE:
987+
result = false;
988+
break;
989+
}
990+
991+
return result;
992+
}
993+
994+
static Node *
995+
transformVariableFence(ParseState *pstate, VariableFence *vf)
996+
{
997+
Param *param;
998+
Oid typid;
999+
int32 typmod;
1000+
Oid collid;
1001+
1002+
/* VariableFence can be used only in context when variables are supported */
1003+
if (!expr_kind_allows_session_variables(pstate->p_expr_kind))
1004+
ereport(ERROR,
1005+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1006+
errmsg("session variable reference is not supported here"),
1007+
parser_errposition(pstate, vf->location)));
1008+
1009+
get_session_variable_type_typmod_collid(vf->varname,
1010+
&typid, &typmod, &collid);
1011+
1012+
1013+
param = makeNode(Param);
1014+
1015+
param->paramkind = PARAM_VARIABLE;
1016+
param->paramvarname = pstrdup(vf->varname);
1017+
param->paramtype = typid;
1018+
param->paramtypmod = typmod;
1019+
param->paramcollid = collid;
1020+
1021+
pstate->p_hasSessionVariables = true;
1022+
1023+
return (Node *) param;
1024+
}
1025+
9071026
/* Test whether an a_expr is a plain NULL constant or not */
9081027
static bool
9091028
exprIsNullConstant(Node *arg)

src/backend/parser/parse_merge.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt)
405405

406406
qry->hasTargetSRFs = false;
407407
qry->hasSubLinks = pstate->p_hasSubLinks;
408+
qry->hasSessionVariables = pstate->p_hasSessionVariables;
408409

409410
assign_query_collations(pstate, qry);
410411

src/backend/parser/parse_target.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2033,6 +2033,13 @@ FigureColnameInternal(Node *node, char **name)
20332033
(int) ((JsonFuncExpr *) node)->op);
20342034
}
20352035
break;
2036+
case T_VariableFence:
2037+
{
2038+
/* return last field name */
2039+
*name = ((VariableFence *) node)->varname;
2040+
return 2;
2041+
}
2042+
break;
20362043
default:
20372044
break;
20382045
}

src/backend/utils/adt/ruleutils.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8816,6 +8816,14 @@ get_parameter(Param *param, deparse_context *context)
88168816
}
88178817
}
88188818

8819+
/* Note: can be be used by EXPLAIN */
8820+
if (param->paramkind == PARAM_VARIABLE)
8821+
{
8822+
appendStringInfo(context->buf, "VARIABLE(%s)",
8823+
quote_identifier(param->paramvarname));
8824+
return;
8825+
}
8826+
88198827
/*
88208828
* Not PARAM_EXEC, or couldn't find referent: just print $N.
88218829
*

0 commit comments

Comments
 (0)