module PGTrunk::Operations::ForeignKeys

@private Definitions for foreign keys

We overload only add/drop operations to support features like composite keys along with anonymous key deletion.

@!parse

class ActiveRecord::Migration
  # Create a foreign key constraint
  #
  # @param [#to_s] table (nil) The qualified name of the table
  # @param [#to_s] reference (nil) The qualified name of the reference table
  # @option options [#to_s] :name (nil) The current name of the foreign key
  # @option options [#to_s] :to (nil) The new name for the foreign key
  # @option options [Array<#to_s>] :columns ([]) The list of columns of the table
  # @option options [#to_s] :column (nil) An alias for :columns for the case of single-column keys
  # @option options [Array<#to_s>] :primary_key ([]) The list of columns of the reference table
  # @option options [Symbol] :match (:full) Define how to match rows
  #   Supported values: :full (default), :partial, :simple
  # @option options [Symbol] :on_delete (:restrict)
  #   Define how to handle the deletion of the referred row.
  #   Supported values: :restrict (default), :cascade, :nullify, :reset
  # @option options [Symbol] :on_update (:restrict)
  #   Define how to handle the update of the referred row.
  #   Supported values: :restrict (default), :cascade, :nullify, :reset
  # @yield [k] the block with the key's definition
  # @yieldparam Object receiver of methods specifying the foreign key
  # @return [void]
  #
  # The table and reference of the new key must be set explicitly.
  # All the rest (including the name) can be generated by default:
  #
  # ```ruby
  # # same as `..., column: 'role_id', primary_key: 'id'`
  # add_foreign_key :users, :roles
  # ```
  #
  # The block syntax can be used for any argument:
  #
  # ```ruby
  # add_foreign_key do |k|
  #   k.table "users"
  #   k.reference "roles"
  #   k.column "role_id" # (generated by default from reference and pk)
  #   k.primary_key "id" # (default)
  #   k.on_update :cascade # :restrict (default)
  #   k.on_delete :cascade # :restrict (default)
  #   k.name "user_roles_fk" # can be generated
  #   k.comment "Phone is 10+ chars long"
  # end
  # ```
  #
  # Composite foreign keys are supported as well:
  #
  # ```ruby
  # add_foreign_key "users", "roles" do |k|
  #   k.columns %w[role_name role_id]
  #   k.primary_key %w[name id] # Requires unique index
  #   k.match :full # :partial, :simple (default)
  # end
  # ```
  #
  # The operation is always invertible.
  def add_foreign_key(table, reference, **options, &block); end
end

@!parse

class ActiveRecord::Migration
  # Drops a foreign key constraint
  #
  # @param [#to_s] table (nil) The qualified name of the table
  # @param [#to_s] reference (nil) The qualified name of the reference table
  # @option options [#to_s] :name (nil) The current name of the foreign key
  # @option options [Boolean] :if_exists (false) Suppress the error when the constraint is absent
  # @option options [#to_s] :to (nil) The new name for the foreign key
  # @option options [Array<#to_s>] :columns ([]) The list of columns of the table
  # @option options [#to_s] :column (nil) An alias for :columns for the case of single-column keys
  # @option options [Array<#to_s>] :primary_key ([]) The list of columns of the reference table
  # @option options [Symbol] :match (:full) Define how to match rows
  #   Supported values: :full (default), :partial, :simple
  # @option options [Symbol] :on_delete (:restrict)
  #   Define how to handle the deletion of the referred row.
  #   Supported values: :restrict (default), :cascade, :nullify, :reset
  # @option options [Symbol] :on_update (:restrict)
  #   Define how to handle the update of the referred row.
  #   Supported values: :restrict (default), :cascade, :nullify, :reset
  # @yield [k] the block with the key's definition
  # @yieldparam Object receiver of methods specifying the foreign key
  # @return [void]
  #
  # The key can be identified by table/name (not invertible):
  #
  # ```ruby
  # drop_foreign_key "users", name: "user_roles_fk"
  # ```
  #
  # To make it invertible use the same options like
  # in the `add_foreign_key` operation.
  #
  # ```ruby
  # drop_foreign_key do |k|
  #   k.table "users"
  #   k.reference "roles"
  #   k.column "role_id"
  #   k.primary_key "id"
  #   k.on_update :cascade
  #   k.on_delete :cascade
  #   k.comment "Phone is 10+ chars long"
  # end
  # ```
  #
  # Notice that the name can be skipped, in this case we would
  # find it in the database.
  #
  # The operation can be called with `if_exists` option.
  #
  # ```ruby
  # drop_foreign_key "users", name: "user_roles_fk", if_exists: true
  # ```
  #
  # In this case the operation is always irreversible due to
  # uncertainty of the previous state of the database.
  def drop_foreign_key(table, reference, **options, &block); end
end

@!parse

class ActiveRecord::Migration
  # Rename a foreign key
  #
  # @param [#to_s] table (nil) The qualified name of the table
  # @param [#to_s] reference (nil) The qualified name of the reference table
  # @option options [#to_s] :name (nil) The current name of the foreign key
  # @option options [#to_s] :to (nil) The new name for the foreign key
  # @option options [Array<#to_s>] :columns ([]) The list of columns of the table
  # @option options [#to_s] :column (nil) An alias for :columns for the case of single-column keys
  # @option options [Array<#to_s>] :primary_key ([]) The list of columns of the reference table
  # @yield [k] the block with the key's definition
  # @yieldparam Object receiver of methods specifying the foreign key
  # @return [void]
  #
  # You can rename the foreign key constraint identified by its explicit name:
  #
  # ```ruby
  # rename_foreign_key :users,
  #                    name: "user_roles_fk",
  #                    to: "constraints.users_by_roles_fk"
  # ```
  #
  # The key can also be found in the database by table/reference/columns/pk
  #
  # ```ruby
  # rename_foreign_key :users, :roles, primary_key: "name", to: "user_roles"
  # ```
  #
  # If a new name is missed, then the name will be reset to the auto-generated one:
  #
  # ```ruby
  # rename_foreign_key :users, "user_roles_fk"
  # ```
  #
  # The operation is always reversible.
  def rename_foreign_key(table, reference, **options, &block); end
end