2 39.4. Rules on INSERT, UPDATE, and DELETE #
4 39.4.1. How Update Rules Work
5 39.4.2. Cooperation with Views
7 Rules that are defined on INSERT, UPDATE, and DELETE are significantly
8 different from the view rules described in the previous sections.
9 First, their CREATE RULE command allows more:
10 * They are allowed to have no action.
11 * They can have multiple actions.
12 * They can be INSTEAD or ALSO (the default).
13 * The pseudorelations NEW and OLD become useful.
14 * They can have rule qualifications.
16 Second, they don't modify the query tree in place. Instead they create
17 zero or more new query trees and can throw away the original one.
21 In many cases, tasks that could be performed by rules on
22 INSERT/UPDATE/DELETE are better done with triggers. Triggers are
23 notationally a bit more complicated, but their semantics are much
24 simpler to understand. Rules tend to have surprising results when the
25 original query contains volatile functions: volatile functions may get
26 executed more times than expected in the process of carrying out the
29 Also, there are some cases that are not supported by these types of
30 rules at all, notably including WITH clauses in the original query and
31 multiple-assignment sub-SELECTs in the SET list of UPDATE queries. This
32 is because copying these constructs into a rule query would result in
33 multiple evaluations of the sub-query, contrary to the express intent
34 of the query's author.
36 39.4.1. How Update Rules Work #
39 CREATE [ OR REPLACE ] RULE name AS ON event
40 TO table [ WHERE condition ]
41 DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) }
43 in mind. In the following, update rules means rules that are defined on
44 INSERT, UPDATE, or DELETE.
46 Update rules get applied by the rule system when the result relation
47 and the command type of a query tree are equal to the object and event
48 given in the CREATE RULE command. For update rules, the rule system
49 creates a list of query trees. Initially the query-tree list is empty.
50 There can be zero (NOTHING key word), one, or multiple actions. To
51 simplify, we will look at a rule with one action. This rule can have a
52 qualification or not and it can be INSTEAD or ALSO (the default).
54 What is a rule qualification? It is a restriction that tells when the
55 actions of the rule should be done and when not. This qualification can
56 only reference the pseudorelations NEW and/or OLD, which basically
57 represent the relation that was given as object (but with a special
60 So we have three cases that produce the following query trees for a
63 No qualification, with either ALSO or INSTEAD
64 the query tree from the rule action with the original query
65 tree's qualification added
67 Qualification given and ALSO
68 the query tree from the rule action with the rule qualification
69 and the original query tree's qualification added
71 Qualification given and INSTEAD
72 the query tree from the rule action with the rule qualification
73 and the original query tree's qualification; and the original
74 query tree with the negated rule qualification added
76 Finally, if the rule is ALSO, the unchanged original query tree is
77 added to the list. Since only qualified INSTEAD rules already add the
78 original query tree, we end up with either one or two output query
79 trees for a rule with one action.
81 For ON INSERT rules, the original query (if not suppressed by INSTEAD)
82 is done before any actions added by rules. This allows the actions to
83 see the inserted row(s). But for ON UPDATE and ON DELETE rules, the
84 original query is done after the actions added by rules. This ensures
85 that the actions can see the to-be-updated or to-be-deleted rows;
86 otherwise, the actions might do nothing because they find no rows
87 matching their qualifications.
89 The query trees generated from rule actions are thrown into the rewrite
90 system again, and maybe more rules get applied resulting in additional
91 or fewer query trees. So a rule's actions must have either a different
92 command type or a different result relation than the rule itself is on,
93 otherwise this recursive process will end up in an infinite loop.
94 (Recursive expansion of a rule will be detected and reported as an
97 The query trees found in the actions of the pg_rewrite system catalog
98 are only templates. Since they can reference the range-table entries
99 for NEW and OLD, some substitutions have to be made before they can be
100 used. For any reference to NEW, the target list of the original query
101 is searched for a corresponding entry. If found, that entry's
102 expression replaces the reference. Otherwise, NEW means the same as OLD
103 (for an UPDATE) or is replaced by a null value (for an INSERT). Any
104 reference to OLD is replaced by a reference to the range-table entry
105 that is the result relation.
107 After the system is done applying update rules, it applies view rules
108 to the produced query tree(s). Views cannot insert new update actions
109 so there is no need to apply update rules to the output of view
112 39.4.1.1. A First Rule Step by Step #
114 Say we want to trace changes to the sl_avail column in the
115 shoelace_data relation. So we set up a log table and a rule that
116 conditionally writes a log entry when an UPDATE is performed on
118 CREATE TABLE shoelace_log (
119 sl_name text, -- shoelace changed
120 sl_avail integer, -- new available value
121 log_who text, -- who did it
122 log_when timestamp -- when
125 CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data
126 WHERE NEW.sl_avail <> OLD.sl_avail
127 DO INSERT INTO shoelace_log VALUES (
135 UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7';
137 and we look at the log table:
138 SELECT * FROM shoelace_log;
140 sl_name | sl_avail | log_who | log_when
141 ---------+----------+---------+----------------------------------
142 sl7 | 6 | Al | Tue Oct 20 16:14:45 1998 MET DST
145 That's what we expected. What happened in the background is the
146 following. The parser created the query tree:
147 UPDATE shoelace_data SET sl_avail = 6
148 FROM shoelace_data shoelace_data
149 WHERE shoelace_data.sl_name = 'sl7';
151 There is a rule log_shoelace that is ON UPDATE with the rule
152 qualification expression:
153 NEW.sl_avail <> OLD.sl_avail
156 INSERT INTO shoelace_log VALUES (
157 new.sl_name, new.sl_avail,
158 current_user, current_timestamp )
159 FROM shoelace_data new, shoelace_data old;
161 (This looks a little strange since you cannot normally write INSERT ...
162 VALUES ... FROM. The FROM clause here is just to indicate that there
163 are range-table entries in the query tree for new and old. These are
164 needed so that they can be referenced by variables in the INSERT
165 command's query tree.)
167 The rule is a qualified ALSO rule, so the rule system has to return two
168 query trees: the modified rule action and the original query tree. In
169 step 1, the range table of the original query is incorporated into the
170 rule's action query tree. This results in:
171 INSERT INTO shoelace_log VALUES (
172 new.sl_name, new.sl_avail,
173 current_user, current_timestamp )
174 FROM shoelace_data new, shoelace_data old,
175 shoelace_data shoelace_data;
177 In step 2, the rule qualification is added to it, so the result set is
178 restricted to rows where sl_avail changes:
179 INSERT INTO shoelace_log VALUES (
180 new.sl_name, new.sl_avail,
181 current_user, current_timestamp )
182 FROM shoelace_data new, shoelace_data old,
183 shoelace_data shoelace_data
184 WHERE new.sl_avail <> old.sl_avail;
186 (This looks even stranger, since INSERT ... VALUES doesn't have a WHERE
187 clause either, but the planner and executor will have no difficulty
188 with it. They need to support this same functionality anyway for INSERT
191 In step 3, the original query tree's qualification is added,
192 restricting the result set further to only the rows that would have
193 been touched by the original query:
194 INSERT INTO shoelace_log VALUES (
195 new.sl_name, new.sl_avail,
196 current_user, current_timestamp )
197 FROM shoelace_data new, shoelace_data old,
198 shoelace_data shoelace_data
199 WHERE new.sl_avail <> old.sl_avail
200 AND shoelace_data.sl_name = 'sl7';
202 Step 4 replaces references to NEW by the target list entries from the
203 original query tree or by the matching variable references from the
205 INSERT INTO shoelace_log VALUES (
206 shoelace_data.sl_name, 6,
207 current_user, current_timestamp )
208 FROM shoelace_data new, shoelace_data old,
209 shoelace_data shoelace_data
210 WHERE 6 <> old.sl_avail
211 AND shoelace_data.sl_name = 'sl7';
213 Step 5 changes OLD references into result relation references:
214 INSERT INTO shoelace_log VALUES (
215 shoelace_data.sl_name, 6,
216 current_user, current_timestamp )
217 FROM shoelace_data new, shoelace_data old,
218 shoelace_data shoelace_data
219 WHERE 6 <> shoelace_data.sl_avail
220 AND shoelace_data.sl_name = 'sl7';
222 That's it. Since the rule is ALSO, we also output the original query
223 tree. In short, the output from the rule system is a list of two query
224 trees that correspond to these statements:
225 INSERT INTO shoelace_log VALUES (
226 shoelace_data.sl_name, 6,
227 current_user, current_timestamp )
229 WHERE 6 <> shoelace_data.sl_avail
230 AND shoelace_data.sl_name = 'sl7';
232 UPDATE shoelace_data SET sl_avail = 6
233 WHERE sl_name = 'sl7';
235 These are executed in this order, and that is exactly what the rule was
238 The substitutions and the added qualifications ensure that, if the
239 original query would be, say:
240 UPDATE shoelace_data SET sl_color = 'green'
241 WHERE sl_name = 'sl7';
243 no log entry would get written. In that case, the original query tree
244 does not contain a target list entry for sl_avail, so NEW.sl_avail will
245 get replaced by shoelace_data.sl_avail. Thus, the extra command
246 generated by the rule is:
247 INSERT INTO shoelace_log VALUES (
248 shoelace_data.sl_name, shoelace_data.sl_avail,
249 current_user, current_timestamp )
251 WHERE shoelace_data.sl_avail <> shoelace_data.sl_avail
252 AND shoelace_data.sl_name = 'sl7';
254 and that qualification will never be true.
256 It will also work if the original query modifies multiple rows. So if
257 someone issued the command:
258 UPDATE shoelace_data SET sl_avail = 0
259 WHERE sl_color = 'black';
261 four rows in fact get updated (sl1, sl2, sl3, and sl4). But sl3 already
262 has sl_avail = 0. In this case, the original query trees qualification
263 is different and that results in the extra query tree:
264 INSERT INTO shoelace_log
265 SELECT shoelace_data.sl_name, 0,
266 current_user, current_timestamp
268 WHERE 0 <> shoelace_data.sl_avail
269 AND shoelace_data.sl_color = 'black';
271 being generated by the rule. This query tree will surely insert three
272 new log entries. And that's absolutely correct.
274 Here we can see why it is important that the original query tree is
275 executed last. If the UPDATE had been executed first, all the rows
276 would have already been set to zero, so the logging INSERT would not
277 find any row where 0 <> shoelace_data.sl_avail.
279 39.4.2. Cooperation with Views #
281 A simple way to protect view relations from the mentioned possibility
282 that someone can try to run INSERT, UPDATE, or DELETE on them is to let
283 those query trees get thrown away. So we could create the rules:
284 CREATE RULE shoe_ins_protect AS ON INSERT TO shoe
286 CREATE RULE shoe_upd_protect AS ON UPDATE TO shoe
288 CREATE RULE shoe_del_protect AS ON DELETE TO shoe
291 If someone now tries to do any of these operations on the view relation
292 shoe, the rule system will apply these rules. Since the rules have no
293 actions and are INSTEAD, the resulting list of query trees will be
294 empty and the whole query will become nothing because there is nothing
295 left to be optimized or executed after the rule system is done with it.
297 A more sophisticated way to use the rule system is to create rules that
298 rewrite the query tree into one that does the right operation on the
299 real tables. To do that on the shoelace view, we create the following
301 CREATE RULE shoelace_ins AS ON INSERT TO shoelace
303 INSERT INTO shoelace_data VALUES (
311 CREATE RULE shoelace_upd AS ON UPDATE TO shoelace
314 SET sl_name = NEW.sl_name,
315 sl_avail = NEW.sl_avail,
316 sl_color = NEW.sl_color,
318 sl_unit = NEW.sl_unit
319 WHERE sl_name = OLD.sl_name;
321 CREATE RULE shoelace_del AS ON DELETE TO shoelace
323 DELETE FROM shoelace_data
324 WHERE sl_name = OLD.sl_name;
326 If you want to support RETURNING queries on the view, you need to make
327 the rules include RETURNING clauses that compute the view rows. This is
328 usually pretty trivial for views on a single table, but it's a bit
329 tedious for join views such as shoelace. An example for the insert case
331 CREATE RULE shoelace_ins AS ON INSERT TO shoelace
333 INSERT INTO shoelace_data VALUES (
342 (SELECT shoelace_data.sl_len * u.un_fact
343 FROM unit u WHERE shoelace_data.sl_unit = u.un_name);
345 Note that this one rule supports both INSERT and INSERT RETURNING
346 queries on the view — the RETURNING clause is simply ignored for
349 Note that in the RETURNING clause of a rule, OLD and NEW refer to the
350 pseudorelations added as extra range table entries to the rewritten
351 query, rather than old/new rows in the result relation. Thus, for
352 example, in a rule supporting UPDATE queries on this view, if the
353 RETURNING clause contained old.sl_name, the old name would always be
354 returned, regardless of whether the RETURNING clause in the query on
355 the view specified OLD or NEW, which might be confusing. To avoid this
356 confusion, and support returning old and new values in queries on the
357 view, the RETURNING clause in the rule definition should refer to
358 entries from the result relation such as shoelace_data.sl_name, without
359 specifying OLD or NEW.
361 Now assume that once in a while, a pack of shoelaces arrives at the
362 shop and a big parts list along with it. But you don't want to manually
363 update the shoelace view every time. Instead we set up two little
364 tables: one where you can insert the items from the part list, and one
365 with a special trick. The creation commands for these are:
366 CREATE TABLE shoelace_arrive (
371 CREATE TABLE shoelace_ok (
376 CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok
379 SET sl_avail = sl_avail + NEW.ok_quant
380 WHERE sl_name = NEW.ok_name;
382 Now you can fill the table shoelace_arrive with the data from the parts
384 SELECT * FROM shoelace_arrive;
387 ----------+-----------
393 Take a quick look at the current data:
394 SELECT * FROM shoelace;
396 sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm
397 ----------+----------+----------+--------+---------+-----------
398 sl1 | 5 | black | 80 | cm | 80
399 sl2 | 6 | black | 100 | cm | 100
400 sl7 | 6 | brown | 60 | cm | 60
401 sl3 | 0 | black | 35 | inch | 88.9
402 sl4 | 8 | black | 40 | inch | 101.6
403 sl8 | 1 | brown | 40 | inch | 101.6
404 sl5 | 4 | brown | 1 | m | 100
405 sl6 | 0 | brown | 0.9 | m | 90
408 Now move the arrived shoelaces in:
409 INSERT INTO shoelace_ok SELECT * FROM shoelace_arrive;
411 and check the results:
412 SELECT * FROM shoelace ORDER BY sl_name;
414 sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm
415 ----------+----------+----------+--------+---------+-----------
416 sl1 | 5 | black | 80 | cm | 80
417 sl2 | 6 | black | 100 | cm | 100
418 sl7 | 6 | brown | 60 | cm | 60
419 sl4 | 8 | black | 40 | inch | 101.6
420 sl3 | 10 | black | 35 | inch | 88.9
421 sl8 | 21 | brown | 40 | inch | 101.6
422 sl5 | 4 | brown | 1 | m | 100
423 sl6 | 20 | brown | 0.9 | m | 90
426 SELECT * FROM shoelace_log;
428 sl_name | sl_avail | log_who| log_when
429 ---------+----------+--------+----------------------------------
430 sl7 | 6 | Al | Tue Oct 20 19:14:45 1998 MET DST
431 sl3 | 10 | Al | Tue Oct 20 19:25:16 1998 MET DST
432 sl6 | 20 | Al | Tue Oct 20 19:25:16 1998 MET DST
433 sl8 | 21 | Al | Tue Oct 20 19:25:16 1998 MET DST
436 It's a long way from the one INSERT ... SELECT to these results. And
437 the description of the query-tree transformation will be the last in
438 this chapter. First, there is the parser's output:
439 INSERT INTO shoelace_ok
440 SELECT shoelace_arrive.arr_name, shoelace_arrive.arr_quant
441 FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok;
443 Now the first rule shoelace_ok_ins is applied and turns this into:
445 SET sl_avail = shoelace.sl_avail + shoelace_arrive.arr_quant
446 FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
447 shoelace_ok old, shoelace_ok new,
449 WHERE shoelace.sl_name = shoelace_arrive.arr_name;
451 and throws away the original INSERT on shoelace_ok. This rewritten
452 query is passed to the rule system again, and the second applied rule
453 shoelace_upd produces:
455 SET sl_name = shoelace.sl_name,
456 sl_avail = shoelace.sl_avail + shoelace_arrive.arr_quant,
457 sl_color = shoelace.sl_color,
458 sl_len = shoelace.sl_len,
459 sl_unit = shoelace.sl_unit
460 FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
461 shoelace_ok old, shoelace_ok new,
462 shoelace shoelace, shoelace old,
463 shoelace new, shoelace_data shoelace_data
464 WHERE shoelace.sl_name = shoelace_arrive.arr_name
465 AND shoelace_data.sl_name = shoelace.sl_name;
467 Again it's an INSTEAD rule and the previous query tree is trashed. Note
468 that this query still uses the view shoelace. But the rule system isn't
469 finished with this step, so it continues and applies the _RETURN rule
472 SET sl_name = s.sl_name,
473 sl_avail = s.sl_avail + shoelace_arrive.arr_quant,
474 sl_color = s.sl_color,
477 FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
478 shoelace_ok old, shoelace_ok new,
479 shoelace shoelace, shoelace old,
480 shoelace new, shoelace_data shoelace_data,
481 shoelace old, shoelace new,
482 shoelace_data s, unit u
483 WHERE s.sl_name = shoelace_arrive.arr_name
484 AND shoelace_data.sl_name = s.sl_name;
486 Finally, the rule log_shoelace gets applied, producing the extra query
488 INSERT INTO shoelace_log
490 s.sl_avail + shoelace_arrive.arr_quant,
493 FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
494 shoelace_ok old, shoelace_ok new,
495 shoelace shoelace, shoelace old,
496 shoelace new, shoelace_data shoelace_data,
497 shoelace old, shoelace new,
498 shoelace_data s, unit u,
499 shoelace_data old, shoelace_data new
500 shoelace_log shoelace_log
501 WHERE s.sl_name = shoelace_arrive.arr_name
502 AND shoelace_data.sl_name = s.sl_name
503 AND (s.sl_avail + shoelace_arrive.arr_quant) <> s.sl_avail;
505 After that the rule system runs out of rules and returns the generated
508 So we end up with two final query trees that are equivalent to the SQL
510 INSERT INTO shoelace_log
512 s.sl_avail + shoelace_arrive.arr_quant,
515 FROM shoelace_arrive shoelace_arrive, shoelace_data shoelace_data,
517 WHERE s.sl_name = shoelace_arrive.arr_name
518 AND shoelace_data.sl_name = s.sl_name
519 AND s.sl_avail + shoelace_arrive.arr_quant <> s.sl_avail;
522 SET sl_avail = shoelace_data.sl_avail + shoelace_arrive.arr_quant
523 FROM shoelace_arrive shoelace_arrive,
524 shoelace_data shoelace_data,
526 WHERE s.sl_name = shoelace_arrive.sl_name
527 AND shoelace_data.sl_name = s.sl_name;
529 The result is that data coming from one relation inserted into another,
530 changed into updates on a third, changed into updating a fourth plus
531 logging that final update in a fifth gets reduced into two queries.
533 There is a little detail that's a bit ugly. Looking at the two queries,
534 it turns out that the shoelace_data relation appears twice in the range
535 table where it could definitely be reduced to one. The planner does not
536 handle it and so the execution plan for the rule systems output of the
545 -> Seq Scan on shoelace_arrive
546 -> Seq Scan on shoelace_data
548 while omitting the extra range table entry would result in a
555 -> Seq Scan on shoelace_arrive
557 which produces exactly the same entries in the log table. Thus, the
558 rule system caused one extra scan on the table shoelace_data that is
559 absolutely not necessary. And the same redundant scan is done once more
560 in the UPDATE. But it was a really hard job to make that all possible
563 Now we make a final demonstration of the PostgreSQL rule system and its
564 power. Say you add some shoelaces with extraordinary colors to your
566 INSERT INTO shoelace VALUES ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
567 INSERT INTO shoelace VALUES ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);
569 We would like to make a view to check which shoelace entries do not fit
570 any shoe in color. The view for this is:
571 CREATE VIEW shoelace_mismatch AS
572 SELECT * FROM shoelace WHERE NOT EXISTS
573 (SELECT shoename FROM shoe WHERE slcolor = sl_color);
576 SELECT * FROM shoelace_mismatch;
578 sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm
579 ---------+----------+----------+--------+---------+-----------
580 sl9 | 0 | pink | 35 | inch | 88.9
581 sl10 | 1000 | magenta | 40 | inch | 101.6
583 Now we want to set it up so that mismatching shoelaces that are not in
584 stock are deleted from the database. To make it a little harder for
585 PostgreSQL, we don't delete it directly. Instead we create one more
587 CREATE VIEW shoelace_can_delete AS
588 SELECT * FROM shoelace_mismatch WHERE sl_avail = 0;
591 DELETE FROM shoelace WHERE EXISTS
592 (SELECT * FROM shoelace_can_delete
593 WHERE sl_name = shoelace.sl_name);
596 SELECT * FROM shoelace;
598 sl_name | sl_avail | sl_color | sl_len | sl_unit | sl_len_cm
599 ---------+----------+----------+--------+---------+-----------
600 sl1 | 5 | black | 80 | cm | 80
601 sl2 | 6 | black | 100 | cm | 100
602 sl7 | 6 | brown | 60 | cm | 60
603 sl4 | 8 | black | 40 | inch | 101.6
604 sl3 | 10 | black | 35 | inch | 88.9
605 sl8 | 21 | brown | 40 | inch | 101.6
606 sl10 | 1000 | magenta | 40 | inch | 101.6
607 sl5 | 4 | brown | 1 | m | 100
608 sl6 | 20 | brown | 0.9 | m | 90
611 A DELETE on a view, with a subquery qualification that in total uses 4
612 nesting/joined views, where one of them itself has a subquery
613 qualification containing a view and where calculated view columns are
614 used, gets rewritten into one single query tree that deletes the
615 requested data from a real table.
617 There are probably only a few situations out in the real world where
618 such a construct is necessary. But it makes you feel comfortable that