*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* ----------
*/
int
-plpgsql_parse_word(char *word)
+plpgsql_parse_word(const char *word)
{
PLpgSQL_nsitem *nse;
char *cp[1];
/*
* 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)
{
/*
* 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;
* Nothing found - up to now it's a word without any special meaning for
* us.
*/
- pfree(cp[0]);
return T_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]);
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
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
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;
* ----------
*/
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]);
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;
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;
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)
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;
}
* plpgsql_parse_tripwordtype Same lookup for word.word.word%TYPE
* ----------
*/
-#define TYPE_JUNK_LEN 5
-
int
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;
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;
/*
* 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);
* 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;
/* 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),
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* ----------
- * 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
/* ----------
- * 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 */
}
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 ");
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
*/
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);
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);
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);
/* ----------