module ActiveRecord::ConnectionAdapters::SchemaStatements
def add_column(table_name, column_name, type, options = {})
Adds a new column to the named table.
def add_column(table_name, column_name, type, options = {}) add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" add_column_options!(add_column_sql, options) execute(add_column_sql) end
def add_column_options!(sql, options) #:nodoc:
def add_column_options!(sql, options) #:nodoc: sql << " DEFAULT #{quote(options[:default], options[:column])}" if options_include_default?(options) # must explicitly check for :null to allow change_column to work on migrations if options[:null] == false sql << " NOT NULL" end end
def add_index(table_name, column_name, options = {})
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})
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 accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
generates
add_index(:accounts, [:branch_id, :party_id], :unique => true)
====== Creating a unique index
CREATE INDEX suppliers_name_index ON suppliers(name)
generates
add_index(:suppliers, :name)
====== Creating a simple index
===== Examples
name.
makes sense, because you will never have to create a singular index with this
index for the first column :first. Using just the first name for this index
[:first, :last], the DBMS creates an index for both columns as well as an
for the index. For example, when you specify an index on two columns
When creating an index on multiple columns, the first column is used as a name
unless you pass :name as an option.
The index will be named after the table and the first column name,
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 = {}) column_names = Array(column_name) index_name = index_name(table_name, :column => column_names) if Hash === options # legacy support, since this param was a string index_type = options[:unique] ? "UNIQUE" : "" index_name = options[:name].to_s if options[:name] else index_type = options end if index_name.length > index_name_length @logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.") return end if index_exists?(table_name, index_name, false) @logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.") return end quoted_column_names = quoted_columns_for_index(column_names, options).join(", ") execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})" end
def add_order_by_for_association_limiting!(sql, options)
ORDER BY clause for the passed order option.
def add_order_by_for_association_limiting!(sql, options) sql << " ORDER BY #{options[:order]}" end
def add_timestamps(table_name)
===== Examples
Adds timestamps (created_at and updated_at) columns to the named table.
def add_timestamps(table_name) add_column table_name, :created_at, :datetime add_column table_name, :updated_at, :datetime end
def assume_migrated_upto_version(version, migrations_path = ActiveRecord::Migrator.migrations_path)
def assume_migrated_upto_version(version, migrations_path = ActiveRecord::Migrator.migrations_path) version = version.to_i sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name) migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i) versions = Dir["#{migrations_path}/[0-9]*_*.rb"].map do |filename| filename.split('/').last.split('_').first.to_i end unless migrated.include?(version) execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')" end inserted = Set.new (versions - migrated).each do |v| if inserted.include?(v) raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict." elsif v < version execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" inserted << v end end end
def change_column(table_name, column_name, type, options = {})
change_column(:suppliers, :name, :string, :limit => 80)
===== Examples
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_default(table_name, column_name, default)
change_column_default(:suppliers, :qualification, 'new')
===== Examples
DatabaseStatements#execute the appropriate SQL statement yourself.
value to +NULL+, you are out of luck. You need to
Sets a new default value for a column. If you want to set the default
def change_column_default(table_name, column_name, default) raise NotImplementedError, "change_column_default is not implemented" end
def change_table(table_name)
See also Table for details on
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(integer) columns
end
t.belongs_to :company, :polymorphic => true
change_table(:suppliers) do |t|
====== Add a polymorphic foreign key column
Creates a company_id(integer) 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.column :name, :string, :limit => 60
change_table(:suppliers) do |t|
====== Add a column
===== Examples
end
# Other column alterations here
t.column :name, :string, :limit => 60
change_table(:suppliers) do |t|
# change_table() yields a Table instance
=== Example
A block for changing columns in +table+.
def change_table(table_name) yield Table.new(table_name, self) end
def columns(table_name, name = nil) end
Returns an array of Column objects for the table specified by +table_name+.
def columns(table_name, name = nil) end
def create_table(table_name, options = {})
)
supplier_id int
category_id int,
CREATE TABLE categories_suppliers (
generates:
end
t.column :supplier_id, :integer
t.column :category_id, :integer
create_table(:categories_suppliers, :id => false) do |t|
====== Do not add a primary key column
)
name varchar(80)
guid int(11) DEFAULT NULL 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=utf8
id int(11) DEFAULT NULL auto_increment PRIMARY KEY
CREATE TABLE suppliers (
generates:
create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
====== Add a backend specific option to the generated SQL (MySQL)
===== Examples
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]
Defaults to +id+.
The name of the primary key, if one is to be added automatically.
[:primary_key]
Join tables for +has_and_belongs_to_many+ should set :id => 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.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, options = {}) table_definition = TableDefinition.new(self) table_definition.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false yield table_definition if block_given? if options[:force] && table_exists?(table_name) drop_table(table_name, options) end create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE " create_sql << "#{quote_table_name(table_name)} (" create_sql << table_definition.to_sql create_sql << ") #{options[:options]}" execute create_sql end
def distinct(columns, order_by)
Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax.
SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
def distinct(columns, order_by) "DISTINCT #{columns}" end
def drop_table(table_name, options = {})
def drop_table(table_name, options = {}) execute "DROP TABLE #{quote_table_name(table_name)}" end
def dump_schema_information #:nodoc:
def dump_schema_information #:nodoc: sm_table = ActiveRecord::Migrator.schema_migrations_table_name migrated = select_values("SELECT version FROM #{sm_table}") migrated.map { |v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}');" }.join("\n\n") end
def index_exists?(table_name, index_name, default)
The default argument is returned if the underlying implementation does not define the indexes method,
Verify the existence of an index.
def index_exists?(table_name, index_name, default) return default unless respond_to?(:indexes) index_name = index_name.to_s indexes(table_name).detect { |i| i.name == index_name } end
def index_name(table_name, options) #:nodoc:
def index_name(table_name, options) #:nodoc: if Hash === options # legacy support if options[:column] "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}" elsif options[:name] options[:name] else raise ArgumentError, "You must specify the index name" end else index_name(table_name, :column => options) end end
def initialize_schema_migrations_table
Should not be called normally, but this operation is non-destructive.
def initialize_schema_migrations_table sm_table = ActiveRecord::Migrator.schema_migrations_table_name unless tables.detect { |t| t == sm_table } create_table(sm_table, :id => false) do |schema_migrations_table| schema_migrations_table.column :version, :string, :null => false end add_index sm_table, :version, :unique => true, :name => "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}" # Backwards-compatibility: if we find schema_info, assume we've # migrated up to that point: si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix if tables.detect { |t| t == si_table } old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i assume_migrated_upto_version(old_version) drop_table(si_table) end end end
def native_database_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_include_default?(options)
def options_include_default?(options) options.include?(:default) && !(options[:null] == false && options[:default].nil?) end
def quoted_columns_for_index(column_names, options = {})
def quoted_columns_for_index(column_names, options = {}) column_names.map {|name| quote_column_name(name) } end
def remove_column(table_name, *column_names)
remove_column(:suppliers, :qualification)
===== Examples
Removes the column(s) from the table definition.
def remove_column(table_name, *column_names) raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty? column_names.flatten.each do |column_name| execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}" end end
def remove_index(table_name, options = {})
Remove the index named by_branch_party in the accounts table.
remove_index :accounts, :column => [:branch_id, :party_id]
Remove the index named accounts_branch_id_party_id_index in the accounts table.
remove_index :accounts, :column => :branch_id
Remove the index named accounts_branch_id_index in the accounts table.
remove_index :suppliers, :name
Remove the suppliers_name_index in the suppliers table.
Remove the given index from the table.
def remove_index(table_name, options = {}) index_name = index_name(table_name, options) unless index_exists?(table_name, index_name, true) @logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.") return end remove_index!(table_name, index_name) end
def remove_index!(table_name, index_name) #:nodoc:
def remove_index!(table_name, index_name) #:nodoc: execute "DROP INDEX #{quote_column_name(index_name)} ON #{table_name}" end
def remove_timestamps(table_name)
===== Examples
Removes the timestamp columns (created_at and updated_at) from the table definition.
def remove_timestamps(table_name) remove_column table_name, :updated_at remove_column table_name, :created_at end
def rename_column(table_name, column_name, new_column_name)
===== Example
Renames a column.
def rename_column(table_name, column_name, new_column_name) raise NotImplementedError, "rename_column is not implemented" end
def rename_index(table_name, old_name, new_name)
Rename the index_people_on_last_name index to index_users_on_last_name
Rename an index.
def rename_index(table_name, old_name, new_name) # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance) old_index_def = indexes(table_name).detect { |i| i.name == old_name } return unless old_index_def remove_index(table_name, :name => old_name) add_index(table_name, old_index_def.columns, :name => new_name, :unique => old_index_def.unique) end
def rename_table(table_name, new_name)
===== Example
Renames a table.
def rename_table(table_name, new_name) raise NotImplementedError, "rename_table is not implemented" end
def structure_dump
Returns a string of CREATE TABLE SQL statement(s) for recreating the
def structure_dump end
def table_alias_for(table_name)
def table_alias_for(table_name) table_name[0..table_alias_length-1].gsub(/\./, '_') end
def table_exists?(table_name)
def table_exists?(table_name) tables.include?(table_name.to_s) end
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc: 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 if specified" end elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit]) column_type_sql << "(#{limit})" end column_type_sql else type end end