8000 Parse ? as parameter reference instead of operator. · rflynn/postgres@e18ecb8 · GitHub
[go: up one dir, main page]

Skip to content

Commit e18ecb8

Browse files
committed
Parse ? as parameter reference instead of operator.
1 parent f455ee8 commit e18ecb8

File tree

2 files changed

+158
-14
lines changed

2 files changed

+158
-14
lines changed

src/backend/parser/gram.y

Lines changed: 120 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ static Node *makeBitStringConst(char *str, int location);
136136
static Node *makeNullAConst(int location);
137137
static Node *makeAConst(Value *v, int location);
138138
static Node *makeBoolAConst(bool state, int location);
139+
static Node *makeParamRef(int number, int location);
140+
static Node *makeParamRefCast(int number, int location, TypeName *typename);
141+
static Node *makeInterval_or_AExprOp(Node *lexpr, Node *rexpr, int location);
139142
static void check_qualified_name(List *names, core_yyscan_t yyscanner);
140143
static List *check_func_name(List *names, core_yyscan_t yyscanner);
141144
static List *check_indirection(List *indirection, core_yyscan_t yyscanner);
@@ -461,6 +464,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
461464
%type <str> ColId ColLabel var_name type_function_name param_name
462465
%type <str> NonReservedWord NonReservedWord_or_Sconst
463466
%type <node> var_value zone_value
467+
%type <node> Iconst_or_Normalized Sconst_or_Normalized NonReservedWord_or_Sconst_or_Normalized
464468

465469
%type <keyword> unreserved_keyword type_func_name_keyword
466470
%type <keyword> col_name_keyword reserved_keyword
@@ -663,6 +667,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
663667
%left '+' '-'
664668
%left '*' '/' '%'
665669
%left '^'
670+
%left '?'
666671
/* Unary Operators */
667672
%left AT /* sets precedence for AT TIME ZONE */
668673
%left COLLATE
@@ -1399,12 +1404,12 @@ set_rest_more: /* Generic SET syntaxes: */
13991404
parser_errposition(@2)));
14001405
$$ = NULL; /*not reached*/
14011406
}
1402-
| SCHEMA Sconst
1407+
| SCHEMA Sconst_or_Normalized
14031408
{
14041409
VariableSetStmt *n = makeNode(VariableSetStmt);
14051410
n->kind = VAR_SET_VALUE;
14061411
n->name = "search_path";
1407-
n->args = list_make1(makeStringConst($2, @2));
1412+
n->args = list_make1($2);
14081413
$$ = n;
14091414
}
14101415
| NAMES opt_encoding
@@ -1418,20 +1423,20 @@ set_rest_more: /* Generic SET syntaxes: */
14181423
n->kind = VAR_SET_DEFAULT;
14191424
$$ = n;
14201425
}
1421-
| ROLE NonReservedWord_or_Sconst
1426+
| ROLE NonReservedWord_or_Sconst_or_Normalized
14221427
{
14231428
VariableSetStmt *n = makeNode(VariableSetStmt);
14241429
n->kind = VAR_SET_VALUE;
14251430
n->name = "role";
1426-
n->args = list_make1(makeStringConst($2, @2));
1431+
n->args = list_make1($2);
14271432
$$ = n;
14281433
}
1429-
| SESSION AUTHORIZATION NonReservedWord_or_Sconst
1434+
| SESSION AUTHORIZATION NonReservedWord_or_Sconst_or_Normalized
14301435
{
14311436
VariableSetStmt *n = makeNode(VariableSetStmt);
14321437
n->kind = VAR_SET_VALUE;
14331438
n->name = "session_authorization";
1434-
n->args = list_make1(makeStringConst($3, @3));
1439+
n->args = list_make1($3);
14351440
$$ = n;
14361441
}
14371442
| SESSION AUTHORIZATION DEFAULT
@@ -1473,6 +1478,8 @@ var_value: opt_boolean_or_string
14731478
{ $$ = makeStringConst($1, @1); }
14741479
| NumericOnly
14751480
{ $$ = makeAConst($1, @1); }
1481+
| '?'
1482+
{ $$ = makeStringConst("?", @1); }
14761483
;
14771484

