2011-07-17 53 views
0

我在postgresql的規則內子查詢的執行順序內運行了一個奇怪的行爲(或者這是postgresql中的一個錯誤?)。考慮以下SQL:PostgreSQL子查詢在ON UPDATE規則內執行(可能是postgresql中的錯誤?)

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查詢中引用(又名'outer')查詢。其結果是,該_value柱(其子查詢中定義的)還沒有被當外部查詢求值定義,從而使調試(「update_outer_2 - >」 ::文本|| _value ::文本)默默地失效(和不打印通知)。

奇怪的是,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" 
+0

使用說明,看看其中的語句完成的順序。 –

+0

爲了'mu太短',通知來自代碼塊頂部定義的debug()函數。在「update2_outer_2」是所謂的代碼塊開頭:ROW(FALSE,FALSE)IN(SELECT ...而且,是「調試(」 update_inner「::文本)」被稱爲在2個不同的地方,但就是意圖。 – archmeta

+0

以「丹尼斯」,我不能夠使用規則內講解,爲pgsql的丟棄行。解釋上,它是在正確的順序執行的獨立選擇的節目。但原來的問題是,pgsql的正在執行不正確的順序規則中查詢,而在執行順序獨立的選擇是好的。 – archmeta

回答

4

PostgreSQL,或者就此而言,SQL本身並不能保證查詢的不同部分執行的順序。它只是定義了最終結果。事實上,查詢的不同部分可以執行intemixed - 或者完全並行化(如果數據庫支持的話)。

現在,RULE會讓事情變得更糟,因爲它們通常不會像用戶所期望的那樣工作。規則在解析器級別工作,而不是在執行。所以你的不同部分可能不止一次運行 - 僅僅是因爲他們會在分析樹中突然出現多次。

在大多數情況下,你想要的是一個TRIGGER而不是一個RULE。

儘管如此,您的應用程序不應依賴查詢中的特定子查詢(或連接或其他)以特定順序執行。

+0

由於種種原因,我鎖定在這方面使用規則,而不是一個觸發。我不依靠順序因爲debug()聲明只是爲了說明導致問題的執行路徑。最終的問題在於,pgsql RULE正在評估子查詢,使得FINAL RESULT是FINAL RESULT不正確,因爲由th定義的列e WITH子查詢似乎對SELECT不可用,這似乎違背了文檔。 – archmeta

+0

在任何情況下,最後的結果是不正確的,因爲該規則內的WITH子查詢以正確的順序(內後跟外)被執行的,但內WITH查詢中定義的柱(_value)不可用的SELECT外部查詢內在RULE內部,而_value可用於RULE外部的SELECT。 – archmeta

+0

我猜我的關鍵問題是,假設我們被限制在RULE內部執行此操作,如何修改查詢以便foobar中的_value列(在RULE中的WITH內定義)變得對SELECT可見?現在_value似乎被傳遞給SELECT作爲RULE內的NULL或VOID。 – archmeta