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.
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
17 -- Triggers ensure consistency of the patching information.
19 -- Functions are used to build up powerful views that let
20 -- you look behind the wall when looking at a patchfield
27 create unique index Room_rno on Room using btree (roomno bpchar_ops);
34 create unique index WSlot_name on WSlot using btree (slotname bpchar_ops);
39 create unique index PField_name on PField using btree (name text_ops);
46 create unique index PSlot_name on PSlot using btree (slotname bpchar_ops);
53 create unique index PLine_name on PLine using btree (slotname bpchar_ops);
59 create unique index Hub_name on Hub using btree (name bpchar_ops);
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);
72 create unique index System_name on System using btree (name text_ops);
79 create unique index IFace_name on IFace using btree (slotname bpchar_ops);
85 create unique index PHone_name on PHone using btree (slotname bpchar_ops);
86 -- ************************************************************
88 -- * Trigger procedures and functions for the patchfield
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 '
98 if new.roomno != old.roomno then
99 update WSlot set roomno = new.roomno where roomno = old.roomno;
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 '
112 delete from WSlot where roomno = old.roomno;
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 $$
124 if count(*) = 0 from Room where roomno = new.roomno then
125 raise exception 'Room % does not exist', new.roomno;
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 '
138 if new.name != old.name then
139 update PSlot set pfname = new.name where pfname = old.name;
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 '
152 delete from PSlot where pfname = old.name;
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$
167 select into pfrec * from PField where name = ps.pfname;
169 raise exception $$Patchfield "%" does not exist$$, ps.pfname;
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 '
182 if new.name != old.name then
183 update IFace set sysname = new.name where sysname = old.name;
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 $$
199 select into sysrec * from system where name = new.sysname;
201 raise exception $q$system "%" does not exist$q$, new.sysname;
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;
209 new.slotname := sname;
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 '
224 if tg_op = ''INSERT'' then
225 dummy := tg_hub_adjustslots(new.name, 0, new.nslots);
228 if tg_op = ''UPDATE'' then
229 if new.name != old.name then
230 update HSlot set hubname = new.name where hubname = old.name;
232 dummy := tg_hub_adjustslots(new.name, old.nslots, new.nslots);
235 if tg_op = ''DELETE'' then
236 dummy := tg_hub_adjustslots(old.name, old.nslots, 0);
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,
251 if newnslots = oldnslots then
254 if newnslots < oldnslots then
255 delete from HSlot where hubname = hname and slotno > newnslots;
258 for i in oldnslots + 1 .. newnslots loop
259 insert into HSlot (slotname, hubname, slotno, slotlink)
260 values (''HS.dummy'', hname, i, '''');
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 '
278 xname HSlot.slotname%TYPE;
281 select into hubrec * from Hub where name = new.hubname;
283 raise exception ''no manual manipulation of HSlot'';
285 if new.slotno < 1 or new.slotno > hubrec.nslots then
286 raise exception ''no manual manipulation of HSlot'';
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'';
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;
299 new.slotname := sname;
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 '
313 select into hubrec * from Hub where name = old.hubname;
317 if old.slotno > hubrec.nslots then
320 raise exception ''no manual manipulation of HSlot'';
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 '
331 if substr(new.slotname, 1, 2) != tg_argv[0] then
332 raise exception ''slotname must begin with %'', tg_argv[0];
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 '
353 if new.slotlink isnull then
354 new.slotlink := '''';
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 '
375 if new.backlink isnull then
376 new.backlink := '''';
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 '
393 if new.slotname != old.slotname then
394 delete from PSlot where slotname = old.slotname;
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 '
419 if new.slotname != old.slotname then
420 delete from WSlot where slotname = old.slotname;
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 '
445 if new.slotname != old.slotname then
446 delete from PLine where slotname = old.slotname;
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 '
471 if new.slotname != old.slotname then
472 delete from IFace where slotname = old.slotname;
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 '
497 if new.slotname != old.slotname or new.hubname != old.hubname then
498 delete from HSlot where slotname = old.slotname;
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 '
523 if new.slotname != old.slotname then
524 delete from PHone where slotname = old.slotname;
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 '
549 if tg_op = ''INSERT'' then
550 if new.backlink != '''' then
551 dummy := tg_backlink_set(new.backlink, new.slotname);
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);
560 if new.backlink != '''' then
561 dummy := tg_backlink_set(new.backlink, new.slotname);
564 if new.slotname != old.slotname and new.backlink != '''' then
565 dummy := tg_slotlink_set(new.backlink, new.slotname);
570 if tg_op = ''DELETE'' then
571 if old.backlink != '''' then
572 dummy := tg_backlink_unset(old.backlink, old.slotname);
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)
595 mytype := substr(myname, 1, 2);
596 link := mytype || substr(blname, 1, 2);
597 if link = ''PLPL'' then
599 ''backlink between two phone lines does not make sense'';
601 if link in (''PLWS'', ''WSPL'') then
603 ''direct link of phone line to wall slot not permitted'';
605 if mytype = ''PS'' then
606 select into rec * from PSlot where slotname = myname;
608 raise exception ''% does not exist'', myname;
610 if rec.backlink != blname then
611 update PSlot set backlink = blname where slotname = myname;
615 if mytype = ''WS'' then
616 select into rec * from WSlot where slotname = myname;
618 raise exception ''% does not exist'', myname;
620 if rec.backlink != blname then
621 update WSlot set backlink = blname where slotname = myname;
625 if mytype = ''PL'' then
626 select into rec * from PLine where slotname = myname;
628 raise exception ''% does not exist'', myname;
630 if rec.backlink != blname then
631 update PLine set backlink = blname where slotname = myname;
635 raise exception ''illegal backlink beginning with %'', mytype;
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)
650 mytype := substr(myname, 1, 2);
651 if mytype = ''PS'' then
652 select into rec * from PSlot where slotname = myname;
656 if rec.backlink = blname then
657 update PSlot set backlink = '''' where slotname = myname;
661 if mytype = ''WS'' then
662 select into rec * from WSlot where slotname = myname;
666 if rec.backlink = blname then
667 update WSlot set backlink = '''' where slotname = myname;
671 if mytype = ''PL'' then
672 select into rec * from PLine where slotname = myname;
676 if rec.backlink = blname then
677 update PLine set backlink = '''' where slotname = myname;
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 '
691 if tg_op = ''INSERT'' then
692 if new.slotlink != '''' then
693 dummy := tg_slotlink_set(new.slotlink, new.slotname);
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);
702 if new.slotlink != '''' then
703 dummy := tg_slotlink_set(new.slotlink, new.slotname);
706 if new.slotname != old.slotname and new.slotlink != '''' then
707 dummy := tg_slotlink_set(new.slotlink, new.slotname);
712 if tg_op = ''DELETE'' then
713 if old.slotlink != '''' then
714 dummy := tg_slotlink_unset(old.slotlink, old.slotname);
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)
743 mytype := substr(myname, 1, 2);
744 link := mytype || substr(blname, 1, 2);
745 if link = ''PHPH'' then
747 ''slotlink between two phones does not make sense'';
749 if link in (''PHHS'', ''HSPH'') then
751 ''link of phone to hub does not make sense'';
753 if link in (''PHIF'', ''IFPH'') then
755 ''link of phone to hub does not make sense'';
757 if link in (''PSWS'', ''WSPS'') then
759 ''slotlink from patchslot to wallslot not permitted'';
761 if mytype = ''PS'' then
762 select into rec * from PSlot where slotname = myname;
764 raise exception ''% does not exist'', myname;
766 if rec.slotlink != blname then
767 update PSlot set slotlink = blname where slotname = myname;
771 if mytype = ''WS'' then
772 select into rec * from WSlot where slotname = myname;
774 raise exception ''% does not exist'', myname;
776 if rec.slotlink != blname then
777 update WSlot set slotlink = blname where slotname = myname;
781 if mytype = ''IF'' then
782 select into rec * from IFace where slotname = myname;
784 raise exception ''% does not exist'', myname;
786 if rec.slotlink != blname then
787 update IFace set slotlink = blname where slotname = myname;
791 if mytype = ''HS'' then
792 select into rec * from HSlot where slotname = myname;
794 raise exception ''% does not exist'', myname;
796 if rec.slotlink != blname then
797 update HSlot set slotlink = blname where slotname = myname;
801 if mytype = ''PH'' then
802 select into rec * from PHone where slotname = myname;
804 raise exception ''% does not exist'', myname;
806 if rec.slotlink != blname then
807 update PHone set slotlink = blname where slotname = myname;
811 raise exception ''illegal slotlink beginning with %'', mytype;
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)
826 mytype := substr(myname, 1, 2);
827 if mytype = ''PS'' then
828 select into rec * from PSlot where slotname = myname;
832 if rec.slotlink = blname then
833 update PSlot set slotlink = '''' where slotname = myname;
837 if mytype = ''WS'' then
838 select into rec * from WSlot where slotname = myname;
842 if rec.slotlink = blname then
843 update WSlot set slotlink = '''' where slotname = myname;
847 if mytype = ''IF'' then
848 select into rec * from IFace where slotname = myname;
852 if rec.slotlink = blname then
853 update IFace set slotlink = '''' where slotname = myname;
857 if mytype = ''HS'' then
858 select into rec * from HSlot where slotname = myname;
862 if rec.slotlink = blname then
863 update HSlot set slotlink = '''' where slotname = myname;
867 if mytype = ''PH'' then
868 select into rec * from PHone where slotname = myname;
872 if rec.slotlink = blname then
873 update PHone set slotlink = '''' where slotname = myname;
879 -- ************************************************************
880 -- * Describe the backside of a patchfield slot
881 -- ************************************************************
882 create function pslot_backlink_view(bpchar)
890 select into rec * from PSlot where slotname = $1;
894 if rec.backlink = '''' then
897 bltype := substr(rec.backlink, 1, 2);
898 if bltype = ''PL'' then
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 || '')'';
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);
922 -- ************************************************************
923 -- * Describe the front of a patchfield slot
924 -- ************************************************************
925 create function pslot_slotlink_view(bpchar)
932 select into psrec * from PSlot where slotname = $1;
936 if psrec.slotlink = '''' then
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);
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;
953 return psrec.slotlink;
956 -- ************************************************************
957 -- * Describe the front of a wall connector slot
958 -- ************************************************************
959 create function wslot_slotlink_view(bpchar)
966 select into rec * from WSlot where slotname = $1;
970 if rec.slotlink = '''' then
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 || '')'';
984 if sltype = ''IF'' then
986 syrow System%RowType;
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 || '')'';
1001 return rec.slotlink;
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
1012 -- First we build the house - so we create the rooms
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');
1025 -- Second we install the wall connectors
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', '', '');
1070 -- Now create the patch fields and their slots
1072 insert into PField values ('PF0_1', 'Wallslots basement');
1074 -- The cables for these will be made later, so they are unconnected for now
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', '', '');
1083 -- These are already wired to the wall connectors
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');
1098 -- This patchfield will be renamed later into PF0_2 - so its
1099 -- slots references in pfname should follow
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');
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
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
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 | |
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
1174 WS.001.2a | 001 | | PS.base.a3
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 | |
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
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 | |
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.
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
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
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
1252 WS.001.3b | 001 | | PS.base.a6
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
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
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
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', '', '');
1303 -- Fix the wrong name for patchfield PF0_2
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 | |
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
1425 -- Install the central phone system and create the phone numbers.
1426 -- They are wired on insert to the patchfields. Again the
1427 -- triggers automatically tell the PSlots to update their
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');
1451 -- Buy some phones, plug them into the wall and patch the
1452 -- phone lines to the corresponding patchfield slots.
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';
1463 -- Install a hub at one of the patchfields, plug a computers
1464 -- ethernet interface into the wall and patch it to the hub.
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';
1471 -- Now we take a look at the patchfield
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 -> - | -
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 | -
1514 -- Finally we want errors
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_set(character,character) line 30 at RAISE
1522 PL/pgSQL function tg_backlink_a() line 17 at assignment
1523 update PSlot set backlink = 'XX.illegal' where slotname = 'PS.base.a1';
1524 ERROR: illegal backlink beginning with XX
1525 CONTEXT: PL/pgSQL function tg_backlink_set(character,character) line 47 at RAISE
1526 PL/pgSQL function tg_backlink_a() line 17 at assignment
1527 update PSlot set slotlink = 'PS.not.there' where slotname = 'PS.base.a1';
1528 ERROR: PS.not.there does not exist
1529 CONTEXT: PL/pgSQL function tg_slotlink_set(character,character) line 30 at RAISE
1530 PL/pgSQL function tg_slotlink_a() line 17 at assignment
1531 update PSlot set slotlink = 'XX.illegal' where slotname = 'PS.base.a1';
1532 ERROR: illegal slotlink beginning with XX
1533 CONTEXT: PL/pgSQL function tg_slotlink_set(character,character) line 77 at RAISE
1534 PL/pgSQL function tg_slotlink_a() line 17 at assignment
1535 insert into HSlot values ('HS', 'base.hub1', 1, '');
1536 ERROR: duplicate key value violates unique constraint "hslot_name"
1537 DETAIL: Key (slotname)=(HS.base.hub1.1 ) already exists.
1538 insert into HSlot values ('HS', 'base.hub1', 20, '');
1539 ERROR: no manual manipulation of HSlot
1540 CONTEXT: PL/pgSQL function tg_hslot_biu() line 12 at RAISE
1542 ERROR: no manual manipulation of HSlot
1543 CONTEXT: PL/pgSQL function tg_hslot_bd() line 12 at RAISE
1544 insert into IFace values ('IF', 'notthere', 'eth0', '');
1545 ERROR: system "notthere" does not exist
1546 CONTEXT: PL/pgSQL function tg_iface_biu() line 8 at RAISE
1547 insert into IFace values ('IF', 'orion', 'ethernet_interface_name_too_long', '');
1548 ERROR: IFace slotname "IF.orion.ethernet_interface_name_too_long" too long (20 char max)
1549 CONTEXT: PL/pgSQL function tg_iface_biu() line 14 at RAISE
1551 -- The following tests are unrelated to the scenario outlined above;
1552 -- they merely exercise specific parts of PL/pgSQL
1555 -- Test recursion, per bug report 7-Sep-01
1557 CREATE FUNCTION recursion_test(int,int) RETURNS text AS '
1561 rslt = CAST($2 AS TEXT);
1563 rslt = CAST($1 AS TEXT) || '','' || recursion_test($1 - 1, $2);
1566 END;' LANGUAGE plpgsql;
1567 SELECT recursion_test(4,3);
1574 -- Test the FOUND magic variable
1576 CREATE TABLE found_test_tbl (a int);
1577 create function test_found()
1578 returns boolean as '
1581 insert into found_test_tbl values (1);
1583 insert into found_test_tbl values (2);
1586 update found_test_tbl set a = 100 where a = 1;
1588 insert into found_test_tbl values (3);
1591 delete from found_test_tbl where a = 9999; -- matches no rows
1593 insert into found_test_tbl values (4);
1596 for i in 1 .. 10 loop
1597 -- no need to do anything
1600 insert into found_test_tbl values (5);
1603 -- never executes the loop
1604 for i in 2 .. 1 loop
1605 -- no need to do anything
1608 insert into found_test_tbl values (6);
1611 end;' language plpgsql;
1612 select test_found();
1618 select * from found_test_tbl;
1630 -- Test set-returning functions for PL/pgSQL
1632 create function test_table_func_rec() returns setof found_test_tbl as '
1636 FOR rec IN select * from found_test_tbl LOOP
1640 END;' language plpgsql;
1641 select * from test_table_func_rec();
1652 create function test_table_func_row() returns setof found_test_tbl as '
1654 row found_test_tbl%ROWTYPE;
1656 FOR row IN select * from found_test_tbl LOOP
1660 END;' language plpgsql;
1661 select * from test_table_func_row();
1672 create function test_ret_set_scalar(int,int) returns setof int as '
1676 FOR i IN $1 .. $2 LOOP
1680 END;' language plpgsql;
1681 select * from test_ret_set_scalar(1,10);
1683 ---------------------
1696 create function test_ret_set_rec_dyn(int) returns setof record as '
1701 SELECT INTO retval 5, 10, 15;
1705 SELECT INTO retval 50, 5::numeric, ''xxx''::text;
1710 END;' language plpgsql;
1711 SELECT * FROM test_ret_set_rec_dyn(1500) AS (a int, b int, c int);
1718 SELECT * FROM test_ret_set_rec_dyn(5) AS (a int, b numeric, c text);
1725 create function test_ret_rec_dyn(int) returns record as '
1730 SELECT INTO retval 5, 10, 15;
1733 SELECT INTO retval 50, 5::numeric, ''xxx''::text;
1736 END;' language plpgsql;
1737 SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int);
1743 SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text);
1750 -- Test some simple polymorphism cases.
1752 create function f1(x anyelement) returns anyelement as $$
1755 end$$ language plpgsql;
1756 select f1(42) as int, f1(4.5) as num;
1762 select f1(point(3,4)); -- fail for lack of + operator
1763 ERROR: operator does not exist: point + integer
1766 HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
1768 CONTEXT: PL/pgSQL function f1(anyelement) line 3 at RETURN
1769 drop function f1(x anyelement);
1770 create function f1(x anyelement) returns anyarray as $$
1772 return array[x + 1, x + 2];
1773 end$$ language plpgsql;
1774 select f1(42) as int, f1(4.5) as num;
1776 ---------+-----------
1780 drop function f1(x anyelement);
1781 create function f1(x anyarray) returns anyelement as $$
1784 end$$ language plpgsql;
1785 select f1(array[2,4]) as int, f1(array[4.5, 7.7]) as num;
1791 select f1(stavalues1) from pg_statistic; -- fail, can't infer element type
1792 ERROR: cannot determine element type of "anyarray" argument
1793 drop function f1(x anyarray);
1794 create function f1(x anyarray) returns anyarray as $$
1797 end$$ language plpgsql;
1798 select f1(array[2,4]) as int, f1(array[4.5, 7.7]) as num;
1804 select f1(stavalues1) from pg_statistic; -- fail, can't infer element type
1805 ERROR: PL/pgSQL functions cannot accept type anyarray
1806 CONTEXT: compilation of PL/pgSQL function "f1" near line 1
1807 drop function f1(x anyarray);
1808 -- fail, can't infer type:
1809 create function f1(x anyelement) returns anyrange as $$
1811 return array[x + 1, x + 2];
1812 end$$ language plpgsql;
1813 ERROR: cannot determine result data type
1814 DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
1815 create function f1(x anyrange) returns anyarray as $$
1817 return array[lower(x), upper(x)];
1818 end$$ language plpgsql;
1819 select f1(int4range(42, 49)) as int, f1(float8range(4.5, 7.8)) as num;
1821 ---------+-----------
1825 drop function f1(x anyrange);
1826 create function f1(x anycompatible, y anycompatible) returns anycompatiblearray as $$
1829 end$$ language plpgsql;
1830 select f1(2, 4) as int, f1(2, 4.5) as num;
1836 drop function f1(x anycompatible, y anycompatible);
1837 create function f1(x anycompatiblerange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
1839 return array[lower(x), upper(x), y, z];
1840 end$$ language plpgsql;
1841 select f1(int4range(42, 49), 11, 2::smallint) as int, f1(float8range(4.5, 7.8), 7.8, 11::real) as num;
1843 --------------+------------------
1844 {42,49,11,2} | {4.5,7.8,7.8,11}
1847 select f1(int4range(42, 49), 11, 4.5) as fail; -- range type doesn't fit
1848 ERROR: function f1(int4range, integer, numeric) does not exist
1849 LINE 1: select f1(int4range(42, 49), 11, 4.5) as fail;
1851 HINT: No function matches the given name and argument types. You might need to add explicit type casts.
1852 drop function f1(x anycompatiblerange, y anycompatible, z anycompatible);
1853 -- fail, can't infer type:
1854 create function f1(x anycompatible) returns anycompatiblerange as $$
1856 return array[x + 1, x + 2];
1857 end$$ language plpgsql;
1858 ERROR: cannot determine result data type
1859 DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
1860 create function f1(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
1863 end$$ language plpgsql;
1864 select f1(int4range(42, 49), array[11]) as int, f1(float8range(4.5, 7.8), array[7]) as num;
1866 ---------+-----------
1870 drop function f1(x anycompatiblerange, y anycompatiblearray);
1871 create function f1(a anyelement, b anyarray,
1872 c anycompatible, d anycompatible,
1873 OUT x anyarray, OUT y anycompatiblearray)
1878 end$$ language plpgsql;
1879 select x, pg_typeof(x), y, pg_typeof(y)
1880 from f1(11, array[1, 2], 42, 34.5);
1881 x | pg_typeof | y | pg_typeof
1882 ----------+-----------+-----------+-----------
1883 {11,1,2} | integer[] | {42,34.5} | numeric[]
1886 select x, pg_typeof(x), y, pg_typeof(y)
1887 from f1(11, array[1, 2], point(1,2), point(3,4));
1888 x | pg_typeof | y | pg_typeof
1889 ----------+-----------+-------------------+-----------
1890 {11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[]
1893 select x, pg_typeof(x), y, pg_typeof(y)
1894 from f1(11, '{1,2}', point(1,2), '(3,4)');
1895 x | pg_typeof | y | pg_typeof
1896 ----------+-----------+-------------------+-----------
1897 {11,1,2} | integer[] | {"(1,2)","(3,4)"} | point[]
1900 select x, pg_typeof(x), y, pg_typeof(y)
1901 from f1(11, array[1, 2.2], 42, 34.5); -- fail
1902 ERROR: function f1(integer, numeric[], integer, numeric) does not exist
1903 LINE 2: from f1(11, array[1, 2.2], 42, 34.5);
1905 HINT: No function matches the given name and argument types. You might need to add explicit type casts.
1906 drop function f1(a anyelement, b anyarray,
1907 c anycompatible, d anycompatible);
1909 -- Test handling of OUT parameters, including polymorphic cases.
1910 -- Note that RETURN is optional with OUT params; we try both ways.
1912 -- wrong way to do it:
1913 create function f1(in i int, out j int) returns int as $$
1916 end$$ language plpgsql;
1917 ERROR: RETURN cannot have a parameter in function with OUT parameters
1920 create function f1(in i int, out j int) as $$
1924 end$$ language plpgsql;
1931 select * from f1(42);
1937 create or replace function f1(inout i int) as $$
1940 end$$ language plpgsql;
1947 select * from f1(42);
1953 drop function f1(int);
1954 create function f1(in i int, out j int) returns setof int as $$
1961 end$$ language plpgsql;
1962 select * from f1(42);
1969 drop function f1(int);
1970 create function f1(in i int, out j int, out k text) as $$
1975 end$$ language plpgsql;
1982 select * from f1(42);
1988 drop function f1(int);
1989 create function f1(in i int, out j int, out k text) returns setof record as $$
1997 end$$ language plpgsql;
1998 select * from f1(42);
2005 drop function f1(int);
2006 create function duplic(in i anyelement, out j anyelement, out k anyarray) as $$
2011 end$$ language plpgsql;
2012 select * from duplic(42);
2018 select * from duplic('foo'::text);
2024 drop function duplic(anyelement);
2025 create function duplic(in i anycompatiblerange, out j anycompatible, out k anycompatiblearray) as $$
2028 k := array[lower(i),upper(i)];
2030 end$$ language plpgsql;
2031 select * from duplic(int4range(42,49));
2037 select * from duplic(textrange('aaa', 'bbb'));
2043 drop function duplic(anycompatiblerange);
2047 create table perform_test (
2051 create function perform_simple_func(int) returns boolean as '
2054 INSERT INTO perform_test VALUES ($1, $1 + 10);
2059 END;' language plpgsql;
2060 create function perform_test_func() returns void as '
2063 INSERT INTO perform_test VALUES (100, 100);
2066 PERFORM perform_simple_func(5);
2069 INSERT INTO perform_test VALUES (100, 100);
2072 PERFORM perform_simple_func(50);
2075 INSERT INTO perform_test VALUES (100, 100);
2079 END;' language plpgsql;
2080 SELECT perform_test_func();
2086 SELECT * FROM perform_test;
2094 drop table perform_test;
2096 -- Test proper snapshot handling in simple expressions
2098 create temp table users(login text, id serial);
2099 create function sp_id_user(a_login text) returns int as $$
2102 select into x id from users where login = a_login;
2103 if found then return x; end if;
2105 end$$ language plpgsql stable;
2106 insert into users values('user1');
2107 select sp_id_user('user1');
2113 select sp_id_user('userx');
2119 create function sp_add_user(a_login text) returns int as $$
2120 declare my_id_user int;
2122 my_id_user = sp_id_user( a_login );
2123 IF my_id_user > 0 THEN
2124 RETURN -1; -- error code for existing user
2126 INSERT INTO users ( login ) VALUES ( a_login );
2127 my_id_user = sp_id_user( a_login );
2128 IF my_id_user = 0 THEN
2129 RETURN -2; -- error code for insertion failure
2132 end$$ language plpgsql;
2133 select sp_add_user('user1');
2139 select sp_add_user('user2');
2145 select sp_add_user('user2');
2151 select sp_add_user('user3');
2157 select sp_add_user('user3');
2163 drop function sp_add_user(text);
2164 drop function sp_id_user(text);
2166 -- tests for refcursors
2168 create table rc_test (a int, b int);
2169 copy rc_test from stdin;
2170 create function return_unnamed_refcursor() returns refcursor as $$
2174 open rc for select a from rc_test;
2177 $$ language plpgsql;
2178 create function use_refcursor(rc refcursor) returns int as $$
2183 rc := return_unnamed_refcursor();
2184 fetch next from rc into x;
2187 $$ language plpgsql;
2188 select use_refcursor(return_unnamed_refcursor());
2194 create function return_refcursor(rc refcursor) returns refcursor as $$
2196 open rc for select a from rc_test;
2199 $$ language plpgsql;
2200 create function refcursor_test1(refcursor) returns refcursor as $$
2202 perform return_refcursor($1);
2205 $$ language plpgsql;
2207 select refcursor_test1('test1');
2213 fetch next in test1;
2219 select refcursor_test1('test2');
2225 fetch all from test2;
2235 fetch next from test1;
2236 ERROR: cursor "test1" does not exist
2237 create function refcursor_test2(int, int) returns boolean as $$
2239 c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
2243 fetch c1 into nonsense;
2251 $$ language plpgsql;
2252 select refcursor_test2(20000, 20000) as "Should be false",
2253 refcursor_test2(20, 20) as "Should be true";
2254 Should be false | Should be true
2255 -----------------+----------------
2260 create function constant_refcursor() returns refcursor as $$
2262 rc constant refcursor;
2264 open rc for select a from rc_test;
2267 $$ language plpgsql;
2268 select constant_refcursor();
2269 ERROR: variable "rc" is declared CONSTANT
2270 CONTEXT: PL/pgSQL function constant_refcursor() line 5 at OPEN
2271 -- but it's okay like this
2272 create or replace function constant_refcursor() returns refcursor as $$
2274 rc constant refcursor := 'my_cursor_name';
2276 open rc for select a from rc_test;
2279 $$ language plpgsql;
2280 select constant_refcursor();
2282 --------------------
2287 -- tests for cursors with named parameter arguments
2289 create function namedparmcursor_test1(int, int) returns boolean as $$
2291 c1 cursor (param1 int, param12 int) for select * from rc_test where a > param1 and b > param12;
2294 open c1(param12 := $2, param1 := $1);
2295 fetch c1 into nonsense;
2303 $$ language plpgsql;
2304 select namedparmcursor_test1(20000, 20000) as "Should be false",
2305 namedparmcursor_test1(20, 20) as "Should be true";
2306 Should be false | Should be true
2307 -----------------+----------------
2311 -- mixing named and positional argument notations
2312 create function namedparmcursor_test2(int, int) returns boolean as $$
2314 c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
2317 open c1(param1 := $1, $2);
2318 fetch c1 into nonsense;
2326 $$ language plpgsql;
2327 select namedparmcursor_test2(20, 20);
2328 namedparmcursor_test2
2329 -----------------------
2333 -- mixing named and positional: param2 is given twice, once in named notation
2334 -- and second time in positional notation. Should throw an error at parse time
2335 create function namedparmcursor_test3() returns void as $$
2337 c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
2339 open c1(param2 := 20, 21);
2341 $$ language plpgsql;
2342 ERROR: value for parameter "param2" of cursor "c1" specified more than once
2343 LINE 5: open c1(param2 := 20, 21);
2345 -- mixing named and positional: same as previous test, but param1 is duplicated
2346 create function namedparmcursor_test4() returns void as $$
2348 c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
2350 open c1(20, param1 := 21);
2352 $$ language plpgsql;
2353 ERROR: value for parameter "param1" of cursor "c1" specified more than once
2354 LINE 5: open c1(20, param1 := 21);
2356 -- duplicate named parameter, should throw an error at parse time
2357 create function namedparmcursor_test5() returns void as $$
2359 c1 cursor (p1 int, p2 int) for
2360 select * from tenk1 where thousand = p1 and tenthous = p2;
2362 open c1 (p2 := 77, p2 := 42);
2364 $$ language plpgsql;
2365 ERROR: value for parameter "p2" of cursor "c1" specified more than once
2366 LINE 6: open c1 (p2 := 77, p2 := 42);
2368 -- not enough parameters, should throw an error at parse time
2369 create function namedparmcursor_test6() returns void as $$
2371 c1 cursor (p1 int, p2 int) for
2372 select * from tenk1 where thousand = p1 and tenthous = p2;
2376 $$ language plpgsql;
2377 ERROR: not enough arguments for cursor "c1"
2378 LINE 6: open c1 (p2 := 77);
2380 -- division by zero runtime error, the context given in the error message
2381 -- should be sensible
2382 create function namedparmcursor_test7() returns void as $$
2384 c1 cursor (p1 int, p2 int) for
2385 select * from tenk1 where thousand = p1 and tenthous = p2;
2387 open c1 (p2 := 77, p1 := 42/0);
2388 end $$ language plpgsql;
2389 select namedparmcursor_test7();
2390 ERROR: division by zero
2391 CONTEXT: PL/pgSQL expression "42/0 AS p1, 77 AS p2"
2392 PL/pgSQL function namedparmcursor_test7() line 6 at OPEN
2393 -- check that line comments work correctly within the argument list
2394 -- (this used to require a special hack in the code; it no longer does,
2395 -- but let's keep the test anyway)
2396 create function namedparmcursor_test8() returns int4 as $$
2398 c1 cursor (p1 int, p2 int) for
2399 select count(*) from tenk1 where thousand = p1 and tenthous = p2;
2406 end $$ language plpgsql;
2407 select namedparmcursor_test8();
2408 namedparmcursor_test8
2409 -----------------------
2413 -- cursor parameter name can match plpgsql variable or unreserved keyword
2414 create function namedparmcursor_test9(p1 int) returns int4 as $$
2416 c1 cursor (p1 int, p2 int, debug int) for
2417 select count(*) from tenk1 where thousand = p1 and tenthous = p2
2422 open c1 (p1 := p1, p2 := p2, debug := 2);
2425 end $$ language plpgsql;
2426 select namedparmcursor_test9(6);
2427 namedparmcursor_test9
2428 -----------------------
2433 -- tests for "raise" processing
2435 create function raise_test1(int) returns int as $$
2437 raise notice 'This message has too many parameters!', $1;
2440 $$ language plpgsql;
2441 ERROR: too many parameters specified for RAISE
2442 CONTEXT: compilation of PL/pgSQL function "raise_test1" near line 3
2443 create function raise_test2(int) returns int as $$
2445 raise notice 'This message has too few parameters: %, %, %', $1, $1;
2448 $$ language plpgsql;
2449 ERROR: too few parameters specified for RAISE
2450 CONTEXT: compilation of PL/pgSQL function "raise_test2" near line 3
2451 create function raise_test3(int) returns int as $$
2453 raise notice 'This message has no parameters (despite having %% signs in it)!';
2456 $$ language plpgsql;
2457 select raise_test3(1);
2458 NOTICE: This message has no parameters (despite having % signs in it)!
2464 -- Test re-RAISE inside a nested exception block. This case is allowed
2465 -- by Oracle's PL/SQL but was handled differently by PG before 9.1.
2466 CREATE FUNCTION reraise_test() RETURNS void AS $$
2471 WHEN syntax_error THEN
2473 raise notice 'exception % thrown in inner block, reraising', sqlerrm;
2477 raise notice 'RIGHT - exception % caught in inner block', sqlerrm;
2482 raise notice 'WRONG - exception % caught in outer block', sqlerrm;
2484 $$ LANGUAGE plpgsql;
2485 SELECT reraise_test();
2486 NOTICE: exception syntax_error thrown in inner block, reraising
2487 NOTICE: RIGHT - exception syntax_error caught in inner block
2494 -- reject function definitions that contain malformed SQL queries at
2495 -- compile-time, where possible
2497 create function bad_sql1() returns int as $$
2504 end$$ language plpgsql;
2505 ERROR: syntax error at or near "Johnny"
2506 LINE 5: Johnny Yuma;
2508 create function bad_sql2() returns int as $$
2511 for r in select I fought the law, the law won LOOP
2512 raise notice 'in loop';
2515 end;$$ language plpgsql;
2516 ERROR: syntax error at or near "the"
2517 LINE 4: for r in select I fought the law, the law won LOOP
2519 -- a RETURN expression is mandatory, except for void-returning
2520 -- functions, where it is not allowed
2521 create function missing_return_expr() returns int as $$
2524 end;$$ language plpgsql;
2525 ERROR: missing expression at or near ";"
2528 create function void_return_expr() returns void as $$
2531 end;$$ language plpgsql;
2532 ERROR: RETURN cannot have a parameter in function returning void
2535 -- VOID functions are allowed to omit RETURN
2536 create function void_return_expr() returns void as $$
2539 end;$$ language plpgsql;
2540 select void_return_expr();
2546 -- but ordinary functions are not
2547 create function missing_return_expr() returns int as $$
2550 end;$$ language plpgsql;
2551 select missing_return_expr();
2552 ERROR: control reached end of function without RETURN
2553 CONTEXT: PL/pgSQL function missing_return_expr()
2554 drop function void_return_expr();
2555 drop function missing_return_expr();
2557 -- EXECUTE ... INTO test
2559 create table eifoo (i integer, y integer);
2560 create type eitype as (i integer, y integer);
2561 create or replace function execute_into_test(varchar) returns record as $$
2570 execute 'insert into '||$1||' values(10,15)';
2571 execute 'select (row).* from (select row(10,1)::eifoo) s' into _r;
2572 raise notice '% %', _r.i, _r.y;
2573 execute 'select * from '||$1||' limit 1' into _rt;
2574 raise notice '% %', _rt.i, _rt.y;
2575 execute 'select *, 20 from '||$1||' limit 1' into i, j, k;
2576 raise notice '% % %', i, j, k;
2577 execute 'select 1,2' into _v;
2579 end; $$ language plpgsql;
2580 select execute_into_test('eifoo');
2589 drop table eifoo cascade;
2590 drop type eitype cascade;
2592 -- SQLSTATE and SQLERRM test
2594 create function excpt_test1() returns void as $$
2596 raise notice '% %', sqlstate, sqlerrm;
2597 end; $$ language plpgsql;
2598 -- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION
2600 select excpt_test1();
2601 ERROR: column "sqlstate" does not exist
2605 CONTEXT: PL/pgSQL function excpt_test1() line 3 at RAISE
2606 create function excpt_test2() returns void as $$
2610 raise notice '% %', sqlstate, sqlerrm;
2613 end; $$ language plpgsql;
2615 select excpt_test2();
2616 ERROR: column "sqlstate" does not exist
2620 CONTEXT: PL/pgSQL function excpt_test2() line 5 at RAISE
2621 create function excpt_test3() returns void as $$
2624 raise exception 'user exception';
2625 exception when others then
2626 raise notice 'caught exception % %', sqlstate, sqlerrm;
2628 raise notice '% %', sqlstate, sqlerrm;
2631 when substring_error then
2632 -- this exception handler shouldn't be invoked
2633 raise notice 'unexpected exception: % %', sqlstate, sqlerrm;
2634 when division_by_zero then
2635 raise notice 'caught exception % %', sqlstate, sqlerrm;
2637 raise notice '% %', sqlstate, sqlerrm;
2639 end; $$ language plpgsql;
2640 select excpt_test3();
2641 NOTICE: caught exception P0001 user exception
2642 NOTICE: P0001 user exception
2643 NOTICE: caught exception 22012 division by zero
2644 NOTICE: P0001 user exception
2650 create function excpt_test4() returns text as $$
2653 exception when others then return sqlerrm; end;
2654 end; $$ language plpgsql;
2655 select excpt_test4();
2661 drop function excpt_test1();
2662 drop function excpt_test2();
2663 drop function excpt_test3();
2664 drop function excpt_test4();
2665 -- parameters of raise stmt can be expressions
2666 create function raise_exprs() returns void as $$
2668 a integer[] = '{10,20,30}';
2673 raise notice '%; %; %; %; %; %', a, a[i], c, (select c || 'abc'), row(10,'aaa',NULL,30), NULL;
2674 end;$$ language plpgsql;
2675 select raise_exprs();
2676 NOTICE: {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); <NULL>
2682 drop function raise_exprs();
2683 -- regression test: verify that multiple uses of same plpgsql datum within
2684 -- a SQL command all get mapped to the same $n parameter. The return value
2685 -- of the SELECT is not important, we only care that it doesn't fail with
2686 -- a complaint about an ungrouped column reference.
2687 create function multi_datum_use(p1 int) returns bool as $$
2692 select into x,y unique1/p1, unique1/$1 from tenk1 group by unique1/p1;
2694 end$$ language plpgsql;
2695 select multi_datum_use(42);
2702 -- Test STRICT limiter in both planned and EXECUTE invocations.
2703 -- Note that a data-modifying query is quasi strict (disallow multi rows)
2704 -- by default in the planned case, but not in EXECUTE.
2706 create temp table foo (f1 int, f2 int);
2707 insert into foo values (1,2), (3,4);
2708 create or replace function stricttest() returns void as $$
2712 insert into foo values(5,6) returning * into x;
2713 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2714 end$$ language plpgsql;
2715 select stricttest();
2716 NOTICE: x.f1 = 5, x.f2 = 6
2722 create or replace function stricttest() returns void as $$
2725 -- should fail due to implicit strict
2726 insert into foo values(7,8),(9,10) returning * into x;
2727 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2728 end$$ language plpgsql;
2729 select stricttest();
2730 ERROR: query returned more than one row
2731 HINT: Make sure the query returns a single row, or use LIMIT 1.
2732 CONTEXT: PL/pgSQL function stricttest() line 5 at SQL statement
2733 create or replace function stricttest() returns void as $$
2737 execute 'insert into foo values(5,6) returning *' into x;
2738 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2739 end$$ language plpgsql;
2740 select stricttest();
2741 NOTICE: x.f1 = 5, x.f2 = 6
2747 create or replace function stricttest() returns void as $$
2750 -- this should work since EXECUTE isn't as picky
2751 execute 'insert into foo values(7,8),(9,10) returning *' into x;
2752 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2753 end$$ language plpgsql;
2754 select stricttest();
2755 NOTICE: x.f1 = 7, x.f2 = 8
2772 create or replace function stricttest() returns void as $$
2776 select * from foo where f1 = 3 into strict x;
2777 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2778 end$$ language plpgsql;
2779 select stricttest();
2780 NOTICE: x.f1 = 3, x.f2 = 4
2786 create or replace function stricttest() returns void as $$
2789 -- should fail, no rows
2790 select * from foo where f1 = 0 into strict x;
2791 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2792 end$$ language plpgsql;
2793 select stricttest();
2794 ERROR: query returned no rows
2795 CONTEXT: PL/pgSQL function stricttest() line 5 at SQL statement
2796 create or replace function stricttest() returns void as $$
2799 -- should fail, too many rows
2800 select * from foo where f1 > 3 into strict x;
2801 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2802 end$$ language plpgsql;
2803 select stricttest();
2804 ERROR: query returned more than one row
2805 HINT: Make sure the query returns a single row, or use LIMIT 1.
2806 CONTEXT: PL/pgSQL function stricttest() line 5 at SQL statement
2807 create or replace function stricttest() returns void as $$
2811 execute 'select * from foo where f1 = 3' into strict x;
2812 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2813 end$$ language plpgsql;
2814 select stricttest();
2815 NOTICE: x.f1 = 3, x.f2 = 4
2821 create or replace function stricttest() returns void as $$
2824 -- should fail, no rows
2825 execute 'select * from foo where f1 = 0' into strict x;
2826 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2827 end$$ language plpgsql;
2828 select stricttest();
2829 ERROR: query returned no rows
2830 CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE
2831 create or replace function stricttest() returns void as $$
2834 -- should fail, too many rows
2835 execute 'select * from foo where f1 > 3' into strict x;
2836 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2837 end$$ language plpgsql;
2838 select stricttest();
2839 ERROR: query returned more than one row
2840 CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE
2841 drop function stricttest();
2842 -- test printing parameters after failure due to STRICT
2843 set plpgsql.print_strict_params to true;
2844 create or replace function stricttest() returns void as $$
2851 select * from foo where f1 = p1 and f1::text = p3 into strict x;
2852 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2853 end$$ language plpgsql;
2854 select stricttest();
2855 ERROR: query returned no rows
2856 DETAIL: parameters: p1 = '2', p3 = 'foo'
2857 CONTEXT: PL/pgSQL function stricttest() line 8 at SQL statement
2858 create or replace function stricttest() returns void as $$
2862 p3 text := $a$'Valame Dios!' dijo Sancho; 'no le dije yo a vuestra merced que mirase bien lo que hacia?'$a$;
2865 select * from foo where f1 = p1 and f1::text = p3 into strict x;
2866 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2867 end$$ language plpgsql;
2868 select stricttest();
2869 ERROR: query returned no rows
2870 DETAIL: parameters: p1 = '2', p3 = '''Valame Dios!'' dijo Sancho; ''no le dije yo a vuestra merced que mirase bien lo que hacia?'''
2871 CONTEXT: PL/pgSQL function stricttest() line 8 at SQL statement
2872 create or replace function stricttest() returns void as $$
2879 select * from foo where f1 > p1 or f1::text = p3 into strict x;
2880 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2881 end$$ language plpgsql;
2882 select stricttest();
2883 ERROR: query returned more than one row
2884 DETAIL: parameters: p1 = '2', p3 = 'foo'
2885 HINT: Make sure the query returns a single row, or use LIMIT 1.
2886 CONTEXT: PL/pgSQL function stricttest() line 8 at SQL statement
2887 create or replace function stricttest() returns void as $$
2890 -- too many rows, no params
2891 select * from foo where f1 > 3 into strict x;
2892 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2893 end$$ language plpgsql;
2894 select stricttest();
2895 ERROR: query returned more than one row
2896 HINT: Make sure the query returns a single row, or use LIMIT 1.
2897 CONTEXT: PL/pgSQL function stricttest() line 5 at SQL statement
2898 create or replace function stricttest() returns void as $$
2902 execute 'select * from foo where f1 = $1 or f1::text = $2' using 0, 'foo' into strict x;
2903 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2904 end$$ language plpgsql;
2905 select stricttest();
2906 ERROR: query returned no rows
2907 DETAIL: parameters: $1 = '0', $2 = 'foo'
2908 CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE
2909 create or replace function stricttest() returns void as $$
2913 execute 'select * from foo where f1 > $1' using 1 into strict x;
2914 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2915 end$$ language plpgsql;
2916 select stricttest();
2917 ERROR: query returned more than one row
2918 DETAIL: parameters: $1 = '1'
2919 CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE
2920 create or replace function stricttest() returns void as $$
2923 -- too many rows, no parameters
2924 execute 'select * from foo where f1 > 3' into strict x;
2925 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2926 end$$ language plpgsql;
2927 select stricttest();
2928 ERROR: query returned more than one row
2929 CONTEXT: PL/pgSQL function stricttest() line 5 at EXECUTE
2930 create or replace function stricttest() returns void as $$
2931 -- override the global
2932 #print_strict_params off
2939 select * from foo where f1 > p1 or f1::text = p3 into strict x;
2940 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2941 end$$ language plpgsql;
2942 select stricttest();
2943 ERROR: query returned more than one row
2944 HINT: Make sure the query returns a single row, or use LIMIT 1.
2945 CONTEXT: PL/pgSQL function stricttest() line 10 at SQL statement
2946 reset plpgsql.print_strict_params;
2947 create or replace function stricttest() returns void as $$
2948 -- override the global
2949 #print_strict_params on
2956 select * from foo where f1 > p1 or f1::text = p3 into strict x;
2957 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2958 end$$ language plpgsql;
2959 select stricttest();
2960 ERROR: query returned more than one row
2961 DETAIL: parameters: p1 = '2', p3 = 'foo'
2962 HINT: Make sure the query returns a single row, or use LIMIT 1.
2963 CONTEXT: PL/pgSQL function stricttest() line 10 at SQL statement
2964 -- test warnings and errors
2965 set plpgsql.extra_warnings to 'all';
2966 set plpgsql.extra_warnings to 'none';
2967 set plpgsql.extra_errors to 'all';
2968 set plpgsql.extra_errors to 'none';
2969 -- test warnings when shadowing a variable
2970 set plpgsql.extra_warnings to 'shadowed_variables';
2971 -- simple shadowing of input and output parameters
2972 create or replace function shadowtest(in1 int)
2973 returns table (out1 int) as $$
2979 $$ language plpgsql;
2980 WARNING: variable "in1" shadows a previously defined variable
2983 WARNING: variable "out1" shadows a previously defined variable
2986 select shadowtest(1);
2991 set plpgsql.extra_warnings to 'shadowed_variables';
2992 select shadowtest(1);
2997 create or replace function shadowtest(in1 int)
2998 returns table (out1 int) as $$
3004 $$ language plpgsql;
3005 WARNING: variable "in1" shadows a previously defined variable
3008 WARNING: variable "out1" shadows a previously defined variable
3011 select shadowtest(1);
3016 drop function shadowtest(int);
3017 -- shadowing in a second DECLARE block
3018 create or replace function shadowtest()
3027 end$$ language plpgsql;
3028 WARNING: variable "f1" shadows a previously defined variable
3031 drop function shadowtest();
3032 -- several levels of shadowing
3033 create or replace function shadowtest(in1 int)
3042 end$$ language plpgsql;
3043 WARNING: variable "in1" shadows a previously defined variable
3046 WARNING: variable "in1" shadows a previously defined variable
3049 drop function shadowtest(int);
3050 -- shadowing in cursor definitions
3051 create or replace function shadowtest()
3055 c1 cursor (f1 int) for select 1;
3057 end$$ language plpgsql;
3058 WARNING: variable "f1" shadows a previously defined variable
3059 LINE 5: c1 cursor (f1 int) for select 1;
3061 drop function shadowtest();
3062 -- test errors when shadowing a variable
3063 set plpgsql.extra_errors to 'shadowed_variables';
3064 create or replace function shadowtest(f1 int)
3065 returns boolean as $$
3066 declare f1 int; begin return 1; end $$ language plpgsql;
3067 ERROR: variable "f1" shadows a previously defined variable
3068 LINE 3: declare f1 int; begin return 1; end $$ language plpgsql;
3070 select shadowtest(1);
3071 ERROR: function shadowtest(integer) does not exist
3072 LINE 1: select shadowtest(1);
3074 HINT: No function matches the given name and argument types. You might need to add explicit type casts.
3075 reset plpgsql.extra_errors;
3076 reset plpgsql.extra_warnings;
3077 create or replace function shadowtest(f1 int)
3078 returns boolean as $$
3079 declare f1 int; begin return 1; end $$ language plpgsql;
3080 select shadowtest(1);
3086 -- runtime extra checks
3087 set plpgsql.extra_warnings to 'too_many_rows';
3091 select v from generate_series(1,2) g(v) into x;
3094 WARNING: query returned more than one row
3095 HINT: Make sure the query returns a single row, or use LIMIT 1.
3096 set plpgsql.extra_errors to 'too_many_rows';
3100 select v from generate_series(1,2) g(v) into x;
3103 ERROR: query returned more than one row
3104 HINT: Make sure the query returns a single row, or use LIMIT 1.
3105 CONTEXT: PL/pgSQL function inline_code_block line 4 at SQL statement
3106 reset plpgsql.extra_errors;
3107 reset plpgsql.extra_warnings;
3108 set plpgsql.extra_warnings to 'strict_multi_assignment';
3115 select 1,2 into x, y;
3116 select 1,2,3 into x, y;
3119 WARNING: number of source and target fields in assignment does not match
3120 DETAIL: strict_multi_assignment check of extra_warnings is active.
3121 HINT: Make sure the query returns the exact list of columns.
3122 WARNING: number of source and target fields in assignment does not match
3123 DETAIL: strict_multi_assignment check of extra_warnings is active.
3124 HINT: Make sure the query returns the exact list of columns.
3125 set plpgsql.extra_errors to 'strict_multi_assignment';
3132 select 1,2 into x, y;
3133 select 1,2,3 into x, y;
3136 ERROR: number of source and target fields in assignment does not match
3137 DETAIL: strict_multi_assignment check of extra_errors is active.
3138 HINT: Make sure the query returns the exact list of columns.
3139 CONTEXT: PL/pgSQL function inline_code_block line 6 at SQL statement
3140 create table test_01(a int, b int, c int);
3141 alter table test_01 drop column a;
3142 -- the check is active only when source table is not empty
3143 insert into test_01 values(10,20);
3149 select * from test_01 into x, y; -- should be ok
3151 select * from test_01 into x; -- should to fail
3155 ERROR: number of source and target fields in assignment does not match
3156 DETAIL: strict_multi_assignment check of extra_errors is active.
3157 HINT: Make sure the query returns the exact list of columns.
3158 CONTEXT: PL/pgSQL function inline_code_block line 8 at SQL statement
3163 select 1, 2 into t; -- should be ok
3165 select 1, 2, 3 into t; -- should fail;
3169 ERROR: number of source and target fields in assignment does not match
3170 DETAIL: strict_multi_assignment check of extra_errors is active.
3171 HINT: Make sure the query returns the exact list of columns.
3172 CONTEXT: PL/pgSQL function inline_code_block line 7 at SQL statement
3177 select 1 into t; -- should fail;
3180 ERROR: number of source and target fields in assignment does not match
3181 DETAIL: strict_multi_assignment check of extra_errors is active.
3182 HINT: Make sure the query returns the exact list of columns.
3183 CONTEXT: PL/pgSQL function inline_code_block line 5 at SQL statement
3185 reset plpgsql.extra_errors;
3186 reset plpgsql.extra_warnings;
3187 -- test scrollable cursor support
3188 create function sc_test() returns setof integer as $$
3190 c scroll cursor for select f1 from int4_tbl;
3194 fetch last from c into x;
3197 fetch prior from c into x;
3201 $$ language plpgsql;
3202 select * from sc_test();
3212 create or replace function sc_test() returns setof integer as $$
3214 c no scroll cursor for select f1 from int4_tbl;
3218 fetch last from c into x;
3221 fetch prior from c into x;
3225 $$ language plpgsql;
3226 select * from sc_test(); -- fails because of NO SCROLL specification
3227 ERROR: cursor can only scan forward
3228 HINT: Declare it with SCROLL option to enable backward scan.
3229 CONTEXT: PL/pgSQL function sc_test() line 7 at FETCH
3230 create or replace function sc_test() returns setof integer as $$
3235 open c scroll for select f1 from int4_tbl;
3236 fetch last from c into x;
3239 fetch prior from c into x;
3243 $$ language plpgsql;
3244 select * from sc_test();
3254 create or replace function sc_test() returns setof integer as $$
3259 open c scroll for execute 'select f1 from int4_tbl';
3260 fetch last from c into x;
3263 fetch relative -2 from c into x;
3267 $$ language plpgsql;
3268 select * from sc_test();
3276 create or replace function sc_test() returns setof integer as $$
3281 open c scroll for execute 'select f1 from int4_tbl';
3282 fetch last from c into x;
3285 move backward 2 from c;
3286 fetch relative -1 from c into x;
3290 $$ language plpgsql;
3291 select * from sc_test();
3298 create or replace function sc_test() returns setof integer as $$
3300 c cursor for select * from generate_series(1, 10);
3305 move relative 2 in c;
3309 fetch next from c into x;
3316 $$ language plpgsql;
3317 select * from sc_test();
3325 create or replace function sc_test() returns setof integer as $$
3327 c cursor for select * from generate_series(1, 10);
3331 move forward all in c;
3332 fetch backward from c into x;
3338 $$ language plpgsql;
3339 select * from sc_test();
3345 drop function sc_test();
3346 -- test qualified variable names
3347 create function pl_qual_names (param1 int) returns void as $$
3356 raise notice 'param1 = %', param1;
3357 raise notice 'pl_qual_names.param1 = %', pl_qual_names.param1;
3358 raise notice 'outerblock.param1 = %', outerblock.param1;
3359 raise notice 'innerblock.param1 = %', innerblock.param1;
3362 $$ language plpgsql;
3363 select pl_qual_names(42);
3365 NOTICE: pl_qual_names.param1 = 42
3366 NOTICE: outerblock.param1 = 1
3367 NOTICE: innerblock.param1 = 2
3373 drop function pl_qual_names(int);
3374 -- tests for RETURN QUERY
3375 create function ret_query1(out int, out int) returns setof record as $$
3380 return query select x + 1, x * 10 from generate_series(0, 10) s (x);
3383 $$ language plpgsql;
3384 select * from ret_query1();
3402 create type record_type as (x text, y int, z boolean);
3403 create or replace function ret_query2(lim int) returns setof record_type as $$
3405 return query select fipshash(s.x::text), s.x, s.x > 0
3406 from generate_series(-8, lim) s (x) where s.x % 2 = 0;
3408 $$ language plpgsql;
3409 select * from ret_query2(8);
3411 ----------------------------------+----+---
3412 e91592205d3881e3ea35d66973bb4898 | -8 | f
3413 03b26944890929ff751653acb2f2af79 | -6 | f
3414 e5e0093f285a4fb94c3fcc2ad7fd04ed | -4 | f
3415 cf3bae39dd692048a8bf961182e6a34d | -2 | f
3416 5feceb66ffc86f38d952786c6d696c79 | 0 | f
3417 d4735e3a265e16eee03f59718b9b5d03 | 2 | t
3418 4b227777d4dd1fc61c6f884f48641d02 | 4 | t
3419 e7f6c011776e8db7cd330b54174fd76f | 6 | t
3420 2c624232cdd221771294dfbb310aca00 | 8 | t
3423 -- test EXECUTE USING
3424 create function exc_using(int, text) returns int as $$
3427 for i in execute 'select * from generate_series(1,$1)' using $1+1 loop
3428 raise notice '%', i;
3430 execute 'select $2 + $2*3 + length($1)' into i using $2,$1;
3433 $$ language plpgsql;
3434 select exc_using(5, 'foobar');
3446 drop function exc_using(int, text);
3447 create or replace function exc_using(int) returns void as $$
3452 open c for execute 'select * from generate_series(1,$1)' using $1+1;
3455 exit when not found;
3456 raise notice '%', i;
3461 $$ language plpgsql;
3462 select exc_using(5);
3474 drop function exc_using(int);
3475 -- test FOR-over-cursor
3476 create or replace function forc01() returns void as $$
3478 c cursor(r1 integer, r2 integer)
3479 for select * from generate_series(r1,r2) i;
3481 for select * from generate_series(41,43) i;
3483 -- assign portal names to cursors to get stable output
3486 for r in c(5,7) loop
3487 raise notice '% from %', r.i, c;
3489 -- again, to test if cursor was closed properly
3490 for r in c(9,10) loop
3491 raise notice '% from %', r.i, c;
3493 -- and test a parameterless cursor
3495 raise notice '% from %', r.i, c2;
3497 -- and try it with a hand-assigned name
3498 raise notice 'after loop, c2 = %', c2;
3499 c2 := 'special_name';
3501 raise notice '% from %', r.i, c2;
3503 raise notice 'after loop, c2 = %', c2;
3504 -- and try it with a generated name
3505 -- (which we can't show in the output because it's variable)
3508 raise notice '%', r.i;
3510 raise notice 'after loop, c2 = %', c2;
3513 $$ language plpgsql;
3523 NOTICE: after loop, c2 = c2
3524 NOTICE: 41 from special_name
3525 NOTICE: 42 from special_name
3526 NOTICE: 43 from special_name
3527 NOTICE: after loop, c2 = special_name
3531 NOTICE: after loop, c2 = <NULL>
3537 -- try updating the cursor's current row
3538 create temp table forc_test as
3539 select n as i, n as j from generate_series(1,10) n;
3540 create or replace function forc01() returns void as $$
3542 c cursor for select * from forc_test;
3545 raise notice '%, %', r.i, r.j;
3546 update forc_test set i = i * 100, j = r.j * 2 where current of c;
3549 $$ language plpgsql;
3566 select * from forc_test;
3581 -- same, with a cursor whose portal name doesn't match variable name
3582 create or replace function forc01() returns void as $$
3584 c refcursor := 'fooled_ya';
3587 open c for select * from forc_test;
3590 exit when not found;
3591 raise notice '%, %', r.i, r.j;
3592 update forc_test set i = i * 100, j = r.j * 2 where current of c;
3595 $$ language plpgsql;
3612 select * from forc_test;
3627 drop function forc01();
3628 -- it's okay to re-use a cursor variable name, even when bound
3630 declare cnt int := 0;
3631 c1 cursor for select * from forc_test;
3634 declare c1 cursor for select * from forc_test;
3641 raise notice 'cnt = %', cnt;
3644 -- fail because cursor has no query bound to it
3645 create or replace function forc_bad() returns void as $$
3650 raise notice '%', r.i;
3653 $$ language plpgsql;
3654 ERROR: cursor FOR loop must use a bound cursor variable
3655 LINE 5: for r in c loop
3657 -- test RETURN QUERY EXECUTE
3658 create or replace function return_dquery()
3659 returns setof int as $$
3661 return query execute 'select * from (values(10),(20)) f';
3662 return query execute 'select * from (values($1),($2)) f' using 40,50;
3664 $$ language plpgsql;
3665 select * from return_dquery();
3674 drop function return_dquery();
3675 -- test RETURN QUERY with dropped columns
3676 create table tabwithcols(a int, b int, c int, d int);
3677 insert into tabwithcols values(10,20,30,40),(50,60,70,80);
3678 create or replace function returnqueryf()
3679 returns setof tabwithcols as $$
3681 return query select * from tabwithcols;
3682 return query execute 'select * from tabwithcols';
3684 $$ language plpgsql;
3685 select * from returnqueryf();
3694 alter table tabwithcols drop column b;
3695 select * from returnqueryf();
3704 alter table tabwithcols drop column d;
3705 select * from returnqueryf();
3714 alter table tabwithcols add column d int;
3715 select * from returnqueryf();
3724 drop function returnqueryf();
3725 drop table tabwithcols;
3727 -- Tests for composite-type results
3729 create type compostype as (x int, y varchar);
3730 -- test: use of variable of composite type in return statement
3731 create or replace function compos() returns compostype as $$
3738 $$ language plpgsql;
3745 -- test: use of variable of record type in return statement
3746 create or replace function compos() returns compostype as $$
3750 v := (1, 'hello'::varchar);
3753 $$ language plpgsql;
3760 -- test: use of row expr in return statement
3761 create or replace function compos() returns compostype as $$
3763 return (1, 'hello'::varchar);
3765 $$ language plpgsql;
3772 -- this does not work currently (no implicit casting)
3773 create or replace function compos() returns compostype as $$
3775 return (1, 'hello');
3777 $$ language plpgsql;
3779 ERROR: returned record type does not match expected record type
3780 DETAIL: Returned type unknown does not match expected type character varying in column 2.
3781 CONTEXT: PL/pgSQL function compos() while casting return value to function's return type
3782 -- ... but this does
3783 create or replace function compos() returns compostype as $$
3785 return (1, 'hello')::compostype;
3787 $$ language plpgsql;
3794 drop function compos();
3795 -- test: return a row expr as record.
3796 create or replace function composrec() returns record as $$
3803 $$ language plpgsql;
3810 -- test: return row expr in return statement.
3811 create or replace function composrec() returns record as $$
3813 return (1, 'hello');
3815 $$ language plpgsql;
3822 drop function composrec();
3823 -- test: row expr in RETURN NEXT statement.
3824 create or replace function compos() returns setof compostype as $$
3828 return next (1, 'hello'::varchar);
3830 return next null::compostype;
3831 return next (2, 'goodbye')::compostype;
3833 $$ language plpgsql;
3834 select * from compos();
3844 drop function compos();
3845 -- test: use invalid expr in return statement.
3846 create or replace function compos() returns compostype as $$
3850 $$ language plpgsql;
3852 ERROR: cannot return non-composite value from function returning composite type
3853 CONTEXT: PL/pgSQL function compos() line 3 at RETURN
3854 -- RETURN variable is a different code path ...
3855 create or replace function compos() returns compostype as $$
3856 declare x int := 42;
3860 $$ language plpgsql;
3861 select * from compos();
3862 ERROR: cannot return non-composite value from function returning composite type
3863 CONTEXT: PL/pgSQL function compos() line 4 at RETURN
3864 drop function compos();
3865 -- test: invalid use of composite variable in scalar-returning function
3866 create or replace function compos() returns int as $$
3873 $$ language plpgsql;
3875 ERROR: invalid input syntax for type integer: "(1,hello)"
3876 CONTEXT: PL/pgSQL function compos() while casting return value to function's return type
3877 -- test: invalid use of composite expression in scalar-returning function
3878 create or replace function compos() returns int as $$
3880 return (1, 'hello')::compostype;
3882 $$ language plpgsql;
3884 ERROR: invalid input syntax for type integer: "(1,hello)"
3885 CONTEXT: PL/pgSQL function compos() while casting return value to function's return type
3886 drop function compos();
3887 drop type compostype;
3889 -- Tests for 8.4's new RAISE features
3891 create or replace function raise_test() returns void as $$
3893 raise notice '% % %', 1, 2, 3
3894 using errcode = '55001', detail = 'some detail info', hint = 'some hint';
3895 raise '% % %', 1, 2, 3
3896 using errcode = 'division_by_zero', detail = 'some detail info';
3898 $$ language plpgsql;
3899 select raise_test();
3901 DETAIL: some detail info
3904 DETAIL: some detail info
3905 CONTEXT: PL/pgSQL function raise_test() line 5 at RAISE
3906 -- Since we can't actually see the thrown SQLSTATE in default psql output,
3907 -- test it like this; this also tests re-RAISE
3908 create or replace function raise_test() returns void as $$
3911 using errcode = 'division_by_zero', detail = 'some detail info';
3914 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3917 $$ language plpgsql;
3918 select raise_test();
3919 NOTICE: SQLSTATE: 22012 SQLERRM: check me
3921 DETAIL: some detail info
3922 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
3923 create or replace function raise_test() returns void as $$
3926 using errcode = '1234F', detail = 'some detail info';
3929 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3932 $$ language plpgsql;
3933 select raise_test();
3934 NOTICE: SQLSTATE: 1234F SQLERRM: check me
3936 DETAIL: some detail info
3937 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
3938 -- SQLSTATE specification in WHEN
3939 create or replace function raise_test() returns void as $$
3942 using errcode = '1234F', detail = 'some detail info';
3944 when sqlstate '1234F' then
3945 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3948 $$ language plpgsql;
3949 select raise_test();
3950 NOTICE: SQLSTATE: 1234F SQLERRM: check me
3952 DETAIL: some detail info
3953 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
3954 create or replace function raise_test() returns void as $$
3956 raise division_by_zero using detail = 'some detail info';
3959 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3962 $$ language plpgsql;
3963 select raise_test();
3964 NOTICE: SQLSTATE: 22012 SQLERRM: division_by_zero
3965 ERROR: division_by_zero
3966 DETAIL: some detail info
3967 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
3968 create or replace function raise_test() returns void as $$
3970 raise division_by_zero;
3972 $$ language plpgsql;
3973 select raise_test();
3974 ERROR: division_by_zero
3975 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
3976 create or replace function raise_test() returns void as $$
3978 raise sqlstate '1234F';
3980 $$ language plpgsql;
3981 select raise_test();
3983 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
3984 create or replace function raise_test() returns void as $$
3986 raise division_by_zero using message = 'custom' || ' message';
3988 $$ language plpgsql;
3989 select raise_test();
3990 ERROR: custom message
3991 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
3992 create or replace function raise_test() returns void as $$
3994 raise using message = 'custom' || ' message', errcode = '22012';
3996 $$ language plpgsql;
3997 select raise_test();
3998 ERROR: custom message
3999 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
4000 -- conflict on message
4001 create or replace function raise_test() returns void as $$
4003 raise notice 'some message' using message = 'custom' || ' message', errcode = '22012';
4005 $$ language plpgsql;
4006 select raise_test();
4007 ERROR: RAISE option already specified: MESSAGE
4008 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
4009 -- conflict on errcode
4010 create or replace function raise_test() returns void as $$
4012 raise division_by_zero using message = 'custom' || ' message', errcode = '22012';
4014 $$ language plpgsql;
4015 select raise_test();
4016 ERROR: RAISE option already specified: ERRCODE
4017 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
4018 -- nothing to re-RAISE
4019 create or replace function raise_test() returns void as $$
4023 $$ language plpgsql;
4024 select raise_test();
4025 ERROR: RAISE without parameters cannot be used outside an exception handler
4026 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
4027 -- test access to exception data
4028 create function zero_divide() returns int as $$
4033 $$ language plpgsql parallel safe;
4034 create or replace function raise_test() returns void as $$
4036 raise exception 'custom exception'
4037 using detail = 'some detail of custom exception',
4038 hint = 'some hint related to custom exception';
4040 $$ language plpgsql;
4041 create function stacked_diagnostics_test() returns void as $$
4042 declare _sqlstate text;
4046 perform zero_divide();
4047 exception when others then
4048 get stacked diagnostics
4049 _sqlstate = returned_sqlstate,
4050 _message = message_text,
4051 _context = pg_exception_context;
4052 raise notice 'sqlstate: %, message: %, context: [%]',
4053 _sqlstate, _message, replace(_context, E'\n', ' <- ');
4055 $$ language plpgsql;
4056 select stacked_diagnostics_test();
4057 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]
4058 stacked_diagnostics_test
4059 --------------------------
4063 create or replace function stacked_diagnostics_test() returns void as $$
4064 declare _detail text;
4068 perform raise_test();
4069 exception when others then
4070 get stacked diagnostics
4071 _message = message_text,
4072 _detail = pg_exception_detail,
4073 _hint = pg_exception_hint;
4074 raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
4076 $$ language plpgsql;
4077 select stacked_diagnostics_test();
4078 NOTICE: message: custom exception, detail: some detail of custom exception, hint: some hint related to custom exception
4079 stacked_diagnostics_test
4080 --------------------------
4084 -- fail, cannot use stacked diagnostics statement outside handler
4085 create or replace function stacked_diagnostics_test() returns void as $$
4086 declare _detail text;
4090 get stacked diagnostics
4091 _message = message_text,
4092 _detail = pg_exception_detail,
4093 _hint = pg_exception_hint;
4094 raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
4096 $$ language plpgsql;
4097 select stacked_diagnostics_test();
4098 ERROR: GET STACKED DIAGNOSTICS cannot be used outside an exception handler
4099 CONTEXT: PL/pgSQL function stacked_diagnostics_test() line 6 at GET STACKED DIAGNOSTICS
4100 drop function stacked_diagnostics_test();
4101 -- Test that an error recovery subtransaction is parallel safe
4102 create function error_trap_test() returns text as $$
4104 perform zero_divide();
4105 return 'no error detected!';
4106 exception when division_by_zero then
4107 return 'division_by_zero detected';
4109 $$ language plpgsql parallel safe;
4110 set debug_parallel_query to on;
4111 explain (verbose, costs off) select error_trap_test();
4113 -----------------------------------
4115 Output: (error_trap_test())
4119 Output: error_trap_test()
4122 select error_trap_test();
4124 ---------------------------
4125 division_by_zero detected
4128 reset debug_parallel_query;
4129 drop function error_trap_test();
4130 drop function zero_divide();
4131 -- check cases where implicit SQLSTATE variable could be confused with
4132 -- SQLSTATE as a keyword, cf bug #5524
4133 create or replace function raise_test() returns void as $$
4137 when sqlstate '22012' then
4138 raise notice using message = sqlstate;
4139 raise sqlstate '22012' using message = 'substitute message';
4141 $$ language plpgsql;
4142 select raise_test();
4144 ERROR: substitute message
4145 CONTEXT: PL/pgSQL function raise_test() line 7 at RAISE
4146 drop function raise_test();
4147 -- test passing column_name, constraint_name, datatype_name, table_name
4148 -- and schema_name error fields
4149 create or replace function stacked_diagnostics_test() returns void as $$
4150 declare _column_name text;
4151 _constraint_name text;
4152 _datatype_name text;
4156 raise exception using
4157 column = '>>some column name<<',
4158 constraint = '>>some constraint name<<',
4159 datatype = '>>some datatype name<<',
4160 table = '>>some table name<<',
4161 schema = '>>some schema name<<';
4162 exception when others then
4163 get stacked diagnostics
4164 _column_name = column_name,
4165 _constraint_name = constraint_name,
4166 _datatype_name = pg_datatype_name,
4167 _table_name = table_name,
4168 _schema_name = schema_name;
4169 raise notice 'column %, constraint %, type %, table %, schema %',
4170 _column_name, _constraint_name, _datatype_name, _table_name, _schema_name;
4172 $$ language plpgsql;
4173 select stacked_diagnostics_test();
4174 NOTICE: column >>some column name<<, constraint >>some constraint name<<, type >>some datatype name<<, table >>some table name<<, schema >>some schema name<<
4175 stacked_diagnostics_test
4176 --------------------------
4180 drop function stacked_diagnostics_test();
4181 -- test variadic functions
4182 create or replace function vari(variadic int[])
4185 for i in array_lower($1,1)..array_upper($1,1) loop
4186 raise notice '%', $1[i];
4188 $$ language plpgsql;
4189 select vari(1,2,3,4,5);
4209 select vari(variadic array[5,6,7]);
4218 drop function vari(int[]);
4220 create or replace function pleast(variadic numeric[])
4221 returns numeric as $$
4222 declare aux numeric = $1[array_lower($1,1)];
4224 for i in array_lower($1,1)+1..array_upper($1,1) loop
4225 if $1[i] < aux then aux := $1[i]; end if;
4229 $$ language plpgsql immutable strict;
4230 select pleast(10,1,2,3,-16);
4236 select pleast(10.2,2.2,-1.1);
4242 select pleast(10.2,10, -20);
4248 select pleast(10,20, -1.0);
4254 -- in case of conflict, non-variadic version is preferred
4255 create or replace function pleast(numeric)
4256 returns numeric as $$
4258 raise notice 'non-variadic function called';
4261 $$ language plpgsql immutable strict;
4263 NOTICE: non-variadic function called
4269 drop function pleast(numeric[]);
4270 drop function pleast(numeric);
4271 -- test table functions
4272 create function tftest(int) returns table(a int, b int) as $$
4274 return query select $1, $1+i from generate_series(1,5) g(i);
4276 $$ language plpgsql immutable strict;
4277 select * from tftest(10);
4287 create or replace function tftest(a1 int) returns table(a int, b int) as $$
4289 a := a1; b := a1 + 1;
4291 a := a1 * 10; b := a1 * 10 + 1;
4294 $$ language plpgsql immutable strict;
4295 select * from tftest(10);
4302 drop function tftest(int);
4303 create function rttest()
4304 returns setof int as $$
4307 return query values(10),(20);
4308 get diagnostics rc = row_count;
4309 raise notice '% %', found, rc;
4310 return query select * from (values(10),(20)) f(a) where false;
4311 get diagnostics rc = row_count;
4312 raise notice '% %', found, rc;
4313 return query execute 'values(10),(20)';
4314 get diagnostics rc = row_count;
4315 raise notice '% %', found, rc;
4316 return query execute 'select * from (values(10),(20)) f(a) where false';
4317 get diagnostics rc = row_count;
4318 raise notice '% %', found, rc;
4320 $$ language plpgsql;
4321 select * from rttest();
4334 -- check some error cases, too
4335 create or replace function rttest()
4336 returns setof int as $$
4338 return query select 10 into no_such_table;
4340 $$ language plpgsql;
4341 select * from rttest();
4342 ERROR: SELECT INTO query does not return tuples
4343 CONTEXT: SQL statement "select 10 into no_such_table"
4344 PL/pgSQL function rttest() line 3 at RETURN QUERY
4345 create or replace function rttest()
4346 returns setof int as $$
4348 return query execute 'select 10 into no_such_table';
4350 $$ language plpgsql;
4351 select * from rttest();
4352 ERROR: SELECT INTO query does not return tuples
4353 CONTEXT: SQL statement "select 10 into no_such_table"
4354 PL/pgSQL function rttest() line 3 at RETURN QUERY
4355 select * from no_such_table;
4356 ERROR: relation "no_such_table" does not exist
4357 LINE 1: select * from no_such_table;
4359 drop function rttest();
4360 -- Test for proper cleanup at subtransaction exit. This example
4361 -- exposed a bug in PG 8.2.
4362 CREATE FUNCTION leaker_1(fail BOOL) RETURNS INTEGER AS $$
4367 v_var := (leaker_2(fail)).error_code;
4369 WHEN others THEN RETURN 0;
4373 $$ LANGUAGE plpgsql;
4374 CREATE FUNCTION leaker_2(fail BOOL, OUT error_code INTEGER, OUT new_id INTEGER)
4375 RETURNS RECORD AS $$
4378 RAISE EXCEPTION 'fail ...';
4384 $$ LANGUAGE plpgsql;
4385 SELECT * FROM leaker_1(false);
4391 SELECT * FROM leaker_1(true);
4397 DROP FUNCTION leaker_1(bool);
4398 DROP FUNCTION leaker_2(bool);
4399 -- Test for appropriate cleanup of non-simple expression evaluations
4400 -- (bug in all versions prior to August 2010)
4401 CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$
4407 arr := array[array['foo','bar'], array['baz', 'quux']];
4410 -- use sub-SELECTs to make expressions non-simple
4411 arr[(SELECT i)][(SELECT i+1)] := (SELECT lr);
4414 $$ LANGUAGE plpgsql;
4415 SELECT nonsimple_expr_test();
4417 -------------------------
4418 {{foo,fool},{baz,quux}}
4421 DROP FUNCTION nonsimple_expr_test();
4422 CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$
4424 i integer NOT NULL := 0;
4427 i := (SELECT NULL::integer); -- should throw error
4430 i := (SELECT 1::integer);
4434 $$ LANGUAGE plpgsql;
4435 SELECT nonsimple_expr_test();
4437 ---------------------
4441 DROP FUNCTION nonsimple_expr_test();
4443 -- Test cases involving recursion and error recovery in simple expressions
4444 -- (bugs in all versions before October 2010). The problems are most
4445 -- easily exposed by mutual recursion between plpgsql and sql functions.
4447 create function recurse(float8) returns float8 as
4451 return sql_recurse($1 - 1);
4456 $$ language plpgsql;
4457 -- "limit" is to prevent this from being inlined
4458 create function sql_recurse(float8) returns float8 as
4459 $$ select recurse($1) limit 1; $$ language sql;
4466 create function error1(text) returns text language sql as
4467 $$ SELECT relname::text FROM pg_class c WHERE c.oid = $1::regclass $$;
4468 create function error2(p_name_table text) returns text language plpgsql as $$
4470 return error1(p_name_table);
4473 create table public.stuffs (stuff text);
4475 select error2('nonexistent.stuffs');
4476 ERROR: schema "nonexistent" does not exist
4477 CONTEXT: SQL function "error1" statement 1
4478 PL/pgSQL function error2(text) line 3 at RETURN
4480 select error2('public.stuffs');
4487 drop function error2(p_name_table text);
4488 drop function error1(text);
4489 -- Test for proper handling of cast-expression caching
4490 create function sql_to_date(integer) returns date as $$
4491 select $1::text::date
4492 $$ language sql immutable strict;
4493 create cast (integer as date) with function sql_to_date(integer) as assignment;
4494 create function cast_invoker(integer) returns date as $$
4497 end$$ language plpgsql;
4498 select cast_invoker(20150717);
4504 select cast_invoker(20150718); -- second call crashed in pre-release 9.5
4511 select cast_invoker(20150717);
4517 select cast_invoker(20150718);
4524 select cast_invoker(20150718);
4530 select cast_invoker(-1); -- fails
4531 ERROR: invalid input syntax for type date: "-1"
4532 CONTEXT: SQL function "sql_to_date" statement 1
4533 PL/pgSQL function cast_invoker(integer) while casting return value to function's return type
4534 rollback to savepoint s1;
4535 select cast_invoker(20150719);
4541 select cast_invoker(20150720);
4548 drop function cast_invoker(integer);
4549 drop function sql_to_date(integer) cascade;
4550 NOTICE: drop cascades to cast from integer to date
4551 -- Test handling of cast cache inside DO blocks
4552 -- (to check the original crash case, this must be a cast not previously
4553 -- used in this session)
4555 do $$ declare x text[]; begin x := '{1.23, 4.56}'::numeric[]; end $$;
4556 do $$ declare x text[]; begin x := '{1.23, 4.56}'::numeric[]; end $$;
4558 -- Test for consistent reporting of error context
4559 create function fail() returns int language plpgsql as $$
4565 ERROR: division by zero
4566 CONTEXT: PL/pgSQL expression "1/0"
4567 PL/pgSQL function fail() line 3 at RETURN
4569 ERROR: division by zero
4570 CONTEXT: PL/pgSQL expression "1/0"
4571 PL/pgSQL function fail() line 3 at RETURN
4572 drop function fail();
4573 -- Test handling of string literals.
4574 set standard_conforming_strings = off;
4575 create or replace function strtest() returns text as $$
4577 raise notice 'foo\\bar\041baz';
4578 return 'foo\\bar\041baz';
4580 $$ language plpgsql;
4581 WARNING: nonstandard use of \\ in a string literal
4582 LINE 3: raise notice 'foo\\bar\041baz';
4584 HINT: Use the escape string syntax for backslashes, e.g., E'\\'.
4585 WARNING: nonstandard use of \\ in a string literal
4586 LINE 4: return 'foo\\bar\041baz';
4588 HINT: Use the escape string syntax for backslashes, e.g., E'\\'.
4589 WARNING: nonstandard use of \\ in a string literal
4590 LINE 4: return 'foo\\bar\041baz';
4592 HINT: Use the escape string syntax for backslashes, e.g., E'\\'.
4595 WARNING: nonstandard use of \\ in a string literal
4596 LINE 1: 'foo\\bar\041baz'
4598 HINT: Use the escape string syntax for backslashes, e.g., E'\\'.
4599 QUERY: 'foo\\bar\041baz'
4605 create or replace function strtest() returns text as $$
4607 raise notice E'foo\\bar\041baz';
4608 return E'foo\\bar\041baz';
4610 $$ language plpgsql;
4618 set standard_conforming_strings = on;
4619 create or replace function strtest() returns text as $$
4621 raise notice 'foo\\bar\041baz\';
4622 return 'foo\\bar\041baz\';
4624 $$ language plpgsql;
4626 NOTICE: foo\\bar\041baz\
4632 create or replace function strtest() returns text as $$
4634 raise notice E'foo\\bar\041baz';
4635 return E'foo\\bar\041baz';
4637 $$ language plpgsql;
4645 drop function strtest();
4646 -- Test anonymous code blocks.
4650 FOR r IN SELECT rtrim(roomno) AS roomno, comment FROM Room ORDER BY roomno
4652 RAISE NOTICE '%, %', r.roomno, r.comment;
4655 NOTICE: 001, Entrance
4658 NOTICE: 004, Technical
4660 NOTICE: 102, Conference
4661 NOTICE: 103, Restroom
4662 NOTICE: 104, Technical
4665 -- these are to check syntax error reporting
4666 DO LANGUAGE plpgsql $$begin return 1; end$$;
4667 ERROR: RETURN cannot have a parameter in function returning void
4668 LINE 1: DO LANGUAGE plpgsql $$begin return 1; end$$;
4673 FOR r IN SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
4675 RAISE NOTICE '%, %', r.roomno, r.comment;
4678 ERROR: column "foo" does not exist
4679 LINE 1: SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomn...
4681 QUERY: SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
4682 CONTEXT: PL/pgSQL function inline_code_block line 4 at FOR over SELECT rows
4683 -- Check handling of errors thrown from/into anonymous code blocks.
4696 exception when division_by_zero then
4697 raise notice 'caught division by zero';
4702 NOTICE: caught division by zero
4703 NOTICE: caught division by zero
4704 NOTICE: caught division by zero
4705 NOTICE: caught division by zero
4706 NOTICE: caught division by zero
4707 NOTICE: caught division by zero
4708 NOTICE: caught division by zero
4709 NOTICE: caught division by zero
4710 NOTICE: caught division by zero
4711 NOTICE: caught division by zero
4712 -- Check variable scoping -- a var is not available in its own or prior
4713 -- default expressions, but it is available in later ones.
4715 declare x int := x + 1; -- error
4717 raise notice 'x = %', x;
4720 ERROR: column "x" does not exist
4724 CONTEXT: PL/pgSQL function inline_code_block line 2 during statement block local variable initialization
4726 declare y int := x + 1; -- error
4729 raise notice 'x = %, y = %', x, y;
4732 ERROR: column "x" does not exist
4736 CONTEXT: PL/pgSQL function inline_code_block line 2 during statement block local variable initialization
4738 declare x int := 42;
4741 raise notice 'x = %, y = %', x, y;
4744 NOTICE: x = 42, y = 43
4746 declare x int := 42;
4748 declare y int := x + 1;
4752 raise notice 'x = %, y = %, z = %', x, y, z;
4756 NOTICE: x = 44, y = 43, z = 440
4757 -- Check handling of conflicts between plpgsql vars and table columns.
4758 set plpgsql.variable_conflict = error;
4759 create function conflict_test() returns setof int8_tbl as $$
4763 for r in select q1,q2 from int8_tbl loop
4767 $$ language plpgsql;
4768 select * from conflict_test();
4769 ERROR: column reference "q1" is ambiguous
4770 LINE 1: select q1,q2 from int8_tbl
4772 DETAIL: It could refer to either a PL/pgSQL variable or a table column.
4773 QUERY: select q1,q2 from int8_tbl
4774 CONTEXT: PL/pgSQL function conflict_test() line 5 at FOR over SELECT rows
4775 create or replace function conflict_test() returns setof int8_tbl as $$
4776 #variable_conflict use_variable
4780 for r in select q1,q2 from int8_tbl loop
4784 $$ language plpgsql;
4785 select * from conflict_test();
4787 ----+-------------------
4789 42 | 4567890123456789
4791 42 | 4567890123456789
4792 42 | -4567890123456789
4795 create or replace function conflict_test() returns setof int8_tbl as $$
4796 #variable_conflict use_column
4800 for r in select q1,q2 from int8_tbl loop
4804 $$ language plpgsql;
4805 select * from conflict_test();
4807 ------------------+-------------------
4809 123 | 4567890123456789
4810 4567890123456789 | 123
4811 4567890123456789 | 4567890123456789
4812 4567890123456789 | -4567890123456789
4815 drop function conflict_test();
4816 -- Check that an unreserved keyword can be used as a variable name
4817 create function unreserved_test() returns int as $$
4821 forward := forward * 2;
4824 $$ language plpgsql;
4825 select unreserved_test();
4831 create or replace function unreserved_test() returns int as $$
4835 return := return + 1;
4838 $$ language plpgsql;
4839 select unreserved_test();
4845 create or replace function unreserved_test() returns int as $$
4849 comment := comment * 2;
4850 comment on function unreserved_test() is 'this is a test';
4853 $$ language plpgsql;
4854 select unreserved_test();
4860 select obj_description('unreserved_test()'::regprocedure, 'pg_proc');
4866 drop function unreserved_test();
4868 -- Test FOREACH over arrays
4870 create function foreach_test(anyarray)
4874 foreach x in array $1
4876 raise notice '%', x;
4879 $$ language plpgsql;
4880 select foreach_test(ARRAY[1,2,3,4]);
4890 select foreach_test(ARRAY[[1,2],[3,4]]);
4900 create or replace function foreach_test(anyarray)
4904 foreach x slice 1 in array $1
4906 raise notice '%', x;
4909 $$ language plpgsql;
4911 select foreach_test(ARRAY[1,2,3,4]);
4912 ERROR: FOREACH ... SLICE loop variable must be of an array type
4913 CONTEXT: PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
4914 select foreach_test(ARRAY[[1,2],[3,4]]);
4915 ERROR: FOREACH ... SLICE loop variable must be of an array type
4916 CONTEXT: PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
4917 create or replace function foreach_test(anyarray)
4921 foreach x slice 1 in array $1
4923 raise notice '%', x;
4926 $$ language plpgsql;
4927 select foreach_test(ARRAY[1,2,3,4]);
4934 select foreach_test(ARRAY[[1,2],[3,4]]);
4942 -- higher level of slicing
4943 create or replace function foreach_test(anyarray)
4947 foreach x slice 2 in array $1
4949 raise notice '%', x;
4952 $$ language plpgsql;
4954 select foreach_test(ARRAY[1,2,3,4]);
4955 ERROR: slice dimension (2) is out of the valid range 0..1
4956 CONTEXT: PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
4958 select foreach_test(ARRAY[[1,2],[3,4]]);
4959 NOTICE: {{1,2},{3,4}}
4965 select foreach_test(ARRAY[[[1,2]],[[3,4]]]);
4973 create type xy_tuple AS (x int, y int);
4974 -- iteration over array of records
4975 create or replace function foreach_test(anyarray)
4979 foreach r in array $1
4981 raise notice '%', r;
4984 $$ language plpgsql;
4985 select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
4994 select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
5004 create or replace function foreach_test(anyarray)
5006 declare x int; y int;
5008 foreach x, y in array $1
5010 raise notice 'x = %, y = %', x, y;
5013 $$ language plpgsql;
5014 select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
5015 NOTICE: x = 10, y = 20
5016 NOTICE: x = 40, y = 69
5017 NOTICE: x = 35, y = 78
5023 select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
5024 NOTICE: x = 10, y = 20
5025 NOTICE: x = 40, y = 69
5026 NOTICE: x = 35, y = 78
5027 NOTICE: x = 88, y = 76
5033 -- slicing over array of composite types
5034 create or replace function foreach_test(anyarray)
5036 declare x xy_tuple[];
5038 foreach x slice 1 in array $1
5040 raise notice '%', x;
5043 $$ language plpgsql;
5044 select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
5045 NOTICE: {"(10,20)","(40,69)","(35,78)"}
5051 select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
5052 NOTICE: {"(10,20)","(40,69)"}
5053 NOTICE: {"(35,78)","(88,76)"}
5059 drop function foreach_test(anyarray);
5062 -- Assorted tests for array subscript assignment
5064 create temp table rtype (id int, ar text[]);
5065 create function arrayassign1() returns text[] language plpgsql as $$
5069 r := row(12, '{foo,bar,baz}')::rtype;
5070 r.ar[2] := 'replace';
5073 select arrayassign1();
5079 select arrayassign1(); -- try again to exercise internal caching
5085 create domain orderedarray as int[2]
5086 constraint sorted check (value[1] < value[2]);
5087 select '{1,2}'::orderedarray;
5093 select '{2,1}'::orderedarray; -- fail
5094 ERROR: value for domain orderedarray violates check constraint "sorted"
5095 create function testoa(x1 int, x2 int, x3 int) returns orderedarray
5096 language plpgsql as $$
5097 declare res orderedarray;
5099 res := array[x1, x2];
5103 select testoa(1,2,3);
5109 select testoa(1,2,3); -- try again to exercise internal caching
5115 select testoa(2,1,3); -- fail at initial assign
5116 ERROR: value for domain orderedarray violates check constraint "sorted"
5117 CONTEXT: PL/pgSQL function testoa(integer,integer,integer) line 4 at assignment
5118 select testoa(1,2,1); -- fail at update
5119 ERROR: value for domain orderedarray violates check constraint "sorted"
5120 CONTEXT: PL/pgSQL function testoa(integer,integer,integer) line 5 at assignment
5121 drop function arrayassign1();
5122 drop function testoa(x1 int, x2 int, x3 int);
5124 -- Test handling of expanded arrays
5126 create function returns_rw_array(int) returns int[]
5127 language plpgsql as $$
5129 begin r := array[$1, $1]; return r; end;
5131 create function consumes_rw_array(int[]) returns int
5132 language plpgsql as $$
5133 begin return $1[1]; end;
5135 select consumes_rw_array(returns_rw_array(42));
5142 explain (verbose, costs off)
5144 (select returns_rw_array(1) as a offset 0) ss,
5145 lateral consumes_rw_array(a) i;
5147 -----------------------------------------------------------------
5149 Output: i.i, (returns_rw_array(1))
5151 Output: returns_rw_array(1)
5152 -> Function Scan on public.consumes_rw_array i
5154 Function Call: consumes_rw_array((returns_rw_array(1)))
5158 (select returns_rw_array(1) as a offset 0) ss,
5159 lateral consumes_rw_array(a) i;
5165 explain (verbose, costs off)
5166 select consumes_rw_array(a), a from returns_rw_array(1) a;
5168 --------------------------------------------
5169 Function Scan on public.returns_rw_array a
5170 Output: consumes_rw_array(a), a
5171 Function Call: returns_rw_array(1)
5174 select consumes_rw_array(a), a from returns_rw_array(1) a;
5175 consumes_rw_array | a
5176 -------------------+-------
5180 explain (verbose, costs off)
5181 select consumes_rw_array(a), a from
5182 (values (returns_rw_array(1)), (returns_rw_array(2))) v(a);
5184 ---------------------------------------------------------------------
5185 Values Scan on "*VALUES*"
5186 Output: consumes_rw_array("*VALUES*".column1), "*VALUES*".column1
5189 select consumes_rw_array(a), a from
5190 (values (returns_rw_array(1)), (returns_rw_array(2))) v(a);
5191 consumes_rw_array | a
5192 -------------------+-------
5198 declare a int[] := array[1,2];
5201 raise notice 'a = %', a;
5205 -- Test access to call stack
5207 create function inner_func(int)
5209 declare _context text;
5211 get diagnostics _context = pg_context;
5212 raise notice '***%***', _context;
5213 -- lets do it again, just for fun..
5214 get diagnostics _context = pg_context;
5215 raise notice '***%***', _context;
5216 raise notice 'lets make sure we didnt break anything';
5219 $$ language plpgsql;
5220 create or replace function outer_func(int)
5225 raise notice 'calling down into inner_func()';
5226 myresult := inner_func($1);
5227 raise notice 'inner_func() done';
5230 $$ language plpgsql;
5231 create or replace function outer_outer_func(int)
5236 raise notice 'calling down into outer_func()';
5237 myresult := outer_func($1);
5238 raise notice 'outer_func() done';
5241 $$ language plpgsql;
5242 select outer_outer_func(10);
5243 NOTICE: calling down into outer_func()
5244 NOTICE: calling down into inner_func()
5245 NOTICE: ***PL/pgSQL function inner_func(integer) line 4 at GET DIAGNOSTICS
5246 PL/pgSQL function outer_func(integer) line 6 at assignment
5247 PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
5248 NOTICE: ***PL/pgSQL function inner_func(integer) line 7 at GET DIAGNOSTICS
5249 PL/pgSQL function outer_func(integer) line 6 at assignment
5250 PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
5251 NOTICE: lets make sure we didnt break anything
5252 NOTICE: inner_func() done
5253 NOTICE: outer_func() done
5259 -- repeated call should work
5260 select outer_outer_func(20);
5261 NOTICE: calling down into outer_func()
5262 NOTICE: calling down into inner_func()
5263 NOTICE: ***PL/pgSQL function inner_func(integer) line 4 at GET DIAGNOSTICS
5264 PL/pgSQL function outer_func(integer) line 6 at assignment
5265 PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
5266 NOTICE: ***PL/pgSQL function inner_func(integer) line 7 at GET DIAGNOSTICS
5267 PL/pgSQL function outer_func(integer) line 6 at assignment
5268 PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
5269 NOTICE: lets make sure we didnt break anything
5270 NOTICE: inner_func() done
5271 NOTICE: outer_func() done
5277 drop function outer_outer_func(int);
5278 drop function outer_func(int);
5279 drop function inner_func(int);
5280 -- access to call stack from exception
5281 create function inner_func(int)
5290 when division_by_zero then
5291 get diagnostics _context = pg_context;
5292 raise notice '***%***', _context;
5295 -- lets do it again, just for fun..
5296 get diagnostics _context = pg_context;
5297 raise notice '***%***', _context;
5298 raise notice 'lets make sure we didnt break anything';
5301 $$ language plpgsql;
5302 create or replace function outer_func(int)
5307 raise notice 'calling down into inner_func()';
5308 myresult := inner_func($1);
5309 raise notice 'inner_func() done';
5312 $$ language plpgsql;
5313 create or replace function outer_outer_func(int)
5318 raise notice 'calling down into outer_func()';
5319 myresult := outer_func($1);
5320 raise notice 'outer_func() done';
5323 $$ language plpgsql;
5324 select outer_outer_func(10);
5325 NOTICE: calling down into outer_func()
5326 NOTICE: calling down into inner_func()
5327 NOTICE: ***PL/pgSQL function inner_func(integer) line 10 at GET DIAGNOSTICS
5328 PL/pgSQL function outer_func(integer) line 6 at assignment
5329 PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
5330 NOTICE: ***PL/pgSQL function inner_func(integer) line 15 at GET DIAGNOSTICS
5331 PL/pgSQL function outer_func(integer) line 6 at assignment
5332 PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
5333 NOTICE: lets make sure we didnt break anything
5334 NOTICE: inner_func() done
5335 NOTICE: outer_func() done
5341 -- repeated call should work
5342 select outer_outer_func(20);
5343 NOTICE: calling down into outer_func()
5344 NOTICE: calling down into inner_func()
5345 NOTICE: ***PL/pgSQL function inner_func(integer) line 10 at GET DIAGNOSTICS
5346 PL/pgSQL function outer_func(integer) line 6 at assignment
5347 PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
5348 NOTICE: ***PL/pgSQL function inner_func(integer) line 15 at GET DIAGNOSTICS
5349 PL/pgSQL function outer_func(integer) line 6 at assignment
5350 PL/pgSQL function outer_outer_func(integer) line 6 at assignment***
5351 NOTICE: lets make sure we didnt break anything
5352 NOTICE: inner_func() done
5353 NOTICE: outer_func() done
5359 drop function outer_outer_func(int);
5360 drop function outer_func(int);
5361 drop function inner_func(int);
5362 -- Test pg_routine_oid
5363 create function current_function(text)
5364 returns regprocedure as $$
5366 fn_oid regprocedure;
5368 get diagnostics fn_oid = pg_routine_oid;
5371 $$ language plpgsql;
5372 select current_function('foo');
5374 ------------------------
5375 current_function(text)
5378 drop function current_function(text);
5379 -- shouldn't fail in DO, even though there's no useful data
5384 get diagnostics fn_oid = pg_routine_oid;
5385 raise notice 'pg_routine_oid = %', fn_oid;
5388 NOTICE: pg_routine_oid = 0
5394 assert 1=1; -- should succeed
5399 assert 1=0; -- should fail
5402 ERROR: assertion failed
5403 CONTEXT: PL/pgSQL function inline_code_block line 3 at ASSERT
5406 assert NULL; -- should fail
5409 ERROR: assertion failed
5410 CONTEXT: PL/pgSQL function inline_code_block line 3 at ASSERT
5411 -- check controlling GUC
5412 set plpgsql.check_asserts = off;
5415 assert 1=0; -- won't be tested
5418 reset plpgsql.check_asserts;
5419 -- test custom message
5421 declare var text := 'some value';
5423 assert 1=0, format('assertion failed, var = "%s"', var);
5426 ERROR: assertion failed, var = "some value"
5427 CONTEXT: PL/pgSQL function inline_code_block line 4 at ASSERT
5428 -- ensure assertions are not trapped by 'others'
5431 assert 1=0, 'unhandled assertion';
5432 exception when others then
5436 ERROR: unhandled assertion
5437 CONTEXT: PL/pgSQL function inline_code_block line 3 at ASSERT
5438 -- Test use of plpgsql in a domain check constraint (cf. bug #14414)
5439 create function plpgsql_domain_check(val int) returns boolean as $$
5440 begin return val > 0; end
5441 $$ language plpgsql immutable;
5442 create domain plpgsql_domain as integer check(plpgsql_domain_check(value));
5444 declare v_test plpgsql_domain;
5450 declare v_test plpgsql_domain := 1;
5452 v_test := 0; -- fail
5455 ERROR: value for domain plpgsql_domain violates check constraint "plpgsql_domain_check"
5456 CONTEXT: PL/pgSQL function inline_code_block line 4 at assignment
5457 -- Test handling of expanded array passed to a domain constraint (bug #14472)
5458 create function plpgsql_arr_domain_check(val int[]) returns boolean as $$
5459 begin return val[1] > 0; end
5460 $$ language plpgsql immutable;
5461 create domain plpgsql_arr_domain as int[] check(plpgsql_arr_domain_check(value));
5463 declare v_test plpgsql_arr_domain;
5466 v_test := v_test || 2;
5470 declare v_test plpgsql_arr_domain := array[1];
5472 v_test := 0 || v_test; -- fail
5475 ERROR: value for domain plpgsql_arr_domain violates check constraint "plpgsql_arr_domain_check"
5476 CONTEXT: PL/pgSQL function inline_code_block line 4 at assignment
5478 -- test usage of transition tables in AFTER triggers
5480 CREATE TABLE transition_table_base (id int PRIMARY KEY, val text);
5481 CREATE FUNCTION transition_table_base_ins_func()
5492 EXPLAIN (TIMING off, COSTS off, VERBOSE on)
5493 SELECT * FROM newtable
5495 t = t || l || E'\n';
5502 CREATE TRIGGER transition_table_base_ins_trig
5503 AFTER INSERT ON transition_table_base
5504 REFERENCING OLD TABLE AS oldtable NEW TABLE AS newtable
5506 EXECUTE PROCEDURE transition_table_base_ins_func();
5507 ERROR: OLD TABLE can only be specified for a DELETE or UPDATE trigger
5508 CREATE TRIGGER transition_table_base_ins_trig
5509 AFTER INSERT ON transition_table_base
5510 REFERENCING NEW TABLE AS newtable
5512 EXECUTE PROCEDURE transition_table_base_ins_func();
5513 INSERT INTO transition_table_base VALUES (1, 'One'), (2, 'Two');
5514 INFO: Named Tuplestore Scan
5517 INSERT INTO transition_table_base VALUES (3, 'Three'), (4, 'Four');
5518 INFO: Named Tuplestore Scan
5521 CREATE OR REPLACE FUNCTION transition_table_base_upd_func()
5532 EXPLAIN (TIMING off, COSTS off, VERBOSE on)
5533 SELECT * FROM oldtable ot FULL JOIN newtable nt USING (id)
5535 t = t || l || E'\n';
5542 CREATE TRIGGER transition_table_base_upd_trig
5543 AFTER UPDATE ON transition_table_base
5544 REFERENCING OLD TABLE AS oldtable NEW TABLE AS newtable
5546 EXECUTE PROCEDURE transition_table_base_upd_func();
5547 UPDATE transition_table_base
5548 SET val = '*' || val || '*'
5549 WHERE id BETWEEN 2 AND 3;
5550 INFO: Hash Full Join
5551 Output: COALESCE(ot.id, nt.id), ot.val, nt.val
5552 Hash Cond: (ot.id = nt.id)
5553 -> Named Tuplestore Scan
5554 Output: ot.id, ot.val
5556 Output: nt.id, nt.val
5557 -> Named Tuplestore Scan
5558 Output: nt.id, nt.val
5560 CREATE TABLE transition_table_level1
5562 level1_no serial NOT NULL ,
5563 level1_node_name varchar(255),
5564 PRIMARY KEY (level1_no)
5566 CREATE TABLE transition_table_level2
5568 level2_no serial NOT NULL ,
5569 parent_no int NOT NULL,
5570 level1_node_name varchar(255),
5571 PRIMARY KEY (level2_no)
5573 CREATE TABLE transition_table_status
5576 node_no int NOT NULL,
5578 PRIMARY KEY (level, node_no)
5580 CREATE FUNCTION transition_table_level1_ri_parent_del_func()
5586 PERFORM FROM p JOIN transition_table_level2 c ON c.parent_no = p.level1_no;
5588 RAISE EXCEPTION 'RI error';
5593 CREATE TRIGGER transition_table_level1_ri_parent_del_trigger
5594 AFTER DELETE ON transition_table_level1
5595 REFERENCING OLD TABLE AS p
5596 FOR EACH STATEMENT EXECUTE PROCEDURE
5597 transition_table_level1_ri_parent_del_func();
5598 CREATE FUNCTION transition_table_level1_ri_parent_upd_func()
5605 WITH p AS (SELECT level1_no, sum(delta) cnt
5606 FROM (SELECT level1_no, 1 AS delta FROM i
5608 SELECT level1_no, -1 AS delta FROM d) w
5610 HAVING sum(delta) < 0)
5612 FROM p JOIN transition_table_level2 c ON c.parent_no = p.level1_no
5615 RAISE EXCEPTION 'RI error';
5620 CREATE TRIGGER transition_table_level1_ri_parent_upd_trigger
5621 AFTER UPDATE ON transition_table_level1
5622 REFERENCING OLD TABLE AS d NEW TABLE AS i
5623 FOR EACH STATEMENT EXECUTE PROCEDURE
5624 transition_table_level1_ri_parent_upd_func();
5625 CREATE FUNCTION transition_table_level2_ri_child_insupd_func()
5631 LEFT JOIN transition_table_level1 p
5632 ON p.level1_no IS NOT NULL AND p.level1_no = i.parent_no
5633 WHERE p.level1_no IS NULL;
5635 RAISE EXCEPTION 'RI error';
5640 CREATE TRIGGER transition_table_level2_ri_child_ins_trigger
5641 AFTER INSERT ON transition_table_level2
5642 REFERENCING NEW TABLE AS i
5643 FOR EACH STATEMENT EXECUTE PROCEDURE
5644 transition_table_level2_ri_child_insupd_func();
5645 CREATE TRIGGER transition_table_level2_ri_child_upd_trigger
5646 AFTER UPDATE ON transition_table_level2
5647 REFERENCING NEW TABLE AS i
5648 FOR EACH STATEMENT EXECUTE PROCEDURE
5649 transition_table_level2_ri_child_insupd_func();
5650 -- create initial test data
5651 INSERT INTO transition_table_level1 (level1_no)
5652 SELECT generate_series(1,200);
5653 ANALYZE transition_table_level1;
5654 INSERT INTO transition_table_level2 (level2_no, parent_no)
5655 SELECT level2_no, level2_no / 50 + 1 AS parent_no
5656 FROM generate_series(1,9999) level2_no;
5657 ANALYZE transition_table_level2;
5658 INSERT INTO transition_table_status (level, node_no, status)
5659 SELECT 1, level1_no, 0 FROM transition_table_level1;
5660 INSERT INTO transition_table_status (level, node_no, status)
5661 SELECT 2, level2_no, 0 FROM transition_table_level2;
5662 ANALYZE transition_table_status;
5663 INSERT INTO transition_table_level1(level1_no)
5664 SELECT generate_series(201,1000);
5665 ANALYZE transition_table_level1;
5666 -- behave reasonably if someone tries to modify a transition table
5667 CREATE FUNCTION transition_table_level2_bad_usage_func()
5672 INSERT INTO dx VALUES (1000000, 1000000, 'x');
5676 CREATE TRIGGER transition_table_level2_bad_usage_trigger
5677 AFTER DELETE ON transition_table_level2
5678 REFERENCING OLD TABLE AS dx
5679 FOR EACH STATEMENT EXECUTE PROCEDURE
5680 transition_table_level2_bad_usage_func();
5681 DELETE FROM transition_table_level2
5682 WHERE level2_no BETWEEN 301 AND 305;
5683 ERROR: relation "dx" cannot be the target of a modifying statement
5684 CONTEXT: SQL statement "INSERT INTO dx VALUES (1000000, 1000000, 'x')"
5685 PL/pgSQL function transition_table_level2_bad_usage_func() line 3 at SQL statement
5686 DROP TRIGGER transition_table_level2_bad_usage_trigger
5687 ON transition_table_level2;
5688 -- attempt modifications which would break RI (should all fail)
5689 DELETE FROM transition_table_level1
5690 WHERE level1_no = 25;
5692 CONTEXT: PL/pgSQL function transition_table_level1_ri_parent_del_func() line 6 at RAISE
5693 UPDATE transition_table_level1 SET level1_no = -1
5694 WHERE level1_no = 30;
5696 CONTEXT: PL/pgSQL function transition_table_level1_ri_parent_upd_func() line 15 at RAISE
5697 INSERT INTO transition_table_level2 (level2_no, parent_no)
5698 VALUES (10000, 10000);
5700 CONTEXT: PL/pgSQL function transition_table_level2_ri_child_insupd_func() line 8 at RAISE
5701 UPDATE transition_table_level2 SET parent_no = 2000
5702 WHERE level2_no = 40;
5704 CONTEXT: PL/pgSQL function transition_table_level2_ri_child_insupd_func() line 8 at RAISE
5705 -- attempt modifications which would not break RI (should all succeed)
5706 DELETE FROM transition_table_level1
5707 WHERE level1_no BETWEEN 201 AND 1000;
5708 DELETE FROM transition_table_level1
5709 WHERE level1_no BETWEEN 100000000 AND 100000010;
5710 SELECT count(*) FROM transition_table_level1;
5716 DELETE FROM transition_table_level2
5717 WHERE level2_no BETWEEN 211 AND 220;
5718 SELECT count(*) FROM transition_table_level2;
5724 CREATE TABLE alter_table_under_transition_tables
5729 CREATE FUNCTION alter_table_under_transition_tables_upd_func()
5734 RAISE WARNING 'old table = %, new table = %',
5735 (SELECT string_agg(id || '=' || name, ',') FROM d),
5736 (SELECT string_agg(id || '=' || name, ',') FROM i);
5737 RAISE NOTICE 'one = %', (SELECT 1 FROM alter_table_under_transition_tables LIMIT 1);
5741 -- should fail, TRUNCATE is not compatible with transition tables
5742 CREATE TRIGGER alter_table_under_transition_tables_upd_trigger
5743 AFTER TRUNCATE OR UPDATE ON alter_table_under_transition_tables
5744 REFERENCING OLD TABLE AS d NEW TABLE AS i
5745 FOR EACH STATEMENT EXECUTE PROCEDURE
5746 alter_table_under_transition_tables_upd_func();
5747 ERROR: TRUNCATE triggers with transition tables are not supported
5749 CREATE TRIGGER alter_table_under_transition_tables_upd_trigger
5750 AFTER UPDATE ON alter_table_under_transition_tables
5751 REFERENCING OLD TABLE AS d NEW TABLE AS i
5752 FOR EACH STATEMENT EXECUTE PROCEDURE
5753 alter_table_under_transition_tables_upd_func();
5754 INSERT INTO alter_table_under_transition_tables
5755 VALUES (1, '1'), (2, '2'), (3, '3');
5756 UPDATE alter_table_under_transition_tables
5757 SET name = name || name;
5758 WARNING: old table = 1=1,2=2,3=3, new table = 1=11,2=22,3=33
5760 -- now change 'name' to an integer to see what happens...
5761 ALTER TABLE alter_table_under_transition_tables
5762 ALTER COLUMN name TYPE int USING name::integer;
5763 UPDATE alter_table_under_transition_tables
5764 SET name = (name::text || name::text)::integer;
5765 WARNING: old table = 1=11,2=22,3=33, new table = 1=1111,2=2222,3=3333
5767 -- now drop column 'name'
5768 ALTER TABLE alter_table_under_transition_tables
5770 UPDATE alter_table_under_transition_tables
5772 ERROR: column "name" does not exist
5773 LINE 1: (SELECT string_agg(id || '=' || name, ',') FROM d)
5775 QUERY: (SELECT string_agg(id || '=' || name, ',') FROM d)
5776 CONTEXT: PL/pgSQL function alter_table_under_transition_tables_upd_func() line 3 at RAISE
5778 -- Test multiple reference to a transition table
5780 CREATE TABLE multi_test (i int);
5781 INSERT INTO multi_test VALUES (1);
5782 CREATE OR REPLACE FUNCTION multi_test_trig() RETURNS trigger
5783 LANGUAGE plpgsql AS $$
5785 RAISE NOTICE 'count = %', (SELECT COUNT(*) FROM new_test);
5786 RAISE NOTICE 'count union = %',
5788 FROM (SELECT * FROM new_test UNION ALL SELECT * FROM new_test) ss);
5791 CREATE TRIGGER my_trigger AFTER UPDATE ON multi_test
5792 REFERENCING NEW TABLE AS new_test OLD TABLE as old_test
5793 FOR EACH STATEMENT EXECUTE PROCEDURE multi_test_trig();
5794 UPDATE multi_test SET i = i;
5796 NOTICE: count union = 2
5797 DROP TABLE multi_test;
5798 DROP FUNCTION multi_test_trig();
5800 -- Check type parsing and record fetching from partitioned tables
5802 CREATE TABLE partitioned_table (a int, b text) PARTITION BY LIST (a);
5803 CREATE TABLE pt_part1 PARTITION OF partitioned_table FOR VALUES IN (1);
5804 CREATE TABLE pt_part2 PARTITION OF partitioned_table FOR VALUES IN (2);
5805 INSERT INTO partitioned_table VALUES (1, 'Row 1');
5806 INSERT INTO partitioned_table VALUES (2, 'Row 2');
5807 CREATE OR REPLACE FUNCTION get_from_partitioned_table(partitioned_table.a%type)
5808 RETURNS partitioned_table AS $$
5810 a_val partitioned_table.a%TYPE;
5811 result partitioned_table%ROWTYPE;
5814 SELECT * INTO result FROM partitioned_table WHERE a = a_val;
5816 END; $$ LANGUAGE plpgsql;
5817 NOTICE: type reference partitioned_table.a%TYPE converted to integer
5818 SELECT * FROM get_from_partitioned_table(1) AS t;
5824 CREATE OR REPLACE FUNCTION list_partitioned_table()
5825 RETURNS SETOF public.partitioned_table.a%TYPE AS $$
5827 row public.partitioned_table%ROWTYPE;
5828 a_val public.partitioned_table.a%TYPE;
5830 FOR row IN SELECT * FROM public.partitioned_table ORDER BY a LOOP
5835 END; $$ LANGUAGE plpgsql;
5836 NOTICE: type reference public.partitioned_table.a%TYPE converted to integer
5837 SELECT * FROM list_partitioned_table() AS t;
5845 -- Check argument name is used instead of $n in error message
5847 CREATE FUNCTION fx(x WSlot) RETURNS void AS $$
5849 GET DIAGNOSTICS x = ROW_COUNT;
5851 END; $$ LANGUAGE plpgsql;
5852 ERROR: "x" is not a scalar variable
5853 LINE 3: GET DIAGNOSTICS x = ROW_COUNT;