%{ /********************************************************************** * gram.y - Parser for the PL/pgSQL * procedural language * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.16 2001/02/19 19:49:53 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * * The author hereby grants permission to use, copy, modify, * distribute, and license this software and its documentation * for any purpose, provided that existing copyright notices are * retained in all copies and that this notice is included * verbatim in any distributions. No written agreement, license, * or royalty fee is required for any of the authorized uses. * Modifications to this software may be copyrighted by their * author and need not follow the licensing terms described * here, provided that the new terms are clearly indicated on * the first page of each file where they apply. * * IN NO EVENT SHALL THE AUTHOR OR DISTRIBUTORS BE LIABLE TO ANY * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN * IF THE AUTHOR HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * THE AUTHOR AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON * AN "AS IS" BASIS, AND THE AUTHOR AND DISTRIBUTORS HAVE NO * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, * ENHANCEMENTS, OR MODIFICATIONS. * **********************************************************************/ #include #include #include "plpgsql.h" #ifdef YYBISON #include "pl_scan.c" /* GNU bison wants it here */ #endif static PLpgSQL_expr *read_sqlstmt(int until, char *s, char *sqlstart); static PLpgSQL_stmt *make_select_stmt(void); static PLpgSQL_expr *make_tupret_expr(PLpgSQL_row *row); %} %union { int32 ival; char *str; struct { char *name; int lineno; } varname; struct { int nalloc; int nused; int *nums; } intlist; struct { int nalloc; int nused; PLpgSQL_diag_item *dtitems; } dtlist; struct { int reverse; PLpgSQL_expr *expr; } forilow; struct { char *label; int n_initvars; int *initvarnos; } declhdr; PLpgSQL_type *dtype; PLpgSQL_var *var; PLpgSQL_row *row; PLpgSQL_rec *rec; PLpgSQL_recfield *recfield; PLpgSQL_trigarg *trigarg; PLpgSQL_expr *expr; PLpgSQL_stmt *stmt; PLpgSQL_stmts *stmts; PLpgSQL_stmt_block *program; PLpgSQL_nsitem *nsitem; } %type decl_sect %type decl_varname %type decl_renname %type decl_const, decl_notnull, decl_atttypmod, decl_atttypmodval %type decl_defval %type decl_datatype, decl_dtypename %type decl_rowtype %type decl_aliasitem %type decl_stmts, decl_stmt %type expr_until_semi, expr_until_then, expr_until_loop %type opt_exitcond %type assign_var %type fori_var %type fori_varname %type fori_lower %type fors_target %type opt_lblname, opt_label %type opt_exitlabel %type execsql_start %type proc_sect, proc_stmts, stmt_else, loop_body %type proc_stmt, pl_block %type stmt_assign, stmt_if, stmt_loop, stmt_while, stmt_exit %type stmt_return, stmt_raise, stmt_execsql, stmt_fori %type stmt_fors, stmt_select, stmt_perform %type stmt_dynexecute, stmt_dynfors, stmt_getdiag %type raise_params %type raise_level, raise_param %type raise_msg %type getdiag_list %type getdiag_item, getdiag_target %type lno /* * Keyword tokens */ %token K_ALIAS %token K_ASSIGN %token K_BEGIN %token K_CONSTANT %token K_DEBUG %token K_DECLARE %token K_DEFAULT %token K_DIAGNOSTICS %token K_DOTDOT %token K_ELSE %token K_END %token K_EXCEPTION %token K_EXECUTE %token K_EXIT %token K_FOR %token K_FROM %token K_GET %token K_IF %token K_IN %token K_INTO %token K_LOOP %token K_NOT %token K_NOTICE %token K_NULL %token K_PERFORM %token K_ROW_COUNT %token K_RAISE %token K_RECORD %token K_RENAME %token K_RESULT_OID %token K_RETURN %token K_REVERSE %token K_SELECT %token K_THEN %token K_TO %token K_TYPE %token K_WHEN %token K_WHILE /* * Other tokens */ %token T_FUNCTION %token T_TRIGGER %token T_CHAR %token T_BPCHAR %token T_VARCHAR %token T_LABEL %token T_STRING %token T_VARIABLE %token T_ROW %token T_ROWTYPE %token T_RECORD %token T_RECFIELD %token T_TGARGV %token T_DTYPE %token T_WORD %token T_NUMBER %token T_ERROR %token O_OPTION %token O_DUMP %% pl_function : T_FUNCTION comp_optsect pl_block { yylval.program = (PLpgSQL_stmt_block *)$3; } | T_TRIGGER comp_optsect pl_block { yylval.program = (PLpgSQL_stmt_block *)$3; } ; comp_optsect : | comp_options ; comp_options : comp_options comp_option | comp_option ; comp_option : O_OPTION O_DUMP { plpgsql_DumpExecTree = 1; } ; pl_block : decl_sect K_BEGIN lno proc_sect K_END ';' { PLpgSQL_stmt_block *new; new = malloc(sizeof(PLpgSQL_stmt_block)); memset(new, 0, sizeof(PLpgSQL_stmt_block)); new->cmd_type = PLPGSQL_STMT_BLOCK; new->lineno = $3; new->label = $1.label; new->n_initvars = $1.n_initvars; new->initvarnos = $1.initvarnos; new->body = $4; plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } ; decl_sect : opt_label { plpgsql_ns_setlocal(false); $$.label = $1; $$.n_initvars = 0; $$.initvarnos = NULL; plpgsql_add_initdatums(NULL); } | opt_label decl_start { plpgsql_ns_setlocal(false); $$.label = $1; $$.n_initvars = 0; $$.initvarnos = NULL; plpgsql_add_initdatums(NULL); } | opt_label decl_start decl_stmts { plpgsql_ns_setlocal(false); if ($3 != NULL) $$.label = $3; else $$.label = $1; $$.n_initvars = plpgsql_add_initdatums(&($$.initvarnos)); } ; decl_start : K_DECLARE { plpgsql_ns_setlocal(true); } ; decl_stmts : decl_stmts decl_stmt { $$ = $2; } | decl_stmt { $$ = $1; } ; decl_stmt : '<' '<' opt_lblname '>' '>' { $$ = $3; } | K_DECLARE { $$ = NULL; } | decl_statement { $$ = NULL; } ; decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval { PLpgSQL_var *new; new = malloc(sizeof(PLpgSQL_var)); new->dtype = PLPGSQL_DTYPE_VAR; new->refname = $1.name; new->lineno = $1.lineno; new->datatype = $3; new->isconst = $2; new->notnull = $4; new->default_val = $5; plpgsql_adddatum((PLpgSQL_datum *)new); plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, new->varno, $1.name); } | decl_varname K_RECORD ';' { PLpgSQL_rec *new; new = malloc(sizeof(PLpgSQL_var)); new->dtype = PLPGSQL_DTYPE_REC; new->refname = $1.name; new->lineno = $1.lineno; plpgsql_adddatum((PLpgSQL_datum *)new); plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, new->recno, $1.name); } | decl_varname decl_rowtype ';' { $2->dtype = PLPGSQL_DTYPE_ROW; $2->refname = $1.name; $2->lineno = $1.lineno; plpgsql_adddatum((PLpgSQL_datum *)$2); plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW, $2->rowno, $1.name); } | decl_varname K_ALIAS K_FOR decl_aliasitem ';' { plpgsql_ns_additem($4->itemtype, $4->itemno, $1.name); } | K_RENAME decl_renname K_TO decl_renname ';' { plpgsql_ns_rename($2, $4); } ; decl_aliasitem : T_WORD { PLpgSQL_nsitem *nsi; char *name; plpgsql_ns_setlocal(false); name = plpgsql_tolower(yytext); if (name[0] != '$') elog(ERROR, "can only alias positional parameters"); nsi = plpgsql_ns_lookup(name, NULL); if (nsi == NULL) elog(ERROR, "function has no parameter %s", name); plpgsql_ns_setlocal(true); $$ = nsi; } ; decl_rowtype : T_ROW { $$ = yylval.row; } ; decl_varname : T_WORD { /* name should be malloc'd for use as varname */ $$.name = strdup(plpgsql_tolower(yytext)); $$.lineno = yylineno; } ; decl_renname : T_WORD { /* the result must be palloc'd, see plpgsql_ns_rename */ $$ = plpgsql_tolower(yytext); } ; decl_const : { $$ = 0; } | K_CONSTANT { $$ = 1; } ; decl_datatype : decl_dtypename { $$ = $1; } ; decl_dtypename : T_DTYPE { $$ = yylval.dtype; } | T_CHAR decl_atttypmod { if ($2 < 0) { plpgsql_parse_word("char"); $$ = yylval.dtype; } else { plpgsql_parse_word("bpchar"); $$ = yylval.dtype; $$->atttypmod = $2; } } | T_VARCHAR decl_atttypmod { plpgsql_parse_word("varchar"); $$ = yylval.dtype; $$->atttypmod = $2; } | T_BPCHAR '(' decl_atttypmodval ')' { plpgsql_parse_word("bpchar"); $$ = yylval.dtype; $$->atttypmod = $3; } ; decl_atttypmod : { $$ = -1; } | '(' decl_atttypmodval ')' { $$ = $2; } ; decl_atttypmodval : T_NUMBER { $$ = pg_atoi(yytext, sizeof(int16), '\0') + VARHDRSZ; } ; decl_notnull : { $$ = 0; } | K_NOT K_NULL { $$ = 1; } ; decl_defval : ';' { $$ = NULL; } | decl_defkey { int tok; int lno; PLpgSQL_dstring ds; PLpgSQL_expr *expr; lno = yylineno; expr = malloc(sizeof(PLpgSQL_expr)); plpgsql_dstring_init(&ds); plpgsql_dstring_append(&ds, "SELECT "); expr->dtype = PLPGSQL_DTYPE_EXPR; expr->plan = NULL; expr->nparams = 0; tok = yylex(); switch (tok) { case 0: plpgsql_error_lineno = lno; plpgsql_comperrinfo(); elog(ERROR, "unexpected end of file"); case K_NULL: if (yylex() != ';') { plpgsql_error_lineno = lno; plpgsql_comperrinfo(); elog(ERROR, "expectec ; after NULL"); } free(expr); plpgsql_dstring_free(&ds); $$ = NULL; break; default: plpgsql_dstring_append(&ds, yytext); while ((tok = yylex()) != ';') { if (tok == 0) { plpgsql_error_lineno = lno; plpgsql_comperrinfo(); elog(ERROR, "unterminated default value"); } if (plpgsql_SpaceScanned) plpgsql_dstring_append(&ds, " "); plpgsql_dstring_append(&ds, yytext); } expr->query = strdup(plpgsql_dstring_get(&ds)); plpgsql_dstring_free(&ds); $$ = expr; break; } } ; decl_defkey : K_ASSIGN | K_DEFAULT proc_sect : { PLpgSQL_stmts *new; new = malloc(sizeof(PLpgSQL_stmts)); memset(new, 0, sizeof(PLpgSQL_stmts)); $$ = new; } | proc_stmts { $$ = $1; } ; proc_stmts : proc_stmts proc_stmt { if ($1->stmts_used == $1->stmts_alloc) { $1->stmts_alloc *= 2; $1->stmts = realloc($1->stmts, sizeof(PLpgSQL_stmt *) * $1->stmts_alloc); } $1->stmts[$1->stmts_used++] = (struct PLpgSQL_stmt *)$2; $$ = $1; } | proc_stmt { PLpgSQL_stmts *new; new = malloc(sizeof(PLpgSQL_stmts)); memset(new, 0, sizeof(PLpgSQL_stmts)); new->stmts_alloc = 64; new->stmts_used = 1; new->stmts = malloc(sizeof(PLpgSQL_stmt *) * new->stmts_alloc); new->stmts[0] = (struct PLpgSQL_stmt *)$1; $$ = new; } ; proc_stmt : pl_block { $$ = $1; } | stmt_assign { $$ = $1; } | stmt_if { $$ = $1; } | stmt_loop { $$ = $1; } | stmt_while { $$ = $1; } | stmt_fori { $$ = $1; } | stmt_fors { $$ = $1; } | stmt_select { $$ = $1; } | stmt_exit { $$ = $1; } | stmt_return { $$ = $1; } | stmt_raise { $$ = $1; } | stmt_execsql { $$ = $1; } | stmt_dynexecute { $$ = $1; } | stmt_dynfors { $$ = $1; } | stmt_perform { $$ = $1; } | stmt_getdiag { $$ = $1; } ; stmt_perform : K_PERFORM lno expr_until_semi { PLpgSQL_stmt_assign *new; new = malloc(sizeof(PLpgSQL_stmt_assign)); memset(new, 0, sizeof(PLpgSQL_stmt_assign)); new->cmd_type = PLPGSQL_STMT_ASSIGN; new->lineno = $2; new->varno = -1; new->expr = $3; $$ = (PLpgSQL_stmt *)new; } ; stmt_assign : assign_var lno K_ASSIGN expr_until_semi { PLpgSQL_stmt_assign *new; new = malloc(sizeof(PLpgSQL_stmt_assign)); memset(new, 0, 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 = malloc(sizeof(PLpgSQL_stmt_getdiag)); memset(new, 0, sizeof(PLpgSQL_stmt_getdiag)); new->cmd_type = PLPGSQL_STMT_GETDIAG; new->lineno = $3; new->ndtitems = $4.nused; new->dtitems = malloc(sizeof(PLpgSQL_diag_item) * $4.nused); memcpy(new->dtitems, $4.dtitems, sizeof(PLpgSQL_diag_item) * $4.nused); $$ = (PLpgSQL_stmt *)new; } ; getdiag_list : getdiag_list ',' getdiag_target K_ASSIGN getdiag_item { if ($1.nused == $1.nalloc) { $1.nalloc *= 2; $1.dtitems = repalloc($1.dtitems, sizeof(PLpgSQL_diag_item) * $1.nalloc); } $1.dtitems[$1.nused].target = $3; $1.dtitems[$1.nused].item = $5; $1.nused++; $$.nalloc = $1.nalloc; $$.nused = $1.nused; $$.dtitems = $1.dtitems; } | getdiag_target K_ASSIGN getdiag_item { $$.nalloc = 1; $$.nused = 1; $$.dtitems = palloc(sizeof(PLpgSQL_diag_item) * $$.nalloc); $$.dtitems[0].target = $1; $$.dtitems[0].item = $3; } ; getdiag_item : K_ROW_COUNT { $$ = PLPGSQL_GETDIAG_ROW_COUNT; } | K_RESULT_OID { $$ = PLPGSQL_GETDIAG_RESULT_OID; } ; getdiag_target : T_VARIABLE { if (yylval.var->isconst) { plpgsql_comperrinfo(); elog(ERROR, "%s is declared CONSTANT; can not receive diagnostics", yylval.var->refname); } $$ = yylval.var->varno; } | T_RECFIELD { $$ = yylval.recfield->rfno; } ; assign_var : T_VARIABLE { if (yylval.var->isconst) { plpgsql_comperrinfo(); elog(ERROR, "%s is declared CONSTANT", yylval.var->refname); } $$ = yylval.var->varno; } | T_RECFIELD { $$ = yylval.recfield->rfno; } ; stmt_if : K_IF lno expr_until_then proc_sect stmt_else K_END K_IF ';' { PLpgSQL_stmt_if *new; new = malloc(sizeof(PLpgSQL_stmt_if)); memset(new, 0, 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 : { PLpgSQL_stmts *new; new = malloc(sizeof(PLpgSQL_stmts)); memset(new, 0, sizeof(PLpgSQL_stmts)); $$ = new; } | K_ELSE proc_sect { $$ = $2; } ; stmt_loop : opt_label K_LOOP lno loop_body { PLpgSQL_stmt_loop *new; new = malloc(sizeof(PLpgSQL_stmt_loop)); memset(new, 0, sizeof(PLpgSQL_stmt_loop)); new->cmd_type = PLPGSQL_STMT_LOOP; new->lineno = $3; new->label = $1; new->body = $4; plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } ; stmt_while : opt_label K_WHILE lno expr_until_loop loop_body { PLpgSQL_stmt_while *new; new = malloc(sizeof(PLpgSQL_stmt_while)); memset(new, 0, sizeof(PLpgSQL_stmt_while)); new->cmd_type = PLPGSQL_STMT_WHILE; new->lineno = $3; new->label = $1; new->cond = $4; new->body = $5; plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } ; stmt_fori : opt_label K_FOR lno fori_var K_IN fori_lower expr_until_loop loop_body { PLpgSQL_stmt_fori *new; new = malloc(sizeof(PLpgSQL_stmt_fori)); memset(new, 0, sizeof(PLpgSQL_stmt_fori)); new->cmd_type = PLPGSQL_STMT_FORI; new->lineno = $3; new->label = $1; new->var = $4; new->reverse = $6.reverse; new->lower = $6.expr; new->upper = $7; new->body = $8; plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } ; fori_var : fori_varname { PLpgSQL_var *new; new = malloc(sizeof(PLpgSQL_var)); new->dtype = PLPGSQL_DTYPE_VAR; new->refname = $1.name; new->lineno = $1.lineno; plpgsql_parse_word("integer"); new->datatype = yylval.dtype; new->isconst = false; new->notnull = false; new->default_val = NULL; plpgsql_adddatum((PLpgSQL_datum *)new); plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, new->varno, $1.name); plpgsql_add_initdatums(NULL); $$ = new; } ; fori_varname : T_VARIABLE { $$.name = strdup(yytext); $$.lineno = yylineno; } | T_WORD { $$.name = strdup(yytext); $$.lineno = yylineno; } ; fori_lower : { int tok; int lno; PLpgSQL_dstring ds; int nparams = 0; int params[1024]; char buf[32]; PLpgSQL_expr *expr; int firsttok = 1; lno = yylineno; plpgsql_dstring_init(&ds); plpgsql_dstring_append(&ds, "SELECT "); $$.reverse = 0; while((tok = yylex()) != K_DOTDOT) { if (firsttok) { firsttok = 0; if (tok == K_REVERSE) { $$.reverse = 1; continue; } } if (tok == ';') break; if (plpgsql_SpaceScanned) plpgsql_dstring_append(&ds, " "); switch (tok) { case T_VARIABLE: params[nparams] = yylval.var->varno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; case T_RECFIELD: params[nparams] = yylval.recfield->rfno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; case T_TGARGV: params[nparams] = yylval.trigarg->dno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; default: if (tok == 0) { plpgsql_error_lineno = lno; plpgsql_comperrinfo(); elog(ERROR, "missing .. to terminate lower bound of for loop"); } plpgsql_dstring_append(&ds, yytext); break; } } expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - 1); expr->dtype = PLPGSQL_DTYPE_EXPR; expr->query = strdup(plpgsql_dstring_get(&ds)); expr->plan = NULL; expr->nparams = nparams; while(nparams-- > 0) expr->params[nparams] = params[nparams]; plpgsql_dstring_free(&ds); $$.expr = expr; } stmt_fors : opt_label K_FOR lno fors_target K_IN K_SELECT expr_until_loop loop_body { PLpgSQL_stmt_fors *new; new = malloc(sizeof(PLpgSQL_stmt_fors)); memset(new, 0, sizeof(PLpgSQL_stmt_fors)); new->cmd_type = PLPGSQL_STMT_FORS; new->lineno = $3; new->label = $1; switch ($4->dtype) { case PLPGSQL_DTYPE_REC: new->rec = $4; break; case PLPGSQL_DTYPE_ROW: new->row = (PLpgSQL_row *)$4; break; default: plpgsql_comperrinfo(); elog(ERROR, "unknown dtype %d in stmt_fors", $4->dtype); } new->query = $7; new->body = $8; plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } stmt_dynfors : opt_label K_FOR lno fors_target K_IN K_EXECUTE expr_until_loop loop_body { PLpgSQL_stmt_dynfors *new; new = malloc(sizeof(PLpgSQL_stmt_dynfors)); memset(new, 0, sizeof(PLpgSQL_stmt_dynfors)); new->cmd_type = PLPGSQL_STMT_DYNFORS; new->lineno = $3; new->label = $1; switch ($4->dtype) { case PLPGSQL_DTYPE_REC: new->rec = $4; break; case PLPGSQL_DTYPE_ROW: new->row = (PLpgSQL_row *)$4; break; default: plpgsql_comperrinfo(); elog(ERROR, "unknown dtype %d in stmt_dynfors", $4->dtype); } new->query = $7; new->body = $8; plpgsql_ns_pop(); $$ = (PLpgSQL_stmt *)new; } fors_target : T_RECORD { $$ = yylval.rec; } | T_ROW { $$ = (PLpgSQL_rec *)(yylval.row); } ; stmt_select : K_SELECT lno { $$ = make_select_stmt(); $$->lineno = $2; } ; stmt_exit : K_EXIT lno opt_exitlabel opt_exitcond { PLpgSQL_stmt_exit *new; new = malloc(sizeof(PLpgSQL_stmt_exit)); memset(new, 0, sizeof(PLpgSQL_stmt_exit)); new->cmd_type = PLPGSQL_STMT_EXIT; new->lineno = $2; new->label = $3; new->cond = $4; $$ = (PLpgSQL_stmt *)new; } ; stmt_return : K_RETURN lno { PLpgSQL_stmt_return *new; PLpgSQL_expr *expr = NULL; int tok; new = malloc(sizeof(PLpgSQL_stmt_return)); memset(new, 0, sizeof(PLpgSQL_stmt_return)); if (plpgsql_curr_compile->fn_retistuple) { new->retistuple = true; new->retrecno = -1; switch (tok = yylex()) { case K_NULL: expr = NULL; break; case T_ROW: expr = make_tupret_expr(yylval.row); break; case T_RECORD: new->retrecno = yylval.rec->recno; expr = NULL; break; default: yyerror("return type mismatch in function returning table row"); break; } if (yylex() != ';') yyerror("expected ';'"); } else { new->retistuple = false; expr = plpgsql_read_expression(';', ";"); } new->cmd_type = PLPGSQL_STMT_RETURN; new->lineno = $2; new->expr = expr; $$ = (PLpgSQL_stmt *)new; } ; stmt_raise : K_RAISE lno raise_level raise_msg raise_params ';' { PLpgSQL_stmt_raise *new; new = malloc(sizeof(PLpgSQL_stmt_raise)); new->cmd_type = PLPGSQL_STMT_RAISE; new->lineno = $2; new->elog_level = $3; new->message = $4; new->nparams = $5.nused; new->params = malloc(sizeof(int) * $5.nused); memcpy(new->params, $5.nums, sizeof(int) * $5.nused); $$ = (PLpgSQL_stmt *)new; } | K_RAISE lno raise_level raise_msg ';' { PLpgSQL_stmt_raise *new; new = malloc(sizeof(PLpgSQL_stmt_raise)); new->cmd_type = PLPGSQL_STMT_RAISE; new->lineno = $2; new->elog_level = $3; new->message = $4; new->nparams = 0; new->params = NULL; $$ = (PLpgSQL_stmt *)new; } ; raise_msg : T_STRING { $$ = strdup(yytext); } ; raise_level : K_EXCEPTION { $$ = ERROR; } | K_NOTICE { $$ = NOTICE; } | K_DEBUG { $$ = DEBUG; } ; raise_params : raise_params raise_param { if ($1.nused == $1.nalloc) { $1.nalloc *= 2; $1.nums = repalloc($1.nums, sizeof(int) * $1.nalloc); } $1.nums[$1.nused++] = $2; $$.nalloc = $1.nalloc; $$.nused = $1.nused; $$.nums = $1.nums; } | raise_param { $$.nalloc = 1; $$.nused = 1; $$.nums = palloc(sizeof(int) * $$.nalloc); $$.nums[0] = $1; } ; raise_param : ',' T_VARIABLE { $$ = yylval.var->varno; } | ',' T_RECFIELD { $$ = yylval.recfield->rfno; } | ',' T_TGARGV { $$ = yylval.trigarg->dno; } ; loop_body : proc_sect K_END K_LOOP ';' { $$ = $1; } ; stmt_execsql : execsql_start lno { PLpgSQL_stmt_execsql *new; new = malloc(sizeof(PLpgSQL_stmt_execsql)); new->cmd_type = PLPGSQL_STMT_EXECSQL; new->lineno = $2; new->sqlstmt = read_sqlstmt(';', ";", $1); $$ = (PLpgSQL_stmt *)new; } ; stmt_dynexecute : K_EXECUTE lno expr_until_semi { PLpgSQL_stmt_dynexecute *new; new = malloc(sizeof(PLpgSQL_stmt_dynexecute)); new->cmd_type = PLPGSQL_STMT_DYNEXECUTE; new->lineno = $2; new->query = $3; $$ = (PLpgSQL_stmt *)new; } ; execsql_start : T_WORD { $$ = strdup(yytext); } | T_ERROR { $$ = strdup(yytext); } ; expr_until_semi : { $$ = plpgsql_read_expression(';', ";"); } ; expr_until_then : { $$ = plpgsql_read_expression(K_THEN, "THEN"); } ; expr_until_loop : { $$ = plpgsql_read_expression(K_LOOP, "LOOP"); } ; opt_label : { plpgsql_ns_push(NULL); $$ = NULL; } | '<' '<' opt_lblname '>' '>' { plpgsql_ns_push($3); $$ = $3; } ; opt_exitlabel : { $$ = NULL; } | T_LABEL { $$ = strdup(yytext); } ; opt_exitcond : ';' { $$ = NULL; } | K_WHEN expr_until_semi { $$ = $2; } ; opt_lblname : T_WORD { $$ = strdup(yytext); } ; lno : { plpgsql_error_lineno = yylineno; $$ = yylineno; } ; %% #ifndef YYBISON #include "pl_scan.c" /* BSD yacc wants it here */ #endif PLpgSQL_expr * plpgsql_read_expression (int until, char *s) { return read_sqlstmt(until, s, "SELECT "); } static PLpgSQL_expr * read_sqlstmt (int until, char *s, char *sqlstart) { int tok; int lno; PLpgSQL_dstring ds; int nparams = 0; int params[1024]; char buf[32]; PLpgSQL_expr *expr; lno = yylineno; plpgsql_dstring_init(&ds); plpgsql_dstring_append(&ds, sqlstart); while((tok = yylex()) != until) { if (tok == ';') break; if (plpgsql_SpaceScanned) plpgsql_dstring_append(&ds, " "); switch (tok) { case T_VARIABLE: params[nparams] = yylval.var->varno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; case T_RECFIELD: params[nparams] = yylval.recfield->rfno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; case T_TGARGV: params[nparams] = yylval.trigarg->dno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; default: if (tok == 0) { plpgsql_error_lineno = lno; plpgsql_comperrinfo(); elog(ERROR, "missing %s at end of SQL statement", s); } plpgsql_dstring_append(&ds, yytext); break; } } expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - 1); expr->dtype = PLPGSQL_DTYPE_EXPR; expr->query = strdup(plpgsql_dstring_get(&ds)); expr->plan = NULL; expr->nparams = nparams; while(nparams-- > 0) expr->params[nparams] = params[nparams]; plpgsql_dstring_free(&ds); return expr; } static PLpgSQL_stmt * make_select_stmt() { int tok; int lno; PLpgSQL_dstring ds; int nparams = 0; int params[1024]; char buf[32]; PLpgSQL_expr *expr; PLpgSQL_row *row = NULL; PLpgSQL_rec *rec = NULL; PLpgSQL_stmt_select *select; int have_nexttok = 0; lno = yylineno; plpgsql_dstring_init(&ds); plpgsql_dstring_append(&ds, "SELECT "); while((tok = yylex()) != K_INTO) { if (tok == ';') { PLpgSQL_stmt_execsql *execsql; expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - 1); expr->dtype = PLPGSQL_DTYPE_EXPR; expr->query = strdup(plpgsql_dstring_get(&ds)); expr->plan = NULL; expr->nparams = nparams; while(nparams-- > 0) expr->params[nparams] = params[nparams]; plpgsql_dstring_free(&ds); execsql = malloc(sizeof(PLpgSQL_stmt_execsql)); execsql->cmd_type = PLPGSQL_STMT_EXECSQL; execsql->sqlstmt = expr; return (PLpgSQL_stmt *)execsql; } if (plpgsql_SpaceScanned) plpgsql_dstring_append(&ds, " "); switch (tok) { case T_VARIABLE: params[nparams] = yylval.var->varno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; case T_RECFIELD: params[nparams] = yylval.recfield->rfno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; case T_TGARGV: params[nparams] = yylval.trigarg->dno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; default: if (tok == 0) { plpgsql_error_lineno = yylineno; plpgsql_comperrinfo(); elog(ERROR, "unexpected end of file"); } plpgsql_dstring_append(&ds, yytext); break; } } tok = yylex(); switch (tok) { case T_ROW: row = yylval.row; break; case T_RECORD: rec = yylval.rec; break; case T_VARIABLE: case T_RECFIELD: { PLpgSQL_var *var; PLpgSQL_recfield *recfield; int nfields = 1; char *fieldnames[1024]; int varnos[1024]; switch (tok) { case T_VARIABLE: var = yylval.var; fieldnames[0] = strdup(yytext); varnos[0] = var->varno; break; case T_RECFIELD: recfield = yylval.recfield; fieldnames[0] = strdup(yytext); varnos[0] = recfield->rfno; break; } while ((tok = yylex()) == ',') { tok = yylex(); switch(tok) { case T_VARIABLE: var = yylval.var; fieldnames[nfields] = strdup(yytext); varnos[nfields++] = var->varno; break; case T_RECFIELD: recfield = yylval.recfield; fieldnames[0] = strdup(yytext); varnos[0] = recfield->rfno; break; default: elog(ERROR, "plpgsql: %s is not a variable or record field", yytext); } } row = malloc(sizeof(PLpgSQL_row)); row->dtype = PLPGSQL_DTYPE_ROW; row->refname = strdup("*internal*"); row->lineno = yylineno; row->rowtypeclass = InvalidOid; row->nfields = nfields; row->fieldnames = malloc(sizeof(char *) * nfields); row->varnos = malloc(sizeof(int) * nfields); while (--nfields >= 0) { row->fieldnames[nfields] = fieldnames[nfields]; row->varnos[nfields] = varnos[nfields]; } plpgsql_adddatum((PLpgSQL_datum *)row); have_nexttok = 1; } break; default: { if (plpgsql_SpaceScanned) plpgsql_dstring_append(&ds, " "); plpgsql_dstring_append(&ds, yytext); while(1) { tok = yylex(); if (tok == ';') { PLpgSQL_stmt_execsql *execsql; expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - 1); expr->dtype = PLPGSQL_DTYPE_EXPR; expr->query = strdup(plpgsql_dstring_get(&ds)); expr->plan = NULL; expr->nparams = nparams; while(nparams-- > 0) { expr->params[nparams] = params[nparams]; } plpgsql_dstring_free(&ds); execsql = malloc(sizeof(PLpgSQL_stmt_execsql)); execsql->cmd_type = PLPGSQL_STMT_EXECSQL; execsql->sqlstmt = expr; return (PLpgSQL_stmt *)execsql; } if (plpgsql_SpaceScanned) plpgsql_dstring_append(&ds, " "); switch (tok) { case T_VARIABLE: params[nparams] = yylval.var->varno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; case T_RECFIELD: params[nparams] = yylval.recfield->rfno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; case T_TGARGV: params[nparams] = yylval.trigarg->dno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; default: if (tok == 0) { plpgsql_error_lineno = yylineno; plpgsql_comperrinfo(); elog(ERROR, "unexpected end of file"); } plpgsql_dstring_append(&ds, yytext); break; } } } } /************************************************************ * Eat up the rest of the statement after the target fields ************************************************************/ while(1) { if (!have_nexttok) { tok = yylex(); } have_nexttok = 0; if (tok == ';') { break; } if (plpgsql_SpaceScanned) plpgsql_dstring_append(&ds, " "); switch (tok) { case T_VARIABLE: params[nparams] = yylval.var->varno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; case T_RECFIELD: params[nparams] = yylval.recfield->rfno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; case T_TGARGV: params[nparams] = yylval.trigarg->dno; sprintf(buf, " $%d ", ++nparams); plpgsql_dstring_append(&ds, buf); break; default: if (tok == 0) { plpgsql_error_lineno = yylineno; plpgsql_comperrinfo(); elog(ERROR, "unexpected end of file"); } plpgsql_dstring_append(&ds, yytext); break; } } expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * (nparams - 1)); expr->dtype = PLPGSQL_DTYPE_EXPR; expr->query = strdup(plpgsql_dstring_get(&ds)); expr->plan = NULL; expr->nparams = nparams; while(nparams-- > 0) expr->params[nparams] = params[nparams]; plpgsql_dstring_free(&ds); select = malloc(sizeof(PLpgSQL_stmt_select)); memset(select, 0, sizeof(PLpgSQL_stmt_select)); select->cmd_type = PLPGSQL_STMT_SELECT; select->rec = rec; select->row = row; select->query = expr; return (PLpgSQL_stmt *)select; } static PLpgSQL_expr * make_tupret_expr(PLpgSQL_row *row) { PLpgSQL_dstring ds; PLpgSQL_expr *expr; int i; char buf[16]; expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * (row->nfields - 1)); expr->dtype = PLPGSQL_DTYPE_EXPR; plpgsql_dstring_init(&ds); plpgsql_dstring_append(&ds, "SELECT "); for (i = 0; i < row->nfields; i++) { sprintf(buf, "%s$%d", (i > 0) ? "," : "", i + 1); plpgsql_dstring_append(&ds, buf); expr->params[i] = row->varnos[i]; } expr->query = strdup(plpgsql_dstring_get(&ds)); expr->plan = NULL; expr->plan_argtypes = NULL; expr->nparams = row->nfields; plpgsql_dstring_free(&ds); return expr; }