activerecord 6.1.3.2 → 7.0.0.alpha2

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +734 -1058
  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 +35 -7
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +16 -6
  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 +1 -1
  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 +24 -25
  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/preloader/association.rb +161 -49
  25. data/lib/active_record/associations/preloader/batch.rb +51 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +37 -11
  28. data/lib/active_record/associations/preloader.rb +46 -110
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +1 -1
  31. data/lib/active_record/associations.rb +76 -81
  32. data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +41 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +6 -9
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +3 -18
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +11 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -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 +31 -558
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
  61. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  66. data/lib/active_record/connection_adapters/pool_config.rb +1 -3
  67. data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  69. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  76. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
  82. data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
  83. data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
  84. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
  85. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  86. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  87. data/lib/active_record/connection_adapters.rb +8 -5
  88. data/lib/active_record/connection_handling.rb +20 -38
  89. data/lib/active_record/core.rb +129 -117
  90. data/lib/active_record/database_configurations/database_config.rb +12 -0
  91. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  92. data/lib/active_record/database_configurations/url_config.rb +2 -2
  93. data/lib/active_record/database_configurations.rb +18 -9
  94. data/lib/active_record/delegated_type.rb +33 -11
  95. data/lib/active_record/destroy_association_async_job.rb +1 -1
  96. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  97. data/lib/active_record/dynamic_matchers.rb +1 -1
  98. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  99. data/lib/active_record/encryption/cipher.rb +53 -0
  100. data/lib/active_record/encryption/config.rb +44 -0
  101. data/lib/active_record/encryption/configurable.rb +61 -0
  102. data/lib/active_record/encryption/context.rb +35 -0
  103. data/lib/active_record/encryption/contexts.rb +72 -0
  104. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  105. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  106. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  107. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  108. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  109. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  110. data/lib/active_record/encryption/encryptor.rb +155 -0
  111. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  112. data/lib/active_record/encryption/errors.rb +15 -0
  113. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  114. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  115. data/lib/active_record/encryption/key.rb +28 -0
  116. data/lib/active_record/encryption/key_generator.rb +42 -0
  117. data/lib/active_record/encryption/key_provider.rb +46 -0
  118. data/lib/active_record/encryption/message.rb +33 -0
  119. data/lib/active_record/encryption/message_serializer.rb +80 -0
  120. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  121. data/lib/active_record/encryption/properties.rb +76 -0
  122. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  123. data/lib/active_record/encryption/scheme.rb +99 -0
  124. data/lib/active_record/encryption.rb +55 -0
  125. data/lib/active_record/enum.rb +44 -46
  126. data/lib/active_record/errors.rb +66 -3
  127. data/lib/active_record/fixture_set/file.rb +15 -1
  128. data/lib/active_record/fixture_set/table_row.rb +40 -5
  129. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  130. data/lib/active_record/fixtures.rb +16 -11
  131. data/lib/active_record/future_result.rb +139 -0
  132. data/lib/active_record/gem_version.rb +4 -4
  133. data/lib/active_record/inheritance.rb +55 -17
  134. data/lib/active_record/insert_all.rb +39 -6
  135. data/lib/active_record/integration.rb +1 -1
  136. data/lib/active_record/internal_metadata.rb +3 -5
  137. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  138. data/lib/active_record/locking/optimistic.rb +10 -9
  139. data/lib/active_record/log_subscriber.rb +6 -2
  140. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  141. data/lib/active_record/middleware/database_selector.rb +8 -3
  142. data/lib/active_record/migration/command_recorder.rb +4 -4
  143. data/lib/active_record/migration/compatibility.rb +83 -1
  144. data/lib/active_record/migration/join_table.rb +1 -1
  145. data/lib/active_record/migration.rb +109 -79
  146. data/lib/active_record/model_schema.rb +46 -32
  147. data/lib/active_record/nested_attributes.rb +3 -3
  148. data/lib/active_record/no_touching.rb +2 -2
  149. data/lib/active_record/null_relation.rb +2 -6
  150. data/lib/active_record/persistence.rb +134 -45
  151. data/lib/active_record/query_cache.rb +2 -2
  152. data/lib/active_record/query_logs.rb +203 -0
  153. data/lib/active_record/querying.rb +15 -5
  154. data/lib/active_record/railtie.rb +117 -17
  155. data/lib/active_record/railties/controller_runtime.rb +1 -1
  156. data/lib/active_record/railties/databases.rake +83 -58
  157. data/lib/active_record/readonly_attributes.rb +11 -0
  158. data/lib/active_record/reflection.rb +45 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  160. data/lib/active_record/relation/batches.rb +3 -3
  161. data/lib/active_record/relation/calculations.rb +42 -25
  162. data/lib/active_record/relation/delegation.rb +6 -6
  163. data/lib/active_record/relation/finder_methods.rb +32 -23
  164. data/lib/active_record/relation/merger.rb +20 -13
  165. data/lib/active_record/relation/predicate_builder.rb +1 -6
  166. data/lib/active_record/relation/query_attribute.rb +5 -11
  167. data/lib/active_record/relation/query_methods.rb +233 -50
  168. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  169. data/lib/active_record/relation/spawn_methods.rb +2 -2
  170. data/lib/active_record/relation/where_clause.rb +22 -15
  171. data/lib/active_record/relation.rb +170 -87
  172. data/lib/active_record/result.rb +17 -2
  173. data/lib/active_record/runtime_registry.rb +2 -4
  174. data/lib/active_record/sanitization.rb +11 -7
  175. data/lib/active_record/schema_dumper.rb +3 -3
  176. data/lib/active_record/schema_migration.rb +0 -4
  177. data/lib/active_record/scoping/default.rb +62 -15
  178. data/lib/active_record/scoping/named.rb +3 -11
  179. data/lib/active_record/scoping.rb +40 -22
  180. data/lib/active_record/serialization.rb +1 -1
  181. data/lib/active_record/signed_id.rb +1 -1
  182. data/lib/active_record/statement_cache.rb +2 -2
  183. data/lib/active_record/tasks/database_tasks.rb +107 -23
  184. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  185. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
  186. data/lib/active_record/test_databases.rb +1 -1
  187. data/lib/active_record/test_fixtures.rb +45 -4
  188. data/lib/active_record/timestamp.rb +3 -4
  189. data/lib/active_record/transactions.rb +9 -14
  190. data/lib/active_record/translation.rb +2 -2
  191. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  192. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  193. data/lib/active_record/type/internal/timezone.rb +2 -2
  194. data/lib/active_record/type/serialized.rb +1 -1
  195. data/lib/active_record/type/type_map.rb +17 -20
  196. data/lib/active_record/type.rb +1 -2
  197. data/lib/active_record/validations/associated.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +1 -1
  199. data/lib/active_record.rb +170 -2
  200. data/lib/arel/attributes/attribute.rb +0 -8
  201. data/lib/arel/collectors/bind.rb +2 -2
  202. data/lib/arel/collectors/composite.rb +3 -3
  203. data/lib/arel/collectors/sql_string.rb +1 -1
  204. data/lib/arel/collectors/substitute_binds.rb +1 -1
  205. data/lib/arel/crud.rb +18 -22
  206. data/lib/arel/delete_manager.rb +2 -4
  207. data/lib/arel/insert_manager.rb +2 -3
  208. data/lib/arel/nodes/casted.rb +1 -1
  209. data/lib/arel/nodes/delete_statement.rb +8 -13
  210. data/lib/arel/nodes/homogeneous_in.rb +4 -0
  211. data/lib/arel/nodes/insert_statement.rb +2 -2
  212. data/lib/arel/nodes/select_core.rb +2 -2
  213. data/lib/arel/nodes/select_statement.rb +2 -2
  214. data/lib/arel/nodes/update_statement.rb +3 -2
  215. data/lib/arel/predications.rb +3 -3
  216. data/lib/arel/select_manager.rb +10 -4
  217. data/lib/arel/table.rb +0 -1
  218. data/lib/arel/tree_manager.rb +0 -12
  219. data/lib/arel/update_manager.rb +2 -4
  220. data/lib/arel/visitors/dot.rb +80 -90
  221. data/lib/arel/visitors/mysql.rb +6 -1
  222. data/lib/arel/visitors/postgresql.rb +0 -10
  223. data/lib/arel/visitors/to_sql.rb +44 -3
  224. data/lib/arel.rb +1 -1
  225. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  227. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  228. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  229. metadata +55 -16
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  def self.run
29
29
  pools = []
