↑↓ 选择 ↵ 打开 ⌫ 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

不受支持的版本: 7.0 / 6.5
历史版本PostgreSQL 7.0 已于 2005 年 5 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本手册首页。

42.3. INSERT、UPDATE 和 DELETE 上的规则

42.3.1. 与视图规则的区别

定义在 ON INSERT、UPDATE 和 DELETE 上的规则与前一节描述的视图规则完全不同。首先,它们的 CREATE RULE 命令允许更多:

  • 它们可以没有动作。

  • 它们可以有多个动作。

  • INSTEAD 关键字是可选的。

  • 伪关系 NEW 和 OLD 可以派上用场。

  • 它们可以有规则条件。

第二,它们不是就地修改解析树,而是创建零棵或多棵新解析树,并且可能丢弃原始查询树。

42.3.2. 这些规则如何工作

记住以下语法:

CREATE RULE rule_name AS ON event
    TO object [WHERE rule_qualification]
    DO [INSTEAD] [action | (actions) | NOTHING];

在后文中,“更新规则”是指定义在 ON INSERT、UPDATE 或 DELETE 上的规则。

当解析树的结果关系和命令类型分别等于 CREATE RULE 命令中给出的对象和事件时,规则系统就会应用更新规则。对于更新规则,规则系统会创建一个解析树列表,初始时该列表为空。动作可以有零个(NOTHING 关键字)、一个或多个。为简化说明,我们来看只有一个动作的规则。这个规则可以有条件,也可以没有条件;它可以是 INSTEAD,也可以不是。

什么是规则条件?它是一种限制,用来说明何时执行规则动作、何时不执行。这个条件只能引用 NEW 和/或 OLD 伪关系,它们基本上就是作为对象给出的那个关系,只是带有特殊含义。

因此,对于只有一个动作的规则,有以下四种情况会产生相应的解析树。

  • 没有条件,也不是 INSTEAD:

    • 规则动作的解析树,其中附加了原始解析树的条件。

  • 没有条件,但带有 INSTEAD:

    • 规则动作的解析树,其中附加了原始解析树的条件。

  • 给出了条件,不是 INSTEAD:

    • 规则动作的解析树,其中附加了规则条件和原始解析树的条件。

  • 给出了条件,带有 INSTEAD:

    • 规则动作的解析树,其中附加了规则条件和原始解析树的条件。

    • 原始解析树,其中附加了规则条件取反后的条件。

最后,如果规则不是 INSTEAD,就把未经更改的原始解析树加入列表。由于只有带条件的 INSTEAD 规则已经加入了原始解析树,因此,对于只有一个动作的规则,最终最多会得到两棵输出解析树。

由规则动作生成的解析树会再次送入重写系统,随后可能还会有更多规则被应用,从而产生更多或更少的解析树。因此,规则动作中的解析树必须具有不同的命令类型,或者具有不同的结果关系。否则,这种递归过程就会陷入循环。目前内置的递归上限是 10 次迭代。如果经过 10 次迭代之后仍有更新规则要应用,规则系统就会认为出现了多个规则定义之间的循环,并中止事务。

pg_rewrite 系统目录中动作里的解析树只是模板。由于它们可以引用 NEW 和 OLD 的范围表项,因此在使用前必须进行一些替换。对任何 NEW 的引用,都会先在原始查询的目标列表中查找相应项。如果找到了,就用该项的表达式替换该引用。否则,NEW 就与 OLD 含义相同。任何对 OLD 的引用,都会被替换为对作为 resultrelation 的那个 rangetable 项的引用。

42.3.2.1. 第一个规则:逐步分析

我们想跟踪 shoelace_data 关系中 sl_avail 列的变化。因此,我们建立一个日志表和一条规则,使其在 shoelace_data 上执行 UPDATE 时,每次都为我们写一些日志项。

    CREATE TABLE shoelace_log (
        sl_name    char(10),      -- shoelace changed
        sl_avail   integer,       -- new available value
        log_who    name,          -- who did it
        log_when   datetime       -- 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,
                                        getpgusername(),
                                        'now'::text
                                    );

