问题
I am running into a strange behavior (or is this a bug within postgresql?) within postgresql's execution order of subqueries within rules. Consider the following 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;
The above code when executed generates the following output:
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"
From the output, it shows that the problem is the subquery (aka 'inner') is executed AFTER its referencing (aka 'outer') query within the 2 SELECT queries in the foo_update_rule. As a result, the _value column (which is defined within the subquery) is not yet defined when the outer query is evaluated, causing the debug('update_outer_2 -> '::text || _value::text) to silently fail (and not print out a notice).
The wierd thing is, the same SQL within an ON INSERT rule will work fine (printing out both of the 'outer_2 -> ...' notices). But for some reason the SQL does not work within an ON UPDATE rule.
How can the above query be fixed so that the following 2 notices are printed?
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:
PostgreSQL, or for that matter SQL itself, makes no guarantee in which order different parts of the query executes. It only defines the final result. in fact, the different parts of the query can execute intemixed - or fully parallelized if the database were to support that.
Now, RULEs make things even worse, in that they generally don't work the way users expect. RULEs work at the parser level, not at the execution. So your different parts may very well run more than once - simply because they will suddenly appear more than once in the parse tree.
In most cases, what you want is a TRIGGER rather than a RULE.
Bottom line is, though, that your application should not rely on the specific subqueries (or joins or whatever) in a query to execute in a particular order.
来源:https://stackoverflow.com/questions/6721788/postgresql-subquery-execution-within-on-update-rule-maybe-bug-in-postgresql