activerecord 6.1.7.4 → 7.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1449 -1014
  3. data/README.rdoc +3 -3
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +19 -21
  17. data/lib/active_record/associations/collection_proxy.rb +10 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +14 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +15 -7
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +138 -100
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -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 +49 -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 +8 -6
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  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 +19 -22
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +17 -28
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +14 -16
  47. data/lib/active_record/coders/yaml_column.rb +4 -8
  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 +52 -23
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +82 -25
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -84
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +45 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  70. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -1
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -17
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +71 -71
  83. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  86. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +40 -21
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -106
  90. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  93. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  94. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +97 -32
  95. data/lib/active_record/connection_adapters.rb +6 -5
  96. data/lib/active_record/connection_handling.rb +49 -55
  97. data/lib/active_record/core.rb +123 -148
  98. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  99. data/lib/active_record/database_configurations/database_config.rb +12 -9
  100. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  101. data/lib/active_record/database_configurations/url_config.rb +2 -2
  102. data/lib/active_record/database_configurations.rb +15 -32
  103. data/lib/active_record/delegated_type.rb +53 -12
  104. data/lib/active_record/destroy_association_async_job.rb +1 -1
  105. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  106. data/lib/active_record/dynamic_matchers.rb +1 -1
  107. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  108. data/lib/active_record/encryption/cipher.rb +53 -0
  109. data/lib/active_record/encryption/config.rb +44 -0
  110. data/lib/active_record/encryption/configurable.rb +67 -0
  111. data/lib/active_record/encryption/context.rb +35 -0
  112. data/lib/active_record/encryption/contexts.rb +72 -0
  113. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  114. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  115. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  116. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  117. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  118. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  119. data/lib/active_record/encryption/encryptor.rb +155 -0
  120. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  121. data/lib/active_record/encryption/errors.rb +15 -0
  122. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  123. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  124. data/lib/active_record/encryption/key.rb +28 -0
  125. data/lib/active_record/encryption/key_generator.rb +42 -0
  126. data/lib/active_record/encryption/key_provider.rb +46 -0
  127. data/lib/active_record/encryption/message.rb +33 -0
  128. data/lib/active_record/encryption/message_serializer.rb +90 -0
  129. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  130. data/lib/active_record/encryption/properties.rb +76 -0
  131. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  132. data/lib/active_record/encryption/scheme.rb +99 -0
  133. data/lib/active_record/encryption.rb +55 -0
  134. data/lib/active_record/enum.rb +50 -43
  135. data/lib/active_record/errors.rb +67 -4
  136. data/lib/active_record/explain_registry.rb +11 -6
  137. data/lib/active_record/explain_subscriber.rb +1 -1
  138. data/lib/active_record/fixture_set/file.rb +15 -1
  139. data/lib/active_record/fixture_set/table_row.rb +41 -6
  140. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  141. data/lib/active_record/fixtures.rb +20 -23
  142. data/lib/active_record/future_result.rb +139 -0
  143. data/lib/active_record/gem_version.rb +5 -5
  144. data/lib/active_record/inheritance.rb +55 -17
  145. data/lib/active_record/insert_all.rb +80 -14
  146. data/lib/active_record/integration.rb +4 -3
  147. data/lib/active_record/internal_metadata.rb +1 -5
  148. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  149. data/lib/active_record/locking/optimistic.rb +36 -21
  150. data/lib/active_record/locking/pessimistic.rb +10 -4
  151. data/lib/active_record/log_subscriber.rb +23 -7
  152. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  153. data/lib/active_record/middleware/database_selector.rb +18 -6
  154. data/lib/active_record/middleware/shard_selector.rb +60 -0
  155. data/lib/active_record/migration/command_recorder.rb +8 -9
  156. data/lib/active_record/migration/compatibility.rb +91 -2
  157. data/lib/active_record/migration/join_table.rb +1 -1
  158. data/lib/active_record/migration.rb +115 -84
  159. data/lib/active_record/model_schema.rb +58 -59
  160. data/lib/active_record/nested_attributes.rb +13 -12
  161. data/lib/active_record/no_touching.rb +3 -3
  162. data/lib/active_record/null_relation.rb +2 -6
  163. data/lib/active_record/persistence.rb +228 -60
  164. data/lib/active_record/query_cache.rb +2 -2
  165. data/lib/active_record/query_logs.rb +149 -0
  166. data/lib/active_record/querying.rb +16 -6
  167. data/lib/active_record/railtie.rb +136 -22
  168. data/lib/active_record/railties/controller_runtime.rb +1 -1
  169. data/lib/active_record/railties/databases.rake +78 -136
  170. data/lib/active_record/readonly_attributes.rb +11 -0
  171. data/lib/active_record/reflection.rb +80 -49
  172. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  173. data/lib/active_record/relation/batches.rb +6 -6
  174. data/lib/active_record/relation/calculations.rb +92 -60
  175. data/lib/active_record/relation/delegation.rb +7 -7
  176. data/lib/active_record/relation/finder_methods.rb +31 -35
  177. data/lib/active_record/relation/merger.rb +20 -13
  178. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -1
  179. data/lib/active_record/relation/predicate_builder.rb +2 -6
  180. data/lib/active_record/relation/query_attribute.rb +5 -11
  181. data/lib/active_record/relation/query_methods.rb +285 -68
  182. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  183. data/lib/active_record/relation/spawn_methods.rb +2 -2
  184. data/lib/active_record/relation/where_clause.rb +10 -19
  185. data/lib/active_record/relation.rb +189 -88
  186. data/lib/active_record/result.rb +23 -11
  187. data/lib/active_record/runtime_registry.rb +9 -13
  188. data/lib/active_record/sanitization.rb +17 -12
  189. data/lib/active_record/schema.rb +38 -23
  190. data/lib/active_record/schema_dumper.rb +29 -19
  191. data/lib/active_record/schema_migration.rb +4 -4
  192. data/lib/active_record/scoping/default.rb +60 -13
  193. data/lib/active_record/scoping/named.rb +3 -11
  194. data/lib/active_record/scoping.rb +64 -34
  195. data/lib/active_record/serialization.rb +6 -1
  196. data/lib/active_record/signed_id.rb +3 -3
  197. data/lib/active_record/store.rb +2 -2
  198. data/lib/active_record/suppressor.rb +11 -15
  199. data/lib/active_record/table_metadata.rb +5 -1
  200. data/lib/active_record/tasks/database_tasks.rb +127 -60
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  203. data/lib/active_record/test_databases.rb +1 -1
  204. data/lib/active_record/test_fixtures.rb +9 -6
  205. data/lib/active_record/timestamp.rb +3 -4
  206. data/lib/active_record/transactions.rb +9 -14
  207. data/lib/active_record/translation.rb +3 -3
  208. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  209. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  210. data/lib/active_record/type/internal/timezone.rb +2 -2
  211. data/lib/active_record/type/serialized.rb +5 -5
  212. data/lib/active_record/type/type_map.rb +17 -20
  213. data/lib/active_record/type.rb +1 -2
  214. data/lib/active_record/validations/associated.rb +4 -4
  215. data/lib/active_record/validations/presence.rb +2 -2
  216. data/lib/active_record/validations/uniqueness.rb +4 -4
  217. data/lib/active_record/version.rb +1 -1
  218. data/lib/active_record.rb +225 -27
  219. data/lib/arel/attributes/attribute.rb +0 -8
  220. data/lib/arel/crud.rb +28 -22
  221. data/lib/arel/delete_manager.rb +18 -4
  222. data/lib/arel/filter_predications.rb +9 -0
  223. data/lib/arel/insert_manager.rb +2 -3
  224. data/lib/arel/nodes/casted.rb +1 -1
  225. data/lib/arel/nodes/delete_statement.rb +12 -13
  226. data/lib/arel/nodes/filter.rb +10 -0
  227. data/lib/arel/nodes/function.rb +1 -0
  228. data/lib/arel/nodes/insert_statement.rb +2 -2
  229. data/lib/arel/nodes/select_core.rb +2 -2
  230. data/lib/arel/nodes/select_statement.rb +2 -2
  231. data/lib/arel/nodes/update_statement.rb +8 -3
  232. data/lib/arel/nodes.rb +1 -0
  233. data/lib/arel/predications.rb +11 -3
  234. data/lib/arel/select_manager.rb +10 -4
  235. data/lib/arel/table.rb +0 -1
  236. data/lib/arel/tree_manager.rb +0 -12
  237. data/lib/arel/update_manager.rb +18 -4
  238. data/lib/arel/visitors/dot.rb +80 -90
  239. data/lib/arel/visitors/mysql.rb +8 -2
  240. data/lib/arel/visitors/postgresql.rb +0 -10
  241. data/lib/arel/visitors/to_sql.rb +58 -2
  242. data/lib/arel.rb +2 -1
  243. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  244. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  245. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  246. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  247. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  248. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  249. metadata +58 -14
