activerecord 5.2.8.1 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -816
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +35 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  12. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  14. data/lib/active_record/associations/collection_association.rb +11 -25
  15. data/lib/active_record/associations/collection_proxy.rb +32 -6
  16. data/lib/active_record/associations/foreign_association.rb +7 -0
  17. data/lib/active_record/associations/has_many_association.rb +1 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  19. data/lib/active_record/associations/has_one_association.rb +28 -30
  20. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  21. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  22. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/preloader/association.rb +1 -2
  25. data/lib/active_record/associations/preloader.rb +32 -29
  26. data/lib/active_record/associations/singular_association.rb +2 -16
  27. data/lib/active_record/associations.rb +16 -12
  28. data/lib/active_record/attribute_assignment.rb +7 -10
  29. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  31. data/lib/active_record/attribute_methods/read.rb +16 -48
  32. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  34. data/lib/active_record/attribute_methods/write.rb +15 -16
  35. data/lib/active_record/attribute_methods.rb +34 -56
  36. data/lib/active_record/autosave_association.rb +7 -21
  37. data/lib/active_record/base.rb +2 -2
  38. data/lib/active_record/callbacks.rb +3 -17
  39. data/lib/active_record/coders/yaml_column.rb +1 -13
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +75 -52
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations/database_config.rb +37 -0
  83. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  84. data/lib/active_record/database_configurations/url_config.rb +74 -0
  85. data/lib/active_record/database_configurations.rb +184 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration/command_recorder.rb +35 -5
  102. data/lib/active_record/migration/compatibility.rb +34 -16
  103. data/lib/active_record/migration.rb +38 -37
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -60
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation/batches.rb +13 -10
  116. data/lib/active_record/relation/calculations.rb +38 -28
  117. data/lib/active_record/relation/delegation.rb +4 -13
  118. data/lib/active_record/relation/finder_methods.rb +12 -25
  119. data/lib/active_record/relation/merger.rb +2 -6
  120. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  121. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  122. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  123. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  124. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  125. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  126. data/lib/active_record/relation/predicate_builder.rb +4 -6
  127. data/lib/active_record/relation/query_attribute.rb +15 -12
  128. data/lib/active_record/relation/query_methods.rb +29 -52
  129. data/lib/active_record/relation/where_clause.rb +4 -0
  130. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  131. data/lib/active_record/relation.rb +150 -69
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping/default.rb +10 -3
  138. data/lib/active_record/scoping/named.rb +10 -14
  139. data/lib/active_record/scoping.rb +9 -8
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  153. data/lib/active_record/type.rb +3 -4
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/active_record.rb +2 -1
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes/attribute.rb +37 -0
  160. data/lib/arel/attributes.rb +22 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes/and.rb +32 -0
  174. data/lib/arel/nodes/ascending.rb +23 -0
  175. data/lib/arel/nodes/binary.rb +52 -0
  176. data/lib/arel/nodes/bind_param.rb +36 -0
  177. data/lib/arel/nodes/case.rb +55 -0
  178. data/lib/arel/nodes/casted.rb +50 -0
  179. data/lib/arel/nodes/count.rb +12 -0
  180. data/lib/arel/nodes/delete_statement.rb +45 -0
  181. data/lib/arel/nodes/descending.rb +23 -0
  182. data/lib/arel/nodes/equality.rb +18 -0
  183. data/lib/arel/nodes/extract.rb +24 -0
  184. data/lib/arel/nodes/false.rb +16 -0
  185. data/lib/arel/nodes/full_outer_join.rb +8 -0
  186. data/lib/arel/nodes/function.rb +44 -0
  187. data/lib/arel/nodes/grouping.rb +8 -0
  188. data/lib/arel/nodes/in.rb +8 -0
  189. data/lib/arel/nodes/infix_operation.rb +80 -0
  190. data/lib/arel/nodes/inner_join.rb +8 -0
  191. data/lib/arel/nodes/insert_statement.rb +37 -0
  192. data/lib/arel/nodes/join_source.rb +20 -0
  193. data/lib/arel/nodes/matches.rb +18 -0
  194. data/lib/arel/nodes/named_function.rb +23 -0
  195. data/lib/arel/nodes/node.rb +50 -0
  196. data/lib/arel/nodes/node_expression.rb +13 -0
  197. data/lib/arel/nodes/outer_join.rb +8 -0
  198. data/lib/arel/nodes/over.rb +15 -0
  199. data/lib/arel/nodes/regexp.rb +16 -0
  200. data/lib/arel/nodes/right_outer_join.rb +8 -0
  201. data/lib/arel/nodes/select_core.rb +63 -0
  202. data/lib/arel/nodes/select_statement.rb +41 -0
  203. data/lib/arel/nodes/sql_literal.rb +16 -0
  204. data/lib/arel/nodes/string_join.rb +11 -0
  205. data/lib/arel/nodes/table_alias.rb +27 -0
  206. data/lib/arel/nodes/terminal.rb +16 -0
  207. data/lib/arel/nodes/true.rb +16 -0
  208. data/lib/arel/nodes/unary.rb +44 -0
  209. data/lib/arel/nodes/unary_operation.rb +20 -0
  210. data/lib/arel/nodes/unqualified_column.rb +22 -0
  211. data/lib/arel/nodes/update_statement.rb +41 -0
  212. data/lib/arel/nodes/values.rb +16 -0
  213. data/lib/arel/nodes/values_list.rb +24 -0
  214. data/lib/arel/nodes/window.rb +126 -0
  215. data/lib/arel/nodes/with.rb +11 -0
  216. data/lib/arel/nodes.rb +67 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors/depth_first.rb +199 -0
  224. data/lib/arel/visitors/dot.rb +292 -0
  225. data/lib/arel/visitors/ibm_db.rb +21 -0
  226. data/lib/arel/visitors/informix.rb +56 -0
  227. data/lib/arel/visitors/mssql.rb +143 -0
  228. data/lib/arel/visitors/mysql.rb +83 -0
  229. data/lib/arel/visitors/oracle.rb +159 -0
  230. data/lib/arel/visitors/oracle12.rb +67 -0
  231. data/lib/arel/visitors/postgresql.rb +116 -0
  232. data/lib/arel/visitors/sqlite.rb +39 -0
  233. data/lib/arel/visitors/to_sql.rb +913 -0
  234. data/lib/arel/visitors/visitor.rb +42 -0
  235. data/lib/arel/visitors/where_sql.rb +23 -0
  236. data/lib/arel/visitors.rb +20 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/arel.rb +44 -0
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/migration.rb +14 -1
  241. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  242. metadata +107 -29
@@ -7,10 +7,10 @@ module ActiveRecord
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 5
11
- MINOR = 2
12
- TINY = 8
13
- PRE = "1"
10
+ MAJOR = 6
11
+ MINOR = 0
12
+ TINY = 0
13
+ PRE = "beta1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -55,7 +55,11 @@ module ActiveRecord
55
55
  if has_attribute?(inheritance_column)
56
56
  subclass = subclass_from_attributes(attributes)
57
57
 
58
- if subclass.nil? && base_class == self
58
+ if subclass.nil? && scope_attributes = current_scope&.scope_for_create
59
+ subclass = subclass_from_attributes(scope_attributes)
60
+ end
61
+
62
+ if subclass.nil? && base_class?
59
63
  subclass = subclass_from_attributes(column_defaults)
60
64
  end
61
65
  end
@@ -104,6 +108,12 @@ module ActiveRecord
104
108
  end
105
109
  end
106
110
 
111
+ # Returns whether the class is a base class.
112
+ # See #base_class for more information.
113
+ def base_class?
114
+ base_class == self
115
+ end
116
+
107
117
  # Set this to +true+ if this is an abstract class (see
108
118
  # <tt>abstract_class?</tt>).
109
119
  # If you are using inheritance with Active Record and don't want a class
@@ -170,7 +180,7 @@ module ActiveRecord
170
180
  # Returns the class type of the record using the current module as a prefix. So descendants of
171
181
  # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
172
182
  def compute_type(type_name)
173
- if type_name.start_with?("::".freeze)
183
+ if type_name.start_with?("::")
174
184
  # If the type is prefixed with a scope operator then we assume that
175
185
  # the type_name is an absolute reference.
176
186
  ActiveSupport::Dependencies.constantize(type_name)
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  # Indicates whether to use a stable #cache_key method that is accompanied
21
21
  # by a changing version in the #cache_version method.
22
22
  #
23
- # This is +false+, by default until Rails 6.0.
23
+ # This is +true+, by default on Rails 5.2 and above.
24
24
  class_attribute :cache_versioning, instance_writer: false, default: false
25
25
  end
26
26
 
@@ -60,24 +60,15 @@ module ActiveRecord
60
60
  # the cache key will also include a version.
61
61
  #
62
62
  # Product.cache_versioning = false
63
- # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
64
- def cache_key(*timestamp_names)
63
+ # Product.find(5).cache_key # => "products/5-20071224150000" (updated_at available)
64
+ def cache_key
65
65
  if new_record?
66
66
  "#{model_name.cache_key}/new"
67
67
  else
68
- if cache_version && timestamp_names.none?
68
+ if cache_version
69
69
  "#{model_name.cache_key}/#{id}"
70
70
  else
71
- timestamp = if timestamp_names.any?
72
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
73
- Specifying a timestamp name for #cache_key has been deprecated in favor of
74
- the explicit #cache_version method that can be overwritten.
75
- MSG
76
-
77
- max_updated_column_timestamp(timestamp_names)
78
- else
79
- max_updated_column_timestamp
80
- end
71
+ timestamp = max_updated_column_timestamp
81
72
 
82
73
  if timestamp
83
74
  timestamp = timestamp.utc.to_s(cache_timestamp_format)
@@ -96,8 +87,19 @@ module ActiveRecord
96
87
  # Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
97
88
  # +false+ (which it is by default until Rails 6.0).
98
89
  def cache_version
99
- if cache_versioning && timestamp = try(:updated_at)
100
- timestamp.utc.to_s(:usec)
90
+ return unless cache_versioning
91
+
92
+ if has_attribute?("updated_at")
93
+ timestamp = updated_at_before_type_cast
94
+ if can_use_fast_cache_version?(timestamp)
95
+ raw_timestamp_to_cache_version(timestamp)
96
+ elsif timestamp = updated_at
97
+ timestamp.utc.to_s(cache_timestamp_format)
98
+ end
99
+ else
100
+ if self.class.has_attribute?("updated_at")
101
+ raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
102
+ end
101
103
  end
102
104
  end
103
105
 
@@ -151,5 +153,43 @@ module ActiveRecord
151
153
  end
152
154
  end
153
155
  end
156
+
157
+ private
158
+ # Detects if the value before type cast
159
+ # can be used to generate a cache_version.
160
+ #
161
+ # The fast cache version only works with a
162
+ # string value directly from the database.
163
+ #
164
+ # We also must check if the timestamp format has been changed
165
+ # or if the timezone is not set to UTC then
166
+ # we cannot apply our transformations correctly.
167
+ def can_use_fast_cache_version?(timestamp)
168
+ timestamp.is_a?(String) &&
169
+ cache_timestamp_format == :usec &&
170
+ default_timezone == :utc &&
171
+ !updated_at_came_from_user?
172
+ end
173
+
174
+ # Converts a raw database string to `:usec`
175
+ # format.
176
+ #
177
+ # Example:
178
+ #
179
+ # timestamp = "2018-10-15 20:02:15.266505"
180
+ # raw_timestamp_to_cache_version(timestamp)
181
+ # # => "20181015200215266505"
182
+ #
183
+ # Postgres truncates trailing zeros,
184
+ # https://github.com/postgres/postgres/commit/3e1beda2cde3495f41290e1ece5d544525810214
185
+ # to account for this we pad the output with zeros
186
+ def raw_timestamp_to_cache_version(timestamp)
187
+ key = timestamp.delete("- :.")
188
+ if key.length < 20
189
+ key.ljust(20, "0")
190
+ else
191
+ key
192
+ end
193
+ end
154
194
  end
155
195
  end
@@ -8,6 +8,10 @@ module ActiveRecord
8
8
  # as which environment migrations were run in.
9
9
  class InternalMetadata < ActiveRecord::Base # :nodoc:
10
10
  class << self
11
+ def _internal?
12
+ true
13
+ end
14
+
11
15
  def primary_key
12
16
  "key"
13
17
  end
@@ -17,7 +21,7 @@ module ActiveRecord
17
21
  end
18
22
 
19
23
  def []=(key, value)
20
- find_or_initialize_by(key: key).update_attributes!(value: value)
24
+ find_or_initialize_by(key: key).update!(value: value)
21
25
  end
22
26
 
23
27
  def [](key)
@@ -61,7 +61,7 @@ module ActiveRecord
61
61
  end
62
62
 
63
63
  private
64
- def _create_record(attribute_names = self.attribute_names, *)
64
+ def _create_record(attribute_names = self.attribute_names)
65
65
  if locking_enabled?
66
66
  # We always want to persist the locking version, even if we don't detect
67
67
  # a change from the default, since the database might have no default
@@ -165,7 +165,7 @@ module ActiveRecord
165
165
  def inherited(subclass)
166
166
  subclass.class_eval do
167
167
  is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
168
- decorate_matching_attribute_types(is_lock_column, :_optimistic_locking) do |type|
168
+ decorate_matching_attribute_types(is_lock_column, "_optimistic_locking") do |type|
169
169
  LockingType.new(type)
170
170
  end
171
171
  end
@@ -14,9 +14,9 @@ module ActiveRecord
14
14
  # of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
15
15
  #
16
16
  # Account.transaction do
17
- # # select * from accounts where name = 'shugo' limit 1 for update
18
- # shugo = Account.where("name = 'shugo'").lock(true).first
19
- # yuko = Account.where("name = 'yuko'").lock(true).first
17
+ # # select * from accounts where name = 'shugo' limit 1 for update nowait
18
+ # shugo = Account.lock("FOR UPDATE NOWAIT").find_by(name: "shugo")
19
+ # yuko = Account.lock("FOR UPDATE NOWAIT").find_by(name: "yuko")
20
20
  # shugo.balance -= 100
21
21
  # shugo.save!
22
22
  # yuko.balance += 100
@@ -4,6 +4,8 @@ module ActiveRecord
4
4
  class LogSubscriber < ActiveSupport::LogSubscriber
5
5
  IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
6
6
 
7
+ class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
8
+
7
9
  def self.runtime=(value)
8
10
  ActiveRecord::RuntimeRegistry.sql_runtime = value
9
11
  end
@@ -100,36 +102,15 @@ module ActiveRecord
100
102
  end
101
103
 
102
104
  def log_query_source
103
- source_line, line_number = extract_callstack(caller_locations)
104
-
105
- if source_line
106
- if defined?(::Rails.root)
107
- app_root = "#{::Rails.root.to_s}/".freeze
108
- source_line = source_line.sub(app_root, "")
109
- end
110
-
111
- logger.debug(" ↳ #{ source_line }:#{ line_number }")
112
- end
113
- end
105
+ source = extract_query_source_location(caller)
114
106
 
115
- def extract_callstack(callstack)
116
- line = callstack.find do |frame|
117
- frame.absolute_path && !ignored_callstack(frame.absolute_path)
107
+ if source
108
+ logger.debug(" ↳ #{source}")
118
109
  end
119
-
120
- offending_line = line || callstack.first
121
-
122
- [
123
- offending_line.path,
124
- offending_line.lineno
125
- ]
126
110
  end
127
111
 
128
- RAILS_GEM_ROOT = File.expand_path("../../..", __dir__) + "/"
129
-
130
- def ignored_callstack(path)
131
- path.start_with?(RAILS_GEM_ROOT) ||
132
- path.start_with?(RbConfig::CONFIG["rubylibdir"])
112
+ def extract_query_source_location(locations)
113
+ backtrace_cleaner.clean(locations).first
133
114
  end
134
115
  end
135
116
  end
@@ -85,7 +85,7 @@ module ActiveRecord
85
85
  # invert the +command+.
86
86
  def inverse_of(command, args, &block)
87
87
  method = :"invert_#{command}"
88
- raise IrreversibleMigration, <<-MSG.strip_heredoc unless respond_to?(method, true)
88
+ raise IrreversibleMigration, <<~MSG unless respond_to?(method, true)
89
89
  This migration uses #{command}, which is not automatically reversible.
90
90
  To make the migration reversible you can either:
91
91
  1. Define #up and #down methods in place of the #change method.
@@ -108,11 +108,17 @@ module ActiveRecord
108
108
  yield delegate.update_table_definition(table_name, self)
109
109
  end
110
110
 
111
+ def replay(migration)
112
+ commands.each do |cmd, args, block|
113
+ migration.send(cmd, *args, &block)
114
+ end
115
+ end
116
+
111
117
  private
112
118
 
113
119
  module StraightReversions # :nodoc:
114
120
  private
115
- { transaction: :transaction,
121
+ {
116
122
  execute_block: :execute_block,
117
123
  create_table: :drop_table,
118
124
  create_join_table: :drop_join_table,
@@ -133,6 +139,17 @@ module ActiveRecord
133
139
 
134
140
  include StraightReversions
135
141
 
142
+ def invert_transaction(args)
143
+ sub_recorder = CommandRecorder.new(delegate)
144
+ sub_recorder.revert { yield }
145
+
146
+ invertions_proc = proc {
147
+ sub_recorder.replay(self)
148
+ }
149
+
150
+ [:transaction, args, invertions_proc]
151
+ end
152
+
136
153
  def invert_drop_table(args, &block)
137
154
  if args.size == 1 && block == nil
138
155
  raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
@@ -214,11 +231,24 @@ module ActiveRecord
214
231
  end
215
232
 
216
233
  def invert_remove_foreign_key(args)
217
- from_table, to_table, remove_options = args
218
- raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil? || to_table.is_a?(Hash)
234
+ from_table, options_or_to_table, options_or_nil = args
235
+
236
+ to_table = if options_or_to_table.is_a?(Hash)
237
+ options_or_to_table[:to_table]
238
+ else
239
+ options_or_to_table
240
+ end
241
+
242
+ remove_options = if options_or_to_table.is_a?(Hash)
243
+ options_or_to_table.except(:to_table)
244
+ else
245
+ options_or_nil
246
+ end
247
+
248
+ raise ActiveRecord::IrreversibleMigration, "remove_foreign_key is only reversible if given a second table" if to_table.nil?
219
249
 
220
250
  reversed_args = [from_table, to_table]
221
- reversed_args << remove_options if remove_options
251
+ reversed_args << remove_options if remove_options.present?
222
252
 
223
253
  [:add_foreign_key, reversed_args]
224
254
  end
@@ -13,22 +13,42 @@ module ActiveRecord
13
13
  const_get(name)
14
14
  end
15
15
 
16
- V5_2 = Current
16
+ V6_0 = Current
17
+
18
+ class V5_2 < V6_0
19
+ module CommandRecorder
20
+ def invert_transaction(args, &block)
21
+ [:transaction, args, block]
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def command_recorder
28
+ recorder = super
29
+ class << recorder
30
+ prepend CommandRecorder
31
+ end
32
+ recorder
33
+ end
34
+ end
17
35
 
18
36
  class V5_1 < V5_2
19
37
  def change_column(table_name, column_name, type, options = {})
20
- if connection.adapter_name == "PostgreSQL"
21
- super(table_name, column_name, type, options.except(:default, :null, :comment))
22
- connection.change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
23
- connection.change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
24
- connection.change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
38
+ if adapter_name == "PostgreSQL"
39
+ clear_cache!
40
+ sql = connection.send(:change_column_sql, table_name, column_name, type, options)
41
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql}"
42
+ change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
43
+ change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
44
+ change_column_comment(table_name, column_name, options[:comment]) if options.key?(:comment)
25
45
  else
26
46
  super
27
47
  end
28
48
  end
29
49
 
30
50
  def create_table(table_name, options = {})
31
- if connection.adapter_name == "Mysql2"
51
+ if adapter_name == "Mysql2"
32
52
  super(table_name, options: "ENGINE=InnoDB", **options)
33
53
  else
34
54
  super
@@ -50,13 +70,13 @@ module ActiveRecord
50
70
  end
51
71
 
52
72
  def create_table(table_name, options = {})
53
- if connection.adapter_name == "PostgreSQL"
73
+ if adapter_name == "PostgreSQL"
54
74
  if options[:id] == :uuid && !options.key?(:default)
55
75
  options[:default] = "uuid_generate_v4()"
56
76
  end
57
77
  end
58
78
 
59
- unless connection.adapter_name == "Mysql2" && options[:id] == :bigint
79
+ unless adapter_name == "Mysql2" && options[:id] == :bigint
60
80
  if [:integer, :bigint].include?(options[:id]) && !options.key?(:default)
61
81
  options[:default] = nil
62
82
  end
@@ -173,7 +193,7 @@ module ActiveRecord
173
193
  if options[:name].present?
174
194
  options[:name].to_s
175
195
  else
176
- connection.index_name(table_name, column: column_names)
196
+ index_name(table_name, column: column_names)
177
197
  end
178
198
  super
179
199
  end
@@ -193,17 +213,15 @@ module ActiveRecord
193
213
  end
194
214
 
195
215
  def index_name_for_remove(table_name, options = {})
196
- index_name = connection.index_name(table_name, options)
216
+ index_name = index_name(table_name, options)
197
217
 
198
- unless connection.index_name_exists?(table_name, index_name)
218
+ unless index_name_exists?(table_name, index_name)
199
219
  if options.is_a?(Hash) && options.has_key?(:name)
200
220
  options_without_column = options.dup
201
221
  options_without_column.delete :column
202
- index_name_without_column = connection.index_name(table_name, options_without_column)
222
+ index_name_without_column = index_name(table_name, options_without_column)
203
223
 
204
- if connection.index_name_exists?(table_name, index_name_without_column)
205
- return index_name_without_column
206
- end
224
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column)
207
225
  end
208
226
 
209
227
  raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "benchmark"
3
4
  require "set"
4
5
  require "zlib"
5
6
  require "active_support/core_ext/module/attribute_accessors"
@@ -22,7 +23,7 @@ module ActiveRecord
22
23
  # t.string :zipcode
23
24
  # end
24
25
  #
25
- # execute <<-SQL
26
+ # execute <<~SQL
26
27
  # ALTER TABLE distributors
27
28
  # ADD CONSTRAINT zipchk
28
29
  # CHECK (char_length(zipcode) = 5) NO INHERIT;
@@ -40,7 +41,7 @@ module ActiveRecord
40
41
  # t.string :zipcode
41
42
  # end
42
43
  #
43
- # execute <<-SQL
44
+ # execute <<~SQL
44
45
  # ALTER TABLE distributors
45
46
  # ADD CONSTRAINT zipchk
46
47
  # CHECK (char_length(zipcode) = 5) NO INHERIT;
@@ -48,7 +49,7 @@ module ActiveRecord
48
49
  # end
49
50
  #
50
51
  # def down
51
- # execute <<-SQL
52
+ # execute <<~SQL
52
53
  # ALTER TABLE distributors
53
54
  # DROP CONSTRAINT zipchk
54
55
  # SQL
@@ -67,7 +68,7 @@ module ActiveRecord
67
68
  #
68
69
  # reversible do |dir|
69
70
  # dir.up do
70
- # execute <<-SQL
71
+ # execute <<~SQL
71
72
  # ALTER TABLE distributors
72
73
  # ADD CONSTRAINT zipchk
73
74
  # CHECK (char_length(zipcode) = 5) NO INHERIT;
@@ -75,7 +76,7 @@ module ActiveRecord
75
76
  # end
76
77
  #
77
78
  # dir.down do
78
- # execute <<-SQL
79
+ # execute <<~SQL
79
80
  # ALTER TABLE distributors
80
81
  # DROP CONSTRAINT zipchk
81
82
  # SQL
@@ -129,9 +130,9 @@ module ActiveRecord
129
130
  class PendingMigrationError < MigrationError#:nodoc:
130
131
  def initialize(message = nil)
131
132
  if !message && defined?(Rails.env)
132
- super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate RAILS_ENV=#{::Rails.env}")
133
+ super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate RAILS_ENV=#{::Rails.env}")
133
134
  elsif !message
134
- super("Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate")
135
+ super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate")
135
136
  else
136
137
  super
137
138
  end
@@ -139,8 +140,8 @@ module ActiveRecord
139
140
  end
140
141
 
141
142
  class ConcurrentMigrationError < MigrationError #:nodoc:
142
- DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running.".freeze
143
- RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock".freeze
143
+ DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running."
144
+ RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock"
144
145
 
145
146
  def initialize(message = DEFAULT_MESSAGE)
146
147
  super
@@ -149,7 +150,7 @@ module ActiveRecord
149
150
 
150
151
  class NoEnvironmentInSchemaError < MigrationError #:nodoc:
151
152
  def initialize
152
- msg = "Environment data not found in the schema. To resolve this issue, run: \n\n bin/rails db:environment:set"
153
+ msg = "Environment data not found in the schema. To resolve this issue, run: \n\n rails db:environment:set"
153
154
  if defined?(Rails.env)
154
155
  super("#{msg} RAILS_ENV=#{::Rails.env}")
155
156
  else
@@ -160,7 +161,7 @@ module ActiveRecord
160
161
 
161
162
  class ProtectedEnvironmentError < ActiveRecordError #:nodoc:
162
163
  def initialize(env = "production")
163
- msg = "You are attempting to run a destructive action against your '#{env}' database.\n".dup
164
+ msg = +"You are attempting to run a destructive action against your '#{env}' database.\n"
164
165
  msg << "If you are sure you want to continue, run the same command with the environment variable:\n"
165
166
  msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
166
167
  super(msg)
@@ -169,10 +170,10 @@ module ActiveRecord
169
170
 
170
171
  class EnvironmentMismatchError < ActiveRecordError
171
172
  def initialize(current: nil, stored: nil)
172
- msg = "You are attempting to modify a database that was last run in `#{ stored }` environment.\n".dup
173
+ msg = +"You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
173
174
  msg << "You are running in `#{ current }` environment. "
174
175
  msg << "If you are sure you want to continue, first set the environment using:\n\n"
175
- msg << " bin/rails db:environment:set"
176
+ msg << " rails db:environment:set"
176
177
  if defined?(Rails.env)
177
178
  super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
178
179
  else
@@ -351,7 +352,7 @@ module ActiveRecord
351
352
  # <tt>rails db:migrate</tt>. This will update the database by running all of the
352
353
  # pending migrations, creating the <tt>schema_migrations</tt> table
353
354
  # (see "About the schema_migrations table" section below) if missing. It will also
354
- # invoke the db:schema:dump task, which will update your db/schema.rb file
355
+ # invoke the db:schema:dump command, which will update your db/schema.rb file
355
356
  # to match the structure of your database.
356
357
  #
357
358
  # To roll the database back to a previous migration version, use
@@ -677,15 +678,13 @@ module ActiveRecord
677
678
  if connection.respond_to? :revert
678
679
  connection.revert { yield }
679
680
  else
680
- recorder = CommandRecorder.new(connection)
681
+ recorder = command_recorder
681
682
  @connection = recorder
682
683
  suppress_messages do
683
684
  connection.revert { yield }
684
685
  end
685
686
  @connection = recorder.delegate
686
- recorder.commands.each do |cmd, args, block|
687
- send(cmd, *args, &block)
688
- end
687
+ recorder.replay(self)
689
688
  end
690
689
  end
691
690
  end
@@ -830,10 +829,14 @@ module ActiveRecord
830
829
  write "== %s %s" % [text, "=" * length]
831
830
  end
832
831
 
832
+ # Takes a message argument and outputs it as is.
833
+ # A second boolean argument can be passed to specify whether to indent or not.
833
834
  def say(message, subitem = false)
834
835
  write "#{subitem ? " ->" : "--"} #{message}"
835
836
  end
836
837
 
838
+ # Outputs text along with how long it took to run its block.
839
+ # If the block returns an integer it assumes it is the number of rows affected.
837
840
  def say_with_time(message)
838
841
  say(message)
839
842
  result = nil
@@ -843,6 +846,7 @@ module ActiveRecord
843
846
  result
844
847
  end
845
848
 
849
+ # Takes a block as an argument and suppresses any output generated by the block.
846
850
  def suppress_messages
847
851
  save, self.verbose = verbose, false
848
852
  yield
@@ -885,7 +889,7 @@ module ActiveRecord
885
889
  source_migrations.each do |migration|
886
890
  source = File.binread(migration.filename)
887
891
  inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
888
- magic_comments = "".dup
892
+ magic_comments = +""
889
893
  loop do
890
894
  # If we have a magic comment in the original migration,
891
895
  # insert our comment after the first newline(end of the magic comment line)
@@ -956,6 +960,10 @@ module ActiveRecord
956
960
  yield
957
961
  end
958
962
  end
963
+
964
+ def command_recorder
965
+ CommandRecorder.new(connection)
966
+ end
959
967
  end
960
968
 
961
969
  # MigrationProxy is used to defer loading of the actual migration classes
@@ -1079,10 +1087,6 @@ module ActiveRecord
1079
1087
  migrations.last || NullMigration.new
1080
1088
  end
1081
1089
 
1082
- def parse_migration_filename(filename) # :nodoc:
1083
- File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1084
- end
1085
-
1086
1090
  def migrations
1087
1091
  migrations = migration_files.map do |file|
1088
1092
  version, name, scope = parse_migration_filename(file)
@@ -1114,11 +1118,6 @@ module ActiveRecord
1114
1118
  (db_list + file_list).sort_by { |_, version, _| version }
1115
1119
  end
1116
1120
 
1117
- def migration_files
1118
- paths = Array(migrations_paths)
1119
- Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
1120
- end
1121
-
1122
1121
  def current_environment
1123
1122
  ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
1124
1123
  end
@@ -1137,6 +1136,15 @@ module ActiveRecord
1137
1136
  end
1138
1137
 
1139
1138
  private
1139
+ def migration_files
1140
+ paths = Array(migrations_paths)
1141
+ Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
1142
+ end
1143
+
1144
+ def parse_migration_filename(filename)
1145
+ File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
1146
+ end
1147
+
1140
1148
  def move(direction, steps)
1141
1149
  migrator = Migrator.new(direction, migrations)
1142
1150
 
@@ -1161,13 +1169,6 @@ module ActiveRecord
1161
1169
  class << self
1162
1170
  attr_accessor :migrations_paths
1163
1171
 
1164
- def migrations_path=(path)
1165
- ActiveSupport::Deprecation.warn \
1166
- "`ActiveRecord::Migrator.migrations_path=` is now deprecated and will be removed in Rails 6.0. " \
1167
- "You can set the `migrations_paths` on the `connection` instead through the `database.yml`."
1168
- self.migrations_paths = [path]
1169
- end
1170
-
1171
1172
  # For cases where a table doesn't exist like loading from schema cache
1172
1173
  def current_version
1173
1174
  MigrationContext.new(migrations_paths).current_version
@@ -1293,7 +1294,7 @@ module ActiveRecord
1293
1294
  record_version_state_after_migrating(migration.version)
1294
1295
  end
1295
1296
  rescue => e
1296
- msg = "An error has occurred, ".dup
1297
+ msg = +"An error has occurred, "
1297
1298
  msg << "this and " if use_transaction?(migration)
1298
1299
  msg << "all later migrations canceled:\n\n#{e}"
1299
1300
  raise StandardError, msg, e.backtrace
@@ -1351,7 +1352,7 @@ module ActiveRecord
1351
1352
  end
1352
1353
 
1353
1354
  def use_advisory_lock?
1354
- Base.connection.supports_advisory_locks?
1355
+ Base.connection.advisory_locks_enabled?
1355
1356
  end
1356
1357
 
1357
1358
  def with_advisory_lock