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 1wVPIL-001wJa-2p for pgsql-hackers@arkaria.postgresql.org; Fri, 05 Jun 2026 07:48:46 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wVPIK-00AlOx-0h for pgsql-hackers@arkaria.postgresql.org; Fri, 05 Jun 2026 07:48:44 +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 1wVPIJ-00AlOe-29 for pgsql-hackers@lists.postgresql.org; Fri, 05 Jun 2026 07:48:43 +0000 Received: from mail-pf1-x42c.google.com ([2607:f8b0:4864:20::42c]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wVPIH-00000001Caa-1EUb for pgsql-hackers@lists.postgresql.org; Fri, 05 Jun 2026 07:48:42 +0000 Received: by mail-pf1-x42c.google.com with SMTP id d2e1a72fcca58-8423610ec93so1272682b3a.2 for ; Fri, 05 Jun 2026 00:48:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780645720; x=1781250520; darn=lists.postgresql.org; h=to:cc:date:message-id:subject:mime-version:from:from:to:cc:subject :date:message-id:reply-to; bh=yfnoZlrmEd8fxzqsHkavChFvsVv6C87TYKP7UT5IW6g=; b=QAtGudAGxGF3HubF90gmjHUd2k8IzebU07LnNpF78tItR+xumUqWEmjIci7zYnUnKv gnqyZNcJZ69F2BXH4/NC+bADzQMRx6qBpZmPKQACEW55R96UbfYmroUnm0Lyu2vVr1ZM vBVF62o1ZT1r3dhI5NswtgYqmVlmOZntd+jHjTbVHGtQl2TzugRaTVkqAfqwjVcWlLXJ S+eXX1mYwxBjHFRuSalDNlm9V+Lv9FdK9hIE1oMgDKojPjtrJs8Cyzn4wB8JKrBkr596 7dlAT439QvqkSkkVNnBArGr8x3YqxOZaoHALd50wFYeGT6TEOHwjFjzFTDp+WUvYdy6r gc0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780645720; x=1781250520; h=to:cc:date:message-id:subject:mime-version:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=yfnoZlrmEd8fxzqsHkavChFvsVv6C87TYKP7UT5IW6g=; b=q16/qa1aB0U8Uj12D01BMGI1LDvTGqEMJskogW+chm4VcvpnRyluEW6OhtzET1LDn+ PLOGVmsB+U6Kas4icNM8mGnQTX2PZ/JUwTT8yjPRyzwwSVisez4wdYJJiBW8ny0cPvEE NPq2BA8gCUfZ99TKenUza7i8lE1VMgyXgACydRDFV9atra8C6iM7mmROMOOohQbev69q ruREZg1K+ZBkN+L1Dk1GR7rSB6R5vEdqcwpVskrHk4241D+kDN76jqAtSbJK0x+vw6f7 pIGqZlXCHq94n0wdvY2wqKYCs/rsrH+W5XqaU3J+7vhRQRManZXE5N5HZ/t8PjLdHSx5 5dGA== X-Gm-Message-State: AOJu0YxAmDn4Eb6Fgx/T/ITh53nmNiG0wts+2smv1M02qOr/oHvNxF1y KH66AYlsm8k40XbrDwoCsjagf01EVfm3EF4tWI9UWSWhOaUohmWlvGGyedAtZ/IC X-Gm-Gg: Acq92OHLnvQcikUmdqfPTEvUcEjMIllGrwgS7Y5CoxPgUZ4557WXVjM0kKXDSjF05ga zaP1vkhsyrX04Jn2jSqWFZgvATg6Z1Ny+OARqUCtMPnWdh/JnWBdCo7QMcKk0pD6Fp6vV0cM8D4 tDxcsZKgFCBqCVjKvECe91Fgn6a6Ogiol7D+VIhKV5Df4sy9SsnUpf+cJRXNj/aqnUm4fyKbM8+ pBX86pq4wKx4YxbvOp3ihoci4kise+jpVeE+QUCSMX5s5xUAr8jQagfke5TTYFOpRhVNTgVN+Vi YIDhD+wURfKBGVTk3EMRi3x92kYNa9OQ3v1AkaYRSCouWvnml0a9+LWlNAo719S+p1RMOcxjRfb j/GeEqKGuFU3Hxer1Q8OuScjT+QzoYQaep3pg4YN4QgMlVxY/nm+t5RE/43ANneWduYaKeLAEq8 mr3eDUKOW91+GTfrNYSm7csRfSp25swBBY17sU/sHnpg== X-Received: by 2002:a05:6a00:3e07:b0:842:6419:3ed2 with SMTP id d2e1a72fcca58-842b0eecc85mr2476853b3a.33.1780645720349; Fri, 05 Jun 2026 00:48:40 -0700 (PDT) Received: from smtpclient.apple ([45.32.121.103]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84282220d1bsm9103852b3a.12.2026.06.05.00.48.37 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Fri, 05 Jun 2026 00:48:39 -0700 (PDT) From: Chao Li Content-Type: multipart/mixed; boundary="Apple-Mail=_919D82ED-2F95-4B87-A045-048DE2A3192A" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3864.600.51.1.1\)) Subject: Fix domain fast defaults on empty tables Message-Id: <7033D663-DDB4-4B35-922C-F33DE53B1502@gmail.com> Date: Fri, 5 Jun 2026 15:48:00 +0800 Cc: Andrew Dunstan , jian he To: Postgres hackers X-Mailer: Apple Mail (2.3864.600.51.1.1) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Apple-Mail=_919D82ED-2F95-4B87-A045-048DE2A3192A Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 Hi, I tested "[a0b6ef29a] Enable fast default for domains with non-volatile = constraints". After tracing some cases from the regression tests, I came = up with this test case and found a bug: ``` evantest=3D# create domain d_div as int check (1 / (value - 1) > 0); CREATE DOMAIN evantest=3D# create table t (a int); CREATE TABLE evantest=3D# alter table t add column b d_div default 1; ERROR: division by zero ``` It looks like errors inside the CHECK expression itself, such as the = int4div division-by-zero in this test, are still hard errors that can = fail the ALTER TABLE command. For the fix, I worked out two solutions: Solution 1 =3D=3D=3D=3D=3D=3D=3D=3D We can add PG_TRY/PG_CATCH to capture those hard errors. But as = a0b6ef29a's commit message mentions, the fallback should only apply = while evaluating the CoerceToDomain expression. To avoid broadly = suppressing hard errors, I only catch hard errors from = domain_check_safe(), which verifies the default value against the = domain. The default expression itself is still evaluated with hard = errors. My concern with this solution is that there might be some error from = domain_check_safe() that I am not aware of and that would be hidden by = the try-catch. But that may be acceptable, because it matches the = behavior before a0b6ef29a, so at least it is not a regression. Solution 2 =3D=3D=3D=3D=3D=3D=3D=3D This solution is simpler and is based on the purpose of the original = feature. a0b6ef29a's commit message says the purpose of the feature is = to avoid table rewriting. Since an empty table has no data to rewrite, = we can bypass the fast path for empty tables. The problem with this solution is that I currently use = RelationGetNumberOfBlocks(rel) to decide whether the table is empty, so = it cannot handle cases like: ``` Begin; Delete from t; Alter table t add column =E2=80=A6 ``` See the attached patches for details. v1-s1 is solution 1, and v1-s2 is = solution 2. Please let me know which solution is preferred. Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/ --Apple-Mail=_919D82ED-2F95-4B87-A045-048DE2A3192A Content-Disposition: attachment; filename=v1-s1-0001-Handle-throwing-domain-checks-when-probing-fas.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v1-s1-0001-Handle-throwing-domain-checks-when-probing-fas.patch" Content-Transfer-Encoding: quoted-printable =46rom=20576164f2334a958dcaf39747ba8e068c852e4fc3=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20"Chao=20Li=20(Evan)"=20=0A= Date:=20Thu,=204=20Jun=202026=2018:06:01=20+0800=0ASubject:=20[PATCH=20= v1-s1]=20Handle=20throwing=20domain=20checks=20when=20probing=20fast=0A=20= defaults.=0A=0AALTER=20TABLE=20ADD=20COLUMN=20tries=20to=20prove=20that=20= a=20default=20value=20can=20be=20stored=0Aas=20a=20missing=20value,=20= using=20soft=20errors=20so=20that=20a=20failed=20domain=20constraint=0A= falls=20back=20to=20a=20table=20rewrite.=20=20However,=20a=20domain=20= CHECK=20expression=20can=0Athrow=20an=20error=20before=20returning=20= false,=20for=20example=20due=20to=20division=20by=0Azero.=20=20That=20= made=20the=20fast-default=20probe=20fail=20immediately,=20even=20for=20= an=0Aempty=20table=20where=20the=20rewrite=20path=20would=20have=20= succeeded.=0A=0AFor=20direct=20domain=20coercions,=20evaluate=20the=20= default=20expression=20separately=0Aand=20then=20validate=20the=20= resulting=20datum=20with=20domain_check_safe().=20=20Catch=0Aerrors=20= only=20around=20the=20domain=20validation=20step,=20so=20errors=20from=20= the=20default=0Aexpression=20itself=20are=20still=20reported=20normally.=0A= =0AAuthor:=20Chao=20Li=20=0A---=0A=20= src/backend/commands/tablecmds.c=20=20=20=20=20=20=20=20=20=20=20|=2066=20= ++++++++++++++++++----=0A=20src/test/regress/expected/fast_default.out=20= |=2019=20++++++-=0A=20src/test/regress/sql/fast_default.sql=20=20=20=20=20= =20|=2017=20+++++-=0A=203=20files=20changed,=2087=20insertions(+),=2015=20= deletions(-)=0A=0Adiff=20--git=20a/src/backend/commands/tablecmds.c=20= b/src/backend/commands/tablecmds.c=0Aindex=20a1845240a98..585df658258=20= 100644=0A---=20a/src/backend/commands/tablecmds.c=0A+++=20= b/src/backend/commands/tablecmds.c=0A@@=20-7640,23=20+7640,67=20@@=20= ATExecAddColumn(List=20**wqueue,=20AlteredTableInfo=20*tab,=20Relation=20= rel,=0A=20=09=09=09=09ExprState=20=20*exprState;=0A=20=09=09=09=09Datum=09= =09missingval;=0A=20=09=09=09=09bool=09=09missingIsNull;=0A+=09=09=09=09= volatile=20bool=20caught_error=20=3D=20false;=0A=20=09=09=09=09= ErrorSaveContext=20escontext=20=3D=20{T_ErrorSaveContext};=0A+=09=09=09=09= CoerceToDomain=20*ctest=20=3D=20NULL;=0A=20=0A-=09=09=09=09/*=20Evaluate=20= the=20default=20expression=20with=20soft=20errors=20*/=0A+=09=09=09=09if=20= (has_domain_constraints=20&&=20IsA(defval,=20CoerceToDomain))=0A+=09=09=09= =09=09ctest=20=3D=20castNode(CoerceToDomain,=20defval);=0A+=0A+=09=09=09=09= /*=0A+=09=09=09=09=20*=20Evaluate=20the=20default=20expression,=20using=20= soft=20errors=20where=0A+=09=09=09=09=20*=20possible=0A+=09=09=09=09=20= */=0A=20=09=09=09=09estate=20=3D=20CreateExecutorState();=0A-=09=09=09=09= exprState=20=3D=20ExecPrepareExprWithContext(defval,=20estate,=0A-=09=09=09= =09=09=09=09=09=09=09=09=09=09=20=20=20(Node=20*)=20&escontext);=0A-=09=09= =09=09missingval=20=3D=20ExecEvalExpr(exprState,=0A-=09=09=09=09=09=09=09= =09=09=09=20=20GetPerTupleExprContext(estate),=0A-=09=09=09=09=09=09=09=09= =09=09=20=20&missingIsNull);=0A+=09=09=09=09if=20(ctest)=0A+=09=09=09=09= {=0A+=09=09=09=09=09MemoryContext=20oldcxt=20=3D=20CurrentMemoryContext;=0A= +=0A+=09=09=09=09=09/*=0A+=09=09=09=09=09=20*=20Evaluate=20the=20default=20= expression=20itself=20with=20hard=0A+=09=09=09=09=09=20*=20errors.=0A+=09= =09=09=09=09=20*/=0A+=09=09=09=09=09exprState=20=3D=20= ExecPrepareExpr(ctest->arg,=20estate);=0A+=09=09=09=09=09missingval=20=3D=20= ExecEvalExpr(exprState,=0A+=09=09=09=09=09=09=09=09=09=09=09=20=20= GetPerTupleExprContext(estate),=0A+=09=09=09=09=09=09=09=09=09=09=09=20=20= &missingIsNull);=0A+=0A+=09=09=09=09=09/*=0A+=09=09=09=09=09=20*=20A=20= domain=20CHECK=20expression=20can=20fail=20by=20throwing=20an=0A+=09=09=09= =09=09=20*=20error,=20rather=20than=20by=20returning=20false.=20=20Treat=20= that=20like=0A+=09=09=09=09=09=20*=20any=20other=20failed=20proof=20that=20= the=20value=20is=20safe=20to=20cache.=0A+=09=09=09=09=09=20*/=0A+=09=09=09= =09=09PG_TRY();=0A+=09=09=09=09=09{=0A+=09=09=09=09=09=09(void)=20= domain_check_safe(missingval,=20missingIsNull,=0A+=09=09=09=09=09=09=09=09= =09=09=09=09=20ctest->resulttype,=0A+=09=09=09=09=09=09=09=09=09=09=09=09= =20NULL,=20NULL,=0A+=09=09=09=09=09=09=09=09=09=09=09=09=20(Node=20*)=20= &escontext);=0A+=09=09=09=09=09}=0A+=09=09=09=09=09PG_CATCH();=0A+=09=09=09= =09=09{=0A+=09=09=09=09=09=09MemoryContextSwitchTo(oldcxt);=0A+=09=09=09=09= =09=09FlushErrorState();=0A+=09=09=09=09=09=09caught_error=20=3D=20true;=0A= +=09=09=09=09=09}=0A+=09=09=09=09=09PG_END_TRY();=0A+=09=09=09=09}=0A+=09= =09=09=09else=0A+=09=09=09=09{=0A+=09=09=09=09=09exprState=20=3D=20= ExecPrepareExprWithContext(defval,=20estate,=0A+=09=09=09=09=09=09=09=09=09= =09=09=09=09=09=20=20=20(Node=20*)=20&escontext);=0A+=09=09=09=09=09= missingval=20=3D=20ExecEvalExpr(exprState,=0A+=09=09=09=09=09=09=09=09=09= =09=09=20=20GetPerTupleExprContext(estate),=0A+=09=09=09=09=09=09=09=09=09= =09=09=20=20&missingIsNull);=0A+=09=09=09=09}=0A=20=0A=20=09=09=09=09/*=0A= -=09=09=09=09=20*=20If=20the=20domain=20constraint=20check=20failed=20= (via=20errsave),=0A-=09=09=09=09=20*=20missingval=20is=20unreliable.=20=20= Fall=20back=20to=20a=20table=20rewrite;=0A-=09=09=09=09=20*=20Phase=203=20= will=20re-evaluate=20with=20hard=20errors,=20so=20the=20user=20gets=0A-=09= =09=09=09=20*=20an=20error=20only=20if=20the=20table=20has=20rows.=0A+=09= =09=09=09=20*=20If=20the=20domain=20constraint=20check=20failed,=20= missingval=20is=0A+=09=09=09=09=20*=20unreliable.=20=20Fall=20back=20to=20= a=20table=20rewrite;=20Phase=203=20will=0A+=09=09=09=09=20*=20= re-evaluate=20with=20hard=20errors,=20so=20the=20user=20gets=20an=20= error=0A+=09=09=09=09=20*=20only=20if=20the=20table=20has=20rows.=0A=20=09= =09=09=09=20*/=0A-=09=09=09=09if=20(escontext.error_occurred)=0A+=09=09=09= =09if=20(caught_error=20||=20escontext.error_occurred)=0A=20=09=09=09=09= {=0A=20=09=09=09=09=09missingIsNull=20=3D=20true;=0A=20=09=09=09=09=09= tab->rewrite=20|=3D=20AT_REWRITE_DEFAULT_VAL;=0Adiff=20--git=20= a/src/test/regress/expected/fast_default.out=20= b/src/test/regress/expected/fast_default.out=0Aindex=20= 5813f1c61a5..bdaa5aedb42=20100644=0A---=20= a/src/test/regress/expected/fast_default.out=0A+++=20= b/src/test/regress/expected/fast_default.out=0A@@=20-322,12=20+322,23=20= @@=20CREATE=20DOMAIN=20domain5=20AS=20int=20CHECK(value=20>=2010)=20= DEFAULT=208;=0A=20CREATE=20DOMAIN=20domain6=20as=20int=20CHECK(value=20>=20= 10)=20DEFAULT=20random(min=3D>11,=20max=3D>100);=0A=20CREATE=20DOMAIN=20= domain7=20as=20int=20CHECK((value=20+=20random(min=3D>11::int,=20= max=3D>11))=20>=2012);=0A=20CREATE=20DOMAIN=20domain8=20as=20int=20NOT=20= NULL;=0A+CREATE=20DOMAIN=20domain9=20AS=20int=20CHECK(1=20/=20(value=20-=20= 1)=20>=200);=0A=20CREATE=20TABLE=20test_add_domain_col(a=20int);=0A=20--=20= succeeds=20despite=20constraint-violating=20default=20because=20table=20= is=20empty=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20COLUMN=20a1=20= domain5;=0A=20NOTICE:=20=20rewriting=20table=20test_add_domain_col=20for=20= reason=202=0A=20ALTER=20TABLE=20test_add_domain_col=20DROP=20COLUMN=20= a1;=0A=20INSERT=20INTO=20test_add_domain_col=20VALUES(1),(2);=0A+--=20= likewise,=20an=20empty=20table=20succeeds=20if=20the=20domain=20check=20= expression=20errors=0A+CREATE=20TABLE=20test_add_domain_col_empty(a=20= int);=0A+ALTER=20TABLE=20test_add_domain_col_empty=20ADD=20COLUMN=20b=20= domain9=20DEFAULT=201;=0A+NOTICE:=20=20rewriting=20table=20= test_add_domain_col_empty=20for=20reason=202=0A+DROP=20TABLE=20= test_add_domain_col_empty;=0A+--=20but=20errors=20in=20the=20default=20= expression=20itself=20should=20not=20be=20hidden=0A+CREATE=20TABLE=20= test_add_domain_col_bad_default(a=20int);=0A+ALTER=20TABLE=20= test_add_domain_col_bad_default=20ADD=20COLUMN=20b=20domain5=20DEFAULT=20= 1=20/=200;=0A+ERROR:=20=20division=20by=20zero=0A+DROP=20TABLE=20= test_add_domain_col_bad_default;=0A=20--=20tests=20with=20non-empty=20= table=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20COLUMN=20b=20= domain5;=20--=20table=20rewrite,=20then=20fail=0A=20NOTICE:=20=20= rewriting=20table=20test_add_domain_col=20for=20reason=202=0A@@=20-338,6=20= +349,9=20@@=20ERROR:=20=20domain=20domain8=20does=20not=20allow=20null=20= values=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20COLUMN=20b=20= domain5=20DEFAULT=201;=20--=20table=20rewrite,=20then=20fail=0A=20= NOTICE:=20=20rewriting=20table=20test_add_domain_col=20for=20reason=202=0A= =20ERROR:=20=20value=20for=20domain=20domain5=20violates=20check=20= constraint=20"domain5_check"=0A+ALTER=20TABLE=20test_add_domain_col=20= ADD=20COLUMN=20b=20domain9=20DEFAULT=201;=20--=20table=20rewrite,=20then=20= fail=0A+NOTICE:=20=20rewriting=20table=20test_add_domain_col=20for=20= reason=202=0A+ERROR:=20=20division=20by=20zero=0A=20ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20b=20domain5=20DEFAULT=2012;=20--=20= ok,=20no=20table=20rewrite=0A=20--=20explicit=20column=20default=20= expression=20overrides=20domain's=20default=0A=20--=20expression,=20so=20= no=20table=20rewrite=0A@@=20-365,8=20+379,8=20@@=20ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20f=20domain7;=0A=20NOTICE:=20=20= rewriting=20table=20test_add_domain_col=20for=20reason=202=0A=20--=20= domain=20with=20both=20volatile=20and=20non-volatile=20CHECK=20= constraints:=20the=0A=20--=20volatile=20one=20forces=20a=20table=20= rewrite=0A-CREATE=20DOMAIN=20domain9=20AS=20int=20CHECK(value=20>=2010)=20= CHECK((value=20+=20random(min=3D>1::int,=20max=3D>1))=20>=200);=0A-ALTER=20= TABLE=20test_add_domain_col=20ADD=20COLUMN=20g=20domain9=20DEFAULT=2014;=0A= +CREATE=20DOMAIN=20domain10=20AS=20int=20CHECK(value=20>=2010)=20= CHECK((value=20+=20random(min=3D>1::int,=20max=3D>1))=20>=200);=0A+ALTER=20= TABLE=20test_add_domain_col=20ADD=20COLUMN=20g=20domain10=20DEFAULT=20= 14;=0A=20NOTICE:=20=20rewriting=20table=20test_add_domain_col=20for=20= reason=202=0A=20--=20virtual=20generated=20columns=20cannot=20have=20= domain=20types=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20COLUMN=20= h=20domain5=0A@@=20-383,6=20+397,7=20@@=20DROP=20DOMAIN=20domain6;=0A=20= DROP=20DOMAIN=20domain7;=0A=20DROP=20DOMAIN=20domain8;=0A=20DROP=20= DOMAIN=20domain9;=0A+DROP=20DOMAIN=20domain10;=0A=20DROP=20FUNCTION=20= foo(INT);=0A=20--=20Fall=20back=20to=20full=20rewrite=20for=20volatile=20= expressions=0A=20CREATE=20TABLE=20T(pk=20INT=20NOT=20NULL=20PRIMARY=20= KEY);=0Adiff=20--git=20a/src/test/regress/sql/fast_default.sql=20= b/src/test/regress/sql/fast_default.sql=0Aindex=20= e5d9a3d2fd1..290321fe60a=20100644=0A---=20= a/src/test/regress/sql/fast_default.sql=0A+++=20= b/src/test/regress/sql/fast_default.sql=0A@@=20-292,6=20+292,7=20@@=20= CREATE=20DOMAIN=20domain5=20AS=20int=20CHECK(value=20>=2010)=20DEFAULT=20= 8;=0A=20CREATE=20DOMAIN=20domain6=20as=20int=20CHECK(value=20>=2010)=20= DEFAULT=20random(min=3D>11,=20max=3D>100);=0A=20CREATE=20DOMAIN=20= domain7=20as=20int=20CHECK((value=20+=20random(min=3D>11::int,=20= max=3D>11))=20>=2012);=0A=20CREATE=20DOMAIN=20domain8=20as=20int=20NOT=20= NULL;=0A+CREATE=20DOMAIN=20domain9=20AS=20int=20CHECK(1=20/=20(value=20-=20= 1)=20>=200);=0A=20=0A=20CREATE=20TABLE=20test_add_domain_col(a=20int);=0A= =20--=20succeeds=20despite=20constraint-violating=20default=20because=20= table=20is=20empty=0A@@=20-299,10=20+300,21=20@@=20ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20a1=20domain5;=0A=20ALTER=20TABLE=20= test_add_domain_col=20DROP=20COLUMN=20a1;=0A=20INSERT=20INTO=20= test_add_domain_col=20VALUES(1),(2);=0A=20=0A+--=20likewise,=20an=20= empty=20table=20succeeds=20if=20the=20domain=20check=20expression=20= errors=0A+CREATE=20TABLE=20test_add_domain_col_empty(a=20int);=0A+ALTER=20= TABLE=20test_add_domain_col_empty=20ADD=20COLUMN=20b=20domain9=20DEFAULT=20= 1;=0A+DROP=20TABLE=20test_add_domain_col_empty;=0A+=0A+--=20but=20errors=20= in=20the=20default=20expression=20itself=20should=20not=20be=20hidden=0A= +CREATE=20TABLE=20test_add_domain_col_bad_default(a=20int);=0A+ALTER=20= TABLE=20test_add_domain_col_bad_default=20ADD=20COLUMN=20b=20domain5=20= DEFAULT=201=20/=200;=0A+DROP=20TABLE=20test_add_domain_col_bad_default;=0A= +=0A=20--=20tests=20with=20non-empty=20table=0A=20ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20b=20domain5;=20--=20table=20= rewrite,=20then=20fail=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20= COLUMN=20b=20domain8;=20--=20table=20rewrite,=20then=20fail=0A=20ALTER=20= TABLE=20test_add_domain_col=20ADD=20COLUMN=20b=20domain5=20DEFAULT=201;=20= --=20table=20rewrite,=20then=20fail=0A+ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20b=20domain9=20DEFAULT=201;=20--=20= table=20rewrite,=20then=20fail=0A=20ALTER=20TABLE=20test_add_domain_col=20= ADD=20COLUMN=20b=20domain5=20DEFAULT=2012;=20--=20ok,=20no=20table=20= rewrite=0A=20=0A=20--=20explicit=20column=20default=20expression=20= overrides=20domain's=20default=0A@@=20-325,8=20+337,8=20@@=20ALTER=20= TABLE=20test_add_domain_col=20ADD=20COLUMN=20f=20domain7;=0A=20=0A=20--=20= domain=20with=20both=20volatile=20and=20non-volatile=20CHECK=20= constraints:=20the=0A=20--=20volatile=20one=20forces=20a=20table=20= rewrite=0A-CREATE=20DOMAIN=20domain9=20AS=20int=20CHECK(value=20>=2010)=20= CHECK((value=20+=20random(min=3D>1::int,=20max=3D>1))=20>=200);=0A-ALTER=20= TABLE=20test_add_domain_col=20ADD=20COLUMN=20g=20domain9=20DEFAULT=2014;=0A= +CREATE=20DOMAIN=20domain10=20AS=20int=20CHECK(value=20>=2010)=20= CHECK((value=20+=20random(min=3D>1::int,=20max=3D>1))=20>=200);=0A+ALTER=20= TABLE=20test_add_domain_col=20ADD=20COLUMN=20g=20domain10=20DEFAULT=20= 14;=0A=20=0A=20--=20virtual=20generated=20columns=20cannot=20have=20= domain=20types=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20COLUMN=20= h=20domain5=0A@@=20-343,6=20+355,7=20@@=20DROP=20DOMAIN=20domain6;=0A=20= DROP=20DOMAIN=20domain7;=0A=20DROP=20DOMAIN=20domain8;=0A=20DROP=20= DOMAIN=20domain9;=0A+DROP=20DOMAIN=20domain10;=0A=20DROP=20FUNCTION=20= foo(INT);=0A=20=0A=20--=20Fall=20back=20to=20full=20rewrite=20for=20= volatile=20expressions=0A--=20=0A2.50.1=20(Apple=20Git-155)=0A=0A= --Apple-Mail=_919D82ED-2F95-4B87-A045-048DE2A3192A Content-Disposition: attachment; filename=v1-s2-0001-Preserve-empty-table-behavior-for-domain-fast-.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v1-s2-0001-Preserve-empty-table-behavior-for-domain-fast-.patch" Content-Transfer-Encoding: quoted-printable =46rom=20697a9568279634b0cf4e000be7850e8845e37ce0=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20"Chao=20Li=20(Evan)"=20=0A= Date:=20Thu,=204=20Jun=202026=2018:06:01=20+0800=0ASubject:=20[PATCH=20= v1-s2]=20Preserve=20empty-table=20behavior=20for=20domain=20fast=20= defaults.=0A=0ACommit=20a0b6ef29a=20allowed=20ALTER=20TABLE=20ADD=20= COLUMN=20to=20use=20the=0Amissing-value=20fast=20path=20for=20domains=20= with=20non-volatile=20constraints.=0AHowever,=20while=20proving=20that=20= the=20default=20can=20be=20stored=20as=20a=20missing=20value,=0Aa=20= domain=20CHECK=20expression=20can=20throw=20an=20error,=20for=20example=20= division=20by=0Azero.=20=20That=20changed=20behavior=20for=20freshly=20= empty=20tables,=20where=20the=20old=0Arewrite=20path=20scanned=20no=20= rows=20and=20therefore=20did=20not=20evaluate=20the=20invalid=0Adomain=20= value=20during=20ALTER=20TABLE.=0A=0ASkip=20the=20fast-default=20probe=20= for=20constrained=20domains=20when=20the=20relation=20has=0Ano=20heap=20= blocks.=20=20In=20that=20case=20there=20is=20no=20data=20rewrite=20to=20= avoid,=20and=20using=0Athe=20rewrite=20path=20preserves=20the=20previous=20= behavior.=0A=0AAdd=20regression=20coverage=20for=20an=20empty=20table=20= whose=20domain=20CHECK=20expression=0Athrows=20during=20evaluation,=20= while=20keeping=20the=20non-empty=20case=20as=20an=20error.=0A=0AAuthor:=20= Chao=20Li=20=0A---=0A=20src/backend/commands/tablecmds.c=20= =20=20=20=20=20=20=20=20=20=20|=2013=20+++++++++++++=0A=20= src/test/regress/expected/fast_default.out=20|=2018=20++++++++++++++++--=0A= =20src/test/regress/sql/fast_default.sql=20=20=20=20=20=20|=2017=20= +++++++++++++++--=0A=203=20files=20changed,=2044=20insertions(+),=204=20= deletions(-)=0A=0Adiff=20--git=20a/src/backend/commands/tablecmds.c=20= b/src/backend/commands/tablecmds.c=0Aindex=20a1845240a98..d74635d0a23=20= 100644=0A---=20a/src/backend/commands/tablecmds.c=0A+++=20= b/src/backend/commands/tablecmds.c=0A@@=20-7548,6=20+7548,7=20@@=20= ATExecAddColumn(List=20**wqueue,=20AlteredTableInfo=20*tab,=20Relation=20= rel,=0A=20=09=09bool=09=09has_domain_constraints;=0A=20=09=09bool=09=09= has_missing=20=3D=20false;=0A=20=09=09bool=09=09has_volatile=20=3D=20= false;=0A+=09=09bool=09=09skip_fast_default=20=3D=20false;=0A=20=0A=20=09= =09/*=0A=20=09=09=20*=20For=20an=20identity=20column,=20we=20can't=20use=20= build_column_default(),=0A@@=20-7599,6=20+7600,17=20@@=20= ATExecAddColumn(List=20**wqueue,=20AlteredTableInfo=20*tab,=20Relation=20= rel,=0A=20=09=09=09=09elog(ERROR,=20"failed=20to=20coerce=20base=20type=20= to=20domain");=0A=20=09=09}=0A=20=0A+=09=09/*=0A+=09=09=20*=20For=20= constrained=20domains,=20a=20physically=20empty=20table=20does=20not=20= need=20the=0A+=09=09=20*=20fast=20default=20optimization.=20=20Use=20the=20= rewrite=20path=20instead,=0A+=09=09=20*=20preserving=20the=20old=20= behavior=20of=20not=20evaluating=20the=20domain=20constraint=0A+=09=09=20= *=20when=20there=20are=20no=20heap=20tuples=20to=20rewrite.=0A+=09=09=20= */=0A+=09=09if=20(has_domain_constraints=20&&=0A+=09=09=09= rel->rd_rel->relkind=20=3D=3D=20RELKIND_RELATION=20&&=0A+=09=09=09= RelationGetNumberOfBlocks(rel)=20=3D=3D=200)=0A+=09=09=09= skip_fast_default=20=3D=20true;=0A+=0A=20=09=09if=20(defval)=0A=20=09=09= {=0A=20=09=09=09NewColumnValue=20*newval;=0A@@=20-7632,6=20+7644,7=20@@=20= ATExecAddColumn(List=20**wqueue,=20AlteredTableInfo=20*tab,=20Relation=20= rel,=0A=20=09=09=09=20*=20such=20a=20failure=20is=20only=20raised=20when=20= the=20table=20has=20rows.=0A=20=09=09=09=20*/=0A=20=09=09=09if=20= (rel->rd_rel->relkind=20=3D=3D=20RELKIND_RELATION=20&&=0A+=09=09=09=09= !skip_fast_default=20&&=0A=20=09=09=09=09!colDef->generated=20&&=0A=20=09= =09=09=09!has_volatile=20&&=0A=20=09=09=09=09= !contain_volatile_functions((Node=20*)=20defval))=0Adiff=20--git=20= a/src/test/regress/expected/fast_default.out=20= b/src/test/regress/expected/fast_default.out=0Aindex=20= 5813f1c61a5..1bcc7be43e3=20100644=0A---=20= a/src/test/regress/expected/fast_default.out=0A+++=20= b/src/test/regress/expected/fast_default.out=0A@@=20-322,12=20+322,23=20= @@=20CREATE=20DOMAIN=20domain5=20AS=20int=20CHECK(value=20>=2010)=20= DEFAULT=208;=0A=20CREATE=20DOMAIN=20domain6=20as=20int=20CHECK(value=20>=20= 10)=20DEFAULT=20random(min=3D>11,=20max=3D>100);=0A=20CREATE=20DOMAIN=20= domain7=20as=20int=20CHECK((value=20+=20random(min=3D>11::int,=20= max=3D>11))=20>=2012);=0A=20CREATE=20DOMAIN=20domain8=20as=20int=20NOT=20= NULL;=0A+CREATE=20DOMAIN=20domain9=20AS=20int=20CHECK(1=20/=20(value=20-=20= 1)=20>=200);=0A=20CREATE=20TABLE=20test_add_domain_col(a=20int);=0A=20--=20= succeeds=20despite=20constraint-violating=20default=20because=20table=20= is=20empty=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20COLUMN=20a1=20= domain5;=0A=20NOTICE:=20=20rewriting=20table=20test_add_domain_col=20for=20= reason=202=0A=20ALTER=20TABLE=20test_add_domain_col=20DROP=20COLUMN=20= a1;=0A=20INSERT=20INTO=20test_add_domain_col=20VALUES(1),(2);=0A+--=20= likewise,=20an=20empty=20table=20succeeds=20if=20the=20domain=20check=20= expression=20errors=0A+CREATE=20TABLE=20test_add_domain_col_empty(a=20= int);=0A+ALTER=20TABLE=20test_add_domain_col_empty=20ADD=20COLUMN=20b=20= domain9=20DEFAULT=201;=0A+NOTICE:=20=20rewriting=20table=20= test_add_domain_col_empty=20for=20reason=202=0A+DROP=20TABLE=20= test_add_domain_col_empty;=0A+--=20but=20errors=20in=20the=20default=20= expression=20itself=20should=20not=20be=20hidden=0A+CREATE=20TABLE=20= test_add_domain_col_bad_default(a=20int);=0A+ALTER=20TABLE=20= test_add_domain_col_bad_default=20ADD=20COLUMN=20b=20domain5=20DEFAULT=20= 1=20/=200;=0A+ERROR:=20=20division=20by=20zero=0A+DROP=20TABLE=20= test_add_domain_col_bad_default;=0A=20--=20tests=20with=20non-empty=20= table=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20COLUMN=20b=20= domain5;=20--=20table=20rewrite,=20then=20fail=0A=20NOTICE:=20=20= rewriting=20table=20test_add_domain_col=20for=20reason=202=0A@@=20-338,6=20= +349,8=20@@=20ERROR:=20=20domain=20domain8=20does=20not=20allow=20null=20= values=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20COLUMN=20b=20= domain5=20DEFAULT=201;=20--=20table=20rewrite,=20then=20fail=0A=20= NOTICE:=20=20rewriting=20table=20test_add_domain_col=20for=20reason=202=0A= =20ERROR:=20=20value=20for=20domain=20domain5=20violates=20check=20= constraint=20"domain5_check"=0A+ALTER=20TABLE=20test_add_domain_col=20= ADD=20COLUMN=20b=20domain9=20DEFAULT=201;=20--=20fast=20proof=20fails=0A= +ERROR:=20=20division=20by=20zero=0A=20ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20b=20domain5=20DEFAULT=2012;=20--=20= ok,=20no=20table=20rewrite=0A=20--=20explicit=20column=20default=20= expression=20overrides=20domain's=20default=0A=20--=20expression,=20so=20= no=20table=20rewrite=0A@@=20-365,8=20+378,8=20@@=20ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20f=20domain7;=0A=20NOTICE:=20=20= rewriting=20table=20test_add_domain_col=20for=20reason=202=0A=20--=20= domain=20with=20both=20volatile=20and=20non-volatile=20CHECK=20= constraints:=20the=0A=20--=20volatile=20one=20forces=20a=20table=20= rewrite=0A-CREATE=20DOMAIN=20domain9=20AS=20int=20CHECK(value=20>=2010)=20= CHECK((value=20+=20random(min=3D>1::int,=20max=3D>1))=20>=200);=0A-ALTER=20= TABLE=20test_add_domain_col=20ADD=20COLUMN=20g=20domain9=20DEFAULT=2014;=0A= +CREATE=20DOMAIN=20domain10=20AS=20int=20CHECK(value=20>=2010)=20= CHECK((value=20+=20random(min=3D>1::int,=20max=3D>1))=20>=200);=0A+ALTER=20= TABLE=20test_add_domain_col=20ADD=20COLUMN=20g=20domain10=20DEFAULT=20= 14;=0A=20NOTICE:=20=20rewriting=20table=20test_add_domain_col=20for=20= reason=202=0A=20--=20virtual=20generated=20columns=20cannot=20have=20= domain=20types=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20COLUMN=20= h=20domain5=0A@@=20-383,6=20+396,7=20@@=20DROP=20DOMAIN=20domain6;=0A=20= DROP=20DOMAIN=20domain7;=0A=20DROP=20DOMAIN=20domain8;=0A=20DROP=20= DOMAIN=20domain9;=0A+DROP=20DOMAIN=20domain10;=0A=20DROP=20FUNCTION=20= foo(INT);=0A=20--=20Fall=20back=20to=20full=20rewrite=20for=20volatile=20= expressions=0A=20CREATE=20TABLE=20T(pk=20INT=20NOT=20NULL=20PRIMARY=20= KEY);=0Adiff=20--git=20a/src/test/regress/sql/fast_default.sql=20= b/src/test/regress/sql/fast_default.sql=0Aindex=20= e5d9a3d2fd1..8b58c6d44d0=20100644=0A---=20= a/src/test/regress/sql/fast_default.sql=0A+++=20= b/src/test/regress/sql/fast_default.sql=0A@@=20-292,6=20+292,7=20@@=20= CREATE=20DOMAIN=20domain5=20AS=20int=20CHECK(value=20>=2010)=20DEFAULT=20= 8;=0A=20CREATE=20DOMAIN=20domain6=20as=20int=20CHECK(value=20>=2010)=20= DEFAULT=20random(min=3D>11,=20max=3D>100);=0A=20CREATE=20DOMAIN=20= domain7=20as=20int=20CHECK((value=20+=20random(min=3D>11::int,=20= max=3D>11))=20>=2012);=0A=20CREATE=20DOMAIN=20domain8=20as=20int=20NOT=20= NULL;=0A+CREATE=20DOMAIN=20domain9=20AS=20int=20CHECK(1=20/=20(value=20-=20= 1)=20>=200);=0A=20=0A=20CREATE=20TABLE=20test_add_domain_col(a=20int);=0A= =20--=20succeeds=20despite=20constraint-violating=20default=20because=20= table=20is=20empty=0A@@=20-299,10=20+300,21=20@@=20ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20a1=20domain5;=0A=20ALTER=20TABLE=20= test_add_domain_col=20DROP=20COLUMN=20a1;=0A=20INSERT=20INTO=20= test_add_domain_col=20VALUES(1),(2);=0A=20=0A+--=20likewise,=20an=20= empty=20table=20succeeds=20if=20the=20domain=20check=20expression=20= errors=0A+CREATE=20TABLE=20test_add_domain_col_empty(a=20int);=0A+ALTER=20= TABLE=20test_add_domain_col_empty=20ADD=20COLUMN=20b=20domain9=20DEFAULT=20= 1;=0A+DROP=20TABLE=20test_add_domain_col_empty;=0A+=0A+--=20but=20errors=20= in=20the=20default=20expression=20itself=20should=20not=20be=20hidden=0A= +CREATE=20TABLE=20test_add_domain_col_bad_default(a=20int);=0A+ALTER=20= TABLE=20test_add_domain_col_bad_default=20ADD=20COLUMN=20b=20domain5=20= DEFAULT=201=20/=200;=0A+DROP=20TABLE=20test_add_domain_col_bad_default;=0A= +=0A=20--=20tests=20with=20non-empty=20table=0A=20ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20b=20domain5;=20--=20table=20= rewrite,=20then=20fail=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20= COLUMN=20b=20domain8;=20--=20table=20rewrite,=20then=20fail=0A=20ALTER=20= TABLE=20test_add_domain_col=20ADD=20COLUMN=20b=20domain5=20DEFAULT=201;=20= --=20table=20rewrite,=20then=20fail=0A+ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20b=20domain9=20DEFAULT=201;=20--=20= fast=20proof=20fails=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20= COLUMN=20b=20domain5=20DEFAULT=2012;=20--=20ok,=20no=20table=20rewrite=0A= =20=0A=20--=20explicit=20column=20default=20expression=20overrides=20= domain's=20default=0A@@=20-325,8=20+337,8=20@@=20ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20f=20domain7;=0A=20=0A=20--=20domain=20= with=20both=20volatile=20and=20non-volatile=20CHECK=20constraints:=20the=0A= =20--=20volatile=20one=20forces=20a=20table=20rewrite=0A-CREATE=20DOMAIN=20= domain9=20AS=20int=20CHECK(value=20>=2010)=20CHECK((value=20+=20= random(min=3D>1::int,=20max=3D>1))=20>=200);=0A-ALTER=20TABLE=20= test_add_domain_col=20ADD=20COLUMN=20g=20domain9=20DEFAULT=2014;=0A= +CREATE=20DOMAIN=20domain10=20AS=20int=20CHECK(value=20>=2010)=20= CHECK((value=20+=20random(min=3D>1::int,=20max=3D>1))=20>=200);=0A+ALTER=20= TABLE=20test_add_domain_col=20ADD=20COLUMN=20g=20domain10=20DEFAULT=20= 14;=0A=20=0A=20--=20virtual=20generated=20columns=20cannot=20have=20= domain=20types=0A=20ALTER=20TABLE=20test_add_domain_col=20ADD=20COLUMN=20= h=20domain5=0A@@=20-343,6=20+355,7=20@@=20DROP=20DOMAIN=20domain6;=0A=20= DROP=20DOMAIN=20domain7;=0A=20DROP=20DOMAIN=20domain8;=0A=20DROP=20= DOMAIN=20domain9;=0A+DROP=20DOMAIN=20domain10;=0A=20DROP=20FUNCTION=20= foo(INT);=0A=20=0A=20--=20Fall=20back=20to=20full=20rewrite=20for=20= volatile=20expressions=0A--=20=0A2.50.1=20(Apple=20Git-155)=0A=0A= --Apple-Mail=_919D82ED-2F95-4B87-A045-048DE2A3192A--