Skip to content

Commit 24876ab

Browse files
okbob@github.comCommitfest Bot
authored andcommitted
session variable fences parsing
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 a05a2dc commit 24876ab

File tree

18 files changed

+659
-4
lines changed

18 files changed

+659
-4
lines changed

doc/src/sgml/ddl.sgml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5396,6 +5396,17 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate >= DATE '2008-01-01';
53965396
The session variable holds value in session memory. This value is private
53975397
to each session and is released when the session ends.
53985398
</para>
5399+
5400+
<para>
5401+
In an query the session variable can be used only inside
5402+
<firstterm>variable fence</firstterm>. This is special syntax for
5403+
session variable identifier, and can be used only for session variable
5404+
identifier. The special syntax for accessing session variables removes
5405+
risk of collisions between variable identifiers and column names.
5406+
<programlisting>
5407+
SELECT VARIABLE(current_user_id);
5408+
</programlisting>
5409+
</para>
53995410
</sect1>
54005411

54015412
<sect1 id="ddl-others">

src/backend/catalog/namespace.c

Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3567,6 +3567,295 @@ NamesFromList(List *names)
35673567
return result;
35683568
}
35693569

3570+
/* -----
3571+
* IdentifyVariable - try to find a variable from a list of identifiers
3572+
*
3573+
* Returns the OID of the variable found, or InvalidOid.
3574+
*
3575+
* "names" is a list of up to four identifiers; possible meanings are:
3576+
* - variable (searched on the search_path)
3577+
* - schema.variable
3578+
* - variable.attribute (searched on the search_path)
3579+
* - schema.variable.attribute
3580+
* - database.schema.variable
3581+
* - database.schema.variable.attribute
3582+
*
3583+
* If there is more than one way to identify a variable, "not_unique" will be
3584+
* set to true.
3585+
*
3586+
* Unless "noerror" is true, an error is raised if there are more than four
3587+
* identifiers in the list, or if the named database is not the current one.
3588+
* This is useful if we want to identify a shadowed variable.
3589+
*
3590+
* If an attribute is identified, it is stored in "attrname", otherwise the
3591+
* parameter is set to NULL.
3592+
*
3593+
* The identified session variable will be locked with an AccessShareLock.
3594+
* -----
3595+
*/
3596+
Oid
3597+
IdentifyVariable(List *names, char **attrname, bool *not_unique, bool noerror)
3598+
{
3599+
Oid varid = InvalidOid;
3600+
Oid old_varid = InvalidOid;
3601+
uint64 inval_count;
3602+
bool retry = false;
3603+
3604+
/*
3605+
* DDL operations can change the results of a name lookup. Since all such
3606+
* operations will generate invalidation messages, we keep track of
3607+
* whether any such messages show up while we're performing the operation,
3608+
* and retry until either (1) no more invalidation messages show up or (2)
3609+
* the answer doesn't change.
3610+
*/
3611+
for (;;)
3612+
{
3613+
Node *field1 = NULL;
3614+
Node *field2 = NULL;
3615+
Node *field3 = NULL;
3616+
Node *field4 = NULL;
3617+
char *a = NULL;
3618+
char *b = NULL;
3619+
char *c = NULL;
3620+
char *d = NULL;
3621+
Oid varoid_without_attr = InvalidOid;
3622+
Oid varoid_with_attr = InvalidOid;
3623+
3624+
*not_unique = false;
3625+
*attrname = NULL;
3626+
varid = InvalidOid;
3627+
3628+
inval_count = SharedInvalidMessageCounter;
3629+
3630+
switch (list_length(names))
3631+
{
3632+
case 1:
3633+
field1 = linitial(names);
3634+
3635+
Assert(IsA(field1, String));
3636+
3637+
varid = LookupVariable(NULL, strVal(field1), true);
3638+
break;
3639+
3640+
case 2:
3641+
field1 = linitial(names);
3642+
field2 = lsecond(names);
3643+
3644+
Assert(IsA(field1, String));
3645+
a = strVal(field1);
3646+
3647+
if (IsA(field2, String))
3648+
{
3649+
/* when both fields are of string type */
3650+
b = strVal(field2);
3651+
3652+
/*
3653+
* a.b can mean "schema"."variable" or
3654+
* "variable"."attribute". Check both variants, and
3655+
* returns InvalidOid with not_unique flag, when both
3656+
* interpretations are possible.
3657+
*/
3658+
varoid_without_attr = LookupVariable(a, b, true);
3659+
varoid_with_attr = LookupVariable(NULL, a, true);
3660+
}
3661+
else
3662+
{
3663+
/* the last field of list can be star too */
3664+
Assert(IsA(field2, A_Star));
3665+
3666+
/*
3667+
* The syntax ident.* is used only by relation aliases,
3668+
* and then this identifier cannot be a reference to
3669+
* session variable.
3670+
*/
3671+
return InvalidOid;
3672+
}
3673+
3674+
if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
3675+
{
3676+
*not_unique = true;
3677+
varid = varoid_without_attr;
3678+
}
3679+
else if (OidIsValid(varoid_without_attr))
3680+
{
3681+
varid = varoid_without_attr;
3682+
}
3683+
else if (OidIsValid(varoid_with_attr))
3684+
{
3685+
*attrname = b;
3686+
varid = varoid_with_attr;
3687+
}
3688+
break;
3689+
3690+
case 3:
3691+
{
3692+
bool field1_is_catalog = false;
3693+
3694+
field1 = linitial(names);
3695+
field2 = lsecond(names);
3696+
field3 = lthird(names);
3697+
3698+
Assert(IsA(field1, String));
3699+
Assert(IsA(field2, String));
3700+
3701+
a = strVal(field1);
3702+
b = strVal(field2);
3703+
3704+
if (IsA(field3, String))
3705+
{
3706+
c = strVal(field3);
3707+
3708+
/*
3709+
* a.b.c can mean catalog.schema.variable or
3710+
* schema.variable.attribute.
3711+
*
3712+
* Check both variants, and set not_unique flag, when
3713+
* both interpretations are possible.
3714+
*
3715+
* When third node is star, only possible
3716+
* interpretation is schema.variable.*, but this
3717+
* pattern is not supported now.
3718+
*/
3719+
varoid_with_attr = LookupVariable(a, b, true);
3720+
3721+
/*
3722+
* check pattern catalog.schema.variable only when
3723+
* there is possibility to success.
3724+
*/
3725+
if (strcmp(a, get_database_name(MyDatabaseId)) == 0)
3726+
{
3727+
field1_is_catalog = true;
3728+
varoid_without_attr = LookupVariable(b, c, true);
3729+
}
3730+
}
3731+
else
3732+
{
3733+
Assert(IsA(field3, A_Star));
3734+
return InvalidOid;
3735+
}
3736+
3737+
if (OidIsValid(varoid_without_attr) && OidIsValid(varoid_with_attr))
3738+
{
3739+
*not_unique = true;
3740+
varid = varoid_without_attr;
3741+
}
3742+
else if (OidIsValid(varoid_without_attr))
3743+
{
3744+
varid = varoid_without_attr;
3745+
}
3746+
else if (OidIsValid(varoid_with_attr))
3747+
{
3748+
*attrname = c;
3749+
varid = varoid_with_attr;
3750+
}
3751+
3752+
/*
3753+
* When we didn't find variable, we can (when it is
3754+
* allowed) raise cross-database reference error.
3755+
*/
3756+
if (!OidIsValid(varid) && !noerror && !field1_is_catalog)
3757+
ereport(ERROR,
3758+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3759+
errmsg("cross-database references are not implemented: %s",
3760+
NameListToString(names))));
3761+
}
3762+
break;
3763+
3764+
case 4:
3765+
{
3766+
field1 = linitial(names);
3767+
field2 = lsecond(names);
3768+
field3 = lthird(names);
3769+
field4 = lfourth(names);
3770+
3771+
Assert(IsA(field1, String));
3772+
Assert(IsA(field2, String));
3773+
Assert(IsA(field3, String));
3774+
3775+
a = strVal(field1);
3776+
b = strVal(field2);
3777+
c = strVal(field3);
3778+
3779+
/*
3780+
* In this case, "a" is used as catalog name - check it.
3781+
*/
3782+
if (strcmp(a, get_database_name(MyDatabaseId)) != 0)
3783+
{
3784+
if (!noerror)
3785+
ereport(ERROR,
3786+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3787+
errmsg("cross-database references are not implemented: %s",
3788+
NameListToString(names))));
3789+
}
3790+
3791+
if (IsA(field4, String))
3792+
{
3793+
d = strVal(field4);
3794+
}
3795+
else
3796+
{
3797+
Assert(IsA(field4, A_Star));
3798+
return InvalidOid;
3799+
}
3800+
3801+
*attrname = d;
3802+
varid = LookupVariable(b, c, true);
3803+
}
3804+
break;
3805+
3806+
default:
3807+
if (!noerror)
3808+
ereport(ERROR,
3809+
(errcode(ERRCODE_SYNTAX_ERROR),
3810+
errmsg("improper qualified name (too many dotted names): %s",
3811+
NameListToString(names))));
3812+
return InvalidOid;
3813+
}
3814+
3815+
/*
3816+
* If, upon retry, we get back the same OID we did last time, then the
3817+
* invalidation messages we processed did not change the final answer.
3818+
* So we're done.
3819+
*
3820+
* If we got a different OID, we've locked the variable that used to
3821+
* have this name rather than the one that does now. So release the
3822+
* lock.
3823+
*/
3824+
if (retry)
3825+
{
3826+
if (old_varid == varid)
3827+
break;
3828+
3829+
if (OidIsValid(old_varid))
3830+
UnlockDatabaseObject(VariableRelationId, old_varid, 0, AccessShareLock);
3831+
}
3832+
3833+
/*
3834+
* Lock the variable. This will also accept any pending invalidation
3835+
* messages. If we got back InvalidOid, indicating not found, then
3836+
* there's nothing to lock, but we accept invalidation messages
3837+
* anyway, to flush any negative catcache entries that may be
3838+
* lingering.
3839+
*/
3840+
if (!OidIsValid(varid))
3841+
AcceptInvalidationMessages();
3842+
else
3843+
LockDatabaseObject(VariableRelationId, varid, 0, AccessShareLock);
3844+
3845+
/*
3846+
* If no invalidation message were processed, we're done!
3847+
*/
3848+
if (inval_count == SharedInvalidMessageCounter)
3849+
break;
3850+
3851+
retry = true;
3852+
old_varid = varid;
3853+
varid = InvalidOid;
3854+
}
3855+
3856+
return varid;
3857+
}
3858+
35703859
/*
35713860
* DeconstructQualifiedName
35723861
* Given a possibly-qualified name expressed as a list of String nodes,

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/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));

0 commit comments

Comments
 (0)