14781485
iso_level: READ UNCOMMITTED { $$ = "read uncommitted"; }
@@ -1502,9 +1509,9 @@ opt_boolean_or_string:
15021509
* so use IDENT (meaning we reject anything that is a key word).
15031510
*/
15041511
zone_value:
1505-
Sconst
1512+
Sconst_or_Normalized
15061513
{
1507-
$$ = makeStringConst($1, @1);
1514+
$$ = $1;
15081515
}
15091516
| IDENT
15101517
{
@@ -10580,13 +10587,13 @@ ConstCharacter: CharacterWithLength
1058010587
}
1058110588
;
1058210589

10583-
CharacterWithLength: character '(' Iconst ')' opt_charset
10590+
CharacterWithLength: character '(' Iconst_or_Normalized ')' opt_charset
1058410591
{
1058510592
if (($5 != NULL) && (strcmp($5, "sql_text") != 0))
1058610593
$1 = psprintf("%s_%s", $1, $5);
1058710594

1058810595
$$ = SystemTypeName($1);
10589-
$$->typmods = list_make1(makeIntConst($3, @3));
10596+
$$->typmods = list_make1($3);
1059010597
$$->location = @1;
1059110598
}
1059210599
;
@@ -10827,6 +10834,13 @@ a_expr: c_expr { $$ = $1; }
1082710834
| a_expr '=' a_expr
1082810835
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); }
1082910836

