aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pl/plpgsql/src/gram.y40
-rw-r--r--src/pl/plpgsql/src/pl_comp.c193
-rw-r--r--src/pl/plpgsql/src/pl_funcs.c120
-rw-r--r--src/pl/plpgsql/src/plpgsql.h16
4 files changed, 190 insertions, 179 deletions
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index ffb16bea1c0..4541c9839d8 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.106 2007/11/09 23:58:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.107 2007/11/27 19:58:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,6 +43,7 @@ static PLpgSQL_row *make_scalar_list1(const char *initial_name,
int lineno);
static void check_sql_expr(const char *stmt);
static void plpgsql_sql_error_callback(void *arg);
+static char *check_label(const char *yytxt);
static void check_labels(const char *start_label,
const char *end_label);
@@ -214,7 +215,6 @@ static void check_labels(const char *start_label,
%token T_ROW
%token T_RECORD
%token T_DTYPE
-%token T_LABEL
%token T_WORD
%token T_ERROR
@@ -505,7 +505,8 @@ decl_aliasitem : T_WORD
yyerror("only positional parameters can be aliased");
plpgsql_ns_setlocal(false);
- nsi = plpgsql_ns_lookup(name, NULL);
+
+ nsi = plpgsql_ns_lookup(name, NULL, NULL, NULL);
if (nsi == NULL)
{
plpgsql_error_lineno = plpgsql_scanner_lineno();
@@ -1642,20 +1643,28 @@ opt_block_label :
}
;
+/*
+ * need all the options because scanner will have tried to resolve as variable
+ */
opt_label :
{
$$ = NULL;
}
- | T_LABEL
+ | T_WORD
+ {
+ $$ = check_label(yytext);
+ }
+ | T_SCALAR
{
- char *label_name;
- plpgsql_convert_ident(yytext, &label_name, 1);
- $$ = label_name;
+ $$ = check_label(yytext);
}
- | T_WORD
+ | T_RECORD
{
- /* just to give a better error than "syntax error" */
- yyerror("no such label");
+ $$ = check_label(yytext);
+ }
+ | T_ROW
+ {
+ $$ = check_label(yytext);
}
;
@@ -2484,6 +2493,17 @@ plpgsql_sql_error_callback(void *arg)
errposition(0);
}
+static char *
+check_label(const char *yytxt)
+{
+ char *label_name;
+
+ plpgsql_convert_ident(yytxt, &label_name, 1);
+ if (plpgsql_ns_lookup_label(label_name) == NULL)
+ yyerror("no such label");
+ return label_name;
+}
+
static void
check_labels(const char *start_label, const char *end_label)
{
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index edb423e5215..2e76b5e187d 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.119 2007/11/15 21:14:46 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.120 2007/11/27 19:58:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -787,7 +787,7 @@ plpgsql_compile_error_callback(void *arg)
* ----------
*/
int
-plpgsql_parse_word(char *word)
+plpgsql_parse_word(const char *word)
{
PLpgSQL_nsitem *nse;
char *cp[1];
@@ -797,6 +797,7 @@ plpgsql_parse_word(char *word)
/*
* Recognize tg_argv when compiling triggers
+ * (XXX this sucks, it should be a regular variable in the namestack)
*/
if (plpgsql_curr_compile->fn_functype == T_TRIGGER)
{
@@ -825,15 +826,13 @@ plpgsql_parse_word(char *word)
/*
* Do a lookup on the compiler's namestack
*/
- nse = plpgsql_ns_lookup(cp[0], NULL);
+ nse = plpgsql_ns_lookup(cp[0], NULL, NULL, NULL);
+ pfree(cp[0]);
+
if (nse != NULL)
{
- pfree(cp[0]);
switch (nse->itemtype)
{
- case PLPGSQL_NSTYPE_LABEL:
- return T_LABEL;
-
case PLPGSQL_NSTYPE_VAR:
plpgsql_yylval.scalar = plpgsql_Datums[nse->itemno];
return T_SCALAR;
@@ -855,7 +854,6 @@ plpgsql_parse_word(char *word)
* Nothing found - up to now it's a word without any special meaning for
* us.
*/
- pfree(cp[0]);
return T_WORD;
}
@@ -866,18 +864,19 @@ plpgsql_parse_word(char *word)
* ----------
*/
int
-plpgsql_parse_dblword(char *word)
+plpgsql_parse_dblword(const char *word)
{
PLpgSQL_nsitem *ns;
char *cp[2];
+ int nnames;
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 2);
/*
- * Lookup the first word
+ * Do a lookup on the compiler's namestack
*/
- ns = plpgsql_ns_lookup(cp[0], NULL);
+ ns = plpgsql_ns_lookup(cp[0], cp[1], NULL, &nnames);
if (ns == NULL)
{
pfree(cp[0]);
@@ -887,39 +886,15 @@ plpgsql_parse_dblword(char *word)
switch (ns->itemtype)
{
- case PLPGSQL_NSTYPE_LABEL:
-
- /*
- * First word is a label, so second word could be a variable,
- * record or row in that bodies namestack. Anything else could
- * only be something in a query given to the SPI manager and
- * T_ERROR will get eaten up by the collector routines.
- */
- ns = plpgsql_ns_lookup(cp[1], cp[0]);
+ case PLPGSQL_NSTYPE_VAR:
+ /* Block-qualified reference to scalar variable. */
+ plpgsql_yylval.scalar = plpgsql_Datums[ns->itemno];
pfree(cp[0]);
pfree(cp[1]);
- if (ns == NULL)
- return T_ERROR;
- switch (ns->itemtype)
- {
- case PLPGSQL_NSTYPE_VAR:
- plpgsql_yylval.scalar = plpgsql_Datums[ns->itemno];
- return T_SCALAR;
-
- case PLPGSQL_NSTYPE_REC:
- plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]);
- return T_RECORD;
-
- case PLPGSQL_NSTYPE_ROW:
- plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
- return T_ROW;
-
- default:
- return T_ERROR;
- }
- break;
+ return T_SCALAR;
case PLPGSQL_NSTYPE_REC:
+ if (nnames == 1)
{
/*
* First word is a record name, so second word must be a field
@@ -940,8 +915,17 @@ plpgsql_parse_dblword(char *word)
pfree(cp[1]);
return T_SCALAR;
}
+ else
+ {
+ /* Block-qualified reference to record variable. */
+ plpgsql_yylval.rec = (PLpgSQL_rec *) (plpgsql_Datums[ns->itemno]);
+ pfree(cp[0]);
+ pfree(cp[1]);
+ return T_RECORD;
+ }
case PLPGSQL_NSTYPE_ROW:
+ if (nnames == 1)
{
/*
* First word is a row name, so second word must be a field in
@@ -967,6 +951,14 @@ plpgsql_parse_dblword(char *word)
errmsg("row \"%s\" has no field \"%s\"",
cp[0], cp[1])));
}
+ else
+ {
+ /* Block-qualified reference to row variable. */
+ plpgsql_yylval.row = (PLpgSQL_row *) (plpgsql_Datums[ns->itemno]);
+ pfree(cp[0]);
+ pfree(cp[1]);
+ return T_ROW;
+ }
default:
break;
@@ -984,38 +976,21 @@ plpgsql_parse_dblword(char *word)
* ----------
*/
int
-plpgsql_parse_tripword(char *word)
+plpgsql_parse_tripword(const char *word)
{
PLpgSQL_nsitem *ns;
char *cp[3];
+ int nnames;
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 3);
/*
- * Lookup the first word - it must be a label
- */
- ns = plpgsql_ns_lookup(cp[0], NULL);
- if (ns == NULL)
- {
- pfree(cp[0]);
- pfree(cp[1]);
- pfree(cp[2]);
- return T_ERROR;
- }
- if (ns->itemtype != PLPGSQL_NSTYPE_LABEL)
- {
- pfree(cp[0]);
- pfree(cp[1]);
- pfree(cp[2]);
- return T_ERROR;
- }
-
- /*
- * First word is a label, so second word could be a record or row
+ * Do a lookup on the compiler's namestack.
+ * Must find a qualified reference.
*/
- ns = plpgsql_ns_lookup(cp[1], cp[0]);
- if (ns == NULL)
+ ns = plpgsql_ns_lookup(cp[0], cp[1], cp[2], &nnames);
+ if (ns == NULL || nnames != 2)
{
pfree(cp[0]);
pfree(cp[1]);
@@ -1028,7 +1003,7 @@ plpgsql_parse_tripword(char *word)
case PLPGSQL_NSTYPE_REC:
{
/*
- * This word is a record name, so third word must be a field
+ * words 1/2 are a record name, so third word must be a field
* in this record.
*/
PLpgSQL_recfield *new;
@@ -1052,7 +1027,7 @@ plpgsql_parse_tripword(char *word)
case PLPGSQL_NSTYPE_ROW:
{
/*
- * This word is a row name, so third word must be a field in
+ * words 1/2 are a row name, so third word must be a field in
* this row.
*/
PLpgSQL_row *row;
@@ -1114,11 +1089,10 @@ plpgsql_parse_wordtype(char *word)
pfree(cp[1]);
/*
- * Do a lookup on the compiler's namestack. But ensure it moves up to the
- * toplevel.
+ * Do a lookup on the compiler's namestack. Ensure we scan all levels.
*/
old_nsstate = plpgsql_ns_setlocal(false);
- nse = plpgsql_ns_lookup(cp[0], NULL);
+ nse = plpgsql_ns_lookup(cp[0], NULL, NULL, NULL);
plpgsql_ns_setlocal(old_nsstate);
if (nse != NULL)
@@ -1200,32 +1174,21 @@ plpgsql_parse_dblwordtype(char *word)
word[i] = '.';
plpgsql_convert_ident(word, cp, 3);
word[i] = '%';
+ pfree(cp[2]);
/*
- * Lookup the first word
+ * Do a lookup on the compiler's namestack. Ensure we scan all levels.
+ * We don't need to check number of names matched, because we will only
+ * consider scalar variables.
*/
- nse = plpgsql_ns_lookup(cp[0], NULL);
+ old_nsstate = plpgsql_ns_setlocal(false);
+ nse = plpgsql_ns_lookup(cp[0], cp[1], NULL, NULL);
+ plpgsql_ns_setlocal(old_nsstate);
- /*
- * If this is a label lookup the second word in that label's namestack
- * level
- */
- if (nse != NULL)
+ if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
{
- if (nse->itemtype == PLPGSQL_NSTYPE_LABEL)
- {
- old_nsstate = plpgsql_ns_setlocal(false);
- nse = plpgsql_ns_lookup(cp[1], cp[0]);
- plpgsql_ns_setlocal(old_nsstate);
-
- if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
- {
- plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
- result = T_DTYPE;
- }
- }
-
- /* Return T_ERROR if not found, otherwise T_DTYPE */
+ plpgsql_yylval.dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
+ result = T_DTYPE;
goto done;
}
@@ -1291,8 +1254,6 @@ done:
* plpgsql_parse_tripwordtype Same lookup for word.word.word%TYPE
* ----------
*/
-#define TYPE_JUNK_LEN 5
-
int
plpgsql_parse_tripwordtype(char *word)
{
@@ -1302,10 +1263,7 @@ plpgsql_parse_tripwordtype(char *word)
HeapTuple typetup = NULL;
Form_pg_class classStruct;
Form_pg_attribute attrStruct;
- char *cp[2];
- char *colname[1];
- int qualified_att_len;
- int numdots = 0;
+ char *cp[4];
int i;
RangeVar *relvar;
MemoryContext oldCxt;
@@ -1315,27 +1273,15 @@ plpgsql_parse_tripwordtype(char *word)
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
/* Do case conversion and word separation */
- qualified_att_len = strlen(word) - TYPE_JUNK_LEN;
- Assert(word[qualified_att_len] == '%');
-
- for (i = 0; i < qualified_att_len; i++)
- {
- if (word[i] == '.' && ++numdots == 2)
- break;
- }
-
- cp[0] = (char *) palloc((i + 1) * sizeof(char));
- memcpy(cp[0], word, i * sizeof(char));
- cp[0][i] = '\0';
-
- /*
- * qualified_att_len - one based position + 1 (null terminator)
- */
- cp[1] = (char *) palloc((qualified_att_len - i) * sizeof(char));
- memcpy(cp[1], &word[i + 1], (qualified_att_len - i - 1) * sizeof(char));
- cp[1][qualified_att_len - i - 1] = '\0';
+ /* We convert %type to .type momentarily to keep converter happy */
+ i = strlen(word) - 5;
+ Assert(word[i] == '%');
+ word[i] = '.';
+ plpgsql_convert_ident(word, cp, 4);
+ word[i] = '%';
+ pfree(cp[3]);
- relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp[0]));
+ relvar = makeRangeVar(cp[0], cp[1]);
classOid = RangeVarGetRelid(relvar, true);
if (!OidIsValid(classOid))
goto done;
@@ -1359,8 +1305,7 @@ plpgsql_parse_tripwordtype(char *word)
/*
* Fetch the named table field and its type
*/
- plpgsql_convert_ident(cp[1], colname, 1);
- attrtup = SearchSysCacheAttName(classOid, colname[0]);
+ attrtup = SearchSysCacheAttName(classOid, cp[2]);
if (!HeapTupleIsValid(attrtup))
goto done;
attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup);
@@ -1436,13 +1381,11 @@ plpgsql_parse_wordrowtype(char *word)
* So word must be a namespace qualified table name.
* ----------
*/
-#define ROWTYPE_JUNK_LEN 8
-
int
plpgsql_parse_dblwordrowtype(char *word)
{
Oid classOid;
- char *cp;
+ char *cp[3];
int i;
RangeVar *relvar;
MemoryContext oldCxt;
@@ -1452,19 +1395,19 @@ plpgsql_parse_dblwordrowtype(char *word)
/* Do case conversion and word separation */
/* We convert %rowtype to .rowtype momentarily to keep converter happy */
- i = strlen(word) - ROWTYPE_JUNK_LEN;
+ i = strlen(word) - 8;
Assert(word[i] == '%');
- word[i] = '\0';
- cp = pstrdup(word);
+ word[i] = '.';
+ plpgsql_convert_ident(word, cp, 3);
word[i] = '%';
/* Lookup the relation */
- relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp));
+ relvar = makeRangeVar(cp[0], cp[1]);
classOid = RangeVarGetRelid(relvar, true);
if (!OidIsValid(classOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
- errmsg("relation \"%s\" does not exist", cp)));
+ errmsg("relation \"%s.%s\" does not exist", cp[0], cp[1])));
/* Build and return the row type struct */
plpgsql_yylval.dtype = plpgsql_build_datatype(get_rel_type_id(classOid),
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index 573bfd4e0c9..66e0f47d8c8 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.65 2007/11/15 22:25:17 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.66 2007/11/27 19:58:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -126,9 +126,15 @@ plpgsql_ns_init(void)
/* ----------
- * plpgsql_ns_setlocal Tell plpgsql_ns_lookup to or to
- * not look into the current level
- * only.
+ * plpgsql_ns_setlocal Tell plpgsql_ns_lookup whether to
+ * look into the current level only.
+ *
+ * This is a crock, but in the current design we need it because scan.l
+ * initiates name lookup, and the scanner does not know whether we are
+ * examining a name being declared in a DECLARE section. For that case
+ * we only want to know if there is a conflicting name earlier in the
+ * same DECLARE section. So the grammar must temporarily set local mode
+ * before scanning decl_varnames.
* ----------
*/
bool
@@ -219,59 +225,98 @@ plpgsql_ns_additem(int itemtype, int itemno, const char *name)
/* ----------
- * plpgsql_ns_lookup Lookup for a word in the namestack
+ * plpgsql_ns_lookup Lookup an identifier in the namestack
+ *
+ * Note that this only searches for variables, not labels.
+ *
+ * name1 must be non-NULL. Pass NULL for name2 and/or name3 if parsing a name
+ * with fewer than three components.
+ *
+ * If names_used isn't NULL, *names_used receives the number of names
+ * matched: 0 if no match, 1 if name1 matched an unqualified variable name,
+ * 2 if name1 and name2 matched a block label + variable name.
+ *
+ * Note that name3 is never directly matched to anything. However, if it
+ * isn't NULL, we will disregard qualified matches to scalar variables.
+ * Similarly, if name2 isn't NULL, we disregard unqualified matches to
+ * scalar variables.
* ----------
*/
PLpgSQL_nsitem *
-plpgsql_ns_lookup(const char *name, const char *label)
+plpgsql_ns_lookup(const char *name1, const char *name2, const char *name3,
+ int *names_used)
{
PLpgSQL_ns *ns;
int i;
- /*
- * If a label is specified, lookup only in that
- */
- if (label != NULL)
+ /* Scan each level of the namestack */
+ for (ns = ns_current; ns != NULL; ns = ns->upper)
{
- for (ns = ns_current; ns != NULL; ns = ns->upper)
+ /* Check for unqualified match to variable name */
+ for (i = 1; i < ns->items_used; i++)
+ {
+ PLpgSQL_nsitem *nsitem = ns->items[i];
+
+ if (strcmp(nsitem->name, name1) == 0)
+ {
+ if (name2 == NULL ||
+ nsitem->itemtype != PLPGSQL_NSTYPE_VAR)
+ {
+ if (names_used)
+ *names_used = 1;
+ return nsitem;
+ }
+ }
+ }
+
+ /* Check for qualified match to variable name */
+ if (name2 != NULL &&
+ strcmp(ns->items[0]->name, name1) == 0)
{
- if (strcmp(ns->items[0]->name, label) == 0)
+ for (i = 1; i < ns->items_used; i++)
{
- for (i = 1; i < ns->items_used; i++)
+ PLpgSQL_nsitem *nsitem = ns->items[i];
+
+ if (strcmp(nsitem->name, name2) == 0)
{
- if (strcmp(ns->items[i]->name, name) == 0)
- return ns->items[i];
+ if (name3 == NULL ||
+ nsitem->itemtype != PLPGSQL_NSTYPE_VAR)
+ {
+ if (names_used)
+ *names_used = 2;
+ return nsitem;
+ }
}
- return NULL; /* name not found in specified label */
}
}
- return NULL; /* label not found */
+
+ if (ns_localmode)
+ break; /* do not look into upper levels */
}
- /*
- * No label given, lookup for visible labels ignoring localmode
- */
+ /* This is just to suppress possibly-uninitialized-variable warnings */
+ if (names_used)
+ *names_used = 0;
+ return NULL; /* No match found */
+}
+
+
+/* ----------
+ * plpgsql_ns_lookup_label Lookup a label in the namestack
+ * ----------
+ */
+PLpgSQL_nsitem *
+plpgsql_ns_lookup_label(const char *name)
+{
+ PLpgSQL_ns *ns;
+
for (ns = ns_current; ns != NULL; ns = ns->upper)
{
if (strcmp(ns->items[0]->name, name) == 0)
return ns->items[0];
}
- /*
- * Finally lookup name in the namestack
- */
- for (ns = ns_current; ns != NULL; ns = ns->upper)
- {
- for (i = 1; i < ns->items_used; i++)
- {
- if (strcmp(ns->items[i]->name, name) == 0)
- return ns->items[i];
- }
- if (ns_localmode)
- return NULL; /* name not found in current namespace */
- }
-
- return NULL;
+ return NULL; /* label not found */
}
@@ -846,8 +891,9 @@ static void
dump_exit(PLpgSQL_stmt_exit *stmt)
{
dump_ind();
- printf("%s label='%s'",
- stmt->is_exit ? "EXIT" : "CONTINUE", stmt->label);
+ printf("%s", stmt->is_exit ? "EXIT" : "CONTINUE");
+ if (stmt->label != NULL)
+ printf(" label='%s'", stmt->label);
if (stmt->cond != NULL)
{
printf(" WHEN ");
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 6ba0126b494..7bbed551ab5 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.93 2007/11/15 22:25:17 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.94 2007/11/27 19:58:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -473,7 +473,7 @@ typedef struct
int cmd_type;
int lineno;
bool is_exit; /* Is this an exit or a continue? */
- char *label;
+ char *label; /* NULL if it's an unlabelled EXIT/CONTINUE */
PLpgSQL_expr *cond;
} PLpgSQL_stmt_exit;
@@ -723,9 +723,9 @@ extern PLpgSQL_plugin **plugin_ptr;
*/
extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
bool forValidator);
-extern int plpgsql_parse_word(char *word);
-extern int plpgsql_parse_dblword(char *word);
-extern int plpgsql_parse_tripword(char *word);
+extern int plpgsql_parse_word(const char *word);
+extern int plpgsql_parse_dblword(const char *word);
+extern int plpgsql_parse_tripword(const char *word);
extern int plpgsql_parse_wordtype(char *word);
extern int plpgsql_parse_dblwordtype(char *word);
extern int plpgsql_parse_tripwordtype(char *word);
@@ -773,7 +773,7 @@ extern void plpgsql_dstring_append_char(PLpgSQL_dstring *ds, char c);
extern char *plpgsql_dstring_get(PLpgSQL_dstring *ds);
/* ----------
- * Functions for the namestack handling in pl_funcs.c
+ * Functions for namestack handling in pl_funcs.c
* ----------
*/
extern void plpgsql_ns_init(void);
@@ -781,7 +781,9 @@ extern bool plpgsql_ns_setlocal(bool flag);
extern void plpgsql_ns_push(const char *label);
extern void plpgsql_ns_pop(void);
extern void plpgsql_ns_additem(int itemtype, int itemno, const char *name);
-extern PLpgSQL_nsitem *plpgsql_ns_lookup(const char *name, const char *nsname);
+extern PLpgSQL_nsitem *plpgsql_ns_lookup(const char *name1, const char *name2,
+ const char *name3, int *names_used);
+extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(const char *name);
extern void plpgsql_ns_rename(char *oldname, char *newname);
/* ----------