【问题标题】:Postgresql subquery execution within ON UPDATE rule (maybe bug in postgresql?)在 ON UPDATE 规则中执行 Postgresql 子查询(可能是 postgresql 中的错误?)
【发布时间】:2011-07-17 03:43:38
【问题描述】:

在 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 列(在子查询中定义),导致 debug('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"

【问题讨论】:

  • 使用说明查看语句的执行顺序。
  • 对于“mu 太短”,通知来自代码块顶部定义的 debug() 函数。 'update2_outer_2' 在代码块中被调用,开头为: ROW(FALSE,FALSE) IN ( SELECT... 而且,是的,'debug('update_inner'::text)' 在 2 个不同的地方被调用,但这就是目的.
  • 对于“丹尼斯”,我无法在规则中使用 EXPLAIN,因为 pgsql 会丢弃这些行。独立 SELECT 上的 EXPLAIN 表明它以正确的顺序执行。但最初的问题是 pgsql 以错误的顺序执行规则中的查询,而独立 SELECT 中的执行顺序很好。

标签: postgresql subquery rules


【解决方案1】:

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

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

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

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

【讨论】:

  • 由于各种原因,在这种情况下,我被锁定在使用规则而不是触发器。我不依赖查询的执行顺序,因为 debug() 通知仅用于说明导致问题的执行路径。这里的最终问题是 pgsql RULE 以最终结果不正确的方式评估子查询,因为 WITH 子查询定义的列似乎对 SELECT 不可用,这似乎与文档背道而驰。
  • 无论如何,FINAL结果都是不正确的,因为RULE里面的WITH子查询是按照正确的顺序执行的(inner后跟outer),但是WITH查询中定义的列(_value)是在 RULE 内的 SELECT 外部查询中不可用,而 _value 对 RULE 外的 SELECT 可用。
  • 我想我的关键问题是,假设我们被限制在规则中执行此操作,如何修改查询以便 foobar 中的 _value 列(在规则中的 WITH 中定义) 对 SELECT 可见?现在 _value 似乎在 RULE 中作为 NULL 或 VOID 传递给 SELECT。
猜你喜欢
  • 2019-10-07
  • 2021-10-21
  • 1970-01-01
  • 2015-11-22
  • 2017-02-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-17
  • 1970-01-01
相关资源
最近更新 更多