Permalink
Browse files

create indexes inline in CREATE TABLE for MySQL

This is important, because adding an index on a temporary table after
it has been created would commit the transaction

Conflicts:
	activerecord/CHANGELOG.md
  • Loading branch information...
1 parent 470108b commit afa148a4f0ac5e2a446b5fe87881a130e8a24f3d @ccutrer ccutrer committed with steverice Jan 21, 2014
View
21 activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -186,24 +186,23 @@ def column_exists?(table_name, column_name, type = nil, options = {})
def create_table(table_name, options = {})
td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
- if !options[:as]
- unless options[:id] == false
- pk = options.fetch(:primary_key) {
- Base.get_primary_key table_name.to_s.singularize
- }
-
- td.primary_key pk, options.fetch(:id, :primary_key), options
- end
+ unless options[:id] == false || options[:as]
+ pk = options.fetch(:primary_key) {
+ Base.get_primary_key table_name.to_s.singularize
+ }
- yield td if block_given?
+ td.primary_key pk, options.fetch(:id, :primary_key), options
end
+ yield td if block_given?
+
if options[:force] && table_exists?(table_name)
drop_table(table_name, options)
end
- execute schema_creation.accept td
- td.indexes.each_pair { |c,o| add_index table_name, c, o }
+ result = execute schema_creation.accept td
+ td.indexes.each_pair { |c,o| add_index table_name, c, o } unless supports_indexes_in_create?
+ result
end
# Creates a new join table with the name created using the lexical order of the first two
View
6 activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -217,6 +217,12 @@ def supports_extensions?
false
end
+ # Does this adapter support creating indexes in the same statement as
+ # creating the table? As of this writing, only mysql does.
+ def supports_indexes_in_create?
+ false
+ end
+
# This is meant to be implemented by the adapters that support extensions
def disable_extension(name)
end
View
20 activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -6,6 +6,17 @@ class AbstractMysqlAdapter < AbstractAdapter
include Savepoints
class SchemaCreation < AbstractAdapter::SchemaCreation
+ def visit_TableDefinition(o)
+ create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
+ create_sql << "#{quote_table_name(o.name)} "
+ statements = []
+ statements.concat(o.columns.map { |c| accept c })
+ statements.concat(o.indexes.map { |(column_name, options)| index_in_create(o.name, column_name, options) })
+ create_sql << "(#{statements.join(', ')}) "
+ create_sql << "#{o.options}"
+ create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
+ create_sql
+ end
def visit_AddColumn(o)
add_column_position!(super, column_options(o))
@@ -29,6 +40,11 @@ def add_column_position!(sql, options)
end
sql
end
+
+ def index_in_create(table_name, column_name, options)
+ index_name, index_type, index_columns, index_options, index_algorithm, index_using = @conn.send(:add_index_options, table_name, column_name, options)
+ "#{index_type} INDEX #{quote_column_name(index_name)} #{index_using} (#{index_columns})#{index_options} #{index_algorithm}".gsub(' ', ' ').strip
+ end
end
def schema_creation
@@ -225,6 +241,10 @@ def supports_transaction_isolation?
version[0] >= 5
end
+ def supports_indexes_in_create?
+ true
+ end
+
def native_database_types
NATIVE_DATABASE_TYPES
end
View
11 activerecord/test/cases/adapters/mysql/active_schema_test.rb
@@ -116,6 +116,17 @@ def test_remove_timestamps
end
end
+ def test_indexes_in_create
+ begin
+ ActiveRecord::Base.connection.stubs(:index_name_exists?).returns(false)
+ expected = "CREATE TEMPORARY TABLE `temp` (INDEX `index_temp_on_zip` (`zip`)) ENGINE=InnoDB AS SELECT id, name, zip FROM a_really_complicated_query"
+ actual = ActiveRecord::Base.connection.create_table(:temp, temporary: true, as: "SELECT id, name, zip FROM a_really_complicated_query") do |t|
+ t.index :zip
+ end
+ assert_equal expected, actual
+ end
+ end
+
private
def with_real_execute
ActiveRecord::Base.connection.singleton_class.class_eval do
View
11 activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -116,6 +116,17 @@ def test_remove_timestamps
end
end
+ def test_indexes_in_create
+ begin
+ ActiveRecord::Base.connection.stubs(:index_name_exists?).returns(false)
+ expected = "CREATE TEMPORARY TABLE `temp` (INDEX `index_temp_on_zip` (`zip`)) ENGINE=InnoDB AS SELECT id, name, zip FROM a_really_complicated_query"
+ actual = ActiveRecord::Base.connection.create_table(:temp, temporary: true, as: "SELECT id, name, zip FROM a_really_complicated_query") do |t|
+ t.index :zip
+ end
+ assert_equal expected, actual
+ end
+ end
+
private
def with_real_execute
ActiveRecord::Base.connection.singleton_class.class_eval do

0 comments on commit afa148a

Please sign in to comment.