~ubuntu-branches/debian/sid/postgresql-9.3/sid

« back to all changes in this revision

Viewing changes to src/test/regress/expected/plpgsql.out

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2013-05-08 05:39:52 UTC
  • Revision ID: package-import@ubuntu.com-20130508053952-1j7uilp7mjtrvq8q
Tags: upstream-9.3~beta1
ImportĀ upstreamĀ versionĀ 9.3~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
--
 
2
-- PLPGSQL
 
3
--
 
4
-- Scenario:
 
5
--
 
6
--     A building with a modern TP cable installation where any
 
7
--     of the wall connectors can be used to plug in phones,
 
8
--     ethernet interfaces or local office hubs. The backside
 
9
--     of the wall connectors is wired to one of several patch-
 
10
--     fields in the building.
 
11
--
 
12
--     In the patchfields, there are hubs and all the slots
 
13
--     representing the wall connectors. In addition there are
 
14
--     slots that can represent a phone line from the central
 
15
--     phone system.
 
16
--
 
17
--     Triggers ensure consistency of the patching information.
 
18
--
 
19
--     Functions are used to build up powerful views that let
 
20
--     you look behind the wall when looking at a patchfield
 
21
--     or into a room.
 
22
--
 
23
create table Room (
 
24
    roomno      char(8),
 
25
    comment     text
 
26
);
 
27
create unique index Room_rno on Room using btree (roomno bpchar_ops);
 
28
create table WSlot (
 
29
    slotname    char(20),
 
30
    roomno      char(8),
 
31
    slotlink    char(20),
 
32
    backlink    char(20)
 
33
);
 
34
create unique index WSlot_name on WSlot using btree (slotname bpchar_ops);
 
35
create table PField (
 
36
    name        text,
 
37
    comment     text
 
38
);
 
39
create unique index PField_name on PField using btree (name text_ops);
 
40
create table PSlot (
 
41
    slotname    char(20),
 
42
    pfname      text,
 
43
    slotlink    char(20),
 
44
    backlink    char(20)
 
45
);
 
46
create unique index PSlot_name on PSlot using btree (slotname bpchar_ops);
 
47
create table PLine (
 
48
    slotname    char(20),
 
49
    phonenumber char(20),
 
50
    comment     text,
 
51
    backlink    char(20)
 
52
);
 
53
create unique index PLine_name on PLine using btree (slotname bpchar_ops);
 
54
create table Hub (
 
55
    name        char(14),
 
56
    comment     text,
 
57
    nslots      integer
 
58
);
 
59
create unique index Hub_name on Hub using btree (name bpchar_ops);
 
60
create table HSlot (
 
61
    slotname    char(20),
 
62
    hubname     char(14),
 
63
    slotno      integer,
 
64
    slotlink    char(20)
 
65
);
 
66
create unique index HSlot_name on HSlot using btree (slotname bpchar_ops);
 
67
create index HSlot_hubname on HSlot using btree (hubname bpchar_ops);
 
68
create table System (
 
69
    name        text,
 
70
    comment     text
 
71
);
 
72
create unique index System_name on System using btree (name text_ops);
 
73
create table IFace (
 
74
    slotname    char(20),
 
75
    sysname     text,
 
76
    ifname      text,
 
77
    slotlink    char(20)
 
78
);
 
79
create unique index IFace_name on IFace using btree (slotname bpchar_ops);
 
80
create table PHone (
 
81
    slotname    char(20),
 
82
    comment     text,
 
83
    slotlink    char(20)
 
84
);
 
85
create unique index PHone_name on PHone using btree (slotname bpchar_ops);
 
86
-- ************************************************************
 
87
-- *
 
88
-- * Trigger procedures and functions for the patchfield
 
89
-- * test of PL/pgSQL
 
90
-- *
 
91
-- ************************************************************
 
92
-- ************************************************************
 
93
-- * AFTER UPDATE on Room
 
94
-- *    - If room no changes let wall slots follow
 
95
-- ************************************************************
 
96
create function tg_room_au() returns trigger as '
 
97
begin
 
98
    if new.roomno != old.roomno then
 
99
        update WSlot set roomno = new.roomno where roomno = old.roomno;
 
100
    end if;
 
101
    return new;
 
102
end;
 
103
' language plpgsql;
 
104
create trigger tg_room_au after update
 
105
    on Room for each row execute procedure tg_room_au();
 
106
-- ************************************************************
 
107
-- * AFTER DELETE on Room
 
108
-- *    - delete wall slots in this room
 
109
-- ************************************************************
 
110
create function tg_room_ad() returns trigger as '
 
111
begin
 
112
    delete from WSlot where roomno = old.roomno;
 
113
    return old;
 
114
end;
 
115
' language plpgsql;
 
116
create trigger tg_room_ad after delete
 
117
    on Room for each row execute procedure tg_room_ad();
 
118
-- ************************************************************
 
119
-- * BEFORE INSERT or UPDATE on WSlot
 
120
-- *    - Check that room exists
 
121
-- ************************************************************
 
122
create function tg_wslot_biu() returns trigger as $$
 
123
begin
 
124
    if count(*) = 0 from Room where roomno = new.roomno then
 
125
        raise exception 'Room % does not exist', new.roomno;
 
126
    end if;
 
127
    return new;
 
128
end;
 
129
$$ language plpgsql;
 
130
create trigger tg_wslot_biu before insert or update
 
131
    on WSlot for each row execute procedure tg_wslot_biu();
 
132
-- ************************************************************
 
133
-- * AFTER UPDATE on PField
 
134
-- *    - Let PSlots of this field follow
 
135
-- ************************************************************
 
136
create function tg_pfield_au() returns trigger as '
 
137
begin
 
138
    if new.name != old.name then
 
139
        update PSlot set pfname = new.name where pfname = old.name;
 
140
    end if;
 
141
    return new;
 
142
end;
 
143
' language plpgsql;
 
144
create trigger tg_pfield_au after update
 
145
    on PField for each row execute procedure tg_pfield_au();
 
146
-- ************************************************************
 
147
-- * AFTER DELETE on PField
 
148
-- *    - Remove all slots of this patchfield
 
149
-- ************************************************************
 
150
create function tg_pfield_ad() returns trigger as '
 
151
begin
 
152
    delete from PSlot where pfname = old.name;
 
153
    return old;
 
154
end;
 
155
' language plpgsql;
 
156
create trigger tg_pfield_ad after delete
 
157
    on PField for each row execute procedure tg_pfield_ad();
 
158
-- ************************************************************
 
159
-- * BEFORE INSERT or UPDATE on PSlot
 
160
-- *    - Ensure that our patchfield does exist
 
161
-- ************************************************************
 
162
create function tg_pslot_biu() returns trigger as $proc$
 
163
declare
 
164
    pfrec       record;
 
165
    ps          alias for new;
 
166
begin
 
167
    select into pfrec * from PField where name = ps.pfname;
 
168
    if not found then
 
169
        raise exception $$Patchfield "%" does not exist$$, ps.pfname;
 
170
    end if;
 
171
    return ps;
 
172
end;
 
173
$proc$ language plpgsql;
 
174
create trigger tg_pslot_biu before insert or update
 
175
    on PSlot for each row execute procedure tg_pslot_biu();
 
176
-- ************************************************************
 
177
-- * AFTER UPDATE on System
 
178
-- *    - If system name changes let interfaces follow
 
179
-- ************************************************************
 
180
create function tg_system_au() returns trigger as '
 
181
begin
 
182
    if new.name != old.name then
 
183
        update IFace set sysname = new.name where sysname = old.name;
 
184
    end if;
 
185
    return new;
 
186
end;
 
187
' language plpgsql;
 
188
create trigger tg_system_au after update
 
189
    on System for each row execute procedure tg_system_au();
 
190
-- ************************************************************
 
191
-- * BEFORE INSERT or UPDATE on IFace
 
192
-- *    - set the slotname to IF.sysname.ifname
 
193
-- ************************************************************
 
194
create function tg_iface_biu() returns trigger as $$
 
195
declare
 
196
    sname       text;
 
197
    sysrec      record;
 
198
begin
 
199
    select into sysrec * from system where name = new.sysname;
 
200
    if not found then
 
201
        raise exception $q$system "%" does not exist$q$, new.sysname;
 
202
    end if;
 
203
    sname := 'IF.' || new.sysname;
 
204
    sname := sname || '.';
 
205
    sname := sname || new.ifname;
 
206
    if length(sname) > 20 then
 
207
        raise exception 'IFace slotname "%" too long (20 char max)', sname;
 
208
    end if;
 
209
    new.slotname := sname;
 
210
    return new;
 
211
end;
 
212
$$ language plpgsql;
 
213
create trigger tg_iface_biu before insert or update
 
214
    on IFace for each row execute procedure tg_iface_biu();
 
215
-- ************************************************************
 
216
-- * AFTER INSERT or UPDATE or DELETE on Hub
 
217
-- *    - insert/delete/rename slots as required
 
218
-- ************************************************************
 
219
create function tg_hub_a() returns trigger as '
 
220
declare
 
221
    hname       text;
 
222
    dummy       integer;
 
223
begin
 
224
    if tg_op = ''INSERT'' then
 
225
        dummy := tg_hub_adjustslots(new.name, 0, new.nslots);
 
226
        return new;
 
227
    end if;
 
228
    if tg_op = ''UPDATE'' then
 
229
        if new.name != old.name then
 
230
            update HSlot set hubname = new.name where hubname = old.name;
 
231
        end if;
 
232
        dummy := tg_hub_adjustslots(new.name, old.nslots, new.nslots);
 
233
        return new;
 
234
    end if;
 
235
    if tg_op = ''DELETE'' then
 
236
        dummy := tg_hub_adjustslots(old.name, old.nslots, 0);
 
237
        return old;
 
238
    end if;
 
239
end;
 
240
' language plpgsql;
 
241
create trigger tg_hub_a after insert or update or delete
 
242
    on Hub for each row execute procedure tg_hub_a();
 
243
-- ************************************************************
 
244
-- * Support function to add/remove slots of Hub
 
245
-- ************************************************************
 
246
create function tg_hub_adjustslots(hname bpchar,
 
247
                                   oldnslots integer,
 
248
                                   newnslots integer)
 
249
returns integer as '
 
250
begin
 
251
    if newnslots = oldnslots then
 
252
        return 0;
 
253
    end if;
 
254
    if newnslots < oldnslots then
 
255
        delete from HSlot where hubname = hname and slotno > newnslots;
 
256
        return 0;
 
257
    end if;
 
258
    for i in oldnslots + 1 .. newnslots loop
 
259
        insert into HSlot (slotname, hubname, slotno, slotlink)
 
260
                values (''HS.dummy'', hname, i, '''');
 
261
    end loop;
 
262
    return 0;
 
263
end
 
264
' language plpgsql;
 
265
-- Test comments
 
266
COMMENT ON FUNCTION tg_hub_adjustslots_wrong(bpchar, integer, integer) IS 'function with args';
 
267
ERROR:  function tg_hub_adjustslots_wrong(character, integer, integer) does not exist
 
268
COMMENT ON FUNCTION tg_hub_adjustslots(bpchar, integer, integer) IS 'function with args';
 
269
COMMENT ON FUNCTION tg_hub_adjustslots(bpchar, integer, integer) IS NULL;
 
270
-- ************************************************************
 
271
-- * BEFORE INSERT or UPDATE on HSlot
 
272
-- *    - prevent from manual manipulation
 
273
-- *    - set the slotname to HS.hubname.slotno
 
274
-- ************************************************************
 
275
create function tg_hslot_biu() returns trigger as '
 
276
declare
 
277
    sname       text;
 
278
    xname       HSlot.slotname%TYPE;
 
279
    hubrec      record;
 
280
begin
 
281
    select into hubrec * from Hub where name = new.hubname;
 
282
    if not found then
 
283
        raise exception ''no manual manipulation of HSlot'';
 
284
    end if;
 
285
    if new.slotno < 1 or new.slotno > hubrec.nslots then
 
286
        raise exception ''no manual manipulation of HSlot'';
 
287
    end if;
 
288
    if tg_op = ''UPDATE'' and new.hubname != old.hubname then
 
289
        if count(*) > 0 from Hub where name = old.hubname then
 
290
            raise exception ''no manual manipulation of HSlot'';
 
291
        end if;
 
292
    end if;
 
293
    sname := ''HS.'' || trim(new.hubname);
 
294
    sname := sname || ''.'';
 
295
    sname := sname || new.slotno::text;
 
296
    if length(sname) > 20 then
 
297
        raise exception ''HSlot slotname "%" too long (20 char max)'', sname;
 
298
    end if;
 
299
    new.slotname := sname;
 
300
    return new;
 
301
end;
 
302
' language plpgsql;
 
303
create trigger tg_hslot_biu before insert or update
 
304
    on HSlot for each row execute procedure tg_hslot_biu();
 
305
-- ************************************************************
 
306
-- * BEFORE DELETE on HSlot
 
307
-- *    - prevent from manual manipulation
 
308
-- ************************************************************
 
309
create function tg_hslot_bd() returns trigger as '
 
310
declare
 
311
    hubrec      record;
 
312
begin
 
313
    select into hubrec * from Hub where name = old.hubname;
 
314
    if not found then
 
315
        return old;
 
316
    end if;
 
317
    if old.slotno > hubrec.nslots then
 
318
        return old;
 
319
    end if;
 
320
    raise exception ''no manual manipulation of HSlot'';
 
321
end;
 
322
' language plpgsql;
 
323
create trigger tg_hslot_bd before delete
 
324
    on HSlot for each row execute procedure tg_hslot_bd();
 
325
-- ************************************************************
 
326
-- * BEFORE INSERT on all slots
 
327
-- *    - Check name prefix
 
328
-- ************************************************************
 
329
create function tg_chkslotname() returns trigger as '
 
330
begin
 
331
    if substr(new.slotname, 1, 2) != tg_argv[0] then
 
332
        raise exception ''slotname must begin with %'', tg_argv[0];
 
333
    end if;
 
334
    return new;
 
335
end;
 
336
' language plpgsql;
 
337
create trigger tg_chkslotname before insert
 
338
    on PSlot for each row execute procedure tg_chkslotname('PS');
 
339
create trigger tg_chkslotname before insert
 
340
    on WSlot for each row execute procedure tg_chkslotname('WS');
 
341
create trigger tg_chkslotname before insert
 
342
    on PLine for each row execute procedure tg_chkslotname('PL');
 
343
create trigger tg_chkslotname before insert
 
344
    on IFace for each row execute procedure tg_chkslotname('IF');
 
345
create trigger tg_chkslotname before insert
 
346
    on PHone for each row execute procedure tg_chkslotname('PH');
 
347
-- ************************************************************
 
348
-- * BEFORE INSERT or UPDATE on all slots with slotlink
 
349
-- *    - Set slotlink to empty string if NULL value given
 
350
-- ************************************************************
 
351
create function tg_chkslotlink() returns trigger as '
 
352
begin
 
353
    if new.slotlink isnull then
 
354
        new.slotlink := '''';
 
355
    end if;
 
356
    return new;
 
357
end;
 
358
' language plpgsql;
 
359
create trigger tg_chkslotlink before insert or update
 
360
    on PSlot for each row execute procedure tg_chkslotlink();
 
361
create trigger tg_chkslotlink before insert or update
 
362
    on WSlot for each row execute procedure tg_chkslotlink();
 
363
create trigger tg_chkslotlink before insert or update
 
364
    on IFace for each row execute procedure tg_chkslotlink();
 
365
create trigger tg_chkslotlink before insert or update
 
366
    on HSlot for each row execute procedure tg_chkslotlink();
 
367
create trigger tg_chkslotlink before insert or update
 
368
    on PHone for each row execute procedure tg_chkslotlink();
 
369
-- ************************************************************
 
370
-- * BEFORE INSERT or UPDATE on all slots with backlink
 
371
-- *    - Set backlink to empty string if NULL value given
 
372
-- ************************************************************
 
373
create function tg_chkbacklink() returns trigger as '
 
374
begin
 
375
    if new.backlink isnull then
 
376
        new.backlink := '''';
 
377
    end if;
 
378
    return new;
 
379
end;
 
380
' language plpgsql;
 
381
create trigger tg_chkbacklink before insert or update
 
382
    on PSlot for each row execute procedure tg_chkbacklink();
 
383
create trigger tg_chkbacklink before insert or update
 
384
    on WSlot for each row execute procedure tg_chkbacklink();
 
385
create trigger tg_chkbacklink before insert or update
 
386
    on PLine for each row execute procedure tg_chkbacklink();
 
387
-- ************************************************************
 
388
-- * BEFORE UPDATE on PSlot
 
389
-- *    - do delete/insert instead of update if name changes
 
390
-- ************************************************************
 
391
create function tg_pslot_bu() returns trigger as '
 
392
begin
 
393
    if new.slotname != old.slotname then
 
394
        delete from PSlot where slotname = old.slotname;
 
395
        insert into PSlot (
 
396
                    slotname,
 
397
                    pfname,
 
398
                    slotlink,
 
399
                    backlink
 
400
                ) values (
 
401
                    new.slotname,
 
402
                    new.pfname,
 
403
                    new.slotlink,
 
404
                    new.backlink
 
405
                );
 
406
        return null;
 
407
    end if;
 
408
    return new;
 
409
end;
 
410
' language plpgsql;
 
411
create trigger tg_pslot_bu before update
 
412
    on PSlot for each row execute procedure tg_pslot_bu();
 
413
-- ************************************************************
 
414
-- * BEFORE UPDATE on WSlot
 
415
-- *    - do delete/insert instead of update if name changes
 
416
-- ************************************************************
 
417
create function tg_wslot_bu() returns trigger as '
 
418
begin
 
419
    if new.slotname != old.slotname then
 
420
        delete from WSlot where slotname = old.slotname;
 
421
        insert into WSlot (
 
422
                    slotname,
 
423
                    roomno,
 
424
                    slotlink,
 
425
                    backlink
 
426
                ) values (
 
427
                    new.slotname,
 
428
                    new.roomno,
 
429
                    new.slotlink,
 
430
                    new.backlink
 
431
                );
 
432
        return null;
 
433
    end if;
 
434
    return new;
 
435
end;
 
436
' language plpgsql;
 
437
create trigger tg_wslot_bu before update
 
438
    on WSlot for each row execute procedure tg_Wslot_bu();
 
439
-- ************************************************************
 
440
-- * BEFORE UPDATE on PLine
 
441
-- *    - do delete/insert instead of update if name changes
 
442
-- ************************************************************
 
443
create function tg_pline_bu() returns trigger as '
 
444
begin
 
445
    if new.slotname != old.slotname then
 
446
        delete from PLine where slotname = old.slotname;
 
447
        insert into PLine (
 
448
                    slotname,
 
449
                    phonenumber,
 
450
                    comment,
 
451
                    backlink
 
452
                ) values (
 
453
                    new.slotname,
 
454
                    new.phonenumber,
 
455
                    new.comment,
 
456
                    new.backlink
 
457
                );
 
458
        return null;
 
459
    end if;
 
460
    return new;
 
461
end;
 
462
' language plpgsql;
 
463
create trigger tg_pline_bu before update
 
464
    on PLine for each row execute procedure tg_pline_bu();
 
465
-- ************************************************************
 
466
-- * BEFORE UPDATE on IFace
 
467
-- *    - do delete/insert instead of update if name changes
 
468
-- ************************************************************
 
469
create function tg_iface_bu() returns trigger as '
 
470
begin
 
471
    if new.slotname != old.slotname then
 
472
        delete from IFace where slotname = old.slotname;
 
473
        insert into IFace (
 
474
                    slotname,
 
475
                    sysname,
 
476
                    ifname,
 
477
                    slotlink
 
478
                ) values (
 
479
                    new.slotname,
 
480
                    new.sysname,
 
481
                    new.ifname,
 
482
                    new.slotlink
 
483
                );
 
484
        return null;
 
485
    end if;
 
486
    return new;
 
487
end;
 
488
' language plpgsql;
 
489
create trigger tg_iface_bu before update
 
490
    on IFace for each row execute procedure tg_iface_bu();
 
491
-- ************************************************************
 
492
-- * BEFORE UPDATE on HSlot
 
493
-- *    - do delete/insert instead of update if name changes
 
494
-- ************************************************************
 
495
create function tg_hslot_bu() returns trigger as '
 
496
begin
 
497
    if new.slotname != old.slotname or new.hubname != old.hubname then
 
498
        delete from HSlot where slotname = old.slotname;
 
499
        insert into HSlot (
 
500
                    slotname,
 
501
                    hubname,
 
502
                    slotno,
 
503
                    slotlink
 
504
                ) values (
 
505
                    new.slotname,
 
506
                    new.hubname,
 
507
                    new.slotno,
 
508
                    new.slotlink
 
509
                );
 
510
        return null;
 
511
    end if;
 
512
    return new;
 
513
end;
 
514
' language plpgsql;
 
515
create trigger tg_hslot_bu before update
 
516
    on HSlot for each row execute procedure tg_hslot_bu();
 
517
-- ************************************************************
 
518
-- * BEFORE UPDATE on PHone
 
519
-- *    - do delete/insert instead of update if name changes
 
520
-- ************************************************************
 
521
create function tg_phone_bu() returns trigger as '
 
522
begin
 
523
    if new.slotname != old.slotname then
 
524
        delete from PHone where slotname = old.slotname;
 
525
        insert into PHone (
 
526
                    slotname,
 
527
                    comment,
 
528
                    slotlink
 
529
                ) values (
 
530
                    new.slotname,
 
531
                    new.comment,
 
532
                    new.slotlink
 
533
                );
 
534
        return null;
 
535
    end if;
 
536
    return new;
 
537
end;
 
538
' language plpgsql;
 
539
create trigger tg_phone_bu before update
 
540
    on PHone for each row execute procedure tg_phone_bu();
 
541
-- ************************************************************
 
542
-- * AFTER INSERT or UPDATE or DELETE on slot with backlink
 
543
-- *    - Ensure that the opponent correctly points back to us
 
544
-- ************************************************************
 
545
create function tg_backlink_a() returns trigger as '
 
546
declare
 
547
    dummy       integer;
 
548
begin
 
549
    if tg_op = ''INSERT'' then
 
550
        if new.backlink != '''' then
 
551
            dummy := tg_backlink_set(new.backlink, new.slotname);
 
552
        end if;
 
553
        return new;
 
554
    end if;
 
555
    if tg_op = ''UPDATE'' then
 
556
        if new.backlink != old.backlink then
 
557
            if old.backlink != '''' then
 
558
                dummy := tg_backlink_unset(old.backlink, old.slotname);
 
559
            end if;
 
560
            if new.backlink != '''' then
 
561
                dummy := tg_backlink_set(new.backlink, new.slotname);
 
562
            end if;
 
563
        else
 
564
            if new.slotname != old.slotname and new.backlink != '''' then
 
565
                dummy := tg_slotlink_set(new.backlink, new.slotname);
 
566
            end if;
 
567
        end if;
 
568
        return new;
 
569
    end if;
 
570
    if tg_op = ''DELETE'' then
 
571
        if old.backlink != '''' then
 
572
            dummy := tg_backlink_unset(old.backlink, old.slotname);
 
573
        end if;
 
574
        return old;
 
575
    end if;
 
576
end;
 
577
' language plpgsql;
 
578
create trigger tg_backlink_a after insert or update or delete
 
579
    on PSlot for each row execute procedure tg_backlink_a('PS');
 
580
create trigger tg_backlink_a after insert or update or delete
 
581
    on WSlot for each row execute procedure tg_backlink_a('WS');
 
582
create trigger tg_backlink_a after insert or update or delete
 
583
    on PLine for each row execute procedure tg_backlink_a('PL');
 
584
-- ************************************************************
 
585
-- * Support function to set the opponents backlink field
 
586
-- * if it does not already point to the requested slot
 
587
-- ************************************************************
 
588
create function tg_backlink_set(myname bpchar, blname bpchar)
 
589
returns integer as '
 
590
declare
 
591
    mytype      char(2);
 
592
    link        char(4);
 
593
    rec         record;
 
594
begin
 
595
    mytype := substr(myname, 1, 2);
 
596
    link := mytype || substr(blname, 1, 2);
 
597
    if link = ''PLPL'' then
 
598
        raise exception
 
599
                ''backlink between two phone lines does not make sense'';
 
600
    end if;
 
601
    if link in (''PLWS'', ''WSPL'') then
 
602
        raise exception
 
603
                ''direct link of phone line to wall slot not permitted'';
 
604
    end if;
 
605
    if mytype = ''PS'' then
 
606
        select into rec * from PSlot where slotname = myname;
 
607
        if not found then
 
608
            raise exception ''% does not exist'', myname;
 
609
        end if;
 
610
        if rec.backlink != blname then
 
611
            update PSlot set backlink = blname where slotname = myname;
 
612
        end if;
 
613
        return 0;
 
614
    end if;
 
615
    if mytype = ''WS'' then
 
616
        select into rec * from WSlot where slotname = myname;
 
617
        if not found then
 
618
            raise exception ''% does not exist'', myname;
 
619
        end if;
 
620
        if rec.backlink != blname then
 
621
            update WSlot set backlink = blname where slotname = myname;
 
622
        end if;
 
623
        return 0;
 
624
    end if;
 
625
    if mytype = ''PL'' then
 
626
        select into rec * from PLine where slotname = myname;
 
627
        if not found then
 
628
            raise exception ''% does not exist'', myname;
 
629
        end if;
 
630
        if rec.backlink != blname then
 
631
            update PLine set backlink = blname where slotname = myname;
 
632
        end if;
 
633
        return 0;
 
634
    end if;
 
635
    raise exception ''illegal backlink beginning with %'', mytype;
 
636
end;
 
637
' language plpgsql;
 
638
-- ************************************************************
 
639
-- * Support function to clear out the backlink field if
 
640
-- * it still points to specific slot
 
641
-- ************************************************************
 
642
create function tg_backlink_unset(bpchar, bpchar)
 
643
returns integer as '
 
644
declare
 
645
    myname      alias for $1;
 
646
    blname      alias for $2;
 
647
    mytype      char(2);
 
648
    rec         record;
 
649
begin
 
650
    mytype := substr(myname, 1, 2);
 
651
    if mytype = ''PS'' then
 
652
        select into rec * from PSlot where slotname = myname;
 
653
        if not found then
 
654
            return 0;
 
655
        end if;
 
656
        if rec.backlink = blname then
 
657
            update PSlot set backlink = '''' where slotname = myname;
 
658
        end if;
 
659
        return 0;
 
660
    end if;
 
661
    if mytype = ''WS'' then
 
662
        select into rec * from WSlot where slotname = myname;
 
663
        if not found then
 
664
            return 0;
 
665
        end if;
 
666
        if rec.backlink = blname then
 
667
            update WSlot set backlink = '''' where slotname = myname;
 
668
        end if;
 
669
        return 0;
 
670
    end if;
 
671
    if mytype = ''PL'' then
 
672
        select into rec * from PLine where slotname = myname;
 
673
        if not found then
 
674
            return 0;
 
675
        end if;
 
676
        if rec.backlink = blname then
 
677
            update PLine set backlink = '''' where slotname = myname;
 
678
        end if;
 
679
        return 0;
 
680
    end if;
 
681
end
 
682
' language plpgsql;
 
683
-- ************************************************************
 
684
-- * AFTER INSERT or UPDATE or DELETE on slot with slotlink
 
685
-- *    - Ensure that the opponent correctly points back to us
 
686
-- ************************************************************
 
687
create function tg_slotlink_a() returns trigger as '
 
688
declare
 
689
    dummy       integer;
 
690
begin
 
691
    if tg_op = ''INSERT'' then
 
692
        if new.slotlink != '''' then
 
693
            dummy := tg_slotlink_set(new.slotlink, new.slotname);
 
694
        end if;
 
695
        return new;
 
696
    end if;
 
697
    if tg_op = ''UPDATE'' then
 
698
        if new.slotlink != old.slotlink then
 
699
            if old.slotlink != '''' then
 
700
                dummy := tg_slotlink_unset(old.slotlink, old.slotname);
 
701
            end if;
 
702
            if new.slotlink != '''' then
 
703
                dummy := tg_slotlink_set(new.slotlink, new.slotname);
 
704
            end if;
 
705
        else
 
706
            if new.slotname != old.slotname and new.slotlink != '''' then
 
707
                dummy := tg_slotlink_set(new.slotlink, new.slotname);
 
708
            end if;
 
709
        end if;
 
710
        return new;
 
711
    end if;
 
712
    if tg_op = ''DELETE'' then
 
713
        if old.slotlink != '''' then
 
714
            dummy := tg_slotlink_unset(old.slotlink, old.slotname);
 
715
        end if;
 
716
        return old;
 
717
    end if;
 
718
end;
 
719
' language plpgsql;
 
720
create trigger tg_slotlink_a after insert or update or delete
 
721
    on PSlot for each row execute procedure tg_slotlink_a('PS');
 
722
create trigger tg_slotlink_a after insert or update or delete
 
723
    on WSlot for each row execute procedure tg_slotlink_a('WS');
 
724
create trigger tg_slotlink_a after insert or update or delete
 
725
    on IFace for each row execute procedure tg_slotlink_a('IF');
 
726
create trigger tg_slotlink_a after insert or update or delete
 
727
    on HSlot for each row execute procedure tg_slotlink_a('HS');
 
728
create trigger tg_slotlink_a after insert or update or delete
 
729
    on PHone for each row execute procedure tg_slotlink_a('PH');
 
730
-- ************************************************************
 
731
-- * Support function to set the opponents slotlink field
 
732
-- * if it does not already point to the requested slot
 
733
-- ************************************************************
 
734
create function tg_slotlink_set(bpchar, bpchar)
 
735
returns integer as '
 
736
declare
 
737
    myname      alias for $1;
 
738
    blname      alias for $2;
 
739
    mytype      char(2);
 
740
    link        char(4);
 
741
    rec         record;
 
742
begin
 
743
    mytype := substr(myname, 1, 2);
 
744
    link := mytype || substr(blname, 1, 2);
 
745
    if link = ''PHPH'' then
 
746
        raise exception
 
747
                ''slotlink between two phones does not make sense'';
 
748
    end if;
 
749
    if link in (''PHHS'', ''HSPH'') then
 
750
        raise exception
 
751
                ''link of phone to hub does not make sense'';
 
752
    end if;
 
753
    if link in (''PHIF'', ''IFPH'') then
 
754
        raise exception
 
755
                ''link of phone to hub does not make sense'';
 
756
    end if;
 
757
    if link in (''PSWS'', ''WSPS'') then
 
758
        raise exception
 
759
                ''slotlink from patchslot to wallslot not permitted'';
 
760
    end if;
 
761
    if mytype = ''PS'' then
 
762
        select into rec * from PSlot where slotname = myname;
 
763
        if not found then
 
764
            raise exception ''% does not exist'', myname;
 
765
        end if;
 
766
        if rec.slotlink != blname then
 
767
            update PSlot set slotlink = blname where slotname = myname;
 
768
        end if;
 
769
        return 0;
 
770
    end if;
 
771
    if mytype = ''WS'' then
 
772
        select into rec * from WSlot where slotname = myname;
 
773
        if not found then
 
774
            raise exception ''% does not exist'', myname;
 
775
        end if;
 
776
        if rec.slotlink != blname then
 
777
            update WSlot set slotlink = blname where slotname = myname;
 
778
        end if;
 
779
        return 0;
 
780
    end if;
 
781
    if mytype = ''IF'' then
 
782
        select into rec * from IFace where slotname = myname;
 
783
        if not found then
 
784
            raise exception ''% does not exist'', myname;
 
785
        end if;
 
786
        if rec.slotlink != blname then
 
787
            update IFace set slotlink = blname where slotname = myname;
 
788
        end if;
 
789
        return 0;
 
790
    end if;
 
791
    if mytype = ''HS'' then
 
792
        select into rec * from HSlot where slotname = myname;
 
793
        if not found then
 
794
            raise exception ''% does not exist'', myname;
 
795
        end if;
 
796
        if rec.slotlink != blname then
 
797
            update HSlot set slotlink = blname where slotname = myname;
 
798
        end if;
 
799
        return 0;
 
800
    end if;
 
801
    if mytype = ''PH'' then
 
802
        select into rec * from PHone where slotname = myname;
 
803
        if not found then
 
804
            raise exception ''% does not exist'', myname;
 
805
        end if;
 
806
        if rec.slotlink != blname then
 
807
            update PHone set slotlink = blname where slotname = myname;
 
808
        end if;
 
809
        return 0;
 
810
    end if;
 
811
    raise exception ''illegal slotlink beginning with %'', mytype;
 
812
end;
 
813
' language plpgsql;
 
814
-- ************************************************************
 
815
-- * Support function to clear out the slotlink field if
 
816
-- * it still points to specific slot
 
817
-- ************************************************************
 
818
create function tg_slotlink_unset(bpchar, bpchar)
 
819
returns integer as '
 
820
declare
 
821
    myname      alias for $1;
 
822
    blname      alias for $2;
 
823
    mytype      char(2);
 
824
    rec         record;
 
825
begin
 
826
    mytype := substr(myname, 1, 2);
 
827
    if mytype = ''PS'' then
 
828
        select into rec * from PSlot where slotname = myname;
 
829
        if not found then
 
830
            return 0;
 
831
        end if;
 
832
        if rec.slotlink = blname then
 
833
            update PSlot set slotlink = '''' where slotname = myname;
 
834
        end if;
 
835
        return 0;
 
836
    end if;
 
837
    if mytype = ''WS'' then
 
838
        select into rec * from WSlot where slotname = myname;
 
839
        if not found then
 
840
            return 0;
 
841
        end if;
 
842
        if rec.slotlink = blname then
 
843
            update WSlot set slotlink = '''' where slotname = myname;
 
844
        end if;
 
845
        return 0;
 
846
    end if;
 
847
    if mytype = ''IF'' then
 
848
        select into rec * from IFace where slotname = myname;
 
849
        if not found then
 
850
            return 0;
 
851
        end if;
 
852
        if rec.slotlink = blname then
 
853
            update IFace set slotlink = '''' where slotname = myname;
 
854
        end if;
 
855
        return 0;
 
856
    end if;
 
857
    if mytype = ''HS'' then
 
858
        select into rec * from HSlot where slotname = myname;
 
859
        if not found then
 
860
            return 0;
 
861
        end if;
 
862
        if rec.slotlink = blname then
 
863
            update HSlot set slotlink = '''' where slotname = myname;
 
864
        end if;
 
865
        return 0;
 
866
    end if;
 
867
    if mytype = ''PH'' then
 
868
        select into rec * from PHone where slotname = myname;
 
869
        if not found then
 
870
            return 0;
 
871
        end if;
 
872
        if rec.slotlink = blname then
 
873
            update PHone set slotlink = '''' where slotname = myname;
 
874
        end if;
 
875
        return 0;
 
876
    end if;
 
877
end;
 
878
' language plpgsql;
 
879
-- ************************************************************
 
880
-- * Describe the backside of a patchfield slot
 
881
-- ************************************************************
 
882
create function pslot_backlink_view(bpchar)
 
883
returns text as '
 
884
<<outer>>
 
885
declare
 
886
    rec         record;
 
887
    bltype      char(2);
 
888
    retval      text;
 
889
begin
 
890
    select into rec * from PSlot where slotname = $1;
 
891
    if not found then
 
892
        return '''';
 
893
    end if;
 
894
    if rec.backlink = '''' then
 
895
        return ''-'';
 
896
    end if;
 
897
    bltype := substr(rec.backlink, 1, 2);
 
898
    if bltype = ''PL'' then
 
899
        declare
 
900
            rec         record;
 
901
        begin
 
902
            select into rec * from PLine where slotname = "outer".rec.backlink;
 
903
            retval := ''Phone line '' || trim(rec.phonenumber);
 
904
            if rec.comment != '''' then
 
905
                retval := retval || '' ('';
 
906
                retval := retval || rec.comment;
 
907
                retval := retval || '')'';
 
908
            end if;
 
909
            return retval;
 
910
        end;
 
911
    end if;
 
912
    if bltype = ''WS'' then
 
913
        select into rec * from WSlot where slotname = rec.backlink;
 
914
        retval := trim(rec.slotname) || '' in room '';
 
915
        retval := retval || trim(rec.roomno);
 
916
        retval := retval || '' -> '';
 
917
        return retval || wslot_slotlink_view(rec.slotname);
 
918
    end if;
 
919
    return rec.backlink;
 
920
end;
 
921
' language plpgsql;
 
922
-- ************************************************************
 
923
-- * Describe the front of a patchfield slot
 
924
-- ************************************************************
 
925
create function pslot_slotlink_view(bpchar)
 
926
returns text as '
 
927
declare
 
928
    psrec       record;
 
929
    sltype      char(2);
 
930
    retval      text;
 
931
begin
 
932
    select into psrec * from PSlot where slotname = $1;
 
933
    if not found then
 
934
        return '''';
 
935
    end if;
 
936
    if psrec.slotlink = '''' then
 
937
        return ''-'';
 
938
    end if;
 
939
    sltype := substr(psrec.slotlink, 1, 2);
 
940
    if sltype = ''PS'' then
 
941
        retval := trim(psrec.slotlink) || '' -> '';
 
942
        return retval || pslot_backlink_view(psrec.slotlink);
 
943
    end if;
 
944
    if sltype = ''HS'' then
 
945
        retval := comment from Hub H, HSlot HS
 
946
                        where HS.slotname = psrec.slotlink
 
947
                          and H.name = HS.hubname;
 
948
        retval := retval || '' slot '';
 
949
        retval := retval || slotno::text from HSlot
 
950
                        where slotname = psrec.slotlink;
 
951
        return retval;
 
952
    end if;
 
953
    return psrec.slotlink;
 
954
end;
 
955
' language plpgsql;
 
956
-- ************************************************************
 
957
-- * Describe the front of a wall connector slot
 
958
-- ************************************************************
 
959
create function wslot_slotlink_view(bpchar)
 
960
returns text as '
 
961
declare
 
962
    rec         record;
 
963
    sltype      char(2);
 
964
    retval      text;
 
965
begin
 
966
    select into rec * from WSlot where slotname = $1;
 
967
    if not found then
 
968
        return '''';
 
969
    end if;
 
970
    if rec.slotlink = '''' then
 
971
        return ''-'';
 
972
    end if;
 
973
    sltype := substr(rec.slotlink, 1, 2);
 
974
    if sltype = ''PH'' then
 
975
        select into rec * from PHone where slotname = rec.slotlink;
 
976
        retval := ''Phone '' || trim(rec.slotname);
 
977
        if rec.comment != '''' then
 
978
            retval := retval || '' ('';
 
979
            retval := retval || rec.comment;
 
980
            retval := retval || '')'';
 
981
        end if;
 
982
        return retval;
 
983
    end if;
 
984
    if sltype = ''IF'' then
 
985
        declare
 
986
            syrow       System%RowType;
 
987
            ifrow       IFace%ROWTYPE;
 
988
        begin
 
989
            select into ifrow * from IFace where slotname = rec.slotlink;
 
990
            select into syrow * from System where name = ifrow.sysname;
 
991
            retval := syrow.name || '' IF '';
 
992
            retval := retval || ifrow.ifname;
 
993
            if syrow.comment != '''' then
 
994
                retval := retval || '' ('';
 
995
                retval := retval || syrow.comment;
 
996
                retval := retval || '')'';
 
997
            end if;
 
998
            return retval;
 
999
        end;
 
1000
    end if;
 
1001
    return rec.slotlink;
 
1002
end;
 
1003
' language plpgsql;
 
1004
-- ************************************************************
 
1005
-- * View of a patchfield describing backside and patches
 
1006
-- ************************************************************
 
1007
create view Pfield_v1 as select PF.pfname, PF.slotname,
 
1008
        pslot_backlink_view(PF.slotname) as backside,
 
1009
        pslot_slotlink_view(PF.slotname) as patch
 
1010
    from PSlot PF;
 
1011
--
 
1012
-- First we build the house - so we create the rooms
 
1013
--
 
1014
insert into Room values ('001', 'Entrance');
 
1015
insert into Room values ('002', 'Office');
 
1016
insert into Room values ('003', 'Office');
 
1017
insert into Room values ('004', 'Technical');
 
1018
insert into Room values ('101', 'Office');
 
1019
insert into Room values ('102', 'Conference');
 
1020
insert into Room values ('103', 'Restroom');
 
1021
insert into Room values ('104', 'Technical');
 
1022
insert into Room values ('105', 'Office');
 
1023
insert into Room values ('106', 'Office');
 
1024
--
 
1025
-- Second we install the wall connectors
 
1026
--
 
1027
insert into WSlot values ('WS.001.1a', '001', '', '');
 
1028
insert into WSlot values ('WS.001.1b', '001', '', '');
 
1029
insert into WSlot values ('WS.001.2a', '001', '', '');
 
1030
insert into WSlot values ('WS.001.2b', '001', '', '');
 
1031
insert into WSlot values ('WS.001.3a', '001', '', '');
 
1032
insert into WSlot values ('WS.001.3b', '001', '', '');
 
1033
insert into WSlot values ('WS.002.1a', '002', '', '');
 
1034
insert into WSlot values ('WS.002.1b', '002', '', '');
 
1035
insert into WSlot values ('WS.002.2a', '002', '', '');
 
1036
insert into WSlot values ('WS.002.2b', '002', '', '');
 
1037
insert into WSlot values ('WS.002.3a', '002', '', '');
 
1038
insert into WSlot values ('WS.002.3b', '002', '', '');
 
1039
insert into WSlot values ('WS.003.1a', '003', '', '');
 
1040
insert into WSlot values ('WS.003.1b', '003', '', '');
 
1041
insert into WSlot values ('WS.003.2a', '003', '', '');
 
1042
insert into WSlot values ('WS.003.2b', '003', '', '');
 
1043
insert into WSlot values ('WS.003.3a', '003', '', '');
 
1044
insert into WSlot values ('WS.003.3b', '003', '', '');
 
1045
insert into WSlot values ('WS.101.1a', '101', '', '');
 
1046
insert into WSlot values ('WS.101.1b', '101', '', '');
 
1047
insert into WSlot values ('WS.101.2a', '101', '', '');
 
1048
insert into WSlot values ('WS.101.2b', '101', '', '');
 
1049
insert into WSlot values ('WS.101.3a', '101', '', '');
 
1050
insert into WSlot values ('WS.101.3b', '101', '', '');
 
1051
insert into WSlot values ('WS.102.1a', '102', '', '');
 
1052
insert into WSlot values ('WS.102.1b', '102', '', '');
 
1053
insert into WSlot values ('WS.102.2a', '102', '', '');
 
1054
insert into WSlot values ('WS.102.2b', '102', '', '');
 
1055
insert into WSlot values ('WS.102.3a', '102', '', '');
 
1056
insert into WSlot values ('WS.102.3b', '102', '', '');
 
1057
insert into WSlot values ('WS.105.1a', '105', '', '');
 
1058
insert into WSlot values ('WS.105.1b', '105', '', '');
 
1059
insert into WSlot values ('WS.105.2a', '105', '', '');
 
1060
insert into WSlot values ('WS.105.2b', '105', '', '');
 
1061
insert into WSlot values ('WS.105.3a', '105', '', '');
 
1062
insert into WSlot values ('WS.105.3b', '105', '', '');
 
1063
insert into WSlot values ('WS.106.1a', '106', '', '');
 
1064
insert into WSlot values ('WS.106.1b', '106', '', '');
 
1065
insert into WSlot values ('WS.106.2a', '106', '', '');
 
1066
insert into WSlot values ('WS.106.2b', '106', '', '');
 
1067
insert into WSlot values ('WS.106.3a', '106', '', '');
 
1068
insert into WSlot values ('WS.106.3b', '106', '', '');
 
1069
--
 
1070
-- Now create the patch fields and their slots
 
1071
--
 
1072
insert into PField values ('PF0_1', 'Wallslots basement');
 
1073
--
 
1074
-- The cables for these will be made later, so they are unconnected for now
 
1075
--
 
1076
insert into PSlot values ('PS.base.a1', 'PF0_1', '', '');
 
1077
insert into PSlot values ('PS.base.a2', 'PF0_1', '', '');
 
1078
insert into PSlot values ('PS.base.a3', 'PF0_1', '', '');
 
1079
insert into PSlot values ('PS.base.a4', 'PF0_1', '', '');
 
1080
insert into PSlot values ('PS.base.a5', 'PF0_1', '', '');
 
1081
insert into PSlot values ('PS.base.a6', 'PF0_1', '', '');
 
1082
--
 
1083
-- These are already wired to the wall connectors
 
1084
--
 
1085
insert into PSlot values ('PS.base.b1', 'PF0_1', '', 'WS.002.1a');
 
1086
insert into PSlot values ('PS.base.b2', 'PF0_1', '', 'WS.002.1b');
 
1087
insert into PSlot values ('PS.base.b3', 'PF0_1', '', 'WS.002.2a');
 
1088
insert into PSlot values ('PS.base.b4', 'PF0_1', '', 'WS.002.2b');
 
1089
insert into PSlot values ('PS.base.b5', 'PF0_1', '', 'WS.002.3a');
 
1090
insert into PSlot values ('PS.base.b6', 'PF0_1', '', 'WS.002.3b');
 
1091
insert into PSlot values ('PS.base.c1', 'PF0_1', '', 'WS.003.1a');
 
1092
insert into PSlot values ('PS.base.c2', 'PF0_1', '', 'WS.003.1b');
 
1093
insert into PSlot values ('PS.base.c3', 'PF0_1', '', 'WS.003.2a');
 
1094
insert into PSlot values ('PS.base.c4', 'PF0_1', '', 'WS.003.2b');
 
1095
insert into PSlot values ('PS.base.c5', 'PF0_1', '', 'WS.003.3a');
 
1096
insert into PSlot values ('PS.base.c6', 'PF0_1', '', 'WS.003.3b');
 
1097
--
 
1098
-- This patchfield will be renamed later into PF0_2 - so its
 
1099
-- slots references in pfname should follow
 
1100
--
 
1101
insert into PField values ('PF0_X', 'Phonelines basement');
 
1102
insert into PSlot values ('PS.base.ta1', 'PF0_X', '', '');
 
1103
insert into PSlot values ('PS.base.ta2', 'PF0_X', '', '');
 
1104
insert into PSlot values ('PS.base.ta3', 'PF0_X', '', '');
 
1105
insert into PSlot values ('PS.base.ta4', 'PF0_X', '', '');
 
1106
insert into PSlot values ('PS.base.ta5', 'PF0_X', '', '');
 
1107
insert into PSlot values ('PS.base.ta6', 'PF0_X', '', '');
 
1108
insert into PSlot values ('PS.base.tb1', 'PF0_X', '', '');
 
1109
insert into PSlot values ('PS.base.tb2', 'PF0_X', '', '');
 
1110
insert into PSlot values ('PS.base.tb3', 'PF0_X', '', '');
 
1111
insert into PSlot values ('PS.base.tb4', 'PF0_X', '', '');
 
1112
insert into PSlot values ('PS.base.tb5', 'PF0_X', '', '');
 
1113
insert into PSlot values ('PS.base.tb6', 'PF0_X', '', '');
 
1114
insert into PField values ('PF1_1', 'Wallslots first floor');
 
1115
insert into PSlot values ('PS.first.a1', 'PF1_1', '', 'WS.101.1a');
 
1116
insert into PSlot values ('PS.first.a2', 'PF1_1', '', 'WS.101.1b');
 
1117
insert into PSlot values ('PS.first.a3', 'PF1_1', '', 'WS.101.2a');
 
1118
insert into PSlot values ('PS.first.a4', 'PF1_1', '', 'WS.101.2b');
 
1119
insert into PSlot values ('PS.first.a5', 'PF1_1', '', 'WS.101.3a');
 
1120
insert into PSlot values ('PS.first.a6', 'PF1_1', '', 'WS.101.3b');
 
1121
insert into PSlot values ('PS.first.b1', 'PF1_1', '', 'WS.102.1a');
 
1122
insert into PSlot values ('PS.first.b2', 'PF1_1', '', 'WS.102.1b');
 
1123
insert into PSlot values ('PS.first.b3', 'PF1_1', '', 'WS.102.2a');
 
1124
insert into PSlot values ('PS.first.b4', 'PF1_1', '', 'WS.102.2b');
 
1125
insert into PSlot values ('PS.first.b5', 'PF1_1', '', 'WS.102.3a');
 
1126
insert into PSlot values ('PS.first.b6', 'PF1_1', '', 'WS.102.3b');
 
1127
insert into PSlot values ('PS.first.c1', 'PF1_1', '', 'WS.105.1a');
 
1128
insert into PSlot values ('PS.first.c2', 'PF1_1', '', 'WS.105.1b');
 
1129
insert into PSlot values ('PS.first.c3', 'PF1_1', '', 'WS.105.2a');
 
1130
insert into PSlot values ('PS.first.c4', 'PF1_1', '', 'WS.105.2b');
 
1131
insert into PSlot values ('PS.first.c5', 'PF1_1', '', 'WS.105.3a');
 
1132
insert into PSlot values ('PS.first.c6', 'PF1_1', '', 'WS.105.3b');
 
1133
insert into PSlot values ('PS.first.d1', 'PF1_1', '', 'WS.106.1a');
 
1134
insert into PSlot values ('PS.first.d2', 'PF1_1', '', 'WS.106.1b');
 
1135
insert into PSlot values ('PS.first.d3', 'PF1_1', '', 'WS.106.2a');
 
1136
insert into PSlot values ('PS.first.d4', 'PF1_1', '', 'WS.106.2b');
 
1137
insert into PSlot values ('PS.first.d5', 'PF1_1', '', 'WS.106.3a');
 
1138
insert into PSlot values ('PS.first.d6', 'PF1_1', '', 'WS.106.3b');
 
1139
--
 
1140
-- Now we wire the wall connectors 1a-2a in room 001 to the
 
1141
-- patchfield. In the second update we make an error, and
 
1142
-- correct it after
 
1143
--
 
1144
update PSlot set backlink = 'WS.001.1a' where slotname = 'PS.base.a1';
 
1145
update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a3';
 
1146
select * from WSlot where roomno = '001' order by slotname;
 
1147
       slotname       |  roomno  |       slotlink       |       backlink       
 
1148
----------------------+----------+----------------------+----------------------
 
1149
 WS.001.1a            | 001      |                      | PS.base.a1          
 
1150
 WS.001.1b            | 001      |                      | PS.base.a3          
 
1151
 WS.001.2a            | 001      |                      |                     
 
1152
 WS.001.2b            | 001      |                      |                     
 
1153
 WS.001.3a            | 001      |                      |                     
 
1154
 WS.001.3b            | 001      |                      |                     
 
1155
(6 rows)
 
1156
 
 
1157
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 
1158
       slotname       | pfname |       slotlink       |       backlink       
 
1159
----------------------+--------+----------------------+----------------------
 
1160
 PS.base.a1           | PF0_1  |                      | WS.001.1a           
 
1161
 PS.base.a2           | PF0_1  |                      |                     
 
1162
 PS.base.a3           | PF0_1  |                      | WS.001.1b           
 
1163
 PS.base.a4           | PF0_1  |                      |                     
 
1164
 PS.base.a5           | PF0_1  |                      |                     
 
1165
 PS.base.a6           | PF0_1  |                      |                     
 
1166
(6 rows)
 
1167
 
 
1168
update PSlot set backlink = 'WS.001.2a' where slotname = 'PS.base.a3';
 
1169
select * from WSlot where roomno = '001' order by slotname;
 
1170
       slotname       |  roomno  |       slotlink       |       backlink       
 
1171
----------------------+----------+----------------------+----------------------
 
1172
 WS.001.1a            | 001      |                      | PS.base.a1          
 
1173
 WS.001.1b            | 001      |                      |                     
 
1174
 WS.001.2a            | 001      |                      | PS.base.a3          
 
1175
 WS.001.2b            | 001      |                      |                     
 
1176
 WS.001.3a            | 001      |                      |                     
 
1177
 WS.001.3b            | 001      |                      |                     
 
1178
(6 rows)
 
1179
 
 
1180
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 
1181
       slotname       | pfname |       slotlink       |       backlink       
 
1182
----------------------+--------+----------------------+----------------------
 
1183
 PS.base.a1           | PF0_1  |                      | WS.001.1a           
 
1184
 PS.base.a2           | PF0_1  |                      |                     
 
1185
 PS.base.a3           | PF0_1  |                      | WS.001.2a           
 
1186
 PS.base.a4           | PF0_1  |                      |                     
 
1187
 PS.base.a5           | PF0_1  |                      |                     
 
1188
 PS.base.a6           | PF0_1  |                      |                     
 
1189
(6 rows)
 
1190
 
 
1191
update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a2';
 
1192
select * from WSlot where roomno = '001' order by slotname;
 
1193
       slotname       |  roomno  |       slotlink       |       backlink       
 
1194
----------------------+----------+----------------------+----------------------
 
1195
 WS.001.1a            | 001      |                      | PS.base.a1          
 
1196
 WS.001.1b            | 001      |                      | PS.base.a2          
 
1197
 WS.001.2a            | 001      |                      | PS.base.a3          
 
1198
 WS.001.2b            | 001      |                      |                     
 
1199
 WS.001.3a            | 001      |                      |                     
 
1200
 WS.001.3b            | 001      |                      |                     
 
1201
(6 rows)
 
1202
 
 
1203
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 
1204
       slotname       | pfname |       slotlink       |       backlink       
 
1205
----------------------+--------+----------------------+----------------------
 
1206
 PS.base.a1           | PF0_1  |                      | WS.001.1a           
 
1207
 PS.base.a2           | PF0_1  |                      | WS.001.1b           
 
1208
 PS.base.a3           | PF0_1  |                      | WS.001.2a           
 
1209
 PS.base.a4           | PF0_1  |                      |                     
 
1210
 PS.base.a5           | PF0_1  |                      |                     
 
1211
 PS.base.a6           | PF0_1  |                      |                     
 
1212
(6 rows)
 
1213
 
 
1214
--
 
1215
-- Same procedure for 2b-3b but this time updating the WSlot instead
 
1216
-- of the PSlot. Due to the triggers the result is the same:
 
1217
-- WSlot and corresponding PSlot point to each other.
 
1218
--
 
1219
update WSlot set backlink = 'PS.base.a4' where slotname = 'WS.001.2b';
 
1220
update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3a';
 
1221
select * from WSlot where roomno = '001' order by slotname;
 
1222
       slotname       |  roomno  |       slotlink       |       backlink       
 
1223
----------------------+----------+----------------------+----------------------
 
1224
 WS.001.1a            | 001      |                      | PS.base.a1          
 
1225
 WS.001.1b            | 001      |                      | PS.base.a2          
 
1226
 WS.001.2a            | 001      |                      | PS.base.a3          
 
1227
 WS.001.2b            | 001      |                      | PS.base.a4          
 
1228
 WS.001.3a            | 001      |                      | PS.base.a6          
 
1229
 WS.001.3b            | 001      |                      |                     
 
1230
(6 rows)
 
1231
 
 
1232
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 
1233
       slotname       | pfname |       slotlink       |       backlink       
 
1234
----------------------+--------+----------------------+----------------------
 
1235
 PS.base.a1           | PF0_1  |                      | WS.001.1a           
 
1236
 PS.base.a2           | PF0_1  |                      | WS.001.1b           
 
1237
 PS.base.a3           | PF0_1  |                      | WS.001.2a           
 
1238
 PS.base.a4           | PF0_1  |                      | WS.001.2b           
 
1239
 PS.base.a5           | PF0_1  |                      |                     
 
1240
 PS.base.a6           | PF0_1  |                      | WS.001.3a           
 
1241
(6 rows)
 
1242
 
 
1243
update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3b';
 
1244
select * from WSlot where roomno = '001' order by slotname;
 
1245
       slotname       |  roomno  |       slotlink       |       backlink       
 
1246
----------------------+----------+----------------------+----------------------
 
1247
 WS.001.1a            | 001      |                      | PS.base.a1          
 
1248
 WS.001.1b            | 001      |                      | PS.base.a2          
 
1249
 WS.001.2a            | 001      |                      | PS.base.a3          
 
1250
 WS.001.2b            | 001      |                      | PS.base.a4          
 
1251
 WS.001.3a            | 001      |                      |                     
 
1252
 WS.001.3b            | 001      |                      | PS.base.a6          
 
1253
(6 rows)
 
1254
 
 
1255
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 
1256
       slotname       | pfname |       slotlink       |       backlink       
 
1257
----------------------+--------+----------------------+----------------------
 
1258
 PS.base.a1           | PF0_1  |                      | WS.001.1a           
 
1259
 PS.base.a2           | PF0_1  |                      | WS.001.1b           
 
1260
 PS.base.a3           | PF0_1  |                      | WS.001.2a           
 
1261
 PS.base.a4           | PF0_1  |                      | WS.001.2b           
 
1262
 PS.base.a5           | PF0_1  |                      |                     
 
1263
 PS.base.a6           | PF0_1  |                      | WS.001.3b           
 
1264
(6 rows)
 
1265
 
 
1266
update WSlot set backlink = 'PS.base.a5' where slotname = 'WS.001.3a';
 
1267
select * from WSlot where roomno = '001' order by slotname;
 
1268
       slotname       |  roomno  |       slotlink       |       backlink       
 
1269
----------------------+----------+----------------------+----------------------
 
1270
 WS.001.1a            | 001      |                      | PS.base.a1          
 
1271
 WS.001.1b            | 001      |                      | PS.base.a2          
 
1272
 WS.001.2a            | 001      |                      | PS.base.a3          
 
1273
 WS.001.2b            | 001      |                      | PS.base.a4          
 
1274
 WS.001.3a            | 001      |                      | PS.base.a5          
 
1275
 WS.001.3b            | 001      |                      | PS.base.a6          
 
1276
(6 rows)
 
1277
 
 
1278
select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
 
1279
       slotname       | pfname |       slotlink       |       backlink       
 
1280
----------------------+--------+----------------------+----------------------
 
1281
 PS.base.a1           | PF0_1  |                      | WS.001.1a           
 
1282
 PS.base.a2           | PF0_1  |                      | WS.001.1b           
 
1283
 PS.base.a3           | PF0_1  |                      | WS.001.2a           
 
1284
 PS.base.a4           | PF0_1  |                      | WS.001.2b           
 
1285
 PS.base.a5           | PF0_1  |                      | WS.001.3a           
 
1286
 PS.base.a6           | PF0_1  |                      | WS.001.3b           
 
1287
(6 rows)
 
1288
 
 
1289
insert into PField values ('PF1_2', 'Phonelines first floor');
 
1290
insert into PSlot values ('PS.first.ta1', 'PF1_2', '', '');
 
1291
insert into PSlot values ('PS.first.ta2', 'PF1_2', '', '');
 
1292
insert into PSlot values ('PS.first.ta3', 'PF1_2', '', '');
 
1293
insert into PSlot values ('PS.first.ta4', 'PF1_2', '', '');
 
1294
insert into PSlot values ('PS.first.ta5', 'PF1_2', '', '');
 
1295
insert into PSlot values ('PS.first.ta6', 'PF1_2', '', '');
 
1296
insert into PSlot values ('PS.first.tb1', 'PF1_2', '', '');
 
1297
insert into PSlot values ('PS.first.tb2', 'PF1_2', '', '');
 
1298
insert into PSlot values ('PS.first.tb3', 'PF1_2', '', '');
 
1299
insert into PSlot values ('PS.first.tb4', 'PF1_2', '', '');
 
1300
insert into PSlot values ('PS.first.tb5', 'PF1_2', '', '');
 
1301
insert into PSlot values ('PS.first.tb6', 'PF1_2', '', '');
 
1302
--
 
1303
-- Fix the wrong name for patchfield PF0_2
 
1304
--
 
1305
update PField set name = 'PF0_2' where name = 'PF0_X';
 
1306
select * from PSlot order by slotname;
 
1307
       slotname       | pfname |       slotlink       |       backlink       
 
1308
----------------------+--------+----------------------+----------------------
 
1309
 PS.base.a1           | PF0_1  |                      | WS.001.1a           
 
1310
 PS.base.a2           | PF0_1  |                      | WS.001.1b           
 
1311
 PS.base.a3           | PF0_1  |                      | WS.001.2a           
 
1312
 PS.base.a4           | PF0_1  |                      | WS.001.2b           
 
1313
 PS.base.a5           | PF0_1  |                      | WS.001.3a           
 
1314
 PS.base.a6           | PF0_1  |                      | WS.001.3b           
 
1315
 PS.base.b1           | PF0_1  |                      | WS.002.1a           
 
1316
 PS.base.b2           | PF0_1  |                      | WS.002.1b           
 
1317
 PS.base.b3           | PF0_1  |                      | WS.002.2a           
 
1318
 PS.base.b4           | PF0_1  |                      | WS.002.2b           
 
1319
 PS.base.b5           | PF0_1  |                      | WS.002.3a           
 
1320
 PS.base.b6           | PF0_1  |                      | WS.002.3b           
 
1321
 PS.base.c1           | PF0_1  |                      | WS.003.1a           
 
1322
 PS.base.c2           | PF0_1  |                      | WS.003.1b           
 
1323
 PS.base.c3           | PF0_1  |                      | WS.003.2a           
 
1324
 PS.base.c4           | PF0_1  |                      | WS.003.2b           
 
1325
 PS.base.c5           | PF0_1  |                      | WS.003.3a           
 
1326
 PS.base.c6           | PF0_1  |                      | WS.003.3b           
 
1327
 PS.base.ta1          | PF0_2  |                      |                     
 
1328
 PS.base.ta2          | PF0_2  |                      |                     
 
1329
 PS.base.ta3          | PF0_2  |                      |                     
 
1330
 PS.base.ta4          | PF0_2  |                      |                     
 
1331
 PS.base.ta5          | PF0_2  |                      |                     
 
1332
 PS.base.ta6          | PF0_2  |                      |                     
 
1333
 PS.base.tb1          | PF0_2  |                      |                     
 
1334
 PS.base.tb2          | PF0_2  |                      |                     
 
1335
 PS.base.tb3          | PF0_2  |                      |                     
 
1336
 PS.base.tb4          | PF0_2  |                      |                     
 
1337
 PS.base.tb5          | PF0_2  |                      |                     
 
1338
 PS.base.tb6          | PF0_2  |                      |                     
 
1339
 PS.first.a1          | PF1_1  |                      | WS.101.1a           
 
1340
 PS.first.a2          | PF1_1  |                      | WS.101.1b           
 
1341
 PS.first.a3          | PF1_1  |                      | WS.101.2a           
 
1342
 PS.first.a4          | PF1_1  |                      | WS.101.2b           
 
1343
 PS.first.a5          | PF1_1  |                      | WS.101.3a           
 
1344
 PS.first.a6          | PF1_1  |                      | WS.101.3b           
 
1345
 PS.first.b1          | PF1_1  |                      | WS.102.1a           
 
1346
 PS.first.b2          | PF1_1  |                      | WS.102.1b           
 
1347
 PS.first.b3          | PF1_1  |                      | WS.102.2a           
 
1348
 PS.first.b4          | PF1_1  |                      | WS.102.2b           
 
1349
 PS.first.b5          | PF1_1  |                      | WS.102.3a           
 
1350
 PS.first.b6          | PF1_1  |                      | WS.102.3b           
 
1351
 PS.first.c1          | PF1_1  |                      | WS.105.1a           
 
1352
 PS.first.c2          | PF1_1  |                      | WS.105.1b           
 
1353
 PS.first.c3          | PF1_1  |                      | WS.105.2a           
 
1354
 PS.first.c4          | PF1_1  |                      | WS.105.2b           
 
1355
 PS.first.c5          | PF1_1  |                      | WS.105.3a           
 
1356
 PS.first.c6          | PF1_1  |                      | WS.105.3b           
 
1357
 PS.first.d1          | PF1_1  |                      | WS.106.1a           
 
1358
 PS.first.d2          | PF1_1  |                      | WS.106.1b           
 
1359
 PS.first.d3          | PF1_1  |                      | WS.106.2a           
 
1360
 PS.first.d4          | PF1_1  |                      | WS.106.2b           
 
1361
 PS.first.d5          | PF1_1  |                      | WS.106.3a           
 
1362
 PS.first.d6          | PF1_1  |                      | WS.106.3b           
 
1363
 PS.first.ta1         | PF1_2  |                      |                     
 
1364
 PS.first.ta2         | PF1_2  |                      |                     
 
1365
 PS.first.ta3         | PF1_2  |                      |                     
 
1366
 PS.first.ta4         | PF1_2  |                      |                     
 
1367
 PS.first.ta5         | PF1_2  |                      |                     
 
1368
 PS.first.ta6         | PF1_2  |                      |                     
 
1369
 PS.first.tb1         | PF1_2  |                      |                     
 
1370
 PS.first.tb2         | PF1_2  |                      |                     
 
1371
 PS.first.tb3         | PF1_2  |                      |                     
 
1372
 PS.first.tb4         | PF1_2  |                      |                     
 
1373
 PS.first.tb5         | PF1_2  |                      |                     
 
1374
 PS.first.tb6         | PF1_2  |                      |                     
 
1375
(66 rows)
 
1376
 
 
1377
select * from WSlot order by slotname;
 
1378
       slotname       |  roomno  |       slotlink       |       backlink       
 
1379
----------------------+----------+----------------------+----------------------
 
1380
 WS.001.1a            | 001      |                      | PS.base.a1          
 
1381
 WS.001.1b            | 001      |                      | PS.base.a2          
 
1382
 WS.001.2a            | 001      |                      | PS.base.a3          
 
1383
 WS.001.2b            | 001      |                      | PS.base.a4          
 
1384
 WS.001.3a            | 001      |                      | PS.base.a5          
 
1385
 WS.001.3b            | 001      |                      | PS.base.a6          
 
1386
 WS.002.1a            | 002      |                      | PS.base.b1          
 
1387
 WS.002.1b            | 002      |                      | PS.base.b2          
 
1388
 WS.002.2a            | 002      |                      | PS.base.b3          
 
1389
 WS.002.2b            | 002      |                      | PS.base.b4          
 
1390
 WS.002.3a            | 002      |                      | PS.base.b5          
 
1391
 WS.002.3b            | 002      |                      | PS.base.b6          
 
1392
 WS.003.1a            | 003      |                      | PS.base.c1          
 
1393
 WS.003.1b            | 003      |                      | PS.base.c2          
 
1394
 WS.003.2a            | 003      |                      | PS.base.c3          
 
1395
 WS.003.2b            | 003      |                      | PS.base.c4          
 
1396
 WS.003.3a            | 003      |                      | PS.base.c5          
 
1397
 WS.003.3b            | 003      |                      | PS.base.c6          
 
1398
 WS.101.1a            | 101      |                      | PS.first.a1         
 
1399
 WS.101.1b            | 101      |                      | PS.first.a2         
 
1400
 WS.101.2a            | 101      |                      | PS.first.a3         
 
1401
 WS.101.2b            | 101      |                      | PS.first.a4         
 
1402
 WS.101.3a            | 101      |                      | PS.first.a5         
 
1403
 WS.101.3b            | 101      |                      | PS.first.a6         
 
1404
 WS.102.1a            | 102      |                      | PS.first.b1         
 
1405
 WS.102.1b            | 102      |                      | PS.first.b2         
 
1406
 WS.102.2a            | 102      |                      | PS.first.b3         
 
1407
 WS.102.2b            | 102      |                      | PS.first.b4         
 
1408
 WS.102.3a            | 102      |                      | PS.first.b5         
 
1409
 WS.102.3b            | 102      |                      | PS.first.b6         
 
1410
 WS.105.1a            | 105      |                      | PS.first.c1         
 
1411
 WS.105.1b            | 105      |                      | PS.first.c2         
 
1412
 WS.105.2a            | 105      |                      | PS.first.c3         
 
1413
 WS.105.2b            | 105      |                      | PS.first.c4         
 
1414
 WS.105.3a            | 105      |                      | PS.first.c5         
 
1415
 WS.105.3b            | 105      |                      | PS.first.c6         
 
1416
 WS.106.1a            | 106      |                      | PS.first.d1         
 
1417
 WS.106.1b            | 106      |                      | PS.first.d2         
 
1418
 WS.106.2a            | 106      |                      | PS.first.d3         
 
1419
 WS.106.2b            | 106      |                      | PS.first.d4         
 
1420
 WS.106.3a            | 106      |                      | PS.first.d5         
 
1421
 WS.106.3b            | 106      |                      | PS.first.d6         
 
1422
(42 rows)
 
1423
 
 
1424
--
 
1425
-- Install the central phone system and create the phone numbers.
 
1426
-- They are weired on insert to the patchfields. Again the
 
1427
-- triggers automatically tell the PSlots to update their
 
1428
-- backlink field.
 
1429
--
 
1430
insert into PLine values ('PL.001', '-0', 'Central call', 'PS.base.ta1');
 
1431
insert into PLine values ('PL.002', '-101', '', 'PS.base.ta2');
 
1432
insert into PLine values ('PL.003', '-102', '', 'PS.base.ta3');
 
1433
insert into PLine values ('PL.004', '-103', '', 'PS.base.ta5');
 
1434
insert into PLine values ('PL.005', '-104', '', 'PS.base.ta6');
 
1435
insert into PLine values ('PL.006', '-106', '', 'PS.base.tb2');
 
1436
insert into PLine values ('PL.007', '-108', '', 'PS.base.tb3');
 
1437
insert into PLine values ('PL.008', '-109', '', 'PS.base.tb4');
 
1438
insert into PLine values ('PL.009', '-121', '', 'PS.base.tb5');
 
1439
insert into PLine values ('PL.010', '-122', '', 'PS.base.tb6');
 
1440
insert into PLine values ('PL.015', '-134', '', 'PS.first.ta1');
 
1441
insert into PLine values ('PL.016', '-137', '', 'PS.first.ta3');
 
1442
insert into PLine values ('PL.017', '-139', '', 'PS.first.ta4');
 
1443
insert into PLine values ('PL.018', '-362', '', 'PS.first.tb1');
 
1444
insert into PLine values ('PL.019', '-363', '', 'PS.first.tb2');
 
1445
insert into PLine values ('PL.020', '-364', '', 'PS.first.tb3');
 
1446
insert into PLine values ('PL.021', '-365', '', 'PS.first.tb5');
 
1447
insert into PLine values ('PL.022', '-367', '', 'PS.first.tb6');
 
1448
insert into PLine values ('PL.028', '-501', 'Fax entrance', 'PS.base.ta2');
 
1449
insert into PLine values ('PL.029', '-502', 'Fax first floor', 'PS.first.ta1');
 
1450
--
 
1451
-- Buy some phones, plug them into the wall and patch the
 
1452
-- phone lines to the corresponding patchfield slots.
 
1453
--
 
1454
insert into PHone values ('PH.hc001', 'Hicom standard', 'WS.001.1a');
 
1455
update PSlot set slotlink = 'PS.base.ta1' where slotname = 'PS.base.a1';
 
1456
insert into PHone values ('PH.hc002', 'Hicom standard', 'WS.002.1a');
 
1457
update PSlot set slotlink = 'PS.base.ta5' where slotname = 'PS.base.b1';
 
1458
insert into PHone values ('PH.hc003', 'Hicom standard', 'WS.002.2a');
 
1459
update PSlot set slotlink = 'PS.base.tb2' where slotname = 'PS.base.b3';
 
1460
insert into PHone values ('PH.fax001', 'Canon fax', 'WS.001.2a');
 
1461
update PSlot set slotlink = 'PS.base.ta2' where slotname = 'PS.base.a3';
 
1462
--
 
1463
-- Install a hub at one of the patchfields, plug a computers
 
1464
-- ethernet interface into the wall and patch it to the hub.
 
1465
--
 
1466
insert into Hub values ('base.hub1', 'Patchfield PF0_1 hub', 16);
 
1467
insert into System values ('orion', 'PC');
 
1468
insert into IFace values ('IF', 'orion', 'eth0', 'WS.002.1b');
 
1469
update PSlot set slotlink = 'HS.base.hub1.1' where slotname = 'PS.base.b2';
 
1470
--
 
1471
-- Now we take a look at the patchfield
 
1472
--
 
1473
select * from PField_v1 where pfname = 'PF0_1' order by slotname;
 
1474
 pfname |       slotname       |                         backside                         |                     patch                     
 
1475
--------+----------------------+----------------------------------------------------------+-----------------------------------------------
 
1476
 PF0_1  | PS.base.a1           | WS.001.1a in room 001 -> Phone PH.hc001 (Hicom standard) | PS.base.ta1 -> Phone line -0 (Central call)
 
1477
 PF0_1  | PS.base.a2           | WS.001.1b in room 001 -> -                               | -
 
1478
 PF0_1  | PS.base.a3           | WS.001.2a in room 001 -> Phone PH.fax001 (Canon fax)     | PS.base.ta2 -> Phone line -501 (Fax entrance)
 
1479
 PF0_1  | PS.base.a4           | WS.001.2b in room 001 -> -                               | -
 
1480
 PF0_1  | PS.base.a5           | WS.001.3a in room 001 -> -                               | -
 
1481
 PF0_1  | PS.base.a6           | WS.001.3b in room 001 -> -                               | -
 
1482
 PF0_1  | PS.base.b1           | WS.002.1a in room 002 -> Phone PH.hc002 (Hicom standard) | PS.base.ta5 -> Phone line -103
 
1483
 PF0_1  | PS.base.b2           | WS.002.1b in room 002 -> orion IF eth0 (PC)              | Patchfield PF0_1 hub slot 1
 
1484
 PF0_1  | PS.base.b3           | WS.002.2a in room 002 -> Phone PH.hc003 (Hicom standard) | PS.base.tb2 -> Phone line -106
 
1485
 PF0_1  | PS.base.b4           | WS.002.2b in room 002 -> -                               | -
 
1486
 PF0_1  | PS.base.b5           | WS.002.3a in room 002 -> -                               | -
 
1487
 PF0_1  | PS.base.b6           | WS.002.3b in room 002 -> -                               | -
 
1488
 PF0_1  | PS.base.c1           | WS.003.1a in room 003 -> -                               | -
 
1489
 PF0_1  | PS.base.c2           | WS.003.1b in room 003 -> -                               | -
 
1490
 PF0_1  | PS.base.c3           | WS.003.2a in room 003 -> -                               | -
 
1491
 PF0_1  | PS.base.c4           | WS.003.2b in room 003 -> -                               | -
 
1492
 PF0_1  | PS.base.c5           | WS.003.3a in room 003 -> -                               | -
 
1493
 PF0_1  | PS.base.c6           | WS.003.3b in room 003 -> -                               | -
 
1494
(18 rows)
 
1495
 
 
1496
select * from PField_v1 where pfname = 'PF0_2' order by slotname;
 
1497
 pfname |       slotname       |            backside            |                                 patch                                  
 
1498
--------+----------------------+--------------------------------+------------------------------------------------------------------------
 
1499
 PF0_2  | PS.base.ta1          | Phone line -0 (Central call)   | PS.base.a1 -> WS.001.1a in room 001 -> Phone PH.hc001 (Hicom standard)
 
1500
 PF0_2  | PS.base.ta2          | Phone line -501 (Fax entrance) | PS.base.a3 -> WS.001.2a in room 001 -> Phone PH.fax001 (Canon fax)
 
1501
 PF0_2  | PS.base.ta3          | Phone line -102                | -
 
1502
 PF0_2  | PS.base.ta4          | -                              | -
 
1503
 PF0_2  | PS.base.ta5          | Phone line -103                | PS.base.b1 -> WS.002.1a in room 002 -> Phone PH.hc002 (Hicom standard)
 
1504
 PF0_2  | PS.base.ta6          | Phone line -104                | -
 
1505
 PF0_2  | PS.base.tb1          | -                              | -
 
1506
 PF0_2  | PS.base.tb2          | Phone line -106                | PS.base.b3 -> WS.002.2a in room 002 -> Phone PH.hc003 (Hicom standard)
 
1507
 PF0_2  | PS.base.tb3          | Phone line -108                | -
 
1508
 PF0_2  | PS.base.tb4          | Phone line -109                | -
 
1509
 PF0_2  | PS.base.tb5          | Phone line -121                | -
 
1510
 PF0_2  | PS.base.tb6          | Phone line -122                | -
 
1511
(12 rows)
 
1512
 
 
1513
--
 
1514
-- Finally we want errors
 
1515
--
 
1516
insert into PField values ('PF1_1', 'should fail due to unique index');
 
1517
ERROR:  duplicate key value violates unique constraint "pfield_name"
 
1518
DETAIL:  Key (name)=(PF1_1) already exists.
 
1519
update PSlot set backlink = 'WS.not.there' where slotname = 'PS.base.a1';
 
1520
ERROR:  WS.not.there         does not exist
 
1521
CONTEXT:  PL/pgSQL function tg_backlink_a() line 17 at assignment
 
1522
update PSlot set backlink = 'XX.illegal' where slotname = 'PS.base.a1';
 
1523
ERROR:  illegal backlink beginning with XX
 
1524
CONTEXT:  PL/pgSQL function tg_backlink_a() line 17 at assignment
 
1525
update PSlot set slotlink = 'PS.not.there' where slotname = 'PS.base.a1';
 
1526
ERROR:  PS.not.there         does not exist
 
1527
CONTEXT:  PL/pgSQL function tg_slotlink_a() line 17 at assignment
 
1528
update PSlot set slotlink = 'XX.illegal' where slotname = 'PS.base.a1';
 
1529
ERROR:  illegal slotlink beginning with XX
 
1530
CONTEXT:  PL/pgSQL function tg_slotlink_a() line 17 at assignment
 
1531
insert into HSlot values ('HS', 'base.hub1', 1, '');
 
1532
ERROR:  duplicate key value violates unique constraint "hslot_name"
 
1533
DETAIL:  Key (slotname)=(HS.base.hub1.1      ) already exists.
 
1534
insert into HSlot values ('HS', 'base.hub1', 20, '');
 
1535
ERROR:  no manual manipulation of HSlot
 
1536
delete from HSlot;
 
1537
ERROR:  no manual manipulation of HSlot
 
1538
insert into IFace values ('IF', 'notthere', 'eth0', '');
 
1539
ERROR:  system "notthere" does not exist
 
1540
insert into IFace values ('IF', 'orion', 'ethernet_interface_name_too_long', '');
 
1541
ERROR:  IFace slotname "IF.orion.ethernet_interface_name_too_long" too long (20 char max)
 
1542
--
 
1543
-- The following tests are unrelated to the scenario outlined above;
 
1544
-- they merely exercise specific parts of PL/pgSQL
 
1545
--
 
1546
--
 
1547
-- Test recursion, per bug report 7-Sep-01
 
1548
--
 
1549
CREATE FUNCTION recursion_test(int,int) RETURNS text AS '
 
1550
DECLARE rslt text;
 
1551
BEGIN
 
1552
    IF $1 <= 0 THEN
 
1553
        rslt = CAST($2 AS TEXT);
 
1554
    ELSE
 
1555
        rslt = CAST($1 AS TEXT) || '','' || recursion_test($1 - 1, $2);
 
1556
    END IF;
 
1557
    RETURN rslt;
 
1558
END;' LANGUAGE plpgsql;
 
1559
SELECT recursion_test(4,3);
 
1560
 recursion_test 
 
1561
----------------
 
1562
 4,3,2,1,3
 
1563
(1 row)
 
1564
 
 
1565
--
 
1566
-- Test the FOUND magic variable
 
1567
--
 
1568
CREATE TABLE found_test_tbl (a int);
 
1569
create function test_found()
 
1570
  returns boolean as '
 
1571
  declare
 
1572
  begin
 
1573
  insert into found_test_tbl values (1);
 
1574
  if FOUND then
 
1575
     insert into found_test_tbl values (2);
 
1576
  end if;
 
1577
 
 
1578
  update found_test_tbl set a = 100 where a = 1;
 
1579
  if FOUND then
 
1580
    insert into found_test_tbl values (3);
 
1581
  end if;
 
1582
 
 
1583
  delete from found_test_tbl where a = 9999; -- matches no rows
 
1584
  if not FOUND then
 
1585
    insert into found_test_tbl values (4);
 
1586
  end if;
 
1587
 
 
1588
  for i in 1 .. 10 loop
 
1589
    -- no need to do anything
 
1590
  end loop;
 
1591
  if FOUND then
 
1592
    insert into found_test_tbl values (5);
 
1593
  end if;
 
1594
 
 
1595
  -- never executes the loop
 
1596
  for i in 2 .. 1 loop
 
1597
    -- no need to do anything
 
1598
  end loop;
 
1599
  if not FOUND then
 
1600
    insert into found_test_tbl values (6);
 
1601
  end if;
 
1602
  return true;
 
1603
  end;' language plpgsql;
 
1604
select test_found();
 
1605
 test_found 
 
1606
------------
 
1607
 t
 
1608
(1 row)
 
1609
 
 
1610
select * from found_test_tbl;
 
1611
  a  
 
1612
-----
 
1613
   2
 
1614
 100
 
1615
   3
 
1616
   4
 
1617
   5
 
1618
   6
 
1619
(6 rows)
 
1620
 
 
1621
--
 
1622
-- Test set-returning functions for PL/pgSQL
 
1623
--
 
1624
create function test_table_func_rec() returns setof found_test_tbl as '
 
1625
DECLARE
 
1626
        rec RECORD;
 
1627
BEGIN
 
1628
        FOR rec IN select * from found_test_tbl LOOP
 
1629
                RETURN NEXT rec;
 
1630
        END LOOP;
 
1631
        RETURN;
 
1632
END;' language plpgsql;
 
1633
select * from test_table_func_rec();
 
1634
  a  
 
1635
-----
 
1636
   2
 
1637
 100
 
1638
   3
 
1639
   4
 
1640
   5
 
1641
   6
 
1642
(6 rows)
 
1643
 
 
1644
create function test_table_func_row() returns setof found_test_tbl as '
 
1645
DECLARE
 
1646
        row found_test_tbl%ROWTYPE;
 
1647
BEGIN
 
1648
        FOR row IN select * from found_test_tbl LOOP
 
1649
                RETURN NEXT row;
 
1650
        END LOOP;
 
1651
        RETURN;
 
1652
END;' language plpgsql;
 
1653
select * from test_table_func_row();
 
1654
  a  
 
1655
-----
 
1656
   2
 
1657
 100
 
1658
   3
 
1659
   4
 
1660
   5
 
1661
   6
 
1662
(6 rows)
 
1663
 
 
1664
create function test_ret_set_scalar(int,int) returns setof int as '
 
1665
DECLARE
 
1666
        i int;
 
1667
BEGIN
 
1668
        FOR i IN $1 .. $2 LOOP
 
1669
                RETURN NEXT i + 1;
 
1670
        END LOOP;
 
1671
        RETURN;
 
1672
END;' language plpgsql;
 
1673
select * from test_ret_set_scalar(1,10);
 
1674
 test_ret_set_scalar 
 
1675
---------------------
 
1676
                   2
 
1677
                   3
 
1678
                   4
 
1679
                   5
 
1680
                   6
 
1681
                   7
 
1682
                   8
 
1683
                   9
 
1684
                  10
 
1685
                  11
 
1686
(10 rows)
 
1687
 
 
1688
create function test_ret_set_rec_dyn(int) returns setof record as '
 
1689
DECLARE
 
1690
        retval RECORD;
 
1691
BEGIN
 
1692
        IF $1 > 10 THEN
 
1693
                SELECT INTO retval 5, 10, 15;
 
1694
                RETURN NEXT retval;
 
1695
                RETURN NEXT retval;
 
1696
        ELSE
 
1697
                SELECT INTO retval 50, 5::numeric, ''xxx''::text;
 
1698
                RETURN NEXT retval;
 
1699
                RETURN NEXT retval;
 
1700
        END IF;
 
1701
        RETURN;
 
1702
END;' language plpgsql;
 
1703
SELECT * FROM test_ret_set_rec_dyn(1500) AS (a int, b int, c int);
 
1704
 a | b  | c  
 
1705
---+----+----
 
1706
 5 | 10 | 15
 
1707
 5 | 10 | 15
 
1708
(2 rows)
 
1709
 
 
1710
SELECT * FROM test_ret_set_rec_dyn(5) AS (a int, b numeric, c text);
 
1711
 a  | b |  c  
 
1712
----+---+-----
 
1713
 50 | 5 | xxx
 
1714
 50 | 5 | xxx
 
1715
(2 rows)
 
1716
 
 
1717
create function test_ret_rec_dyn(int) returns record as '
 
1718
DECLARE
 
1719
        retval RECORD;
 
1720
BEGIN
 
1721
        IF $1 > 10 THEN
 
1722
                SELECT INTO retval 5, 10, 15;
 
1723
                RETURN retval;
 
1724
        ELSE
 
1725
                SELECT INTO retval 50, 5::numeric, ''xxx''::text;
 
1726
                RETURN retval;
 
1727
        END IF;
 
1728
END;' language plpgsql;
 
1729
SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int);
 
