From 51a8cedad8cb3ddbb8a6f5828def01b4e2bb1cac Mon Sep 17 00:00:00 2001 From: "okbob@github.com" Date: Wed, 12 Jun 2024 21:34:05 +0200 Subject: [PATCH 1/3] use strict rules for parsing PL/pgSQL expressions Originally the rule PLpgSQL_Expr allows almost all SQL clauses. It was designed to allow old undocumented syntax var := col FROM tab; The reason for support of this "strange" syntax was technical. The PLpgSQL parser cannot use SQL parser accurately (it was really primitive), and people found this undocumented syntax. Lattery, when it was possible to do exact parsing, from compatibility reasons, the parsing of PL/pgSQL expressions allows described syntax. Unfortunately, with support almost all SQL clauses, the PLpgSQL can accept really broken code like DO $$ DECLARE l_cnt int; BEGIN l_cnt := 1 DELETE FROM foo3 WHERE id=1; END; $$; proposed patch introduce new extra error check strict_expr_check, that solve this issue. --- doc/src/sgml/plpgsql.sgml | 18 ++++ src/backend/executor/spi.c | 7 ++ src/backend/parser/gram.y | 149 +++++++++++++++++++++++++++ src/backend/parser/parser.c | 6 ++ src/include/parser/parser.h | 22 ++++ src/interfaces/ecpg/preproc/parse.pl | 10 +- src/pl/plpgsql/src/pl_comp.c | 31 ++++++ src/pl/plpgsql/src/pl_gram.y | 48 ++++++--- src/pl/plpgsql/src/pl_handler.c | 2 + src/pl/plpgsql/src/plpgsql.h | 19 ++++ 10 files changed, 298 insertions(+), 14 deletions(-) diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index 6f880b705f..e4b5febf15 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -5347,6 +5347,24 @@ a_output := a_output || $$ if v_$$ || referrer_keys.kind || $$ like '$$ + + + strict_expr_check + + + Enabling this check will cause PL/pgSQL to + check if a PL/pgSQL expression is just an + expression without any SQL clauses like FROM, + ORDER BY. This undocumented form of expressions + is allowed for compatibility reasons, but in some special cases + it doesn't to allow to detect broken code. + + + + This check is allowed only plpgsql.extra_errors. + + + The following example shows the effect of plpgsql.extra_warnings diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index e516c0a67c..94a48b837d 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -2976,11 +2976,18 @@ _SPI_error_callback(void *arg) switch (carg->mode) { case RAW_PARSE_PLPGSQL_EXPR: + case RAW_PARSE_PLPGSQL_STRICT_EXPR: + case RAW_PARSE_PLPGSQL_STRICT_EXPR_LIST: + case RAW_PARSE_PLPGSQL_STRICT_NAMED_EXPR_LIST: + /* ToDo: fix to PL/pgSQL expression - in another patch */ errcontext("SQL expression \"%s\"", query); break; case RAW_PARSE_PLPGSQL_ASSIGN1: case RAW_PARSE_PLPGSQL_ASSIGN2: case RAW_PARSE_PLPGSQL_ASSIGN3: + case RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN1: + case RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN2: + case RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN3: errcontext("PL/pgSQL assignment \"%s\"", query); break; default: diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 4d582950b7..bbc13a1a58 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -324,6 +324,11 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type select_no_parens select_with_parens select_clause simple_select values_clause PLpgSQL_Expr PLAssignStmt + PLpgSQLStrictExpr PLpgSQLStrictExprs PLpgSQLStrictNamedExprs + PLAssignStmtStrictExpr + +%type plpgsql_strict_expr plpgsql_strict_named_expr +%type plpgsql_strict_expr_list plpgsql_strict_named_expr_list %type opt_single_name %type opt_qualified_name @@ -827,6 +832,12 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %token MODE_PLPGSQL_ASSIGN1 %token MODE_PLPGSQL_ASSIGN2 %token MODE_PLPGSQL_ASSIGN3 +%token MODE_PLPGSQL_STRICT_EXPR +%token MODE_PLPGSQL_STRICT_EXPR_LIST +%token MODE_PLPGSQL_STRICT_NAMED_EXPR_LIST +%token MODE_PLPGSQL_STRICT_EXPR_ASSIGN1 +%token MODE_PLPGSQL_STRICT_EXPR_ASSIGN2 +%token MODE_PLPGSQL_STRICT_EXPR_ASSIGN3 /* Precedence: lowest to highest */ @@ -958,6 +969,46 @@ parse_toplevel: pg_yyget_extra(yyscanner)->parsetree = list_make1(makeRawStmt((Node *) n, 0)); } + | MODE_PLPGSQL_STRICT_EXPR PLpgSQLStrictExpr + { + pg_yyget_extra(yyscanner)->parsetree = + list_make1(makeRawStmt($2, 0)); + } + | MODE_PLPGSQL_STRICT_EXPR_LIST PLpgSQLStrictExprs + { + pg_yyget_extra(yyscanner)->parsetree = + list_make1(makeRawStmt($2, 0)); + } + | MODE_PLPGSQL_STRICT_NAMED_EXPR_LIST PLpgSQLStrictNamedExprs + { + pg_yyget_extra(yyscanner)->parsetree = + list_make1(makeRawStmt($2, 0)); + } + | MODE_PLPGSQL_STRICT_EXPR_ASSIGN1 PLAssignStmtStrictExpr + { + PLAssignStmt *n = (PLAssignStmt *) $2; + + n->nnames = 1; + pg_yyget_extra(yyscanner)->parsetree = + list_make1(makeRawStmt((Node *) n, 0)); + } + | MODE_PLPGSQL_STRICT_EXPR_ASSIGN2 PLAssignStmtStrictExpr + { + PLAssignStmt *n = (PLAssignStmt *) $2; + + n->nnames = 2; + pg_yyget_extra(yyscanner)->parsetree = + list_make1(makeRawStmt((Node *) n, 0)); + } + | MODE_PLPGSQL_STRICT_EXPR_ASSIGN3 PLAssignStmtStrictExpr + { + PLAssignStmt *n = (PLAssignStmt *) $2; + + n->nnames = 3; + pg_yyget_extra(yyscanner)->parsetree = + list_make1(makeRawStmt((Node *) n, 0)); + } + ; /* @@ -17496,6 +17547,104 @@ plassign_equals: COLON_EQUALS | '=' ; +/* + * In "strict" mode plpgsql expressions are just an a_expr. From compatibility + * reasons (with default mode) it returns SelectStmt still. + */ +PLpgSQLStrictExpr: a_expr + { + SelectStmt *n = makeNode(SelectStmt); + ResTarget *rt = makeNode(ResTarget); + + rt->name = NULL; + rt->indirection = NIL; + rt->val = (Node *) $1; + rt->location = @1; + + n->targetList = list_make1((Node *) rt); + $$ = (Node *) n; + } + ; + +PLAssignStmtStrictExpr: plassign_target opt_indirection plassign_equals PLpgSQLStrictExpr + { + PLAssignStmt *n = makeNode(PLAssignStmt); + + n->name = $1; + n->indirection = check_indirection($2, yyscanner); + /* nnames will be filled by calling production */ + n->val = (SelectStmt *) $4; + n->location = @1; + $$ = (Node *) n; + } + ; + +/* + * Used for unnamed plpgsql cursor's argument and plpgsql case in + * "strict" mode. + */ +PLpgSQLStrictExprs: plpgsql_strict_expr_list + { + SelectStmt *n = makeNode(SelectStmt); + + n->targetList = $1; + $$ = (Node *) n; + } + ; + +plpgsql_strict_expr_list: + plpgsql_strict_expr + { + $$ = list_make1($1); + } + | plpgsql_strict_expr_list ',' plpgsql_strict_expr + { + $$ = lappend($1, $3); + } + ; + +plpgsql_strict_expr: a_expr + { + $$ = makeNode(ResTarget); + $$->name = NULL; + $$->indirection = NIL; + $$->val = (Node *) $1; + $$->location = @1; + } + ; + +/* + * Used for named cursor's arguments in "strict" mode + */ +PLpgSQLStrictNamedExprs: plpgsql_strict_named_expr_list + { + SelectStmt *n = makeNode(SelectStmt); + + n->targetList = $1; + $$ = (Node *) n; + } + ; + +plpgsql_strict_named_expr_list: + plpgsql_strict_named_expr + { + $$ = list_make1($1); + } + | plpgsql_strict_named_expr_list ',' plpgsql_strict_named_expr + { + $$ = lappend($1, $3); + } + ; + +plpgsql_strict_named_expr: a_expr AS ColLabel + { + $$ = makeNode(ResTarget); + $$->name = $3; + $$->indirection = NIL; + $$->val = (Node *) $1; + $$->location = @1; + } + ; /* * Name classification hierarchy. diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c index 118488c3f3..9d2dde6793 100644 --- a/src/backend/parser/parser.c +++ b/src/backend/parser/parser.c @@ -62,6 +62,12 @@ raw_parser(const char *str, RawParseMode mode) [RAW_PARSE_PLPGSQL_ASSIGN1] = MODE_PLPGSQL_ASSIGN1, [RAW_PARSE_PLPGSQL_ASSIGN2] = MODE_PLPGSQL_ASSIGN2, [RAW_PARSE_PLPGSQL_ASSIGN3] = MODE_PLPGSQL_ASSIGN3, + [RAW_PARSE_PLPGSQL_STRICT_EXPR] = MODE_PLPGSQL_STRICT_EXPR, + [RAW_PARSE_PLPGSQL_STRICT_EXPR_LIST] = MODE_PLPGSQL_STRICT_EXPR_LIST, + [RAW_PARSE_PLPGSQL_STRICT_NAMED_EXPR_LIST] = MODE_PLPGSQL_STRICT_NAMED_EXPR_LIST, + [RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN1] = MODE_PLPGSQL_STRICT_EXPR_ASSIGN1, + [RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN2] = MODE_PLPGSQL_STRICT_EXPR_ASSIGN2, + [RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN3] = MODE_PLPGSQL_STRICT_EXPR_ASSIGN3, }; yyextra.have_lookahead = true; diff --git a/src/include/parser/parser.h b/src/include/parser/parser.h index be184ec506..8e41907bd4 100644 --- a/src/include/parser/parser.h +++ b/src/include/parser/parser.h @@ -33,6 +33,22 @@ * RAW_PARSE_PLPGSQL_ASSIGNn: parse a PL/pgSQL assignment statement, * and return a one-element List containing a RawStmt node. "n" * gives the number of dotted names comprising the target ColumnRef. + * + * RAW_PARSE_PLPGSQL_STRICT_EXPR: parse a PL/pgSQL expression, and + * return a one-element List containing a RwaStmt node. The result is + * compatible with RAW_PARSE_PLPGSQL_EXPR, but parser allows only + * a_expr (instead almost all complete query). + * + * RAW_PARSE_PLPGSQL_STRICT_EXPR_LIST: parse a comma separated list + * of PL/pgSQL expressions (only a_expr are allowed). It is used by + * PLpGSQL CASE and OPEN commands. + * + * RAW_PARSE_PLPGSQL_STRICT_NAMED_EXPR_LIST: parse a comma separated + * list of a_expr node with labels. It is used for evaluation of + * named arguments of PLpgSQL OPEN (cursor) statement. + * + * RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGNn: parse a PL/pgSQL assignment + * statement, but only a_expr are allowed). */ typedef enum { @@ -42,6 +58,12 @@ typedef enum RAW_PARSE_PLPGSQL_ASSIGN1, RAW_PARSE_PLPGSQL_ASSIGN2, RAW_PARSE_PLPGSQL_ASSIGN3, + RAW_PARSE_PLPGSQL_STRICT_EXPR, + RAW_PARSE_PLPGSQL_STRICT_EXPR_LIST, + RAW_PARSE_PLPGSQL_STRICT_NAMED_EXPR_LIST, + RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN1, + RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN2, + RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN3, } RawParseMode; /* Values for the backslash_quote GUC */ diff --git a/src/interfaces/ecpg/preproc/parse.pl b/src/interfaces/ecpg/preproc/parse.pl index fe8d3e5178..433f406778 100644 --- a/src/interfaces/ecpg/preproc/parse.pl +++ b/src/interfaces/ecpg/preproc/parse.pl @@ -89,7 +89,15 @@ my %replace_types = ( 'PLpgSQL_Expr' => 'ignore', 'PLAssignStmt' => 'ignore', 'plassign_target' => 'ignore', - 'plassign_equals' => 'ignore',); + 'plassign_equals' => 'ignore', + 'plpgsql_strict_expr' => 'ignore', + 'plpgsql_strict_named_expr' => 'ignore', + 'plpgsql_strict_expr_list' => 'ignore', + 'plpgsql_strict_named_expr_list' => 'ignore', + 'PLpgSQLStrictExpr' => 'ignore', + 'PLpgSQLStrictExprs' => 'ignore', + 'PLpgSQLStrictNamedExprs' => 'ignore', + 'PLAssignStmtStrictExpr' => 'ignore',); # these replace_line commands excise certain keywords from the core keyword # lists. Be sure to account for these in ColLabel and related productions. diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index f1bce708d6..8f9d3ed64d 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -84,6 +84,23 @@ static const ExceptionLabelMap exception_label_map[] = { {NULL, 0} }; +static const PLpgSQL_parse_modes default_parse_modes = { + RAW_PARSE_PLPGSQL_EXPR, + RAW_PARSE_PLPGSQL_EXPR, + RAW_PARSE_PLPGSQL_EXPR, + RAW_PARSE_PLPGSQL_ASSIGN1, + RAW_PARSE_PLPGSQL_ASSIGN2, + RAW_PARSE_PLPGSQL_ASSIGN3 +}; + +static const PLpgSQL_parse_modes strict_expr_parse_modes = { + RAW_PARSE_PLPGSQL_STRICT_EXPR, + RAW_PARSE_PLPGSQL_STRICT_EXPR_LIST, + RAW_PARSE_PLPGSQL_STRICT_NAMED_EXPR_LIST, + RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN1, + RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN2, + RAW_PARSE_PLPGSQL_STRICT_EXPR_ASSIGN3 +}; /* ---------- * static prototypes @@ -355,6 +372,11 @@ do_compile(FunctionCallInfo fcinfo, function->extra_warnings = forValidator ? plpgsql_extra_warnings : 0; function->extra_errors = forValidator ? plpgsql_extra_errors : 0; + if (function->extra_errors & PLPGSQL_XCHECK_STRICTEXPRCHECK) + function->pmodes = &strict_expr_parse_modes; + else + function->pmodes = &default_parse_modes; + if (is_dml_trigger) function->fn_is_trigger = PLPGSQL_DML_TRIGGER; else if (is_event_trigger) @@ -898,6 +920,15 @@ plpgsql_compile_inline(char *proc_source) function->extra_warnings = 0; function->extra_errors = 0; + /* + * Although function->extra_errors is disabled, we want to + * do strict_expr_check inside annoymous block too. + */ + if (plpgsql_extra_errors & PLPGSQL_XCHECK_STRICTEXPRCHECK) + function->pmodes = &strict_expr_parse_modes; + else + function->pmodes = &default_parse_modes; + function->nstatements = 0; function->requires_procedure_resowner = false; diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y index a29d2dfacd..d86792195d 100644 --- a/src/pl/plpgsql/src/pl_gram.y +++ b/src/pl/plpgsql/src/pl_gram.y @@ -178,6 +178,7 @@ static void check_raise_parameters(PLpgSQL_stmt_raise *stmt); %type expr_until_semi %type expr_until_then expr_until_loop opt_expr_until_when %type opt_exitcond +%type expressions_until_then %type cursor_variable %type decl_cursor_arg @@ -957,13 +958,13 @@ stmt_assign : T_DATUM switch ($1.ident ? 1 : list_length($1.idents)) { case 1: - pmode = RAW_PARSE_PLPGSQL_ASSIGN1; + pmode = plpgsql_curr_compile->pmodes->pmode_assign1; break; case 2: - pmode = RAW_PARSE_PLPGSQL_ASSIGN2; + pmode = plpgsql_curr_compile->pmodes->pmode_assign2; break; case 3: - pmode = RAW_PARSE_PLPGSQL_ASSIGN3; + pmode = plpgsql_curr_compile->pmodes->pmode_assign3; break; default: elog(ERROR, "unexpected number of names"); @@ -1239,7 +1240,7 @@ case_when_list : case_when_list case_when } ; -case_when : K_WHEN expr_until_then proc_sect +case_when : K_WHEN expressions_until_then proc_sect { PLpgSQL_case_when *new = palloc(sizeof(PLpgSQL_case_when)); @@ -1269,6 +1270,14 @@ opt_case_else : } ; +expressions_until_then : + { + $$ = read_sql_construct(K_THEN, 0, 0, "THEN", + plpgsql_curr_compile->pmodes->pmode_expr_list, + true, true, NULL, NULL); + } + ; + stmt_loop : opt_loop_label K_LOOP loop_body { PLpgSQL_stmt_loop *new; @@ -1488,7 +1497,8 @@ for_control : for_variable K_IN * Relabel first expression as an expression; * then we can check its syntax. */ - expr1->parseMode = RAW_PARSE_PLPGSQL_EXPR; + expr1->parseMode = plpgsql_curr_compile->pmodes->pmode_expr; + check_sql_expr(expr1->query, expr1->parseMode, expr1loc); @@ -1858,6 +1868,8 @@ stmt_raise : K_RAISE */ if (tok == SCONST) { + RawParseMode pmode_expr; + /* old style message and parameters */ new->message = yylval.str; /* @@ -1870,13 +1882,15 @@ stmt_raise : K_RAISE if (tok != ',' && tok != ';' && tok != K_USING) yyerror("syntax error"); + pmode_expr = plpgsql_curr_compile->pmodes->pmode_expr; + while (tok == ',') { PLpgSQL_expr *expr; expr = read_sql_construct(',', ';', K_USING, ", or ; or USING", - RAW_PARSE_PLPGSQL_EXPR, + pmode_expr, true, true, NULL, &tok); new->params = lappend(new->params, expr); @@ -2010,10 +2024,13 @@ stmt_dynexecute : K_EXECUTE PLpgSQL_stmt_dynexecute *new; PLpgSQL_expr *expr; int endtoken; + RawParseMode pmode_expr; + + pmode_expr = plpgsql_curr_compile->pmodes->pmode_expr; expr = read_sql_construct(K_INTO, K_USING, ';', "INTO or USING or ;", - RAW_PARSE_PLPGSQL_EXPR, + pmode_expr, true, true, NULL, &endtoken); @@ -2052,7 +2069,7 @@ stmt_dynexecute : K_EXECUTE { expr = read_sql_construct(',', ';', K_INTO, ", or ; or INTO", - RAW_PARSE_PLPGSQL_EXPR, + pmode_expr, true, true, NULL, &endtoken); new->params = lappend(new->params, expr); @@ -2637,7 +2654,7 @@ static PLpgSQL_expr * read_sql_expression(int until, const char *expected) { return read_sql_construct(until, 0, 0, expected, - RAW_PARSE_PLPGSQL_EXPR, + plpgsql_curr_compile->pmodes->pmode_expr, true, true, NULL, NULL); } @@ -2647,7 +2664,7 @@ read_sql_expression2(int until, int until2, const char *expected, int *endtoken) { return read_sql_construct(until, until2, 0, expected, - RAW_PARSE_PLPGSQL_EXPR, + plpgsql_curr_compile->pmodes->pmode_expr, true, true, NULL, endtoken); } @@ -3840,6 +3857,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until) char **argv; StringInfoData ds; bool any_named = false; + RawParseMode pmode_expr; tok = yylex(); if (cursor->cursor_explicit_argrow < 0) @@ -3866,6 +3884,8 @@ read_cursor_args(PLpgSQL_var *cursor, int until) cursor->refname), parser_errposition(yylloc))); + pmode_expr = plpgsql_curr_compile->pmodes->pmode_expr; + /* * Read the arguments, one by one. */ @@ -3936,7 +3956,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until) */ item = read_sql_construct(',', ')', 0, ",\" or \")", - RAW_PARSE_PLPGSQL_EXPR, + pmode_expr, true, true, NULL, &endtoken); @@ -3977,7 +3997,9 @@ read_cursor_args(PLpgSQL_var *cursor, int until) expr = palloc0(sizeof(PLpgSQL_expr)); expr->query = pstrdup(ds.data); - expr->parseMode = RAW_PARSE_PLPGSQL_EXPR; + expr->parseMode = any_named ? + plpgsql_curr_compile->pmodes->pmode_named_expr_list + : plpgsql_curr_compile->pmodes->pmode_expr_list; expr->plan = NULL; expr->paramnos = NULL; expr->target_param = -1; @@ -4151,7 +4173,7 @@ make_case(int location, PLpgSQL_expr *t_expr, StringInfoData ds; /* We expect to have expressions not statements */ - Assert(expr->parseMode == RAW_PARSE_PLPGSQL_EXPR); + Assert(expr->parseMode == plpgsql_curr_compile->pmodes->pmode_expr_list); /* Do the string hacking */ initStringInfo(&ds); diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index fce459ade0..f34b3befc4 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -94,6 +94,8 @@ plpgsql_extra_checks_check_hook(char **newvalue, void **extra, GucSource source) extrachecks |= PLPGSQL_XCHECK_TOOMANYROWS; else if (pg_strcasecmp(tok, "strict_multi_assignment") == 0) extrachecks |= PLPGSQL_XCHECK_STRICTMULTIASSIGNMENT; + else if (pg_strcasecmp(tok, "strict_expr_check") == 0) + extrachecks |= PLPGSQL_XCHECK_STRICTEXPRCHECK; else if (pg_strcasecmp(tok, "all") == 0 || pg_strcasecmp(tok, "none") == 0) { GUC_check_errdetail("Key word \"%s\" cannot be combined with other key words.", tok); diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 50c3b28472..b2fb67e851 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -960,6 +960,21 @@ typedef enum PLpgSQL_trigtype PLPGSQL_NOT_TRIGGER, } PLpgSQL_trigtype; +/* + * Raw parse modes, that should be used for expressions, + * assignment, expression's list. When extra_errors.strict_expr_check + * is active, then only a_expr parsing is allowed. + */ +typedef struct PLpgSQL_parse_modes +{ + RawParseMode pmode_expr; + RawParseMode pmode_expr_list; + RawParseMode pmode_named_expr_list; + RawParseMode pmode_assign1; + RawParseMode pmode_assign2; + RawParseMode pmode_assign3; +} PLpgSQL_parse_modes; + /* * Complete compiled function */ @@ -1010,6 +1025,9 @@ typedef struct PLpgSQL_function unsigned int nstatements; /* counter for assigning stmtids */ bool requires_procedure_resowner; /* contains CALL or DO? */ + /* Raw parse modes configuration */ + const PLpgSQL_parse_modes *pmodes; + /* these fields change when the function is used */ struct PLpgSQL_execstate *cur_estate; unsigned long use_count; @@ -1204,6 +1222,7 @@ extern bool plpgsql_check_asserts; #define PLPGSQL_XCHECK_SHADOWVAR (1 << 1) #define PLPGSQL_XCHECK_TOOMANYROWS (1 << 2) #define PLPGSQL_XCHECK_STRICTMULTIASSIGNMENT (1 << 3) +#define PLPGSQL_XCHECK_STRICTEXPRCHECK (1 << 4) #define PLPGSQL_XCHECK_ALL ((int) ~0) extern int plpgsql_extra_warnings; -- 2.45.2