# 41.4. Rules onINSERT,UPDATE, andDELETE

41.4.1. How Update Rules Work

41.4.2. Cooperation with Views

Rules that are defined onINSERT,UPDATE, andDELETEare significantly different from the view rules described in the previous section. First, theirCREATE RULEcommand allows more:

  • They are allowed to have no action.

  • They can have multiple actions.

  • They can beINSTEADorALSO(the default).

  • The pseudorelationsNEWandOLDbecome 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/DELETEare 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 includingWITHclauses in the original query and multiple-assignment sub-SELECTs in theSETlist ofUPDATEqueries. 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 toOLDis 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_availcolumn in theshoelace_datarelation. So we set up a log table and a rule that conditionally writes a log entry when anUPDATEis 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