1--
2-- RULES
3-- From Jan's original setup_ruletest.sql and run_ruletest.sql
4-- - thomas 1998-09-13
5--
6
7--
8-- Tables and rules for the view test
9--
10create table rtest_t1 (a int4, b int4);
11create table rtest_t2 (a int4, b int4);
12create table rtest_t3 (a int4, b int4);
13
14create view rtest_v1 as select * from rtest_t1;
15create rule rtest_v1_ins as on insert to rtest_v1 do instead
16	insert into rtest_t1 values (new.a, new.b);
17create rule rtest_v1_upd as on update to rtest_v1 do instead
18	update rtest_t1 set a = new.a, b = new.b
19	where a = old.a;
20create rule rtest_v1_del as on delete to rtest_v1 do instead
21	delete from rtest_t1 where a = old.a;
22-- Test comments
23COMMENT ON RULE rtest_v1_bad ON rtest_v1 IS 'bad rule';
24COMMENT ON RULE rtest_v1_del ON rtest_v1 IS 'delete rule';
25COMMENT ON RULE rtest_v1_del ON rtest_v1 IS NULL;
26--
27-- Tables and rules for the constraint update/delete test
28--
29-- Note:
30-- 	Now that we have multiple action rule support, we check
31-- 	both possible syntaxes to define them (The last action
32--  can but must not have a semicolon at the end).
33--
34create table rtest_system (sysname text, sysdesc text);
35create table rtest_interface (sysname text, ifname text);
36create table rtest_person (pname text, pdesc text);
37create table rtest_admin (pname text, sysname text);
38
39create rule rtest_sys_upd as on update to rtest_system do also (
40	update rtest_interface set sysname = new.sysname
41		where sysname = old.sysname;
42	update rtest_admin set sysname = new.sysname
43		where sysname = old.sysname
44	);
45
46create rule rtest_sys_del as on delete to rtest_system do also (
47	delete from rtest_interface where sysname = old.sysname;
48	delete from rtest_admin where sysname = old.sysname;
49	);
50
51create rule rtest_pers_upd as on update to rtest_person do also
52	update rtest_admin set pname = new.pname where pname = old.pname;
53
54create rule rtest_pers_del as on delete to rtest_person do also
55	delete from rtest_admin where pname = old.pname;
56
57--
58-- Tables and rules for the logging test
59--
60create table rtest_emp (ename char(20), salary money);
61create table rtest_emplog (ename char(20), who name, action char(10), newsal money, oldsal money);
62create table rtest_empmass (ename char(20), salary money);
63
64create rule rtest_emp_ins as on insert to rtest_emp do
65	insert into rtest_emplog values (new.ename, current_user,
66			'hired', new.salary, '0.00');
67
68create rule rtest_emp_upd as on update to rtest_emp where new.salary != old.salary do
69	insert into rtest_emplog values (new.ename, current_user,
70			'honored', new.salary, old.salary);
71
72create rule rtest_emp_del as on delete to rtest_emp do
73	insert into rtest_emplog values (old.ename, current_user,
74			'fired', '0.00', old.salary);
75
76--
77-- Tables and rules for the multiple cascaded qualified instead
78-- rule test
79--
80create table rtest_t4 (a int4, b text);
81create table rtest_t5 (a int4, b text);
82create table rtest_t6 (a int4, b text);
83create table rtest_t7 (a int4, b text);
84create table rtest_t8 (a int4, b text);
85create table rtest_t9 (a int4, b text);
86
87create rule rtest_t4_ins1 as on insert to rtest_t4
88		where new.a >= 10 and new.a < 20 do instead
89	insert into rtest_t5 values (new.a, new.b);
90
91create rule rtest_t4_ins2 as on insert to rtest_t4
92		where new.a >= 20 and new.a < 30 do
93	insert into rtest_t6 values (new.a, new.b);
94
95create rule rtest_t5_ins as on insert to rtest_t5
96		where new.a > 15 do
97	insert into rtest_t7 values (new.a, new.b);
98
99create rule rtest_t6_ins as on insert to rtest_t6
100		where new.a > 25 do instead
101	insert into rtest_t8 values (new.a, new.b);
102
103--
104-- Tables and rules for the rule fire order test
105--
106-- As of PG 7.3, the rules should fire in order by name, regardless
107-- of INSTEAD attributes or creation order.
108--
109create table rtest_order1 (a int4);
110create table rtest_order2 (a int4, b int4, c text);
111
112create sequence rtest_seq;
113
114create rule rtest_order_r3 as on insert to rtest_order1 do instead
115	insert into rtest_order2 values (new.a, nextval('rtest_seq'),
116		'rule 3 - this should run 3rd');
117
118create rule rtest_order_r4 as on insert to rtest_order1
119		where a < 100 do instead
120	insert into rtest_order2 values (new.a, nextval('rtest_seq'),
121		'rule 4 - this should run 4th');
122
123create rule rtest_order_r2 as on insert to rtest_order1 do
124	insert into rtest_order2 values (new.a, nextval('rtest_seq'),
125		'rule 2 - this should run 2nd');
126
127create rule rtest_order_r1 as on insert to rtest_order1 do instead
128	insert into rtest_order2 values (new.a, nextval('rtest_seq'),
129		'rule 1 - this should run 1st');
130
131--
132-- Tables and rules for the instead nothing test
133--
134create table rtest_nothn1 (a int4, b text);
135create table rtest_nothn2 (a int4, b text);
136create table rtest_nothn3 (a int4, b text);
137create table rtest_nothn4 (a int4, b text);
138
139create rule rtest_nothn_r1 as on insert to rtest_nothn1
140	where new.a >= 10 and new.a < 20 do instead nothing;
141
142create rule rtest_nothn_r2 as on insert to rtest_nothn1
143	where new.a >= 30 and new.a < 40 do instead nothing;
144
145create rule rtest_nothn_r3 as on insert to rtest_nothn2
146	where new.a >= 100 do instead
147	insert into rtest_nothn3 values (new.a, new.b);
148
149create rule rtest_nothn_r4 as on insert to rtest_nothn2
150	do instead nothing;
151
152--
153-- Tests on a view that is select * of a table
154-- and has insert/update/delete instead rules to
155-- behave close like the real table.
156--
157
158--
159-- We need test date later
160--
161insert into rtest_t2 values (1, 21);
162insert into rtest_t2 values (2, 22);
163insert into rtest_t2 values (3, 23);
164
165insert into rtest_t3 values (1, 31);
166insert into rtest_t3 values (2, 32);
167insert into rtest_t3 values (3, 33);
168insert into rtest_t3 values (4, 34);
169insert into rtest_t3 values (5, 35);
170
171-- insert values
172insert into rtest_v1 values (1, 11);
173insert into rtest_v1 values (2, 12);
174select * from rtest_v1;
175
176-- delete with constant expression
177delete from rtest_v1 where a = 1;
178select * from rtest_v1;
179insert into rtest_v1 values (1, 11);
180delete from rtest_v1 where b = 12;
181select * from rtest_v1;
182insert into rtest_v1 values (2, 12);
183insert into rtest_v1 values (2, 13);
184select * from rtest_v1;
185** Remember the delete rule on rtest_v1: It says
186** DO INSTEAD DELETE FROM rtest_t1 WHERE a = old.a
187** So this time both rows with a = 2 must get deleted
188\p
189\r
190delete from rtest_v1 where b = 12;
191select * from rtest_v1;
192delete from rtest_v1;
193
194-- insert select
195insert into rtest_v1 select * from rtest_t2;
196select * from rtest_v1;
197delete from rtest_v1;
198
199-- same with swapped targetlist
200insert into rtest_v1 (b, a) select b, a from rtest_t2;
201select * from rtest_v1;
202
203-- now with only one target attribute
204insert into rtest_v1 (a) select a from rtest_t3;
205select * from rtest_v1;
206select * from rtest_v1 where b isnull;
207
208-- let attribute a differ (must be done on rtest_t1 - see above)
209update rtest_t1 set a = a + 10 where b isnull;
210delete from rtest_v1 where b isnull;
211select * from rtest_v1;
212
213-- now updates with constant expression
214update rtest_v1 set b = 42 where a = 2;
215select * from rtest_v1;
216update rtest_v1 set b = 99 where b = 42;
217select * from rtest_v1;
218update rtest_v1 set b = 88 where b < 50;
219select * from rtest_v1;
220delete from rtest_v1;
221insert into rtest_v1 select rtest_t2.a, rtest_t3.b
222    from rtest_t2, rtest_t3
223    where rtest_t2.a = rtest_t3.a;
224select * from rtest_v1;
225
226-- updates in a mergejoin
227update rtest_v1 set b = rtest_t2.b from rtest_t2 where rtest_v1.a = rtest_t2.a;
228select * from rtest_v1;
229insert into rtest_v1 select * from rtest_t3;
230select * from rtest_v1;
231update rtest_t1 set a = a + 10 where b > 30;
232select * from rtest_v1;
233update rtest_v1 set a = rtest_t3.a + 20 from rtest_t3 where rtest_v1.b = rtest_t3.b;
234select * from rtest_v1;
235
236--
237-- Test for constraint updates/deletes
238--
239insert into rtest_system values ('orion', 'Linux Jan Wieck');
240insert into rtest_system values ('notjw', 'WinNT Jan Wieck (notebook)');
241insert into rtest_system values ('neptun', 'Fileserver');
242
243insert into rtest_interface values ('orion', 'eth0');
244insert into rtest_interface values ('orion', 'eth1');
245insert into rtest_interface values ('notjw', 'eth0');
246insert into rtest_interface values ('neptun', 'eth0');
247
248insert into rtest_person values ('jw', 'Jan Wieck');
249insert into rtest_person values ('bm', 'Bruce Momjian');
250
251insert into rtest_admin values ('jw', 'orion');
252insert into rtest_admin values ('jw', 'notjw');
253insert into rtest_admin values ('bm', 'neptun');
254
255update rtest_system set sysname = 'pluto' where sysname = 'neptun';
256
257select * from rtest_interface;
258select * from rtest_admin;
259
260update rtest_person set pname = 'jwieck' where pdesc = 'Jan Wieck';
261
262-- Note: use ORDER BY here to ensure consistent output across all systems.
263-- The above UPDATE affects two rows with equal keys, so they could be
264-- updated in either order depending on the whim of the local qsort().
265
266select * from rtest_admin order by pname, sysname;
267
268delete from rtest_system where sysname = 'orion';
269
270select * from rtest_interface;
271select * from rtest_admin;
272
273--
274-- Rule qualification test
275--
276insert into rtest_emp values ('wiecc', '5000.00');
277insert into rtest_emp values ('gates', '80000.00');
278update rtest_emp set ename = 'wiecx' where ename = 'wiecc';
279update rtest_emp set ename = 'wieck', salary = '6000.00' where ename = 'wiecx';
280update rtest_emp set salary = '7000.00' where ename = 'wieck';
281delete from rtest_emp where ename = 'gates';
282
283select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal;
284insert into rtest_empmass values ('meyer', '4000.00');
285insert into rtest_empmass values ('maier', '5000.00');
286insert into rtest_empmass values ('mayr', '6000.00');
287insert into rtest_emp select * from rtest_empmass;
288select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal;
289update rtest_empmass set salary = salary + '1000.00';
290update rtest_emp set salary = rtest_empmass.salary from rtest_empmass where rtest_emp.ename = rtest_empmass.ename;
291select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal;
292delete from rtest_emp using rtest_empmass where rtest_emp.ename = rtest_empmass.ename;
293select ename, who = current_user as "matches user", action, newsal, oldsal from rtest_emplog order by ename, action, newsal;
294
295--
296-- Multiple cascaded qualified instead rule test
297--
298insert into rtest_t4 values (1, 'Record should go to rtest_t4');
299insert into rtest_t4 values (2, 'Record should go to rtest_t4');
300insert into rtest_t4 values (10, 'Record should go to rtest_t5');
301insert into rtest_t4 values (15, 'Record should go to rtest_t5');
302insert into rtest_t4 values (19, 'Record should go to rtest_t5 and t7');
303insert into rtest_t4 values (20, 'Record should go to rtest_t4 and t6');
304insert into rtest_t4 values (26, 'Record should go to rtest_t4 and t8');
305insert into rtest_t4 values (28, 'Record should go to rtest_t4 and t8');
306insert into rtest_t4 values (30, 'Record should go to rtest_t4');
307insert into rtest_t4 values (40, 'Record should go to rtest_t4');
308
309select * from rtest_t4;
310select * from rtest_t5;
311select * from rtest_t6;
312select * from rtest_t7;
313select * from rtest_t8;
314
315delete from rtest_t4;
316delete from rtest_t5;
317delete from rtest_t6;
318delete from rtest_t7;
319delete from rtest_t8;
320
321insert into rtest_t9 values (1, 'Record should go to rtest_t4');
322insert into rtest_t9 values (2, 'Record should go to rtest_t4');
323insert into rtest_t9 values (10, 'Record should go to rtest_t5');
324insert into rtest_t9 values (15, 'Record should go to rtest_t5');
325insert into rtest_t9 values (19, 'Record should go to rtest_t5 and t7');
326insert into rtest_t9 values (20, 'Record should go to rtest_t4 and t6');
327insert into rtest_t9 values (26, 'Record should go to rtest_t4 and t8');
328insert into rtest_t9 values (28, 'Record should go to rtest_t4 and t8');
329insert into rtest_t9 values (30, 'Record should go to rtest_t4');
330insert into rtest_t9 values (40, 'Record should go to rtest_t4');
331
332insert into rtest_t4 select * from rtest_t9 where a < 20;
333
334select * from rtest_t4;
335select * from rtest_t5;
336select * from rtest_t6;
337select * from rtest_t7;
338select * from rtest_t8;
339
340insert into rtest_t4 select * from rtest_t9 where b ~ 'and t8';
341
342select * from rtest_t4;
343select * from rtest_t5;
344select * from rtest_t6;
345select * from rtest_t7;
346select * from rtest_t8;
347
348insert into rtest_t4 select a + 1, b from rtest_t9 where a in (20, 30, 40);
349
350select * from rtest_t4;
351select * from rtest_t5;
352select * from rtest_t6;
353select * from rtest_t7;
354select * from rtest_t8;
355
356--
357-- Check that the ordering of rules fired is correct
358--
359insert into rtest_order1 values (1);
360select * from rtest_order2;
361
362--
363-- Check if instead nothing w/without qualification works
364--
365insert into rtest_nothn1 values (1, 'want this');
366insert into rtest_nothn1 values (2, 'want this');
367insert into rtest_nothn1 values (10, 'don''t want this');
368insert into rtest_nothn1 values (19, 'don''t want this');
369insert into rtest_nothn1 values (20, 'want this');
370insert into rtest_nothn1 values (29, 'want this');
371insert into rtest_nothn1 values (30, 'don''t want this');
372insert into rtest_nothn1 values (39, 'don''t want this');
373insert into rtest_nothn1 values (40, 'want this');
374insert into rtest_nothn1 values (50, 'want this');
375insert into rtest_nothn1 values (60, 'want this');
376
377select * from rtest_nothn1;
378
379insert into rtest_nothn2 values (10, 'too small');
380insert into rtest_nothn2 values (50, 'too small');
381insert into rtest_nothn2 values (100, 'OK');
382insert into rtest_nothn2 values (200, 'OK');
383
384select * from rtest_nothn2;
385select * from rtest_nothn3;
386
387delete from rtest_nothn1;
388delete from rtest_nothn2;
389delete from rtest_nothn3;
390
391insert into rtest_nothn4 values (1, 'want this');
392insert into rtest_nothn4 values (2, 'want this');
393insert into rtest_nothn4 values (10, 'don''t want this');
394insert into rtest_nothn4 values (19, 'don''t want this');
395insert into rtest_nothn4 values (20, 'want this');
396insert into rtest_nothn4 values (29, 'want this');
397insert into rtest_nothn4 values (30, 'don''t want this');
398insert into rtest_nothn4 values (39, 'don''t want this');
399insert into rtest_nothn4 values (40, 'want this');
400insert into rtest_nothn4 values (50, 'want this');
401insert into rtest_nothn4 values (60, 'want this');
402
403insert into rtest_nothn1 select * from rtest_nothn4;
404
405select * from rtest_nothn1;
406
407delete from rtest_nothn4;
408
409insert into rtest_nothn4 values (10, 'too small');
410insert into rtest_nothn4 values (50, 'too small');
411insert into rtest_nothn4 values (100, 'OK');
412insert into rtest_nothn4 values (200, 'OK');
413
414insert into rtest_nothn2 select * from rtest_nothn4;
415
416select * from rtest_nothn2;
417select * from rtest_nothn3;
418
419create table rtest_view1 (a int4, b text, v bool);
420create table rtest_view2 (a int4);
421create table rtest_view3 (a int4, b text);
422create table rtest_view4 (a int4, b text, c int4);
423create view rtest_vview1 as select a, b from rtest_view1 X
424	where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a);
425create view rtest_vview2 as select a, b from rtest_view1 where v;
426create view rtest_vview3 as select a, b from rtest_vview2 X
427	where 0 < (select count(*) from rtest_view2 Y where Y.a = X.a);
428create view rtest_vview4 as select X.a, X.b, count(Y.a) as refcount
429	from rtest_view1 X, rtest_view2 Y
430	where X.a = Y.a
431	group by X.a, X.b;
432create function rtest_viewfunc1(int4) returns int4 as
433	'select count(*)::int4 from rtest_view2 where a = $1'
434	language sql;
435create view rtest_vview5 as select a, b, rtest_viewfunc1(a) as refcount
436	from rtest_view1;
437
438insert into rtest_view1 values (1, 'item 1', 't');
439insert into rtest_view1 values (2, 'item 2', 't');
440insert into rtest_view1 values (3, 'item 3', 't');
441insert into rtest_view1 values (4, 'item 4', 'f');
442insert into rtest_view1 values (5, 'item 5', 't');
443insert into rtest_view1 values (6, 'item 6', 'f');
444insert into rtest_view1 values (7, 'item 7', 't');
445insert into rtest_view1 values (8, 'item 8', 't');
446
447insert into rtest_view2 values (2);
448insert into rtest_view2 values (2);
449insert into rtest_view2 values (4);
450insert into rtest_view2 values (5);
451insert into rtest_view2 values (7);
452insert into rtest_view2 values (7);
453insert into rtest_view2 values (7);
454insert into rtest_view2 values (7);
455
456select * from rtest_vview1;
457select * from rtest_vview2;
458select * from rtest_vview3;
459select * from rtest_vview4 order by a, b;
460select * from rtest_vview5;
461
462insert into rtest_view3 select * from rtest_vview1 where a < 7;
463select * from rtest_view3;
464delete from rtest_view3;
465
466insert into rtest_view3 select * from rtest_vview2 where a != 5 and b !~ '2';
467select * from rtest_view3;
468delete from rtest_view3;
469
470insert into rtest_view3 select * from rtest_vview3;
471select * from rtest_view3;
472delete from rtest_view3;
473
474insert into rtest_view4 select * from rtest_vview4 where 3 > refcount;
475select * from rtest_view4 order by a, b;
476delete from rtest_view4;
477
478insert into rtest_view4 select * from rtest_vview5 where a > 2 and refcount = 0;
479select * from rtest_view4;
480delete from rtest_view4;
481--
482-- Test for computations in views
483--
484create table rtest_comp (
485	part	text,
486	unit	char(4),
487	size	float
488);
489
490
491create table rtest_unitfact (
492	unit	char(4),
493	factor	float
494);
495
496create view rtest_vcomp as
497	select X.part, (X.size * Y.factor) as size_in_cm
498			from rtest_comp X, rtest_unitfact Y
499			where X.unit = Y.unit;
500
501
502insert into rtest_unitfact values ('m', 100.0);
503insert into rtest_unitfact values ('cm', 1.0);
504insert into rtest_unitfact values ('inch', 2.54);
505
506insert into rtest_comp values ('p1', 'm', 5.0);
507insert into rtest_comp values ('p2', 'm', 3.0);
508insert into rtest_comp values ('p3', 'cm', 5.0);
509insert into rtest_comp values ('p4', 'cm', 15.0);
510insert into rtest_comp values ('p5', 'inch', 7.0);
511insert into rtest_comp values ('p6', 'inch', 4.4);
512
513select * from rtest_vcomp order by part;
514
515select * from rtest_vcomp where size_in_cm > 10.0 order by size_in_cm using >;
516
517--
518-- In addition run the (slightly modified) queries from the
519-- programmers manual section on the rule system.
520--
521CREATE TABLE shoe_data (
522	shoename   char(10),      -- primary key
523	sh_avail   integer,       -- available # of pairs
524	slcolor    char(10),      -- preferred shoelace color
525	slminlen   float,         -- minimum shoelace length
526	slmaxlen   float,         -- maximum shoelace length
527	slunit     char(8)        -- length unit
528);
529
530CREATE TABLE shoelace_data (
531	sl_name    char(10),      -- primary key
532	sl_avail   integer,       -- available # of pairs
533	sl_color   char(10),      -- shoelace color
534	sl_len     float,         -- shoelace length
535	sl_unit    char(8)        -- length unit
536);
537
538CREATE TABLE unit (
539	un_name    char(8),       -- the primary key
540	un_fact    float          -- factor to transform to cm
541);
542
543CREATE VIEW shoe AS
544	SELECT sh.shoename,
545		   sh.sh_avail,
546		   sh.slcolor,
547		   sh.slminlen,
548		   sh.slminlen * un.un_fact AS slminlen_cm,
549		   sh.slmaxlen,
550		   sh.slmaxlen * un.un_fact AS slmaxlen_cm,
551		   sh.slunit
552	  FROM shoe_data sh, unit un
553	 WHERE sh.slunit = un.un_name;
554
555CREATE VIEW shoelace AS
556	SELECT s.sl_name,
557		   s.sl_avail,
558		   s.sl_color,
559		   s.sl_len,
560		   s.sl_unit,
561		   s.sl_len * u.un_fact AS sl_len_cm
562	  FROM shoelace_data s, unit u
563	 WHERE s.sl_unit = u.un_name;
564
565CREATE VIEW shoe_ready AS
566	SELECT rsh.shoename,
567		   rsh.sh_avail,
568		   rsl.sl_name,
569		   rsl.sl_avail,
570		   int4smaller(rsh.sh_avail, rsl.sl_avail) AS total_avail
571	  FROM shoe rsh, shoelace rsl
572	 WHERE rsl.sl_color = rsh.slcolor
573	   AND rsl.sl_len_cm >= rsh.slminlen_cm
574	   AND rsl.sl_len_cm <= rsh.slmaxlen_cm;
575
576INSERT INTO unit VALUES ('cm', 1.0);
577INSERT INTO unit VALUES ('m', 100.0);
578INSERT INTO unit VALUES ('inch', 2.54);
579
580INSERT INTO shoe_data VALUES ('sh1', 2, 'black', 70.0, 90.0, 'cm');
581INSERT INTO shoe_data VALUES ('sh2', 0, 'black', 30.0, 40.0, 'inch');
582INSERT INTO shoe_data VALUES ('sh3', 4, 'brown', 50.0, 65.0, 'cm');
583INSERT INTO shoe_data VALUES ('sh4', 3, 'brown', 40.0, 50.0, 'inch');
584
585INSERT INTO shoelace_data VALUES ('sl1', 5, 'black', 80.0, 'cm');
586INSERT INTO shoelace_data VALUES ('sl2', 6, 'black', 100.0, 'cm');
587INSERT INTO shoelace_data VALUES ('sl3', 0, 'black', 35.0 , 'inch');
588INSERT INTO shoelace_data VALUES ('sl4', 8, 'black', 40.0 , 'inch');
589INSERT INTO shoelace_data VALUES ('sl5', 4, 'brown', 1.0 , 'm');
590INSERT INTO shoelace_data VALUES ('sl6', 0, 'brown', 0.9 , 'm');
591INSERT INTO shoelace_data VALUES ('sl7', 7, 'brown', 60 , 'cm');
592INSERT INTO shoelace_data VALUES ('sl8', 1, 'brown', 40 , 'inch');
593
594-- SELECTs in doc
595SELECT * FROM shoelace ORDER BY sl_name;
596SELECT * FROM shoe_ready WHERE total_avail >= 2 ORDER BY 1;
597
598    CREATE TABLE shoelace_log (
599        sl_name    char(10),      -- shoelace changed
600        sl_avail   integer,       -- new available value
601        log_who    name,          -- who did it
602        log_when   timestamp      -- when
603    );
604
605-- Want "log_who" to be CURRENT_USER,
606-- but that is non-portable for the regression test
607-- - thomas 1999-02-21
608
609    CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data
610        WHERE NEW.sl_avail != OLD.sl_avail
611        DO INSERT INTO shoelace_log VALUES (
612                                        NEW.sl_name,
613                                        NEW.sl_avail,
614                                        'Al Bundy',
615                                        'epoch'
616                                    );
617
618UPDATE shoelace_data SET sl_avail = 6 WHERE  sl_name = 'sl7';
619
620SELECT * FROM shoelace_log;
621
622    CREATE RULE shoelace_ins AS ON INSERT TO shoelace
623        DO INSTEAD
624        INSERT INTO shoelace_data VALUES (
625               NEW.sl_name,
626               NEW.sl_avail,
627               NEW.sl_color,
628               NEW.sl_len,
629               NEW.sl_unit);
630
631    CREATE RULE shoelace_upd AS ON UPDATE TO shoelace
632        DO INSTEAD
633        UPDATE shoelace_data SET
634               sl_name = NEW.sl_name,
635               sl_avail = NEW.sl_avail,
636               sl_color = NEW.sl_color,
637               sl_len = NEW.sl_len,
638               sl_unit = NEW.sl_unit
639         WHERE sl_name = OLD.sl_name;
640
641    CREATE RULE shoelace_del AS ON DELETE TO shoelace
642        DO INSTEAD
643        DELETE FROM shoelace_data
644         WHERE sl_name = OLD.sl_name;
645
646    CREATE TABLE shoelace_arrive (
647        arr_name    char(10),
648        arr_quant   integer
649    );
650
651    CREATE TABLE shoelace_ok (
652        ok_name     char(10),
653        ok_quant    integer
654    );
655
656    CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok
657        DO INSTEAD
658        UPDATE shoelace SET
659               sl_avail = sl_avail + NEW.ok_quant
660         WHERE sl_name = NEW.ok_name;
661
662INSERT INTO shoelace_arrive VALUES ('sl3', 10);
663INSERT INTO shoelace_arrive VALUES ('sl6', 20);
664INSERT INTO shoelace_arrive VALUES ('sl8', 20);
665
666SELECT * FROM shoelace ORDER BY sl_name;
667
668insert into shoelace_ok select * from shoelace_arrive;
669
670SELECT * FROM shoelace ORDER BY sl_name;
671
672SELECT * FROM shoelace_log ORDER BY sl_name;
673
674    CREATE VIEW shoelace_obsolete AS
675	SELECT * FROM shoelace WHERE NOT EXISTS
676	    (SELECT shoename FROM shoe WHERE slcolor = sl_color);
677
678    CREATE VIEW shoelace_candelete AS
679	SELECT * FROM shoelace_obsolete WHERE sl_avail = 0;
680
681insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
682insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);
683-- Unsupported (even though a similar updatable view construct is)
684insert into shoelace values ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0)
685  on conflict do nothing;
686
687SELECT * FROM shoelace_obsolete ORDER BY sl_len_cm;
688SELECT * FROM shoelace_candelete;
689
690DELETE FROM shoelace WHERE EXISTS
691    (SELECT * FROM shoelace_candelete
692             WHERE sl_name = shoelace.sl_name);
693
694SELECT * FROM shoelace ORDER BY sl_name;
695
696SELECT * FROM shoe ORDER BY shoename;
697SELECT count(*) FROM shoe;
698
699
700--
701-- Simple test of qualified ON INSERT ... this did not work in 7.0 ...
702--
703create table foo (f1 int);
704create table foo2 (f1 int);
705
706create rule foorule as on insert to foo where f1 < 100
707do instead nothing;
708
709insert into foo values(1);
710insert into foo values(1001);
711select * from foo;
712
713drop rule foorule on foo;
714
715-- this should fail because f1 is not exposed for unqualified reference:
716create rule foorule as on insert to foo where f1 < 100
717do instead insert into foo2 values (f1);
718-- this is the correct way:
719create rule foorule as on insert to foo where f1 < 100
720do instead insert into foo2 values (new.f1);
721
722insert into foo values(2);
723insert into foo values(100);
724
725select * from foo;
726select * from foo2;
727
728drop rule foorule on foo;
729drop table foo;
730drop table foo2;
731
732
733--
734-- Test rules containing INSERT ... SELECT, which is a very ugly special
735-- case as of 7.1.  Example is based on bug report from Joel Burton.
736--
737create table pparent (pid int, txt text);
738insert into pparent values (1,'parent1');
739insert into pparent values (2,'parent2');
740
741create table cchild (pid int, descrip text);
742insert into cchild values (1,'descrip1');
743
744create view vview as
745  select pparent.pid, txt, descrip from
746    pparent left join cchild using (pid);
747
748create rule rrule as
749  on update to vview do instead
750(
751  insert into cchild (pid, descrip)
752    select old.pid, new.descrip where old.descrip isnull;
753  update cchild set descrip = new.descrip where cchild.pid = old.pid;
754);
755
756select * from vview;
757update vview set descrip='test1' where pid=1;
758select * from vview;
759update vview set descrip='test2' where pid=2;
760select * from vview;
761update vview set descrip='test3' where pid=3;
762select * from vview;
763select * from cchild;
764
765drop rule rrule on vview;
766drop view vview;
767drop table pparent;
768drop table cchild;
769
770
771--
772-- Check that ruleutils are working
773--
774
775-- temporarily disable fancy output, so view changes create less diff noise
776\a\t
777
778SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname;
779
780SELECT tablename, rulename, definition FROM pg_rules
781	ORDER BY tablename, rulename;
782
783-- restore normal output mode
784\a\t
785
786--
787-- CREATE OR REPLACE RULE
788--
789
790CREATE TABLE ruletest_tbl (a int, b int);
791CREATE TABLE ruletest_tbl2 (a int, b int);
792
793CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl
794	DO INSTEAD INSERT INTO ruletest_tbl2 VALUES (10, 10);
795
796INSERT INTO ruletest_tbl VALUES (99, 99);
797
798CREATE OR REPLACE RULE myrule AS ON INSERT TO ruletest_tbl
799	DO INSTEAD INSERT INTO ruletest_tbl2 VALUES (1000, 1000);
800
801INSERT INTO ruletest_tbl VALUES (99, 99);
802
803SELECT * FROM ruletest_tbl2;
804
805-- Check that rewrite rules splitting one INSERT into multiple
806-- conditional statements does not disable FK checking.
807create table rule_and_refint_t1 (
808	id1a integer,
809	id1b integer,
810
811	primary key (id1a, id1b)
812);
813
814create table rule_and_refint_t2 (
815	id2a integer,
816	id2c integer,
817
818	primary key (id2a, id2c)
819);
820
821create table rule_and_refint_t3 (
822	id3a integer,
823	id3b integer,
824	id3c integer,
825	data text,
826
827	primary key (id3a, id3b, id3c),
828
829	foreign key (id3a, id3b) references rule_and_refint_t1 (id1a, id1b),
830	foreign key (id3a, id3c) references rule_and_refint_t2 (id2a, id2c)
831);
832
833
834insert into rule_and_refint_t1 values (1, 11);
835insert into rule_and_refint_t1 values (1, 12);
836insert into rule_and_refint_t1 values (2, 21);
837insert into rule_and_refint_t1 values (2, 22);
838
839insert into rule_and_refint_t2 values (1, 11);
840insert into rule_and_refint_t2 values (1, 12);
841insert into rule_and_refint_t2 values (2, 21);
842insert into rule_and_refint_t2 values (2, 22);
843
844insert into rule_and_refint_t3 values (1, 11, 11, 'row1');
845insert into rule_and_refint_t3 values (1, 11, 12, 'row2');
846insert into rule_and_refint_t3 values (1, 12, 11, 'row3');
847insert into rule_and_refint_t3 values (1, 12, 12, 'row4');
848insert into rule_and_refint_t3 values (1, 11, 13, 'row5');
849insert into rule_and_refint_t3 values (1, 13, 11, 'row6');
850-- Ordinary table
851insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
852  on conflict do nothing;
853-- rule not fired, so fk violation
854insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
855  on conflict (id3a, id3b, id3c) do update
856  set id3b = excluded.id3b;
857-- rule fired, so unsupported
858insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0)
859  on conflict (sl_name) do update
860  set sl_avail = excluded.sl_avail;
861
862create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3
863	where (exists (select 1 from rule_and_refint_t3
864			where (((rule_and_refint_t3.id3a = new.id3a)
865			and (rule_and_refint_t3.id3b = new.id3b))
866			and (rule_and_refint_t3.id3c = new.id3c))))
867	do instead update rule_and_refint_t3 set data = new.data
868	where (((rule_and_refint_t3.id3a = new.id3a)
869	and (rule_and_refint_t3.id3b = new.id3b))
870	and (rule_and_refint_t3.id3c = new.id3c));
871
872insert into rule_and_refint_t3 values (1, 11, 13, 'row7');
873insert into rule_and_refint_t3 values (1, 13, 11, 'row8');
874
875--
876-- disallow dropping a view's rule (bug #5072)
877--
878
879create view fooview as select 'foo'::text;
880drop rule "_RETURN" on fooview;
881drop view fooview;
882
883--
884-- test conversion of table to view (needed to load some pg_dump files)
885--
886
887create table fooview (x int, y text);
888select xmin, * from fooview;
889
890create rule "_RETURN" as on select to fooview do instead
891  select 1 as x, 'aaa'::text as y;
892
893select * from fooview;
894select xmin, * from fooview;  -- fail, views don't have such a column
895
896select reltoastrelid, relkind, relfrozenxid
897  from pg_class where oid = 'fooview'::regclass;
898
899drop view fooview;
900
901-- cannot convert an inheritance parent or child to a view, though
902create table fooview (x int, y text);
903create table fooview_child () inherits (fooview);
904
905create rule "_RETURN" as on select to fooview do instead
906  select 1 as x, 'aaa'::text as y;
907create rule "_RETURN" as on select to fooview_child do instead
908  select 1 as x, 'aaa'::text as y;
909
910drop table fooview cascade;
911
912-- likewise, converting a partitioned table or partition to view is not allowed
913create table fooview (x int, y text) partition by list (x);
914create rule "_RETURN" as on select to fooview do instead
915  select 1 as x, 'aaa'::text as y;
916
917create table fooview_part partition of fooview for values in (1);
918create rule "_RETURN" as on select to fooview_part do instead
919  select 1 as x, 'aaa'::text as y;
920
921drop table fooview;
922
923--
924-- check for planner problems with complex inherited UPDATES
925--
926
927create table id (id serial primary key, name text);
928-- currently, must respecify PKEY for each inherited subtable
929create table test_1 (id integer primary key) inherits (id);
930create table test_2 (id integer primary key) inherits (id);
931create table test_3 (id integer primary key) inherits (id);
932
933insert into test_1 (name) values ('Test 1');
934insert into test_1 (name) values ('Test 2');
935insert into test_2 (name) values ('Test 3');
936insert into test_2 (name) values ('Test 4');
937insert into test_3 (name) values ('Test 5');
938insert into test_3 (name) values ('Test 6');
939
940create view id_ordered as select * from id order by id;
941
942create rule update_id_ordered as on update to id_ordered
943	do instead update id set name = new.name where id = old.id;
944
945select * from id_ordered;
946update id_ordered set name = 'update 2' where id = 2;
947update id_ordered set name = 'update 4' where id = 4;
948update id_ordered set name = 'update 5' where id = 5;
949select * from id_ordered;
950
951\set VERBOSITY terse \\ -- suppress cascade details
952drop table id cascade;
953\set VERBOSITY default
954
955--
956-- check corner case where an entirely-dummy subplan is created by
957-- constraint exclusion
958--
959
960create temp table t1 (a integer primary key);
961
962create temp table t1_1 (check (a >= 0 and a < 10)) inherits (t1);
963create temp table t1_2 (check (a >= 10 and a < 20)) inherits (t1);
964
965create rule t1_ins_1 as on insert to t1
966	where new.a >= 0 and new.a < 10
967	do instead
968	insert into t1_1 values (new.a);
969create rule t1_ins_2 as on insert to t1
970	where new.a >= 10 and new.a < 20
971	do instead
972	insert into t1_2 values (new.a);
973
974create rule t1_upd_1 as on update to t1
975	where old.a >= 0 and old.a < 10
976	do instead
977	update t1_1 set a = new.a where a = old.a;
978create rule t1_upd_2 as on update to t1
979	where old.a >= 10 and old.a < 20
980	do instead
981	update t1_2 set a = new.a where a = old.a;
982
983set constraint_exclusion = on;
984
985insert into t1 select * from generate_series(5,19,1) g;
986update t1 set a = 4 where a = 5;
987
988select * from only t1;
989select * from only t1_1;
990select * from only t1_2;
991
992reset constraint_exclusion;
993
994-- test FOR UPDATE in rules
995
996create table rules_base(f1 int, f2 int);
997insert into rules_base values(1,2), (11,12);
998create rule r1 as on update to rules_base do instead
999  select * from rules_base where f1 = 1 for update;
1000update rules_base set f2 = f2 + 1;
1001create or replace rule r1 as on update to rules_base do instead
1002  select * from rules_base where f1 = 11 for update of rules_base;
1003update rules_base set f2 = f2 + 1;
1004create or replace rule r1 as on update to rules_base do instead
1005  select * from rules_base where f1 = 11 for update of old; -- error
1006drop table rules_base;
1007
1008-- test various flavors of pg_get_viewdef()
1009
1010select pg_get_viewdef('shoe'::regclass) as unpretty;
1011select pg_get_viewdef('shoe'::regclass,true) as pretty;
1012select pg_get_viewdef('shoe'::regclass,0) as prettier;
1013
1014--
1015-- check multi-row VALUES in rules
1016--
1017
1018create table rules_src(f1 int, f2 int);
1019create table rules_log(f1 int, f2 int, tag text);
1020insert into rules_src values(1,2), (11,12);
1021create rule r1 as on update to rules_src do also
1022  insert into rules_log values(old.*, 'old'), (new.*, 'new');
1023update rules_src set f2 = f2 + 1;
1024update rules_src set f2 = f2 * 10;
1025select * from rules_src;
1026select * from rules_log;
1027create rule r2 as on update to rules_src do also
1028  values(old.*, 'old'), (new.*, 'new');
1029update rules_src set f2 = f2 / 10;
1030select * from rules_src;
1031select * from rules_log;
1032create rule r3 as on delete to rules_src do notify rules_src_deletion;
1033\d+ rules_src
1034
1035--
1036-- Ensure an aliased target relation for insert is correctly deparsed.
1037--
1038create rule r4 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
1039create rule r5 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
1040\d+ rules_src
1041
1042--
1043-- check alter rename rule
1044--
1045CREATE TABLE rule_t1 (a INT);
1046CREATE VIEW rule_v1 AS SELECT * FROM rule_t1;
1047
1048CREATE RULE InsertRule AS
1049    ON INSERT TO rule_v1
1050    DO INSTEAD
1051        INSERT INTO rule_t1 VALUES(new.a);
1052
1053ALTER RULE InsertRule ON rule_v1 RENAME to NewInsertRule;
1054
1055INSERT INTO rule_v1 VALUES(1);
1056SELECT * FROM rule_v1;
1057
1058\d+ rule_v1
1059
1060--
1061-- error conditions for alter rename rule
1062--
1063ALTER RULE InsertRule ON rule_v1 RENAME TO NewInsertRule; -- doesn't exist
1064ALTER RULE NewInsertRule ON rule_v1 RENAME TO "_RETURN"; -- already exists
1065ALTER RULE "_RETURN" ON rule_v1 RENAME TO abc; -- ON SELECT rule cannot be renamed
1066
1067DROP VIEW rule_v1;
1068DROP TABLE rule_t1;
1069
1070--
1071-- check display of VALUES in view definitions
1072--
1073create view rule_v1 as values(1,2);
1074\d+ rule_v1
1075alter table rule_v1 rename column column2 to q2;
1076\d+ rule_v1
1077drop view rule_v1;
1078create view rule_v1(x) as values(1,2);
1079\d+ rule_v1
1080drop view rule_v1;
1081create view rule_v1(x) as select * from (values(1,2)) v;
1082\d+ rule_v1
1083drop view rule_v1;
1084create view rule_v1(x) as select * from (values(1,2)) v(q,w);
1085\d+ rule_v1
1086drop view rule_v1;
1087
1088--
1089-- Check DO INSTEAD rules with ON CONFLICT
1090--
1091CREATE TABLE hats (
1092	hat_name    char(10) primary key,
1093	hat_color   char(10)      -- hat color
1094);
1095
1096CREATE TABLE hat_data (
1097	hat_name    char(10),
1098	hat_color   char(10)      -- hat color
1099);
1100create unique index hat_data_unique_idx
1101  on hat_data (hat_name COLLATE "C" bpchar_pattern_ops);
1102
1103-- DO NOTHING with ON CONFLICT
1104CREATE RULE hat_nosert AS ON INSERT TO hats
1105    DO INSTEAD
1106    INSERT INTO hat_data VALUES (
1107           NEW.hat_name,
1108           NEW.hat_color)
1109        ON CONFLICT (hat_name COLLATE "C" bpchar_pattern_ops) WHERE hat_color = 'green'
1110        DO NOTHING
1111        RETURNING *;
1112SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename;
1113
1114-- Works (projects row)
1115INSERT INTO hats VALUES ('h7', 'black') RETURNING *;
1116-- Works (does nothing)
1117INSERT INTO hats VALUES ('h7', 'black') RETURNING *;
1118SELECT tablename, rulename, definition FROM pg_rules
1119	WHERE tablename = 'hats';
1120DROP RULE hat_nosert ON hats;
1121
1122-- DO NOTHING without ON CONFLICT
1123CREATE RULE hat_nosert_all AS ON INSERT TO hats
1124    DO INSTEAD
1125    INSERT INTO hat_data VALUES (
1126           NEW.hat_name,
1127           NEW.hat_color)
1128        ON CONFLICT
1129        DO NOTHING
1130        RETURNING *;
1131SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename;
1132DROP RULE hat_nosert_all ON hats;
1133
1134-- Works (does nothing)
1135INSERT INTO hats VALUES ('h7', 'black') RETURNING *;
1136
1137-- DO UPDATE with a WHERE clause
1138CREATE RULE hat_upsert AS ON INSERT TO hats
1139    DO INSTEAD
1140    INSERT INTO hat_data VALUES (
1141           NEW.hat_name,
1142           NEW.hat_color)
1143        ON CONFLICT (hat_name)
1144        DO UPDATE
1145           SET hat_name = hat_data.hat_name, hat_color = excluded.hat_color
1146           WHERE excluded.hat_color <>  'forbidden' AND hat_data.* != excluded.*
1147        RETURNING *;
1148SELECT definition FROM pg_rules WHERE tablename = 'hats' ORDER BY rulename;
1149
1150-- Works (does upsert)
1151INSERT INTO hats VALUES ('h8', 'black') RETURNING *;
1152SELECT * FROM hat_data WHERE hat_name = 'h8';
1153INSERT INTO hats VALUES ('h8', 'white') RETURNING *;
1154SELECT * FROM hat_data WHERE hat_name = 'h8';
1155INSERT INTO hats VALUES ('h8', 'forbidden') RETURNING *;
1156SELECT * FROM hat_data WHERE hat_name = 'h8';
1157SELECT tablename, rulename, definition FROM pg_rules
1158	WHERE tablename = 'hats';
1159-- ensure explain works for on insert conflict rules
1160explain (costs off) INSERT INTO hats VALUES ('h8', 'forbidden') RETURNING *;
1161
1162-- ensure upserting into a rule, with a CTE (different offsets!) works
1163WITH data(hat_name, hat_color) AS (
1164    VALUES ('h8', 'green'),
1165        ('h9', 'blue'),
1166        ('h7', 'forbidden')
1167)
1168INSERT INTO hats
1169    SELECT * FROM data
1170RETURNING *;
1171EXPLAIN (costs off) WITH data(hat_name, hat_color) AS (
1172    VALUES ('h8', 'green'),
1173        ('h9', 'blue'),
1174        ('h7', 'forbidden')
1175)
1176INSERT INTO hats
1177    SELECT * FROM data
1178RETURNING *;
1179SELECT * FROM hat_data WHERE hat_name IN ('h8', 'h9', 'h7') ORDER BY hat_name;
1180
1181DROP RULE hat_upsert ON hats;
1182
1183drop table hats;
1184drop table hat_data;
1185
1186-- test for pg_get_functiondef properly regurgitating SET parameters
1187-- Note that the function is kept around to stress pg_dump.
1188CREATE FUNCTION func_with_set_params() RETURNS integer
1189    AS 'select 1;'
1190    LANGUAGE SQL
1191    SET search_path TO PG_CATALOG
1192    SET extra_float_digits TO 2
1193    SET work_mem TO '4MB'
1194    SET datestyle to iso, mdy
1195    SET local_preload_libraries TO "Mixed/Case", 'c:/''a"/path', '', '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'
1196    IMMUTABLE STRICT;
1197SELECT pg_get_functiondef('func_with_set_params()'::regprocedure);
1198
1199-- tests for pg_get_*def with invalid objects
1200SELECT pg_get_constraintdef(0);
1201SELECT pg_get_functiondef(0);
1202SELECT pg_get_indexdef(0);
1203SELECT pg_get_ruledef(0);
1204SELECT pg_get_statisticsobjdef(0);
1205SELECT pg_get_triggerdef(0);
1206SELECT pg_get_viewdef(0);
1207SELECT pg_get_function_arguments(0);
1208SELECT pg_get_function_identity_arguments(0);
1209SELECT pg_get_function_result(0);
1210SELECT pg_get_function_arg_default(0, 0);
1211SELECT pg_get_function_arg_default('pg_class'::regclass, 0);
1212SELECT pg_get_partkeydef(0);
1213
1214-- test rename for a rule defined on a partitioned table
1215CREATE TABLE parted_table (a int) PARTITION BY LIST (a);
1216CREATE TABLE parted_table_1 PARTITION OF parted_table FOR VALUES IN (1);
1217CREATE RULE parted_table_insert AS ON INSERT to parted_table
1218    DO INSTEAD INSERT INTO parted_table_1 VALUES (NEW.*);
1219ALTER RULE parted_table_insert ON parted_table RENAME TO parted_table_insert_redirect;
1220DROP TABLE parted_table;
1221