hi.

rebase because of commit: 8dd7c7cd0a2605d5301266a6b67a569d6a305106
also did minor enhancement.

v4-0001 add function: ExecPrepareExprSafe and ExecInitExprSafe.
v4-0002 add function: DomainHaveVolatileConstraints
v4-0003 tests and apply fast default for domain with constraints.

v4-0003 table with empty rows aligned with master behavior.
also will do table rewrite if the new column is domain with volatile
check constraints,
so less surprising behavior.
From 09547bbc65f20846ac28efce035c016b21a75825 Mon Sep 17 00:00:00 2001
From: jian he <jian.universal...@gmail.com>
Date: Mon, 24 Mar 2025 16:07:46 +0800
Subject: [PATCH v4 1/3] soft error variant of ExecPrepareExpr, ExecInitExpr

ExecInitExprSafe: soft error of ExecInitExpr.
ExecPrepareExprSafe: soft error of ExecPrepareExpr.
ExecPrepareExprSafe initialize for expression execution with soft error support.
not all expression node support it. Like node CoerceToDomain support it.

XXX more comments.

discussion: https://postgr.es/m/cacjufxe_+izbr1i49k_ahigpppwltji6km8nosc7fwvkdem...@mail.gmail.com
---
 src/backend/executor/execExpr.c | 63 +++++++++++++++++++++++++++++++++
 src/include/executor/executor.h |  2 ++
 2 files changed, 65 insertions(+)

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index f1569879b52..9182ba446a0 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -170,6 +170,47 @@ ExecInitExpr(Expr *node, PlanState *parent)
 	return state;
 }
 
+/*
+ * ExecInitExprSafe: soft error variant of ExecInitExpr.
+ *
+ * use it only for expression nodes support soft errors, not all expression
+ * nodes support it.
+*/
+ExprState *
+ExecInitExprSafe(Expr *node, PlanState *parent)
+{
+	ExprState  *state;
+	ExprEvalStep scratch = {0};
+
+	/* Special case: NULL expression produces a NULL ExprState pointer */
+	if (node == NULL)
+		return NULL;
+
+	/* Initialize ExprState with empty step list */
+	state = makeNode(ExprState);
+	state->expr = node;
+	state->parent = parent;
+	state->ext_params = NULL;
+	state->escontext = makeNode(ErrorSaveContext);
+	state->escontext->type = T_ErrorSaveContext;
+	state->escontext->error_occurred = false;
+	state->escontext->details_wanted = true;
+
+	/* Insert setup steps as needed */
+	ExecCreateExprSetupSteps(state, (Node *) node);
+
+	/* Compile the expression proper */
+	ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
+
+	/* Finally, append a DONE step */
+	scratch.opcode = EEOP_DONE_RETURN;
+	ExprEvalPushStep(state, &scratch);
+
+	ExecReadyExpr(state);
+
+	return state;
+}
+
 /*
  * ExecInitExprWithParams: prepare a standalone expression tree for execution
  *
@@ -778,6 +819,28 @@ ExecPrepareExpr(Expr *node, EState *estate)
 	return result;
 }
 
+/*
+ * ExecPrepareExprSafe: soft error variant of ExecPrepareExpr.
+ *
+ * use it when expression node *support* soft error expression execution.
+ */
+ExprState *
+ExecPrepareExprSafe(Expr *node, EState *estate)
+{
+	ExprState  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	node = expression_planner(node);
+
+	result = ExecInitExprSafe(node, NULL);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
 /*
  * ExecPrepareQual --- initialize for qual execution outside a normal
  * Plan tree context.
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 0db5d18ba22..464c7318de1 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -305,6 +305,7 @@ ExecProcNode(PlanState *node)
  * prototypes from functions in execExpr.c
  */
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
+extern ExprState *ExecInitExprSafe(Expr *node, PlanState *parent);
 extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params);
 extern ExprState *ExecInitQual(List *qual, PlanState *parent);
 extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
