Thread: Re: Schemas not available for pl/pgsql %TYPE....
Tom Lane wrote: > Sean Chittenden <sean@chittenden.org> 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();
Your patch has been added to the PostgreSQL unapplied patches list at: http://candle.pha.pa.us/cgi-bin/pgpatches I will try to apply it within the next 48 hours. --------------------------------------------------------------------------- Joe Conway wrote: > Tom Lane wrote: > > Sean Chittenden <sean@chittenden.org> 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 majordomo@postgresql.org so that your > message can get through to the mailing list cleanly -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001+ If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania19073
Patch applied. Thanks. --------------------------------------------------------------------------- Joe Conway wrote: > Tom Lane wrote: > > Sean Chittenden <sean@chittenden.org> 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 majordomo@postgresql.org so that your > message can get through to the mailing list cleanly -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001+ If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania19073
Does anyone know if such effort is also required to pl/python to become "schema aware"? Regards, Greg Copeland On Wed, 2002-09-11 at 19:24, Bruce Momjian wrote: > > Patch applied. Thanks. > > --------------------------------------------------------------------------- > > > Joe Conway wrote: > > Tom Lane wrote: > > > Sean Chittenden <sean@chittenden.org> 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 majordomo@postgresql.org so that your > > message can get through to the mailing list cleanly > > -- > Bruce Momjian | http://candle.pha.pa.us > pgman@candle.pha.pa.us | (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 majordomo@postgresql.org)
Does pl/python even have a DECLARE section that can mimick the data type of an existing table column? --------------------------------------------------------------------------- Greg Copeland wrote: -- Start of PGP signed section. > Does anyone know if such effort is also required to pl/python to become > "schema aware"? > > Regards, > > Greg Copeland > > > On Wed, 2002-09-11 at 19:24, Bruce Momjian wrote: > > > > Patch applied. Thanks. > > > > --------------------------------------------------------------------------- > > > > > > Joe Conway wrote: > > > Tom Lane wrote: > > > > Sean Chittenden <sean@chittenden.org> 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 majordomo@postgresql.org so that your > > > message can get through to the mailing list cleanly > > > > -- > > Bruce Momjian | http://candle.pha.pa.us > > pgman@candle.pha.pa.us | (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 majordomo@postgresql.org) > -- End of PGP section, PGP failed! -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001+ If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania19073