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

时间:2011-07-17 03:43:39

标签: postgresql subquery rules

我在postgresql的规则中子查询的执行顺序中遇到了一个奇怪的行为(或者这是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"

从输出中,它显示问题是子查询(也称为“内部”)在foo_update_rule中的2个SELECT查询中的引用(也称为“外部”)查询之后执行。因此,在评估外部查询时,尚未定义_value列(在子查询中定义),导致调试('update_outer_2 - >':: text || _value :: text)无提示失败(并且不打印通知)。

奇怪的是,ON INSERT规则中的相同SQL将正常工作(打印出'outer_2 - > ...'两个通知)。但由于某种原因,SQL在ON UPDATE规则中不起作用。

如何修复上述查询以便打印以下2个通知?

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"

1 个答案:

答案 0 :(得分:4)

PostgreSQL,或者就此而言,SQL本身并不能保证查询的不同部分执行的顺序。它只定义了最终结果。事实上,如果数据库要支持,查询的不同部分可以执行intemixed - 或完全并行化。

现在,RULEs让事情变得更糟,因为它们通常不像用户期望的那样工作。 RULEs在解析器级别工作,而不是在执行时工作。所以你的不同部分可能会运行不止一次 - 只是因为它们会突然出现在解析树中不止一次。

在大多数情况下,你想要的是触发而不是规则。

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