1730
 a | b  | c  
 
1731
---+----+----
 
1732
 5 | 10 | 15
 
1733
(1 row)
 
1734
 
 
1735
SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text);
 
1736
 a  | b |  c  
 
1737
----+---+-----
 
1738
 50 | 5 | xxx
 
1739
(1 row)
 
1740
 
 
1741
--
 
1742
-- Test handling of OUT parameters, including polymorphic cases.
 
1743
-- Note that RETURN is optional with OUT params; we try both ways.
 
1744
--
 
1745
-- wrong way to do it:
 
1746
create function f1(in i int, out j int) returns int as $$
 
1747
begin
 
1748
  return i+1;
 
1749
end$$ language plpgsql;
 
1750
ERROR:  RETURN cannot have a parameter in function with OUT parameters
 
1751
LINE 3:   return i+1;
 
1752
                 ^
 
1753
create function f1(in i int, out j int) as $$
 
1754
begin
 
1755
  j := i+1;
 
1756
  return;
 
1757
end$$ language plpgsql;
 
1758
select f1(42);
 
1759
 f1 
 
1760
----
 
1761
 43
 
1762
(1 row)
 
1763
 
 
1764
select * from f1(42);
 
1765
 j  
 
1766
----
 
1767
 43
 
1768
(1 row)
 
1769
 
 
1770
create or replace function f1(inout i int) as $$
 
1771
begin
 
1772
  i := i+1;
 
1773
end$$ language plpgsql;
 
1774
select f1(42);
 
1775
 f1 
 
1776
----
 
1777
 43
 
1778
(1 row)
 
1779
 
 
1780
select * from f1(42);
 
1781
 i  
 
1782
----
 
1783
 43
 
1784
(1 row)
 
1785
 
 
1786
drop function f1(int);
 
1787
create function f1(in i int, out j int) returns setof int as $$
 
1788
begin
 
1789
  j := i+1;
 
1790
  return next;
 
1791
  j := i+2;
 
1792
  return next;
 
1793
  return;
 
1794
end$$ language plpgsql;
 
1795
select * from f1(42);
 
1796
 j  
 
1797
----
 
1798
 43
 
1799
 44
 
1800
(2 rows)
 
1801
 
 
1802
drop function f1(int);
 
1803
create function f1(in i int, out j int, out k text) as $$
 
1804
begin
 
1805
  j := i;
 
1806
  j := j+1;
 
1807
  k := 'foo';
 
1808
end$$ language plpgsql;
 
1809
select f1(42);
 
1810
    f1    
 
1811
----------
 
1812
 (43,foo)
 
1813
(1 row)
 
1814
 
 
1815
select * from f1(42);
 
1816
 j  |  k  
 
1817
----+-----
 
1818
 43 | foo
 
1819
(1 row)
 
1820
 
 
1821
drop function f1(int);
 
1822
create function f1(in i int, out j int, out k text) returns setof record as $$
 
1823
begin
 
1824
  j := i+1;
 
1825
  k := 'foo';
 
1826
  return next;
 
1827
  j := j+1;
 
1828
  k := 'foot';
 
1829
  return next;
 
1830
end$$ language plpgsql;
 
1831
select * from f1(42);
 
1832
 j  |  k   
 
1833
----+------
 
1834
 43 | foo
 
1835
 44 | foot
 
1836
(2 rows)
 
1837
 
 
1838
drop function f1(int);
 
1839
create function duplic(in i anyelement, out j anyelement, out k anyarray) as $$
 
1840
begin
 
1841
  j := i;
 
1842
  k := array[j,j];
 
1843
  return;
 
1844
end$$ language plpgsql;
 
1845
select * from duplic(42);
 
1846
 j  |    k    
 
1847
----+---------
 
1848
 42 | {42,42}
 
1849
(1 row)
 
1850
 
 
1851
select * from duplic('foo'::text);
 
1852
  j  |     k     
 
1853
-----+-----------
 
1854
 foo | {foo,foo}
 
1855
(1 row)
 
1856
 
 
1857
drop function duplic(anyelement);
 
1858
--
 
1859
-- test PERFORM
 
1860
--
 
1861
create table perform_test (
 
1862
        a       INT,
 
1863
        b       INT
 
1864
);
 
1865
create function simple_func(int) returns boolean as '
 
1866
BEGIN
 
1867
        IF $1 < 20 THEN
 
1868
                INSERT INTO perform_test VALUES ($1, $1 + 10);
 
1869
                RETURN TRUE;
 
1870
        ELSE
 
1871
                RETURN FALSE;
 
1872
        END IF;
 
1873
END;' language plpgsql;
 
1874
create function perform_test_func() returns void as '
 
1875
BEGIN
 
1876
        IF FOUND then
 
1877
                INSERT INTO perform_test VALUES (100, 100);
 
1878
        END IF;
 
1879
 
 
1880
        PERFORM simple_func(5);
 
1881
 
 
1882
        IF FOUND then
 
1883
                INSERT INTO perform_test VALUES (100, 100);
 
1884
        END IF;
 
1885
 
 
1886
        PERFORM simple_func(50);
 
1887
 
 
1888
        IF FOUND then
 
1889
                INSERT INTO perform_test VALUES (100, 100);
 
1890
        END IF;
 
1891
 
 
1892
        RETURN;
 
1893
END;' language plpgsql;
 
1894
SELECT perform_test_func();
 
1895
 perform_test_func 
 
1896
-------------------
 
1897
 
 
1898
(1 row)
 
1899
 
 
1900
SELECT * FROM perform_test;
 
1901
  a  |  b  
 
1902
-----+-----
 
1903
   5 |  15
 
1904
 100 | 100
 
1905
 100 | 100
 
1906
(3 rows)
 
1907
 
 
1908
drop table perform_test;
 
1909
--
 
1910
-- Test error trapping
 
1911
--
 
1912
create function trap_zero_divide(int) returns int as $$
 
1913
declare x int;
 
1914
        sx smallint;
 
1915
begin
 
1916
        begin   -- start a subtransaction
 
1917
                raise notice 'should see this';
 
1918
                x := 100 / $1;
 
1919
                raise notice 'should see this only if % <> 0', $1;
 
1920
                sx := $1;
 
1921
                raise notice 'should see this only if % fits in smallint', $1;
 
1922
                if $1 < 0 then
 
1923
                        raise exception '% is less than zero', $1;
 
1924
                end if;
 
1925
        exception
 
1926
                when division_by_zero then
 
1927
                        raise notice 'caught division_by_zero';
 
1928
                        x := -1;
 
1929
                when NUMERIC_VALUE_OUT_OF_RANGE then
 
1930
                        raise notice 'caught numeric_value_out_of_range';
 
1931
                        x := -2;
 
1932
        end;
 
1933
        return x;
 
1934
end$$ language plpgsql;
 
1935
select trap_zero_divide(50);
 
1936
NOTICE:  should see this
 
1937
NOTICE:  should see this only if 50 <> 0
 
1938
NOTICE:  should see this only if 50 fits in smallint
 
1939
 trap_zero_divide 
 
1940
------------------
 
1941
                2
 
1942
(1 row)
 
1943
 
 
1944
select trap_zero_divide(0);
 
1945
NOTICE:  should see this
 
1946
NOTICE:  caught division_by_zero
 
1947
 trap_zero_divide 
 
1948
------------------
 
1949
               -1
 
1950
(1 row)
 
1951
 
 
1952
select trap_zero_divide(100000);
 
1953
NOTICE:  should see this
 
1954
NOTICE:  should see this only if 100000 <> 0
 
1955
NOTICE:  caught numeric_value_out_of_range
 
1956
 trap_zero_divide 
 
1957
------------------
 
1958
               -2
 
1959
(1 row)
 
1960
 
 
1961
select trap_zero_divide(-100);
 
1962
NOTICE:  should see this
 
1963
NOTICE:  should see this only if -100 <> 0
 
1964
NOTICE:  should see this only if -100 fits in smallint
 
1965
ERROR:  -100 is less than zero
 
1966
create function trap_matching_test(int) returns int as $$
 
1967
declare x int;
 
1968
        sx smallint;
 
1969
        y int;
 
1970
begin
 
1971
        begin   -- start a subtransaction
 
1972
                x := 100 / $1;
 
1973
                sx := $1;
 
1974
                select into y unique1 from tenk1 where unique2 =
 
1975
                        (select unique2 from tenk1 b where ten = $1);
 
1976
        exception
 
1977
                when data_exception then  -- category match
 
1978
                        raise notice 'caught data_exception';
 
1979
                        x := -1;
 
1980
                when NUMERIC_VALUE_OUT_OF_RANGE OR CARDINALITY_VIOLATION then
 
1981
                        raise notice 'caught numeric_value_out_of_range or cardinality_violation';
 
1982
                        x := -2;
 
1983
        end;
 
1984
        return x;
 
1985
end$$ language plpgsql;
 
1986
select trap_matching_test(50);
 
1987
 trap_matching_test 
 
1988
--------------------
 
1989
                  2
 
1990
(1 row)
 
1991
 
 
1992
select trap_matching_test(0);
 
1993
NOTICE:  caught data_exception
 
1994
 trap_matching_test 
 
1995
--------------------
 
1996
                 -1
 
1997
(1 row)
 
1998
 
 
1999
select trap_matching_test(100000);
 
2000
NOTICE:  caught data_exception
 
2001
 trap_matching_test 
 
2002
--------------------
 
2003
                 -1
 
2004
(1 row)
 
2005
 
 
2006
select trap_matching_test(1);
 
2007
NOTICE:  caught numeric_value_out_of_range or cardinality_violation
 
2008
 trap_matching_test 
 
2009
--------------------
 
2010
                 -2
 
2011
(1 row)
 
2012
 
 
2013
create temp table foo (f1 int);
 
2014
create function blockme() returns int as $$
 
2015
declare x int;
 
2016
begin
 
2017
  x := 1;
 
2018
  insert into foo values(x);
 
2019
  begin
 
2020
    x := x + 1;
 
2021
    insert into foo values(x);
 
2022
    -- we assume this will take longer than 2 seconds:
 
2023
    select count(*) into x from tenk1 a, tenk1 b, tenk1 c;
 
2024
  exception
 
2025
    when others then
 
2026
      raise notice 'caught others?';
 
2027
      return -1;
 
2028
    when query_canceled then
 
2029
      raise notice 'nyeah nyeah, can''t stop me';
 
2030
      x := x * 10;
 
2031
  end;
 
2032
  insert into foo values(x);
 
2033
  return x;
 
2034
end$$ language plpgsql;
 
2035
set statement_timeout to 2000;
 
2036
select blockme();
 
2037
NOTICE:  nyeah nyeah, can't stop me
 
2038
 blockme 
 
2039
---------
 
2040
      20
 
2041
(1 row)
 
2042
 
 
2043
reset statement_timeout;
 
2044
select * from foo;
 
2045
 f1 
 
2046
----
 
2047
  1
 
2048
 20
 
2049
(2 rows)
 
2050
 
 
2051
drop table foo;
 
2052
-- Test for pass-by-ref values being stored in proper context
 
2053
create function test_variable_storage() returns text as $$
 
2054
declare x text;
 
2055
begin
 
2056
  x := '1234';
 
2057
  begin
 
2058
    x := x || '5678';
 
2059
    -- force error inside subtransaction SPI context
 
2060
    perform trap_zero_divide(-100);
 
2061
  exception
 
2062
    when others then
 
2063
      x := x || '9012';
 
2064
  end;
 
2065
  return x;
 
2066
end$$ language plpgsql;
 
2067
select test_variable_storage();
 
2068
NOTICE:  should see this
 
2069
CONTEXT:  SQL statement "SELECT trap_zero_divide(-100)"
 
2070
PL/pgSQL function test_variable_storage() line 8 at PERFORM
 
2071
NOTICE:  should see this only if -100 <> 0
 
2072
CONTEXT:  SQL statement "SELECT trap_zero_divide(-100)"
 
2073
PL/pgSQL function test_variable_storage() line 8 at PERFORM
 
2074
NOTICE:  should see this only if -100 fits in smallint
 
2075
CONTEXT:  SQL statement "SELECT trap_zero_divide(-100)"
 
2076
PL/pgSQL function test_variable_storage() line 8 at PERFORM
 
2077
 test_variable_storage 
 
2078
-----------------------
 
2079
 123456789012
 
2080
(1 row)
 
2081
 
 
2082
--
 
2083
-- test foreign key error trapping
 
2084
--
 
2085
create temp table master(f1 int primary key);
 
2086
create temp table slave(f1 int references master deferrable);
 
2087
insert into master values(1);
 
2088
insert into slave values(1);
 
2089
insert into slave values(2);    -- fails
 
2090
ERROR:  insert or update on table "slave" violates foreign key constraint "slave_f1_fkey"
 
2091
DETAIL:  Key (f1)=(2) is not present in table "master".
 
2092
create function trap_foreign_key(int) returns int as $$
 
2093
begin
 
2094
        begin   -- start a subtransaction
 
2095
                insert into slave values($1);
 
2096
        exception
 
2097
                when foreign_key_violation then
 
2098
                        raise notice 'caught foreign_key_violation';
 
2099
                        return 0;
 
2100
        end;
 
2101
        return 1;
 
2102
end$$ language plpgsql;
 
2103
create function trap_foreign_key_2() returns int as $$
 
2104
begin
 
2105
        begin   -- start a subtransaction
 
2106
                set constraints all immediate;
 
2107
        exception
 
2108
                when foreign_key_violation then
 
2109
                        raise notice 'caught foreign_key_violation';
 
2110
                        return 0;
 
2111
        end;
 
2112
        return 1;
 
2113
end$$ language plpgsql;
 
2114
select trap_foreign_key(1);
 
2115
 trap_foreign_key 
 
2116
------------------
 
2117
                1
 
2118
(1 row)
 
2119
 
 
2120
select trap_foreign_key(2);     -- detects FK violation
 
2121
NOTICE:  caught foreign_key_violation
 
2122
 trap_foreign_key 
 
2123
------------------
 
2124
                0
 
2125
(1 row)
 
2126
 
 
2127
begin;
 
2128
  set constraints all deferred;
 
2129
  select trap_foreign_key(2);   -- should not detect FK violation
 
2130
 trap_foreign_key 
 
2131
------------------
 
2132
                1
 
2133
(1 row)
 
2134
 
 
2135
  savepoint x;
 
2136
    set constraints all immediate; -- fails
 
2137
ERROR:  insert or update on table "slave" violates foreign key constraint "slave_f1_fkey"
 
2138
DETAIL:  Key (f1)=(2) is not present in table "master".
 
2139
  rollback to x;
 
2140
  select trap_foreign_key_2();  -- detects FK violation
 
2141
NOTICE:  caught foreign_key_violation
 
2142
 trap_foreign_key_2 
 
2143
--------------------
 
2144
                  0
 
2145
(1 row)
 
2146
 
 
2147
commit;                         -- still fails
 
2148
ERROR:  insert or update on table "slave" violates foreign key constraint "slave_f1_fkey"
 
2149
DETAIL:  Key (f1)=(2) is not present in table "master".
 
2150
drop function trap_foreign_key(int);
 
2151
drop function trap_foreign_key_2();
 
2152
--
 
2153
-- Test proper snapshot handling in simple expressions
 
2154
--
 
2155
create temp table users(login text, id serial);
 
2156
create function sp_id_user(a_login text) returns int as $$
 
2157
declare x int;
 
2158
begin
 
2159
  select into x id from users where login = a_login;
 
2160
  if found then return x; end if;
 
2161
  return 0;
 
2162
end$$ language plpgsql stable;
 
2163
insert into users values('user1');
 
2164
select sp_id_user('user1');
 
2165
 sp_id_user 
 
2166
------------
 
2167
          1
 
2168
(1 row)
 
2169
 
 
2170
select sp_id_user('userx');
 
2171
 sp_id_user 
 
2172
------------
 
2173
          0
 
2174
(1 row)
 
2175
 
 
2176
create function sp_add_user(a_login text) returns int as $$
 
2177
declare my_id_user int;
 
2178
begin
 
2179
  my_id_user = sp_id_user( a_login );
 
2180
  IF  my_id_user > 0 THEN
 
2181
    RETURN -1;  -- error code for existing user
 
2182
  END IF;
 
2183
  INSERT INTO users ( login ) VALUES ( a_login );
 
2184
  my_id_user = sp_id_user( a_login );
 
2185
  IF  my_id_user = 0 THEN
 
2186
    RETURN -2;  -- error code for insertion failure
 
2187
  END IF;
 
2188
  RETURN my_id_user;
 
2189
end$$ language plpgsql;
 
2190
select sp_add_user('user1');
 
2191
 sp_add_user 
 
2192
-------------
 
2193
          -1
 
2194
(1 row)
 
2195
 
 
2196
select sp_add_user('user2');
 
2197
 sp_add_user 
 
2198
-------------
 
2199
           2
 
2200
(1 row)
 
2201
 
 
2202
select sp_add_user('user2');
 
2203
 sp_add_user 
 
2204
-------------
 
2205
          -1
 
2206
(1 row)
 
2207
 
 
2208
select sp_add_user('user3');
 
2209
 sp_add_user 
 
2210
-------------
 
2211
           3
 
2212
(1 row)
 
2213
 
 
2214
select sp_add_user('user3');
 
2215
 sp_add_user 
 
2216
-------------
 
2217
          -1
 
2218
(1 row)
 
2219
 
 
2220
drop function sp_add_user(text);
 
2221
drop function sp_id_user(text);
 
2222
--
 
2223
-- tests for refcursors
 
2224
--
 
2225
create table rc_test (a int, b int);
 
2226
copy rc_test from stdin;
 
2227
create function return_refcursor(rc refcursor) returns refcursor as $$
 
2228
begin
 
2229
    open rc for select a from rc_test;
 
2230
    return rc;
 
2231
end
 
2232
$$ language plpgsql;
 
2233
create function refcursor_test1(refcursor) returns refcursor as $$
 
2234
begin
 
2235
    perform return_refcursor($1);
 
2236
    return $1;
 
2237
end
 
2238
$$ language plpgsql;
 
2239
begin;
 
2240
select refcursor_test1('test1');
 
2241
 refcursor_test1 
 
2242
-----------------
 
2243
 test1
 
2244
(1 row)
 
2245
 
 
2246
fetch next in test1;
 
2247
 a 
 
2248
---
 
2249
 5
 
2250
(1 row)
 
2251
 
 
2252
select refcursor_test1('test2');
 
2253
 refcursor_test1 
 
2254
-----------------
 
2255
 test2
 
2256
(1 row)
 
2257
 
 
2258
fetch all from test2;
 
2259
  a  
 
2260
-----
 
2261
   5
 
2262
  50
 
2263
 500
 
2264
(3 rows)
 
2265
 
 
2266
commit;
 
2267
-- should fail
 
2268
fetch next from test1;
 
2269
ERROR:  cursor "test1" does not exist
 
2270
create function refcursor_test2(int, int) returns boolean as $$
 
2271
declare
 
2272
    c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
 
2273
    nonsense record;
 
2274
begin
 
2275
    open c1($1, $2);
 
2276
    fetch c1 into nonsense;
 
2277
    close c1;
 
2278
    if found then
 
2279
        return true;
 
2280
    else
 
2281
        return false;
 
2282
    end if;
 
2283
end
 
2284
$$ language plpgsql;
 
2285
select refcursor_test2(20000, 20000) as "Should be false",
 
2286
       refcursor_test2(20, 20) as "Should be true";
 
2287
 Should be false | Should be true 
 
2288
-----------------+----------------
 
2289
 f               | t
 
2290
(1 row)
 
2291
 
 
2292
--
 
2293
-- tests for cursors with named parameter arguments
 
2294
--
 
2295
create function namedparmcursor_test1(int, int) returns boolean as $$
 
2296
declare
 
2297
    c1 cursor (param1 int, param12 int) for select * from rc_test where a > param1 and b > param12;
 
2298
    nonsense record;
 
2299
begin
 
2300
    open c1(param12 := $2, param1 := $1);
 
2301
    fetch c1 into nonsense;
 
2302
    close c1;
 
2303
    if found then
 
2304
        return true;
 
2305
    else
 
2306
        return false;
 
2307
    end if;
 
2308
end
 
2309
$$ language plpgsql;
 
2310
select namedparmcursor_test1(20000, 20000) as "Should be false",
 
2311
       namedparmcursor_test1(20, 20) as "Should be true";
 
2312
 Should be false | Should be true 
 
2313
-----------------+----------------
 
2314
 f               | t
 
2315
(1 row)
 
2316
 
 
2317
-- mixing named and positional argument notations
 
2318
create function namedparmcursor_test2(int, int) returns boolean as $$
 
2319
declare
 
2320
    c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
 
2321
    nonsense record;
 
2322
begin
 
2323
    open c1(param1 := $1, $2);
 
2324
    fetch c1 into nonsense;
 
2325
    close c1;
 
2326
    if found then
 
2327
        return true;
 
2328
    else
 
2329
        return false;
 
2330
    end if;
 
2331
end
 
2332
$$ language plpgsql;
 
2333
select namedparmcursor_test2(20, 20);
 
2334
 namedparmcursor_test2 
 
2335
-----------------------
 
2336
 t
 
2337
(1 row)
 
2338
 
 
2339
-- mixing named and positional: param2 is given twice, once in named notation
 
2340
-- and second time in positional notation. Should throw an error at parse time
 
2341
create function namedparmcursor_test3() returns void as $$
 
2342
declare
 
2343
    c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
 
2344
begin
 
2345
    open c1(param2 := 20, 21);
 
2346
end
 
2347
$$ language plpgsql;
 
2348
ERROR:  value for parameter "param2" of cursor "c1" specified more than once
 
2349
LINE 5:     open c1(param2 := 20, 21);
 
2350
                                  ^
 
2351
-- mixing named and positional: same as previous test, but param1 is duplicated
 
2352
create function namedparmcursor_test4() returns void as $$
 
2353
declare
 
2354
    c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
 
2355
begin
 
2356
    open c1(20, param1 := 21);
 
2357
end
 
2358
$$ language plpgsql;
 
2359
ERROR:  value for parameter "param1" of cursor "c1" specified more than once
 
2360
LINE 5:     open c1(20, param1 := 21);
 
2361
                        ^
 
2362
-- duplicate named parameter, should throw an error at parse time
 
2363
create function namedparmcursor_test5() returns void as $$
 
2364
declare
 
2365
  c1 cursor (p1 int, p2 int) for
 
2366
    select * from tenk1 where thousand = p1 and tenthous = p2;
 
2367
begin
 
2368
  open c1 (p2 := 77, p2 := 42);
 
2369
end
 
2370
$$ language plpgsql;
 
2371
ERROR:  value for parameter "p2" of cursor "c1" specified more than once
 
2372
LINE 6:   open c1 (p2 := 77, p2 := 42);
 
2373
                             ^
 
2374
-- not enough parameters, should throw an error at parse time
 
2375
create function namedparmcursor_test6() returns void as $$
 
2376
declare
 
2377
  c1 cursor (p1 int, p2 int) for
 
2378
    select * from tenk1 where thousand = p1 and tenthous = p2;
 
2379
begin
 
2380
  open c1 (p2 := 77);
 
2381
end
 
2382
$$ language plpgsql;
 
2383
ERROR:  not enough arguments for cursor "c1"
 
2384
LINE 6:   open c1 (p2 := 77);
 
2385
                           ^
 
2386
-- division by zero runtime error, the context given in the error message
 
2387
-- should be sensible
 
2388
create function namedparmcursor_test7() returns void as $$
 
2389
declare
 
2390
  c1 cursor (p1 int, p2 int) for
 
2391
    select * from tenk1 where thousand = p1 and tenthous = p2;
 
2392
begin
 
2393
  open c1 (p2 := 77, p1 := 42/0);
 
2394
end $$ language plpgsql;
 
2395
select namedparmcursor_test7();
 
2396
ERROR:  division by zero
 
2397
CONTEXT:  SQL statement "SELECT 42/0 AS p1, 77 AS p2;"
 
2398
PL/pgSQL function namedparmcursor_test7() line 6 at OPEN
 
2399
-- check that line comments work correctly within the argument list (there
 
2400
-- is some special handling of this case in the code: the newline after the
 
2401
-- comment must be preserved when the argument-evaluating query is
 
2402
-- constructed, otherwise the comment effectively comments out the next
 
2403
-- argument, too)
 
2404
create function namedparmcursor_test8() returns int4 as $$
 
2405
declare
 
2406
  c1 cursor (p1 int, p2 int) for
 
2407
    select count(*) from tenk1 where thousand = p1 and tenthous = p2;
 
2408
  n int4;
 
2409
begin
 
2410
  open c1 (77 -- test
 
2411
  , 42);
 
2412
  fetch c1 into n;
 
2413
  return n;
 
2414
end $$ language plpgsql;
 
2415
select namedparmcursor_test8();
 
2416
 namedparmcursor_test8 
 
2417
-----------------------
 
2418
                     0
 
2419
(1 row)
 
2420
 
 
2421
-- cursor parameter name can match plpgsql variable or unreserved keyword
 
2422
create function namedparmcursor_test9(p1 int) returns int4 as $$
 
2423
declare
 
2424
  c1 cursor (p1 int, p2 int, debug int) for
 
2425
    select count(*) from tenk1 where thousand = p1 and tenthous = p2
 
2426
      and four = debug;
 
2427
  p2 int4 := 1006;
 
2428
  n int4;
 
2429
begin
 
2430
  open c1 (p1 := p1, p2 := p2, debug := 2);
 
2431
  fetch c1 into n;
 
2432
  return n;
 
2433
end $$ language plpgsql;
 
2434
select namedparmcursor_test9(6);
 
2435
 namedparmcursor_test9 
 
2436
-----------------------
 
2437
                     1
 
2438
(1 row)
 
2439
 
 
2440
--
 
2441
-- tests for "raise" processing
 
2442
--
 
2443
create function raise_test1(int) returns int as $$
 
2444
begin
 
2445
    raise notice 'This message has too many parameters!', $1;
 
2446
    return $1;
 
2447
end;
 
2448
$$ language plpgsql;
 
2449
select raise_test1(5);
 
2450
ERROR:  too many parameters specified for RAISE
 
2451
CONTEXT:  PL/pgSQL function raise_test1(integer) line 3 at RAISE
 
2452
create function raise_test2(int) returns int as $$
 
2453
begin
 
2454
    raise notice 'This message has too few parameters: %, %, %', $1, $1;
 
2455
    return $1;
 
2456
end;
 
2457
$$ language plpgsql;
 
2458
select raise_test2(10);
 
2459
ERROR:  too few parameters specified for RAISE
 
2460
CONTEXT:  PL/pgSQL function raise_test2(integer) line 3 at RAISE
 
2461
-- Test re-RAISE inside a nested exception block.  This case is allowed
 
2462
-- by Oracle's PL/SQL but was handled differently by PG before 9.1.
 
2463
CREATE FUNCTION reraise_test() RETURNS void AS $$
 
2464
BEGIN
 
2465
   BEGIN
 
2466
       RAISE syntax_error;
 
2467
   EXCEPTION
 
2468
       WHEN syntax_error THEN
 
2469
           BEGIN
 
2470
               raise notice 'exception % thrown in inner block, reraising', sqlerrm;
 
2471
               RAISE;
 
2472
           EXCEPTION
 
2473
               WHEN OTHERS THEN
 
2474
                   raise notice 'RIGHT - exception % caught in inner block', sqlerrm;
 
2475
           END;
 
2476
   END;
 
2477
EXCEPTION
 
2478
   WHEN OTHERS THEN
 
2479
       raise notice 'WRONG - exception % caught in outer block', sqlerrm;
 
2480
END;
 
2481
$$ LANGUAGE plpgsql;
 
2482
SELECT reraise_test();
 
2483
NOTICE:  exception syntax_error thrown in inner block, reraising
 
2484
NOTICE:  RIGHT - exception syntax_error caught in inner block
 
2485
 reraise_test 
 
2486
--------------
 
2487
 
 
2488
(1 row)
 
2489
 
 
2490
--
 
2491
-- reject function definitions that contain malformed SQL queries at
 
2492
-- compile-time, where possible
 
2493
--
 
2494
create function bad_sql1() returns int as $$
 
2495
declare a int;
 
2496
begin
 
2497
    a := 5;
 
2498
    Johnny Yuma;
 
2499
    a := 10;
 
2500
    return a;
 
2501
end$$ language plpgsql;
 
2502
ERROR:  syntax error at or near "Johnny"
 
2503
LINE 5:     Johnny Yuma;
 
2504
            ^
 
2505
create function bad_sql2() returns int as $$
 
2506
declare r record;
 
2507
begin
 
2508
    for r in select I fought the law, the law won LOOP
 
2509
        raise notice 'in loop';
 
2510
    end loop;
 
2511
    return 5;
 
2512
end;$$ language plpgsql;
 
2513
ERROR:  syntax error at or near "the"
 
2514
LINE 4:     for r in select I fought the law, the law won LOOP
 
2515
                                     ^
 
2516
-- a RETURN expression is mandatory, except for void-returning
 
2517
-- functions, where it is not allowed
 
2518
create function missing_return_expr() returns int as $$
 
2519
begin
 
2520
    return ;
 
2521
end;$$ language plpgsql;
 
2522
ERROR:  missing expression at or near ";"
 
2523
LINE 3:     return ;
 
2524
                   ^
 
2525
create function void_return_expr() returns void as $$
 
2526
begin
 
2527
    return 5;
 
2528
end;$$ language plpgsql;
 
2529
ERROR:  RETURN cannot have a parameter in function returning void
 
2530
LINE 3:     return 5;
 
2531
                   ^
 
2532
-- VOID functions are allowed to omit RETURN
 
2533
create function void_return_expr() returns void as $$
 
2534
begin
 
2535
    perform 2+2;
 
2536
end;$$ language plpgsql;
 
2537
select void_return_expr();
 
2538
 void_return_expr 
 
2539
------------------
 
2540
 
 
2541
(1 row)
 
2542
 
 
2543
-- but ordinary functions are not
 
2544
create function missing_return_expr() returns int as $$
 
2545
begin
 
2546
    perform 2+2;
 
2547
end;$$ language plpgsql;
 
2548
select missing_return_expr();
 
2549
ERROR:  control reached end of function without RETURN
 
2550
CONTEXT:  PL/pgSQL function missing_return_expr()
 
2551
drop function void_return_expr();
 
2552
drop function missing_return_expr();
 
2553
--
 
2554
-- EXECUTE ... INTO test
 
2555
--
 
2556
create table eifoo (i integer, y integer);
 
2557
create type eitype as (i integer, y integer);
 
2558
create or replace function execute_into_test(varchar) returns record as $$
 
2559
declare
 
2560
    _r record;
 
2561
    _rt eifoo%rowtype;
 
2562
    _v eitype;
 
2563
    i int;
 
2564
    j int;
 
2565
    k int;
 
2566
begin
 
2567
    execute 'insert into '||$1||' values(10,15)';
 
2568
    execute 'select (row).* from (select row(10,1)::eifoo) s' into _r;
 
2569
    raise notice '% %', _r.i, _r.y;
 
2570
    execute 'select * from '||$1||' limit 1' into _rt;
 
2571
    raise notice '% %', _rt.i, _rt.y;
 
2572
    execute 'select *, 20 from '||$1||' limit 1' into i, j, k;
 
2573
    raise notice '% % %', i, j, k;
 
2574
    execute 'select 1,2' into _v;
 
2575
    return _v;
 
2576
end; $$ language plpgsql;
 
2577
select execute_into_test('eifoo');
 
2578
NOTICE:  10 1
 
2579
NOTICE:  10 15
 
2580
NOTICE:  10 15 20
 
2581
 execute_into_test 
 
2582
-------------------
 
2583
 (1,2)
 
2584
(1 row)
 
2585
 
 
2586
drop table eifoo cascade;
 
2587
drop type eitype cascade;
 
2588
--
 
2589
-- SQLSTATE and SQLERRM test
 
2590
--
 
2591
create function excpt_test1() returns void as $$
 
2592
begin
 
2593
    raise notice '% %', sqlstate, sqlerrm;
 
2594
end; $$ language plpgsql;
 
2595
-- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION
 
2596
-- blocks
 
2597
select excpt_test1();
 
2598
ERROR:  column "sqlstate" does not exist
 
2599
LINE 1: SELECT sqlstate
 
2600
               ^
 
2601
QUERY:  SELECT sqlstate
 
2602
CONTEXT:  PL/pgSQL function excpt_test1() line 3 at RAISE
 
2603
create function excpt_test2() returns void as $$
 
2604
begin
 
2605
    begin
 
2606
        begin
 
2607
            raise notice '% %', sqlstate, sqlerrm;
 
2608
        end;
 
2609
    end;
 
2610
end; $$ language plpgsql;
 
2611
-- should fail
 
2612
select excpt_test2();
 
2613
ERROR:  column "sqlstate" does not exist
 
2614
LINE 1: SELECT sqlstate
 
2615
               ^
 
2616
QUERY:  SELECT sqlstate
 
2617
CONTEXT:  PL/pgSQL function excpt_test2() line 5 at RAISE
 
2618
create function excpt_test3() returns void as $$
 
2619
begin
 
2620
    begin
 
2621
        raise exception 'user exception';
 
2622
    exception when others then
 
2623
            raise notice 'caught exception % %', sqlstate, sqlerrm;
 
2624
            begin
 
2625
                raise notice '% %', sqlstate, sqlerrm;
 
2626
                perform 10/0;
 
2627
        exception
 
2628
            when substring_error then
 
2629
                -- this exception handler shouldn't be invoked
 
2630
                raise notice 'unexpected exception: % %', sqlstate, sqlerrm;
 
2631
                when division_by_zero then
 
2632
                    raise notice 'caught exception % %', sqlstate, sqlerrm;
 
2633
            end;
 
2634
            raise notice '% %', sqlstate, sqlerrm;
 
2635
    end;
 
2636
end; $$ language plpgsql;
 
2637
select excpt_test3();
 
2638
NOTICE:  caught exception P0001 user exception
 
2639
NOTICE:  P0001 user exception
 
2640
NOTICE:  caught exception 22012 division by zero
 
2641
NOTICE:  P0001 user exception
 
2642
 excpt_test3 
 
2643
-------------
 
2644
 
 
2645
(1 row)
 
2646
 
 
2647
drop function excpt_test1();
 
2648
drop function excpt_test2();
 
2649
drop function excpt_test3();
 
2650
-- parameters of raise stmt can be expressions
 
2651
create function raise_exprs() returns void as $$
 
2652
declare
 
2653
    a integer[] = '{10,20,30}';
 
2654
    c varchar = 'xyz';
 
2655
    i integer;
 
2656
begin
 
2657
    i := 2;
 
2658
    raise notice '%; %; %; %; %; %', a, a[i], c, (select c || 'abc'), row(10,'aaa',NULL,30), NULL;
 
2659
end;$$ language plpgsql;
 
2660
select raise_exprs();
 
2661
NOTICE:  {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); <NULL>
 
2662
 raise_exprs 
 
2663
-------------
 
2664
 
 
2665
(1 row)
 
2666
 
 
2667
drop function raise_exprs();
 
2668
-- continue statement
 
2669
create table conttesttbl(idx serial, v integer);
 
2670
insert into conttesttbl(v) values(10);
 
2671
insert into conttesttbl(v) values(20);
 
2672
insert into conttesttbl(v) values(30);
 
2673
insert into conttesttbl(v) values(40);
 
2674
create function continue_test1() returns void as $$
 
2675
declare _i integer = 0; _r record;
 
2676
begin
 
2677
  raise notice '---1---';
 
2678
  loop
 
2679
    _i := _i + 1;
 
2680
    raise notice '%', _i;
 
2681
    continue when _i < 10;
 
2682
    exit;
 
2683
  end loop;
 
2684
 
 
2685
  raise notice '---2---';
 
2686
  <<lbl>>
 
2687
  loop
 
2688
    _i := _i - 1;
 
2689
    loop
 
2690
      raise notice '%', _i;
 
2691
      continue lbl when _i > 0;
 
2692
      exit lbl;
 
2693
    end loop;
 
2694
  end loop;
 
2695
 
 
2696
  raise notice '---3---';
 
2697
  <<the_loop>>
 
2698
  while _i < 10 loop
 
2699
    _i := _i + 1;
 
2700
    continue the_loop when _i % 2 = 0;
 
2701
    raise notice '%', _i;
 
2702
  end loop;
 
2703
 
 
2704
  raise notice '---4---';
 
2705
  for _i in 1..10 loop
 
2706
    begin
 
2707
      -- applies to outer loop, not the nested begin block
 
2708
      continue when _i < 5;
 
2709
      raise notice '%', _i;
 
2710
    end;
 
2711
  end loop;
 
2712
 
 
2713
  raise notice '---5---';
 
2714
  for _r in select * from conttesttbl loop
 
2715
    continue when _r.v <= 20;
 
2716
    raise notice '%', _r.v;
 
2717
  end loop;
 
2718
 
 
2719
  raise notice '---6---';
 
2720
  for _r in execute 'select * from conttesttbl' loop
 
2721
    continue when _r.v <= 20;
 
2722
    raise notice '%', _r.v;
 
2723
  end loop;
 
2724
 
 
2725
  raise notice '---7---';
 
2726
  for _i in 1..3 loop
 
2727
    raise notice '%', _i;
 
2728
    continue when _i = 3;
 
2729
  end loop;
 
2730
 
 
2731
  raise notice '---8---';
 
2732
  _i := 1;
 
2733
  while _i <= 3 loop
 
2734
    raise notice '%', _i;
 
2735
    _i := _i + 1;
 
2736
    continue when _i = 3;
 
2737
  end loop;
 
2738
 
 
2739
  raise notice '---9---';
 
2740
  for _r in select * from conttesttbl order by v limit 1 loop
 
2741
    raise notice '%', _r.v;
 
2742
    continue;
 
2743
  end loop;
 
2744
 
 
2745
  raise notice '---10---';
 
2746
  for _r in execute 'select * from conttesttbl order by v limit 1' loop
 
2747
    raise notice '%', _r.v;
 
2748
    continue;
 
2749
  end loop;
 
2750
end; $$ language plpgsql;
 
2751
select continue_test1();
 
2752
NOTICE:  ---1---
 
2753
NOTICE:  1
 
2754
NOTICE:  2
 
2755
NOTICE:  3
 
2756
NOTICE:  4
 
2757
NOTICE:  5
 
2758
NOTICE:  6
 
2759
NOTICE:  7
 
2760
NOTICE:  8
 
2761
NOTICE:  9
 
2762
NOTICE:  10
 
2763
NOTICE:  ---2---
 
2764
NOTICE:  9
 
2765
NOTICE:  8
 
2766
NOTICE:  7
 
2767
NOTICE:  6
 
2768
NOTICE:  5
 
2769
NOTICE:  4
 
2770
NOTICE:  3
 
2771
NOTICE:  2
 
2772
NOTICE:  1
 
2773
NOTICE:  0
 
2774
NOTICE:  ---3---
 
2775
NOTICE:  1
 
2776
NOTICE:  3
 
2777
NOTICE:  5
 
2778
NOTICE:  7
 
2779
NOTICE:  9
 
2780
NOTICE:  ---4---
 
2781
NOTICE:  5
 
2782
NOTICE:  6
 
2783
NOTICE:  7
 
2784
NOTICE:  8
 
2785
NOTICE:  9
 
2786
NOTICE:  10
 
2787
NOTICE:  ---5---
 
2788
NOTICE:  30
 
2789
NOTICE:  40
 
2790
NOTICE:  ---6---
 
2791
NOTICE:  30
 
2792
NOTICE:  40
 
2793
NOTICE:  ---7---
 
2794
NOTICE:  1
 
2795
NOTICE:  2
 
2796
NOTICE:  3
 
2797
NOTICE:  ---8---
 
2798
NOTICE:  1
 
2799
NOTICE:  2
 
2800
NOTICE:  3
 
2801
NOTICE:  ---9---
 
2802
NOTICE:  10
 
2803
NOTICE:  ---10---
 
2804
NOTICE:  10
 
2805
 continue_test1 
 
2806
----------------
 
2807
 
 
2808
(1 row)
 
2809
 
 
2810
-- CONTINUE is only legal inside a loop
 
2811
create function continue_test2() returns void as $$
 
2812
begin
 
2813
    begin
 
2814
        continue;
 
2815
    end;
 
2816
    return;
 
2817
end;
 
2818
$$ language plpgsql;
 
2819
-- should fail
 
2820
select continue_test2();
 
2821
ERROR:  CONTINUE cannot be used outside a loop
 
2822
CONTEXT:  PL/pgSQL function continue_test2()
 
2823
-- CONTINUE can't reference the label of a named block
 
2824
create function continue_test3() returns void as $$
 
2825
begin
 
2826
    <<begin_block1>>
 
2827
    begin
 
2828
        loop
 
2829
            continue begin_block1;
 
2830
        end loop;
 
2831
    end;
 
2832
end;
 
2833
$$ language plpgsql;
 
2834
-- should fail
 
2835
select continue_test3();
 
2836
ERROR:  CONTINUE cannot be used outside a loop
 
2837
CONTEXT:  PL/pgSQL function continue_test3()
 
2838
drop function continue_test1();
 
2839
drop function continue_test2();
 
2840
drop function continue_test3();
 
2841
drop table conttesttbl;
 
2842
-- verbose end block and end loop
 
2843
create function end_label1() returns void as $$
 
2844
<<blbl>>
 
2845
begin
 
2846
  <<flbl1>>
 
2847
  for _i in 1 .. 10 loop
 
2848
    exit flbl1;
 
2849
  end loop flbl1;
 
2850
  <<flbl2>>
 
2851
  for _i in 1 .. 10 loop
 
2852
    exit flbl2;
 
2853
  end loop;
 
2854
end blbl;
 
2855
$$ language plpgsql;
 
2856
select end_label1();
 
2857
 end_label1 
 
2858
------------
 
2859
 
 
2860
(1 row)
 
2861
 
 
2862
drop function end_label1();
 
2863
-- should fail: undefined end label
 
2864
create function end_label2() returns void as $$
 
2865
begin
 
2866
  for _i in 1 .. 10 loop
 
2867
    exit;
 
2868
  end loop flbl1;
 
2869
end;
 
2870
$$ language plpgsql;
 
2871
ERROR:  label does not exist at or near "flbl1"
 
2872
LINE 5:   end loop flbl1;
 
2873
                   ^
 
2874
-- should fail: end label does not match start label
 
2875
create function end_label3() returns void as $$
 
2876
<<outer_label>>
 
2877
begin
 
2878
  <<inner_label>>
 
2879
  for _i in 1 .. 10 loop
 
2880
    exit;
 
2881
  end loop outer_label;
 
2882
end;
 
2883
$$ language plpgsql;
 
2884
ERROR:  end label "outer_label" differs from block's label "inner_label"
 
2885
LINE 7:   end loop outer_label;
 
2886
                   ^
 
2887
-- should fail: end label on a block without a start label
 
2888
create function end_label4() returns void as $$
 
2889
<<outer_label>>
 
2890
begin
 
2891
  for _i in 1 .. 10 loop
 
2892
    exit;
 
2893
  end loop outer_label;
 
2894
end;
 
2895
$$ language plpgsql;
 
2896
ERROR:  end label "outer_label" specified for unlabelled block
 
2897
LINE 6:   end loop outer_label;
 
2898
                   ^
 
2899
-- using list of scalars in fori and fore stmts
 
2900
create function for_vect() returns void as $proc$
 
2901
<<lbl>>declare a integer; b varchar; c varchar; r record;
 
2902
begin
 
2903
  -- fori
 
2904
  for i in 1 .. 3 loop
 
2905
    raise notice '%', i;
 
2906
  end loop;
 
2907
  -- fore with record var
 
2908
  for r in select gs as aa, 'BB' as bb, 'CC' as cc from generate_series(1,4) gs loop
 
2909
    raise notice '% % %', r.aa, r.bb, r.cc;
 
2910
  end loop;
 
2911
  -- fore with single scalar
 
2912
  for a in select gs from generate_series(1,4) gs loop
 
2913
    raise notice '%', a;
 
2914
  end loop;
 
2915
  -- fore with multiple scalars
 
2916
  for a,b,c in select gs, 'BB','CC' from generate_series(1,4) gs loop
 
2917
    raise notice '% % %', a, b, c;
 
2918
  end loop;
 
2919
  -- using qualified names in fors, fore is enabled, disabled only for fori
 
2920
  for lbl.a, lbl.b, lbl.c in execute $$select gs, 'bb','cc' from generate_series(1,4) gs$$ loop
 
2921
    raise notice '% % %', a, b, c;
 
2922
  end loop;
 
2923
end;
 
2924
$proc$ language plpgsql;
 
2925
select for_vect();
 
2926
NOTICE:  1
 
2927
NOTICE:  2
 
2928
NOTICE:  3
 
2929
NOTICE:  1 BB CC
 
2930
NOTICE:  2 BB CC
 
2931
NOTICE:  3 BB CC
 
2932
NOTICE:  4 BB CC
 
2933
NOTICE:  1
 
2934
NOTICE:  2
 
2935
NOTICE:  3
 
2936
NOTICE:  4
 
2937
NOTICE:  1 BB CC
 
2938
NOTICE:  2 BB CC
 
2939
NOTICE:  3 BB CC
 
2940
NOTICE:  4 BB CC
 
2941
NOTICE:  1 bb cc
 
2942
NOTICE:  2 bb cc
 
2943
NOTICE:  3 bb cc
 
2944
NOTICE:  4 bb cc
 
2945
 for_vect 
 
2946
----------
 
2947
 
 
2948
(1 row)
 
2949
 
 
2950
-- regression test: verify that multiple uses of same plpgsql datum within
 
2951
-- a SQL command all get mapped to the same $n parameter.  The return value
 
2952
-- of the SELECT is not important, we only care that it doesn't fail with
 
2953
-- a complaint about an ungrouped column reference.
 
2954
create function multi_datum_use(p1 int) returns bool as $$
 
2955
declare
 
2956
  x int;
 
2957
  y int;
 
2958
begin
 
2959
  select into x,y unique1/p1, unique1/$1 from tenk1 group by unique1/p1;
 
2960
  return x = y;
 
2961
end$$ language plpgsql;
 
2962
select multi_datum_use(42);
 
2963
 multi_datum_use 
 
2964
-----------------
 
2965
 t
 
2966
(1 row)
 
2967
 
 
2968
--
 
2969
-- Test STRICT limiter in both planned and EXECUTE invocations.
 
2970
-- Note that a data-modifying query is quasi strict (disallow multi rows)
 
2971
-- by default in the planned case, but not in EXECUTE.
 
2972
--
 
2973
create temp table foo (f1 int, f2 int);
 
2974
insert into foo values (1,2), (3,4);
 
2975
create or replace function footest() returns void as $$
 
2976
declare x record;
 
2977
begin
 
2978
  -- should work
 
2979
  insert into foo values(5,6) returning * into x;
 
2980
  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 
2981
end$$ language plpgsql;
 
2982
select footest();
 
2983
NOTICE:  x.f1 = 5, x.f2 = 6
 
2984
 footest 
 
2985
---------
 
2986
 
 
2987
(1 row)
 
2988
 
 
2989
create or replace function footest() returns void as $$
 
2990
declare x record;
 
2991
begin
 
2992
  -- should fail due to implicit strict
 
2993
  insert into foo values(7,8),(9,10) returning * into x;
 
2994
  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 
2995
end$$ language plpgsql;
 
2996
select footest();
 
2997
ERROR:  query returned more than one row
 
2998
CONTEXT:  PL/pgSQL function footest() line 5 at SQL statement
 
2999
create or replace function footest() returns void as $$
 
3000
declare x record;
 
3001
begin
 
3002
  -- should work
 
3003
  execute 'insert into foo values(5,6) returning *' into x;
 
3004
  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 
3005
end$$ language plpgsql;
 
3006
select footest();
 
3007
NOTICE:  x.f1 = 5, x.f2 = 6
 
3008
 footest 
 
3009
---------
 
3010
 
 
3011
(1 row)
 
3012
 
 
3013
create or replace function footest() returns void as $$
 
3014
declare x record;
 
3015
begin
 
3016
  -- this should work since EXECUTE isn't as picky
 
3017
  execute 'insert into foo values(7,8),(9,10) returning *' into x;
 
3018
  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 
3019
end$$ language plpgsql;
 
3020
select footest();
 
3021
NOTICE:  x.f1 = 7, x.f2 = 8
 
3022
 footest 
 
3023
---------
 
3024
 
 
3025
(1 row)
 
3026
 
 
3027
select * from foo;
 
3028
 f1 | f2 
 
3029
----+----
 
3030
  1 |  2
 
3031
  3 |  4
 
3032
  5 |  6
 
3033
  5 |  6
 
3034
  7 |  8
 
3035
  9 | 10
 
3036
(6 rows)
 
3037
 
 
3038
create or replace function footest() returns void as $$
 
3039
declare x record;
 
3040
begin
 
3041
  -- should work
 
3042
  select * from foo where f1 = 3 into strict x;
 
3043
  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 
3044
end$$ language plpgsql;
 
3045
select footest();
 
3046
NOTICE:  x.f1 = 3, x.f2 = 4
 
3047
 footest 
 
3048
---------
 
3049
 
 
3050
(1 row)
 
3051
 
 
3052
create or replace function footest() returns void as $$
 
3053
declare x record;
 
3054
begin
 
3055
  -- should fail, no rows
 
3056
  select * from foo where f1 = 0 into strict x;
 
3057
  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 
3058
end$$ language plpgsql;
 
3059
select footest();
 
3060
ERROR:  query returned no rows
 
3061
CONTEXT:  PL/pgSQL function footest() line 5 at SQL statement
 
3062
create or replace function footest() returns void as $$
 
3063
declare x record;
 
3064
begin
 
3065
  -- should fail, too many rows
 
3066
  select * from foo where f1 > 3 into strict x;
 
3067
  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 
3068
end$$ language plpgsql;
 
3069
select footest();
 
3070
ERROR:  query returned more than one row
 
3071
CONTEXT:  PL/pgSQL function footest() line 5 at SQL statement
 
3072
create or replace function footest() returns void as $$
 
3073
declare x record;
 
3074
begin
 
3075
  -- should work
 
3076
  execute 'select * from foo where f1 = 3' into strict x;
 
3077
  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 
3078
end$$ language plpgsql;
 
3079
select footest();
 
3080
NOTICE:  x.f1 = 3, x.f2 = 4
 
3081
 footest 
 
3082
---------
 
3083
 
 
3084
(1 row)
 
3085
 
 
3086
create or replace function footest() returns void as $$
 
3087
declare x record;
 
3088
begin
 
3089
  -- should fail, no rows
 
3090
  execute 'select * from foo where f1 = 0' into strict x;
 
3091
  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 
3092
end$$ language plpgsql;
 
3093
select footest();
 
3094
ERROR:  query returned no rows
 
3095
CONTEXT:  PL/pgSQL function footest() line 5 at EXECUTE statement
 
3096
create or replace function footest() returns void as $$
 
3097
declare x record;
 
3098
begin
 
3099
  -- should fail, too many rows
 
3100
  execute 'select * from foo where f1 > 3' into strict x;
 
3101
  raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
 
3102
end$$ language plpgsql;
 
3103
select footest();
 
3104
ERROR:  query returned more than one row
 
3105
CONTEXT:  PL/pgSQL function footest() line 5 at EXECUTE statement
 
3106
drop function footest();
 
3107
-- test scrollable cursor support
 
3108
create function sc_test() returns setof integer as $$
 
3109
declare
 
3110
  c scroll cursor for select f1 from int4_tbl;
 
3111
  x integer;
 
3112
begin
 
3113
  open c;
 
3114
  fetch last from c into x;
 
3115
  while found loop
 
3116
    return next x;
 
3117
    fetch prior from c into x;
 
3118
  end loop;
 
3119
  close c;
 
3120
end;
 
3121
$$ language plpgsql;
 
3122
select * from sc_test();
 
3123
   sc_test   
 
3124
-------------
 
3125
 -2147483647
 
3126
  2147483647
 
3127
     -123456
 
3128
      123456
 
3129
           0
 
3130
(5 rows)
 
3131
 
 
3132
create or replace function sc_test() returns setof integer as $$
 
3133
declare
 
3134
  c no scroll cursor for select f1 from int4_tbl;
 
3135
  x integer;
 
3136
begin
 
3137
  open c;
 
3138
  fetch last from c into x;
 
3139
  while found loop
 
3140
    return next x;
 
3141
    fetch prior from c into x;
 
3142
  end loop;
 
3143
  close c;
 
3144
end;
 
3145
$$ language plpgsql;
 
3146
select * from sc_test();  -- fails because of NO SCROLL specification
 
3147
ERROR:  cursor can only scan forward
 
3148
HINT:  Declare it with SCROLL option to enable backward scan.
 
3149
CONTEXT:  PL/pgSQL function sc_test() line 7 at FETCH
 
3150
create or replace function sc_test() returns setof integer as $$
 
3151
declare
 
3152
  c refcursor;
 
3153
  x integer;
 
3154
begin
 
3155
  open c scroll for select f1 from int4_tbl;
 
3156
  fetch last from c into x;
 
3157
  while found loop
 
3158
    return next x;
 
3159
    fetch prior from c into x;
 
3160
  end loop;
 
3161
  close c;
 
3162
end;
 
3163
$$ language plpgsql;
 
3164
select * from sc_test();
 
3165
   sc_test   
 
3166
-------------
 
3167
 -2147483647
 
3168
  2147483647
 
3169
     -123456
 
3170
      123456
 
3171
           0
 
3172
(5 rows)
 
3173
 
 
3174
create or replace function sc_test() returns setof integer as $$
 
3175
declare
 
3176
  c refcursor;
 
3177
  x integer;
 
3178
begin
 
3179
  open c scroll for execute 'select f1 from int4_tbl';
 
3180
  fetch last from c into x;
 
3181
  while found loop
 
3182
    return next x;
 
3183
    fetch relative -2 from c into x;
 
3184
  end loop;
 
3185
  close c;
 
3186
end;
 
3187
$$ language plpgsql;
 
3188
select * from sc_test();
 
3189
   sc_test   
 
3190
-------------
 
3191
 -2147483647
 
3192
     -123456
 
3193
           0
 
3194
(3 rows)
 
3195
 
 
3196
create or replace function sc_test() returns setof integer as $$
 
3197
declare
 
3198
  c refcursor;
 
3199
  x integer;
 
3200
begin
 
3201
  open c scroll for execute 'select f1 from int4_tbl';
 
3202
  fetch last from c into x;
 
3203
  while found loop
 
3204
    return next x;
 
3205
    move backward 2 from c;
 
3206
    fetch relative -1 from c into x;
 
3207
  end loop;
 
3208
  close c;
 
3209
end;
 
3210
$$ language plpgsql;
 
3211
select * from sc_test();
 
3212
   sc_test   
 
3213
-------------
 
3214
 -2147483647
 
3215
      123456
 
3216
(2 rows)
 
3217
 
 
3218
create or replace function sc_test() returns setof integer as $$
 
3219
declare
 
3220
  c cursor for select * from generate_series(1, 10);
 
3221
  x integer;
 
3222
begin
 
3223
  open c;
 
3224
  loop
 
3225
      move relative 2 in c;
 
3226
      if not found then
 
3227
          exit;
 
3228
      end if;
 
3229
      fetch next from c into x;
 
3230
      if found then
 
3231
          return next x;
 
3232
      end if;
 
3233
  end loop;
 
3234
  close c;
 
3235
end;
 
3236
$$ language plpgsql;
 
3237
select * from sc_test();
 
3238
 sc_test 
 
3239
---------
 
3240
       3
 
3241
       6
 
3242
       9
 
3243
(3 rows)
 
3244
 
 
3245
create or replace function sc_test() returns setof integer as $$
 
3246
declare
 
3247
  c cursor for select * from generate_series(1, 10);
 
3248
  x integer;
 
3249
begin
 
3250
  open c;
 
3251
  move forward all in c;
 
3252
  fetch backward from c into x;
 
3253
  if found then
 
3254
    return next x;
 
3255
  end if;
 
3256
  close c;
 
3257
end;
 
3258
$$ language plpgsql;
 
3259
select * from sc_test();
 
3260
 sc_test 
 
3261
---------
 
3262
      10
 
3263
(1 row)
 
3264
 
 
3265
drop function sc_test();
 
3266
-- test qualified variable names
 
3267
create function pl_qual_names (param1 int) returns void as $$
 
3268
<<outerblock>>
 
3269
declare
 
3270
  param1 int := 1;
 
3271
begin
 
3272
  <<innerblock>>
 
3273
  declare
 
3274
    param1 int := 2;
 
3275
  begin
 
3276
    raise notice 'param1 = %', param1;
 
3277
    raise notice 'pl_qual_names.param1 = %', pl_qual_names.param1;
 
3278
    raise notice 'outerblock.param1 = %', outerblock.param1;
 
3279
    raise notice 'innerblock.param1 = %', innerblock.param1;
 
3280
  end;
 
3281
end;
 
3282
$$ language plpgsql;
 
3283
select pl_qual_names(42);
 
3284
NOTICE:  param1 = 2
 
3285
NOTICE:  pl_qual_names.param1 = 42
 
3286
NOTICE:  outerblock.param1 = 1
 
3287
NOTICE:  innerblock.param1 = 2
 
3288
 pl_qual_names 
 
3289
---------------
 
3290
 
 
3291
(1 row)
 
3292
 
 
3293
drop function pl_qual_names(int);
 
3294
-- tests for RETURN QUERY
 
3295
create function ret_query1(out int, out int) returns setof record as $$
 
3296
begin
 
3297
    $1 := -1;
 
3298
    $2 := -2;
 
3299
    return next;
 
3300
    return query select x + 1, x * 10 from generate_series(0, 10) s (x);
 
3301
    return next;
 
3302
end;
 
3303
$$ language plpgsql;
 
3304
select * from ret_query1();
 
3305
 column1 | column2 
 
3306
---------+---------
 
3307
      -1 |      -2
 
3308
       1 |       0
 
3309
       2 |      10
 
3310
       3 |      20
 
3311
       4 |      30
 
3312
       5 |      40
 
3313
       6 |      50
 
3314
       7 |      60
 
3315
       8 |      70
 
3316
       9 |      80
 
3317
      10 |      90
 
3318
      11 |     100
 
3319
      -1 |      -2
 
3320
(13 rows)
 
3321
 
 
3322
create type record_type as (x text, y int, z boolean);
 
3323
create or replace function ret_query2(lim int) returns setof record_type as $$
 
3324
begin
 
3325
    return query select md5(s.x::text), s.x, s.x > 0
 
3326
                 from generate_series(-8, lim) s (x) where s.x % 2 = 0;
 
3327
end;
 
3328
$$ language plpgsql;
 
3329
select * from ret_query2(8);
 
3330
                x                 | y  | z 
 
3331
----------------------------------+----+---
 
3332
 a8d2ec85eaf98407310b72eb73dda247 | -8 | f
 
3333
 596a3d04481816330f07e4f97510c28f | -6 | f
 
3334
 0267aaf632e87a63288a08331f22c7c3 | -4 | f
 
3335
 5d7b9adcbe1c629ec722529dd12e5129 | -2 | f
 
3336
 cfcd208495d565ef66e7dff9f98764da |  0 | f
 
3337
 c81e728d9d4c2f636f067f89cc14862c |  2 | t
 
3338
 a87ff679a2f3e71d9181a67b7542122c |  4 | t
 
3339
 1679091c5a880faf6fb5e6087eb1b2dc |  6 | t
 
3340
 c9f0f895fb98ab9159f51fd0297e236d |  8 | t
 
3341
(9 rows)
 
3342
 
 
3343
-- test EXECUTE USING
 
3344
create function exc_using(int, text) returns int as $$
 
3345
declare i int;
 
3346
begin
 
3347
  for i in execute 'select * from generate_series(1,$1)' using $1+1 loop
 
3348
    raise notice '%', i;
 
3349
  end loop;
 
3350
  execute 'select $2 + $2*3 + length($1)' into i using $2,$1;
 
3351
  return i;
 
3352
end
 
3353
$$ language plpgsql;
 
3354
select exc_using(5, 'foobar');
 
3355
NOTICE:  1
 
3356
NOTICE:  2
 
3357
NOTICE:  3
 
3358
NOTICE:  4
 
3359
NOTICE:  5
 
3360
NOTICE:  6
 
3361
 exc_using 
 
3362
-----------
 
3363
        26
 
3364
(1 row)
 
3365
 
 
3366
drop function exc_using(int, text);
 
3367
create or replace function exc_using(int) returns void as $$
 
3368
declare
 
3369
  c refcursor;
 
3370
  i int;
 
3371
begin
 
3372
  open c for execute 'select * from generate_series(1,$1)' using $1+1;
 
3373
  loop
 
3374
    fetch c into i;
 
3375
    exit when not found;
 
3376
    raise notice '%', i;
 
3377
  end loop;
 
3378
  close c;
 
3379
  return;
 
3380
end;
 
3381
$$ language plpgsql;
 
3382
select exc_using(5);
 
3383
NOTICE:  1
 
3384
NOTICE:  2
 
3385
NOTICE:  3
 
3386
NOTICE:  4
 
3387
NOTICE:  5
 
3388
NOTICE:  6
 
3389
 exc_using 
 
3390
-----------
 
3391
 
 
3392
(1 row)
 
3393
 
 
3394
drop function exc_using(int);
 
3395
-- test FOR-over-cursor
 
3396
create or replace function forc01() returns void as $$
 
3397
declare
 
3398
  c cursor(r1 integer, r2 integer)
 
3399
       for select * from generate_series(r1,r2) i;
 
3400
  c2 cursor
 
3401
       for select * from generate_series(41,43) i;
 
3402
begin
 
3403
  for r in c(5,7) loop
 
3404
    raise notice '% from %', r.i, c;
 
3405
  end loop;
 
3406
  -- again, to test if cursor was closed properly
 
3407
  for r in c(9,10) loop
 
3408
    raise notice '% from %', r.i, c;
 
3409
  end loop;
 
3410
  -- and test a parameterless cursor
 
3411
  for r in c2 loop
 
3412
    raise notice '% from %', r.i, c2;
 
3413
  end loop;
 
3414
  -- and try it with a hand-assigned name
 
3415
  raise notice 'after loop, c2 = %', c2;
 
3416
  c2 := 'special_name';
 
3417
  for r in c2 loop
 
3418
    raise notice '% from %', r.i, c2;
 
3419
  end loop;
 
3420
  raise notice 'after loop, c2 = %', c2;
 
3421
  -- and try it with a generated name
 
3422
  -- (which we can't show in the output because it's variable)
 
3423
  c2 := null;
 
3424
  for r in c2 loop
 
3425
    raise notice '%', r.i;
 
3426
  end loop;
 
3427
  raise notice 'after loop, c2 = %', c2;
 
3428
  return;
 
3429
end;
 
3430
$$ language plpgsql;
 
3431
select forc01();
 
3432
NOTICE:  5 from c
 
3433
NOTICE:  6 from c
 
3434
NOTICE:  7 from c
 
3435
NOTICE:  9 from c
 
3436
NOTICE:  10 from c
 
3437
NOTICE:  41 from c2
 
3438
NOTICE:  42 from c2
 
3439
NOTICE:  43 from c2
 
3440
NOTICE:  after loop, c2 = c2
 
3441
NOTICE:  41 from special_name
 
3442
NOTICE:  42 from special_name
 
3443
NOTICE:  43 from special_name
 
3444
NOTICE:  after loop, c2 = special_name
 
3445
NOTICE:  41
 
3446
NOTICE:  42
 
3447
NOTICE:  43
 
3448
NOTICE:  after loop, c2 = <NULL>
 
3449
 forc01 
 
3450
--------
 
3451
 
 
3452
(1 row)
 
3453
 
 
3454
-- try updating the cursor's current row
 
3455
create temp table forc_test as
 
3456
  select n as i, n as j from generate_series(1,10) n;
 
3457
create or replace function forc01() returns void as $$
 
3458
declare
 
3459
  c cursor for select * from forc_test;
 
3460
begin
 
3461
  for r in c loop
 
3462
    raise notice '%, %', r.i, r.j;
 
3463
    update forc_test set i = i * 100, j = r.j * 2 where current of c;
 
3464
  end loop;
 
3465
end;
 
3466
$$ language plpgsql;
 
3467
select forc01();
 
3468
NOTICE:  1, 1
 
3469
NOTICE:  2, 2
 
3470
NOTICE:  3, 3
 
3471
NOTICE:  4, 4
 
3472
NOTICE:  5, 5
 
3473
NOTICE:  6, 6
 
3474
NOTICE:  7, 7
 
3475
NOTICE:  8, 8
 
3476
NOTICE:  9, 9
 
3477
NOTICE:  10, 10
 
3478
 forc01 
 
3479
--------
 
3480
 
 
3481
(1 row)
 
3482
 
 
3483
select * from forc_test;
 
3484
  i   | j  
 
3485
------+----
 
3486
  100 |  2
 
3487
  200 |  4
 
3488
  300 |  6
 
3489
  400 |  8
 
3490
  500 | 10
 
3491
  600 | 12
 
3492
  700 | 14
 
3493
  800 | 16
 
3494
  900 | 18
 
3495
 1000 | 20
 
3496
(10 rows)
 
3497
 
 
3498
-- same, with a cursor whose portal name doesn't match variable name
 
3499
create or replace function forc01() returns void as $$
 
3500
declare
 
3501
  c refcursor := 'fooled_ya';
 
3502
  r record;
 
3503
begin
 
3504
  open c for select * from forc_test;
 
3505
  loop
 
3506
    fetch c into r;
 
3507
    exit when not found;
 
3508
    raise notice '%, %', r.i, r.j;
 
3509
    update forc_test set i = i * 100, j = r.j * 2 where current of c;
 
3510
  end loop;
 
3511
end;
 
3512
$$ language plpgsql;
 
3513
select forc01();
 
3514
NOTICE:  100, 2
 
3515
NOTICE:  200, 4
 
3516
NOTICE:  300, 6
 
3517
NOTICE:  400, 8
 
3518
NOTICE:  500, 10
 
3519
NOTICE:  600, 12
 
3520
NOTICE:  700, 14
 
3521
NOTICE:  800, 16
 
3522
NOTICE:  900, 18
 
3523
NOTICE:  1000, 20
 
3524
 forc01 
 
3525
--------
 
3526
 
 
3527
(1 row)
 
3528
 
 
3529
select * from forc_test;
 
3530
   i    | j  
 
3531
--------+----
 
3532
  10000 |  4
 
3533
  20000 |  8
 
3534
  30000 | 12
 
3535
  40000 | 16
 
3536
  50000 | 20
 
3537
  60000 | 24
 
3538
  70000 | 28
 
3539
  80000 | 32
 
3540
  90000 | 36
 
3541
 100000 | 40
 
3542
(10 rows)
 
3543
 
 
3544
drop function forc01();
 
3545
-- fail because cursor has no query bound to it
 
3546
create or replace function forc_bad() returns void as $$
 
3547
declare
 
3548
  c refcursor;
 
3549
begin
 
3550
  for r in c loop
 
3551
    raise notice '%', r.i;
 
3552
  end loop;
 
3553
end;
 
3554
$$ language plpgsql;
 
3555
ERROR:  cursor FOR loop must use a bound cursor variable
 
3556
LINE 5:   for r in c loop
 
3557
                   ^
 
3558
-- test RETURN QUERY EXECUTE
 
3559
create or replace function return_dquery()
 
3560
returns setof int as $$
 
3561
begin
 
3562
  return query execute 'select * from (values(10),(20)) f';
 
3563
  return query execute 'select * from (values($1),($2)) f' using 40,50;
 
3564
end;
 
3565
$$ language plpgsql;
 
3566
select * from return_dquery();
 
3567
 return_dquery 
 
3568
---------------
 
3569
            10
 
3570
            20
 
3571
            40
 
3572
            50
 
3573
(4 rows)
 
3574
 
 
3575
drop function return_dquery();
 
3576
-- test RETURN QUERY with dropped columns
 
3577
create table tabwithcols(a int, b int, c int, d int);
 
3578
insert into tabwithcols values(10,20,30,40),(50,60,70,80);
 
3579
create or replace function returnqueryf()
 
3580
returns setof tabwithcols as $$
 
3581
begin
 
3582
  return query select * from tabwithcols;
 
3583
  return query execute 'select * from tabwithcols';
 
3584
end;
 
3585
$$ language plpgsql;
 
3586
select * from returnqueryf();
 
3587
 a  | b  | c  | d  
 
3588
----+----+----+----
 
3589
 10 | 20 | 30 | 40
 
3590
 50 | 60 | 70 | 80
 
3591
 10 | 20 | 30 | 40
 
3592
 50 | 60 | 70 | 80
 
3593
(4 rows)
 
3594
 
 
3595
alter table tabwithcols drop column b;
 
3596
select * from returnqueryf();
 
3597
 a  | c  | d  
 
3598
----+----+----
 
3599
 10 | 30 | 40
 
3600
 50 | 70 | 80
 
3601
 10 | 30 | 40
 
3602
 50 | 70 | 80
 
3603
(4 rows)
 
3604
 
 
3605
alter table tabwithcols drop column d;
 
3606
select * from returnqueryf();
 
3607
 a  | c  
 
3608
----+----
 
3609
 10 | 30
 
3610
 50 | 70
 
3611
 10 | 30
 
3612
 50 | 70
 
3613
(4 rows)
 
3614
 
 
3615
alter table tabwithcols add column d int;
 
3616
select * from returnqueryf();
 
3617
 a  | c  | d 
 
3618
----+----+---
 
3619
 10 | 30 |  
 
3620
 50 | 70 |  
 
3621
 10 | 30 |  
 
3622
 50 | 70 |  
 
3623
(4 rows)
 
3624
 
 
3625
drop function returnqueryf();
 
3626
drop table tabwithcols;
 
3627
--
 
3628
-- Tests for composite-type results
 
3629
--
 
3630
create type footype as (x int, y varchar);
 
3631
-- test: use of variable of composite type in return statement
 
3632
create or replace function foo() returns footype as $$
 
3633
declare
 
3634
  v footype;
 
3635
begin
 
3636
  v := (1, 'hello');
 
3637
  return v;
 
3638
end;
 
3639
$$ language plpgsql;
 
3640
select foo();
 
3641
    foo    
 
3642
-----------
 
3643
 (1,hello)
 
3644
(1 row)
 
3645
 
 
3646
-- test: use of variable of record type in return statement
 
3647
create or replace function foo() returns footype as $$
 
3648
declare
 
3649
  v record;
 
3650
begin
 
3651
  v := (1, 'hello'::varchar);
 
3652
  return v;
 
3653
end;
 
3654
$$ language plpgsql;
 
3655
select foo();
 
3656
    foo    
 
3657
-----------
 
3658
 (1,hello)
 
3659
(1 row)
 
3660
 
 
3661
-- test: use of row expr in return statement
 
3662
create or replace function foo() returns footype as $$
 
3663
begin
 
3664
  return (1, 'hello'::varchar);
 
3665
end;
 
3666
$$ language plpgsql;
 
3667
select foo();
 
3668
    foo    
 
3669
-----------
 
3670
 (1,hello)
 
3671
(1 row)
 
3672
 
 
3673
-- this does not work currently (no implicit casting)
 
3674
create or replace function foo() returns footype as $$
 
3675
begin
 
3676
  return (1, 'hello');
 
3677
end;
 
3678
$$ language plpgsql;
 
3679
select foo();
 
3680
ERROR:  returned record type does not match expected record type
 
3681
DETAIL:  Returned type unknown does not match expected type character varying in column 2.
 
3682
CONTEXT:  PL/pgSQL function foo() while casting return value to function's return type
 
3683
-- ... but this does
 
3684
create or replace function foo() returns footype as $$
 
3685
begin
 
3686
  return (1, 'hello')::footype;
 
3687
end;
 
3688
$$ language plpgsql;
 
3689
select foo();
 
3690
    foo    
 
3691
-----------
 
3692
 (1,hello)
 
3693
(1 row)
 
3694
 
 
3695
drop function foo();
 
3696
-- test: return a row expr as record.
 
3697
create or replace function foorec() returns record as $$
 
3698
declare
 
3699
  v record;
 
3700
begin
 
3701
  v := (1, 'hello');
 
3702
  return v;
 
3703
end;
 
3704
$$ language plpgsql;
 
3705
select foorec();
 
3706
  foorec   
 
3707
-----------
 
3708
 (1,hello)
 
3709
(1 row)
 
3710
 
 
3711
-- test: return row expr in return statement.
 
3712
create or replace function foorec() returns record as $$
 
3713
begin
 
3714
  return (1, 'hello');
 
3715
end;
 
3716
$$ language plpgsql;
 
3717
select foorec();
 
3718
  foorec   
 
3719
-----------
 
3720
 (1,hello)
 
3721
(1 row)
 
3722
 
 
3723
drop function foorec();
 
3724
-- test: row expr in RETURN NEXT statement.
 
3725
create or replace function foo() returns setof footype as $$
 
3726
begin
 
3727
  for i in 1..3
 
3728
  loop
 
3729
    return next (1, 'hello'::varchar);
 
3730
  end loop;
 
3731
  return next null::footype;
 
3732
  return next (2, 'goodbye')::footype;
 
3733
end;
 
3734
$$ language plpgsql;
 
3735
select * from foo();
 
3736
 x |    y    
 
3737
---+---------
 
3738
 1 | hello
 
3739
 1 | hello
 
3740
 1 | hello
 
3741
   | 
 
3742
 2 | goodbye
 
3743
(5 rows)
 
3744
 
 
3745
drop function foo();
 
3746
-- test: use invalid expr in return statement.
 
3747
create or replace function foo() returns footype as $$
 
3748
begin
 
3749
  return 1 + 1;
 
3750
end;
 
3751
$$ language plpgsql;
 
3752
select foo();
 
3753
ERROR:  cannot return non-composite value from function returning composite type
 
3754
CONTEXT:  PL/pgSQL function foo() line 3 at RETURN
 
3755
drop function foo();
 
3756
drop type footype;
 
3757
--
 
3758
-- Tests for 8.4's new RAISE features
 
3759
--
 
3760
create or replace function raise_test() returns void as $$
 
3761
begin
 
3762
  raise notice '% % %', 1, 2, 3
 
3763
     using errcode = '55001', detail = 'some detail info', hint = 'some hint';
 
3764
  raise '% % %', 1, 2, 3
 
3765
     using errcode = 'division_by_zero', detail = 'some detail info';
 
3766
end;
 
3767
$$ language plpgsql;
 
3768
select raise_test();
 
3769
NOTICE:  1 2 3
 
3770
DETAIL:  some detail info
 
3771
HINT:  some hint
 
3772
ERROR:  1 2 3
 
3773
DETAIL:  some detail info
 
3774
-- Since we can't actually see the thrown SQLSTATE in default psql output,
 
3775
-- test it like this; this also tests re-RAISE
 
3776
create or replace function raise_test() returns void as $$
 
3777
begin
 
3778
  raise 'check me'
 
3779
     using errcode = 'division_by_zero', detail = 'some detail info';
 
3780
  exception
 
3781
    when others then
 
3782
      raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
 
3783
      raise;
 
3784
end;
 
3785
$$ language plpgsql;
 
3786
select raise_test();
 
3787
NOTICE:  SQLSTATE: 22012 SQLERRM: check me
 
3788
ERROR:  check me
 
3789
DETAIL:  some detail info
 
3790
create or replace function raise_test() returns void as $$
 
3791
begin
 
3792
  raise 'check me'
 
3793
     using errcode = '1234F', detail = 'some detail info';
 
3794
  exception
 
3795
    when others then
 
3796
      raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
 
3797
      raise;
 
3798
end;
 
3799
$$ language plpgsql;
 
3800
select raise_test();
 
3801
NOTICE:  SQLSTATE: 1234F SQLERRM: check me
 
3802
ERROR:  check me
 
3803
DETAIL:  some detail info
 
3804
-- SQLSTATE specification in WHEN
 
3805
create or replace function raise_test() returns void as $$
 
3806
begin
 
3807
  raise 'check me'
 
3808
     using errcode = '1234F', detail = 'some detail info';
 
3809
  exception
 
3810
    when sqlstate '1234F' then
 
3811
      raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
 
3812
      raise;
 
3813
end;
 
3814
$$ language plpgsql;
 
3815
select raise_test();
 
3816
NOTICE:  SQLSTATE: 1234F SQLERRM: check me
 
3817
ERROR:  check me
 
3818
DETAIL:  some detail info
 
3819
create or replace function raise_test() returns void as $$
 
3820
begin
 
3821
  raise division_by_zero using detail = 'some detail info';
 
3822
  exception
 
3823
    when others then
 
3824
      raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
 
3825
      raise;
 
3826
end;
 
3827
$$ language plpgsql;
 
3828
select raise_test();
 
3829
NOTICE:  SQLSTATE: 22012 SQLERRM: division_by_zero
 
3830
ERROR:  division_by_zero
 
3831
DETAIL:  some detail info
 
3832
create or replace function raise_test() returns void as $$
 
3833
begin
 
3834
  raise division_by_zero;
 
3835
end;
 
3836
$$ language plpgsql;
 
3837
select raise_test();
 
3838
ERROR:  division_by_zero
 
3839
create or replace function raise_test() returns void as $$
 
3840
begin
 
3841
  raise sqlstate '1234F';
 
3842
end;
 
3843
$$ language plpgsql;
 
3844
select raise_test();
 
3845
ERROR:  1234F
 
3846
create or replace function raise_test() returns void as $$
 
3847
begin
 
3848
  raise division_by_zero using message = 'custom' || ' message';
 
3849
end;
 
3850
$$ language plpgsql;
 
3851
select raise_test();
 
3852
ERROR:  custom message
 
3853
create or replace function raise_test() returns void as $$
 
3854
begin
 
3855
  raise using message = 'custom' || ' message', errcode = '22012';
 
3856
end;
 
3857
$$ language plpgsql;
 
3858
select raise_test();
 
3859
ERROR:  custom message
 
3860
-- conflict on message
 
3861
create or replace function raise_test() returns void as $$
 
3862
begin
 
3863
  raise notice 'some message' using message = 'custom' || ' message', errcode = '22012';
 
3864
end;
 
3865
$$ language plpgsql;
 
3866
select raise_test();
 
3867
ERROR:  RAISE option already specified: MESSAGE
 
3868
CONTEXT:  PL/pgSQL function raise_test() line 3 at RAISE
 
3869
-- conflict on errcode
 
3870
create or replace function raise_test() returns void as $$
 
3871
begin
 
3872
  raise division_by_zero using message = 'custom' || ' message', errcode = '22012';
 
3873
end;
 
3874
$$ language plpgsql;
 
3875
select raise_test();
 
3876
ERROR:  RAISE option already specified: ERRCODE
 
3877
CONTEXT:  PL/pgSQL function raise_test() line 3 at RAISE
 
3878
-- nothing to re-RAISE
 
3879
create or replace function raise_test() returns void as $$
 
3880
begin
 
3881
  raise;
 
3882
end;
 
3883
$$ language plpgsql;
 
3884
select raise_test();
 
3885
ERROR:  RAISE without parameters cannot be used outside an exception handler
 
3886
CONTEXT:  PL/pgSQL function raise_test() line 3 at RAISE
 
3887
-- test access to exception data
 
3888
create function zero_divide() returns int as $$
 
3889
declare v int := 0;
 
3890
begin
 
3891
  return 10 / v;
 
3892
end;
 
3893
$$ language plpgsql;
 
3894
create or replace function raise_test() returns void as $$
 
3895
begin
 
3896
  raise exception 'custom exception'
 
3897
     using detail = 'some detail of custom exception',
 
3898
           hint = 'some hint related to custom exception';
 
3899
end;
 
3900
$$ language plpgsql;
 
3901
create function stacked_diagnostics_test() returns void as $$
 
3902
declare _sqlstate text;
 
3903
        _message text;
 
3904
        _context text;
 
3905
begin
 
3906
  perform zero_divide();
 
3907
exception when others then
 
3908
  get stacked diagnostics
 
3909
        _sqlstate = returned_sqlstate,
 
3910
        _message = message_text,
 
3911
        _context = pg_exception_context;
 
3912
  raise notice 'sqlstate: %, message: %, context: [%]',
 
3913
    _sqlstate, _message, replace(_context, E'\n', ' <- ');
 
3914
end;
 
3915
$$ language plpgsql;
 
3916
select stacked_diagnostics_test();
 
3917
NOTICE:  sqlstate: 22012, message: division by zero, context: [PL/pgSQL function zero_divide() line 4 at RETURN <- SQL statement "SELECT zero_divide()" <- PL/pgSQL function stacked_diagnostics_test() line 6 at PERFORM]
 
3918
 stacked_diagnostics_test 
 
3919
--------------------------
 
3920
 
 
3921
(1 row)
 
3922
 
 
3923
create or replace function stacked_diagnostics_test() returns void as $$
 
3924
declare _detail text;
 
3925
        _hint text;
 
3926
        _message text;
 
3927
begin
 
3928
  perform raise_test();
 
3929
exception when others then
 
3930
  get stacked diagnostics
 
3931
        _message = message_text,
 
3932
        _detail = pg_exception_detail,
 
3933
        _hint = pg_exception_hint;
 
3934
  raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
 
3935
end;
 
3936
$$ language plpgsql;
 
3937
select stacked_diagnostics_test();
 
3938
NOTICE:  message: custom exception, detail: some detail of custom exception, hint: some hint related to custom exception
 
3939
 stacked_diagnostics_test 
 
3940
--------------------------
 
3941
 
 
3942
(1 row)
 
3943
 
 
3944
-- fail, cannot use stacked diagnostics statement outside handler
 
3945
create or replace function stacked_diagnostics_test() returns void as $$
 
3946
declare _detail text;
 
3947
        _hint text;
 
3948
        _message text;
 
3949
begin
 
3950
  get stacked diagnostics
 
3951
        _message = message_text,
 
3952
        _detail = pg_exception_detail,
 
3953
        _hint = pg_exception_hint;
 
3954
  raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
 
3955
end;
 
3956
$$ language plpgsql;
 
3957
select stacked_diagnostics_test();
 
3958
ERROR:  GET STACKED DIAGNOSTICS cannot be used outside an exception handler
 
3959
CONTEXT:  PL/pgSQL function stacked_diagnostics_test() line 6 at GET DIAGNOSTICS
 
3960
drop function zero_divide();
 
3961
drop function stacked_diagnostics_test();
 
3962
-- check cases where implicit SQLSTATE variable could be confused with
 
3963
-- SQLSTATE as a keyword, cf bug #5524
 
3964
create or replace function raise_test() returns void as $$
 
3965
begin
 
3966
  perform 1/0;
 
3967
exception
 
3968
  when sqlstate '22012' then
 
3969
    raise notice using message = sqlstate;
 
3970
    raise sqlstate '22012' using message = 'substitute message';
 
3971
end;
 
3972
$$ language plpgsql;
 
3973
select raise_test();
 
3974
NOTICE:  22012
 
3975
ERROR:  substitute message
 
3976
drop function raise_test();
 
3977
-- test CASE statement
 
3978
create or replace function case_test(bigint) returns text as $$
 
3979
declare a int = 10;
 
3980
        b int = 1;
 
3981
begin
 
3982
  case $1
 
3983
    when 1 then
 
3984
      return 'one';
 
3985
    when 2 then
 
3986
      return 'two';
 
3987
    when 3,4,3+5 then
 
3988
      return 'three, four or eight';
 
3989
    when a then
 
3990
      return 'ten';
 
3991
    when a+b, a+b+1 then
 
3992
      return 'eleven, twelve';
 
3993
  end case;
 
3994
end;
 
3995
$$ language plpgsql immutable;
 
3996
select case_test(1);
 
3997
 case_test 
 
3998
-----------
 
3999
 one
 
4000
(1 row)
 
4001
 
 
4002
select case_test(2);
 
4003
 case_test 
 
4004
-----------
 
4005
 two
 
4006
(1 row)
 
4007
 
 
4008
select case_test(3);
 
4009
      case_test       
 
4010
----------------------
 
4011
 three, four or eight
 
4012
(1 row)
 
4013
 
 
4014
select case_test(4);
 
4015
      case_test       
 
4016
----------------------
 
4017
 three, four or eight
 
4018
(1 row)
 
4019
 
 
4020
select case_test(5); -- fails
 
4021
ERROR:  case not found
 
4022
HINT:  CASE statement is missing ELSE part.
 
4023
CONTEXT:  PL/pgSQL function case_test(bigint) line 5 at CASE
 
4024
select case_test(8);
 
4025
      case_test       
 
4026
----------------------
 
4027
 three, four or eight
 
4028
(1 row)
 
4029
 
 
4030
select case_test(10);
 
4031
 case_test 
 
4032
-----------
 
4033
 ten
 
4034
(1 row)
 
4035
 
 
4036
select case_test(11);
 
4037
   case_test    
 
4038
----------------
 
4039
 eleven, twelve
 
4040
(1 row)
 
4041
 
 
4042
select case_test(12);
 
4043
   case_test    
 
4044
----------------
 
4045
 eleven, twelve
 
4046
(1 row)
 
4047
 
 
4048
select case_test(13); -- fails
 
4049
ERROR:  case not found
 
4050
HINT:  CASE statement is missing ELSE part.
 
4051
CONTEXT:  PL/pgSQL function case_test(bigint) line 5 at CASE
 
4052
create or replace function catch() returns void as $$
 
4053
begin
 
4054
  raise notice '%', case_test(6);
 
4055
exception
 
4056
  when case_not_found then
 
4057
    raise notice 'caught case_not_found % %', SQLSTATE, SQLERRM;
 
4058
end
 
4059
$$ language plpgsql;
 
4060
select catch();
 
4061
NOTICE:  caught case_not_found 20000 case not found
 
4062
 catch 
 
4063
-------
 
4064
 
 
4065
(1 row)
 
4066
 
 
4067
-- test the searched variant too, as well as ELSE
 
4068
create or replace function case_test(bigint) returns text as $$
 
4069
declare a int = 10;
 
4070
begin
 
4071
  case
 
4072
    when $1 = 1 then
 
4073
      return 'one';
 
4074
    when $1 = a + 2 then
 
4075
      return 'twelve';
 
4076
    else
 
4077
      return 'other';
 
4078
  end case;
 
4079
end;
 
4080
$$ language plpgsql immutable;
 
4081
select case_test(1);
 
4082
 case_test 
 
4083
-----------
 
4084
 one
 
4085
(1 row)
 
4086
 
 
4087
select case_test(2);
 
4088
 case_test 
 
4089
-----------
 
4090
 other
 
4091
(1 row)
 
4092
 
 
4093
select case_test(12);
 
4094
 case_test 
 
4095
-----------
 
4096
 twelve
 
4097
(1 row)
 
4098
 
 
4099
select case_test(13);
 
4100
 case_test 
 
4101
-----------
 
4102
 other
 
4103
(1 row)
 
4104
 
 
4105
drop function catch();
 
4106
drop function case_test(bigint);
 
4107
-- test variadic functions
 
4108
create or replace function vari(variadic int[])
 
4109
returns void as $$
 
4110
begin
 
4111
  for i in array_lower($1,1)..array_upper($1,1) loop
 
4112
    raise notice '%', $1[i];
 
4113
  end loop; end;
 
4114
$$ language plpgsql;
 
4115
select vari(1,2,3,4,5);
 
4116
NOTICE:  1
 
4117
NOTICE:  2
 
4118
NOTICE:  3
 
4119
NOTICE:  4
 
4120
NOTICE:  5
 
4121
 vari 
 
4122
------
 
4123
 
 
4124
(1 row)
 
4125
 
 
4126
select vari(3,4,5);
 
4127
NOTICE:  3
 
4128
NOTICE:  4
 
4129
NOTICE:  5
 
4130
 vari 
 
4131
------
 
4132
 
 
4133
(1 row)
 
4134
 
 
4135
select vari(variadic array[5,6,7]);
 
4136
NOTICE:  5
 
4137
NOTICE:  6
 
4138
NOTICE:  7
 
4139
 vari 
 
4140
------
 
4141
 
 
4142
(1 row)
 
4143
 
 
4144
drop function vari(int[]);
 
4145
-- coercion test
 
4146
create or replace function pleast(variadic numeric[])
 
4147
returns numeric as $$
 
4148
declare aux numeric = $1[array_lower($1,1)];
 
4149
begin
 
4150
  for i in array_lower($1,1)+1..array_upper($1,1) loop
 
4151
    if $1[i] < aux then aux := $1[i]; end if;
 
4152
  end loop;
 
4153
  return aux;
 
4154
end;
 
4155
$$ language plpgsql immutable strict;
 
4156
select pleast(10,1,2,3,-16);
 
4157
 pleast 
 
4158
--------
 
4159
    -16
 
4160
(1 row)
 
4161
 
 
4162
select pleast(10.2,2.2,-1.1);
 
4163
 pleast 
 
4164
--------
 
4165
   -1.1
 
4166
(1 row)
 
4167
 
 
4168
select pleast(10.2,10, -20);
 
4169
 pleast 
 
4170
--------
 
4171
    -20
 
4172
(1 row)
 
4173
 
 
4174
select pleast(10,20, -1.0);
 
4175
 pleast 
 
4176
--------
 
4177
   -1.0
 
4178
(1 row)
 
4179
 
 
4180
-- in case of conflict, non-variadic version is preferred
 
4181
create or replace function pleast(numeric)
 
4182
returns numeric as $$
 
4183
begin
 
4184
  raise notice 'non-variadic function called';
 
4185
  return $1;
 
4186
end;
 
4187
$$ language plpgsql immutable strict;
 
4188
select pleast(10);
 
4189
NOTICE:  non-variadic function called
 
4190
 pleast 
 
4191
--------
 
4192
     10
 
4193
(1 row)
 
4194
 
 
4195
drop function pleast(numeric[]);
 
4196
drop function pleast(numeric);
 
4197
-- test table functions
 
4198
create function tftest(int) returns table(a int, b int) as $$
 
4199
begin
 
4200
  return query select $1, $1+i from generate_series(1,5) g(i);
 
4201
end;
 
4202
$$ language plpgsql immutable strict;
 
4203
select * from tftest(10);
 
