activerecord 6.0.0.beta1 → 6.0.0

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.

Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +455 -9
  3. data/README.rdoc +3 -1
  4. data/lib/active_record/associations/association.rb +18 -1
  5. data/lib/active_record/associations/builder/association.rb +14 -18
  6. data/lib/active_record/associations/builder/belongs_to.rb +5 -2
  7. data/lib/active_record/associations/builder/collection_association.rb +5 -15
  8. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
  9. data/lib/active_record/associations/builder/has_many.rb +2 -0
  10. data/lib/active_record/associations/builder/has_one.rb +35 -1
  11. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  12. data/lib/active_record/associations/collection_association.rb +5 -6
  13. data/lib/active_record/associations/collection_proxy.rb +13 -42
  14. data/lib/active_record/associations/has_many_association.rb +1 -9
  15. data/lib/active_record/associations/has_many_through_association.rb +4 -11
  16. data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
  17. data/lib/active_record/associations/join_dependency.rb +10 -9
  18. data/lib/active_record/associations/preloader/association.rb +37 -34
  19. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  20. data/lib/active_record/associations/preloader.rb +11 -6
  21. data/lib/active_record/associations.rb +3 -2
  22. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  23. data/lib/active_record/attribute_methods/dirty.rb +47 -14
  24. data/lib/active_record/attribute_methods/primary_key.rb +7 -15
  25. data/lib/active_record/attribute_methods/query.rb +2 -3
  26. data/lib/active_record/attribute_methods/read.rb +3 -9
  27. data/lib/active_record/attribute_methods/write.rb +6 -12
  28. data/lib/active_record/attribute_methods.rb +3 -53
  29. data/lib/active_record/attributes.rb +13 -0
  30. data/lib/active_record/autosave_association.rb +15 -5
  31. data/lib/active_record/base.rb +0 -1
  32. data/lib/active_record/callbacks.rb +3 -3
  33. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +124 -23
  34. data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
  35. data/lib/active_record/connection_adapters/abstract/database_statements.rb +101 -70
  36. data/lib/active_record/connection_adapters/abstract/query_cache.rb +11 -5
  37. data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
  38. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
  39. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
  40. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
  42. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  43. data/lib/active_record/connection_adapters/abstract_adapter.rb +108 -39
  44. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +93 -134
  45. data/lib/active_record/connection_adapters/column.rb +17 -13
  46. data/lib/active_record/connection_adapters/connection_specification.rb +1 -1
  47. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
  48. data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -7
  49. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  50. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  51. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  52. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
  53. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  54. data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
  55. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
  56. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +5 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  58. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  59. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  60. data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
  61. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
  62. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
  63. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
  64. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
  65. data/lib/active_record/connection_adapters/postgresql_adapter.rb +91 -24
  66. data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
  67. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  68. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  69. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +69 -118
  72. data/lib/active_record/connection_handling.rb +32 -16
  73. data/lib/active_record/core.rb +27 -20
  74. data/lib/active_record/database_configurations/hash_config.rb +11 -11
  75. data/lib/active_record/database_configurations/url_config.rb +21 -16
  76. data/lib/active_record/database_configurations.rb +99 -50
  77. data/lib/active_record/dynamic_matchers.rb +1 -1
  78. data/lib/active_record/enum.rb +15 -0
  79. data/lib/active_record/errors.rb +18 -13
  80. data/lib/active_record/fixtures.rb +11 -6
  81. data/lib/active_record/gem_version.rb +1 -1
  82. data/lib/active_record/inheritance.rb +1 -1
  83. data/lib/active_record/insert_all.rb +179 -0
  84. data/lib/active_record/integration.rb +13 -1
  85. data/lib/active_record/internal_metadata.rb +5 -1
  86. data/lib/active_record/locking/optimistic.rb +3 -4
  87. data/lib/active_record/log_subscriber.rb +1 -1
  88. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  89. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  90. data/lib/active_record/middleware/database_selector.rb +75 -0
  91. data/lib/active_record/migration/command_recorder.rb +28 -14
  92. data/lib/active_record/migration/compatibility.rb +72 -63
  93. data/lib/active_record/migration.rb +62 -44
  94. data/lib/active_record/persistence.rb +212 -19
  95. data/lib/active_record/querying.rb +18 -14
  96. data/lib/active_record/railtie.rb +9 -1
  97. data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
  98. data/lib/active_record/railties/databases.rake +124 -25
  99. data/lib/active_record/reflection.rb +18 -32
  100. data/lib/active_record/relation/calculations.rb +40 -44
  101. data/lib/active_record/relation/delegation.rb +23 -31
  102. data/lib/active_record/relation/finder_methods.rb +13 -13
  103. data/lib/active_record/relation/merger.rb +11 -16
  104. data/lib/active_record/relation/query_attribute.rb +5 -3
  105. data/lib/active_record/relation/query_methods.rb +217 -68
  106. data/lib/active_record/relation/spawn_methods.rb +1 -1
  107. data/lib/active_record/relation/where_clause.rb +10 -10
  108. data/lib/active_record/relation.rb +184 -35
  109. data/lib/active_record/sanitization.rb +33 -4
  110. data/lib/active_record/schema.rb +1 -1
  111. data/lib/active_record/schema_dumper.rb +10 -1
  112. data/lib/active_record/schema_migration.rb +1 -1
  113. data/lib/active_record/scoping/default.rb +7 -15
  114. data/lib/active_record/scoping/named.rb +10 -2
  115. data/lib/active_record/scoping.rb +6 -7
  116. data/lib/active_record/statement_cache.rb +2 -2
  117. data/lib/active_record/store.rb +48 -0
  118. data/lib/active_record/table_metadata.rb +9 -13
  119. data/lib/active_record/tasks/database_tasks.rb +109 -6
  120. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
  121. data/lib/active_record/test_databases.rb +1 -16
  122. data/lib/active_record/test_fixtures.rb +2 -2
  123. data/lib/active_record/timestamp.rb +35 -19
  124. data/lib/active_record/touch_later.rb +4 -2
  125. data/lib/active_record/transactions.rb +55 -45
  126. data/lib/active_record/type_caster/connection.rb +16 -10
  127. data/lib/active_record/validations/uniqueness.rb +4 -4
  128. data/lib/active_record/validations.rb +1 -0
  129. data/lib/active_record.rb +7 -1
  130. data/lib/arel/insert_manager.rb +3 -3
  131. data/lib/arel/nodes/and.rb +1 -1
  132. data/lib/arel/nodes/case.rb +1 -1
  133. data/lib/arel/nodes/comment.rb +29 -0
  134. data/lib/arel/nodes/select_core.rb +16 -12
  135. data/lib/arel/nodes/unary.rb +1 -0
  136. data/lib/arel/nodes/values_list.rb +2 -17
  137. data/lib/arel/nodes.rb +2 -1
  138. data/lib/arel/select_manager.rb +10 -10
  139. data/lib/arel/visitors/depth_first.rb +7 -2
  140. data/lib/arel/visitors/dot.rb +7 -2
  141. data/lib/arel/visitors/ibm_db.rb +13 -0
  142. data/lib/arel/visitors/informix.rb +6 -0
  143. data/lib/arel/visitors/mssql.rb +15 -1
  144. data/lib/arel/visitors/oracle12.rb +4 -5
  145. data/lib/arel/visitors/postgresql.rb +4 -10
  146. data/lib/arel/visitors/to_sql.rb +107 -131
  147. data/lib/arel/visitors/visitor.rb +9 -5
  148. data/lib/arel.rb +7 -0
  149. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  150. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  151. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  152. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  153. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  154. metadata +17 -13
  155. data/lib/active_record/collection_cache_key.rb +0 -53
  156. data/lib/arel/nodes/values.rb +0 -16
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  end
16
16
 
17
17
  delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
18
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options,
18
+ :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys?, :foreign_key_options,
19
19
  to: :@conn, private: true
20
20
 
21
21
  private
@@ -50,7 +50,7 @@ module ActiveRecord
50
50
  statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
51
51
  end
52
52
 
53
- if supports_foreign_keys_in_create?
53
+ if supports_foreign_keys?
54
54
  statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
55
55
  end
56
56
 
@@ -127,6 +127,9 @@ module ActiveRecord
127
127
  end
128
128
 
129
129
  def foreign_key_in_create(from_table, to_table, options)
130
+ prefix = ActiveRecord::Base.table_name_prefix
131
+ suffix = ActiveRecord::Base.table_name_suffix
132
+ to_table = "#{prefix}#{to_table}#{suffix}"
130
133
  options = foreign_key_options(from_table, to_table, options)
131
134
  accept ForeignKeyDefinition.new(from_table, to_table, options)
132
135
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/deprecation"
4
-
5
3
  module ActiveRecord
6
4
  module ConnectionAdapters #:nodoc:
7
5
  # Abstract representation of an index definition on a table. Instances of
@@ -104,16 +102,12 @@ module ActiveRecord
104
102
  alias validated? validate?
105
103
 
106
104
  def export_name_on_schema_dump?
107
- name !~ ActiveRecord::SchemaDumper.fk_ignore_pattern
105
+ !ActiveRecord::SchemaDumper.fk_ignore_pattern.match?(name) if name
108
106
  end
109
107
 
110
- def defined_for?(to_table_ord = nil, to_table: nil, **options)
111
- if to_table_ord
112
- self.to_table == to_table_ord.to_s
113
- else
114
- (to_table.nil? || to_table.to_s == self.to_table) &&
115
- options.all? { |k, v| self.options[k].to_s == v.to_s }
116
- end
108
+ def defined_for?(to_table: nil, **options)
109
+ (to_table.nil? || to_table.to_s == self.to_table) &&
110
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
117
111
  end
118
112
 
119
113
  private
@@ -200,41 +194,44 @@ module ActiveRecord
200
194
  end
201
195
 
202
196
  module ColumnMethods
197
+ extend ActiveSupport::Concern
198
+
203
199
  # Appends a primary key definition to the table definition.
204
200
  # Can be called multiple times, but this is probably not a good idea.
205
201
  def primary_key(name, type = :primary_key, **options)
206
202
  column(name, type, options.merge(primary_key: true))
207
203
  end
208
204
 
205
+ ##
206
+ # :method: column
207
+ # :call-seq: column(name, type, **options)
208
+ #
209
209
  # Appends a column or columns of a specified type.
210
210
  #
211
211
  # t.string(:goat)
212
212
  # t.string(:goat, :sheep)
213
213
  #
214
214
  # See TableDefinition#column
215
- [
216
- :bigint,
217
- :binary,
218
- :boolean,
219
- :date,
220
- :datetime,
221
- :decimal,
222
- :float,
223
- :integer,
224
- :json,
225
- :string,
226
- :text,
227
- :time,
228
- :timestamp,
229
- :virtual,
230
- ].each do |column_type|
231
- module_eval <<-CODE, __FILE__, __LINE__ + 1
232
- def #{column_type}(*args, **options)
233
- args.each { |name| column(name, :#{column_type}, options) }
215
+
216
+ included do
217
+ define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
218
+ :float, :integer, :json, :string, :text, :time, :timestamp, :virtual
219
+
220
+ alias :numeric :decimal
221
+ end
222
+
223
+ class_methods do
224
+ private def define_column_methods(*column_types) # :nodoc:
225
+ column_types.each do |column_type|
226
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
227
+ def #{column_type}(*names, **options)
228
+ raise ArgumentError, "Missing column name(s) for #{column_type}" if names.empty?
229
+ names.each { |name| column(name, :#{column_type}, options) }
230
+ end
231
+ RUBY
234
232
  end
235
- CODE
233
+ end
236
234
  end
237
- alias_method :numeric, :decimal
238
235
  end
239
236
 
240
237
  # Represents the schema of an SQL table in an abstract way. This class
@@ -259,10 +256,9 @@ module ActiveRecord
259
256
  include ColumnMethods
260
257
 
261
258
  attr_reader :name, :temporary, :if_not_exists, :options, :as, :comment, :indexes, :foreign_keys
262
- attr_writer :indexes
263
- deprecate :indexes=
264
259
 
265
260
  def initialize(
261
+ conn,
266
262
  name,
267
263
  temporary: false,
268
264
  if_not_exists: false,
@@ -271,6 +267,7 @@ module ActiveRecord
271
267
  comment: nil,
272
268
  **
273
269
  )
270
+ @conn = conn
274
271
  @columns_hash = {}
275
272
  @indexes = []
276
273
  @foreign_keys = []
@@ -363,7 +360,7 @@ module ActiveRecord
363
360
  # t.references :tagger, polymorphic: true
364
361
  # t.references :taggable, polymorphic: { default: 'Photo' }, index: false
365
362
  # end
366
- def column(name, type, options = {})
363
+ def column(name, type, **options)
367
364
  name = name.to_s
368
365
  type = type.to_sym if type
369
366
  options = options.dup
@@ -397,10 +394,7 @@ module ActiveRecord
397
394
  end
398
395
 
399
396
  def foreign_key(table_name, options = {}) # :nodoc:
400
- table_name_prefix = ActiveRecord::Base.table_name_prefix
401
- table_name_suffix = ActiveRecord::Base.table_name_suffix
402
- table_name = "#{table_name_prefix}#{table_name}#{table_name_suffix}"
403
- foreign_keys.push([table_name, options])
397
+ foreign_keys << [table_name, options]
404
398
  end
405
399
 
406
400
  # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
@@ -410,6 +404,10 @@ module ActiveRecord
410
404
  def timestamps(**options)
411
405
  options[:null] = false if options[:null].nil?
412
406
 
407
+ if !options.key?(:precision) && @conn.supports_datetime_with_precision?
408
+ options[:precision] = 6
409
+ end
410
+
413
411
  column(:created_at, :datetime, options)
414
412
  column(:updated_at, :datetime, options)
415
413
  end
@@ -418,6 +416,7 @@ module ActiveRecord
418
416
  #
419
417
  # t.references(:user)
420
418
  # t.belongs_to(:supplier, foreign_key: true)
419
+ # t.belongs_to(:supplier, foreign_key: true, type: :integer)
421
420
  #
422
421
  # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
423
422
  def references(*args, **options)
@@ -517,6 +516,7 @@ module ActiveRecord
517
516
  # t.json
518
517
  # t.virtual
519
518
  # t.remove
519
+ # t.remove_foreign_key
520
520
  # t.remove_references
521
521
  # t.remove_belongs_to
522
522
  # t.remove_index
@@ -538,7 +538,7 @@ module ActiveRecord
538
538
  # t.column(:name, :string)
539
539
  #
540
540
  # See TableDefinition#column for details of the options you can use.
541
- def column(column_name, type, options = {})
541
+ def column(column_name, type, **options)
542
542
  index_options = options.delete(:index)
543
543
  @base.add_column(name, column_name, type, options)
544
544
  index(column_name, index_options.is_a?(Hash) ? index_options : {}) if index_options
@@ -680,15 +680,26 @@ module ActiveRecord
680
680
  end
681
681
  alias :remove_belongs_to :remove_references
682
682
 
683
- # Adds a foreign key.
683
+ # Adds a foreign key to the table using a supplied table name.
684
684
  #
685
685
  # t.foreign_key(:authors)
686
+ # t.foreign_key(:authors, column: :author_id, primary_key: "id")
686
687
  #
687
688
  # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
688
689
  def foreign_key(*args)
689
690
  @base.add_foreign_key(name, *args)
690
691
  end
691
692
 
693
+ # Removes the given foreign key from the table.
694
+ #
695
+ # t.remove_foreign_key(:authors)
696
+ # t.remove_foreign_key(column: :author_id)
697
+ #
698
+ # See {connection.remove_foreign_key}[rdoc-ref:SchemaStatements#remove_foreign_key]
699
+ def remove_foreign_key(*args)
700
+ @base.remove_foreign_key(name, *args)
701
+ end
702
+
692
703
  # Checks to see if a foreign key exists.
693
704
  #
694
705
  # t.foreign_key(:authors) unless t.foreign_key_exists?(:authors)
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  def column_spec_for_primary_key(column)
16
16
  return {} if default_primary_key?(column)
17
17
  spec = { id: schema_type(column).inspect }
18
- spec.merge!(prepare_column_options(column).except!(:null))
18
+ spec.merge!(prepare_column_options(column).except!(:null, :comment))
19
19
  spec[:default] ||= "nil" if explicit_primary_key_default?(column)
20
20
  spec
21
21
  end
@@ -101,7 +101,7 @@ module ActiveRecord
101
101
  def index_exists?(table_name, column_name, options = {})
102
102
  column_names = Array(column_name).map(&:to_s)
103
103
  checks = []
104
- checks << lambda { |i| i.columns == column_names }
104
+ checks << lambda { |i| Array(i.columns) == column_names }
105
105
  checks << lambda { |i| i.unique } if options[:unique]
106
106
  checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
107
107
 
@@ -130,11 +130,11 @@ module ActiveRecord
130
130
  # column_exists?(:suppliers, :name, :string, null: false)
131
131
  # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
132
132
  #
133
- def column_exists?(table_name, column_name, type = nil, options = {})
133
+ def column_exists?(table_name, column_name, type = nil, **options)
134
134
  column_name = column_name.to_s
135
135
  checks = []
136
136
  checks << lambda { |c| c.name == column_name }
137
- checks << lambda { |c| c.type == type } if type
137
+ checks << lambda { |c| c.type == type.to_sym rescue nil } if type
138
138
  column_options_keys.each do |attr|
139
139
  checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
140
140
  end
@@ -302,7 +302,7 @@ module ActiveRecord
302
302
  if pk.is_a?(Array)
303
303
  td.primary_keys pk
304
304
  else
305
- td.primary_key pk, options.fetch(:id, :primary_key), options
305
+ td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
306
306
  end
307
307
  end
308
308
 
@@ -518,14 +518,15 @@ module ActiveRecord
518
518
  # Available options are (none of these exists by default):
519
519
  # * <tt>:limit</tt> -
520
520
  # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
521
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt> and <tt>:integer</tt> columns.
521
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
522
522
  # This option is ignored by some backends.
523
523
  # * <tt>:default</tt> -
524
524
  # The column's default value. Use +nil+ for +NULL+.
525
525
  # * <tt>:null</tt> -
526
526
  # Allows or disallows +NULL+ values in the column.
527
527
  # * <tt>:precision</tt> -
528
- # Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
528
+ # Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
529
+ # <tt>:datetime</tt>, and <tt>:time</tt> columns.
529
530
  # * <tt>:scale</tt> -
530
531
  # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
531
532
  # * <tt>:collation</tt> -
@@ -584,7 +585,7 @@ module ActiveRecord
584
585
  # # Defines a column with a database-specific type.
585
586
  # add_column(:shapes, :triangle, 'polygon')
586
587
  # # ALTER TABLE "shapes" ADD "triangle" polygon
587
- def add_column(table_name, column_name, type, options = {})
588
+ def add_column(table_name, column_name, type, **options)
588
589
  at = create_alter_table table_name
589
590
  at.add_column(column_name, type, options)
590
591
  execute schema_creation.accept at
@@ -770,6 +771,17 @@ module ActiveRecord
770
771
  # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
771
772
  #
772
773
  # Note: only supported by MySQL.
774
+ #
775
+ # ====== Creating an index with a specific algorithm
776
+ #
777
+ # add_index(:developers, :name, algorithm: :concurrently)
778
+ # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
779
+ #
780
+ # Note: only supported by PostgreSQL.
781
+ #
782
+ # Concurrently adding an index is not supported in a transaction.
783
+ #
784
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
773
785
  def add_index(table_name, column_name, options = {})
774
786
  index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
775
787
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
@@ -793,6 +805,15 @@ module ActiveRecord
793
805
  #
794
806
  # remove_index :accounts, name: :by_branch_party
795
807
  #
808
+ # Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
809
+ #
810
+ # remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
811
+ #
812
+ # Note: only supported by PostgreSQL.
813
+ #
814
+ # Concurrently removing an index is not supported in a transaction.
815
+ #
816
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
796
817
  def remove_index(table_name, options = {})
797
818
  index_name = index_name_for_remove(table_name, options)
798
819
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
@@ -852,7 +873,7 @@ module ActiveRecord
852
873
  # [<tt>:null</tt>]
853
874
  # Whether the column allows nulls. Defaults to true.
854
875
  #
855
- # ====== Create a user_id bigint column without a index
876
+ # ====== Create a user_id bigint column without an index
856
877
  #
857
878
  # add_reference(:products, :user, index: false)
858
879
  #
@@ -966,7 +987,7 @@ module ActiveRecord
966
987
  # [<tt>:on_update</tt>]
967
988
  # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
968
989
  # [<tt>:validate</tt>]
969
- # (Postgres only) Specify whether or not the constraint should be validated. Defaults to +true+.
990
+ # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
970
991
  def add_foreign_key(from_table, to_table, options = {})
971
992
  return unless supports_foreign_keys?
972
993
 
@@ -1002,10 +1023,10 @@ module ActiveRecord
1002
1023
  # with an addition of
1003
1024
  # [<tt>:to_table</tt>]
1004
1025
  # The name of the table that contains the referenced primary key.
1005
- def remove_foreign_key(from_table, options_or_to_table = {})
1026
+ def remove_foreign_key(from_table, to_table = nil, **options)
1006
1027
  return unless supports_foreign_keys?
1007
1028
 
1008
- fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
1029
+ fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
1009
1030
 
1010
1031
  at = create_alter_table from_table
1011
1032
  at.drop_foreign_key fk_name_to_delete
@@ -1024,14 +1045,12 @@ module ActiveRecord
1024
1045
  # # Checks to see if a foreign key with a custom name exists.
1025
1046
  # foreign_key_exists?(:accounts, name: "special_fk_name")
1026
1047
  #
1027
- def foreign_key_exists?(from_table, options_or_to_table = {})
1028
- foreign_key_for(from_table, options_or_to_table).present?
1048
+ def foreign_key_exists?(from_table, to_table = nil, **options)
1049
+ foreign_key_for(from_table, to_table: to_table, **options).present?
1029
1050
  end
1030
1051
 
1031
1052
  def foreign_key_column_for(table_name) # :nodoc:
1032
- prefix = Base.table_name_prefix
1033
- suffix = Base.table_name_suffix
1034
- name = table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1053
+ name = strip_table_name_prefix_and_suffix(table_name)
1035
1054
  "#{name.singularize}_id"
1036
1055
  end
1037
1056
 
@@ -1042,8 +1061,8 @@ module ActiveRecord
1042
1061
  options
1043
1062
  end
1044
1063
 
1045
- def dump_schema_information #:nodoc:
1046
- versions = ActiveRecord::SchemaMigration.all_versions
1064
+ def dump_schema_information # :nodoc:
1065
+ versions = schema_migration.all_versions
1047
1066
  insert_versions_sql(versions) if versions.any?
1048
1067
  end
1049
1068
 
@@ -1053,13 +1072,13 @@ module ActiveRecord
1053
1072
 
1054
1073
  def assume_migrated_upto_version(version, migrations_paths = nil)
1055
1074
  unless migrations_paths.nil?
1056
- ActiveSupport::Deprecation.warn(<<~MSG)
1075
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
1057
1076
  Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
1058
1077
  MSG
1059
1078
  end
1060
1079
 
1061
1080
  version = version.to_i
1062
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1081
+ sm_table = quote_table_name(schema_migration.table_name)
1063
1082
 
1064
1083
  migrated = migration_context.get_all_versions
1065
1084
  versions = migration_context.migrations.map(&:version)
@@ -1099,7 +1118,7 @@ module ActiveRecord
1099
1118
  if (0..6) === precision
1100
1119
  column_type_sql << "(#{precision})"
1101
1120
  else
1102
- raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
1121
+ raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
1103
1122
  end
1104
1123
  elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
1105
1124
  column_type_sql << "(#{limit})"
@@ -1129,6 +1148,10 @@ module ActiveRecord
1129
1148
  def add_timestamps(table_name, options = {})
1130
1149
  options[:null] = false if options[:null].nil?
1131
1150
 
1151
+ if !options.key?(:precision) && supports_datetime_with_precision?
1152
+ options[:precision] = 6
1153
+ end
1154
+
1132
1155
  add_column table_name, :created_at, :datetime, options
1133
1156
  add_column table_name, :updated_at, :datetime, options
1134
1157
  end
@@ -1183,12 +1206,22 @@ module ActiveRecord
1183
1206
  end
1184
1207
 
1185
1208
  # Changes the comment for a table or removes it if +nil+.
1186
- def change_table_comment(table_name, comment)
1209
+ #
1210
+ # Passing a hash containing +:from+ and +:to+ will make this change
1211
+ # reversible in migration:
1212
+ #
1213
+ # change_table_comment(:posts, from: "old_comment", to: "new_comment")
1214
+ def change_table_comment(table_name, comment_or_changes)
1187
1215
  raise NotImplementedError, "#{self.class} does not support changing table comments"
1188
1216
  end
1189
1217
 
1190
1218
  # Changes the comment for a column or removes it if +nil+.
1191
- def change_column_comment(table_name, column_name, comment)
1219
+ #
1220
+ # Passing a hash containing +:from+ and +:to+ will make this change
1221
+ # reversible in migration:
1222
+ #
1223
+ # change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
1224
+ def change_column_comment(table_name, column_name, comment_or_changes)
1192
1225
  raise NotImplementedError, "#{self.class} does not support changing column comments"
1193
1226
  end
1194
1227
 
@@ -1290,7 +1323,7 @@ module ActiveRecord
1290
1323
  end
1291
1324
 
1292
1325
  def create_table_definition(*args)
1293
- TableDefinition.new(*args)
1326
+ TableDefinition.new(self, *args)
1294
1327
  end
1295
1328
 
1296
1329
  def create_alter_table(name)
@@ -1324,6 +1357,12 @@ module ActiveRecord
1324
1357
  { column: column_names }
1325
1358
  end
1326
1359
 
1360
+ def strip_table_name_prefix_and_suffix(table_name)
1361
+ prefix = Base.table_name_prefix
1362
+ suffix = Base.table_name_suffix
1363
+ table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1364
+ end
1365
+
1327
1366
  def foreign_key_name(table_name, options)
1328
1367
  options.fetch(:name) do
1329
1368
  identifier = "#{table_name}_#{options.fetch(:column)}_fk"
@@ -1333,14 +1372,14 @@ module ActiveRecord
1333
1372
  end
1334
1373
  end
1335
1374
 
1336
- def foreign_key_for(from_table, options_or_to_table = {})
1375
+ def foreign_key_for(from_table, **options)
1337
1376
  return unless supports_foreign_keys?
1338
- foreign_keys(from_table).detect { |fk| fk.defined_for? options_or_to_table }
1377
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
1339
1378
  end
1340
1379
 
1341
- def foreign_key_for!(from_table, options_or_to_table = {})
1342
- foreign_key_for(from_table, options_or_to_table) || \
1343
- raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}")
1380
+ def foreign_key_for!(from_table, to_table: nil, **options)
1381
+ foreign_key_for(from_table, to_table: to_table, **options) ||
1382
+ raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
1344
1383
  end
1345
1384
 
1346
1385
  def extract_foreign_key_action(specifier)
@@ -1366,11 +1405,37 @@ module ActiveRecord
1366
1405
  default_or_changes
1367
1406
  end
1368
1407
  end
1408
+ alias :extract_new_comment_value :extract_new_default_value
1369
1409
 
1370
1410
  def can_remove_index_by_name?(options)
1371
1411
  options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1372
1412
  end
1373
1413
 
1414
+ def bulk_change_table(table_name, operations)
1415
+ sql_fragments = []
1416
+ non_combinable_operations = []
1417
+
1418
+ operations.each do |command, args|
1419
+ table, arguments = args.shift, args
1420
+ method = :"#{command}_for_alter"
1421
+
1422
+ if respond_to?(method, true)
1423
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1424
+ sql_fragments << sqls
1425
+ non_combinable_operations.concat(procs)
1426
+ else
1427
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1428
+ non_combinable_operations.each(&:call)
1429
+ sql_fragments = []
1430
+ non_combinable_operations = []
1431
+ send(command, table, *arguments)
1432
+ end
1433
+ end
1434
+
1435
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1436
+ non_combinable_operations.each(&:call)
1437
+ end
1438
+
1374
1439
  def add_column_for_alter(table_name, column_name, type, options = {})
1375
1440
  td = create_table_definition(table_name)
1376
1441
  cd = td.new_column_definition(column_name, type, options)
@@ -1386,7 +1451,7 @@ module ActiveRecord
1386
1451
  end
1387
1452
 
1388
1453
  def insert_versions_sql(versions)
1389
- sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1454
+ sm_table = quote_table_name(schema_migration.table_name)
1390
1455
 
1391
1456
  if versions.is_a?(Array)
1392
1457
  sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
@@ -98,9 +98,13 @@ module ActiveRecord
98
98
  end
99
99
 
100
100
  def rollback_records
101
- ite = records.uniq
101
+ ite = records.uniq(&:object_id)
102
+ already_run_callbacks = {}
102
103
  while record = ite.shift
103
- record.rolledback!(force_restore_state: full_rollback?)
104
+ trigger_callbacks = record.trigger_transactional_callbacks?
105
+ should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
106
+ already_run_callbacks[record] ||= trigger_callbacks
107
+ record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
104
108
  end
105
109
  ensure
106
110
  ite.each do |i|
@@ -113,10 +117,14 @@ module ActiveRecord
113
117
  end
114
118
 
115
119
  def commit_records
116
- ite = records.uniq
120
+ ite = records.uniq(&:object_id)
121
+ already_run_callbacks = {}
117
122
  while record = ite.shift
118
123
  if @run_commit_callbacks
119
- record.committed!
124
+ trigger_callbacks = record.trigger_transactional_callbacks?
125
+ should_run_callbacks = !already_run_callbacks[record] && trigger_callbacks
126
+ already_run_callbacks[record] ||= trigger_callbacks
127
+ record.committed!(should_run_callbacks: should_run_callbacks)
120
128
  else
121
129
  # if not running callbacks, only adds the record to the parent transaction
122
130
  connection.add_transaction_record(record)
@@ -205,9 +213,12 @@ module ActiveRecord
205
213
  run_commit_callbacks: run_commit_callbacks)
206
214
  end
207
215
 
208
- transaction.materialize! unless @connection.supports_lazy_transactions? && lazy_transactions_enabled?
216
+ if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && options[:_lazy] != false
217
+ @has_unmaterialized_transactions = true
218
+ else
219
+ transaction.materialize!
220
+ end
209
221
  @stack.push(transaction)
210
- @has_unmaterialized_transactions = true if @connection.supports_lazy_transactions?
211
222
  transaction
212
223
  end
213
224
  end