Recognize plpgsql EXCEPTION condition names at function compile time

instead of runtime, for better detection of invalid condition names
(and maybe a little more speed, too).
This commit is contained in:
Tom Lane 2004-08-20 22:00:14 +00:00
parent 009b0d1a85
commit bf9d9bd2f3
4 changed files with 91 additions and 62 deletions

View File

@ -4,7 +4,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.60 2004/08/16 17:52:06 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.61 2004/08/20 22:00:14 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
@ -1568,7 +1568,7 @@ proc_exceptions : proc_exceptions proc_exception
new = malloc(sizeof(PLpgSQL_exceptions)); new = malloc(sizeof(PLpgSQL_exceptions));
memset(new, 0, sizeof(PLpgSQL_exceptions)); memset(new, 0, sizeof(PLpgSQL_exceptions));
new->exceptions_alloc = 64; new->exceptions_alloc = 16;
new->exceptions_used = 1; new->exceptions_used = 1;
new->exceptions = malloc(sizeof(PLpgSQL_exception *) * new->exceptions_alloc); new->exceptions = malloc(sizeof(PLpgSQL_exception *) * new->exceptions_alloc);
new->exceptions[0] = $1; new->exceptions[0] = $1;
@ -1594,32 +1594,17 @@ proc_exception : K_WHEN lno proc_conditions K_THEN proc_sect
proc_conditions : proc_conditions K_OR opt_lblname proc_conditions : proc_conditions K_OR opt_lblname
{ {
PLpgSQL_condition *new;
PLpgSQL_condition *old; PLpgSQL_condition *old;
new = malloc(sizeof(PLpgSQL_condition));
memset(new, 0, sizeof(PLpgSQL_condition));
new->condname = $3;
new->next = NULL;
for (old = $1; old->next != NULL; old = old->next) for (old = $1; old->next != NULL; old = old->next)
/* skip */ ; /* skip */ ;
old->next = new; old->next = plpgsql_parse_err_condition($3);
$$ = $1; $$ = $1;
} }
| opt_lblname | opt_lblname
{ {
PLpgSQL_condition *new; $$ = plpgsql_parse_err_condition($1);
new = malloc(sizeof(PLpgSQL_condition));
memset(new, 0, sizeof(PLpgSQL_condition));
new->condname = $1;
new->next = NULL;
$$ = new;
} }
; ;

View File

@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.78 2004/07/31 00:45:46 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.79 2004/08/20 22:00:14 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
@ -94,6 +94,20 @@ typedef struct plpgsql_hashent
#define FUNCS_PER_USER 128 /* initial table size */ #define FUNCS_PER_USER 128 /* initial table size */
/* ----------
* Lookup table for EXCEPTION condition names
* ----------
*/
typedef struct {
const char *label;
int sqlerrstate;
} ExceptionLabelMap;
static const ExceptionLabelMap exception_label_map[] = {
#include "plerrcodes.h"
{ NULL, 0 }
};
/* ---------- /* ----------
* static prototypes * static prototypes
@ -1710,6 +1724,59 @@ build_datatype(HeapTuple typeTup, int32 typmod)
return typ; return typ;
} }
/*
* plpgsql_parse_err_condition
* Generate PLpgSQL_condition entry(s) for an exception condition name
*
* This has to be able to return a list because there are some duplicate
* names in the table of error code names.
*/
PLpgSQL_condition *
plpgsql_parse_err_condition(char *condname)
{
int i;
PLpgSQL_condition *new;
PLpgSQL_condition *prev;
/*
* XXX Eventually we will want to look for user-defined exception names
* here.
*/
/*
* OTHERS is represented as code 0 (which would map to '00000', but
* we have no need to represent that as an exception condition).
*/
if (strcmp(condname, "others") == 0)
{
new = malloc(sizeof(PLpgSQL_condition));
new->sqlerrstate = 0;
new->condname = condname;
new->next = NULL;
return new;
}
prev = NULL;
for (i = 0; exception_label_map[i].label != NULL; i++)
{
if (strcmp(condname, exception_label_map[i].label) == 0)
{
new = malloc(sizeof(PLpgSQL_condition));
new->sqlerrstate = exception_label_map[i].sqlerrstate;
new->condname = condname;
new->next = prev;
prev = new;
}
}
if (!prev)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unrecognized exception condition \"%s\"",
condname)));
return prev;
}
/* ---------- /* ----------
* plpgsql_adddatum Add a variable, record or row * plpgsql_adddatum Add a variable, record or row

View File

@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.115 2004/08/13 18:47:56 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.116 2004/08/20 22:00:14 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
@ -56,15 +56,6 @@
static const char *const raise_skip_msg = "RAISE"; static const char *const raise_skip_msg = "RAISE";
typedef struct {
const char *label;
int sqlerrstate;
} ExceptionLabelMap;
static const ExceptionLabelMap exception_label_map[] = {
#include "plerrcodes.h"
{ NULL, 0 }
};
/* /*
* All plpgsql function executions within a single transaction share * All plpgsql function executions within a single transaction share
@ -799,40 +790,24 @@ exception_matches_conditions(ErrorData *edata, PLpgSQL_condition *cond)
{ {
for (; cond != NULL; cond = cond->next) for (; cond != NULL; cond = cond->next)
{ {
const char *condname = cond->condname; int sqlerrstate = cond->sqlerrstate;
int i;
/* /*
* OTHERS matches everything *except* query-canceled; * OTHERS matches everything *except* query-canceled;
* if you're foolish enough, you can match that explicitly. * if you're foolish enough, you can match that explicitly.
*/ */
if (strcmp(condname, "others") == 0) if (sqlerrstate == 0)
{ {
if (edata->sqlerrcode == ERRCODE_QUERY_CANCELED) if (edata->sqlerrcode != ERRCODE_QUERY_CANCELED)
return false;
else
return true; return true;
} }
for (i = 0; exception_label_map[i].label != NULL; i++)
{
if (strcmp(condname, exception_label_map[i].label) == 0)
{
int labelerrcode = exception_label_map[i].sqlerrstate;
/* Exact match? */ /* Exact match? */
if (edata->sqlerrcode == labelerrcode) else if (edata->sqlerrcode == sqlerrstate)
return true; return true;
/* Category match? */ /* Category match? */
if (ERRCODE_IS_CATEGORY(labelerrcode) && else if (ERRCODE_IS_CATEGORY(sqlerrstate) &&
ERRCODE_TO_CATEGORY(edata->sqlerrcode) == labelerrcode) ERRCODE_TO_CATEGORY(edata->sqlerrcode) == sqlerrstate)
return true; return true;
/*
* You would think we should "break" here, but there are some
* duplicate names in the table, so keep looking.
*/
}
}
/* Should we raise an error if condname is unrecognized?? */
} }
return false; return false;
} }

View File

@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.50 2004/08/01 17:32:22 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.51 2004/08/20 22:00:14 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
@ -324,7 +324,8 @@ typedef struct
typedef struct PLpgSQL_condition typedef struct PLpgSQL_condition
{ /* One EXCEPTION condition name */ { /* One EXCEPTION condition name */
char *condname; int sqlerrstate; /* SQLSTATE code */
char *condname; /* condition name (for debugging) */
struct PLpgSQL_condition *next; struct PLpgSQL_condition *next;
} PLpgSQL_condition; } PLpgSQL_condition;
@ -682,6 +683,7 @@ 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(char *refname, int lineno,
PLpgSQL_type *dtype, PLpgSQL_type *dtype,
bool add2namespace); bool add2namespace);
extern PLpgSQL_condition *plpgsql_parse_err_condition(char *condname);
extern void plpgsql_adddatum(PLpgSQL_datum *new); extern void plpgsql_adddatum(PLpgSQL_datum *new);
extern int plpgsql_add_initdatums(int **varnos); extern int plpgsql_add_initdatums(int **varnos);
extern void plpgsql_HashTableInit(void); extern void plpgsql_HashTableInit(void);