Add column and index query methods to ActiveRecord::Schema

[#4219 state:resolved]

Signed-off-by: José Valim <jose.valim@gmail.com>
This commit is contained in:
Andrew White
2010-03-18 16:49:23 +00:00
committed by José Valim
parent efbd0eb9f7
commit 11ff3da5f4
4 changed files with 170 additions and 11 deletions

View File

@@ -582,6 +582,11 @@ module ActiveRecord
@base.add_column(@table_name, column_name, type, options)
end
# Checks to see if a column exists. See SchemaStatements#column_exists?
def column_exists?(column_name, type = nil, options = nil)
@base.column_exists?(@table_name, column_name, type, options)
end
# Adds a new index to the table. +column_name+ can be a single Symbol, or
# an Array of Symbols. See SchemaStatements#add_index
#
@@ -596,6 +601,11 @@ module ActiveRecord
@base.add_index(@table_name, column_name, options)
end
# Checks to see if an index exists. See SchemaStatements#index_exists?
def index_exists?(column_name, options = {})
@base.index_exists?(@table_name, column_name, options)
end
# Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#add_timestamps
# ===== Example
# t.timestamps

View File

@@ -24,10 +24,59 @@ module ActiveRecord
# Returns an array of indexes for the given table.
# def indexes(table_name, name = nil) end
# Checks to see if an index exists on a table for a given index definition
#
# === Examples
# # Check an index exists
# index_exists?(:suppliers, :company_id)
#
# # Check an index on multiple columns exists
# index_exists?(:suppliers, [:company_id, :company_type])
#
# # Check a unique index exists
# index_exists?(:suppliers, :company_id, :unique => true)
#
# # Check an index with a custom name exists
# index_exists?(:suppliers, :company_id, :name => "idx_company_id"
def index_exists?(table_name, column_name, options = {})
column_names = Array.wrap(column_name)
index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
if options[:unique]
indexes(table_name).any?{ |i| i.unique && i.name == index_name }
else
indexes(table_name).any?{ |i| i.name == index_name }
end
end
# Returns an array of Column objects for the table specified by +table_name+.
# See the concrete implementation for details on the expected parameter values.
def columns(table_name, name = nil) end
# Checks to see if a column exists in a given table.
#
# === Examples
# # Check a column exists
# column_exists?(:suppliers, :name)
#
# # Check a column exists of a particular type
# column_exists?(:suppliers, :name, :string)
#
# # Check a column exists with a specific definition
# column_exists?(:suppliers, :name, :string, :limit => 100)
def column_exists?(table_name, column_name, type = nil, options = nil)
column_name = column_name.to_s
if type
if options
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
columns(table_name).any?{ |c| c.name == column_name && c.sql_type == sql_type }
else
columns(table_name).any?{ |c| c.name == column_name && c.type == type }
end
else
columns(table_name).any?{ |c| c.name == column_name }
end
end
# Creates a new table with the name +table_name+. +table_name+ may either
# be a String or a Symbol.
#
@@ -293,7 +342,7 @@ module ActiveRecord
@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)
if index_name_exists?(table_name, index_name, false)
@logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.")
return
end
@@ -314,7 +363,7 @@ module ActiveRecord
# remove_index :accounts, :name => :by_branch_party
def remove_index(table_name, options = {})
index_name = index_name(table_name, options)
unless index_exists?(table_name, index_name, true)
unless index_name_exists?(table_name, index_name, true)
@logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.")
return
end
@@ -351,11 +400,11 @@ module ActiveRecord
end
end
# Verify the existence of an index.
# Verify the existence of an index with a given name.
#
# The default argument is returned if the underlying implementation does not define the indexes method,
# as there's no way to determine the correct answer in that case.
def index_exists?(table_name, index_name, default)
def index_name_exists?(table_name, index_name, default)
return default unless respond_to?(:indexes)
indexes(table_name).detect { |i| i.name == index_name }
end

View File

@@ -17,8 +17,8 @@ class ActiveSchemaTest < ActiveRecord::TestCase
end
def test_add_index
# add_index calls index_exists? which can't work since execute is stubbed
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_exists?) do |*|
# add_index calls index_name_exists? which can't work since execute is stubbed
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_name_exists?) do |*|
false
end
expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)"
@@ -35,7 +35,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))"
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10})
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:remove_method, :index_exists?)
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:remove_method, :index_name_exists?)
end
def test_drop_table

View File

@@ -128,9 +128,9 @@ if ActiveRecord::Base.connection.supports_migrations?
good_index_name = 'x' * Person.connection.index_name_length
too_long_index_name = good_index_name + 'x'
assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => too_long_index_name) }
assert !Person.connection.index_exists?("people", too_long_index_name, false)
assert !Person.connection.index_name_exists?("people", too_long_index_name, false)
assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => good_index_name) }
assert Person.connection.index_exists?("people", good_index_name, false)
assert Person.connection.index_name_exists?("people", good_index_name, false)
end
def test_remove_nonexistent_index
@@ -146,8 +146,8 @@ if ActiveRecord::Base.connection.supports_migrations?
Person.connection.add_index('people', [:first_name], :name => 'old_idx')
assert_nothing_raised { Person.connection.rename_index('people', 'old_idx', 'new_idx') }
# if the adapter doesn't support the indexes call, pick defaults that let the test pass
assert !Person.connection.index_exists?('people', 'old_idx', false)
assert Person.connection.index_exists?('people', 'new_idx', true)
assert !Person.connection.index_name_exists?('people', 'old_idx', false)
assert Person.connection.index_name_exists?('people', 'new_idx', true)
end
end
@@ -158,6 +158,53 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
def test_index_exists
Person.connection.create_table :testings do |t|
t.column :foo, :string, :limit => 100
t.column :bar, :string, :limit => 100
end
Person.connection.add_index :testings, :foo
assert Person.connection.index_exists?(:testings, :foo)
assert !Person.connection.index_exists?(:testings, :bar)
ensure
Person.connection.drop_table :testings rescue nil
end
def test_index_exists_on_multiple_columns
Person.connection.create_table :testings do |t|
t.column :foo, :string, :limit => 100
t.column :bar, :string, :limit => 100
end
Person.connection.add_index :testings, [:foo, :bar]
assert Person.connection.index_exists?(:testings, [:foo, :bar])
ensure
Person.connection.drop_table :testings rescue nil
end
def test_unique_index_exists
Person.connection.create_table :testings do |t|
t.column :foo, :string, :limit => 100
end
Person.connection.add_index :testings, :foo, :unique => true
assert Person.connection.index_exists?(:testings, :foo, :unique => true)
ensure
Person.connection.drop_table :testings rescue nil
end
def test_named_index_exists
Person.connection.create_table :testings do |t|
t.column :foo, :string, :limit => 100
end
Person.connection.add_index :testings, :foo, :name => "custom_index_name"
assert Person.connection.index_exists?(:testings, :foo, :name => "custom_index_name")
ensure
Person.connection.drop_table :testings rescue nil
end
def testing_table_with_only_foo_attribute
Person.connection.create_table :testings, :id => false do |t|
t.column :foo, :string
@@ -974,6 +1021,45 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_nil Person.new.first_name
end
def test_column_exists
Person.connection.create_table :testings do |t|
t.column :foo, :string
end
assert Person.connection.column_exists?(:testings, :foo)
assert !Person.connection.column_exists?(:testings, :bar)
ensure
Person.connection.drop_table :testings rescue nil
end
def test_column_exists_with_type
Person.connection.create_table :testings do |t|
t.column :foo, :string
t.column :bar, :decimal, :precision => 8, :scale => 2
end
assert Person.connection.column_exists?(:testings, :foo, :string)
assert !Person.connection.column_exists?(:testings, :foo, :integer)
assert Person.connection.column_exists?(:testings, :bar, :decimal)
assert !Person.connection.column_exists?(:testings, :bar, :integer)
ensure
Person.connection.drop_table :testings rescue nil
end
def test_column_exists_with_definition
Person.connection.create_table :testings do |t|
t.column :foo, :string, :limit => 100
t.column :bar, :decimal, :precision => 8, :scale => 2
end
assert Person.connection.column_exists?(:testings, :foo, :string, :limit => 100)
assert !Person.connection.column_exists?(:testings, :foo, :string, :limit => 50)
assert Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 8, :scale => 2)
assert !Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 10, :scale => 2)
ensure
Person.connection.drop_table :testings rescue nil
end
def test_add_table
assert !Reminder.table_exists?
@@ -1684,6 +1770,20 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
def test_index_exists
with_change_table do |t|
@connection.expects(:index_exists?).with(:delete_me, :bar, {})
t.index_exists?(:bar)
end
end
def test_index_exists_with_options
with_change_table do |t|
@connection.expects(:index_exists?).with(:delete_me, :bar, {:unique => true})
t.index_exists?(:bar, :unique => true)
end
end
def test_change_changes_column
with_change_table do |t|
@connection.expects(:change_column).with(:delete_me, :bar, :string, {})