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
WHERE new.sl_avail <> old.sl_avail
<emphasis>AND shoelace_data.sl_name = 'sl7'</emphasis>;
</programlisting>
</para>
<para>
Step 4 replaces references to <literal>NEW</literal> by the target list entries from the
original query tree or by the matching variable references
from the result relation:
<programlisting>
INSERT INTO shoelace_log VALUES (
<emphasis>shoelace_data.sl_name</emphasis>, <emphasis>6</emphasis>,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old,
shoelace_data shoelace_data
WHERE <emphasis>6</emphasis> <> old.sl_avail
AND shoelace_data.sl_name = 'sl7';
</programlisting>
</para>
<para>
Step 5 changes <literal>OLD</literal> references into result relation references:
<programlisting>
INSERT INTO shoelace_log VALUES (
shoelace_data.sl_name, 6,
current_user, current_timestamp )
FROM shoelace_data new, shoelace_data old,
shoelace_data shoelace_data
WHERE 6 <> <emphasis>shoelace_data.sl_avail</emphasis>
AND shoelace_data.sl_name = 'sl7';
</programlisting>
</para>
<para>
That's it. Since the rule is <literal>ALSO</literal>, we also output the
original query tree. In short, the output from the rule system
is a list of two query trees that correspond to these statements:
<programlisting>
INSERT INTO shoelace_log VALUES (
shoelace_data.sl_name, 6,
current_user, current_timestamp )
FROM shoelace_data
WHERE 6 <> shoelace_data.sl_avail
AND shoelace_data.sl_name = 'sl7';
UPDATE shoelace_data SET sl_avail = 6
WHERE sl_name = 'sl7';
</programlisting>
These are executed in this order, and that is exactly what
the rule was meant to do.
</para>
<para>
The substitutions and the added qualifications
ensure that, if the original query would be, say:
<programlisting>
UPDATE shoelace_data SET sl_color = 'green'
WHERE sl_name = 'sl7';
</programlisting>
no log entry would get written. In that case, the original query
tree does not contain a target list entry for
<literal>sl_avail</literal>, so <literal>NEW.sl_avail</literal> will get
replaced by <literal>shoelace_data.sl_avail</literal>. Thus, the extra
command generated by the rule is:
<programlisting>
INSERT INTO shoelace_log VALUES (
shoelace_data.sl_name, <emphasis>shoelace_data.sl_avail</emphasis>,
current_user, current_timestamp )
FROM shoelace_data
WHERE <emphasis>shoelace_data.sl_avail</emphasis> <> shoelace_data.sl_avail
AND shoelace_data.sl_name = 'sl7';
</programlisting>
and that qualification will never be true.
</para>
<para>
It will also work if the original query modifies multiple rows. So
if someone issued the command:
<programlisting>
UPDATE shoelace_data SET sl_avail = 0
WHERE sl_color = 'black';
</programlisting>
four rows in fact get updated (<literal>sl1</literal>, <literal>sl2</literal>, <literal>sl3</literal>,