activerecord 3.2.22.4 → 4.0.13
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2799 -617
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -32
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations/alias_tracker.rb +4 -2
- data/lib/active_record/associations/association.rb +60 -46
- data/lib/active_record/associations/association_scope.rb +46 -40
- data/lib/active_record/associations/belongs_to_association.rb +17 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +73 -56
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +130 -96
- data/lib/active_record/associations/collection_proxy.rb +916 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
- data/lib/active_record/associations/has_many_association.rb +35 -8
- data/lib/active_record/associations/has_many_through_association.rb +37 -17
- data/lib/active_record/associations/has_one_association.rb +42 -19
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
- data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
- data/lib/active_record/associations/join_dependency.rb +30 -9
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/preloader.rb +20 -43
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +223 -282
- data/lib/active_record/attribute_assignment.rb +134 -154
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +36 -29
- data/lib/active_record/attribute_methods/primary_key.rb +45 -31
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +67 -90
- data/lib/active_record/attribute_methods/serialization.rb +133 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
- data/lib/active_record/attribute_methods/write.rb +34 -39
- data/lib/active_record/attribute_methods.rb +268 -108
- data/lib/active_record/autosave_association.rb +80 -73
- data/lib/active_record/base.rb +54 -451
- data/lib/active_record/callbacks.rb +60 -22
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
- data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
- data/lib/active_record/connection_adapters/column.rb +67 -36
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
- data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
- data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +472 -0
- data/lib/active_record/counter_cache.rb +107 -108
- data/lib/active_record/dynamic_matchers.rb +115 -63
- data/lib/active_record/errors.rb +36 -18
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +8 -4
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +159 -155
- data/lib/active_record/inheritance.rb +93 -59
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +39 -43
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration/command_recorder.rb +102 -33
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +411 -173
- data/lib/active_record/model_schema.rb +81 -94
- data/lib/active_record/nested_attributes.rb +173 -131
- data/lib/active_record/null_relation.rb +67 -0
- data/lib/active_record/persistence.rb +254 -106
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +113 -38
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +115 -368
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +110 -61
- data/lib/active_record/relation/batches.rb +29 -29
- data/lib/active_record/relation/calculations.rb +155 -125
- data/lib/active_record/relation/delegation.rb +94 -18
- data/lib/active_record/relation/finder_methods.rb +151 -203
- data/lib/active_record/relation/merger.rb +188 -0
- data/lib/active_record/relation/predicate_builder.rb +85 -42
- data/lib/active_record/relation/query_methods.rb +793 -146
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/relation.rb +293 -173
- data/lib/active_record/result.rb +48 -7
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +41 -54
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +41 -41
- data/lib/active_record/schema_migration.rb +46 -0
- data/lib/active_record/scoping/default.rb +56 -52
- data/lib/active_record/scoping/named.rb +78 -103
- data/lib/active_record/scoping.rb +54 -124
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +131 -15
- data/lib/active_record/tasks/database_tasks.rb +204 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +67 -38
- data/lib/active_record/timestamp.rb +16 -11
- data/lib/active_record/transactions.rb +73 -51
- data/lib/active_record/validations/associated.rb +19 -13
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +110 -57
- data/lib/active_record/validations.rb +18 -17
- data/lib/active_record/version.rb +7 -6
- data/lib/active_record.rb +63 -45
- data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -5
- metadata +43 -29
- data/examples/associations.png +0 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,7 +1,5 @@
|
|
1
|
-
require "active_support/core_ext/module/delegation"
|
2
1
|
require "active_support/core_ext/class/attribute_accessors"
|
3
|
-
require
|
4
|
-
require 'active_support/deprecation'
|
2
|
+
require 'set'
|
5
3
|
|
6
4
|
module ActiveRecord
|
7
5
|
# Exception that can be raised to stop migrations from going backwards.
|
@@ -32,6 +30,16 @@ module ActiveRecord
|
|
32
30
|
end
|
33
31
|
end
|
34
32
|
|
33
|
+
class PendingMigrationError < ActiveRecordError#:nodoc:
|
34
|
+
def initialize
|
35
|
+
if defined?(Rails)
|
36
|
+
super("Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=#{::Rails.env}' to resolve this issue.")
|
37
|
+
else
|
38
|
+
super("Migrations are pending; run 'bin/rake db:migrate' to resolve this issue.")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
35
43
|
# = Active Record Migrations
|
36
44
|
#
|
37
45
|
# Migrations can manage the evolution of a schema used by several physical
|
@@ -46,7 +54,7 @@ module ActiveRecord
|
|
46
54
|
#
|
47
55
|
# class AddSsl < ActiveRecord::Migration
|
48
56
|
# def up
|
49
|
-
# add_column :accounts, :ssl_enabled, :boolean, :
|
57
|
+
# add_column :accounts, :ssl_enabled, :boolean, default: true
|
50
58
|
# end
|
51
59
|
#
|
52
60
|
# def down
|
@@ -58,7 +66,7 @@ module ActiveRecord
|
|
58
66
|
# if you're backing out of the migration. It shows how all migrations have
|
59
67
|
# two methods +up+ and +down+ that describes the transformations
|
60
68
|
# required to implement or remove the migration. These methods can consist
|
61
|
-
# of both the migration specific methods like add_column and remove_column
|
69
|
+
# of both the migration specific methods like +add_column+ and +remove_column+,
|
62
70
|
# but may also contain regular Ruby code for generating data needed for the
|
63
71
|
# transformations.
|
64
72
|
#
|
@@ -74,9 +82,9 @@ module ActiveRecord
|
|
74
82
|
# t.integer :position
|
75
83
|
# end
|
76
84
|
#
|
77
|
-
# SystemSetting.create :
|
78
|
-
# :
|
79
|
-
# :
|
85
|
+
# SystemSetting.create name: 'notice',
|
86
|
+
# label: 'Use notice?',
|
87
|
+
# value: 1
|
80
88
|
# end
|
81
89
|
#
|
82
90
|
# def down
|
@@ -84,19 +92,22 @@ module ActiveRecord
|
|
84
92
|
# end
|
85
93
|
# end
|
86
94
|
#
|
87
|
-
# This migration first adds the system_settings table, then creates the very
|
95
|
+
# This migration first adds the +system_settings+ table, then creates the very
|
88
96
|
# first row in it using the Active Record model that relies on the table. It
|
89
|
-
# also uses the more advanced create_table syntax where you can specify a
|
97
|
+
# also uses the more advanced +create_table+ syntax where you can specify a
|
90
98
|
# complete table schema in one block call.
|
91
99
|
#
|
92
100
|
# == Available transformations
|
93
101
|
#
|
94
|
-
# * <tt>create_table(name, options)</tt
|
102
|
+
# * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
|
95
103
|
# makes the table object available to a block that can then add columns to it,
|
96
|
-
# following the same format as add_column
|
104
|
+
# following the same format as +add_column+. See example above. The options hash
|
97
105
|
# is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
|
98
106
|
# table definition.
|
99
107
|
# * <tt>drop_table(name)</tt>: Drops the table called +name+.
|
108
|
+
# * <tt>change_table(name, options)</tt>: Allows to make column alterations to
|
109
|
+
# the table called +name+. It makes the table object available to a block that
|
110
|
+
# can then add/remove columns, indexes or foreign keys to it.
|
100
111
|
# * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
|
101
112
|
# to +new_name+.
|
102
113
|
# * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
|
@@ -105,24 +116,24 @@ module ActiveRecord
|
|
105
116
|
# <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
|
106
117
|
# <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
|
107
118
|
# <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
|
108
|
-
# specified by passing an +options+ hash like <tt>{ :
|
119
|
+
# specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
|
109
120
|
# Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
|
110
|
-
# <tt>{ :
|
121
|
+
# <tt>{ limit: 50, null: false }</tt>) -- see
|
111
122
|
# ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
|
112
123
|
# * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
|
113
124
|
# a column but keeps the type and content.
|
114
125
|
# * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
|
115
126
|
# the column to a different type using the same parameters as add_column.
|
116
|
-
# * <tt>remove_column(table_name,
|
117
|
-
# +
|
127
|
+
# * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
|
128
|
+
# named +column_name+ from the table called +table_name+.
|
118
129
|
# * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
|
119
130
|
# with the name of the column. Other options include
|
120
131
|
# <tt>:name</tt>, <tt>:unique</tt> (e.g.
|
121
|
-
# <tt>{ :
|
122
|
-
# (e.g. { :
|
123
|
-
# * <tt>remove_index(table_name, :
|
132
|
+
# <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
|
133
|
+
# (e.g. <tt>{ order: { name: :desc } }</tt>).
|
134
|
+
# * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
|
124
135
|
# specified by +column_name+.
|
125
|
-
# * <tt>remove_index(table_name, :
|
136
|
+
# * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
|
126
137
|
# specified by +index_name+.
|
127
138
|
#
|
128
139
|
# == Irreversible transformations
|
@@ -323,11 +334,53 @@ module ActiveRecord
|
|
323
334
|
#
|
324
335
|
# For a list of commands that are reversible, please see
|
325
336
|
# <tt>ActiveRecord::Migration::CommandRecorder</tt>.
|
337
|
+
#
|
338
|
+
# == Transactional Migrations
|
339
|
+
#
|
340
|
+
# If the database adapter supports DDL transactions, all migrations will
|
341
|
+
# automatically be wrapped in a transaction. There are queries that you
|
342
|
+
# can't execute inside a transaction though, and for these situations
|
343
|
+
# you can turn the automatic transactions off.
|
344
|
+
#
|
345
|
+
# class ChangeEnum < ActiveRecord::Migration
|
346
|
+
# disable_ddl_transaction!
|
347
|
+
#
|
348
|
+
# def up
|
349
|
+
# execute "ALTER TYPE model_size ADD VALUE 'new_value'"
|
350
|
+
# end
|
351
|
+
# end
|
352
|
+
#
|
353
|
+
# Remember that you can still open your own transactions, even if you
|
354
|
+
# are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
|
326
355
|
class Migration
|
327
356
|
autoload :CommandRecorder, 'active_record/migration/command_recorder'
|
328
357
|
|
358
|
+
|
359
|
+
# This class is used to verify that all migrations have been run before
|
360
|
+
# loading a web page if config.active_record.migration_error is set to :page_load
|
361
|
+
class CheckPending
|
362
|
+
def initialize(app)
|
363
|
+
@app = app
|
364
|
+
@last_check = 0
|
365
|
+
end
|
366
|
+
|
367
|
+
def call(env)
|
368
|
+
mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
|
369
|
+
if @last_check < mtime
|
370
|
+
ActiveRecord::Migration.check_pending!
|
371
|
+
@last_check = mtime
|
372
|
+
end
|
373
|
+
@app.call(env)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
329
377
|
class << self
|
330
378
|
attr_accessor :delegate # :nodoc:
|
379
|
+
attr_accessor :disable_ddl_transaction # :nodoc:
|
380
|
+
end
|
381
|
+
|
382
|
+
def self.check_pending!
|
383
|
+
raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?
|
331
384
|
end
|
332
385
|
|
333
386
|
def self.method_missing(name, *args, &block) # :nodoc:
|
@@ -338,30 +391,145 @@ module ActiveRecord
|
|
338
391
|
new.migrate direction
|
339
392
|
end
|
340
393
|
|
341
|
-
|
394
|
+
# Disable DDL transactions for this migration.
|
395
|
+
def self.disable_ddl_transaction!
|
396
|
+
@disable_ddl_transaction = true
|
397
|
+
end
|
398
|
+
|
399
|
+
def disable_ddl_transaction # :nodoc:
|
400
|
+
self.class.disable_ddl_transaction
|
401
|
+
end
|
342
402
|
|
403
|
+
cattr_accessor :verbose
|
343
404
|
attr_accessor :name, :version
|
344
405
|
|
345
|
-
def initialize
|
346
|
-
@name =
|
347
|
-
@version =
|
406
|
+
def initialize(name = self.class.name, version = nil)
|
407
|
+
@name = name
|
408
|
+
@version = version
|
348
409
|
@connection = nil
|
349
|
-
@reverting = false
|
350
410
|
end
|
351
411
|
|
412
|
+
self.verbose = true
|
352
413
|
# instantiate the delegate object after initialize is defined
|
353
|
-
self.verbose = true
|
354
414
|
self.delegate = new
|
355
415
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
416
|
+
# Reverses the migration commands for the given block and
|
417
|
+
# the given migrations.
|
418
|
+
#
|
419
|
+
# The following migration will remove the table 'horses'
|
420
|
+
# and create the table 'apples' on the way up, and the reverse
|
421
|
+
# on the way down.
|
422
|
+
#
|
423
|
+
# class FixTLMigration < ActiveRecord::Migration
|
424
|
+
# def change
|
425
|
+
# revert do
|
426
|
+
# create_table(:horses) do |t|
|
427
|
+
# t.text :content
|
428
|
+
# t.datetime :remind_at
|
429
|
+
# end
|
430
|
+
# end
|
431
|
+
# create_table(:apples) do |t|
|
432
|
+
# t.string :variety
|
433
|
+
# end
|
434
|
+
# end
|
435
|
+
# end
|
436
|
+
#
|
437
|
+
# Or equivalently, if +TenderloveMigration+ is defined as in the
|
438
|
+
# documentation for Migration:
|
439
|
+
#
|
440
|
+
# require_relative '2012121212_tenderlove_migration'
|
441
|
+
#
|
442
|
+
# class FixupTLMigration < ActiveRecord::Migration
|
443
|
+
# def change
|
444
|
+
# revert TenderloveMigration
|
445
|
+
#
|
446
|
+
# create_table(:apples) do |t|
|
447
|
+
# t.string :variety
|
448
|
+
# end
|
449
|
+
# end
|
450
|
+
# end
|
451
|
+
#
|
452
|
+
# This command can be nested.
|
453
|
+
def revert(*migration_classes)
|
454
|
+
run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
|
455
|
+
if block_given?
|
456
|
+
if @connection.respond_to? :revert
|
457
|
+
@connection.revert { yield }
|
458
|
+
else
|
459
|
+
recorder = CommandRecorder.new(@connection)
|
460
|
+
@connection = recorder
|
461
|
+
suppress_messages do
|
462
|
+
@connection.revert { yield }
|
463
|
+
end
|
464
|
+
@connection = recorder.delegate
|
465
|
+
recorder.commands.each do |cmd, args, block|
|
466
|
+
send(cmd, *args, &block)
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
361
470
|
end
|
362
471
|
|
363
472
|
def reverting?
|
364
|
-
@reverting
|
473
|
+
@connection.respond_to?(:reverting) && @connection.reverting
|
474
|
+
end
|
475
|
+
|
476
|
+
class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
|
477
|
+
def up
|
478
|
+
yield unless reverting
|
479
|
+
end
|
480
|
+
|
481
|
+
def down
|
482
|
+
yield if reverting
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
# Used to specify an operation that can be run in one direction or another.
|
487
|
+
# Call the methods +up+ and +down+ of the yielded object to run a block
|
488
|
+
# only in one given direction.
|
489
|
+
# The whole block will be called in the right order within the migration.
|
490
|
+
#
|
491
|
+
# In the following example, the looping on users will always be done
|
492
|
+
# when the three columns 'first_name', 'last_name' and 'full_name' exist,
|
493
|
+
# even when migrating down:
|
494
|
+
#
|
495
|
+
# class SplitNameMigration < ActiveRecord::Migration
|
496
|
+
# def change
|
497
|
+
# add_column :users, :first_name, :string
|
498
|
+
# add_column :users, :last_name, :string
|
499
|
+
#
|
500
|
+
# reversible do |dir|
|
501
|
+
# User.reset_column_information
|
502
|
+
# User.all.each do |u|
|
503
|
+
# dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
|
504
|
+
# dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
|
505
|
+
# u.save
|
506
|
+
# end
|
507
|
+
# end
|
508
|
+
#
|
509
|
+
# revert { add_column :users, :full_name, :string }
|
510
|
+
# end
|
511
|
+
# end
|
512
|
+
def reversible
|
513
|
+
helper = ReversibleBlockHelper.new(reverting?)
|
514
|
+
execute_block{ yield helper }
|
515
|
+
end
|
516
|
+
|
517
|
+
# Runs the given migration classes.
|
518
|
+
# Last argument can specify options:
|
519
|
+
# - :direction (default is :up)
|
520
|
+
# - :revert (default is false)
|
521
|
+
def run(*migration_classes)
|
522
|
+
opts = migration_classes.extract_options!
|
523
|
+
dir = opts[:direction] || :up
|
524
|
+
dir = (dir == :down ? :up : :down) if opts[:revert]
|
525
|
+
if reverting?
|
526
|
+
# If in revert and going :up, say, we want to execute :down without reverting, so
|
527
|
+
revert { run(*migration_classes, direction: dir, revert: true) }
|
528
|
+
else
|
529
|
+
migration_classes.each do |migration_class|
|
530
|
+
migration_class.new.exec_migration(@connection, dir)
|
531
|
+
end
|
532
|
+
end
|
365
533
|
end
|
366
534
|
|
367
535
|
def up
|
@@ -387,29 +555,9 @@ module ActiveRecord
|
|
387
555
|
|
388
556
|
time = nil
|
389
557
|
ActiveRecord::Base.connection_pool.with_connection do |conn|
|
390
|
-
|
391
|
-
|
392
|
-
if direction == :down
|
393
|
-
recorder = CommandRecorder.new(@connection)
|
394
|
-
suppress_messages do
|
395
|
-
@connection = recorder
|
396
|
-
change
|
397
|
-
end
|
398
|
-
@connection = conn
|
399
|
-
time = Benchmark.measure {
|
400
|
-
self.revert {
|
401
|
-
recorder.inverse.each do |cmd, args|
|
402
|
-
send(cmd, *args)
|
403
|
-
end
|
404
|
-
}
|
405
|
-
}
|
406
|
-
else
|
407
|
-
time = Benchmark.measure { change }
|
408
|
-
end
|
409
|
-
else
|
410
|
-
time = Benchmark.measure { send(direction) }
|
558
|
+
time = Benchmark.measure do
|
559
|
+
exec_migration(conn, direction)
|
411
560
|
end
|
412
|
-
@connection = nil
|
413
561
|
end
|
414
562
|
|
415
563
|
case direction
|
@@ -418,6 +566,21 @@ module ActiveRecord
|
|
418
566
|
end
|
419
567
|
end
|
420
568
|
|
569
|
+
def exec_migration(conn, direction)
|
570
|
+
@connection = conn
|
571
|
+
if respond_to?(:change)
|
572
|
+
if direction == :down
|
573
|
+
revert { change }
|
574
|
+
else
|
575
|
+
change
|
576
|
+
end
|
577
|
+
else
|
578
|
+
send(direction)
|
579
|
+
end
|
580
|
+
ensure
|
581
|
+
@connection = nil
|
582
|
+
end
|
583
|
+
|
421
584
|
def write(text="")
|
422
585
|
puts(text) if verbose
|
423
586
|
end
|
@@ -456,9 +619,9 @@ module ActiveRecord
|
|
456
619
|
arg_list = arguments.map{ |a| a.inspect } * ', '
|
457
620
|
|
458
621
|
say_with_time "#{method}(#{arg_list})" do
|
459
|
-
unless
|
460
|
-
unless arguments.empty? ||
|
461
|
-
arguments[0] = Migrator.proper_table_name(arguments.first)
|
622
|
+
unless @connection.respond_to? :revert
|
623
|
+
unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
|
624
|
+
arguments[0] = Migrator.proper_table_name(arguments.first)
|
462
625
|
arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
|
463
626
|
end
|
464
627
|
end
|
@@ -470,7 +633,7 @@ module ActiveRecord
|
|
470
633
|
def copy(destination, sources, options = {})
|
471
634
|
copied = []
|
472
635
|
|
473
|
-
FileUtils.mkdir_p(destination) unless File.
|
636
|
+
FileUtils.mkdir_p(destination) unless File.exist?(destination)
|
474
637
|
|
475
638
|
destination_migrations = ActiveRecord::Migrator.migrations(destination)
|
476
639
|
last = destination_migrations.last
|
@@ -478,8 +641,17 @@ module ActiveRecord
|
|
478
641
|
source_migrations = ActiveRecord::Migrator.migrations(path)
|
479
642
|
|
480
643
|
source_migrations.each do |migration|
|
481
|
-
source = File.
|
482
|
-
|
644
|
+
source = File.binread(migration.filename)
|
645
|
+
inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
|
646
|
+
if /\A#.*\b(?:en)?coding:\s*\S+/ =~ source
|
647
|
+
# If we have a magic comment in the original migration,
|
648
|
+
# insert our comment after the first newline(end of the magic comment line)
|
649
|
+
# so the magic keep working.
|
650
|
+
# Note that magic comments must be at the first line(except sh-bang).
|
651
|
+
source[/\n/] = "\n#{inserted_comment}"
|
652
|
+
else
|
653
|
+
source = "#{inserted_comment}#{source}"
|
654
|
+
end
|
483
655
|
|
484
656
|
if duplicate = destination_migrations.detect { |m| m.name == migration.name }
|
485
657
|
if options[:on_skip] && duplicate.scope != scope.to_s
|
@@ -493,7 +665,7 @@ module ActiveRecord
|
|
493
665
|
old_path, migration.filename = migration.filename, new_path
|
494
666
|
last = migration
|
495
667
|
|
496
|
-
File.
|
668
|
+
File.binwrite(migration.filename, source)
|
497
669
|
copied << migration
|
498
670
|
options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
|
499
671
|
destination_migrations << migration
|
@@ -507,7 +679,16 @@ module ActiveRecord
|
|
507
679
|
if ActiveRecord::Base.timestamped_migrations
|
508
680
|
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
|
509
681
|
else
|
510
|
-
|
682
|
+
SchemaMigration.normalize_migration_number(number)
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
private
|
687
|
+
def execute_block
|
688
|
+
if connection.respond_to? :execute_block
|
689
|
+
super # use normal delegation to record the block
|
690
|
+
else
|
691
|
+
yield
|
511
692
|
end
|
512
693
|
end
|
513
694
|
end
|
@@ -525,7 +706,11 @@ module ActiveRecord
|
|
525
706
|
File.basename(filename)
|
526
707
|
end
|
527
708
|
|
528
|
-
|
709
|
+
def mtime
|
710
|
+
File.mtime filename
|
711
|
+
end
|
712
|
+
|
713
|
+
delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
|
529
714
|
|
530
715
|
private
|
531
716
|
|
@@ -535,11 +720,21 @@ module ActiveRecord
|
|
535
720
|
|
536
721
|
def load_migration
|
537
722
|
require(File.expand_path(filename))
|
538
|
-
name.constantize.new
|
723
|
+
name.constantize.new(name, version)
|
539
724
|
end
|
540
725
|
|
541
726
|
end
|
542
727
|
|
728
|
+
class NullMigration < MigrationProxy #:nodoc:
|
729
|
+
def initialize
|
730
|
+
super(nil, 0, nil, nil)
|
731
|
+
end
|
732
|
+
|
733
|
+
def mtime
|
734
|
+
0
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
543
738
|
class Migrator#:nodoc:
|
544
739
|
class << self
|
545
740
|
attr_writer :migrations_paths
|
@@ -547,14 +742,14 @@ module ActiveRecord
|
|
547
742
|
|
548
743
|
def migrate(migrations_paths, target_version = nil, &block)
|
549
744
|
case
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
745
|
+
when target_version.nil?
|
746
|
+
up(migrations_paths, target_version, &block)
|
747
|
+
when current_version == 0 && target_version == 0
|
748
|
+
[]
|
749
|
+
when current_version > target_version
|
750
|
+
down(migrations_paths, target_version, &block)
|
751
|
+
else
|
752
|
+
up(migrations_paths, target_version, &block)
|
558
753
|
end
|
559
754
|
end
|
560
755
|
|
@@ -566,25 +761,34 @@ module ActiveRecord
|
|
566
761
|
move(:up, migrations_paths, steps)
|
567
762
|
end
|
568
763
|
|
569
|
-
def up(migrations_paths, target_version = nil
|
570
|
-
|
764
|
+
def up(migrations_paths, target_version = nil)
|
765
|
+
migrations = migrations(migrations_paths)
|
766
|
+
migrations.select! { |m| yield m } if block_given?
|
767
|
+
|
768
|
+
self.new(:up, migrations, target_version).migrate
|
571
769
|
end
|
572
770
|
|
573
771
|
def down(migrations_paths, target_version = nil, &block)
|
574
|
-
|
772
|
+
migrations = migrations(migrations_paths)
|
773
|
+
migrations.select! { |m| yield m } if block_given?
|
774
|
+
|
775
|
+
self.new(:down, migrations, target_version).migrate
|
575
776
|
end
|
576
777
|
|
577
778
|
def run(direction, migrations_paths, target_version)
|
578
|
-
self.new(direction, migrations_paths, target_version).run
|
779
|
+
self.new(direction, migrations(migrations_paths), target_version).run
|
780
|
+
end
|
781
|
+
|
782
|
+
def open(migrations_paths)
|
783
|
+
self.new(:up, migrations(migrations_paths), nil)
|
579
784
|
end
|
580
785
|
|
581
786
|
def schema_migrations_table_name
|
582
|
-
|
787
|
+
SchemaMigration.table_name
|
583
788
|
end
|
584
789
|
|
585
790
|
def get_all_versions
|
586
|
-
|
587
|
-
Base.connection.select_values(table.project(table['version'])).map{ |v| v.to_i }.sort
|
791
|
+
SchemaMigration.all.map { |x| x.version.to_i }.sort
|
588
792
|
end
|
589
793
|
|
590
794
|
def current_version
|
@@ -596,35 +800,41 @@ module ActiveRecord
|
|
596
800
|
end
|
597
801
|
end
|
598
802
|
|
803
|
+
def needs_migration?
|
804
|
+
current_version < last_version
|
805
|
+
end
|
806
|
+
|
807
|
+
def last_version
|
808
|
+
last_migration.version
|
809
|
+
end
|
810
|
+
|
811
|
+
def last_migration #:nodoc:
|
812
|
+
migrations(migrations_paths).last || NullMigration.new
|
813
|
+
end
|
814
|
+
|
599
815
|
def proper_table_name(name)
|
600
816
|
# Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
|
601
|
-
name.table_name
|
817
|
+
if name.respond_to? :table_name
|
818
|
+
name.table_name
|
819
|
+
else
|
820
|
+
"#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
|
821
|
+
end
|
602
822
|
end
|
603
823
|
|
604
824
|
def migrations_paths
|
605
825
|
@migrations_paths ||= ['db/migrate']
|
606
826
|
# just to not break things if someone uses: migration_path = some_string
|
607
|
-
Array
|
827
|
+
Array(@migrations_paths)
|
608
828
|
end
|
609
829
|
|
610
830
|
def migrations_path
|
611
831
|
migrations_paths.first
|
612
832
|
end
|
613
833
|
|
614
|
-
def migrations(paths
|
615
|
-
|
616
|
-
subdirectories = true
|
617
|
-
else
|
618
|
-
subdirectories = args.first
|
619
|
-
ActiveSupport::Deprecation.warn "The `subdirectories` argument to `migrations` is deprecated"
|
620
|
-
end
|
621
|
-
|
622
|
-
paths = Array.wrap(paths)
|
834
|
+
def migrations(paths)
|
835
|
+
paths = Array(paths)
|
623
836
|
|
624
|
-
|
625
|
-
files = Dir[*paths.map { |p| "#{p}/#{glob}[0-9]*_*.rb" }]
|
626
|
-
|
627
|
-
seen = Hash.new false
|
837
|
+
files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
|
628
838
|
|
629
839
|
migrations = files.map do |file|
|
630
840
|
version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
|
@@ -633,11 +843,6 @@ module ActiveRecord
|
|
633
843
|
version = version.to_i
|
634
844
|
name = name.camelize
|
635
845
|
|
636
|
-
raise DuplicateMigrationVersionError.new(version) if seen[version]
|
637
|
-
raise DuplicateMigrationNameError.new(name) if seen[name]
|
638
|
-
|
639
|
-
seen[version] = seen[name] = true
|
640
|
-
|
641
846
|
MigrationProxy.new(name, version, file, scope)
|
642
847
|
end
|
643
848
|
|
@@ -647,7 +852,7 @@ module ActiveRecord
|
|
647
852
|
private
|
648
853
|
|
649
854
|
def move(direction, migrations_paths, steps)
|
650
|
-
migrator = self.new(direction, migrations_paths)
|
855
|
+
migrator = self.new(direction, migrations(migrations_paths))
|
651
856
|
start_index = migrator.migrations.index(migrator.current_migration)
|
652
857
|
|
653
858
|
if start_index
|
@@ -658,124 +863,157 @@ module ActiveRecord
|
|
658
863
|
end
|
659
864
|
end
|
660
865
|
|
661
|
-
def initialize(direction,
|
866
|
+
def initialize(direction, migrations, target_version = nil)
|
662
867
|
raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
|
868
|
+
|
869
|
+
@direction = direction
|
870
|
+
@target_version = target_version
|
871
|
+
@migrated_versions = nil
|
872
|
+
|
873
|
+
if Array(migrations).grep(String).empty?
|
874
|
+
@migrations = migrations
|
875
|
+
else
|
876
|
+
ActiveSupport::Deprecation.warn "instantiate this class with a list of migrations"
|
877
|
+
@migrations = self.class.migrations(migrations)
|
878
|
+
end
|
879
|
+
|
880
|
+
validate(@migrations)
|
881
|
+
|
663
882
|
Base.connection.initialize_schema_migrations_table
|
664
|
-
@direction, @migrations_paths, @target_version = direction, migrations_paths, target_version
|
665
883
|
end
|
666
884
|
|
667
885
|
def current_version
|
668
|
-
migrated.
|
886
|
+
migrated.max || 0
|
669
887
|
end
|
670
888
|
|
671
889
|
def current_migration
|
672
890
|
migrations.detect { |m| m.version == current_version }
|
673
891
|
end
|
892
|
+
alias :current :current_migration
|
674
893
|
|
675
894
|
def run
|
676
|
-
|
677
|
-
raise UnknownMigrationVersionError.new(@target_version) if
|
678
|
-
unless (up? && migrated.include?(
|
679
|
-
|
680
|
-
|
895
|
+
migration = migrations.detect { |m| m.version == @target_version }
|
896
|
+
raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
|
897
|
+
unless (up? && migrated.include?(migration.version.to_i)) || (down? && !migrated.include?(migration.version.to_i))
|
898
|
+
begin
|
899
|
+
execute_migration_in_transaction(migration, @direction)
|
900
|
+
rescue => e
|
901
|
+
canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : ""
|
902
|
+
raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace
|
903
|
+
end
|
681
904
|
end
|
682
905
|
end
|
683
906
|
|
684
|
-
def migrate
|
685
|
-
|
686
|
-
target = migrations.detect { |m| m.version == @target_version }
|
687
|
-
|
688
|
-
if target.nil? && @target_version && @target_version > 0
|
907
|
+
def migrate
|
908
|
+
if !target && @target_version && @target_version > 0
|
689
909
|
raise UnknownMigrationVersionError.new(@target_version)
|
690
910
|
end
|
691
911
|
|
692
|
-
|
693
|
-
finish = migrations.index(target) || migrations.size - 1
|
694
|
-
runnable = migrations[start..finish]
|
912
|
+
running = runnable
|
695
913
|
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
if block && !block.call(migration)
|
702
|
-
next
|
703
|
-
end
|
914
|
+
if block_given?
|
915
|
+
message = "block argument to migrate is deprecated, please filter migrations before constructing the migrator"
|
916
|
+
ActiveSupport::Deprecation.warn message
|
917
|
+
running.select! { |m| yield m }
|
918
|
+
end
|
704
919
|
|
920
|
+
running.each do |migration|
|
705
921
|
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
|
706
922
|
|
707
|
-
seen = migrated.include?(migration.version.to_i)
|
708
|
-
|
709
|
-
# On our way up, we skip migrating the ones we've already migrated
|
710
|
-
next if up? && seen
|
711
|
-
|
712
|
-
# On our way down, we skip reverting the ones we've never migrated
|
713
|
-
if down? && !seen
|
714
|
-
migration.announce 'never migrated, skipping'; migration.write
|
715
|
-
next
|
716
|
-
end
|
717
|
-
|
718
923
|
begin
|
719
|
-
|
720
|
-
migration.migrate(@direction)
|
721
|
-
record_version_state_after_migrating(migration.version)
|
722
|
-
end
|
723
|
-
ran << migration
|
924
|
+
execute_migration_in_transaction(migration, @direction)
|
724
925
|
rescue => e
|
725
|
-
canceled_msg =
|
926
|
+
canceled_msg = use_transaction?(migration) ? "this and " : ""
|
726
927
|
raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
|
727
928
|
end
|
728
929
|
end
|
729
|
-
ran
|
730
930
|
end
|
731
931
|
|
732
|
-
def
|
733
|
-
|
734
|
-
|
735
|
-
|
932
|
+
def runnable
|
933
|
+
runnable = migrations[start..finish]
|
934
|
+
if up?
|
935
|
+
runnable.reject { |m| ran?(m) }
|
936
|
+
else
|
937
|
+
# skip the last migration if we're headed down, but not ALL the way down
|
938
|
+
runnable.pop if target
|
939
|
+
runnable.find_all { |m| ran?(m) }
|
736
940
|
end
|
737
941
|
end
|
738
942
|
|
943
|
+
def migrations
|
944
|
+
down? ? @migrations.reverse : @migrations.sort_by(&:version)
|
945
|
+
end
|
946
|
+
|
739
947
|
def pending_migrations
|
740
948
|
already_migrated = migrated
|
741
|
-
migrations.reject { |m| already_migrated.include?(m.version
|
949
|
+
migrations.reject { |m| already_migrated.include?(m.version) }
|
742
950
|
end
|
743
951
|
|
744
952
|
def migrated
|
745
|
-
@migrated_versions ||= self.class.get_all_versions
|
953
|
+
@migrated_versions ||= Set.new(self.class.get_all_versions)
|
746
954
|
end
|
747
955
|
|
748
956
|
private
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
@migrated_versions ||= []
|
753
|
-
if down?
|
754
|
-
@migrated_versions.delete(version)
|
755
|
-
stmt = table.where(table["version"].eq(version.to_s)).compile_delete
|
756
|
-
Base.connection.delete stmt
|
757
|
-
else
|
758
|
-
@migrated_versions.push(version).sort!
|
759
|
-
stmt = table.compile_insert table["version"] => version.to_s
|
760
|
-
Base.connection.insert stmt
|
761
|
-
end
|
762
|
-
end
|
957
|
+
def ran?(migration)
|
958
|
+
migrated.include?(migration.version.to_i)
|
959
|
+
end
|
763
960
|
|
764
|
-
|
765
|
-
|
961
|
+
def execute_migration_in_transaction(migration, direction)
|
962
|
+
ddl_transaction(migration) do
|
963
|
+
migration.migrate(direction)
|
964
|
+
record_version_state_after_migrating(migration.version)
|
766
965
|
end
|
966
|
+
end
|
967
|
+
|
968
|
+
def target
|
969
|
+
migrations.detect { |m| m.version == @target_version }
|
970
|
+
end
|
971
|
+
|
972
|
+
def finish
|
973
|
+
migrations.index(target) || migrations.size - 1
|
974
|
+
end
|
975
|
+
|
976
|
+
def start
|
977
|
+
up? ? 0 : (migrations.index(current) || 0)
|
978
|
+
end
|
767
979
|
|
768
|
-
|
769
|
-
|
980
|
+
def validate(migrations)
|
981
|
+
name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
|
982
|
+
raise DuplicateMigrationNameError.new(name) if name
|
983
|
+
|
984
|
+
version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
|
985
|
+
raise DuplicateMigrationVersionError.new(version) if version
|
986
|
+
end
|
987
|
+
|
988
|
+
def record_version_state_after_migrating(version)
|
989
|
+
if down?
|
990
|
+
migrated.delete(version)
|
991
|
+
ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
|
992
|
+
else
|
993
|
+
migrated << version
|
994
|
+
ActiveRecord::SchemaMigration.create!(:version => version.to_s)
|
770
995
|
end
|
996
|
+
end
|
771
997
|
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
998
|
+
def up?
|
999
|
+
@direction == :up
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
def down?
|
1003
|
+
@direction == :down
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
# Wrap the migration in a transaction only if supported by the adapter.
|
1007
|
+
def ddl_transaction(migration)
|
1008
|
+
if use_transaction?(migration)
|
1009
|
+
Base.transaction { yield }
|
1010
|
+
else
|
1011
|
+
yield
|
779
1012
|
end
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
def use_transaction?(migration)
|
1016
|
+
!migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
|
1017
|
+
end
|
780
1018
|
end
|
781
1019
|
end
|