activerecord 6.0.4.8 → 6.1.0.rc1

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 +764 -883
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +22 -14
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +39 -27
  9. data/lib/active_record/associations/association_scope.rb +11 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +19 -13
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +29 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +13 -5
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +114 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +4 -4
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +52 -48
  43. data/lib/active_record/attributes.rb +27 -7
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +32 -22
  47. data/lib/active_record/coders/yaml_column.rb +1 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +180 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -70
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -10
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  91. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  93. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  94. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  95. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
  96. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  98. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  99. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  100. data/lib/active_record/connection_adapters.rb +50 -0
  101. data/lib/active_record/connection_handling.rb +210 -71
  102. data/lib/active_record/core.rb +215 -49
  103. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  104. data/lib/active_record/database_configurations/database_config.rb +52 -9
  105. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  106. data/lib/active_record/database_configurations/url_config.rb +15 -40
  107. data/lib/active_record/database_configurations.rb +124 -85
  108. data/lib/active_record/delegated_type.rb +209 -0
  109. data/lib/active_record/destroy_association_async_job.rb +36 -0
  110. data/lib/active_record/enum.rb +33 -23
  111. data/lib/active_record/errors.rb +47 -12
  112. data/lib/active_record/explain.rb +9 -4
  113. data/lib/active_record/explain_subscriber.rb +1 -1
  114. data/lib/active_record/fixture_set/file.rb +10 -17
  115. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  116. data/lib/active_record/fixture_set/render_context.rb +1 -1
  117. data/lib/active_record/fixture_set/table_row.rb +2 -2
  118. data/lib/active_record/fixtures.rb +54 -8
  119. data/lib/active_record/gem_version.rb +3 -3
  120. data/lib/active_record/inheritance.rb +40 -18
  121. data/lib/active_record/insert_all.rb +32 -5
  122. data/lib/active_record/integration.rb +3 -5
  123. data/lib/active_record/internal_metadata.rb +15 -4
  124. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  125. data/lib/active_record/locking/optimistic.rb +13 -16
  126. data/lib/active_record/locking/pessimistic.rb +6 -2
  127. data/lib/active_record/log_subscriber.rb +26 -8
  128. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  129. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/migration/command_recorder.rb +47 -27
  132. data/lib/active_record/migration/compatibility.rb +67 -17
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/model_schema.rb +88 -42
  135. data/lib/active_record/nested_attributes.rb +2 -3
  136. data/lib/active_record/no_touching.rb +1 -1
  137. data/lib/active_record/persistence.rb +50 -45
  138. data/lib/active_record/query_cache.rb +15 -5
  139. data/lib/active_record/querying.rb +11 -6
  140. data/lib/active_record/railtie.rb +64 -44
  141. data/lib/active_record/railties/databases.rake +253 -98
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +59 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +100 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -2
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/query_methods.rb +319 -196
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +6 -5
  158. data/lib/active_record/relation/where_clause.rb +104 -57
  159. data/lib/active_record/relation.rb +90 -64
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +0 -4
  165. data/lib/active_record/scoping/named.rb +1 -17
  166. data/lib/active_record/secure_token.rb +16 -8
  167. data/lib/active_record/serialization.rb +5 -3
  168. data/lib/active_record/signed_id.rb +116 -0
  169. data/lib/active_record/statement_cache.rb +20 -4
  170. data/lib/active_record/store.rb +2 -2
  171. data/lib/active_record/suppressor.rb +2 -2
  172. data/lib/active_record/table_metadata.rb +36 -52
  173. data/lib/active_record/tasks/database_tasks.rb +139 -113
  174. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  175. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  176. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  177. data/lib/active_record/test_databases.rb +5 -4
  178. data/lib/active_record/test_fixtures.rb +36 -33
  179. data/lib/active_record/timestamp.rb +4 -6
  180. data/lib/active_record/touch_later.rb +21 -21
  181. data/lib/active_record/transactions.rb +15 -64
  182. data/lib/active_record/type/serialized.rb +6 -2
  183. data/lib/active_record/type.rb +8 -1
  184. data/lib/active_record/type_caster/connection.rb +0 -1
  185. data/lib/active_record/type_caster/map.rb +8 -5
  186. data/lib/active_record/validations/associated.rb +1 -1
  187. data/lib/active_record/validations/numericality.rb +35 -0
  188. data/lib/active_record/validations/uniqueness.rb +24 -4
  189. data/lib/active_record/validations.rb +1 -0
  190. data/lib/active_record.rb +7 -14
  191. data/lib/arel/attributes/attribute.rb +4 -0
  192. data/lib/arel/collectors/bind.rb +5 -0
  193. data/lib/arel/collectors/composite.rb +8 -0
  194. data/lib/arel/collectors/sql_string.rb +7 -0
  195. data/lib/arel/collectors/substitute_binds.rb +7 -0
  196. data/lib/arel/nodes/binary.rb +82 -8
  197. data/lib/arel/nodes/bind_param.rb +8 -0
  198. data/lib/arel/nodes/casted.rb +21 -9
  199. data/lib/arel/nodes/equality.rb +6 -9
  200. data/lib/arel/nodes/grouping.rb +3 -0
  201. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  202. data/lib/arel/nodes/in.rb +8 -1
  203. data/lib/arel/nodes/infix_operation.rb +13 -1
  204. data/lib/arel/nodes/join_source.rb +1 -1
  205. data/lib/arel/nodes/node.rb +7 -6
  206. data/lib/arel/nodes/ordering.rb +27 -0
  207. data/lib/arel/nodes/sql_literal.rb +3 -0
  208. data/lib/arel/nodes/table_alias.rb +7 -3
  209. data/lib/arel/nodes/unary.rb +0 -1
  210. data/lib/arel/nodes.rb +3 -1
  211. data/lib/arel/predications.rb +12 -18
  212. data/lib/arel/select_manager.rb +1 -2
  213. data/lib/arel/table.rb +13 -5
  214. data/lib/arel/visitors/dot.rb +14 -2
  215. data/lib/arel/visitors/mysql.rb +11 -1
  216. data/lib/arel/visitors/postgresql.rb +15 -4
  217. data/lib/arel/visitors/to_sql.rb +89 -78
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel.rb +5 -13
  220. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  221. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  222. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  225. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  226. metadata +30 -31
  227. data/lib/active_record/advisory_lock_base.rb +0 -18
  228. data/lib/active_record/attribute_decorators.rb +0 -88
  229. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  230. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  231. data/lib/active_record/define_callbacks.rb +0 -22
  232. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  233. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  234. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  235. data/lib/arel/attributes.rb +0 -22
  236. data/lib/arel/visitors/depth_first.rb +0 -203
  237. data/lib/arel/visitors/ibm_db.rb +0 -34
  238. data/lib/arel/visitors/informix.rb +0 -62
  239. data/lib/arel/visitors/mssql.rb +0 -156
  240. data/lib/arel/visitors/oracle.rb +0 -158
  241. data/lib/arel/visitors/oracle12.rb +0 -65
  242. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/enumerable"
4
+
3
5
  module ActiveRecord
4
6
  class InsertAll # :nodoc:
5
7
  attr_reader :model, :connection, :inserts, :keys
@@ -8,13 +10,19 @@ module ActiveRecord
8
10
  def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
9
11
  raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
10
12
 
11
- @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s).to_set
13
+ @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
12
14
  @on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
13
15
 
16
+ if model.scope_attributes?
17
+ @scope_attributes = model.scope_attributes
18
+ @keys |= @scope_attributes.keys
19
+ end
20
+ @keys = @keys.to_set
21
+
14
22
  @returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
15
23
  @returning = false if @returning == []
16
24
 
17
- @unique_by = find_unique_index_for(unique_by) if unique_by
25
+ @unique_by = find_unique_index_for(unique_by)
18
26
  @on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
19
27
 
20
28
  ensure_valid_options_for_connection!
@@ -32,7 +40,7 @@ module ActiveRecord
32
40
  end
33
41
 
34
42
  def primary_keys
35
- Array(model.primary_key)
43
+ Array(connection.schema_cache.primary_keys(model.table_name))
36
44
  end
37
45
 
38
46
 
@@ -47,6 +55,8 @@ module ActiveRecord
47
55
  def map_key_with_value
48
56
  inserts.map do |attributes|
49
57
  attributes = attributes.stringify_keys
58
+ attributes.merge!(scope_attributes) if scope_attributes
59
+
50
60
  verify_attributes(attributes)
51
61
 
52
62
  keys.map do |key|
@@ -56,13 +66,18 @@ module ActiveRecord
56
66
  end
57
67
 
58
68
  private
69
+ attr_reader :scope_attributes
70
+
59
71
  def find_unique_index_for(unique_by)
60
- match = Array(unique_by).map(&:to_s)
72
+ name_or_columns = unique_by || model.primary_key
73
+ match = Array(name_or_columns).map(&:to_s)
61
74
 
62
75
  if index = unique_indexes.find { |i| match.include?(i.name) || i.columns == match }
63
76
  index
77
+ elsif match == primary_keys
78
+ unique_by.nil? ? nil : ActiveRecord::ConnectionAdapters::IndexDefinition.new(model.table_name, "#{model.table_name}_primary_key", true, match)
64
79
  else
65
- raise ArgumentError, "No unique index found for #{unique_by}"
80
+ raise ArgumentError, "No unique index found for #{name_or_columns}"
66
81
  end
67
82
  end
68
83
 
@@ -151,9 +166,21 @@ module ActiveRecord
151
166
  quote_columns(insert_all.updatable_columns)
152
167
  end
153
168
 
169
+ def touch_model_timestamps_unless(&block)
170
+ model.send(:timestamp_attributes_for_update_in_model).map do |column_name|
171
+ if touch_timestamp_attribute?(column_name)
172
+ "#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE CURRENT_TIMESTAMP END),"
173
+ end
174
+ end.compact.join
175
+ end
176
+
154
177
  private
155
178
  attr_reader :connection, :insert_all
156
179
 
180
+ def touch_timestamp_attribute?(column_name)
181
+ update_duplicates? && !insert_all.updatable_columns.include?(column_name)
182
+ end
183
+
157
184
  def columns_list
158
185
  format_columns(insert_all.keys)
159
186
  end
@@ -93,7 +93,7 @@ module ActiveRecord
93
93
  # cache_version, but this method can be overwritten to return something else.
94
94
  #
95
95
  # Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
96
- # +false+ (which it is by default until Rails 6.0).
96
+ # +false+.
97
97
  def cache_version
98
98
  return unless cache_versioning
99
99
 
@@ -104,10 +104,8 @@ module ActiveRecord
104
104
  elsif timestamp = updated_at
105
105
  timestamp.utc.to_s(cache_timestamp_format)
106
106
  end
107
- else
108
- if self.class.has_attribute?("updated_at")
109
- raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
110
- end
107
+ elsif self.class.has_attribute?("updated_at")
108
+ raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
111
109
  end
112
110
  end
113
111
 
@@ -6,8 +6,15 @@ require "active_record/scoping/named"
6
6
  module ActiveRecord
7
7
  # This class is used to create a table that keeps track of values and keys such
8
8
  # as which environment migrations were run in.
9
+ #
10
+ # This is enabled by default. To disable this functionality set
11
+ # `use_metadata_table` to false in your database configuration.
9
12
  class InternalMetadata < ActiveRecord::Base # :nodoc:
10
13
  class << self
14
+ def enabled?
15
+ ActiveRecord::Base.connection.use_metadata_table?
16
+ end
17
+
11
18
  def _internal?
12
19
  true
13
20
  end
@@ -21,19 +28,21 @@ module ActiveRecord
21
28
  end
22
29
 
23
30
  def []=(key, value)
31
+ return unless enabled?
32
+
24
33
  find_or_initialize_by(key: key).update!(value: value)
25
34
  end
26
35
 
27
36
  def [](key)
28
- where(key: key).pluck(:value).first
29
- end
37
+ return unless enabled?
30
38
 
31
- def table_exists?
32
- connection.table_exists?(table_name)
39
+ where(key: key).pluck(:value).first
33
40
  end
34
41
 
35
42
  # Creates an internal metadata table with columns +key+ and +value+
36
43
  def create_table
44
+ return unless enabled?
45
+
37
46
  unless table_exists?
38
47
  key_options = connection.internal_string_options_for_primary_key
39
48
 
@@ -46,6 +55,8 @@ module ActiveRecord
46
55
  end
47
56
 
48
57
  def drop_table
58
+ return unless enabled?
59
+
49
60
  connection.drop_table table_name, if_exists: true
50
61
  end
51
62
  end
@@ -1,13 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- module LegacyYamlAdapter
4
+ module LegacyYamlAdapter # :nodoc:
5
5
  def self.convert(klass, coder)
6
6
  return coder unless coder.is_a?(Psych::Coder)
7
7
 
8
8
  case coder["active_record_yaml_version"]
9
9
  when 1, 2 then coder
10
10
  else
11
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
12
+ YAML loading from legacy format older than Rails 5.0 is deprecated
13
+ and will be removed in Rails 6.2.
14
+ MSG
11
15
  if coder["attributes"].is_a?(ActiveModel::AttributeSet)
12
16
  Rails420.convert(klass, coder)
13
17
  else
@@ -16,7 +20,7 @@ module ActiveRecord
16
20
  end
17
21
  end
18
22
 
19
- module Rails420
23
+ module Rails420 # :nodoc:
20
24
  def self.convert(klass, coder)
21
25
  attribute_set = coder["attributes"]
22
26
 
@@ -32,7 +36,7 @@ module ActiveRecord
32
36
  end
33
37
  end
34
38
 
35
- module Rails41
39
+ module Rails41 # :nodoc:
36
40
  def self.convert(klass, coder)
37
41
  attributes = klass.attributes_builder
38
42
  .build_from_database(coder["attributes"])
@@ -89,7 +89,8 @@ module ActiveRecord
89
89
 
90
90
  begin
91
91
  locking_column = self.class.locking_column
92
- previous_lock_value = read_attribute_before_type_cast(locking_column)
92
+ previous_lock_value = attribute_before_type_cast(locking_column)
93
+ attribute_names = attribute_names.dup if attribute_names.frozen?
93
94
  attribute_names << locking_column
94
95
 
95
96
  self[locking_column] += 1
@@ -97,7 +98,7 @@ module ActiveRecord
97
98
  affected_rows = self.class._update_record(
98
99
  attributes_with_values(attribute_names),
99
100
  @primary_key => id_in_database,
100
- locking_column => previous_lock_value
101
+ locking_column => @attributes[locking_column].original_value_for_database
101
102
  )
102
103
 
103
104
  if affected_rows != 1
@@ -120,7 +121,7 @@ module ActiveRecord
120
121
 
121
122
  affected_rows = self.class._delete_record(
122
123
  @primary_key => id_in_database,
123
- locking_column => read_attribute_before_type_cast(locking_column)
124
+ locking_column => attribute_before_type_cast(locking_column)
124
125
  )
125
126
 
126
127
  if affected_rows != 1
@@ -164,20 +165,12 @@ module ActiveRecord
164
165
  super
165
166
  end
166
167
 
167
- private
168
- # We need to apply this decorator here, rather than on module inclusion. The closure
169
- # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
170
- # sub class being decorated. As such, changes to `lock_optimistically`, or
171
- # `locking_column` would not be picked up.
172
- def inherited(subclass)
173
- subclass.class_eval do
174
- is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
175
- decorate_matching_attribute_types(is_lock_column, "_optimistic_locking") do |type|
176
- LockingType.new(type)
177
- end
178
- end
179
- super
168
+ def define_attribute(name, cast_type, **) # :nodoc:
169
+ if lock_optimistically && name == locking_column
170
+ cast_type = LockingType.new(cast_type)
180
171
  end
172
+ super
173
+ end
181
174
  end
182
175
  end
183
176
 
@@ -185,6 +178,10 @@ module ActiveRecord
185
178
  # `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
186
179
  # during update record.
187
180
  class LockingType < DelegateClass(Type::Value) # :nodoc:
181
+ def self.new(subtype)
182
+ self === subtype ? subtype : super
183
+ end
184
+
188
185
  def deserialize(value)
189
186
  super.to_i
190
187
  end
@@ -53,8 +53,12 @@ module ActiveRecord
53
53
  # end
54
54
  #
55
55
  # Database-specific information on row locking:
56
- # MySQL: https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
57
- # PostgreSQL: https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
56
+ #
57
+ # [MySQL]
58
+ # https://dev.mysql.com/doc/refman/en/innodb-locking-reads.html
59
+ #
60
+ # [PostgreSQL]
61
+ # https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
58
62
  module Pessimistic
59
63
  # Obtain a row lock on this record. Reloads the record to obtain the requested
60
64
  # lock. Pass an SQL locking clause to append the end of the SELECT statement
@@ -19,6 +19,15 @@ module ActiveRecord
19
19
  rt
20
20
  end
21
21
 
22
+ def strict_loading_violation(event)
23
+ debug do
24
+ owner = event.payload[:owner]
25
+ association = event.payload[:association]
26
+
27
+ color("Strict loading violation: #{association} lazily loaded on #{owner}.", RED)
28
+ end
29
+ end
30
+
22
31
  def sql(event)
23
32
  self.class.runtime += event.duration
24
33
  return unless logger.debug?
@@ -32,11 +41,15 @@ module ActiveRecord
32
41
  sql = payload[:sql]
33
42
  binds = nil
34
43
 
35
- unless (payload[:binds] || []).empty?
44
+ if payload[:binds]&.any?
36
45
  casted_params = type_casted_binds(payload[:type_casted_binds])
37
- binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
38
- render_bind(attr, value)
39
- }.inspect
46
+
47
+ binds = []
48
+ payload[:binds].each_with_index do |attr, i|
49
+ binds << render_bind(attr, casted_params[i])
50
+ end
51
+ binds = binds.inspect
52
+ binds.prepend(" ")
40
53
  end
41
54
 
42
55
  name = colorize_payload_name(name, payload[:name])
@@ -51,13 +64,18 @@ module ActiveRecord
51
64
  end
52
65
 
53
66
  def render_bind(attr, value)
54
- if attr.is_a?(Array)
67
+ case attr
68
+ when ActiveModel::Attribute
69
+ if attr.type.binary? && attr.value
70
+ value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
71
+ end
72
+ when Array
55
73
  attr = attr.first
56
- elsif attr.type.binary? && attr.value
57
- value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
74
+ else
75
+ attr = nil
58
76
  end
59
77
 
60
- [attr && attr.name, value]
78
+ [attr&.name, value]
61
79
  end
62
80
 
63
81
  def colorize_payload_name(name, payload_name)
@@ -38,6 +38,9 @@ module ActiveRecord
38
38
  def update_last_write_timestamp
39
39
  session[:last_write] = self.class.convert_time_to_timestamp(Time.now)
40
40
  end
41
+
42
+ def save(response)
43
+ end
41
44
  end
42
45
  end
43
46
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_record/middleware/database_selector/resolver/session"
4
+ require "active_support/core_ext/numeric/time"
4
5
 
5
6
  module ActiveRecord
6
7
  module Middleware
@@ -43,6 +44,10 @@ module ActiveRecord
43
44
  write_to_primary(&blk)
44
45
  end
45
46
 
47
+ def update_context(response)
48
+ context.save(response)
49
+ end
50
+
46
51
  private
47
52
  def read_from_primary(&blk)
48
53
  ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
@@ -59,11 +59,14 @@ module ActiveRecord
59
59
  context = context_klass.call(request)
60
60
  resolver = resolver_klass.call(context, options)
61
61
 
62
- if reading_request?(request)
62
+ response = if reading_request?(request)
63
63
  resolver.read(&blk)
64
64
  else
65
65
  resolver.write(&blk)
66
66
  end
67
+
68
+ resolver.update_context(response)
69
+ response
67
70
  end
68
71
 
69
72
  def reading_request?(request)
@@ -8,6 +8,7 @@ module ActiveRecord
8
8
  #
9
9
  # * add_column
10
10
  # * add_foreign_key
11
+ # * add_check_constraint
11
12
  # * add_index
12
13
  # * add_reference
13
14
  # * add_timestamps
@@ -25,6 +26,7 @@ module ActiveRecord
25
26
  # * remove_column (must supply a type)
26
27
  # * remove_columns (must specify at least one column name or more)
27
28
  # * remove_foreign_key (must supply a second table)
29
+ # * remove_check_constraint
28
30
  # * remove_index
29
31
  # * remove_reference
30
32
  # * remove_timestamps
@@ -39,7 +41,8 @@ module ActiveRecord
39
41
  :drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension,
40
42
  :change_column, :execute, :remove_columns, :change_column_null,
41
43
  :add_foreign_key, :remove_foreign_key,
42
- :change_column_comment, :change_table_comment
44
+ :change_column_comment, :change_table_comment,
45
+ :add_check_constraint, :remove_check_constraint
43
46
  ]
44
47
  include JoinTable
45
48
 
@@ -85,6 +88,11 @@ module ActiveRecord
85
88
  # recorder.inverse_of(:rename_table, [:old, :new])
86
89
  # # => [:rename_table, [:new, :old]]
87
90
  #
91
+ # If the inverse of a command requires several commands, returns array of commands.
92
+ #
93
+ # recorder.inverse_of(:remove_columns, [:some_table, :foo, :bar, type: :string])
94
+ # # => [[:add_column, :some_table, :foo, :string], [:add_column, :some_table, :bar, :string]]
95
+ #
88
96
  # This method will raise an +IrreversibleMigration+ exception if it cannot
89
97
  # invert the +command+.
90
98
  def inverse_of(command, args, &block)
@@ -127,9 +135,11 @@ module ActiveRecord
127
135
  create_table: :drop_table,
128
136
  create_join_table: :drop_join_table,
129
137
  add_column: :remove_column,
138
+ add_index: :remove_index,
130
139
  add_timestamps: :remove_timestamps,
131
140
  add_reference: :remove_reference,
132
141
  add_foreign_key: :remove_foreign_key,
142
+ add_check_constraint: :remove_check_constraint,
133
143
  enable_extension: :disable_extension
134
144
  }.each do |cmd, inv|
135
145
  [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
@@ -171,44 +181,49 @@ module ActiveRecord
171
181
  super
172
182
  end
173
183
 
184
+ def invert_remove_columns(args)
185
+ unless args[-1].is_a?(Hash) && args[-1].has_key?(:type)
186
+ raise ActiveRecord::IrreversibleMigration, "remove_columns is only reversible if given a type."
187
+ end
188
+
189
+ [:add_columns, args]
190
+ end
191
+
174
192
  def invert_rename_index(args)
175
- [:rename_index, [args.first] + args.last(2).reverse]
193
+ table_name, old_name, new_name = args
194
+ [:rename_index, [table_name, new_name, old_name]]
176
195
  end
177
196
 
178
197
  def invert_rename_column(args)
179
- [:rename_column, [args.first] + args.last(2).reverse]
198
+ table_name, old_name, new_name = args
199
+ [:rename_column, [table_name, new_name, old_name]]
180
200
  end
181
201
 
182
- def invert_add_index(args)
183
- table, columns, options = *args
184
- options ||= {}
185
-
186
- options_hash = options.slice(:name, :algorithm)
187
- options_hash[:column] = columns if !options_hash[:name]
202
+ def invert_remove_index(args)
203
+ options = args.extract_options!
204
+ table, columns = args
188
205
 
189
- [:remove_index, [table, options_hash]]
190
- end
206
+ columns ||= options.delete(:column)
191
207
 
192
- def invert_remove_index(args)
193
- table, options_or_column = *args
194
- if (options = options_or_column).is_a?(Hash)
195
- unless options[:column]
196
- raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
197
- end
198
- options = options.dup
199
- [:add_index, [table, options.delete(:column), options]]
200
- elsif (column = options_or_column).present?
201
- [:add_index, [table, column]]
208
+ unless columns
209
+ raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
202
210
  end
211
+
212
+ options.delete(:if_exists)
213
+
214
+ args = [table, columns]
215
+ args << options unless options.empty?
216
+
217
+ [:add_index, args]
203
218
  end
204
219
 
205
220
  alias :invert_add_belongs_to :invert_add_reference
206
221
  alias :invert_remove_belongs_to :invert_remove_reference
207
222
 
208
223
  def invert_change_column_default(args)
209
- table, column, options = *args
224
+ table, column, options = args
210
225
 
211
- unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
226
+ unless options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
212
227
  raise ActiveRecord::IrreversibleMigration, "change_column_default is only reversible if given a :from and :to option."
213
228
  end
214
229
 
@@ -235,9 +250,9 @@ module ActiveRecord
235
250
  end
236
251
 
237
252
  def invert_change_column_comment(args)
238
- table, column, options = *args
253
+ table, column, options = args
239
254
 
240
- unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
255
+ unless options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
241
256
  raise ActiveRecord::IrreversibleMigration, "change_column_comment is only reversible if given a :from and :to option."
242
257
  end
243
258
 
@@ -245,15 +260,20 @@ module ActiveRecord
245
260
  end
246
261
 
247
262
  def invert_change_table_comment(args)
248
- table, options = *args
263
+ table, options = args
249
264
 
250
- unless options && options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
265
+ unless options.is_a?(Hash) && options.has_key?(:from) && options.has_key?(:to)
251
266
  raise ActiveRecord::IrreversibleMigration, "change_table_comment is only reversible if given a :from and :to option."
252
267
  end
253
268
 
254
269
  [:change_table_comment, [table, from: options[:to], to: options[:from]]]
255
270
  end
256
271
 
272
+ def invert_remove_check_constraint(args)
273
+ raise ActiveRecord::IrreversibleMigration, "remove_check_constraint is only reversible if given an expression." if args.size < 2
274
+ super
275
+ end
276
+
257
277
  def respond_to_missing?(method, _)
258
278
  super || delegate.respond_to?(method)
259
279
  end
@@ -13,7 +13,61 @@ module ActiveRecord
13
13
  const_get(name)
14
14
  end
15
15
 
16
- V6_0 = Current
16
+ V6_1 = Current
17
+
18
+ class V6_0 < V6_1
19
+ class ReferenceDefinition < ConnectionAdapters::ReferenceDefinition
20
+ def index_options(table_name)
21
+ as_options(index)
22
+ end
23
+ end
24
+
25
+ module TableDefinition
26
+ def references(*args, **options)
27
+ args.each do |ref_name|
28
+ ReferenceDefinition.new(ref_name, **options).add_to(self)
29
+ end
30
+ end
31
+ alias :belongs_to :references
32
+ end
33
+
34
+ def create_table(table_name, **options)
35
+ if block_given?
36
+ super { |t| yield compatible_table_definition(t) }
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ def change_table(table_name, **options)
43
+ if block_given?
44
+ super { |t| yield compatible_table_definition(t) }
45
+ else
46
+ super
47
+ end
48
+ end
49
+
50
+ def create_join_table(table_1, table_2, **options)
51
+ if block_given?
52
+ super { |t| yield compatible_table_definition(t) }
53
+ else
54
+ super
55
+ end
56
+ end
57
+
58
+ def add_reference(table_name, ref_name, **options)
59
+ ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
60
+ end
61
+ alias :add_belongs_to :add_reference
62
+
63
+ private
64
+ def compatible_table_definition(t)
65
+ class << t
66
+ prepend TableDefinition
67
+ end
68
+ t
69
+ end
70
+ end
17
71
 
18
72
  class V5_2 < V6_0
19
73
  module TableDefinition
@@ -29,13 +83,11 @@ module ActiveRecord
29
83
  end
30
84
 
31
85
  def invert_change_column_comment(args)
32
- table_name, column_name, comment = args
33
- [:change_column_comment, [table_name, column_name, from: comment, to: comment]]
86
+ [:change_column_comment, args]
34
87
  end
35
88
 
36
89
  def invert_change_table_comment(args)
37
- table_name, comment = args
38
- [:change_table_comment, [table_name, from: comment, to: comment]]
90
+ [:change_table_comment, args]
39
91
  end
40
92
  end
41
93
 
@@ -86,9 +138,9 @@ module ActiveRecord
86
138
  end
87
139
 
88
140
  class V5_1 < V5_2
89
- def change_column(table_name, column_name, type, options = {})
141
+ def change_column(table_name, column_name, type, **options)
90
142
  if connection.adapter_name == "PostgreSQL"
91
- super(table_name, column_name, type, options.except(:default, :null, :comment))
143
+ super(table_name, column_name, type, **options.except(:default, :null, :comment))
92
144
  connection.change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
93
145
  connection.change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
94
146
  connection.change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
@@ -194,7 +246,7 @@ module ActiveRecord
194
246
  super
195
247
  end
196
248
 
197
- def index_exists?(table_name, column_name, options = {})
249
+ def index_exists?(table_name, column_name, **options)
198
250
  column_names = Array(column_name).map(&:to_s)
199
251
  options[:name] =
200
252
  if options[:name].present?
@@ -205,10 +257,9 @@ module ActiveRecord
205
257
  super
206
258
  end
207
259
 
208
- def remove_index(table_name, options = {})
209
- options = { column: options } unless options.is_a?(Hash)
210
- options[:name] = index_name_for_remove(table_name, options)
211
- super(table_name, options)
260
+ def remove_index(table_name, column_name = nil, **options)
261
+ options[:name] = index_name_for_remove(table_name, column_name, options)
262
+ super
212
263
  end
213
264
 
214
265
  private
@@ -219,13 +270,12 @@ module ActiveRecord
219
270
  super
220
271
  end
221
272
 
222
- def index_name_for_remove(table_name, options = {})
223
- index_name = connection.index_name(table_name, options)
273
+ def index_name_for_remove(table_name, column_name, options)
274
+ index_name = connection.index_name(table_name, column_name || options)
224
275
 
225
276
  unless connection.index_name_exists?(table_name, index_name)
226
- if options.is_a?(Hash) && options.has_key?(:name)
227
- options_without_column = options.dup
228
- options_without_column.delete :column
277
+ if options.key?(:name)
278
+ options_without_column = options.except(:column)
229
279
  index_name_without_column = connection.index_name(table_name, options_without_column)
230
280
 
231
281
  if connection.index_name_exists?(table_name, index_name_without_column)