and a rule that conditionally writes a log entry when an
<command>UPDATE</command> is performed on
<literal>shoelace_data</literal>.
<programlisting>
CREATE TABLE shoelace_log (
sl_name text, -- shoelace changed
sl_avail integer, -- new available value
log_who text, -- who did it
log_when timestamp -- when
);
CREATE RULE log_shoelace AS ON UPDATE TO shoelace_data
WHERE NEW.sl_avail <> OLD.sl_avail
DO INSERT INTO shoelace_log VALUES (
NEW.sl_name,
NEW.sl_avail,
current_user,
current_timestamp
);
</programlisting>
</para>
<para>
Now someone does:
<programlisting>
UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7';
</programlisting>
and we look at the log table:
<programlisting>
SELECT * FROM shoelace_log;
sl_name | sl_avail | log_who | log_when
---------+----------+---------+----------------------------------
sl7 | 6 | Al | Tue Oct 20 16:14:45 1998 MET DST
(1 row)
</programlisting>
</para>
<para>
That's what we expected. What happened in the background is the following.
The parser created the query tree:
<programlisting>
UPDATE shoelace_data SET sl_avail = 6
FROM shoelace_data shoelace_data
WHERE shoelace_data.sl_name = 'sl7';
</programlisting>
There is a rule <literal>log_shoelace</literal> that is <literal>ON UPDATE</literal> with the rule
qualification expression:
<programlisting>
NEW.sl_avail <> OLD.sl_avail
</programlisting>
and the action:
<programlisting>
INSERT INTO shoelace_log VALUES (
new.sl_name, new.sl_avail,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old;
</programlisting>
(This looks a little strange since you cannot normally write
<literal>INSERT ... VALUES ... FROM</literal>. The <literal>FROM</literal>
clause here is just to indicate that there are range-table entries
in the query tree for <literal>new</literal> and <literal>old</literal>.
These are needed so that they can be referenced by variables in
the <command>INSERT</command> command's query tree.)
</para>
<para>
The rule is a qualified <literal>ALSO</literal> rule, so the rule system
has to return two query trees: the modified rule action and the original
query tree. In step 1, the range table of the original query is
incorporated into the rule's action query tree. This results in:
<programlisting>
INSERT INTO shoelace_log VALUES (
new.sl_name, new.sl_avail,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old,
<emphasis>shoelace_data shoelace_data</emphasis>;
</programlisting>
In step 2, the rule qualification is added to it, so the result set
is restricted to rows where <literal>sl_avail</literal> changes:
<programlisting>
INSERT INTO shoelace_log VALUES (
new.sl_name, new.sl_avail,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old,
shoelace_data shoelace_data
<emphasis>WHERE new.sl_avail <> old.sl_avail</emphasis>;
</programlisting>
(This looks even stranger, since <literal>INSERT ... VALUES</literal> doesn't have
a <literal>WHERE</literal> clause either, but the planner and executor will have no
difficulty with it. They need to support this same functionality
anyway for <literal>INSERT ... SELECT</literal>.)
</para>
<para>
In step 3, the original query tree's qualification is added,
restricting the result set further to only the rows that would have been touched
by the original query:
<programlisting>
INSERT INTO shoelace_log VALUES (
new.sl_name, new.sl_avail,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old,
shoelace_data shoelace_data