Skip to content

AddColumn emits an inline CHECK, then the planner adds it again — 42710 on every ALTER-path migration #96

Description

@MelbourneDeveloper

Version

DataProviderMigrate 0.9.12-beta (current latest release).

Summary

Adding a column that declares checkConstraint to an existing table plans two
operations that both create the same constraint. The second one fails and the whole
migration rolls back, so the change can never be applied.

Phase: all — applying 4 of 4 operation(s):
  AddColumnOperation
  AddCheckConstraintOperation
  CreateRlsPolicyOperation
  CreateRlsPolicyOperation
Error: migration apply failed: 42710: constraint "tool_calls_execution_target_chk"
for relation "tool_calls" already exists

AddColumnOperation already emits the CHECK inline as part of the ADD COLUMN, and
the diff engine independently notices the constraint is absent and schedules
AddCheckConstraintOperation for it too.

Proof that AddColumn is the one creating it

After the failed run, the database has neither the column nor the constraint —
the apply is transactional and rolled back cleanly:

execution_target column : None | nullable: None | default: None
constraints             : []

So the constraint did not pre-exist. It existed by the time operation 2 ran, and the
only thing between those points is operation 1.

Schema fragment

- name: execution_target
  type: Text
  isNullable: false
  defaultValue: "'client'::text"
  checkConstraint: "execution_target IN ('client', 'workspace')"

Why this is easy to miss

It only occurs on the ALTER path. On a fresh database the table is created with the
column inline, so AddColumnOperation never runs and no duplicate is planned. Our
test suite migrates a throwaway Postgres from scratch on every run and is completely
green; only the migration against the long-lived database fails. Any project whose
tests build the schema fresh will pass CI and then fail in production.

Expected

Adding a column with checkConstraint to an existing table applies once. Either
AddColumnOperation emits the bare column and leaves the CHECK to
AddCheckConstraintOperation, or the diff engine does not schedule a constraint that
the column operation already carries.

Impact

There is no way to express this without working around the bug — checkConstraint on
a column is the only form the schema language offers (we use it 19 times), and
--phase structural still contains both operations. The migration is simply
unappliable, which blocks the deploy that depends on it.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions