activerecord 6.1.4.1 → 7.0.1

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 (237) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1132 -936
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +33 -17
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +34 -27
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/join_dependency.rb +6 -2
  25. data/lib/active_record/associations/preloader/association.rb +187 -55
  26. data/lib/active_record/associations/preloader/batch.rb +48 -0
  27. data/lib/active_record/associations/preloader/branch.rb +147 -0
  28. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  29. data/lib/active_record/associations/preloader.rb +39 -113
  30. data/lib/active_record/associations/singular_association.rb +8 -2
  31. data/lib/active_record/associations/through_association.rb +3 -3
  32. data/lib/active_record/associations.rb +118 -90
  33. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  34. data/lib/active_record/attribute_assignment.rb +1 -1
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  37. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  38. data/lib/active_record/attribute_methods/query.rb +2 -2
  39. data/lib/active_record/attribute_methods/read.rb +7 -5
  40. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  42. data/lib/active_record/attribute_methods/write.rb +7 -10
  43. data/lib/active_record/attribute_methods.rb +13 -14
  44. data/lib/active_record/attributes.rb +24 -35
  45. data/lib/active_record/autosave_association.rb +6 -21
  46. data/lib/active_record/base.rb +19 -1
  47. data/lib/active_record/callbacks.rb +2 -2
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -9
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +78 -22
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
  62. data/lib/active_record/connection_adapters/column.rb +4 -0
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +37 -23
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
  65. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +5 -1
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  67. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  68. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -12
  70. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  73. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  77. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +50 -50
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +29 -18
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
  85. data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
  86. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +27 -19
  87. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  88. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +16 -14
  89. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +89 -30
  90. data/lib/active_record/connection_adapters.rb +6 -5
  91. data/lib/active_record/connection_handling.rb +47 -53
  92. data/lib/active_record/core.rb +122 -132
  93. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  94. data/lib/active_record/database_configurations/database_config.rb +12 -9
  95. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  96. data/lib/active_record/database_configurations/url_config.rb +2 -2
  97. data/lib/active_record/database_configurations.rb +16 -32
  98. data/lib/active_record/delegated_type.rb +52 -11
  99. data/lib/active_record/destroy_association_async_job.rb +1 -1
  100. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  101. data/lib/active_record/dynamic_matchers.rb +1 -1
  102. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  103. data/lib/active_record/encryption/cipher.rb +53 -0
  104. data/lib/active_record/encryption/config.rb +44 -0
  105. data/lib/active_record/encryption/configurable.rb +61 -0
  106. data/lib/active_record/encryption/context.rb +35 -0
  107. data/lib/active_record/encryption/contexts.rb +72 -0
  108. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  109. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  110. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  111. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  112. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  113. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  114. data/lib/active_record/encryption/encryptor.rb +155 -0
  115. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  116. data/lib/active_record/encryption/errors.rb +15 -0
  117. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  118. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  119. data/lib/active_record/encryption/key.rb +28 -0
  120. data/lib/active_record/encryption/key_generator.rb +42 -0
  121. data/lib/active_record/encryption/key_provider.rb +46 -0
  122. data/lib/active_record/encryption/message.rb +33 -0
  123. data/lib/active_record/encryption/message_serializer.rb +90 -0
  124. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  125. data/lib/active_record/encryption/properties.rb +76 -0
  126. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  127. data/lib/active_record/encryption/scheme.rb +99 -0
  128. data/lib/active_record/encryption.rb +55 -0
  129. data/lib/active_record/enum.rb +49 -42
  130. data/lib/active_record/errors.rb +67 -4
  131. data/lib/active_record/explain_registry.rb +11 -6
  132. data/lib/active_record/fixture_set/file.rb +15 -1
  133. data/lib/active_record/fixture_set/table_row.rb +41 -6
  134. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  135. data/lib/active_record/fixtures.rb +17 -20
  136. data/lib/active_record/future_result.rb +139 -0
  137. data/lib/active_record/gem_version.rb +4 -4
  138. data/lib/active_record/inheritance.rb +55 -17
  139. data/lib/active_record/insert_all.rb +80 -14
  140. data/lib/active_record/integration.rb +4 -3
  141. data/lib/active_record/internal_metadata.rb +3 -5
  142. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  143. data/lib/active_record/locking/optimistic.rb +10 -9
  144. data/lib/active_record/locking/pessimistic.rb +9 -3
  145. data/lib/active_record/log_subscriber.rb +14 -3
  146. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  147. data/lib/active_record/middleware/database_selector.rb +8 -3
  148. data/lib/active_record/middleware/shard_selector.rb +60 -0
  149. data/lib/active_record/migration/command_recorder.rb +4 -4
  150. data/lib/active_record/migration/compatibility.rb +107 -3
  151. data/lib/active_record/migration/join_table.rb +1 -1
  152. data/lib/active_record/migration.rb +109 -79
  153. data/lib/active_record/model_schema.rb +45 -58
  154. data/lib/active_record/nested_attributes.rb +13 -12
  155. data/lib/active_record/no_touching.rb +3 -3
  156. data/lib/active_record/null_relation.rb +2 -6
  157. data/lib/active_record/persistence.rb +219 -52
  158. data/lib/active_record/query_cache.rb +2 -2
  159. data/lib/active_record/query_logs.rb +138 -0
  160. data/lib/active_record/querying.rb +15 -5
  161. data/lib/active_record/railtie.rb +127 -17
  162. data/lib/active_record/railties/controller_runtime.rb +1 -1
  163. data/lib/active_record/railties/databases.rake +66 -129
  164. data/lib/active_record/readonly_attributes.rb +11 -0
  165. data/lib/active_record/reflection.rb +67 -50
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  167. data/lib/active_record/relation/batches.rb +3 -3
  168. data/lib/active_record/relation/calculations.rb +43 -38
  169. data/lib/active_record/relation/delegation.rb +6 -6
  170. data/lib/active_record/relation/finder_methods.rb +31 -35
  171. data/lib/active_record/relation/merger.rb +20 -13
  172. data/lib/active_record/relation/predicate_builder.rb +1 -6
  173. data/lib/active_record/relation/query_attribute.rb +5 -11
  174. data/lib/active_record/relation/query_methods.rb +243 -61
  175. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  176. data/lib/active_record/relation/spawn_methods.rb +2 -2
  177. data/lib/active_record/relation/where_clause.rb +10 -19
  178. data/lib/active_record/relation.rb +184 -84
  179. data/lib/active_record/result.rb +17 -7
  180. data/lib/active_record/runtime_registry.rb +9 -13
  181. data/lib/active_record/sanitization.rb +11 -7
  182. data/lib/active_record/schema_dumper.rb +10 -3
  183. data/lib/active_record/schema_migration.rb +4 -4
  184. data/lib/active_record/scoping/default.rb +61 -12
  185. data/lib/active_record/scoping/named.rb +3 -11
  186. data/lib/active_record/scoping.rb +64 -34
  187. data/lib/active_record/serialization.rb +1 -1
  188. data/lib/active_record/signed_id.rb +1 -1
  189. data/lib/active_record/suppressor.rb +11 -15
  190. data/lib/active_record/tasks/database_tasks.rb +120 -58
  191. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  192. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
  193. data/lib/active_record/test_databases.rb +1 -1
  194. data/lib/active_record/test_fixtures.rb +4 -4
  195. data/lib/active_record/timestamp.rb +3 -4
  196. data/lib/active_record/transactions.rb +9 -14
  197. data/lib/active_record/translation.rb +2 -2
  198. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  199. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  200. data/lib/active_record/type/internal/timezone.rb +2 -2
  201. data/lib/active_record/type/serialized.rb +1 -1
  202. data/lib/active_record/type/type_map.rb +17 -20
  203. data/lib/active_record/type.rb +1 -2
  204. data/lib/active_record/validations/associated.rb +1 -1
  205. data/lib/active_record/validations/uniqueness.rb +1 -1
  206. data/lib/active_record.rb +204 -28
  207. data/lib/arel/attributes/attribute.rb +0 -8
  208. data/lib/arel/crud.rb +28 -22
  209. data/lib/arel/delete_manager.rb +18 -4
  210. data/lib/arel/filter_predications.rb +9 -0
  211. data/lib/arel/insert_manager.rb +2 -3
  212. data/lib/arel/nodes/casted.rb +1 -1
  213. data/lib/arel/nodes/delete_statement.rb +12 -13
  214. data/lib/arel/nodes/filter.rb +10 -0
  215. data/lib/arel/nodes/function.rb +1 -0
  216. data/lib/arel/nodes/insert_statement.rb +2 -2
  217. data/lib/arel/nodes/select_core.rb +2 -2
  218. data/lib/arel/nodes/select_statement.rb +2 -2
  219. data/lib/arel/nodes/update_statement.rb +8 -3
  220. data/lib/arel/nodes.rb +1 -0
  221. data/lib/arel/predications.rb +11 -3
  222. data/lib/arel/select_manager.rb +10 -4
  223. data/lib/arel/table.rb +0 -1
  224. data/lib/arel/tree_manager.rb +0 -12
  225. data/lib/arel/update_manager.rb +18 -4
  226. data/lib/arel/visitors/dot.rb +80 -90
  227. data/lib/arel/visitors/mysql.rb +8 -2
  228. data/lib/arel/visitors/postgresql.rb +0 -10
  229. data/lib/arel/visitors/to_sql.rb +58 -2
  230. data/lib/arel.rb +2 -1
  231. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  232. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  233. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  234. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  235. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  236. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  237. metadata +59 -14
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class FutureResult # :nodoc:
5
+ class EventBuffer
6
+ def initialize(future_result, instrumenter)
7
+ @future_result = future_result
8
+ @instrumenter = instrumenter
9
+ @events = []
10
+ end
11
+
12
+ def instrument(name, payload = {}, &block)
13
+ event = @instrumenter.new_event(name, payload)
14
+ @events << event
15
+ event.record(&block)
16
+ end
17
+
18
+ def flush
19
+ events, @events = @events, []
20
+ events.each do |event|
21
+ event.payload[:lock_wait] = @future_result.lock_wait
22
+ ActiveSupport::Notifications.publish_event(event)
23
+ end
24
+ end
25
+ end
26
+
27
+ Canceled = Class.new(ActiveRecordError)
28
+
29
+ delegate :empty?, :to_a, to: :result
30
+
31
+ attr_reader :lock_wait
32
+
33
+ def initialize(pool, *args, **kwargs)
34
+ @mutex = Mutex.new
35
+
36
+ @session = nil
37
+ @pool = pool
38
+ @args = args
39
+ @kwargs = kwargs
40
+
41
+ @pending = true
42
+ @error = nil
43
+ @result = nil
44
+ @instrumenter = ActiveSupport::Notifications.instrumenter
45
+ @event_buffer = nil
46
+ end
47
+
48
+ def schedule!(session)
49
+ @session = session
50
+ @pool.schedule_query(self)
51
+ end
52
+
53
+ def execute!(connection)
54
+ execute_query(connection)
55
+ end
56
+
57
+ def cancel
58
+ @pending = false
59
+ @error = Canceled
60
+ self
61
+ end
62
+
63
+ def execute_or_skip
64
+ return unless pending?
65
+
66
+ @pool.with_connection do |connection|
67
+ return unless @mutex.try_lock
68
+ begin
69
+ if pending?
70
+ @event_buffer = EventBuffer.new(self, @instrumenter)
71
+ connection.with_instrumenter(@event_buffer) do
72
+ execute_query(connection, async: true)
73
+ end
74
+ end
75
+ ensure
76
+ @mutex.unlock
77
+ end
78
+ end
79
+ end
80
+
81
+ def result
82
+ execute_or_wait
83
+ @event_buffer&.flush
84
+
85
+ if canceled?
86
+ raise Canceled
87
+ elsif @error
88
+ raise @error
89
+ else
90
+ @result
91
+ end
92
+ end
93
+
94
+ def pending?
95
+ @pending && (!@session || @session.active?)
96
+ end
97
+
98
+ private
99
+ def canceled?
100
+ @session && !@session.active?
101
+ end
102
+
103
+ def execute_or_wait
104
+ if pending?
105
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
106
+ @mutex.synchronize do
107
+ if pending?
108
+ execute_query(@pool.connection)
109
+ else
110
+ @lock_wait = (Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start)
111
+ end
112
+ end
113
+ else
114
+ @lock_wait = 0.0
115
+ end
116
+ end
117
+
118
+ def execute_query(connection, async: false)
119
+ @result = exec_query(connection, *@args, **@kwargs, async: async)
120
+ rescue => error
121
+ @error = error
122
+ ensure
123
+ @pending = false
124
+ end
125
+
126
+ def exec_query(connection, *args, **kwargs)
127
+ connection.exec_query(*args, **kwargs)
128
+ end
129
+
130
+ class SelectAll < FutureResult # :nodoc:
131
+ private
132
+ def exec_query(*, **)
133
+ super
134
+ rescue ::RangeError
135
+ ActiveRecord::Result.empty
136
+ end
137
+ end
138
+ end
139
+ end
@@ -7,10 +7,10 @@ module ActiveRecord
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 6
11
- MINOR = 1
12
- TINY = 4
13
- PRE = "1"
10
+ MAJOR = 7
11
+ MINOR = 0
12
+ TINY = 1
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/inflector"
3
4
  require "active_support/core_ext/hash/indifferent_access"
4
5
 
5
6
  module ActiveRecord
@@ -43,6 +44,8 @@ module ActiveRecord
43
44
  # Determines whether to store the full constant name including namespace when using STI.
44
45
  # This is true, by default.
45
46
  class_attribute :store_full_sti_class, instance_writer: false, default: true
47
+
48
+ set_base_class
46
49
  end
47
50
 
48
51
  module ClassMethods
@@ -85,7 +88,7 @@ module ActiveRecord
85
88
  end
86
89
  end
87
90
 
88
- def finder_needs_type_condition? #:nodoc:
91
+ def finder_needs_type_condition? # :nodoc:
89
92
  # This is like this because benchmarking justifies the strange :false stuff
90
93
  :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
91
94
  end
@@ -98,17 +101,7 @@ module ActiveRecord
98
101
  #
99
102
  # If B < A and C < B and if A is an abstract_class then both B.base_class
100
103
  # and C.base_class would return B as the answer since A is an abstract_class.
101
- def base_class
102
- unless self < Base
103
- raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
104
- end
105
-
106
- if superclass == Base || superclass.abstract_class?
107
- self
108
- else
109
- superclass.base_class
110
- end
111
- end
104
+ attr_reader :base_class
112
105
 
113
106
  # Returns whether the class is a base class.
114
107
  # See #base_class for more information.
@@ -164,6 +157,21 @@ module ActiveRecord
164
157
  defined?(@abstract_class) && @abstract_class == true
165
158
  end
166
159
 
160
+ # Sets the application record class for Active Record
161
+ #
162
+ # This is useful if your application uses a different class than
163
+ # ApplicationRecord for your primary abstract class. This class
164
+ # will share a database connection with Active Record. It is the class
165
+ # that connects to your primary database.
166
+ def primary_abstract_class
167
+ if ActiveRecord.application_record_class && ActiveRecord.application_record_class.name != name
168
+ raise ArgumentError, "The `primary_abstract_class` is already set to #{ActiveRecord.application_record_class.inspect}. There can only be one `primary_abstract_class` in an application."
169
+ end
170
+
171
+ self.abstract_class = true
172
+ ActiveRecord.application_record_class = self
173
+ end
174
+
167
175
  # Returns the value to be stored in the inheritance column for STI.
168
176
  def sti_name
169
177
  store_full_sti_class && store_full_class_name ? name : name.demodulize
@@ -174,7 +182,7 @@ module ActiveRecord
174
182
  # It is used to find the class correspondent to the value stored in the inheritance column.
175
183
  def sti_class_for(type_name)
176
184
  if store_full_sti_class && store_full_class_name
177
- ActiveSupport::Dependencies.constantize(type_name)
185
+ type_name.constantize
178
186
  else
179
187
  compute_type(type_name)
180
188
  end
@@ -196,17 +204,31 @@ module ActiveRecord
196
204
  # It is used to find the class correspondent to the value stored in the polymorphic type column.
197
205
  def polymorphic_class_for(name)
198
206
  if store_full_class_name
199
- ActiveSupport::Dependencies.constantize(name)
207
+ name.constantize
200
208
  else
201
209
  compute_type(name)
202
210
  end
203
211
  end
204
212
 
205
213
  def inherited(subclass)
214
+ subclass.set_base_class
206
215
  subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
207
216
  super
208
217
  end
209
218
 
219
+ def dup # :nodoc:
220
+ # `initialize_dup` / `initialize_copy` don't work when defined
221
+ # in the `singleton_class`.
222
+ other = super
223
+ other.set_base_class
224
+ other
225
+ end
226
+
227
+ def initialize_clone(other) # :nodoc:
228
+ super
229
+ set_base_class
230
+ end
231
+
210
232
  protected
211
233
  # Returns the class type of the record using the current module as a prefix. So descendants of
212
234
  # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
@@ -214,10 +236,10 @@ module ActiveRecord
214
236
  if type_name.start_with?("::")
215
237
  # If the type is prefixed with a scope operator then we assume that
216
238
  # the type_name is an absolute reference.
217
- ActiveSupport::Dependencies.constantize(type_name)
239
+ type_name.constantize
218
240
  else
219
241
  type_candidate = @_type_candidates_cache[type_name]
220
- if type_candidate && type_constant = ActiveSupport::Dependencies.safe_constantize(type_candidate)
242
+ if type_candidate && type_constant = type_candidate.safe_constantize
221
243
  return type_constant
222
244
  end
223
245
 
@@ -227,7 +249,7 @@ module ActiveRecord
227
249
  candidates << type_name
228
250
 
229
251
  candidates.each do |candidate|
230
- constant = ActiveSupport::Dependencies.safe_constantize(candidate)
252
+ constant = candidate.safe_constantize
231
253
  if candidate == constant.to_s
232
254
  @_type_candidates_cache[type_name] = candidate
233
255
  return constant
@@ -238,6 +260,22 @@ module ActiveRecord
238
260
  end
239
261
  end
240
262
 
263
+ def set_base_class # :nodoc:
264
+ @base_class = if self == Base
265
+ self
266
+ else
267
+ unless self < Base
268
+ raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
269
+ end
270
+
271
+ if superclass == Base || superclass.abstract_class?
272
+ self
273
+ else
274
+ superclass.base_class
275
+ end
276
+ end
277
+ end
278
+
241
279
  private
242
280
  # Called by +instantiate+ to decide which class to use for a new
243
281
  # record instance. For single-table inheritance, we check the record
@@ -5,13 +5,19 @@ require "active_support/core_ext/enumerable"
5
5
  module ActiveRecord
6
6
  class InsertAll # :nodoc:
7
7
  attr_reader :model, :connection, :inserts, :keys
8
- attr_reader :on_duplicate, :returning, :unique_by
8
+ attr_reader :on_duplicate, :update_only, :returning, :unique_by, :update_sql
9
9
 
10
- def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
10
+ def initialize(model, inserts, on_duplicate:, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
11
11
  raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
12
12
 
13
13
  @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
14
- @on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
14
+ @on_duplicate, @update_only, @returning, @unique_by = on_duplicate, update_only, returning, unique_by
15
+ @record_timestamps = record_timestamps.nil? ? model.record_timestamps : record_timestamps
16
+
17
+ disallow_raw_sql!(on_duplicate)
18
+ disallow_raw_sql!(returning)
19
+
20
+ configure_on_duplicate_update_logic
15
21
 
16
22
  if model.scope_attributes?
17
23
  @scope_attributes = model.scope_attributes
@@ -36,7 +42,7 @@ module ActiveRecord
36
42
  end
37
43
 
38
44
  def updatable_columns
39
- keys - readonly_columns - unique_by_columns
45
+ @updatable_columns ||= keys - readonly_columns - unique_by_columns
40
46
  end
41
47
 
42
48
  def primary_keys
@@ -56,18 +62,50 @@ module ActiveRecord
56
62
  inserts.map do |attributes|
57
63
  attributes = attributes.stringify_keys
58
64
  attributes.merge!(scope_attributes) if scope_attributes
65
+ attributes.reverse_merge!(timestamps_for_create) if record_timestamps?
59
66
 
60
67
  verify_attributes(attributes)
61
68
 
62
- keys.map do |key|
69
+ keys_including_timestamps.map do |key|
63
70
  yield key, attributes[key]
64
71
  end
65
72
  end
66
73
  end
67
74
 
75
+ def record_timestamps?
76
+ @record_timestamps
77
+ end
78
+
79
+ # TODO: Consider remaining this method, as it only conditionally extends keys, not always
80
+ def keys_including_timestamps
81
+ @keys_including_timestamps ||= if record_timestamps?
82
+ keys + model.all_timestamp_attributes_in_model
83
+ else
84
+ keys
85
+ end
86
+ end
87
+
68
88
  private
69
89
  attr_reader :scope_attributes
70
90
 
91
+ def configure_on_duplicate_update_logic
92
+ if custom_update_sql_provided? && update_only.present?
93
+ raise ArgumentError, "You can't set :update_only and provide custom update SQL via :on_duplicate at the same time"
94
+ end
95
+
96
+ if update_only.present?
97
+ @updatable_columns = Array(update_only)
98
+ @on_duplicate = :update
99
+ elsif custom_update_sql_provided?
100
+ @update_sql = on_duplicate
101
+ @on_duplicate = :update
102
+ end
103
+ end
104
+
105
+ def custom_update_sql_provided?
106
+ @custom_update_sql_provided ||= Arel.arel_node?(on_duplicate)
107
+ end
108
+
71
109
  def find_unique_index_for(unique_by)
72
110
  if !connection.supports_insert_conflict_target?
73
111
  return if unique_by.nil?
@@ -126,15 +164,28 @@ module ActiveRecord
126
164
 
127
165
 
128
166
  def verify_attributes(attributes)
129
- if keys != attributes.keys.to_set
167
+ if keys_including_timestamps != attributes.keys.to_set
130
168
  raise ArgumentError, "All objects being inserted must have the same keys"
131
169
  end
132
170
  end
133
171
 
172
+ def disallow_raw_sql!(value)
173
+ return if !value.is_a?(String) || Arel.arel_node?(value)
174
+
175
+ raise ArgumentError, "Dangerous query method (method whose arguments are used as raw " \
176
+ "SQL) called: #{value}. " \
177
+ "Known-safe values can be passed " \
178
+ "by wrapping them in Arel.sql()."
179
+ end
180
+
181
+ def timestamps_for_create
182
+ model.all_timestamp_attributes_in_model.index_with(connection.high_precision_current_timestamp)
183
+ end
184
+
134
185
  class Builder # :nodoc:
135
186
  attr_reader :model
136
187
 
137
- delegate :skip_duplicates?, :update_duplicates?, :keys, to: :insert_all
188
+ delegate :skip_duplicates?, :update_duplicates?, :keys, :keys_including_timestamps, :record_timestamps?, to: :insert_all
138
189
 
139
190
  def initialize(insert_all)
140
191
  @insert_all, @model, @connection = insert_all, insert_all.model, insert_all.connection
@@ -145,9 +196,10 @@ module ActiveRecord
145
196
  end
146
197
 
147
198
  def values_list
148
- types = extract_types_from_columns_on(model.table_name, keys: keys)
199
+ types = extract_types_from_columns_on(model.table_name, keys: keys_including_timestamps)
149
200
 
150
201
  values_list = insert_all.map_key_with_value do |key, value|
202
+ next value if Arel::Nodes::SqlLiteral === value
151
203
  connection.with_yaml_fallback(types[key].serialize(value))
152
204
  end
153
205
 
@@ -155,7 +207,13 @@ module ActiveRecord
155
207
  end
156
208
 
157
209
  def returning
158
- format_columns(insert_all.returning) if insert_all.returning
210
+ return unless insert_all.returning
211
+
212
+ if insert_all.returning.is_a?(String)
213
+ insert_all.returning
214
+ else
215
+ format_columns(insert_all.returning)
216
+ end
159
217
  end
160
218
 
161
219
  def conflict_target
@@ -173,22 +231,30 @@ module ActiveRecord
173
231
  end
174
232
 
175
233
  def touch_model_timestamps_unless(&block)
176
- model.send(:timestamp_attributes_for_update_in_model).map do |column_name|
234
+ return "" unless update_duplicates? && record_timestamps?
235
+
236
+ model.timestamp_attributes_for_update_in_model.filter_map do |column_name|
177
237
  if touch_timestamp_attribute?(column_name)
178
- "#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE CURRENT_TIMESTAMP END),"
238
+ "#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE #{connection.high_precision_current_timestamp} END),"
179
239
  end
180
- end.compact.join
240
+ end.join
181
241
  end
182
242
 
243
+ def raw_update_sql
244
+ insert_all.update_sql
245
+ end
246
+
247
+ alias raw_update_sql? raw_update_sql
248
+
183
249
  private
184
250
  attr_reader :connection, :insert_all
185
251
 
186
252
  def touch_timestamp_attribute?(column_name)
187
- update_duplicates? && !insert_all.updatable_columns.include?(column_name)
253
+ insert_all.updatable_columns.exclude?(column_name)
188
254
  end
189
255
 
190
256
  def columns_list
191
- format_columns(insert_all.keys)
257
+ format_columns(insert_all.keys_including_timestamps)
192
258
  end
193
259
 
194
260
  def extract_types_from_columns_on(table_name, keys:)
@@ -79,7 +79,7 @@ module ActiveRecord
79
79
  timestamp = max_updated_column_timestamp
80
80
 
81
81
  if timestamp
82
- timestamp = timestamp.utc.to_s(cache_timestamp_format)
82
+ timestamp = timestamp.utc.to_formatted_s(cache_timestamp_format)
83
83
  "#{model_name.cache_key}/#{id}-#{timestamp}"
84
84
  else
85
85
  "#{model_name.cache_key}/#{id}"
@@ -101,8 +101,9 @@ module ActiveRecord
101
101
  timestamp = updated_at_before_type_cast
102
102
  if can_use_fast_cache_version?(timestamp)
103
103
  raw_timestamp_to_cache_version(timestamp)
104
+
104
105
  elsif timestamp = updated_at
105
- timestamp.utc.to_s(cache_timestamp_format)
106
+ timestamp.utc.to_formatted_s(cache_timestamp_format)
106
107
  end
107
108
  elsif self.class.has_attribute?("updated_at")
108
109
  raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
@@ -177,7 +178,7 @@ module ActiveRecord
177
178
  def can_use_fast_cache_version?(timestamp)
178
179
  timestamp.is_a?(String) &&
179
180
  cache_timestamp_format == :usec &&
180
- default_timezone == :utc &&
181
+ ActiveRecord.default_timezone == :utc &&
181
182
  !updated_at_came_from_user?
182
183
  end
183
184
 
@@ -10,15 +10,13 @@ module ActiveRecord
10
10
  # This is enabled by default. To disable this functionality set
11
11
  # `use_metadata_table` to false in your database configuration.
12
12
  class InternalMetadata < ActiveRecord::Base # :nodoc:
13
+ self.record_timestamps = true
14
+
13
15
  class << self
14
16
  def enabled?
15
17
  ActiveRecord::Base.connection.use_metadata_table?
16
18
  end
17
19
 
18
- def _internal?
19
- true
20
- end
21
-
22
20
  def primary_key
23
21
  "key"
24
22
  end
@@ -36,7 +34,7 @@ module ActiveRecord
36
34
  def [](key)
37
35
  return unless enabled?
38
36
 
39
- where(key: key).pluck(:value).first
37
+ where(key: key).pick(:value)
40
38
  end
41
39
 
42
40
  # Creates an internal metadata table with columns +key+ and +value+
@@ -2,50 +2,13 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module LegacyYamlAdapter # :nodoc:
5
- def self.convert(klass, coder)
5
+ def self.convert(coder)
6
6
  return coder unless coder.is_a?(Psych::Coder)
7
7
 
8
8
  case coder["active_record_yaml_version"]
9
9
  when 1, 2 then coder
10
10
  else
11
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
12
- YAML loading from legacy format older than Rails 5.0 is deprecated
13
- and will be removed in Rails 6.2.
14
- MSG
15
- if coder["attributes"].is_a?(ActiveModel::AttributeSet)
16
- Rails420.convert(klass, coder)
17
- else
18
- Rails41.convert(klass, coder)
19
- end
20
- end
21
- end
22
-
23
- module Rails420 # :nodoc:
24
- def self.convert(klass, coder)
25
- attribute_set = coder["attributes"]
26
-
27
- klass.attribute_names.each do |attr_name|
28
- attribute = attribute_set[attr_name]
29
- if attribute.type.is_a?(Delegator)
30
- type_from_klass = klass.type_for_attribute(attr_name)
31
- attribute_set[attr_name] = attribute.with_type(type_from_klass)
32
- end
33
- end
34
-
35
- coder
36
- end
37
- end
38
-
39
- module Rails41 # :nodoc:
40
- def self.convert(klass, coder)
41
- attributes = klass.attributes_builder
42
- .build_from_database(coder["attributes"])
43
- new_record = coder["attributes"][klass.primary_key].blank?
44
-
45
- {
46
- "attributes" => attributes,
47
- "new_record" => new_record,
48
- }
11
+ raise("Active Record doesn't know how to load YAML with this format.")
49
12
  end
50
13
  end
51
14
  end
@@ -56,11 +56,11 @@ module ActiveRecord
56
56
  class_attribute :lock_optimistically, instance_writer: false, default: true
57
57
  end
58
58
 
59
- def locking_enabled? #:nodoc:
59
+ def locking_enabled? # :nodoc:
60
60
  self.class.locking_enabled?
61
61
  end
62
62
 
63
- def increment!(*, **) #:nodoc:
63
+ def increment!(*, **) # :nodoc:
64
64
  super.tap do
65
65
  if locking_enabled?
66
66
  self[self.class.locking_column] += 1
@@ -90,7 +90,9 @@ module ActiveRecord
90
90
  begin
91
91
  locking_column = self.class.locking_column
92
92
  lock_attribute_was = @attributes[locking_column]
93
- lock_value_for_database = _lock_value_for_database(locking_column)
93
+
94
+ update_constraints = _primary_key_constraints_hash
95
+ update_constraints[locking_column] = _lock_value_for_database(locking_column)
94
96
 
95
97
  attribute_names = attribute_names.dup if attribute_names.frozen?
96
98
  attribute_names << locking_column
@@ -99,8 +101,7 @@ module ActiveRecord
99
101
 
100
102
  affected_rows = self.class._update_record(
101
103
  attributes_with_values(attribute_names),
102
- @primary_key => id_in_database,
103
- locking_column => lock_value_for_database
104
+ update_constraints
104
105
  )
105
106
 
106
107
  if affected_rows != 1
@@ -121,10 +122,10 @@ module ActiveRecord
121
122
 
122
123
  locking_column = self.class.locking_column
123
124
 
124
- affected_rows = self.class._delete_record(
125
- @primary_key => id_in_database,
126
- locking_column => _lock_value_for_database(locking_column)
127
- )
125
+ delete_constraints = _primary_key_constraints_hash
126
+ delete_constraints[locking_column] = _lock_value_for_database(locking_column)
127
+
128
+ affected_rows = self.class._delete_record(delete_constraints)
128
129
 
129
130
  if affected_rows != 1
130
131
  raise ActiveRecord::StaleObjectError.new(self, "destroy")
@@ -81,9 +81,15 @@ module ActiveRecord
81
81
 
82
82
  # Wraps the passed block in a transaction, locking the object
83
83
  # before yielding. You can pass the SQL locking clause
84
- # as argument (see <tt>lock!</tt>).
85
- def with_lock(lock = true)
86
- transaction do
84
+ # as an optional argument (see <tt>#lock!</tt>).
85
+ #
86
+ # You can also pass options like <tt>requires_new:</tt>, <tt>isolation:</tt>,
87
+ # and <tt>joinable:</tt> to the wrapping transaction (see
88
+ # <tt>ActiveRecord::ConnectionAdapters::DatabaseStatements#transaction</tt>).
89
+ def with_lock(*args)
90
+ transaction_opts = args.extract_options!
91
+ lock = args.present? ? args.first : true
92
+ transaction(**transaction_opts) do
87
93
  lock!(lock)
88
94
  yield
89
95
  end