Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wrxKu-000JR7-25 for pgsql-hackers@arkaria.postgresql.org; Thu, 06 Aug 2026 12:36:37 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wrxKu-004E8k-0P for pgsql-hackers@arkaria.postgresql.org; Thu, 06 Aug 2026 12:36:35 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wrxKt-004E8b-1U for pgsql-hackers@lists.postgresql.org; Thu, 06 Aug 2026 12:36:35 +0000 Received: from mail-wm1-x32a.google.com ([2a00:1450:4864:20::32a]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wrxKn-00000000Ury-2MVy for pgsql-hackers@postgresql.org; Thu, 06 Aug 2026 12:36:33 +0000 Received: by mail-wm1-x32a.google.com with SMTP id 5b1f17b1804b1-490cf322ed0so17642645e9.1 for ; Thu, 06 Aug 2026 05:36:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1786019788; x=1786624588; darn=postgresql.org; h=in-reply-to:content-transfer-encoding:content-disposition :content-type:mime-version:references:message-id:subject:cc:to:from :date:from:to:cc:subject:date:message-id:reply-to:content-type; bh=wEfZb8vGoO6jq7B2Trj7aa+dpsXijEVfwNpZ5czx4AQ=; b=Lb/mFobWdma1YCq8gPhpMwJNmgwFM/th22qy6KQ6A+uPA6m107fX3tUA/bSVWIGipc aDkX0+D7WcfXj3JdB7vuWOJU/UUB05iML3ElAIcRxpDd7KrOvwHo5C2ia1mIHZo9XBLS uYxbU3BJTIYfTgcevXei12QGiUonkuFyCs9VFwGU68Fo4SaUU9TX18b0snMlTBb/psxt Eec49Hk5Uwc7i8dc7y9Le3SATZkrZKLTUw4s7LM5YpSf0HK7/DSlTsqdmJ4Dsfhb2XIh MpRa0WyfAtBOQOhhR/yiVmwyuWRtDGJOEZNp46za+boR4wfPQbT3b9IE/PVItEsgx1Ik WLgg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786019788; x=1786624588; h=in-reply-to:content-transfer-encoding:content-disposition :content-type:mime-version:references:message-id:subject:cc:to:from :date:x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=wEfZb8vGoO6jq7B2Trj7aa+dpsXijEVfwNpZ5czx4AQ=; b=WtIfLm/dmxvCV4bD3/idPXVs+EsV+YZDlFxi8b/EOPfRAgpLRevi/Xs6xXaIOTEzY9 Ekm4dDtuTSDz2r7miBbSjj4eovPAuoyohni4LFIbpIhuUQZR41dYyOJ7UCY1i7iI5qat 24okLXG9RQbTxc9/g76DTVWWMisAuNDCzNU6+IiEeCQ7bOhB8UqbDocjwAtLNciYCeAC RM3mcy6XJKASb0Sk5FSy0Lt/saBfW3XPDdku+6rSX7erc4aOkwKBAJTY/8EbXT+jlLJb In0dMqL+FNPFDj2qL8wc8+84UwRjfwhoQ4xlEcVRfpGVYs33zarL7O01ShxPvX+ZGWVy t9yQ== X-Forwarded-Encrypted: i=1; AHgh+RqQzyP3f3BcoTOL164cl8epHpLBXpvVyX7b3yWtp3JZ5+lrP2jeUDhEDDR9b/rfvmi8g1q0HJFBYEMa2HBn@postgresql.org X-Gm-Message-State: AOJu0Yy1A6S/gPazsS/OxvIqeQY7ICLlKVS4n8k4GVyjo8AwT3gNx7AI NC/UMZzlRWWTIfLtw8CxiDfakjAw4ylhnlgxB8UcnRNId9Eff/Fwv41e X-Gm-Gg: AR+sD12fgj/1vSQEhl+Ch0AAelVVRkmbUy3CNsKtG06IQ4glQ/1mqswjsnuPP5dns2X Mpid4NSUTy2kXP6MTWVv0+/FxwCjuk0HSqGQEE8farTS0zzrS66ask0Rbxf/dcTkpfs19CUp6e8 VXYETPELmE/jXqDFuTIxqWJekmJHjlDq1Ipuc08Vgm9dWg4TsrVxATSb0Y9CEpPGO2KrfWNdZ53 D40VKi5BiXSXgiB/9mCWa5XanmzCfs90DvnfwkXbEbl0/KBWDQIAcRRWyvou/jr3ssKJ01NOxv6 vBv2UtK+ytlGXCVynR3bxpd7czLcoO9j4F+pO7G2wvgGr1EEBnhBRgLKL8bOa64fufyWWnjZbeC akGhPM4mtWsgfbg6/JycNsArtlIUxsDzwfHpoaYNhxxKPJ3CT3o4Yxhxn1SN6pBO7oI11apRNky PiXcNjeDHsq5fL6jcO/ewgD8c1IM3e50N0DAd/U+XjGwztuh0FuVFQOjpbzAwBqqk8FZ6aV4Atp 9wFeAGzfxmY7l1d X-Received: by 2002:a05:600c:3b04:b0:495:5e86:4e11 with SMTP id 5b1f17b1804b1-4994e7baa85mr186943255e9.12.1786019787712; Thu, 06 Aug 2026 05:36:27 -0700 (PDT) Received: from localhost ([85.195.242.240]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4995420cb4esm59919465e9.2.2026.08.06.05.36.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Aug 2026 05:36:26 -0700 (PDT) Date: Thu, 6 Aug 2026 14:36:21 +0200 From: Alberto Piai To: Laurenz Albe , Alberto Piai , pgsql-hackers@postgresql.org Cc: =?utf-8?Q?=C3=81lvaro?= Herrera Subject: Re: Adding a stored generated column without long-lived locks Message-ID: X-Mailer: aerc 0.21.0 References: <3af338791e8ab27c4a7efbc4217c97162de76bc8.camel@cybertec.at> <2eb68e74ea95fc2f691de7d8cd28448d00d1d216.camel@cybertec.at> <79b63e772179abb4f5ad749d9e067d48a5537599.camel@cybertec.at> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="v5hipyfsvi3ngylm" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <79b63e772179abb4f5ad749d9e067d48a5537599.camel@cybertec.at> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --v5hipyfsvi3ngylm Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: 8bit Hi, On Fri Jul 10, 2026 at 8:51 AM CEST, Laurenz Albe wrote: > I take your point about keeping the STORED. If VIRTUAL is the default > when creating a generated column, then keeping the explicit STORED makes > sense. > > About GENERATED vs. EXPRESSION: my feelings go the other way. If I read > > ALTER TABLE tab ALTER col ADD EXPRESSION ... > > then it is not clear to me *what* expression is added. After all, there > are not only generation expressions, but also column default expressions, > and I think it is good to make clear that this is about generated columns. > > I think I understand your preference: after all, we are not ADDing a > GENERATED column, but only an EXPRESSION that turns a regular column > into a generated column. Perhaps I can convince you by showing the > similar > > ALTER TABLE tab ALTER col ADD GENERATED ALWAYS AS IDENTITY; > > Here, too, we are not adding a new generated column, only turning a > regular column into a generated one. I think it is good to stay as > close to that already established syntax as possible. Yes that is convincing. PFA v7, implementing this version of the command: ... ALTER col ADD GENERATED USING CONSTRAINT constr_name STORED Differences from v6, besides the new syntax: - both the = and the not-distinct form of the constraints now work with both orders of the operands - comments and error messages changed to address the feedback from the latest review round - simplified tests a bit, trying to avoid creating/dropping test tables unnecessarily - ATPrepAddGenStore doesn't try to lock child tables anymore, following what was done for DROP EXPRESSION in fef160d8b0ddf72e1089133300d30109293f5a71 [0] In particular, all the error messages now follow the guidelines for error reporting. I tried to use a consistent error message everywhere, adding details and hints where appropriate. All the error cases are grouped together now in the regress suite, so looking at the expected output should give a good overview of all the error conditions and how they are reported to the user. A note about this one: > The following error message is not very helpful: > > > CREATE TABLE tab ( > a integer DEFAULT 2, > b integer > CONSTRAINT con CHECK (b IS NOT DISTINCT FROM 2 + random()) > ); > > > ALTER TABLE tab ALTER b ADD GENERATED ALWAYS STORED USING CONSTRAINT con; > ERROR: cannot convert a column into a stored generated column without a constraint to prove that the values are consistent > DETAIL: could not find a valid constraint "con" CHECK ("b" IS NOT DISTINCT FROM (expr)) This was interesting. What's going on here is that since random() returns a float, the whole expression returns a float. The column b is an int, so since there is an implicit cast from int to float, the resulting expression for the CHECK constraint is b::float IS NOT DISTINCT FROM 2 + random() I think in cases like this there's not much I can do: the constraint isn't an equality to b anymore, but an equality to f(b) where f is the function defined for the cast. The constraint is simply not usable for our purpose. For this reason, I think it doesn't make too much sense in this case to look at the other operand, hunt down the random() and complain about the function being volatile: the core problem here is the return type, and the same situation can happen with an immutable function. I tried detecting implicit casts though, because I think this is a mistake that's quite easy to make, so it's worth trying to give a hint to the user about what's going on and what to do. This is now reported in this way (from the regress test suite): alter table tgen.t1 add constraint chk_gen check (b is not distinct from (a + random())); -- the hint should inform about the type cast alter table tgen.t1 alter column b add generated using constraint chk_gen stored; ERROR: cannot convert column "b" to generated DETAIL: Could not find a valid constraint "chk_gen" CHECK ("b" IS NOT DISTINCT FROM expr). HINT: Ensure that the type of the expression matches the type of the column. In this situation, \d would show the constraint being CHECK (b::double precision = (.... expr with random()) instead of b = ...expr, which makes me think the hint is clear enough. But I'm curious to hear what you think about it. Independently from the problem with casts, the immutability of the generation expression is of course also checked: alter table tgen.t2 add constraint chk_gen check (b is not distinct from (a + random()::int)); alter table tgen.t2 alter column b add generated using constraint chk_gen stored; ERROR: generation expression is not immutable Looking forward to your comments! Regards, Alberto [0] https://www.postgresql.org/message-id/anGRnFgKM6RFmGLm%40alvherre.pgsql -- Alberto Piai Sensational AG Zürich, Switzerland --v5hipyfsvi3ngylm Content-Type: text/plain; charset=utf-8 Content-Disposition: attachment; filename="v7-0001-Support-changing-a-column-into-a-stored-generated.patch"