Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 43b46aa

Browse files
committed
Clean up some edge cases in plpgsql's %TYPE parsing.
Support referencing a composite-type variable in %TYPE. Remove the undocumented, untested, and pretty useless ability to have the subject of %TYPE be an (unqualified) type name. You get the same result by just not writing %TYPE. Add or adjust some test cases to improve code coverage here. Discussion: https://postgr.es/m/716852.1704402127@sss.pgh.pa.us
1 parent dbad1c5 commit 43b46aa

File tree

5 files changed

+119
-46
lines changed

5 files changed

+119
-46
lines changed

src/pl/plpgsql/src/expected/plpgsql_record.out

+46
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,52 @@ NOTICE: r1 = (1,2)
306306
ERROR: record "r1" has no field "nosuchfield"
307307
CONTEXT: SQL expression "r1.nosuchfield"
308308
PL/pgSQL function inline_code_block line 9 at RAISE
309+
-- check %type with block-qualified variable names
310+
do $$
311+
<<blk>>
312+
declare
313+
v int;
314+
r two_int8s;
315+
v1 v%type;
316+
v2 blk.v%type;
317+
r1 r%type;
318+
r2 blk.r%type;
319+
begin
320+
raise notice '%', pg_typeof(v1);
321+
raise notice '%', pg_typeof(v2);
322+
raise notice '%', pg_typeof(r1);
323+
raise notice '%', pg_typeof(r2);
324+
end$$;
325+
NOTICE: integer
326+
NOTICE: integer
327+
NOTICE: two_int8s
328+
NOTICE: two_int8s
329+
-- check that type record can be passed through %type
330+
do $$
331+
declare r1 record;
332+
r2 r1%type;
333+
begin
334+
r2 := row(1,2);
335+
raise notice 'r2 = %', r2;
336+
r2 := row(3,4,5);
337+
raise notice 'r2 = %', r2;
338+
end$$;
339+
NOTICE: r2 = (1,2)
340+
NOTICE: r2 = (3,4,5)
341+
-- arrays of record are not supported at the moment
342+
do $$
343+
declare r1 record[];
344+
begin
345+
end$$;
346+
ERROR: variable "r1" has pseudo-type record[]
347+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 2
348+
do $$
349+
declare r1 record;
350+
r2 r1%type[];
351+
begin
352+
end$$;
353+
ERROR: variable "r2" has pseudo-type record[]
354+
CONTEXT: compilation of PL/pgSQL function "inline_code_block" near line 3
309355
-- check repeated assignments to composite fields
310356
create table some_table (id int, data text);
311357
do $$

src/pl/plpgsql/src/pl_comp.c

+24-37
Original file line numberDiff line numberDiff line change
@@ -1596,19 +1596,16 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3,
15961596

15971597

15981598
/* ----------
1599-
* plpgsql_parse_wordtype The scanner found word%TYPE. word can be
1600-
* a variable name or a basetype.
1599+
* plpgsql_parse_wordtype The scanner found word%TYPE. word should be
1600+
* a pre-existing variable name.
16011601
*
16021602
* Returns datatype struct, or NULL if no match found for word.
16031603
* ----------
16041604
*/
16051605
PLpgSQL_type *
16061606
plpgsql_parse_wordtype(char *ident)
16071607
{
1608-
PLpgSQL_type *dtype;
16091608
PLpgSQL_nsitem *nse;
1610-
TypeName *typeName;
1611-
HeapTuple typeTup;
16121609

16131610
/*
16141611
* Do a lookup in the current namespace stack
@@ -1623,39 +1620,13 @@ plpgsql_parse_wordtype(char *ident)
16231620
{
16241621
case PLPGSQL_NSTYPE_VAR:
16251622
return ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
1626-
1627-
/* XXX perhaps allow REC/ROW here? */
1628-
1623+
case PLPGSQL_NSTYPE_REC:
1624+
return ((PLpgSQL_rec *) (plpgsql_Datums[nse->itemno]))->datatype;
16291625
default:
16301626
return NULL;
16311627
}
16321628
}
16331629

1634-
/*
1635-
* Word wasn't found in the namespace stack. Try to find a data type with
1636-
* that name, but ignore shell types and complex types.
1637-
*/
1638-
typeName = makeTypeName(ident);
1639-
typeTup = LookupTypeName(NULL, typeName, NULL, false);
1640-
if (typeTup)
1641-
{
1642-
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
1643-
1644-
if (!typeStruct->typisdefined ||
1645-
typeStruct->typrelid != InvalidOid)
1646-
{
1647-
ReleaseSysCache(typeTup);
1648-
return NULL;
1649-
}
1650-
1651-
dtype = build_datatype(typeTup, -1,
1652-
plpgsql_curr_compile->fn_input_collation,
1653-
typeName);
1654-
1655-
ReleaseSysCache(typeTup);
1656-
return dtype;
1657-
}
1658-
16591630
/*
16601631
* Nothing found - up to now it's a word without any special meaning for
16611632
* us.
@@ -1666,13 +1637,17 @@ plpgsql_parse_wordtype(char *ident)
16661637

16671638
/* ----------
16681639
* plpgsql_parse_cwordtype Same lookup for compositeword%TYPE
1640+
*
1641+
* Here, we allow either a block-qualified variable name, or a reference
1642+
* to a column of some table.
16691643
* ----------
16701644
*/
16711645
PLpgSQL_type *
16721646
plpgsql_parse_cwordtype(List *idents)
16731647
{
16741648
PLpgSQL_type *dtype = NULL;
16751649
PLpgSQL_nsitem *nse;
1650+
int nnames;
16761651
const char *fldname;
16771652
Oid classOid;
16781653
HeapTuple classtup = NULL;
@@ -1688,21 +1663,27 @@ plpgsql_parse_cwordtype(List *idents)
16881663
if (list_length(idents) == 2)
16891664
{
16901665
/*
1691-
* Do a lookup in the current namespace stack. We don't need to check
1692-
* number of names matched, because we will only consider scalar
1693-
* variables.
1666+
* Do a lookup in the current namespace stack
16941667
*/
16951668
nse = plpgsql_ns_lookup(plpgsql_ns_top(), false,
16961669
strVal(linitial(idents)),
16971670
strVal(lsecond(idents)),
16981671
NULL,
1699-
NULL);
1672+
&nnames);
17001673

17011674
if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
17021675
{
1676+
/* Block-qualified reference to scalar variable. */
17031677
dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
17041678
goto done;
17051679
}
1680+
else if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_REC &&
1681+
nnames == 2)
1682+
{
1683+
/* Block-qualified reference to record variable. */
1684+
dtype = ((PLpgSQL_rec *) (plpgsql_Datums[nse->itemno]))->datatype;
1685+
goto done;
1686+
}
17061687

17071688
/*
17081689
* First word could also be a table name
@@ -1716,6 +1697,12 @@ plpgsql_parse_cwordtype(List *idents)
17161697
{
17171698
RangeVar *relvar;
17181699

1700+
/*
1701+
* We could check for a block-qualified reference to a field of a
1702+
* record variable, but %TYPE is documented as applying to variables,
1703+
* not fields of variables. Things would get rather ambiguous if we
1704+
* allowed either interpretation.
1705+
*/
17191706
relvar = makeRangeVar(strVal(linitial(idents)),
17201707
strVal(lsecond(idents)),
17211708
-1);

src/pl/plpgsql/src/sql/plpgsql_record.sql

+40
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,46 @@ begin
199199
raise notice 'r1.nosuchfield = %', r1.nosuchfield;
200200
end$$;
201201

202+
-- check %type with block-qualified variable names
203+
do $$
204+
<<blk>>
205+
declare
206+
v int;
207+
r two_int8s;
208+
v1 v%type;
209+
v2 blk.v%type;
210+
r1 r%type;
211+
r2 blk.r%type;
212+
begin
213+
raise notice '%', pg_typeof(v1);
214+
raise notice '%', pg_typeof(v2);
215+
raise notice '%', pg_typeof(r1);
216+
raise notice '%', pg_typeof(r2);
217+
end$$;
218+
219+
-- check that type record can be passed through %type
220+
do $$
221+
declare r1 record;
222+
r2 r1%type;
223+
begin
224+
r2 := row(1,2);
225+
raise notice 'r2 = %', r2;
226+
r2 := row(3,4,5);
227+
raise notice 'r2 = %', r2;
228+
end$$;
229+
230+
-- arrays of record are not supported at the moment
231+
do $$
232+
declare r1 record[];
233+
begin
234+
end$$;
235+
236+
do $$
237+
declare r1 record;
238+
r2 r1%type[];
239+
begin
240+
end$$;
241+
202242
-- check repeated assignments to composite fields
203243
create table some_table (id int, data text);
204244

src/test/regress/expected/plpgsql.out

+5-5
Original file line numberDiff line numberDiff line change
@@ -5795,18 +5795,18 @@ SELECT * FROM get_from_partitioned_table(1) AS t;
57955795
(1 row)
57965796

57975797
CREATE OR REPLACE FUNCTION list_partitioned_table()
5798-
RETURNS SETOF partitioned_table.a%TYPE AS $$
5798+
RETURNS SETOF public.partitioned_table.a%TYPE AS $$
57995799
DECLARE
5800-
row partitioned_table%ROWTYPE;
5801-
a_val partitioned_table.a%TYPE;
5800+
row public.partitioned_table%ROWTYPE;
5801+
a_val public.partitioned_table.a%TYPE;
58025802
BEGIN
5803-
FOR row IN SELECT * FROM partitioned_table ORDER BY a LOOP
5803+
FOR row IN SELECT * FROM public.partitioned_table ORDER BY a LOOP
58045804
a_val := row.a;
58055805
RETURN NEXT a_val;
58065806
END LOOP;
58075807
RETURN;
58085808
END; $$ LANGUAGE plpgsql;
5809-
NOTICE: type reference partitioned_table.a%TYPE converted to integer
5809+
NOTICE: type reference public.partitioned_table.a%TYPE converted to integer
58105810
SELECT * FROM list_partitioned_table() AS t;
58115811
t
58125812
---

src/test/regress/sql/plpgsql.sql

+4-4
Original file line numberDiff line numberDiff line change
@@ -4734,12 +4734,12 @@ END; $$ LANGUAGE plpgsql;
47344734
SELECT * FROM get_from_partitioned_table(1) AS t;
47354735

47364736
CREATE OR REPLACE FUNCTION list_partitioned_table()
4737-
RETURNS SETOF partitioned_table.a%TYPE AS $$
4737+
RETURNS SETOF public.partitioned_table.a%TYPE AS $$
47384738
DECLARE
4739-
row partitioned_table%ROWTYPE;
4740-
a_val partitioned_table.a%TYPE;
4739+
row public.partitioned_table%ROWTYPE;
4740+
a_val public.partitioned_table.a%TYPE;
47414741
BEGIN
4742-
FOR row IN SELECT * FROM partitioned_table ORDER BY a LOOP
4742+
FOR row IN SELECT * FROM public.partitioned_table ORDER BY a LOOP
47434743
a_val := row.a;
47444744
RETURN NEXT a_val;
47454745
END LOOP;

0 commit comments

Comments
 (0)