一个有趣的细节是规则 INSERT 动作中把 'now' 转换为 text 类型。如果不这样做,解析器在 CREATE RULE 时就会看到 shoelace_log 中的目标类型是 datetime,并试图用它构造一个常量——而且会成功。于是一个常量 datetime 值会被存储在规则动作中,所有日志项的时间都将是 CREATE RULE 语句的时间。这并不完全是我们想要的。这个转换使解析器改而构造出一个 datetime('now'::text),它会在规则执行时才被求值。

现在 Al 执行了:

    al_bundy=> UPDATE shoelace_data SET sl_avail = 6                       
    al_bundy->     WHERE sl_name = 'sl7';

然后我们查看日志表:

    al_bundy=> 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)

这正是我们预期的结果。后台发生的事情如下。解析器创建了 parsetree(这一次突出显示的是原始 parsetree 中的部分,因为对更新规则而言,操作的基础是规则动作):

    UPDATE shoelace_data SET sl_avail = 6
      FROM shoelace_data shoelace_data
     WHERE bpchareq(shoelace_data.sl_name, 'sl7');

此时存在一条 ON UPDATE 规则 'log_shoelace',它带有如下规则条件表达式:

    int4ne(NEW.sl_avail, OLD.sl_avail)

以及一个动作:

    INSERT INTO shoelace_log SELECT 
           *NEW*.sl_name, *NEW*.sl_avail,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data *NEW*, shoelace_data *OLD*,
           shoelace_log shoelace_log;

不要相信 pg_rules 系统视图的输出。它专门处理了 INSERT 中只有对 NEW 和 OLD 的引用的情形,输出 INSERT 的 VALUES 格式。实际上,INSERT ... VALUES 和 INSERT ... SELECT 在 parsetree 层面没有区别。它们都有 rangetable、targetlist,可能还有限定条件等。优化器稍后会决定为该 parsetree 创建哪种类型的执行计划:result、seqscan、indexscan、join 或其他。如果 parsetree 中不再有对 rangetable 项的引用(leftin),它就会变成一个 result 执行计划(即 INSERT ... VALUES 版本)。上面的规则动作确实(truely)两种结果都可能出现。

该规则是一条带条件的非 INSTEAD 规则,因此规则系统必须返回两个 parsetree:修改后的规则动作,以及原始 parsetree。第一步中,原始查询的 rangetable 会并入规则动作的 parsetree,得到:

    INSERT INTO shoelace_log SELECT 
           *NEW*.sl_name, *NEW*.sl_avai,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data shoelace_data, shoelace_data *NEW*,
           shoelace_data *OLD*, shoelace_log shoelace_log;

第 2 步将规则条件加进去,因此结果集被限制为 sl_avail 发生变化的行:

    INSERT INTO shoelace_log SELECT 
           *NEW*.sl_name, *NEW*.sl_avai,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data shoelace_data, shoelace_data *NEW*,
           shoelace_data *OLD*, shoelace_log shoelace_log
     WHERE int4ne(*NEW*.sl_avail, *OLD*.sl_avail);

第 3 步把原始 parsetree 的条件加进去,把结果集进一步限制为只有那些原始 parsetree 会触及的行:

    INSERT INTO shoelace_log SELECT 
           *NEW*.sl_name, *NEW*.sl_avai,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data shoelace_data, shoelace_data *NEW*,
           shoelace_data *OLD*, shoelace_log shoelace_log
     WHERE int4ne(*NEW*.sl_avail, *OLD*.sl_avail)
       AND bpchareq(shoelace_data.sl_name, 'sl7');

第 4 步把 NEW 引用替换为原始 parsetree 中的 targetlist 项,或者用结果关系中相应的变量引用来替换:

    INSERT INTO shoelace_log SELECT 
           shoelace_data.sl_name, 6,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data shoelace_data, shoelace_data *NEW*,
           shoelace_data *OLD*, shoelace_log shoelace_log
     WHERE int4ne(6, *OLD*.sl_avail)
       AND bpchareq(shoelace_data.sl_name, 'sl7');

第 5 步把 OLD 引用替换为 resultrelation 引用:

    INSERT INTO shoelace_log SELECT 
           shoelace_data.sl_name, 6,
           getpgusername(), datetime('now'::text)
      FROM shoelace_data shoelace_data, shoelace_data *NEW*,
           shoelace_data *OLD*, shoelace_log shoelace_log
     WHERE int4ne(6, shoelace_data.sl_avail)
       AND bpchareq(shoelace_data.sl_name, 'sl7');

至此就完成了。化简到极致之后,规则系统的输出是一个包含两个 parsetree 的列表,它们等同于以下语句:

    INSERT INTO shoelace_log SELECT
           shoelace_data.sl_name, 6,
           getpgusername(), 'now'
      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';

它们会按这个顺序执行,而这正是该规则所定义的行为。上述替换(subtitutions)以及附加的条件能够保证:如果原始查询是下面这样,

    UPDATE shoelace_data SET sl_color = 'green'
     WHERE sl_name = 'sl7';

就不会写入任何日志项,因为这一次原始 parsetree 不包含 sl_avail 的 targetlist 项,NEW.sl_avail 会被 shoelace_data.sl_avail 替换,于是规则产生的额外查询是:

    INSERT INTO shoelace_log SELECT
           shoelace_data.sl_name, shoelace_data.sl_avail,
           getpgusername(), 'now'
      FROM shoelace_data
     WHERE shoelace_data.sl_avail != shoelace_data.sl_avail
       AND shoelace_data.sl_name = 'sl7';

而该条件永远不可能为真。由于 INSERT ... SELECT 与 INSERT ... VALUES 在 parsetree 层面没有区别,如果原始查询修改多行,这种机制同样能够正常工作。因此,如果 Al 发出如下命令:

    UPDATE shoelace_data SET sl_avail = 0
     WHERE sl_color = 'black';

实际上会更新四行(sl1、sl2、sl3 和 sl4)。但 sl3 本来就已经是 sl_avail = 0。这一次,原始 parsetree 的条件不同,因此规则会产生额外的 parsetree:

    INSERT INTO shoelace_log SELECT
           shoelace_data.sl_name, 0,
           getpgusername(), 'now'
      FROM shoelace_data
     WHERE 0 != shoelace_data.sl_avail
       AND shoelace_data.sl_color = 'black';

这棵 parsetree 必然会插入三条新的日志项。这完全正确。

原始 parsetree 最后执行至关重要。Postgres 的“traffic cop”(交通警察)会在两个 parsetree 的执行之间做一次命令计数器递增,使第二个能看到第一个所做的更改。如果先执行 UPDATE,那么所有行都已经被设为零,记日志的 INSERT 就找不到任何满足 0 != shoelace_data.sl_avail 的行了。

42.3.3. 与视图的协作

为了防止有人像前面提到的那样在视图关系上 INSERT、UPDATE 和 DELETE 不可见数据,一种简单的办法是让那些解析树直接被丢弃。我们创建如下规则:

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;

如果现在 Al 尝试对视图关系 shoe 执行这些操作中的任何一种,规则系统就会应用这些规则。由于这些规则没有动作而且是 INSTEAD,生成的解析树列表将为空;整个查询也就什么都不会做,因为规则系统处理完后已经没有任何内容可供优化或执行。

注意

这一事实可能会让前端应用程序感到困惑,因为数据库上绝对什么都没有发生,因而后端不会为该查询返回任何东西。在 libpq 中连 PGRES_EMPTY_QUERY 之类的东西都得不到。在 psql 中,什么都不会发生。这一点将来可能会改变。

另一种更完善的做法,是创建一些规则,把解析树重写成在真实表上执行正确操作的解析树。要在 shoelace 视图上做到这一点,我们创建下列规则:

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;

现在有一批鞋带到货进入 Al 的商店,随附一份很长的部件清单。Al 不太擅长计算,因此我们不想让他手工更新 shoelace 视图。于是我们建立两个小表:一个让他可以插入部件清单中的项目,另一个则采用一个特殊技巧。创建命令如下:

CREATE TABLE shoelace_arrive (
    arr_name    char(10),
    arr_quant   integer
);

CREATE TABLE shoelace_ok (
    ok_name     char(10),
    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;

现在 Al 可以坐下来随便干点什么,直到

al_bundy=> SELECT * FROM shoelace_arrive;
arr_name  |arr_quant
----------+---------
sl3       |       10
sl6       |       20
sl8       |       20
(3 rows)

与部件清单上的内容完全一致为止。我们快速查看一下当前数据,

al_bundy=> SELECT * FROM shoelace ORDER BY sl_name;
sl_name   |sl_avail|sl_color  |sl_len|sl_unit |sl_len_cm
----------+--------+----------+------+--------+---------
sl1       |       5|black     |    80|cm      |       80
sl2       |       6|black     |   100|cm      |      100
sl7       |       6|brown     |    60|cm      |       60
sl3       |       0|black     |    35|inch    |     88.9
sl4       |       8|black     |    40|inch    |    101.6
sl8       |       1|brown     |    40|inch    |    101.6
sl5       |       4|brown     |     1|m       |      100
sl6       |       0|brown     |   0.9|m       |       90
(8 rows)

把到货的鞋带入库:

al_bundy=> INSERT INTO shoelace_ok SELECT * FROM shoelace_arrive;

然后检查结果:

al_bundy=> SELECT * FROM shoelace ORDER BY sl_name;
sl_name   |sl_avail|sl_color  |sl_len|sl_unit |sl_len_cm
----------+--------+----------+------+--------+---------
sl1       |       5|black     |    80|cm      |       80
sl2       |       6|black     |   100|cm      |      100
sl7       |       6|brown     |    60|cm      |       60
sl4       |       8|black     |    40|inch    |    101.6
sl3       |      10|black     |    35|inch    |     88.9
sl8       |      21|brown     |    40|inch    |    101.6
sl5       |       4|brown     |     1|m       |      100
sl6       |      20|brown     |   0.9|m       |       90
(8 rows)

al_bundy=> SELECT * FROM shoelace_log;
sl_name   |sl_avail|log_who|log_when                        
----------+--------+-------+--------------------------------
sl7       |       6|Al     |Tue Oct 20 19:14:45 1998 MET DST
sl3       |      10|Al     |Tue Oct 20 19:25:16 1998 MET DST
sl6       |      20|Al     |Tue Oct 20 19:25:16 1998 MET DST
sl8       |      21|Al     |Tue Oct 20 19:25:16 1998 MET DST
(4 rows)

从一条 INSERT ... SELECT 到这些结果,中间要经历相当长的一段过程。对它的描述将是本文中的最后一个(但不是最后一个例子 :-))。首先是解析器的输出:

INSERT INTO shoelace_ok SELECT
       shoelace_arrive.arr_name, shoelace_arrive.arr_quant
  FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok;

现在应用第一条规则 'shoelace_ok_ins',它会把这一输出转换成:

UPDATE shoelace SET
       sl_avail = int4pl(shoelace.sl_avail, shoelace_arrive.arr_quant)
  FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
       shoelace_ok *OLD*, shoelace_ok *NEW*,
       shoelace shoelace
 WHERE bpchareq(shoelace.sl_name, showlace_arrive.arr_name);

同时会丢弃针对 shoelace_ok 的原始 INSERT。这个重写后的查询会再次交给规则系统,随后应用的规则 'shoelace_upd' 产生:

UPDATE shoelace_data SET
       sl_name = shoelace.sl_name,
       sl_avail = int4pl(shoelace.sl_avail, shoelace_arrive.arr_quant),
       sl_color = shoelace.sl_color,
       sl_len = shoelace.sl_len,
       sl_unit = shoelace.sl_unit
  FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
       shoelace_ok *OLD*, shoelace_ok *NEW*,
       shoelace shoelace, shoelace *OLD*,
       shoelace *NEW*, shoelace_data showlace_data
 WHERE bpchareq(shoelace.sl_name, showlace_arrive.arr_name)
   AND bpchareq(shoelace_data.sl_name, shoelace.sl_name);

这同样是一条 INSTEAD 规则,因此前一个解析树会被丢弃。注意,这个查询仍然使用视图 shoelace。但规则系统在这个循环中尚未完成,因此它会继续在其上应用规则 '_RETshoelace',于是得到:

UPDATE shoelace_data SET
       sl_name = s.sl_name,
       sl_avail = int4pl(s.sl_avail, shoelace_arrive.arr_quant),
       sl_color = s.sl_color,
       sl_len = s.sl_len,
       sl_unit = s.sl_unit
  FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
       shoelace_ok *OLD*, shoelace_ok *NEW*,
       shoelace shoelace, shoelace *OLD*,
       shoelace *NEW*, shoelace_data showlace_data,
       shoelace *OLD*, shoelace *NEW*,
       shoelace_data s, unit u
 WHERE bpchareq(s.sl_name, showlace_arrive.arr_name)
   AND bpchareq(shoelace_data.sl_name, s.sl_name);

又有一条更新规则被应用,于是车轮继续转动,我们进入了第 3 轮重写。这一次应用的是规则 'log_shoelace',这产生了额外的解析树:

INSERT INTO shoelace_log SELECT
       s.sl_name,
       int4pl(s.sl_avail, shoelace_arrive.arr_quant),
       getpgusername(),
       datetime('now'::text)
  FROM shoelace_arrive shoelace_arrive, shoelace_ok shoelace_ok,
       shoelace_ok *OLD*, shoelace_ok *NEW*,
       shoelace shoelace, shoelace *OLD*,
       shoelace *NEW*, shoelace_data showlace_data,
       shoelace *OLD*, shoelace *NEW*,
       shoelace_data s, unit u,
       shoelace_data *OLD*, shoelace_data *NEW*
       shoelace_log shoelace_log
 WHERE bpchareq(s.sl_name,  showlace_arrive.arr_name)
   AND bpchareq(shoelace_data.sl_name, s.sl_name);
   AND int4ne(int4pl(s.sl_avail, shoelace_arrive.arr_quant), s.sl_avail);

到这里,规则系统已经没有更多规则可用,返回生成的解析树。于是我们最终得到两棵解析树,它们等同于以下 SQL 语句:

INSERT INTO shoelace_log SELECT
       s.sl_name,
       s.sl_avail + shoelace_arrive.arr_quant,
       getpgusername(),
       'now'
  FROM shoelace_arrive shoelace_arrive, shoelace_data shoelace_data,
       shoelace_data s
 WHERE s.sl_name = shoelace_arrive.arr_name
   AND shoelace_data.sl_name = s.sl_name
   AND s.sl_avail + shoelace_arrive.arr_quant != s.sl_avail;

UPDATE shoelace_data SET
       sl_avail = shoelace_data.sl_avail + shoelace_arrive.arr_quant
  FROM shoelace_arrive shoelace_arrive,
       shoelace_data shoelace_data,
       shoelace_data s
 WHERE s.sl_name = shoelace_arrive.sl_name
   AND shoelace_data.sl_name = s.sl_name;

结果是:来自一个关系的数据被插入到另一个关系中,这个插入被改写为对第三个关系的更新,再被改写为对第四个关系的更新外加在第五个关系中记录该更新,最后整个过程被化简为两个查询。

这里有个稍显难看的小细节。观察这两个查询就会发现,shoelace_data 关系在范围表中出现了两次,而实际上完全可以缩减成一次。优化器不会处理这一点,因此规则系统为 INSERT 输出的执行计划会是:

Nested Loop
  ->  Merge Join
        ->  Seq Scan
              ->  Sort
                    ->  Seq Scan on s
        ->  Seq Scan
              ->  Sort
                    ->  Seq Scan on shoelace_arrive
  ->  Seq Scan on shoelace_data

而省略那个额外的范围表项则会得到:

Merge Join
  ->  Seq Scan
        ->  Sort
              ->  Seq Scan on s
  ->  Seq Scan
        ->  Sort
              ->  Seq Scan on shoelace_arrive

后者完全会在日志关系中产生相同的项。因此,规则系统导致了对 shoelace_data 关系的一次完全不必要的额外扫描。而在 UPDATE 中,同样的冗余扫描还会再发生一次。不过,能让这一切总体上工作起来,已经是一项相当艰巨的工作。

最后来演示一下 Postgres 规则系统及其威力。有一位漂亮的金发女郎卖鞋带。而 Al 永远不会意识到的是,她不仅漂亮,还很聪明——甚至有点太聪明了。于是,Al 不时会订购一些完全卖不出去的鞋带。这一次他订购了 1000 双品红色(magenta)鞋带,而且由于另一种鞋带暂时缺货、而他已承诺购买一些,他又为粉色(pink)的鞋带做好了数据库准备:

al_bundy=> INSERT INTO shoelace VALUES 
al_bundy->     ('sl9', 0, 'pink', 35.0, 'inch', 0.0);
al_bundy=> INSERT INTO shoelace VALUES 
al_bundy->     ('sl10', 1000, 'magenta', 40.0, 'inch', 0.0);

由于这种事经常发生,我们必须不时地找出那些绝对不适合任何鞋子的鞋带项。我们可以每次都用一条复杂的语句来完成,也可以为此建立一个视图。这个视图是:

CREATE VIEW shoelace_obsolete AS
    SELECT * FROM shoelace WHERE NOT EXISTS
        (SELECT shoename FROM shoe WHERE slcolor = sl_color);

它的输出是:

al_bundy=> SELECT * FROM shoelace_obsolete;
sl_name   |sl_avail|sl_color  |sl_len|sl_unit |sl_len_cm
----------+--------+----------+------+--------+---------
sl9       |       0|pink      |    35|inch    |     88.9
sl10      |    1000|magenta   |    40|inch    |    101.6

那 1000 双品红色鞋带,我们得先向 Al 讨了债才能把它们扔掉,不过那是另一个问题了。粉色的条目我们要删掉。为了给 Postgres 增加一点难度,我们不直接删除,而是再创建一个视图:

CREATE VIEW shoelace_candelete AS
    SELECT * FROM shoelace_obsolete WHERE sl_avail = 0;

然后这样执行:

DELETE FROM shoelace WHERE EXISTS
    (SELECT * FROM shoelace_candelete
             WHERE sl_name = shoelace.sl_name);

Voila:

al_bundy=> SELECT * FROM shoelace;
sl_name   |sl_avail|sl_color  |sl_len|sl_unit |sl_len_cm
----------+--------+----------+------+--------+---------
sl1       |       5|black     |    80|cm      |       80
sl2       |       6|black     |   100|cm      |      100
sl7       |       6|brown     |    60|cm      |       60
sl4       |       8|black     |    40|inch    |    101.6
sl3       |      10|black     |    35|inch    |     88.9
sl8       |      21|brown     |    40|inch    |    101.6
sl10      |    1000|magenta   |    40|inch    |    101.6
sl5       |       4|brown     |     1|m       |      100
sl6       |      20|brown     |   0.9|m       |       90
(9 rows)

作用于一个视图上的 DELETE,其子查询条件总共使用了四个嵌套/连接的视图,其中一个视图自身又带有一个包含视图的子查询条件,并且还用到了计算得到的视图列,最终仍会被重写成单棵解析树,从真正的表中删除所请求的数据。

我想在现实世界中,大概只有很少的场景会需要这样的构造。但知道它确实能工作,总归让人安心。

实情是

在写这一章的过程中,我又发现了一个 bug。不过修完那个 bug 之后,这一切居然能工作,倒是让我有点惊讶。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。