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

Commit f926300

Browse files
committed
Support CONSTANT/NOT NULL/initial value for plpgsql composite variables.
These features were never implemented previously for composite or record variables ... not that the documentation admitted it, so there's no doc updates here. This also fixes some issues concerning enforcing DOMAIN NOT NULL constraints against plpgsql variables, although I'm not sure that that topic is completely dealt with. I created a new plpgsql test file for these features, and moved the one relevant existing test case into that file. Tom Lane, reviewed by Daniel Gustafsson Discussion: https://postgr.es/m/18362.1514605650@sss.pgh.pa.us
1 parent fd333bc commit f926300

File tree

10 files changed

+686
-134
lines changed

10 files changed

+686
-134
lines changed

src/pl/plpgsql/src/Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ DATA = plpgsql.control plpgsql--1.0.sql plpgsql--unpackaged--1.0.sql
2626

2727
REGRESS_OPTS = --dbname=$(PL_TESTDB)
2828

29-
REGRESS = plpgsql_call plpgsql_control plpgsql_record plpgsql_transaction
29+
REGRESS = plpgsql_call plpgsql_control plpgsql_record \
30+
plpgsql_transaction plpgsql_varprops
3031

3132
all: all-lib
3233

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
--
2+
-- Tests for PL/pgSQL variable properties: CONSTANT, NOT NULL, initializers
3+
--
4+
create type var_record as (f1 int4, f2 int4);
5+
create domain int_nn as int not null;
6+
create domain var_record_nn as var_record not null;
7+
create domain var_record_colnn as var_record check((value).f2 is not null);
8+
-- CONSTANT
9+
do $$
10+
declare x constant int := 42;
11+
begin
12+
raise notice 'x = %', x;
13+
end$$;
14+
NOTICE: x = 42
15+
do $$
16+
declare x constant int;
17+
begin
18+
x := 42; -- fail
19+
end$$;
20+
ERROR: variable "x" is declared CONSTANT
21+
LINE 4: x := 42; -- fail
22+
^
23+
do $$
24+
declare x constant int; y int;
25+
begin
26+
for x, y in select 1, 2 loop -- fail
27+
end loop;
28+
end$$;
29+
ERROR: variable "x" is declared CONSTANT
30+
LINE 4: for x, y in select 1, 2 loop -- fail
31+
^
32+
do $$
33+
declare x constant int[];
34+
begin
35+
x[1] := 42; -- fail
36+
end$$;
37+
ERROR: variable "x" is declared CONSTANT
38+
LINE 4: x[1] := 42; -- fail
39+
^
40+
do $$
41+
declare x constant int[]; y int;
42+
begin
43+
for x[1], y in select 1, 2 loop -- fail (currently, unsupported syntax)
44+
end loop;
45+
end$$;
46+
ERROR: syntax error at or near "["
47+
LINE 4: for x[1], y in select 1, 2 loop -- fail (currently, unsup...
48+
^
49+
do $$
50+
declare x constant var_record;
51+
begin
52+
x.f1 := 42; -- fail
53+
end$$;
54+
ERROR: variable "x" is declared CONSTANT
55+
LINE 4: x.f1 := 42; -- fail
56+
^
57+
do $$
58+
declare x constant var_record; y int;
59+
begin
60+
for x.f1, y in select 1, 2 loop -- fail
61+
end loop;
62+
end$$;
63+
ERROR: variable "x" is declared CONSTANT
64+
LINE 4: for x.f1, y in select 1, 2 loop -- fail
65+
^
66+
-- initializer expressions
67+
do $$
68+
declare x int := sin(0);
69+
begin
70+
raise notice 'x = %', x;
71+
end$$;
72+
NOTICE: x = 0
73+
do $$
74+
declare x int := 1/0; -- fail
75+
begin
76+
raise notice 'x = %', x;
77+
end$$;
78+
ERROR: division by zero
79+
CONTEXT: SQL statement "SELECT 1/0"
80+
PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
81+
do $$
82+
declare x bigint[] := array[1,3,5];
83+
begin
84+
raise notice 'x = %', x;
85+
end$$;
86+
NOTICE: x = {1,3,5}
87+
do $$
88+
declare x record := row(1,2,3);
89+
begin
90+
raise notice 'x = %', x;
91+
end$$;
92+
NOTICE: x = (1,2,3)
93+
do $$
94+
declare x var_record := row(1,2);
95+
begin
96+
raise notice 'x = %', x;
97+
end$$;
98+
NOTICE: x = (1,2)
99+
-- NOT NULL
100+
do $$
101+
declare x int not null; -- fail
102+
begin
103+
raise notice 'x = %', x;
104+
end$$;
105+
ERROR: variable "x" must have a default value, since it's declared NOT NULL
106+
LINE 2: declare x int not null; -- fail
107+
^
108+
do $$
109+
declare x int not null := 42;
110+
begin
111+
raise notice 'x = %', x;
112+
x := null; -- fail
113+
end$$;
114+
NOTICE: x = 42
115+
ERROR: null value cannot be assigned to variable "x" declared NOT NULL
116+
CONTEXT: PL/pgSQL function inline_code_block line 5 at assignment
117+
do $$
118+
declare x int not null := null; -- fail
119+
begin
120+
raise notice 'x = %', x;
121+
end$$;
122+
ERROR: null value cannot be assigned to variable "x" declared NOT NULL
123+
CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
124+
do $$
125+
declare x record not null; -- fail
126+
begin
127+
raise notice 'x = %', x;
128+
end$$;
129+
ERROR: variable "x" must have a default value, since it's declared NOT NULL
130+
LINE 2: declare x record not null; -- fail
131+
^
132+
do $$
133+
declare x record not null := row(42);
134+
begin
135+
raise notice 'x = %', x;
136+
x := row(null); -- ok
137+
raise notice 'x = %', x;
138+
x := null; -- fail
139+
end$$;
140+
NOTICE: x = (42)
141+
NOTICE: x = ()
142+
ERROR: null value cannot be assigned to variable "x" declared NOT NULL
143+
CONTEXT: PL/pgSQL function inline_code_block line 7 at assignment
144+
do $$
145+
declare x record not null := null; -- fail
146+
begin
147+
raise notice 'x = %', x;
148+
end$$;
149+
ERROR: null value cannot be assigned to variable "x" declared NOT NULL
150+
CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
151+
do $$
152+
declare x var_record not null; -- fail
153+
begin
154+
raise notice 'x = %', x;
155+
end$$;
156+
ERROR: variable "x" must have a default value, since it's declared NOT NULL
157+
LINE 2: declare x var_record not null; -- fail
158+
^
159+
do $$
160+
declare x var_record not null := row(41,42);
161+
begin
162+
raise notice 'x = %', x;
163+
x := row(null,null); -- ok
164+
raise notice 'x = %', x;
165+
x := null; -- fail
166+
end$$;
167+
NOTICE: x = (41,42)
168+
NOTICE: x = (,)
169+
ERROR: null value cannot be assigned to variable "x" declared NOT NULL
170+
CONTEXT: PL/pgSQL function inline_code_block line 7 at assignment
171+
do $$
172+
declare x var_record not null := null; -- fail
173+
begin
174+
raise notice 'x = %', x;
175+
end$$;
176+
ERROR: null value cannot be assigned to variable "x" declared NOT NULL
177+
CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
178+
-- Check that variables are reinitialized on block re-entry.
179+
\set VERBOSITY terse \\ -- needed for output stability
180+
do $$
181+
begin
182+
for i in 1..3 loop
183+
declare
184+
x int;
185+
y int := i;
186+
r record;
187+
c var_record;
188+
begin
189+
if i = 1 then
190+
x := 42;
191+
r := row(i, i+1);
192+
c := row(i, i+1);
193+
end if;
194+
raise notice 'x = %', x;
195+
raise notice 'y = %', y;
196+
raise notice 'r = %', r;
197+
raise notice 'c = %', c;
198+
end;
199+
end loop;
200+
end$$;
201+
NOTICE: x = 42
202+
NOTICE: y = 1
203+
NOTICE: r = (1,2)
204+
NOTICE: c = (1,2)
205+
NOTICE: x = <NULL>
206+
NOTICE: y = 2
207+
NOTICE: r = <NULL>
208+
NOTICE: c = <NULL>
209+
NOTICE: x = <NULL>
210+
NOTICE: y = 3
211+
NOTICE: r = <NULL>
212+
NOTICE: c = <NULL>
213+
\set VERBOSITY default
214+
-- Check enforcement of domain constraints during initialization
215+
do $$
216+
declare x int_nn; -- fail
217+
begin
218+
raise notice 'x = %', x;
219+
end$$;
220+
ERROR: domain int_nn does not allow null values
221+
CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
222+
do $$
223+
declare x int_nn := null; -- fail
224+
begin
225+
raise notice 'x = %', x;
226+
end$$;
227+
ERROR: domain int_nn does not allow null values
228+
CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
229+
do $$
230+
declare x int_nn := 42;
231+
begin
232+
raise notice 'x = %', x;
233+
x := null; -- fail
234+
end$$;
235+
NOTICE: x = 42
236+
ERROR: domain int_nn does not allow null values
237+
CONTEXT: PL/pgSQL function inline_code_block line 5 at assignment
238+
do $$
239+
declare x var_record_nn; -- fail
240+
begin
241+
raise notice 'x = %', x;
242+
end$$;
243+
ERROR: domain var_record_nn does not allow null values
244+
CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
245+
do $$
246+
declare x var_record_nn := null; -- fail
247+
begin
248+
raise notice 'x = %', x;
249+
end$$;
250+
ERROR: domain var_record_nn does not allow null values
251+
CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
252+
do $$
253+
declare x var_record_nn := row(1,2);
254+
begin
255+
raise notice 'x = %', x;
256+
x := row(null,null); -- ok
257+
x := null; -- fail
258+
end$$;
259+
NOTICE: x = (1,2)
260+
ERROR: domain var_record_nn does not allow null values
261+
CONTEXT: PL/pgSQL function inline_code_block line 6 at assignment
262+
do $$
263+
declare x var_record_colnn; -- fail
264+
begin
265+
raise notice 'x = %', x;
266+
end$$;
267+
ERROR: value for domain var_record_colnn violates check constraint "var_record_colnn_check"
268+
CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
269+
do $$
270+
declare x var_record_colnn := null; -- fail
271+
begin
272+
raise notice 'x = %', x;
273+
end$$;
274+
ERROR: value for domain var_record_colnn violates check constraint "var_record_colnn_check"
275+
CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
276+
do $$
277+
declare x var_record_colnn := row(1,null); -- fail
278+
begin
279+
raise notice 'x = %', x;
280+
end$$;
281+
ERROR: value for domain var_record_colnn violates check constraint "var_record_colnn_check"
282+
CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
283+
do $$
284+
declare x var_record_colnn := row(1,2);
285+
begin
286+
raise notice 'x = %', x;
287+
x := null; -- fail
288+
end$$;
289+
NOTICE: x = (1,2)
290+
ERROR: value for domain var_record_colnn violates check constraint "var_record_colnn_check"
291+
CONTEXT: PL/pgSQL function inline_code_block line 5 at assignment
292+
do $$
293+
declare x var_record_colnn := row(1,2);
294+
begin
295+
raise notice 'x = %', x;
296+
x := row(null,null); -- fail
297+
end$$;
298+
NOTICE: x = (1,2)
299+
ERROR: value for domain var_record_colnn violates check constraint "var_record_colnn_check"
300+
CONTEXT: PL/pgSQL function inline_code_block line 5 at assignment

src/pl/plpgsql/src/pl_comp.c

+12-5
Original file line numberDiff line numberDiff line change
@@ -594,11 +594,11 @@ do_compile(FunctionCallInfo fcinfo,
594594
errhint("The arguments of the trigger can be accessed through TG_NARGS and TG_ARGV instead.")));
595595

596596
/* Add the record for referencing NEW ROW */
597-
rec = plpgsql_build_record("new", 0, RECORDOID, true);
597+
rec = plpgsql_build_record("new", 0, NULL, RECORDOID, true);
598598
function->new_varno = rec->dno;
599599

600600
/* Add the record for referencing OLD ROW */
601-
rec = plpgsql_build_record("old", 0, RECORDOID, true);
601+
rec = plpgsql_build_record("old", 0, NULL, RECORDOID, true);
602602
function->old_varno = rec->dno;
603603

604604
/* Add the variable tg_name */
@@ -1811,7 +1811,7 @@ plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
18111811
var->refname = pstrdup(refname);
18121812
var->lineno = lineno;
18131813
var->datatype = dtype;
1814-
/* other fields might be filled by caller */
1814+
/* other fields are left as 0, might be changed by caller */
18151815

18161816
/* preset to NULL */
18171817
var->value = 0;
@@ -1831,7 +1831,8 @@ plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
18311831
/* Composite type -- build a record variable */
18321832
PLpgSQL_rec *rec;
18331833

1834-
rec = plpgsql_build_record(refname, lineno, dtype->typoid,
1834+
rec = plpgsql_build_record(refname, lineno,
1835+
dtype, dtype->typoid,
18351836
add2namespace);
18361837
result = (PLpgSQL_variable *) rec;
18371838
break;
@@ -1856,7 +1857,8 @@ plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
18561857
* Build empty named record variable, and optionally add it to namespace
18571858
*/
18581859
PLpgSQL_rec *
1859-
plpgsql_build_record(const char *refname, int lineno, Oid rectypeid,
1860+
plpgsql_build_record(const char *refname, int lineno,
1861+
PLpgSQL_type *dtype, Oid rectypeid,
18601862
bool add2namespace)
18611863
{
18621864
PLpgSQL_rec *rec;
@@ -1865,6 +1867,8 @@ plpgsql_build_record(const char *refname, int lineno, Oid rectypeid,
18651867
rec->dtype = PLPGSQL_DTYPE_REC;
18661868
rec->refname = pstrdup(refname);
18671869
rec->lineno = lineno;
1870+
/* other fields are left as 0, might be changed by caller */
1871+
rec->datatype = dtype;
18681872
rec->rectypeid = rectypeid;
18691873
rec->firstfield = -1;
18701874
rec->erh = NULL;
@@ -1899,6 +1903,9 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars)
18991903
int32 typmod;
19001904
Oid typcoll;
19011905

1906+
/* Member vars of a row should never be const */
1907+
Assert(!var->isconst);
1908+
19021909
switch (var->dtype)
19031910
{
19041911
case PLPGSQL_DTYPE_VAR:

0 commit comments

Comments
 (0)