4204
 a  | b  
 
4205
----+----
 
4206
 10 | 11
 
4207
 10 | 12
 
4208
 10 | 13
 
4209
 10 | 14
 
4210
 10 | 15
 
4211
(5 rows)
 
4212
 
 
4213
create or replace function tftest(a1 int) returns table(a int, b int) as $$
 
4214
begin
 
4215
  a := a1; b := a1 + 1;
 
4216
  return next;
 
4217
  a := a1 * 10; b := a1 * 10 + 1;
 
4218
  return next;
 
4219
end;
 
4220
$$ language plpgsql immutable strict;
 
4221
select * from tftest(10);
 
4222
  a  |  b  
 
4223
-----+-----
 
4224
  10 |  11
 
4225
 100 | 101
 
4226
(2 rows)
 
4227
 
 
4228
drop function tftest(int);
 
4229
create or replace function rttest()
 
4230
returns setof int as $$
 
4231
declare rc int;
 
4232
begin
 
4233
  return query values(10),(20);
 
4234
  get diagnostics rc = row_count;
 
4235
  raise notice '% %', found, rc;
 
4236
  return query select * from (values(10),(20)) f(a) where false;
 
4237
  get diagnostics rc = row_count;
 
4238
  raise notice '% %', found, rc;
 
4239
  return query execute 'values(10),(20)';
 
4240
  get diagnostics rc = row_count;
 
4241
  raise notice '% %', found, rc;
 
4242
  return query execute 'select * from (values(10),(20)) f(a) where false';
 
4243
  get diagnostics rc = row_count;
 
4244
  raise notice '% %', found, rc;
 
4245
end;
 
4246
$$ language plpgsql;
 
4247
select * from rttest();
 
4248
NOTICE:  t 2
 
4249
NOTICE:  f 0
 
4250
NOTICE:  t 2
 
4251
NOTICE:  f 0
 
4252
 rttest 
 
4253
--------
 
4254
     10
 
4255
     20
 
4256
     10
 
4257
     20
 
4258
(4 rows)
 
4259
 
 
4260
drop function rttest();
 
4261
-- Test for proper cleanup at subtransaction exit.  This example
 
4262
-- exposed a bug in PG 8.2.
 
4263
CREATE FUNCTION leaker_1(fail BOOL) RETURNS INTEGER AS $$
 
4264
DECLARE
 
4265
  v_var INTEGER;
 
4266
BEGIN
 
4267
  BEGIN
 
4268
    v_var := (leaker_2(fail)).error_code;
 
4269
  EXCEPTION
 
4270
    WHEN others THEN RETURN 0;
 
4271
  END;
 
4272
  RETURN 1;
 
4273
END;
 
4274
$$ LANGUAGE plpgsql;
 
4275
CREATE FUNCTION leaker_2(fail BOOL, OUT error_code INTEGER, OUT new_id INTEGER)
 
4276
  RETURNS RECORD AS $$
 
4277
BEGIN
 
4278
  IF fail THEN
 
4279
    RAISE EXCEPTION 'fail ...';
 
4280
  END IF;
 
4281
  error_code := 1;
 
4282
  new_id := 1;
 
4283
  RETURN;
 
4284
END;
 
4285
$$ LANGUAGE plpgsql;
 
4286
SELECT * FROM leaker_1(false);
 
4287
 leaker_1 
 
4288
----------
 
4289
        1
 
4290
(1 row)
 
4291
 
 
4292
SELECT * FROM leaker_1(true);
 
4293
 leaker_1 
 
4294
----------
 
4295
        0
 
4296
(1 row)
 
4297
 
 
4298
DROP FUNCTION leaker_1(bool);
 
4299
DROP FUNCTION leaker_2(bool);
 
4300
-- Test for appropriate cleanup of non-simple expression evaluations
 
4301
-- (bug in all versions prior to August 2010)
 
4302
CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$
 
4303
DECLARE
 
4304
  arr text[];
 
4305
  lr text;
 
4306
  i integer;
 
4307
BEGIN
 
4308
  arr := array[array['foo','bar'], array['baz', 'quux']];
 
4309
  lr := 'fool';
 
4310
  i := 1;
 
4311
  -- use sub-SELECTs to make expressions non-simple
 
4312
  arr[(SELECT i)][(SELECT i+1)] := (SELECT lr);
 
4313
  RETURN arr;
 
4314
END;
 
4315
$$ LANGUAGE plpgsql;
 
4316
SELECT nonsimple_expr_test();
 
4317
   nonsimple_expr_test   
 
4318
-------------------------
 
4319
 {{foo,fool},{baz,quux}}
 
4320
(1 row)
 
4321
 
 
4322
DROP FUNCTION nonsimple_expr_test();
 
4323
CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$
 
4324
declare
 
4325
   i integer NOT NULL := 0;
 
4326
begin
 
4327
  begin
 
4328
    i := (SELECT NULL::integer);  -- should throw error
 
4329
  exception
 
4330
    WHEN OTHERS THEN
 
4331
      i := (SELECT 1::integer);
 
4332
  end;
 
4333
  return i;
 
4334
end;
 
4335
$$ LANGUAGE plpgsql;
 
4336
SELECT nonsimple_expr_test();
 
4337
 nonsimple_expr_test 
 
4338
---------------------
 
4339
                   1
 
4340
(1 row)
 
4341
 
 
4342
DROP FUNCTION nonsimple_expr_test();
 
4343
--
 
4344
-- Test cases involving recursion and error recovery in simple expressions
 
4345
-- (bugs in all versions before October 2010).  The problems are most
 
4346
-- easily exposed by mutual recursion between plpgsql and sql functions.
 
4347
--
 
4348
create function recurse(float8) returns float8 as
 
4349
$$
 
4350
begin
 
4351
  if ($1 > 0) then
 
4352
    return sql_recurse($1 - 1);
 
4353
  else
 
4354
    return $1;
 
4355
  end if;
 
4356
end;
 
4357
$$ language plpgsql;
 
4358
-- "limit" is to prevent this from being inlined
 
4359
create function sql_recurse(float8) returns float8 as
 
4360
$$ select recurse($1) limit 1; $$ language sql;
 
4361
select recurse(10);
 
4362
 recurse 
 
4363
---------
 
4364
       0
 
4365
(1 row)
 
4366
 
 
4367
create function error1(text) returns text language sql as
 
4368
$$ SELECT relname::text FROM pg_class c WHERE c.oid = $1::regclass $$;
 
4369
create function error2(p_name_table text) returns text language plpgsql as $$
 
4370
begin
 
4371
  return error1(p_name_table);
 
4372
end$$;
 
4373
BEGIN;
 
4374
create table public.stuffs (stuff text);
 
4375
SAVEPOINT a;
 
4376
select error2('nonexistent.stuffs');
 
4377
ERROR:  schema "nonexistent" does not exist
 
4378
CONTEXT:  SQL function "error1" statement 1
 
4379
PL/pgSQL function error2(text) line 3 at RETURN
 
4380
ROLLBACK TO a;
 
4381
select error2('public.stuffs');
 
4382
 error2 
 
4383
--------
 
4384
 stuffs
 
4385
(1 row)
 
4386
 
 
4387
rollback;
 
4388
drop function error2(p_name_table text);
 
4389
drop function error1(text);
 
4390
-- Test for consistent reporting of error context
 
4391
create function fail() returns int language plpgsql as $$
 
4392
begin
 
4393
  return 1/0;
 
4394
end
 
4395
$$;
 
4396
select fail();
 
4397
ERROR:  division by zero
 
4398
CONTEXT:  SQL statement "SELECT 1/0"
 
4399
PL/pgSQL function fail() line 3 at RETURN
 
4400
select fail();
 
4401
ERROR:  division by zero
 
4402
CONTEXT:  SQL statement "SELECT 1/0"
 
4403
PL/pgSQL function fail() line 3 at RETURN
 
4404
drop function fail();
 
4405
-- Test handling of string literals.
 
4406
set standard_conforming_strings = off;
 
4407
create or replace function strtest() returns text as $$
 
4408
begin
 
4409
  raise notice 'foo\\bar\041baz';
 
4410
  return 'foo\\bar\041baz';
 
4411
end
 
4412
$$ language plpgsql;
 
4413
WARNING:  nonstandard use of \\ in a string literal
 
4414
LINE 3:   raise notice 'foo\\bar\041baz';
 
4415
                       ^
 
4416
HINT:  Use the escape string syntax for backslashes, e.g., E'\\'.
 
4417
WARNING:  nonstandard use of \\ in a string literal
 
4418
LINE 4:   return 'foo\\bar\041baz';
 
4419
                 ^
 
4420
HINT:  Use the escape string syntax for backslashes, e.g., E'\\'.
 
4421
WARNING:  nonstandard use of \\ in a string literal
 
4422
LINE 4:   return 'foo\\bar\041baz';
 
4423
                 ^
 
4424
HINT:  Use the escape string syntax for backslashes, e.g., E'\\'.
 
4425
select strtest();
 
4426
NOTICE:  foo\bar!baz
 
4427
WARNING:  nonstandard use of \\ in a string literal
 
4428
LINE 1: SELECT 'foo\\bar\041baz'
 
4429
               ^
 
4430
HINT:  Use the escape string syntax for backslashes, e.g., E'\\'.
 
4431
QUERY:  SELECT 'foo\\bar\041baz'
 
4432
CONTEXT:  PL/pgSQL function strtest() line 4 at RETURN
 
4433
   strtest   
 
4434
-------------
 
4435
 foo\bar!baz
 
4436
(1 row)
 
4437
 
 
4438
create or replace function strtest() returns text as $$
 
4439
begin
 
4440
  raise notice E'foo\\bar\041baz';
 
4441
  return E'foo\\bar\041baz';
 
4442
end
 
4443
$$ language plpgsql;
 
4444
select strtest();
 
4445
NOTICE:  foo\bar!baz
 
4446
   strtest   
 
4447
-------------
 
4448
 foo\bar!baz
 
4449
(1 row)
 
4450
 
 
4451
set standard_conforming_strings = on;
 
4452
create or replace function strtest() returns text as $$
 
4453
begin
 
4454
  raise notice 'foo\\bar\041baz\';
 
4455
  return 'foo\\bar\041baz\';
 
4456
end
 
4457
$$ language plpgsql;
 
4458
select strtest();
 
4459
NOTICE:  foo\\bar\041baz\
 
4460
     strtest      
 
4461
------------------
 
4462
 foo\\bar\041baz\
 
4463
(1 row)
 
4464
 
 
4465
create or replace function strtest() returns text as $$
 
4466
begin
 
4467
  raise notice E'foo\\bar\041baz';
 
4468
  return E'foo\\bar\041baz';
 
4469
end
 
4470
$$ language plpgsql;
 
4471
select strtest();
 
4472
NOTICE:  foo\bar!baz
 
4473
   strtest   
 
4474
-------------
 
4475
 foo\bar!baz
 
4476
(1 row)
 
4477
 
 
4478
drop function strtest();
 
4479
-- Test anonymous code blocks.
 
4480
DO $$
 
4481
DECLARE r record;
 
4482
BEGIN
 
4483
    FOR r IN SELECT rtrim(roomno) AS roomno, comment FROM Room ORDER BY roomno
 
4484
    LOOP
 
4485
        RAISE NOTICE '%, %', r.roomno, r.comment;
 
4486
    END LOOP;
 
4487
END$$;
 
4488
NOTICE:  001, Entrance
 
4489
NOTICE:  002, Office
 
4490
NOTICE:  003, Office
 
4491
NOTICE:  004, Technical
 
4492
NOTICE:  101, Office
 
4493
NOTICE:  102, Conference
 
4494
NOTICE:  103, Restroom
 
4495
NOTICE:  104, Technical
 
4496
NOTICE:  105, Office
 
4497
NOTICE:  106, Office
 
4498
-- these are to check syntax error reporting
 
4499
DO LANGUAGE plpgsql $$begin return 1; end$$;
 
4500
ERROR:  RETURN cannot have a parameter in function returning void
 
4501
LINE 1: DO LANGUAGE plpgsql $$begin return 1; end$$;
 
4502
                                           ^
 
4503
DO $$
 
4504
DECLARE r record;
 
4505
BEGIN
 
4506
    FOR r IN SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
 
4507
    LOOP
 
4508
        RAISE NOTICE '%, %', r.roomno, r.comment;
 
4509
    END LOOP;
 
4510
END$$;
 
4511
ERROR:  column "foo" does not exist
 
4512
LINE 1: SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomn...
 
4513
                                        ^
 
4514
QUERY:  SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
 
4515
CONTEXT:  PL/pgSQL function inline_code_block line 4 at FOR over SELECT rows
 
4516
-- Check variable scoping -- a var is not available in its own or prior
 
4517
-- default expressions.
 
4518
create function scope_test() returns int as $$
 
4519
declare x int := 42;
 
4520
begin
 
4521
  declare y int := x + 1;
 
4522
          x int := x + 2;
 
4523
  begin
 
4524
    return x * 100 + y;
 
4525
  end;
 
4526
end;
 
4527
$$ language plpgsql;
 
4528
select scope_test();
 
4529
 scope_test 
 
4530
------------
 
4531
       4443
 
4532
(1 row)
 
4533
 
 
4534
drop function scope_test();
 
4535
-- Check handling of conflicts between plpgsql vars and table columns.
 
4536
set plpgsql.variable_conflict = error;
 
4537
create function conflict_test() returns setof int8_tbl as $$
 
4538
declare r record;
 
4539
  q1 bigint := 42;
 
4540
begin
 
4541
  for r in select q1,q2 from int8_tbl loop
 
4542
    return next r;
 
4543
  end loop;
 
4544
end;
 
4545
$$ language plpgsql;
 
4546
select * from conflict_test();
 
4547
ERROR:  column reference "q1" is ambiguous
 
4548
LINE 1: select q1,q2 from int8_tbl
 
4549
               ^
 
4550
DETAIL:  It could refer to either a PL/pgSQL variable or a table column.
 
4551
QUERY:  select q1,q2 from int8_tbl
 
4552
CONTEXT:  PL/pgSQL function conflict_test() line 5 at FOR over SELECT rows
 
4553
create or replace function conflict_test() returns setof int8_tbl as $$
 
4554
#variable_conflict use_variable
 
4555
declare r record;
 
4556
  q1 bigint := 42;
 
4557
begin
 
4558
  for r in select q1,q2 from int8_tbl loop
 
4559
    return next r;
 
4560
  end loop;
 
4561
end;
 
4562
$$ language plpgsql;
 
4563
select * from conflict_test();
 
4564
 q1 |        q2         
 
4565
----+-------------------
 
4566
 42 |               456
 
4567
 42 |  4567890123456789
 
4568
 42 |               123
 
4569
 42 |  4567890123456789
 
4570
 42 | -4567890123456789
 
4571
(5 rows)
 
4572
 
 
4573
create or replace function conflict_test() returns setof int8_tbl as $$
 
4574
#variable_conflict use_column
 
4575
declare r record;
 
4576
  q1 bigint := 42;
 
4577
begin
 
4578
  for r in select q1,q2 from int8_tbl loop
 
4579
    return next r;
 
4580
  end loop;
 
4581
end;
 
4582
$$ language plpgsql;
 
4583
select * from conflict_test();
 
4584
        q1        |        q2         
 
4585
------------------+-------------------
 
4586
              123 |               456
 
4587
              123 |  4567890123456789
 
4588
 4567890123456789 |               123
 
4589
 4567890123456789 |  4567890123456789
 
4590
 4567890123456789 | -4567890123456789
 
4591
(5 rows)
 
4592
 
 
4593
drop function conflict_test();
 
4594
-- Check that an unreserved keyword can be used as a variable name
 
4595
create function unreserved_test() returns int as $$
 
4596
declare
 
4597
  forward int := 21;
 
4598
begin
 
4599
  forward := forward * 2;
 
4600
  return forward;
 
4601
end
 
4602
$$ language plpgsql;
 
4603
select unreserved_test();
 
4604
 unreserved_test 
 
4605
-----------------
 
4606
              42
 
4607
(1 row)
 
4608
 
 
4609
drop function unreserved_test();
 
4610
--
 
4611
-- Test FOREACH over arrays
 
4612
--
 
4613
create function foreach_test(anyarray)
 
4614
returns void as $$
 
4615
declare x int;
 
4616
begin
 
4617
  foreach x in array $1
 
4618
  loop
 
4619
    raise notice '%', x;
 
4620
  end loop;
 
4621
  end;
 
4622
$$ language plpgsql;
 
4623
select foreach_test(ARRAY[1,2,3,4]);
 
4624
NOTICE:  1
 
4625
NOTICE:  2
 
4626
NOTICE:  3
 
4627
NOTICE:  4
 
4628
 foreach_test 
 
4629
--------------
 
4630
 
 
4631
(1 row)
 
4632
 
 
4633
select foreach_test(ARRAY[[1,2],[3,4]]);
 
4634
NOTICE:  1
 
4635
NOTICE:  2
 
4636
NOTICE:  3
 
4637
NOTICE:  4
 
4638
 foreach_test 
 
4639
--------------
 
4640
 
 
4641
(1 row)
 
4642
 
 
4643
create or replace function foreach_test(anyarray)
 
4644
returns void as $$
 
4645
declare x int;
 
4646
begin
 
4647
  foreach x slice 1 in array $1
 
4648
  loop
 
4649
    raise notice '%', x;
 
4650
  end loop;
 
4651
  end;
 
4652
$$ language plpgsql;
 
4653
-- should fail
 
4654
select foreach_test(ARRAY[1,2,3,4]);
 
4655
ERROR:  FOREACH ... SLICE loop variable must be of an array type
 
4656
CONTEXT:  PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
 
4657
select foreach_test(ARRAY[[1,2],[3,4]]);
 
4658
ERROR:  FOREACH ... SLICE loop variable must be of an array type
 
4659
CONTEXT:  PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
 
4660
create or replace function foreach_test(anyarray)
 
4661
returns void as $$
 
4662
declare x int[];
 
4663
begin
 
4664
  foreach x slice 1 in array $1
 
4665
  loop
 
4666
    raise notice '%', x;
 
4667
  end loop;
 
4668
  end;
 
4669
$$ language plpgsql;
 
4670
select foreach_test(ARRAY[1,2,3,4]);
 
4671
NOTICE:  {1,2,3,4}
 
4672
 foreach_test 
 
4673
--------------
 
4674
 
 
4675
(1 row)
 
4676
 
 
4677
select foreach_test(ARRAY[[1,2],[3,4]]);
 
4678
NOTICE:  {1,2}
 
4679
NOTICE:  {3,4}
 
4680
 foreach_test 
 
4681
--------------
 
4682
 
 
4683
(1 row)
 
4684
 
 
4685
-- higher level of slicing
 
4686
create or replace function foreach_test(anyarray)
 
4687
returns void as $$
 
4688
declare x int[];
 
4689
begin
 
4690
  foreach x slice 2 in array $1
 
4691
  loop
 
4692
    raise notice '%', x;
 
4693
  end loop;
 
4694
  end;
 
4695
$$ language plpgsql;
 
4696
-- should fail
 
4697
select foreach_test(ARRAY[1,2,3,4]);
 
4698
ERROR:  slice dimension (2) is out of the valid range 0..1
 
4699
CONTEXT:  PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
 
4700
-- ok
 
4701
select foreach_test(ARRAY[[1,2],[3,4]]);
 
4702
NOTICE:  {{1,2},{3,4}}
 
4703
 foreach_test 
 
4704
--------------
 
4705
 
 
4706
(1 row)
 
4707
 
 
4708
select foreach_test(ARRAY[[[1,2]],[[3,4]]]);
 
4709
NOTICE:  {{1,2}}
 
4710
NOTICE:  {{3,4}}
 
4711
 foreach_test 
 
4712
--------------
 
4713
 
 
4714
(1 row)
 
4715
 
 
4716
create type xy_tuple AS (x int, y int);
 
4717
-- iteration over array of records
 
4718
create or replace function foreach_test(anyarray)
 
4719
returns void as $$
 
4720
declare r record;
 
4721
begin
 
4722
  foreach r in array $1
 
4723
  loop
 
4724
    raise notice '%', r;
 
4725
  end loop;
 
4726
  end;
 
4727
$$ language plpgsql;
 
4728
select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
 
4729
NOTICE:  (10,20)
 
4730
NOTICE:  (40,69)
 
4731
NOTICE:  (35,78)
 
4732
 foreach_test 
 
4733
--------------
 
4734
 
 
4735
(1 row)
 
4736
 
 
4737
select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
 
4738
NOTICE:  (10,20)
 
4739
NOTICE:  (40,69)
 
4740
NOTICE:  (35,78)
 
4741
NOTICE:  (88,76)
 
4742
 foreach_test 
 
4743
--------------
 
4744
 
 
4745
(1 row)
 
4746
 
 
4747
create or replace function foreach_test(anyarray)
 
4748
returns void as $$
 
4749
declare x int; y int;
 
4750
begin
 
4751
  foreach x, y in array $1
 
4752
  loop
 
4753
    raise notice 'x = %, y = %', x, y;
 
4754
  end loop;
 
4755
  end;
 
4756
$$ language plpgsql;
 
4757
select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
 
4758
NOTICE:  x = 10, y = 20
 
4759
NOTICE:  x = 40, y = 69
 
4760
NOTICE:  x = 35, y = 78
 
4761
 foreach_test 
 
4762
--------------
 
4763
 
 
4764
(1 row)
 
4765
 
 
4766
select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
 
4767
NOTICE:  x = 10, y = 20
 
4768
NOTICE:  x = 40, y = 69
 
4769
NOTICE:  x = 35, y = 78
 
4770
NOTICE:  x = 88, y = 76
 
4771
 foreach_test 
 
4772
--------------
 
4773
 
 
4774
(1 row)
 
4775
 
 
4776
-- slicing over array of composite types
 
4777
create or replace function foreach_test(anyarray)
 
4778
returns void as $$
 
4779
declare x xy_tuple[];
 
4780
begin
 
4781
  foreach x slice 1 in array $1
 
4782
  loop
 
4783
    raise notice '%', x;
 
4784
  end loop;
 
4785
  end;
 
4786
$$ language plpgsql;
 
4787
select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
 
4788
NOTICE:  {"(10,20)","(40,69)","(35,78)"}
 
4789
 foreach_test 
 
4790
--------------
 
4791
 
 
4792
(1 row)
 
4793
 
 
4794
select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
 
4795
NOTICE:  {"(10,20)","(40,69)"}
 
4796
NOTICE:  {"(35,78)","(88,76)"}
 
4797
 foreach_test 
 
4798
--------------
 
4799
 
 
4800
(1 row)
 
4801
 
 
4802
drop function foreach_test(anyarray);
 
4803
drop type xy_tuple;
 
4804
--
 
4805
-- Assorted tests for array subscript assignment
 
4806
--
 
4807
create temp table rtype (id int, ar text[]);
 
4808
create function arrayassign1() returns text[] language plpgsql as $$
 
4809
declare
 
4810
 r record;
 
4811
begin
 
4812
  r := row(12, '{foo,bar,baz}')::rtype;
 
4813
  r.ar[2] := 'replace';
 
4814
  return r.ar;
 
4815
end$$;
 
4816
select arrayassign1();
 
4817
   arrayassign1    
 
4818
-------------------
 
4819
 {foo,replace,baz}
 
4820
(1 row)
 
4821
 
 
4822
select arrayassign1(); -- try again to exercise internal caching
 
4823
   arrayassign1    
 
4824
-------------------
 
4825
 {foo,replace,baz}
 
4826
(1 row)
 
4827
 
 
4828
create domain orderedarray as int[2]
 
4829
  constraint sorted check (value[1] < value[2]);
 
4830
select '{1,2}'::orderedarray;
 
4831
 orderedarray 
 
4832
--------------
 
4833
 {1,2}
 
4834
(1 row)
 
4835
 
 
4836
select '{2,1}'::orderedarray;  -- fail
 
4837
ERROR:  value for domain orderedarray violates check constraint "sorted"
 
4838
create function testoa(x1 int, x2 int, x3 int) returns orderedarray
 
4839
language plpgsql as $$
 
4840
declare res orderedarray;
 
4841
begin
 
4842
  res := array[x1, x2];
 
4843
  res[2] := x3;
 
4844
  return res;
 
4845
end$$;
 
4846
select testoa(1,2,3);
 
4847
 testoa 
 
4848
--------
 
4849
 {1,3}
 
4850
(1 row)
 
4851
 
 
4852
select testoa(1,2,3); -- try again to exercise internal caching
 
4853
 testoa 
 
4854
--------
 
4855
 {1,3}
 
4856
(1 row)
 
4857
 
 
4858
select testoa(2,1,3); -- fail at initial assign
 
4859
ERROR:  value for domain orderedarray violates check constraint "sorted"
 
4860
CONTEXT:  PL/pgSQL function testoa(integer,integer,integer) line 4 at assignment
 
4861
select testoa(1,2,1); -- fail at update
 
4862
ERROR:  value for domain orderedarray violates check constraint "sorted"
 
4863
CONTEXT:  PL/pgSQL function testoa(integer,integer,integer) line 5 at assignment
 
4864
drop function arrayassign1();
 
4865
drop function testoa(x1 int, x2 int, x3 int);