📄 gram.y
字号:
loop_body : proc_sect K_END K_LOOP opt_label ';' { $$.stmts = $1; $$.end_label = $4; } ;stmt_execsql : execsql_start lno { $$ = make_execsql_stmt($1, $2); } ;/* this matches any otherwise-unrecognized starting keyword */execsql_start : T_WORD { $$ = pstrdup(yytext); } | T_ERROR { $$ = pstrdup(yytext); } ;stmt_execsql_insert : K_INSERT lno K_INTO { /* * We have to special-case INSERT so that its INTO * won't be treated as an INTO-variables clause. * * Fortunately, this is the only valid use of INTO * in a pl/pgsql SQL command, and INTO is already * a fully reserved word in the main grammar. */ $$ = make_execsql_stmt("INSERT INTO", $2); } ;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->into = false; new->strict = false; new->rec = NULL; new->row = NULL; /* If we found "INTO", collect the argument */ if (endtoken == K_INTO) { new->into = true; read_into_target(&new->rec, &new->row, &new->strict); if (yylex() != ';') yyerror("syntax error"); } $$ = (PLpgSQL_stmt *)new; } ;stmt_open : K_OPEN lno cursor_variable { 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; new->cursor_options = CURSOR_OPT_FAST_PLAN; if ($3->cursor_explicit_expr == NULL) { /* be nice if we could use opt_scrollable here */ tok = yylex(); if (tok == K_NOSCROLL) { new->cursor_options |= CURSOR_OPT_NO_SCROLL; tok = yylex(); } else if (tok == K_SCROLL) { new->cursor_options |= CURSOR_OPT_SCROLL; 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 opt_fetch_direction cursor_variable K_INTO { PLpgSQL_stmt_fetch *fetch = $3; PLpgSQL_rec *rec; PLpgSQL_row *row; /* We have already parsed everything through the INTO keyword */ read_into_target(&rec, &row, NULL); if (yylex() != ';') yyerror("syntax error"); fetch->lineno = $2; fetch->rec = rec; fetch->row = row; fetch->curvar = $4->varno; fetch->is_move = false; $$ = (PLpgSQL_stmt *)fetch; } ; stmt_move : K_MOVE lno opt_fetch_direction cursor_variable ';' { PLpgSQL_stmt_fetch *fetch = $3; fetch->lineno = $2; fetch->curvar = $4->varno; fetch->is_move = true; $$ = (PLpgSQL_stmt *)fetch; } ;opt_fetch_direction : { $$ = read_fetch_direction(); } ;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->varno; $$ = (PLpgSQL_stmt *)new; } ;stmt_null : K_NULL ';' { /* We do not bother building a node for NULL */ $$ = NULL; } ;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 cursor or refcursor", ((PLpgSQL_var *) yylval.scalar)->refname))); } $$ = (PLpgSQL_var *) yylval.scalar; } ;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; } ;/* * need all the options because scanner will have tried to resolve as variable */opt_label : { $$ = NULL; } | T_WORD { $$ = check_label(yytext); } | T_SCALAR { $$ = check_label(yytext); } | T_RECORD { $$ = check_label(yytext); } | T_ROW { $$ = check_label(yytext); } ;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);}/* * Read a SQL construct and build a PLpgSQL_expr for it. * * until: token code for expected terminator * until2: token code for alternate terminator (pass 0 if none) * expected: text to use in complaining that terminator was not found * sqlstart: text to prefix to the accumulated SQL text * isexpression: whether to say we're reading an "expression" or a "statement" * valid_sql: whether to check the syntax of the expr (prefixed with sqlstart) * endtoken: if not NULL, ending token is stored at *endtoken * (this is only interesting if until2 isn't zero) */static PLpgSQL_expr *read_sql_construct(int until, int until2, const char *expected, const char *sqlstart, bool isexpression, bool valid_sql, int *endtoken){ int tok; int lno; PLpgSQL_dstring ds; int parenlevel = 0; int nparams = 0; int params[MAX_EXPR_PARAMS]; char buf[32]; PLpgSQL_expr *expr; lno = plpgsql_scanner_lineno(); plpgsql_dstring_init(&ds); plpgsql_dstring_append(&ds, sqlstart); for (;;) { tok = yylex(); if (tok == until && parenlevel == 0) break; if (tok == until2 && parenlevel == 0) break; if (tok == '(' || tok == '[') parenlevel++; else if (tok == ')' || tok == ']') { parenlevel--; if (parenlevel < 0) yyerror("mismatched parentheses"); } /* * End of function definition is an error, and we don't expect to * hit a semicolon either (unless it's the until symbol, in which * case we should have fallen out above). */ if (tok == 0 || tok == ';') { if (parenlevel != 0) yyerror("mismatched parentheses"); plpgsql_error_lineno = lno; if (isexpression) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("missing \"%s\" at end of SQL expression", expected))); else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("missing \"%s\" at end of SQL statement", expected))); } if (plpgsql_SpaceScanned) plpgsql_dstring_append(&ds, " "); switch (tok) { case T_SCALAR: snprintf(buf, sizeof(buf), " $%d ", assign_expr_param(yylval.scalar->dno, params, &nparams)); plpgsql_dstring_append(&ds, buf); break; case T_ROW: snprintf(buf, sizeof(buf), " $%d ", assign_expr_param(yylval.row->rowno, params, &nparams)); plpgsql_dstring_append(&ds, buf); break; case T_RECORD: snprintf(buf, sizeof(buf), " $%d ", assign_expr_param(yylval.rec->recno, params, &nparams)); plpgsql_dstring_append(&ds, buf); break; default: plpgsql_dstring_append(&ds, yytext); break; } } if (endtoken) *endtoken = tok; expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int)); expr->dtype = PLPGSQL_DTYPE_EXPR; expr->query = pstrdup(plpgsql_dstring_get(&ds)); expr->plan = NULL; expr->nparams = nparams; while(nparams-- > 0) expr->params[nparams] = params[nparams]; plpgsql_dstring_free(&ds); if (valid_sql) check_sql_expr(expr->query); return expr;}static PLpgSQL_type *read_datatype(int tok){ int lno; PLpgSQL_dstring ds; char *type_name; PLpgSQL_type *result; bool needspace = false; int parenlevel = 0; lno = plpgsql_scanner_lineno(); /* Often there will be a lookahead token, but if not, get one */ if (tok == YYEMPTY) tok = yylex(); if (tok == T_DTYPE) { /* lexer found word%TYPE and did its thing already */ return yylval.dtype; } plpgsql_dstring_init(&ds); while (tok != ';') { if (tok == 0) { if (parenlevel != 0) yyerror("mismatched parentheses"); else yyerror("incomplete datatype declaration"); } /* Possible followers for datatype in a declaration */ if (tok == K_NOT || tok == K_ASSIGN || tok == K_DEFAULT) break; /* Possible followers for datatype in a cursor_arg list */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -