Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pixeltrix/453112 to your computer and use it in GitHub Desktop.
Save pixeltrix/453112 to your computer and use it in GitHub Desktop.
From 3111fc9b26e64370b2216e7690d6542de8206db7 Mon Sep 17 00:00:00 2001
From: Andrew White <andyw@pixeltrix.co.uk>
Date: Thu, 18 Mar 2010 16:49:23 +0000
Subject: [PATCH] Add column and index query methods to ActiveRecord::Schema
---
.../abstract/schema_definitions.rb | 10 ++
.../abstract/schema_statements.rb | 57 ++++++++++-
.../test/cases/active_schema_test_mysql.rb | 6 +-
activerecord/test/cases/migration_test.rb | 108 +++++++++++++++++++-
4 files changed, 170 insertions(+), 11 deletions(-)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 7d58bc2..7691b6a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -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
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 638e5d7..5625dba 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -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
diff --git a/activerecord/test/cases/active_schema_test_mysql.rb b/activerecord/test/cases/active_schema_test_mysql.rb
index d7431e5..6e66455 100644
--- a/activerecord/test/cases/active_schema_test_mysql.rb
+++ b/activerecord/test/cases/active_schema_test_mysql.rb
@@ -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
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 9ece2fb..99a3a12 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -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, {})
--
1.7.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment