Postgresql子查询在ON UPDATE规则内执行(可能是Postgresql的bug ?)



我遇到了一个奇怪的行为(或者这是postgresql的一个bug ?)在postgresql的执行顺序中的子查询规则。考虑以下SQL:

BEGIN;
CREATE OR REPLACE FUNCTION debug(anyelement) RETURNS bool AS $$
pg_raise('notice', 'debug(): ' . json_encode($args[0]));
RETURN TRUE;
$$ LANGUAGE PLPHP IMMUTABLE STRICT;
CREATE TABLE foo_table (c1 text);
CREATE OR REPLACE RULE foo_update_rule AS ON UPDATE TO foo_table DO INSTEAD
(
    WITH foobar_update AS
    (
        SELECT unnest('{a,b}'::text[]) AS _value, debug('update_inner'::text)
    )
    SELECT *, debug('update_outer_1'::text), debug('update_outer_2 -> '::text || _value::text) FROM foobar_update;

SELECT
        ( ROW(FALSE,FALSE) IN ( SELECT 
                        debug('update2_outer_1'::text), debug('update2_outer_2 -> '::text || _value::text)
                   FROM ( SELECT unnest('{a,b}'::text[]) AS _value, debug('update_inner'::text) ) AS foobar_update2     ))
);
-----------------------------------------------
WITH foobar_select AS
(
    SELECT unnest('{a,b}'::text[]) AS _value, debug('select_inner'::text)
)
SELECT *, debug('select_outer_1'::text), debug('select_outer_2 -> '::text || _value::text), debug('select_outer_3'::text) FROM foobar_select;
UPDATE foo_table SET c1 = NULL where c1 = 'aaa';
ROLLBACK;

上面的代码在执行时会产生以下输出:

NOTICE:  plphp: debug(): "select_inner"
NOTICE:  plphp: debug(): "select_outer_1"
NOTICE:  plphp: debug(): "select_outer_3"
NOTICE:  plphp: debug(): "select_outer_2 -> a"
NOTICE:  plphp: debug(): "select_outer_2 -> b"
NOTICE:  plphp: debug(): "update_inner"
NOTICE:  plphp: debug(): "update_outer_1"
NOTICE:  plphp: debug(): "update2_outer_1"
NOTICE:  plphp: debug(): "update_inner"

从输出中可以看出,问题是子查询(又名'inner')在foo_update_rule中的2个SELECT查询中的引用(又名'outer')查询之后执行。因此,在计算外部查询时,_value列(在子查询中定义)尚未定义,导致调试('update_outer_2 -> '::text || _value::text)静默失败(并且不打印出通知)。

奇怪的是,同样的SQL在一个ON INSERT规则中会工作得很好(打印出'outer_2 ->…"通知)。但是由于某些原因,SQL不能在ON UPDATE规则中工作。

如何修正上述查询,以便打印以下两个通知?

NOTICE:  plphp: debug(): "update_outer_2 -> a"
NOTICE:  plphp: debug(): "update_outer_2 -> b"
NOTICE:  plphp: debug(): "update2_outer_2 -> a"
NOTICE:  plphp: debug(): "update2_outer_2 -> b"

PostgreSQL,或者SQL本身,不保证查询的不同部分执行的顺序。它只定义最终结果。事实上,如果数据库支持的话,查询的不同部分可以混合或完全并行执行。

现在,规则使事情变得更糟,因为它们通常不会按用户期望的方式工作。规则在解析器级别工作,而不是在执行阶段。所以你的不同部分很可能会运行不止一次——仅仅是因为它们会突然在解析树中出现不止一次。

在大多数情况下,您需要的是TRIGGER而不是RULE。

但是,最重要的是,您的应用程序不应该依赖于查询中的特定子查询(或连接或其他)以特定顺序执行。

最新更新