* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.26 2001/10/09 04:15:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.27 2001/10/09 15:59:56 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
plpgsql_ns_setlocal(false);
name = plpgsql_tolower(yytext);
if (name[0] != '$')
+ {
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "can only alias positional parameters");
+ }
nsi = plpgsql_ns_lookup(name, NULL);
if (nsi == NULL)
+ {
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "function has no parameter %s", name);
+ }
plpgsql_ns_setlocal(true);
{
case 0:
plpgsql_error_lineno = lno;
- plpgsql_comperrinfo();
elog(ERROR, "unexpected end of file");
case K_NULL:
if (yylex() != ';')
{
plpgsql_error_lineno = lno;
- plpgsql_comperrinfo();
- elog(ERROR, "expectec ; after NULL");
+ elog(ERROR, "expected ; after NULL");
}
free(expr);
plpgsql_dstring_free(&ds);
if (tok == 0)
{
plpgsql_error_lineno = lno;
- plpgsql_comperrinfo();
elog(ERROR, "unterminated default value");
}
if (plpgsql_SpaceScanned)
{
if (yylval.var->isconst)
{
- plpgsql_comperrinfo();
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "%s is declared CONSTANT; can not receive diagnostics", yylval.var->refname);
}
$$ = yylval.var->varno;
{
if (yylval.var->isconst)
{
- plpgsql_comperrinfo();
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "%s is declared CONSTANT", yylval.var->refname);
}
$$ = yylval.var->varno;
if (tok == 0)
{
plpgsql_error_lineno = lno;
- plpgsql_comperrinfo();
elog(ERROR, "missing .. to terminate lower bound of for loop");
}
plpgsql_dstring_append(&ds, yytext);
new->row = (PLpgSQL_row *)$4;
break;
default:
- plpgsql_comperrinfo();
elog(ERROR, "unknown dtype %d in stmt_fors", $4->dtype);
}
new->query = $7;
new->row = (PLpgSQL_row *)$4;
break;
default:
- plpgsql_comperrinfo();
elog(ERROR, "unknown dtype %d in stmt_dynfors", $4->dtype);
}
new->query = $7;
if (tok != K_FOR)
{
- plpgsql_comperrinfo();
+ plpgsql_error_lineno = $2;
elog(ERROR, "syntax error at \"%s\" - expected FOR to open a reference cursor", yytext);
}
break;
default:
- plpgsql_comperrinfo();
+ plpgsql_error_lineno = $2;
elog(ERROR, "syntax error at \"%s\"", yytext);
}
if (tok != '(')
{
- plpgsql_comperrinfo();
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "cursor %s has arguments", $3->refname);
}
--cp;
if (*cp != ')')
{
- plpgsql_comperrinfo();
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "missing )");
}
*cp = '\0';
if (tok == '(')
{
- plpgsql_comperrinfo();
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "cursor %s has no arguments", $3->refname);
}
if (tok != ';')
{
- plpgsql_comperrinfo();
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "syntax error at \"%s\"", yytext);
}
}
{
if (yylval.var->datatype->typoid != REFCURSOROID)
{
- plpgsql_comperrinfo();
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "%s must be of type cursor or refcursor", yylval.var->refname);
}
$$ = yylval.var;
{
if (yylval.var->datatype->typoid != REFCURSOROID)
{
- plpgsql_comperrinfo();
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "%s must be of type refcursor", yylval.var->refname);
}
$$ = yylval.var->varno;
{
case 0:
plpgsql_error_lineno = lno;
- plpgsql_comperrinfo();
elog(ERROR, "missing %s at end of SQL statement", s);
break;
if (tok == 0)
{
plpgsql_error_lineno = lno;
- plpgsql_comperrinfo();
elog(ERROR, "incomplete datatype declaration");
}
/* Possible followers for datatype in a declaration */
plpgsql_push_back_token(tok);
+ plpgsql_error_lineno = lno; /* in case of error in parse_datatype */
+
result = plpgsql_parse_datatype(plpgsql_dstring_get(&ds));
plpgsql_dstring_free(&ds);
if (tok == 0)
{
plpgsql_error_lineno = yylineno;
- plpgsql_comperrinfo();
elog(ERROR, "unexpected end of file");
}
plpgsql_dstring_append(&ds, yytext);
break;
default:
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "plpgsql: %s is not a variable or record field", yytext);
}
}
if (tok == 0)
{
plpgsql_error_lineno = yylineno;
- plpgsql_comperrinfo();
elog(ERROR, "unexpected end of file");
}
plpgsql_dstring_append(&ds, yytext);
if (tok == 0)
{
plpgsql_error_lineno = yylineno;
- plpgsql_comperrinfo();
elog(ERROR, "unexpected end of file");
}
plpgsql_dstring_append(&ds, yytext);
break;
default:
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "plpgsql: %s is not a variable or record field", yytext);
}
}
break;
default:
- {
- elog(ERROR, "syntax error at '%s'", yytext);
- }
+ plpgsql_error_lineno = yylineno;
+ elog(ERROR, "syntax error at '%s'", yytext);
}
if (!have_nexttok)
tok = yylex();
if (tok != ';')
+ {
+ plpgsql_error_lineno = yylineno;
elog(ERROR, "syntax error at '%s'", yytext);
+ }
fetch = malloc(sizeof(PLpgSQL_stmt_select));
memset(fetch, 0, sizeof(PLpgSQL_stmt_fetch));
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.35 2001/10/09 04:15:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.36 2001/10/09 15:59:56 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
#include
#include
#include
+#include
#include "pl.tab.h"
#include "fmgr.h"
#include "parser/gramparse.h"
#include "parser/parse_type.h"
+#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
PLpgSQL_rec *rec;
int i;
int arg_varnos[FUNC_MAX_ARGS];
-
- /*
- * Initialize the compiler
- */
- plpgsql_ns_init();
- plpgsql_ns_push(NULL);
- plpgsql_DumpExecTree = 0;
-
- datums_alloc = 128;
- plpgsql_nDatums = 0;
- plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);
- datums_last = 0;
+ sigjmp_buf save_restart;
/*
* Lookup the pg_proc tuple by Oid
plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname));
plpgsql_error_lineno = 0;
+ /*
+ * Catch elog() so we can provide notice about where the error is
+ */
+ memcpy(&save_restart, &Warn_restart, sizeof(save_restart));
+ if (sigsetjmp(Warn_restart, 1) != 0)
+ {
+ memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+
+ /*
+ * If we are the first of cascaded error catchings, print where
+ * this happened
+ */
+ if (plpgsql_error_funcname != NULL)
+ {
+ elog(NOTICE, "plpgsql: ERROR during compile of %s near line %d",
+ plpgsql_error_funcname, plpgsql_error_lineno);
+
+ plpgsql_error_funcname = NULL;
+ }
+
+ siglongjmp(Warn_restart, 1);
+ }
+
+ /*
+ * Initialize the compiler
+ */
+ plpgsql_ns_init();
+ plpgsql_ns_push(NULL);
+ plpgsql_DumpExecTree = 0;
+
+ datums_alloc = 128;
+ plpgsql_nDatums = 0;
+ plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);
+ datums_last = 0;
+
/*
* Create the new function node
*/
memset(function, 0, sizeof(PLpgSQL_function));
plpgsql_curr_compile = function;
- function->fn_functype = functype;
- function->fn_oid = fn_oid;
function->fn_name = strdup(NameStr(procStruct->proname));
+ function->fn_oid = fn_oid;
+ function->fn_xmin = procTup->t_data->t_xmin;
+ function->fn_cmin = procTup->t_data->t_cmin;
+ function->fn_functype = functype;
switch (functype)
{
0, 0, 0);
if (!HeapTupleIsValid(typeTup))
{
- plpgsql_comperrinfo();
if (!OidIsValid(procStruct->prorettype))
elog(ERROR, "plpgsql functions cannot return type \"opaque\""
"\n\texcept when used as triggers");
0, 0, 0);
if (!HeapTupleIsValid(typeTup))
{
- plpgsql_comperrinfo();
if (!OidIsValid(procStruct->proargtypes[i]))
elog(ERROR, "plpgsql functions cannot take type \"opaque\"");
else
*/
sprintf(buf, "%s%%rowtype", NameStr(typeStruct->typname));
if (plpgsql_parse_wordrowtype(buf) != T_ROW)
- {
- plpgsql_comperrinfo();
elog(ERROR, "cannot get tuple struct of argument %d",
i + 1);
- }
row = plpgsql_yylval.row;
sprintf(buf, "$%d", i + 1);
*/
parse_rc = plpgsql_yyparse();
if (parse_rc != 0)
- {
- plpgsql_comperrinfo();
elog(ERROR, "plpgsql: parser returned %d ???", parse_rc);
- }
/*
* If that was successful, complete the functions info.
ReleaseSysCache(procTup);
+ /*
+ * Restore the previous elog() jump target
+ */
+ plpgsql_error_funcname = NULL;
+ plpgsql_error_lineno = 0;
+ memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
+
/*
* Finally return the compiled function
*/
return T_VARIABLE;
}
}
- plpgsql_comperrinfo();
elog(ERROR, "row %s doesn't have a field %s",
word1, word2);
}
return T_VARIABLE;
}
}
- plpgsql_comperrinfo();
elog(ERROR, "row %s.%s doesn't have a field %s",
word1, word2, word3);
}
}
/*
- * It must be a (shared) relation class
+ * It must be a relation, sequence or view
*/
classStruct = (Form_pg_class) GETSTRUCT(classtup);
- if (classStruct->relkind != 'r' && classStruct->relkind != 's')
+ if (classStruct->relkind != RELKIND_RELATION &&
+ classStruct->relkind != RELKIND_SEQUENCE &&
+ classStruct->relkind != RELKIND_VIEW)
{
ReleaseSysCache(classtup);
pfree(word1);
ObjectIdGetDatum(attrStruct->atttypid),
0, 0, 0);
if (!HeapTupleIsValid(typetup))
- {
- plpgsql_comperrinfo();
elog(ERROR, "cache lookup for type %u of %s.%s failed",
attrStruct->atttypid, word1, word2);
- }
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
/*
PointerGetDatum(word1),
0, 0, 0);
if (!HeapTupleIsValid(classtup))
- {
- plpgsql_comperrinfo();
elog(ERROR, "%s: no such class", word1);
- }
classStruct = (Form_pg_class) GETSTRUCT(classtup);
/* accept relation, sequence, or view pg_class entries */
- if (classStruct->relkind != 'r' &&
- classStruct->relkind != 's' &&
- classStruct->relkind != 'v')
- {
- plpgsql_comperrinfo();
+ if (classStruct->relkind != RELKIND_RELATION &&
+ classStruct->relkind != RELKIND_SEQUENCE &&
+ classStruct->relkind != RELKIND_VIEW)
elog(ERROR, "%s isn't a table", word1);
- }
/*
* Fetch the table's pg_type tuple too
PointerGetDatum(word1),
0, 0, 0);
if (!HeapTupleIsValid(typetup))
- {
- plpgsql_comperrinfo();
elog(ERROR, "cache lookup for %s in pg_type failed", word1);
- }
/*
* Create a row datum entry and all the required variables that it
Int16GetDatum(i + 1),
0, 0);
if (!HeapTupleIsValid(attrtup))
- {
- plpgsql_comperrinfo();
elog(ERROR, "cache lookup for attribute %d of class %s failed",
i + 1, word1);
- }
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
cp = pstrdup(NameStr(attrStruct->attname));
ObjectIdGetDatum(attrStruct->atttypid),
0, 0, 0);
if (!HeapTupleIsValid(typetup))
- {
- plpgsql_comperrinfo();
elog(ERROR, "cache lookup for type %u of %s.%s failed",
attrStruct->atttypid, word1, cp);
- }
typeStruct = (Form_pg_type) GETSTRUCT(typetup);
/*
}
-/* ----------
- * plpgsql_comperrinfo Called before elog(ERROR, ...)
- * during compile.
- * ----------
- */
-void
-plpgsql_comperrinfo()
-{
- elog(NOTICE, "plpgsql: ERROR during compile of %s near line %d",
- plpgsql_error_funcname, plpgsql_error_lineno);
-}
-
-
/* ---------
* plpgsql_yyerror Handle parser error
* ---------
plpgsql_yyerror(const char *s)
{
plpgsql_error_lineno = plpgsql_yylineno;
- plpgsql_comperrinfo();
elog(ERROR, "%s at or near \"%s\"", s, plpgsql_yytext);
}
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.15 2001/07/12 17:42:08 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.16 2001/10/09 15:59:56 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
char *
plpgsql_tolower(char *s)
{
+ char *sstart = s;
char *ret;
char *cp;
*cp++ = *s++;
}
if (*s != '"')
- {
- plpgsql_comperrinfo();
- elog(ERROR, "unterminated \"");
- }
+ elog(ERROR, "unterminated \" in name %s", sstart);
s++;
}
else
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.8 2001/03/22 06:16:21 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.9 2001/10/09 15:59:56 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
static PLpgSQL_function *compiled_functions = NULL;
+static bool func_up_to_date(PLpgSQL_function *func);
+
+
/* ----------
* plpgsql_call_handler
*
plpgsql_call_handler(PG_FUNCTION_ARGS)
{
bool isTrigger = CALLED_AS_TRIGGER(fcinfo);
+ Oid funcOid = fcinfo->flinfo->fn_oid;
PLpgSQL_function *func;
Datum retval;
* (ie, current FmgrInfo has been used before)
*/
func = (PLpgSQL_function *) fcinfo->flinfo->fn_extra;
- if (func == NULL)
+ if (func != NULL)
{
-
+ Assert(func->fn_oid == funcOid);
/*
- * Check if we already compiled this function
+ * But is the function still up to date?
*/
- Oid funcOid = fcinfo->flinfo->fn_oid;
+ if (! func_up_to_date(func))
+ func = NULL;
+ }
+ if (func == NULL)
+ {
+ /*
+ * Check if we already compiled this function for another caller
+ */
for (func = compiled_functions; func != NULL; func = func->next)
{
- if (funcOid == func->fn_oid)
+ if (funcOid == func->fn_oid && func_up_to_date(func))
break;
}
return retval;
}
+
+
+/*
+ * Check to see if a compiled function is still up-to-date. This
+ * is needed because CREATE OR REPLACE FUNCTION can modify the
+ * function's pg_proc entry without changing its OID.
+ */
+static bool
+func_up_to_date(PLpgSQL_function *func)
+{
+ HeapTuple procTup;
+ bool result;
+
+ procTup = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(func->fn_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(procTup))
+ elog(ERROR, "plpgsql: cache lookup for proc %u failed",
+ func->fn_oid);
+
+ result = (func->fn_xmin == procTup->t_data->t_xmin &&
+ func->fn_cmin == procTup->t_data->t_cmin);
+
+ ReleaseSysCache(procTup);
+
+ return result;
+}
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.18 2001/10/09 04:15:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.19 2001/10/09 15:59:56 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
typedef struct PLpgSQL_function
{ /* Complete compiled function */
- Oid fn_oid;
char *fn_name;
+ Oid fn_oid;
+ TransactionId fn_xmin;
+ CommandId fn_cmin;
int fn_functype;
+
Oid fn_rettype;
int fn_rettyplen;
bool fn_retbyval;
int ndatums;
PLpgSQL_datum **datums;
PLpgSQL_stmt_block *action;
- struct PLpgSQL_function *next;
+
+ struct PLpgSQL_function *next; /* for chaining list of functions */
} PLpgSQL_function;
extern PLpgSQL_type *plpgsql_parse_datatype(char *string);
extern void plpgsql_adddatum(PLpgSQL_datum * new);
extern int plpgsql_add_initdatums(int **varnos);
-extern void plpgsql_comperrinfo(void);
extern void plpgsql_yyerror(const char *s);
/* ----------
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.15 2001/10/09 04:15:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.16 2001/10/09 15:59:56 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
\*\/ { BEGIN INITIAL; }
\n ;
. ;
-<> { plpgsql_comperrinfo();
- elog(ERROR, "unterminated comment starting on line %d",
- start_lineno);
+<> {
+ plpgsql_error_lineno = start_lineno;
+ elog(ERROR, "unterminated comment");
}
/* ----------
' { BEGIN INITIAL;
return T_STRING;
}
-<> { plpgsql_comperrinfo();
- elog(ERROR, "unterminated string starting on line %d",
- start_lineno);
+<> {
+ plpgsql_error_lineno = start_lineno;
+ elog(ERROR, "unterminated string");
}
[^'\\]* { yymore(); }