📄 gram.y
字号:
if (!plpgsql_curr_compile->fn_retset) yyerror("cannot use RETURN NEXT in a non-SETOF function"); new = palloc0(sizeof(PLpgSQL_stmt_return_next)); new->cmd_type = PLPGSQL_STMT_RETURN_NEXT; new->lineno = $2; new->expr = NULL; new->retvarno = -1; if (plpgsql_curr_compile->out_param_varno >= 0) { if (yylex() != ';') yyerror("RETURN NEXT cannot have a parameter in function with OUT parameters"); new->retvarno = plpgsql_curr_compile->out_param_varno; } else if (plpgsql_curr_compile->fn_retistuple) { switch (yylex()) { case T_ROW: new->retvarno = yylval.row->rowno; break; case T_RECORD: new->retvarno = yylval.rec->recno; break; default: yyerror("RETURN NEXT must specify a record or row variable in function returning tuple"); break; } if (yylex() != ';') yyerror("RETURN NEXT must specify a record or row variable in function returning tuple"); } else new->expr = plpgsql_read_expression(';', ";"); $$ = (PLpgSQL_stmt *)new; } ;stmt_raise : K_RAISE lno raise_level raise_msg { PLpgSQL_stmt_raise *new; int tok; new = palloc(sizeof(PLpgSQL_stmt_raise)); new->cmd_type = PLPGSQL_STMT_RAISE; new->lineno = $2; new->elog_level = $3; new->message = $4; new->params = NIL; tok = yylex(); /* * We expect either a semi-colon, which * indicates no parameters, or a comma that * begins the list of parameter expressions */ if (tok != ',' && tok != ';') yyerror("syntax error"); if (tok == ',') { PLpgSQL_expr *expr; int term; for (;;) { expr = read_sql_construct(',', ';', ", or ;", "SELECT ", true, true, &term); new->params = lappend(new->params, expr); if (term == ';') break; } } $$ = (PLpgSQL_stmt *)new; } ;raise_msg : T_STRING { $$ = plpgsql_get_string_value(); } ;raise_level : K_EXCEPTION { $$ = ERROR; } | K_WARNING { $$ = WARNING; } | K_NOTICE { $$ = NOTICE; } | K_INFO { $$ = INFO; } | K_LOG { $$ = LOG; } | K_DEBUG { $$ = DEBUG1; } ;loop_body : proc_sect K_END K_LOOP opt_label ';' { $$.stmts = $1; $$.end_label = $4; } ;stmt_execsql : execsql_start lno { PLpgSQL_stmt_execsql *new; new = palloc(sizeof(PLpgSQL_stmt_execsql)); new->cmd_type = PLPGSQL_STMT_EXECSQL; new->lineno = $2; new->sqlstmt = read_sql_stmt($1); $$ = (PLpgSQL_stmt *)new; } ;stmt_dynexecute : K_EXECUTE lno { PLpgSQL_stmt_dynexecute *new; PLpgSQL_expr *expr; int endtoken; expr = read_sql_construct(K_INTO, ';', "INTO|;", "SELECT ", true, true, &endtoken); new = palloc(sizeof(PLpgSQL_stmt_dynexecute)); new->cmd_type = PLPGSQL_STMT_DYNEXECUTE; new->lineno = $2; new->query = expr; new->rec = NULL; new->row = NULL; /* * If we saw "INTO", look for a following row * var, record var, or list of scalars. */ if (endtoken == K_INTO) { switch (yylex()) { case T_ROW: check_assignable((PLpgSQL_datum *) yylval.row); new->row = yylval.row; break; case T_RECORD: check_assignable((PLpgSQL_datum *) yylval.row); new->rec = yylval.rec; break; case T_SCALAR: new->row = read_into_scalar_list(yytext, yylval.scalar); break; default: plpgsql_error_lineno = $2; ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error at \"%s\"", yytext), errdetail("Expected record variable, row variable, " "or list of scalar variables."))); } if (yylex() != ';') yyerror("syntax error"); } $$ = (PLpgSQL_stmt *)new; } ;stmt_open : K_OPEN lno cursor_varptr { PLpgSQL_stmt_open *new; int tok; new = palloc0(sizeof(PLpgSQL_stmt_open)); new->cmd_type = PLPGSQL_STMT_OPEN; new->lineno = $2; new->curvar = $3->varno; if ($3->cursor_explicit_expr == NULL) { tok = yylex(); if (tok != K_FOR) { plpgsql_error_lineno = $2; ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error at \"%s\"", yytext), errdetail("Expected FOR to open a reference cursor."))); } tok = yylex(); if (tok == K_EXECUTE) { new->dynquery = read_sql_stmt("SELECT "); } else { plpgsql_push_back_token(tok); new->query = read_sql_stmt(""); } } else { if ($3->cursor_explicit_argrow >= 0) { char *cp; tok = yylex(); if (tok != '(') { plpgsql_error_lineno = plpgsql_scanner_lineno(); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cursor \"%s\" has arguments", $3->refname))); } /* * Push back the '(', else read_sql_stmt * will complain about unbalanced parens. */ plpgsql_push_back_token(tok); new->argquery = read_sql_stmt("SELECT "); /* * Now remove the leading and trailing parens, * because we want "select 1, 2", not * "select (1, 2)". */ cp = new->argquery->query; if (strncmp(cp, "SELECT", 6) != 0) { plpgsql_error_lineno = plpgsql_scanner_lineno(); /* internal error */ elog(ERROR, "expected \"SELECT (\", got \"%s\"", new->argquery->query); } cp += 6; while (*cp == ' ') /* could be more than 1 space here */ cp++; if (*cp != '(') { plpgsql_error_lineno = plpgsql_scanner_lineno(); /* internal error */ elog(ERROR, "expected \"SELECT (\", got \"%s\"", new->argquery->query); } *cp = ' '; cp += strlen(cp) - 1; if (*cp != ')') yyerror("expected \")\""); *cp = '\0'; } else { tok = yylex(); if (tok == '(') { plpgsql_error_lineno = plpgsql_scanner_lineno(); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cursor \"%s\" has no arguments", $3->refname))); } if (tok != ';') { plpgsql_error_lineno = plpgsql_scanner_lineno(); ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("syntax error at \"%s\"", yytext))); } } } $$ = (PLpgSQL_stmt *)new; } ;stmt_fetch : K_FETCH lno cursor_variable K_INTO { PLpgSQL_stmt_fetch *new; new = (PLpgSQL_stmt_fetch *)make_fetch_stmt(); new->curvar = $3; $$ = (PLpgSQL_stmt *)new; $$->lineno = $2; } ;stmt_close : K_CLOSE lno cursor_variable ';' { PLpgSQL_stmt_close *new; new = palloc(sizeof(PLpgSQL_stmt_close)); new->cmd_type = PLPGSQL_STMT_CLOSE; new->lineno = $2; new->curvar = $3; $$ = (PLpgSQL_stmt *)new; } ;stmt_null : K_NULL ';' { /* We do not bother building a node for NULL */ $$ = NULL; } ;cursor_varptr : T_SCALAR { if (yylval.scalar->dtype != PLPGSQL_DTYPE_VAR) yyerror("cursor variable must be a simple variable"); if (((PLpgSQL_var *) yylval.scalar)->datatype->typoid != REFCURSOROID) { plpgsql_error_lineno = plpgsql_scanner_lineno(); ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("\"%s\" must be of type cursor or refcursor", ((PLpgSQL_var *) yylval.scalar)->refname))); } $$ = (PLpgSQL_var *) yylval.scalar; } ;cursor_variable : T_SCALAR { if (yylval.scalar->dtype != PLPGSQL_DTYPE_VAR) yyerror("cursor variable must be a simple variable"); if (((PLpgSQL_var *) yylval.scalar)->datatype->typoid != REFCURSOROID) { plpgsql_error_lineno = plpgsql_scanner_lineno(); ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("\"%s\" must be of type refcursor", ((PLpgSQL_var *) yylval.scalar)->refname))); } $$ = yylval.scalar->dno; } ;execsql_start : T_WORD { $$ = pstrdup(yytext); } | T_ERROR { $$ = pstrdup(yytext); } ;exception_sect : { $$ = NULL; } | K_EXCEPTION lno { /* * We use a mid-rule action to add these * special variables to the namespace before * parsing the WHEN clauses themselves. */ PLpgSQL_exception_block *new = palloc(sizeof(PLpgSQL_exception_block)); PLpgSQL_variable *var; var = plpgsql_build_variable("sqlstate", $2, plpgsql_build_datatype(TEXTOID, -1), true); ((PLpgSQL_var *) var)->isconst = true; new->sqlstate_varno = var->dno; var = plpgsql_build_variable("sqlerrm", $2, plpgsql_build_datatype(TEXTOID, -1), true); ((PLpgSQL_var *) var)->isconst = true; new->sqlerrm_varno = var->dno; $<exception_block>$ = new; } proc_exceptions { PLpgSQL_exception_block *new = $<exception_block>3; new->exc_list = $4; $$ = new; } ;proc_exceptions : proc_exceptions proc_exception { $$ = lappend($1, $2); } | proc_exception { $$ = list_make1($1); } ;proc_exception : K_WHEN lno proc_conditions K_THEN proc_sect { PLpgSQL_exception *new; new = palloc0(sizeof(PLpgSQL_exception)); new->lineno = $2; new->conditions = $3; new->action = $5; $$ = new; } ;proc_conditions : proc_conditions K_OR opt_lblname { PLpgSQL_condition *old; for (old = $1; old->next != NULL; old = old->next) /* skip */ ; old->next = plpgsql_parse_err_condition($3); $$ = $1; } | opt_lblname { $$ = plpgsql_parse_err_condition($1); } ;expr_until_semi : { $$ = plpgsql_read_expression(';', ";"); } ;expr_until_rightbracket : { $$ = plpgsql_read_expression(']', "]"); } ;expr_until_then : { $$ = plpgsql_read_expression(K_THEN, "THEN"); } ;expr_until_loop : { $$ = plpgsql_read_expression(K_LOOP, "LOOP"); } ;opt_block_label : { plpgsql_ns_push(NULL); $$ = NULL; } | '<' '<' opt_lblname '>' '>' { plpgsql_ns_push($3); $$ = $3; } ;opt_label : { $$ = NULL; } | T_LABEL { char *label_name; plpgsql_convert_ident(yytext, &label_name, 1); $$ = label_name; } | T_WORD { /* just to give a better error than "syntax error" */ yyerror("no such label"); } ;opt_exitcond : ';' { $$ = NULL; } | K_WHEN expr_until_semi { $$ = $2; } ;opt_lblname : T_WORD { char *name; plpgsql_convert_ident(yytext, &name, 1); $$ = name; } ;lno : { $$ = plpgsql_error_lineno = plpgsql_scanner_lineno(); } ;%%#define MAX_EXPR_PARAMS 1024/* * determine the expression parameter position to use for a plpgsql datum * * It is important that any given plpgsql datum map to just one parameter. * We used to be sloppy and assign a separate parameter for each occurrence * of a datum reference, but that fails for situations such as "select DATUM * from ... group by DATUM". * * The params[] array must be of size MAX_EXPR_PARAMS. */static intassign_expr_param(int dno, int *params, int *nparams){ int i; /* already have an instance of this dno? */ for (i = 0; i < *nparams; i++) { if (params[i] == dno) return i+1; } /* check for array overflow */ if (*nparams >= MAX_EXPR_PARAMS) { plpgsql_error_lineno = plpgsql_scanner_lineno(); ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("too many variables specified in SQL statement"))); } /* add new parameter dno to array */ params[*nparams] = dno; (*nparams)++; return *nparams;}PLpgSQL_expr *plpgsql_read_expression(int until, const char *expected){ return read_sql_construct(until, 0, expected, "SELECT ", true, true, NULL);}static PLpgSQL_expr *read_sql_stmt(const char *sqlstart){ return read_sql_construct(';', 0, ";", sqlstart, false, true, NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -