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 weired 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_a() line 17 at assignment
1522 update PSlot set backlink = 'XX.illegal' where slotname = 'PS.base.a1';
1523 ERROR: illegal backlink beginning with XX
1524 CONTEXT: PL/pgSQL function tg_backlink_a() line 17 at assignment
1525 update PSlot set slotlink = 'PS.not.there' where slotname = 'PS.base.a1';
1526 ERROR: PS.not.there does not exist
1527 CONTEXT: PL/pgSQL function tg_slotlink_a() line 17 at assignment
1528 update PSlot set slotlink = 'XX.illegal' where slotname = 'PS.base.a1';
1529 ERROR: illegal slotlink beginning with XX
1530 CONTEXT: PL/pgSQL function tg_slotlink_a() line 17 at assignment
1531 insert into HSlot values ('HS', 'base.hub1', 1, '');
1532 ERROR: duplicate key value violates unique constraint "hslot_name"
1533 DETAIL: Key (slotname)=(HS.base.hub1.1 ) already exists.
1534 insert into HSlot values ('HS', 'base.hub1', 20, '');
1535 ERROR: no manual manipulation of HSlot
1537 ERROR: no manual manipulation of HSlot
1538 insert into IFace values ('IF', 'notthere', 'eth0', '');
1539 ERROR: system "notthere" does not exist
1540 insert into IFace values ('IF', 'orion', 'ethernet_interface_name_too_long', '');
1541 ERROR: IFace slotname "IF.orion.ethernet_interface_name_too_long" too long (20 char max)
1543 -- The following tests are unrelated to the scenario outlined above;
1544 -- they merely exercise specific parts of PL/pgSQL
1547 -- Test recursion, per bug report 7-Sep-01
1549 CREATE FUNCTION recursion_test(int,int) RETURNS text AS '
1553 rslt = CAST($2 AS TEXT);
1555 rslt = CAST($1 AS TEXT) || '','' || recursion_test($1 - 1, $2);
1558 END;' LANGUAGE plpgsql;
1559 SELECT recursion_test(4,3);
1566 -- Test the FOUND magic variable
1568 CREATE TABLE found_test_tbl (a int);
1569 create function test_found()
1570 returns boolean as '
1573 insert into found_test_tbl values (1);
1575 insert into found_test_tbl values (2);
1578 update found_test_tbl set a = 100 where a = 1;
1580 insert into found_test_tbl values (3);
1583 delete from found_test_tbl where a = 9999; -- matches no rows
1585 insert into found_test_tbl values (4);
1588 for i in 1 .. 10 loop
1589 -- no need to do anything
1592 insert into found_test_tbl values (5);
1595 -- never executes the loop
1596 for i in 2 .. 1 loop
1597 -- no need to do anything
1600 insert into found_test_tbl values (6);
1603 end;' language plpgsql;
1604 select test_found();
1610 select * from found_test_tbl;
1622 -- Test set-returning functions for PL/pgSQL
1624 create function test_table_func_rec() returns setof found_test_tbl as '
1628 FOR rec IN select * from found_test_tbl LOOP
1632 END;' language plpgsql;
1633 select * from test_table_func_rec();
1644 create function test_table_func_row() returns setof found_test_tbl as '
1646 row found_test_tbl%ROWTYPE;
1648 FOR row IN select * from found_test_tbl LOOP
1652 END;' language plpgsql;
1653 select * from test_table_func_row();
1664 create function test_ret_set_scalar(int,int) returns setof int as '
1668 FOR i IN $1 .. $2 LOOP
1672 END;' language plpgsql;
1673 select * from test_ret_set_scalar(1,10);
1675 ---------------------
1688 create function test_ret_set_rec_dyn(int) returns setof record as '
1693 SELECT INTO retval 5, 10, 15;
1697 SELECT INTO retval 50, 5::numeric, ''xxx''::text;
1702 END;' language plpgsql;
1703 SELECT * FROM test_ret_set_rec_dyn(1500) AS (a int, b int, c int);
1710 SELECT * FROM test_ret_set_rec_dyn(5) AS (a int, b numeric, c text);
1717 create function test_ret_rec_dyn(int) returns record as '
1722 SELECT INTO retval 5, 10, 15;
1725 SELECT INTO retval 50, 5::numeric, ''xxx''::text;
1728 END;' language plpgsql;
1729 SELECT * FROM test_ret_rec_dyn(1500) AS (a int, b int, c int);
1735 SELECT * FROM test_ret_rec_dyn(5) AS (a int, b numeric, c text);
1742 -- Test handling of OUT parameters, including polymorphic cases.
1743 -- Note that RETURN is optional with OUT params; we try both ways.
1745 -- wrong way to do it:
1746 create function f1(in i int, out j int) returns int as $$
1749 end$$ language plpgsql;
1750 ERROR: RETURN cannot have a parameter in function with OUT parameters
1753 create function f1(in i int, out j int) as $$
1757 end$$ language plpgsql;
1764 select * from f1(42);
1770 create or replace function f1(inout i int) as $$
1773 end$$ language plpgsql;
1780 select * from f1(42);
1786 drop function f1(int);
1787 create function f1(in i int, out j int) returns setof int as $$
1794 end$$ language plpgsql;
1795 select * from f1(42);
1802 drop function f1(int);
1803 create function f1(in i int, out j int, out k text) as $$
1808 end$$ language plpgsql;
1815 select * from f1(42);
1821 drop function f1(int);
1822 create function f1(in i int, out j int, out k text) returns setof record as $$
1830 end$$ language plpgsql;
1831 select * from f1(42);
1838 drop function f1(int);
1839 create function duplic(in i anyelement, out j anyelement, out k anyarray) as $$
1844 end$$ language plpgsql;
1845 select * from duplic(42);
1851 select * from duplic('foo'::text);
1857 drop function duplic(anyelement);
1861 create table perform_test (
1865 create function simple_func(int) returns boolean as '
1868 INSERT INTO perform_test VALUES ($1, $1 + 10);
1873 END;' language plpgsql;
1874 create function perform_test_func() returns void as '
1877 INSERT INTO perform_test VALUES (100, 100);
1880 PERFORM simple_func(5);
1883 INSERT INTO perform_test VALUES (100, 100);
1886 PERFORM simple_func(50);
1889 INSERT INTO perform_test VALUES (100, 100);
1893 END;' language plpgsql;
1894 SELECT perform_test_func();
1900 SELECT * FROM perform_test;
1908 drop table perform_test;
1910 -- Test error trapping
1912 create function trap_zero_divide(int) returns int as $$
1916 begin -- start a subtransaction
1917 raise notice 'should see this';
1919 raise notice 'should see this only if % <> 0', $1;
1921 raise notice 'should see this only if % fits in smallint', $1;
1923 raise exception '% is less than zero', $1;
1926 when division_by_zero then
1927 raise notice 'caught division_by_zero';
1929 when NUMERIC_VALUE_OUT_OF_RANGE then
1930 raise notice 'caught numeric_value_out_of_range';
1934 end$$ language plpgsql;
1935 select trap_zero_divide(50);
1936 NOTICE: should see this
1937 NOTICE: should see this only if 50 <> 0
1938 NOTICE: should see this only if 50 fits in smallint
1944 select trap_zero_divide(0);
1945 NOTICE: should see this
1946 NOTICE: caught division_by_zero
1952 select trap_zero_divide(100000);
1953 NOTICE: should see this
1954 NOTICE: should see this only if 100000 <> 0
1955 NOTICE: caught numeric_value_out_of_range
1961 select trap_zero_divide(-100);
1962 NOTICE: should see this
1963 NOTICE: should see this only if -100 <> 0
1964 NOTICE: should see this only if -100 fits in smallint
1965 ERROR: -100 is less than zero
1966 create function trap_matching_test(int) returns int as $$
1971 begin -- start a subtransaction
1974 select into y unique1 from tenk1 where unique2 =
1975 (select unique2 from tenk1 b where ten = $1);
1977 when data_exception then -- category match
1978 raise notice 'caught data_exception';
1980 when NUMERIC_VALUE_OUT_OF_RANGE OR CARDINALITY_VIOLATION then
1981 raise notice 'caught numeric_value_out_of_range or cardinality_violation';
1985 end$$ language plpgsql;
1986 select trap_matching_test(50);
1988 --------------------
1992 select trap_matching_test(0);
1993 NOTICE: caught data_exception
1995 --------------------
1999 select trap_matching_test(100000);
2000 NOTICE: caught data_exception
2002 --------------------
2006 select trap_matching_test(1);
2007 NOTICE: caught numeric_value_out_of_range or cardinality_violation
2009 --------------------
2013 create temp table foo (f1 int);
2014 create function blockme() returns int as $$
2018 insert into foo values(x);
2021 insert into foo values(x);
2022 -- we assume this will take longer than 2 seconds:
2023 select count(*) into x from tenk1 a, tenk1 b, tenk1 c;
2026 raise notice 'caught others?';
2028 when query_canceled then
2029 raise notice 'nyeah nyeah, can''t stop me';
2032 insert into foo values(x);
2034 end$$ language plpgsql;
2035 set statement_timeout to 2000;
2037 NOTICE: nyeah nyeah, can't stop me
2043 reset statement_timeout;
2052 -- Test for pass-by-ref values being stored in proper context
2053 create function test_variable_storage() returns text as $$
2059 -- force error inside subtransaction SPI context
2060 perform trap_zero_divide(-100);
2066 end$$ language plpgsql;
2067 select test_variable_storage();
2068 NOTICE: should see this
2069 CONTEXT: SQL statement "SELECT trap_zero_divide(-100)"
2070 PL/pgSQL function test_variable_storage() line 8 at PERFORM
2071 NOTICE: should see this only if -100 <> 0
2072 CONTEXT: SQL statement "SELECT trap_zero_divide(-100)"
2073 PL/pgSQL function test_variable_storage() line 8 at PERFORM
2074 NOTICE: should see this only if -100 fits in smallint
2075 CONTEXT: SQL statement "SELECT trap_zero_divide(-100)"
2076 PL/pgSQL function test_variable_storage() line 8 at PERFORM
2077 test_variable_storage
2078 -----------------------
2083 -- test foreign key error trapping
2085 create temp table master(f1 int primary key);
2086 NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "master_pkey" for table "master"
2087 create temp table slave(f1 int references master deferrable);
2088 insert into master values(1);
2089 insert into slave values(1);
2090 insert into slave values(2); -- fails
2091 ERROR: insert or update on table "slave" violates foreign key constraint "slave_f1_fkey"
2092 DETAIL: Key (f1)=(2) is not present in table "master".
2093 create function trap_foreign_key(int) returns int as $$
2095 begin -- start a subtransaction
2096 insert into slave values($1);
2098 when foreign_key_violation then
2099 raise notice 'caught foreign_key_violation';
2103 end$$ language plpgsql;
2104 create function trap_foreign_key_2() returns int as $$
2106 begin -- start a subtransaction
2107 set constraints all immediate;
2109 when foreign_key_violation then
2110 raise notice 'caught foreign_key_violation';
2114 end$$ language plpgsql;
2115 select trap_foreign_key(1);
2121 select trap_foreign_key(2); -- detects FK violation
2122 NOTICE: caught foreign_key_violation
2129 set constraints all deferred;
2130 select trap_foreign_key(2); -- should not detect FK violation
2137 set constraints all immediate; -- fails
2138 ERROR: insert or update on table "slave" violates foreign key constraint "slave_f1_fkey"
2139 DETAIL: Key (f1)=(2) is not present in table "master".
2141 select trap_foreign_key_2(); -- detects FK violation
2142 NOTICE: caught foreign_key_violation
2144 --------------------
2148 commit; -- still fails
2149 ERROR: insert or update on table "slave" violates foreign key constraint "slave_f1_fkey"
2150 DETAIL: Key (f1)=(2) is not present in table "master".
2151 drop function trap_foreign_key(int);
2152 drop function trap_foreign_key_2();
2154 -- Test proper snapshot handling in simple expressions
2156 create temp table users(login text, id serial);
2157 NOTICE: CREATE TABLE will create implicit sequence "users_id_seq" for serial column "users.id"
2158 create function sp_id_user(a_login text) returns int as $$
2161 select into x id from users where login = a_login;
2162 if found then return x; end if;
2164 end$$ language plpgsql stable;
2165 insert into users values('user1');
2166 select sp_id_user('user1');
2172 select sp_id_user('userx');
2178 create function sp_add_user(a_login text) returns int as $$
2179 declare my_id_user int;
2181 my_id_user = sp_id_user( a_login );
2182 IF my_id_user > 0 THEN
2183 RETURN -1; -- error code for existing user
2185 INSERT INTO users ( login ) VALUES ( a_login );
2186 my_id_user = sp_id_user( a_login );
2187 IF my_id_user = 0 THEN
2188 RETURN -2; -- error code for insertion failure
2191 end$$ language plpgsql;
2192 select sp_add_user('user1');
2198 select sp_add_user('user2');
2204 select sp_add_user('user2');
2210 select sp_add_user('user3');
2216 select sp_add_user('user3');
2222 drop function sp_add_user(text);
2223 drop function sp_id_user(text);
2225 -- tests for refcursors
2227 create table rc_test (a int, b int);
2228 copy rc_test from stdin;
2229 create function return_refcursor(rc refcursor) returns refcursor as $$
2231 open rc for select a from rc_test;
2234 $$ language plpgsql;
2235 create function refcursor_test1(refcursor) returns refcursor as $$
2237 perform return_refcursor($1);
2240 $$ language plpgsql;
2242 select refcursor_test1('test1');
2248 fetch next in test1;
2254 select refcursor_test1('test2');
2260 fetch all from test2;
2270 fetch next from test1;
2271 ERROR: cursor "test1" does not exist
2272 create function refcursor_test2(int, int) returns boolean as $$
2274 c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
2278 fetch c1 into nonsense;
2286 $$ language plpgsql;
2287 select refcursor_test2(20000, 20000) as "Should be false",
2288 refcursor_test2(20, 20) as "Should be true";
2289 Should be false | Should be true
2290 -----------------+----------------
2295 -- tests for cursors with named parameter arguments
2297 create function namedparmcursor_test1(int, int) returns boolean as $$
2299 c1 cursor (param1 int, param12 int) for select * from rc_test where a > param1 and b > param12;
2302 open c1(param12 := $2, param1 := $1);
2303 fetch c1 into nonsense;
2311 $$ language plpgsql;
2312 select namedparmcursor_test1(20000, 20000) as "Should be false",
2313 namedparmcursor_test1(20, 20) as "Should be true";
2314 Should be false | Should be true
2315 -----------------+----------------
2319 -- mixing named and positional argument notations
2320 create function namedparmcursor_test2(int, int) returns boolean as $$
2322 c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
2325 open c1(param1 := $1, $2);
2326 fetch c1 into nonsense;
2334 $$ language plpgsql;
2335 select namedparmcursor_test2(20, 20);
2336 namedparmcursor_test2
2337 -----------------------
2341 -- mixing named and positional: param2 is given twice, once in named notation
2342 -- and second time in positional notation. Should throw an error at parse time
2343 create function namedparmcursor_test3() returns void as $$
2345 c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
2347 open c1(param2 := 20, 21);
2349 $$ language plpgsql;
2350 ERROR: duplicate value for cursor "c1" parameter "param2"
2351 LINE 5: open c1(param2 := 20, 21);
2353 -- mixing named and positional: same as previous test, but param1 is duplicated
2354 create function namedparmcursor_test4() returns void as $$
2356 c1 cursor (param1 int, param2 int) for select * from rc_test where a > param1 and b > param2;
2358 open c1(20, param1 := 21);
2360 $$ language plpgsql;
2361 ERROR: duplicate value for cursor "c1" parameter "param1"
2362 LINE 5: open c1(20, param1 := 21);
2364 -- duplicate named parameter, should throw an error at parse time
2365 create function namedparmcursor_test5() returns void as $$
2367 c1 cursor (p1 int, p2 int) for
2368 select * from tenk1 where thousand = p1 and tenthous = p2;
2370 open c1 (p2 := 77, p2 := 42);
2372 $$ language plpgsql;
2373 ERROR: duplicate value for cursor "c1" parameter "p2"
2374 LINE 6: open c1 (p2 := 77, p2 := 42);
2376 -- not enough parameters, should throw an error at parse time
2377 create function namedparmcursor_test6() returns void as $$
2379 c1 cursor (p1 int, p2 int) for
2380 select * from tenk1 where thousand = p1 and tenthous = p2;
2384 $$ language plpgsql;
2385 ERROR: not enough arguments for cursor "c1"
2386 LINE 6: open c1 (p2 := 77);
2388 -- division by zero runtime error, the context given in the error message
2389 -- should be sensible
2390 create function namedparmcursor_test7() returns void as $$
2392 c1 cursor (p1 int, p2 int) for
2393 select * from tenk1 where thousand = p1 and tenthous = p2;
2395 open c1 (p2 := 77, p1 := 42/0);
2396 end $$ language plpgsql;
2397 select namedparmcursor_test7();
2398 ERROR: division by zero
2399 CONTEXT: SQL statement "SELECT 42/0 AS p1, 77 AS p2;"
2400 PL/pgSQL function namedparmcursor_test7() line 6 at OPEN
2401 -- check that line comments work correctly within the argument list (there
2402 -- is some special handling of this case in the code: the newline after the
2403 -- comment must be preserved when the argument-evaluating query is
2404 -- constructed, otherwise the comment effectively comments out the next
2406 create function namedparmcursor_test8() returns int4 as $$
2408 c1 cursor (p1 int, p2 int) for
2409 select count(*) from tenk1 where thousand = p1 and tenthous = p2;
2416 end $$ language plpgsql;
2417 select namedparmcursor_test8();
2418 namedparmcursor_test8
2419 -----------------------
2423 -- cursor parameter name can match plpgsql variable or unreserved keyword
2424 create function namedparmcursor_test9(p1 int) returns int4 as $$
2426 c1 cursor (p1 int, p2 int, debug int) for
2427 select count(*) from tenk1 where thousand = p1 and tenthous = p2
2432 open c1 (p1 := p1, p2 := p2, debug := 2);
2435 end $$ language plpgsql;
2436 select namedparmcursor_test9(6);
2437 namedparmcursor_test9
2438 -----------------------
2443 -- tests for "raise" processing
2445 create function raise_test1(int) returns int as $$
2447 raise notice 'This message has too many parameters!', $1;
2450 $$ language plpgsql;
2451 select raise_test1(5);
2452 ERROR: too many parameters specified for RAISE
2453 CONTEXT: PL/pgSQL function raise_test1(integer) line 3 at RAISE
2454 create function raise_test2(int) returns int as $$
2456 raise notice 'This message has too few parameters: %, %, %', $1, $1;
2459 $$ language plpgsql;
2460 select raise_test2(10);
2461 ERROR: too few parameters specified for RAISE
2462 CONTEXT: PL/pgSQL function raise_test2(integer) line 3 at RAISE
2463 -- Test re-RAISE inside a nested exception block. This case is allowed
2464 -- by Oracle's PL/SQL but was handled differently by PG before 9.1.
2465 CREATE FUNCTION reraise_test() RETURNS void AS $$
2470 WHEN syntax_error THEN
2472 raise notice 'exception % thrown in inner block, reraising', sqlerrm;
2476 raise notice 'RIGHT - exception % caught in inner block', sqlerrm;
2481 raise notice 'WRONG - exception % caught in outer block', sqlerrm;
2483 $$ LANGUAGE plpgsql;
2484 SELECT reraise_test();
2485 NOTICE: exception syntax_error thrown in inner block, reraising
2486 NOTICE: RIGHT - exception syntax_error caught in inner block
2493 -- reject function definitions that contain malformed SQL queries at
2494 -- compile-time, where possible
2496 create function bad_sql1() returns int as $$
2503 end$$ language plpgsql;
2504 ERROR: syntax error at or near "Johnny"
2505 LINE 5: Johnny Yuma;
2507 create function bad_sql2() returns int as $$
2510 for r in select I fought the law, the law won LOOP
2511 raise notice 'in loop';
2514 end;$$ language plpgsql;
2515 ERROR: syntax error at or near "the"
2516 LINE 4: for r in select I fought the law, the law won LOOP
2518 -- a RETURN expression is mandatory, except for void-returning
2519 -- functions, where it is not allowed
2520 create function missing_return_expr() returns int as $$
2523 end;$$ language plpgsql;
2524 ERROR: missing expression at or near ";"
2527 create function void_return_expr() returns void as $$
2530 end;$$ language plpgsql;
2531 ERROR: RETURN cannot have a parameter in function returning void
2534 -- VOID functions are allowed to omit RETURN
2535 create function void_return_expr() returns void as $$
2538 end;$$ language plpgsql;
2539 select void_return_expr();
2545 -- but ordinary functions are not
2546 create function missing_return_expr() returns int as $$
2549 end;$$ language plpgsql;
2550 select missing_return_expr();
2551 ERROR: control reached end of function without RETURN
2552 CONTEXT: PL/pgSQL function missing_return_expr()
2553 drop function void_return_expr();
2554 drop function missing_return_expr();
2556 -- EXECUTE ... INTO test
2558 create table eifoo (i integer, y integer);
2559 create type eitype as (i integer, y integer);
2560 create or replace function execute_into_test(varchar) returns record as $$
2569 execute 'insert into '||$1||' values(10,15)';
2570 execute 'select (row).* from (select row(10,1)::eifoo) s' into _r;
2571 raise notice '% %', _r.i, _r.y;
2572 execute 'select * from '||$1||' limit 1' into _rt;
2573 raise notice '% %', _rt.i, _rt.y;
2574 execute 'select *, 20 from '||$1||' limit 1' into i, j, k;
2575 raise notice '% % %', i, j, k;
2576 execute 'select 1,2' into _v;
2578 end; $$ language plpgsql;
2579 select execute_into_test('eifoo');
2588 drop table eifoo cascade;
2589 drop type eitype cascade;
2591 -- SQLSTATE and SQLERRM test
2593 create function excpt_test1() returns void as $$
2595 raise notice '% %', sqlstate, sqlerrm;
2596 end; $$ language plpgsql;
2597 -- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION
2599 select excpt_test1();
2600 ERROR: column "sqlstate" does not exist
2601 LINE 1: SELECT sqlstate
2603 QUERY: SELECT sqlstate
2604 CONTEXT: PL/pgSQL function excpt_test1() line 3 at RAISE
2605 create function excpt_test2() returns void as $$
2609 raise notice '% %', sqlstate, sqlerrm;
2612 end; $$ language plpgsql;
2614 select excpt_test2();
2615 ERROR: column "sqlstate" does not exist
2616 LINE 1: SELECT sqlstate
2618 QUERY: SELECT sqlstate
2619 CONTEXT: PL/pgSQL function excpt_test2() line 5 at RAISE
2620 create function excpt_test3() returns void as $$
2623 raise exception 'user exception';
2624 exception when others then
2625 raise notice 'caught exception % %', sqlstate, sqlerrm;
2627 raise notice '% %', sqlstate, sqlerrm;
2630 when substring_error then
2631 -- this exception handler shouldn't be invoked
2632 raise notice 'unexpected exception: % %', sqlstate, sqlerrm;
2633 when division_by_zero then
2634 raise notice 'caught exception % %', sqlstate, sqlerrm;
2636 raise notice '% %', sqlstate, sqlerrm;
2638 end; $$ language plpgsql;
2639 select excpt_test3();
2640 NOTICE: caught exception P0001 user exception
2641 NOTICE: P0001 user exception
2642 NOTICE: caught exception 22012 division by zero
2643 NOTICE: P0001 user exception
2649 drop function excpt_test1();
2650 drop function excpt_test2();
2651 drop function excpt_test3();
2652 -- parameters of raise stmt can be expressions
2653 create function raise_exprs() returns void as $$
2655 a integer[] = '{10,20,30}';
2660 raise notice '%; %; %; %; %; %', a, a[i], c, (select c || 'abc'), row(10,'aaa',NULL,30), NULL;
2661 end;$$ language plpgsql;
2662 select raise_exprs();
2663 NOTICE: {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); <NULL>
2669 drop function raise_exprs();
2670 -- continue statement
2671 create table conttesttbl(idx serial, v integer);
2672 NOTICE: CREATE TABLE will create implicit sequence "conttesttbl_idx_seq" for serial column "conttesttbl.idx"
2673 insert into conttesttbl(v) values(10);
2674 insert into conttesttbl(v) values(20);
2675 insert into conttesttbl(v) values(30);
2676 insert into conttesttbl(v) values(40);
2677 create function continue_test1() returns void as $$
2678 declare _i integer = 0; _r record;
2680 raise notice '---1---';
2683 raise notice '%', _i;
2684 continue when _i < 10;
2688 raise notice '---2---';
2693 raise notice '%', _i;
2694 continue lbl when _i > 0;
2699 raise notice '---3---';
2703 continue the_loop when _i % 2 = 0;
2704 raise notice '%', _i;
2707 raise notice '---4---';
2708 for _i in 1..10 loop
2710 -- applies to outer loop, not the nested begin block
2711 continue when _i < 5;
2712 raise notice '%', _i;
2716 raise notice '---5---';
2717 for _r in select * from conttesttbl loop
2718 continue when _r.v <= 20;
2719 raise notice '%', _r.v;
2722 raise notice '---6---';
2723 for _r in execute 'select * from conttesttbl' loop
2724 continue when _r.v <= 20;
2725 raise notice '%', _r.v;
2728 raise notice '---7---';
2730 raise notice '%', _i;
2731 continue when _i = 3;
2734 raise notice '---8---';
2737 raise notice '%', _i;
2739 continue when _i = 3;
2742 raise notice '---9---';
2743 for _r in select * from conttesttbl order by v limit 1 loop
2744 raise notice '%', _r.v;
2748 raise notice '---10---';
2749 for _r in execute 'select * from conttesttbl order by v limit 1' loop
2750 raise notice '%', _r.v;
2753 end; $$ language plpgsql;
2754 select continue_test1();
2813 -- CONTINUE is only legal inside a loop
2814 create function continue_test2() returns void as $$
2821 $$ language plpgsql;
2823 select continue_test2();
2824 ERROR: CONTINUE cannot be used outside a loop
2825 CONTEXT: PL/pgSQL function continue_test2()
2826 -- CONTINUE can't reference the label of a named block
2827 create function continue_test3() returns void as $$
2832 continue begin_block1;
2836 $$ language plpgsql;
2838 select continue_test3();
2839 ERROR: CONTINUE cannot be used outside a loop
2840 CONTEXT: PL/pgSQL function continue_test3()
2841 drop function continue_test1();
2842 drop function continue_test2();
2843 drop function continue_test3();
2844 drop table conttesttbl;
2845 -- verbose end block and end loop
2846 create function end_label1() returns void as $$
2850 for _i in 1 .. 10 loop
2854 for _i in 1 .. 10 loop
2858 $$ language plpgsql;
2859 select end_label1();
2865 drop function end_label1();
2866 -- should fail: undefined end label
2867 create function end_label2() returns void as $$
2869 for _i in 1 .. 10 loop
2873 $$ language plpgsql;
2874 ERROR: label does not exist at or near "flbl1"
2875 LINE 5: end loop flbl1;
2877 -- should fail: end label does not match start label
2878 create function end_label3() returns void as $$
2882 for _i in 1 .. 10 loop
2884 end loop outer_label;
2886 $$ language plpgsql;
2887 ERROR: end label "outer_label" differs from block's label "inner_label"
2888 LINE 7: end loop outer_label;
2890 -- should fail: end label on a block without a start label
2891 create function end_label4() returns void as $$
2894 for _i in 1 .. 10 loop
2896 end loop outer_label;
2898 $$ language plpgsql;
2899 ERROR: end label "outer_label" specified for unlabelled block
2900 LINE 6: end loop outer_label;
2902 -- using list of scalars in fori and fore stmts
2903 create function for_vect() returns void as $proc$
2904 <<lbl>>declare a integer; b varchar; c varchar; r record;
2907 for i in 1 .. 3 loop
2908 raise notice '%', i;
2910 -- fore with record var
2911 for r in select gs as aa, 'BB' as bb, 'CC' as cc from generate_series(1,4) gs loop
2912 raise notice '% % %', r.aa, r.bb, r.cc;
2914 -- fore with single scalar
2915 for a in select gs from generate_series(1,4) gs loop
2916 raise notice '%', a;
2918 -- fore with multiple scalars
2919 for a,b,c in select gs, 'BB','CC' from generate_series(1,4) gs loop
2920 raise notice '% % %', a, b, c;
2922 -- using qualified names in fors, fore is enabled, disabled only for fori
2923 for lbl.a, lbl.b, lbl.c in execute $$select gs, 'bb','cc' from generate_series(1,4) gs$$ loop
2924 raise notice '% % %', a, b, c;
2927 $proc$ language plpgsql;
2953 -- regression test: verify that multiple uses of same plpgsql datum within
2954 -- a SQL command all get mapped to the same $n parameter. The return value
2955 -- of the SELECT is not important, we only care that it doesn't fail with
2956 -- a complaint about an ungrouped column reference.
2957 create function multi_datum_use(p1 int) returns bool as $$
2962 select into x,y unique1/p1, unique1/$1 from tenk1 group by unique1/p1;
2964 end$$ language plpgsql;
2965 select multi_datum_use(42);
2972 -- Test STRICT limiter in both planned and EXECUTE invocations.
2973 -- Note that a data-modifying query is quasi strict (disallow multi rows)
2974 -- by default in the planned case, but not in EXECUTE.
2976 create temp table foo (f1 int, f2 int);
2977 insert into foo values (1,2), (3,4);
2978 create or replace function footest() returns void as $$
2982 insert into foo values(5,6) returning * into x;
2983 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2984 end$$ language plpgsql;
2986 NOTICE: x.f1 = 5, x.f2 = 6
2992 create or replace function footest() returns void as $$
2995 -- should fail due to implicit strict
2996 insert into foo values(7,8),(9,10) returning * into x;
2997 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2998 end$$ language plpgsql;
3000 ERROR: query returned more than one row
3001 CONTEXT: PL/pgSQL function footest() line 5 at SQL statement
3002 create or replace function footest() returns void as $$
3006 execute 'insert into foo values(5,6) returning *' into x;
3007 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
3008 end$$ language plpgsql;
3010 NOTICE: x.f1 = 5, x.f2 = 6
3016 create or replace function footest() returns void as $$
3019 -- this should work since EXECUTE isn't as picky
3020 execute 'insert into foo values(7,8),(9,10) returning *' into x;
3021 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
3022 end$$ language plpgsql;
3024 NOTICE: x.f1 = 7, x.f2 = 8
3041 create or replace function footest() returns void as $$
3045 select * from foo where f1 = 3 into strict x;
3046 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
3047 end$$ language plpgsql;
3049 NOTICE: x.f1 = 3, x.f2 = 4
3055 create or replace function footest() returns void as $$
3058 -- should fail, no rows
3059 select * from foo where f1 = 0 into strict x;
3060 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
3061 end$$ language plpgsql;
3063 ERROR: query returned no rows
3064 CONTEXT: PL/pgSQL function footest() line 5 at SQL statement
3065 create or replace function footest() returns void as $$
3068 -- should fail, too many rows
3069 select * from foo where f1 > 3 into strict x;
3070 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
3071 end$$ language plpgsql;
3073 ERROR: query returned more than one row
3074 CONTEXT: PL/pgSQL function footest() line 5 at SQL statement
3075 create or replace function footest() returns void as $$
3079 execute 'select * from foo where f1 = 3' into strict x;
3080 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
3081 end$$ language plpgsql;
3083 NOTICE: x.f1 = 3, x.f2 = 4
3089 create or replace function footest() returns void as $$
3092 -- should fail, no rows
3093 execute 'select * from foo where f1 = 0' into strict x;
3094 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
3095 end$$ language plpgsql;
3097 ERROR: query returned no rows
3098 CONTEXT: PL/pgSQL function footest() line 5 at EXECUTE statement
3099 create or replace function footest() returns void as $$
3102 -- should fail, too many rows
3103 execute 'select * from foo where f1 > 3' into strict x;
3104 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
3105 end$$ language plpgsql;
3107 ERROR: query returned more than one row
3108 CONTEXT: PL/pgSQL function footest() line 5 at EXECUTE statement
3109 drop function footest();
3110 -- test scrollable cursor support
3111 create function sc_test() returns setof integer as $$
3113 c scroll cursor for select f1 from int4_tbl;
3117 fetch last from c into x;
3120 fetch prior from c into x;
3124 $$ language plpgsql;
3125 select * from sc_test();
3135 create or replace function sc_test() returns setof integer as $$
3137 c no scroll cursor for select f1 from int4_tbl;
3141 fetch last from c into x;
3144 fetch prior from c into x;
3148 $$ language plpgsql;
3149 select * from sc_test(); -- fails because of NO SCROLL specification
3150 ERROR: cursor can only scan forward
3151 HINT: Declare it with SCROLL option to enable backward scan.
3152 CONTEXT: PL/pgSQL function sc_test() line 7 at FETCH
3153 create or replace function sc_test() returns setof integer as $$
3158 open c scroll for select f1 from int4_tbl;
3159 fetch last from c into x;
3162 fetch prior from c into x;
3166 $$ language plpgsql;
3167 select * from sc_test();
3177 create or replace function sc_test() returns setof integer as $$
3182 open c scroll for execute 'select f1 from int4_tbl';
3183 fetch last from c into x;
3186 fetch relative -2 from c into x;
3190 $$ language plpgsql;
3191 select * from sc_test();
3199 create or replace function sc_test() returns setof integer as $$
3204 open c scroll for execute 'select f1 from int4_tbl';
3205 fetch last from c into x;
3208 move backward 2 from c;
3209 fetch relative -1 from c into x;
3213 $$ language plpgsql;
3214 select * from sc_test();
3221 create or replace function sc_test() returns setof integer as $$
3223 c cursor for select * from generate_series(1, 10);
3228 move relative 2 in c;
3232 fetch next from c into x;
3239 $$ language plpgsql;
3240 select * from sc_test();
3248 create or replace function sc_test() returns setof integer as $$
3250 c cursor for select * from generate_series(1, 10);
3254 move forward all in c;
3255 fetch backward from c into x;
3261 $$ language plpgsql;
3262 select * from sc_test();
3268 drop function sc_test();
3269 -- test qualified variable names
3270 create function pl_qual_names (param1 int) returns void as $$
3279 raise notice 'param1 = %', param1;
3280 raise notice 'pl_qual_names.param1 = %', pl_qual_names.param1;
3281 raise notice 'outerblock.param1 = %', outerblock.param1;
3282 raise notice 'innerblock.param1 = %', innerblock.param1;
3285 $$ language plpgsql;
3286 select pl_qual_names(42);
3288 NOTICE: pl_qual_names.param1 = 42
3289 NOTICE: outerblock.param1 = 1
3290 NOTICE: innerblock.param1 = 2
3296 drop function pl_qual_names(int);
3297 -- tests for RETURN QUERY
3298 create function ret_query1(out int, out int) returns setof record as $$
3303 return query select x + 1, x * 10 from generate_series(0, 10) s (x);
3306 $$ language plpgsql;
3307 select * from ret_query1();
3325 create type record_type as (x text, y int, z boolean);
3326 create or replace function ret_query2(lim int) returns setof record_type as $$
3328 return query select md5(s.x::text), s.x, s.x > 0
3329 from generate_series(-8, lim) s (x) where s.x % 2 = 0;
3331 $$ language plpgsql;
3332 select * from ret_query2(8);
3334 ----------------------------------+----+---
3335 a8d2ec85eaf98407310b72eb73dda247 | -8 | f
3336 596a3d04481816330f07e4f97510c28f | -6 | f
3337 0267aaf632e87a63288a08331f22c7c3 | -4 | f
3338 5d7b9adcbe1c629ec722529dd12e5129 | -2 | f
3339 cfcd208495d565ef66e7dff9f98764da | 0 | f
3340 c81e728d9d4c2f636f067f89cc14862c | 2 | t
3341 a87ff679a2f3e71d9181a67b7542122c | 4 | t
3342 1679091c5a880faf6fb5e6087eb1b2dc | 6 | t
3343 c9f0f895fb98ab9159f51fd0297e236d | 8 | t
3346 -- test EXECUTE USING
3347 create function exc_using(int, text) returns int as $$
3350 for i in execute 'select * from generate_series(1,$1)' using $1+1 loop
3351 raise notice '%', i;
3353 execute 'select $2 + $2*3 + length($1)' into i using $2,$1;
3356 $$ language plpgsql;
3357 select exc_using(5, 'foobar');
3369 drop function exc_using(int, text);
3370 create or replace function exc_using(int) returns void as $$
3375 open c for execute 'select * from generate_series(1,$1)' using $1+1;
3378 exit when not found;
3379 raise notice '%', i;
3384 $$ language plpgsql;
3385 select exc_using(5);
3397 drop function exc_using(int);
3398 -- test FOR-over-cursor
3399 create or replace function forc01() returns void as $$
3401 c cursor(r1 integer, r2 integer)
3402 for select * from generate_series(r1,r2) i;
3404 for select * from generate_series(41,43) i;
3406 for r in c(5,7) loop
3407 raise notice '% from %', r.i, c;
3409 -- again, to test if cursor was closed properly
3410 for r in c(9,10) loop
3411 raise notice '% from %', r.i, c;
3413 -- and test a parameterless cursor
3415 raise notice '% from %', r.i, c2;
3417 -- and try it with a hand-assigned name
3418 raise notice 'after loop, c2 = %', c2;
3419 c2 := 'special_name';
3421 raise notice '% from %', r.i, c2;
3423 raise notice 'after loop, c2 = %', c2;
3424 -- and try it with a generated name
3425 -- (which we can't show in the output because it's variable)
3428 raise notice '%', r.i;
3430 raise notice 'after loop, c2 = %', c2;
3433 $$ language plpgsql;
3443 NOTICE: after loop, c2 = c2
3444 NOTICE: 41 from special_name
3445 NOTICE: 42 from special_name
3446 NOTICE: 43 from special_name
3447 NOTICE: after loop, c2 = special_name
3451 NOTICE: after loop, c2 = <NULL>
3457 -- try updating the cursor's current row
3458 create temp table forc_test as
3459 select n as i, n as j from generate_series(1,10) n;
3460 create or replace function forc01() returns void as $$
3462 c cursor for select * from forc_test;
3465 raise notice '%, %', r.i, r.j;
3466 update forc_test set i = i * 100, j = r.j * 2 where current of c;
3469 $$ language plpgsql;
3486 select * from forc_test;
3501 -- same, with a cursor whose portal name doesn't match variable name
3502 create or replace function forc01() returns void as $$
3504 c refcursor := 'fooled_ya';
3507 open c for select * from forc_test;
3510 exit when not found;
3511 raise notice '%, %', r.i, r.j;
3512 update forc_test set i = i * 100, j = r.j * 2 where current of c;
3515 $$ language plpgsql;
3532 select * from forc_test;
3547 drop function forc01();
3548 -- fail because cursor has no query bound to it
3549 create or replace function forc_bad() returns void as $$
3554 raise notice '%', r.i;
3557 $$ language plpgsql;
3558 ERROR: cursor FOR loop must use a bound cursor variable
3559 LINE 5: for r in c loop
3561 -- test RETURN QUERY EXECUTE
3562 create or replace function return_dquery()
3563 returns setof int as $$
3565 return query execute 'select * from (values(10),(20)) f';
3566 return query execute 'select * from (values($1),($2)) f' using 40,50;
3568 $$ language plpgsql;
3569 select * from return_dquery();
3578 drop function return_dquery();
3579 -- test RETURN QUERY with dropped columns
3580 create table tabwithcols(a int, b int, c int, d int);
3581 insert into tabwithcols values(10,20,30,40),(50,60,70,80);
3582 create or replace function returnqueryf()
3583 returns setof tabwithcols as $$
3585 return query select * from tabwithcols;
3586 return query execute 'select * from tabwithcols';
3588 $$ language plpgsql;
3589 select * from returnqueryf();
3598 alter table tabwithcols drop column b;
3599 select * from returnqueryf();
3608 alter table tabwithcols drop column d;
3609 select * from returnqueryf();
3618 alter table tabwithcols add column d int;
3619 select * from returnqueryf();
3628 drop function returnqueryf();
3629 drop table tabwithcols;
3630 -- Tests for 8.4's new RAISE features
3631 create or replace function raise_test() returns void as $$
3633 raise notice '% % %', 1, 2, 3
3634 using errcode = '55001', detail = 'some detail info', hint = 'some hint';
3635 raise '% % %', 1, 2, 3
3636 using errcode = 'division_by_zero', detail = 'some detail info';
3638 $$ language plpgsql;
3639 select raise_test();
3641 DETAIL: some detail info
3644 DETAIL: some detail info
3645 -- Since we can't actually see the thrown SQLSTATE in default psql output,
3646 -- test it like this; this also tests re-RAISE
3647 create or replace function raise_test() returns void as $$
3650 using errcode = 'division_by_zero', detail = 'some detail info';
3653 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3656 $$ language plpgsql;
3657 select raise_test();
3658 NOTICE: SQLSTATE: 22012 SQLERRM: check me
3660 DETAIL: some detail info
3661 create or replace function raise_test() returns void as $$
3664 using errcode = '1234F', detail = 'some detail info';
3667 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3670 $$ language plpgsql;
3671 select raise_test();
3672 NOTICE: SQLSTATE: 1234F SQLERRM: check me
3674 DETAIL: some detail info
3675 -- SQLSTATE specification in WHEN
3676 create or replace function raise_test() returns void as $$
3679 using errcode = '1234F', detail = 'some detail info';
3681 when sqlstate '1234F' then
3682 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3685 $$ language plpgsql;
3686 select raise_test();
3687 NOTICE: SQLSTATE: 1234F SQLERRM: check me
3689 DETAIL: some detail info
3690 create or replace function raise_test() returns void as $$
3692 raise division_by_zero using detail = 'some detail info';
3695 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3698 $$ language plpgsql;
3699 select raise_test();
3700 NOTICE: SQLSTATE: 22012 SQLERRM: division_by_zero
3701 ERROR: division_by_zero
3702 DETAIL: some detail info
3703 create or replace function raise_test() returns void as $$
3705 raise division_by_zero;
3707 $$ language plpgsql;
3708 select raise_test();
3709 ERROR: division_by_zero
3710 create or replace function raise_test() returns void as $$
3712 raise sqlstate '1234F';
3714 $$ language plpgsql;
3715 select raise_test();
3717 create or replace function raise_test() returns void as $$
3719 raise division_by_zero using message = 'custom' || ' message';
3721 $$ language plpgsql;
3722 select raise_test();
3723 ERROR: custom message
3724 create or replace function raise_test() returns void as $$
3726 raise using message = 'custom' || ' message', errcode = '22012';
3728 $$ language plpgsql;
3729 select raise_test();
3730 ERROR: custom message
3731 -- conflict on message
3732 create or replace function raise_test() returns void as $$
3734 raise notice 'some message' using message = 'custom' || ' message', errcode = '22012';
3736 $$ language plpgsql;
3737 select raise_test();
3738 ERROR: RAISE option already specified: MESSAGE
3739 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
3740 -- conflict on errcode
3741 create or replace function raise_test() returns void as $$
3743 raise division_by_zero using message = 'custom' || ' message', errcode = '22012';
3745 $$ language plpgsql;
3746 select raise_test();
3747 ERROR: RAISE option already specified: ERRCODE
3748 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
3749 -- nothing to re-RAISE
3750 create or replace function raise_test() returns void as $$
3754 $$ language plpgsql;
3755 select raise_test();
3756 ERROR: RAISE without parameters cannot be used outside an exception handler
3757 CONTEXT: PL/pgSQL function raise_test() line 3 at RAISE
3758 -- test access to exception data
3759 create function zero_divide() returns int as $$
3764 $$ language plpgsql;
3765 create or replace function raise_test() returns void as $$
3767 raise exception 'custom exception'
3768 using detail = 'some detail of custom exception',
3769 hint = 'some hint related to custom exception';
3771 $$ language plpgsql;
3772 create function stacked_diagnostics_test() returns void as $$
3773 declare _sqlstate text;
3777 perform zero_divide();
3778 exception when others then
3779 get stacked diagnostics
3780 _sqlstate = returned_sqlstate,
3781 _message = message_text,
3782 _context = pg_exception_context;
3783 raise notice 'sqlstate: %, message: %, context: [%]',
3784 _sqlstate, _message, replace(_context, E'\n', ' <- ');
3786 $$ language plpgsql;
3787 select stacked_diagnostics_test();
3788 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]
3789 stacked_diagnostics_test
3790 --------------------------
3794 create or replace function stacked_diagnostics_test() returns void as $$
3795 declare _detail text;
3799 perform raise_test();
3800 exception when others then
3801 get stacked diagnostics
3802 _message = message_text,
3803 _detail = pg_exception_detail,
3804 _hint = pg_exception_hint;
3805 raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
3807 $$ language plpgsql;
3808 select stacked_diagnostics_test();
3809 NOTICE: message: custom exception, detail: some detail of custom exception, hint: some hint related to custom exception
3810 stacked_diagnostics_test
3811 --------------------------
3815 -- fail, cannot use stacked diagnostics statement outside handler
3816 create or replace function stacked_diagnostics_test() returns void as $$
3817 declare _detail text;
3821 get stacked diagnostics
3822 _message = message_text,
3823 _detail = pg_exception_detail,
3824 _hint = pg_exception_hint;
3825 raise notice 'message: %, detail: %, hint: %', _message, _detail, _hint;
3827 $$ language plpgsql;
3828 select stacked_diagnostics_test();
3829 ERROR: GET STACKED DIAGNOSTICS cannot be used outside an exception handler
3830 CONTEXT: PL/pgSQL function stacked_diagnostics_test() line 6 at GET DIAGNOSTICS
3831 drop function zero_divide();
3832 drop function stacked_diagnostics_test();
3833 -- check cases where implicit SQLSTATE variable could be confused with
3834 -- SQLSTATE as a keyword, cf bug #5524
3835 create or replace function raise_test() returns void as $$
3839 when sqlstate '22012' then
3840 raise notice using message = sqlstate;
3841 raise sqlstate '22012' using message = 'substitute message';
3843 $$ language plpgsql;
3844 select raise_test();
3846 ERROR: substitute message
3847 drop function raise_test();
3848 -- test CASE statement
3849 create or replace function case_test(bigint) returns text as $$
3859 return 'three, four or eight';
3862 when a+b, a+b+1 then
3863 return 'eleven, twelve';
3866 $$ language plpgsql immutable;
3867 select case_test(1);
3873 select case_test(2);
3879 select case_test(3);
3881 ----------------------
3882 three, four or eight
3885 select case_test(4);
3887 ----------------------
3888 three, four or eight
3891 select case_test(5); -- fails
3892 ERROR: case not found
3893 HINT: CASE statement is missing ELSE part.
3894 CONTEXT: PL/pgSQL function case_test(bigint) line 5 at CASE
3895 select case_test(8);
3897 ----------------------
3898 three, four or eight
3901 select case_test(10);
3907 select case_test(11);
3913 select case_test(12);
3919 select case_test(13); -- fails
3920 ERROR: case not found
3921 HINT: CASE statement is missing ELSE part.
3922 CONTEXT: PL/pgSQL function case_test(bigint) line 5 at CASE
3923 create or replace function catch() returns void as $$
3925 raise notice '%', case_test(6);
3927 when case_not_found then
3928 raise notice 'caught case_not_found % %', SQLSTATE, SQLERRM;
3930 $$ language plpgsql;
3932 NOTICE: caught case_not_found 20000 case not found
3938 -- test the searched variant too, as well as ELSE
3939 create or replace function case_test(bigint) returns text as $$
3945 when $1 = a + 2 then
3951 $$ language plpgsql immutable;
3952 select case_test(1);
3958 select case_test(2);
3964 select case_test(12);
3970 select case_test(13);
3976 drop function catch();
3977 drop function case_test(bigint);
3978 -- test variadic functions
3979 create or replace function vari(variadic int[])
3982 for i in array_lower($1,1)..array_upper($1,1) loop
3983 raise notice '%', $1[i];
3985 $$ language plpgsql;
3986 select vari(1,2,3,4,5);
4006 select vari(variadic array[5,6,7]);
4015 drop function vari(int[]);
4017 create or replace function pleast(variadic numeric[])
4018 returns numeric as $$
4019 declare aux numeric = $1[array_lower($1,1)];
4021 for i in array_lower($1,1)+1..array_upper($1,1) loop
4022 if $1[i] < aux then aux := $1[i]; end if;
4026 $$ language plpgsql immutable strict;
4027 select pleast(10,1,2,3,-16);
4033 select pleast(10.2,2.2,-1.1);
4039 select pleast(10.2,10, -20);
4045 select pleast(10,20, -1.0);
4051 -- in case of conflict, non-variadic version is preferred
4052 create or replace function pleast(numeric)
4053 returns numeric as $$
4055 raise notice 'non-variadic function called';
4058 $$ language plpgsql immutable strict;
4060 NOTICE: non-variadic function called
4066 drop function pleast(numeric[]);
4067 drop function pleast(numeric);
4068 -- test table functions
4069 create function tftest(int) returns table(a int, b int) as $$
4071 return query select $1, $1+i from generate_series(1,5) g(i);
4073 $$ language plpgsql immutable strict;
4074 select * from tftest(10);
4084 create or replace function tftest(a1 int) returns table(a int, b int) as $$
4086 a := a1; b := a1 + 1;
4088 a := a1 * 10; b := a1 * 10 + 1;
4091 $$ language plpgsql immutable strict;
4092 select * from tftest(10);
4099 drop function tftest(int);
4100 create or replace function rttest()
4101 returns setof int as $$
4104 return query values(10),(20);
4105 get diagnostics rc = row_count;
4106 raise notice '% %', found, rc;
4107 return query select * from (values(10),(20)) f(a) where false;
4108 get diagnostics rc = row_count;
4109 raise notice '% %', found, rc;
4110 return query execute 'values(10),(20)';
4111 get diagnostics rc = row_count;
4112 raise notice '% %', found, rc;
4113 return query execute 'select * from (values(10),(20)) f(a) where false';
4114 get diagnostics rc = row_count;
4115 raise notice '% %', found, rc;
4117 $$ language plpgsql;
4118 select * from rttest();
4131 drop function rttest();
4132 -- Test for proper cleanup at subtransaction exit. This example
4133 -- exposed a bug in PG 8.2.
4134 CREATE FUNCTION leaker_1(fail BOOL) RETURNS INTEGER AS $$
4139 v_var := (leaker_2(fail)).error_code;
4141 WHEN others THEN RETURN 0;
4145 $$ LANGUAGE plpgsql;
4146 CREATE FUNCTION leaker_2(fail BOOL, OUT error_code INTEGER, OUT new_id INTEGER)
4147 RETURNS RECORD AS $$
4150 RAISE EXCEPTION 'fail ...';
4156 $$ LANGUAGE plpgsql;
4157 SELECT * FROM leaker_1(false);
4163 SELECT * FROM leaker_1(true);
4169 DROP FUNCTION leaker_1(bool);
4170 DROP FUNCTION leaker_2(bool);
4171 -- Test for appropriate cleanup of non-simple expression evaluations
4172 -- (bug in all versions prior to August 2010)
4173 CREATE FUNCTION nonsimple_expr_test() RETURNS text[] AS $$
4179 arr := array[array['foo','bar'], array['baz', 'quux']];
4182 -- use sub-SELECTs to make expressions non-simple
4183 arr[(SELECT i)][(SELECT i+1)] := (SELECT lr);
4186 $$ LANGUAGE plpgsql;
4187 SELECT nonsimple_expr_test();
4189 -------------------------
4190 {{foo,fool},{baz,quux}}
4193 DROP FUNCTION nonsimple_expr_test();
4194 CREATE FUNCTION nonsimple_expr_test() RETURNS integer AS $$
4196 i integer NOT NULL := 0;
4199 i := (SELECT NULL::integer); -- should throw error
4202 i := (SELECT 1::integer);
4206 $$ LANGUAGE plpgsql;
4207 SELECT nonsimple_expr_test();
4209 ---------------------
4213 DROP FUNCTION nonsimple_expr_test();
4215 -- Test cases involving recursion and error recovery in simple expressions
4216 -- (bugs in all versions before October 2010). The problems are most
4217 -- easily exposed by mutual recursion between plpgsql and sql functions.
4219 create function recurse(float8) returns float8 as
4223 return sql_recurse($1 - 1);
4228 $$ language plpgsql;
4229 -- "limit" is to prevent this from being inlined
4230 create function sql_recurse(float8) returns float8 as
4231 $$ select recurse($1) limit 1; $$ language sql;
4238 create function error1(text) returns text language sql as
4239 $$ SELECT relname::text FROM pg_class c WHERE c.oid = $1::regclass $$;
4240 create function error2(p_name_table text) returns text language plpgsql as $$
4242 return error1(p_name_table);
4245 create table public.stuffs (stuff text);
4247 select error2('nonexistent.stuffs');
4248 ERROR: schema "nonexistent" does not exist
4249 CONTEXT: SQL function "error1" statement 1
4250 PL/pgSQL function error2(text) line 3 at RETURN
4252 select error2('public.stuffs');
4259 drop function error2(p_name_table text);
4260 drop function error1(text);
4261 -- Test handling of string literals.
4262 set standard_conforming_strings = off;
4263 create or replace function strtest() returns text as $$
4265 raise notice 'foo\\bar\041baz';
4266 return 'foo\\bar\041baz';
4268 $$ language plpgsql;
4269 WARNING: nonstandard use of \\ in a string literal
4270 LINE 3: raise notice 'foo\\bar\041baz';
4272 HINT: Use the escape string syntax for backslashes, e.g., E'\\'.
4273 WARNING: nonstandard use of \\ in a string literal
4274 LINE 4: return 'foo\\bar\041baz';
4276 HINT: Use the escape string syntax for backslashes, e.g., E'\\'.
4277 WARNING: nonstandard use of \\ in a string literal
4278 LINE 4: return 'foo\\bar\041baz';
4280 HINT: Use the escape string syntax for backslashes, e.g., E'\\'.
4283 WARNING: nonstandard use of \\ in a string literal
4284 LINE 1: SELECT 'foo\\bar\041baz'
4286 HINT: Use the escape string syntax for backslashes, e.g., E'\\'.
4287 QUERY: SELECT 'foo\\bar\041baz'
4288 CONTEXT: PL/pgSQL function strtest() line 4 at RETURN
4294 create or replace function strtest() returns text as $$
4296 raise notice E'foo\\bar\041baz';
4297 return E'foo\\bar\041baz';
4299 $$ language plpgsql;
4307 set standard_conforming_strings = on;
4308 create or replace function strtest() returns text as $$
4310 raise notice 'foo\\bar\041baz\';
4311 return 'foo\\bar\041baz\';
4313 $$ language plpgsql;
4315 NOTICE: foo\\bar\041baz\
4321 create or replace function strtest() returns text as $$
4323 raise notice E'foo\\bar\041baz';
4324 return E'foo\\bar\041baz';
4326 $$ language plpgsql;
4334 drop function strtest();
4335 -- Test anonymous code blocks.
4339 FOR r IN SELECT rtrim(roomno) AS roomno, comment FROM Room ORDER BY roomno
4341 RAISE NOTICE '%, %', r.roomno, r.comment;
4344 NOTICE: 001, Entrance
4347 NOTICE: 004, Technical
4349 NOTICE: 102, Conference
4350 NOTICE: 103, Restroom
4351 NOTICE: 104, Technical
4354 -- these are to check syntax error reporting
4355 DO LANGUAGE plpgsql $$begin return 1; end$$;
4356 ERROR: RETURN cannot have a parameter in function returning void
4357 LINE 1: DO LANGUAGE plpgsql $$begin return 1; end$$;
4362 FOR r IN SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
4364 RAISE NOTICE '%, %', r.roomno, r.comment;
4367 ERROR: column "foo" does not exist
4368 LINE 1: SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomn...
4370 QUERY: SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
4371 CONTEXT: PL/pgSQL function inline_code_block line 4 at FOR over SELECT rows
4372 -- Check variable scoping -- a var is not available in its own or prior
4373 -- default expressions.
4374 create function scope_test() returns int as $$
4375 declare x int := 42;
4377 declare y int := x + 1;
4383 $$ language plpgsql;
4384 select scope_test();
4390 drop function scope_test();
4391 -- Check handling of conflicts between plpgsql vars and table columns.
4392 set plpgsql.variable_conflict = error;
4393 create function conflict_test() returns setof int8_tbl as $$
4397 for r in select q1,q2 from int8_tbl loop
4401 $$ language plpgsql;
4402 select * from conflict_test();
4403 ERROR: column reference "q1" is ambiguous
4404 LINE 1: select q1,q2 from int8_tbl
4406 DETAIL: It could refer to either a PL/pgSQL variable or a table column.
4407 QUERY: select q1,q2 from int8_tbl
4408 CONTEXT: PL/pgSQL function conflict_test() line 5 at FOR over SELECT rows
4409 create or replace function conflict_test() returns setof int8_tbl as $$
4410 #variable_conflict use_variable
4414 for r in select q1,q2 from int8_tbl loop
4418 $$ language plpgsql;
4419 select * from conflict_test();
4421 ----+-------------------
4423 42 | 4567890123456789
4425 42 | 4567890123456789
4426 42 | -4567890123456789
4429 create or replace function conflict_test() returns setof int8_tbl as $$
4430 #variable_conflict use_column
4434 for r in select q1,q2 from int8_tbl loop
4438 $$ language plpgsql;
4439 select * from conflict_test();
4441 ------------------+-------------------
4443 123 | 4567890123456789
4444 4567890123456789 | 123
4445 4567890123456789 | 4567890123456789
4446 4567890123456789 | -4567890123456789
4449 drop function conflict_test();
4450 -- Check that an unreserved keyword can be used as a variable name
4451 create function unreserved_test() returns int as $$
4455 forward := forward * 2;
4458 $$ language plpgsql;
4459 select unreserved_test();
4465 drop function unreserved_test();
4467 -- Test FOREACH over arrays
4469 create function foreach_test(anyarray)
4473 foreach x in array $1
4475 raise notice '%', x;
4478 $$ language plpgsql;
4479 select foreach_test(ARRAY[1,2,3,4]);
4489 select foreach_test(ARRAY[[1,2],[3,4]]);
4499 create or replace function foreach_test(anyarray)
4503 foreach x slice 1 in array $1
4505 raise notice '%', x;
4508 $$ language plpgsql;
4510 select foreach_test(ARRAY[1,2,3,4]);
4511 ERROR: FOREACH ... SLICE loop variable must be of an array type
4512 CONTEXT: PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
4513 select foreach_test(ARRAY[[1,2],[3,4]]);
4514 ERROR: FOREACH ... SLICE loop variable must be of an array type
4515 CONTEXT: PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
4516 create or replace function foreach_test(anyarray)
4520 foreach x slice 1 in array $1
4522 raise notice '%', x;
4525 $$ language plpgsql;
4526 select foreach_test(ARRAY[1,2,3,4]);
4533 select foreach_test(ARRAY[[1,2],[3,4]]);
4541 -- higher level of slicing
4542 create or replace function foreach_test(anyarray)
4546 foreach x slice 2 in array $1
4548 raise notice '%', x;
4551 $$ language plpgsql;
4553 select foreach_test(ARRAY[1,2,3,4]);
4554 ERROR: slice dimension (2) is out of the valid range 0..1
4555 CONTEXT: PL/pgSQL function foreach_test(anyarray) line 4 at FOREACH over array
4557 select foreach_test(ARRAY[[1,2],[3,4]]);
4558 NOTICE: {{1,2},{3,4}}
4564 select foreach_test(ARRAY[[[1,2]],[[3,4]]]);
4572 create type xy_tuple AS (x int, y int);
4573 -- iteration over array of records
4574 create or replace function foreach_test(anyarray)
4578 foreach r in array $1
4580 raise notice '%', r;
4583 $$ language plpgsql;
4584 select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
4593 select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
4603 create or replace function foreach_test(anyarray)
4605 declare x int; y int;
4607 foreach x, y in array $1
4609 raise notice 'x = %, y = %', x, y;
4612 $$ language plpgsql;
4613 select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
4614 NOTICE: x = 10, y = 20
4615 NOTICE: x = 40, y = 69
4616 NOTICE: x = 35, y = 78
4622 select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
4623 NOTICE: x = 10, y = 20
4624 NOTICE: x = 40, y = 69
4625 NOTICE: x = 35, y = 78
4626 NOTICE: x = 88, y = 76
4632 -- slicing over array of composite types
4633 create or replace function foreach_test(anyarray)
4635 declare x xy_tuple[];
4637 foreach x slice 1 in array $1
4639 raise notice '%', x;
4642 $$ language plpgsql;
4643 select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]);
4644 NOTICE: {"(10,20)","(40,69)","(35,78)"}
4650 select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]);
4651 NOTICE: {"(10,20)","(40,69)"}
4652 NOTICE: {"(35,78)","(88,76)"}
4658 drop function foreach_test(anyarray);
4661 -- Assorted tests for array subscript assignment
4663 create temp table rtype (id int, ar text[]);
4664 create function arrayassign1() returns text[] language plpgsql as $$
4668 r := row(12, '{foo,bar,baz}')::rtype;
4669 r.ar[2] := 'replace';
4672 select arrayassign1();
4678 select arrayassign1(); -- try again to exercise internal caching
4684 create domain orderedarray as int[2]
4685 constraint sorted check (value[1] < value[2]);
4686 select '{1,2}'::orderedarray;
4692 select '{2,1}'::orderedarray; -- fail
4693 ERROR: value for domain orderedarray violates check constraint "sorted"
4694 create function testoa(x1 int, x2 int, x3 int) returns orderedarray
4695 language plpgsql as $$
4696 declare res orderedarray;
4698 res := array[x1, x2];
4702 select testoa(1,2,3);
4708 select testoa(1,2,3); -- try again to exercise internal caching
4714 select testoa(2,1,3); -- fail at initial assign
4715 ERROR: value for domain orderedarray violates check constraint "sorted"
4716 CONTEXT: PL/pgSQL function testoa(integer,integer,integer) line 4 at assignment
4717 select testoa(1,2,1); -- fail at update
4718 ERROR: value for domain orderedarray violates check constraint "sorted"
4719 CONTEXT: PL/pgSQL function testoa(integer,integer,integer) line 5 at assignment
4720 drop function arrayassign1();
4721 drop function testoa(x1 int, x2 int, x3 int);