Patch applied. Thanks.
--------------------------------------------------------------------------- Joe Conway wrote: > Tom Lane wrote: > > Sean Chittenden <[EMAIL PROTECTED]> writes: > > > >>::sigh:: Is it me or does it look like all > >>of pl/pgsql is schema un-aware (ie, all of the declarations). -sc > > > > > > Yeah. The group of routines parse_word, parse_dblword, etc that are > > called by the lexer certainly all need work. There are some > > definitional issues to think about, too --- plpgsql presently relies on > > the number of names to give it some idea of what to look for, and those > > rules are probably all toast now. Please come up with a sketch of what > > you think the behavior should be before you start hacking code. > > Attached is a diff -c format proposal to fix this. I've also attached a short > test script. Seems to work OK and passes all regression tests. > > Here's a breakdown of how I understand plpgsql's "Special word rules" -- I > think it illustrates the behavior reasonably well. New functions added by this > patch are plpgsql_parse_tripwordtype and plpgsql_parse_dblwordrowtype: > > ============================================================================ > Identifiers (represents) parsing function > ---------------------------------------------------------------------------- > identifier plpgsql_parse_word > tg_argv > T_LABEL (label) > T_VARIABLE (variable) > T_RECORD (record) > T_ROW (row) > ---------------------------------------------------------------------------- > identifier.identifier plpgsql_parse_dblword > T_LABEL > T_VARIABLE (label.variable) > T_RECORD (label.record) > T_ROW (label.row) > T_RECORD > T_VARIABLE (record.variable) > T_ROW > T_VARIABLE (row.variable) > ---------------------------------------------------------------------------- > identifier.identifier.identifier plpgsql_parse_tripword > T_LABEL > T_RECORD > T_VARIABLE (label.record.variable) > T_ROW > T_VARIABLE (label.row.variable) > ---------------------------------------------------------------------------- > identifier%TYPE plpgsql_parse_wordtype > T_VARIABLE > T_DTYPE (variable%TYPE) > T_DTYPE (typname%TYPE) > ---------------------------------------------------------------------------- > identifier.identifier%TYPE plpgsql_parse_dblwordtype > T_LABEL > T_VARIABLE > T_DTYPE (label.variable%TYPE) > T_DTYPE (relname.attname%TYPE) > ---------------------------------------------------------------------------- > <new> > identifier.identifier.identifier%TYPE plpgsql_parse_tripwordtype > T_DTYPE (nspname.relname.attname%TYPE) > ---------------------------------------------------------------------------- > identifier%ROWTYPE plpgsql_parse_wordrowtype > T_DTYPE (relname%ROWTYPE) > ---------------------------------------------------------------------------- > <new> > identifier.identifier%ROWTYPE plpgsql_parse_dblwordrowtype > T_DTYPE (nspname.relname%ROWTYPE) > > ============================================================================ > Parameters - parallels the above > ---------------------------------------------------------------------------- > $# plpgsql_parse_word > $#.identifier plpgsql_parse_dblword > $#.identifier.identifier plpgsql_parse_tripword > $#%TYPE plpgsql_parse_wordtype > $#.identifier%TYPE plpgsql_parse_dblwordtype > $#.identifier.identifier%TYPE plpgsql_parse_tripwordtype > $#%ROWTYPE plpgsql_parse_wordrowtype > $#.identifier%ROWTYPE plpgsql_parse_dblwordrowtype > > Comments? > > Thanks, > > Joe > Index: src/pl/plpgsql/src/pl_comp.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/pl_comp.c,v > retrieving revision 1.51 > diff -c -r1.51 pl_comp.c > *** src/pl/plpgsql/src/pl_comp.c 4 Sep 2002 20:31:47 -0000 1.51 > --- src/pl/plpgsql/src/pl_comp.c 9 Sep 2002 04:22:24 -0000 > *************** > *** 1092,1097 **** > --- 1092,1217 ---- > return T_DTYPE; > } > > + /* ---------- > + * plpgsql_parse_tripwordtype Same lookup for word.word.word%TYPE > + * ---------- > + */ > + #define TYPE_JUNK_LEN 5 > + > + int > + plpgsql_parse_tripwordtype(char *word) > + { > + Oid classOid; > + HeapTuple classtup; > + Form_pg_class classStruct; > + HeapTuple attrtup; > + Form_pg_attribute attrStruct; > + HeapTuple typetup; > + Form_pg_type typeStruct; > + PLpgSQL_type *typ; > + char *cp[2]; > + int qualified_att_len; > + int numdots = 0; > + int i; > + RangeVar *relvar; > + > + /* 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) > + { > + cp[0] = (char *) palloc((i + 1) * sizeof(char)); > + memset(cp[0], 0, (i + 1) * sizeof(char)); > + memcpy(cp[0], word, i * sizeof(char)); > + > + /* qualified_att_len - one based position + 1 (null >terminator) */ > + cp[1] = (char *) palloc((qualified_att_len - i) * >sizeof(char)); > + memset(cp[1], 0, (qualified_att_len - i) * sizeof(char)); > + memcpy(cp[1], &word[i + 1], (qualified_att_len - i - 1) * >sizeof(char)); > + > + break; > + } > + } > + > + relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp[0], >"plpgsql_parse_dblwordtype")); > + classOid = RangeVarGetRelid(relvar, true); > + if (!OidIsValid(classOid)) > + { > + pfree(cp[0]); > + pfree(cp[1]); > + return T_ERROR; > + } > + classtup = SearchSysCache(RELOID, > + ObjectIdGetDatum(classOid), > + 0, 0, 0); > + if (!HeapTupleIsValid(classtup)) > + { > + pfree(cp[0]); > + pfree(cp[1]); > + return T_ERROR; > + } > + > + /* > + * It must be a relation, sequence, view, or type > + */ > + classStruct = (Form_pg_class) GETSTRUCT(classtup); > + if (classStruct->relkind != RELKIND_RELATION && > + classStruct->relkind != RELKIND_SEQUENCE && > + classStruct->relkind != RELKIND_VIEW && > + classStruct->relkind != RELKIND_COMPOSITE_TYPE) > + { > + ReleaseSysCache(classtup); > + pfree(cp[0]); > + pfree(cp[1]); > + return T_ERROR; > + } > + > + /* > + * Fetch the named table field and it's type > + */ > + attrtup = SearchSysCacheAttName(classOid, cp[1]); > + if (!HeapTupleIsValid(attrtup)) > + { > + ReleaseSysCache(classtup); > + pfree(cp[0]); > + pfree(cp[1]); > + return T_ERROR; > + } > + attrStruct = (Form_pg_attribute) GETSTRUCT(attrtup); > + > + typetup = SearchSysCache(TYPEOID, > + >ObjectIdGetDatum(attrStruct->atttypid), > + 0, 0, 0); > + if (!HeapTupleIsValid(typetup)) > + elog(ERROR, "cache lookup for type %u of %s.%s failed", > + attrStruct->atttypid, cp[0], cp[1]); > + typeStruct = (Form_pg_type) GETSTRUCT(typetup); > + > + /* > + * Found that - build a compiler type struct and return it > + */ > + typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type)); > + > + typ->typname = strdup(NameStr(typeStruct->typname)); > + typ->typoid = attrStruct->atttypid; > + perm_fmgr_info(typeStruct->typinput, &(typ->typinput)); > + typ->typelem = typeStruct->typelem; > + typ->typbyval = typeStruct->typbyval; > + typ->typlen = typeStruct->typlen; > + typ->atttypmod = attrStruct->atttypmod; > + > + plpgsql_yylval.dtype = typ; > + > + ReleaseSysCache(classtup); > + ReleaseSysCache(attrtup); > + ReleaseSysCache(typetup); > + pfree(cp[0]); > + pfree(cp[1]); > + return T_DTYPE; > + } > > /* ---------- > * plpgsql_parse_wordrowtype Scanner found word%ROWTYPE. > *************** > *** 1125,1130 **** > --- 1245,1290 ---- > > pfree(cp[0]); > pfree(cp[1]); > + > + return T_ROW; > + } > + > + /* ---------- > + * plpgsql_parse_dblwordrowtype Scanner found word.word%ROWTYPE. > + * So word must be namespace qualified a table name. > + * ---------- > + */ > + #define ROWTYPE_JUNK_LEN 8 > + > + int > + plpgsql_parse_dblwordrowtype(char *word) > + { > + Oid classOid; > + char *cp; > + int i; > + RangeVar *relvar; > + > + /* 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] == '%'); > + > + cp = (char *) palloc((i + 1) * sizeof(char)); > + memset(cp, 0, (i + 1) * sizeof(char)); > + memcpy(cp, word, i * sizeof(char)); > + > + /* Lookup the relation */ > + relvar = makeRangeVarFromNameList(stringToQualifiedNameList(cp, >"plpgsql_parse_dblwordtype")); > + classOid = RangeVarGetRelid(relvar, true); > + if (!OidIsValid(classOid)) > + elog(ERROR, "%s: no such class", cp); > + > + /* > + * Build and return the complete row definition > + */ > + plpgsql_yylval.row = build_rowtype(classOid); > + > + pfree(cp); > > return T_ROW; > } > Index: src/pl/plpgsql/src/plpgsql.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/plpgsql.h,v > retrieving revision 1.27 > diff -c -r1.27 plpgsql.h > *** src/pl/plpgsql/src/plpgsql.h 4 Sep 2002 20:31:47 -0000 1.27 > --- src/pl/plpgsql/src/plpgsql.h 9 Sep 2002 04:21:37 -0000 > *************** > *** 568,574 **** > --- 568,576 ---- > 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); > extern int plpgsql_parse_wordrowtype(char *word); > + extern int plpgsql_parse_dblwordrowtype(char *word); > extern PLpgSQL_type *plpgsql_parse_datatype(char *string); > extern void plpgsql_adddatum(PLpgSQL_datum * new); > extern int plpgsql_add_initdatums(int **varnos); > Index: src/pl/plpgsql/src/scan.l > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/scan.l,v > retrieving revision 1.22 > diff -c -r1.22 scan.l > *** src/pl/plpgsql/src/scan.l 30 Aug 2002 00:28:41 -0000 1.22 > --- src/pl/plpgsql/src/scan.l 9 Sep 2002 04:23:49 -0000 > *************** > *** 170,183 **** > --- 170,187 ---- > {identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { >return plpgsql_parse_tripword(yytext); } > {identifier}{space}*%TYPE { return plpgsql_parse_wordtype(yytext); > } > {identifier}{space}*\.{space}*{identifier}{space}*%TYPE { return >plpgsql_parse_dblwordtype(yytext); } > + >{identifier}{space}*\.{space}*{identifier}{space}*\.{space}*{identifier}{space}*%TYPE > { return plpgsql_parse_tripwordtype(yytext); } > {identifier}{space}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); > } > + {identifier}{space}*\.{space}*{identifier}{space}*%ROWTYPE { return >plpgsql_parse_dblwordrowtype(yytext); } > > \${digit}+ { return >plpgsql_parse_word(yytext); } > \${digit}+{space}*\.{space}*{identifier} { return >plpgsql_parse_dblword(yytext); } > \${digit}+{space}*\.{space}*{identifier}{space}*\.{space}*{identifier} { >return plpgsql_parse_tripword(yytext); } > \${digit}+{space}*%TYPE { return >plpgsql_parse_wordtype(yytext); } > \${digit}+{space}*\.{space}*{identifier}{space}*%TYPE { return >plpgsql_parse_dblwordtype(yytext); } > + >\${digit}+{space}*\.{space}*{identifier}{space}*\.{space}*{identifier}{space}*%TYPE { >return plpgsql_parse_tripwordtype(yytext); } > \${digit}+{space}*%ROWTYPE { return plpgsql_parse_wordrowtype(yytext); > } > + \${digit}+{space}*\.{space}*{identifier}{space}*%ROWTYPE { return >plpgsql_parse_dblwordrowtype(yytext); } > > {digit}+ { return T_NUMBER; } > > -- nspname.relname.attname%TYPE > DROP FUNCTION t(); > CREATE OR REPLACE FUNCTION t() RETURNS TEXT AS ' > DECLARE > col_name pg_catalog.pg_attribute.attname%TYPE; > BEGIN > col_name := ''uga''; > RETURN col_name; > END; > ' LANGUAGE 'plpgsql'; > SELECT t(); > > -- nspname.relname%ROWTYPE > DROP FUNCTION t(); > CREATE OR REPLACE FUNCTION t() RETURNS pg_catalog.pg_attribute AS ' > DECLARE > rec pg_catalog.pg_attribute%ROWTYPE; > BEGIN > SELECT INTO rec * FROM pg_catalog.pg_attribute WHERE attrelid = 1247 AND attname >= ''typname''; > RETURN rec; > END; > ' LANGUAGE 'plpgsql'; > SELECT * FROM t(); > > -- nspname.relname.attname%TYPE > DROP FUNCTION t(); > CREATE OR REPLACE FUNCTION t() RETURNS pg_catalog.pg_attribute.attname%TYPE AS ' > DECLARE > rec pg_catalog.pg_attribute.attname%TYPE; > BEGIN > SELECT INTO rec pg_catalog.pg_attribute.attname FROM pg_catalog.pg_attribute >WHERE attrelid = 1247 AND attname = ''typname''; > RETURN rec; > END; > ' LANGUAGE 'plpgsql'; > SELECT t(); > > -- nspname.relname%ROWTYPE > DROP FUNCTION t(); > CREATE OR REPLACE FUNCTION t() RETURNS pg_catalog.pg_attribute AS ' > DECLARE > rec pg_catalog.pg_attribute%ROWTYPE; > BEGIN > SELECT INTO rec * FROM pg_catalog.pg_attribute WHERE attrelid = 1247 AND attname >= ''typname''; > RETURN rec; > END; > ' LANGUAGE 'plpgsql'; > SELECT * FROM t(); > > ---------------------------(end of broadcast)--------------------------- > TIP 3: if posting/reading through Usenet, please send an appropriate > subscribe-nomail command to [EMAIL PROTECTED] so that your > message can get through to the mailing list cleanly -- Bruce Momjian | http://candle.pha.pa.us [EMAIL PROTECTED] | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073 ---------------------------(end of broadcast)--------------------------- TIP 2: you can get off all lists at once with the unregister command (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])