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:
parent
068580d445
commit
75a2e4a8df
8 changed files with 83 additions and 17 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)}"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue