18
18
#include " catalog/namespace.h"
19
19
#include " catalog/pg_proc.h"
20
20
#include " catalog/pg_type.h"
21
+ #include " nodes/nodeFuncs.h"
21
22
#include " parser/parser.h"
22
23
#include " parser/parse_type.h"
23
24
#include " parser/scanner.h"
@@ -71,6 +72,7 @@ static PLpgSQL_expr *read_sql_construct(int until,
71
72
const char *expected,
72
73
RawParseMode parsemode,
73
74
bool isexpression,
75
+ bool allowlist,
74
76
bool valid_sql,
75
77
int *startloc,
76
78
int *endtoken,
@@ -106,7 +108,7 @@ static PLpgSQL_row *make_scalar_list1(char *initial_name,
106
108
PLpgSQL_datum *initial_datum,
107
109
int lineno, int location, yyscan_t yyscanner);
108
110
static void check_sql_expr (const char *stmt,
109
- RawParseMode parseMode, int location, yyscan_t yyscanner);
111
+ RawParseMode parseMode, bool allowlist, int location, yyscan_t yyscanner);
110
112
static void plpgsql_sql_error_callback (void *arg);
111
113
static PLpgSQL_type *parse_datatype (const char *string, int location, yyscan_t yyscanner);
112
114
static void check_labels (const char *start_label,
@@ -117,6 +119,7 @@ static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor, int until,
117
119
YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
118
120
static List *read_raise_options (YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
119
121
static void check_raise_parameters (PLpgSQL_stmt_raise *stmt);
122
+ static bool is_strict_expr (List *parsetree, int *errpos, bool allowlist);
120
123
121
124
%}
122
125
@@ -193,6 +196,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
193
196
%type <expr> expr_until_semi
194
197
%type <expr> expr_until_then expr_until_loop opt_expr_until_when
195
198
%type <expr> opt_exitcond
199
+ %type <expr> expressions_until_then
196
200
197
201
%type <var> cursor_variable
198
202
%type <datum> decl_cursor_arg
@@ -914,7 +918,7 @@ stmt_perform : K_PERFORM
914
918
*/
915
919
new ->expr = read_sql_construct (' ;' , 0 , 0 , " ;" ,
916
920
RAW_PARSE_DEFAULT,
917
- false , false ,
921
+ false , false , false ,
918
922
&startloc, NULL ,
919
923
&yylval, &yylloc, yyscanner);
920
924
/* overwrite "perform" ... */
@@ -924,7 +928,7 @@ stmt_perform : K_PERFORM
924
928
strlen (new ->expr ->query ));
925
929
/* offset syntax error position to account for that */
926
930
check_sql_expr (new ->expr ->query , new ->expr ->parseMode ,
927
- startloc + 1 , yyscanner);
931
+ false , startloc + 1 , yyscanner);
928
932
929
933
$$ = (PLpgSQL_stmt *) new ;
930
934
}
@@ -1001,7 +1005,7 @@ stmt_assign : T_DATUM
1001
1005
plpgsql_push_back_token (T_DATUM, &yylval, &yylloc, yyscanner);
1002
1006
new ->expr = read_sql_construct (' ;' , 0 , 0 , " ;" ,
1003
1007
pmode,
1004
- false , true ,
1008
+ false , false , true ,
1005
1009
NULL , NULL ,
1006
1010
&yylval, &yylloc, yyscanner);
1007
1011
mark_expr_as_assignment_source (new ->expr , $1 .datum );
@@ -1262,7 +1266,7 @@ case_when_list : case_when_list case_when
1262
1266
}
1263
1267
;
1264
1268
1265
- case_when : K_WHEN expr_until_then proc_sect
1269
+ case_when : K_WHEN expressions_until_then proc_sect
1266
1270
{
1267
1271
PLpgSQL_case_when *new = palloc (sizeof (PLpgSQL_case_when));
1268
1272
@@ -1292,6 +1296,15 @@ opt_case_else :
1292
1296
}
1293
1297
;
1294
1298
1299
+ expressions_until_then :
1300
+ {
1301
+ $$ = read_sql_construct (K_THEN, 0 , 0 , " THEN" ,
1302
+ RAW_PARSE_PLPGSQL_EXPR, /* expr_list */
1303
+ true , true , true , NULL , NULL ,
1304
+ &yylval, &yylloc, yyscanner);
1305
+ }
1306
+ ;
1307
+
1295
1308
stmt_loop : opt_loop_label K_LOOP loop_body
1296
1309
{
1297
1310
PLpgSQL_stmt_loop *new ;
@@ -1495,6 +1508,7 @@ for_control : for_variable K_IN
1495
1508
RAW_PARSE_DEFAULT,
1496
1509
true ,
1497
1510
false ,
1511
+ false ,
1498
1512
&expr1loc,
1499
1513
&tok,
1500
1514
&yylval, &yylloc, yyscanner);
@@ -1513,7 +1527,7 @@ for_control : for_variable K_IN
1513
1527
*/
1514
1528
expr1->parseMode = RAW_PARSE_PLPGSQL_EXPR;
1515
1529
check_sql_expr (expr1->query , expr1->parseMode ,
1516
- expr1loc, yyscanner);
1530
+ false , expr1loc, yyscanner);
1517
1531
1518
1532
/* Read and check the second one */
1519
1533
expr2 = read_sql_expression2 (K_LOOP, K_BY,
@@ -1570,7 +1584,7 @@ for_control : for_variable K_IN
1570
1584
1571
1585
/* Check syntax as a regular query */
1572
1586
check_sql_expr (expr1->query , expr1->parseMode ,
1573
- expr1loc, yyscanner);
1587
+ false , expr1loc, yyscanner);
1574
1588
1575
1589
new = palloc0 (sizeof (PLpgSQL_stmt_fors));
1576
1590
new ->cmd_type = PLPGSQL_STMT_FORS;
@@ -1902,7 +1916,7 @@ stmt_raise : K_RAISE
1902
1916
expr = read_sql_construct (' ,' , ' ;' , K_USING,
1903
1917
" , or ; or USING" ,
1904
1918
RAW_PARSE_PLPGSQL_EXPR,
1905
- true , true ,
1919
+ true , false , true ,
1906
1920
NULL , &tok,
1907
1921
&yylval, &yylloc, yyscanner);
1908
1922
new ->params = lappend (new ->params , expr);
@@ -2040,7 +2054,7 @@ stmt_dynexecute : K_EXECUTE
2040
2054
expr = read_sql_construct (K_INTO, K_USING, ' ;' ,
2041
2055
" INTO or USING or ;" ,
2042
2056
RAW_PARSE_PLPGSQL_EXPR,
2043
- true , true ,
2057
+ true , false , true ,
2044
2058
NULL , &endtoken,
2045
2059
&yylval, &yylloc, yyscanner);
2046
2060
@@ -2080,7 +2094,7 @@ stmt_dynexecute : K_EXECUTE
2080
2094
expr = read_sql_construct (' ,' , ' ;' , K_INTO,
2081
2095
" , or ; or INTO" ,
2082
2096
RAW_PARSE_PLPGSQL_EXPR,
2083
- true , true ,
2097
+ true , false , true ,
2084
2098
NULL , &endtoken,
2085
2099
&yylval, &yylloc, yyscanner);
2086
2100
new ->params = lappend (new ->params , expr);
@@ -2713,7 +2727,7 @@ read_sql_expression(int until, const char *expected, YYSTYPE *yylvalp, YYLTYPE *
2713
2727
{
2714
2728
return read_sql_construct(until, 0, 0, expected,
2715
2729
RAW_PARSE_PLPGSQL_EXPR,
2716
- true, true, NULL, NULL,
2730
+ true, false, true, NULL, NULL,
2717
2731
yylvalp, yyllocp, yyscanner);
2718
2732
}
2719
2733
@@ -2724,7 +2738,7 @@ read_sql_expression2(int until, int until2, const char *expected,
2724
2738
{
2725
2739
return read_sql_construct(until, until2, 0, expected,
2726
2740
RAW_PARSE_PLPGSQL_EXPR,
2727
- true, true, NULL, endtoken,
2741
+ true, false, true, NULL, endtoken,
2728
2742
yylvalp, yyllocp, yyscanner);
2729
2743
}
2730
2744
@@ -2734,7 +2748,7 @@ read_sql_stmt(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
2734
2748
{
2735
2749
return read_sql_construct(';', 0, 0, ";",
2736
2750
RAW_PARSE_DEFAULT,
2737
- false, true, NULL, NULL,
2751
+ false, false, true, NULL, NULL,
2738
2752
yylvalp, yyllocp, yyscanner);
2739
2753
}
2740
2754
@@ -2747,6 +2761,7 @@ read_sql_stmt(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
2747
2761
* expected: text to use in complaining that terminator was not found
2748
2762
* parsemode: raw_parser() mode to use
2749
2763
* isexpression: whether to say we're reading an "expression" or a "statement"
2764
+ * allowlist: the result can be list of expressions
2750
2765
* valid_sql: whether to check the syntax of the expr
2751
2766
* startloc: if not NULL, location of first token is stored at *startloc
2752
2767
* endtoken: if not NULL, ending token is stored at *endtoken
@@ -2759,6 +2774,7 @@ read_sql_construct(int until,
2759
2774
const char *expected,
2760
2775
RawParseMode parsemode,
2761
2776
bool isexpression,
2777
+ bool allowlist,
2762
2778
bool valid_sql,
2763
2779
int *startloc,
2764
2780
int *endtoken,
@@ -2854,7 +2870,7 @@ read_sql_construct(int until,
2854
2870
pfree(ds.data);
2855
2871
2856
2872
if (valid_sql)
2857
- check_sql_expr(expr->query, expr->parseMode, startlocation, yyscanner);
2873
+ check_sql_expr(expr->query, expr->parseMode, allowlist, startlocation, yyscanner);
2858
2874
2859
2875
return expr;
2860
2876
}
@@ -3175,7 +3191,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, YYSTYPE *yylvalp,
3175
3191
expr = make_plpgsql_expr(ds.data, RAW_PARSE_DEFAULT);
3176
3192
pfree(ds.data);
3177
3193
3178
- check_sql_expr(expr->query, expr->parseMode, location, yyscanner);
3194
+ check_sql_expr(expr->query, expr->parseMode, false, location, yyscanner);
3179
3195
3180
3196
execsql = palloc0(sizeof(PLpgSQL_stmt_execsql));
3181
3197
execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
@@ -3775,11 +3791,15 @@ make_scalar_list1(char *initial_name,
3775
3791
* If no error cursor is provided, we'll just point at "location".
3776
3792
*/
3777
3793
static void
3778
- check_sql_expr(const char *stmt, RawParseMode parseMode, int location, yyscan_t yyscanner)
3794
+ check_sql_expr(const char *stmt,
3795
+ RawParseMode parseMode, bool allowlist,
3796
+ int location, yyscan_t yyscanner)
3779
3797
{
3780
3798
sql_error_callback_arg cbarg;
3781
3799
ErrorContextCallback syntax_errcontext;
3782
3800
MemoryContext oldCxt;
3801
+ List *parsetree;
3802
+ int errpos;
3783
3803
3784
3804
if (!plpgsql_check_syntax)
3785
3805
return;
@@ -3793,11 +3813,25 @@ check_sql_expr(const char *stmt, RawParseMode parseMode, int location, yyscan_t
3793
3813
error_context_stack = &syntax_errcontext;
3794
3814
3795
3815
oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
3796
- (void) raw_parser(stmt, parseMode);
3816
+ parsetree = raw_parser(stmt, parseMode);
3797
3817
MemoryContextSwitchTo(oldCxt);
3798
3818
3799
3819
/* Restore former ereport callback */
3800
3820
error_context_stack = syntax_errcontext.previous;
3821
+
3822
+ if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_STRICTEXPRCHECK ||
3823
+ plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_STRICTEXPRCHECK)
3824
+ {
3825
+ /* do this check only for expressions */
3826
+ if (parseMode == RAW_PARSE_DEFAULT)
3827
+ return;
3828
+
3829
+ if (!is_strict_expr(parsetree, &errpos, allowlist))
3830
+ ereport(plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_STRICTEXPRCHECK ? ERROR : WARNING,
3831
+ (errcode(ERRCODE_SYNTAX_ERROR),
3832
+ errmsg("syntax of expression is not strict"),
3833
+ parser_errposition(errpos != -1 ? location + errpos : location)));
3834
+ }
3801
3835
}
3802
3836
3803
3837
static void
@@ -3831,6 +3865,74 @@ plpgsql_sql_error_callback(void *arg)
3831
3865
errposition(0);
3832
3866
}
3833
3867
3868
+ /*
3869
+ * Returns true, when the only targetList is in parsetree. Cursors
3870
+ * can require list of expressions or list of named expressions.
3871
+ */
3872
+ static bool
3873
+ is_strict_expr(List *parsetree, int *errpos, bool allowlist)
3874
+ {
3875
+ RawStmt *rawstmt;
3876
+ SelectStmt *select;
3877
+ int targets = 0;
3878
+ ListCell *lc;
3879
+
3880
+ /* Top should be RawStmt */
3881
+ rawstmt = castNode(RawStmt, linitial(parsetree));
3882
+
3883
+ if (IsA(rawstmt->stmt, SelectStmt))
3884
+ {
3885
+ select = (SelectStmt *) rawstmt->stmt;
3886
+ }
3887
+ else if (IsA(rawstmt->stmt, PLAssignStmt))
3888
+ {
3889
+ select = castNode(SelectStmt, ((PLAssignStmt *) rawstmt->stmt)->val);
3890
+ }
3891
+ else
3892
+ elog(ERROR, "unexpected node type");
3893
+
3894
+ if (!select->targetList)
3895
+ {
3896
+ *errpos = -1;
3897
+ return false;
3898
+ }
3899
+ else
3900
+ *errpos = exprLocation((Node *) select->targetList);
3901
+
3902
+ if (select->distinctClause ||
3903
+ select->fromClause ||
3904
+ select->whereClause ||
3905
+ select->groupClause ||
3906
+ select->groupDistinct ||
3907
+ select->havingClause ||
3908
+ select->windowClause ||
3909
+ select->sortClause ||
3910
+ select->limitOffset ||
3911
+ select->limitCount ||
3912
+ select->limitOption ||
3913
+ select->lockingClause)
3914
+ return false;
3915
+
3916
+ foreach(lc, select->targetList)
3917
+ {
3918
+ ResTarget *rt = castNode(ResTarget, lfirst(lc));
3919
+
3920
+ if (targets++ >= 1 && !allowlist)
3921
+ {
3922
+ *errpos = exprLocation((Node *) rt);
3923
+ return false;
3924
+ }
3925
+
3926
+ if (rt->name)
3927
+ {
3928
+ *errpos = exprLocation((Node *) rt);
3929
+ return false;
3930
+ }
3931
+ }
3932
+
3933
+ return true;
3934
+ }
3935
+
3834
3936
/*
3835
3937
* Parse a SQL datatype name and produce a PLpgSQL_type structure.
3836
3938
*
@@ -4014,7 +4116,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, YYSTYPE *yylvalp, YYLTYPE *yyll
4014
4116
item = read_sql_construct(',', ')', 0,
4015
4117
",\" or \")",
4016
4118
RAW_PARSE_PLPGSQL_EXPR,
4017
- true, true,
4119
+ true, false, true,
4018
4120
NULL, &endtoken,
4019
4121
yylvalp, yyllocp, yyscanner);
4020
4122
0 commit comments