Skip to content

Support DEFERRABLE on CHECK constraints (Oracle-specific extension) #2718

@yahonda

Description

@yahonda

Background

Oracle Database accepts the DEFERRABLE INITIALLY {DEFERRED | IMMEDIATE} / NOT DEFERRABLE clause on CHECK constraints (verified directly: ALTER TABLE t ADD CONSTRAINT c CHECK (...) DEFERRABLE INITIALLY DEFERRED). The state is persisted in all_constraints (deferrable, deferred columns) just like for FK and UNIQUE constraints.

This is an Oracle-specific extension — PostgreSQL explicitly disallows DEFERRABLE on CHECK constraints, MySQL and SQLite do not have the concept. Active Record core therefore does not expose deferrable: on add_check_constraint, and the existing CHECK-constraint integration added in #2717 does not surface it on the Oracle adapter either.

For Oracle-only applications, deferrable CHECK constraints are useful in the same scenarios deferrable FK / UNIQUE constraints are: bulk loading or migrations where intermediate states violate the predicate but the final state is consistent.

Scope

Mirror the FK and UNIQUE deferrable handling already in oracle-enhanced (since #2594 / #2701):

  • add_check_constraint :t, "expr", deferrable: :deferred, name: "..." accepts the option.
  • assert_valid_deferrable(deferrable) is reused to validate :deferred / :immediate / false / nil.
  • OracleEnhanced::SchemaCreation#visit_CheckConstraintDefinition overrides the Rails-abstract visitor (which emits just CONSTRAINT n CHECK (expr)) to append DEFERRABLE INITIALLY ... after the expression, parallel to how visit_ForeignKeyDefinition and visit_UniqueConstraintDefinition already do.
  • OracleEnhanced::SchemaStatements#check_constraints(table_name) reader extends the SQL to also select c.deferrable, c.deferred, and populates options[:deferrable] via the existing extract_foreign_key_deferrable helper.
  • OracleEnhanced::StructureDump#structure_dump_check_constraints extends the emitted DDL with the DEFERRABLE INITIALLY ... clause when applicable, so the SQL structure dump round-trips faithfully too.
  • OracleEnhanced::SchemaDumper already calls check_constraints_in_create (since Wire up CHECK constraint DSL: add_check_constraint, t.check_constraint, dump round-trip #2717), and the abstract dumper's check_parts will pick up deferrable: automatically once the reader exposes it — but verify, and adjust if check_parts filters Oracle-specific options.

Specs cover:

Out of scope

  • A general deferrable: option on the upstream Rails add_check_constraint. That would be a Rails-core change, not an oracle-enhanced one.
  • Interaction with validate: false — separate concern (tracked in the validate_constraint issue / follow-up PR).

Why now

#2717 just landed CHECK constraint support. The feature parity with FK / UC deferrable handling is the natural next step, and adding it now (while the check_constraints reader and visitor are fresh) keeps the change small.

Acceptance criteria

  • add_check_constraint :t, "expr", deferrable: :deferred emits CHECK (expr) DEFERRABLE INITIALLY DEFERRED and round-trips through check_constraints(table_name).
  • add_check_constraint :t, "expr", deferrable: :immediate emits DEFERRABLE INITIALLY IMMEDIATE and round-trips.
  • add_check_constraint :t, "expr" (no :deferrable) emits no DEFERRABLE clause and cc.deferrable == false.
  • Schema dump (Ruby + SQL structure) preserves the option; load reproduces the same state.
  • Specs covering each path.

Depends on: #2716 (PR #2717) landing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions