Выполнение подзапроса Postgresql в правиле ON UPDATE (возможно, ошибка в postgresql?) - PullRequest
0 голосов
/ 17 июля 2011

Я сталкиваюсь со странным поведением (или это ошибка в 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"

Из выходных данных видно, что проблема заключается в том, что подзапрос (он же «внутренний») выполняется ПОСЛЕ его ссылочного (он же «внешнего») запроса в пределах 2 запросов SELECT в foo_update_rule. В результате столбец _value (который определен в подзапросе) еще не определен при оценке внешнего запроса, в результате чего отладка ('update_outer_2 ->' :: text || _value :: text) завершается с ошибкой (и не распечатывать уведомление).

Странно то, что тот же SQL в правиле ON INSERT будет работать нормально (распечатка обоих уведомлений 'external_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 Ответ

4 голосов
/ 17 июля 2011

PostgreSQL, или, в этом отношении, сам SQL, не дает никаких гарантий, в каком порядке выполняются разные части запроса.Это только определяет конечный результат.на самом деле, различные части запроса могут выполняться с перемешиванием - или полностью распараллеливаться, если база данных должна была поддерживать это.

Теперь, ПРАВИЛА делают вещи еще хуже, потому что они обычно не работают так, как ожидают пользователи,Правила работают на уровне синтаксического анализатора , а не на выполнении.Таким образом, ваши различные части могут очень хорошо запускаться более одного раза - просто потому, что они внезапно появятся более одного раза в дереве разбора.

В большинстве случаев вы хотите использовать TRIGGER, а не RULE.

Суть в том, что ваше приложение не должно полагаться на конкретные подзапросы (или объединения или что-то еще) в запросе для выполнения в определенном порядке.

...