Reduce lock level for ALTER DOMAIN ... VALIDATE CONSTRAINT
authorPeter Eisentraut <peter@eisentraut.org>
Fri, 22 Aug 2025 06:51:29 +0000 (08:51 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Fri, 22 Aug 2025 06:56:11 +0000 (08:56 +0200)
Reduce from ShareLock to ShareUpdateExclusivelock.  Validation during
ALTER DOMAIN ... ADD CONSTRAINT keeps using ShareLock.

Example:

    create domain d1 as int;
    create table t (a d1);
    alter domain d1 add constraint cc10 check (value > 10) not valid;

    begin;
    alter domain d1 validate constraint cc10;

    -- another session
    insert into t values (8);

Now we should still be able to perform DML operations on table t while
the domain constraint is being validated.  The equivalent works
already on table constraints.

Author: jian he <jian.universality@gmail.com>
Reviewed-by: Dilip Kumar <dilipbalaut@gmail.com>
Reviewed-by: wenhui qiu <qiuwenhuifx@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/CACJufxHz92A88NLRTA2msgE2dpXpE-EoZ2QO61od76-6bfqurA%40mail.gmail.com

src/backend/commands/typecmds.c

index 26d985193aea42888c250b977cdc2f0d5e91fd75..c6de04819f1747aa987097ac52ea9515550cae75 100644 (file)
@@ -126,7 +126,7 @@ static Oid  findTypeSubscriptingFunction(List *procname, Oid typeOid);
 static Oid findRangeSubOpclass(List *opcname, Oid subtype);
 static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
 static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
-static void validateDomainCheckConstraint(Oid domainoid, const char *ccbin);
+static void validateDomainCheckConstraint(Oid domainoid, const char *ccbin, LOCKMODE lockmode);
 static void validateDomainNotNullConstraint(Oid domainoid);
 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
 static void checkEnumOwner(HeapTuple tup);
@@ -2986,7 +2986,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint,
         * to.
         */
        if (!constr->skip_validation)
-           validateDomainCheckConstraint(domainoid, ccbin);
+           validateDomainCheckConstraint(domainoid, ccbin, ShareLock);
 
        /*
         * We must send out an sinval message for the domain, to ensure that
@@ -3098,7 +3098,12 @@ AlterDomainValidateConstraint(List *names, const char *constrName)
    val = SysCacheGetAttrNotNull(CONSTROID, tuple, Anum_pg_constraint_conbin);
    conbin = TextDatumGetCString(val);
 
-   validateDomainCheckConstraint(domainoid, conbin);
+   /*
+    * Locking related relations with ShareUpdateExclusiveLock is ok because
+    * not-yet-valid constraints are still enforced against concurrent inserts
+    * or updates.
+    */
+   validateDomainCheckConstraint(domainoid, conbin, ShareUpdateExclusiveLock);
 
    /*
     * Now update the catalog, while we have the door open.
@@ -3191,9 +3196,16 @@ validateDomainNotNullConstraint(Oid domainoid)
 /*
  * Verify that all columns currently using the domain satisfy the given check
  * constraint expression.
+ *
+ * It is used to validate existing constraints and to add newly created check
+ * constraints to a domain.
+ *
+ * The lockmode is used for relations using the domain.  It should be
+ * ShareLock when adding a new constraint to domain.  It can be
+ * ShareUpdateExclusiveLock when validating an existing constraint.
  */
 static void
-validateDomainCheckConstraint(Oid domainoid, const char *ccbin)
+validateDomainCheckConstraint(Oid domainoid, const char *ccbin, LOCKMODE lockmode)
 {
    Expr       *expr = (Expr *) stringToNode(ccbin);
    List       *rels;
@@ -3210,9 +3222,7 @@ validateDomainCheckConstraint(Oid domainoid, const char *ccbin)
    exprstate = ExecPrepareExpr(expr, estate);
 
    /* Fetch relation list with attributes based on this domain */
-   /* ShareLock is sufficient to prevent concurrent data changes */
-
-   rels = get_rels_with_domain(domainoid, ShareLock);
+   rels = get_rels_with_domain(domainoid, lockmode);
 
    foreach(rt, rels)
    {