From c2965313475302a0580000a31640295f5362a998 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Wed, 15 Jan 2025 13:18:58 +0800
Subject: [PATCH v4 1/1] better error message for ALTER DOMAIN ADD CONSTRAINT

DomainConstraintElem syntax in gram.y can specify constraint properties such as
([ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE | NO INHERIT ] [ENFORCED | NOT ENFORCED])

According to the ALTER DOMAIN synopsis section, none of these properties are
allowed.  In fact, these properties are wrapped up as a separate individual
Constraints node (see DefineDomain).  However, AlterDomainAddConstraint can only
cope with a single Constraints node.  therefore, error out at
AlterDomainAddConstraint is not possible, so handle error cases in gram.y.

discussion: https://postgr.es/m/CACJufxHitd5LGLBSSAPShhtDWxT0ViVKTHinkYW-skBX93TcpA@mail.gmail.com
---
 src/backend/commands/tablecmds.c     |  3 +-
 src/backend/parser/gram.y            | 46 +++++++++++++++++++++++++---
 src/test/regress/expected/domain.out | 30 ++++++++++++++++--
 src/test/regress/sql/domain.sql      |  8 +++++
 4 files changed, 79 insertions(+), 8 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 4fc54bd6eb..34a7ee890d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5405,8 +5405,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
 				ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
 									true, true, lockmode);
 			break;
-		case AT_ReAddDomainConstraint:	/* Re-add pre-existing domain check
-										 * constraint */
+		case AT_ReAddDomainConstraint:	/* add domain check or not-null constraint */
 			address =
 				AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
 										 ((AlterDomainStmt *) cmd->def)->def,
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6079de70e0..70b612e33d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4343,8 +4343,27 @@ DomainConstraintElem:
 					n->raw_expr = $3;
 					n->cooked_expr = NULL;
 					processCASbits($5, @5, "CHECK",
-								   NULL, NULL, NULL, &n->skip_validation,
-								   &n->is_no_inherit, yyscanner);
+								   &n->deferrable, &n->initdeferred, &n->is_enforced,
+								   &n->skip_validation, &n->is_no_inherit, yyscanner);
+
+					if ($5 & (CAS_NOT_DEFERRABLE | CAS_DEFERRABLE | CAS_INITIALLY_IMMEDIATE |
+							  CAS_INITIALLY_DEFERRED))
+						ereport(ERROR,
+							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							errmsg("specifying constraint deferrability not supported for domains"),
+							parser_errposition(@5));
+
+					if ($5 & (CAS_NOT_ENFORCED | CAS_ENFORCED))
+						ereport(ERROR,
+							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							errmsg("specifying constraint enforceability not supported for domains"),
+							parser_errposition(@5));
+					if (n->is_no_inherit)
+						ereport(ERROR,
+							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							errmsg("constraint specified as no-inherit is not supported for domains"),
+							parser_errposition(@5));
+
 					n->is_enforced = true;
 					n->initially_valid = !n->skip_validation;
 					$$ = (Node *) n;
@@ -4358,8 +4377,27 @@ DomainConstraintElem:
 					n->keys = list_make1(makeString("value"));
 					/* no NOT VALID, NO INHERIT support */
 					processCASbits($3, @3, "NOT NULL",
-								   NULL, NULL, NULL,
-								   NULL, NULL, yyscanner);
+								   &n->deferrable, &n->initdeferred, &n->is_enforced,
+								   NULL, &n->is_no_inherit, yyscanner);
+
+					if ($3 & (CAS_NOT_DEFERRABLE | CAS_DEFERRABLE | CAS_INITIALLY_IMMEDIATE |
+							  CAS_INITIALLY_DEFERRED))
+						ereport(ERROR,
+							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							errmsg("specifying constraint deferrability not supported for domains"),
+							parser_errposition(@3));
+
+					if ($3 & (CAS_NOT_ENFORCED | CAS_ENFORCED))
+						ereport(ERROR,
+							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							errmsg("specifying constraint enforceability not supported for domains"),
+							parser_errposition(@3));
+					if (n->is_no_inherit)
+						ereport(ERROR,
+							errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+							errmsg("constraint specified as no-inherit is not supported for domains"),
+							parser_errposition(@3));
+
 					n->initially_valid = true;
 					$$ = (Node *) n;
 				}
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index ba6f05eeb7..fe44455162 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -68,6 +68,32 @@ create domain d_fail as int4 constraint cc check (values > 1) deferrable;
 ERROR:  specifying constraint deferrability not supported for domains
 LINE 1: ...n d_fail as int4 constraint cc check (values > 1) deferrable...
                                                              ^
+create domain d_int as int4;
+alter domain d_int add constraint nn not null no inherit;
+ERROR:  constraint specified as no-inherit is not supported for domains
+LINE 1: alter domain d_int add constraint nn not null no inherit;
+                                                      ^
+alter domain d_int add constraint nn not null not enforced;
+ERROR:  specifying constraint enforceability not supported for domains
+LINE 1: alter domain d_int add constraint nn not null not enforced;
+                                                      ^
+alter domain d_int add constraint nn not null not deferrable initially immediate;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: alter domain d_int add constraint nn not null not deferrable...
+                                                      ^
+alter domain d_int add constraint cc check(value > 1) no inherit;
+ERROR:  constraint specified as no-inherit is not supported for domains
+LINE 1: ...r domain d_int add constraint cc check(value > 1) no inherit...
+                                                             ^
+alter domain d_int add constraint cc check(value > 1) not enforced;
+ERROR:  specifying constraint enforceability not supported for domains
+LINE 1: ...r domain d_int add constraint cc check(value > 1) not enforc...
+                                                             ^
+alter domain d_int add constraint cc check(value > 1) not deferrable initially immediate;
+ERROR:  specifying constraint deferrability not supported for domains
+LINE 1: ...r domain d_int add constraint cc check(value > 1) not deferr...
+                                                             ^
+drop domain d_int;
 -- Test domain input.
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
 -- exercises CoerceToDomain while COPY exercises domain_in.
@@ -1363,11 +1389,11 @@ LINE 1: ...S int CONSTRAINT the_constraint CHECK (value > 0) NOT ENFORC...
 CREATE DOMAIN constraint_enforced_dom AS int;
 -- XXX misleading error messages
 ALTER DOMAIN constraint_enforced_dom ADD CONSTRAINT the_constraint CHECK (value > 0) ENFORCED;
-ERROR:  CHECK constraints cannot be marked ENFORCED
+ERROR:  specifying constraint enforceability not supported for domains
 LINE 1: ...om ADD CONSTRAINT the_constraint CHECK (value > 0) ENFORCED;
                                                               ^
 ALTER DOMAIN constraint_enforced_dom ADD CONSTRAINT the_constraint CHECK (value > 0) NOT ENFORCED;
-ERROR:  CHECK constraints cannot be marked NOT ENFORCED
+ERROR:  specifying constraint enforceability not supported for domains
 LINE 1: ...m ADD CONSTRAINT the_constraint CHECK (value > 0) NOT ENFORC...
                                                              ^
 DROP DOMAIN constraint_enforced_dom;
diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql
index b752a63ab5..9501ad874d 100644
--- a/src/test/regress/sql/domain.sql
+++ b/src/test/regress/sql/domain.sql
@@ -31,6 +31,14 @@ create domain d_fail as int4 constraint cc generated by default as identity;
 create domain d_fail as int4 constraint cc check (values > 1) no inherit;
 create domain d_fail as int4 constraint cc check (values > 1) deferrable;
 
+create domain d_int as int4;
+alter domain d_int add constraint nn not null no inherit;
+alter domain d_int add constraint nn not null not enforced;
+alter domain d_int add constraint nn not null not deferrable initially immediate;
+alter domain d_int add constraint cc check(value > 1) no inherit;
+alter domain d_int add constraint cc check(value > 1) not enforced;
+alter domain d_int add constraint cc check(value > 1) not deferrable initially immediate;
+drop domain d_int;
 -- Test domain input.
 
 -- Note: the point of checking both INSERT and COPY FROM is that INSERT
-- 
2.34.1

