* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.65 2005/02/13 01:25:50 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.66 2005/02/22 07:18:24 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
#include "plpgsql.h"
+#include "parser/parser.h"
-static PLpgSQL_expr *read_sql_construct(int until,
+static PLpgSQL_expr *read_sql_construct(int until,
int until2,
const char *expected,
- bool isexpression,
const char *sqlstart,
+ bool isexpression,
+ bool valid_sql,
int *endtoken);
static PLpgSQL_expr *read_sql_stmt(const char *sqlstart);
static PLpgSQL_type *read_datatype(int tok);
static PLpgSQL_stmt *make_select_stmt(void);
static PLpgSQL_stmt *make_fetch_stmt(void);
-static void check_assignable(PLpgSQL_datum *datum);
+static void check_assignable(PLpgSQL_datum *datum);
+static PLpgSQL_row *read_into_scalar_list(const char *initial_name,
+ PLpgSQL_datum *initial_datum);
+static void check_sql_expr(const char *stmt);
+static void plpgsql_sql_error_callback(void *arg);
%}
int lineno;
} varname;
struct
- {
- int nalloc;
- int nused;
- int *nums;
- } intlist;
- struct
- {
- int nalloc;
- int nused;
- PLpgSQL_diag_item *dtitems;
- } dtlist;
- struct
- {
+ {
char *name;
int lineno;
- PLpgSQL_rec *rec;
- PLpgSQL_row *row;
+ PLpgSQL_rec *rec;
+ PLpgSQL_row *row;
} forvariable;
struct
{
int n_initvars;
int *initvarnos;
} declhdr;
+ List *list;
PLpgSQL_type *dtype;
PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */
PLpgSQL_variable *variable; /* a VAR, REC, or ROW */
PLpgSQL_rec *rec;
PLpgSQL_expr *expr;
PLpgSQL_stmt *stmt;
- PLpgSQL_stmts *stmts;
PLpgSQL_stmt_block *program;
PLpgSQL_condition *condition;
PLpgSQL_exception *exception;
- PLpgSQL_exceptions *exceptions;
PLpgSQL_nsitem *nsitem;
+ PLpgSQL_diag_item *diagitem;
}
%type <declhdr> decl_sect
%type <ival> decl_const decl_notnull
%type <expr> decl_defval decl_cursor_query
%type <dtype> decl_datatype
-%type <row> decl_cursor_args decl_cursor_arglist
+%type <row> decl_cursor_args
+%type <list> decl_cursor_arglist
%type <nsitem> decl_aliasitem
%type <str> decl_stmts decl_stmt
%type <str> opt_exitlabel
%type <str> execsql_start
-%type <stmts> proc_sect proc_stmts stmt_else loop_body
+%type <list> proc_sect proc_stmts stmt_else loop_body
%type <stmt> proc_stmt pl_block
%type <stmt> stmt_assign stmt_if stmt_loop stmt_while stmt_exit
%type <stmt> stmt_return stmt_return_next stmt_raise stmt_execsql
%type <stmt> stmt_dynexecute stmt_getdiag
%type <stmt> stmt_open stmt_fetch stmt_close stmt_null
-%type <exceptions> exception_sect proc_exceptions
+%type <list> exception_sect proc_exceptions
%type <exception> proc_exception
%type <condition> proc_conditions
-%type <intlist> raise_params
+%type <list> raise_params
%type <ival> raise_level raise_param
%type <str> raise_msg
-%type <dtlist> getdiag_list
-%type <ival> getdiag_item getdiag_target
+%type <list> getdiag_list
+%type <diagitem> getdiag_list_item
+%type <ival> getdiag_kind getdiag_target
%type <ival> lno
comp_option : O_OPTION O_DUMP
{
- plpgsql_DumpExecTree = 1;
+ plpgsql_DumpExecTree = true;
}
;
{
PLpgSQL_stmt_block *new;
- new = malloc(sizeof(PLpgSQL_stmt_block));
- memset(new, 0, sizeof(PLpgSQL_stmt_block));
+ new = palloc0(sizeof(PLpgSQL_stmt_block));
new->cmd_type = PLPGSQL_STMT_BLOCK;
new->lineno = $3;
-1),
true);
- curname_def = malloc(sizeof(PLpgSQL_expr));
- memset(curname_def, 0, sizeof(PLpgSQL_expr));
+ curname_def = palloc0(sizeof(PLpgSQL_expr));
curname_def->dtype = PLPGSQL_DTYPE_EXPR;
strcpy(buf, "SELECT '");
*cp2++ = *cp1++;
}
strcpy(cp2, "'::refcursor");
- curname_def->query = strdup(buf);
+ curname_def->query = pstrdup(buf);
new->default_val = curname_def;
new->cursor_explicit_expr = $6;
$$ = NULL;
}
| '(' decl_cursor_arglist ')'
- {
- /* Copy the temp arrays to malloc'd storage */
- int nfields = $2->nfields;
- char **ftmp;
- int *vtmp;
-
- ftmp = malloc(nfields * sizeof(char *));
- vtmp = malloc(nfields * sizeof(int));
- memcpy(ftmp, $2->fieldnames, nfields * sizeof(char *));
- memcpy(vtmp, $2->varnos, nfields * sizeof(int));
-
- pfree($2->fieldnames);
- pfree($2->varnos);
-
- $2->fieldnames = ftmp;
- $2->varnos = vtmp;
-
- plpgsql_adddatum((PLpgSQL_datum *)$2);
-
- $$ = $2;
- }
- ;
-
-decl_cursor_arglist : decl_cursor_arg
{
PLpgSQL_row *new;
+ int i;
+ ListCell *l;
- new = malloc(sizeof(PLpgSQL_row));
- memset(new, 0, sizeof(PLpgSQL_row));
-
+ new = palloc0(sizeof(PLpgSQL_row));
new->dtype = PLPGSQL_DTYPE_ROW;
- new->refname = strdup("*internal*");
new->lineno = plpgsql_scanner_lineno();
new->rowtupdesc = NULL;
- /*
- * We make temporary fieldnames/varnos arrays that
- * are much bigger than necessary. We will resize
- * them to just the needed size in the
- * decl_cursor_args production.
- */
- new->fieldnames = palloc(1024 * sizeof(char *));
- new->varnos = palloc(1024 * sizeof(int));
- new->nfields = 1;
+ new->nfields = list_length($2);
+ new->fieldnames = palloc(new->nfields * sizeof(char *));
+ new->varnos = palloc(new->nfields * sizeof(int));
- new->fieldnames[0] = $1->refname;
- new->varnos[0] = $1->dno;
+ i = 0;
+ foreach (l, $2)
+ {
+ PLpgSQL_variable *arg = (PLpgSQL_variable *) lfirst(l);
+ new->fieldnames[i] = arg->refname;
+ new->varnos[i] = arg->dno;
+ i++;
+ }
+ list_free($2);
+ plpgsql_adddatum((PLpgSQL_datum *) new);
$$ = new;
}
- | decl_cursor_arglist ',' decl_cursor_arg
- {
- int i = $1->nfields++;
-
- $1->fieldnames[i] = $3->refname;
- $1->varnos[i] = $3->dno;
+ ;
- $$ = $1;
+decl_cursor_arglist : decl_cursor_arglist decl_cursor_arg
+ {
+ $$ = lappend($1, $2);
+ }
+ | decl_cursor_arg
+ {
+ $$ = list_make1($1);
}
;
char *name;
plpgsql_convert_ident(yytext, &name, 1);
- /* name should be malloc'd for use as varname */
- $$.name = strdup(name);
+ $$.name = name;
$$.lineno = plpgsql_scanner_lineno();
- pfree(name);
}
;
proc_sect :
{
- PLpgSQL_stmts *new;
-
- new = malloc(sizeof(PLpgSQL_stmts));
- memset(new, 0, sizeof(PLpgSQL_stmts));
- $$ = new;
+ $$ = NIL;
}
| proc_stmts
{ $$ = $1; }
proc_stmts : proc_stmts proc_stmt
{
- if ($2 != NULL)
- {
- 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++] = $2;
- }
- $$ = $1;
+ if ($2 == NULL)
+ $$ = $1;
+ else
+ $$ = lappend($1, $2);
}
| proc_stmt
{
- PLpgSQL_stmts *new;
-
- new = malloc(sizeof(PLpgSQL_stmts));
- memset(new, 0, sizeof(PLpgSQL_stmts));
-
- new->stmts_alloc = 32;
- new->stmts = malloc(sizeof(PLpgSQL_stmt *) * new->stmts_alloc);
-
- if ($1 != NULL)
- new->stmts[new->stmts_used++] = $1;
-
- $$ = new;
+ if ($1 == NULL)
+ $$ = NULL;
+ else
+ $$ = list_make1($1);
}
;
{
PLpgSQL_stmt_perform *new;
- new = malloc(sizeof(PLpgSQL_stmt_perform));
- memset(new, 0, sizeof(PLpgSQL_stmt_perform));
-
+ new = palloc0(sizeof(PLpgSQL_stmt_perform));
new->cmd_type = PLPGSQL_STMT_PERFORM;
new->lineno = $2;
new->expr = $3;
{
PLpgSQL_stmt_assign *new;
- new = malloc(sizeof(PLpgSQL_stmt_assign));
- memset(new, 0, sizeof(PLpgSQL_stmt_assign));
-
+ new = palloc0(sizeof(PLpgSQL_stmt_assign));
new->cmd_type = PLPGSQL_STMT_ASSIGN;
new->lineno = $2;
new->varno = $1;
{
PLpgSQL_stmt_getdiag *new;
- new = malloc(sizeof(PLpgSQL_stmt_getdiag));
- memset(new, 0, sizeof(PLpgSQL_stmt_getdiag));
-
+ new = palloc0(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);
+ new->diag_items = $4;
$$ = (PLpgSQL_stmt *)new;
}
;
-getdiag_list : getdiag_list ',' getdiag_target K_ASSIGN getdiag_item
+getdiag_list : getdiag_list ',' getdiag_list_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;
+ $$ = lappend($1, $3);
}
- | getdiag_target K_ASSIGN getdiag_item
+ | getdiag_list_item
{
- $$.nalloc = 1;
- $$.nused = 1;
- $$.dtitems = palloc(sizeof(PLpgSQL_diag_item) * $$.nalloc);
- $$.dtitems[0].target = $1;
- $$.dtitems[0].item = $3;
+ $$ = list_make1($1);
}
;
-getdiag_item : K_ROW_COUNT
+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;
}
{
PLpgSQL_arrayelem *new;
- new = malloc(sizeof(PLpgSQL_arrayelem));
- memset(new, 0, sizeof(PLpgSQL_arrayelem));
-
+ new = palloc0(sizeof(PLpgSQL_arrayelem));
new->dtype = PLPGSQL_DTYPE_ARRAYELEM;
new->subscript = $3;
new->arrayparentno = $1;
{
PLpgSQL_stmt_if *new;
- new = malloc(sizeof(PLpgSQL_stmt_if));
- memset(new, 0, sizeof(PLpgSQL_stmt_if));
-
+ new = palloc0(sizeof(PLpgSQL_stmt_if));
new->cmd_type = PLPGSQL_STMT_IF;
new->lineno = $2;
new->cond = $3;
stmt_else :
{
- PLpgSQL_stmts *new;
-
- new = malloc(sizeof(PLpgSQL_stmts));
- memset(new, 0, sizeof(PLpgSQL_stmts));
- $$ = new;
+ $$ = 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_stmts *new;
+ /*
+ * 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 = malloc(sizeof(PLpgSQL_stmt_if));
- memset(new_if, 0, sizeof(PLpgSQL_stmt_if));
-
+ 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;
-
- /* this is a 'container' for the if-statement */
- 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] = (PLpgSQL_stmt *) new_if;
- $$ = new;
-
+ /* wrap the if-statement in a "container" list */
+ $$ = list_make1(new_if);
}
| K_ELSE proc_sect
{
PLpgSQL_stmt_loop *new;
- new = malloc(sizeof(PLpgSQL_stmt_loop));
- memset(new, 0, sizeof(PLpgSQL_stmt_loop));
-
+ new = palloc0(sizeof(PLpgSQL_stmt_loop));
new->cmd_type = PLPGSQL_STMT_LOOP;
new->lineno = $3;
new->label = $1;
{
PLpgSQL_stmt_while *new;
- new = malloc(sizeof(PLpgSQL_stmt_while));
- memset(new, 0, sizeof(PLpgSQL_stmt_while));
-
+ new = palloc0(sizeof(PLpgSQL_stmt_while));
new->cmd_type = PLPGSQL_STMT_WHILE;
new->lineno = $3;
new->label = $1;
}
;
-for_control : lno for_variable K_IN
+for_control :
+ lno for_variable K_IN
{
- int tok;
- bool reverse = false;
- bool execute = false;
- PLpgSQL_expr *expr1;
-
- /* check for REVERSE and EXECUTE */
- tok = yylex();
- if (tok == K_REVERSE)
- {
- reverse = true;
- tok = yylex();
- }
+ int tok = yylex();
+ /* Simple case: EXECUTE is a dynamic FOR loop */
if (tok == K_EXECUTE)
- execute = true;
- else
- plpgsql_push_back_token(tok);
-
- /* Collect one or two expressions */
- expr1 = read_sql_construct(K_DOTDOT,
- K_LOOP,
- "LOOP",
- true,
- "SELECT ",
- &tok);
-
- if (tok == K_DOTDOT)
{
- /* Found .., so it must be an integer loop */
- PLpgSQL_stmt_fori *new;
- PLpgSQL_expr *expr2;
- PLpgSQL_var *fvar;
-
- expr2 = plpgsql_read_expression(K_LOOP, "LOOP");
-
- if (execute)
- {
- plpgsql_error_lineno = $1;
- yyerror("cannot specify EXECUTE in integer for-loop");
- }
-
- /* name should be malloc'd for use as varname */
- fvar = (PLpgSQL_var *)
- plpgsql_build_variable(strdup($2.name),
- $2.lineno,
- plpgsql_build_datatype(INT4OID,
- -1),
- true);
-
- /* put the for-variable into the local block */
- plpgsql_add_initdatums(NULL);
-
- new = malloc(sizeof(PLpgSQL_stmt_fori));
- memset(new, 0, 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 if (execute)
- {
- /* No .., so it must be a loop over rows */
PLpgSQL_stmt_dynfors *new;
+ PLpgSQL_expr *expr;
- if (reverse)
- {
- plpgsql_error_lineno = $1;
- yyerror("cannot specify REVERSE in loop over rows");
- }
-
- new = malloc(sizeof(PLpgSQL_stmt_dynfors));
- memset(new, 0, sizeof(PLpgSQL_stmt_dynfors));
+ 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)
plpgsql_error_lineno = $1;
yyerror("loop variable of loop over rows must be a record or row variable");
}
- new->query = expr1;
+ new->query = expr;
$$ = (PLpgSQL_stmt *) new;
}
else
{
- /* No .., so it must be a loop over rows */
- PLpgSQL_stmt_fors *new;
- char *newquery;
+ 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);
- if (reverse)
+ /*
+ * 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)
{
- plpgsql_error_lineno = $1;
- yyerror("cannot specify REVERSE in loop over rows");
+ /* 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;
}
-
- new = malloc(sizeof(PLpgSQL_stmt_fors));
- memset(new, 0, 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 a record or row variable");
- }
- /*
- * Must get rid of the "SELECT " we prepended
- * to expr1's text
- */
- newquery = strdup(expr1->query + 7);
- free(expr1->query);
- expr1->query = newquery;
-
- new->query = expr1;
+ /*
+ * 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");
+ }
- $$ = (PLpgSQL_stmt *) new;
+ new->query = expr1;
+ $$ = (PLpgSQL_stmt *) new;
+ }
}
}
;
{
PLpgSQL_stmt_exit *new;
- new = malloc(sizeof(PLpgSQL_stmt_exit));
- memset(new, 0, sizeof(PLpgSQL_stmt_exit));
-
+ new = palloc0(sizeof(PLpgSQL_stmt_exit));
new->cmd_type = PLPGSQL_STMT_EXIT;
new->lineno = $2;
new->label = $3;
{
PLpgSQL_stmt_return *new;
- new = malloc(sizeof(PLpgSQL_stmt_return));
- memset(new, 0, sizeof(PLpgSQL_stmt_return));
+ new = palloc0(sizeof(PLpgSQL_stmt_return));
new->expr = NULL;
new->retrecno = -1;
new->retrowno = -1;
if (yylex() != ';')
yyerror("RETURN must specify a record or row variable in function returning tuple");
}
+ else if (plpgsql_curr_compile->fn_rettype == VOIDOID)
+ {
+ if (yylex() != ';')
+ yyerror("function returning void cannot specify RETURN expression");
+ }
else
{
- /* ordinary expression case */
+ /*
+ * Note that a well-formed expression is
+ * _required_ here; anything else is a
+ * compile-time error.
+ */
new->expr = plpgsql_read_expression(';', ";");
}
if (!plpgsql_curr_compile->fn_retset)
yyerror("cannot use RETURN NEXT in a non-SETOF function");
- new = malloc(sizeof(PLpgSQL_stmt_return_next));
- memset(new, 0, sizeof(PLpgSQL_stmt_return_next));
-
+ new = palloc0(sizeof(PLpgSQL_stmt_return_next));
new->cmd_type = PLPGSQL_STMT_RETURN_NEXT;
new->lineno = $2;
{
PLpgSQL_stmt_raise *new;
- new = malloc(sizeof(PLpgSQL_stmt_raise));
+ new = palloc(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);
+ new->params = $5;
$$ = (PLpgSQL_stmt *)new;
}
{
PLpgSQL_stmt_raise *new;
- new = malloc(sizeof(PLpgSQL_stmt_raise));
+ new = palloc(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;
+ new->params = NIL;
$$ = (PLpgSQL_stmt *)new;
}
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;
+ $$ = lappend_int($1, $2);
}
| raise_param
{
- $$.nalloc = 1;
- $$.nused = 1;
- $$.nums = palloc(sizeof(int) * $$.nalloc);
- $$.nums[0] = $1;
+ $$ = list_make1_int($1);
}
;
{
PLpgSQL_stmt_execsql *new;
- new = malloc(sizeof(PLpgSQL_stmt_execsql));
+ new = palloc(sizeof(PLpgSQL_stmt_execsql));
new->cmd_type = PLPGSQL_STMT_EXECSQL;
new->lineno = $2;
new->sqlstmt = read_sql_stmt($1);
;
stmt_dynexecute : K_EXECUTE lno expr_until_semi
- {
- PLpgSQL_stmt_dynexecute *new;
+ {
+ PLpgSQL_stmt_dynexecute *new;
- new = malloc(sizeof(PLpgSQL_stmt_dynexecute));
- new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
- new->lineno = $2;
- new->query = $3;
+ new = palloc(sizeof(PLpgSQL_stmt_dynexecute));
+ new->cmd_type = PLPGSQL_STMT_DYNEXECUTE;
+ new->lineno = $2;
+ new->query = $3;
- $$ = (PLpgSQL_stmt *)new;
- }
+ $$ = (PLpgSQL_stmt *)new;
+ }
;
stmt_open : K_OPEN lno cursor_varptr
PLpgSQL_stmt_open *new;
int tok;
- new = malloc(sizeof(PLpgSQL_stmt_open));
- memset(new, 0, sizeof(PLpgSQL_stmt_open));
-
+ new = palloc0(sizeof(PLpgSQL_stmt_open));
new->cmd_type = PLPGSQL_STMT_OPEN;
new->lineno = $2;
new->curvar = $3->varno;
{
PLpgSQL_stmt_close *new;
- new = malloc(sizeof(PLpgSQL_stmt_close));
+ new = palloc(sizeof(PLpgSQL_stmt_close));
new->cmd_type = PLPGSQL_STMT_CLOSE;
new->lineno = $2;
new->curvar = $3;
;
execsql_start : T_WORD
- { $$ = strdup(yytext); }
+ { $$ = pstrdup(yytext); }
| T_ERROR
- { $$ = strdup(yytext); }
+ { $$ = pstrdup(yytext); }
;
exception_sect :
- { $$ = NULL; }
+ { $$ = NIL; }
| K_EXCEPTION proc_exceptions
{ $$ = $2; }
;
proc_exceptions : proc_exceptions proc_exception
{
- if ($1->exceptions_used == $1->exceptions_alloc)
- {
- $1->exceptions_alloc *= 2;
- $1->exceptions = realloc($1->exceptions, sizeof(PLpgSQL_exception *) * $1->exceptions_alloc);
- }
- $1->exceptions[$1->exceptions_used++] = $2;
-
- $$ = $1;
+ $$ = lappend($1, $2);
}
| proc_exception
{
- PLpgSQL_exceptions *new;
-
- new = malloc(sizeof(PLpgSQL_exceptions));
- memset(new, 0, sizeof(PLpgSQL_exceptions));
-
- new->exceptions_alloc = 16;
- new->exceptions_used = 1;
- new->exceptions = malloc(sizeof(PLpgSQL_exception *) * new->exceptions_alloc);
- new->exceptions[0] = $1;
-
- $$ = new;
+ $$ = list_make1($1);
}
;
{
PLpgSQL_exception *new;
- new = malloc(sizeof(PLpgSQL_exception));
- memset(new, 0, sizeof(PLpgSQL_exception));
-
+ new = palloc0(sizeof(PLpgSQL_exception));
new->lineno = $2;
new->conditions = $3;
new->action = $5;
char *name;
plpgsql_convert_ident(yytext, &name, 1);
- $$ = strdup(name);
- pfree(name);
+ $$ = name;
}
| T_WORD
{
char *name;
plpgsql_convert_ident(yytext, &name, 1);
- $$ = strdup(name);
- pfree(name);
+ $$ = name;
}
;
PLpgSQL_expr *
plpgsql_read_expression(int until, const char *expected)
{
- return read_sql_construct(until, 0, expected, true, "SELECT ", NULL);
+ 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, ";", false, sqlstart, NULL);
+ return read_sql_construct(';', 0, ";", sqlstart, false, true, NULL);
}
/*
* 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
- * isexpression: whether to say we're reading an "expression" or a "statement"
* 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 expression (plus sqlstart)
* endtoken: if not NULL, ending token is stored at *endtoken
* (this is only interesting if until2 isn't zero)
*/
read_sql_construct(int until,
int until2,
const char *expected,
- bool isexpression,
const char *sqlstart,
+ bool isexpression,
+ bool valid_sql,
int *endtoken)
{
int tok;
errmsg("missing \"%s\" at end of SQL statement",
expected)));
}
+
if (plpgsql_SpaceScanned)
plpgsql_dstring_append(&ds, " ");
+
+ /* Check for array overflow */
+ if (nparams >= 1024)
+ {
+ plpgsql_error_lineno = lno;
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("too many variables specified in SQL statement")));
+ }
+
switch (tok)
{
case T_SCALAR:
if (endtoken)
*endtoken = tok;
- expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
+ expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR;
- expr->query = strdup(plpgsql_dstring_get(&ds));
+ 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;
}
return result;
}
-
static PLpgSQL_stmt *
make_select_stmt(void)
{
PLpgSQL_expr *expr;
PLpgSQL_row *row = NULL;
PLpgSQL_rec *rec = NULL;
- int tok = 0;
- int have_nexttok = 0;
- int have_into = 0;
+ int tok;
+ bool have_into = false;
plpgsql_dstring_init(&ds);
plpgsql_dstring_append(&ds, "SELECT ");
- while(1)
+ while (1)
{
- if (!have_nexttok)
- tok = yylex();
- have_nexttok = 0;
+ tok = yylex();
+
if (tok == ';')
break;
if (tok == 0)
{
case T_ROW:
row = yylval.row;
- have_into = 1;
+ have_into = true;
break;
case T_RECORD:
rec = yylval.rec;
- have_into = 1;
+ have_into = true;
break;
case T_SCALAR:
- {
- int nfields = 1;
- char *fieldnames[1024];
- int varnos[1024];
-
- check_assignable(yylval.scalar);
- fieldnames[0] = strdup(yytext);
- varnos[0] = yylval.scalar->dno;
-
- while ((tok = yylex()) == ',')
- {
- tok = yylex();
- switch(tok)
- {
- case T_SCALAR:
- check_assignable(yylval.scalar);
- fieldnames[nfields] = strdup(yytext);
- varnos[nfields++] = yylval.scalar->dno;
- break;
-
- default:
- plpgsql_error_lineno = plpgsql_scanner_lineno();
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("\"%s\" is not a variable",
- yytext)));
- }
- }
- have_nexttok = 1;
-
- row = malloc(sizeof(PLpgSQL_row));
- row->dtype = PLPGSQL_DTYPE_ROW;
- row->refname = strdup("*internal*");
- row->lineno = plpgsql_scanner_lineno();
- row->rowtupdesc = NULL;
- 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_into = 1;
- }
- break;
+ row = read_into_scalar_list(yytext, yylval.scalar);
+ have_into = true;
+ break;
default:
/* Treat the INTO as non-special */
plpgsql_dstring_append(&ds, " INTO ");
- have_nexttok = 1;
+ plpgsql_push_back_token(tok);
break;
}
continue;
if (plpgsql_SpaceScanned)
plpgsql_dstring_append(&ds, " ");
+
+ /* Check for array overflow */
+ if (nparams >= 1024)
+ {
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("too many parameters specified in SQL statement")));
+ }
+
switch (tok)
{
case T_SCALAR:
}
}
- expr = malloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
+ expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr->dtype = PLPGSQL_DTYPE_EXPR;
- expr->query = strdup(plpgsql_dstring_get(&ds));
+ expr->query = pstrdup(plpgsql_dstring_get(&ds));
expr->plan = NULL;
expr->nparams = nparams;
while(nparams-- > 0)
{
PLpgSQL_stmt_select *select;
- select = malloc(sizeof(PLpgSQL_stmt_select));
- memset(select, 0, sizeof(PLpgSQL_stmt_select));
+ select = palloc0(sizeof(PLpgSQL_stmt_select));
select->cmd_type = PLPGSQL_STMT_SELECT;
select->rec = rec;
select->row = row;
{
PLpgSQL_stmt_execsql *execsql;
- execsql = malloc(sizeof(PLpgSQL_stmt_execsql));
+ execsql = palloc(sizeof(PLpgSQL_stmt_execsql));
execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
execsql->sqlstmt = expr;
PLpgSQL_row *row = NULL;
PLpgSQL_rec *rec = NULL;
PLpgSQL_stmt_fetch *fetch;
- int have_nexttok = 0;
/* We have already parsed everything through the INTO keyword */
break;
case T_SCALAR:
- {
- int nfields = 1;
- char *fieldnames[1024];
- int varnos[1024];
-
- check_assignable(yylval.scalar);
- fieldnames[0] = strdup(yytext);
- varnos[0] = yylval.scalar->dno;
-
- while ((tok = yylex()) == ',')
- {
- tok = yylex();
- switch(tok)
- {
- case T_SCALAR:
- check_assignable(yylval.scalar);
- fieldnames[nfields] = strdup(yytext);
- varnos[nfields++] = yylval.scalar->dno;
- break;
-
- default:
- plpgsql_error_lineno = plpgsql_scanner_lineno();
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("\"%s\" is not a variable",
- yytext)));
- }
- }
- have_nexttok = 1;
-
- row = malloc(sizeof(PLpgSQL_row));
- row->dtype = PLPGSQL_DTYPE_ROW;
- row->refname = strdup("*internal*");
- row->lineno = plpgsql_scanner_lineno();
- row->rowtupdesc = NULL;
- 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);
- }
+ row = read_into_scalar_list(yytext, yylval.scalar);
break;
default:
yyerror("syntax error");
}
- if (!have_nexttok)
- tok = yylex();
-
+ tok = yylex();
if (tok != ';')
yyerror("syntax error");
- fetch = malloc(sizeof(PLpgSQL_stmt_select));
- memset(fetch, 0, sizeof(PLpgSQL_stmt_fetch));
+ fetch = palloc0(sizeof(PLpgSQL_stmt_select));
fetch->cmd_type = PLPGSQL_STMT_FETCH;
fetch->rec = rec;
fetch->row = row;
}
}
+/*
+ * Given the first datum and name in the INTO list, continue to read
+ * comma-separated scalar variables until we run out. Then construct
+ * and return a fake "row" variable that represents the list of
+ * scalars.
+ */
+static PLpgSQL_row *
+read_into_scalar_list(const char *initial_name,
+ PLpgSQL_datum *initial_datum)
+{
+ int nfields;
+ char *fieldnames[1024];
+ int varnos[1024];
+ PLpgSQL_row *row;
+ int tok;
+
+ check_assignable(initial_datum);
+ fieldnames[0] = pstrdup(initial_name);
+ varnos[0] = initial_datum->dno;
+ nfields = 1;
+
+ while ((tok = yylex()) == ',')
+ {
+ /* Check for array overflow */
+ if (nfields >= 1024)
+ {
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("too many INTO variables specified")));
+ }
+
+ tok = yylex();
+ switch(tok)
+ {
+ case T_SCALAR:
+ check_assignable(yylval.scalar);
+ fieldnames[nfields] = pstrdup(yytext);
+ varnos[nfields++] = yylval.scalar->dno;
+ break;
+
+ default:
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("\"%s\" is not a variable",
+ yytext)));
+ }
+ }
+
+ /*
+ * We read an extra, non-comma character from yylex(), so push it
+ * back onto the input stream
+ */
+ plpgsql_push_back_token(tok);
+
+ row = palloc(sizeof(PLpgSQL_row));
+ row->dtype = PLPGSQL_DTYPE_ROW;
+ row->refname = pstrdup("*internal*");
+ row->lineno = plpgsql_scanner_lineno();
+ row->rowtupdesc = NULL;
+ row->nfields = nfields;
+ row->fieldnames = palloc(sizeof(char *) * nfields);
+ row->varnos = palloc(sizeof(int) * nfields);
+ while (--nfields >= 0)
+ {
+ row->fieldnames[nfields] = fieldnames[nfields];
+ row->varnos[nfields] = varnos[nfields];
+ }
+
+ plpgsql_adddatum((PLpgSQL_datum *)row);
+
+ return row;
+}
+
+/*
+ * When the PL/PgSQL parser expects to see a SQL statement, it is very
+ * liberal in what it accepts; for example, we often assume an
+ * unrecognized keyword is the beginning of a SQL statement. This
+ * avoids the need to duplicate parts of the SQL grammar in the
+ * PL/PgSQL grammar, but it means we can accept wildly malformed
+ * input. To try and catch some of the more obviously invalid input,
+ * we run the strings we expect to be SQL statements through the main
+ * SQL parser.
+ *
+ * We only invoke the raw parser (not the analyzer); this doesn't do
+ * any database access and does not check any semantic rules, it just
+ * checks for basic syntactic correctness. We do this here, rather
+ * than after parsing has finished, because a malformed SQL statement
+ * may cause the PL/PgSQL parser to become confused about statement
+ * borders. So it is best to bail out as early as we can.
+ */
+static void
+check_sql_expr(const char *stmt)
+{
+ ErrorContextCallback syntax_errcontext;
+ ErrorContextCallback *previous_errcontext;
+ MemoryContext oldCxt;
+
+ if (!plpgsql_check_syntax)
+ return;
+
+ /*
+ * Setup error traceback support for ereport(). The previous
+ * ereport callback is installed by pl_comp.c, but we don't want
+ * that to be invoked (since it will try to transpose the syntax
+ * error to be relative to the CREATE FUNCTION), so temporarily
+ * remove it from the list of callbacks.
+ */
+ Assert(error_context_stack->callback == plpgsql_compile_error_callback);
+
+ previous_errcontext = error_context_stack;
+ syntax_errcontext.callback = plpgsql_sql_error_callback;
+ syntax_errcontext.arg = (char *) stmt;
+ syntax_errcontext.previous = error_context_stack->previous;
+ error_context_stack = &syntax_errcontext;
+
+ oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
+ (void) raw_parser(stmt);
+ MemoryContextSwitchTo(oldCxt);
+
+ /* Restore former ereport callback */
+ error_context_stack = previous_errcontext;
+}
+
+static void
+plpgsql_sql_error_callback(void *arg)
+{
+ char *sql_stmt = (char *) arg;
+
+ Assert(plpgsql_error_funcname);
+
+ errcontext("SQL statement in PL/PgSQL function \"%s\" near line %d",
+ plpgsql_error_funcname, plpgsql_error_lineno);
+ internalerrquery(sql_stmt);
+ internalerrposition(geterrposition());
+ errposition(0);
+}
+
#include "pl_scan.c"
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.83 2004/11/30 03:50:29 neilc Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.84 2005/02/22 07:18:24 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
int plpgsql_error_lineno;
char *plpgsql_error_funcname;
-int plpgsql_DumpExecTree = 0;
+bool plpgsql_DumpExecTree = false;
+bool plpgsql_check_syntax = false;
PLpgSQL_function *plpgsql_curr_compile;
+/* A context appropriate for short-term allocs during compilation */
+MemoryContext compile_tmp_cxt;
+
/* ----------
* Hash table for compiled functions
* ----------
HeapTuple procTup,
PLpgSQL_func_hashkey *hashkey,
bool forValidator);
-static void plpgsql_compile_error_callback(void *arg);
static char **fetchArgNames(HeapTuple procTup, int nargs);
static PLpgSQL_row *build_row_var(Oid classOid);
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
static void plpgsql_HashTableInsert(PLpgSQL_function *function,
PLpgSQL_func_hashkey *func_key);
static void plpgsql_HashTableDelete(PLpgSQL_function *function);
-
-/*
- * This routine is a crock, and so is everyplace that calls it. The problem
- * is that the compiled form of a plpgsql function is allocated permanently
- * (mostly via malloc()) and never released until backend exit. Subsidiary
- * data structures such as fmgr info records therefore must live forever
- * as well. A better implementation would store all this stuff in a per-
- * function memory context that could be reclaimed at need. In the meantime,
- * fmgr_info_cxt must be called specifying TopMemoryContext so that whatever
- * it might allocate, and whatever the eventual function might allocate using
- * fn_mcxt, will live forever too.
- */
-static void
-perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
-{
- fmgr_info_cxt(functionId, finfo, TopMemoryContext);
-}
-
+static void delete_function(PLpgSQL_function *func);
/* ----------
* plpgsql_compile Make an execution tree for a PL/pgSQL function.
if (!function)
{
- /* First time through in this backend? If so, init hashtable */
- if (!plpgsql_HashTable)
- plpgsql_HashTableInit();
-
/* Compute hashkey using function signature and actual arg types */
compute_function_hashkey(fcinfo, procStruct, &hashkey, forValidator);
hashkey_valid = true;
if (!(function->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
function->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data)))
{
- /*
- * Nope, drop the hashtable entry. XXX someday, free all the
- * subsidiary storage as well.
- */
- plpgsql_HashTableDelete(function);
-
+ /* Nope, drop the function and associated storage */
+ delete_function(function);
function = NULL;
}
}
/*
* This is the slow part of plpgsql_compile().
+ *
+ * While compiling a function, the CurrentMemoryContext is the
+ * per-function memory context of the function we are compiling. That
+ * means a palloc() will allocate storage with the same lifetime as
+ * the function itself.
+ *
+ * Because palloc()'d storage will not be immediately freed, temporary
+ * allocations should either be performed in a short-lived memory
+ * context or explicitly pfree'd. Since not all backend functions are
+ * careful about pfree'ing their allocations, it is also wise to
+ * switch into a short-term context before calling into the
+ * backend. An appropriate context for performing short-term
+ * allocations is the compile_tmp_cxt.
*/
static PLpgSQL_function *
do_compile(FunctionCallInfo fcinfo,
int parse_rc;
Oid rettypeid;
char **argnames;
+ MemoryContext func_cxt;
/*
* Setup the scanner input and error info. We assume that this
* Setup error traceback support for ereport()
*/
plerrcontext.callback = plpgsql_compile_error_callback;
- plerrcontext.arg = forValidator ? proc_source : (char *) NULL;
+ plerrcontext.arg = forValidator ? proc_source : NULL;
plerrcontext.previous = error_context_stack;
error_context_stack = &plerrcontext;
*/
plpgsql_ns_init();
plpgsql_ns_push(NULL);
- plpgsql_DumpExecTree = 0;
+ plpgsql_DumpExecTree = false;
datums_alloc = 128;
plpgsql_nDatums = 0;
+ /* This is short-lived, so needn't allocate in function's cxt */
plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);
datums_last = 0;
/*
- * Create the new function node
+ * Do extra syntax checks when validating the function
+ * definition. We skip this when actually compiling functions for
+ * execution, for performance reasons.
*/
- function = malloc(sizeof(PLpgSQL_function));
- MemSet(function, 0, sizeof(PLpgSQL_function));
+ plpgsql_check_syntax = forValidator;
+
+ /*
+ * Create the new function node. We allocate the function and all
+ * of its compile-time storage (e.g. parse tree) in its own memory
+ * context. This allows us to reclaim the function's storage
+ * cleanly.
+ */
+ func_cxt = AllocSetContextCreate(TopMemoryContext,
+ "PL/PgSQL function context",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ compile_tmp_cxt = MemoryContextSwitchTo(func_cxt);
+ function = palloc0(sizeof(*function));
plpgsql_curr_compile = function;
- function->fn_name = strdup(NameStr(procStruct->proname));
+ function->fn_name = pstrdup(NameStr(procStruct->proname));
function->fn_oid = fcinfo->flinfo->fn_oid;
function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
function->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
function->fn_functype = functype;
+ function->fn_cxt = func_cxt;
switch (functype)
{
case T_FUNCTION:
-
/*
* Check for a polymorphic returntype. If found, use the
* actual returntype type from the caller's FuncExpr node, if
function->fn_retbyval = typeStruct->typbyval;
function->fn_rettyplen = typeStruct->typlen;
function->fn_rettypioparam = getTypeIOParam(typeTup);
- perm_fmgr_info(typeStruct->typinput, &(function->fn_retinput));
+ fmgr_info(typeStruct->typinput, &(function->fn_retinput));
/*
* install $0 reference, but only for polymorphic return
if (procStruct->prorettype == ANYARRAYOID ||
procStruct->prorettype == ANYELEMENTOID)
{
- (void) plpgsql_build_variable(strdup("$0"), 0,
+ (void) plpgsql_build_variable("$0", 0,
build_datatype(typeTup, -1),
true);
}
ReleaseSysCache(typeTup);
/*
- * Create the variables for the procedure's parameters
+ * Create the variables for the procedure's
+ * parameters. Allocations aren't needed permanently, so
+ * make them in tmp cxt.
*/
+ MemoryContextSwitchTo(compile_tmp_cxt);
argnames = fetchArgNames(procTup, procStruct->pronargs);
+ MemoryContextSwitchTo(func_cxt);
for (i = 0; i < procStruct->pronargs; i++)
{
format_type_be(argtypeid))));
/* Build variable and add to datum list */
- argvariable = plpgsql_build_variable(strdup(buf), 0,
+ argvariable = plpgsql_build_variable(buf, 0,
argdtype, false);
if (argvariable->dtype == PLPGSQL_DTYPE_VAR)
plpgsql_ns_additem(argitemtype, argvariable->dno, buf);
/* If there's a name for the argument, make an alias */
- if (argnames && argnames[i] && argnames[i][0])
+ if (argnames)
plpgsql_ns_additem(argitemtype, argvariable->dno,
argnames[i]);
}
break;
case T_TRIGGER:
-
- /*
- * Trigger procedures return type is unknown yet
- */
+ /* Trigger procedure's return type is unknown yet */
function->fn_rettype = InvalidOid;
function->fn_retbyval = false;
function->fn_retistuple = true;
function->fn_retset = false;
- /*
- * Add the record for referencing NEW
- */
- rec = malloc(sizeof(PLpgSQL_rec));
- memset(rec, 0, sizeof(PLpgSQL_rec));
+ /* Add the record for referencing NEW */
+ rec = palloc0(sizeof(PLpgSQL_rec));
rec->dtype = PLPGSQL_DTYPE_REC;
- rec->refname = strdup("new");
+ rec->refname = pstrdup("new");
rec->tup = NULL;
rec->tupdesc = NULL;
rec->freetup = false;
plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname);
function->new_varno = rec->recno;
- /*
- * Add the record for referencing OLD
- */
- rec = malloc(sizeof(PLpgSQL_rec));
- memset(rec, 0, sizeof(PLpgSQL_rec));
+ /* Add the record for referencing OLD */
+ rec = palloc0(sizeof(PLpgSQL_rec));
rec->dtype = PLPGSQL_DTYPE_REC;
- rec->refname = strdup("old");
+ rec->refname = pstrdup("old");
rec->tup = NULL;
rec->tupdesc = NULL;
rec->freetup = false;
plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname);
function->old_varno = rec->recno;
- /*
- * Add the variable tg_name
- */
- var = plpgsql_build_variable(strdup("tg_name"), 0,
+ /* Add the variable tg_name */
+ var = plpgsql_build_variable("tg_name", 0,
plpgsql_build_datatype(NAMEOID, -1),
true);
function->tg_name_varno = var->dno;
- /*
- * Add the variable tg_when
- */
- var = plpgsql_build_variable(strdup("tg_when"), 0,
+ /* Add the variable tg_when */
+ var = plpgsql_build_variable("tg_when", 0,
plpgsql_build_datatype(TEXTOID, -1),
true);
function->tg_when_varno = var->dno;
- /*
- * Add the variable tg_level
- */
- var = plpgsql_build_variable(strdup("tg_level"), 0,
+ /* Add the variable tg_level */
+ var = plpgsql_build_variable("tg_level", 0,
plpgsql_build_datatype(TEXTOID, -1),
true);
function->tg_level_varno = var->dno;
- /*
- * Add the variable tg_op
- */
- var = plpgsql_build_variable(strdup("tg_op"), 0,
+ /* Add the variable tg_op */
+ var = plpgsql_build_variable("tg_op", 0,
plpgsql_build_datatype(TEXTOID, -1),
true);
function->tg_op_varno = var->dno;
- /*
- * Add the variable tg_relid
- */
- var = plpgsql_build_variable(strdup("tg_relid"), 0,
+ /* Add the variable tg_relid */
+ var = plpgsql_build_variable("tg_relid", 0,
plpgsql_build_datatype(OIDOID, -1),
true);
function->tg_relid_varno = var->dno;
- /*
- * Add the variable tg_relname
- */
- var = plpgsql_build_variable(strdup("tg_relname"), 0,
+ /* Add the variable tg_relname */
+ var = plpgsql_build_variable("tg_relname", 0,
plpgsql_build_datatype(NAMEOID, -1),
true);
function->tg_relname_varno = var->dno;
- /*
- * Add the variable tg_nargs
- */
- var = plpgsql_build_variable(strdup("tg_nargs"), 0,
+ /* Add the variable tg_nargs */
+ var = plpgsql_build_variable("tg_nargs", 0,
plpgsql_build_datatype(INT4OID, -1),
true);
function->tg_nargs_varno = var->dno;
/*
* Create the magic FOUND variable.
*/
- var = plpgsql_build_variable(strdup("found"), 0,
+ var = plpgsql_build_variable("found", 0,
plpgsql_build_datatype(BOOLOID, -1),
true);
function->found_varno = var->dno;
for (i = 0; i < function->fn_nargs; i++)
function->fn_argvarnos[i] = arg_varnos[i];
function->ndatums = plpgsql_nDatums;
- function->datums = malloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
+ function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
for (i = 0; i < plpgsql_nDatums; i++)
function->datums[i] = plpgsql_Datums[i];
function->action = plpgsql_yylval.program;
plpgsql_error_funcname = NULL;
plpgsql_error_lineno = 0;
+ plpgsql_check_syntax = false;
+
+ MemoryContextSwitchTo(compile_tmp_cxt);
+ compile_tmp_cxt = NULL;
return function;
}
/*
- * error context callback to let us supply a call-stack traceback
- *
- * If we are validating, the function source is passed as argument.
+ * error context callback to let us supply a call-stack traceback. If
+ * we are validating, the function source is passed as an
+ * argument. This function is public only for the sake of an assertion
+ * in gram.y
*/
-static void
+void
plpgsql_compile_error_callback(void *arg)
{
if (arg)
{
if (strcmp(cp[0], "tg_argv") == 0)
{
- int save_spacescanned = plpgsql_SpaceScanned;
+ bool save_spacescanned = plpgsql_SpaceScanned;
PLpgSQL_trigarg *trigarg;
- trigarg = malloc(sizeof(PLpgSQL_trigarg));
- memset(trigarg, 0, sizeof(PLpgSQL_trigarg));
+ trigarg = palloc0(sizeof(PLpgSQL_trigarg));
trigarg->dtype = PLPGSQL_DTYPE_TRIGARG;
if (plpgsql_yylex() != '[')
*/
PLpgSQL_recfield *new;
- new = malloc(sizeof(PLpgSQL_recfield));
+ new = palloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
- new->fieldname = strdup(cp[1]);
+ new->fieldname = pstrdup(cp[1]);
new->recparentno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
*/
PLpgSQL_recfield *new;
- new = malloc(sizeof(PLpgSQL_recfield));
+ new = palloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
- new->fieldname = strdup(cp[2]);
+ new->fieldname = pstrdup(cp[2]);
new->recparentno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
pfree(cp[1]);
/*
- * Do a lookup on the compilers namestack. But ensure it moves up to
+ * Do a lookup on the compiler's namestack. But ensure it moves up to
* the toplevel.
*/
old_nsstate = plpgsql_ns_setlocal(false);
PLpgSQL_nsitem *nse;
bool old_nsstate;
Oid classOid;
- HeapTuple classtup;
+ HeapTuple classtup = NULL;
+ HeapTuple attrtup = NULL;
+ HeapTuple typetup = NULL;
Form_pg_class classStruct;
- HeapTuple attrtup;
Form_pg_attribute attrStruct;
- HeapTuple typetup;
char *cp[3];
int i;
+ MemoryContext oldCxt;
+ int result = T_ERROR;
+
+ /* Avoid memory leaks in the long-term function context */
+ oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
/* Do case conversion and word separation */
/* We convert %type to .type momentarily to keep converter happy */
word[i] = '.';
plpgsql_convert_ident(word, cp, 3);
word[i] = '%';
- pfree(cp[2]);
/*
* Lookup the first word
nse = plpgsql_ns_lookup(cp[0], NULL);
/*
- * If this is a label lookup the second word in that labels namestack
- * level
+ * If this is a label lookup the second word in that label's
+ * namestack level
*/
if (nse != NULL)
{
nse = plpgsql_ns_lookup(cp[1], cp[0]);
plpgsql_ns_setlocal(old_nsstate);
- pfree(cp[0]);
- pfree(cp[1]);
-
- if (nse != NULL)
+ if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
{
- switch (nse->itemtype)
- {
- case PLPGSQL_NSTYPE_VAR:
- plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
- return T_DTYPE;
-
- default:
- return T_ERROR;
- }
+ plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
+ result = T_DTYPE;
}
- return T_ERROR;
}
- pfree(cp[0]);
- pfree(cp[1]);
- return T_ERROR;
+
+ /* Return T_ERROR if not found, otherwise T_DTYPE */
+ goto done;
}
/*
*/
classOid = RelnameGetRelid(cp[0]);
if (!OidIsValid(classOid))
- {
- pfree(cp[0]);
- pfree(cp[1]);
- return T_ERROR;
- }
+ goto done;
+
classtup = SearchSysCache(RELOID,
ObjectIdGetDatum(classOid),
0, 0, 0);
if (!HeapTupleIsValid(classtup))
- {
- pfree(cp[0]);
- pfree(cp[1]);
- return T_ERROR;
- }
+ goto done;
/*
* It must be a relation, sequence, view, or type
classStruct->relkind != RELKIND_SEQUENCE &&
classStruct->relkind != RELKIND_VIEW &&
classStruct->relkind != RELKIND_COMPOSITE_TYPE)
- {
- ReleaseSysCache(classtup);
- pfree(cp[0]);
- pfree(cp[1]);
- return T_ERROR;
- }
+ goto done;
/*
* Fetch the named table field and it's type
*/
attrtup = SearchSysCacheAttName(classOid, cp[1]);
if (!HeapTupleIsValid(attrtup))
- {
- ReleaseSysCache(classtup);
- pfree(cp[0]);
- pfree(cp[1]);
- return T_ERROR;
- }
- attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
+ goto done;
+ attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
typetup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(attrStruct->atttypid),
0, 0, 0);
elog(ERROR, "cache lookup failed for type %u", attrStruct->atttypid);
/*
- * Found that - build a compiler type struct and return it
+ * Found that - build a compiler type struct in the caller's cxt
+ * and return it
*/
+ MemoryContextSwitchTo(oldCxt);
plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod);
+ MemoryContextSwitchTo(compile_tmp_cxt);
+ result = T_DTYPE;
- ReleaseSysCache(classtup);
- ReleaseSysCache(attrtup);
- ReleaseSysCache(typetup);
- pfree(cp[0]);
- pfree(cp[1]);
- return T_DTYPE;
+done:
+ if (HeapTupleIsValid(classtup))
+ ReleaseSysCache(classtup);
+ if (HeapTupleIsValid(attrtup))
+ ReleaseSysCache(attrtup);
+ if (HeapTupleIsValid(typetup))
+ ReleaseSysCache(typetup);
+
+ MemoryContextSwitchTo(oldCxt);
+ return result;
}
/* ----------
plpgsql_parse_tripwordtype(char *word)
{
Oid classOid;
- HeapTuple classtup;
+ HeapTuple classtup = NULL;
Form_pg_class classStruct;
- HeapTuple attrtup;
+ HeapTuple attrtup = NULL;
Form_pg_attribute attrStruct;
- HeapTuple typetup;
+ HeapTuple typetup = NULL;
char *cp[2];
char *colname[1];
int qualified_att_len;
int numdots = 0;
int i;
RangeVar *relvar;
+ MemoryContext oldCxt;
+ int result = T_ERROR;
+
+ /* Avoid memory leaks in the long-term function context */
+ oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
/* Do case conversion and word separation */
qualified_att_len = strlen(word) - TYPE_JUNK_LEN;
relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp[0], "plpgsql_parse_tripwordtype"));
classOid = RangeVarGetRelid(relvar, true);
if (!OidIsValid(classOid))
- {
- pfree(cp[0]);
- pfree(cp[1]);
- return T_ERROR;
- }
+ goto done;
+
classtup = SearchSysCache(RELOID,
ObjectIdGetDatum(classOid),
0, 0, 0);
if (!HeapTupleIsValid(classtup))
- {
- pfree(cp[0]);
- pfree(cp[1]);
- return T_ERROR;
- }
+ goto done;
/*
* It must be a relation, sequence, view, or type
classStruct->relkind != RELKIND_SEQUENCE &&
classStruct->relkind != RELKIND_VIEW &&
classStruct->relkind != RELKIND_COMPOSITE_TYPE)
- {
- ReleaseSysCache(classtup);
- pfree(cp[0]);
- pfree(cp[1]);
- return T_ERROR;
- }
+ goto done;
/*
* Fetch the named table field and it's type
*/
plpgsql_convert_ident(cp[1], colname, 1);
attrtup = SearchSysCacheAttName(classOid, colname[0]);
- pfree(colname[0]);
-
if (!HeapTupleIsValid(attrtup))
- {
- ReleaseSysCache(classtup);
- pfree(cp[0]);
- pfree(cp[1]);
- return T_ERROR;
- }
- attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
+ goto done;
+ attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
typetup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(attrStruct->atttypid),
0, 0, 0);
elog(ERROR, "cache lookup failed for type %u", attrStruct->atttypid);
/*
- * Found that - build a compiler type struct and return it
+ * Found that - build a compiler type struct in the caller's cxt
+ * and return it
*/
+ MemoryContextSwitchTo(oldCxt);
plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod);
+ MemoryContextSwitchTo(compile_tmp_cxt);
+ result = T_DTYPE;
- ReleaseSysCache(classtup);
- ReleaseSysCache(attrtup);
- ReleaseSysCache(typetup);
- pfree(cp[0]);
- pfree(cp[1]);
+done:
+ if (HeapTupleIsValid(classtup))
+ ReleaseSysCache(classtup);
+ if (HeapTupleIsValid(classtup))
+ ReleaseSysCache(attrtup);
+ if (HeapTupleIsValid(typetup))
+ ReleaseSysCache(typetup);
- return T_DTYPE;
+ MemoryContextSwitchTo(oldCxt);
+ return result;
}
/* ----------
char *cp;
int i;
RangeVar *relvar;
+ MemoryContext oldCxt;
+
+ /* Avoid memory leaks in long-term function context */
+ oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
/* Do case conversion and word separation */
/* We convert %rowtype to .rowtype momentarily to keep converter happy */
i = strlen(word) - ROWTYPE_JUNK_LEN;
Assert(word[i] == '%');
-
- cp = (char *) palloc((i + 1) * sizeof(char));
- memset(cp, 0, (i + 1) * sizeof(char));
- memcpy(cp, word, i * sizeof(char));
+ word[i] = '\0';
+ cp = pstrdup(word);
+ word[i] = '%';
/* Lookup the relation */
relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp, "plpgsql_parse_dblwordrowtype"));
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation \"%s\" does not exist", cp)));
- /*
- * Build and return the row type struct
- */
+ /* Build and return the row type struct */
plpgsql_yylval.dtype = plpgsql_build_datatype(get_rel_type_id(classOid),
-1);
- pfree(cp);
-
+ MemoryContextSwitchTo(oldCxt);
return T_DTYPE;
}
/*
- * plpgsql_build_variable - build a datum-array entry of a given datatype
+ * plpgsql_build_variable - build a datum-array entry of a given
+ * datatype
*
- * The returned struct may be a PLpgSQL_var, PLpgSQL_row, or PLpgSQL_rec
- * depending on the given datatype. The struct is automatically added
- * to the current datum array, and optionally to the current namespace.
+ * The returned struct may be a PLpgSQL_var, PLpgSQL_row, or
+ * PLpgSQL_rec depending on the given datatype, and is allocated via
+ * palloc. The struct is automatically added to the current datum
+ * array, and optionally to the current namespace.
*/
PLpgSQL_variable *
-plpgsql_build_variable(char *refname, int lineno, PLpgSQL_type *dtype,
+plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
bool add2namespace)
{
PLpgSQL_variable *result;
/* Ordinary scalar datatype */
PLpgSQL_var *var;
- var = malloc(sizeof(PLpgSQL_var));
- memset(var, 0, sizeof(PLpgSQL_var));
-
+ var = palloc0(sizeof(PLpgSQL_var));
var->dtype = PLPGSQL_DTYPE_VAR;
- var->refname = refname;
+ var->refname = pstrdup(refname);
var->lineno = lineno;
var->datatype = dtype;
/* other fields might be filled by caller */
row = build_row_var(dtype->typrelid);
row->dtype = PLPGSQL_DTYPE_ROW;
- row->refname = refname;
+ row->refname = pstrdup(refname);
row->lineno = lineno;
plpgsql_adddatum((PLpgSQL_datum *) row);
*/
PLpgSQL_rec *rec;
- rec = malloc(sizeof(PLpgSQL_rec));
- memset(rec, 0, sizeof(PLpgSQL_rec));
-
+ rec = palloc0(sizeof(PLpgSQL_rec));
rec->dtype = PLPGSQL_DTYPE_REC;
- rec->refname = refname;
+ rec->refname = pstrdup(refname);
rec->lineno = lineno;
plpgsql_adddatum((PLpgSQL_datum *) rec);
break;
}
case PLPGSQL_TTYPE_PSEUDO:
- {
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("variable \"%s\" has pseudo-type %s",
- refname, format_type_be(dtype->typoid))));
- result = NULL; /* keep compiler quiet */
- break;
- }
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("variable \"%s\" has pseudo-type %s",
+ refname, format_type_be(dtype->typoid))));
+ result = NULL; /* keep compiler quiet */
+ break;
default:
elog(ERROR, "unrecognized ttype: %d", dtype->ttype);
result = NULL; /* keep compiler quiet */
Form_pg_class classStruct;
const char *relname;
int i;
- MemoryContext oldcxt;
/*
* Open the relation to get info.
* Create a row datum entry and all the required variables that it
* will point to.
*/
- row = malloc(sizeof(PLpgSQL_row));
- memset(row, 0, sizeof(PLpgSQL_row));
-
+ row = palloc0(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
-
- /*
- * This is a bit ugly --- need a permanent copy of the rel's tupdesc.
- * Someday all these mallocs should go away in favor of a per-function
- * memory context ...
- */
- oldcxt = MemoryContextSwitchTo(TopMemoryContext);
row->rowtupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
- MemoryContextSwitchTo(oldcxt);
-
row->nfields = classStruct->relnatts;
- row->fieldnames = malloc(sizeof(char *) * row->nfields);
- row->varnos = malloc(sizeof(int) * row->nfields);
+ row->fieldnames = palloc(sizeof(char *) * row->nfields);
+ row->varnos = palloc(sizeof(int) * row->nfields);
for (i = 0; i < row->nfields; i++)
{
/*
* Get the attribute and check for dropped column
*/
- attrStruct = RelationGetDescr(rel)->attrs[i];
+ attrStruct = row->rowtupdesc->attrs[i];
if (!attrStruct->attisdropped)
{
- const char *attname;
- char *refname;
+ char *attname;
+ char refname[(NAMEDATALEN * 2) + 100];
PLpgSQL_variable *var;
attname = NameStr(attrStruct->attname);
- refname = malloc(strlen(relname) + strlen(attname) + 2);
- strcpy(refname, relname);
- strcat(refname, ".");
- strcat(refname, attname);
+ snprintf(refname, sizeof(refname), "%s.%s", relname, attname);
/*
* Create the internal variable for the field
attrStruct->atttypmod),
false);
- /*
- * Add the variable to the row.
- */
- row->fieldnames[i] = strdup(attname);
+ /* Add the variable to the row */
+ row->fieldnames[i] = attname;
row->varnos[i] = var->dno;
}
else
errmsg("type \"%s\" is only a shell",
NameStr(typeStruct->typname))));
- typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
+ typ = (PLpgSQL_type *) palloc(sizeof(PLpgSQL_type));
- typ->typname = strdup(NameStr(typeStruct->typname));
+ typ->typname = pstrdup(NameStr(typeStruct->typname));
typ->typoid = HeapTupleGetOid(typeTup);
switch (typeStruct->typtype)
{
typ->typbyval = typeStruct->typbyval;
typ->typrelid = typeStruct->typrelid;
typ->typioparam = getTypeIOParam(typeTup);
- perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
+ fmgr_info(typeStruct->typinput, &(typ->typinput));
typ->atttypmod = typmod;
return typ;
*/
if (strcmp(condname, "others") == 0)
{
- new = malloc(sizeof(PLpgSQL_condition));
+ new = palloc(sizeof(PLpgSQL_condition));
new->sqlerrstate = 0;
new->condname = condname;
new->next = NULL;
{
if (strcmp(condname, exception_label_map[i].label) == 0)
{
- new = malloc(sizeof(PLpgSQL_condition));
+ new = palloc(sizeof(PLpgSQL_condition));
new->sqlerrstate = exception_label_map[i].sqlerrstate;
new->condname = condname;
new->next = prev;
{
if (n > 0)
{
- *varnos = (int *) malloc(sizeof(int) * n);
+ *varnos = (int *) palloc(sizeof(int) * n);
n = 0;
for (i = datums_last; i < plpgsql_nDatums; i++)
}
}
+static void
+delete_function(PLpgSQL_function *func)
+{
+ /* remove function from hash table */
+ plpgsql_HashTableDelete(func);
+
+ /* release the function's storage */
+ MemoryContextDelete(func->fn_cxt);
+
+ /*
+ * Caller should be sure not to use passed-in pointer, as it now
+ * points to pfree'd storage
+ */
+}
+
/* exported so we can call it from plpgsql_init() */
void
plpgsql_HashTableInit(void)
{
HASHCTL ctl;
+ /* don't allow double-initialization */
+ Assert(plpgsql_HashTable == NULL);
+
memset(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(PLpgSQL_func_hashkey);
ctl.entrysize = sizeof(plpgsql_HashEnt);
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.129 2005/02/22 04:43:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.130 2005/02/22 07:18:24 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
* Local function forward declarations
************************************************************/
static void plpgsql_exec_error_callback(void *arg);
+static PLpgSQL_datum *copy_plpgsql_datum(PLpgSQL_datum *datum);
static PLpgSQL_var *copy_var(PLpgSQL_var *var);
static PLpgSQL_rec *copy_rec(PLpgSQL_rec *rec);
static int exec_stmt_block(PLpgSQL_execstate *estate,
PLpgSQL_stmt_block *block);
static int exec_stmts(PLpgSQL_execstate *estate,
- PLpgSQL_stmts *stmts);
+ List *stmts);
static int exec_stmt(PLpgSQL_execstate *estate,
PLpgSQL_stmt *stmt);
static int exec_stmt_assign(PLpgSQL_execstate *estate,
*/
estate.err_text = gettext_noop("during initialization of execution state");
for (i = 0; i < func->ndatums; i++)
- {
- switch (func->datums[i]->dtype)
- {
- case PLPGSQL_DTYPE_VAR:
- estate.datums[i] = (PLpgSQL_datum *)
- copy_var((PLpgSQL_var *) (func->datums[i]));
- break;
-
- case PLPGSQL_DTYPE_REC:
- estate.datums[i] = (PLpgSQL_datum *)
- copy_rec((PLpgSQL_rec *) (func->datums[i]));
- break;
-
- case PLPGSQL_DTYPE_ROW:
- case PLPGSQL_DTYPE_RECFIELD:
- case PLPGSQL_DTYPE_ARRAYELEM:
- estate.datums[i] = func->datums[i];
- break;
-
- default:
- elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype);
- }
- }
+ estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
/*
* Store the actual call argument values into the variables
*/
estate.err_text = gettext_noop("during initialization of execution state");
for (i = 0; i < func->ndatums; i++)
- {
- switch (func->datums[i]->dtype)
- {
- case PLPGSQL_DTYPE_VAR:
- estate.datums[i] = (PLpgSQL_datum *)
- copy_var((PLpgSQL_var *) (func->datums[i]));
- break;
-
- case PLPGSQL_DTYPE_REC:
- estate.datums[i] = (PLpgSQL_datum *)
- copy_rec((PLpgSQL_rec *) (func->datums[i]));
- break;
-
- case PLPGSQL_DTYPE_ROW:
- case PLPGSQL_DTYPE_RECFIELD:
- case PLPGSQL_DTYPE_ARRAYELEM:
- case PLPGSQL_DTYPE_TRIGARG:
- estate.datums[i] = func->datums[i];
- break;
-
- default:
- elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype);
- }
- }
+ estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
/*
* Put the OLD and NEW tuples into record variables
* Support functions for copying local execution variables
* ----------
*/
+static PLpgSQL_datum *
+copy_plpgsql_datum(PLpgSQL_datum *datum)
+{
+ PLpgSQL_datum *result = NULL;
+
+ switch (datum->dtype)
+ {
+ case PLPGSQL_DTYPE_VAR:
+ result = (PLpgSQL_datum *) copy_var((PLpgSQL_var *) datum);
+ break;
+
+ case PLPGSQL_DTYPE_REC:
+ result = (PLpgSQL_datum *) copy_rec((PLpgSQL_rec *) datum);
+ break;
+
+ case PLPGSQL_DTYPE_ROW:
+ case PLPGSQL_DTYPE_RECFIELD:
+ case PLPGSQL_DTYPE_ARRAYELEM:
+ case PLPGSQL_DTYPE_TRIGARG:
+ result = datum;
+ break;
+
+ default:
+ elog(ERROR, "unrecognized dtype: %d", datum->dtype);
+ }
+
+ return result;
+}
+
static PLpgSQL_var *
copy_var(PLpgSQL_var *var)
{
}
PG_CATCH();
{
- ErrorData *edata;
- PLpgSQL_exceptions *exceptions;
- int j;
+ ErrorData *edata;
+ ListCell *e;
/* Save error info */
MemoryContextSwitchTo(oldcontext);
SPI_restore_connection();
/* Look for a matching exception handler */
- exceptions = block->exceptions;
- for (j = 0; j < exceptions->exceptions_used; j++)
+ foreach (e, block->exceptions)
{
- PLpgSQL_exception *exception = exceptions->exceptions[j];
+ PLpgSQL_exception *exception = (PLpgSQL_exception *) lfirst(e);
if (exception_matches_conditions(edata, exception->conditions))
{
}
/* If no match found, re-throw the error */
- if (j >= exceptions->exceptions_used)
+ if (e == NULL)
ReThrowError(edata);
else
FreeErrorData(edata);
* ----------
*/
static int
-exec_stmts(PLpgSQL_execstate *estate, PLpgSQL_stmts *stmts)
+exec_stmts(PLpgSQL_execstate *estate, List *stmts)
{
- int rc;
- int i;
+ ListCell *s;
- for (i = 0; i < stmts->stmts_used; i++)
+ foreach (s, stmts)
{
- rc = exec_stmt(estate, stmts->stmts[i]);
+ PLpgSQL_stmt *stmt = (PLpgSQL_stmt *) lfirst(s);
+ int rc = exec_stmt(estate, stmt);
if (rc != PLPGSQL_RC_OK)
return rc;
}
static int
exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
{
- int i;
- PLpgSQL_datum *var;
- bool isnull = false;
+ ListCell *lc;
- for (i = 0; i < stmt->ndtitems; i++)
+ foreach (lc, stmt->diag_items)
{
- PLpgSQL_diag_item *dtitem = &stmt->dtitems[i];
+ PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
+ PLpgSQL_datum *var;
+ bool isnull = false;
- if (dtitem->target <= 0)
+ if (diag_item->target <= 0)
continue;
- var = (estate->datums[dtitem->target]);
+ var = estate->datums[diag_item->target];
if (var == NULL)
continue;
- switch (dtitem->item)
+ switch (diag_item->kind)
{
case PLPGSQL_GETDIAG_ROW_COUNT:
default:
elog(ERROR, "unrecognized attribute request: %d",
- dtitem->item);
+ diag_item->kind);
}
}
if (!isnull && value)
{
- if (stmt->true_body != NULL)
+ if (stmt->true_body != NIL)
return exec_stmts(estate, stmt->true_body);
}
else
{
- if (stmt->false_body != NULL)
+ if (stmt->false_body != NIL)
return exec_stmts(estate, stmt->false_body);
}
if (estate->fn_rettype == VOIDOID)
{
/* Special hack for function returning VOID */
+ Assert(stmt->expr == NULL);
estate->retval = (Datum) 0;
estate->retisnull = false;
estate->rettype = VOIDOID;
static int
exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
{
- Oid paramtypeid;
- Datum paramvalue;
- bool paramisnull;
- char *extval;
- int pidx = 0;
- char c[2] = {0, 0};
char *cp;
PLpgSQL_dstring ds;
+ ListCell *current_param;
plpgsql_dstring_init(&ds);
+ current_param = list_head(stmt->params);
for (cp = stmt->message; *cp; cp++)
{
/*
- * Occurrences of a single % are replaced by the next argument's
+ * Occurrences of a single % are replaced by the next parameter's
* external representation. Double %'s are converted to one %.
*/
- if ((c[0] = *cp) == '%')
+ if (cp[0] == '%')
{
- cp++;
- if (*cp == '%')
- {
- plpgsql_dstring_append(&ds, c);
- continue;
- }
- cp--;
- if (pidx >= stmt->nparams)
+ Oid paramtypeid;
+ Datum paramvalue;
+ bool paramisnull;
+ char *extval;
+
+ if (cp[1] == '%')
{
- plpgsql_dstring_append(&ds, c);
+ plpgsql_dstring_append_char(&ds, cp[1]);
+ cp++;
continue;
}
- exec_eval_datum(estate, estate->datums[stmt->params[pidx]],
+
+ if (current_param == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("too few parameters specified for RAISE")));
+
+ exec_eval_datum(estate, estate->datums[lfirst_int(current_param)],
InvalidOid,
¶mtypeid, ¶mvalue, ¶misnull);
if (paramisnull)
else
extval = convert_value_to_string(paramvalue, paramtypeid);
plpgsql_dstring_append(&ds, extval);
- pidx++;
+ current_param = lnext(current_param);
continue;
}
- plpgsql_dstring_append(&ds, c);
+ plpgsql_dstring_append_char(&ds, cp[0]);
}
+ /*
+ * If more parameters were specified than were required to process
+ * the format string, throw an error
+ */
+ if (current_param != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("too many parameters specified for RAISE")));
+
/*
* Throw the error (may or may not come back)
*/
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.38 2004/10/10 23:37:45 neilc Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.39 2005/02/22 07:18:24 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
plpgsql_dstring_init(PLpgSQL_dstring *ds)
{
ds->value = palloc(ds->alloc = 512);
- ds->used = 0;
+ ds->used = 1;
ds->value[0] = '\0';
}
pfree(ds->value);
}
+static void
+plpgsql_dstring_expand(PLpgSQL_dstring *ds, int needed)
+{
+ /* Don't allow truncating the string */
+ Assert(needed > ds->alloc);
+ Assert(ds->used <= ds->alloc);
+
+ /* Might have to double more than once, if needed is large */
+ do
+ {
+ ds->alloc *= 2;
+ } while (needed > ds->alloc);
+ ds->value = repalloc(ds->value, ds->alloc);
+}
/* ----------
* plpgsql_dstring_append Dynamic string extending
plpgsql_dstring_append(PLpgSQL_dstring *ds, const char *str)
{
int len = strlen(str);
- int needed = ds->used + len + 1;
+ int needed = ds->used + len;
if (needed > ds->alloc)
- {
- /* might have to double more than once, if len is large */
- do
- {
- ds->alloc *= 2;
- } while (needed > ds->alloc);
- ds->value = repalloc(ds->value, ds->alloc);
- }
+ plpgsql_dstring_expand(ds, needed);
- strcpy(&(ds->value[ds->used]), str);
+ memcpy(&(ds->value[ds->used - 1]), str, len);
ds->used += len;
+ ds->value[ds->used - 1] = '\0';
+}
+
+/* ----------
+ * plpgsql_dstring_append_char Append a single character
+ * to a dynamic string
+ * ----------
+ */
+void
+plpgsql_dstring_append_char(PLpgSQL_dstring *ds, char c)
+{
+ if (ds->used == ds->alloc)
+ plpgsql_dstring_expand(ds, ds->used + 1);
+
+ ds->value[ds->used - 1] = c;
+ ds->value[ds->used] = '\0';
+ ds->used++;
}
* ----------
*/
void
-plpgsql_ns_additem(int itemtype, int itemno, char *name)
+plpgsql_ns_additem(int itemtype, int itemno, const char *name)
{
PLpgSQL_ns *ns = ns_current;
PLpgSQL_nsitem *nse;
int i;
/*
- * Lookup in the current namespace only
- */
-
- /*
- * Lookup name in the namestack
+ * Lookup name in the namestack; do the lookup in the current
+ * namespace only.
*/
for (ns = ns_current; ns != NULL; ns = ns->upper)
{
}
static void
-dump_stmts(PLpgSQL_stmts *stmts)
+dump_stmts(List *stmts)
{
- int i;
+ ListCell *s;
dump_indent += 2;
- for (i = 0; i < stmts->stmts_used; i++)
- dump_stmt(stmts->stmts[i]);
+ foreach (s, stmts)
+ dump_stmt((PLpgSQL_stmt *) lfirst(s));
dump_indent -= 2;
}
static void
dump_block(PLpgSQL_stmt_block *block)
{
- int i;
char *name;
if (block->label == NULL)
if (block->exceptions)
{
- for (i = 0; i < block->exceptions->exceptions_used; i++)
+ ListCell *e;
+
+ foreach (e, block->exceptions)
{
- PLpgSQL_exception *exc = block->exceptions->exceptions[i];
+ PLpgSQL_exception *exc = (PLpgSQL_exception *) lfirst(e);
PLpgSQL_condition *cond;
dump_ind();
static void
dump_raise(PLpgSQL_stmt_raise *stmt)
{
- int i;
+ ListCell *l;
dump_ind();
printf("RAISE '%s'", stmt->message);
- for (i = 0; i < stmt->nparams; i++)
- printf(" %d", stmt->params[i]);
+ foreach (l, stmt->params)
+ printf(" %d", lfirst_int(l));
printf("\n");
}
static void
dump_getdiag(PLpgSQL_stmt_getdiag *stmt)
{
- int i;
+ ListCell *lc;
dump_ind();
printf("GET DIAGNOSTICS ");
- for (i = 0; i < stmt->ndtitems; i++)
+ foreach (lc, stmt->diag_items)
{
- PLpgSQL_diag_item *dtitem = &stmt->dtitems[i];
+ PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
- if (i != 0)
+ if (lc != list_head(stmt->diag_items))
printf(", ");
- printf("{var %d} = ", dtitem->target);
+ printf("{var %d} = ", diag_item->target);
- switch (dtitem->item)
+ switch (diag_item->kind)
{
case PLPGSQL_GETDIAG_ROW_COUNT:
printf("ROW_COUNT");
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.23 2004/08/01 17:32:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.24 2005/02/22 07:18:24 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
extern DLLIMPORT bool check_function_bodies;
-static int plpgsql_firstcall = 1;
+static bool plpgsql_firstcall = true;
static void plpgsql_init_all(void);
return;
plpgsql_HashTableInit();
-
RegisterXactCallback(plpgsql_xact_cb, NULL);
-
- plpgsql_firstcall = 0;
+ plpgsql_firstcall = false;
}
/*
plpgsql_init_all(void)
{
/* Execute any postmaster-startup safe initialization */
- if (plpgsql_firstcall)
- plpgsql_init();
+ plpgsql_init();
/*
* Any other initialization that must be done each time a new backend
* starts -- currently none
*/
-
}
/* ----------
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.56 2004/09/16 16:58:44 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.57 2005/02/22 07:18:24 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
**********************************************************************/
/* ----------
- * Compilers namestack item types
+ * Compiler's namestack item types
* ----------
*/
enum
typedef struct
{ /* Dynamic string control structure */
int alloc;
- int used;
+ int used; /* Including NUL terminator */
char *value;
} PLpgSQL_dstring;
} PLpgSQL_nsitem;
+/* XXX: consider adapting this to use List */
typedef struct PLpgSQL_ns
{ /* Compiler namestack level */
int items_alloc;
} PLpgSQL_stmt;
-typedef struct
-{ /* List of execution nodes */
- int stmts_alloc; /* XXX this oughta just be a List ... */
- int stmts_used;
- PLpgSQL_stmt **stmts;
-} PLpgSQL_stmts;
-
-
typedef struct PLpgSQL_condition
{ /* One EXCEPTION condition name */
int sqlerrstate; /* SQLSTATE code */
{ /* One EXCEPTION ... WHEN clause */
int lineno;
PLpgSQL_condition *conditions;
- PLpgSQL_stmts *action;
+ List *action; /* List of statements */
} PLpgSQL_exception;
-typedef struct
-{ /* List of WHEN clauses */
- int exceptions_alloc; /* XXX this oughta just be a List
- * ... */
- int exceptions_used;
- PLpgSQL_exception **exceptions;
-} PLpgSQL_exceptions;
-
-
typedef struct
{ /* Block of statements */
int cmd_type;
int lineno;
char *label;
- PLpgSQL_stmts *body;
- PLpgSQL_exceptions *exceptions;
+ List *body; /* List of statements */
+ List *exceptions; /* List of WHEN clauses */
int n_initvars;
int *initvarnos;
} PLpgSQL_stmt_block;
typedef struct
{ /* Get Diagnostics item */
- int item; /* id for diagnostic value desired */
+ int kind; /* id for diagnostic value desired */
int target; /* where to assign it */
} PLpgSQL_diag_item;
{ /* Get Diagnostics statement */
int cmd_type;
int lineno;
- int ndtitems;
- PLpgSQL_diag_item *dtitems;
+ List *diag_items; /* List of PLpgSQL_diag_item */
} PLpgSQL_stmt_getdiag;
int cmd_type;
int lineno;
PLpgSQL_expr *cond;
- PLpgSQL_stmts *true_body;
- PLpgSQL_stmts *false_body;
+ List *true_body; /* List of statements */
+ List *false_body; /* List of statements */
} PLpgSQL_stmt_if;
int cmd_type;
int lineno;
char *label;
- PLpgSQL_stmts *body;
+ List *body; /* List of statements */
} PLpgSQL_stmt_loop;
int lineno;
char *label;
PLpgSQL_expr *cond;
- PLpgSQL_stmts *body;
+ List *body; /* List of statements */
} PLpgSQL_stmt_while;
PLpgSQL_expr *lower;
PLpgSQL_expr *upper;
int reverse;
- PLpgSQL_stmts *body;
+ List *body; /* List of statements */
} PLpgSQL_stmt_fori;
PLpgSQL_rec *rec;
PLpgSQL_row *row;
PLpgSQL_expr *query;
- PLpgSQL_stmts *body;
+ List *body; /* List of statements */
} PLpgSQL_stmt_fors;
PLpgSQL_rec *rec;
PLpgSQL_row *row;
PLpgSQL_expr *query;
- PLpgSQL_stmts *body;
+ List *body; /* List of statements */
} PLpgSQL_stmt_dynfors;
int lineno;
int elog_level;
char *message;
- int nparams;
- int *params;
+ List *params;
} PLpgSQL_stmt_raise;
CommandId fn_cmin;
int fn_functype;
PLpgSQL_func_hashkey *fn_hashkey; /* back-link to hashtable key */
+ MemoryContext fn_cxt;
Oid fn_rettype;
int fn_rettyplen;
* Global variable declarations
**********************************************************************/
-extern int plpgsql_DumpExecTree;
-extern int plpgsql_SpaceScanned;
+extern bool plpgsql_DumpExecTree;
+extern bool plpgsql_SpaceScanned;
extern int plpgsql_nDatums;
extern PLpgSQL_datum **plpgsql_Datums;
#define plpgsql_yytext plpgsql_base_yytext
extern PLpgSQL_function *plpgsql_curr_compile;
+extern bool plpgsql_check_syntax;
+extern MemoryContext compile_tmp_cxt;
/**********************************************************************
* Function declarations
extern int plpgsql_parse_dblwordrowtype(char *word);
extern PLpgSQL_type *plpgsql_parse_datatype(const char *string);
extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod);
-extern PLpgSQL_variable *plpgsql_build_variable(char *refname, int lineno,
+extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
PLpgSQL_type *dtype,
bool add2namespace);
extern PLpgSQL_condition *plpgsql_parse_err_condition(char *condname);
extern void plpgsql_adddatum(PLpgSQL_datum *new);
extern int plpgsql_add_initdatums(int **varnos);
extern void plpgsql_HashTableInit(void);
+extern void plpgsql_compile_error_callback(void *arg);
/* ----------
* Functions in pl_handler.c
extern void plpgsql_dstring_init(PLpgSQL_dstring *ds);
extern void plpgsql_dstring_free(PLpgSQL_dstring *ds);
extern void plpgsql_dstring_append(PLpgSQL_dstring *ds, const char *str);
+extern void plpgsql_dstring_append_char(PLpgSQL_dstring *ds, char c);
extern char *plpgsql_dstring_get(PLpgSQL_dstring *ds);
/* ----------
extern bool plpgsql_ns_setlocal(bool flag);
extern void plpgsql_ns_push(char *label);
extern void plpgsql_ns_pop(void);
-extern void plpgsql_ns_additem(int itemtype, int itemno, char *name);
+extern void plpgsql_ns_additem(int itemtype, int itemno, const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_lookup(char *name, char *nsname);
extern void plpgsql_ns_rename(char *oldname, char *newname);
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.38 2004/12/17 03:51:36 neilc Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.39 2005/02/22 07:18:24 neilc Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
static const char *scanstr; /* original input string */
static int scanner_functype;
-static int scanner_typereported;
+static bool scanner_typereported;
static int pushback_token;
static bool have_pushback_token;
static int lookahead_token;
static char *dolqstart; /* current $foo$ quote start string */
static int dolqlen; /* signal to plpgsql_get_string_value */
-int plpgsql_SpaceScanned = 0;
+bool plpgsql_SpaceScanned = false;
%}
%option 8bit
* ----------
*/
BEGIN(INITIAL);
- plpgsql_SpaceScanned = 0;
+ plpgsql_SpaceScanned = false;
/* ----------
* On the first call to a new source report the
*/
if (!scanner_typereported)
{
- scanner_typereported = 1;
+ scanner_typereported = true;
return scanner_functype;
}
* Ignore whitespaces but remember this happened
* ----------
*/
-{space}+ { plpgsql_SpaceScanned = 1; }
+{space}+ { plpgsql_SpaceScanned = true; }
/* ----------
* Eat up comments
\/\* { start_lineno = plpgsql_scanner_lineno();
BEGIN(IN_COMMENT);
}
-<IN_COMMENT>\*\/ { BEGIN(INITIAL); plpgsql_SpaceScanned = 1; }
+<IN_COMMENT>\*\/ { BEGIN(INITIAL); plpgsql_SpaceScanned = true; }
<IN_COMMENT>\n ;
<IN_COMMENT>. ;
<IN_COMMENT><<EOF>> {
scanstr = str;
scanner_functype = functype;
- scanner_typereported = 0;
+ scanner_typereported = false;
have_pushback_token = false;
have_lookahead_token = false;
/*
* Called after a T_STRING token is read to get the string literal's value
- * as a malloc'd string. (We make this a separate call because in many
+ * as a palloc'd string. (We make this a separate call because in many
* scenarios there's no need to get the decoded value.)
*
* Note: we expect the literal to be the most recently lexed token. This
/* Token is a $foo$...$foo$ string */
len = yyleng - 2 * dolqlen;
Assert(len >= 0);
- result = (char *) malloc(len + 1);
+ result = (char *) palloc(len + 1);
memcpy(result, yytext + dolqlen, len);
result[len] = '\0';
}
else
{
/* Token is a '...' string */
- result = (char *) malloc(yyleng + 1); /* more than enough room */
+ result = (char *) palloc(yyleng + 1); /* more than enough room */
len = 0;
for (cp = yytext; *cp; cp++)
{
f | t
(1 row)
+--
+-- tests for "raise" processing
+--
+create function raise_test1(int) returns int as $$
+begin
+ raise notice 'This message has too many parameters!', $1;
+ return $1;
+end;
+$$ language plpgsql;
+select raise_test1(5);
+ERROR: too many parameters specified for RAISE
+CONTEXT: PL/pgSQL function "raise_test1" line 2 at raise
+create function raise_test2(int) returns int as $$
+begin
+ raise notice 'This message has too few parameters: %, %, %', $1, $1;
+ return $1;
+end;
+$$ language plpgsql;
+select raise_test2(10);
+ERROR: too few parameters specified for RAISE
+CONTEXT: PL/pgSQL function "raise_test2" line 2 at raise
+--
+-- reject function definitions that contain malformed SQL queries at
+-- compile-time, where possible
+--
+create function bad_sql1() returns int as $$
+declare a int;
+begin
+ a := 5;
+ Johnny Yuma;
+ a := 10;
+ return a;
+end$$ language plpgsql;
+ERROR: syntax error at or near "Johnny" at character 1
+QUERY: Johnny Yuma
+CONTEXT: SQL statement in PL/PgSQL function "bad_sql1" near line 4
+LINE 1: Johnny Yuma
+ ^
+create function bad_sql2() returns int as $$
+declare r record;
+begin
+ for r in select I fought the law, the law won LOOP
+ raise notice 'in loop';
+ end loop;
+ return 5;
+end;$$ language plpgsql;
+ERROR: syntax error at or near "fought" at character 11
+QUERY: select I fought the law, the law won
+CONTEXT: SQL statement in PL/PgSQL function "bad_sql2" near line 3
+LINE 1: select I fought the law, the law won
+ ^
+-- a RETURN expression is mandatory, except for void-returning
+-- functions, where it is not allowed
+create function missing_return_expr() returns int as $$
+begin
+ return ;
+end;$$ language plpgsql;
+ERROR: syntax error at end of input at character 8
+QUERY: SELECT
+CONTEXT: SQL statement in PL/PgSQL function "missing_return_expr" near line 2
+LINE 1: SELECT
+ ^
+create function void_return_expr() returns void as $$
+begin
+ return 5;
+end;$$ language plpgsql;
+ERROR: function returning void cannot specify RETURN expression at or near "5" at character 72
+LINE 3: return 5;
+ ^
select refcursor_test2(20000) as "Should be false",
refcursor_test2(20) as "Should be true";
+
+--
+-- tests for "raise" processing
+--
+create function raise_test1(int) returns int as $$
+begin
+ raise notice 'This message has too many parameters!', $1;
+ return $1;
+end;
+$$ language plpgsql;
+
+select raise_test1(5);
+
+create function raise_test2(int) returns int as $$
+begin
+ raise notice 'This message has too few parameters: %, %, %', $1, $1;
+ return $1;
+end;
+$$ language plpgsql;
+
+select raise_test2(10);
+
+--
+-- reject function definitions that contain malformed SQL queries at
+-- compile-time, where possible
+--
+create function bad_sql1() returns int as $$
+declare a int;
+begin
+ a := 5;
+ Johnny Yuma;
+ a := 10;
+ return a;
+end$$ language plpgsql;
+
+create function bad_sql2() returns int as $$
+declare r record;
+begin
+ for r in select I fought the law, the law won LOOP
+ raise notice 'in loop';
+ end loop;
+ return 5;
+end;$$ language plpgsql;
+
+-- a RETURN expression is mandatory, except for void-returning
+-- functions, where it is not allowed
+create function missing_return_expr() returns int as $$
+begin
+ return ;
+end;$$ language plpgsql;
+
+create function void_return_expr() returns void as $$
+begin
+ return 5;
+end;$$ language plpgsql;
\ No newline at end of file