📄 gram.y
字号:
{ $$ = $1; } | stmt_open { $$ = $1; } | stmt_fetch { $$ = $1; } | stmt_move { $$ = $1; } | stmt_close { $$ = $1; } | stmt_null { $$ = $1; } ;stmt_perform : K_PERFORM lno expr_until_semi { PLpgSQL_stmt_perform *new; new = palloc0(sizeof(PLpgSQL_stmt_perform)); new->cmd_type = PLPGSQL_STMT_PERFORM; new->lineno = $2; new->expr = $3; $$ = (PLpgSQL_stmt *)new; } ;stmt_assign : assign_var lno K_ASSIGN expr_until_semi { PLpgSQL_stmt_assign *new; new = palloc0(sizeof(PLpgSQL_stmt_assign)); new->cmd_type = PLPGSQL_STMT_ASSIGN; new->lineno = $2; new->varno = $1; new->expr = $4; $$ = (PLpgSQL_stmt *)new; } ;stmt_getdiag : K_GET K_DIAGNOSTICS lno getdiag_list ';' { PLpgSQL_stmt_getdiag *new; new = palloc0(sizeof(PLpgSQL_stmt_getdiag)); new->cmd_type = PLPGSQL_STMT_GETDIAG; new->lineno = $3; new->diag_items = $4; $$ = (PLpgSQL_stmt *)new; } ;getdiag_list : getdiag_list ',' getdiag_list_item { $$ = lappend($1, $3); } | getdiag_list_item { $$ = list_make1($1); } ;getdiag_list_item : getdiag_target K_ASSIGN getdiag_kind { PLpgSQL_diag_item *new; new = palloc(sizeof(PLpgSQL_diag_item)); new->target = $1; new->kind = $3; $$ = new; } ;getdiag_kind : K_ROW_COUNT { $$ = PLPGSQL_GETDIAG_ROW_COUNT; } | K_RESULT_OID { $$ = PLPGSQL_GETDIAG_RESULT_OID; } ;getdiag_target : T_SCALAR { check_assignable(yylval.scalar); $$ = yylval.scalar->dno; } ;assign_var : T_SCALAR { check_assignable(yylval.scalar); $$ = yylval.scalar->dno; } | T_ROW { check_assignable((PLpgSQL_datum *) yylval.row); $$ = yylval.row->rowno; } | T_RECORD { check_assignable((PLpgSQL_datum *) yylval.rec); $$ = yylval.rec->recno; } | assign_var '[' expr_until_rightbracket { PLpgSQL_arrayelem *new; new = palloc0(sizeof(PLpgSQL_arrayelem)); new->dtype = PLPGSQL_DTYPE_ARRAYELEM; new->subscript = $3; new->arrayparentno = $1; plpgsql_adddatum((PLpgSQL_datum *)new); $$ = new->dno; } ;stmt_if : K_IF lno expr_until_then proc_sect stmt_else K_END K_IF ';' { PLpgSQL_stmt_if *new; new = palloc0(sizeof(PLpgSQL_stmt_if)); new->cmd_type = PLPGSQL_STMT_IF; new->lineno = $2; new->cond = $3; new->true_body = $4; new->false_body = $5; $$ = (PLpgSQL_stmt *)new; } ;stmt_else : { $$ = NIL; } | K_ELSIF lno expr_until_then proc_sect stmt_else { /* * Translate the structure: into: * * IF c1 THEN IF c1 THEN * ... ... * ELSIF c2 THEN ELSE * IF c2 THEN * ... ... * ELSE ELSE * ... ... * END IF END IF * END IF */ PLpgSQL_stmt_if *new_if; /* first create a new if-statement */ new_if = palloc0(sizeof(PLpgSQL_stmt_if)); new_if->cmd_type = PLPGSQL_STMT_IF; new_if->lineno = $2; new_if->cond = $3; new_if->true_body = $4; new_if->false_body = $5; /* wrap the if-statement in a "container" list */ $$ = list_make1(new_if); } | K_ELSE proc_sect { $$ = $2; } ;stmt_loop : opt_block_label K_LOOP lno loop_body { PLpgSQL_stmt_loop *new; new = palloc0(sizeof(PLpgSQL_stmt_loop)); new->cmd_type = PLPGSQL_STMT_LOOP; new->lineno = $3; new->label = $1; new->body = $4.stmts; check_labels($1, $4.end_label); plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } ;stmt_while : opt_block_label K_WHILE lno expr_until_loop loop_body { PLpgSQL_stmt_while *new; new = palloc0(sizeof(PLpgSQL_stmt_while)); new->cmd_type = PLPGSQL_STMT_WHILE; new->lineno = $3; new->label = $1; new->cond = $4; new->body = $5.stmts; check_labels($1, $5.end_label); plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } ;stmt_for : opt_block_label K_FOR for_control loop_body { /* This runs after we've scanned the loop body */ if ($3->cmd_type == PLPGSQL_STMT_FORI) { PLpgSQL_stmt_fori *new; new = (PLpgSQL_stmt_fori *) $3; new->label = $1; new->body = $4.stmts; $$ = (PLpgSQL_stmt *) new; } else if ($3->cmd_type == PLPGSQL_STMT_FORS) { PLpgSQL_stmt_fors *new; new = (PLpgSQL_stmt_fors *) $3; new->label = $1; new->body = $4.stmts; $$ = (PLpgSQL_stmt *) new; } else { PLpgSQL_stmt_dynfors *new; Assert($3->cmd_type == PLPGSQL_STMT_DYNFORS); new = (PLpgSQL_stmt_dynfors *) $3; new->label = $1; new->body = $4.stmts; $$ = (PLpgSQL_stmt *) new; } check_labels($1, $4.end_label); /* close namespace started in opt_block_label */ plpgsql_ns_pop(); } ;for_control : lno for_variable K_IN { int tok = yylex(); /* Simple case: EXECUTE is a dynamic FOR loop */ if (tok == K_EXECUTE) { PLpgSQL_stmt_dynfors *new; PLpgSQL_expr *expr; expr = plpgsql_read_expression(K_LOOP, "LOOP"); new = palloc0(sizeof(PLpgSQL_stmt_dynfors)); new->cmd_type = PLPGSQL_STMT_DYNFORS; new->lineno = $1; if ($2.rec) { new->rec = $2.rec; check_assignable((PLpgSQL_datum *) new->rec); } else if ($2.row) { new->row = $2.row; check_assignable((PLpgSQL_datum *) new->row); } else if ($2.scalar) { /* convert single scalar to list */ new->row = make_scalar_list1($2.name, $2.scalar, $2.lineno); /* no need for check_assignable */ } else { plpgsql_error_lineno = $2.lineno; yyerror("loop variable of loop over rows must be a record or row variable or list of scalar variables"); } new->query = expr; $$ = (PLpgSQL_stmt *) new; } else { PLpgSQL_expr *expr1; bool reverse = false; /* * We have to distinguish between two * alternatives: FOR var IN a .. b and FOR * var IN query. Unfortunately this is * tricky, since the query in the second * form needn't start with a SELECT * keyword. We use the ugly hack of * looking for two periods after the first * token. We also check for the REVERSE * keyword, which means it must be an * integer loop. */ if (tok == K_REVERSE) reverse = true; else plpgsql_push_back_token(tok); /* * Read tokens until we see either a ".." * or a LOOP. The text we read may not * necessarily be a well-formed SQL * statement, so we need to invoke * read_sql_construct directly. */ expr1 = read_sql_construct(K_DOTDOT, K_LOOP, "LOOP", "SELECT ", true, false, &tok); if (tok == K_DOTDOT) { /* Saw "..", so it must be an integer loop */ PLpgSQL_expr *expr2; PLpgSQL_expr *expr_by; PLpgSQL_var *fvar; PLpgSQL_stmt_fori *new; char *varname; /* Check first expression is well-formed */ check_sql_expr(expr1->query); /* Read and check the second one */ expr2 = read_sql_construct(K_LOOP, K_BY, "LOOP", "SELECT ", true, true, &tok); /* Get the BY clause if any */ if (tok == K_BY) expr_by = plpgsql_read_expression(K_LOOP, "LOOP"); else expr_by = NULL; /* Should have had a single variable name */ plpgsql_error_lineno = $2.lineno; if ($2.scalar && $2.row) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("integer FOR loop must have just one target variable"))); /* create loop's private variable */ plpgsql_convert_ident($2.name, &varname, 1); fvar = (PLpgSQL_var *) plpgsql_build_variable(varname, $2.lineno, plpgsql_build_datatype(INT4OID, -1), true); new = palloc0(sizeof(PLpgSQL_stmt_fori)); new->cmd_type = PLPGSQL_STMT_FORI; new->lineno = $1; new->var = fvar; new->reverse = reverse; new->lower = expr1; new->upper = expr2; new->step = expr_by; $$ = (PLpgSQL_stmt *) new; } else { /* * No "..", so it must be a query loop. We've prefixed an * extra SELECT to the query text, so we need to remove that * before performing syntax checking. */ char *tmp_query; PLpgSQL_stmt_fors *new; if (reverse) yyerror("cannot specify REVERSE in query FOR loop"); Assert(strncmp(expr1->query, "SELECT ", 7) == 0); tmp_query = pstrdup(expr1->query + 7); pfree(expr1->query); expr1->query = tmp_query; check_sql_expr(expr1->query); new = palloc0(sizeof(PLpgSQL_stmt_fors)); new->cmd_type = PLPGSQL_STMT_FORS; new->lineno = $1; if ($2.rec) { new->rec = $2.rec; check_assignable((PLpgSQL_datum *) new->rec); } else if ($2.row) { new->row = $2.row; check_assignable((PLpgSQL_datum *) new->row); } else if ($2.scalar) { /* convert single scalar to list */ new->row = make_scalar_list1($2.name, $2.scalar, $2.lineno); /* no need for check_assignable */ } else { plpgsql_error_lineno = $2.lineno; yyerror("loop variable of loop over rows must be a record or row variable or list of scalar variables"); } new->query = expr1; $$ = (PLpgSQL_stmt *) new; } } } ;/* * Processing the for_variable is tricky because we don't yet know if the * FOR is an integer FOR loop or a loop over query results. In the former * case, the variable is just a name that we must instantiate as a loop * local variable, regardless of any other definition it might have. * Therefore, we always save the actual identifier into $$.name where it * can be used for that case. We also save the outer-variable definition, * if any, because that's what we need for the loop-over-query case. Note * that we must NOT apply check_assignable() or any other semantic check * until we know what's what. * * However, if we see a comma-separated list of names, we know that it * can't be an integer FOR loop and so it's OK to check the variables * immediately. In particular, for T_WORD followed by comma, we should * complain that the name is not known rather than say it's a syntax error. * Note that the non-error result of this case sets *both* $$.scalar and * $$.row; see the for_control production. */for_variable : T_SCALAR { int tok; $$.name = pstrdup(yytext); $$.lineno = plpgsql_scanner_lineno(); $$.scalar = yylval.scalar; $$.rec = NULL; $$.row = NULL; /* check for comma-separated list */ tok = yylex(); plpgsql_push_back_token(tok); if (tok == ',') $$.row = read_into_scalar_list($$.name, $$.scalar); } | T_WORD { int tok; $$.name = pstrdup(yytext); $$.lineno = plpgsql_scanner_lineno(); $$.scalar = NULL; $$.rec = NULL; $$.row = NULL; /* check for comma-separated list */ tok = yylex(); plpgsql_push_back_token(tok); if (tok == ',') { plpgsql_error_lineno = $$.lineno; ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("\"%s\" is not a scalar variable", $$.name))); } } | T_RECORD { $$.name = pstrdup(yytext); $$.lineno = plpgsql_scanner_lineno(); $$.scalar = NULL; $$.rec = yylval.rec; $$.row = NULL; } | T_ROW { $$.name = pstrdup(yytext); $$.lineno = plpgsql_scanner_lineno(); $$.scalar = NULL; $$.row = yylval.row; $$.rec = NULL; } ;stmt_exit : exit_type lno opt_label opt_exitcond { PLpgSQL_stmt_exit *new; new = palloc0(sizeof(PLpgSQL_stmt_exit)); new->cmd_type = PLPGSQL_STMT_EXIT; new->is_exit = $1; new->lineno = $2; new->label = $3; new->cond = $4; $$ = (PLpgSQL_stmt *)new; } ;exit_type : K_EXIT { $$ = true; } | K_CONTINUE { $$ = false; } ;stmt_return : K_RETURN lno { int tok; tok = yylex(); if (tok == 0) yyerror("unexpected end of function definition"); /* * To avoid making NEXT and QUERY effectively be * reserved words within plpgsql, recognize them * via yytext. */ if (pg_strcasecmp(yytext, "next") == 0) { $$ = make_return_next_stmt($2); } else if (pg_strcasecmp(yytext, "query") == 0) { $$ = make_return_query_stmt($2); } else { plpgsql_push_back_token(tok); $$ = make_return_stmt($2); } } ;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; } ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -