1818#include " catalog/namespace.h"
1919#include " catalog/pg_proc.h"
2020#include " catalog/pg_type.h"
21+ #include " nodes/nodeFuncs.h"
2122#include " parser/parser.h"
2223#include " parser/parse_type.h"
2324#include " parser/scanner.h"
@@ -71,6 +72,7 @@ static PLpgSQL_expr *read_sql_construct(int until,
7172 const char *expected,
7273 RawParseMode parsemode,
7374 bool isexpression,
75+ bool allowlist,
7476 bool valid_sql,
7577 int *startloc,
7678 int *endtoken,
@@ -106,7 +108,7 @@ static PLpgSQL_row *make_scalar_list1(char *initial_name,
106108 PLpgSQL_datum *initial_datum,
107109 int lineno, int location, yyscan_t yyscanner);
108110static 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);
110112static void plpgsql_sql_error_callback (void *arg);
111113static PLpgSQL_type *parse_datatype (const char *string, int location, yyscan_t yyscanner);
112114static void check_labels (const char *start_label,
@@ -117,6 +119,7 @@ static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor, int until,
117119 YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
118120static List *read_raise_options (YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner);
119121static void check_raise_parameters (PLpgSQL_stmt_raise *stmt);
122+ static bool is_strict_expr (List *parsetree, int *errpos, bool allowlist);
120123
121124%}
122125
@@ -193,6 +196,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt);
193196%type <expr> expr_until_semi
194197%type <expr> expr_until_then expr_until_loop opt_expr_until_when
195198%type <expr> opt_exitcond
199+ %type <expr> expressions_until_then
196200
197201%type <var> cursor_variable
198202%type <datum> decl_cursor_arg
@@ -914,7 +918,7 @@ stmt_perform : K_PERFORM
914918 */
915919 new ->expr = read_sql_construct (' ;' , 0 , 0 , " ;" ,
916920 RAW_PARSE_DEFAULT,
917- false , false ,
921+ false , false , false ,
918922 &startloc, NULL ,
919923 &yylval, &yylloc, yyscanner);
920924 /* overwrite "perform" ... */
@@ -924,7 +928,7 @@ stmt_perform : K_PERFORM
924928 strlen (new ->expr ->query ));
925929 /* offset syntax error position to account for that */
926930 check_sql_expr (new ->expr ->query , new ->expr ->parseMode ,
927- startloc + 1 , yyscanner);
931+ false , startloc + 1 , yyscanner);
928932
929933 $$ = (PLpgSQL_stmt *) new ;
930934 }
@@ -1001,7 +1005,7 @@ stmt_assign : T_DATUM
10011005 plpgsql_push_back_token (T_DATUM, &yylval, &yylloc, yyscanner);
10021006 new ->expr = read_sql_construct (' ;' , 0 , 0 , " ;" ,
10031007 pmode,
1004- false , true ,
1008+ false , false , true ,
10051009 NULL , NULL ,
10061010 &yylval, &yylloc, yyscanner);
10071011 mark_expr_as_assignment_source (new ->expr , $1 .datum );
@@ -1262,7 +1266,7 @@ case_when_list : case_when_list case_when
12621266 }
12631267 ;
12641268
1265- case_when : K_WHEN expr_until_then proc_sect
1269+ case_when : K_WHEN expressions_until_then proc_sect
12661270 {
12671271 PLpgSQL_case_when *new = palloc (sizeof (PLpgSQL_case_when));
12681272
@@ -1292,6 +1296,15 @@ opt_case_else :
12921296 }
12931297 ;
12941298
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+
12951308stmt_loop : opt_loop_label K_LOOP loop_body
12961309 {
12971310 PLpgSQL_stmt_loop *new ;
@@ -1496,6 +1509,7 @@ for_control : for_variable K_IN
14961509 RAW_PARSE_DEFAULT,
14971510 true ,
14981511 false ,
1512+ false ,
14991513 &expr1loc,
15001514 &tok,
15011515 &yylval, &yylloc, yyscanner);
@@ -1514,7 +1528,7 @@ for_control : for_variable K_IN
15141528 */
15151529 expr1->parseMode = RAW_PARSE_PLPGSQL_EXPR;
15161530 check_sql_expr (expr1->query , expr1->parseMode ,
1517- expr1loc, yyscanner);
1531+ false , expr1loc, yyscanner);
15181532
15191533 /* Read and check the second one */
15201534 expr2 = read_sql_expression2 (K_LOOP, K_BY,
@@ -1571,7 +1585,7 @@ for_control : for_variable K_IN
15711585
15721586 /* Check syntax as a regular query */
15731587 check_sql_expr (expr1->query , expr1->parseMode ,
1574- expr1loc, yyscanner);
1588+ false , expr1loc, yyscanner);
15751589
15761590 new = palloc0 (sizeof (PLpgSQL_stmt_fors));
15771591 new ->cmd_type = PLPGSQL_STMT_FORS;
@@ -1903,7 +1917,7 @@ stmt_raise : K_RAISE
19031917 expr = read_sql_construct (' ,' , ' ;' , K_USING,
19041918 " , or ; or USING" ,
19051919 RAW_PARSE_PLPGSQL_EXPR,
1906- true , true ,
1920+ true , false , true ,
19071921 NULL , &tok,
19081922 &yylval, &yylloc, yyscanner);
19091923 new ->params = lappend (new ->params , expr);
@@ -2041,7 +2055,7 @@ stmt_dynexecute : K_EXECUTE
20412055 expr = read_sql_construct (K_INTO, K_USING, ' ;' ,
20422056 " INTO or USING or ;" ,
20432057 RAW_PARSE_PLPGSQL_EXPR,
2044- true , true ,
2058+ true , false , true ,
20452059 NULL , &endtoken,
20462060 &yylval, &yylloc, yyscanner);
20472061
@@ -2081,7 +2095,7 @@ stmt_dynexecute : K_EXECUTE
20812095 expr = read_sql_construct (' ,' , ' ;' , K_INTO,
20822096 " , or ; or INTO" ,
20832097 RAW_PARSE_PLPGSQL_EXPR,
2084- true , true ,
2098+ true , false , true ,
20852099 NULL , &endtoken,
20862100 &yylval, &yylloc, yyscanner);
20872101 new ->params = lappend (new ->params , expr);
@@ -2717,7 +2731,7 @@ read_sql_expression(int until, const char *expected, YYSTYPE *yylvalp, YYLTYPE *
27172731{
27182732 return read_sql_construct(until, 0, 0, expected,
27192733 RAW_PARSE_PLPGSQL_EXPR,
2720- true, true, NULL, NULL,
2734+ true, false, true, NULL, NULL,
27212735 yylvalp, yyllocp, yyscanner);
27222736}
27232737
@@ -2728,7 +2742,7 @@ read_sql_expression2(int until, int until2, const char *expected,
27282742{
27292743 return read_sql_construct(until, until2, 0, expected,
27302744 RAW_PARSE_PLPGSQL_EXPR,
2731- true, true, NULL, endtoken,
2745+ true, false, true, NULL, endtoken,
27322746 yylvalp, yyllocp, yyscanner);
27332747}
27342748
@@ -2738,7 +2752,7 @@ read_sql_stmt(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
27382752{
27392753 return read_sql_construct(';', 0, 0, ";",
27402754 RAW_PARSE_DEFAULT,
2741- false, true, NULL, NULL,
2755+ false, false, true, NULL, NULL,
27422756 yylvalp, yyllocp, yyscanner);
27432757}
27442758
@@ -2751,6 +2765,7 @@ read_sql_stmt(YYSTYPE *yylvalp, YYLTYPE *yyllocp, yyscan_t yyscanner)
27512765 * expected: text to use in complaining that terminator was not found
27522766 * parsemode: raw_parser() mode to use
27532767 * isexpression: whether to say we're reading an "expression" or a "statement"
2768+ * allowlist: the result can be list of expressions
27542769 * valid_sql: whether to check the syntax of the expr
27552770 * startloc: if not NULL, location of first token is stored at *startloc
27562771 * endtoken: if not NULL, ending token is stored at *endtoken
@@ -2763,6 +2778,7 @@ read_sql_construct(int until,
27632778 const char *expected,
27642779 RawParseMode parsemode,
27652780 bool isexpression,
2781+ bool allowlist,
27662782 bool valid_sql,
27672783 int *startloc,
27682784 int *endtoken,
@@ -2858,7 +2874,7 @@ read_sql_construct(int until,
28582874 pfree(ds.data);
28592875
28602876 if (valid_sql)
2861- check_sql_expr(expr->query, expr->parseMode, startlocation, yyscanner);
2877+ check_sql_expr(expr->query, expr->parseMode, allowlist, startlocation, yyscanner);
28622878
28632879 return expr;
28642880}
@@ -3179,7 +3195,7 @@ make_execsql_stmt(int firsttoken, int location, PLword *word, YYSTYPE *yylvalp,
31793195 expr = make_plpgsql_expr(ds.data, RAW_PARSE_DEFAULT);
31803196 pfree(ds.data);
31813197
3182- check_sql_expr(expr->query, expr->parseMode, location, yyscanner);
3198+ check_sql_expr(expr->query, expr->parseMode, false, location, yyscanner);
31833199
31843200 execsql = palloc0(sizeof(PLpgSQL_stmt_execsql));
31853201 execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
@@ -3780,11 +3796,15 @@ make_scalar_list1(char *initial_name,
37803796 * If no error cursor is provided, we'll just point at "location".
37813797 */
37823798static void
3783- check_sql_expr(const char *stmt, RawParseMode parseMode, int location, yyscan_t yyscanner)
3799+ check_sql_expr(const char *stmt,
3800+ RawParseMode parseMode, bool allowlist,
3801+ int location, yyscan_t yyscanner)
37843802{
37853803 sql_error_callback_arg cbarg;
37863804 ErrorContextCallback syntax_errcontext;
37873805 MemoryContext oldCxt;
3806+ List *parsetree;
3807+ int errpos;
37883808
37893809 if (!plpgsql_check_syntax)
37903810 return;
@@ -3798,11 +3818,25 @@ check_sql_expr(const char *stmt, RawParseMode parseMode, int location, yyscan_t
37983818 error_context_stack = &syntax_errcontext;
37993819
38003820 oldCxt = MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
3801- (void) raw_parser(stmt, parseMode);
3821+ parsetree = raw_parser(stmt, parseMode);
38023822 MemoryContextSwitchTo(oldCxt);
38033823
38043824 /* Restore former ereport callback */
38053825 error_context_stack = syntax_errcontext.previous;
3826+
3827+ if (plpgsql_curr_compile->extra_warnings & PLPGSQL_XCHECK_STRICTEXPRCHECK ||
3828+ plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_STRICTEXPRCHECK)
3829+ {
3830+ /* do this check only for expressions */
3831+ if (parseMode == RAW_PARSE_DEFAULT)
3832+ return;
3833+
3834+ if (!is_strict_expr(parsetree, &errpos, allowlist))
3835+ ereport(plpgsql_curr_compile->extra_errors & PLPGSQL_XCHECK_STRICTEXPRCHECK ? ERROR : WARNING,
3836+ (errcode(ERRCODE_SYNTAX_ERROR),
3837+ errmsg("syntax of expression is not strict"),
3838+ parser_errposition(errpos != -1 ? location + errpos : location)));
3839+ }
38063840}
38073841
38083842static void
@@ -3836,6 +3870,74 @@ plpgsql_sql_error_callback(void *arg)
38363870 errposition(0);
38373871}
38383872
3873+ /*
3874+ * Returns true, when the only targetList is in parsetree. Cursors
3875+ * can require list of expressions or list of named expressions.
3876+ */
3877+ static bool
3878+ is_strict_expr(List *parsetree, int *errpos, bool allowlist)
3879+ {
3880+ RawStmt *rawstmt;
3881+ SelectStmt *select;
3882+ int targets = 0;
3883+ ListCell *lc;
3884+
3885+ /* Top should be RawStmt */
3886+ rawstmt = castNode(RawStmt, linitial(parsetree));
3887+
3888+ if (IsA(rawstmt->stmt, SelectStmt))
3889+ {
3890+ select = (SelectStmt *) rawstmt->stmt;
3891+ }
3892+ else if (IsA(rawstmt->stmt, PLAssignStmt))
3893+ {
3894+ select = castNode(SelectStmt, ((PLAssignStmt *) rawstmt->stmt)->val);
3895+ }
3896+ else
3897+ elog(ERROR, "unexpected node type");
3898+
3899+ if (!select->targetList)
3900+ {
3901+ *errpos = -1;
3902+ return false;
3903+ }
3904+ else
3905+ *errpos = exprLocation((Node *) select->targetList);
3906+
3907+ if (select->distinctClause ||
3908+ select->fromClause ||
3909+ select->whereClause ||
3910+ select->groupClause ||
3911+ select->groupDistinct ||
3912+ select->havingClause ||
3913+ select->windowClause ||
3914+ select->sortClause ||
3915+ select->limitOffset ||
3916+ select->limitCount ||
3917+ select->limitOption ||
3918+ select->lockingClause)
3919+ return false;
3920+
3921+ foreach(lc, select->targetList)
3922+ {
3923+ ResTarget *rt = castNode(ResTarget, lfirst(lc));
3924+
3925+ if (targets++ >= 1 && !allowlist)
3926+ {
3927+ *errpos = exprLocation((Node *) rt);
3928+ return false;
3929+ }
3930+
3931+ if (rt->name)
3932+ {
3933+ *errpos = exprLocation((Node *) rt);
3934+ return false;
3935+ }
3936+ }
3937+
3938+ return true;
3939+ }
3940+
38393941/*
38403942 * Parse a SQL datatype name and produce a PLpgSQL_type structure.
38413943 *
@@ -4025,7 +4127,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until, YYSTYPE *yylvalp, YYLTYPE *yyll
40254127 item = read_sql_construct(',', ')', 0,
40264128 ",\" or \")",
40274129 RAW_PARSE_PLPGSQL_EXPR,
4028- true, true,
4130+ true, false, true,
40294131 NULL, &endtoken,
40304132 yylvalp, yyllocp, yyscanner);
40314133
0 commit comments