1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

support creating temporary tables from queries

also override drop_table in AbstractMySQLAdapter to properly drop
temporary tables without committing the transaction
This commit is contained in:
Cody Cutrer 2013-12-13 15:20:03 -07:00 committed by Cody Cutrer
parent 068580d445
commit 75a2e4a8df
8 changed files with 83 additions and 17 deletions

View file

@ -1,3 +1,13 @@
* Respect temporary option when dropping tables with MySQL
Normal DROP TABLE also works, but commits the transaction.
*Cody Cutrer*
* Add option to create tables from a query.
*Cody Cutrer*
* Create a whitelist of delegable methods to `Array`.
Currently `Relation` directly delegates methods to `Array`. With this change,

View file

@ -34,9 +34,10 @@ module ActiveRecord
def visit_TableDefinition(o)
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
create_sql << "#{quote_table_name(o.name)} ("
create_sql << o.columns.map { |c| accept c }.join(', ')
create_sql << ") #{o.options}"
create_sql << "#{quote_table_name(o.name)} "
create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " unless o.as
create_sql << "#{o.options}"
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
create_sql
end

View file

@ -49,14 +49,15 @@ module ActiveRecord
# An array of ColumnDefinition objects, representing the column changes
# that have been defined.
attr_accessor :indexes
attr_reader :name, :temporary, :options
attr_reader :name, :temporary, :options, :as
def initialize(types, name, temporary, options)
def initialize(types, name, temporary, options, as = nil)
@columns_hash = {}
@indexes = {}
@native = types
@temporary = temporary
@options = options
@as = as
@name = name
end

View file

@ -131,6 +131,9 @@ module ActiveRecord
# [<tt>:force</tt>]
# Set to true to drop the table before creating it.
# Defaults to false.
# [<tt>:as]
# SQL to use to generate the table. When this option is used, the block is
# ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
#
# ====== Add a backend specific option to the generated SQL (MySQL)
#
@ -169,20 +172,32 @@ module ActiveRecord
# supplier_id int
# )
#
# ====== Create a temporary table based on a query
#
# create_table(:long_query, temporary: true,
# as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
#
# generates:
#
# CREATE TEMPORARY TABLE long_query AS
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
#
# See also TableDefinition#column for details on how to create columns.
def create_table(table_name, options = {})
td = create_table_definition table_name, options[:temporary], options[:options]
td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
unless options[:id] == false
pk = options.fetch(:primary_key) {
Base.get_primary_key table_name.to_s.singularize
}
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
td.primary_key pk, options.fetch(:id, :primary_key), options
end
yield td if block_given?
end
yield td if block_given?
if options[:force] && table_exists?(table_name)
drop_table(table_name, options)
end
@ -826,8 +841,8 @@ module ActiveRecord
end
private
def create_table_definition(name, temporary, options)
TableDefinition.new native_database_types, name, temporary, options
def create_table_definition(name, temporary, options, as = nil)
TableDefinition.new native_database_types, name, temporary, options, as
end
def create_alter_table(name)

View file

@ -492,6 +492,10 @@ module ActiveRecord
rename_table_indexes(table_name, new_name)
end
def drop_table(table_name, options = {})
execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}"
end
def rename_index(table_name, old_name, new_name)
if (version[0] == 5 && version[1] >= 7) || version[0] >= 6
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}"

View file

@ -973,8 +973,8 @@ module ActiveRecord
$1.strip if $1
end
def create_table_definition(name, temporary, options)
TableDefinition.new native_database_types, name, temporary, options
def create_table_definition(name, temporary, options, as = nil)
TableDefinition.new native_database_types, name, temporary, options, as
end
def update_table_definition(table_name, base)

View file

@ -65,6 +65,15 @@ module ActiveRecord
assert_nil index_c.using
assert_equal :fulltext, index_c.type
end
def test_drop_temporary_table
@connection.transaction do
@connection.create_table(:temp_table, temporary: true)
# if it doesn't properly say DROP TEMPORARY TABLE, the transaction commit
# will complain that no transaction is active
@connection.drop_table(:temp_table, temporary: true)
end
end
end
end
end

View file

@ -443,6 +443,32 @@ class MigrationTest < ActiveRecord::TestCase
Person.connection.drop_table :binary_testings rescue nil
end
def test_create_table_with_query
Person.connection.drop_table :table_from_query_testings rescue nil
Person.connection.create_table(:person, force: true)
Person.connection.create_table :table_from_query_testings, as: "SELECT id FROM person"
columns = Person.connection.columns(:table_from_query_testings)
assert_equal 1, columns.length
assert_equal "id", columns.first.name
Person.connection.drop_table :table_from_query_testings rescue nil
end
def test_create_table_with_query_from_relation
Person.connection.drop_table :table_from_query_testings rescue nil
Person.connection.create_table(:person, force: true)
Person.connection.create_table :table_from_query_testings, as: Person.select(:id)
columns = Person.connection.columns(:table_from_query_testings)
assert_equal 1, columns.length
assert_equal "id", columns.first.name
Person.connection.drop_table :table_from_query_testings rescue nil
end
if current_adapter? :OracleAdapter
def test_create_table_with_custom_sequence_name
# table name is 29 chars, the standard sequence name will