]> begriffs open source - ai-pg/blob - full-docs/txt/rules-update.txt
Convert HTML docs to more streamlined TXT
[ai-pg] / full-docs / txt / rules-update.txt
1
2 39.4. Rules on INSERT, UPDATE, and DELETE #
3
4    39.4.1. How Update Rules Work
5    39.4.2. Cooperation with Views
6
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.
15
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.
18
19 Caution
20
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
27    rules.
28
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.
35
36 39.4.1. How Update Rules Work #
37
38    Keep the syntax:
39 CREATE [ OR REPLACE ] RULE name AS ON event
40     TO table [ WHERE condition ]
41     DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) }
42
43    in mind. In the following, update rules means rules that are defined on
44    INSERT, UPDATE, or DELETE.
45
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).
53
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
58    meaning).
59
60    So we have three cases that produce the following query trees for a
61    one-action rule.
62
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
66
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
70
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
75
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.
80
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.
88
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
95    error.)
96
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.
106
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
110    rewriting.
111
112 39.4.1.1. A First Rule Step by Step #
113
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
117    shoelace_data.
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
123 );
124
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 (
128                                     NEW.sl_name,
129                                     NEW.sl_avail,
130                                     current_user,
131                                     current_timestamp
132                                 );
133
134    Now someone does:
135 UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7';
136
137    and we look at the log table:
138 SELECT * FROM shoelace_log;
139
140  sl_name | sl_avail | log_who | log_when
141 ---------+----------+---------+----------------------------------
142  sl7     |        6 | Al      | Tue Oct 20 16:14:45 1998 MET DST
143 (1 row)
144
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';
150
151    There is a rule log_shoelace that is ON UPDATE with the rule
152    qualification expression:
153 NEW.sl_avail <> OLD.sl_avail
154
155    and the action:
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;
160
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.)
166
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;
176
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;
185
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
189    ... SELECT.)
190
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';
201
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
204    result relation:
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';
212
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';
221
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 )
228   FROM shoelace_data
229  WHERE 6 <> shoelace_data.sl_avail
230    AND shoelace_data.sl_name = 'sl7';
231
232 UPDATE shoelace_data SET sl_avail = 6
233  WHERE sl_name = 'sl7';
234
235    These are executed in this order, and that is exactly what the rule was
236    meant to do.
237
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';
242
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 )
250   FROM shoelace_data
251  WHERE shoelace_data.sl_avail <> shoelace_data.sl_avail
252    AND shoelace_data.sl_name = 'sl7';
253
254    and that qualification will never be true.
255
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';
260
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
267   FROM shoelace_data
268  WHERE 0 <> shoelace_data.sl_avail
269    AND shoelace_data.sl_color = 'black';
270
271    being generated by the rule. This query tree will surely insert three
272    new log entries. And that's absolutely correct.
273
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.
278
279 39.4.2. Cooperation with Views #
280
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
285     DO INSTEAD NOTHING;
286 CREATE RULE shoe_upd_protect AS ON UPDATE TO shoe
287     DO INSTEAD NOTHING;
288 CREATE RULE shoe_del_protect AS ON DELETE TO shoe
289     DO INSTEAD NOTHING;
290
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.
296
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
300    rules:
301 CREATE RULE shoelace_ins AS ON INSERT TO shoelace
302     DO INSTEAD
303     INSERT INTO shoelace_data VALUES (
304            NEW.sl_name,
305            NEW.sl_avail,
306            NEW.sl_color,
307            NEW.sl_len,
308            NEW.sl_unit
309     );
310
311 CREATE RULE shoelace_upd AS ON UPDATE TO shoelace
312     DO INSTEAD
313     UPDATE shoelace_data
314        SET sl_name = NEW.sl_name,
315            sl_avail = NEW.sl_avail,
316            sl_color = NEW.sl_color,
317            sl_len = NEW.sl_len,
318            sl_unit = NEW.sl_unit
319      WHERE sl_name = OLD.sl_name;
320
321 CREATE RULE shoelace_del AS ON DELETE TO shoelace
322     DO INSTEAD
323     DELETE FROM shoelace_data
324      WHERE sl_name = OLD.sl_name;
325
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
330    is:
331 CREATE RULE shoelace_ins AS ON INSERT TO shoelace
332     DO INSTEAD
333     INSERT INTO shoelace_data VALUES (
334            NEW.sl_name,
335            NEW.sl_avail,
336            NEW.sl_color,
337            NEW.sl_len,
338            NEW.sl_unit
339     )
340     RETURNING
341            shoelace_data.*,
342            (SELECT shoelace_data.sl_len * u.un_fact
343             FROM unit u WHERE shoelace_data.sl_unit = u.un_name);
344
345    Note that this one rule supports both INSERT and INSERT RETURNING
346    queries on the view — the RETURNING clause is simply ignored for
347    INSERT.
348
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.
360
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 (
367     arr_name    text,
368     arr_quant   integer
369 );
370
371 CREATE TABLE shoelace_ok (
372     ok_name     text,
373     ok_quant    integer
374 );
375
376 CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok
377     DO INSTEAD
378     UPDATE shoelace
379        SET sl_avail = sl_avail + NEW.ok_quant
380      WHERE sl_name = NEW.ok_name;
381
382    Now you can fill the table shoelace_arrive with the data from the parts
383    list:
384 SELECT * FROM shoelace_arrive;
385
386  arr_name | arr_quant
387 ----------+-----------
388  sl3      |        10
389  sl6      |        20
390  sl8      |        20
391 (3 rows)
392
393    Take a quick look at the current data:
394 SELECT * FROM shoelace;
395
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
406 (8 rows)
407
408    Now move the arrived shoelaces in:
409 INSERT INTO shoelace_ok SELECT * FROM shoelace_arrive;
410
411    and check the results:
412 SELECT * FROM shoelace ORDER BY sl_name;
413
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
424 (8 rows)
425
426 SELECT * FROM shoelace_log;
427
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
434 (4 rows)
435
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;
442
443    Now the first rule shoelace_ok_ins is applied and turns this into:
444 UPDATE shoelace
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,
448        shoelace shoelace
449  WHERE shoelace.sl_name = shoelace_arrive.arr_name;
450
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:
454 UPDATE shoelace_data
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;
466
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
470    on it, and we get:
471 UPDATE shoelace_data
472    SET sl_name = s.sl_name,
473        sl_avail = s.sl_avail + shoelace_arrive.arr_quant,
474        sl_color = s.sl_color,
475        sl_len = s.sl_len,
476        sl_unit = s.sl_unit
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;
485
486    Finally, the rule log_shoelace gets applied, producing the extra query
487    tree:
488 INSERT INTO shoelace_log
489 SELECT s.sl_name,
490        s.sl_avail + shoelace_arrive.arr_quant,
491        current_user,
492        current_timestamp
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;
504
505    After that the rule system runs out of rules and returns the generated
506    query trees.
507
508    So we end up with two final query trees that are equivalent to the SQL
509    statements:
510 INSERT INTO shoelace_log
511 SELECT s.sl_name,
512        s.sl_avail + shoelace_arrive.arr_quant,
513        current_user,
514        current_timestamp
515   FROM shoelace_arrive shoelace_arrive, shoelace_data shoelace_data,
516        shoelace_data s
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;
520
521 UPDATE shoelace_data
522    SET sl_avail = shoelace_data.sl_avail + shoelace_arrive.arr_quant
523   FROM shoelace_arrive shoelace_arrive,
524        shoelace_data shoelace_data,
525        shoelace_data s
526  WHERE s.sl_name = shoelace_arrive.sl_name
527    AND shoelace_data.sl_name = s.sl_name;
528
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.
532
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
537    INSERT will be
538 Nested Loop
539   ->  Merge Join
540         ->  Seq Scan
541               ->  Sort
542                     ->  Seq Scan on s
543         ->  Seq Scan
544               ->  Sort
545                     ->  Seq Scan on shoelace_arrive
546   ->  Seq Scan on shoelace_data
547
548    while omitting the extra range table entry would result in a
549 Merge Join
550   ->  Seq Scan
551         ->  Sort
552               ->  Seq Scan on s
553   ->  Seq Scan
554         ->  Sort
555               ->  Seq Scan on shoelace_arrive
556
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
561    at all.
562
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
565    database:
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);
568
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);
574
575    Its output is:
576 SELECT * FROM shoelace_mismatch;
577
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
582
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
586    view:
587 CREATE VIEW shoelace_can_delete AS
588     SELECT * FROM shoelace_mismatch WHERE sl_avail = 0;
589
590    and do it this way:
591 DELETE FROM shoelace WHERE EXISTS
592     (SELECT * FROM shoelace_can_delete
593              WHERE sl_name = shoelace.sl_name);
594
595    The results are:
596 SELECT * FROM shoelace;
597
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
609 (9 rows)
610
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.
616
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
619    it works.