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.
This commit is contained in:
Neil Conway 2005-02-22 07:18:27 +00:00
parent e3ebe2521e
commit 5a9dd0dc4f
9 changed files with 962 additions and 923 deletions

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
* 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.
*
@ -76,10 +76,14 @@ static int datums_last = 0;
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
* ----------
@ -118,7 +122,6 @@ static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
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);
@ -130,24 +133,7 @@ static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key)
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.
@ -187,10 +173,6 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
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;
@ -205,12 +187,8 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
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;
}
}
@ -250,6 +228,19 @@ plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator)
/*
* 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,
@ -273,6 +264,7 @@ 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
@ -293,7 +285,7 @@ do_compile(FunctionCallInfo fcinfo,
* 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;
@ -302,30 +294,46 @@ do_compile(FunctionCallInfo fcinfo,
*/
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
@ -398,7 +406,7 @@ do_compile(FunctionCallInfo fcinfo,
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
@ -407,7 +415,7 @@ do_compile(FunctionCallInfo fcinfo,
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);
}
@ -415,9 +423,13 @@ do_compile(FunctionCallInfo fcinfo,
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++)
{
@ -449,7 +461,7 @@ do_compile(FunctionCallInfo fcinfo,
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)
@ -471,29 +483,23 @@ do_compile(FunctionCallInfo fcinfo,
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;
@ -501,13 +507,10 @@ do_compile(FunctionCallInfo fcinfo,
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;
@ -515,58 +518,44 @@ do_compile(FunctionCallInfo fcinfo,
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;
@ -584,7 +573,7 @@ do_compile(FunctionCallInfo fcinfo,
/*
* 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;
@ -611,7 +600,7 @@ do_compile(FunctionCallInfo fcinfo,
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;
@ -632,16 +621,21 @@ do_compile(FunctionCallInfo fcinfo,
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)
@ -725,11 +719,10 @@ plpgsql_parse_word(char *word)
{
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() != '[')
@ -851,9 +844,9 @@ plpgsql_parse_dblword(char *word)
*/
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);
@ -957,9 +950,9 @@ plpgsql_parse_tripword(char *word)
*/
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);
@ -1038,7 +1031,7 @@ plpgsql_parse_wordtype(char *word)
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);
@ -1112,13 +1105,18 @@ plpgsql_parse_dblwordtype(char *word)
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 */
@ -1127,7 +1125,6 @@ plpgsql_parse_dblwordtype(char *word)
word[i] = '.';
plpgsql_convert_ident(word, cp, 3);
word[i] = '%';
pfree(cp[2]);
/*
* Lookup the first word
@ -1135,8 +1132,8 @@ plpgsql_parse_dblwordtype(char *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)
{
@ -1146,26 +1143,15 @@ plpgsql_parse_dblwordtype(char *word)
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;
}
/*
@ -1173,20 +1159,13 @@ plpgsql_parse_dblwordtype(char *word)
*/
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
@ -1196,26 +1175,16 @@ plpgsql_parse_dblwordtype(char *word)
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);
@ -1223,16 +1192,24 @@ plpgsql_parse_dblwordtype(char *word)
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;
}
/* ----------
@ -1245,17 +1222,22 @@ int
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;
@ -1284,20 +1266,13 @@ plpgsql_parse_tripwordtype(char *word)
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
@ -1307,29 +1282,17 @@ plpgsql_parse_tripwordtype(char *word)
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);
@ -1337,17 +1300,24 @@ plpgsql_parse_tripwordtype(char *word)
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;
}
/* ----------
@ -1403,15 +1373,18 @@ plpgsql_parse_dblwordrowtype(char *word)
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"));
@ -1421,26 +1394,25 @@ plpgsql_parse_dblwordrowtype(char *word)
(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;
@ -1452,11 +1424,9 @@ plpgsql_build_variable(char *refname, int lineno, PLpgSQL_type *dtype,
/* 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 */
@ -1482,7 +1452,7 @@ plpgsql_build_variable(char *refname, int lineno, PLpgSQL_type *dtype,
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);
@ -1501,11 +1471,9 @@ plpgsql_build_variable(char *refname, int lineno, PLpgSQL_type *dtype,
*/
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);
@ -1517,14 +1485,12 @@ plpgsql_build_variable(char *refname, int lineno, PLpgSQL_type *dtype,
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 */
@ -1545,7 +1511,6 @@ build_row_var(Oid classOid)
Form_pg_class classStruct;
const char *relname;
int i;
MemoryContext oldcxt;
/*
* Open the relation to get info.
@ -1567,23 +1532,12 @@ build_row_var(Oid classOid)
* 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++)
{
@ -1592,19 +1546,16 @@ build_row_var(Oid classOid)
/*
* 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
@ -1621,10 +1572,8 @@ build_row_var(Oid classOid)
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
@ -1697,9 +1646,9 @@ build_datatype(HeapTuple typeTup, int32 typmod)
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)
{
@ -1726,7 +1675,7 @@ build_datatype(HeapTuple typeTup, int32 typmod)
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;
@ -1757,7 +1706,7 @@ plpgsql_parse_err_condition(char *condname)
*/
if (strcmp(condname, "others") == 0)
{
new = malloc(sizeof(PLpgSQL_condition));
new = palloc(sizeof(PLpgSQL_condition));
new->sqlerrstate = 0;
new->condname = condname;
new->next = NULL;
@ -1769,7 +1718,7 @@ plpgsql_parse_err_condition(char *condname)
{
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;
@ -1836,7 +1785,7 @@ plpgsql_add_initdatums(int **varnos)
{
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++)
@ -1930,12 +1879,30 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
}
}
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);

View File

@ -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)
{
@ -920,9 +905,8 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
}
PG_CATCH();
{
ErrorData *edata;
PLpgSQL_exceptions *exceptions;
int j;
ErrorData *edata;
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) == '%')
if (cp[0] == '%')
{
cp++;
if (*cp == '%')
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;
}
cp--;
if (pidx >= stmt->nparams)
{
plpgsql_dstring_append(&ds, c);
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)
*/

View File

@ -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");

View File

@ -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();
plpgsql_init();
/*
* Any other initialization that must be done each time a new backend
* starts -- currently none
*/
}
/* ----------

View File

@ -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);

View File

@ -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++)
{

View File

@ -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;
^

View File

@ -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;