activerecord 5.2.8.1 → 6.0.0.beta1

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -816
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +35 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  12. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  14. data/lib/active_record/associations/collection_association.rb +11 -25
  15. data/lib/active_record/associations/collection_proxy.rb +32 -6
  16. data/lib/active_record/associations/foreign_association.rb +7 -0
  17. data/lib/active_record/associations/has_many_association.rb +1 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  19. data/lib/active_record/associations/has_one_association.rb +28 -30
  20. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  21. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  22. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/preloader/association.rb +1 -2
  25. data/lib/active_record/associations/preloader.rb +32 -29
  26. data/lib/active_record/associations/singular_association.rb +2 -16
  27. data/lib/active_record/associations.rb +16 -12
  28. data/lib/active_record/attribute_assignment.rb +7 -10
  29. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  31. data/lib/active_record/attribute_methods/read.rb +16 -48
  32. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  34. data/lib/active_record/attribute_methods/write.rb +15 -16
  35. data/lib/active_record/attribute_methods.rb +34 -56
  36. data/lib/active_record/autosave_association.rb +7 -21
  37. data/lib/active_record/base.rb +2 -2
  38. data/lib/active_record/callbacks.rb +3 -17
  39. data/lib/active_record/coders/yaml_column.rb +1 -13
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +75 -52
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations/database_config.rb +37 -0
  83. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  84. data/lib/active_record/database_configurations/url_config.rb +74 -0
  85. data/lib/active_record/database_configurations.rb +184 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration/command_recorder.rb +35 -5
  102. data/lib/active_record/migration/compatibility.rb +34 -16
  103. data/lib/active_record/migration.rb +38 -37
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -60
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation/batches.rb +13 -10
  116. data/lib/active_record/relation/calculations.rb +38 -28
  117. data/lib/active_record/relation/delegation.rb +4 -13
  118. data/lib/active_record/relation/finder_methods.rb +12 -25
  119. data/lib/active_record/relation/merger.rb +2 -6
  120. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  121. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  122. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  123. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  124. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  125. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  126. data/lib/active_record/relation/predicate_builder.rb +4 -6
  127. data/lib/active_record/relation/query_attribute.rb +15 -12
  128. data/lib/active_record/relation/query_methods.rb +29 -52
  129. data/lib/active_record/relation/where_clause.rb +4 -0
  130. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  131. data/lib/active_record/relation.rb +150 -69
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping/default.rb +10 -3
  138. data/lib/active_record/scoping/named.rb +10 -14
  139. data/lib/active_record/scoping.rb +9 -8
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  153. data/lib/active_record/type.rb +3 -4
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/active_record.rb +2 -1
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes/attribute.rb +37 -0
  160. data/lib/arel/attributes.rb +22 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes/and.rb +32 -0
  174. data/lib/arel/nodes/ascending.rb +23 -0
  175. data/lib/arel/nodes/binary.rb +52 -0
  176. data/lib/arel/nodes/bind_param.rb +36 -0
  177. data/lib/arel/nodes/case.rb +55 -0
  178. data/lib/arel/nodes/casted.rb +50 -0
  179. data/lib/arel/nodes/count.rb +12 -0
  180. data/lib/arel/nodes/delete_statement.rb +45 -0
  181. data/lib/arel/nodes/descending.rb +23 -0
  182. data/lib/arel/nodes/equality.rb +18 -0
  183. data/lib/arel/nodes/extract.rb +24 -0
  184. data/lib/arel/nodes/false.rb +16 -0
  185. data/lib/arel/nodes/full_outer_join.rb +8 -0
  186. data/lib/arel/nodes/function.rb +44 -0
  187. data/lib/arel/nodes/grouping.rb +8 -0
  188. data/lib/arel/nodes/in.rb +8 -0
  189. data/lib/arel/nodes/infix_operation.rb +80 -0
  190. data/lib/arel/nodes/inner_join.rb +8 -0
  191. data/lib/arel/nodes/insert_statement.rb +37 -0
  192. data/lib/arel/nodes/join_source.rb +20 -0
  193. data/lib/arel/nodes/matches.rb +18 -0
  194. data/lib/arel/nodes/named_function.rb +23 -0
  195. data/lib/arel/nodes/node.rb +50 -0
  196. data/lib/arel/nodes/node_expression.rb +13 -0
  197. data/lib/arel/nodes/outer_join.rb +8 -0
  198. data/lib/arel/nodes/over.rb +15 -0
  199. data/lib/arel/nodes/regexp.rb +16 -0
  200. data/lib/arel/nodes/right_outer_join.rb +8 -0
  201. data/lib/arel/nodes/select_core.rb +63 -0
  202. data/lib/arel/nodes/select_statement.rb +41 -0
  203. data/lib/arel/nodes/sql_literal.rb +16 -0
  204. data/lib/arel/nodes/string_join.rb +11 -0
  205. data/lib/arel/nodes/table_alias.rb +27 -0
  206. data/lib/arel/nodes/terminal.rb +16 -0
  207. data/lib/arel/nodes/true.rb +16 -0
  208. data/lib/arel/nodes/unary.rb +44 -0
  209. data/lib/arel/nodes/unary_operation.rb +20 -0
  210. data/lib/arel/nodes/unqualified_column.rb +22 -0
  211. data/lib/arel/nodes/update_statement.rb +41 -0
  212. data/lib/arel/nodes/values.rb +16 -0
  213. data/lib/arel/nodes/values_list.rb +24 -0
  214. data/lib/arel/nodes/window.rb +126 -0
  215. data/lib/arel/nodes/with.rb +11 -0
  216. data/lib/arel/nodes.rb +67 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors/depth_first.rb +199 -0
  224. data/lib/arel/visitors/dot.rb +292 -0
  225. data/lib/arel/visitors/ibm_db.rb +21 -0
  226. data/lib/arel/visitors/informix.rb +56 -0
  227. data/lib/arel/visitors/mssql.rb +143 -0
  228. data/lib/arel/visitors/mysql.rb +83 -0
  229. data/lib/arel/visitors/oracle.rb +159 -0
  230. data/lib/arel/visitors/oracle12.rb +67 -0
  231. data/lib/arel/visitors/postgresql.rb +116 -0
  232. data/lib/arel/visitors/sqlite.rb +39 -0
  233. data/lib/arel/visitors/to_sql.rb +913 -0
  234. data/lib/arel/visitors/visitor.rb +42 -0
  235. data/lib/arel/visitors/where_sql.rb +23 -0
  236. data/lib/arel/visitors.rb +20 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/arel.rb +44 -0
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/migration.rb +14 -1
  241. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  242. metadata +107 -29
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/string/strip"
4
-
5
3
  module ActiveRecord
6
4
  module ConnectionAdapters
7
5
  class AbstractAdapter
@@ -17,14 +15,13 @@ module ActiveRecord
17
15
  end
18
16
 
19
17
  delegate :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
20
- :options_include_default?, :supports_indexes_in_create?, :supports_foreign_keys_in_create?, :foreign_key_options, to: :@conn
21
- private :quote_column_name, :quote_table_name, :quote_default_expression, :type_to_sql,
22
- :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_in_create?, :foreign_key_options,
19
+ to: :@conn, private: true
23
20
 
24
21
  private
25
22
 
26
23
  def visit_AlterTable(o)
27
- sql = "ALTER TABLE #{quote_table_name(o.name)} ".dup
24
+ sql = +"ALTER TABLE #{quote_table_name(o.name)} "
28
25
  sql << o.adds.map { |col| accept col }.join(" ")
29
26
  sql << o.foreign_key_adds.map { |fk| visit_AddForeignKey fk }.join(" ")
30
27
  sql << o.foreign_key_drops.map { |fk| visit_DropForeignKey fk }.join(" ")
@@ -32,17 +29,19 @@ module ActiveRecord
32
29
 
33
30
  def visit_ColumnDefinition(o)
34
31
  o.sql_type = type_to_sql(o.type, o.options)
35
- column_sql = "#{quote_column_name(o.name)} #{o.sql_type}".dup
32
+ column_sql = +"#{quote_column_name(o.name)} #{o.sql_type}"
36
33
  add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
37
34
  column_sql
38
35
  end
39
36
 
40
37
  def visit_AddColumnDefinition(o)
41
- "ADD #{accept(o.column)}".dup
38
+ +"ADD #{accept(o.column)}"
42
39
  end
43
40
 
44
41
  def visit_TableDefinition(o)
45
- create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} ".dup
42
+ create_sql = +"CREATE#{table_modifier_in_create(o)} TABLE "
43
+ create_sql << "IF NOT EXISTS " if o.if_not_exists
44
+ create_sql << "#{quote_table_name(o.name)} "
46
45
 
47
46
  statements = o.columns.map { |c| accept c }
48
47
  statements << accept(o.primary_keys) if o.primary_keys
@@ -66,7 +65,7 @@ module ActiveRecord
66
65
  end
67
66
 
68
67
  def visit_ForeignKeyDefinition(o)
69
- sql = <<-SQL.strip_heredoc
68
+ sql = +<<~SQL
70
69
  CONSTRAINT #{quote_column_name(o.name)}
71
70
  FOREIGN KEY (#{quote_column_name(o.column)})
72
71
  REFERENCES #{quote_table_name(o.to_table)} (#{quote_column_name(o.primary_key)})
@@ -122,6 +121,11 @@ module ActiveRecord
122
121
  sql
123
122
  end
124
123
 
124
+ # Returns any SQL string to go between CREATE and TABLE. May be nil.
125
+ def table_modifier_in_create(o)
126
+ " TEMPORARY" if o.temporary
127
+ end
128
+
125
129
  def foreign_key_in_create(from_table, to_table, options)
126
130
  options = foreign_key_options(from_table, to_table, options)
127
131
  accept ForeignKeyDefinition.new(from_table, to_table, options)
@@ -133,7 +137,7 @@ module ActiveRecord
133
137
  when :cascade then "ON #{action} CASCADE"
134
138
  when :restrict then "ON #{action} RESTRICT"
135
139
  else
136
- raise ArgumentError, <<-MSG.strip_heredoc
140
+ raise ArgumentError, <<~MSG
137
141
  '#{dependency}' is not supported for :on_update or :on_delete.
138
142
  Supported values are: :nullify, :cascade, :restrict
139
143
  MSG
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/deprecation"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters #:nodoc:
5
7
  # Abstract representation of an index definition on a table. Instances of
@@ -101,6 +103,10 @@ module ActiveRecord
101
103
  end
102
104
  alias validated? validate?
103
105
 
106
+ def export_name_on_schema_dump?
107
+ name !~ ActiveRecord::SchemaDumper.fk_ignore_pattern
108
+ end
109
+
104
110
  def defined_for?(to_table_ord = nil, to_table: nil, **options)
105
111
  if to_table_ord
106
112
  self.to_table == to_table_ord.to_s
@@ -151,13 +157,8 @@ module ActiveRecord
151
157
  end
152
158
  end
153
159
 
154
- # TODO Change this to private once we've dropped Ruby 2.2 support.
155
- # Workaround for Ruby 2.2 "private attribute?" warning.
156
- protected
157
-
158
- attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
159
-
160
160
  private
161
+ attr_reader :name, :polymorphic, :index, :foreign_key, :type, :options
161
162
 
162
163
  def as_options(value)
163
164
  value.is_a?(Hash) ? value : {}
@@ -257,15 +258,25 @@ module ActiveRecord
257
258
  class TableDefinition
258
259
  include ColumnMethods
259
260
 
260
- attr_accessor :indexes
261
- attr_reader :name, :temporary, :options, :as, :foreign_keys, :comment
261
+ attr_reader :name, :temporary, :if_not_exists, :options, :as, :comment, :indexes, :foreign_keys
262
+ attr_writer :indexes
263
+ deprecate :indexes=
262
264
 
263
- def initialize(name, temporary = false, options = nil, as = nil, comment: nil)
265
+ def initialize(
266
+ name,
267
+ temporary: false,
268
+ if_not_exists: false,
269
+ options: nil,
270
+ as: nil,
271
+ comment: nil,
272
+ **
273
+ )
264
274
  @columns_hash = {}
265
275
  @indexes = []
266
276
  @foreign_keys = []
267
277
  @primary_keys = nil
268
278
  @temporary = temporary
279
+ @if_not_exists = if_not_exists
269
280
  @options = options
270
281
  @as = as
271
282
  @name = name
@@ -349,16 +360,20 @@ module ActiveRecord
349
360
  #
350
361
  # create_table :taggings do |t|
351
362
  # t.references :tag, index: { name: 'index_taggings_on_tag_id' }
352
- # t.references :tagger, polymorphic: true, index: true
353
- # t.references :taggable, polymorphic: { default: 'Photo' }
363
+ # t.references :tagger, polymorphic: true
364
+ # t.references :taggable, polymorphic: { default: 'Photo' }, index: false
354
365
  # end
355
366
  def column(name, type, options = {})
356
367
  name = name.to_s
357
368
  type = type.to_sym if type
358
369
  options = options.dup
359
370
 
360
- if @columns_hash[name] && @columns_hash[name].primary_key?
361
- raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
371
+ if @columns_hash[name]
372
+ if @columns_hash[name].primary_key?
373
+ raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
374
+ else
375
+ raise ArgumentError, "you can't define an already defined column '#{name}'."
376
+ end
362
377
  end
363
378
 
364
379
  index_options = options.delete(:index)
@@ -524,7 +539,9 @@ module ActiveRecord
524
539
  #
525
540
  # See TableDefinition#column for details of the options you can use.
526
541
  def column(column_name, type, options = {})
542
+ index_options = options.delete(:index)
527
543
  @base.add_column(name, column_name, type, options)
544
+ index(column_name, index_options.is_a?(Hash) ? index_options : {}) if index_options
528
545
  end
529
546
 
530
547
  # Checks to see if a column exists.
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/hash/compact"
4
-
5
3
  module ActiveRecord
6
4
  module ConnectionAdapters # :nodoc:
7
5
  class SchemaDumper < SchemaDumper # :nodoc:
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "active_record/migration/join_table"
4
4
  require "active_support/core_ext/string/access"
5
+ require "active_support/deprecation"
5
6
  require "digest/sha2"
6
7
 
7
8
  module ActiveRecord
@@ -100,7 +101,7 @@ module ActiveRecord
100
101
  def index_exists?(table_name, column_name, options = {})
101
102
  column_names = Array(column_name).map(&:to_s)
102
103
  checks = []
103
- checks << lambda { |i| Array(i.columns) == column_names }
104
+ checks << lambda { |i| i.columns == column_names }
104
105
  checks << lambda { |i| i.unique } if options[:unique]
105
106
  checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
106
107
 
@@ -205,19 +206,22 @@ module ActiveRecord
205
206
  # Set to true to drop the table before creating it.
206
207
  # Set to +:cascade+ to drop dependent objects as well.
207
208
  # Defaults to false.
209
+ # [<tt>:if_not_exists</tt>]
210
+ # Set to true to avoid raising an error when the table already exists.
211
+ # Defaults to false.
208
212
  # [<tt>:as</tt>]
209
213
  # SQL to use to generate the table. When this option is used, the block is
210
214
  # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
211
215
  #
212
216
  # ====== Add a backend specific option to the generated SQL (MySQL)
213
217
  #
214
- # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
218
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
215
219
  #
216
220
  # generates:
217
221
  #
218
222
  # CREATE TABLE suppliers (
219
223
  # id bigint auto_increment PRIMARY KEY
220
- # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
224
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
221
225
  #
222
226
  # ====== Rename the primary key column
223
227
  #
@@ -287,8 +291,8 @@ module ActiveRecord
287
291
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
288
292
  #
289
293
  # See also TableDefinition#column for details on how to create columns.
290
- def create_table(table_name, comment: nil, **options)
291
- td = create_table_definition table_name, options[:temporary], options[:options], options[:as], comment: comment
294
+ def create_table(table_name, **options)
295
+ td = create_table_definition(table_name, options)
292
296
 
293
297
  if options[:id] != false && !options[:as]
294
298
  pk = options.fetch(:primary_key) do
@@ -317,7 +321,9 @@ module ActiveRecord
317
321
  end
318
322
 
319
323
  if supports_comments? && !supports_comments_in_create?
320
- change_table_comment(table_name, comment) if comment.present?
324
+ if table_comment = options[:comment].presence
325
+ change_table_comment(table_name, table_comment)
326
+ end
321
327
 
322
328
  td.columns.each do |column|
323
329
  change_column_comment(table_name, column.name, column.comment) if column.comment.present?
@@ -522,6 +528,9 @@ module ActiveRecord
522
528
  # Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
523
529
  # * <tt>:scale</tt> -
524
530
  # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
531
+ # * <tt>:collation</tt> -
532
+ # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
533
+ # column will have the same collation as the table.
525
534
  # * <tt>:comment</tt> -
526
535
  # Specifies the comment for the column. This option is ignored by some backends.
527
536
  #
@@ -599,6 +608,7 @@ module ActiveRecord
599
608
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
600
609
  # to provide these in a migration's +change+ method so it can be reverted.
601
610
  # In that case, +type+ and +options+ will be used by #add_column.
611
+ # Indexes on the column are automatically removed.
602
612
  def remove_column(table_name, column_name, type = nil, options = {})
603
613
  execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
604
614
  end
@@ -842,17 +852,17 @@ module ActiveRecord
842
852
  # [<tt>:null</tt>]
843
853
  # Whether the column allows nulls. Defaults to true.
844
854
  #
845
- # ====== Create a user_id bigint column
855
+ # ====== Create a user_id bigint column without a index
846
856
  #
847
- # add_reference(:products, :user)
857
+ # add_reference(:products, :user, index: false)
848
858
  #
849
859
  # ====== Create a user_id string column
850
860
  #
851
861
  # add_reference(:products, :user, type: :string)
852
862
  #
853
- # ====== Create supplier_id, supplier_type columns and appropriate index
863
+ # ====== Create supplier_id, supplier_type columns
854
864
  #
855
- # add_reference(:products, :supplier, polymorphic: true, index: true)
865
+ # add_reference(:products, :supplier, polymorphic: true)
856
866
  #
857
867
  # ====== Create a supplier_id column with a unique index
858
868
  #
@@ -880,7 +890,7 @@ module ActiveRecord
880
890
  #
881
891
  # ====== Remove the reference
882
892
  #
883
- # remove_reference(:products, :user, index: true)
893
+ # remove_reference(:products, :user, index: false)
884
894
  #
885
895
  # ====== Remove polymorphic reference
886
896
  #
@@ -888,7 +898,7 @@ module ActiveRecord
888
898
  #
889
899
  # ====== Remove the reference with a foreign key
890
900
  #
891
- # remove_reference(:products, :user, index: true, foreign_key: true)
901
+ # remove_reference(:products, :user, foreign_key: true)
892
902
  #
893
903
  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
894
904
  if foreign_key
@@ -980,11 +990,18 @@ module ActiveRecord
980
990
  #
981
991
  # remove_foreign_key :accounts, column: :owner_id
982
992
  #
993
+ # Removes the foreign key on +accounts.owner_id+.
994
+ #
995
+ # remove_foreign_key :accounts, to_table: :owners
996
+ #
983
997
  # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
984
998
  #
985
999
  # remove_foreign_key :accounts, name: :special_fk_name
986
1000
  #
987
- # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key.
1001
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
1002
+ # with an addition of
1003
+ # [<tt>:to_table</tt>]
1004
+ # The name of the table that contains the referenced primary key.
988
1005
  def remove_foreign_key(from_table, options_or_to_table = {})
989
1006
  return unless supports_foreign_keys?
990
1007
 
@@ -1034,15 +1051,18 @@ module ActiveRecord
1034
1051
  { primary_key: true }
1035
1052
  end
1036
1053
 
1037
- def assume_migrated_upto_version(version, migrations_paths)
1038
- migrations_paths = Array(migrations_paths)
1054
+ def assume_migrated_upto_version(version, migrations_paths = nil)
1055
+ unless migrations_paths.nil?
1056
+ ActiveSupport::Deprecation.warn(<<~MSG)
1057
+ Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
1058
+ MSG
1059
+ end
1060
+
1039
1061
  version = version.to_i
1040
1062
  sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1041
1063
 
1042
- migrated = ActiveRecord::SchemaMigration.all_versions.map(&:to_i)
1043
- versions = migration_context.migration_files.map do |file|
1044
- migration_context.parse_migration_filename(file).first.to_i
1045
- end
1064
+ migrated = migration_context.get_all_versions
1065
+ versions = migration_context.migrations.map(&:version)
1046
1066
 
1047
1067
  unless migrated.include?(version)
1048
1068
  execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
@@ -1053,13 +1073,7 @@ module ActiveRecord
1053
1073
  if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
1054
1074
  raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
1055
1075
  end
1056
- if supports_multi_insert?
1057
- execute insert_versions_sql(inserting)
1058
- else
1059
- inserting.each do |v|
1060
- execute insert_versions_sql(v)
1061
- end
1062
- end
1076
+ execute insert_versions_sql(inserting)
1063
1077
  end
1064
1078
  end
1065
1079
 
@@ -1375,7 +1389,7 @@ module ActiveRecord
1375
1389
  sm_table = quote_table_name(ActiveRecord::SchemaMigration.table_name)
1376
1390
 
1377
1391
  if versions.is_a?(Array)
1378
- sql = "INSERT INTO #{sm_table} (version) VALUES\n".dup
1392
+ sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1379
1393
  sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1380
1394
  sql << ";\n\n"
1381
1395
  sql
@@ -40,24 +40,6 @@ module ActiveRecord
40
40
  committed? || rolledback?
41
41
  end
42
42
 
43
- def set_state(state)
44
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
45
- The set_state method is deprecated and will be removed in
46
- Rails 6.0. Please use rollback! or commit! to set transaction
47
- state directly.
48
- MSG
49
- case state
50
- when :rolledback
51
- rollback!
52
- when :committed
53
- commit!
54
- when nil
55
- nullify!
56
- else
57
- raise ArgumentError, "Invalid transaction state: #{state}"
58
- end
59
- end
60
-
61
43
  def rollback!
62
44
  @children.each { |c| c.rollback! }
63
45
  @state = :rolledback
@@ -91,13 +73,14 @@ module ActiveRecord
91
73
  end
92
74
 
93
75
  class Transaction #:nodoc:
94
- attr_reader :connection, :state, :records, :savepoint_name
95
- attr_writer :joinable
76
+ attr_reader :connection, :state, :records, :savepoint_name, :isolation_level
96
77
 
97
78
  def initialize(connection, options, run_commit_callbacks: false)
98
79
  @connection = connection
99
80
  @state = TransactionState.new
100
81
  @records = []
82
+ @isolation_level = options[:isolation]
83
+ @materialized = false
101
84
  @joinable = options.fetch(:joinable, true)
102
85
  @run_commit_callbacks = run_commit_callbacks
103
86
  end
@@ -106,6 +89,14 @@ module ActiveRecord
106
89
  records << record
107
90
  end
108
91
 
92
+ def materialize!
93
+ @materialized = true
94
+ end
95
+
96
+ def materialized?
97
+ @materialized
98
+ end
99
+
109
100
  def rollback_records
110
101
  ite = records.uniq
111
102
  while record = ite.shift
@@ -128,7 +119,7 @@ module ActiveRecord
128
119
  record.committed!
129
120
  else
130
121
  # if not running callbacks, only adds the record to the parent transaction
131
- record.add_to_transaction
122
+ connection.add_transaction_record(record)
132
123
  end
133
124
  end
134
125
  ensure
@@ -142,24 +133,30 @@ module ActiveRecord
142
133
  end
143
134
 
144
135
  class SavepointTransaction < Transaction
145
- def initialize(connection, savepoint_name, parent_transaction, options, *args)
146
- super(connection, options, *args)
136
+ def initialize(connection, savepoint_name, parent_transaction, *args)
137
+ super(connection, *args)
147
138
 
148
139
  parent_transaction.state.add_child(@state)
149
140
 
150
- if options[:isolation]
141
+ if isolation_level
151
142
  raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
152
143
  end
153
- connection.create_savepoint(@savepoint_name = savepoint_name)
144
+
145
+ @savepoint_name = savepoint_name
146
+ end
147
+
148
+ def materialize!
149
+ connection.create_savepoint(savepoint_name)
150
+ super
154
151
  end
155
152
 
156
153
  def rollback
157
- connection.rollback_to_savepoint(savepoint_name)
154
+ connection.rollback_to_savepoint(savepoint_name) if materialized?
158
155
  @state.rollback!
159
156
  end
160
157
 
161
158
  def commit
162
- connection.release_savepoint(savepoint_name)
159
+ connection.release_savepoint(savepoint_name) if materialized?
163
160
  @state.commit!
164
161
  end
165
162
 
@@ -167,22 +164,23 @@ module ActiveRecord
167
164
  end
168
165
 
169
166
  class RealTransaction < Transaction
170
- def initialize(connection, options, *args)
171
- super
172
- if options[:isolation]
173
- connection.begin_isolated_db_transaction(options[:isolation])
167
+ def materialize!
168
+ if isolation_level
169
+ connection.begin_isolated_db_transaction(isolation_level)
174
170
  else
175
171
  connection.begin_db_transaction
176
172
  end
173
+
174
+ super
177
175
  end
178
176
 
179
177
  def rollback
180
- connection.rollback_db_transaction
178
+ connection.rollback_db_transaction if materialized?
181
179
  @state.full_rollback!
182
180
  end
183
181
 
184
182
  def commit
185
- connection.commit_db_transaction
183
+ connection.commit_db_transaction if materialized?
186
184
  @state.full_commit!
187
185
  end
188
186
  end
@@ -191,6 +189,9 @@ module ActiveRecord
191
189
  def initialize(connection)
192
190
  @stack = []
193
191
  @connection = connection
192
+ @has_unmaterialized_transactions = false
193
+ @materializing_transactions = false
194
+ @lazy_transactions_enabled = true
194
195
  end
195
196
 
196
197
  def begin_transaction(options = {})
@@ -204,11 +205,41 @@ module ActiveRecord
204
205
  run_commit_callbacks: run_commit_callbacks)
205
206
  end
206
207
 
208
+ transaction.materialize! unless @connection.supports_lazy_transactions? && lazy_transactions_enabled?
207
209
  @stack.push(transaction)
210
+ @has_unmaterialized_transactions = true if @connection.supports_lazy_transactions?
208
211
  transaction
209
212
  end
210
213
  end
211
214
 
215
+ def disable_lazy_transactions!
216
+ materialize_transactions
217
+ @lazy_transactions_enabled = false
218
+ end
219
+
220
+ def enable_lazy_transactions!
221
+ @lazy_transactions_enabled = true
222
+ end
223
+
224
+ def lazy_transactions_enabled?
225
+ @lazy_transactions_enabled
226
+ end
227
+
228
+ def materialize_transactions
229
+ return if @materializing_transactions
230
+ return unless @has_unmaterialized_transactions
231
+
232
+ @connection.lock.synchronize do
233
+ begin
234
+ @materializing_transactions = true
235
+ @stack.each { |t| t.materialize! unless t.materialized? }
236
+ ensure
237
+ @materializing_transactions = false
238
+ end
239
+ @has_unmaterialized_transactions = false
240
+ end
241
+ end
242
+
212
243
  def commit_transaction
213
244
  @connection.lock.synchronize do
214
245
  transaction = @stack.last
@@ -234,26 +265,24 @@ module ActiveRecord
234
265
 
235
266
  def within_new_transaction(options = {})
236
267
  @connection.lock.synchronize do
237
- begin
238
- transaction = begin_transaction options
239
- yield
240
- rescue Exception => error
241
- if transaction
268
+ transaction = begin_transaction options
269
+ yield
270
+ rescue Exception => error
271
+ if transaction
272
+ rollback_transaction
273
+ after_failure_actions(transaction, error)
274
+ end
275
+ raise
276
+ ensure
277
+ if !error && transaction
278
+ if Thread.current.status == "aborting"
242
279
  rollback_transaction
243
- after_failure_actions(transaction, error)
244
- end
245
- raise
246
- ensure
247
- unless error
248
- if Thread.current.status == "aborting"
249
- rollback_transaction if transaction
250
- else
251
- begin
252
- commit_transaction if transaction
253
- rescue Exception
254
- rollback_transaction(transaction) unless transaction.state.completed?
255
- raise
256
- end
280
+ else
281
+ begin
282
+ commit_transaction
283
+ rescue Exception
284
+ rollback_transaction(transaction) unless transaction.state.completed?
285
+ raise
257
286
  end
258
287
  end
259
288
  end