Skip to content

Commit

Permalink
Add column and index query methods to ActiveRecord::Schema
Browse files Browse the repository at this point in the history
[#4219 state:resolved]

Signed-off-by: José Valim <jose.valim@gmail.com>
  • Loading branch information
pixeltrix authored and josevalim committed Jun 25, 2010
1 parent efbd0eb commit 11ff3da
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 11 deletions.
Expand Up @@ -582,6 +582,11 @@ def column(column_name, type, options = {})
@base.add_column(@table_name, column_name, type, options) @base.add_column(@table_name, column_name, type, options)
end 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 # Adds a new index to the table. +column_name+ can be a single Symbol, or
# an Array of Symbols. See SchemaStatements#add_index # an Array of Symbols. See SchemaStatements#add_index
# #
Expand All @@ -596,6 +601,11 @@ def index(column_name, options = {})
@base.add_index(@table_name, column_name, options) @base.add_index(@table_name, column_name, options)
end 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 # Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#add_timestamps
# ===== Example # ===== Example
# t.timestamps # t.timestamps
Expand Down
Expand Up @@ -24,10 +24,59 @@ def table_exists?(table_name)
# Returns an array of indexes for the given table. # Returns an array of indexes for the given table.
# def indexes(table_name, name = nil) end # 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+. # Returns an array of Column objects for the table specified by +table_name+.
# See the concrete implementation for details on the expected parameter values. # See the concrete implementation for details on the expected parameter values.
def columns(table_name, name = nil) end 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 # Creates a new table with the name +table_name+. +table_name+ may either
# be a String or a Symbol. # be a String or a Symbol.
# #
Expand Down Expand Up @@ -293,7 +342,7 @@ def add_index(table_name, column_name, options = {})
@logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.") @logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.")
return return
end 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.") @logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.")
return return
end end
Expand All @@ -314,7 +363,7 @@ def add_index(table_name, column_name, options = {})
# remove_index :accounts, :name => :by_branch_party # remove_index :accounts, :name => :by_branch_party
def remove_index(table_name, options = {}) def remove_index(table_name, options = {})
index_name = index_name(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.") @logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.")
return return
end end
Expand Down Expand Up @@ -351,11 +400,11 @@ def index_name(table_name, options) #:nodoc:
end end
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, # 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. # 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) return default unless respond_to?(:indexes)
indexes(table_name).detect { |i| i.name == index_name } indexes(table_name).detect { |i| i.name == index_name }
end end
Expand Down
6 changes: 3 additions & 3 deletions activerecord/test/cases/active_schema_test_mysql.rb
Expand Up @@ -17,8 +17,8 @@ def teardown
end end


def test_add_index def test_add_index
# add_index calls index_exists? which can't work since execute is stubbed # add_index calls index_name_exists? which can't work since execute is stubbed
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_exists?) do |*| ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_name_exists?) do |*|
false false
end end
expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)" expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)"
Expand All @@ -35,7 +35,7 @@ def test_add_index


expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))" 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}) 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 end


def test_drop_table def test_drop_table
Expand Down
108 changes: 104 additions & 4 deletions activerecord/test/cases/migration_test.rb
Expand Up @@ -128,9 +128,9 @@ def test_add_index_length_limit
good_index_name = 'x' * Person.connection.index_name_length good_index_name = 'x' * Person.connection.index_name_length
too_long_index_name = good_index_name + 'x' too_long_index_name = good_index_name + 'x'
assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => too_long_index_name) } 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_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 end


def test_remove_nonexistent_index def test_remove_nonexistent_index
Expand All @@ -146,8 +146,8 @@ def test_rename_index
Person.connection.add_index('people', [:first_name], :name => 'old_idx') Person.connection.add_index('people', [:first_name], :name => 'old_idx')
assert_nothing_raised { Person.connection.rename_index('people', 'old_idx', 'new_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 # 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_name_exists?('people', 'old_idx', false)
assert Person.connection.index_exists?('people', 'new_idx', true) assert Person.connection.index_name_exists?('people', 'new_idx', true)
end end
end end


Expand All @@ -158,6 +158,53 @@ def test_double_add_index
end end
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 def testing_table_with_only_foo_attribute
Person.connection.create_table :testings, :id => false do |t| Person.connection.create_table :testings, :id => false do |t|
t.column :foo, :string t.column :foo, :string
Expand Down Expand Up @@ -974,6 +1021,45 @@ def test_change_column_default_to_null
assert_nil Person.new.first_name assert_nil Person.new.first_name
end 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 def test_add_table
assert !Reminder.table_exists? assert !Reminder.table_exists?


Expand Down Expand Up @@ -1684,6 +1770,20 @@ def test_index_creates_index_with_options
end end
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 def test_change_changes_column
with_change_table do |t| with_change_table do |t|
@connection.expects(:change_column).with(:delete_me, :bar, :string, {}) @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
Expand Down

0 comments on commit 11ff3da

Please sign in to comment.