From 487cf2cbd2f5a32cf9756eb9bd6dbf9f3956c8b7 Mon Sep 17 00:00:00 2001 From: Andrew Dunstan Date: Thu, 12 Mar 2026 17:52:33 -0400 Subject: [PATCH] Extend DomainHasConstraints() to optionally check constraint volatility Add an optional bool *has_volatile output parameter to DomainHasConstraints(). When non-NULL, the function checks whether any CHECK constraint contains a volatile expression. Callers that don't need this information pass NULL and get the same behavior as before. This is needed by a subsequent commit that enables the fast default optimization for domains with non-volatile constraints: we can safely evaluate such constraints once at ALTER TABLE time, but volatile constraints require a full table rewrite. Author: Jian He Reviewed-by: Tom Lane Reviewed-by: Andrew Dunstan Reviewed-by: Viktor Holmberg Discussion: https://postgr.es/m/CACJufxE_+iZBR1i49k_AHigppPwLTJi6km8NOsC7FWvKdEmmXg@mail.gmail.com --- src/backend/commands/copyfrom.c | 2 +- src/backend/commands/tablecmds.c | 4 ++-- src/backend/executor/execExpr.c | 2 +- src/backend/optimizer/util/clauses.c | 2 +- src/backend/parser/parse_expr.c | 2 +- src/backend/utils/cache/typcache.c | 27 +++++++++++++++++++++++++-- src/include/utils/typcache.h | 2 +- 7 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c index 2f42f55e229..0ece40557c8 100644 --- a/src/backend/commands/copyfrom.c +++ b/src/backend/commands/copyfrom.c @@ -1653,7 +1653,7 @@ BeginCopyFrom(ParseState *pstate, Form_pg_attribute att = TupleDescAttr(tupDesc, attno - 1); - cstate->domain_with_constraint[i] = DomainHasConstraints(att->atttypid); + cstate->domain_with_constraint[i] = DomainHasConstraints(att->atttypid, NULL); } } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index a7c32679b22..54d93d0a0de 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -7531,7 +7531,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, defval = (Expr *) build_column_default(rel, attribute->attnum); /* Build CoerceToDomain(NULL) expression if needed */ - has_domain_constraints = DomainHasConstraints(attribute->atttypid); + has_domain_constraints = DomainHasConstraints(attribute->atttypid, NULL); if (!defval && has_domain_constraints) { Oid baseTypeId; @@ -14893,7 +14893,7 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno) { CoerceToDomain *d = (CoerceToDomain *) expr; - if (DomainHasConstraints(d->resulttype)) + if (DomainHasConstraints(d->resulttype, NULL)) return true; expr = (Node *) d->arg; } diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 088eca24021..b96eb0c55cc 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -5061,6 +5061,6 @@ ExecInitJsonCoercion(ExprState *state, JsonReturning *returning, scratch.d.jsonexpr_coercion.exists_cast_to_int = exists_coerce && getBaseType(returning->typid) == INT4OID; scratch.d.jsonexpr_coercion.exists_check_domain = exists_coerce && - DomainHasConstraints(returning->typid); + DomainHasConstraints(returning->typid, NULL); ExprEvalPushStep(state, &scratch); } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index f0f8e2515ec..9fb266d089d 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -4039,7 +4039,7 @@ eval_const_expressions_mutator(Node *node, arg = eval_const_expressions_mutator((Node *) cdomain->arg, context); if (context->estimate || - !DomainHasConstraints(cdomain->resulttype)) + !DomainHasConstraints(cdomain->resulttype, NULL)) { /* Record dependency, if this isn't estimation mode */ if (context->root && !context->estimate) diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index dcfe1acc4c3..96991cae764 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -4628,7 +4628,7 @@ transformJsonFuncExpr(ParseState *pstate, JsonFuncExpr *func) if (jsexpr->returning->typid != TEXTOID) { if (get_typtype(jsexpr->returning->typid) == TYPTYPE_DOMAIN && - DomainHasConstraints(jsexpr->returning->typid)) + DomainHasConstraints(jsexpr->returning->typid, NULL)) jsexpr->use_json_coercion = true; else jsexpr->use_io_coercion = true; diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index 627e534609a..cebe7a916fb 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -1485,10 +1485,14 @@ UpdateDomainConstraintRef(DomainConstraintRef *ref) /* * DomainHasConstraints --- utility routine to check if a domain has constraints * + * Returns true if the domain has any constraints at all. If has_volatile + * is not NULL, also checks whether any CHECK constraint contains a volatile + * expression and sets *has_volatile accordingly. + * * This is defined to return false, not fail, if type is not a domain. */ bool -DomainHasConstraints(Oid type_id) +DomainHasConstraints(Oid type_id, bool *has_volatile) { TypeCacheEntry *typentry; @@ -1498,7 +1502,26 @@ DomainHasConstraints(Oid type_id) */ typentry = lookup_type_cache(type_id, TYPECACHE_DOMAIN_CONSTR_INFO); - return (typentry->domainData != NULL); + if (typentry->domainData == NULL) + return false; + + if (has_volatile) + { + *has_volatile = false; + + foreach_node(DomainConstraintState, constrstate, + typentry->domainData->constraints) + { + if (constrstate->constrainttype == DOM_CONSTRAINT_CHECK && + contain_volatile_functions((Node *) constrstate->check_expr)) + { + *has_volatile = true; + break; + } + } + } + + return true; } diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h index 0e3945aa244..5a4aa9ec840 100644 --- a/src/include/utils/typcache.h +++ b/src/include/utils/typcache.h @@ -183,7 +183,7 @@ extern void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref, extern void UpdateDomainConstraintRef(DomainConstraintRef *ref); -extern bool DomainHasConstraints(Oid type_id); +extern bool DomainHasConstraints(Oid type_id, bool *has_volatile); extern TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod);