Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2a6b47c

Browse files
committed
Improve plpgsql's error messages for incorrect %TYPE and %ROWTYPE.
If one of these constructs referenced a nonexistent object, we'd fallthrough to feeding the whole construct to the core parser, which wouldreject it with a "syntax error" message. That's pretty unhelpful andmisleading. There's no good reason for plpgsql_parse_wordtype andfriends not to throw a useful error for incorrect input, so make themdo that instead of returning NULL.Discussion:https://postgr.es/m/1964516.1708977740@sss.pgh.pa.us
1 parent363eb05 commit2a6b47c

File tree

4 files changed

+83
-57
lines changed

4 files changed

+83
-57
lines changed

‎src/pl/plpgsql/src/expected/plpgsql_misc.out

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,39 @@ CREATE OR REPLACE PROCEDURE public.test2(IN x integer)
2929
BEGIN ATOMIC
3030
SELECT (x + 2);
3131
END
32+
-- Test %TYPE and %ROWTYPE error cases
33+
create table misc_table(f1 int);
34+
do $$ declare x foo%type; begin end $$;
35+
ERROR: variable "foo" does not exist
36+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
37+
do $$ declare x notice%type; begin end $$; -- covers unreserved-keyword case
38+
ERROR: variable "notice" does not exist
39+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
40+
do $$ declare x foo.bar%type; begin end $$;
41+
ERROR: relation "foo" does not exist
42+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
43+
do $$ declare x foo.bar.baz%type; begin end $$;
44+
ERROR: schema "foo" does not exist
45+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
46+
do $$ declare x public.foo.bar%type; begin end $$;
47+
ERROR: relation "public.foo" does not exist
48+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
49+
do $$ declare x public.misc_table.zed%type; begin end $$;
50+
ERROR: column "zed" of relation "misc_table" does not exist
51+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
52+
do $$ declare x foo%rowtype; begin end $$;
53+
ERROR: relation "foo" does not exist
54+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
55+
do $$ declare x notice%rowtype; begin end $$; -- covers unreserved-keyword case
56+
ERROR: relation "notice" does not exist
57+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
58+
do $$ declare x foo.bar%rowtype; begin end $$;
59+
ERROR: schema "foo" does not exist
60+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
61+
do $$ declare x foo.bar.baz%rowtype; begin end $$;
62+
ERROR: cross-database references are not implemented: "foo.bar.baz"
63+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
64+
do $$ declare x public.foo%rowtype; begin end $$;
65+
ERROR: relation "public.foo" does not exist
66+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 1
67+
do $$ declare x public.misc_table%rowtype; begin end $$;

‎src/pl/plpgsql/src/pl_comp.c

Lines changed: 29 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,7 +1599,7 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3,
15991599
* plpgsql_parse_wordtypeThe scanner found word%TYPE. word should be
16001600
*a pre-existing variable name.
16011601
*
1602-
* Returns datatype struct, or NULL if no match found for word.
1602+
* Returns datatype struct. Throws error if no match found for word.
16031603
* ----------
16041604
*/
16051605
PLpgSQL_type*
@@ -1623,23 +1623,24 @@ plpgsql_parse_wordtype(char *ident)
16231623
casePLPGSQL_NSTYPE_REC:
16241624
return ((PLpgSQL_rec*) (plpgsql_Datums[nse->itemno]))->datatype;
16251625
default:
1626-
returnNULL;
1626+
break;
16271627
}
16281628
}
16291629

1630-
/*
1631-
* Nothing found - up to now it's a word without any special meaning for
1632-
* us.
1633-
*/
1634-
returnNULL;
1630+
/* No match, complain */
1631+
ereport(ERROR,
1632+
(errcode(ERRCODE_UNDEFINED_OBJECT),
1633+
errmsg("variable \"%s\" does not exist",ident)));
1634+
returnNULL;/* keep compiler quiet */
16351635
}
16361636

16371637

16381638
/* ----------
16391639
* plpgsql_parse_cwordtypeSame lookup for compositeword%TYPE
16401640
*
16411641
* Here, we allow either a block-qualified variable name, or a reference
1642-
* to a column of some table.
1642+
* to a column of some table. (If we must throw error, we assume that the
1643+
* latter case was intended.)
16431644
* ----------
16441645
*/
16451646
PLpgSQL_type*
@@ -1648,12 +1649,11 @@ plpgsql_parse_cwordtype(List *idents)
16481649
PLpgSQL_type*dtype=NULL;
16491650
PLpgSQL_nsitem*nse;
16501651
intnnames;
1651-
constchar*fldname;
1652+
RangeVar*relvar=NULL;
1653+
constchar*fldname=NULL;
16521654
OidclassOid;
1653-
HeapTupleclasstup=NULL;
16541655
HeapTupleattrtup=NULL;
16551656
HeapTupletypetup=NULL;
1656-
Form_pg_classclassStruct;
16571657
Form_pg_attributeattrStruct;
16581658
MemoryContextoldCxt;
16591659

@@ -1688,57 +1688,39 @@ plpgsql_parse_cwordtype(List *idents)
16881688
/*
16891689
* First word could also be a table name
16901690
*/
1691-
classOid=RelnameGetRelid(strVal(linitial(idents)));
1692-
if (!OidIsValid(classOid))
1693-
gotodone;
1691+
relvar=makeRangeVar(NULL,
1692+
strVal(linitial(idents)),
1693+
-1);
16941694
fldname=strVal(lsecond(idents));
16951695
}
1696-
elseif (list_length(idents)==3)
1696+
else
16971697
{
1698-
RangeVar*relvar;
1699-
17001698
/*
17011699
* We could check for a block-qualified reference to a field of a
17021700
* record variable, but %TYPE is documented as applying to variables,
17031701
* not fields of variables. Things would get rather ambiguous if we
17041702
* allowed either interpretation.
17051703
*/
1706-
relvar=makeRangeVar(strVal(linitial(idents)),
1707-
strVal(lsecond(idents)),
1708-
-1);
1709-
/* Can't lock relation - we might not have privileges. */
1710-
classOid=RangeVarGetRelid(relvar,NoLock, true);
1711-
if (!OidIsValid(classOid))
1712-
gotodone;
1713-
fldname=strVal(lthird(idents));
1714-
}
1715-
else
1716-
gotodone;
1704+
List*rvnames;
17171705

1718-
classtup=SearchSysCache1(RELOID,ObjectIdGetDatum(classOid));
1719-
if (!HeapTupleIsValid(classtup))
1720-
gotodone;
1721-
classStruct= (Form_pg_class)GETSTRUCT(classtup);
1706+
Assert(list_length(idents)>2);
1707+
rvnames=list_delete_last(list_copy(idents));
1708+
relvar=makeRangeVarFromNameList(rvnames);
1709+
fldname=strVal(llast(idents));
1710+
}
17221711

1723-
/*
1724-
* It must be a relation, sequence, view, materialized view, composite
1725-
* type, or foreign table
1726-
*/
1727-
if (classStruct->relkind!=RELKIND_RELATION&&
1728-
classStruct->relkind!=RELKIND_SEQUENCE&&
1729-
classStruct->relkind!=RELKIND_VIEW&&
1730-
classStruct->relkind!=RELKIND_MATVIEW&&
1731-
classStruct->relkind!=RELKIND_COMPOSITE_TYPE&&
1732-
classStruct->relkind!=RELKIND_FOREIGN_TABLE&&
1733-
classStruct->relkind!=RELKIND_PARTITIONED_TABLE)
1734-
gotodone;
1712+
/* Look up relation name. Can't lock it - we might not have privileges. */
1713+
classOid=RangeVarGetRelid(relvar,NoLock, false);
17351714

17361715
/*
17371716
* Fetch the named table field and its type
17381717
*/
17391718
attrtup=SearchSysCacheAttName(classOid,fldname);
17401719
if (!HeapTupleIsValid(attrtup))
1741-
gotodone;
1720+
ereport(ERROR,
1721+
(errcode(ERRCODE_UNDEFINED_COLUMN),
1722+
errmsg("column \"%s\" of relation \"%s\" does not exist",
1723+
fldname,relvar->relname)));
17421724
attrStruct= (Form_pg_attribute)GETSTRUCT(attrtup);
17431725

17441726
typetup=SearchSysCache1(TYPEOID,
@@ -1759,8 +1741,6 @@ plpgsql_parse_cwordtype(List *idents)
17591741
MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
17601742

17611743
done:
1762-
if (HeapTupleIsValid(classtup))
1763-
ReleaseSysCache(classtup);
17641744
if (HeapTupleIsValid(attrtup))
17651745
ReleaseSysCache(attrtup);
17661746
if (HeapTupleIsValid(typetup))
@@ -1824,16 +1804,12 @@ plpgsql_parse_cwordrowtype(List *idents)
18241804
* As above, this is a relation lookup but could be a type lookup if we
18251805
* weren't being backwards-compatible about error wording.
18261806
*/
1827-
if (list_length(idents)!=2)
1828-
returnNULL;
18291807

18301808
/* Avoid memory leaks in long-term function context */
18311809
oldCxt=MemoryContextSwitchTo(plpgsql_compile_tmp_cxt);
18321810

18331811
/* Look up relation name. Can't lock it - we might not have privileges. */
1834-
relvar=makeRangeVar(strVal(linitial(idents)),
1835-
strVal(lsecond(idents)),
1836-
-1);
1812+
relvar=makeRangeVarFromNameList(idents);
18371813
classOid=RangeVarGetRelid(relvar,NoLock, false);
18381814

18391815
/* Some relkinds lack type OIDs */
@@ -1842,7 +1818,7 @@ plpgsql_parse_cwordrowtype(List *idents)
18421818
ereport(ERROR,
18431819
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
18441820
errmsg("relation \"%s\" does not have a composite type",
1845-
strVal(lsecond(idents)))));
1821+
relvar->relname)));
18461822

18471823
MemoryContextSwitchTo(oldCxt);
18481824

‎src/pl/plpgsql/src/pl_gram.y

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2810,10 +2810,7 @@ read_datatype(int tok)
28102810

28112811
/*
28122812
* If we have a simple or composite identifier, check for %TYPE and
2813-
* %ROWTYPE constructs. (Note that if plpgsql_parse_wordtype et al fail
2814-
* to recognize the identifier, we'll fall through and pass the whole
2815-
* string to parse_datatype, which will assuredly give an unhelpful
2816-
* "syntax error". Should we try to give a more specific error?)
2813+
* %ROWTYPE constructs.
28172814
*/
28182815
if (tok == T_WORD)
28192816
{

‎src/pl/plpgsql/src/sql/plpgsql_misc.sql

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,20 @@ $$;
2020

2121
\sf test1
2222
\sf test2
23+
24+
-- Test %TYPE and %ROWTYPE error cases
25+
createtablemisc_table(f1int);
26+
27+
do $$ declare x foo%type;begin end $$;
28+
do $$ declare x notice%type;begin end $$;-- covers unreserved-keyword case
29+
do $$ declare xfoo.bar%type;begin end $$;
30+
do $$ declare xfoo.bar.baz%type;begin end $$;
31+
do $$ declare xpublic.foo.bar%type;begin end $$;
32+
do $$ declare xpublic.misc_table.zed%type;begin end $$;
33+
34+
do $$ declare x foo%rowtype;begin end $$;
35+
do $$ declare x notice%rowtype;begin end $$;-- covers unreserved-keyword case
36+
do $$ declare xfoo.bar%rowtype;begin end $$;
37+
do $$ declare xfoo.bar.baz%rowtype;begin end $$;
38+
do $$ declare xpublic.foo%rowtype;begin end $$;
39+
do $$ declare xpublic.misc_table%rowtype;begin end $$;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp