Skip to content
  • Benedikt Deicke's avatar
    25aa5c4b
    Adds support for deferrable foreign key constraints in PostgreSQL · 25aa5c4b
    Benedikt Deicke authored
    By default, foreign key constraints in PostgreSQL are checked after each statement. This works for most use cases, but becomes a major limitation when creating related records before the parent record is inserted into the database.
    
    One example of this is looking up / creating a person via one or more unique alias.
    
    ```ruby
    Person.transaction do
      alias = Alias
        .create_with(user_id: SecureRandom.uuid)
        .create_or_find_by(name: "DHH")
    
      person = Person
        .create_with(name: "David Heinemeier Hansson")
        .create_or_find_by(id: alias.user_id)
    end
    ```
    
    Using the default behavior, the transaction would fail when executing the first `INSERT` statement.
    
    This pull request adds support for deferrable foreign key constraints by adding a new option to the `add_foreign_key` statement in migrations:
    
    ```ruby
    add_foreign_key :aliases, :person, deferrable: true
    ```
    
    The `deferrable: true` leaves the default behavior, but allows manually deferring the checks using `SET CONSTRAINTS ALL DEFERRED` within a transaction. This will cause the foreign keys to be checked after the transaction.
    
    It's also possible to adjust the default behavior from an immediate check (after the statement), to a deferred check (after the transaction).
    
    ```ruby
    add_foreign_key :aliases, :person, deferrable: :deferred
    ```
    25aa5c4b
    Adds support for deferrable foreign key constraints in PostgreSQL
    Benedikt Deicke authored
    By default, foreign key constraints in PostgreSQL are checked after each statement. This works for most use cases, but becomes a major limitation when creating related records before the parent record is inserted into the database.
    
    One example of this is looking up / creating a person via one or more unique alias.
    
    ```ruby
    Person.transaction do
      alias = Alias
        .create_with(user_id: SecureRandom.uuid)
        .create_or_find_by(name: "DHH")
    
      person = Person
        .create_with(name: "David Heinemeier Hansson")
        .create_or_find_by(id: alias.user_id)
    end
    ```
    
    Using the default behavior, the transaction would fail when executing the first `INSERT` statement.
    
    This pull request adds support for deferrable foreign key constraints by adding a new option to the `add_foreign_key` statement in migrations:
    
    ```ruby
    add_foreign_key :aliases, :person, deferrable: true
    ```
    
    The `deferrable: true` leaves the default behavior, but allows manually deferring the checks using `SET CONSTRAINTS ALL DEFERRED` within a transaction. This will cause the foreign keys to be checked after the transaction.
    
    It's also possible to adjust the default behavior from an immediate check (after the statement), to a deferred check (after the transaction).
    
    ```ruby
    add_foreign_key :aliases, :person, deferrable: :deferred
    ```
Loading