activerecord 6.0.0 → 6.1.4

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 +1178 -600
  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 +30 -10
  7. data/lib/active_record/associations/alias_tracker.rb +19 -16
  8. data/lib/active_record/associations/association.rb +55 -29
  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 +25 -8
  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 +77 -42
  28. data/lib/active_record/associations/preloader/association.rb +51 -25
  29. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  30. data/lib/active_record/associations/preloader.rb +13 -8
  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 +120 -13
  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 +63 -44
  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 +12 -3
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  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 +141 -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 +76 -79
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  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 +17 -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 +83 -65
  104. data/lib/active_record/connection_adapters/schema_cache.rb +106 -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 +61 -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 +219 -81
  114. data/lib/active_record/core.rb +268 -71
  115. data/lib/active_record/counter_cache.rb +4 -1
  116. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -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 +124 -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 +43 -10
  137. data/lib/active_record/integration.rb +3 -5
  138. data/lib/active_record/internal_metadata.rb +16 -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 +14 -14
  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 +71 -20
  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 +120 -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 +55 -17
  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 +344 -181
  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 -82
  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 +3 -3
  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 +79 -16
  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 +26 -73
  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 +15 -12
  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 +27 -24
  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,20 +44,21 @@ 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
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
50
- ActiveRecord::Base.connection_handler.while_preventing_writes(true) do
51
- instrumenter.instrument("database_selector.active_record.read_from_primary") do
52
- yield
53
- end
53
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
54
+ instrumenter.instrument("database_selector.active_record.read_from_primary") do
55
+ yield
54
56
  end
55
57
  end
56
58
  end
57
59
 
58
60
  def read_from_replica(&blk)
59
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role) do
61
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.reading_role, prevent_writes: true) do
60
62
  instrumenter.instrument("database_selector.active_record.read_from_replica") do
61
63
  yield
62
64
  end
@@ -64,13 +66,11 @@ module ActiveRecord
64
66
  end
65
67
 
66
68
  def write_to_primary(&blk)
67
- ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role) do
68
- ActiveRecord::Base.connection_handler.while_preventing_writes(false) do
69
- instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
70
- yield
71
- ensure
72
- context.update_last_write_timestamp
73
- end
69
+ ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: false) do
70
+ instrumenter.instrument("database_selector.active_record.wrote_to_primary") do
71
+ yield
72
+ ensure
73
+ context.update_last_write_timestamp
74
74
  end
75
75
  end
76
76
  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,62 @@ 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)
60
+ .add_to(connection.update_table_definition(table_name, self))
61
+ end
62
+ alias :add_belongs_to :add_reference
63
+
64
+ private
65
+ def compatible_table_definition(t)
66
+ class << t
67
+ prepend TableDefinition
68
+ end
69
+ t
70
+ end
71
+ end
17
72
 
18
73
  class V5_2 < V6_0
19
74
  module TableDefinition
@@ -29,13 +84,11 @@ module ActiveRecord
29
84
  end
30
85
 
31
86
  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]]
87
+ [:change_column_comment, args]
34
88
  end
35
89
 
36
90
  def invert_change_table_comment(args)
37
- table_name, comment = args
38
- [:change_table_comment, [table_name, from: comment, to: comment]]
91
+ [:change_table_comment, args]
39
92
  end
40
93
  end
41
94
 
@@ -86,9 +139,9 @@ module ActiveRecord
86
139
  end
87
140
 
88
141
  class V5_1 < V5_2
89
- def change_column(table_name, column_name, type, options = {})
142
+ def change_column(table_name, column_name, type, **options)
90
143
  if connection.adapter_name == "PostgreSQL"
91
- super(table_name, column_name, type, options.except(:default, :null, :comment))
144
+ super(table_name, column_name, type, **options.except(:default, :null, :comment))
92
145
  connection.change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
93
146
  connection.change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
94
147
  connection.change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
@@ -97,7 +150,7 @@ module ActiveRecord
97
150
  end
98
151
  end
99
152
 
100
- def create_table(table_name, options = {})
153
+ def create_table(table_name, **options)
101
154
  if connection.adapter_name == "Mysql2"
102
155
  super(table_name, options: "ENGINE=InnoDB", **options)
103
156
  else
@@ -119,7 +172,7 @@ module ActiveRecord
119
172
  alias :belongs_to :references
120
173
  end
121
174
 
122
- def create_table(table_name, options = {})
175
+ def create_table(table_name, **options)
123
176
  if connection.adapter_name == "PostgreSQL"
124
177
  if options[:id] == :uuid && !options.key?(:default)
125
178
  options[:default] = "uuid_generate_v4()"
@@ -147,7 +200,7 @@ module ActiveRecord
147
200
  super
148
201
  end
149
202
 
150
- def add_column(table_name, column_name, type, options = {})
203
+ def add_column(table_name, column_name, type, **options)
151
204
  if type == :primary_key
152
205
  type = :integer
153
206
  options[:primary_key] = true
@@ -194,7 +247,7 @@ module ActiveRecord
194
247
  super
195
248
  end
196
249
 
197
- def index_exists?(table_name, column_name, options = {})
250
+ def index_exists?(table_name, column_name, **options)
198
251
  column_names = Array(column_name).map(&:to_s)
199
252
  options[:name] =
200
253
  if options[:name].present?
@@ -205,10 +258,9 @@ module ActiveRecord
205
258
  super
206
259
  end
207
260
 
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)
261
+ def remove_index(table_name, column_name = nil, **options)
262
+ options[:name] = index_name_for_remove(table_name, column_name, options)
263
+ super
212
264
  end
213
265
 
214
266
  private
@@ -219,13 +271,12 @@ module ActiveRecord
219
271
  super
220
272
  end
221
273
 
222
- def index_name_for_remove(table_name, options = {})
223
- index_name = connection.index_name(table_name, options)
274
+ def index_name_for_remove(table_name, column_name, options)
275
+ index_name = connection.index_name(table_name, column_name || options)
224
276
 
225
277
  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
278
+ if options.key?(:name)
279
+ options_without_column = options.except(:column)
229
280
  index_name_without_column = connection.index_name(table_name, options_without_column)
230
281
 
231
282
  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