Postgresql

使用數字字元串幫助理解 try_cast() 的行為

  • August 27, 2021

try_castErwin為 postgres製作了一個很棒的通用函式(複製如下)。我有興趣將它用於 EAV 模式,其中屬性與類型欄位一起儲存。我可以使用一個類似的函式(返回一個布爾值)作為觸發器來測試是否可以將值字元串轉換為相應的數據類型以添加一些類型安全性。

CREATE OR REPLACE FUNCTION try_cast(_in text, INOUT _out ANYELEMENT) AS
$func$
BEGIN
  EXECUTE format('SELECT %L::%s', $1, pg_typeof(_out))
  INTO  _out;
EXCEPTION WHEN others THEN
  -- do nothing: _out already carries default
END
$func$  LANGUAGE plpgsql;

但是在測試它時(在 PG11 中),我發現如果將十進制數字字元串轉換為整數類型(smallint、int 和 bigint),它會失敗 - try_cast('50.0', NULL::int)。我猜這是因為 postgres 在轉換為整數之前無法將字元串轉換為數字/十進制類型(這是有道理的)。try_cast('50.0', NULL::numeric)確實有效,所以我可能會稱之為足夠好。

但是,我玩了這條線:

EXECUTE format('SELECT %L::%s', $1, pg_typeof(_out))

並將其更改%L%s

EXECUTE format('SELECT %s::%s', $1, pg_typeof(_out))

並且try_cast('50.0', NULL::<integer-type>)沒有問題。缺點是其他類型,例如macaddr/cidr,在它們之前工作時無法轉換

try_cast('192.168'::text, NULL::cidr) -- returns null
-- but 'SELECT '192.168'::cidr' works

我真的很好奇為什麼%s修復了數字類型轉換並破壞了其他類型,以及是否有任何方法可以改進函式以滿足這種極端情況。如果沒有,我可以忍受numeric,但我擔心其他人將來使用這個功能而不了解它的局限性。

感謝您的任何幫助或想法!

因為%s省略了引號。format()您可以通過互動嘗試看到這一點:

testdb=# select format('%s', '50.0') as "%s", format('%L', '50.0') as "%L";
 %s  |   %L   
------+--------
50.0 | '50.0'
(1 row)

我認為在 try_cast(); 中沒有任何方法可以做到這一點。在我看來,它在做正確的事情。字元串'50.0'合法地不是整數的表示,因此首先轉換為數字是正確的。

引用自:https://dba.stackexchange.com/questions/298604