10837+
| a_expr '?' a_expr %prec Op
10838+
{ $$ = makeInterval_or_AExprOp($1, $3, @2); }
10839+
/*| '?' a_expr %prec Op
10840+
{ $$ = makeInterval_or_AExprOp(NULL, $2, @1); }*/
10841+
| a_expr '?' %prec POSTFIXOP
10842+
{ $$ = makeInterval_or_AExprOp($1, NULL, @2); }
10843+
1083010844
| a_expr qual_Op a_expr %prec Op
1083110845
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
1083210846
| qual_Op a_expr %prec Op
@@ -11190,6 +11204,14 @@ b_expr: c_expr
1119011204
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $3, @2); }
1119111205
| b_expr '=' b_expr
1119211206
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "=", $1, $3, @2); }
11207+
11208+
| b_expr '?' b_expr %prec Op
11209+
{ $$ = makeInterval_or_AExprOp($1, $3, @2); }
11210+
/*| '?' b_expr %prec Op
11211+
{ $$ = makeInterval_or_AExprOp(NULL, $2, @1); }*/
11212+
| b_expr '?' %prec POSTFIXOP
11213+
{ $$ = makeInterval_or_AExprOp($1, NULL, @2); }
11214+
1119311215
| b_expr qual_Op b_expr %prec Op
1119411216
{ $$ = (Node *) makeA_Expr(AEXPR_OP, $2, $1, $3, @2); }
1119511217
| qual_Op b_expr %prec Op
@@ -11237,6 +11259,18 @@ b_expr: c_expr
1123711259
*/
1123811260
c_expr: columnref { $$ = $1; }
1123911261
| AexprConst { $$ = $1; }
11262+
| '?' opt_indirection
11263+
{
11264+
if ($2)
11265+
{
11266+
A_Indirection *n = makeNode(A_Indirection);
11267+
n->arg = makeParamRef(0, @1);
11268+
n->indirection = check_indirection($2, yyscanner);
11269+
$$ = (Node *) n;
11270+
}
11271+
else
11272+
$$ = makeParamRef(0, @1);
11273+
}
1124011274
| PARAM opt_indirection
1124111275
{
1124211276
ParamRef *p = makeNode(ParamRef);
@@ -12105,6 +12139,7 @@ MathOp: '+' { $$ = "+"; }
1210512139
| '<' { $$ = "<"; }
1210612140
| '>' { $$ = ">"; }
1210712141
| '=' { $$ = "="; }
12142+
| '?' { $$ = "?"; }
1210812143
;
1210912144

1211012145
qual_Op: Op
@@ -12207,6 +12242,10 @@ extract_list:
1220712242
{
1220812243
$$ = list_make2(makeStringConst($1, @1), $3);
1220912244
}
12245+
| '?' FROM a_expr
12246+
{
12247+
$$ = list_make2(makeParamRef(0, @1), $3);
12248+
}
1221012249
| /*EMPTY*/ { $$ = NIL; }
1221112250
;
1221212251

@@ -12694,6 +12733,19 @@ AexprConst: Iconst
1269412733
makeIntConst($3, @3));
1269512734
$$ = makeStringConstCast($5, @5, t);
1269612735
}
12736+
/* Version without () is handled in a_expr/b_expr logic due to ? mis-parsing as operator */
12737+
| ConstInterval '(' '?' ')' '?' opt_interval
12738+
{
12739+
TypeName *t = $1;
12740+
if ($6 != NIL)
12741+
{
12742+
t->typmods = lappend($6, makeParamRef(0, @3));
12743+
}
12744+
else
12745+
t->typmods = list_make2(makeIntConst(INTERVAL_FULL_RANGE, -1),
12746+
makeParamRef(0, @3));
12747+
$$ = makeParamRefCast(0, @5, t);
12748+
}
1269712749
| TRUE_P
1269812750
{
1269912751
$$ = makeBoolAConst(TRUE, @1);
@@ -12723,6 +12775,21 @@ SignedIconst: Iconst { $$ = $1; }
1272312775
| '-' Iconst { $$ = - $2; }
1272412776
;
1272512777

12778+
/* Const that would be replaced by ? if run through pg_stat_statements
12779+
Note: This reduces to a node and returns a ParamRef if its ? */
12780+
12781+
Iconst_or_Normalized: Iconst { $$ = makeIntConst($1, @1); }
12782+
| '?' { $$ = makeParamRef( 0, @1); }
12783+
;
12784+
12785+
Sconst_or_Normalized: Sconst { $$ = makeStringConst($1, @1); }
12786+
| '?' { $$ = makeParamRef( 0, @1); }
12787+
;
12788+
12789+
NonReservedWord_or_Sconst_or_Normalized: NonReservedWord_or_Sconst { $$ = makeStringConst($1, @1); }
12790+
| '?' { $$ = makeParamRef( 0, @1); }
12791+
;
12792+
1272612793
/*
1272712794
* Name classification hierarchy.
1272812795
*
@@ -13406,6 +13473,49 @@ makeBoolAConst(bool state, int location)
1340613473
return makeTypeCast((Node *)n, SystemTypeName("bool"), -1);
1340713474
}
1340813475

13476+
/* makeParamRef
13477+
* Creates a new ParamRef node
13478+
*/
13479+
static Node* makeParamRef(int number, int location)
13480+
{
13481+
ParamRef *p = makeNode(ParamRef);
13482+
p->number = number;
13483+
p->location = location;
13484+
return (Node *) p;
13485+
}
13486+
13487+
static Node *
13488+
makeParamRefCast(int number, int location, TypeName *typename)
13489+
{
13490+
Node *p = makeParamRef(number, location);
13491+
return makeTypeCast(p, typename, -1);
13492+
}
13493+
13494+
/*
13495+
* Makes INTERVAL-like nodes for "INTERVAL ? typemod", otherwise treat as A_EXPR
13496+
*/
13497+
static Node *
13498+
makeInterval_or_AExprOp(Node *lexpr, Node *rexpr, int location)
13499+
{
13500+
if (lexpr && IsA(lexpr, ColumnRef)) {
13501+
ColumnRef *c = (ColumnRef *) lexpr;
13502+
if (strcmp(strVal(linitial(c->fields)), "interval") == 0 ) {
13503+
TypeName *t = SystemTypeName("interval");
13504+
t->location = c->location;
13505+
13506+
/* Its too difficult to tell the parser to give us the right typemod,
13507+
* just use a dummy one if present
13508+
*/
13509+
if (rexpr)
13510+
t->typmods = list_make1(makeIntConst(0, -1));
13511+
13512+
return makeParamRefCast(0, location, t);
13513+
}
13514+
}
13515+
13516+
return (Node *) makeA_Expr(AEXPR_OP, list_make1(makeString("?")), lexpr, rexpr, location);
13517+
}
13518+
1340913519
/* check_qualified_name --- check the result of qualified_name production
1341013520
*
1341113521
* It's easiest to let the grammar production for qualified_name allow

src/backend/parser/scan.l

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ xehexesc [\\]x[0-9A-Fa-f]{1,2}
253253
xeunicode [\\](u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})
254254
xeunicodefail [\\](u[0-9A-Fa-f]{0,3}|U[0-9A-Fa-f]{0,7})
255255

256+
/* Normalized escaped string */
257+
xe_normalized [eE]\?
258+
256259
/* Extended quote
257260
* xqdouble implements embedded quote, ''''
258261
*/
@@ -328,8 +331,9 @@ xcinside [^*/]+
328331
digit [0-9]
329332
ident_start [A-Za-z\200-\377_]
330333
ident_cont [A-Za-z\200-\377_0-9\$]
334+
ident_end [\?]
331335

332-
identifier {ident_start}{ident_cont}*
336+
identifier {ident_start}{ident_cont}*{ident_end}*
333337

334338
typecast "::"
335339
dot_dot \.\.
@@ -345,7 +349,7 @@ colon_equals ":="
345349
* If you change either set, adjust the character lists appearing in the
346350
* rule for "operator"!
347351
*/
348-
self [,()\[\].;\:\+\-\*\/\%\^\<\>\=]
352+
self [,()\[\].;\:\+\-\*\/\%\^\<\>\=\?]
349353
op_chars [\~\!\@\#\^\&\|\`\?\+\-\*\/\%\<\>\=]
350354
operator {op_chars}+
351355

@@ -793,6 +797,11 @@ other .
793797
return IDENT;
794798
}
795799

800+
{xe_normalized} {
801+
/* ignore E */
802+
return yytext[1];
803+
}
804+
796805
{typecast} {
797806
SET_YYLLOC();
798807
return TYPECAST;
@@ -859,6 +868,25 @@ other .
859868
nchars--; /* else remove the +/-, and check again */
860869
}
861870

871+
/* We don't accept leading ? in any multi-character operators
872+
* except for those in use by hstore, JSON and geometric operators.
873+
*
874+
* We don't accept contained or trailing ? in any
875+
* multi-character operators.
876+
*
877+
* This is necessary in order to support normalized queries without
878+
* spacing between ? as a substition character and a simple operator (e.g. "?=?")
879+
*/
880+
if (yytext[0] == '?' &&
881+
strcmp(yytext, "?|") != 0 && strcmp(yytext, "?&") != 0 &&
882+
strcmp(yytext, "?#") != 0 && strcmp(yytext, "?-") != 0 &&
883+
strcmp(yytext, "?-|") != 0 && strcmp(yytext, "?||") != 0)
884+
nchars = 1;
885+
886+
if (yytext[0] != '?' && strchr(yytext, '?'))
887+
/* Lex up to just before the ? character */
888+
nchars = strchr(yytext, '?') - yytext;
889+
862890
SET_YYLLOC();
863891

864892
if (nchars < yyleng)
@@ -872,7 +900,7 @@ other .
872900
* that the "self" rule would have.
873901
*/
874902
if (nchars == 1 &&
875-
strchr(",()[].;:+-*/%^<>=", yytext[0]))
903+
strchr(",()[].;:+-*/%^<>=?", yytext[0]))
876904
return yytext[0];
877905
}
878906

@@ -943,15 +971,21 @@ other .
943971
{identifier} {
944972
const ScanKeyword *keyword;
945973
char *ident;
974+
char *keyword_text = pstrdup(yytext);
946975

947976
SET_YYLLOC();
948977

949978
/* Is it a keyword? */
950-
keyword = ScanKeywordLookup(yytext,
979+
if (yytext[yyleng - 1] == '?')
980+
keyword_text[yyleng - 1] = '\0';
981+
982+
keyword = ScanKeywordLookup(keyword_text,
951983
yyextra->keywords,
952984
yyextra->num_keywords);
953985
if (keyword != NULL)
954986
{
987+
if (keyword_text[yyleng - 1] == '\0')
988+
yyless(yyleng - 1);
955989
yylval->keyword = keyword->name;
956990
return keyword->value;
957991
}

0 commit comments

Comments
 (0)
0