@@ -0,0 +1,149 @@
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
+ # Escaping is performed on the string returned, however untrusted user input should not be used.
37
+ #
38
+ # Example:
39
+ #
40
+ # tags = [
41
+ # :application,
42
+ # {
43
+ # custom_tag: ->(context) { context[:controller]&.controller_name },
44
+ # custom_value: -> { Custom.value },
45
+ # }
46
+ # ]
47
+ # ActiveRecord::QueryLogs.tags = tags
48
+ #
49
+ # The QueryLogs +context+ can be manipulated via the +ActiveSupport::ExecutionContext.set+ method.
50
+ #
51
+ # Temporary updates limited to the execution of a block:
52
+ #
53
+ # ActiveSupport::ExecutionContext.set(foo: Bar.new) do
54
+ # posts = Post.all
55
+ # end
56
+ #
57
+ # Direct updates to a context value:
58
+ #
59
+ # ActiveSupport::ExecutionContext[:foo] = Bar.new
60
+ #
61
+ # Tag comments can be prepended to the query:
62
+ #
63
+ # ActiveRecord::QueryLogs.prepend_comment = true
64
+ #
65
+ # For applications where the content will not change during the lifetime of
66
+ # the request or job execution, the tags can be cached for reuse in every query:
67
+ #
68
+ # ActiveRecord::QueryLogs.cache_query_log_tags = true
69
+ #
70
+ # This option can be set during application configuration or in a Rails initializer:
71
+ #
72
+ # config.active_record.cache_query_log_tags = true
73
+ module QueryLogs
74
+ mattr_accessor :taggings, instance_accessor: false, default: {}
75
+ mattr_accessor :tags, instance_accessor: false, default: [ :application ]
76
+ mattr_accessor :prepend_comment, instance_accessor: false, default: false
77
+ mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
78
+ thread_mattr_accessor :cached_comment, instance_accessor: false
79
+
80
+ class << self
81
+ def call(sql) # :nodoc:
82
+ if prepend_comment
83
+ "#{self.comment} #{sql}"
84
+ else
85
+ "#{sql} #{self.comment}"
86
+ end.strip
87
+ end
88
+
89
+ def clear_cache # :nodoc:
90
+ self.cached_comment = nil
91
+ end
92
+
93
+ ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
94
+
95
+ private
96
+ # Returns an SQL comment +String+ containing the query log tags.
97
+ # Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
98
+ def comment
99
+ if cache_query_log_tags
100
+ self.cached_comment ||= uncached_comment
101
+ else
102
+ uncached_comment
103
+ end
104
+ end
105
+
106
+ def uncached_comment
107
+ content = tag_content
108
+ if content.present?
109
+ "/*#{escape_sql_comment(content)}*/"
110
+ end
111
+ end
112
+
113
+ def escape_sql_comment(content)
114
+ # Sanitize a string to appear within a SQL comment
115
+ # For compatibility, this also surrounding "/*+", "/*", and "*/"
116
+ # charcacters, possibly with single surrounding space.
117
+ # Then follows that by replacing any internal "*/" or "/ *" with
118
+ # "* /" or "/ *"
119
+ comment = content.to_s.dup
120
+ comment.gsub!(%r{\A\s*/\*\+?\s?|\s?\*/\s*\Z}, "")
121
+ comment.gsub!("*/", "* /")
122
+ comment.gsub!("/*", "/ *")
123
+ comment
124
+ end
125
+
126
+ def tag_content
127
+ context = ActiveSupport::ExecutionContext.to_h
128
+
129
+ tags.flat_map { |i| [*i] }.filter_map do |tag|
130
+ key, handler = tag
131
+ handler ||= taggings[key]
132
+
133
+ val = if handler.nil?
134
+ context[key]
135
+ elsif handler.respond_to?(:call)
136
+ if handler.arity == 0
137
+ handler.call
138
+ else
139
+ handler.call(context)
140
+ end
141
+ else
142
+ handler
143
+ end
144
+ "#{key}:#{val}" unless val.nil?
145
+ end.join(",")
146
+ end
147
+ end
148
+ end
149
+ 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
 
@@ -39,12 +39,22 @@ module ActiveRecord
39
39
  # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
40
40
  # # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "author"=>"Quentin"}>, ...]
41
41
  #
42
- # You can use the same string replacement techniques as you can with <tt>ActiveRecord::QueryMethods#where</tt>:
42
+ # You can use the same string replacement techniques as you can with ActiveRecord::QueryMethods#where :
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,12 @@ 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
78
+ end
79
+ end
80
+
81
+ initializer "active_record.postgresql_time_zone_aware_types" do
82
+ ActiveSupport.on_load(:active_record_postgresqladapter) do
83
+ ActiveRecord::Base.time_zone_aware_types << :timestamptz
74
84
  end
75
85
  end
76
86
 
@@ -83,21 +93,13 @@ module ActiveRecord
83
93
  end
84
94
 
85
95
  initializer "active_record.migration_error" do |app|
86
- if config.active_record.delete(:migration_error) == :page_load
96
+ if config.active_record.migration_error == :page_load
87
97
  config.app_middleware.insert_after ::ActionDispatch::Callbacks,
88
98
  ActiveRecord::Migration::CheckPending,
89
99
  file_watcher: app.config.file_watcher
90
100
  end
91
101
  end
92
102
 
93
- 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)
97
- config.app_middleware.use ActiveRecord::Middleware::DatabaseSelector, resolver, operations, options
98
- end
99
- end
100
-
101
103
  initializer "Check for cache versioning support" do
102
104
  config.after_initialize do |app|
103
105
  ActiveSupport.on_load(:active_record) do
@@ -124,9 +126,9 @@ To keep using the current cache store, you can turn off cache versioning entirel
124
126
  end
125
127
 
126
128
  initializer "active_record.check_schema_cache_dump" do
127
- check_schema_cache_dump_version = config.active_record.delete(:check_schema_cache_dump_version)
129
+ check_schema_cache_dump_version = config.active_record.check_schema_cache_dump_version
128
130
 
129
- if config.active_record.delete(:use_schema_cache_dump)
131
+ if config.active_record.use_schema_cache_dump && !config.active_record.lazily_load_schema_cache
130
132
  config.after_initialize do |app|
131
133
  ActiveSupport.on_load(:active_record) do
132
134
  db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first
@@ -149,11 +151,12 @@ To keep using the current cache store, you can turn off cache versioning entirel
149
151
  next if current_version.nil?
150
152
 
151
153
  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}."
154
+ 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
155
  next
154
156
  end
155
157
  end
156
158
 
159
+ Rails.logger.info("Using schema cache file #{filename}")
157
160
  connection_pool.set_schema_cache(cache)
158
161
  end
159
162
  end
@@ -166,10 +169,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
166
169
  if app.config.eager_load
167
170
  begin
168
171
  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
172
  # If the schema cache was loaded from a dump, we can use it without connecting
174
173
  schema_cache = model.connection_pool.schema_cache
175
174
 
@@ -201,12 +200,58 @@ To keep using the current cache store, you can turn off cache versioning entirel
201
200
  end
202
201
  end
203
202
 
203
+ SQLITE3_PRODUCTION_WARN = "You are running SQLite in production, this is generally not recommended."\
204
+ " You can disable this warning by setting \"config.active_record.sqlite3_production_warning=false\"."
205
+ initializer "active_record.sqlite3_production_warning" do
206
+ if config.active_record.sqlite3_production_warning && Rails.env.production?
207
+ ActiveSupport.on_load(:active_record_sqlite3adapter) do
208
+ Rails.logger.warn(SQLITE3_PRODUCTION_WARN)
209
+ end
210
+ end
211
+ end
212
+
204
213
  initializer "active_record.set_configs" do |app|
214
+ configs = app.config.active_record
215
+
216
+ config.after_initialize do
217
+ configs.each do |k, v|
218
+ next if k == :encryption
219
+ setter = "#{k}="
220
+ if ActiveRecord.respond_to?(setter)
221
+ ActiveRecord.send(setter, v)
222
+ end
223
+ end
224
+ end
225
+
205
226
  ActiveSupport.on_load(:active_record) do
206
- configs = app.config.active_record
227
+ # Configs used in other initializers
228
+ configs = configs.except(
229
+ :migration_error,
230
+ :database_selector,
231
+ :database_resolver,
232
+ :database_resolver_context,
233
+ :shard_selector,
234
+ :shard_resolver,
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,72 @@ 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, run_once: true) do
354
+ if ActiveRecord::Encryption.config.add_to_filter_parameters
355
+ ActiveRecord::Encryption.install_auto_filtered_parameters_hook(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
382
+
383
+ initializer "active_record.unregister_current_scopes_on_unload" do |app|
384
+ config.after_initialize do
385
+ unless app.config.cache_classes
386
+ Rails.autoloaders.main.on_unload do |_cpath, value, _abspath|
387
+ # Conditions are written this way to be robust against custom
388
+ # implementations of value#is_a? or value#<.
389
+ if Class === value && ActiveRecord::Base > value
390
+ value.current_scope = nil
391
+ end
392
+ end
393
+ end
394
+ end
395
+ end
282
396
  end
283
397
  end
@@ -5,7 +5,7 @@ require "active_record/log_subscriber"
5
5
 
6
6
  module ActiveRecord
7
7
  module Railties # :nodoc:
8
- module ControllerRuntime #:nodoc:
8
+ module ControllerRuntime # :nodoc:
9
9
  extend ActiveSupport::Concern
10
10
 
11
11
  module ClassMethods # :nodoc: