1--
2-- Tests for procedures / CALL syntax
3--
4CREATE PROCEDURE test_proc1()
5LANGUAGE plpgsql
6AS $$
7BEGIN
8    NULL;
9END;
10$$;
11CALL test_proc1();
12-- error: can't return non-NULL
13CREATE PROCEDURE test_proc2()
14LANGUAGE plpgsql
15AS $$
16BEGIN
17    RETURN 5;
18END;
19$$;
20ERROR:  RETURN cannot have a parameter in a procedure
21LINE 5:     RETURN 5;
22                   ^
23CREATE TABLE test1 (a int);
24CREATE PROCEDURE test_proc3(x int)
25LANGUAGE plpgsql
26AS $$
27BEGIN
28    INSERT INTO test1 VALUES (x);
29END;
30$$;
31CALL test_proc3(55);
32SELECT * FROM test1;
33 a
34----
35 55
36(1 row)
37
38-- nested CALL
39TRUNCATE TABLE test1;
40CREATE PROCEDURE test_proc4(y int)
41LANGUAGE plpgsql
42AS $$
43BEGIN
44    CALL test_proc3(y);
45    CALL test_proc3($1);
46END;
47$$;
48CALL test_proc4(66);
49SELECT * FROM test1;
50 a
51----
52 66
53 66
54(2 rows)
55
56CALL test_proc4(66);
57SELECT * FROM test1;
58 a
59----
60 66
61 66
62 66
63 66
64(4 rows)
65
66-- output arguments
67CREATE PROCEDURE test_proc5(INOUT a text)
68LANGUAGE plpgsql
69AS $$
70BEGIN
71    a := a || '+' || a;
72END;
73$$;
74CALL test_proc5('abc');
75    a
76---------
77 abc+abc
78(1 row)
79
80CREATE PROCEDURE test_proc6(a int, INOUT b int, INOUT c int)
81LANGUAGE plpgsql
82AS $$
83BEGIN
84    b := b * a;
85    c := c * a;
86END;
87$$;
88CALL test_proc6(2, 3, 4);
89 b | c
90---+---
91 6 | 8
92(1 row)
93
94DO
95LANGUAGE plpgsql
96$$
97DECLARE
98    x int := 3;
99    y int := 4;
100BEGIN
101    CALL test_proc6(2, x, y);
102    RAISE INFO 'x = %, y = %', x, y;
103END;
104$$;
105INFO:  x = 6, y = 8
106DO
107LANGUAGE plpgsql
108$$
109DECLARE
110    x int := 3;
111    y int := 4;
112BEGIN
113    CALL test_proc6(2, x + 1, y);  -- error
114    RAISE INFO 'x = %, y = %', x, y;
115END;
116$$;
117ERROR:  procedure parameter "b" is an output parameter but corresponding argument is not writable
118CONTEXT:  PL/pgSQL function inline_code_block line 6 at CALL
119DO
120LANGUAGE plpgsql
121$$
122DECLARE
123    x int := 3;
124    y int := 4;
125BEGIN
126    FOR i IN 1..5 LOOP
127        CALL test_proc6(i, x, y);
128        RAISE INFO 'x = %, y = %', x, y;
129    END LOOP;
130END;
131$$;
132INFO:  x = 3, y = 4
133INFO:  x = 6, y = 8
134INFO:  x = 18, y = 24
135INFO:  x = 72, y = 96
136INFO:  x = 360, y = 480
137-- recursive with output arguments
138CREATE PROCEDURE test_proc7(x int, INOUT a int, INOUT b numeric)
139LANGUAGE plpgsql
140AS $$
141BEGIN
142IF x > 1 THEN
143    a := x / 10;
144    b := x / 2;
145    CALL test_proc7(b::int, a, b);
146END IF;
147END;
148$$;
149CALL test_proc7(100, -1, -1);
150 a | b
151---+---
152 0 | 1
153(1 row)
154
155-- inner COMMIT with output arguments
156CREATE PROCEDURE test_proc7c(x int, INOUT a int, INOUT b numeric)
157LANGUAGE plpgsql
158AS $$
159BEGIN
160  a := x / 10;
161  b := x / 2;
162  COMMIT;
163END;
164$$;
165CREATE PROCEDURE test_proc7cc(_x int)
166LANGUAGE plpgsql
167AS $$
168DECLARE _a int; _b numeric;
169BEGIN
170  CALL test_proc7c(_x, _a, _b);
171  RAISE NOTICE '_x: %,_a: %, _b: %', _x, _a, _b;
172END
173$$;
174CALL test_proc7cc(10);
175NOTICE:  _x: 10,_a: 1, _b: 5
176-- named parameters and defaults
177CREATE PROCEDURE test_proc8a(INOUT a int, INOUT b int)
178LANGUAGE plpgsql
179AS $$
180BEGIN
181  RAISE NOTICE 'a: %, b: %', a, b;
182  a := a * 10;
183  b := b + 10;
184END;
185$$;
186CALL test_proc8a(10, 20);
187NOTICE:  a: 10, b: 20
188  a  | b
189-----+----
190 100 | 30
191(1 row)
192
193CALL test_proc8a(b => 20, a => 10);
194NOTICE:  a: 10, b: 20
195  a  | b
196-----+----
197 100 | 30
198(1 row)
199
200DO $$
201DECLARE _a int; _b int;
202BEGIN
203  _a := 10; _b := 30;
204  CALL test_proc8a(_a, _b);
205  RAISE NOTICE '_a: %, _b: %', _a, _b;
206  CALL test_proc8a(b => _b, a => _a);
207  RAISE NOTICE '_a: %, _b: %', _a, _b;
208END
209$$;
210NOTICE:  a: 10, b: 30
211NOTICE:  _a: 100, _b: 40
212NOTICE:  a: 100, b: 40
213NOTICE:  _a: 1000, _b: 50
214CREATE PROCEDURE test_proc8b(INOUT a int, INOUT b int, INOUT c int)
215LANGUAGE plpgsql
216AS $$
217BEGIN
218  RAISE NOTICE 'a: %, b: %, c: %', a, b, c;
219  a := a * 10;
220  b := b + 10;
221  c := c * -10;
222END;
223$$;
224DO $$
225DECLARE _a int; _b int; _c int;
226BEGIN
227  _a := 10; _b := 30; _c := 50;
228  CALL test_proc8b(_a, _b, _c);
229  RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
230  CALL test_proc8b(_a, c => _c, b => _b);
231  RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
232END
233$$;
234NOTICE:  a: 10, b: 30, c: 50
235NOTICE:  _a: 100, _b: 40, _c: -500
236NOTICE:  a: 100, b: 40, c: -500
237NOTICE:  _a: 1000, _b: 50, _c: 5000
238CREATE PROCEDURE test_proc8c(INOUT a int, INOUT b int, INOUT c int DEFAULT 11)
239LANGUAGE plpgsql
240AS $$
241BEGIN
242  RAISE NOTICE 'a: %, b: %, c: %', a, b, c;
243  a := a * 10;
244  b := b + 10;
245  c := c * -10;
246END;
247$$;
248DO $$
249DECLARE _a int; _b int; _c int;
250BEGIN
251  _a := 10; _b := 30; _c := 50;
252  CALL test_proc8c(_a, _b, _c);
253  RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
254  _a := 10; _b := 30; _c := 50;
255  CALL test_proc8c(_a, c => _c, b => _b);
256  RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
257  _a := 10; _b := 30; _c := 50;
258  CALL test_proc8c(c => _c, b => _b, a => _a);
259  RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
260END
261$$;
262NOTICE:  a: 10, b: 30, c: 50
263NOTICE:  _a: 100, _b: 40, _c: -500
264NOTICE:  a: 10, b: 30, c: 50
265NOTICE:  _a: 100, _b: 40, _c: -500
266NOTICE:  a: 10, b: 30, c: 50
267NOTICE:  _a: 100, _b: 40, _c: -500
268DO $$
269DECLARE _a int; _b int; _c int;
270BEGIN
271  _a := 10; _b := 30; _c := 50;
272  CALL test_proc8c(_a, _b);  -- fail, no output argument for c
273  RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
274END
275$$;
276ERROR:  procedure parameter "c" is an output parameter but corresponding argument is not writable
277CONTEXT:  PL/pgSQL function inline_code_block line 5 at CALL
278DO $$
279DECLARE _a int; _b int; _c int;
280BEGIN
281  _a := 10; _b := 30; _c := 50;
282  CALL test_proc8c(_a, b => _b);  -- fail, no output argument for c
283  RAISE NOTICE '_a: %, _b: %, _c: %', _a, _b, _c;
284END
285$$;
286ERROR:  procedure parameter "c" is an output parameter but corresponding argument is not writable
287CONTEXT:  PL/pgSQL function inline_code_block line 5 at CALL
288-- transition variable assignment
289TRUNCATE test1;
290CREATE FUNCTION triggerfunc1() RETURNS trigger
291LANGUAGE plpgsql
292AS $$
293DECLARE
294    z int := 0;
295BEGIN
296    CALL test_proc6(2, NEW.a, NEW.a);
297    RETURN NEW;
298END;
299$$;
300CREATE TRIGGER t1 BEFORE INSERT ON test1 EXECUTE PROCEDURE triggerfunc1();
301INSERT INTO test1 VALUES (1), (2), (3);
302UPDATE test1 SET a = 22 WHERE a = 2;
303SELECT * FROM test1 ORDER BY a;
304 a
305----
306  1
307  3
308 22
309(3 rows)
310
311DROP PROCEDURE test_proc1;
312DROP PROCEDURE test_proc3;
313DROP PROCEDURE test_proc4;
314DROP TABLE test1;
315-- more checks for named-parameter handling
316CREATE PROCEDURE p1(v_cnt int, v_Text inout text = NULL)
317AS $$
318BEGIN
319  v_Text := 'v_cnt = ' || v_cnt;
320END
321$$ LANGUAGE plpgsql;
322DO $$
323DECLARE
324  v_Text text;
325  v_cnt  integer := 42;
326BEGIN
327  CALL p1(v_cnt := v_cnt);  -- error, must supply something for v_Text
328  RAISE NOTICE '%', v_Text;
329END;
330$$;
331ERROR:  procedure parameter "v_text" is an output parameter but corresponding argument is not writable
332CONTEXT:  PL/pgSQL function inline_code_block line 6 at CALL
333DO $$
334DECLARE
335  v_Text text;
336  v_cnt  integer := 42;
337BEGIN
338  CALL p1(v_cnt := v_cnt, v_Text := v_Text);
339  RAISE NOTICE '%', v_Text;
340END;
341$$;
342NOTICE:  v_cnt = 42
343DO $$
344DECLARE
345  v_Text text;
346BEGIN
347  CALL p1(10, v_Text := v_Text);
348  RAISE NOTICE '%', v_Text;
349END;
350$$;
351NOTICE:  v_cnt = 10
352DO $$
353DECLARE
354  v_Text text;
355  v_cnt  integer;
356BEGIN
357  CALL p1(v_Text := v_Text, v_cnt := v_cnt);
358  RAISE NOTICE '%', v_Text;
359END;
360$$;
361NOTICE:  <NULL>
362