# 41.4. Rules onINSERT
,UPDATE
, andDELETE
41.4.2. Cooperation with Views
Rules that are defined onINSERT
,UPDATE
, andDELETE
are significantly different from the view rules described in the previous section. First, theirCREATE RULE
command allows more:
They are allowed to have no action.
They can have multiple actions.
They can be
INSTEAD
orALSO
(the default).The pseudorelations
NEW
andOLD
become useful.They can have rule qualifications.
Second, they don't modify the query tree in place. Instead they create zero or more new query trees and can throw away the original one.
# Caution
In many cases, tasks that could be performed by rules onINSERT
/UPDATE
/DELETE
are better done with triggers. Triggers are notationally a bit more complicated, but their semantics are much simpler to understand. Rules tend to have surprising results when the original query contains volatile functions: volatile functions may get executed more times than expected in the process of carrying out the rules.
Also, there are some cases that are not supported by these types of rules at all, notably includingWITH
clauses in the original query and multiple-assignment sub-SELECT
s in theSET
list ofUPDATE
queries. This is because copying these constructs into a rule query would result in multiple evaluations of the sub-query, contrary to the express intent of the query's author.
# 41.4.1. How Update Rules Work
Keep the syntax:
CREATE [ OR REPLACE ] RULE name AS ON event
TO table [ WHERE condition ]
DO [ ALSO | INSTEAD ] { NOTHING | command | ( command ; command ... ) }
in mind. In the following,update rulesmeans rules that are defined onINSERT
,UPDATE
, orDELETE
.
当查询树的结果关系和命令类型等于给定的对象和事件时,规则系统将应用更新规则创建规则
命令。对于更新规则,规则系统会创建一个查询树列表。最初,查询树列表是空的。可以有零(没有
关键字),一个或多个动作。为简化起见,我们将通过一个操作来查看规则。这条规则可以有或没有,它可以是反而
要么还
(默认)。
什么是规则资格?这是一个限制,它告诉何时应该执行规则的操作,何时不执行。此限定只能引用伪关系新的
和/或老的
,它基本上表示作为对象给出的关系(但具有特殊含义)。
因此,我们有三个案例为单操作规则生成以下查询树。
没有资格,有任何一个还
要么反而
添加了原始查询树限定条件的规则操作中的查询树
获得的资格和还
来自规则操作的查询树,添加了规则限定和原始查询树的限定
获得的资格和反而
来自具有规则限定和原始查询树限定的规则操作的查询树;并添加了否定规则限定的原始查询树
最后,如果规则是还
,未更改的原始查询树被添加到列表中。由于只有合格反而
规则已经添加了原始查询树,我们最终得到一个或两个输出查询树,用于具有一个操作的规则。
为了插入
规则,原始查询(如果没有被反而
) 在规则添加的任何操作之前完成。这允许操作查看插入的行。但对于在更新
和删除时
规则,原始查询是在规则添加的操作之后完成的。这确保了操作可以看到要更新或要删除的行;否则,这些操作可能什么都不做,因为它们找不到符合其条件的行。
从规则操作生成的查询树再次被扔到重写系统中,并且可能会应用更多规则,从而产生更多或更少的查询树。因此,规则的操作必须具有与规则本身不同的命令类型或不同的结果关系,否则此递归过程将最终陷入无限循环。(将检测到规则的递归扩展并报告为错误。)
在操作中找到的查询树pg_rewrite
系统目录只是模板。因为他们可以引用范围表条目新的
和老的
, 必须先进行一些替换才能使用。对于任何参考新的
,在原始查询的目标列表中搜索相应的条目。如果找到,该条目的表达式将替换引用。除此以外,新的
意思是一样的老的
(为更新
) 或被空值替换(对于插入
). Any reference toOLD
is replaced by a reference to the range-table entry that is the result relation.
After the system is done applying update rules, it applies view rules to the produced query tree(s). Views cannot insert new update actions so there is no need to apply update rules to the output of view rewriting.
# 41.4.1.1. A First Rule Step by Step
Say we want to trace changes to thesl_avail
column in theshoelace_data
relation. So we set up a log table and a rule that conditionally writes a log entry when anUPDATE
is performed onshoelace_data
.
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
);
Now someone does:
UPDATE shoelace_data SET sl_avail = 6 WHERE sl_name = 'sl7';
and we look at the log table:
SELECT * FROM shoelace_log;
sl_name | sl_avail | log_who | log_when
### 41.4.2. Cooperation with Views
[]()
A simple way to protect view relations from the mentioned possibility that someone can try to run `INSERT`, `UPDATE`, or `DELETE` on them is to let those query trees get thrown away. So we could create the rules:
CREATE RULE shoe_ins_protect AS ON INSERT TO shoe DO INSTEAD NOTHING; CREATE RULE shoe_upd_protect AS ON UPDATE TO shoe DO INSTEAD NOTHING; CREATE RULE shoe_del_protect AS ON DELETE TO shoe DO INSTEAD NOTHING;
If someone now tries to do any of these operations on the view relation `shoe`, the rule system will apply these rules. Since the rules have no actions and are `INSTEAD`, the resulting list of query trees will be empty and the whole query will become nothing because there is nothing left to be optimized or executed after the rule system is done with it.
A more sophisticated way to use the rule system is to create rules that rewrite the query tree into one that does the right operation on the real tables. To do that on the `shoelace` view, we create the following rules:
CREATE RULE shoelace_ins AS ON INSERT TO shoelace DO INSTEAD INSERT INTO shoelace_data VALUES ( NEW.sl_name, NEW.sl_avail, NEW.sl_color, NEW.sl_len, NEW.sl_unit );
CREATE RULE shoelace_upd AS ON UPDATE TO shoelace DO INSTEAD UPDATE shoelace_data SET sl_name = NEW.sl_name, sl_avail = NEW.sl_avail, sl_color = NEW.sl_color, sl_len = NEW.sl_len, sl_unit = NEW.sl_unit WHERE sl_name = OLD.sl_name;
CREATE RULE shoelace_del AS ON DELETE TO shoelace DO INSTEAD DELETE FROM shoelace_data WHERE sl_name = OLD.sl_name;
If you want to support `RETURNING` queries on the view, you need to make the rules include `RETURNING` clauses that compute the view rows. This is usually pretty trivial for views on a single table, but it's a bit tedious for join views such as `shoelace`. An example for the insert case is:
CREATE RULE shoelace_ins AS ON INSERT TO shoelace DO INSTEAD INSERT INTO shoelace_data VALUES ( NEW.sl_name, NEW.sl_avail, NEW.sl_color, NEW.sl_len, NEW.sl_unit ) RETURNING shoelace_data., (SELECT shoelace_data.sl_lenu.un_fact FROM unit u WHERE shoelace_data.sl_unit = u.un_name);
Note that this one rule supports both `INSERT` and `INSERT RETURNING` queries on the view — the `RETURNING` clause is simply ignored for `INSERT`.
Now assume that once in a while, a pack of shoelaces arrives at the shop and a big parts list along with it. But you don't want to manually update the `shoelace` view every time. Instead we set up two little tables: one where you can insert the items from the part list, and one with a special trick. The creation commands for these are:
CREATE TABLE shoelace_arrive ( arr_name text, arr_quant integer );
CREATE TABLE shoelace_ok ( ok_name text, ok_quant integer );
CREATE RULE shoelace_ok_ins AS ON INSERT TO shoelace_ok DO INSTEAD UPDATE shoelace SET sl_avail = sl_avail + NEW.ok_quant WHERE sl_name = NEW.ok_name;
Now you can fill the table `shoelace_arrive` with the data from the parts list:
SELECT * FROM shoelace_arrive;
arr_name | arr_quant