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'' then
289 if new.hubname != old.hubname then
290 if count(*) > 0 from Hub where name = old.hubname then
291 raise exception ''no manual manipulation of HSlot'';
295 sname := ''HS.'' || trim(new.hubname);
296 sname := sname || ''.'';
297 sname := sname || new.slotno::text;
298 if length(sname) > 20 then
299 raise exception ''HSlot slotname "%" too long (20 char max)'', sname;
301 new.slotname := sname;
305 create trigger tg_hslot_biu before insert or update
306 on HSlot for each row execute procedure tg_hslot_biu();
307 -- ************************************************************
308 -- * BEFORE DELETE on HSlot
309 -- * - prevent from manual manipulation
310 -- ************************************************************
311 create function tg_hslot_bd() returns trigger as '
315 select into hubrec * from Hub where name = old.hubname;
319 if old.slotno > hubrec.nslots then
322 raise exception ''no manual manipulation of HSlot'';
325 create trigger tg_hslot_bd before delete
326 on HSlot for each row execute procedure tg_hslot_bd();
327 -- ************************************************************
328 -- * BEFORE INSERT on all slots
329 -- * - Check name prefix
330 -- ************************************************************
331 create function tg_chkslotname() returns trigger as '
333 if substr(new.slotname, 1, 2) != tg_argv[0] then
334 raise exception ''slotname must begin with %'', tg_argv[0];
339 create trigger tg_chkslotname before insert
340 on PSlot for each row execute procedure tg_chkslotname('PS');
341 create trigger tg_chkslotname before insert
342 on WSlot for each row execute procedure tg_chkslotname('WS');
343 create trigger tg_chkslotname before insert
344 on PLine for each row execute procedure tg_chkslotname('PL');
345 create trigger tg_chkslotname before insert
346 on IFace for each row execute procedure tg_chkslotname('IF');
347 create trigger tg_chkslotname before insert
348 on PHone for each row execute procedure tg_chkslotname('PH');
349 -- ************************************************************
350 -- * BEFORE INSERT or UPDATE on all slots with slotlink
351 -- * - Set slotlink to empty string if NULL value given
352 -- ************************************************************
353 create function tg_chkslotlink() returns trigger as '
355 if new.slotlink isnull then
356 new.slotlink := '''';
361 create trigger tg_chkslotlink before insert or update
362 on PSlot for each row execute procedure tg_chkslotlink();
363 create trigger tg_chkslotlink before insert or update
364 on WSlot for each row execute procedure tg_chkslotlink();
365 create trigger tg_chkslotlink before insert or update
366 on IFace for each row execute procedure tg_chkslotlink();
367 create trigger tg_chkslotlink before insert or update
368 on HSlot for each row execute procedure tg_chkslotlink();
369 create trigger tg_chkslotlink before insert or update
370 on PHone for each row execute procedure tg_chkslotlink();
371 -- ************************************************************
372 -- * BEFORE INSERT or UPDATE on all slots with backlink
373 -- * - Set backlink to empty string if NULL value given
374 -- ************************************************************
375 create function tg_chkbacklink() returns trigger as '
377 if new.backlink isnull then
378 new.backlink := '''';
383 create trigger tg_chkbacklink before insert or update
384 on PSlot for each row execute procedure tg_chkbacklink();
385 create trigger tg_chkbacklink before insert or update
386 on WSlot for each row execute procedure tg_chkbacklink();
387 create trigger tg_chkbacklink before insert or update
388 on PLine for each row execute procedure tg_chkbacklink();
389 -- ************************************************************
390 -- * BEFORE UPDATE on PSlot
391 -- * - do delete/insert instead of update if name changes
392 -- ************************************************************
393 create function tg_pslot_bu() returns trigger as '
395 if new.slotname != old.slotname then
396 delete from PSlot where slotname = old.slotname;
413 create trigger tg_pslot_bu before update
414 on PSlot for each row execute procedure tg_pslot_bu();
415 -- ************************************************************
416 -- * BEFORE UPDATE on WSlot
417 -- * - do delete/insert instead of update if name changes
418 -- ************************************************************
419 create function tg_wslot_bu() returns trigger as '
421 if new.slotname != old.slotname then
422 delete from WSlot where slotname = old.slotname;
439 create trigger tg_wslot_bu before update
440 on WSlot for each row execute procedure tg_Wslot_bu();
441 -- ************************************************************
442 -- * BEFORE UPDATE on PLine
443 -- * - do delete/insert instead of update if name changes
444 -- ************************************************************
445 create function tg_pline_bu() returns trigger as '
447 if new.slotname != old.slotname then
448 delete from PLine where slotname = old.slotname;
465 create trigger tg_pline_bu before update
466 on PLine for each row execute procedure tg_pline_bu();
467 -- ************************************************************
468 -- * BEFORE UPDATE on IFace
469 -- * - do delete/insert instead of update if name changes
470 -- ************************************************************
471 create function tg_iface_bu() returns trigger as '
473 if new.slotname != old.slotname then
474 delete from IFace where slotname = old.slotname;
491 create trigger tg_iface_bu before update
492 on IFace for each row execute procedure tg_iface_bu();
493 -- ************************************************************
494 -- * BEFORE UPDATE on HSlot
495 -- * - do delete/insert instead of update if name changes
496 -- ************************************************************
497 create function tg_hslot_bu() returns trigger as '
499 if new.slotname != old.slotname or new.hubname != old.hubname then
500 delete from HSlot where slotname = old.slotname;
517 create trigger tg_hslot_bu before update
518 on HSlot for each row execute procedure tg_hslot_bu();
519 -- ************************************************************
520 -- * BEFORE UPDATE on PHone
521 -- * - do delete/insert instead of update if name changes
522 -- ************************************************************
523 create function tg_phone_bu() returns trigger as '
525 if new.slotname != old.slotname then
526 delete from PHone where slotname = old.slotname;
541 create trigger tg_phone_bu before update
542 on PHone for each row execute procedure tg_phone_bu();
543 -- ************************************************************
544 -- * AFTER INSERT or UPDATE or DELETE on slot with backlink
545 -- * - Ensure that the opponent correctly points back to us
546 -- ************************************************************
547 create function tg_backlink_a() returns trigger as '
551 if tg_op = ''INSERT'' then
552 if new.backlink != '''' then
553 dummy := tg_backlink_set(new.backlink, new.slotname);
557 if tg_op = ''UPDATE'' then
558 if new.backlink != old.backlink then
559 if old.backlink != '''' then
560 dummy := tg_backlink_unset(old.backlink, old.slotname);
562 if new.backlink != '''' then
563 dummy := tg_backlink_set(new.backlink, new.slotname);
566 if new.slotname != old.slotname and new.backlink != '''' then
567 dummy := tg_slotlink_set(new.backlink, new.slotname);
572 if tg_op = ''DELETE'' then
573 if old.backlink != '''' then
574 dummy := tg_backlink_unset(old.backlink, old.slotname);
580 create trigger tg_backlink_a after insert or update or delete
581 on PSlot for each row execute procedure tg_backlink_a('PS');
582 create trigger tg_backlink_a after insert or update or delete
583 on WSlot for each row execute procedure tg_backlink_a('WS');
584 create trigger tg_backlink_a after insert or update or delete
585 on PLine for each row execute procedure tg_backlink_a('PL');
586 -- ************************************************************
587 -- * Support function to set the opponents backlink field
588 -- * if it does not already point to the requested slot
589 -- ************************************************************
590 create function tg_backlink_set(myname bpchar, blname bpchar)
597 mytype := substr(myname, 1, 2);
598 link := mytype || substr(blname, 1, 2);
599 if link = ''PLPL'' then
601 ''backlink between two phone lines does not make sense'';
603 if link in (''PLWS'', ''WSPL'') then
605 ''direct link of phone line to wall slot not permitted'';
607 if mytype = ''PS'' then
608 select into rec * from PSlot where slotname = myname;
610 raise exception ''% does not exist'', myname;
612 if rec.backlink != blname then
613 update PSlot set backlink = blname where slotname = myname;
617 if mytype = ''WS'' then
618 select into rec * from WSlot where slotname = myname;
620 raise exception ''% does not exist'', myname;
622 if rec.backlink != blname then
623 update WSlot set backlink = blname where slotname = myname;
627 if mytype = ''PL'' then
628 select into rec * from PLine where slotname = myname;
630 raise exception ''% does not exist'', myname;
632 if rec.backlink != blname then
633 update PLine set backlink = blname where slotname = myname;
637 raise exception ''illegal backlink beginning with %'', mytype;
640 -- ************************************************************
641 -- * Support function to clear out the backlink field if
642 -- * it still points to specific slot
643 -- ************************************************************
644 create function tg_backlink_unset(bpchar, bpchar)
652 mytype := substr(myname, 1, 2);
653 if mytype = ''PS'' then
654 select into rec * from PSlot where slotname = myname;
658 if rec.backlink = blname then
659 update PSlot set backlink = '''' where slotname = myname;
663 if mytype = ''WS'' then
664 select into rec * from WSlot where slotname = myname;
668 if rec.backlink = blname then
669 update WSlot set backlink = '''' where slotname = myname;
673 if mytype = ''PL'' then
674 select into rec * from PLine where slotname = myname;
678 if rec.backlink = blname then
679 update PLine set backlink = '''' where slotname = myname;
685 -- ************************************************************
686 -- * AFTER INSERT or UPDATE or DELETE on slot with slotlink
687 -- * - Ensure that the opponent correctly points back to us
688 -- ************************************************************
689 create function tg_slotlink_a() returns trigger as '
693 if tg_op = ''INSERT'' then
694 if new.slotlink != '''' then
695 dummy := tg_slotlink_set(new.slotlink, new.slotname);
699 if tg_op = ''UPDATE'' then
700 if new.slotlink != old.slotlink then
701 if old.slotlink != '''' then
702 dummy := tg_slotlink_unset(old.slotlink, old.slotname);
704 if new.slotlink != '''' then
705 dummy := tg_slotlink_set(new.slotlink, new.slotname);
708 if new.slotname != old.slotname and new.slotlink != '''' then
709 dummy := tg_slotlink_set(new.slotlink, new.slotname);
714 if tg_op = ''DELETE'' then
715 if old.slotlink != '''' then
716 dummy := tg_slotlink_unset(old.slotlink, old.slotname);
722 create trigger tg_slotlink_a after insert or update or delete
723 on PSlot for each row execute procedure tg_slotlink_a('PS');
724 create trigger tg_slotlink_a after insert or update or delete
725 on WSlot for each row execute procedure tg_slotlink_a('WS');
726 create trigger tg_slotlink_a after insert or update or delete
727 on IFace for each row execute procedure tg_slotlink_a('IF');
728 create trigger tg_slotlink_a after insert or update or delete
729 on HSlot for each row execute procedure tg_slotlink_a('HS');
730 create trigger tg_slotlink_a after insert or update or delete
731 on PHone for each row execute procedure tg_slotlink_a('PH');
732 -- ************************************************************
733 -- * Support function to set the opponents slotlink field
734 -- * if it does not already point to the requested slot
735 -- ************************************************************
736 create function tg_slotlink_set(bpchar, bpchar)
745 mytype := substr(myname, 1, 2);
746 link := mytype || substr(blname, 1, 2);
747 if link = ''PHPH'' then
749 ''slotlink between two phones does not make sense'';
751 if link in (''PHHS'', ''HSPH'') then
753 ''link of phone to hub does not make sense'';
755 if link in (''PHIF'', ''IFPH'') then
757 ''link of phone to hub does not make sense'';
759 if link in (''PSWS'', ''WSPS'') then
761 ''slotlink from patchslot to wallslot not permitted'';
763 if mytype = ''PS'' then
764 select into rec * from PSlot where slotname = myname;
766 raise exception ''% does not exist'', myname;
768 if rec.slotlink != blname then
769 update PSlot set slotlink = blname where slotname = myname;
773 if mytype = ''WS'' then
774 select into rec * from WSlot where slotname = myname;
776 raise exception ''% does not exist'', myname;
778 if rec.slotlink != blname then
779 update WSlot set slotlink = blname where slotname = myname;
783 if mytype = ''IF'' then
784 select into rec * from IFace where slotname = myname;
786 raise exception ''% does not exist'', myname;
788 if rec.slotlink != blname then
789 update IFace set slotlink = blname where slotname = myname;
793 if mytype = ''HS'' then
794 select into rec * from HSlot where slotname = myname;
796 raise exception ''% does not exist'', myname;
798 if rec.slotlink != blname then
799 update HSlot set slotlink = blname where slotname = myname;
803 if mytype = ''PH'' then
804 select into rec * from PHone where slotname = myname;
806 raise exception ''% does not exist'', myname;
808 if rec.slotlink != blname then
809 update PHone set slotlink = blname where slotname = myname;
813 raise exception ''illegal slotlink beginning with %'', mytype;
816 -- ************************************************************
817 -- * Support function to clear out the slotlink field if
818 -- * it still points to specific slot
819 -- ************************************************************
820 create function tg_slotlink_unset(bpchar, bpchar)
828 mytype := substr(myname, 1, 2);
829 if mytype = ''PS'' then
830 select into rec * from PSlot where slotname = myname;
834 if rec.slotlink = blname then
835 update PSlot set slotlink = '''' where slotname = myname;
839 if mytype = ''WS'' then
840 select into rec * from WSlot where slotname = myname;
844 if rec.slotlink = blname then
845 update WSlot set slotlink = '''' where slotname = myname;
849 if mytype = ''IF'' then
850 select into rec * from IFace where slotname = myname;
854 if rec.slotlink = blname then
855 update IFace set slotlink = '''' where slotname = myname;
859 if mytype = ''HS'' then
860 select into rec * from HSlot where slotname = myname;
864 if rec.slotlink = blname then
865 update HSlot set slotlink = '''' where slotname = myname;
869 if mytype = ''PH'' then
870 select into rec * from PHone where slotname = myname;
874 if rec.slotlink = blname then
875 update PHone set slotlink = '''' where slotname = myname;
881 -- ************************************************************
882 -- * Describe the backside of a patchfield slot
883 -- ************************************************************
884 create function pslot_backlink_view(bpchar)
892 select into rec * from PSlot where slotname = $1;
896 if rec.backlink = '''' then
899 bltype := substr(rec.backlink, 1, 2);
900 if bltype = ''PL'' then
904 select into rec * from PLine where slotname = outer.rec.backlink;
905 retval := ''Phone line '' || trim(rec.phonenumber);
906 if rec.comment != '''' then
907 retval := retval || '' ('';
908 retval := retval || rec.comment;
909 retval := retval || '')'';
914 if bltype = ''WS'' then
915 select into rec * from WSlot where slotname = rec.backlink;
916 retval := trim(rec.slotname) || '' in room '';
917 retval := retval || trim(rec.roomno);
918 retval := retval || '' -> '';
919 return retval || wslot_slotlink_view(rec.slotname);
924 -- ************************************************************
925 -- * Describe the front of a patchfield slot
926 -- ************************************************************
927 create function pslot_slotlink_view(bpchar)
934 select into psrec * from PSlot where slotname = $1;
938 if psrec.slotlink = '''' then
941 sltype := substr(psrec.slotlink, 1, 2);
942 if sltype = ''PS'' then
943 retval := trim(psrec.slotlink) || '' -> '';
944 return retval || pslot_backlink_view(psrec.slotlink);
946 if sltype = ''HS'' then
947 retval := comment from Hub H, HSlot HS
948 where HS.slotname = psrec.slotlink
949 and H.name = HS.hubname;
950 retval := retval || '' slot '';
951 retval := retval || slotno::text from HSlot
952 where slotname = psrec.slotlink;
955 return psrec.slotlink;
958 -- ************************************************************
959 -- * Describe the front of a wall connector slot
960 -- ************************************************************
961 create function wslot_slotlink_view(bpchar)
968 select into rec * from WSlot where slotname = $1;
972 if rec.slotlink = '''' then
975 sltype := substr(rec.slotlink, 1, 2);
976 if sltype = ''PH'' then
977 select into rec * from PHone where slotname = rec.slotlink;
978 retval := ''Phone '' || trim(rec.slotname);
979 if rec.comment != '''' then
980 retval := retval || '' ('';
981 retval := retval || rec.comment;
982 retval := retval || '')'';
986 if sltype = ''IF'' then
988 syrow System%RowType;
991 select into ifrow * from IFace where slotname = rec.slotlink;
992 select into syrow * from System where name = ifrow.sysname;
993 retval := syrow.name || '' IF '';
994 retval := retval || ifrow.ifname;
995 if syrow.comment != '''' then
996 retval := retval || '' ('';
997 retval := retval || syrow.comment;
998 retval := retval || '')'';
1003 return rec.slotlink;
1006 -- ************************************************************
1007 -- * View of a patchfield describing backside and patches
1008 -- ************************************************************
1009 create view Pfield_v1 as select PF.pfname, PF.slotname,
1010 pslot_backlink_view(PF.slotname) as backside,
1011 pslot_slotlink_view(PF.slotname) as patch
1014 -- First we build the house - so we create the rooms
1016 insert into Room values ('001', 'Entrance');
1017 insert into Room values ('002', 'Office');
1018 insert into Room values ('003', 'Office');
1019 insert into Room values ('004', 'Technical');
1020 insert into Room values ('101', 'Office');
1021 insert into Room values ('102', 'Conference');
1022 insert into Room values ('103', 'Restroom');
1023 insert into Room values ('104', 'Technical');
1024 insert into Room values ('105', 'Office');
1025 insert into Room values ('106', 'Office');
1027 -- Second we install the wall connectors
1029 insert into WSlot values ('WS.001.1a', '001', '', '');
1030 insert into WSlot values ('WS.001.1b', '001', '', '');
1031 insert into WSlot values ('WS.001.2a', '001', '', '');
1032 insert into WSlot values ('WS.001.2b', '001', '', '');
1033 insert into WSlot values ('WS.001.3a', '001', '', '');
1034 insert into WSlot values ('WS.001.3b', '001', '', '');
1035 insert into WSlot values ('WS.002.1a', '002', '', '');
1036 insert into WSlot values ('WS.002.1b', '002', '', '');
1037 insert into WSlot values ('WS.002.2a', '002', '', '');
1038 insert into WSlot values ('WS.002.2b', '002', '', '');
1039 insert into WSlot values ('WS.002.3a', '002', '', '');
1040 insert into WSlot values ('WS.002.3b', '002', '', '');
1041 insert into WSlot values ('WS.003.1a', '003', '', '');
1042 insert into WSlot values ('WS.003.1b', '003', '', '');
1043 insert into WSlot values ('WS.003.2a', '003', '', '');
1044 insert into WSlot values ('WS.003.2b', '003', '', '');
1045 insert into WSlot values ('WS.003.3a', '003', '', '');
1046 insert into WSlot values ('WS.003.3b', '003', '', '');
1047 insert into WSlot values ('WS.101.1a', '101', '', '');
1048 insert into WSlot values ('WS.101.1b', '101', '', '');
1049 insert into WSlot values ('WS.101.2a', '101', '', '');
1050 insert into WSlot values ('WS.101.2b', '101', '', '');
1051 insert into WSlot values ('WS.101.3a', '101', '', '');
1052 insert into WSlot values ('WS.101.3b', '101', '', '');
1053 insert into WSlot values ('WS.102.1a', '102', '', '');
1054 insert into WSlot values ('WS.102.1b', '102', '', '');
1055 insert into WSlot values ('WS.102.2a', '102', '', '');
1056 insert into WSlot values ('WS.102.2b', '102', '', '');
1057 insert into WSlot values ('WS.102.3a', '102', '', '');
1058 insert into WSlot values ('WS.102.3b', '102', '', '');
1059 insert into WSlot values ('WS.105.1a', '105', '', '');
1060 insert into WSlot values ('WS.105.1b', '105', '', '');
1061 insert into WSlot values ('WS.105.2a', '105', '', '');
1062 insert into WSlot values ('WS.105.2b', '105', '', '');
1063 insert into WSlot values ('WS.105.3a', '105', '', '');
1064 insert into WSlot values ('WS.105.3b', '105', '', '');
1065 insert into WSlot values ('WS.106.1a', '106', '', '');
1066 insert into WSlot values ('WS.106.1b', '106', '', '');
1067 insert into WSlot values ('WS.106.2a', '106', '', '');
1068 insert into WSlot values ('WS.106.2b', '106', '', '');
1069 insert into WSlot values ('WS.106.3a', '106', '', '');
1070 insert into WSlot values ('WS.106.3b', '106', '', '');
1072 -- Now create the patch fields and their slots
1074 insert into PField values ('PF0_1', 'Wallslots basement');
1076 -- The cables for these will be made later, so they are unconnected for now
1078 insert into PSlot values ('PS.base.a1', 'PF0_1', '', '');
1079 insert into PSlot values ('PS.base.a2', 'PF0_1', '', '');
1080 insert into PSlot values ('PS.base.a3', 'PF0_1', '', '');
1081 insert into PSlot values ('PS.base.a4', 'PF0_1', '', '');
1082 insert into PSlot values ('PS.base.a5', 'PF0_1', '', '');
1083 insert into PSlot values ('PS.base.a6', 'PF0_1', '', '');
1085 -- These are already wired to the wall connectors
1087 insert into PSlot values ('PS.base.b1', 'PF0_1', '', 'WS.002.1a');
1088 insert into PSlot values ('PS.base.b2', 'PF0_1', '', 'WS.002.1b');
1089 insert into PSlot values ('PS.base.b3', 'PF0_1', '', 'WS.002.2a');
1090 insert into PSlot values ('PS.base.b4', 'PF0_1', '', 'WS.002.2b');
1091 insert into PSlot values ('PS.base.b5', 'PF0_1', '', 'WS.002.3a');
1092 insert into PSlot values ('PS.base.b6', 'PF0_1', '', 'WS.002.3b');
1093 insert into PSlot values ('PS.base.c1', 'PF0_1', '', 'WS.003.1a');
1094 insert into PSlot values ('PS.base.c2', 'PF0_1', '', 'WS.003.1b');
1095 insert into PSlot values ('PS.base.c3', 'PF0_1', '', 'WS.003.2a');
1096 insert into PSlot values ('PS.base.c4', 'PF0_1', '', 'WS.003.2b');
1097 insert into PSlot values ('PS.base.c5', 'PF0_1', '', 'WS.003.3a');
1098 insert into PSlot values ('PS.base.c6', 'PF0_1', '', 'WS.003.3b');
1100 -- This patchfield will be renamed later into PF0_2 - so its
1101 -- slots references in pfname should follow
1103 insert into PField values ('PF0_X', 'Phonelines basement');
1104 insert into PSlot values ('PS.base.ta1', 'PF0_X', '', '');
1105 insert into PSlot values ('PS.base.ta2', 'PF0_X', '', '');
1106 insert into PSlot values ('PS.base.ta3', 'PF0_X', '', '');
1107 insert into PSlot values ('PS.base.ta4', 'PF0_X', '', '');
1108 insert into PSlot values ('PS.base.ta5', 'PF0_X', '', '');
1109 insert into PSlot values ('PS.base.ta6', 'PF0_X', '', '');
1110 insert into PSlot values ('PS.base.tb1', 'PF0_X', '', '');
1111 insert into PSlot values ('PS.base.tb2', 'PF0_X', '', '');
1112 insert into PSlot values ('PS.base.tb3', 'PF0_X', '', '');
1113 insert into PSlot values ('PS.base.tb4', 'PF0_X', '', '');
1114 insert into PSlot values ('PS.base.tb5', 'PF0_X', '', '');
1115 insert into PSlot values ('PS.base.tb6', 'PF0_X', '', '');
1116 insert into PField values ('PF1_1', 'Wallslots 1st floor');
1117 insert into PSlot values ('PS.1st.a1', 'PF1_1', '', 'WS.101.1a');
1118 insert into PSlot values ('PS.1st.a2', 'PF1_1', '', 'WS.101.1b');
1119 insert into PSlot values ('PS.1st.a3', 'PF1_1', '', 'WS.101.2a');
1120 insert into PSlot values ('PS.1st.a4', 'PF1_1', '', 'WS.101.2b');
1121 insert into PSlot values ('PS.1st.a5', 'PF1_1', '', 'WS.101.3a');
1122 insert into PSlot values ('PS.1st.a6', 'PF1_1', '', 'WS.101.3b');
1123 insert into PSlot values ('PS.1st.b1', 'PF1_1', '', 'WS.102.1a');
1124 insert into PSlot values ('PS.1st.b2', 'PF1_1', '', 'WS.102.1b');
1125 insert into PSlot values ('PS.1st.b3', 'PF1_1', '', 'WS.102.2a');
1126 insert into PSlot values ('PS.1st.b4', 'PF1_1', '', 'WS.102.2b');
1127 insert into PSlot values ('PS.1st.b5', 'PF1_1', '', 'WS.102.3a');
1128 insert into PSlot values ('PS.1st.b6', 'PF1_1', '', 'WS.102.3b');
1129 insert into PSlot values ('PS.1st.c1', 'PF1_1', '', 'WS.105.1a');
1130 insert into PSlot values ('PS.1st.c2', 'PF1_1', '', 'WS.105.1b');
1131 insert into PSlot values ('PS.1st.c3', 'PF1_1', '', 'WS.105.2a');
1132 insert into PSlot values ('PS.1st.c4', 'PF1_1', '', 'WS.105.2b');
1133 insert into PSlot values ('PS.1st.c5', 'PF1_1', '', 'WS.105.3a');
1134 insert into PSlot values ('PS.1st.c6', 'PF1_1', '', 'WS.105.3b');
1135 insert into PSlot values ('PS.1st.d1', 'PF1_1', '', 'WS.106.1a');
1136 insert into PSlot values ('PS.1st.d2', 'PF1_1', '', 'WS.106.1b');
1137 insert into PSlot values ('PS.1st.d3', 'PF1_1', '', 'WS.106.2a');
1138 insert into PSlot values ('PS.1st.d4', 'PF1_1', '', 'WS.106.2b');
1139 insert into PSlot values ('PS.1st.d5', 'PF1_1', '', 'WS.106.3a');
1140 insert into PSlot values ('PS.1st.d6', 'PF1_1', '', 'WS.106.3b');
1142 -- Now we wire the wall connectors 1a-2a in room 001 to the
1143 -- patchfield. In the second update we make an error, and
1146 update PSlot set backlink = 'WS.001.1a' where slotname = 'PS.base.a1';
1147 update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a3';
1148 select * from WSlot where roomno = '001' order by slotname;
1149 slotname | roomno | slotlink | backlink
1150 ----------------------+----------+----------------------+----------------------
1151 WS.001.1a | 001 | | PS.base.a1
1152 WS.001.1b | 001 | | PS.base.a3
1159 select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
1160 slotname | pfname | slotlink | backlink
1161 ----------------------+--------+----------------------+----------------------
1162 PS.base.a1 | PF0_1 | | WS.001.1a
1163 PS.base.a2 | PF0_1 | |
1164 PS.base.a3 | PF0_1 | | WS.001.1b
1165 PS.base.a4 | PF0_1 | |
1166 PS.base.a5 | PF0_1 | |
1167 PS.base.a6 | PF0_1 | |
1170 update PSlot set backlink = 'WS.001.2a' where slotname = 'PS.base.a3';
1171 select * from WSlot where roomno = '001' order by slotname;
1172 slotname | roomno | slotlink | backlink
1173 ----------------------+----------+----------------------+----------------------
1174 WS.001.1a | 001 | | PS.base.a1
1176 WS.001.2a | 001 | | PS.base.a3
1182 select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
1183 slotname | pfname | slotlink | backlink
1184 ----------------------+--------+----------------------+----------------------
1185 PS.base.a1 | PF0_1 | | WS.001.1a
1186 PS.base.a2 | PF0_1 | |
1187 PS.base.a3 | PF0_1 | | WS.001.2a
1188 PS.base.a4 | PF0_1 | |
1189 PS.base.a5 | PF0_1 | |
1190 PS.base.a6 | PF0_1 | |
1193 update PSlot set backlink = 'WS.001.1b' where slotname = 'PS.base.a2';
1194 select * from WSlot where roomno = '001' order by slotname;
1195 slotname | roomno | slotlink | backlink
1196 ----------------------+----------+----------------------+----------------------
1197 WS.001.1a | 001 | | PS.base.a1
1198 WS.001.1b | 001 | | PS.base.a2
1199 WS.001.2a | 001 | | PS.base.a3
1205 select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
1206 slotname | pfname | slotlink | backlink
1207 ----------------------+--------+----------------------+----------------------
1208 PS.base.a1 | PF0_1 | | WS.001.1a
1209 PS.base.a2 | PF0_1 | | WS.001.1b
1210 PS.base.a3 | PF0_1 | | WS.001.2a
1211 PS.base.a4 | PF0_1 | |
1212 PS.base.a5 | PF0_1 | |
1213 PS.base.a6 | PF0_1 | |
1217 -- Same procedure for 2b-3b but this time updating the WSlot instead
1218 -- of the PSlot. Due to the triggers the result is the same:
1219 -- WSlot and corresponding PSlot point to each other.
1221 update WSlot set backlink = 'PS.base.a4' where slotname = 'WS.001.2b';
1222 update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3a';
1223 select * from WSlot where roomno = '001' order by slotname;
1224 slotname | roomno | slotlink | backlink
1225 ----------------------+----------+----------------------+----------------------
1226 WS.001.1a | 001 | | PS.base.a1
1227 WS.001.1b | 001 | | PS.base.a2
1228 WS.001.2a | 001 | | PS.base.a3
1229 WS.001.2b | 001 | | PS.base.a4
1230 WS.001.3a | 001 | | PS.base.a6
1234 select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
1235 slotname | pfname | slotlink | backlink
1236 ----------------------+--------+----------------------+----------------------
1237 PS.base.a1 | PF0_1 | | WS.001.1a
1238 PS.base.a2 | PF0_1 | | WS.001.1b
1239 PS.base.a3 | PF0_1 | | WS.001.2a
1240 PS.base.a4 | PF0_1 | | WS.001.2b
1241 PS.base.a5 | PF0_1 | |
1242 PS.base.a6 | PF0_1 | | WS.001.3a
1245 update WSlot set backlink = 'PS.base.a6' where slotname = 'WS.001.3b';
1246 select * from WSlot where roomno = '001' order by slotname;
1247 slotname | roomno | slotlink | backlink
1248 ----------------------+----------+----------------------+----------------------
1249 WS.001.1a | 001 | | PS.base.a1
1250 WS.001.1b | 001 | | PS.base.a2
1251 WS.001.2a | 001 | | PS.base.a3
1252 WS.001.2b | 001 | | PS.base.a4
1254 WS.001.3b | 001 | | PS.base.a6
1257 select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
1258 slotname | pfname | slotlink | backlink
1259 ----------------------+--------+----------------------+----------------------
1260 PS.base.a1 | PF0_1 | | WS.001.1a
1261 PS.base.a2 | PF0_1 | | WS.001.1b
1262 PS.base.a3 | PF0_1 | | WS.001.2a
1263 PS.base.a4 | PF0_1 | | WS.001.2b
1264 PS.base.a5 | PF0_1 | |
1265 PS.base.a6 | PF0_1 | | WS.001.3b
1268 update WSlot set backlink = 'PS.base.a5' where slotname = 'WS.001.3a';
1269 select * from WSlot where roomno = '001' order by slotname;
1270 slotname | roomno | slotlink | backlink
1271 ----------------------+----------+----------------------+----------------------
1272 WS.001.1a | 001 | | PS.base.a1
1273 WS.001.1b | 001 | | PS.base.a2
1274 WS.001.2a | 001 | | PS.base.a3
1275 WS.001.2b | 001 | | PS.base.a4
1276 WS.001.3a | 001 | | PS.base.a5
1277 WS.001.3b | 001 | | PS.base.a6
1280 select * from PSlot where slotname ~ 'PS.base.a' order by slotname;
1281 slotname | pfname | slotlink | backlink
1282 ----------------------+--------+----------------------+----------------------
1283 PS.base.a1 | PF0_1 | | WS.001.1a
1284 PS.base.a2 | PF0_1 | | WS.001.1b
1285 PS.base.a3 | PF0_1 | | WS.001.2a
1286 PS.base.a4 | PF0_1 | | WS.001.2b
1287 PS.base.a5 | PF0_1 | | WS.001.3a
1288 PS.base.a6 | PF0_1 | | WS.001.3b
1291 insert into PField values ('PF1_2', 'Phonelines 1st floor');
1292 insert into PSlot values ('PS.1st.ta1', 'PF1_2', '', '');
1293 insert into PSlot values ('PS.1st.ta2', 'PF1_2', '', '');
1294 insert into PSlot values ('PS.1st.ta3', 'PF1_2', '', '');
1295 insert into PSlot values ('PS.1st.ta4', 'PF1_2', '', '');
1296 insert into PSlot values ('PS.1st.ta5', 'PF1_2', '', '');
1297 insert into PSlot values ('PS.1st.ta6', 'PF1_2', '', '');
1298 insert into PSlot values ('PS.1st.tb1', 'PF1_2', '', '');
1299 insert into PSlot values ('PS.1st.tb2', 'PF1_2', '', '');
1300 insert into PSlot values ('PS.1st.tb3', 'PF1_2', '', '');
1301 insert into PSlot values ('PS.1st.tb4', 'PF1_2', '', '');
1302 insert into PSlot values ('PS.1st.tb5', 'PF1_2', '', '');
1303 insert into PSlot values ('PS.1st.tb6', 'PF1_2', '', '');
1305 -- Fix the wrong name for patchfield PF0_2
1307 update PField set name = 'PF0_2' where name = 'PF0_X';
1308 select * from PSlot order by slotname;
1309 slotname | pfname | slotlink | backlink
1310 ----------------------+--------+----------------------+----------------------
1311 PS.1st.a1 | PF1_1 | | WS.101.1a
1312 PS.1st.a2 | PF1_1 | | WS.101.1b
1313 PS.1st.a3 | PF1_1 | | WS.101.2a
1314 PS.1st.a4 | PF1_1 | | WS.101.2b
1315 PS.1st.a5 | PF1_1 | | WS.101.3a
1316 PS.1st.a6 | PF1_1 | | WS.101.3b
1317 PS.1st.b1 | PF1_1 | | WS.102.1a
1318 PS.1st.b2 | PF1_1 | | WS.102.1b
1319 PS.1st.b3 | PF1_1 | | WS.102.2a
1320 PS.1st.b4 | PF1_1 | | WS.102.2b
1321 PS.1st.b5 | PF1_1 | | WS.102.3a
1322 PS.1st.b6 | PF1_1 | | WS.102.3b
1323 PS.1st.c1 | PF1_1 | | WS.105.1a
1324 PS.1st.c2 | PF1_1 | | WS.105.1b
1325 PS.1st.c3 | PF1_1 | | WS.105.2a
1326 PS.1st.c4 | PF1_1 | | WS.105.2b
1327 PS.1st.c5 | PF1_1 | | WS.105.3a
1328 PS.1st.c6 | PF1_1 | | WS.105.3b
1329 PS.1st.d1 | PF1_1 | | WS.106.1a
1330 PS.1st.d2 | PF1_1 | | WS.106.1b
1331 PS.1st.d3 | PF1_1 | | WS.106.2a
1332 PS.1st.d4 | PF1_1 | | WS.106.2b
1333 PS.1st.d5 | PF1_1 | | WS.106.3a
1334 PS.1st.d6 | PF1_1 | | WS.106.3b
1335 PS.1st.ta1 | PF1_2 | |
1336 PS.1st.ta2 | PF1_2 | |
1337 PS.1st.ta3 | PF1_2 | |
1338 PS.1st.ta4 | PF1_2 | |
1339 PS.1st.ta5 | PF1_2 | |
1340 PS.1st.ta6 | PF1_2 | |
1341 PS.1st.tb1 | PF1_2 | |
1342 PS.1st.tb2 | PF1_2 | |
1343 PS.1st.tb3 | PF1_2 | |
1344 PS.1st.tb4 | PF1_2 | |
1345 PS.1st.tb5 | PF1_2 | |
1346 PS.1st.tb6 | PF1_2 | |
1347 PS.base.a1 | PF0_1 | | WS.001.1a
1348 PS.base.a2 | PF0_1 | | WS.001.1b
1349 PS.base.a3 | PF0_1 | | WS.001.2a
1350 PS.base.a4 | PF0_1 | | WS.001.2b
1351 PS.base.a5 | PF0_1 | | WS.001.3a
1352 PS.base.a6 | PF0_1 | | WS.001.3b
1353 PS.base.b1 | PF0_1 | | WS.002.1a
1354 PS.base.b2 | PF0_1 | | WS.002.1b
1355 PS.base.b3 | PF0_1 | | WS.002.2a
1356 PS.base.b4 | PF0_1 | | WS.002.2b
1357 PS.base.b5 | PF0_1 | | WS.002.3a
1358 PS.base.b6 | PF0_1 | | WS.002.3b
1359 PS.base.c1 | PF0_1 | | WS.003.1a
1360 PS.base.c2 | PF0_1 | | WS.003.1b
1361 PS.base.c3 | PF0_1 | | WS.003.2a
1362 PS.base.c4 | PF0_1 | | WS.003.2b
1363 PS.base.c5 | PF0_1 | | WS.003.3a
1364 PS.base.c6 | PF0_1 | | WS.003.3b
1365 PS.base.ta1 | PF0_2 | |
1366 PS.base.ta2 | PF0_2 | |
1367 PS.base.ta3 | PF0_2 | |
1368 PS.base.ta4 | PF0_2 | |
1369 PS.base.ta5 | PF0_2 | |
1370 PS.base.ta6 | PF0_2 | |
1371 PS.base.tb1 | PF0_2 | |
1372 PS.base.tb2 | PF0_2 | |
1373 PS.base.tb3 | PF0_2 | |
1374 PS.base.tb4 | PF0_2 | |
1375 PS.base.tb5 | PF0_2 | |
1376 PS.base.tb6 | PF0_2 | |
1379 select * from WSlot order by slotname;
1380 slotname | roomno | slotlink | backlink
1381 ----------------------+----------+----------------------+----------------------
1382 WS.001.1a | 001 | | PS.base.a1
1383 WS.001.1b | 001 | | PS.base.a2
1384 WS.001.2a | 001 | | PS.base.a3
1385 WS.001.2b | 001 | | PS.base.a4
1386 WS.001.3a | 001 | | PS.base.a5
1387 WS.001.3b | 001 | | PS.base.a6
1388 WS.002.1a | 002 | | PS.base.b1
1389 WS.002.1b | 002 | | PS.base.b2
1390 WS.002.2a | 002 | | PS.base.b3
1391 WS.002.2b | 002 | | PS.base.b4
1392 WS.002.3a | 002 | | PS.base.b5
1393 WS.002.3b | 002 | | PS.base.b6
1394 WS.003.1a | 003 | | PS.base.c1
1395 WS.003.1b | 003 | | PS.base.c2
1396 WS.003.2a | 003 | | PS.base.c3
1397 WS.003.2b | 003 | | PS.base.c4
1398 WS.003.3a | 003 | | PS.base.c5
1399 WS.003.3b | 003 | | PS.base.c6
1400 WS.101.1a | 101 | | PS.1st.a1
1401 WS.101.1b | 101 | | PS.1st.a2
1402 WS.101.2a | 101 | | PS.1st.a3
1403 WS.101.2b | 101 | | PS.1st.a4
1404 WS.101.3a | 101 | | PS.1st.a5
1405 WS.101.3b | 101 | | PS.1st.a6
1406 WS.102.1a | 102 | | PS.1st.b1
1407 WS.102.1b | 102 | | PS.1st.b2
1408 WS.102.2a | 102 | | PS.1st.b3
1409 WS.102.2b | 102 | | PS.1st.b4
1410 WS.102.3a | 102 | | PS.1st.b5
1411 WS.102.3b | 102 | | PS.1st.b6
1412 WS.105.1a | 105 | | PS.1st.c1
1413 WS.105.1b | 105 | | PS.1st.c2
1414 WS.105.2a | 105 | | PS.1st.c3
1415 WS.105.2b | 105 | | PS.1st.c4
1416 WS.105.3a | 105 | | PS.1st.c5
1417 WS.105.3b | 105 | | PS.1st.c6
1418 WS.106.1a | 106 | | PS.1st.d1
1419 WS.106.1b | 106 | | PS.1st.d2
1420 WS.106.2a | 106 | | PS.1st.d3
1421 WS.106.2b | 106 | | PS.1st.d4
1422 WS.106.3a | 106 | | PS.1st.d5
1423 WS.106.3b | 106 | | PS.1st.d6
1427 -- Install the central phone system and create the phone numbers.
1428 -- They are weired on insert to the patchfields. Again the
1429 -- triggers automatically tell the PSlots to update their
1432 insert into PLine values ('PL.001', '-0', 'Central call', 'PS.base.ta1');
1433 insert into PLine values ('PL.002', '-101', '', 'PS.base.ta2');
1434 insert into PLine values ('PL.003', '-102', '', 'PS.base.ta3');
1435 insert into PLine values ('PL.004', '-103', '', 'PS.base.ta5');
1436 insert into PLine values ('PL.005', '-104', '', 'PS.base.ta6');
1437 insert into PLine values ('PL.006', '-106', '', 'PS.base.tb2');
1438 insert into PLine values ('PL.007', '-108', '', 'PS.base.tb3');
1439 insert into PLine values ('PL.008', '-109', '', 'PS.base.tb4');
1440 insert into PLine values ('PL.009', '-121', '', 'PS.base.tb5');
1441 insert into PLine values ('PL.010', '-122', '', 'PS.base.tb6');
1442 insert into PLine values ('PL.015', '-134', '', 'PS.1st.ta1');
1443 insert into PLine values ('PL.016', '-137', '', 'PS.1st.ta3');
1444 insert into PLine values ('PL.017', '-139', '', 'PS.1st.ta4');
1445 insert into PLine values ('PL.018', '-362', '', 'PS.1st.tb1');
1446 insert into PLine values ('PL.019', '-363', '', 'PS.1st.tb2');
1447 insert into PLine values ('PL.020', '-364', '', 'PS.1st.tb3');
1448 insert into PLine values ('PL.021', '-365', '', 'PS.1st.tb5');
1449 insert into PLine values ('PL.022', '-367', '', 'PS.1st.tb6');
1450 insert into PLine values ('PL.028', '-501', 'Fax entrance', 'PS.base.ta2');
1451 insert into PLine values ('PL.029', '-502', 'Fax 1st floor', 'PS.1st.ta1');
1453 -- Buy some phones, plug them into the wall and patch the
1454 -- phone lines to the corresponding patchfield slots.
1456 insert into PHone values ('PH.hc001', 'Hicom standard', 'WS.001.1a');
1457 update PSlot set slotlink = 'PS.base.ta1' where slotname = 'PS.base.a1';
1458 insert into PHone values ('PH.hc002', 'Hicom standard', 'WS.002.1a');
1459 update PSlot set slotlink = 'PS.base.ta5' where slotname = 'PS.base.b1';
1460 insert into PHone values ('PH.hc003', 'Hicom standard', 'WS.002.2a');
1461 update PSlot set slotlink = 'PS.base.tb2' where slotname = 'PS.base.b3';
1462 insert into PHone values ('PH.fax001', 'Canon fax', 'WS.001.2a');
1463 update PSlot set slotlink = 'PS.base.ta2' where slotname = 'PS.base.a3';
1465 -- Install a hub at one of the patchfields, plug a computers
1466 -- ethernet interface into the wall and patch it to the hub.
1468 insert into Hub values ('base.hub1', 'Patchfield PF0_1 hub', 16);
1469 insert into System values ('orion', 'PC');
1470 insert into IFace values ('IF', 'orion', 'eth0', 'WS.002.1b');
1471 update PSlot set slotlink = 'HS.base.hub1.1' where slotname = 'PS.base.b2';
1473 -- Now we take a look at the patchfield
1475 select * from PField_v1 where pfname = 'PF0_1' order by slotname;
1476 pfname | slotname | backside | patch
1477 --------+----------------------+----------------------------------------------------------+-----------------------------------------------
1478 PF0_1 | PS.base.a1 | WS.001.1a in room 001 -> Phone PH.hc001 (Hicom standard) | PS.base.ta1 -> Phone line -0 (Central call)
1479 PF0_1 | PS.base.a2 | WS.001.1b in room 001 -> - | -
1480 PF0_1 | PS.base.a3 | WS.001.2a in room 001 -> Phone PH.fax001 (Canon fax) | PS.base.ta2 -> Phone line -501 (Fax entrance)
1481 PF0_1 | PS.base.a4 | WS.001.2b in room 001 -> - | -
1482 PF0_1 | PS.base.a5 | WS.001.3a in room 001 -> - | -
1483 PF0_1 | PS.base.a6 | WS.001.3b in room 001 -> - | -
1484 PF0_1 | PS.base.b1 | WS.002.1a in room 002 -> Phone PH.hc002 (Hicom standard) | PS.base.ta5 -> Phone line -103
1485 PF0_1 | PS.base.b2 | WS.002.1b in room 002 -> orion IF eth0 (PC) | Patchfield PF0_1 hub slot 1
1486 PF0_1 | PS.base.b3 | WS.002.2a in room 002 -> Phone PH.hc003 (Hicom standard) | PS.base.tb2 -> Phone line -106
1487 PF0_1 | PS.base.b4 | WS.002.2b in room 002 -> - | -
1488 PF0_1 | PS.base.b5 | WS.002.3a in room 002 -> - | -
1489 PF0_1 | PS.base.b6 | WS.002.3b in room 002 -> - | -
1490 PF0_1 | PS.base.c1 | WS.003.1a in room 003 -> - | -
1491 PF0_1 | PS.base.c2 | WS.003.1b in room 003 -> - | -
1492 PF0_1 | PS.base.c3 | WS.003.2a in room 003 -> - | -
1493 PF0_1 | PS.base.c4 | WS.003.2b in room 003 -> - | -
1494 PF0_1 | PS.base.c5 | WS.003.3a in room 003 -> - | -
1495 PF0_1 | PS.base.c6 | WS.003.3b in room 003 -> - | -
1498 select * from PField_v1 where pfname = 'PF0_2' order by slotname;
1499 pfname | slotname | backside | patch
1500 --------+----------------------+--------------------------------+------------------------------------------------------------------------
1501 PF0_2 | PS.base.ta1 | Phone line -0 (Central call) | PS.base.a1 -> WS.001.1a in room 001 -> Phone PH.hc001 (Hicom standard)
1502 PF0_2 | PS.base.ta2 | Phone line -501 (Fax entrance) | PS.base.a3 -> WS.001.2a in room 001 -> Phone PH.fax001 (Canon fax)
1503 PF0_2 | PS.base.ta3 | Phone line -102 | -
1504 PF0_2 | PS.base.ta4 | - | -
1505 PF0_2 | PS.base.ta5 | Phone line -103 | PS.base.b1 -> WS.002.1a in room 002 -> Phone PH.hc002 (Hicom standard)
1506 PF0_2 | PS.base.ta6 | Phone line -104 | -
1507 PF0_2 | PS.base.tb1 | - | -
1508 PF0_2 | PS.base.tb2 | Phone line -106 | PS.base.b3 -> WS.002.2a in room 002 -> Phone PH.hc003 (Hicom standard)
1509 PF0_2 | PS.base.tb3 | Phone line -108 | -
1510 PF0_2 | PS.base.tb4 | Phone line -109 | -
1511 PF0_2 | PS.base.tb5 | Phone line -121 | -
1512 PF0_2 | PS.base.tb6 | Phone line -122 | -
1516 -- Finally we want errors
1518 insert into PField values ('PF1_1', 'should fail due to unique index');
1519 ERROR: duplicate key value violates unique constraint "pfield_name"
1520 update PSlot set backlink = 'WS.not.there' where slotname = 'PS.base.a1';
1521 ERROR: WS.not.there does not exist
1522 CONTEXT: PL/pgSQL function "tg_backlink_a" line 16 at assignment
1523 update PSlot set backlink = 'XX.illegal' where slotname = 'PS.base.a1';
1524 ERROR: illegal backlink beginning with XX
1525 CONTEXT: PL/pgSQL function "tg_backlink_a" line 16 at assignment
1526 update PSlot set slotlink = 'PS.not.there' where slotname = 'PS.base.a1';
1527 ERROR: PS.not.there does not exist
1528 CONTEXT: PL/pgSQL function "tg_slotlink_a" line 16 at assignment
1529 update PSlot set slotlink = 'XX.illegal' where slotname = 'PS.base.a1';
1530 ERROR: illegal slotlink beginning with XX
1531 CONTEXT: PL/pgSQL function "tg_slotlink_a" line 16 at assignment
1532 insert into HSlot values ('HS', 'base.hub1', 1, '');
1533 ERROR: duplicate key value violates unique constraint "hslot_name"
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 at or near "i"
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 7 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 7 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 7 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 "raise" processing
2297 create function raise_test1(int) returns int as $$
2299 raise notice 'This message has too many parameters!', $1;
2302 $$ language plpgsql;
2303 select raise_test1(5);
2304 ERROR: too many parameters specified for RAISE
2305 CONTEXT: PL/pgSQL function "raise_test1" line 2 at RAISE
2306 create function raise_test2(int) returns int as $$
2308 raise notice 'This message has too few parameters: %, %, %', $1, $1;
2311 $$ language plpgsql;
2312 select raise_test2(10);
2313 ERROR: too few parameters specified for RAISE
2314 CONTEXT: PL/pgSQL function "raise_test2" line 2 at RAISE
2316 -- reject function definitions that contain malformed SQL queries at
2317 -- compile-time, where possible
2319 create function bad_sql1() returns int as $$
2326 end$$ language plpgsql;
2327 ERROR: syntax error at or near "Johnny"
2331 CONTEXT: SQL statement in PL/PgSQL function "bad_sql1" near line 4
2332 create function bad_sql2() returns int as $$
2335 for r in select I fought the law, the law won LOOP
2336 raise notice 'in loop';
2339 end;$$ language plpgsql;
2340 ERROR: syntax error at or near "the"
2341 LINE 1: select I fought the law, the law won
2343 QUERY: select I fought the law, the law won
2344 CONTEXT: SQL statement in PL/PgSQL function "bad_sql2" near line 3
2345 -- a RETURN expression is mandatory, except for void-returning
2346 -- functions, where it is not allowed
2347 create function missing_return_expr() returns int as $$
2350 end;$$ language plpgsql;
2351 ERROR: syntax error at end of input
2355 CONTEXT: SQL statement in PL/PgSQL function "missing_return_expr" near line 2
2356 create function void_return_expr() returns void as $$
2359 end;$$ language plpgsql;
2360 ERROR: RETURN cannot have a parameter in function returning void at or near "5"
2363 -- VOID functions are allowed to omit RETURN
2364 create function void_return_expr() returns void as $$
2367 end;$$ language plpgsql;
2368 select void_return_expr();
2374 -- but ordinary functions are not
2375 create function missing_return_expr() returns int as $$
2378 end;$$ language plpgsql;
2379 select missing_return_expr();
2380 ERROR: control reached end of function without RETURN
2381 CONTEXT: PL/pgSQL function "missing_return_expr"
2382 drop function void_return_expr();
2383 drop function missing_return_expr();
2385 -- EXECUTE ... INTO test
2387 create table eifoo (i integer, y integer);
2388 create type eitype as (i integer, y integer);
2389 create or replace function execute_into_test(varchar) returns record as $$
2398 execute 'insert into '||$1||' values(10,15)';
2399 execute 'select (row).* from (select row(10,1)::eifoo) s' into _r;
2400 raise notice '% %', _r.i, _r.y;
2401 execute 'select * from '||$1||' limit 1' into _rt;
2402 raise notice '% %', _rt.i, _rt.y;
2403 execute 'select *, 20 from '||$1||' limit 1' into i, j, k;
2404 raise notice '% % %', i, j, k;
2405 execute 'select 1,2' into _v;
2407 end; $$ language plpgsql;
2408 select execute_into_test('eifoo');
2417 drop table eifoo cascade;
2418 drop type eitype cascade;
2420 -- SQLSTATE and SQLERRM test
2422 create function excpt_test1() returns void as $$
2424 raise notice '% %', sqlstate, sqlerrm;
2425 end; $$ language plpgsql;
2426 -- should fail: SQLSTATE and SQLERRM are only in defined EXCEPTION
2428 select excpt_test1();
2429 ERROR: column "sqlstate" does not exist
2430 LINE 1: SELECT sqlstate
2432 QUERY: SELECT sqlstate
2433 CONTEXT: PL/pgSQL function "excpt_test1" line 2 at RAISE
2434 create function excpt_test2() returns void as $$
2438 raise notice '% %', sqlstate, sqlerrm;
2441 end; $$ language plpgsql;
2443 select excpt_test2();
2444 ERROR: column "sqlstate" does not exist
2445 LINE 1: SELECT sqlstate
2447 QUERY: SELECT sqlstate
2448 CONTEXT: PL/pgSQL function "excpt_test2" line 4 at RAISE
2449 create function excpt_test3() returns void as $$
2452 raise exception 'user exception';
2453 exception when others then
2454 raise notice 'caught exception % %', sqlstate, sqlerrm;
2456 raise notice '% %', sqlstate, sqlerrm;
2459 when substring_error then
2460 -- this exception handler shouldn't be invoked
2461 raise notice 'unexpected exception: % %', sqlstate, sqlerrm;
2462 when division_by_zero then
2463 raise notice 'caught exception % %', sqlstate, sqlerrm;
2465 raise notice '% %', sqlstate, sqlerrm;
2467 end; $$ language plpgsql;
2468 select excpt_test3();
2469 NOTICE: caught exception P0001 user exception
2470 NOTICE: P0001 user exception
2471 NOTICE: caught exception 22012 division by zero
2472 NOTICE: P0001 user exception
2478 drop function excpt_test1();
2479 drop function excpt_test2();
2480 drop function excpt_test3();
2481 -- parameters of raise stmt can be expressions
2482 create function raise_exprs() returns void as $$
2484 a integer[] = '{10,20,30}';
2489 raise notice '%; %; %; %; %; %', a, a[i], c, (select c || 'abc'), row(10,'aaa',NULL,30), NULL;
2490 end;$$ language plpgsql;
2491 select raise_exprs();
2492 NOTICE: {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); <NULL>
2498 drop function raise_exprs();
2499 -- continue statement
2500 create table conttesttbl(idx serial, v integer);
2501 NOTICE: CREATE TABLE will create implicit sequence "conttesttbl_idx_seq" for serial column "conttesttbl.idx"
2502 insert into conttesttbl(v) values(10);
2503 insert into conttesttbl(v) values(20);
2504 insert into conttesttbl(v) values(30);
2505 insert into conttesttbl(v) values(40);
2506 create function continue_test1() returns void as $$
2507 declare _i integer = 0; _r record;
2509 raise notice '---1---';
2512 raise notice '%', _i;
2513 continue when _i < 10;
2517 raise notice '---2---';
2522 raise notice '%', _i;
2523 continue lbl when _i > 0;
2528 raise notice '---3---';
2532 continue the_loop when _i % 2 = 0;
2533 raise notice '%', _i;
2536 raise notice '---4---';
2537 for _i in 1..10 loop
2539 -- applies to outer loop, not the nested begin block
2540 continue when _i < 5;
2541 raise notice '%', _i;
2545 raise notice '---5---';
2546 for _r in select * from conttesttbl loop
2547 continue when _r.v <= 20;
2548 raise notice '%', _r.v;
2551 raise notice '---6---';
2552 for _r in execute 'select * from conttesttbl' loop
2553 continue when _r.v <= 20;
2554 raise notice '%', _r.v;
2557 raise notice '---7---';
2559 raise notice '%', _i;
2560 continue when _i = 3;
2563 raise notice '---8---';
2566 raise notice '%', _i;
2568 continue when _i = 3;
2571 raise notice '---9---';
2572 for _r in select * from conttesttbl order by v limit 1 loop
2573 raise notice '%', _r.v;
2577 raise notice '---10---';
2578 for _r in execute 'select * from conttesttbl order by v limit 1' loop
2579 raise notice '%', _r.v;
2582 end; $$ language plpgsql;
2583 select continue_test1();
2642 -- CONTINUE is only legal inside a loop
2643 create function continue_test2() returns void as $$
2650 $$ language plpgsql;
2652 select continue_test2();
2653 ERROR: CONTINUE cannot be used outside a loop
2654 CONTEXT: PL/pgSQL function "continue_test2"
2655 -- CONTINUE can't reference the label of a named block
2656 create function continue_test3() returns void as $$
2661 continue begin_block1;
2665 $$ language plpgsql;
2667 select continue_test3();
2668 ERROR: CONTINUE cannot be used outside a loop
2669 CONTEXT: PL/pgSQL function "continue_test3"
2670 drop function continue_test1();
2671 drop function continue_test2();
2672 drop function continue_test3();
2673 drop table conttesttbl;
2674 -- verbose end block and end loop
2675 create function end_label1() returns void as $$
2679 for _i in 1 .. 10 loop
2683 for _i in 1 .. 10 loop
2687 $$ language plpgsql;
2688 select end_label1();
2694 drop function end_label1();
2695 -- should fail: undefined end label
2696 create function end_label2() returns void as $$
2698 for _i in 1 .. 10 loop
2702 $$ language plpgsql;
2703 ERROR: no such label at or near "flbl1"
2704 LINE 5: end loop flbl1;
2706 -- should fail: end label does not match start label
2707 create function end_label3() returns void as $$
2711 for _i in 1 .. 10 loop
2713 end loop outer_label;
2715 $$ language plpgsql;
2716 ERROR: end label "outer_label" differs from block's label "inner_label"
2717 CONTEXT: compile of PL/pgSQL function "end_label3" near line 6
2718 -- should fail: end label on a block without a start label
2719 create function end_label4() returns void as $$
2722 for _i in 1 .. 10 loop
2724 end loop outer_label;
2726 $$ language plpgsql;
2727 ERROR: end label "outer_label" specified for unlabelled block
2728 CONTEXT: compile of PL/pgSQL function "end_label4" near line 5
2729 -- using list of scalars in fori and fore stmts
2730 create function for_vect() returns void as $proc$
2731 <<lbl>>declare a integer; b varchar; c varchar; r record;
2734 for i in 1 .. 3 loop
2735 raise notice '%', i;
2737 -- fore with record var
2738 for r in select gs as aa, 'BB' as bb, 'CC' as cc from generate_series(1,4) gs loop
2739 raise notice '% % %', r.aa, r.bb, r.cc;
2741 -- fore with single scalar
2742 for a in select gs from generate_series(1,4) gs loop
2743 raise notice '%', a;
2745 -- fore with multiple scalars
2746 for a,b,c in select gs, 'BB','CC' from generate_series(1,4) gs loop
2747 raise notice '% % %', a, b, c;
2749 -- using qualified names in fors, fore is enabled, disabled only for fori
2750 for lbl.a, lbl.b, lbl.c in execute $$select gs, 'bb','cc' from generate_series(1,4) gs$$ loop
2751 raise notice '% % %', a, b, c;
2754 $proc$ language plpgsql;
2780 -- regression test: verify that multiple uses of same plpgsql datum within
2781 -- a SQL command all get mapped to the same $n parameter. The return value
2782 -- of the SELECT is not important, we only care that it doesn't fail with
2783 -- a complaint about an ungrouped column reference.
2784 create function multi_datum_use(p1 int) returns bool as $$
2789 select into x,y unique1/p1, unique1/$1 from tenk1 group by unique1/p1;
2791 end$$ language plpgsql;
2792 select multi_datum_use(42);
2799 -- Test STRICT limiter in both planned and EXECUTE invocations.
2800 -- Note that a data-modifying query is quasi strict (disallow multi rows)
2801 -- by default in the planned case, but not in EXECUTE.
2803 create temp table foo (f1 int, f2 int);
2804 insert into foo values (1,2), (3,4);
2805 create or replace function footest() returns void as $$
2809 insert into foo values(5,6) returning * into x;
2810 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2811 end$$ language plpgsql;
2813 NOTICE: x.f1 = 5, x.f2 = 6
2819 create or replace function footest() returns void as $$
2822 -- should fail due to implicit strict
2823 insert into foo values(7,8),(9,10) returning * into x;
2824 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2825 end$$ language plpgsql;
2827 ERROR: query returned more than one row
2828 CONTEXT: PL/pgSQL function "footest" line 4 at SQL statement
2829 create or replace function footest() returns void as $$
2833 execute 'insert into foo values(5,6) returning *' into x;
2834 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2835 end$$ language plpgsql;
2837 NOTICE: x.f1 = 5, x.f2 = 6
2843 create or replace function footest() returns void as $$
2846 -- this should work since EXECUTE isn't as picky
2847 execute 'insert into foo values(7,8),(9,10) returning *' into x;
2848 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2849 end$$ language plpgsql;
2851 NOTICE: x.f1 = 7, x.f2 = 8
2868 create or replace function footest() returns void as $$
2872 select * from foo where f1 = 3 into strict x;
2873 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2874 end$$ language plpgsql;
2876 NOTICE: x.f1 = 3, x.f2 = 4
2882 create or replace function footest() returns void as $$
2885 -- should fail, no rows
2886 select * from foo where f1 = 0 into strict x;
2887 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2888 end$$ language plpgsql;
2890 ERROR: query returned no rows
2891 CONTEXT: PL/pgSQL function "footest" line 4 at SQL statement
2892 create or replace function footest() returns void as $$
2895 -- should fail, too many rows
2896 select * from foo where f1 > 3 into strict x;
2897 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2898 end$$ language plpgsql;
2900 ERROR: query returned more than one row
2901 CONTEXT: PL/pgSQL function "footest" line 4 at SQL statement
2902 create or replace function footest() returns void as $$
2906 execute 'select * from foo where f1 = 3' into strict x;
2907 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2908 end$$ language plpgsql;
2910 NOTICE: x.f1 = 3, x.f2 = 4
2916 create or replace function footest() returns void as $$
2919 -- should fail, no rows
2920 execute 'select * from foo where f1 = 0' into strict x;
2921 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2922 end$$ language plpgsql;
2924 ERROR: query returned no rows
2925 CONTEXT: PL/pgSQL function "footest" line 4 at EXECUTE statement
2926 create or replace function footest() returns void as $$
2929 -- should fail, too many rows
2930 execute 'select * from foo where f1 > 3' into strict x;
2931 raise notice 'x.f1 = %, x.f2 = %', x.f1, x.f2;
2932 end$$ language plpgsql;
2934 ERROR: query returned more than one row
2935 CONTEXT: PL/pgSQL function "footest" line 4 at EXECUTE statement
2936 drop function footest();
2937 -- test scrollable cursor support
2938 create function sc_test() returns setof integer as $$
2940 c scroll cursor for select f1 from int4_tbl;
2944 fetch last from c into x;
2947 fetch prior from c into x;
2951 $$ language plpgsql;
2952 select * from sc_test();
2962 create or replace function sc_test() returns setof integer as $$
2964 c no scroll cursor for select f1 from int4_tbl;
2968 fetch last from c into x;
2971 fetch prior from c into x;
2975 $$ language plpgsql;
2976 select * from sc_test(); -- fails because of NO SCROLL specification
2977 ERROR: cursor can only scan forward
2978 HINT: Declare it with SCROLL option to enable backward scan.
2979 CONTEXT: PL/pgSQL function "sc_test" line 6 at FETCH
2980 create or replace function sc_test() returns setof integer as $$
2985 open c scroll for select f1 from int4_tbl;
2986 fetch last from c into x;
2989 fetch prior from c into x;
2993 $$ language plpgsql;
2994 select * from sc_test();
3004 create or replace function sc_test() returns setof integer as $$
3009 open c scroll for execute 'select f1 from int4_tbl';
3010 fetch last from c into x;
3013 fetch relative -2 from c into x;
3017 $$ language plpgsql;
3018 select * from sc_test();
3026 create or replace function sc_test() returns setof integer as $$
3028 c cursor for select * from generate_series(1, 10);
3033 move relative 2 in c;
3037 fetch next from c into x;
3044 $$ language plpgsql;
3045 select * from sc_test();
3053 drop function sc_test();
3054 -- test qualified variable names
3055 create function pl_qual_names (param1 int) returns void as $$
3064 raise notice 'param1 = %', param1;
3065 raise notice 'pl_qual_names.param1 = %', pl_qual_names.param1;
3066 raise notice 'outerblock.param1 = %', outerblock.param1;
3067 raise notice 'innerblock.param1 = %', innerblock.param1;
3070 $$ language plpgsql;
3071 select pl_qual_names(42);
3073 NOTICE: pl_qual_names.param1 = 42
3074 NOTICE: outerblock.param1 = 1
3075 NOTICE: innerblock.param1 = 2
3081 drop function pl_qual_names(int);
3082 -- tests for RETURN QUERY
3083 create function ret_query1(out int, out int) returns setof record as $$
3088 return query select x + 1, x * 10 from generate_series(0, 10) s (x);
3091 $$ language plpgsql;
3092 select * from ret_query1();
3110 create type record_type as (x text, y int, z boolean);
3111 create or replace function ret_query2(lim int) returns setof record_type as $$
3113 return query select md5(s.x::text), s.x, s.x > 0
3114 from generate_series(-8, lim) s (x) where s.x % 2 = 0;
3116 $$ language plpgsql;
3117 select * from ret_query2(8);
3119 ----------------------------------+----+---
3120 a8d2ec85eaf98407310b72eb73dda247 | -8 | f
3121 596a3d04481816330f07e4f97510c28f | -6 | f
3122 0267aaf632e87a63288a08331f22c7c3 | -4 | f
3123 5d7b9adcbe1c629ec722529dd12e5129 | -2 | f
3124 cfcd208495d565ef66e7dff9f98764da | 0 | f
3125 c81e728d9d4c2f636f067f89cc14862c | 2 | t
3126 a87ff679a2f3e71d9181a67b7542122c | 4 | t
3127 1679091c5a880faf6fb5e6087eb1b2dc | 6 | t
3128 c9f0f895fb98ab9159f51fd0297e236d | 8 | t
3131 -- test EXECUTE USING
3132 create function exc_using(int, text) returns int as $$
3135 for i in execute 'select * from generate_series(1,$1)' using $1+1 loop
3136 raise notice '%', i;
3138 execute 'select $2 + $2*3 + length($1)' into i using $2,$1;
3141 $$ language plpgsql;
3142 select exc_using(5, 'foobar');
3154 -- test FOR-over-cursor
3155 create or replace function forc01() returns void as $$
3157 c cursor(r1 integer, r2 integer)
3158 for select * from generate_series(r1,r2) i;
3160 for select * from generate_series(41,43) i;
3162 for r in c(5,7) loop
3163 raise notice '% from %', r.i, c;
3165 -- again, to test if cursor was closed properly
3166 for r in c(9,10) loop
3167 raise notice '% from %', r.i, c;
3169 -- and test a parameterless cursor
3171 raise notice '% from %', r.i, c2;
3173 -- and try it with a hand-assigned name
3174 raise notice 'after loop, c2 = %', c2;
3175 c2 := 'special_name';
3177 raise notice '% from %', r.i, c2;
3179 raise notice 'after loop, c2 = %', c2;
3180 -- and try it with a generated name
3181 -- (which we can't show in the output because it's variable)
3184 raise notice '%', r.i;
3186 raise notice 'after loop, c2 = %', c2;
3189 $$ language plpgsql;
3199 NOTICE: after loop, c2 = c2
3200 NOTICE: 41 from special_name
3201 NOTICE: 42 from special_name
3202 NOTICE: 43 from special_name
3203 NOTICE: after loop, c2 = special_name
3207 NOTICE: after loop, c2 = <NULL>
3213 -- try updating the cursor's current row
3214 create temp table forc_test as
3215 select n as i, n as j from generate_series(1,10) n;
3216 create or replace function forc01() returns void as $$
3218 c cursor for select * from forc_test;
3221 raise notice '%, %', r.i, r.j;
3222 update forc_test set i = i * 100, j = r.j * 2 where current of c;
3225 $$ language plpgsql;
3242 select * from forc_test;
3257 drop function forc01();
3258 -- fail because cursor has no query bound to it
3259 create or replace function forc_bad() returns void as $$
3264 raise notice '%', r.i;
3267 $$ language plpgsql;
3268 ERROR: cursor FOR loop must use a bound cursor variable
3269 CONTEXT: compile of PL/pgSQL function "forc_bad" near line 4
3270 -- test RETURN QUERY EXECUTE
3271 create or replace function return_dquery()
3272 returns setof int as $$
3274 return query execute 'select * from (values(10),(20)) f';
3275 return query execute 'select * from (values($1),($2)) f' using 40,50;
3277 $$ language plpgsql;
3278 select * from return_dquery();
3287 drop function return_dquery();
3288 -- Tests for 8.4's new RAISE features
3289 create or replace function raise_test() returns void as $$
3291 raise notice '% % %', 1, 2, 3
3292 using errcode = '55001', detail = 'some detail info', hint = 'some hint';
3293 raise '% % %', 1, 2, 3
3294 using errcode = 'division_by_zero', detail = 'some detail info';
3296 $$ language plpgsql;
3297 select raise_test();
3299 DETAIL: some detail info
3302 DETAIL: some detail info
3303 -- Since we can't actually see the thrown SQLSTATE in default psql output,
3304 -- test it like this; this also tests re-RAISE
3305 create or replace function raise_test() returns void as $$
3308 using errcode = 'division_by_zero', detail = 'some detail info';
3311 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3314 $$ language plpgsql;
3315 select raise_test();
3316 NOTICE: SQLSTATE: 22012 SQLERRM: check me
3318 DETAIL: some detail info
3319 create or replace function raise_test() returns void as $$
3322 using errcode = '1234F', detail = 'some detail info';
3325 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3328 $$ language plpgsql;
3329 select raise_test();
3330 NOTICE: SQLSTATE: 1234F SQLERRM: check me
3332 DETAIL: some detail info
3333 -- SQLSTATE specification in WHEN
3334 create or replace function raise_test() returns void as $$
3337 using errcode = '1234F', detail = 'some detail info';
3339 when sqlstate '1234F' then
3340 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3343 $$ language plpgsql;
3344 select raise_test();
3345 NOTICE: SQLSTATE: 1234F SQLERRM: check me
3347 DETAIL: some detail info
3348 create or replace function raise_test() returns void as $$
3350 raise division_by_zero using detail = 'some detail info';
3353 raise notice 'SQLSTATE: % SQLERRM: %', sqlstate, sqlerrm;
3356 $$ language plpgsql;
3357 select raise_test();
3358 NOTICE: SQLSTATE: 22012 SQLERRM: division_by_zero
3359 ERROR: division_by_zero
3360 DETAIL: some detail info
3361 create or replace function raise_test() returns void as $$
3363 raise division_by_zero;
3365 $$ language plpgsql;
3366 select raise_test();
3367 ERROR: division_by_zero
3368 create or replace function raise_test() returns void as $$
3370 raise sqlstate '1234F';
3372 $$ language plpgsql;
3373 select raise_test();
3375 create or replace function raise_test() returns void as $$
3377 raise division_by_zero using message = 'custom' || ' message';
3379 $$ language plpgsql;
3380 select raise_test();
3381 ERROR: custom message
3382 create or replace function raise_test() returns void as $$
3384 raise using message = 'custom' || ' message', errcode = '22012';
3386 $$ language plpgsql;
3387 select raise_test();
3388 ERROR: custom message
3389 -- conflict on message
3390 create or replace function raise_test() returns void as $$
3392 raise notice 'some message' using message = 'custom' || ' message', errcode = '22012';
3394 $$ language plpgsql;
3395 select raise_test();
3396 ERROR: RAISE option already specified: MESSAGE
3397 CONTEXT: PL/pgSQL function "raise_test" line 2 at RAISE
3398 -- conflict on errcode
3399 create or replace function raise_test() returns void as $$
3401 raise division_by_zero using message = 'custom' || ' message', errcode = '22012';
3403 $$ language plpgsql;
3404 select raise_test();
3405 ERROR: RAISE option already specified: ERRCODE
3406 CONTEXT: PL/pgSQL function "raise_test" line 2 at RAISE
3407 -- nothing to re-RAISE
3408 create or replace function raise_test() returns void as $$
3412 $$ language plpgsql;
3413 select raise_test();
3414 ERROR: RAISE without parameters cannot be used outside an exception handler
3415 CONTEXT: PL/pgSQL function "raise_test"
3416 drop function raise_test();
3417 -- test CASE statement
3418 create or replace function case_test(bigint) returns text as $$
3428 return 'three, four or eight';
3431 when a+b, a+b+1 then
3432 return 'eleven, twelve';
3435 $$ language plpgsql immutable;
3436 select case_test(1);
3442 select case_test(2);
3448 select case_test(3);
3450 ----------------------
3451 three, four or eight
3454 select case_test(4);
3456 ----------------------
3457 three, four or eight
3460 select case_test(5); -- fails
3461 ERROR: case not found
3462 HINT: CASE statement is missing ELSE part.
3463 CONTEXT: PL/pgSQL function "case_test" line 4 at CASE
3464 select case_test(8);
3466 ----------------------
3467 three, four or eight
3470 select case_test(10);
3476 select case_test(11);
3482 select case_test(12);
3488 select case_test(13); -- fails
3489 ERROR: case not found
3490 HINT: CASE statement is missing ELSE part.
3491 CONTEXT: PL/pgSQL function "case_test" line 4 at CASE
3492 create or replace function catch() returns void as $$
3494 raise notice '%', case_test(6);
3496 when case_not_found then
3497 raise notice 'caught case_not_found % %', SQLSTATE, SQLERRM;
3499 $$ language plpgsql;
3501 NOTICE: caught case_not_found 20000 case not found
3507 -- test the searched variant too, as well as ELSE
3508 create or replace function case_test(bigint) returns text as $$
3514 when $1 = a + 2 then
3520 $$ language plpgsql immutable;
3521 select case_test(1);
3527 select case_test(2);
3533 select case_test(12);
3539 select case_test(13);
3545 drop function catch();
3546 drop function case_test(bigint);
3547 -- test variadic functions
3548 create or replace function vari(variadic int[])
3551 for i in array_lower($1,1)..array_upper($1,1) loop
3552 raise notice '%', $1[i];
3554 $$ language plpgsql;
3555 select vari(1,2,3,4,5);
3575 select vari(variadic array[5,6,7]);
3584 drop function vari(int[]);
3586 create or replace function pleast(variadic numeric[])
3587 returns numeric as $$
3588 declare aux numeric = $1[array_lower($1,1)];
3590 for i in array_lower($1,1)+1..array_upper($1,1) loop
3591 if $1[i] < aux then aux := $1[i]; end if;
3595 $$ language plpgsql immutable strict;
3596 select pleast(10,1,2,3,-16);
3602 select pleast(10.2,2.2,-1.1);
3608 select pleast(10.2,10, -20);
3614 select pleast(10,20, -1.0);
3620 -- in case of conflict, non-variadic version is preferred
3621 create or replace function pleast(numeric)
3622 returns numeric as $$
3624 raise notice 'non-variadic function called';
3627 $$ language plpgsql immutable strict;
3629 NOTICE: non-variadic function called
3635 drop function pleast(numeric[]);
3636 drop function pleast(numeric);
3637 -- test table functions
3638 create function tftest(int) returns table(a int, b int) as $$
3640 return query select $1, $1+i from generate_series(1,5) g(i);
3642 $$ language plpgsql immutable strict;
3643 select * from tftest(10);
3653 create or replace function tftest(a1 int) returns table(a int, b int) as $$
3655 a := a1; b := a1 + 1;
3657 a := a1 * 10; b := a1 * 10 + 1;
3660 $$ language plpgsql immutable strict;
3661 select * from tftest(10);
3668 drop function tftest(int);