From b3f52320f6cb8374f3db5397e63b82f595c90681 Mon Sep 17 00:00:00 2001 From: Bruce Momjian <bruce@momjian.us> Date: Thu, 12 Sep 2002 00:24:10 +0000 Subject: [PATCH] > 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: Joe Conway --- src/pl/plpgsql/src/pl_comp.c | 162 ++++++++++++++++++- src/pl/plpgsql/src/plpgsql.h | 4 +- src/pl/plpgsql/src/scan.l | 6 +- src/test/regress/sql/plpgsql-nsp-testing.sql | 47 ++++++ 4 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 src/test/regress/sql/plpgsql-nsp-testing.sql diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index c25f964f7c6..00f2997ae3e 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.51 2002/09/04 20:31:47 momjian Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.52 2002/09/12 00:24:09 momjian Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -1092,6 +1092,126 @@ plpgsql_parse_dblwordtype(char *word) 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. @@ -1129,6 +1249,46 @@ plpgsql_parse_wordrowtype(char *word) 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; +} + /* * Build a rowtype data structure given the pg_class OID. */ diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 1d5ab78a328..cf3e1942d8a 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.27 2002/09/04 20:31:47 momjian Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.28 2002/09/12 00:24:09 momjian Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -568,7 +568,9 @@ 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); 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); diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l index 3976b542756..697be513930 100644 --- a/src/pl/plpgsql/src/scan.l +++ b/src/pl/plpgsql/src/scan.l @@ -4,7 +4,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.22 2002/08/30 00:28:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/Attic/scan.l,v 1.23 2002/09/12 00:24:09 momjian Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -170,14 +170,18 @@ dump { return O_DUMP; } {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; } diff --git a/src/test/regress/sql/plpgsql-nsp-testing.sql b/src/test/regress/sql/plpgsql-nsp-testing.sql new file mode 100644 index 00000000000..8694b8db649 --- /dev/null +++ b/src/test/regress/sql/plpgsql-nsp-testing.sql @@ -0,0 +1,47 @@ +-- 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(); -- GitLab