Index: gram.y =================================================================== RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v retrieving revision 1.106 diff -c -r1.106 gram.y *** gram.y 9 Nov 2007 23:58:32 -0000 1.106 --- gram.y 26 Nov 2007 21:36:11 -0000 *************** *** 43,48 **** --- 43,49 ---- 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,220 **** %token T_ROW %token T_RECORD %token T_DTYPE - %token T_LABEL %token T_WORD %token T_ERROR --- 215,220 ---- *************** *** 505,511 **** yyerror("only positional parameters can be aliased"); plpgsql_ns_setlocal(false); ! nsi = plpgsql_ns_lookup(name, NULL); if (nsi == NULL) { plpgsql_error_lineno = plpgsql_scanner_lineno(); --- 505,512 ---- yyerror("only positional parameters can be aliased"); plpgsql_ns_setlocal(false); ! ! nsi = plpgsql_ns_lookup(name, NULL, NULL, NULL); if (nsi == NULL) { plpgsql_error_lineno = plpgsql_scanner_lineno(); *************** *** 1642,1661 **** } ; opt_label : { $$ = NULL; } ! | T_LABEL { ! char *label_name; ! plpgsql_convert_ident(yytext, &label_name, 1); ! $$ = label_name; } ! | T_WORD { ! /* just to give a better error than "syntax error" */ ! yyerror("no such label"); } ; --- 1643,1670 ---- } ; + /* + * need all the options because scanner will have tried to resolve as variable + */ opt_label : { $$ = NULL; } ! | T_WORD ! { ! $$ = check_label(yytext); ! } ! | T_SCALAR { ! $$ = check_label(yytext); } ! | T_RECORD { ! $$ = check_label(yytext); ! } ! | T_ROW ! { ! $$ = check_label(yytext); } ; *************** *** 2484,2489 **** --- 2493,2509 ---- 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) { Index: pl_comp.c =================================================================== RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v retrieving revision 1.119 diff -c -r1.119 pl_comp.c *** pl_comp.c 15 Nov 2007 21:14:46 -0000 1.119 --- pl_comp.c 26 Nov 2007 21:36:11 -0000 *************** *** 787,793 **** * ---------- */ int ! plpgsql_parse_word(char *word) { PLpgSQL_nsitem *nse; char *cp[1]; --- 787,793 ---- * ---------- */ int ! plpgsql_parse_word(const char *word) { PLpgSQL_nsitem *nse; char *cp[1]; *************** *** 797,802 **** --- 797,803 ---- /* * 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,839 **** /* * Do a lookup on the compiler's namestack */ ! nse = plpgsql_ns_lookup(cp[0], NULL); 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; --- 826,838 ---- /* * Do a lookup on the compiler's namestack */ ! nse = plpgsql_ns_lookup(cp[0], NULL, NULL, NULL); ! pfree(cp[0]); ! if (nse != NULL) { switch (nse->itemtype) { case PLPGSQL_NSTYPE_VAR: plpgsql_yylval.scalar = plpgsql_Datums[nse->itemno]; return T_SCALAR; *************** *** 855,861 **** * Nothing found - up to now it's a word without any special meaning for * us. */ - pfree(cp[0]); return T_WORD; } --- 854,859 ---- *************** *** 866,883 **** * ---------- */ int ! plpgsql_parse_dblword(char *word) { PLpgSQL_nsitem *ns; char *cp[2]; /* Do case conversion and word separation */ plpgsql_convert_ident(word, cp, 2); /* ! * Lookup the first word */ ! ns = plpgsql_ns_lookup(cp[0], NULL); if (ns == NULL) { pfree(cp[0]); --- 864,882 ---- * ---------- */ int ! 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); /* ! * Do a lookup on the compiler's namestack */ ! ns = plpgsql_ns_lookup(cp[0], cp[1], NULL, &nnames); if (ns == NULL) { pfree(cp[0]); *************** *** 887,925 **** 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]); 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; case PLPGSQL_NSTYPE_REC: { /* * First word is a record name, so second word must be a field --- 886,900 ---- switch (ns->itemtype) { ! case PLPGSQL_NSTYPE_VAR: ! /* Block-qualified reference to scalar variable. */ ! plpgsql_yylval.scalar = plpgsql_Datums[ns->itemno]; pfree(cp[0]); pfree(cp[1]); ! return T_SCALAR; case PLPGSQL_NSTYPE_REC: + if (nnames == 1) { /* * First word is a record name, so second word must be a field *************** *** 940,947 **** --- 915,931 ---- 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,972 **** --- 951,964 ---- 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,1021 **** * ---------- */ int ! plpgsql_parse_tripword(char *word) { PLpgSQL_nsitem *ns; char *cp[3]; /* 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 */ ! ns = plpgsql_ns_lookup(cp[1], cp[0]); ! if (ns == NULL) { pfree(cp[0]); pfree(cp[1]); --- 976,996 ---- * ---------- */ int ! 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); /* ! * Do a lookup on the compiler's namestack. ! * Must find a qualified reference. */ ! ns = plpgsql_ns_lookup(cp[0], cp[1], cp[2], &nnames); ! if (ns == NULL || nnames != 2) { pfree(cp[0]); pfree(cp[1]); *************** *** 1028,1034 **** case PLPGSQL_NSTYPE_REC: { /* ! * This word is a record name, so third word must be a field * in this record. */ PLpgSQL_recfield *new; --- 1003,1009 ---- case PLPGSQL_NSTYPE_REC: { /* ! * words 1/2 are a record name, so third word must be a field * in this record. */ PLpgSQL_recfield *new; *************** *** 1052,1058 **** case PLPGSQL_NSTYPE_ROW: { /* ! * This word is a row name, so third word must be a field in * this row. */ PLpgSQL_row *row; --- 1027,1033 ---- case PLPGSQL_NSTYPE_ROW: { /* ! * words 1/2 are a row name, so third word must be a field in * this row. */ PLpgSQL_row *row; *************** *** 1114,1124 **** pfree(cp[1]); /* ! * Do a lookup on the compiler's namestack. But ensure it moves up to the ! * toplevel. */ old_nsstate = plpgsql_ns_setlocal(false); ! nse = plpgsql_ns_lookup(cp[0], NULL); plpgsql_ns_setlocal(old_nsstate); if (nse != NULL) --- 1089,1098 ---- pfree(cp[1]); /* ! * 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, NULL, NULL); plpgsql_ns_setlocal(old_nsstate); if (nse != NULL) *************** *** 1200,1231 **** word[i] = '.'; plpgsql_convert_ident(word, cp, 3); word[i] = '%'; /* ! * Lookup the first word */ ! nse = plpgsql_ns_lookup(cp[0], NULL); ! /* ! * If this is a label lookup the second word in that label's namestack ! * level ! */ ! if (nse != NULL) { ! 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 */ goto done; } --- 1174,1194 ---- word[i] = '.'; plpgsql_convert_ident(word, cp, 3); word[i] = '%'; + pfree(cp[2]); /* ! * 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. */ ! old_nsstate = plpgsql_ns_setlocal(false); ! nse = plpgsql_ns_lookup(cp[0], cp[1], NULL, NULL); ! 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; goto done; } *************** *** 1291,1298 **** * plpgsql_parse_tripwordtype Same lookup for word.word.word%TYPE * ---------- */ - #define TYPE_JUNK_LEN 5 - int plpgsql_parse_tripwordtype(char *word) { --- 1254,1259 ---- *************** *** 1302,1311 **** HeapTuple typetup = NULL; Form_pg_class classStruct; Form_pg_attribute attrStruct; ! char *cp[2]; ! char *colname[1]; ! int qualified_att_len; ! int numdots = 0; int i; RangeVar *relvar; MemoryContext oldCxt; --- 1263,1269 ---- HeapTuple typetup = NULL; Form_pg_class classStruct; Form_pg_attribute attrStruct; ! char *cp[4]; int i; RangeVar *relvar; MemoryContext oldCxt; *************** *** 1315,1341 **** 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'; ! relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp[0])); classOid = RangeVarGetRelid(relvar, true); if (!OidIsValid(classOid)) goto done; --- 1273,1287 ---- oldCxt = MemoryContextSwitchTo(compile_tmp_cxt); /* Do case conversion and word separation */ ! /* 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 = makeRangeVar(cp[0], cp[1]); classOid = RangeVarGetRelid(relvar, true); if (!OidIsValid(classOid)) goto done; *************** *** 1359,1366 **** /* * Fetch the named table field and its type */ ! plpgsql_convert_ident(cp[1], colname, 1); ! attrtup = SearchSysCacheAttName(classOid, colname[0]); if (!HeapTupleIsValid(attrtup)) goto done; attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup); --- 1305,1311 ---- /* * Fetch the named table field and its type */ ! attrtup = SearchSysCacheAttName(classOid, cp[2]); if (!HeapTupleIsValid(attrtup)) goto done; attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup); *************** *** 1436,1448 **** * So word must be a namespace qualified table name. * ---------- */ - #define ROWTYPE_JUNK_LEN 8 - int plpgsql_parse_dblwordrowtype(char *word) { Oid classOid; ! char *cp; int i; RangeVar *relvar; MemoryContext oldCxt; --- 1381,1391 ---- * So word must be a namespace qualified table name. * ---------- */ int plpgsql_parse_dblwordrowtype(char *word) { Oid classOid; ! char *cp[3]; int i; RangeVar *relvar; MemoryContext oldCxt; *************** *** 1452,1470 **** /* 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] == '%'); ! word[i] = '\0'; ! cp = pstrdup(word); word[i] = '%'; /* Lookup the relation */ ! relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp)); classOid = RangeVarGetRelid(relvar, true); if (!OidIsValid(classOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), ! errmsg("relation \"%s\" does not exist", cp))); /* Build and return the row type struct */ plpgsql_yylval.dtype = plpgsql_build_datatype(get_rel_type_id(classOid), --- 1395,1413 ---- /* Do case conversion and word separation */ /* We convert %rowtype to .rowtype momentarily to keep converter happy */ ! i = strlen(word) - 8; Assert(word[i] == '%'); ! word[i] = '.'; ! plpgsql_convert_ident(word, cp, 3); word[i] = '%'; /* Lookup the relation */ ! relvar = makeRangeVar(cp[0], cp[1]); classOid = RangeVarGetRelid(relvar, true); if (!OidIsValid(classOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), ! 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), Index: pl_funcs.c =================================================================== RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v retrieving revision 1.65 diff -c -r1.65 pl_funcs.c *** pl_funcs.c 15 Nov 2007 22:25:17 -0000 1.65 --- pl_funcs.c 26 Nov 2007 21:36:11 -0000 *************** *** 126,134 **** /* ---------- ! * plpgsql_ns_setlocal Tell plpgsql_ns_lookup to or to ! * not look into the current level ! * only. * ---------- */ bool --- 126,140 ---- /* ---------- ! * 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,277 **** /* ---------- ! * plpgsql_ns_lookup Lookup for a word in the namestack * ---------- */ PLpgSQL_nsitem * ! plpgsql_ns_lookup(const char *name, const char *label) { PLpgSQL_ns *ns; int i; ! /* ! * If a label is specified, lookup only in that ! */ ! if (label != NULL) { ! for (ns = ns_current; ns != NULL; ns = ns->upper) { ! if (strcmp(ns->items[0]->name, label) == 0) { ! for (i = 1; i < ns->items_used; i++) { ! if (strcmp(ns->items[i]->name, name) == 0) ! return ns->items[i]; } - return NULL; /* name not found in specified label */ } } ! return NULL; /* label not found */ } ! /* ! * No label given, lookup for visible labels ignoring localmode ! */ 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; } --- 225,322 ---- /* ---------- ! * 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 *name1, const char *name2, const char *name3, ! int *names_used) { PLpgSQL_ns *ns; int i; ! /* Scan each level of the namestack */ ! 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) { ! for (i = 1; i < ns->items_used; i++) { ! PLpgSQL_nsitem *nsitem = ns->items[i]; ! ! if (strcmp(nsitem->name, name2) == 0) { ! if (name3 == NULL || ! nsitem->itemtype != PLPGSQL_NSTYPE_VAR) ! { ! if (names_used) ! *names_used = 2; ! return nsitem; ! } } } } ! ! if (ns_localmode) ! break; /* do not look into upper levels */ } ! /* 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]; } ! return NULL; /* label not found */ } *************** *** 846,853 **** dump_exit(PLpgSQL_stmt_exit *stmt) { dump_ind(); ! printf("%s label='%s'", ! stmt->is_exit ? "EXIT" : "CONTINUE", stmt->label); if (stmt->cond != NULL) { printf(" WHEN "); --- 891,899 ---- dump_exit(PLpgSQL_stmt_exit *stmt) { dump_ind(); ! printf("%s", stmt->is_exit ? "EXIT" : "CONTINUE"); ! if (stmt->label != NULL) ! printf(" label='%s'", stmt->label); if (stmt->cond != NULL) { printf(" WHEN "); Index: plpgsql.h =================================================================== RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v retrieving revision 1.93 diff -c -r1.93 plpgsql.h *** plpgsql.h 15 Nov 2007 22:25:17 -0000 1.93 --- plpgsql.h 26 Nov 2007 21:36:11 -0000 *************** *** 473,479 **** int cmd_type; int lineno; bool is_exit; /* Is this an exit or a continue? */ ! char *label; PLpgSQL_expr *cond; } PLpgSQL_stmt_exit; --- 473,479 ---- int cmd_type; int lineno; bool is_exit; /* Is this an exit or a continue? */ ! char *label; /* NULL if it's an unlabelled EXIT/CONTINUE */ PLpgSQL_expr *cond; } PLpgSQL_stmt_exit; *************** *** 723,731 **** */ 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_wordtype(char *word); extern int plpgsql_parse_dblwordtype(char *word); extern int plpgsql_parse_tripwordtype(char *word); --- 723,731 ---- */ extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo, bool forValidator); ! 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,779 **** extern char *plpgsql_dstring_get(PLpgSQL_dstring *ds); /* ---------- ! * Functions for the namestack handling in pl_funcs.c * ---------- */ extern void plpgsql_ns_init(void); --- 773,779 ---- extern char *plpgsql_dstring_get(PLpgSQL_dstring *ds); /* ---------- ! * Functions for namestack handling in pl_funcs.c * ---------- */ extern void plpgsql_ns_init(void); *************** *** 781,787 **** 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 void plpgsql_ns_rename(char *oldname, char *newname); /* ---------- --- 781,789 ---- 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 *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); /* ----------