-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
procedure_plpgsql
467 lines (426 loc) · 8.97 KB
/
procedure_plpgsql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
# LogicTest: !local-mixed-23.1
statement ok
CREATE TABLE t (
k INT PRIMARY KEY,
i INT,
s STRING
)
statement ok
CREATE PROCEDURE my_upsert(arg_k INT, new_i INT, new_s STRING) AS $$
DECLARE
c INT;
BEGIN
SELECT count(*) INTO c FROM t WHERE k = arg_k;
IF c > 0 THEN
UPDATE t SET i = new_i, s = new_s WHERE k = arg_k;
ELSE
INSERT INTO t VALUES (arg_k, new_i, new_s);
END IF;
END
$$ LANGUAGE PLpgSQL
statement ok
CALL my_upsert(1, 10, 'foo')
statement ok
CALL my_upsert(2, 20, 'bar')
query IIT rowsort
SELECT * FROM t
----
1 10 foo
2 20 bar
statement ok
CALL my_upsert(1, 100, 'baz')
query IIT rowsort
SELECT * FROM t
----
1 100 baz
2 20 bar
subtest composite_into
# Regression test for #114683 - if the target of a SELECT INTO statement is a
# single composite-typed variable, the columns will be wrapped with a tuple,
# which will be assigned to the variable.
statement ok
CREATE TABLE t114683 (x INT, y INT);
INSERT INTO t114683 (SELECT t, t%6 FROM generate_series(1, 13) g(t));
statement ok
CREATE OR REPLACE PROCEDURE get_rows(n INT) LANGUAGE PLpgSQL AS $$
DECLARE
v t114683;
count INT;
i INT := 0;
BEGIN
count := (SELECT count(*) FROM t114683);
WHILE i < count LOOP
IF n = 0 THEN
SELECT x, y INTO v FROM t114683 ORDER BY y, x OFFSET i;
ELSIF n = 1 THEN
SELECT ROW(x, y) INTO v FROM t114683 ORDER BY y, x OFFSET i;
ELSIF n = 2 THEN
SELECT ROW(x, y) INTO v FROM t114683 ORDER BY y, x OFFSET i;
RAISE NOTICE 'v: %', v::TEXT::t114683;
ELSIF n = 3 THEN
SELECT x, y, x+y INTO v FROM t114683 ORDER BY y, x OFFSET i;
ELSE
SELECT x INTO v FROM t114683 ORDER BY y, x OFFSET i;
END IF;
RAISE NOTICE 'v: %', v;
i := i + 1;
END LOOP;
END
$$;
query T noticetrace
CALL get_rows(0);
----
NOTICE: v: (6,0)
NOTICE: v: (12,0)
NOTICE: v: (1,1)
NOTICE: v: (7,1)
NOTICE: v: (13,1)
NOTICE: v: (2,2)
NOTICE: v: (8,2)
NOTICE: v: (3,3)
NOTICE: v: (9,3)
NOTICE: v: (4,4)
NOTICE: v: (10,4)
NOTICE: v: (5,5)
NOTICE: v: (11,5)
statement error pgcode 22P02 could not parse
CALL get_rows(1);
# Casting to text and then to "t114683" shows the error that should occur in
# the previous test case.
statement error pgcode 22P02 could not parse
CALL get_rows(2);
# The number of columns exceeds the length of the INTO variable.
query T noticetrace
CALL get_rows(3);
----
NOTICE: v: (6,0)
NOTICE: v: (12,0)
NOTICE: v: (1,1)
NOTICE: v: (7,1)
NOTICE: v: (13,1)
NOTICE: v: (2,2)
NOTICE: v: (8,2)
NOTICE: v: (3,3)
NOTICE: v: (9,3)
NOTICE: v: (4,4)
NOTICE: v: (10,4)
NOTICE: v: (5,5)
NOTICE: v: (11,5)
# The number of columns is less than the length of the INTO variable.
query T noticetrace
CALL get_rows(4);
----
NOTICE: v: (6,)
NOTICE: v: (12,)
NOTICE: v: (1,)
NOTICE: v: (7,)
NOTICE: v: (13,)
NOTICE: v: (2,)
NOTICE: v: (8,)
NOTICE: v: (3,)
NOTICE: v: (9,)
NOTICE: v: (4,)
NOTICE: v: (10,)
NOTICE: v: (5,)
NOTICE: v: (11,)
# The target of a FETCH statement has the same behavior as above.
statement ok
CREATE OR REPLACE PROCEDURE get_rows(n INT) LANGUAGE PLpgSQL AS $$
DECLARE
curs REFCURSOR;
v t114683;
BEGIN
IF n = 0 THEN
OPEN curs FOR SELECT 1, 2;
ELSIF n = 1 THEN
OPEN curs FOR SELECT ROW(1, 2);
ELSIF n = 2 THEN
OPEN curs FOR SELECT 1, 2, 3;
ELSE
OPEN curs FOR SELECT 1;
END IF;
FETCH curs INTO v;
RAISE NOTICE '%', v;
END
$$;
query T noticetrace
CALL get_rows(0);
----
NOTICE: (1,2)
statement error pgcode 42846 invalid cast
CALL get_rows(1);
query T noticetrace
CALL get_rows(2);
----
NOTICE: (1,2)
query T noticetrace
CALL get_rows(3);
----
NOTICE: (1,)
subtest exit_continue_cond
statement ok
CREATE PROCEDURE p(a INT, b INT, c INT) AS $$
DECLARE
i INT := 0;
BEGIN
LOOP
RAISE NOTICE 'iteration %', i;
i := i + 1;
CONTINUE WHEN i = a;
RAISE NOTICE 'new value of i: %', i;
EXIT WHEN i >= b;
CONTINUE WHEN i <> c;
RAISE NOTICE 'returning';
RETURN;
END LOOP;
RAISE NOTICE 'exited loop';
END
$$ LANGUAGE PLpgSQL;
query T noticetrace
CALL p(2, 1, 4);
----
NOTICE: iteration 0
NOTICE: new value of i: 1
NOTICE: exited loop
query T noticetrace
CALL p(2, 3, 4);
----
NOTICE: iteration 0
NOTICE: new value of i: 1
NOTICE: iteration 1
NOTICE: iteration 2
NOTICE: new value of i: 3
NOTICE: exited loop
query T noticetrace
CALL p(2, 4, 4);
----
NOTICE: iteration 0
NOTICE: new value of i: 1
NOTICE: iteration 1
NOTICE: iteration 2
NOTICE: new value of i: 3
NOTICE: iteration 3
NOTICE: new value of i: 4
NOTICE: exited loop
query T noticetrace
CALL p(2, 5, 4);
----
NOTICE: iteration 0
NOTICE: new value of i: 1
NOTICE: iteration 1
NOTICE: iteration 2
NOTICE: new value of i: 3
NOTICE: iteration 3
NOTICE: new value of i: 4
NOTICE: returning
subtest exit_continue_label
# EXIT can apply to a block with a label.
statement ok
DROP PROCEDURE p;
CREATE PROCEDURE p() AS $$
BEGIN
<<foo>>
BEGIN
RAISE NOTICE 'before EXIT';
EXIT foo;
RAISE NOTICE 'after EXIT';
END;
END
$$ LANGUAGE PLpgSQL;
query T noticetrace
CALL p();
----
NOTICE: before EXIT
statement ok
DROP PROCEDURE p;
# EXIT without a label cannot apply to a block.
statement error pgcode 42601 pq: EXIT cannot be used outside a loop, unless it has a label
CREATE PROCEDURE p() AS $$
BEGIN
<<foo>>
BEGIN
RAISE NOTICE 'before EXIT';
EXIT;
RAISE NOTICE 'after EXIT';
END;
END
$$ LANGUAGE PLpgSQL;
# CONTINUE cannot apply to a block, with or without a label.
statement error pgcode 42601 pq: CONTINUE cannot be used outside a loop
CREATE PROCEDURE p() AS $$
BEGIN
<<foo>>
BEGIN
RAISE NOTICE 'before EXIT';
CONTINUE foo;
RAISE NOTICE 'after EXIT';
END;
END
$$ LANGUAGE PLpgSQL;
# CONTINUE the inner loop.
statement ok
CREATE PROCEDURE p(x INT) AS $$
<<b1>>
DECLARE
i INT := 0;
BEGIN
RAISE NOTICE '>> b1 %', i;
<<l1>>
WHILE i < 2 LOOP
i := i + 1;
RAISE NOTICE '>> l1 %', i;
<<b2>>
DECLARE
j int := 0;
BEGIN
RAISE NOTICE '>> b2 % %', i, j;
<<l2>>
WHILE j < i LOOP
j := j + 1;
RAISE NOTICE '>> l2 % %', i, j;
IF x = 0 THEN
IF j = 1 THEN RAISE NOTICE 'CONTINUE l2'; END IF;
CONTINUE l2 WHEN j = 1;
ELSIF x = 1 THEN
IF j = 1 THEN RAISE NOTICE 'EXIT l2'; END IF;
EXIT l2 WHEN j = 1;
ELSIF x = 2 THEN
IF j = 1 THEN RAISE NOTICE 'CONTINUE l1'; END IF;
CONTINUE l1 WHEN j = 1;
ELSIF x = 3 THEN
IF j = 1 THEN RAISE NOTICE 'EXIT l1'; END IF;
EXIT l1 WHEN j = 1;
ELSIF x = 4 THEN
IF j = 1 THEN RAISE NOTICE 'EXIT b2'; END IF;
EXIT b2 WHEN j = 1;
-- TODO(drewk): uncomment these branches when it's possible to
-- reference the root block.
-- ELSIF x = 5 THEN
-- EXIT b1 WHEN j = 1;
-- ELSE
-- EXIT p WHEN j = 1;
END IF;
RAISE NOTICE '<< l2 % %', i, j;
END LOOP l2;
RAISE NOTICE '<< b2 % %', i, j;
END;
RAISE NOTICE '<< l1 %', i;
END LOOP l1;
RAISE NOTICE '<< b1 %', i;
END;
$$ LANGUAGE PLpgSQL;
# CONTINUE inner loop.
query T noticetrace
CALL p(0);
----
NOTICE: >> b1 0
NOTICE: >> l1 1
NOTICE: >> b2 1 0
NOTICE: >> l2 1 1
NOTICE: CONTINUE l2
NOTICE: << b2 1 1
NOTICE: << l1 1
NOTICE: >> l1 2
NOTICE: >> b2 2 0
NOTICE: >> l2 2 1
NOTICE: CONTINUE l2
NOTICE: >> l2 2 2
NOTICE: << l2 2 2
NOTICE: << b2 2 2
NOTICE: << l1 2
NOTICE: << b1 2
# EXIT inner loop.
query T noticetrace
CALL p(1);
----
NOTICE: >> b1 0
NOTICE: >> l1 1
NOTICE: >> b2 1 0
NOTICE: >> l2 1 1
NOTICE: EXIT l2
NOTICE: << b2 1 1
NOTICE: << l1 1
NOTICE: >> l1 2
NOTICE: >> b2 2 0
NOTICE: >> l2 2 1
NOTICE: EXIT l2
NOTICE: << b2 2 1
NOTICE: << l1 2
NOTICE: << b1 2
# CONTINUE outer loop.
query T noticetrace
CALL p(2);
----
NOTICE: >> b1 0
NOTICE: >> l1 1
NOTICE: >> b2 1 0
NOTICE: >> l2 1 1
NOTICE: CONTINUE l1
NOTICE: >> l1 2
NOTICE: >> b2 2 0
NOTICE: >> l2 2 1
NOTICE: CONTINUE l1
NOTICE: << b1 2
# EXIT outer loop.
query T noticetrace
CALL p(3);
----
NOTICE: >> b1 0
NOTICE: >> l1 1
NOTICE: >> b2 1 0
NOTICE: >> l2 1 1
NOTICE: EXIT l1
NOTICE: << b1 1
# EXIT inner block.
query T noticetrace
CALL p(4);
----
NOTICE: >> b1 0
NOTICE: >> l1 1
NOTICE: >> b2 1 0
NOTICE: >> l2 1 1
NOTICE: EXIT b2
NOTICE: << l1 1
NOTICE: >> l1 2
NOTICE: >> b2 2 0
NOTICE: >> l2 2 1
NOTICE: EXIT b2
NOTICE: << l1 2
NOTICE: << b1 2
statement ok
DROP PROCEDURE p;
CREATE PROCEDURE p() AS $$
DECLARE
i INT := 0;
BEGIN
<<l1>>
LOOP
i := i + 1;
EXIT l1 WHEN i >= 5;
DECLARE
j int := 0;
BEGIN
<<l2>>
LOOP
j := j + 1;
RAISE NOTICE '% %', i, j;
CONTINUE l1 WHEN j >= i;
END LOOP l2;
END;
END LOOP l1;
END;
$$ LANGUAGE PLpgSQL;
query T noticetrace
CALL p();
----
NOTICE: 1 1
NOTICE: 2 1
NOTICE: 2 2
NOTICE: 3 1
NOTICE: 3 2
NOTICE: 3 3
NOTICE: 4 1
NOTICE: 4 2
NOTICE: 4 3
NOTICE: 4 4
subtest end