📄 gram.y
字号:
proc_stmts : proc_stmts proc_stmt { if ($2 == NULL) $$ = $1; else $$ = lappend($1, $2); } | proc_stmt { if ($1 == NULL) $$ = NULL; else $$ = list_make1($1); } ;proc_stmt : pl_block ';' { $$ = $1; } | stmt_assign { $$ = $1; } | stmt_if { $$ = $1; } | stmt_loop { $$ = $1; } | stmt_while { $$ = $1; } | stmt_for { $$ = $1; } | stmt_select { $$ = $1; } | stmt_exit { $$ = $1; } | stmt_return { $$ = $1; } | stmt_return_next { $$ = $1; } | stmt_raise { $$ = $1; } | stmt_execsql { $$ = $1; } | stmt_dynexecute { $$ = $1; } | stmt_perform { $$ = $1; } | stmt_getdiag { $$ = $1; } | stmt_open { $$ = $1; } | stmt_fetch { $$ = $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_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; else if ($2.row) new->row = $2.row; else { plpgsql_error_lineno = $1; yyerror("loop variable of loop over rows must be a record or row variable"); } 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_var *fvar; PLpgSQL_stmt_fori *new; /* First expression is well-formed */ check_sql_expr(expr1->query); expr2 = plpgsql_read_expression(K_LOOP, "LOOP"); fvar = (PLpgSQL_var *) plpgsql_build_variable($2.name, $2.lineno, plpgsql_build_datatype(INT4OID, -1), true); /* put the for-variable into the local block */ plpgsql_add_initdatums(NULL); 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; $$ = (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; else if ($2.row) new->row = $2.row; else { plpgsql_error_lineno = $1; yyerror("loop variable of loop over rows must be record or row variable"); } new->query = expr1; $$ = (PLpgSQL_stmt *) new; } } } ;for_variable : T_SCALAR { char *name; plpgsql_convert_ident(yytext, &name, 1); $$.name = name; $$.lineno = plpgsql_scanner_lineno(); $$.rec = NULL; $$.row = NULL; } | T_WORD { char *name; plpgsql_convert_ident(yytext, &name, 1); $$.name = name; $$.lineno = plpgsql_scanner_lineno(); $$.rec = NULL; $$.row = NULL; } | T_RECORD { char *name; plpgsql_convert_ident(yytext, &name, 1); $$.name = name; $$.lineno = plpgsql_scanner_lineno(); $$.rec = yylval.rec; $$.row = NULL; } | T_ROW { char *name; plpgsql_convert_ident(yytext, &name, 1); $$.name = name; $$.lineno = plpgsql_scanner_lineno(); $$.row = yylval.row; $$.rec = NULL; } ;stmt_select : K_SELECT lno { $$ = make_select_stmt(); $$->lineno = $2; } ;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 { PLpgSQL_stmt_return *new; new = palloc0(sizeof(PLpgSQL_stmt_return)); new->cmd_type = PLPGSQL_STMT_RETURN; new->lineno = $2; new->expr = NULL; new->retvarno = -1; if (plpgsql_curr_compile->fn_retset) { if (yylex() != ';') yyerror("RETURN cannot have a parameter in function returning set; use RETURN NEXT"); } else if (plpgsql_curr_compile->out_param_varno >= 0) { if (yylex() != ';') yyerror("RETURN cannot have a parameter in function with OUT parameters"); new->retvarno = plpgsql_curr_compile->out_param_varno; } else if (plpgsql_curr_compile->fn_rettype == VOIDOID) { if (yylex() != ';') yyerror("RETURN cannot have a parameter in function returning void"); } else if (plpgsql_curr_compile->fn_retistuple) { switch (yylex()) { case K_NULL: /* we allow this to support RETURN NULL in triggers */ break; case T_ROW: new->retvarno = yylval.row->rowno; break; case T_RECORD: new->retvarno = yylval.rec->recno; break; default: yyerror("RETURN must specify a record or row variable in function returning tuple"); break; } if (yylex() != ';') yyerror("RETURN must specify a record or row variable in function returning tuple"); } else { /* * Note that a well-formed expression is * _required_ here; anything else is a * compile-time error. */ new->expr = plpgsql_read_expression(';', ";"); } $$ = (PLpgSQL_stmt *)new; } ;stmt_return_next: K_RETURN_NEXT lno { PLpgSQL_stmt_return_next *new;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -