activerecord 6.0.1 → 6.1.7

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1314 -633
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record/aggregations.rb +5 -6
  6. data/lib/active_record/association_relation.rb +26 -15
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -37
  9. data/lib/active_record/associations/association_scope.rb +19 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  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 -3
  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 +38 -13
  20. data/lib/active_record/associations/collection_proxy.rb +14 -7
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -3
  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 +39 -16
  26. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  27. data/lib/active_record/associations/join_dependency.rb +73 -42
  28. data/lib/active_record/associations/preloader/association.rb +49 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +12 -7
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +119 -12
  34. data/lib/active_record/attribute_assignment.rb +10 -9
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  36. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  37. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  38. data/lib/active_record/attribute_methods/query.rb +3 -6
  39. data/lib/active_record/attribute_methods/read.rb +8 -12
  40. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  42. data/lib/active_record/attribute_methods/write.rb +12 -21
  43. data/lib/active_record/attribute_methods.rb +64 -54
  44. data/lib/active_record/attributes.rb +33 -9
  45. data/lib/active_record/autosave_association.rb +56 -41
  46. data/lib/active_record/base.rb +2 -14
  47. data/lib/active_record/callbacks.rb +153 -24
  48. data/lib/active_record/coders/yaml_column.rb +24 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +190 -136
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -9
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  54. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +152 -116
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +145 -52
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +94 -36
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +63 -77
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +136 -111
  62. data/lib/active_record/connection_adapters/column.rb +15 -1
  63. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  64. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  65. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  67. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  68. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  69. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  70. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  71. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  72. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -13
  73. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  74. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  75. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  76. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  77. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  78. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  79. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  81. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +0 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  87. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  92. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  101. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  102. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql_adapter.rb +80 -66
  104. data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
  105. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  106. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  107. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  108. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  109. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  110. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -57
  111. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  112. data/lib/active_record/connection_adapters.rb +52 -0
  113. data/lib/active_record/connection_handling.rb +218 -87
  114. data/lib/active_record/core.rb +269 -68
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  117. data/lib/active_record/database_configurations/database_config.rb +52 -9
  118. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  119. data/lib/active_record/database_configurations/url_config.rb +15 -41
  120. data/lib/active_record/database_configurations.rb +125 -85
  121. data/lib/active_record/delegated_type.rb +209 -0
  122. data/lib/active_record/destroy_association_async_job.rb +36 -0
  123. data/lib/active_record/dynamic_matchers.rb +2 -3
  124. data/lib/active_record/enum.rb +80 -38
  125. data/lib/active_record/errors.rb +47 -12
  126. data/lib/active_record/explain.rb +9 -5
  127. data/lib/active_record/explain_subscriber.rb +1 -1
  128. data/lib/active_record/fixture_set/file.rb +10 -17
  129. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  130. data/lib/active_record/fixture_set/render_context.rb +1 -1
  131. data/lib/active_record/fixture_set/table_row.rb +2 -3
  132. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  133. data/lib/active_record/fixtures.rb +58 -12
  134. data/lib/active_record/gem_version.rb +2 -2
  135. data/lib/active_record/inheritance.rb +40 -21
  136. data/lib/active_record/insert_all.rb +42 -9
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +18 -7
  139. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  140. data/lib/active_record/locking/optimistic.rb +33 -18
  141. data/lib/active_record/locking/pessimistic.rb +6 -2
  142. data/lib/active_record/log_subscriber.rb +28 -9
  143. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  144. data/lib/active_record/middleware/database_selector/resolver.rb +6 -2
  145. data/lib/active_record/middleware/database_selector.rb +4 -2
  146. data/lib/active_record/migration/command_recorder.rb +53 -45
  147. data/lib/active_record/migration/compatibility.rb +75 -21
  148. data/lib/active_record/migration/join_table.rb +0 -1
  149. data/lib/active_record/migration.rb +115 -85
  150. data/lib/active_record/model_schema.rb +117 -15
  151. data/lib/active_record/nested_attributes.rb +2 -5
  152. data/lib/active_record/no_touching.rb +1 -1
  153. data/lib/active_record/null_relation.rb +0 -1
  154. data/lib/active_record/persistence.rb +50 -46
  155. data/lib/active_record/query_cache.rb +15 -5
  156. data/lib/active_record/querying.rb +12 -7
  157. data/lib/active_record/railtie.rb +65 -45
  158. data/lib/active_record/railties/console_sandbox.rb +2 -4
  159. data/lib/active_record/railties/databases.rake +280 -99
  160. data/lib/active_record/readonly_attributes.rb +4 -0
  161. data/lib/active_record/reflection.rb +77 -63
  162. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  163. data/lib/active_record/relation/batches.rb +38 -32
  164. data/lib/active_record/relation/calculations.rb +106 -45
  165. data/lib/active_record/relation/delegation.rb +9 -7
  166. data/lib/active_record/relation/finder_methods.rb +45 -16
  167. data/lib/active_record/relation/from_clause.rb +5 -1
  168. data/lib/active_record/relation/merger.rb +27 -26
  169. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  170. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  171. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  172. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  173. data/lib/active_record/relation/predicate_builder.rb +59 -40
  174. data/lib/active_record/relation/query_methods.rb +339 -188
  175. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  176. data/lib/active_record/relation/spawn_methods.rb +8 -8
  177. data/lib/active_record/relation/where_clause.rb +111 -62
  178. data/lib/active_record/relation.rb +116 -83
  179. data/lib/active_record/result.rb +41 -34
  180. data/lib/active_record/runtime_registry.rb +2 -2
  181. data/lib/active_record/sanitization.rb +6 -17
  182. data/lib/active_record/schema_dumper.rb +34 -4
  183. data/lib/active_record/schema_migration.rb +2 -8
  184. data/lib/active_record/scoping/default.rb +1 -4
  185. data/lib/active_record/scoping/named.rb +7 -18
  186. data/lib/active_record/scoping.rb +0 -1
  187. data/lib/active_record/secure_token.rb +16 -8
  188. data/lib/active_record/serialization.rb +5 -3
  189. data/lib/active_record/signed_id.rb +116 -0
  190. data/lib/active_record/statement_cache.rb +20 -4
  191. data/lib/active_record/store.rb +9 -4
  192. data/lib/active_record/suppressor.rb +2 -2
  193. data/lib/active_record/table_metadata.rb +42 -36
  194. data/lib/active_record/tasks/database_tasks.rb +140 -113
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  197. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  198. data/lib/active_record/test_databases.rb +5 -4
  199. data/lib/active_record/test_fixtures.rb +87 -20
  200. data/lib/active_record/timestamp.rb +4 -7
  201. data/lib/active_record/touch_later.rb +20 -21
  202. data/lib/active_record/transactions.rb +25 -72
  203. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  204. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  205. data/lib/active_record/type/serialized.rb +6 -3
  206. data/lib/active_record/type/time.rb +10 -0
  207. data/lib/active_record/type/type_map.rb +0 -1
  208. data/lib/active_record/type/unsigned_integer.rb +0 -1
  209. data/lib/active_record/type.rb +8 -2
  210. data/lib/active_record/type_caster/connection.rb +0 -1
  211. data/lib/active_record/type_caster/map.rb +8 -5
  212. data/lib/active_record/validations/associated.rb +1 -2
  213. data/lib/active_record/validations/numericality.rb +35 -0
  214. data/lib/active_record/validations/uniqueness.rb +24 -4
  215. data/lib/active_record/validations.rb +3 -3
  216. data/lib/active_record.rb +7 -13
  217. data/lib/arel/attributes/attribute.rb +4 -0
  218. data/lib/arel/collectors/bind.rb +5 -0
  219. data/lib/arel/collectors/composite.rb +8 -0
  220. data/lib/arel/collectors/sql_string.rb +7 -0
  221. data/lib/arel/collectors/substitute_binds.rb +7 -0
  222. data/lib/arel/nodes/binary.rb +82 -8
  223. data/lib/arel/nodes/bind_param.rb +8 -0
  224. data/lib/arel/nodes/casted.rb +21 -9
  225. data/lib/arel/nodes/equality.rb +6 -9
  226. data/lib/arel/nodes/grouping.rb +3 -0
  227. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  228. data/lib/arel/nodes/in.rb +8 -1
  229. data/lib/arel/nodes/infix_operation.rb +13 -1
  230. data/lib/arel/nodes/join_source.rb +1 -1
  231. data/lib/arel/nodes/node.rb +7 -6
  232. data/lib/arel/nodes/ordering.rb +27 -0
  233. data/lib/arel/nodes/sql_literal.rb +3 -0
  234. data/lib/arel/nodes/table_alias.rb +7 -3
  235. data/lib/arel/nodes/unary.rb +0 -1
  236. data/lib/arel/nodes.rb +3 -1
  237. data/lib/arel/predications.rb +17 -24
  238. data/lib/arel/select_manager.rb +1 -2
  239. data/lib/arel/table.rb +13 -5
  240. data/lib/arel/visitors/dot.rb +14 -3
  241. data/lib/arel/visitors/mysql.rb +11 -1
  242. data/lib/arel/visitors/postgresql.rb +15 -5
  243. data/lib/arel/visitors/sqlite.rb +0 -1
  244. data/lib/arel/visitors/to_sql.rb +89 -79
  245. data/lib/arel/visitors/visitor.rb +0 -1
  246. data/lib/arel/visitors.rb +0 -7
  247. data/lib/arel.rb +5 -9
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  250. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  251. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  252. data/lib/rails/generators/active_record/migration.rb +6 -2
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +30 -29
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -60,6 +60,15 @@ module ActiveRecord
60
60
  self.class.locking_enabled?
61
61
  end
62
62
 
63
+ def increment!(*, **) #:nodoc:
64
+ super.tap do
65
+ if locking_enabled?
66
+ self[self.class.locking_column] += 1
67
+ clear_attribute_change(self.class.locking_column)
68
+ end
69
+ end
70
+ end
71
+
63
72
  private
64
73
  def _create_record(attribute_names = self.attribute_names)
65
74
  if locking_enabled?
@@ -80,7 +89,10 @@ module ActiveRecord
80
89
 
81
90
  begin
82
91
  locking_column = self.class.locking_column
83
- previous_lock_value = read_attribute_before_type_cast(locking_column)
92
+ lock_attribute_was = @attributes[locking_column]
93
+ lock_value_for_database = _lock_value_for_database(locking_column)
94
+
95
+ attribute_names = attribute_names.dup if attribute_names.frozen?
84
96
  attribute_names << locking_column
85
97
 
86
98
  self[locking_column] += 1
@@ -88,7 +100,7 @@ module ActiveRecord
88
100
  affected_rows = self.class._update_record(
89
101
  attributes_with_values(attribute_names),
90
102
  @primary_key => id_in_database,
91
- locking_column => previous_lock_value
103
+ locking_column => lock_value_for_database
92
104
  )
93
105
 
94
106
  if affected_rows != 1
@@ -99,7 +111,7 @@ module ActiveRecord
99
111
 
100
112
  # If something went wrong, revert the locking_column value.
101
113
  rescue Exception
102
- self[locking_column] = previous_lock_value.to_i
114
+ @attributes[locking_column] = lock_attribute_was
103
115
  raise
104
116
  end
105
117
  end
@@ -111,7 +123,7 @@ module ActiveRecord
111
123
 
112
124
  affected_rows = self.class._delete_record(
113
125
  @primary_key => id_in_database,
114
- locking_column => read_attribute_before_type_cast(locking_column)
126
+ locking_column => _lock_value_for_database(locking_column)
115
127
  )
116
128
 
117
129
  if affected_rows != 1
@@ -121,6 +133,14 @@ module ActiveRecord
121
133
  affected_rows
122
134
  end
123
135
 
136
+ def _lock_value_for_database(locking_column)
137
+ if will_save_change_to_attribute?(locking_column)
138
+ @attributes[locking_column].value_for_database
139
+ else
140
+ @attributes[locking_column].original_value_for_database
141
+ end
142
+ end
143
+
124
144
  module ClassMethods
125
145
  DEFAULT_LOCKING_COLUMN = "lock_version"
126
146
 
@@ -155,21 +175,12 @@ module ActiveRecord
155
175
  super
156
176
  end
157
177
 
158
- private
159
-
160
- # We need to apply this decorator here, rather than on module inclusion. The closure
161
- # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
162
- # sub class being decorated. As such, changes to `lock_optimistically`, or
163
- # `locking_column` would not be picked up.
164
- def inherited(subclass)
165
- subclass.class_eval do
166
- is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
167
- decorate_matching_attribute_types(is_lock_column, "_optimistic_locking") do |type|
168
- LockingType.new(type)
169
- end
170
- end
171
- super
178
+ def define_attribute(name, cast_type, **) # :nodoc:
179
+ if lock_optimistically && name == locking_column
180
+ cast_type = LockingType.new(cast_type)
172
181
  end
182
+ super
183
+ end
173
184
  end
174
185
  end
175
186
 
@@ -177,6 +188,10 @@ module ActiveRecord
177
188
  # `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
178
189
  # during update record.
179
190
  class LockingType < DelegateClass(Type::Value) # :nodoc:
191
+ def self.new(subtype)
192
+ self === subtype ? subtype : super
193
+ end
194
+
180
195
  def deserialize(value)
181
196
  super.to_i
182
197
  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,16 @@ 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[:reflection].klass
26
+ name = event.payload[:reflection].name
27
+
28
+ color("Strict loading violation: #{owner} is marked for strict loading. The #{association} association named :#{name} cannot be lazily loaded.", RED)
29
+ end
30
+ end
31
+
22
32
  def sql(event)
23
33
  self.class.runtime += event.duration
24
34
  return unless logger.debug?
@@ -32,15 +42,19 @@ module ActiveRecord
32
42
  sql = payload[:sql]
33
43
  binds = nil
34
44
 
35
- unless (payload[:binds] || []).empty?
45
+ if payload[:binds]&.any?
36
46
  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
47
+
48
+ binds = []
49
+ payload[:binds].each_with_index do |attr, i|
50
+ binds << render_bind(attr, casted_params[i])
51
+ end
52
+ binds = binds.inspect
53
+ binds.prepend(" ")
40
54
  end
41
55
 
42
56
  name = colorize_payload_name(name, payload[:name])
43
- sql = color(sql, sql_color(sql), true)
57
+ sql = color(sql, sql_color(sql), true) if colorize_logging
44
58
 
45
59
  debug " #{name} #{sql}#{binds}"
46
60
  end
@@ -51,13 +65,18 @@ module ActiveRecord
51
65
  end
52
66
 
53
67
  def render_bind(attr, value)
54
- if attr.is_a?(Array)
68
+ case attr
69
+ when ActiveModel::Attribute
70
+ if attr.type.binary? && attr.value
71
+ value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
72
+ end
73
+ when Array
55
74
  attr = attr.first
56
- elsif attr.type.binary? && attr.value
57
- value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
75
+ else
76
+ attr = nil
58
77
  end
59
78
 
60
- [attr && attr.name, value]
79
+ [attr&.name, value]
61
80
  end
62
81
 
63
82
  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,8 +44,11 @@ module ActiveRecord
43
44
  write_to_primary(&blk)
44
45
  end
45
46
 
46
- private
47
+ def update_context(response)
48
+ context.save(response)
49
+ end
47
50
 
51
+ private
48
52
  def read_from_primary(&blk)
49
53
  ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
50
54
  instrumenter.instrument("database_selector.active_record.read_from_primary") do
@@ -54,7 +58,7 @@ module ActiveRecord
54
58
  end
55
59
 
56
60
  def read_from_replica(&blk)
57
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role) do
61
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role, prevent_writes: true) do
58
62
  instrumenter.instrument("database_selector.active_record.read_from_replica") do
59
63
  yield
60
64
  end
@@ -55,16 +55,18 @@ module ActiveRecord
55
55
  end
56
56
 
57
57
  private
58
-
59
58
  def select_database(request, &blk)
60
59
  context = context_klass.call(request)
61
60
  resolver = resolver_klass.call(context, options)
62
61
 
63
- if reading_request?(request)
62
+ response = if reading_request?(request)
64
63
  resolver.read(&blk)
65
64
  else
66
65
  resolver.write(&blk)
67
66
  end
67
+
68
+ resolver.update_context(response)
69
+ response
68
70
  end
69
71
 
70
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
@@ -32,13 +34,15 @@ module ActiveRecord
32
34
  # * rename_index
33
35
  # * rename_table
34
36
  class CommandRecorder
35
- ReversibleAndIrreversibleMethods = [:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
37
+ ReversibleAndIrreversibleMethods = [
38
+ :create_table, :create_join_table, :rename_table, :add_column, :remove_column,
36
39
  :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
37
40
  :change_column_default, :add_reference, :remove_reference, :transaction,
38
41
  :drop_join_table, :drop_table, :execute_block, :enable_extension, :disable_extension,
39
42
  :change_column, :execute, :remove_columns, :change_column_null,
40
43
  :add_foreign_key, :remove_foreign_key,
41
- :change_column_comment, :change_table_comment
44
+ :change_column_comment, :change_table_comment,
45
+ :add_check_constraint, :remove_check_constraint
42
46
  ]
43
47
  include JoinTable
44
48
 
@@ -84,6 +88,11 @@ module ActiveRecord
84
88
  # recorder.inverse_of(:rename_table, [:old, :new])
85
89
  # # => [:rename_table, [:new, :old]]
86
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
+ #
87
96
  # This method will raise an +IrreversibleMigration+ exception if it cannot
88
97
  # invert the +command+.
89
98
  def inverse_of(command, args, &block)
@@ -103,11 +112,12 @@ module ActiveRecord
103
112
  record(:"#{method}", args, &block) # record(:create_table, args, &block)
104
113
  end # end
105
114
  EOV
115
+ ruby2_keywords(method) if respond_to?(:ruby2_keywords, true)
106
116
  end
107
117
  alias :add_belongs_to :add_reference
108
118
  alias :remove_belongs_to :remove_reference
109
119
 
110
- def change_table(table_name, options = {}) # :nodoc:
120
+ def change_table(table_name, **options) # :nodoc:
111
121
  yield delegate.update_table_definition(table_name, self)
112
122
  end
113
123
 
@@ -118,7 +128,6 @@ module ActiveRecord
118
128
  end
119
129
 
120
130
  private
121
-
122
131
  module StraightReversions # :nodoc:
123
132
  private
124
133
  {
@@ -126,8 +135,11 @@ module ActiveRecord
126
135
  create_table: :drop_table,
127
136
  create_join_table: :drop_join_table,
128
137
  add_column: :remove_column,
138
+ add_index: :remove_index,
129
139
  add_timestamps: :remove_timestamps,
130
140
  add_reference: :remove_reference,
141
+ add_foreign_key: :remove_foreign_key,
142
+ add_check_constraint: :remove_check_constraint,
131
143
  enable_extension: :disable_extension
132
144
  }.each do |cmd, inv|
133
145
  [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
@@ -169,44 +181,49 @@ module ActiveRecord
169
181
  super
170
182
  end
171
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
+
172
192
  def invert_rename_index(args)
173
- [: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]]
174
195
  end
175
196
 
176
197
  def invert_rename_column(args)
177
- [: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]]
178
200
  end
179
201
 
180
- def invert_add_index(args)
181
- table, columns, options = *args
182
- options ||= {}
183
-
184
- options_hash = options.slice(:name, :algorithm)
185
- options_hash[:column] = columns if !options_hash[:name]
202
+ def invert_remove_index(args)
203
+ options = args.extract_options!
204
+ table, columns = args
186
205
 
187
- [:remove_index, [table, options_hash]]
188
- end
206
+ columns ||= options.delete(:column)
189
207
 
190
- def invert_remove_index(args)
191
- table, options_or_column = *args
192
- if (options = options_or_column).is_a?(Hash)
193
- unless options[:column]
194
- raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
195
- end
196
- options = options.dup
197
- [:add_index, [table, options.delete(:column), options]]
198
- elsif (column = options_or_column).present?
199
- [:add_index, [table, column]]
208
+ unless columns
209
+ raise ActiveRecord::IrreversibleMigration, "remove_index is only reversible if given a :column option."
200
210
  end
211
+
212
+ options.delete(:if_exists)
213
+
214
+ args = [table, columns]
215
+ args << options unless options.empty?
216
+
217
+ [:add_index, args]
201
218
  end
202
219
 
203
220
  alias :invert_add_belongs_to :invert_add_reference
204
221
  alias :invert_remove_belongs_to :invert_remove_reference
205
222
 
206
223
  def invert_change_column_default(args)
207
- table, column, options = *args
224
+ table, column, options = args
208
225
 
209
- 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)
210
227
  raise ActiveRecord::IrreversibleMigration, "change_column_default is only reversible if given a :from and :to option."
211
228
  end
212
229
 
@@ -218,21 +235,6 @@ module ActiveRecord
218
235
  [:change_column_null, args]
219
236
  end
220
237
 
221
- def invert_add_foreign_key(args)
222
- from_table, to_table, add_options = args
223
- add_options ||= {}
224
-
225
- if add_options[:name]
226
- options = { name: add_options[:name] }
227
- elsif add_options[:column]
228
- options = { column: add_options[:column] }
229
- else
230
- options = to_table
231
- end
232
-
233
- [:remove_foreign_key, [from_table, options]]
234
- end
235
-
236
238
  def invert_remove_foreign_key(args)
237
239
  options = args.extract_options!
238
240
  from_table, to_table = args
@@ -248,9 +250,9 @@ module ActiveRecord
248
250
  end
249
251
 
250
252
  def invert_change_column_comment(args)
251
- table, column, options = *args
253
+ table, column, options = args
252
254
 
253
- 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)
254
256
  raise ActiveRecord::IrreversibleMigration, "change_column_comment is only reversible if given a :from and :to option."
255
257
  end
256
258
 
@@ -258,15 +260,20 @@ module ActiveRecord
258
260
  end
259
261
 
260
262
  def invert_change_table_comment(args)
261
- table, options = *args
263
+ table, options = args
262
264
 
263
- 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)
264
266
  raise ActiveRecord::IrreversibleMigration, "change_table_comment is only reversible if given a :from and :to option."
265
267
  end
266
268
 
267
269
  [:change_table_comment, [table, from: options[:to], to: options[:from]]]
268
270
  end
269
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
+
270
277
  def respond_to_missing?(method, _)
271
278
  super || delegate.respond_to?(method)
272
279
  end
@@ -279,6 +286,7 @@ module ActiveRecord
279
286
  super
280
287
  end
281
288
  end
289
+ ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
282
290
  end
283
291
  end
284
292
  end
@@ -13,7 +13,65 @@ 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
+ options[:_uses_legacy_reference_index_name] = true
28
+ super
29
+ end
30
+ alias :belongs_to :references
31
+ end
32
+
33
+ def create_table(table_name, **options)
34
+ if block_given?
35
+ super { |t| yield compatible_table_definition(t) }
36
+ else
37
+ super
38
+ end
39
+ end
40
+
41
+ def change_table(table_name, **options)
42
+ if block_given?
43
+ super { |t| yield compatible_table_definition(t) }
44
+ else
45
+ super
46
+ end
47
+ end
48
+
49
+ def create_join_table(table_1, table_2, **options)
50
+ if block_given?
51
+ super { |t| yield compatible_table_definition(t) }
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ def add_reference(table_name, ref_name, **options)
58
+ if connection.adapter_name == "SQLite"
59
+ options[:type] = :integer
60
+ end
61
+
62
+ options[:_uses_legacy_reference_index_name] = true
63
+ super
64
+ end
65
+ alias :add_belongs_to :add_reference
66
+
67
+ private
68
+ def compatible_table_definition(t)
69
+ class << t
70
+ prepend TableDefinition
71
+ end
72
+ t
73
+ end
74
+ end
17
75
 
18
76
  class V5_2 < V6_0
19
77
  module TableDefinition
@@ -29,13 +87,11 @@ module ActiveRecord
29
87
  end
30
88
 
31
89
  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]]
90
+ [:change_column_comment, args]
34
91
  end
35
92
 
36
93
  def invert_change_table_comment(args)
37
- table_name, comment = args
38
- [:change_table_comment, [table_name, from: comment, to: comment]]
94
+ [:change_table_comment, args]
39
95
  end
40
96
  end
41
97
 
@@ -73,7 +129,7 @@ module ActiveRecord
73
129
  class << t
74
130
  prepend TableDefinition
75
131
  end
76
- t
132
+ super
77
133
  end
78
134
 
79
135
  def command_recorder
@@ -86,9 +142,9 @@ module ActiveRecord
86
142
  end
87
143
 
88
144
  class V5_1 < V5_2
89
- def change_column(table_name, column_name, type, options = {})
145
+ def change_column(table_name, column_name, type, **options)
90
146
  if connection.adapter_name == "PostgreSQL"
91
- super(table_name, column_name, type, options.except(:default, :null, :comment))
147
+ super(table_name, column_name, type, **options.except(:default, :null, :comment))
92
148
  connection.change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
93
149
  connection.change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
94
150
  connection.change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
@@ -97,7 +153,7 @@ module ActiveRecord
97
153
  end
98
154
  end
99
155
 
100
- def create_table(table_name, options = {})
156
+ def create_table(table_name, **options)
101
157
  if connection.adapter_name == "Mysql2"
102
158
  super(table_name, options: "ENGINE=InnoDB", **options)
103
159
  else
@@ -119,7 +175,7 @@ module ActiveRecord
119
175
  alias :belongs_to :references
120
176
  end
121
177
 
122
- def create_table(table_name, options = {})
178
+ def create_table(table_name, **options)
123
179
  if connection.adapter_name == "PostgreSQL"
124
180
  if options[:id] == :uuid && !options.key?(:default)
125
181
  options[:default] = "uuid_generate_v4()"
@@ -147,7 +203,7 @@ module ActiveRecord
147
203
  super
148
204
  end
149
205
 
150
- def add_column(table_name, column_name, type, options = {})
206
+ def add_column(table_name, column_name, type, **options)
151
207
  if type == :primary_key
152
208
  type = :integer
153
209
  options[:primary_key] = true
@@ -194,7 +250,7 @@ module ActiveRecord
194
250
  super
195
251
  end
196
252
 
197
- def index_exists?(table_name, column_name, options = {})
253
+ def index_exists?(table_name, column_name, **options)
198
254
  column_names = Array(column_name).map(&:to_s)
199
255
  options[:name] =
200
256
  if options[:name].present?
@@ -205,10 +261,9 @@ module ActiveRecord
205
261
  super
206
262
  end
207
263
 
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)
264
+ def remove_index(table_name, column_name = nil, **options)
265
+ options[:name] = index_name_for_remove(table_name, column_name, options)
266
+ super
212
267
  end
213
268
 
214
269
  private
@@ -219,13 +274,12 @@ module ActiveRecord
219
274
  super
220
275
  end
221
276
 
222
- def index_name_for_remove(table_name, options = {})
223
- index_name = connection.index_name(table_name, options)
277
+ def index_name_for_remove(table_name, column_name, options)
278
+ index_name = connection.index_name(table_name, column_name || options)
224
279
 
225
280
  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
281
+ if options.key?(:name)
282
+ options_without_column = options.except(:column)
229
283
  index_name_without_column = connection.index_name(table_name, options_without_column)
230
284
 
231
285
  if connection.index_name_exists?(table_name, index_name_without_column)
@@ -4,7 +4,6 @@ module ActiveRecord
4
4
  class Migration
5
5
  module JoinTable #:nodoc:
6
6
  private
7
-
8
7
  def find_join_table_name(table_1, table_2, options = {})
9
8
  options.delete(:table_name) || join_table_name(table_1, table_2)
10
9
  end