Summary
When allow_drop_extra_constraints=False, dbconform can emit ADD CONSTRAINT for a CHECK constraint that already exists under the same name, causing PostgreSQL DuplicateObjectError instead of a clear skip or error.
Environment
- dbconform: 1.0.5
- PostgreSQL (asyncpg)
- SQLAlchemy
Enum(..., create_constraint=True, native_enum=False) CHECK constraints
Steps to reproduce
- Create a table with a SQLAlchemy string enum column (non-native enum), e.g.
run_status with CHECK constraint named runstatus.
- Change the Python enum allowed values (add/remove members).
- Run dbconform compare/apply with
allow_drop_extra_constraints=False (the default in some consumers).
Expected behavior
One of:
- Skip the ADD step and report that the constraint cannot be updated without dropping it first, or
- Treat same-name CHECK with different expression as a modify and emit DROP + ADD atomically only when drops are allowed, or
- Fail with an actionable message before executing DDL.
Actual behavior
Compare treats the change as removed check + added check (different (name, expression) keys). With allow_drop_extra_constraints=False:
- DROP is skipped (correct).
- ADD still runs →
DuplicateObjectError: constraint "runstatus" for relation "..." already exists.
Example failing SQL:
ALTER TABLE "public"."task_run_attempts" ADD CONSTRAINT "runstatus"
CHECK (run_status IN ('pending', 'in_progress', ..., 'execution_timeout', ...));
Suggested fix
In ConformPlanBuilder.build(), when processing added_checks, if a check with the same name already exists on the table (from removed_checks or reflected schema) and allow_drop_extra_constraints=False, do not emit ADD; add a SkippedStep with reason e.g. "Check constraint update blocked: allow_drop_extra_constraints=False".
Alternatively, diff same-name checks by name only into modified_checks and handle modify as drop-then-add when drops are allowed.
Summary
When
allow_drop_extra_constraints=False, dbconform can emitADD CONSTRAINTfor a CHECK constraint that already exists under the same name, causing PostgreSQLDuplicateObjectErrorinstead of a clear skip or error.Environment
Enum(..., create_constraint=True, native_enum=False)CHECK constraintsSteps to reproduce
run_statuswith CHECK constraint namedrunstatus.allow_drop_extra_constraints=False(the default in some consumers).Expected behavior
One of:
Actual behavior
Compare treats the change as removed check + added check (different
(name, expression)keys). Withallow_drop_extra_constraints=False:DuplicateObjectError: constraint "runstatus" for relation "..." already exists.Example failing SQL:
Suggested fix
In
ConformPlanBuilder.build(), when processingadded_checks, if a check with the samenamealready exists on the table (fromremoved_checksor reflected schema) andallow_drop_extra_constraints=False, do not emit ADD; add aSkippedStepwith reason e.g."Check constraint update blocked: allow_drop_extra_constraints=False".Alternatively, diff same-name checks by name only into
modified_checksand handle modify as drop-then-add when drops are allowed.