2015-06-25 36 views
0

我的函數的工作是提取XML節點。代碼如下:PL/pgSQL函數沒有按預期返回自定義(記錄)類型

CREATE TYPE xml_node_looper_record AS (
    allomany xml, 
    i integer, 
    actual_node text, 
    nodi_parts text[] 
); 

CREATE OR REPLACE FUNCTION ds.xml_node_looper_rec(rec xml_node_looper_record) 
    RETURNS SETOF xml_node_looper_record AS 
$BODY$ 
DECLARE 
    nodes text[]; 
    field_val text; 
    r xml_node_looper_record; 
    n integer; 
BEGIN 
    nodes = xpath(rec.actual_node, rec.allomany); 
    IF nodes[1] IS NOT NULL THEN 
    rec.i = rec.i + 1; 
    FOR n IN 1..array_upper(nodes, 1) LOOP 
     IF rec.i = array_upper(rec.nodi_parts, 1) THEN 
     field_val = trim(ARRAY[xpath(rec.actual_node || '/text()', rec.allomany)]::text, ' {}"'); 
     IF field_val IS NOT NULL AND field_val != '' THEN 
      RAISE NOTICE '% % % %', n, rec.actual_node, rec.i, field_val; 
      RETURN NEXT (NULL::xml, rec.i, rec.actual_node, ARRAY[field_val]::text[]); 
     END IF; 
     END IF; 
     SELECT ds.xml_node_looper_rec((rec.allomany, rec.i, rec.actual_node || '[' || n::text || ']' || rec.nodi_parts[rec.i + 1], rec.nodi_parts)) INTO r; 
    END LOOP; 
    END IF; 
END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE COST 100; 

正如你所看到的功能是遞歸的目標是從我們目前沒有任何信息,我們有多少個節點都有一個標籤下的多個XML節點採集的字段值。 (我有一個帶有返回表的版本,但是這個方法太慢了。)我使用自己定義的自定義類型,當我用RAISE NOTICE檢查返回值時,我可以在消息選項卡上看到pgAdmin的結果,但是RETURN NEXT命令不返回任何內容,只返回空表。

我喜歡的類型的參數:

  • allomany:XML數據
  • 我:nodi_parts的實際深度
  • actual_node:XML節點,我想提取。可以有多個 節點,我用[]標記它們。例如: /tagone/tagtwo []/tagthree []/fieldname
  • nodi_parts:來自actual_node,用[]分割。對於 示例ARRAY [「/ tagone/tagtwo」,「/ tagthree」,「/ fieldname」]

問題是什麼?

回答

2

您不傳播嵌套調用的結果。 RETURN NEXT將結果推送到與函數調用相關的堆棧。但是這個堆棧是私有的 - 如果調用者沒有獲取這個堆棧,那麼結果就會被清除。無論如何 - 任何函數實例(被調用函數)都有自己的結果堆棧。這個堆棧不共享。

PL/pgSQL的遞歸表函數應該看起來像:

postgres=# CREATE OR REPLACE FUNCTION foo(level int) RETURNS SETOF int AS $$ 
BEGIN 
    IF level > 5 THEN RETURN; END IF; 
    RETURN NEXT level; 
    --!! must to take result of nested call 
    RETURN QUERY SELECT * FROM foo(level + 1); 
    RETURN; 
END; 
$$ LANGUAGE plpgsql; 

postgres=# SELECT * FROM foo(1); 
┌─────┐ 
│ foo │ 
╞═════╡ 
│ 1 │ 
│ 2 │ 
│ 3 │ 
│ 4 │ 
│ 5 │ 
└─────┘ 
(5 rows) 

你的代碼是一個代碼相當於:

postgres=# CREATE OR REPLACE FUNCTION foo(level int) RETURNS SETOF int AS $$ 
BEGIN 
    IF level > 5 THEN RETURN; END IF; 
    RETURN NEXT level; 
    -- error, only call of nested function, but returned table is lost 
    PERFORM foo(level + 1); 
    RETURN; 
END; 
$$ LANGUAGE plpgsql; 

postgres=# SELECT * FROM foo(1); 
┌─────┐ 
│ foo │ 
╞═════╡ 
│ 1 │ 
└─────┘ 
(1 row) 
+0

謝謝帕維爾!現在我明白了,我修改了這個功能,並且完美地工作。 – Roscos