module ActiveRecord::ConnectionAdapters::SchemaStatements

def add_check_constraint(table_name, expression, if_not_exists: false, **options)

(PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
[:validate]
Silently ignore if the constraint already exists, rather than raise an error.
[:if_not_exists]
The constraint name. Defaults to chk_rails_.
[:name]
The +options+ hash can include the following keys:

ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)

generates:

add_check_constraint :products, "price > 0", name: "price_check"

representation of verifiable boolean condition.
Adds a new check constraint to the table. +expression+ is a String
def add_check_constraint(table_name, expression, if_not_exists: false, **options)
  return unless supports_check_constraints?
  options = check_constraint_options(table_name, expression, options)
  return if if_not_exists && check_constraint_exists?(table_name, **options)
  at = create_alter_table(table_name)
  at.add_check_constraint(expression, options)
  execute schema_creation.accept(at)
end

def add_column(table_name, column_name, type, **options)

add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
# Ignores the method call if the column exists

# ALTER TABLE "shapes" ADD "triangle" polygon
add_column(:shapes, :triangle, 'polygon')
# Defines a column with a database-specific type.

# ALTER TABLE "users" ADD "skills" text[]
add_column(:users, :skills, :text, array: true)
# Defines a column that stores an array of a type.

# ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
add_column(:measurements, :huge_integer, :decimal, precision: 30)
# probably wouldn't hurt to include it.
# While :scale defaults to zero on most databases, it

# ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)

# ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)

# ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)

# ALTER TABLE "users" ADD "picture" blob(2097152)
add_column(:users, :picture, :binary, limit: 2.megabytes)

== Examples

Default (38,0).
* SqlServer: :precision [1..38], :scale [0..38].
Default is (38,0).
* Oracle: :precision [1..38], :scale [-84..127].
but the maximum supported :precision is 16. No default.
* SQLite3: No restrictions on :precision and :scale,
:scale [0..infinity]. No default.
* PostgreSQL: :precision [1..infinity],
Default is (10,0).
* MySQL: :precision [1..65], :scale [0..30].
:precision.
:precision, and makes no comments about the requirements of
* The SQL standard says the default scale should be 0, :scale <=
:decimal columns:
Please be aware of different RDBMS implementations behavior with

range from -999.99 to 999.99.
and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
the decimal point. For example, the number 123.45 has a precision of 5
and the scale is the number of digits that can be stored following
Note: The precision is the total number of significant digits,

duplicate column errors.
Specifies if the column already exists to not try to re-add it. This will avoid
* :if_not_exists -
Specifies the scale for the :decimal and :numeric columns.
* :scale -
:datetime, and :time columns.
Specifies the precision for the :decimal, :numeric,
* :precision -
Allows or disallows +NULL+ values in the column.
* :null -
This option is ignored by some backends.
and number of bytes for :text, :binary, :blob, and :integer columns.
Requests a maximum column length. This is the number of characters for a :string column
* :limit -
The column's default value. Use +nil+ for +NULL+.
* :default -
If not specified, the column will have the same collation as the table.
Specifies the collation for a :string or :text column.
* :collation -
Specifies the comment for the column. This option is ignored by some backends.
* :comment -
Available options are (none of these exists by default):

agnostic and should usually be avoided.
database (for example, "polygon" in MySQL), but this will not be database
You may use a type not in this list as long as it is supported by your

:binary, :blob, :boolean.
:datetime, :time, :date,
:integer, :bigint, :float, :decimal, :numeric,
:primary_key, :string, :text,
which is one of the following:
The +type+ parameter is normally one of the migrations native types,

See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].

Add a new +type+ column named +column_name+ to +table_name+.
def add_column(table_name, column_name, type, **options)
  add_column_def = build_add_column_definition(table_name, column_name, type, **options)
  return unless add_column_def
  execute schema_creation.accept(add_column_def)
end

def add_column_for_alter(table_name, column_name, type, **options)

def add_column_for_alter(table_name, column_name, type, **options)
  td = create_table_definition(table_name)
  cd = td.new_column_definition(column_name, type, **options)
  schema_creation.accept(AddColumnDefinition.new(cd))
end

def add_columns(table_name, *column_names, type:, **options) # :nodoc:

:nodoc:
def add_columns(table_name, *column_names, type:, **options) # :nodoc:
  column_names.each do |column_name|
    add_column(table_name, column_name, type, **options)
  end
end

def add_foreign_key(from_table, to_table, **options)

+:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
(PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
[:deferrable]
(PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
[:validate]
duplicate column errors.
Specifies if the foreign key already exists to not try to re-add it. This will avoid
[:if_not_exists]
Action that happens ON UPDATE. Valid values are +:nullify+, +:cascade+, and +:restrict+
[:on_update]
Action that happens ON DELETE. Valid values are +:nullify+, +:cascade+, and +:restrict+
[:on_delete]
The constraint name. Defaults to fk_rails_.
[:name]
Pass an array to create a composite foreign key.
The primary key column name on +to_table+. Defaults to +id+.
[:primary_key]
Pass an array to create a composite foreign key.
The foreign key column name on +from_table+. Defaults to to_table.singularize + "_id".
[:column]
The +options+ hash can include the following keys:

ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE

generates:

add_foreign_key :articles, :authors, on_delete: :cascade

====== Creating a cascading foreign key

ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")

generates:

add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]

Assuming "carts" table has "(shop_id, user_id)" as a primary key.

====== Creating a composite foreign key

ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")

generates:

add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"

====== Creating a foreign key on a specific column

add_foreign_key(:articles, :authors, if_not_exists: true)

====== Creating a foreign key, ignoring method call if the foreign key exists

ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")

generates:

add_foreign_key :articles, :authors

====== Creating a simple foreign key

+from_table+ and +column+. A custom name can be specified with the :name option.
+identifier+ is a 10 character long string which is deterministically generated from the
The foreign key will be named after the following pattern: fk_rails_.

+to_table+ contains the referenced primary key.
Adds a new foreign key. +from_table+ is the table with the key column,
def add_foreign_key(from_table, to_table, **options)
  return unless use_foreign_keys?
  return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
  options = foreign_key_options(from_table, to_table, options)
  at = create_alter_table from_table
  at.add_foreign_key to_table, options
  execute schema_creation.accept(at)
end

def add_index(table_name, column_name, **options)

For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].

Concurrently adding an index is not supported in a transaction.

Note: only supported by PostgreSQL.

# CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
add_index(:developers, :name, algorithm: :concurrently)

====== Creating an index with a specific algorithm

Note: only supported by MySQL.

CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL

generates:

add_index(:developers, :name, type: :fulltext)

====== Creating an index with a specific type

Note: only supported by PostgreSQL

# CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops)

# CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
add_index(:developers, [:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops })

# CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
add_index(:developers, :name, using: 'gist', opclass: :gist_trgm_ops)

====== Creating an index with a specific operator class

Note: only supported by PostgreSQL and MySQL

CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL

generates:

add_index(:developers, :name, using: 'btree')

====== Creating an index with a specific method

Note: only supported by PostgreSQL.

CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)

generates:

add_index(:accounts, :branch_id, include: :party_id)

====== Creating an index that includes additional columns

Note: Partial indexes are only supported for PostgreSQL and SQLite.

CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active

generates:

add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")

====== Creating a partial index

Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).

CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)

generates:

add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})

====== Creating an index with a sort order (desc or asc, asc is the default)

Note: only supported by MySQL

CREATE INDEX by_name_surname ON accounts(name(10), surname(15))

generates:

add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})

====== Creating an index with specific key lengths for multiple keys

CREATE INDEX by_name ON accounts(name(10))

generates:

add_index(:accounts, :name, name: 'by_name', length: 10)

====== Creating an index with specific key length

CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)

generates:

add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')

====== Creating a named index

CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)

generates:

add_index(:accounts, [:branch_id, :party_id], unique: true)

====== Creating a unique index

Note: Not supported by MySQL.

CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)

generates:

add_index(:suppliers, :name, if_not_exists: true)

====== Creating a index which already exists

CREATE INDEX index_suppliers_on_name ON suppliers(name)

generates:

add_index(:suppliers, :name)

====== Creating a simple index

you pass :name as an option.
The index will be named after the table and the column name(s), unless

an Array of Symbols.
Adds a new index to the table. +column_name+ can be a single Symbol, or
def add_index(table_name, column_name, **options)
  create_index = build_create_index_definition(table_name, column_name, **options)
  execute schema_creation.accept(create_index)
end

def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:

:nodoc:
def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
  options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
  column_names = index_column_names(column_name)
  index_name = name&.to_s
  index_name ||= index_name(table_name, column_names)
  validate_index_length!(table_name, index_name, internal)
  index = IndexDefinition.new(
    table_name, index_name,
    options[:unique],
    column_names,
    lengths: options[:length] || {},
    orders: options[:order] || {},
    opclasses: options[:opclass] || {},
    where: options[:where],
    type: options[:type],
    using: options[:using],
    include: options[:include],
    nulls_not_distinct: options[:nulls_not_distinct],
    comment: options[:comment]
  )
  [index, index_algorithm(options[:algorithm]), if_not_exists]
end

def add_index_sort_order(quoted_columns, **options)

def add_index_sort_order(quoted_columns, **options)
  orders = options_for_index_columns(options[:order])
  quoted_columns.each do |name, column|
    column << " #{orders[name].upcase}" if orders[name].present?
  end
end

def add_options_for_index_columns(quoted_columns, **options)

the PostgreSQL adapter for supporting operator classes.
Overridden by the MySQL adapter for supporting index lengths and by
def add_options_for_index_columns(quoted_columns, **options)
  if supports_index_sort_order?
    quoted_columns = add_index_sort_order(quoted_columns, **options)
  end
  quoted_columns
end

def add_reference(table_name, ref_name, **options)


add_reference(:products, :supplier, foreign_key: { to_table: :firms })

====== Create a supplier_id column and a foreign key to the firms table

add_reference(:products, :supplier, foreign_key: true)

====== Create a supplier_id column and appropriate foreign key

add_reference(:products, :supplier, index: { name: "my_supplier_index" })

====== Create a supplier_id column with a named index

add_reference(:products, :supplier, index: { unique: true })

====== Create a supplier_id column with a unique index

add_reference(:products, :supplier, polymorphic: true)

====== Create supplier_id, supplier_type columns

add_reference(:products, :user, type: :string)

====== Create a user_id string column

add_reference(:products, :user, index: false)

====== Create a user_id bigint column without an index

Whether the column allows nulls. Defaults to true.
[:null]
Whether an additional +_type+ column should be added. Defaults to false.
[:polymorphic]
pass :to_table with the appropriate table name.
to add. In case the join table can't be inferred from the association
Add an appropriate foreign key constraint. Defaults to false, pass true
[:foreign_key]
See #add_index for usage of this option.
Add an appropriate index. Defaults to true.
[:index]
The reference column type. Defaults to +:bigint+.
[:type]
The +options+ hash can include the following keys:

Optionally adds a +_type+ column, if :polymorphic option is provided.
the :type option can be used to specify a different type.
Adds a reference. The reference column is a bigint by default,
def add_reference(table_name, ref_name, **options)
  ReferenceDefinition.new(ref_name, **options).add(table_name, self)
end

def add_timestamps(table_name, **options)


add_timestamps(:suppliers, null: true)

Additional options (like +:null+) are forwarded to #add_column.
Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
def add_timestamps(table_name, **options)
  fragments = add_timestamps_for_alter(table_name, **options)
  execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
end

def add_timestamps_for_alter(table_name, **options)

def add_timestamps_for_alter(table_name, **options)
  options[:null] = false if options[:null].nil?
  if !options.key?(:precision) && supports_datetime_with_precision?
    options[:precision] = 6
  end
  [
    add_column_for_alter(table_name, :created_at, :datetime, **options),
    add_column_for_alter(table_name, :updated_at, :datetime, **options)
  ]
end

def assume_migrated_upto_version(version)

def assume_migrated_upto_version(version)
  version = version.to_i
  sm_table = quote_table_name(schema_migration.table_name)
  migrated = migration_context.get_all_versions
  versions = migration_context.migrations.map(&:version)
  unless migrated.include?(version)
    execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
  end
  inserting = (versions - migrated).select { |v| v < version }
  if inserting.any?
    if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
      raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
    end
    execute insert_versions_sql(inserting)
  end
end

def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:

:nodoc:
passing a +table_name+, +column_name+, +type+ and other options that can be passed.
if the same arguments were passed to #add_column. See #add_column for information about
This definition object contains information about the column that would be created

Builds an AlterTable object for adding a column to a table.
def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
  return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
  if supports_datetime_with_precision?
    if type == :datetime && !options.key?(:precision)
      options[:precision] = 6
    end
  end
  alter_table = create_alter_table(table_name)
  alter_table.add_column(column_name, type, **options)
  alter_table
end

def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:

:nodoc:
information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
if the same arguments were passed to #change_column_default. See #change_column_default for
This definition object contains information about the column change that would occur

Builds a ChangeColumnDefaultDefinition object.
def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
  raise NotImplementedError, "build_change_column_default_definition is not implemented"
end

def build_create_index_definition(table_name, column_name, **options) # :nodoc:

:nodoc:
passing a +table_name+, +column_name+, and other additional options that can be passed.
if the same arguments were passed to #add_index. See #add_index for information about
This definition object contains information about the index that would be created

Builds a CreateIndexDefinition object.
def build_create_index_definition(table_name, column_name, **options) # :nodoc:
  index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
  CreateIndexDefinition.new(index, algorithm, if_not_exists)
end

def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:

:nodoc:
information about what arguments should be passed.
if the same arguments were passed to #create_join_table. See #create_join_table for
This definition object contains information about the table that would be created

Builds a TableDefinition object for a join table.
def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
  join_table_name = find_join_table_name(table_1, table_2, options)
  column_options.reverse_merge!(null: false, index: false)
  t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
  build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
    td.references t1_ref, **column_options
    td.references t2_ref, **column_options
    yield td if block_given?
  end
end

def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)

passing a +table_name+, and other additional options that can be passed.
if the same arguments were passed to #create_table. See #create_table for information about
Returns a TableDefinition object containing information about the table that would be created
def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
  table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
  table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
  yield table_definition if block_given?
  table_definition
end

def bulk_change_table(table_name, operations) # :nodoc:

:nodoc:
def bulk_change_table(table_name, operations) # :nodoc:
  sql_fragments = []
  non_combinable_operations = []
  operations.each do |command, args|
    table, arguments = args.shift, args
    method = :"#{command}_for_alter"
    if respond_to?(method, true)
      sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
      sql_fragments.concat(sqls)
      non_combinable_operations.concat(procs)
    else
      execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
      non_combinable_operations.each(&:call)
      sql_fragments = []
      non_combinable_operations = []
      send(command, table, *arguments)
    end
  end
  execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
  non_combinable_operations.each(&:call)
end

def can_remove_index_by_name?(column_name, options)

def can_remove_index_by_name?(column_name, options)
  column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
end

def change_column(table_name, column_name, type, **options)


change_column(:accounts, :description, :text)
change_column(:suppliers, :name, :string, limit: 80)

See TableDefinition#column for details of the options you can use.
Changes the column's definition according to the new options.
def change_column(table_name, column_name, type, **options)
  raise NotImplementedError, "change_column is not implemented"
end

def change_column_comment(table_name, column_name, comment_or_changes)

change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")

reversible in migration:
Passing a hash containing +:from+ and +:to+ will make this change

Changes the comment for a column or removes it if +nil+.
def change_column_comment(table_name, column_name, comment_or_changes)
  raise NotImplementedError, "#{self.class} does not support changing column comments"
end

def change_column_default(table_name, column_name, default_or_changes)


change_column_default(:posts, :state, from: nil, to: "draft")

reversible in migration:
Passing a hash containing +:from+ and +:to+ will make this change

change_column_default(:users, :email, nil)

Setting the default to +nil+ effectively drops the default:

change_column_default(:accounts, :authorized, 1)
change_column_default(:suppliers, :qualification, 'new')

Sets a new default value for a column:
def change_column_default(table_name, column_name, default_or_changes)
  raise NotImplementedError, "change_column_default is not implemented"
end

def change_column_default_for_alter(table_name, column_name, default_or_changes)

def change_column_default_for_alter(table_name, column_name, default_or_changes)
  cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
  schema_creation.accept(cd)
end

def change_column_null(table_name, column_name, null, default = nil)

Please note the fourth argument does not set a column's default.

constraint if needed, since otherwise those rows would not be valid.
NULLs with some other value. Use that one when enabling the
The method accepts an optional fourth argument to replace existing

allows them to be +NULL+ (drops the constraint).

change_column_null(:users, :nickname, true)

says nicknames cannot be +NULL+ (adds the constraint), whereas

change_column_null(:users, :nickname, false)

indicates whether the value can be +NULL+. For example
Sets or removes a NOT NULL constraint on a column. The +null+ flag
def change_column_null(table_name, column_name, null, default = nil)
  raise NotImplementedError, "change_column_null is not implemented"
end

def change_table(table_name, base = self, **options)

See also Table for details on all of the various column transformations.

end
t.remove_index :company_id
change_table(:suppliers) do |t|

====== Remove an index

end
t.remove :width, :height
t.remove :company_id
change_table(:suppliers) do |t|

====== Remove several columns

end
t.remove :company
change_table(:suppliers) do |t|

====== Remove a column

Creates company_type(varchar) and company_id(bigint) columns.

end
t.belongs_to :company, polymorphic: true
change_table(:suppliers) do |t|

====== Add a polymorphic foreign key column

Creates a company_id(bigint) column.

end
t.references :company
change_table(:suppliers) do |t|

====== Add a foreign key column

end
t.timestamps
change_table(:suppliers) do |t|

====== Add created_at/updated_at columns

end
t.integer :width, :height, null: false, default: 0
change_table(:suppliers) do |t|

====== Add 2 integer columns

end
t.change :metadata, :json
change_table(:suppliers) do |t|

====== Change type of a column

end
t.column :name, :string, limit: 60
change_table(:suppliers) do |t|

====== Add a column

Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.

Defaults to false.

ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...

Set this to true to make this a bulk alter query, such as
[:bulk]
The +options+ hash can include the following keys:

end
# Other column alterations here
t.column :name, :string, limit: 60
change_table(:suppliers) do |t|
# change_table() yields a Table instance

A block for changing columns in +table+.
def change_table(table_name, base = self, **options)
  if supports_bulk_alter? && options[:bulk]
    recorder = ActiveRecord::Migration::CommandRecorder.new(self)
    yield update_table_definition(table_name, recorder)
    bulk_change_table(table_name, recorder.commands)
  else
    yield update_table_definition(table_name, base)
  end
end

def change_table_comment(table_name, comment_or_changes)

change_table_comment(:posts, from: "old_comment", to: "new_comment")

reversible in migration:
Passing a hash containing +:from+ and +:to+ will make this change

Changes the comment for a table or removes it if +nil+.
def change_table_comment(table_name, comment_or_changes)
  raise NotImplementedError, "#{self.class} does not support changing table comments"
end

def check_constraint_exists?(table_name, **options)


check_constraint_exists?(:products, name: "price_check")

Checks to see if a check constraint exists on a table for a given check constraint definition.
def check_constraint_exists?(table_name, **options)
  if !options.key?(:name) && !options.key?(:expression)
    raise ArgumentError, "At least one of :name or :expression must be supplied"
  end
  check_constraint_for(table_name, **options).present?
end

def check_constraint_for(table_name, **options)

def check_constraint_for(table_name, **options)
  return unless supports_check_constraints?
  chk_name = check_constraint_name(table_name, **options)
  check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
end

def check_constraint_for!(table_name, expression: nil, **options)

def check_constraint_for!(table_name, expression: nil, **options)
  check_constraint_for(table_name, expression: expression, **options) ||
    raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
end

def check_constraint_name(table_name, **options)

def check_constraint_name(table_name, **options)
  options.fetch(:name) do
    expression = options.fetch(:expression)
    identifier = "#{table_name}_#{expression}_chk"
    hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
    "chk_rails_#{hashed_identifier}"
  end
end

def check_constraint_options(table_name, expression, options) # :nodoc:

:nodoc:
def check_constraint_options(table_name, expression, options) # :nodoc:
  options = options.dup
  options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
  options
end

def check_constraints(table_name)

The check constraints are represented as CheckConstraintDefinition objects.
Returns an array of check constraints for the given table.
def check_constraints(table_name)
  raise NotImplementedError
end

def column_exists?(table_name, column_name, type = nil, **options)


column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
column_exists?(:suppliers, :name, :string, null: false)
column_exists?(:suppliers, :name, :string, default: 'default')
column_exists?(:suppliers, :name, :string, limit: 100)
# Check a column exists with a specific definition

column_exists?(:suppliers, :name, :string)
# for types that may get cast to something else (eg. char, bigint).
# This works for standard non-casted types (eg. string) but is unreliable
#
# Check a column exists of a particular type

column_exists?(:suppliers, :name)
# Check a column exists

Checks to see if a column exists in a given table.
def column_exists?(table_name, column_name, type = nil, **options)
  column_name = column_name.to_s
  checks = []
  checks << lambda { |c| c.name == column_name }
  checks << lambda { |c| c.type == type.to_sym rescue nil } if type
  column_options_keys.each do |attr|
    checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
  end
  columns(table_name).any? { |c| checks.all? { |check| check[c] } }
end

def column_options_keys

def column_options_keys
  [:limit, :precision, :scale, :default, :null, :collation, :comment]
end

def columns(table_name)

Returns an array of +Column+ objects for the table specified by +table_name+.
def columns(table_name)
  table_name = table_name.to_s
  definitions = column_definitions(table_name)
  definitions.map do |field|
    new_column_from_field(table_name, field, definitions)
  end
end

def columns_for_distinct(columns, orders) # :nodoc:

:nodoc:

columns_for_distinct("posts.id", ["posts.created_at desc"])

require the order columns appear in the SELECT.
PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
def columns_for_distinct(columns, orders) # :nodoc:
  columns
end

def create_alter_table(name)

def create_alter_table(name)
  AlterTable.new create_table_definition(name)
end

def create_join_table(table_1, table_2, column_options: {}, **options)


) ENGINE=InnoDB DEFAULT CHARSET=utf8
part_id bigint NOT NULL,
assembly_id bigint NOT NULL,
CREATE TABLE assemblies_parts (

generates:

create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')

====== Add a backend specific option to the generated SQL (MySQL)

end
t.index :category_id
t.index :product_id
create_join_table :products, :categories do |t|

its block form to do so yourself:
Note that #create_join_table does not create any indices by default; you can use

Defaults to false.
Set to true to drop the table before creating it.
[:force]
Make a temporary table.
[:temporary]
Any extra options you want appended to the table definition.
[:options]
Any extra options you want appended to the columns definition.
[:column_options]
Sets the table name, overriding the default.
[:table_name]
You can pass an +options+ hash which can include the following keys:

create_join_table(:assemblies, :parts)
# Creates a table called 'assemblies_parts' with no id.

arguments. These arguments can be a String or a Symbol.
Creates a new join table with the name created using the lexical order of the first two
def create_join_table(table_1, table_2, column_options: {}, **options)
  join_table_name = find_join_table_name(table_1, table_2, options)
  column_options.reverse_merge!(null: false, index: false)
  t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
  create_table(join_table_name, **options.merge!(id: false)) do |td|
    td.references t1_ref, **column_options
    td.references t2_ref, **column_options
    yield td if block_given?
  end
end

def create_schema_dumper(options) # :nodoc:

:nodoc:
def create_schema_dumper(options) # :nodoc:
  SchemaDumper.create(self, options)
end

def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)

See also TableDefinition#column for details on how to create columns.

SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
CREATE TEMPORARY TABLE long_query AS

generates:

as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
create_table(:long_query, temporary: true,

====== Create a temporary table based on a query

)
supplier_id bigint
category_id bigint,
CREATE TABLE categories_suppliers (

generates:

end
t.column :supplier_id, :bigint
t.column :category_id, :bigint
create_table(:categories_suppliers, id: false) do |t|

====== Do not add a primary key column

ADD CONSTRAINT orders_pkey PRIMARY KEY (product_id, client_id);
ALTER TABLE ONLY "orders"

);
client_id bigint NOT NULL
product_id bigint NOT NULL,
CREATE TABLE orders (

generates:

end
t.belongs_to :client
t.belongs_to :product
create_table(:orders, primary_key: [:product_id, :client_id]) do |t|

====== Create a composite primary key

)
label varchar
id varchar PRIMARY KEY,
CREATE TABLE tags (

generates:

end
t.column :label, :string
create_table(:tags, id: :string) do |t|

====== Change the primary key column type

)
name varchar(80)
guid bigint auto_increment PRIMARY KEY,
CREATE TABLE objects (

generates:

end
t.column :name, :string, limit: 80
create_table(:objects, primary_key: 'guid') do |t|

====== Rename the primary key column

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
id bigint auto_increment PRIMARY KEY
CREATE TABLE suppliers (

generates:

create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')

====== Add a backend specific option to the generated SQL (MySQL)

ignored, as are the :id and :primary_key options.
SQL to use to generate the table. When this option is used, the block is
[:as]
Defaults to false.
Set to true to avoid raising an error when the table already exists.
[:if_not_exists]
Defaults to false.
Set to +:cascade+ to drop dependent objects as well.
Set to true to drop the table before creating it.
[:force]
Make a temporary table.
[:temporary]
Any extra options you want appended to the table definition.
[:options]

to define the key explicitly.
{self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
primary key. This can be avoided by using
Note that Active Record models will automatically detect their

If an array is passed, a composite primary key will be created.

Defaults to +id+. If :id is false, then this option is ignored.
The name of the primary key, if one is to be added automatically.
[:primary_key]
A Symbol can be used to specify the type of the generated primary key column.

Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
Whether to automatically add a primary key column. Defaults to true.
[:id]
The +options+ hash can include the following keys:

add_column(:suppliers, :name, :string, {limit: 60})
# Add a column to 'suppliers'.
create_table(:suppliers)
# Creates a table called 'suppliers' with no columns.

=== Regular form

end
# Other fields here
t.string :name, limit: 60
create_table(:suppliers) do |t|
# You can also use the column types as method calls, rather than calling the column method.

=== Block form, with shorthand

end
# Other fields here
t.column :name, :string, limit: 60
create_table(:suppliers) do |t|

# table.
# This form will not only create the table, but also columns for the
# create_table() passes a TableDefinition object to the block.

=== Block form

form or the regular form, like this:
There are two ways to work with #create_table. You can use the block

be a String or a Symbol.
Creates a new table with the name +table_name+. +table_name+ may either
def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
  validate_create_table_options!(options)
  validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
  td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
  if force
    drop_table(table_name, force: force, if_exists: true)
  else
    schema_cache.clear_data_source_cache!(table_name.to_s)
  end
  result = execute schema_creation.accept(td)
  unless supports_indexes_in_create?
    td.indexes.each do |column_name, index_options|
      add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
    end
  end
  if supports_comments? && !supports_comments_in_create?
    if table_comment = td.comment.presence
      change_table_comment(table_name, table_comment)
    end
    td.columns.each do |column|
      change_column_comment(table_name, column.name, column.comment) if column.comment.present?
    end
  end
  result
end

def create_table_definition(name, **options)

def create_table_definition(name, **options)
  TableDefinition.new(self, name, **options)
end

def data_source_exists?(name)


data_source_exists?(:ebooks)

Checks to see if the data source +name+ exists on the database.
def data_source_exists?(name)
  query_values(data_source_sql(name), "SCHEMA").any? if name.present?
rescue NotImplementedError
  data_sources.include?(name.to_s)
end

def data_source_sql(name = nil, type: nil)

def data_source_sql(name = nil, type: nil)
  raise NotImplementedError
end

def data_sources

For most adapters this means all #tables and #views.
Returns the relation names usable to back Active Record models.
def data_sources
  query_values(data_source_sql, "SCHEMA")
rescue NotImplementedError
  tables | views
end

def distinct_relation_for_primary_key(relation) # :nodoc:

:nodoc:
def distinct_relation_for_primary_key(relation) # :nodoc:
  primary_key_columns = Array(relation.primary_key).map do |column|
    visitor.compile(relation.table[column])
  end
  values = columns_for_distinct(
    primary_key_columns,
    relation.order_values
  )
  limited = relation.reselect(values).distinct!
  limited_ids = select_rows(limited.arel, "SQL").map do |results|
    results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
  end
  if limited_ids.empty?
    relation.none!
  else
    relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
  end
  relation.limit_value = relation.offset_value = nil
  relation
end

def drop_join_table(table_1, table_2, **options)

In that case, the block will be used by #create_join_table.
to provide one in a migration's +change+ method so it can be reverted.
Although this command ignores the block if one is given, it can be helpful

See #create_join_table and #drop_table for details.
Drops the join table specified by the given arguments.
def drop_join_table(table_1, table_2, **options)
  join_table_name = find_join_table_name(table_1, table_2, options)
  drop_table(join_table_name, **options)
end

def drop_table(table_name, **options)

In that case, +options+ and the block will be used by #create_table.
it can be helpful to provide these in a migration's +change+ method so it can be reverted.
Although this command ignores most +options+ and the block if one is given,

Defaults to false.
Set to +true+ to only drop the table if it exists.
[:if_exists]
Defaults to false.
Set to +:cascade+ to drop dependent objects as well.
[:force]

Drops a table from the database.
def drop_table(table_name, **options)
  schema_cache.clear_data_source_cache!(table_name.to_s)
  execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
end

def dump_schema_information # :nodoc:

:nodoc:
def dump_schema_information # :nodoc:
  versions = schema_migration.versions
  insert_versions_sql(versions) if versions.any?
end

def expression_column_name?(column_name)

Try to identify whether the given column name is an expression
def expression_column_name?(column_name)
  column_name.is_a?(String) && /\W/.match?(column_name)
end

def extract_foreign_key_action(specifier)

def extract_foreign_key_action(specifier)
  case specifier
  when "CASCADE"; :cascade
  when "SET NULL"; :nullify
  when "RESTRICT"; :restrict
  end
end

def extract_new_default_value(default_or_changes)

def extract_new_default_value(default_or_changes)
  if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
    default_or_changes[:to]
  else
    default_or_changes
  end
end

def fetch_type_metadata(sql_type)

def fetch_type_metadata(sql_type)
  cast_type = lookup_cast_type(sql_type)
  SqlTypeMetadata.new(
    sql_type: sql_type,
    type: cast_type.type,
    limit: cast_type.limit,
    precision: cast_type.precision,
    scale: cast_type.scale,
  )
end

def foreign_key_column_for(table_name, column_name) # :nodoc:

:nodoc:
def foreign_key_column_for(table_name, column_name) # :nodoc:
  name = strip_table_name_prefix_and_suffix(table_name)
  "#{name.singularize}_#{column_name}"
end

def foreign_key_exists?(from_table, to_table = nil, **options)


foreign_key_exists?(:accounts, name: "special_fk_name")
# Checks to see if a foreign key with a custom name exists.

foreign_key_exists?(:accounts, column: :owner_id)
# Checks to see if a foreign key on a specified column exists.

foreign_key_exists?(:accounts, :branches)
# Checks to see if a foreign key exists.

Checks to see if a foreign key exists on a table for a given foreign key definition.
def foreign_key_exists?(from_table, to_table = nil, **options)
  foreign_key_for(from_table, to_table: to_table, **options).present?
end

def foreign_key_for(from_table, **options)

def foreign_key_for(from_table, **options)
  return unless use_foreign_keys?
  foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
end

def foreign_key_for!(from_table, to_table: nil, **options)

def foreign_key_for!(from_table, to_table: nil, **options)
  foreign_key_for(from_table, to_table: to_table, **options) ||
    raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
end

def foreign_key_name(table_name, options)

def foreign_key_name(table_name, options)
  options.fetch(:name) do
    columns = Array(options.fetch(:column)).map(&:to_s)
    identifier = "#{table_name}_#{columns * '_and_'}_fk"
    hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
    "fk_rails_#{hashed_identifier}"
  end
end

def foreign_key_options(from_table, to_table, options) # :nodoc:

:nodoc:
def foreign_key_options(from_table, to_table, options) # :nodoc:
  options = options.dup
  if options[:primary_key].is_a?(Array)
    options[:column] ||= options[:primary_key].map do |pk_column|
      foreign_key_column_for(to_table, pk_column)
    end
  else
    options[:column] ||= foreign_key_column_for(to_table, "id")
  end
  options[:name]   ||= foreign_key_name(from_table, options)
  if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
    if Array(options[:primary_key]).size != Array(options[:column]).size
      raise ArgumentError, <<~MSG.squish
        For composite primary keys, specify :column and :primary_key, where
        :column must reference all the :primary_key columns from #{to_table.inspect}
      MSG
    end
  end
  options
end

def foreign_keys(table_name)

The foreign keys are represented as ForeignKeyDefinition objects.
Returns an array of foreign keys for the given table.
def foreign_keys(table_name)
  raise NotImplementedError, "foreign_keys is not implemented"
end

def foreign_keys_enabled?

def foreign_keys_enabled?
  @config.fetch(:foreign_keys, true)
end

def generate_index_name(table_name, column)

def generate_index_name(table_name, column)
  name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
  return name if name.bytesize <= max_index_name_size
  # Fallback to short version, add hash to ensure uniqueness
  hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
  name = "idx_on_#{Array(column) * '_'}"
  short_limit = max_index_name_size - hashed_identifier.bytesize
  short_name = name.mb_chars.limit(short_limit).to_s
  "#{short_name}#{hashed_identifier}"
end

def index_algorithm(algorithm) # :nodoc:

:nodoc:
def index_algorithm(algorithm) # :nodoc:
  index_algorithms.fetch(algorithm) do
    raise ArgumentError, "Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}"
  end if algorithm
end

def index_column_names(column_names)

def index_column_names(column_names)
  if expression_column_name?(column_names)
    column_names
  else
    Array(column_names)
  end
end

def index_exists?(table_name, column_name, **options)


index_exists?(:suppliers, :company_id, valid: true)
# Check a valid index exists (PostgreSQL only)

index_exists?(:suppliers, :company_id, name: "idx_company_id")
# Check an index with a custom name exists

index_exists?(:suppliers, :company_id, unique: true)
# Check a unique index exists

index_exists?(:suppliers, [:company_id, :company_type])
# Check an index on multiple columns exists

index_exists?(:suppliers, :company_id)
# Check an index exists

Checks to see if an index exists on a table for a given index definition.
def index_exists?(table_name, column_name, **options)
  indexes(table_name).any? { |i| i.defined_for?(column_name, **options) }
end

def index_name(table_name, options) # :nodoc:

:nodoc:
def index_name(table_name, options) # :nodoc:
  if Hash === options
    if options[:column]
      generate_index_name(table_name, options[:column])
    elsif options[:name]
      options[:name]
    else
      raise ArgumentError, "You must specify the index name"
    end
  else
    index_name(table_name, index_name_options(options))
  end
end

def index_name_exists?(table_name, index_name)

Verifies the existence of an index with a given name.
def index_name_exists?(table_name, index_name)
  index_name = index_name.to_s
  indexes(table_name).detect { |i| i.name == index_name }
end

def index_name_for_remove(table_name, column_name, options)

def index_name_for_remove(table_name, column_name, options)
  return options[:name] if can_remove_index_by_name?(column_name, options)
  checks = []
  if !options.key?(:name) && expression_column_name?(column_name)
    options[:name] = index_name(table_name, column_name)
    column_names = []
  else
    column_names = index_column_names(column_name || options[:column])
  end
  checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
  if column_names.present? && !(options.key?(:name) && expression_column_name?(column_names))
    checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
  end
  raise ArgumentError, "No name or columns specified" if checks.none?
  matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
  if matching_indexes.count > 1
    raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
      "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
  elsif matching_indexes.none?
    raise ArgumentError, "No indexes found on #{table_name} with the options provided."
  else
    matching_indexes.first.name
  end
end

def index_name_options(column_names)

def index_name_options(column_names)
  if expression_column_name?(column_names)
    column_names = column_names.scan(/\w+/).join("_")
  end
  { column: column_names }
end

def indexes(table_name)

Returns an array of indexes for the given table.
def indexes(table_name)
  raise NotImplementedError, "#indexes is not implemented"
end

def insert_versions_sql(versions)

def insert_versions_sql(versions)
  sm_table = quote_table_name(schema_migration.table_name)
  if versions.is_a?(Array)
    sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
    sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
    sql << ";"
    sql
  else
    "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
  end
end

def internal_string_options_for_primary_key # :nodoc:

:nodoc:
def internal_string_options_for_primary_key # :nodoc:
  { primary_key: true }
end

def max_index_name_size

Returns the maximum length of an index name in bytes.
def max_index_name_size
  62
end

def native_database_types

abstract data types.
database types. See TableDefinition#column for details on the recognized
Returns a hash of mappings from the abstract data types to the native
def native_database_types
  {}
end

def options_for_index_columns(options)

def options_for_index_columns(options)
  if options.is_a?(Hash)
    options.symbolize_keys
  else
    Hash.new { |hash, column| hash[column] = options }
  end
end

def options_include_default?(options)

def options_include_default?(options)
  options.include?(:default) && !(options[:null] == false && options[:default].nil?)
end

def primary_key(table_name)

Returns just a table's primary key
def primary_key(table_name)
  pk = primary_keys(table_name)
  pk = pk.first unless pk.size > 1
  pk
end

def quoted_columns_for_index(column_names, options) # :nodoc:

:nodoc:
def quoted_columns_for_index(column_names, options) # :nodoc:
  quoted_columns = column_names.each_with_object({}) do |name, result|
    result[name.to_sym] = quote_column_name(name).dup
  end
  add_options_for_index_columns(quoted_columns, **options).values.join(", ")
end

def quoted_scope(name = nil, type: nil)

def quoted_scope(name = nil, type: nil)
  raise NotImplementedError
end

def reference_name_for_table(table_name)

def reference_name_for_table(table_name)
  table_name.to_s.singularize
end

def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)

In that case, +expression+ will be used by #add_check_constraint.
to provide this in a migration's +change+ method so it can be reverted.
The +expression+ parameter will be ignored if present. It can be helpful

remove_check_constraint :products, name: "price_check", if_exists: true

use the +if_exists+ option.
To silently ignore a non-existent check constraint rather than raise an error,

remove_check_constraint :products, name: "price_check"

that does not exist will raise an error.
Removes the given check constraint from the table. Removing a check constraint
def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
  return unless supports_check_constraints?
  return if if_exists && !check_constraint_exists?(table_name, **options)
  chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
  at = create_alter_table(table_name)
  at.drop_check_constraint(chk_name_to_delete)
  execute schema_creation.accept(at)
end

def remove_column(table_name, column_name, type = nil, **options)

remove_column(:suppliers, :qualification, if_exists: true)

if the column was already used.
column does not exist. This will silently ignore the migration rather than raising
If the options provided include an +if_exists+ key, it will be used to check if the

automatically removed or modified to remove this column from the index.
Depending on the database you're using, indexes using this column may be
In that case, +type+ and +options+ will be used by #add_column.
to provide these in a migration's +change+ method so it can be reverted.
The +type+ and +options+ parameters will be ignored if present. It can be helpful

remove_column(:suppliers, :qualification)

Removes the column from the table definition.
def remove_column(table_name, column_name, type = nil, **options)
  return if options[:if_exists] == true && !column_exists?(table_name, column_name)
  execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
end

def remove_column_for_alter(table_name, column_name, type = nil, **options)

def remove_column_for_alter(table_name, column_name, type = nil, **options)
  "DROP COLUMN #{quote_column_name(column_name)}"
end

def remove_columns(table_name, *column_names, type: nil, **options)

remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)

+type+ and other column options can be passed to make migration reversible.

remove_columns(:suppliers, :qualification, :experience)

Removes the given columns from the table definition.
def remove_columns(table_name, *column_names, type: nil, **options)
  if column_names.empty?
    raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
  end
  remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
  execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(', ')}"
end

def remove_columns_for_alter(table_name, *column_names, **options)

def remove_columns_for_alter(table_name, *column_names, **options)
  column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
end

def remove_foreign_key(from_table, to_table = nil, **options)

The name of the table that contains the referenced primary key.
[:to_table]
with an addition of
The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key

remove_foreign_key :accounts, :branches, if_exists: true

don't exist.
Checks if the foreign key exists before trying to remove it. Will silently ignore indexes that

remove_foreign_key :accounts, name: :special_fk_name

Removes the foreign key named +special_fk_name+ on the +accounts+ table.

remove_foreign_key :accounts, to_table: :owners

Removes the foreign key on +accounts.owner_id+.

remove_foreign_key :accounts, column: :owner_id

Removes the foreign key on +accounts.owner_id+.

remove_foreign_key :accounts, :branches

Removes the foreign key on +accounts.branch_id+.

key so that the migration can be reverted properly.
It is recommended that you provide any options used when creating the foreign
will be used to re-add the foreign key in case of a migration rollback.
Removes the given foreign key from the table. Any option parameters provided
def remove_foreign_key(from_table, to_table = nil, **options)
  return unless use_foreign_keys?
  return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
  fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
  at = create_alter_table from_table
  at.drop_foreign_key fk_name_to_delete
  execute schema_creation.accept(at)
end

def remove_index(table_name, column_name = nil, **options)

For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].

Concurrently removing an index is not supported in a transaction.

Note: only supported by PostgreSQL.

remove_index :accounts, name: :by_branch_party, algorithm: :concurrently

Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.

remove_index :accounts, if_exists: true

don't exist.
Checks if the index exists before trying to remove it. Will silently ignore indexes that

remove_index :accounts, :branch_id, name: :by_branch_party

Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.

remove_index :accounts, name: :by_branch_party

Removes the index named +by_branch_party+ in the +accounts+ table.

remove_index :accounts, column: [:branch_id, :party_id]

Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.

remove_index :accounts, column: :branch_id

Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.

remove_index :accounts, :branch_id

Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.

Removes the given index from the table.
def remove_index(table_name, column_name = nil, **options)
  return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
  index_name = index_name_for_remove(table_name, column_name, options)
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
end

def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)


remove_reference(:products, :user, foreign_key: true)

====== Remove the reference with a foreign key

remove_reference(:products, :supplier, polymorphic: true)

====== Remove polymorphic reference

remove_reference(:products, :user, index: false)

====== Remove the reference

Removes the reference(s). Also removes a +type+ column if one exists.
def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
  conditional_options = options.slice(:if_exists, :if_not_exists)
  if foreign_key
    reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
    if foreign_key.is_a?(Hash)
      foreign_key_options = foreign_key.merge(conditional_options)
    else
      foreign_key_options = { to_table: reference_name, **conditional_options }
    end
    foreign_key_options[:column] ||= "#{ref_name}_id"
    remove_foreign_key(table_name, **foreign_key_options)
  end
  remove_column(table_name, "#{ref_name}_id", **conditional_options)
  remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
end

def remove_timestamps(table_name, **options)


remove_timestamps(:suppliers)

Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
def remove_timestamps(table_name, **options)
  remove_columns table_name, :updated_at, :created_at
end

def remove_timestamps_for_alter(table_name, **options)

def remove_timestamps_for_alter(table_name, **options)
  remove_columns_for_alter(table_name, :updated_at, :created_at)
end

def rename_column(table_name, column_name, new_column_name)


rename_column(:suppliers, :description, :name)

Renames a column.
def rename_column(table_name, column_name, new_column_name)
  raise NotImplementedError, "rename_column is not implemented"
end

def rename_column_indexes(table_name, column_name, new_column_name)

def rename_column_indexes(table_name, column_name, new_column_name)
  column_name, new_column_name = column_name.to_s, new_column_name.to_s
  indexes(table_name).each do |index|
    next unless index.columns.include?(new_column_name)
    old_columns = index.columns.dup
    old_columns[old_columns.index(new_column_name)] = column_name
    generated_index_name = index_name(table_name, column: old_columns)
    if generated_index_name == index.name
      rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
    end
  end
end

def rename_column_sql(table_name, column_name, new_column_name)

def rename_column_sql(table_name, column_name, new_column_name)
  "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
end

def rename_index(table_name, old_name, new_name)


rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'

Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:

Renames an index.
def rename_index(table_name, old_name, new_name)
  old_name = old_name.to_s
  new_name = new_name.to_s
  validate_index_length!(table_name, new_name)
  # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
  old_index_def = indexes(table_name).detect { |i| i.name == old_name }
  return unless old_index_def
  add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
  remove_index(table_name, name: old_name)
end

def rename_table(table_name, new_name, **)


rename_table('octopuses', 'octopi')

Renames a table.
def rename_table(table_name, new_name, **)
  raise NotImplementedError, "rename_table is not implemented"
end

def rename_table_indexes(table_name, new_name)

def rename_table_indexes(table_name, new_name)
  indexes(new_name).each do |index|
    generated_index_name = index_name(table_name, column: index.columns)
    if generated_index_name == index.name
      rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
    end
  end
end

def schema_creation # :nodoc:

:nodoc:
object and return DDL.
Returns an instance of SchemaCreation, which can be used to visit a schema definition
def schema_creation # :nodoc:
  SchemaCreation.new(self)
end

def strip_table_name_prefix_and_suffix(table_name)

def strip_table_name_prefix_and_suffix(table_name)
  prefix = Base.table_name_prefix
  suffix = Base.table_name_suffix
  table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
end

def table_alias_for(table_name)

Truncates a table alias according to the limits of the current adapter.
def table_alias_for(table_name)
  table_name[0...table_alias_length].tr(".", "_")
end

def table_comment(table_name)

Returns the table comment that's stored in database metadata.
def table_comment(table_name)
  nil
end

def table_exists?(table_name)


table_exists?(:developers)

Checks to see if the table +table_name+ exists on the database.
def table_exists?(table_name)
  query_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
rescue NotImplementedError
  tables.include?(table_name.to_s)
end

def table_options(table_name)

def table_options(table_name)
  nil
end

def tables

Returns an array of table names defined in the database.
def tables
  query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
end

def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:

:nodoc:
def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
  type = type.to_sym if type
  if native = native_database_types[type]
    column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
    if type == :decimal # ignore limit, use precision and scale
      scale ||= native[:scale]
      if precision ||= native[:precision]
        if scale
          column_type_sql << "(#{precision},#{scale})"
        else
          column_type_sql << "(#{precision})"
        end
      elsif scale
        raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
      end
    elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
      if (0..6) === precision
        column_type_sql << "(#{precision})"
      else
        raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
      end
    elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
      column_type_sql << "(#{limit})"
    end
    column_type_sql
  else
    type.to_s
  end
end

def update_table_definition(table_name, base) # :nodoc:

:nodoc:
def update_table_definition(table_name, base) # :nodoc:
  Table.new(table_name, base)
end

def use_foreign_keys?

def use_foreign_keys?
  supports_foreign_keys? && foreign_keys_enabled?
end

def valid_column_definition_options # :nodoc:

:nodoc:
def valid_column_definition_options # :nodoc:
  ColumnDefinition::OPTION_NAMES
end

def valid_primary_key_options # :nodoc:

:nodoc:
def valid_primary_key_options # :nodoc:
  [:limit, :default, :precision]
end

def valid_table_definition_options # :nodoc:

:nodoc:
def valid_table_definition_options # :nodoc:
  [:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
end

def validate_change_column_null_argument!(value)

def validate_change_column_null_argument!(value)
  unless value == true || value == false
    raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
  end
end

def validate_create_table_options!(options)

def validate_create_table_options!(options)
  unless options[:_skip_validate_options]
    options
      .except(:_uses_legacy_table_name, :_skip_validate_options)
      .assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
  end
end

def validate_index_length!(table_name, new_name, internal = false)

def validate_index_length!(table_name, new_name, internal = false)
  if new_name.length > index_name_length
    raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
  end
end

def validate_table_length!(table_name)

def validate_table_length!(table_name)
  if table_name.length > table_name_length
    raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
  end
end

def view_exists?(view_name)


view_exists?(:ebooks)

Checks to see if the view +view_name+ exists on the database.
def view_exists?(view_name)
  query_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
rescue NotImplementedError
  views.include?(view_name.to_s)
end

def views

Returns an array of view names defined in the database.
def views
  query_values(data_source_sql(type: "VIEW"), "SCHEMA")
end