30
30
 
31
- if ActiveRecord::Base.legacy_connection_handling
31
+ if ActiveRecord.legacy_connection_handling
32
32
  ActiveRecord::Base.connection_handlers.each do |key, handler|
33
33
  pools.concat(handler.connection_pool_list.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
34
34
  end
@@ -42,7 +42,7 @@ module ActiveRecord
42
42
  def self.complete(pools)
43
43
  pools.each { |pool| pool.disable_query_cache! }
44
44
 
45
- if ActiveRecord::Base.legacy_connection_handling
45
+ if ActiveRecord.legacy_connection_handling
46
46
  ActiveRecord::Base.connection_handlers.each do |_, handler|
47
47
  handler.connection_pool_list.each do |pool|
48
48
  pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/attribute_accessors_per_thread"
4
+
5
+ module ActiveRecord
6
+ # = Active Record Query Logs
7
+ #
8
+ # Automatically tag SQL queries with runtime information.
9
+ #
10
+ # Default tags available for use:
11
+ #
12
+ # * +application+
13
+ # * +pid+
14
+ # * +socket+
15
+ # * +db_host+
16
+ # * +database+
17
+ #
18
+ # _Action Controller and Active Job tags are also defined when used in Rails:_
19
+ #
20
+ # * +controller+
21
+ # * +action+
22
+ # * +job+
23
+ #
24
+ # The tags used in a query can be configured directly:
25
+ #
26
+ # ActiveRecord::QueryLogs.tags = [ :application, :controller, :action, :job ]
27
+ #
28
+ # or via Rails configuration:
29
+ #
30
+ # config.active_record.query_log_tags = [ :application, :controller, :action, :job ]
31
+ #
32
+ # To add new comment tags, add a hash to the tags array containing the keys and values you
33
+ # want to add to the comment. Dynamic content can be created by setting a proc or lambda value in a hash,
34
+ # and can reference any value stored in the +context+ object.
35
+ #
36
+ # Example:
37
+ #
38
+ # tags = [
39
+ # :application,
40
+ # {
41
+ # custom_tag: ->(context) { context[:controller].controller_name },
42
+ # custom_value: -> { Custom.value },
43
+ # }
44
+ # ]
45
+ # ActiveRecord::QueryLogs.tags = tags
46
+ #
47
+ # The QueryLogs +context+ can be manipulated via +update_context+ & +set_context+ methods.
48
+ #
49
+ # Direct updates to a context value:
50
+ #
51
+ # ActiveRecord::QueryLogs.update_context(foo: Bar.new)
52
+ #
53
+ # Temporary updates limited to the execution of a block:
54
+ #
55
+ # ActiveRecord::QueryLogs.set_context(foo: Bar.new) do
56
+ # posts = Post.all
57
+ # end
58
+ #
59
+ # Tag comments can be prepended to the query:
60
+ #
61
+ # ActiveRecord::QueryLogs.prepend_comment = true
62
+ #
63
+ # For applications where the content will not change during the lifetime of
64
+ # the request or job execution, the tags can be cached for reuse in every query:
65
+ #
66
+ # ActiveRecord::QueryLogs.cache_query_log_tags = true
67
+ #
68
+ # This option can be set during application configuration or in a Rails initializer:
69
+ #
70
+ # config.active_record.cache_query_log_tags = true
71
+ module QueryLogs
72
+ mattr_accessor :taggings, instance_accessor: false, default: {}
73
+ mattr_accessor :tags, instance_accessor: false, default: [ :application ]
74
+ mattr_accessor :prepend_comment, instance_accessor: false, default: false
75
+ mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
76
+ thread_mattr_accessor :cached_comment, instance_accessor: false
77
+
78
+ class NullObject # :nodoc:
79
+ def method_missing(method, *args, &block)
80
+ NullObject.new
81
+ end
82
+
83
+ def nil?
84
+ true
85
+ end
86
+
87
+ private
88
+ def respond_to_missing?(method, include_private = false)
89
+ true
90
+ end
91
+ end
92
+
93
+ class << self
94
+ # Updates the context used to construct tags in the SQL comment.
95
+ # Resets the cached comment if <tt>cache_query_log_tags</tt> is +true+.
96
+ def update_context(**options)
97
+ context.merge!(**options.symbolize_keys)
98
+ self.cached_comment = nil
99
+ end
100
+
101
+ # Updates the context used to construct tags in the SQL comment during
102
+ # execution of the provided block. Resets the provided keys to their
103
+ # previous value once the block exits.
104
+ def set_context(**options)
105
+ keys = options.keys
106
+ previous_context = keys.zip(context.values_at(*keys)).to_h
107
+ update_context(**options)
108
+ yield if block_given?
109
+ ensure
110
+ update_context(**previous_context)
111
+ end
112
+
113
+ # Temporarily tag any query executed within `&block`. Can be nested.
114
+ def with_tag(tag, &block)
115
+ inline_tags.push(tag)
116
+ yield if block_given?
117
+ ensure
118
+ inline_tags.pop
119
+ end
120
+
121
+ def call(sql) # :nodoc:
122
+ parts = self.comments
123
+ if prepend_comment
124
+ parts << sql
125
+ else
126
+ parts.unshift(sql)
127
+ end
128
+ parts.join(" ")
129
+ end
130
+
131
+ private
132
+ # Returns an array of comments which need to be added to the query, comprised
133
+ # of configured and inline tags.
134
+ def comments
135
+ [ comment, inline_comment ].compact
136
+ end
137
+
138
+ # Returns an SQL comment +String+ containing the query log tags.
139
+ # Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
140
+ def comment
141
+ if cache_query_log_tags
142
+ self.cached_comment ||= uncached_comment
143
+ else
144
+ uncached_comment
145
+ end
146
+ end
147
+
148
+ def uncached_comment
149
+ content = tag_content
150
+ if content.present?
151
+ "/*#{escape_sql_comment(content)}*/"
152
+ end
153
+ end
154
+
155
+ # Returns a +String+ containing any inline comments from +with_tag+.
156
+ def inline_comment
157
+ return nil unless inline_tags.present?
158
+ "/*#{escape_sql_comment(inline_tag_content)}*/"
159
+ end
160
+
161
+ # Return the set of active inline tags from +with_tag+.
162
+ def inline_tags
163
+ if context[:inline_tags].nil?
164
+ context[:inline_tags] = []
165
+ else
166
+ context[:inline_tags]
167
+ end
168
+ end
169
+
170
+ def context
171
+ Thread.current[:active_record_query_log_tags_context] ||= Hash.new { NullObject.new }
172
+ end
173
+
174
+ def escape_sql_comment(content)
175
+ content.to_s.gsub(%r{ (/ (?: | \g<1>) \*) \+? \s* | \s* (\* (?: | \g<2>) /) }x, "")
176
+ end
177
+
178
+ def tag_content
179
+ tags.flat_map { |i| [*i] }.filter_map do |tag|
180
+ key, handler = tag
181
+ handler ||= taggings[key]
182
+
183
+ val = if handler.nil?
184
+ context[key]
185
+ elsif handler.respond_to?(:call)
186
+ if handler.arity == 0
187
+ handler.call
188
+ else
189
+ handler.call(context)
190
+ end
191
+ else
192
+ handler
193
+ end
194
+ "#{key}:#{val}" unless val.nil?
195
+ end.join(",")
196
+ end
197
+
198
+ def inline_tag_content
199
+ inline_tags.join
200
+ end
201
+ end
202
+ end
203
+ end
@@ -3,7 +3,7 @@
3
3
  module ActiveRecord
4
4
  module Querying
5
5
  QUERYING_METHODS = [
6
- :find, :find_by, :find_by!, :take, :take!, :first, :first!, :last, :last!,
6
+ :find, :find_by, :find_by!, :take, :take!, :sole, :find_sole_by, :first, :first!, :last, :last!,
7
7
  :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!,
8
8
  :forty_two, :forty_two!, :third_to_last, :third_to_last!, :second_to_last, :second_to_last!,
9
9
  :exists?, :any?, :many?, :none?, :one?,
@@ -12,12 +12,12 @@ module ActiveRecord
12
12
  :create_or_find_by, :create_or_find_by!,
13
13
  :destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
14
14
  :find_each, :find_in_batches, :in_batches,
15
- :select, :reselect, :order, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
16
- :where, :rewhere, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
15
+ :select, :reselect, :order, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
16
+ :where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
17
17
  :and, :or, :annotate, :optimizer_hints, :extending,
18
18
  :having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
19
19
  :count, :average, :minimum, :maximum, :sum, :calculate,
20
- :pluck, :pick, :ids, :strict_loading
20
+ :pluck, :pick, :ids, :strict_loading, :excluding, :without
21
21
  ].freeze # :nodoc:
22
22
  delegate(*QUERYING_METHODS, to: :all)
23
23
 
@@ -43,8 +43,18 @@ module ActiveRecord
43
43
  #
44
44
  # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
45
45
  # Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
46
+ #
47
+ # Note that building your own SQL query string from user input may expose your application to
48
+ # injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
46
49
  def find_by_sql(sql, binds = [], preparable: nil, &block)
47
- result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
50
+ _load_from_sql(_query_by_sql(sql, binds, preparable: preparable), &block)
51
+ end
52
+
53
+ def _query_by_sql(sql, binds = [], preparable: nil, async: false) # :nodoc:
54
+ connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async)
55
+ end
56
+
57
+ def _load_from_sql(result_set, &block) # :nodoc:
48
58
  column_types = result_set.column_types
49
59
 
50
60
  unless column_types.empty?
@@ -15,6 +15,7 @@ module ActiveRecord
15
15
  # = Active Record Railtie
16
16
  class Railtie < Rails::Railtie # :nodoc:
17
17
  config.active_record = ActiveSupport::OrderedOptions.new
18
+ config.active_record.encryption = ActiveSupport::OrderedOptions.new
18
19
 
19
20
  config.app_generators.orm :active_record, migration: true,
20
21
  timestamps: true
@@ -30,6 +31,10 @@ module ActiveRecord
30
31
  config.active_record.check_schema_cache_dump_version = true
31
32
  config.active_record.maintain_test_schema = true
32
33
  config.active_record.has_many_inversing = false
34
+ config.active_record.sqlite3_production_warning = true
35
+ config.active_record.query_log_tags_enabled = false
36
+ config.active_record.query_log_tags = [ :application ]
37
+ config.active_record.cache_query_log_tags = false
33
38
 
34
39
  config.active_record.queues = ActiveSupport::InheritableOptions.new
35
40
 
@@ -60,7 +65,7 @@ module ActiveRecord
60
65
  console.level = Rails.logger.level
61
66
  Rails.logger.extend ActiveSupport::Logger.broadcast console
62
67
  end
63
- ActiveRecord::Base.verbose_query_logs = false
68
+ ActiveRecord.verbose_query_logs = false
64
69
  end
65
70
 
66
71
  runner do
@@ -70,7 +75,6 @@ module ActiveRecord
70
75
  initializer "active_record.initialize_timezone" do
71
76
  ActiveSupport.on_load(:active_record) do
72
77
  self.time_zone_aware_attributes = true
73
- self.default_timezone = :utc
74
78
  end
75
79
  end
76
80
 
@@ -83,7 +87,7 @@ module ActiveRecord
83
87
  end
84
88
 
85
89
  initializer "active_record.migration_error" do |app|
86
- if config.active_record.delete(:migration_error) == :page_load
90
+ if config.active_record.migration_error == :page_load
87
91
  config.app_middleware.insert_after ::ActionDispatch::Callbacks,
88
92
  ActiveRecord::Migration::CheckPending,
89
93
  file_watcher: app.config.file_watcher
@@ -91,9 +95,9 @@ module ActiveRecord
91
95
  end
92
96
 
93
97
  initializer "active_record.database_selector" do
94
- if options = config.active_record.delete(:database_selector)
95
- resolver = config.active_record.delete(:database_resolver)
96
- operations = config.active_record.delete(:database_resolver_context)
98
+ if options = config.active_record.database_selector
99
+ resolver = config.active_record.database_resolver
100
+ operations = config.active_record.database_resolver_context
97
101
  config.app_middleware.use ActiveRecord::Middleware::DatabaseSelector, resolver, operations, options
98
102
  end
99
103
  end
@@ -124,9 +128,9 @@ To keep using the current cache store, you can turn off cache versioning entirel
124
128
  end
125
129
 
126
130
  initializer "active_record.check_schema_cache_dump" do
127
- check_schema_cache_dump_version = config.active_record.delete(:check_schema_cache_dump_version)
131
+ check_schema_cache_dump_version = config.active_record.check_schema_cache_dump_version
128
132
 
129
- if config.active_record.delete(:use_schema_cache_dump)
133
+ if config.active_record.use_schema_cache_dump
130
134
  config.after_initialize do |app|
131
135
  ActiveSupport.on_load(:active_record) do
132
136
  db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first
@@ -149,11 +153,12 @@ To keep using the current cache store, you can turn off cache versioning entirel
149
153
  next if current_version.nil?
150
154
 
151
155
  if cache.version != current_version
152
- warn "Ignoring #{filename} because it has expired. The current schema version is #{current_version}, but the one in the cache is #{cache.version}."
156
+ warn "Ignoring #{filename} because it has expired. The current schema version is #{current_version}, but the one in the schema cache file is #{cache.version}."
153
157
  next
154
158
  end
155
159
  end
156
160
 
161
+ Rails.logger.info("Using schema cache file #{filename}")
157
162
  connection_pool.set_schema_cache(cache)
158
163
  end
159
164
  end
@@ -166,10 +171,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
166
171
  if app.config.eager_load
167
172
  begin
168
173
  descendants.each do |model|
169
- # SchemaMigration and InternalMetadata both override `table_exists?`
170
- # to bypass the schema cache, so skip them to avoid the extra queries.
171
- next if model._internal?
172
-
173
174
  # If the schema cache was loaded from a dump, we can use it without connecting
174
175
  schema_cache = model.connection_pool.schema_cache
175
176
 
@@ -201,12 +202,56 @@ To keep using the current cache store, you can turn off cache versioning entirel
201
202
  end
202
203
  end
203
204
 
205
+ SQLITE3_PRODUCTION_WARN = "You are running SQLite in production, this is generally not recommended."\
206
+ " You can disable this warning by setting \"config.active_record.sqlite3_production_warning=false\"."
207
+ initializer "active_record.sqlite3_production_warning" do
208
+ if config.active_record.sqlite3_production_warning && Rails.env.production?
209
+ ActiveSupport.on_load(:active_record_sqlite3adapter) do
210
+ Rails.logger.warn(SQLITE3_PRODUCTION_WARN)
211
+ end
212
+ end
213
+ end
214
+
204
215
  initializer "active_record.set_configs" do |app|
216
+ configs = app.config.active_record
217
+
218
+ config.after_initialize do
219
+ configs.each do |k, v|
220
+ next if k == :encryption
221
+ setter = "#{k}="
222
+ if ActiveRecord.respond_to?(setter)
223
+ ActiveRecord.send(setter, v)
224
+ end
225
+ end
226
+ end
227
+
205
228
  ActiveSupport.on_load(:active_record) do
206
- configs = app.config.active_record
229
+ # Configs used in other initializers
230
+ configs = configs.except(
231
+ :migration_error,
232
+ :database_selector,
233
+ :database_resolver,
234
+ :database_resolver_context,
235
+ :query_log_tags_enabled,
236
+ :query_log_tags,
237
+ :cache_query_log_tags,
238
+ :sqlite3_production_warning,
239
+ :check_schema_cache_dump_version,
240
+ :use_schema_cache_dump
241
+ )
207
242
 
208
243
  configs.each do |k, v|
209
- send "#{k}=", v
244
+ next if k == :encryption
245
+ setter = "#{k}="
246
+ # Some existing initializers might rely on Active Record configuration
247
+ # being copied from the config object to their actual destination when
248
+ # `ActiveRecord::Base` is loaded.
249
+ # So to preserve backward compatibility we copy the config a second time.
250
+ if ActiveRecord.respond_to?(setter)
251
+ ActiveRecord.send(setter, v)
252
+ else
253
+ send(setter, v)
254
+ end
210
255
  end
