Skip to content

Commit 9600e0b

Browse files
Add support for FULLTEXT and SPATIAL indexes using the :type flag for MySQL.
1 parent 0739d14 commit 9600e0b

13 files changed

Lines changed: 108 additions & 37 deletions

File tree

activerecord/CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
*Martin Schuerrer*
1717

18-
* Add suport for concurrent indexing in PostgreSQL adapter via the
18+
* Add support for concurrent indexing in PostgreSQL adapter via the
1919
`algorithm: :concurrently` option
2020

2121
add_index(:people, :last_name, algorithm: :concurrently)
@@ -27,6 +27,14 @@
2727

2828
*Dan McClain*
2929

30+
* Add support for fulltext and spatial indexes on MySQL tables with MyISAM database
31+
engine via the `type: 'FULLTEXT'` / `type: 'SPATIAL'` option
32+
33+
add_index(:people, :last_name, type: 'FULLTEXT')
34+
add_index(:people, :last_name, type: 'SPATIAL')
35+
36+
*Ken Mazaika*
37+
3038
* Add an `add_index` override in Postgresql adapter and MySQL adapter
3139
to allow custom index type support. Fixes #6101.
3240

activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module ConnectionAdapters #:nodoc:
88
# Abstract representation of an index definition on a table. Instances of
99
# this type are typically created and returned by methods in database
1010
# adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
11-
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :using) #:nodoc:
11+
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using) #:nodoc:
1212
end
1313

1414
# Abstract representation of a column definition. Instances of this type

activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,13 +498,25 @@ def rename_column(table_name, column_name, new_column_name)
498498
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
499499
#
500500
# ====== Creating an index with a specific method
501+
#
501502
# add_index(:developers, :name, :using => 'btree')
503+
#
502504
# generates
505+
#
503506
# CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
504507
# CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
505508
#
506509
# Note: only supported by PostgreSQL and MySQL
507510
#
511+
# ====== Creating an index with a specific type
512+
#
513+
# add_index(:developers, :name, :type => :fulltext)
514+
#
515+
# generates
516+
#
517+
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
518+
#
519+
# Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
508520
def add_index(table_name, column_name, options = {})
509521
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
510522
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -755,9 +767,10 @@ def add_index_options(table_name, column_name, options = {})
755767
index_name = index_name(table_name, column: column_names)
756768

757769
if Hash === options # legacy support, since this param was a string
758-
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm)
770+
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
759771

760772
index_type = options[:unique] ? "UNIQUE" : ""
773+
index_type = options[:type].to_s if options.key?(:type)
761774
index_name = options[:name].to_s if options.key?(:name)
762775
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
763776

activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ def missing_default_forged_as_empty_string?(default)
151151
:boolean => { :name => "tinyint", :limit => 1 }
152152
}
153153

154+
INDEX_TYPES = [:fulltext, :spatial]
155+
INDEX_USINGS = [:btree, :hash]
156+
154157
class BindSubstitution < Arel::Visitors::MySQL # :nodoc:
155158
include Arel::Visitors::BindVisitor
156159
end
@@ -435,8 +438,11 @@ def indexes(table_name, name = nil) #:nodoc:
435438
if current_index != row[:Key_name]
436439
next if row[:Key_name] == 'PRIMARY' # skip the primary key
437440
current_index = row[:Key_name]
438-
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [])
439-
indexes.last.using = row[:Index_type].downcase.to_sym
441+
442+
mysql_index_type = row[:Index_type].downcase.to_sym
443+
index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil
444+
index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil
445+
indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using)
440446
end
441447

442448
indexes.last.columns << row[:Column_name]

activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ def indexes(table_name, name = nil)
160160
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
161161
orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
162162
where = inddef.scan(/WHERE (.+)$/).flatten[0]
163-
type = inddef.scan(/USING (.+?) /).flatten[0].to_sym
163+
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
164164

165-
column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, type)
165+
column_names.empty? ? nil : IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
166166
end.compact
167167
end
168168

activerecord/lib/active_record/schema_dumper.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ def indexes(table, stream)
187187

188188
statement_parts << ('using: ' + index.using.inspect) if index.using
189189

190+
statement_parts << ('type: ' + index.type.inspect) if index.type
191+
190192
' ' + statement_parts.join(', ')
191193
end
192194

activerecord/test/cases/adapters/mysql/active_schema_test.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,14 @@ def test_add_index
3636
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10)) "
3737
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10})
3838

39-
%w(btree hash).each do |type|
40-
expected = "CREATE INDEX `index_people_on_last_name` USING #{type} ON `people` (`last_name`) "
41-
assert_equal expected, add_index(:people, :last_name, :using => type)
39+
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
40+
expected = "CREATE #{type} INDEX `index_people_on_last_name` ON `people` (`last_name`) "
41+
assert_equal expected, add_index(:people, :last_name, :type => type)
42+
end
43+
44+
%w(btree hash).each do |using|
45+
expected = "CREATE INDEX `index_people_on_last_name` USING #{using} ON `people` (`last_name`) "
46+
assert_equal expected, add_index(:people, :last_name, :using => using)
4247
end
4348

4449
expected = "CREATE INDEX `index_people_on_last_name` USING btree ON `people` (`last_name`(10)) "

activerecord/test/cases/adapters/mysql/schema_test.rb

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,22 +37,25 @@ def test_table_exists_wrong_schema
3737
end
3838

3939
def test_dump_indexes
40-
index_a_name = 'index_post_title'
41-
index_b_name = 'index_post_body'
40+
index_a_name = 'index_key_tests_on_snack'
41+
index_b_name = 'index_key_tests_on_pizza'
42+
index_c_name = 'index_key_tests_on_awesome'
4243

43-
table = Post.table_name
44-
45-
@connection.execute "CREATE INDEX `#{index_a_name}` ON `#{table}` (`title`);"
46-
@connection.execute "CREATE INDEX `#{index_b_name}` USING btree ON `#{table}` (`body`(10));"
44+
table = 'key_tests'
4745

4846
indexes = @connection.indexes(table).sort_by {|i| i.name}
49-
assert_equal 2,indexes.size
50-
51-
assert_equal :btree, indexes.select{|i| i.name == index_a_name}[0].using
52-
assert_equal :btree, indexes.select{|i| i.name == index_b_name}[0].using
53-
54-
@connection.execute "DROP INDEX `#{index_a_name}` ON `#{table}`;"
55-
@connection.execute "DROP INDEX `#{index_b_name}` ON `#{table}`;"
47+
assert_equal 3,indexes.size
48+
49+
index_a = indexes.select{|i| i.name == index_a_name}[0]
50+
index_b = indexes.select{|i| i.name == index_b_name}[0]
51+
index_c = indexes.select{|i| i.name == index_c_name}[0]
52+
assert_equal :btree, index_a.using
53+
assert_nil index_a.type
54+
assert_equal :btree, index_b.using
55+
assert_nil index_b.type
56+
57+
assert_nil index_c.using
58+
assert_equal :fulltext, index_c.type
5659
end
5760
end
5861
end

activerecord/test/cases/adapters/mysql2/active_schema_test.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,14 @@ def test_add_index
3636
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10)) "
3737
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10})
3838

39-
%w(btree hash).each do |type|
40-
expected = "CREATE INDEX `index_people_on_last_name` USING #{type} ON `people` (`last_name`) "
41-
assert_equal expected, add_index(:people, :last_name, :using => type)
39+
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
40+
expected = "CREATE #{type} INDEX `index_people_on_last_name` ON `people` (`last_name`) "
41+
assert_equal expected, add_index(:people, :last_name, :type => type)
42+
end
43+
44+
%w(btree hash).each do |using|
45+
expected = "CREATE INDEX `index_people_on_last_name` USING #{using} ON `people` (`last_name`) "
46+
assert_equal expected, add_index(:people, :last_name, :using => using)
4247
end
4348

4449
expected = "CREATE INDEX `index_people_on_last_name` USING btree ON `people` (`last_name`(10)) "

activerecord/test/cases/adapters/mysql2/schema_test.rb

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,25 @@ def test_tables_quoting
4545
end
4646

4747
def test_dump_indexes
48-
index_a_name = 'index_post_title'
49-
index_b_name = 'index_post_body'
48+
index_a_name = 'index_key_tests_on_snack'
49+
index_b_name = 'index_key_tests_on_pizza'
50+
index_c_name = 'index_key_tests_on_awesome'
5051

51-
table = Post.table_name
52-
53-
@connection.execute "CREATE INDEX `#{index_a_name}` ON `#{table}` (`title`);"
54-
@connection.execute "CREATE INDEX `#{index_b_name}` USING btree ON `#{table}` (`body`(10));"
52+
table = 'key_tests'
5553

5654
indexes = @connection.indexes(table).sort_by {|i| i.name}
57-
assert_equal 2,indexes.size
55+
assert_equal 3,indexes.size
5856

59-
assert_equal :btree, indexes.select{|i| i.name == index_a_name}[0].using
60-
assert_equal :btree, indexes.select{|i| i.name == index_b_name}[0].using
57+
index_a = indexes.select{|i| i.name == index_a_name}[0]
58+
index_b = indexes.select{|i| i.name == index_b_name}[0]
59+
index_c = indexes.select{|i| i.name == index_c_name}[0]
60+
assert_equal :btree, index_a.using
61+
assert_nil index_a.type
62+
assert_equal :btree, index_b.using
63+
assert_nil index_b.type
6164

62-
@connection.execute "DROP INDEX `#{index_a_name}` ON `#{table}`;"
63-
@connection.execute "DROP INDEX `#{index_b_name}` ON `#{table}`;"
65+
assert_nil index_c.using
66+
assert_equal :fulltext, index_c.type
6467
end
6568
end
6669
end

0 commit comments

Comments
 (0)