提交 5a9dd0dc 编写于 作者: N Neil Conway

This patch changes makes some significant changes to how compilation

and parsing work in PL/PgSQL:

- memory management is now done via palloc(). The compiled representation
  of each function now has its own memory context. Therefore, the storage
  consumed by a function can be reclaimed via MemoryContextDelete().

  During compilation, the CurrentMemoryContext is the function's memory
  context. This means that a palloc() is sufficient to allocate memory
  that will have the same lifetime as the function itself. As a result,
  code invoked during compilation should be careful to pfree() temporary
  allocations to avoid leaking memory. Since a lot of the code in the
  backend is not careful about releasing palloc'ed memory, that means
  we should switch into a temporary memory context before invoking
  backend functions. A temporary context appropriate for such allocations
  is `compile_tmp_cxt'.

- The ability to use palloc() allows us to simply a lot of the code in
  the parser. Rather than representing lists of elements via ad hoc
  linked lists or arrays, we can use the List type. Rather than doing
  malloc followed by memset(0), we can just use palloc0().

- We now check that the user has supplied the right number of parameters
  to a RAISE statement. Supplying either too few or too many results in
  an error (at runtime).

- PL/PgSQL's parser needs to accept arbitrary SQL statements. Since we
  do not want to duplicate the SQL grammar in the PL/PgSQL grammar, this
  means we need to be quite lax in what the PL/PgSQL grammar considers
  a "SQL statement". This can lead to misleading behavior if there is a
  syntax error in the function definition, since we assume a malformed
  PL/PgSQL construct is a SQL statement. Furthermore, these errors were
  only detected at runtime (when we tried to execute the alleged "SQL
  statement" via SPI).

  To rectify this, the patch changes the parser to invoke the main SQL
  parser when it sees a string it believes to be a SQL expression. This
  means that synctically-invalid SQL will be rejected during the
  compilation of the PL/PgSQL function. This is only done when compiling
  for "validation" purposes (i.e. at CREATE FUNCTION time), so it should
  not impose a runtime overhead.

- Fixes for the various buffer overruns I've patched in stable branches
  in the past few weeks. I've rewritten code where I thought it was
  warranted (unlike the patches applied to older branches, which were
  minimally invasive).

- Various other minor changes and cleanups.

- Updates to the regression tests.
上级 e3ebe252
此差异已折叠。
此差异已折叠。
......@@ -3,7 +3,7 @@
* 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.
*
......@@ -74,13 +74,14 @@ static PLpgSQL_expr *active_simple_exprs = NULL;
* 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,
......@@ -212,29 +213,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
*/
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
......@@ -467,30 +446,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
*/
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
......@@ -758,6 +714,35 @@ plpgsql_exec_error_callback(void *arg)
* 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)
{
......@@ -921,8 +906,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
PG_CATCH();
{
ErrorData *edata;
PLpgSQL_exceptions *exceptions;
int j;
ListCell *e;
/* Save error info */
MemoryContextSwitchTo(oldcontext);
......@@ -942,10 +926,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
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))
{
......@@ -955,7 +938,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
}
/* If no match found, re-throw the error */
if (j >= exceptions->exceptions_used)
if (e == NULL)
ReThrowError(edata);
else
FreeErrorData(edata);
......@@ -1005,14 +988,14 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
* ----------
*/
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;
}
......@@ -1184,23 +1167,23 @@ exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt)
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:
......@@ -1218,7 +1201,7 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt)
default:
elog(ERROR, "unrecognized attribute request: %d",
dtitem->item);
diag_item->kind);
}
}
......@@ -1242,12 +1225,12 @@ exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt)
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);
}
......@@ -1749,6 +1732,7 @@ exec_stmt_return(PLpgSQL_execstate *estate, PLpgSQL_stmt_return *stmt)
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;
......@@ -1903,38 +1887,39 @@ exec_init_tuple_store(PLpgSQL_execstate *estate)
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) == '%')
{
cp++;
if (*cp == '%')
if (cp[0] == '%')
{
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,
&paramtypeid, &paramvalue, &paramisnull);
if (paramisnull)
......@@ -1942,13 +1927,22 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
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)
*/
......
......@@ -3,7 +3,7 @@
* 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.
*
......@@ -59,7 +59,7 @@ void
plpgsql_dstring_init(PLpgSQL_dstring *ds)
{
ds->value = palloc(ds->alloc = 512);
ds->used = 0;
ds->used = 1;
ds->value[0] = '\0';
}
......@@ -74,6 +74,20 @@ plpgsql_dstring_free(PLpgSQL_dstring *ds)
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
......@@ -83,20 +97,30 @@ void
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++;
}
......@@ -187,7 +211,7 @@ plpgsql_ns_pop(void)
* ----------
*/
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;
......@@ -286,11 +310,8 @@ plpgsql_ns_rename(char *oldname, char *newname)
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)
{
......@@ -584,20 +605,19 @@ dump_stmt(PLpgSQL_stmt *stmt)
}
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)
......@@ -612,9 +632,11 @@ dump_block(PLpgSQL_stmt_block *block)
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();
......@@ -863,12 +885,12 @@ dump_return_next(PLpgSQL_stmt_return_next *stmt)
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");
}
......@@ -907,20 +929,20 @@ dump_dynfors(PLpgSQL_stmt_dynfors *stmt)
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");
......
......@@ -3,7 +3,7 @@
* 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.
*
......@@ -47,7 +47,7 @@
extern DLLIMPORT bool check_function_bodies;
static int plpgsql_firstcall = 1;
static bool plpgsql_firstcall = true;
static void plpgsql_init_all(void);
......@@ -65,10 +65,8 @@ plpgsql_init(void)
return;
plpgsql_HashTableInit();
RegisterXactCallback(plpgsql_xact_cb, NULL);
plpgsql_firstcall = 0;
plpgsql_firstcall = false;
}
/*
......@@ -78,14 +76,12 @@ static void
plpgsql_init_all(void)
{
/* Execute any postmaster-startup safe initialization */
if (plpgsql_firstcall)
plpgsql_init();
/*
* Any other initialization that must be done each time a new backend
* starts -- currently none
*/
}
/* ----------
......
......@@ -3,7 +3,7 @@
* 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.
*
......@@ -50,7 +50,7 @@
**********************************************************************/
/* ----------
* Compilers namestack item types
* Compiler's namestack item types
* ----------
*/
enum
......@@ -147,7 +147,7 @@ enum
typedef struct
{ /* Dynamic string control structure */
int alloc;
int used;
int used; /* Including NUL terminator */
char *value;
} PLpgSQL_dstring;
......@@ -298,6 +298,7 @@ typedef struct
} PLpgSQL_nsitem;
/* XXX: consider adapting this to use List */
typedef struct PLpgSQL_ns
{ /* Compiler namestack level */
int items_alloc;
......@@ -314,14 +315,6 @@ typedef struct
} 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 */
......@@ -333,26 +326,17 @@ typedef struct
{ /* 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;
......@@ -375,7 +359,7 @@ typedef struct
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;
......@@ -383,8 +367,7 @@ typedef struct
{ /* 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;
......@@ -393,8 +376,8 @@ typedef struct
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;
......@@ -403,7 +386,7 @@ typedef struct
int cmd_type;
int lineno;
char *label;
PLpgSQL_stmts *body;
List *body; /* List of statements */
} PLpgSQL_stmt_loop;
......@@ -413,7 +396,7 @@ typedef struct
int lineno;
char *label;
PLpgSQL_expr *cond;
PLpgSQL_stmts *body;
List *body; /* List of statements */
} PLpgSQL_stmt_while;
......@@ -426,7 +409,7 @@ typedef struct
PLpgSQL_expr *lower;
PLpgSQL_expr *upper;
int reverse;
PLpgSQL_stmts *body;
List *body; /* List of statements */
} PLpgSQL_stmt_fori;
......@@ -438,7 +421,7 @@ typedef struct
PLpgSQL_rec *rec;
PLpgSQL_row *row;
PLpgSQL_expr *query;
PLpgSQL_stmts *body;
List *body; /* List of statements */
} PLpgSQL_stmt_fors;
......@@ -450,7 +433,7 @@ typedef struct
PLpgSQL_rec *rec;
PLpgSQL_row *row;
PLpgSQL_expr *query;
PLpgSQL_stmts *body;
List *body; /* List of statements */
} PLpgSQL_stmt_dynfors;
......@@ -527,8 +510,7 @@ typedef struct
int lineno;
int elog_level;
char *message;
int nparams;
int *params;
List *params;
} PLpgSQL_stmt_raise;
......@@ -577,6 +559,7 @@ typedef struct PLpgSQL_function
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;
......@@ -649,8 +632,8 @@ typedef struct
* 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;
......@@ -663,6 +646,8 @@ extern char *plpgsql_base_yytext;
#define plpgsql_yytext plpgsql_base_yytext
extern PLpgSQL_function *plpgsql_curr_compile;
extern bool plpgsql_check_syntax;
extern MemoryContext compile_tmp_cxt;
/**********************************************************************
* Function declarations
......@@ -684,13 +669,14 @@ extern int plpgsql_parse_wordrowtype(char *word);
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
......@@ -717,6 +703,7 @@ extern void plpgsql_xact_cb(XactEvent event, void *arg);
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);
/* ----------
......@@ -727,7 +714,7 @@ extern void plpgsql_ns_init(void);
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);
......
......@@ -4,7 +4,7 @@
* 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.
*
......@@ -54,7 +54,7 @@ static char *scanbuf;
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;
......@@ -64,7 +64,7 @@ static int cur_line_num;
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
......@@ -114,7 +114,7 @@ dolqinside [^$]+
* ----------
*/
BEGIN(INITIAL);
plpgsql_SpaceScanned = 0;
plpgsql_SpaceScanned = false;
/* ----------
* On the first call to a new source report the
......@@ -123,7 +123,7 @@ dolqinside [^$]+
*/
if (!scanner_typereported)
{
scanner_typereported = 1;
scanner_typereported = true;
return scanner_functype;
}
......@@ -255,7 +255,7 @@ dump { return O_DUMP; }
* Ignore whitespaces but remember this happened
* ----------
*/
{space}+ { plpgsql_SpaceScanned = 1; }
{space}+ { plpgsql_SpaceScanned = true; }
/* ----------
* Eat up comments
......@@ -266,7 +266,7 @@ dump { return O_DUMP; }
\/\* { 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>> {
......@@ -502,7 +502,7 @@ plpgsql_scanner_init(const char *str, int functype)
scanstr = str;
scanner_functype = functype;
scanner_typereported = 0;
scanner_typereported = false;
have_pushback_token = false;
have_lookahead_token = false;
......@@ -538,7 +538,7 @@ plpgsql_scanner_finish(void)
/*
* 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
......@@ -557,14 +557,14 @@ plpgsql_get_string_value(void)
/* 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++)
{
......
......@@ -2173,3 +2173,72 @@ select refcursor_test2(20000) as "Should be false",
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;
^
......@@ -1863,3 +1863,58 @@ $$ language 'plpgsql';
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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册