211
256
  end
212
257
  end
@@ -215,10 +260,11 @@ To keep using the current cache store, you can turn off cache versioning entirel
215
260
  # and then establishes the connection.
216
261
  initializer "active_record.initialize_database" do
217
262
  ActiveSupport.on_load(:active_record) do
218
- if ActiveRecord::Base.legacy_connection_handling
219
- self.connection_handlers = { writing_role => ActiveRecord::Base.default_connection_handler }
263
+ if ActiveRecord.legacy_connection_handling
264
+ self.connection_handlers = { ActiveRecord.writing_role => ActiveRecord::Base.default_connection_handler }
220
265
  end
221
266
  self.configurations = Rails.application.config.database_configuration
267
+
222
268
  establish_connection
223
269
  end
224
270
  end
@@ -244,6 +290,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
244
290
 
245
291
  initializer "active_record.set_executor_hooks" do
246
292
  ActiveRecord::QueryCache.install_executor_hooks
293
+ ActiveRecord::AsynchronousQueriesTracker.install_executor_hooks
247
294
  end
248
295
 
249
296
  initializer "active_record.add_watchable_files" do |app|
@@ -279,5 +326,58 @@ To keep using the current cache store, you can turn off cache versioning entirel
279
326
  self.signed_id_verifier_secret ||= -> { Rails.application.key_generator.generate_key("active_record/signed_id") }
280
327
  end
281
328
  end
329
+
330
+ initializer "active_record_encryption.configuration" do |app|
331
+ ActiveRecord::Encryption.configure \
332
+ primary_key: app.credentials.dig(:active_record_encryption, :primary_key),
333
+ deterministic_key: app.credentials.dig(:active_record_encryption, :deterministic_key),
334
+ key_derivation_salt: app.credentials.dig(:active_record_encryption, :key_derivation_salt),
335
+ **config.active_record.encryption
336
+
337
+ ActiveSupport.on_load(:active_record) do
338
+ # Support extended queries for deterministic attributes and validations
339
+ if ActiveRecord::Encryption.config.extend_queries
340
+ ActiveRecord::Encryption::ExtendedDeterministicQueries.install_support
341
+ ActiveRecord::Encryption::ExtendedDeterministicUniquenessValidator.install_support
342
+ end
343
+ end
344
+
345
+ ActiveSupport.on_load(:active_record_fixture_set) do
346
+ # Encrypt active record fixtures
347
+ if ActiveRecord::Encryption.config.encrypt_fixtures
348
+ ActiveRecord::Fixture.prepend ActiveRecord::Encryption::EncryptedFixtures
349
+ end
350
+ end
351
+
352
+ # Filtered params
353
+ ActiveSupport.on_load(:action_controller) do
354
+ if ActiveRecord::Encryption.config.add_to_filter_parameters
355
+ ActiveRecord::Encryption.install_auto_filtered_parameters(app)
356
+ end
357
+ end
358
+ end
359
+
360
+ initializer "active_record.query_log_tags_config" do |app|
361
+ config.after_initialize do
362
+ if app.config.active_record.query_log_tags_enabled
363
+ ActiveRecord.query_transformers << ActiveRecord::QueryLogs
364
+ ActiveRecord::QueryLogs.taggings.merge!(
365
+ application: Rails.application.class.name.split("::").first,
366
+ pid: -> { Process.pid },
367
+ socket: -> { ActiveRecord::Base.connection_db_config.socket },
368
+ db_host: -> { ActiveRecord::Base.connection_db_config.host },
369
+ database: -> { ActiveRecord::Base.connection_db_config.database }
370
+ )
371
+
372
+ if app.config.active_record.query_log_tags.present?
373
+ ActiveRecord::QueryLogs.tags = app.config.active_record.query_log_tags
374
+ end
375
+
376
+ if app.config.active_record.cache_query_log_tags
377
+ ActiveRecord::QueryLogs.cache_query_log_tags = true
378
+ end
379
+ end
380
+ end
381
+ end
282
382
  end
283
383
  end