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 1wWkOv-002pVI-0Z for pgsql-hackers@arkaria.postgresql.org; Tue, 09 Jun 2026 00:33:05 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wWkOr-004PQo-24 for pgsql-hackers@arkaria.postgresql.org; Tue, 09 Jun 2026 00:33:01 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1wWkOr-004PQg-0a for pgsql-hackers@lists.postgresql.org; Tue, 09 Jun 2026 00:33:01 +0000 Received: from mail-pl1-x62f.google.com ([2607:f8b0:4864:20::62f]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wWkOo-000000023vn-1bii for pgsql-hackers@lists.postgresql.org; Tue, 09 Jun 2026 00:33:00 +0000 Received: by mail-pl1-x62f.google.com with SMTP id d9443c01a7336-2c0b944f6edso50895835ad.2 for ; Mon, 08 Jun 2026 17:32:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1780965176; x=1781569976; darn=lists.postgresql.org; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:from:to:cc:subject:date:message-id:reply-to; bh=HU2QMfpQEgVOXG78OafOvqOE4nkvw4ZDmz8XxUGvUDk=; b=Y1v3wJzlP92Y1toHNALhZmUc90UuEDx9EHKOnHzG18uPtB6ROO7bUI3UOnhETYC6C9 w1oBuPCuGOAkGqPDXhv0TBtnAd0frPMZjjwPtmLHMiukykfm5w2zxipQWg05S0XTIb6B MRnl3VSwcggfWro8xqSa5kv0p8tGIVHW1ePqEHptjinouplm9e7lMSXsr8btXsHq3VIh XXn94tVrrUUOmzn30eB3VirXUvkDevLMIQkasV4+1TvIXFXac0AI4mMgypBwCooIamZd NxYWuVZNW9ZRq5hcEeJHQfZcEs1Ahzu1U7ZvkSqvjLrKXV7y9ExxZg20S7Zh+pQFZjt6 IgdQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780965176; x=1781569976; h=references:to:cc:in-reply-to:date:subject:mime-version:message-id :from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=HU2QMfpQEgVOXG78OafOvqOE4nkvw4ZDmz8XxUGvUDk=; b=JOwtZ0bNB9o6hQKfcTjz7yoz3Taald6KIMAZB0DMZBfops6yeltT0bNzn4telcUmcC j5utBwgmEvVFJzpdrE3dtk8KNVTLx3gXheQFVGTK7CWMUxRBQo3DYBQurthRgKqVbmvj yfVOk4FHQfHuE/Ubc9MLml7LJ/XA0QjaNFAzzcYh/bn/oNcVnRxRXwpcJ7PhiCWuVsm7 Q/N2NNs2jKdn0Z7cYrHjT2L9o1VQbR50xmyLFSlBaAmqG5BwiPUiWNejHUJ3lgLIIVVk mL1TSObORuxauW/wqRcJBk9I1Gco20SKZyWkJzIVKimN+j+8HpGABGctLQzh3YgzvEaL kVYw== X-Forwarded-Encrypted: i=1; AFNElJ/Onf9V63kGi0zzgPp6AJqmGlda5TS/4bopHai4QSAWvVDR8mzuVdg4N9uarVpOpwXzvrGwD/hBvgZw6JTK@lists.postgresql.org X-Gm-Message-State: AOJu0Yy64BYQ7yTrWs6GEOWyp9DMNvyxJC+WIbZUnATo8biFRxgPOfdx Q2j8BvM4fPheIi+5lHj0kB6WcXUbsXJ12X2PqDX/s1v/PkiLKsE2//z+ X-Gm-Gg: Acq92OGUChELuPmAI+6r1Fgxcy+B+Xdw/1QDf4PUdn8UnLmQ8k+ImChusKhNB/O3KIg xwVyX5LwrFPHh7D6qdKu0fstLYkuxB1uXg0ZNQq49VMZ+O7htpFuiVT7FPdC7J02eorfSJiXW9s YKc3jjEiO24NIiIlLu8JT+m0Lu7/nHDnfdfPTzo/1mH9avBM5nEVIF1Yq4TZSdT5pOJxK4u/VqF XP4fIpLdEn0lmWsEVIC4UduabpM5JL+E15EU6H09BMpk0If+YjsF7/6+CMSinKADWbYl3hxoHla 5skyaJPJF5Pbp+O3va3+WzC5aWoTzuCsxWqoaxs1BNaMcnacMN10dpZ44FIY6B9dj+xNMHy5LTf Dwxv91Myz+pOgvWSjz7OL6qbhI0bdoGMOaMZZ/W6PA5RNQxL9zgrg6OS893RQQ1cmZIUiu5DpAd OwbKNoYTY1TtTY2iwK3/9hCuFLndxQuU1YoBcQgoQrhQ== X-Received: by 2002:a17:902:d2c4:b0:2c1:a19:839b with SMTP id d9443c01a7336-2c1e8225c2emr214734345ad.27.1780965175830; Mon, 08 Jun 2026 17:32:55 -0700 (PDT) Received: from smtpclient.apple ([203.76.245.26]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2c164f96b3fsm200096015ad.29.2026.06.08.17.32.53 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Mon, 08 Jun 2026 17:32:54 -0700 (PDT) From: Chao Li Message-Id: <240CAFC9-8053-4660-A1D5-0CB2610AB257@gmail.com> Content-Type: multipart/mixed; boundary="Apple-Mail=_2D4E6186-C1AE-456E-A2AC-37699D344460" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3864.600.51.1.1\)) Subject: Re: Fix bug of CHECK constraint enforceability recursion Date: Tue, 9 Jun 2026 08:32:19 +0800 In-Reply-To: Cc: jian he , Zsolt Parragi , pgsql-hackers@lists.postgresql.org To: =?utf-8?Q?=C3=81lvaro_Herrera?= References: 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=_2D4E6186-C1AE-456E-A2AC-37699D344460 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 > On Jun 8, 2026, at 22:52, =C3=81lvaro Herrera = wrote: >=20 > On 2026-Jun-08, Chao Li wrote: >=20 >>> Keep errmsg() messages on a single line to improve grepability. > G >> I=E2=80=99m not sure this needs to be changed. The message is quite = long, and >> there are existing precedents for splitting long errmsg() strings >> across multiple lines. For example: >> ``` >> if (getExtensionOfObject(NamespaceRelationId, nspOid) =3D=3D = extensionOid) >> ereport(ERROR, >> (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), >> errmsg("cannot move extension \"%s\" into schema \"%s\" " >> "because the extension contains the schema", >> extensionName, newschema))); >> ``` >>=20 >> So, this does not seem to be a strict rule. I also don=E2=80=99t = think this >> hurts grepability much in practice, since searching for the >> distinctive part of the message still finds it. >=20 > Personally I find myself upset whenever I come across messages split = in > this way. Kindly do not add more if you can avoid it. It [my dislike > for that] is not strong enough to create a commit to stitch them back, > but I frequently do so in my local editor and later discard the change > when collecting changes to commit via "git add -p". >=20 > Greppability is subjective -- you don't know which part is > "distinctive". For instance, when looking for message > refactoring/rewording I grep for things such as "cannot.*schema" or > such, and any arbitrary splitting could potentially thwart that. >=20 >=20 > Lastly, when the message is in a single line, grammatical mistakes are > easier to see, such as the missing "the" in=20 > errmsg("cannot mark inherited constraint \"%s\" as NOT ENFORCED = because [the] matching constraint on parent table \"%s\" is ENFORCED", = ...) >=20 Thanks for the explanation. I will treat that as a rule in future when = working for PG code. So, noted. >=20 > In this case I would also move the NOT ENFORCED clause out of the > translatable message and replace it with %s; and I'm wondering whether > the part after "because" should be errdetail or not. In v10, I split the =E2=80=9Cbecause=E2=80=9D part to a errdetail, also = moved out NOT ENFORCED out of the translate message. Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/ --Apple-Mail=_2D4E6186-C1AE-456E-A2AC-37699D344460 Content-Disposition: attachment; filename=v10-0001-Prevent-inherited-CHECK-constraints-from-being-w.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v10-0001-Prevent-inherited-CHECK-constraints-from-being-w.patch" Content-Transfer-Encoding: quoted-printable =46rom=209d85b9b0e646fa77be1cc5a264bf5cf384cc0d1b=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20"Chao=20Li=20(Evan)"=20=0A= Date:=20Wed,=2027=20May=202026=2013:49:29=20+0800=0ASubject:=20[PATCH=20= v10=201/3]=20Prevent=20inherited=20CHECK=20constraints=20from=20being=0A=20= weakened=0AMIME-Version:=201.0=0AContent-Type:=20text/plain;=20= charset=3DUTF-8=0AContent-Transfer-Encoding:=208bit=0A=0ADisallow=20= marking=20an=20inherited=20CHECK=20constraint=20as=20NOT=20ENFORCED=20= when=20an=0Aequivalent=20parent=20constraint=20remains=20ENFORCED.=20= This=20prevents=20ALTER=0ACONSTRAINT=20from=20producing=20a=20child=20= constraint=20that=20is=20weaker=20than=20one=20of=0Aits=20inherited=20= parent=20definitions.=0A=0AWhen=20recursively=20altering=20a=20CHECK=20= constraint=20to=20NOT=20ENFORCED,=20collect=20the=0Acorresponding=20= constraints=20in=20the=20affected=20inheritance=20subtree=20and=20ignore=0A= those=20parent=20constraints=20while=20checking=20descendants.=20If=20a=20= descendant=20also=0Ainherits=20an=20equivalent=20ENFORCED=20constraint=20= from=20a=20parent=20outside=20the=0Acurrent=20ALTER,=20keep=20the=20= descendant=20ENFORCED=20by=20merging=20to=20the=20stricter=0Astate.=0A=0A= Add=20regression=20coverage=20for=20direct=20child=20ALTER,=20ONLY=20= ALTER,=20mixed-parent=0Ainheritance,=20and=20a=20common-ancestor=20= diamond=20where=20all=20equivalent=20inherited=0Aconstraints=20can=20be=20= changed=20together.=0A=0AAuthor:=20Chao=20Li=20=0A= Reviewed-by:=20Jian=20He=20=0AReviewed-by:=20= Zsolt=20Parragi=20=0AReviewed-by:=20=C3=81lvaro= =20Herrera=20=0ADiscussion:=20= https://postgr.es/m/E74C57FA-1DD0-4C8E-8FB1-538034752592@gmail.com=0A---=0A= =20src/backend/commands/tablecmds.c=20=20=20=20=20=20=20=20=20=20|=20246=20= ++++++++++++++++++++--=0A=20src/test/regress/expected/constraints.out=20= |=20=2015=20+-=0A=20src/test/regress/expected/inherit.out=20=20=20=20=20= |=20=2092=20++++++++=0A=20src/test/regress/sql/constraints.sql=20=20=20=20= =20=20|=20=20=205=20+-=0A=20src/test/regress/sql/inherit.sql=20=20=20=20=20= =20=20=20=20=20|=20=2052=20+++++=0A=205=20files=20changed,=20385=20= insertions(+),=2025=20deletions(-)=0A=0Adiff=20--git=20= a/src/backend/commands/tablecmds.c=20b/src/backend/commands/tablecmds.c=0A= index=20a1845240a98..f22bed5f40d=20100644=0A---=20= a/src/backend/commands/tablecmds.c=0A+++=20= b/src/backend/commands/tablecmds.c=0A@@=20-437,6=20+437,7=20@@=20static=20= bool=20ATExecAlterFKConstrEnforceability(List=20**wqueue,=20= ATAlterConstraint=20*=0A=20static=20bool=20= ATExecAlterCheckConstrEnforceability(List=20**wqueue,=20= ATAlterConstraint=20*cmdcon,=0A=20=09=09=09=09=09=09=09=09=09=09=09=09=20= Relation=20conrel,=20HeapTuple=20contuple,=0A=20=09=09=09=09=09=09=09=09=09= =09=09=09=20bool=20recurse,=20bool=20recursing,=0A+=09=09=09=09=09=09=09=09= =09=09=09=09=20List=20*changing_conids,=0A=20=09=09=09=09=09=09=09=09=09=09= =09=09=20LOCKMODE=20lockmode);=0A=20static=20bool=20= ATExecAlterConstrDeferrability(List=20**wqueue,=20ATAlterConstraint=20= *cmdcon,=0A=20=09=09=09=09=09=09=09=09=09=09=20=20=20Relation=20conrel,=20= Relation=20tgrel,=20Relation=20rel,=0A@@=20-459,6=20+460,7=20@@=20static=20= void=20AlterFKConstrEnforceabilityRecurse(List=20**wqueue,=20= ATAlterConstraint=0A=20static=20void=20= AlterCheckConstrEnforceabilityRecurse(List=20**wqueue,=20= ATAlterConstraint=20*cmdcon,=0A=20=09=09=09=09=09=09=09=09=09=09=09=09=20= =20Relation=20conrel,=20Oid=20conrelid,=0A=20=09=09=09=09=09=09=09=09=09=09= =09=09=20=20bool=20recurse,=20bool=20recursing,=0A+=09=09=09=09=09=09=09=09= =09=09=09=09=20=20List=20*changing_conids,=0A=20=09=09=09=09=09=09=09=09=09= =09=09=09=20=20LOCKMODE=20lockmode);=0A=20static=20void=20= AlterConstrDeferrabilityRecurse(List=20**wqueue,=20ATAlterConstraint=20= *cmdcon,=0A=20=09=09=09=09=09=09=09=09=09=09=09Relation=20conrel,=20= Relation=20tgrel,=20Relation=20rel,=0A@@=20-466,6=20+468,10=20@@=20= static=20void=20AlterConstrDeferrabilityRecurse(List=20**wqueue,=20= ATAlterConstraint=20*cm=0A=20=09=09=09=09=09=09=09=09=09=09=09List=20= **otherrelids,=20LOCKMODE=20lockmode);=0A=20static=20void=20= AlterConstrUpdateConstraintEntry(ATAlterConstraint=20*cmdcon,=20Relation=20= conrel,=0A=20=09=09=09=09=09=09=09=09=09=09=09=20HeapTuple=20contuple);=0A= +static=20bool=20ATCheckCheckConstrHasEnforcedParent(Relation=20conrel,=20= Relation=20rel,=0A+=09=09=09=09=09=09=09=09=09=09=09=09HeapTuple=20= contuple,=0A+=09=09=09=09=09=09=09=09=09=09=09=09List=20= *changing_conids,=0A+=09=09=09=09=09=09=09=09=09=09=09=09Oid=20= *enforced_parentoid);=0A=20static=20ObjectAddress=20= ATExecValidateConstraint(List=20**wqueue,=0A=20=09=09=09=09=09=09=09=09=09= =09=09=20=20Relation=20rel,=20char=20*constrName,=0A=20=09=09=09=09=09=09= =09=09=09=09=09=20=20bool=20recurse,=20bool=20recursing,=20LOCKMODE=20= lockmode);=0A@@=20-477,6=20+483,7=20@@=20static=20void=20= QueueCheckConstraintValidation(List=20**wqueue,=20Relation=20conrel,=20= Relat=0A=20static=20void=20QueueNNConstraintValidation(List=20**wqueue,=20= Relation=20conrel,=20Relation=20rel,=0A=20=09=09=09=09=09=09=09=09=09=09= HeapTuple=20contuple,=20bool=20recurse,=20bool=20recursing,=0A=20=09=09=09= =09=09=09=09=09=09=09LOCKMODE=20lockmode);=0A+static=20bool=20= constraints_equivalent(HeapTuple=20a,=20HeapTuple=20b,=20TupleDesc=20= tupleDesc);=0A=20static=20int=09transformColumnNameList(Oid=20relId,=20= List=20*colList,=0A=20=09=09=09=09=09=09=09=09=09int16=20*attnums,=20Oid=20= *atttypids,=20Oid=20*attcollids);=0A=20static=20int=09= transformFkeyGetPrimaryKey(Relation=20pkrel,=20Oid=20*indexOid,=0A@@=20= -12484,7=20+12491,7=20@@=20ATExecAlterConstraintInternal(List=20= **wqueue,=20ATAlterConstraint=20*cmdcon,=0A=20=09=09else=20if=20= (currcon->contype=20=3D=3D=20CONSTRAINT_CHECK)=0A=20=09=09=09changed=20=3D= =20ATExecAlterCheckConstrEnforceability(wqueue,=20cmdcon,=20conrel,=0A=20= =09=09=09=09=09=09=09=09=09=09=09=09=09=09=20=20=20contuple,=20recurse,=20= false,=0A-=09=09=09=09=09=09=09=09=09=09=09=09=09=09=20=20=20lockmode);=0A= +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=20=20=20NIL,=20lockmode);=0A=20= =09}=0A=20=09else=20if=20(cmdcon->alterDeferrability=20&&=0A=20=09=09=09=20= ATExecAlterConstrDeferrability(wqueue,=20cmdcon,=20conrel,=20tgrel,=20= rel,=0A@@=20-12671,12=20+12678,16=20@@=20= ATExecAlterFKConstrEnforceability(List=20**wqueue,=20ATAlterConstraint=20= *cmdcon,=0A=20static=20bool=0A=20= ATExecAlterCheckConstrEnforceability(List=20**wqueue,=20= ATAlterConstraint=20*cmdcon,=0A=20=09=09=09=09=09=09=09=09=09=20Relation=20= conrel,=20HeapTuple=20contuple,=0A-=09=09=09=09=09=09=09=09=09=20bool=20= recurse,=20bool=20recursing,=20LOCKMODE=20lockmode)=0A+=09=09=09=09=09=09= =09=09=09=20bool=20recurse,=20bool=20recursing,=0A+=09=09=09=09=09=09=09=09= =09=20List=20*changing_conids,=0A+=09=09=09=09=09=09=09=09=09=20LOCKMODE=20= lockmode)=0A=20{=0A=20=09Form_pg_constraint=20currcon;=0A=20=09Relation=09= rel;=0A=20=09bool=09=09changed=20=3D=20false;=0A=20=09List=09=20=20=20= *children=20=3D=20NIL;=0A+=09bool=09=09target_enforced=20=3D=20= cmdcon->is_enforced;=0A+=09Oid=09=09=09enforced_parentoid=20=3D=20= InvalidOid;=0A=20=0A=20=09/*=20Since=20this=20function=20recurses,=20it=20= could=20be=20driven=20to=20stack=20overflow=20*/=0A=20=09= check_stack_depth();=0A@@=20-12693,16=20+12704,57=20@@=20= ATExecAlterCheckConstrEnforceability(List=20**wqueue,=20= ATAlterConstraint=20*cmdcon,=0A=20=09=20*/=0A=20=09rel=20=3D=20= table_open(currcon->conrelid,=20NoLock);=0A=20=0A-=09if=20= (currcon->conenforced=20!=3D=20cmdcon->is_enforced)=0A+=09/*=0A+=09=20*=20= When=20setting=20a=20constraint=20to=20NOT=20ENFORCED,=20check=20whether=20= any=20matching=0A+=09=20*=20parent=20constraint=20remains=20ENFORCED=20= and=20is=20not=20part=20of=20this=20ALTER.=0A+=09=20*=0A+=09=20*=20For=20= a=20direct=20ALTER=20of=20an=20inherited=20constraint,=20reject=20the=20= command,=0A+=09=20*=20because=20the=20child=20cannot=20be=20weakened=20= while=20its=20parent=20remains=20enforced.=0A+=09=20*=0A+=09=20*=20= During=20recursion,=20another=20parent=20outside=20this=20ALTER=20may=20= still=20enforce=0A+=09=20*=20the=20same=20constraint=20in=20a=20regular=20= inheritance=20hierarchy.=20In=20that=20case,=0A+=09=20*=20keep=20the=20= child=20constraint=20ENFORCED=20so=20that=20its=20merged=20= enforceability=0A+=09=20*=20still=20reflects=20the=20remaining=20= enforced=20parent.=20Partitions=20do=20not=20need=0A+=09=20*=20this=20= recursive=20parent=20check=20because=20a=20partition=20can=20have=20only=20= one=0A+=09=20*=20direct=20parent.=0A+=09=20*/=0A+=09if=20= (!cmdcon->is_enforced=20&&=0A+=09=09(!recursing=20||=20= !rel->rd_rel->relispartition)=20&&=0A+=09=09= ATCheckCheckConstrHasEnforcedParent(conrel,=20rel,=20contuple,=0A+=09=09=09= =09=09=09=09=09=09=09=09changing_conids,=0A+=09=09=09=09=09=09=09=09=09=09= =09&enforced_parentoid))=0A=20=09{=0A-=09=09= AlterConstrUpdateConstraintEntry(cmdcon,=20conrel,=20contuple);=0A+=09=09= if=20(!recursing)=0A+=09=09=09ereport(ERROR,=0A+=09=09=09=09=09= errcode(ERRCODE_INVALID_OBJECT_DEFINITION),=0A+=09=09=09=09=09= errmsg("cannot=20mark=20inherited=20constraint=20\"%s\"=20as=20%s",=0A+=09= =09=09=09=09=09=20=20=20NameStr(currcon->conname),=0A+=09=09=09=09=09=09=20= =20=20"NOT=20ENFORCED"),=0A+=09=09=09=09=09errdetail("The=20matching=20= constraint=20on=20parent=20table=20\"%s\"=20is=20%s.",=0A+=09=09=09=09=09= =09=09=20=20get_rel_name(enforced_parentoid),=20"ENFORCED"));=0A+=0A+=09=09= target_enforced=20=3D=20true;=0A+=09}=0A+=0A+=09/*=0A+=09=20*=20Update=20= to=20the=20merged=20enforceability=20if=20needed.=20This=20may=20differ=20= from=20the=0A+=09=20*=20requested=20enforceability=20when=20another=20= matching=20parent=20constraint=0A+=09=20*=20remains=20enforced.=0A+=09=20= */=0A+=09if=20(currcon->conenforced=20!=3D=20target_enforced)=0A+=09{=0A= +=09=09ATAlterConstraint=20updatecon=20=3D=20*cmdcon;=0A+=0A+=09=09= updatecon.is_enforced=20=3D=20target_enforced;=0A+=09=09= AlterConstrUpdateConstraintEntry(&updatecon,=20conrel,=20contuple);=0A=20= =09=09changed=20=3D=20true;=0A=20=09}=0A=20=0A=20=09/*=0A=20=09=20*=20= Note=20that=20we=20must=20recurse=20even=20when=20trying=20to=20change=20= a=20check=20constraint=0A=20=09=20*=20to=20not=20enforced=20if=20it=20is=20= already=20not=20enforced,=20in=20case=20descendant=0A-=09=20*=20= constraints=20might=20be=20enforced=20and=20need=20to=20be=20changed=20= to=20not=20enforced.=0A+=09=20*=20constraints=20might=20be=20enforced=20= and=20need=20to=20be=20changed=20to=20not=20enforced,=0A+=09=20*=20= unless=20they=20still=20inherit=20an=20enforced=20constraint=20from=20= another=20parent.=0A=20=09=20*=20Conversely,=20we=20should=20do=20= nothing=20if=20a=20constraint=20is=20being=20set=20to=0A=20=09=20*=20= enforced=20and=20is=20already=20enforced,=20as=20descendant=20= constraints=20cannot=20be=0A=20=09=20*=20different=20in=20that=20case.=0A= @@=20-12715,28=20+12767,62=20@@=20= ATExecAlterCheckConstrEnforceability(List=20**wqueue,=20= ATAlterConstraint=20*cmdcon,=0A=20=09=09=20*=20try=20to=20look=20for=20= it=20in=20the=20children.=0A=20=09=09=20*/=0A=20=09=09if=20(!recursing=20= &&=20!currcon->connoinherit)=0A+=09=09{=0A+=09=09=09= Assert(changing_conids=20=3D=3D=20NIL);=0A+=0A=20=09=09=09children=20=3D=20= find_all_inheritors(RelationGetRelid(rel),=0A=20=09=09=09=09=09=09=09=09=09= =09=20=20=20lockmode,=20NULL);=0A=20=0A+=09=09=09/*=0A+=09=09=09=20*=20= When=20setting=20NOT=20ENFORCED,=20build=20the=20set=20of=20equivalent=20= CHECK=0A+=09=09=09=20*=20constraints=20that=20this=20command=20will=20= attempt=20to=20change=20before=0A+=09=09=09=20*=20visiting=20= descendants.=20The=20root=20itself=20has=20already=20been=20checked=0A+=09= =09=09=20*=20above.=0A+=09=09=09=20*/=0A+=09=09=09if=20= (!cmdcon->is_enforced)=0A+=09=09=09=09changing_conids=20=3D=20= list_make1_oid(currcon->oid);=0A+=0A+=09=09=09foreach_oid(childoid,=20= children)=0A+=09=09=09{=0A+=09=09=09=09if=20(childoid=20=3D=3D=20= RelationGetRelid(rel))=0A+=09=09=09=09=09continue;=0A+=0A+=09=09=09=09/*=0A= +=09=09=09=09=20*=20If=20we=20are=20told=20not=20to=20recurse,=20there=20= had=20better=20not=20be=20any=0A+=09=09=09=09=20*=20child=20tables,=20= because=20we=20can't=20change=20constraint=0A+=09=09=09=09=20*=20= enforceability=20on=20the=20parent=20unless=20we=20have=20changed=0A+=09=09= =09=09=20*=20enforceability=20for=20all=20child.=0A+=09=09=09=09=20*/=0A= +=09=09=09=09if=20(!recurse)=0A+=09=09=09=09=09ereport(ERROR,=0A+=09=09=09= =09=09=09=09errcode(ERRCODE_INVALID_TABLE_DEFINITION),=0A+=09=09=09=09=09= =09=09errmsg("constraint=20must=20be=20altered=20on=20child=20tables=20= too"),=0A+=09=09=09=09=09=09=09errhint("Do=20not=20specify=20the=20ONLY=20= keyword."));=0A+=0A+=09=09=09=09/*=0A+=09=09=09=09=20*=20It=20is=20= sufficient=20to=20look=20up=20the=20constraint=20by=20name=20here.=0A+=09= =09=09=09=20*=20Supported=20DDL=20ensures=20that=20inheritable=20CHECK=20= constraints=0A+=09=09=09=09=20*=20with=20the=20same=20name=20have=20= equivalent=20definitions=20when=20they=0A+=09=09=09=09=20*=20are=20= propagated=20to=20children=20or=20when=20inheritance=20is=0A+=09=09=09=09= =20*=20established.=0A+=09=09=09=09=20*/=0A+=09=09=09=09if=20= (!cmdcon->is_enforced)=0A+=09=09=09=09=09changing_conids=20=3D=0A+=09=09=09= =09=09=09list_append_unique_oid(changing_conids,=0A+=09=09=09=09=09=09=09= =09=09=09=09=20=20=20get_relation_constraint_oid(childoid,=0A+=09=09=09=09= =09=09=09=09=09=09=09=09=09=09=09=09=09=09=20=20=20cmdcon->conname,=0A+=09= =09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=20=20=20false));=0A+=09= =09=09}=0A+=09=09}=0A+=0A=20=09=09foreach_oid(childoid,=20children)=0A=20= =09=09{=0A=20=09=09=09if=20(childoid=20=3D=3D=20RelationGetRelid(rel))=0A= =20=09=09=09=09continue;=0A=20=0A-=09=09=09/*=0A-=09=09=09=20*=20If=20we=20= are=20told=20not=20to=20recurse,=20there=20had=20better=20not=20be=20any=0A= -=09=09=09=20*=20child=20tables,=20because=20we=20can't=20change=20= constraint=20enforceability=0A-=09=09=09=20*=20on=20the=20parent=20= unless=20we=20have=20changed=20enforceability=20for=20all=0A-=09=09=09=20= *=20child.=0A-=09=09=09=20*/=0A-=09=09=09if=20(!recurse)=0A-=09=09=09=09= ereport(ERROR,=0A-=09=09=09=09=09=09= errcode(ERRCODE_INVALID_TABLE_DEFINITION),=0A-=09=09=09=09=09=09= errmsg("constraint=20must=20be=20altered=20on=20child=20tables=20too"),=0A= -=09=09=09=09=09=09errhint("Do=20not=20specify=20the=20ONLY=20= keyword."));=0A-=0A=20=09=09=09= AlterCheckConstrEnforceabilityRecurse(wqueue,=20cmdcon,=20conrel,=0A=20=09= =09=09=09=09=09=09=09=09=09=09=09=20=20childoid,=20false,=20true,=0A+=09=09= =09=09=09=09=09=09=09=09=09=09=20=20changing_conids,=0A=20=09=09=09=09=09= =09=09=09=09=09=09=09=20=20lockmode);=0A=20=09=09}=0A=20=09}=0A@@=20= -12748,7=20+12834,7=20@@=20ATExecAlterCheckConstrEnforceability(List=20= **wqueue,=20ATAlterConstraint=20*cmdcon,=0A=20=09=20*/=0A=20=09if=20= (rel->rd_rel->relkind=20=3D=3D=20RELKIND_RELATION=20&&=0A=20=09=09= !currcon->conenforced=20&&=0A-=09=09cmdcon->is_enforced)=0A+=09=09= target_enforced)=0A=20=09{=0A=20=09=09AlteredTableInfo=20*tab;=0A=20=09=09= NewConstraint=20*newcon;=0A@@=20-12788,6=20+12874,7=20@@=20static=20void=0A= =20AlterCheckConstrEnforceabilityRecurse(List=20**wqueue,=20= ATAlterConstraint=20*cmdcon,=0A=20=09=09=09=09=09=09=09=09=09=20=20= Relation=20conrel,=20Oid=20conrelid,=0A=20=09=09=09=09=09=09=09=09=09=20=20= bool=20recurse,=20bool=20recursing,=0A+=09=09=09=09=09=09=09=09=09=20=20= List=20*changing_conids,=0A=20=09=09=09=09=09=09=09=09=09=20=20LOCKMODE=20= lockmode)=0A=20{=0A=20=09SysScanDesc=20pscan;=0A@@=20-12817,11=20= +12904,132=20@@=20AlterCheckConstrEnforceabilityRecurse(List=20**wqueue,=20= ATAlterConstraint=20*cmdcon,=0A=20=09=09=09=09=09=20=20=20= cmdcon->conname,=20get_rel_name(conrelid)));=0A=20=0A=20=09= ATExecAlterCheckConstrEnforceability(wqueue,=20cmdcon,=20conrel,=20= childtup,=0A-=09=09=09=09=09=09=09=09=09=09=20recurse,=20recursing,=20= lockmode);=0A+=09=09=09=09=09=09=09=09=09=09=20recurse,=20recursing,=20= changing_conids,=0A+=09=09=09=09=09=09=09=09=09=09=20lockmode);=0A=20=0A=20= =09systable_endscan(pscan);=0A=20}=0A=20=0A+/*=0A+=20*=20When=20setting=20= an=20inherited=20CHECK=20constraint=20to=20NOT=20ENFORCED,=20look=20for=20= a=0A+=20*=20matching=20parent=20constraint=20that=20remains=20ENFORCED=20= and=20is=20not=20part=20of=20the=20same=0A+=20*=20ALTER.=0A+=20*/=0A= +static=20bool=0A+ATCheckCheckConstrHasEnforcedParent(Relation=20conrel,=20= Relation=20rel,=0A+=09=09=09=09=09=09=09=09=09HeapTuple=20contuple,=0A+=09= =09=09=09=09=09=09=09=09List=20*changing_conids,=0A+=09=09=09=09=09=09=09= =09=09Oid=20*enforced_parentoid)=0A+{=0A+=09Form_pg_constraint=20= currcon;=0A+=09Relation=09inhrel;=0A+=09SysScanDesc=20scan;=0A+=09= ScanKeyData=20skey;=0A+=09HeapTuple=09inheritsTuple;=0A+=0A+=09/*=20= Since=20this=20function=20recurses,=20it=20could=20be=20driven=20to=20= stack=20overflow=20*/=0A+=09check_stack_depth();=0A+=0A+=09currcon=20=3D=20= (Form_pg_constraint)=20GETSTRUCT(contuple);=0A+=09= Assert(currcon->contype=20=3D=3D=20CONSTRAINT_CHECK);=0A+=0A+=09if=20= (currcon->coninhcount=20<=3D=200)=0A+=09=09return=20false;=0A+=0A+=09= inhrel=20=3D=20table_open(InheritsRelationId,=20AccessShareLock);=0A+=0A= +=09ScanKeyInit(&skey,=0A+=09=09=09=09Anum_pg_inherits_inhrelid,=0A+=09=09= =09=09BTEqualStrategyNumber,=20F_OIDEQ,=0A+=09=09=09=09= ObjectIdGetDatum(RelationGetRelid(rel)));=0A+=09scan=20=3D=20= systable_beginscan(inhrel,=20InheritsRelidSeqnoIndexId,=0A+=09=09=09=09=09= =09=09=20=20true,=20NULL,=201,=20&skey);=0A+=0A+=09while=20= (HeapTupleIsValid(inheritsTuple=20=3D=20systable_getnext(scan)))=0A+=09{=0A= +=09=09Oid=09=09=09parentoid;=0A+=09=09Relation=09parentrel=20=3D=20= NULL;=0A+=09=09SysScanDesc=20pscan;=0A+=09=09ScanKeyData=20pkey[3];=0A+=09= =09HeapTuple=09parenttup;=0A+=0A+=09=09parentoid=20=3D=20= ((Form_pg_inherits)=20GETSTRUCT(inheritsTuple))->inhparent;=0A+=0A+=09=09= ScanKeyInit(&pkey[0],=0A+=09=09=09=09=09Anum_pg_constraint_conrelid,=0A+=09= =09=09=09=09BTEqualStrategyNumber,=20F_OIDEQ,=0A+=09=09=09=09=09= ObjectIdGetDatum(parentoid));=0A+=09=09ScanKeyInit(&pkey[1],=0A+=09=09=09= =09=09Anum_pg_constraint_contypid,=0A+=09=09=09=09=09= BTEqualStrategyNumber,=20F_OIDEQ,=0A+=09=09=09=09=09= ObjectIdGetDatum(InvalidOid));=0A+=09=09ScanKeyInit(&pkey[2],=0A+=09=09=09= =09=09Anum_pg_constraint_conname,=0A+=09=09=09=09=09= BTEqualStrategyNumber,=20F_NAMEEQ,=0A+=09=09=09=09=09= NameGetDatum(&currcon->conname));=0A+=0A+=09=09pscan=20=3D=20= systable_beginscan(conrel,=20ConstraintRelidTypidNameIndexId,=0A+=09=09=09= =09=09=09=09=09=20=20=20true,=20NULL,=203,=20pkey);=0A+=0A+=09=09while=20= (HeapTupleIsValid(parenttup=20=3D=20systable_getnext(pscan)))=0A+=09=09{=0A= +=09=09=09Form_pg_constraint=20parentcon;=0A+=0A+=09=09=09parentcon=20=3D=20= (Form_pg_constraint)=20GETSTRUCT(parenttup);=0A+=0A+=09=09=09if=20= (parentcon->contype=20!=3D=20CONSTRAINT_CHECK=20||=0A+=09=09=09=09= parentcon->connoinherit=20||=0A+=09=09=09=09!parentcon->conenforced)=0A+=09= =09=09=09continue;=0A+=0A+=09=09=09if=20= (constraints_equivalent(parenttup,=20contuple,=0A+=09=09=09=09=09=09=09=09= =09=20=20=20RelationGetDescr(conrel)))=0A+=09=09=09{=0A+=09=09=09=09/*=0A= +=09=09=09=09=20*=20A=20parent=20listed=20in=20changing_conids=20is=20= being=20changed=20by=20the=0A+=09=09=09=09=20*=20same=20ALTER,=20but=20= it=20may=20not=20have=20been=20updated=20yet.=20=20For=0A+=09=09=09=09=20= *=20regular=20inheritance,=20recurse=20upward=20to=20check=20whether=20= an=0A+=09=09=09=09=20*=20equivalent=20enforced=20parent=20outside=20the=20= ALTER=20will=20make=20it=0A+=09=09=09=09=20*=20remain=20enforced.=20=20= Partitions=20cannot=20have=20multiple=20parents,=0A+=09=09=09=09=20*=20= so=20they=20do=20not=20need=20this=20check.=0A+=09=09=09=09=20*/=0A+=09=09= =09=09if=20(!rel->rd_rel->relispartition=20&&=0A+=09=09=09=09=09= list_member_oid(changing_conids,=20parentcon->oid))=0A+=09=09=09=09{=0A+=09= =09=09=09=09Oid=09=09=09parent_enforced_parentoid=20=3D=20InvalidOid;=0A= +=0A+=09=09=09=09=09if=20(parentrel=20=3D=3D=20NULL)=0A+=09=09=09=09=09=09= parentrel=20=3D=20table_open(parentoid,=20NoLock);=0A+=0A+=09=09=09=09=09= if=20(!ATCheckCheckConstrHasEnforcedParent(conrel,=0A+=09=09=09=09=09=09=09= =09=09=09=09=09=09=09=09=20parentrel,=0A+=09=09=09=09=09=09=09=09=09=09=09= =09=09=09=09=20parenttup,=0A+=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09= =20changing_conids,=0A+=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=20= &parent_enforced_parentoid))=0A+=09=09=09=09=09=09continue;=0A+=09=09=09=09= }=0A+=0A+=09=09=09=09*enforced_parentoid=20=3D=20parentoid;=0A+=09=09=09=09= if=20(parentrel=20!=3D=20NULL)=0A+=09=09=09=09=09table_close(parentrel,=20= NoLock);=0A+=09=09=09=09systable_endscan(pscan);=0A+=09=09=09=09= systable_endscan(scan);=0A+=09=09=09=09table_close(inhrel,=20= AccessShareLock);=0A+=09=09=09=09return=20true;=0A+=09=09=09}=0A+=09=09}=0A= +=0A+=09=09if=20(parentrel=20!=3D=20NULL)=0A+=09=09=09= table_close(parentrel,=20NoLock);=0A+=09=09systable_endscan(pscan);=0A+=09= }=0A+=0A+=09systable_endscan(scan);=0A+=09table_close(inhrel,=20= AccessShareLock);=0A+=0A+=09return=20false;=0A+}=0A+=0A=20/*=0A=20=20*=20= Returns=20true=20if=20the=20constraint's=20deferrability=20is=20altered.=0A= =20=20*=0Adiff=20--git=20a/src/test/regress/expected/constraints.out=20= b/src/test/regress/expected/constraints.out=0Aindex=20= e54fec7fb57..83f97f684d5=20100644=0A---=20= a/src/test/regress/expected/constraints.out=0A+++=20= b/src/test/regress/expected/constraints.out=0A@@=20-446,8=20+446,15=20@@=20= alter=20table=20parted_ch_2=20alter=20constraint=20cc_2=20enforced;=20= --error=0A=20ERROR:=20=20check=20constraint=20"cc_2"=20of=20relation=20= "parted_ch_2"=20is=20violated=20by=20some=20row=0A=20delete=20from=20= parted_ch=20where=20a=20=3D=2016;=0A=20alter=20table=20parted_ch_2=20= alter=20constraint=20cc_2=20enforced;=0A-alter=20table=20parted_ch_2=20= alter=20constraint=20cc=20not=20enforced;=0A-alter=20table=20parted_ch_2=20= alter=20constraint=20cc_1=20not=20enforced;=0A+alter=20table=20= parted_ch_2=20alter=20constraint=20cc=20not=20enforced;=20--error=0A= +ERROR:=20=20cannot=20mark=20inherited=20constraint=20"cc"=20as=20NOT=20= ENFORCED=0A+DETAIL:=20=20The=20matching=20constraint=20on=20parent=20= table=20"parted_ch"=20is=20ENFORCED.=0A+alter=20table=20only=20= parted_ch_2=20alter=20constraint=20cc=20not=20enforced;=20--error=0A= +ERROR:=20=20cannot=20mark=20inherited=20constraint=20"cc"=20as=20NOT=20= ENFORCED=0A+DETAIL:=20=20The=20matching=20constraint=20on=20parent=20= table=20"parted_ch"=20is=20ENFORCED.=0A+alter=20table=20parted_ch_2=20= alter=20constraint=20cc_1=20not=20enforced;=20--error=0A+ERROR:=20=20= cannot=20mark=20inherited=20constraint=20"cc_1"=20as=20NOT=20ENFORCED=0A= +DETAIL:=20=20The=20matching=20constraint=20on=20parent=20table=20= "parted_ch"=20is=20ENFORCED.=0A=20alter=20table=20parted_ch_2=20alter=20= constraint=20cc_2=20not=20enforced;=0A=20--check=20these=20CHECK=20= constraint=20status=20again=0A=20select=20*=20from=20= check_constraint_status;=0A@@=20-457,12=20+464,12=20@@=20select=20*=20= from=20check_constraint_status;=0A=20=20cc=20=20=20=20=20=20|=20= parted_ch_1=20=20|=20t=20=20=20=20=20=20=20=20=20=20=20|=20t=0A=20=20cc=20= =20=20=20=20=20|=20parted_ch_11=20|=20t=20=20=20=20=20=20=20=20=20=20=20= |=20t=0A=20=20cc=20=20=20=20=20=20|=20parted_ch_12=20|=20t=20=20=20=20=20= =20=20=20=20=20=20|=20t=0A-=20cc=20=20=20=20=20=20|=20parted_ch_2=20=20|=20= f=20=20=20=20=20=20=20=20=20=20=20|=20f=0A+=20cc=20=20=20=20=20=20|=20= parted_ch_2=20=20|=20t=20=20=20=20=20=20=20=20=20=20=20|=20t=0A=20=20= cc_1=20=20=20=20|=20parted_ch=20=20=20=20|=20t=20=20=20=20=20=20=20=20=20= =20=20|=20t=0A=20=20cc_1=20=20=20=20|=20parted_ch_1=20=20|=20t=20=20=20=20= =20=20=20=20=20=20=20|=20t=0A=20=20cc_1=20=20=20=20|=20parted_ch_11=20|=20= t=20=20=20=20=20=20=20=20=20=20=20|=20t=0A=20=20cc_1=20=20=20=20|=20= parted_ch_12=20|=20t=20=20=20=20=20=20=20=20=20=20=20|=20t=0A-=20cc_1=20=20= =20=20|=20parted_ch_2=20=20|=20f=20=20=20=20=20=20=20=20=20=20=20|=20f=0A= +=20cc_1=20=20=20=20|=20parted_ch_2=20=20|=20t=20=20=20=20=20=20=20=20=20= =20=20|=20t=0A=20=20cc_2=20=20=20=20|=20parted_ch_2=20=20|=20f=20=20=20=20= =20=20=20=20=20=20=20|=20f=0A=20(11=20rows)=0A=20=0Adiff=20--git=20= a/src/test/regress/expected/inherit.out=20= b/src/test/regress/expected/inherit.out=0Aindex=20= 3d8e8d8afd2..3c2ff55d3ba=20100644=0A---=20= a/src/test/regress/expected/inherit.out=0A+++=20= b/src/test/regress/expected/inherit.out=0A@@=20-1479,6=20+1479,98=20@@=20= NOTICE:=20=20drop=20cascades=20to=203=20other=20objects=0A=20DETAIL:=20=20= drop=20cascades=20to=20table=20p1_c1=0A=20drop=20cascades=20to=20table=20= p1_c2=0A=20drop=20cascades=20to=20table=20p1_c3=0A+--=20an=20inherited=20= CHECK=20constraint=20cannot=20be=20NOT=20ENFORCED=20under=20an=20= ENFORCED=20parent=0A+create=20table=20p1(f1=20int=20constraint=20= p1_a_check=20check=20(f1=20>=200)=20enforced);=0A+create=20table=20= p1_c1()=20inherits(p1);=0A+alter=20table=20p1_c1=20alter=20constraint=20= p1_a_check=20not=20enforced;=20--error=0A+ERROR:=20=20cannot=20mark=20= inherited=20constraint=20"p1_a_check"=20as=20NOT=20ENFORCED=0A+DETAIL:=20= =20The=20matching=20constraint=20on=20parent=20table=20"p1"=20is=20= ENFORCED.=0A+alter=20table=20p1=20alter=20constraint=20p1_a_check=20not=20= enforced;=20--ok=0A+alter=20table=20p1_c1=20alter=20constraint=20= p1_a_check=20not=20enforced;=20--ok=0A+drop=20table=20p1=20cascade;=0A= +NOTICE:=20=20drop=20cascades=20to=20table=20p1_c1=0A+--=20recursive=20= NOT=20ENFORCED=20merges=20with=20ENFORCED=20constraints=20from=20other=20= parents=0A+create=20table=20p1(a=20int=20constraint=20p1_a_check=20check=20= (a=20>=200)=20enforced);=0A+create=20table=20p2(a=20int=20constraint=20= p1_a_check=20check=20(a=20>=200)=20enforced);=0A+create=20table=20= p1_c1()=20inherits=20(p1,=20p2);=0A+NOTICE:=20=20merging=20multiple=20= inherited=20definitions=20of=20column=20"a"=0A+--=20make=20p1_c1_g2's=20= oid=20smaller=20than=20p1_c1_g1's,=20to=20ensure=20ordering=20of=0A+--=20= pg_constraint=20rows=20to=20not=20impact=20the=20test=20results.=0A= +create=20table=20p1_c1_g2()=20inherits=20(p1_c1);=0A+create=20table=20= p1_c1_g1()=20inherits=20(p1_c1);=0A+alter=20table=20p1_c1_g2=20inherit=20= p1_c1_g1;=0A+create=20table=20p1_c2()=20inherits=20(p1);=0A+alter=20= table=20p1=20alter=20constraint=20p1_a_check=20not=20enforced;=20--ok=0A= +select=20=20conname,=20conenforced,=20convalidated,=20= conrelid::regclass=0A+from=20=20=20=20pg_constraint=0A+where=20=20=20= conname=20=3D=20'p1_a_check'=20and=20contype=20=3D=20'c'=0A+order=20by=20= conrelid::regclass::text=20collate=20"C";=0A+=20=20conname=20=20=20|=20= conenforced=20|=20convalidated=20|=20conrelid=20=0A= +------------+-------------+--------------+----------=0A+=20p1_a_check=20= |=20f=20=20=20=20=20=20=20=20=20=20=20|=20f=20=20=20=20=20=20=20=20=20=20= =20=20|=20p1=0A+=20p1_a_check=20|=20t=20=20=20=20=20=20=20=20=20=20=20|=20= t=20=20=20=20=20=20=20=20=20=20=20=20|=20p1_c1=0A+=20p1_a_check=20|=20t=20= =20=20=20=20=20=20=20=20=20=20|=20t=20=20=20=20=20=20=20=20=20=20=20=20|=20= p1_c1_g1=0A+=20p1_a_check=20|=20t=20=20=20=20=20=20=20=20=20=20=20|=20t=20= =20=20=20=20=20=20=20=20=20=20=20|=20p1_c1_g2=0A+=20p1_a_check=20|=20f=20= =20=20=20=20=20=20=20=20=20=20|=20f=20=20=20=20=20=20=20=20=20=20=20=20|=20= p1_c2=0A+=20p1_a_check=20|=20t=20=20=20=20=20=20=20=20=20=20=20|=20t=20=20= =20=20=20=20=20=20=20=20=20=20|=20p2=0A+(6=20rows)=0A+=0A+alter=20table=20= p1_c1=20alter=20constraint=20p1_a_check=20not=20enforced;=20--error=0A= +ERROR:=20=20cannot=20mark=20inherited=20constraint=20"p1_a_check"=20as=20= NOT=20ENFORCED=0A+DETAIL:=20=20The=20matching=20constraint=20on=20parent=20= table=20"p2"=20is=20ENFORCED.=0A+alter=20table=20p2=20alter=20constraint=20= p1_a_check=20not=20enforced;=20--ok=0A+alter=20table=20p1_c1=20alter=20= constraint=20p1_a_check=20not=20enforced;=20--ok=0A+drop=20table=20p1,=20= p2=20cascade;=0A+NOTICE:=20=20drop=20cascades=20to=204=20other=20objects=0A= +DETAIL:=20=20drop=20cascades=20to=20table=20p1_c1=0A+drop=20cascades=20= to=20table=20p1_c1_g1=0A+drop=20cascades=20to=20table=20p1_c1_g2=0A+drop=20= cascades=20to=20table=20p1_c2=0A+--=20recursive=20NOT=20ENFORCED=20can=20= change=20all=20matching=20enforced=20parents=20together=0A+create=20= table=20gp(a=20int=20constraint=20gp_a_check=20check=20(a=20>=200)=20= enforced);=0A+create=20table=20p1()=20inherits=20(gp);=0A+create=20table=20= p2()=20inherits=20(gp);=0A+create=20table=20p1_c1()=20inherits=20(p1,=20= p2);=0A+NOTICE:=20=20merging=20multiple=20inherited=20definitions=20of=20= column=20"a"=0A+alter=20table=20gp=20alter=20constraint=20gp_a_check=20= not=20enforced;=20--ok=0A+select=20=20conname,=20conenforced,=20= convalidated,=20conrelid::regclass=0A+from=20=20=20=20pg_constraint=0A= +where=20=20=20conname=20=3D=20'gp_a_check'=20and=20contype=20=3D=20'c'=0A= +order=20by=20conrelid::regclass::text=20collate=20"C";=0A+=20=20conname=20= =20=20|=20conenforced=20|=20convalidated=20|=20conrelid=20=0A= +------------+-------------+--------------+----------=0A+=20gp_a_check=20= |=20f=20=20=20=20=20=20=20=20=20=20=20|=20f=20=20=20=20=20=20=20=20=20=20= =20=20|=20gp=0A+=20gp_a_check=20|=20f=20=20=20=20=20=20=20=20=20=20=20|=20= f=20=20=20=20=20=20=20=20=20=20=20=20|=20p1=0A+=20gp_a_check=20|=20f=20=20= =20=20=20=20=20=20=20=20=20|=20f=20=20=20=20=20=20=20=20=20=20=20=20|=20= p1_c1=0A+=20gp_a_check=20|=20f=20=20=20=20=20=20=20=20=20=20=20|=20f=20=20= =20=20=20=20=20=20=20=20=20=20|=20p2=0A+(4=20rows)=0A+=0A+drop=20table=20= gp=20cascade;=0A+NOTICE:=20=20drop=20cascades=20to=203=20other=20objects=0A= +DETAIL:=20=20drop=20cascades=20to=20table=20p1=0A+drop=20cascades=20to=20= table=20p2=0A+drop=20cascades=20to=20table=20p1_c1=0A+--=20recursive=20= NOT=20ENFORCED=20can=20change=20a=20direct-plus-indirect=20diamond=20= together=0A+create=20table=20gp(a=20int=20constraint=20gp_a_check=20= check=20(a=20>=200)=20enforced);=0A+create=20table=20p1_c1()=20inherits=20= (gp);=0A+create=20table=20p1()=20inherits=20(gp);=0A+alter=20table=20= p1_c1=20inherit=20p1;=0A+alter=20table=20gp=20alter=20constraint=20= gp_a_check=20not=20enforced;=20--ok=0A+select=20=20conname,=20= conenforced,=20convalidated,=20conrelid::regclass=0A+from=20=20=20=20= pg_constraint=0A+where=20=20=20conname=20=3D=20'gp_a_check'=20and=20= contype=20=3D=20'c'=0A+order=20by=20conrelid::regclass::text=20collate=20= "C";=0A+=20=20conname=20=20=20|=20conenforced=20|=20convalidated=20|=20= conrelid=20=0A+------------+-------------+--------------+----------=0A+=20= gp_a_check=20|=20f=20=20=20=20=20=20=20=20=20=20=20|=20f=20=20=20=20=20=20= =20=20=20=20=20=20|=20gp=0A+=20gp_a_check=20|=20f=20=20=20=20=20=20=20=20= =20=20=20|=20f=20=20=20=20=20=20=20=20=20=20=20=20|=20p1=0A+=20= gp_a_check=20|=20f=20=20=20=20=20=20=20=20=20=20=20|=20f=20=20=20=20=20=20= =20=20=20=20=20=20|=20p1_c1=0A+(3=20rows)=0A+=0A+drop=20table=20gp=20= cascade;=0A+NOTICE:=20=20drop=20cascades=20to=202=20other=20objects=0A= +DETAIL:=20=20drop=20cascades=20to=20table=20p1=0A+drop=20cascades=20to=20= table=20p1_c1=0A=20--for=20"no=20inherit"=20check=20constraint,=20it=20= will=20not=20recurse=20to=20child=20table=0A=20create=20table=20p1(f1=20= int=20constraint=20p1_a_check=20check=20(f1=20>=200)=20no=20inherit=20= not=20enforced);=0A=20create=20table=20p1_c1(f1=20int=20constraint=20= p1_a_check=20check=20(f1=20>=200)=20not=20enforced);=0Adiff=20--git=20= a/src/test/regress/sql/constraints.sql=20= b/src/test/regress/sql/constraints.sql=0Aindex=20= dc133b124bb..9705962eb9f=20100644=0A---=20= a/src/test/regress/sql/constraints.sql=0A+++=20= b/src/test/regress/sql/constraints.sql=0A@@=20-309,8=20+309,9=20@@=20= select=20*=20from=20check_constraint_status;=0A=20alter=20table=20= parted_ch_2=20alter=20constraint=20cc_2=20enforced;=20--error=0A=20= delete=20from=20parted_ch=20where=20a=20=3D=2016;=0A=20alter=20table=20= parted_ch_2=20alter=20constraint=20cc_2=20enforced;=0A-alter=20table=20= parted_ch_2=20alter=20constraint=20cc=20not=20enforced;=0A-alter=20table=20= parted_ch_2=20alter=20constraint=20cc_1=20not=20enforced;=0A+alter=20= table=20parted_ch_2=20alter=20constraint=20cc=20not=20enforced;=20= --error=0A+alter=20table=20only=20parted_ch_2=20alter=20constraint=20cc=20= not=20enforced;=20--error=0A+alter=20table=20parted_ch_2=20alter=20= constraint=20cc_1=20not=20enforced;=20--error=0A=20alter=20table=20= parted_ch_2=20alter=20constraint=20cc_2=20not=20enforced;=0A=20=0A=20= --check=20these=20CHECK=20constraint=20status=20again=0Adiff=20--git=20= a/src/test/regress/sql/inherit.sql=20b/src/test/regress/sql/inherit.sql=0A= index=208f986904389..072fca13c13=20100644=0A---=20= a/src/test/regress/sql/inherit.sql=0A+++=20= b/src/test/regress/sql/inherit.sql=0A@@=20-535,6=20+535,58=20@@=20where=20= =20=20conname=20=3D=20'inh_check_constraint3'=20and=20contype=20=3D=20= 'c'=0A=20order=20by=20conrelid::regclass::text=20collate=20"C";=0A=20= drop=20table=20p1=20cascade;=0A=20=0A+--=20an=20inherited=20CHECK=20= constraint=20cannot=20be=20NOT=20ENFORCED=20under=20an=20ENFORCED=20= parent=0A+create=20table=20p1(f1=20int=20constraint=20p1_a_check=20check=20= (f1=20>=200)=20enforced);=0A+create=20table=20p1_c1()=20inherits(p1);=0A= +alter=20table=20p1_c1=20alter=20constraint=20p1_a_check=20not=20= enforced;=20--error=0A+alter=20table=20p1=20alter=20constraint=20= p1_a_check=20not=20enforced;=20--ok=0A+alter=20table=20p1_c1=20alter=20= constraint=20p1_a_check=20not=20enforced;=20--ok=0A+drop=20table=20p1=20= cascade;=0A+=0A+--=20recursive=20NOT=20ENFORCED=20merges=20with=20= ENFORCED=20constraints=20from=20other=20parents=0A+create=20table=20p1(a=20= int=20constraint=20p1_a_check=20check=20(a=20>=200)=20enforced);=0A= +create=20table=20p2(a=20int=20constraint=20p1_a_check=20check=20(a=20>=20= 0)=20enforced);=0A+create=20table=20p1_c1()=20inherits=20(p1,=20p2);=0A= +--=20make=20p1_c1_g2's=20oid=20smaller=20than=20p1_c1_g1's,=20to=20= ensure=20ordering=20of=0A+--=20pg_constraint=20rows=20to=20not=20impact=20= the=20test=20results.=0A+create=20table=20p1_c1_g2()=20inherits=20= (p1_c1);=0A+create=20table=20p1_c1_g1()=20inherits=20(p1_c1);=0A+alter=20= table=20p1_c1_g2=20inherit=20p1_c1_g1;=0A+create=20table=20p1_c2()=20= inherits=20(p1);=0A+alter=20table=20p1=20alter=20constraint=20p1_a_check=20= not=20enforced;=20--ok=0A+select=20=20conname,=20conenforced,=20= convalidated,=20conrelid::regclass=0A+from=20=20=20=20pg_constraint=0A= +where=20=20=20conname=20=3D=20'p1_a_check'=20and=20contype=20=3D=20'c'=0A= +order=20by=20conrelid::regclass::text=20collate=20"C";=0A+alter=20table=20= p1_c1=20alter=20constraint=20p1_a_check=20not=20enforced;=20--error=0A= +alter=20table=20p2=20alter=20constraint=20p1_a_check=20not=20enforced;=20= --ok=0A+alter=20table=20p1_c1=20alter=20constraint=20p1_a_check=20not=20= enforced;=20--ok=0A+drop=20table=20p1,=20p2=20cascade;=0A+=0A+--=20= recursive=20NOT=20ENFORCED=20can=20change=20all=20matching=20enforced=20= parents=20together=0A+create=20table=20gp(a=20int=20constraint=20= gp_a_check=20check=20(a=20>=200)=20enforced);=0A+create=20table=20p1()=20= inherits=20(gp);=0A+create=20table=20p2()=20inherits=20(gp);=0A+create=20= table=20p1_c1()=20inherits=20(p1,=20p2);=0A+alter=20table=20gp=20alter=20= constraint=20gp_a_check=20not=20enforced;=20--ok=0A+select=20=20conname,=20= conenforced,=20convalidated,=20conrelid::regclass=0A+from=20=20=20=20= pg_constraint=0A+where=20=20=20conname=20=3D=20'gp_a_check'=20and=20= contype=20=3D=20'c'=0A+order=20by=20conrelid::regclass::text=20collate=20= "C";=0A+drop=20table=20gp=20cascade;=0A+=0A+--=20recursive=20NOT=20= ENFORCED=20can=20change=20a=20direct-plus-indirect=20diamond=20together=0A= +create=20table=20gp(a=20int=20constraint=20gp_a_check=20check=20(a=20>=20= 0)=20enforced);=0A+create=20table=20p1_c1()=20inherits=20(gp);=0A+create=20= table=20p1()=20inherits=20(gp);=0A+alter=20table=20p1_c1=20inherit=20p1;=0A= +alter=20table=20gp=20alter=20constraint=20gp_a_check=20not=20enforced;=20= --ok=0A+select=20=20conname,=20conenforced,=20convalidated,=20= conrelid::regclass=0A+from=20=20=20=20pg_constraint=0A+where=20=20=20= conname=20=3D=20'gp_a_check'=20and=20contype=20=3D=20'c'=0A+order=20by=20= conrelid::regclass::text=20collate=20"C";=0A+drop=20table=20gp=20= cascade;=0A+=0A=20--for=20"no=20inherit"=20check=20constraint,=20it=20= will=20not=20recurse=20to=20child=20table=0A=20create=20table=20p1(f1=20= int=20constraint=20p1_a_check=20check=20(f1=20>=200)=20no=20inherit=20= not=20enforced);=0A=20create=20table=20p1_c1(f1=20int=20constraint=20= p1_a_check=20check=20(f1=20>=200)=20not=20enforced);=0A--=20=0A2.50.1=20= (Apple=20Git-155)=0A=0A= --Apple-Mail=_2D4E6186-C1AE-456E-A2AC-37699D344460 Content-Disposition: attachment; filename=v10-0002-doc-Clarify-inherited-constraint-behavior.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v10-0002-doc-Clarify-inherited-constraint-behavior.patch" Content-Transfer-Encoding: quoted-printable =46rom=20bb62a3a06675ba06d10fba06ebdc63661b99c362=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20"Chao=20Li=20(Evan)"=20=0A= Date:=20Fri,=2029=20May=202026=2006:47:07=20+0800=0ASubject:=20[PATCH=20= v10=202/3]=20doc:=20Clarify=20inherited=20constraint=20behavior=0A=0A= Update=20the=20table=20inheritance=20documentation=20to=20mention=20= not-null=20constraints=0Aalongside=20check=20constraints=20where=20= inherited=20constraints=20are=20discussed.=0A=0AAlso=20clarify=20that=20= some=20properties=20of=20inherited=20constraints=20can=20now=20be=20= altered=0Adirectly=20on=20child=20tables,=20while=20the=20resulting=20= constraint=20must=20remain=20compatible=0Awith=20its=20inherited=20= parent=20constraints.=20=20For=20multiple=20inheritance,=20say=20= explicitly=0Athat=20when=20a=20column=20or=20constraint=20is=20inherited=20= from=20more=20than=20one=20parent,=20the=0Astricter=20definition=20= applies.=0A=0AAuthor:=20Chao=20Li=20=0AReviewed-by:=20= Zsolt=20Parragi=20=0ADiscussion:=20= https://postgr.es/m/E74C57FA-1DD0-4C8E-8FB1-538034752592@gmail.com=0A---=0A= =20doc/src/sgml/ddl.sgml=20|=2033=20+++++++++++++++++++--------------=0A=20= 1=20file=20changed,=2019=20insertions(+),=2014=20deletions(-)=0A=0Adiff=20= --git=20a/doc/src/sgml/ddl.sgml=20b/doc/src/sgml/ddl.sgml=0Aindex=20= 747f929aee3..dac31457267=20100644=0A---=20a/doc/src/sgml/ddl.sgml=0A+++=20= b/doc/src/sgml/ddl.sgml=0A@@=20-4090,7=20+4090,9=20@@=20VALUES=20= ('Albany',=20NULL,=20NULL,=20'NY');=0A=20=20=20=20similar=20fashion.=20=20= Thus,=20for=20example,=20a=20merged=20column=20will=20be=20marked=0A=20=20= =20=20not-null=20if=20any=20one=20of=20the=20column=20definitions=20it=20= came=20from=20is=20marked=0A=20=20=20=20not-null.=20=20Check=20= constraints=20are=20merged=20if=20they=20have=20the=20same=20name,=0A-=20= =20=20and=20the=20merge=20will=20fail=20if=20their=20conditions=20are=20= different.=0A+=20=20=20and=20the=20merge=20will=20fail=20if=20their=20= conditions=20are=20different.=20=20For=20merged=0A+=20=20=20check=20= constraints,=20stricter=20enforceability=20is=20preserved:=20if=20any=20= inherited=0A+=20=20=20copy=20is=20enforced,=20the=20merged=20constraint=20= is=20enforced.=0A=20=20=20=0A=20=0A=20=20=20=0A@@=20-4104,8=20= +4106,9=20@@=20VALUES=20('Albany',=20NULL,=20NULL,=20'NY');=0A=20=20=20=20= To=20do=20this=20the=20new=20child=20table=20must=20already=20include=20= columns=20with=0A=20=20=20=20the=20same=20names=20and=20types=20as=20the=20= columns=20of=20the=20parent.=20It=20must=20also=20include=0A=20=20=20=20= check=20constraints=20with=20the=20same=20names=20and=20check=20= expressions=20as=20those=20of=20the=0A-=20=20=20parent.=20Similarly=20an=20= inheritance=20link=20can=20be=20removed=20from=20a=20child=20using=20the=0A= -=20=20=20NO=20INHERIT=20variant=20of=20= ALTER=20TABLE.=0A+=20=20=20parent,=20as=20well=20as=20= matching=20not-null=20constraints.=20Similarly=20an=20inheritance=0A+=20=20= =20link=20can=20be=20removed=20from=20a=20child=20using=20the=20= NO=20INHERIT=0A+=20=20=20variant=20of=20= ALTER=20TABLE.=0A=20=20=20=20Dynamically=20adding=20= and=20removing=20inheritance=20links=20like=20this=20can=20be=20useful=0A= =20=20=20=20when=20the=20inheritance=20relationship=20is=20being=20used=20= for=20table=0A=20=20=20=20partitioning=20(see=20).=0A@@=20-4124,21=20+4127,23=20@@=20= VALUES=20('Albany',=20NULL,=20NULL,=20'NY');=0A=20=0A=20=20=20=0A=20= =20=20=20A=20parent=20table=20cannot=20be=20dropped=20while=20any=20of=20= its=20children=20remain.=20Neither=0A-=20=20=20can=20columns=20or=20= check=20constraints=20of=20child=20tables=20be=20dropped=20or=20altered=0A= -=20=20=20if=20they=20are=20inherited=0A-=20=20=20from=20any=20parent=20= tables.=20If=20you=20wish=20to=20remove=20a=20table=20and=20all=20of=20= its=0A-=20=20=20descendants,=20one=20easy=20way=20is=20to=20drop=20the=20= parent=20table=20with=20the=0A-=20=20=20CASCADE=20= option=20(see=20).=0A+=20=20=20can=20= inherited=20columns=20or=20inherited=20check=20and=20not-null=20= constraints=20of=20child=0A+=20=20=20tables=20be=20dropped=20directly.=20= =20Some=20properties=20of=20inherited=20constraints=20can=0A+=20=20=20be=20= altered,=20but=20each=20resulting=20constraint=20must=20remain=20= compatible=20with=20all=0A+=20=20=20parent=20constraints=20from=20which=20= it=20is=20inherited.=20=20If=20you=20wish=20to=20remove=20a=0A+=20=20=20= table=20and=20all=20of=20its=20descendants,=20one=20easy=20way=20is=20to=20= drop=20the=20parent=20table=0A+=20=20=20with=20the=20= CASCADE=20option=20(see=20).=0A=20=20=20=0A=20=0A=20=20=20=0A=20= =20=20=20ALTER=20TABLE=20will=0A-=20=20=20propagate=20= any=20changes=20in=20column=20data=20definitions=20and=20check=0A-=20=20=20= constraints=20down=20the=20inheritance=20hierarchy.=20=20Again,=20= dropping=0A-=20=20=20columns=20that=20are=20depended=20on=20by=20other=20= tables=20is=20only=20possible=20when=20using=0A-=20=20=20the=20= CASCADE=20option.=20ALTER=0A-=20=20=20= TABLE=20follows=20the=20same=20rules=20for=20duplicate=20= column=20merging=0A-=20=20=20and=20rejection=20that=20apply=20during=20= CREATE=20TABLE.=0A+=20=20=20propagate=20changes=20in=20= column=20definitions=20and=20in=20inheritable=20constraints=0A+=20=20=20= (check=20and=20not-null=20constraints)=20down=20the=20inheritance=20= hierarchy.=20=20Again,=0A+=20=20=20dropping=20columns=20that=20are=20= depended=20on=20by=20other=20tables=20is=20only=20possible=0A+=20=20=20= when=20using=20the=20CASCADE=20option.=20= ALTER=0A+=20=20=20TABLE=20follows=20the=20same=20= rules=20for=20merging=20or=20rejecting=20duplicate=0A+=20=20=20inherited=20= column=20and=20constraint=20definitions=20that=20apply=20during=0A+=20=20= =20CREATE=20TABLE.=0A=20=20=20=0A=20=0A=20=20=20= =0A--=20=0A2.50.1=20(Apple=20Git-155)=0A=0A= --Apple-Mail=_2D4E6186-C1AE-456E-A2AC-37699D344460 Content-Disposition: attachment; filename=v10-0003-doc-Clarify-ALTER-CONSTRAINT-enforceability-beha.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v10-0003-doc-Clarify-ALTER-CONSTRAINT-enforceability-beha.patch" Content-Transfer-Encoding: quoted-printable =46rom=20b644a1d1c40d731a8c46d4400e4137bed320444b=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20"Chao=20Li=20(Evan)"=20=0A= Date:=20Mon,=2025=20May=202026=2016:59:21=20+0800=0ASubject:=20[PATCH=20= v10=203/3]=20doc:=20Clarify=20ALTER=20CONSTRAINT=20enforceability=20= behavior=0A=0AThe=20ALTER=20TABLE=20documentation=20said=20that=20= FOREIGN=20KEY=20and=20CHECK=0Aconstraints=20may=20be=20altered,=20but=20= did=20not=20distinguish=20between=0Adeferrability=20and=20enforceability=20= attributes.=0A=0AClarify=20that=20deferrability=20attributes=20can=20= currently=20be=20altered=20only=20for=0AFOREIGN=20KEY=20constraints,=20= while=20enforceability=20can=20be=20altered=20for=20both=0AFOREIGN=20KEY=20= and=20CHECK=20constraints.=20=20Also=20document=20that=20setting=20a=0A= constraint=20to=20ENFORCED=20verifies=20existing=20rows=20and=20resumes=20= checking=20new=0Aor=20updated=20rows.=0A=0AAuthor:=20Chao=20Li=20= =0AReviewed-by:=20Zsolt=20Parragi=20= =0ADiscussion:=20= https://postgr.es/m/E74C57FA-1DD0-4C8E-8FB1-538034752592@gmail.com=0A= Discussion:=20= https://postgr.es/m/711B1ED3-1781-4B6C-A573-B58AF20770E5@gmail.com=0A---=0A= =20doc/src/sgml/ref/alter_table.sgml=20|=2014=20++++++++++++--=0A=201=20= file=20changed,=2012=20insertions(+),=202=20deletions(-)=0A=0Adiff=20= --git=20a/doc/src/sgml/ref/alter_table.sgml=20= b/doc/src/sgml/ref/alter_table.sgml=0Aindex=20dec34337d1a..af247d82902=20= 100644=0A---=20a/doc/src/sgml/ref/alter_table.sgml=0A+++=20= b/doc/src/sgml/ref/alter_table.sgml=0A@@=20-586,8=20+586,18=20@@=20WITH=20= (=20MODULUS=20numeric_literal,=20REM=0A=20=20=20=20=20= =0A=20=20=20=20=20=20=0A=20=20=20=20=20=20=20This=20form=20= alters=20the=20attributes=20of=20a=20constraint=20that=20was=20= previously=0A-=20=20=20=20=20=20created.=20Currently=20FOREIGN=20= KEY=20and=20CHECK=0A-=20=20=20=20=20=20= constraints=20may=20be=20altered=20in=20this=20fashion,=20but=20see=20= below.=0A+=20=20=20=20=20=20created.=20=20Currently,=20the=20= deferrability=20attributes=20can=20be=20altered=20only=0A+=20=20=20=20=20= =20for=20FOREIGN=20KEY=20constraints.=20=20The=20= enforceability=0A+=20=20=20=20=20=20attribute=20can=20be=20altered=20for=20= FOREIGN=20KEY=20and=0A+=20=20=20=20=20=20= CHECK=20constraints.=0A+=20=20=20=20=20=0A+=0A= +=20=20=20=20=20=0A+=20=20=20=20=20=20Setting=20a=20constraint=20= to=20NOT=20ENFORCED=20causes=20the=0A+=20=20=20=20=20=20= database=20system=20to=20stop=20checking=20it=20for=20new=20or=20updated=20= rows.=20=20Setting=0A+=20=20=20=20=20=20a=20constraint=20to=20= ENFORCED=20causes=20the=20database=20system=0A+=20=20=20= =20=20=20to=20verify=20that=20existing=20rows=20satisfy=20the=20= constraint=20and=20to=20check=20it=0A+=20=20=20=20=20=20for=20new=20or=20= updated=20rows.=0A=20=20=20=20=20=20=0A=20=20=20=20=20=0A= =20=20=20=20=0A--=20=0A2.50.1=20(Apple=20Git-155)=0A=0A= --Apple-Mail=_2D4E6186-C1AE-456E-A2AC-37699D344460--