2015-05-01 148 views
2

我在具有相同函數名稱的單個數據庫中有兩個類似的模式。 每個模式都由與模式名稱相匹配的角色擁有。使用嵌套函數的PL/pgSQL函數名稱解析

我有關於嵌套函數的函數名稱解析的問題。 我在期待外層函數會調用同一模式中的內層函數,但它不會! 這個名字是在運行時基於search_path動態解析的,這使得一些敏感,但不是如我所願。

這是一個測試用例。比方說,例如模式和角色被命名爲test和prod,如下所示。

測試模式:
CREATE ROLE test NOLOGIN; 
CREATE SCHEMA test AUTHORIZATION test; 

CREATE OR REPLACE FUNCTION test.inner_func() RETURNS TEXT 
AS $BODY$ 
BEGIN 
    RETURN 'test function'; 
END 
$BODY$ LANGUAGE 'plpgsql'; 
ALTER FUNCTION test.inner_func() OWNER TO test; 

CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT 
AS $BODY$ 
BEGIN 
    RETURN QUERY SELECT inner_func(); 
END 
$BODY$ LANGUAGE 'plpgsql'; 
ALTER FUNCTION test.outer_func() OWNER TO test; 
PROD模式:
CREATE ROLE prod NOLOGIN; 
CREATE SCHEMA prod AUTHORIZATION prod; 

CREATE OR REPLACE FUNCTION prod.inner_func() RETURNS TEXT 
AS $BODY$ 
BEGIN 
    RETURN 'prod function'; 
END 
$BODY$ LANGUAGE 'plpgsql'; 
ALTER FUNCTION prod.inner_func() OWNER TO prod; 

CREATE OR REPLACE FUNCTION prod.outer_func() RETURNS SETOF TEXT 
AS $BODY$ 
BEGIN 
    RETURN QUERY SELECT inner_func(); 
END 
$BODY$ LANGUAGE 'plpgsql'; 
ALTER FUNCTION prod.outer_func() OWNER TO prod; 
測試用例:
SET search_path=test,public;  
SELECT outer_func(); 
> test function 

SELECT prod.outer_func(); 
> test function <<<---- was expecting prod function 

SET search_path=prod,public; 
SELECT prod.outer_func(); 
> prod function

試驗表明,功能名稱解析動態基礎上,search_path在運行時。有一種方法可以在模式範圍內綁定內部函數嗎?

我可以通過使用SECURITY DEFINER函數與動態SQL和CURRENT_USER得到這樣的行爲,但我正在尋找更直接的東西。

+0

您可以設置所需的搜索路徑作爲函數的屬性(在函數定義中使用'SET')。 Btw:語言名稱是一個標識符,不要把它放在單引號中。這是已被棄用的語法,將在未來被刪除 –

+0

@ a_horse_with_no_name好!工作得很好。請使用答案按鈕而不是評論,以便我可以分配點並將問題設置爲已回答。 – lessj

+0

@a_horse_with_no_name是正確的。但是,我不確定爲什麼你在連接後不設置search_path。您在這裏做的事聽起來有點像很常見的多租戶模式,您可以根據客戶名稱創建一系列相同的模式,然後在連接時將搜索路徑設置爲當前客戶。將系統作爲「測試」運行,在連接後設置search_path等。 –

回答

0

乾淨的解決方法是架構限定功能:

CREATE OR REPLACE FUNCTION test.outer_func() 
    RETURNS SETOF text AS 
$func$ 
BEGIN 
    RETURN QUERY SELECT test.inner_func(); 
END 
$func$ LANGUAGE plpgsql; -- no quotes!

或者你明確地設置search_path每個功能。您可以set configuration parameters this way

CREATE OR REPLACE FUNCTION test.outer_func() 
    RETURNS SETOF text AS 
$func$ 
BEGIN 
    RETURN QUERY SELECT inner_func(); 
END 
$func$ LANGUAGE plpgsql SET search_path = test, pg_temp;

定製search_path您的需要,可能添加public到列表中。我把pg_temp放在最後,所以臨時模式中的對象不能隱藏持久對象。 (但這不適用於函數。)類似於在the manual for SECURITY DEFINER functions中解釋的內容。

勸依靠用戶設置適當search_path。這隻對「公共」功能有意義,它不符合你的設計。爲什麼創建單獨的功能,然後仍然必須依賴用戶設置?您可以在公共架構中使用單個函數,但在任何情況下我都不會獲得該路由。非常混亂,容易出錯。

另外,PL/pgSQL在內部執行像預準備語句這樣的語句。每次更改search_path時,必須將plpgsql函數中的所有「已準備好」語句取消分配,這不利於優化性能。

其實,你的測試用例的問題,如果你設置不僅工程search_path第一:

SET search_path=test,public; 

否則你想創建

CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT 
AS $BODY$ 
BEGIN 
    RETURN QUERY SELECT inner_func(); 
... 
ERROR: function inner_func() does not exist 
時出錯

語法檢查在創建時針對當前的search_path運行時間 - 除非您提供search_path建議。 That was fixed 2010 after I reported a bug.

細則search_path

而且不要引用語言名稱。這是一個標識符。