PostgreSQL 在某些情況下會忽略隱式轉換
設置(僅在 PostgreSQL 9.6 上嘗試過):
CREATE TYPE MY_ENUM AS ENUM ('a', 'b'); CREATE CAST (CHARACTER VARYING AS MY_ENUM) WITH INOUT AS IMPLICIT; CREATE TABLE t (x MY_ENUM); INSERT INTO t VALUES ('a'::MY_ENUM), ('b'::MY_ENUM);
正如預期的那樣,這些工作正常:
INSERT INTO t VALUES ('a'); SELECT * FROM t WHERE x = 'a';
但這些不會:
PREPARE p(CHARACTER VARYING) AS SELECT * FROM t WHERE x = $1; ;; error: operator does not exist: my_enum = character varying CREATE FUNCTION f(ix CHARACTER VARYING, OUT ox MY_ENUM) AS $$ SELECT * FROM t WHERE x = ix $$ LANGUAGE sql; ;; error: operator does not exist: my_enum = character varying CREATE FUNCTION f(ix CHARACTER VARYING) RETURNS VOID AS $$ BEGIN SELECT * FROM t WHERE input_type = ix; END; $$ LANGUAGE plpgsql; SELECT f('piano'); ;; error: operator does not exist: my_enum = character varying
根據文件:
如果強制轉換標記為 AS
IMPLICIT
,則可以在任何上下文中隱式呼叫它,無論是賦值還是在表達式內部。那麼為什麼會出現錯誤呢?
區別在於:
在前兩個(工作)範例中,
'a'
is 只是一個無類型的字元串文字- 這與varchar
.enum
列舉類型的基本輸入/輸出函式提供了到您的自定義類型的轉換。(系統目錄中沒有明確的條目pg_cast
。) Postgres 確定數據類型的最佳匹配,因為使用者沒有提供。甚至不需要您的自定義演員表。既不用於類型解析,也不用於運算符解析。在以下範例中,您傳遞了一個類型化的值(
varchar
)。現在,您的自定義演員可能會派上用場。例如,如果沒有您的自定義演員表,這將無法工作:INSERT INTO t VALUES ('a'::varchar);
您的範例不是簡單的分配,而是帶有 operator 的表達式
=
。我們處於Operator Type Resolution的領域。第 2a 項規定:如果二元運算符呼叫的一個參數是未知類型,則假定它與此檢查的另一個參數的類型相同。
這將涵蓋
my_enum_column = 'foo'
- 使用無類型文字。但是沒有什麼可以伸展到覆蓋
my_enum_column = 'foo'::varchar
。似乎只有=
左右操作數的通用運算符anyenum
可用這一事實不足以在運算符解析中提供線索:-- missing operators: SELECT oprleft::regtype, oprright::regtype, * FROM pg_operator WHERE oprname = '=' AND ('anyenum'::regtype IN (oprleft, oprright) OR 'my_enum'::regtype IN (oprleft, oprright));
oprleft | oprright | ... --------+----------+---- anyenum | anyenum | ... (1 row)
您必須為此註冊一個更明確的運算符。但我不會那樣做。我會做類似的事情(還要注意各種正交語法修復):
PREPARE p(my_enum) AS SELECT * FROM t WHERE x = $1; -- prepare with type my_enum EXECUTE p('a'::varchar); -- then your cast kicks in EXECUTE p('a'); -- untyped string literal works in any case
CREATE FUNCTION f1(ix my_enum, OUT ox my_enum) -- again, declare enum RETURNS SETOF my_enum AS $$ SELECT x FROM t WHERE x = ix $$ LANGUAGE sql; SELECT * FROM f1('a'::varchar);
CREATE FUNCTION f2(ix my_enum) -- same here RETURNS SETOF t AS $$ BEGIN RETURN QUERY SELECT * FROM t WHERE x = ix; END; $$ LANGUAGE plpgsql; SELECT * FROM f2('a'::varchar); SELECT * FROM f2('a');
db<>在這裡擺弄
或者只是添加明確的演員表。這是最簡單和最安全的。手冊警告(在您引用的同一頁面上):
保守地將演員表標記為隱式是明智的。…
還有更多 - 推薦閱讀。
如果您必須創建該自定義轉換(甚至添加一個運算符),請考慮使用
text
而不是varchar
,它是字元串類型中的“首選類型”,因此對於可能的極端情況問題更加穩健。