postgresql Postgres生成的列不是不可变的

9vw9lbht  于 2023-04-29  发布在  PostgreSQL
关注(0)|答案(2)|浏览(272)

我添加一些列到一个表,并希望生成一个列,将它们组合在一起,我将使用一个唯一的索引。当我尝试添加列时,我得到错误ERROR: generation expression is not immutable
我遵循了this question的解决方案,特别是使用CASE||进行字符串连接,它们应该是immutable

ALTER TABLE tag
  ADD COLUMN prefix VARCHAR(4) NOT NULL,
  ADD COLUMN middle BIGINT NOT NULL,
  ADD COLUMN postfix VARCHAR(4), -- nullable
  -- VARCHAR size is 4 prefix + 19 middle + 4 postfix + 2 delimiter
  ADD COLUMN tag_id VARCHAR(29) NOT NULL GENERATED ALWAYS AS
    (CASE WHEN postfix IS NULL THEN prefix || '-' || middle
          ELSE prefix || '-' || middle || '-' || postfix
          END
    ) STORED;
CREATE UNIQUE INDEX unq_tag_tag_id ON tag(tag_id);

postgres mailing list中,其中一位贡献者澄清了:
整数到文本强制,[...]不一定是不可变的
但是,他没有共享一个不可变的整数到文本的函数。有谁知道是否有一个?

rxztt3cl

rxztt3cl1#

Marmite Bomber的回答显示了解决方案;让我补充一点说明。
text有两个串联运算符:

SELECT oid, oprname,
       oprleft::regtype,
       oprright::regtype,
       oprcode
FROM pg_operator
WHERE oprname = '||'
  AND oprleft = 'text'::regtype;

 oid  │ oprname │ oprleft │  oprright   │  oprcode   
══════╪═════════╪═════════╪═════════════╪════════════
  654 │ ||      │ text    │ text        │ textcat
 2779 │ ||      │ text    │ anynonarray │ textanycat
(2 rows)

第一个操作符将texttext连接起来,第二个操作符将text与其他任何操作符连接起来。
让我们看看这两个函数的波动性:

SELECT oid, proname, provolatile
FROM pg_proc
WHERE pronamespace = 'pg_catalog'::regnamespace
  AND proname IN ('textcat', 'textanycat');

 oid  │  proname   │ provolatile 
══════╪════════════╪═════════════
 1258 │ textcat    │ i
 2003 │ textanycat │ s
(2 rows)

因此,如果您将textbigint连接起来,该操作不是IMMUTABLE,但首先将bigint转换为text会产生IMMUTABLE操作。
并不是因为与整数连接,这个运算符就不是不可变的。但是anynonarray可以是任何数据类型,如timestamp with time zone,其字符串表示取决于timezone的当前设置。

tyg4sfes

tyg4sfes2#

14.1中的测试表明,原因是串联中bigint列到text的隐式转换(即使没有case
显式转换为text不会产生错误-middle::text

ALTER TABLE tag
  ADD COLUMN prefix VARCHAR(4) NOT NULL,
  ADD COLUMN middle BIGINT NOT NULL,
  ADD COLUMN postfix VARCHAR(4), -- nullable
  -- VARCHAR size is 4 prefix + 19 middle + 4 postfix + 2 delimiter
  ADD COLUMN tag_id VARCHAR(29) NOT NULL GENERATED ALWAYS AS
    (CASE WHEN postfix IS NULL THEN prefix || '-' || middle::text
          ELSE prefix || '-' || middle::text || '-' || postfix
          END
    ) STORED;

相关问题