mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-06 15:24:56 +08:00
> 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
This commit is contained in:
parent
81186865fe
commit
b3f52320f6
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
||||
|
47
src/test/regress/sql/plpgsql-nsp-testing.sql
Normal file
47
src/test/regress/sql/plpgsql-nsp-testing.sql
Normal file
@ -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();
|
Loading…
Reference in New Issue
Block a user