@@ -353,6 +354,7 @@ extern ProjectionInfo *ExecBuildUpdateProjection(List *targetList,
 												 TupleTableSlot *slot,
 												 PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
+extern ExprState *ExecPrepareExprSafe(Expr *node, EState *estate);
 extern ExprState *ExecPrepareQual(List *qual, EState *estate);
 extern ExprState *ExecPrepareCheck(List *qual, EState *estate);
 extern List *ExecPrepareExprList(List *nodes, EState *estate);
-- 
2.34.1

From b9739edd3de443b203a14db3a6175988125de135 Mon Sep 17 00:00:00 2001
From: jian he <jian.universal...@gmail.com>
Date: Mon, 24 Mar 2025 19:08:25 +0800
Subject: [PATCH v4 3/3] fast default for domain with constraints

This is primarily done by evaluating CoerceToDomain with soft error support.

If we evaluate CoerceToDomain to false, in ATExecAddColumn, the defval node
evaluation value cannot be cast to the domain.  However, in some scarenio we
cannot fail at the Phase 2 stage in cases where the table is empty. For example,
the to be added column is type of domain x, domain x is "check(value > 10) default 8".

Therefore, if an error occurred while evaluation, do not raise the error,
we signal Phase 3 to do table rewrite, error will be raised on Phase 3.

Thanks to commit aaaf9449ec6be62cb0d30ed3588dc384f56274bf[1],
ExprState.escontext (ErrorSaveContext) was added, and ExecEvalConstraintNotNull,
ExecEvalConstraintCheck were changed to use errsave instead of hard error.
Now we can evaluate CoerceToDomain in a soft error way.

However we do need table rewrite for domain with volatile check constraints.
so there will be less surprising behavior.

discussion: https://postgr.es/m/cacjufxe_+izbr1i49k_ahigpppwltji6km8nosc7fwvkdem...@mail.gmail.com
[1]: https://git.postgresql.org/cgit/postgresql.git/commit/?id=aaaf9449ec6be62cb0d30ed3588dc384f56274bf
---
 src/backend/commands/tablecmds.c           |  66 ++++++++++---
 src/test/regress/expected/fast_default.out | 103 +++++++++++++++++++++
 src/test/regress/sql/fast_default.sql      |  68 ++++++++++++++
 3 files changed, 222 insertions(+), 15 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 1202544ebd0..abddb9cdba7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7342,15 +7342,6 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	 * NULL if so, so without any modification of the tuple data we will get
 	 * the effect of NULL values in the new column.
 	 *
-	 * An exception occurs when the new column is of a domain type: the domain
-	 * might have a not-null constraint, or a check constraint that indirectly
-	 * rejects nulls.  If there are any domain constraints then we construct
-	 * an explicit NULL default value that will be passed through
-	 * CoerceToDomain processing.  (This is a tad inefficient, since it causes
-	 * rewriting the table which we really wouldn't have to do; but we do it
-	 * to preserve the historical behavior that such a failure will be raised
-	 * only if the table currently contains some rows.)
-	 *
 	 * Note: we use build_column_default, and not just the cooked default
 	 * returned by AddRelationNewConstraints, so that the right thing happens
 	 * when a datatype's default applies.
@@ -7369,6 +7360,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	{
 		bool		has_domain_constraints;
 		bool		has_missing = false;
+		bool		explicit_defval = true;
+		bool		has_volatile = false;
 
 		/*
 		 * For an identity column, we can't use build_column_default(),
@@ -7386,8 +7379,31 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		else
 			defval = (Expr *) build_column_default(rel, attribute->attnum);
 
+		/*
+		 * if defval is there, atthasdef is false, that means the defval comes
+		 * from domain default expression and no explicit DEFAULT expression has
+		 * been specified.
+		 * In that case we need evaluate defval error safe way, so new column
+		 * with domain specification such as ``CHECK(VALUE > 10) DEFAULT 8 ``
+		 * can be addded to empty table.
+		*/
+		if (defval)
+		{
+			TupleDesc	rd_att = rel->rd_att;
+			Form_pg_attribute att_tup = TupleDescAttr(rd_att, attribute->attnum - 1);
+			if (!att_tup->atthasdef)
+				explicit_defval = false;
+		}
+
+		has_domain_constraints = DomainHaveVolatileConstraints(attribute->atttypid, &has_volatile);
+		/* new column with volatile domain constraint, then table rewrite. */
+		if (has_volatile)
+		{
+			Assert(has_domain_constraints);
+			tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
+		}
+
 		/* Build CoerceToDomain(NULL) expression if needed */
-		has_domain_constraints = DomainHasConstraints(attribute->atttypid);
 		if (!defval && has_domain_constraints)
 		{
 			Oid			baseTypeId;
@@ -7408,6 +7424,13 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 													-1);
 			if (defval == NULL) /* should not happen */
 				elog(ERROR, "failed to coerce base type to domain");
+
+			/*
+			 * if domain have not-null constraint or check constraint that is
+			 * equivalent to not-null, we only want it failure when table have
+			 * some rows.  let's evaulate defval in a soft error way.
+			*/
+			explicit_defval = false;
 		}
 
 		if (defval)
@@ -7430,13 +7453,16 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 			 * specified DEFAULT value outside of the heap.  This is only
 			 * allowed for plain relations and non-generated columns, and the
 			 * default expression can't be volatile (stable is OK).  Note that
-			 * contain_volatile_functions deems CoerceToDomain immutable, but
-			 * here we consider that coercion to a domain with constraints is
-			 * volatile; else it might fail even when the table is empty.
+			 * contain_volatile_functions deems CoerceToDomain immutable.
+			 * But domain with volatile constraint will need table rewrite,
+			 * regardless of domain's default expression.
+			 * We do support soft error evaluation of CoerceToDomain if
+			 * ExprState->escontext is not NULL. In that case if evaluation
+			 * failed, set table rewrite to true, let's fail on Phase 3.
 			 */
 			if (rel->rd_rel->relkind == RELKIND_RELATION &&
 				!colDef->generated &&
-				!has_domain_constraints &&
+				!has_volatile &&
 				!contain_volatile_functions((Node *) defval))
 			{
 				EState	   *estate;
@@ -7446,10 +7472,20 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 
 				/* Evaluate the default expression */
 				estate = CreateExecutorState();
-				exprState = ExecPrepareExpr(defval, estate);
+				if (explicit_defval)
+					exprState = ExecPrepareExpr(defval, estate);
+				else
+					exprState = ExecPrepareExprSafe(defval, estate);
+
 				missingval = ExecEvalExpr(exprState,
 										  GetPerTupleExprContext(estate),
 										  &missingIsNull);
+
+				if (SOFT_ERROR_OCCURRED(exprState->escontext))
+				{
+					missingIsNull = true;
+					tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
+				}
 				/* If it turns out NULL, nothing to do; else store it */
 				if (!missingIsNull)
 				{
diff --git a/src/test/regress/expected/fast_default.out b/src/test/regress/expected/fast_default.out
index ccbcdf8403f..73fb23fd79b 100644
--- a/src/test/regress/expected/fast_default.out
+++ b/src/test/regress/expected/fast_default.out
@@ -317,11 +317,114 @@ SELECT a, b, length(c) = 3 as c_ok, d, e >= 10 as e_ok FROM t2;
  2 | 3 | t    | {This,is,abcd,the,real,world} | t
 (2 rows)
 
+---test fast default over domains with check constraint
+CREATE DOMAIN domain5 AS int check(value > 10) default 8;
+CREATE DOMAIN domain6 as int not null;
+CREATE DOMAIN domain7 as int check(value is not null);
+CREATE DOMAIN domain8 as int check(value > 10) DEFAULT random(min=>10, max=>100);
+CREATE TABLE t3(a int);
+ALTER TABLE t3 ADD COLUMN b domain5 default 1; --error
+ERROR:  value for domain domain5 violates check constraint "domain5_check"
+ALTER TABLE t3 ADD COLUMN b domain6 default NULL; --error
+ERROR:  domain domain6 does not allow null values
+ALTER TABLE t3 ADD COLUMN b domain6 default 11 + NULL; --error
+ERROR:  domain domain6 does not allow null values
+ALTER TABLE t3 ADD COLUMN b domain7 default 11 + NULL; --error
+ERROR:  value for domain domain7 violates check constraint "domain7_check"
+ALTER TABLE t3 ADD COLUMN d domain5; --ok. because t3 is empty
+NOTICE:  rewriting table t3 for reason 2
+ALTER TABLE t3 ADD COLUMN b domain6; --ok. because t3 is empty
+NOTICE:  rewriting table t3 for reason 2
+ALTER TABLE t3 ADD COLUMN c domain7; --ok. because t3 is empty
+NOTICE:  rewriting table t3 for reason 2
+INSERT INTO t3 default values; --error
+ERROR:  value for domain domain5 violates check constraint "domain5_check"
+INSERT INTO t3(a,b,c,d) values(1,2, 3, 12); --ok
+DROP TABLE t3;
+CREATE TABLE t3(a int);
+INSERT INTO t3 VALUES(1),(2);
+ALTER TABLE t3 ADD COLUMN b domain5; --table rewrite, then fail
+NOTICE:  rewriting table t3 for reason 2
+ERROR:  value for domain domain5 violates check constraint "domain5_check"
+ALTER TABLE t3 ADD COLUMN b domain6; --table rewrite, then fail
+NOTICE:  rewriting table t3 for reason 2
+ERROR:  domain domain6 does not allow null values
+ALTER TABLE t3 ADD COLUMN b domain7; --table rewrite, then fail
+NOTICE:  rewriting table t3 for reason 2
+ERROR:  value for domain domain7 violates check constraint "domain7_check"
+ALTER TABLE t3 ADD COLUMN b domain5 default 12; --no table rewrite
+ALTER TABLE t3 ADD COLUMN c domain6 default 13; --no table rewrite
+ALTER TABLE t3 ADD COLUMN d domain7 default 14; --no table rewrite
+--no table rewrite. explicit column default expression override domain default expression
+ALTER TABLE t3 ADD COLUMN e domain8 default 15;
+SELECT attnum, attname, atthasmissing, atthasdef, attmissingval
+FROM pg_attribute
+WHERE attnum > 0 AND attrelid = 't3'::regclass AND attisdropped is false
+ORDER BY attnum;
+ attnum | attname | atthasmissing | atthasdef | attmissingval 
+--------+---------+---------------+-----------+---------------
+      1 | a       | f             | f         | 
+      2 | b       | t             | t         | {12}
+      3 | c       | t             | t         | {13}
+      4 | d       | t             | t         | {14}
+      5 | e       | t             | t         | {15}
+(5 rows)
+
+--table rewrite. we are applying domain volatile default expresion
+ALTER TABLE t3 ADD COLUMN f domain8;
+NOTICE:  rewriting table t3 for reason 2
+SELECT  attnum, attname, atthasmissing, atthasdef, attmissingval
+FROM    pg_attribute
+WHERE   attnum > 0 AND attrelid = 't3'::regclass and not attisdropped
+ORDER BY attnum;
+ attnum | attname | atthasmissing | atthasdef | attmissingval 
+--------+---------+---------------+-----------+---------------
+      1 | a       | f             | f         | 
+      2 | b       | f             | t         | 
+      3 | c       | f             | t         | 
+      4 | d       | f             | t         | 
+      5 | e       | f             | t         | 
+      6 | f       | f             | f         | 
+(6 rows)
+
+SELECT a,b,c,d,e,f > 10 as f_ok FROM t3 ORDER BY a;
+ a | b  | c  | d  | e  | f_ok 
+---+----+----+----+----+------
+ 1 | 12 | 13 | 14 | 15 | t
+ 2 | 12 | 13 | 14 | 15 | t
+(2 rows)
+
+------test table rewrite for volatile domain constraints.
+CREATE DOMAIN domain9 as int check((value + random(min=>11::int, max=>11)) > 12); --volatile
+CREATE DOMAIN domain10 as int check((value + random(min=>11::int, max=>11)) > 12) default 1; --volatile
+CREATE TABLE t4(a int);
+INSERT INTO t4 VALUES(1),(2);
+--all these will table rewrite and be ok.
+ALTER TABLE t4 ADD COLUMN b domain9; --default to NULL
+NOTICE:  rewriting table t4 for reason 2
+ALTER TABLE t4 ADD COLUMN c domain10 default 14;
+NOTICE:  rewriting table t4 for reason 2
+SELECT COUNT(*) AS expect_zero
+FROM pg_attribute
+WHERE attnum > 0 AND attrelid = 't4'::regclass AND attmissingval IS NOT NULL;
+ expect_zero 
+-------------
+           0
+(1 row)
+
 DROP TABLE t2;
+DROP TABLE t3;
+DROP TABLE t4;
 DROP DOMAIN domain1;
 DROP DOMAIN domain2;
 DROP DOMAIN domain3;
 DROP DOMAIN domain4;
+DROP DOMAIN domain5;
+DROP DOMAIN domain6;
+DROP DOMAIN domain7;
+DROP DOMAIN domain8;
+DROP DOMAIN domain9;
+DROP DOMAIN domain10;
 DROP FUNCTION foo(INT);
 -- Fall back to full rewrite for volatile expressions
 CREATE TABLE T(pk INT NOT NULL PRIMARY KEY);
diff --git a/src/test/regress/sql/fast_default.sql b/src/test/regress/sql/fast_default.sql
index 068dd0bc8aa..e9187cd2198 100644
--- a/src/test/regress/sql/fast_default.sql
+++ b/src/test/regress/sql/fast_default.sql
@@ -287,11 +287,79 @@ ORDER BY attnum;
 
 SELECT a, b, length(c) = 3 as c_ok, d, e >= 10 as e_ok FROM t2;
 
+---test fast default over domains with check constraint
+CREATE DOMAIN domain5 AS int check(value > 10) default 8;
+CREATE DOMAIN domain6 as int not null;
+CREATE DOMAIN domain7 as int check(value is not null);
+CREATE DOMAIN domain8 as int check(value > 10) DEFAULT random(min=>10, max=>100);
+
+CREATE TABLE t3(a int);
+ALTER TABLE t3 ADD COLUMN b domain5 default 1; --error
+ALTER TABLE t3 ADD COLUMN b domain6 default NULL; --error
+ALTER TABLE t3 ADD COLUMN b domain6 default 11 + NULL; --error
+ALTER TABLE t3 ADD COLUMN b domain7 default 11 + NULL; --error
+
+ALTER TABLE t3 ADD COLUMN d domain5; --ok. because t3 is empty
+ALTER TABLE t3 ADD COLUMN b domain6; --ok. because t3 is empty
+ALTER TABLE t3 ADD COLUMN c domain7; --ok. because t3 is empty
+INSERT INTO t3 default values; --error
+INSERT INTO t3(a,b,c,d) values(1,2, 3, 12); --ok
+
+DROP TABLE t3;
+CREATE TABLE t3(a int);
+INSERT INTO t3 VALUES(1),(2);
+
+ALTER TABLE t3 ADD COLUMN b domain5; --table rewrite, then fail
+ALTER TABLE t3 ADD COLUMN b domain6; --table rewrite, then fail
+ALTER TABLE t3 ADD COLUMN b domain7; --table rewrite, then fail
+
+ALTER TABLE t3 ADD COLUMN b domain5 default 12; --no table rewrite
+ALTER TABLE t3 ADD COLUMN c domain6 default 13; --no table rewrite
+ALTER TABLE t3 ADD COLUMN d domain7 default 14; --no table rewrite
+--no table rewrite. explicit column default expression override domain default expression
+ALTER TABLE t3 ADD COLUMN e domain8 default 15;
+
+SELECT attnum, attname, atthasmissing, atthasdef, attmissingval
+FROM pg_attribute
+WHERE attnum > 0 AND attrelid = 't3'::regclass AND attisdropped is false
+ORDER BY attnum;
+
+--table rewrite. we are applying domain volatile default expresion
+ALTER TABLE t3 ADD COLUMN f domain8;
+
+SELECT  attnum, attname, atthasmissing, atthasdef, attmissingval
+FROM    pg_attribute
+WHERE   attnum > 0 AND attrelid = 't3'::regclass and not attisdropped
+ORDER BY attnum;
+
+SELECT a,b,c,d,e,f > 10 as f_ok FROM t3 ORDER BY a;
+------test table rewrite for volatile domain constraints.
+CREATE DOMAIN domain9 as int check((value + random(min=>11::int, max=>11)) > 12); --volatile
+CREATE DOMAIN domain10 as int check((value + random(min=>11::int, max=>11)) > 12) default 1; --volatile
+
+CREATE TABLE t4(a int);
+INSERT INTO t4 VALUES(1),(2);
+--all these will table rewrite and be ok.
+ALTER TABLE t4 ADD COLUMN b domain9; --default to NULL
+ALTER TABLE t4 ADD COLUMN c domain10 default 14;
+
+SELECT COUNT(*) AS expect_zero
+FROM pg_attribute
+WHERE attnum > 0 AND attrelid = 't4'::regclass AND attmissingval IS NOT NULL;
+
 DROP TABLE t2;
+DROP TABLE t3;
+DROP TABLE t4;
 DROP DOMAIN domain1;
 DROP DOMAIN domain2;
 DROP DOMAIN domain3;
 DROP DOMAIN domain4;
+DROP DOMAIN domain5;
+DROP DOMAIN domain6;
+DROP DOMAIN domain7;
+DROP DOMAIN domain8;
+DROP DOMAIN domain9;
+DROP DOMAIN domain10;
 DROP FUNCTION foo(INT);
 
 -- Fall back to full rewrite for volatile expressions
-- 
2.34.1

From bca9beb0d2da988aec25fe776ea1bfda84897d9a Mon Sep 17 00:00:00 2001
From: jian he <jian.universal...@gmail.com>
Date: Thu, 6 Mar 2025 09:58:56 +0800
Subject: [PATCH v4 2/3] add function DomainHaveVolatileConstraints

bool DomainHaveVolatileConstraints(Oid type_id, bool *have_volatile);

Returns true if the Domain has any constraints.
If you want check this domain have any volatile check constraints,
make sure have_volatile is not NULL.

discussion: https://postgr.es/m/cacjufxe_+izbr1i49k_ahigpppwltji6km8nosc7fwvkdem...@mail.gmail.com
---
 src/backend/utils/cache/typcache.c | 37 ++++++++++++++++++++++++++++++
 src/include/utils/typcache.h       |  1 +
 2 files changed, 38 insertions(+)

diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 5a3b3788d02..617d0ec27cf 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -1498,6 +1498,43 @@ DomainHasConstraints(Oid type_id)
 }
 
 
+/*
+ * Returns true if the Domain has any constraints.
+ * To check for the presence of volatile constraints, ensure
+ * have_volatile is not NULL. If a volatile constraint exists,
+ * have_volatile will be true.
+ */
+bool
+DomainHaveVolatileConstraints(Oid type_id, bool *have_volatile)
+{
+	TypeCacheEntry *typentry;
+
+	/*
+	 * Note: a side effect is to cause the typcache's domain data to become
+	 * valid.  This is fine since we'll likely need it soon if there is any.
+	 */
+	typentry = lookup_type_cache(type_id, TYPECACHE_DOMAIN_CONSTR_INFO);
+
+	if (typentry->domainData != NULL)
+	{
+		ListCell   *lc;
+
+		foreach(lc, typentry->domainData->constraints)
+		{
+			DomainConstraintState *r = (DomainConstraintState *) lfirst(lc);
+
+			if (r->constrainttype == DOM_CONSTRAINT_CHECK &&
+				contain_volatile_functions((Node *) r->check_expr))
+			{
+				*have_volatile = true;
+				break;
+			}
+		}
+		return true;
+	}
+	return false;
+}
+
 /*
  * array_element_has_equality and friends are helper routines to check
  * whether we should believe that array_eq and related functions will work
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index 562a581333a..36257c4240c 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -183,6 +183,7 @@ extern void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref,
 extern void UpdateDomainConstraintRef(DomainConstraintRef *ref);
 
 extern bool DomainHasConstraints(Oid type_id);
+extern bool DomainHaveVolatileConstraints(Oid type_id, bool *have_volatile);
 
 extern TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod);
 
-- 
2.34.1

Reply via email to