activerecord 7.0.8.7 → 7.1.0.beta1

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 (227) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1339 -1572
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +15 -16
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -9
  15. data/lib/active_record/associations/collection_proxy.rb +16 -11
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +20 -13
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +10 -8
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader.rb +12 -9
  23. data/lib/active_record/associations/singular_association.rb +1 -1
  24. data/lib/active_record/associations/through_association.rb +22 -11
  25. data/lib/active_record/associations.rb +193 -97
  26. data/lib/active_record/attribute_assignment.rb +0 -2
  27. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  29. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  30. data/lib/active_record/attribute_methods/query.rb +28 -16
  31. data/lib/active_record/attribute_methods/read.rb +18 -5
  32. data/lib/active_record/attribute_methods/serialization.rb +150 -31
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +105 -21
  35. data/lib/active_record/attributes.rb +3 -3
  36. data/lib/active_record/autosave_association.rb +55 -9
  37. data/lib/active_record/base.rb +7 -2
  38. data/lib/active_record/callbacks.rb +10 -24
  39. data/lib/active_record/coders/column_serializer.rb +61 -0
  40. data/lib/active_record/coders/json.rb +1 -1
  41. data/lib/active_record/coders/yaml_column.rb +70 -42
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  45. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +109 -32
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  49. data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
  50. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  51. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  52. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  53. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -122
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
  57. data/lib/active_record/connection_adapters/column.rb +9 -0
  58. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  59. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -143
  60. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
  61. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  62. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  63. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -12
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  67. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  68. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  69. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
  71. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  72. data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
  73. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  74. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  75. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  76. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +42 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
  79. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  80. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  81. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +42 -36
  82. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  83. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  84. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  85. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
  86. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  87. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  88. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  89. data/lib/active_record/connection_adapters.rb +3 -1
  90. data/lib/active_record/connection_handling.rb +71 -94
  91. data/lib/active_record/core.rb +128 -138
  92. data/lib/active_record/counter_cache.rb +46 -25
  93. data/lib/active_record/database_configurations/database_config.rb +9 -3
  94. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  95. data/lib/active_record/database_configurations/url_config.rb +17 -11
  96. data/lib/active_record/database_configurations.rb +86 -33
  97. data/lib/active_record/delegated_type.rb +8 -3
  98. data/lib/active_record/deprecator.rb +7 -0
  99. data/lib/active_record/destroy_association_async_job.rb +2 -0
  100. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  102. data/lib/active_record/encryption/config.rb +25 -1
  103. data/lib/active_record/encryption/configurable.rb +12 -19
  104. data/lib/active_record/encryption/context.rb +10 -3
  105. data/lib/active_record/encryption/contexts.rb +5 -1
  106. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  107. data/lib/active_record/encryption/encryptable_record.rb +36 -18
  108. data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
  109. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
  110. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +2 -2
  111. data/lib/active_record/encryption/key_generator.rb +12 -1
  112. data/lib/active_record/encryption/message_serializer.rb +2 -0
  113. data/lib/active_record/encryption/properties.rb +3 -3
  114. data/lib/active_record/encryption/scheme.rb +19 -22
  115. data/lib/active_record/encryption.rb +1 -0
  116. data/lib/active_record/enum.rb +113 -26
  117. data/lib/active_record/errors.rb +89 -15
  118. data/lib/active_record/explain.rb +23 -3
  119. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  120. data/lib/active_record/fixture_set/render_context.rb +2 -0
  121. data/lib/active_record/fixture_set/table_row.rb +29 -8
  122. data/lib/active_record/fixtures.rb +119 -71
  123. data/lib/active_record/future_result.rb +30 -5
  124. data/lib/active_record/gem_version.rb +4 -4
  125. data/lib/active_record/inheritance.rb +30 -16
  126. data/lib/active_record/insert_all.rb +55 -8
  127. data/lib/active_record/integration.rb +8 -8
  128. data/lib/active_record/internal_metadata.rb +118 -30
  129. data/lib/active_record/locking/pessimistic.rb +5 -2
  130. data/lib/active_record/log_subscriber.rb +29 -12
  131. data/lib/active_record/marshalling.rb +56 -0
  132. data/lib/active_record/message_pack.rb +124 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  134. data/lib/active_record/middleware/database_selector.rb +5 -7
  135. data/lib/active_record/middleware/shard_selector.rb +3 -1
  136. data/lib/active_record/migration/command_recorder.rb +100 -4
  137. data/lib/active_record/migration/compatibility.rb +131 -5
  138. data/lib/active_record/migration/default_strategy.rb +23 -0
  139. data/lib/active_record/migration/execution_strategy.rb +19 -0
  140. data/lib/active_record/migration.rb +213 -109
  141. data/lib/active_record/model_schema.rb +47 -27
  142. data/lib/active_record/nested_attributes.rb +28 -3
  143. data/lib/active_record/normalization.rb +158 -0
  144. data/lib/active_record/persistence.rb +183 -33
  145. data/lib/active_record/promise.rb +84 -0
  146. data/lib/active_record/query_cache.rb +3 -21
  147. data/lib/active_record/query_logs.rb +77 -52
  148. data/lib/active_record/query_logs_formatter.rb +41 -0
  149. data/lib/active_record/querying.rb +15 -2
  150. data/lib/active_record/railtie.rb +107 -45
  151. data/lib/active_record/railties/controller_runtime.rb +10 -5
  152. data/lib/active_record/railties/databases.rake +139 -145
  153. data/lib/active_record/railties/job_runtime.rb +23 -0
  154. data/lib/active_record/readonly_attributes.rb +32 -5
  155. data/lib/active_record/reflection.rb +169 -45
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  157. data/lib/active_record/relation/batches.rb +190 -61
  158. data/lib/active_record/relation/calculations.rb +152 -63
  159. data/lib/active_record/relation/delegation.rb +22 -8
  160. data/lib/active_record/relation/finder_methods.rb +85 -15
  161. data/lib/active_record/relation/merger.rb +2 -0
  162. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  163. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  164. data/lib/active_record/relation/predicate_builder.rb +26 -14
  165. data/lib/active_record/relation/query_attribute.rb +2 -1
  166. data/lib/active_record/relation/query_methods.rb +351 -62
  167. data/lib/active_record/relation/spawn_methods.rb +18 -1
  168. data/lib/active_record/relation.rb +76 -35
  169. data/lib/active_record/result.rb +19 -5
  170. data/lib/active_record/runtime_registry.rb +10 -1
  171. data/lib/active_record/sanitization.rb +51 -11
  172. data/lib/active_record/schema.rb +2 -3
  173. data/lib/active_record/schema_dumper.rb +41 -7
  174. data/lib/active_record/schema_migration.rb +68 -33
  175. data/lib/active_record/scoping/default.rb +15 -5
  176. data/lib/active_record/scoping/named.rb +2 -2
  177. data/lib/active_record/scoping.rb +2 -1
  178. data/lib/active_record/secure_password.rb +60 -0
  179. data/lib/active_record/secure_token.rb +21 -3
  180. data/lib/active_record/signed_id.rb +7 -5
  181. data/lib/active_record/store.rb +8 -8
  182. data/lib/active_record/suppressor.rb +3 -1
  183. data/lib/active_record/table_metadata.rb +10 -1
  184. data/lib/active_record/tasks/database_tasks.rb +127 -105
  185. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  186. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  187. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -7
  188. data/lib/active_record/test_fixtures.rb +113 -96
  189. data/lib/active_record/timestamp.rb +26 -14
  190. data/lib/active_record/token_for.rb +113 -0
  191. data/lib/active_record/touch_later.rb +11 -6
  192. data/lib/active_record/transactions.rb +36 -10
  193. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  194. data/lib/active_record/type/internal/timezone.rb +7 -2
  195. data/lib/active_record/type/time.rb +4 -0
  196. data/lib/active_record/validations/absence.rb +1 -1
  197. data/lib/active_record/validations/numericality.rb +5 -4
  198. data/lib/active_record/validations/presence.rb +5 -28
  199. data/lib/active_record/validations/uniqueness.rb +47 -2
  200. data/lib/active_record/validations.rb +8 -4
  201. data/lib/active_record/version.rb +1 -1
  202. data/lib/active_record.rb +121 -16
  203. data/lib/arel/errors.rb +10 -0
  204. data/lib/arel/factory_methods.rb +4 -0
  205. data/lib/arel/nodes/binary.rb +6 -1
  206. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  207. data/lib/arel/nodes/cte.rb +36 -0
  208. data/lib/arel/nodes/fragments.rb +35 -0
  209. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  210. data/lib/arel/nodes/leading_join.rb +8 -0
  211. data/lib/arel/nodes/node.rb +111 -2
  212. data/lib/arel/nodes/sql_literal.rb +6 -0
  213. data/lib/arel/nodes/table_alias.rb +4 -0
  214. data/lib/arel/nodes.rb +4 -0
  215. data/lib/arel/predications.rb +2 -0
  216. data/lib/arel/table.rb +9 -5
  217. data/lib/arel/visitors/mysql.rb +8 -1
  218. data/lib/arel/visitors/to_sql.rb +81 -17
  219. data/lib/arel/visitors/visitor.rb +2 -2
  220. data/lib/arel.rb +16 -2
  221. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  222. data/lib/rails/generators/active_record/migration.rb +3 -1
  223. data/lib/rails/generators/active_record/model/USAGE +113 -0
  224. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  225. metadata +52 -17
  226. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  227. data/lib/active_record/null_relation.rb +0 -63
@@ -1,13 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/module/attribute_accessors_per_thread"
4
+ require "active_record/query_logs_formatter"
4
5
 
5
6
  module ActiveRecord
6
7
  # = Active Record Query Logs
7
8
  #
8
- # Automatically tag SQL queries with runtime information.
9
+ # Automatically append comments to SQL queries with runtime information tags. This can be used to trace troublesome
10
+ # SQL statements back to the application code that generated these statements.
9
11
  #
10
- # Default tags available for use:
12
+ # Query logs can be enabled via \Rails configuration in <tt>config/application.rb</tt> or an initializer:
13
+ #
14
+ # config.active_record.query_log_tags_enabled = true
15
+ #
16
+ # By default the name of the application, the name and action of the controller, or the name of the job are logged.
17
+ # The default format is {SQLCommenter}[https://open-telemetry.github.io/opentelemetry-sqlcommenter/].
18
+ # The tags shown in a query comment can be configured via \Rails configuration:
19
+ #
20
+ # config.active_record.query_log_tags = [ :application, :controller, :action, :job ]
21
+ #
22
+ # Active Record defines default tags available for use:
11
23
  #
12
24
  # * +application+
13
25
  # * +pid+
@@ -15,48 +27,40 @@ module ActiveRecord
15
27
  # * +db_host+
16
28
  # * +database+
17
29
  #
18
- # _Action Controller and Active Job tags are also defined when used in Rails:_
30
+ # Action Controller adds default tags when loaded:
19
31
  #
20
32
  # * +controller+
21
33
  # * +action+
22
- # * +job+
23
- #
24
- # The tags used in a query can be configured directly:
34
+ # * +namespaced_controller+
25
35
  #
26
- # ActiveRecord::QueryLogs.tags = [ :application, :controller, :action, :job ]
36
+ # Active Job adds default tags when loaded:
27
37
  #
28
- # or via Rails configuration:
29
- #
30
- # config.active_record.query_log_tags = [ :application, :controller, :action, :job ]
38
+ # * +job+
31
39
  #
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.
40
+ # New comment tags can be defined by adding them in a +Hash+ to the tags +Array+. Tags can have dynamic content by
41
+ # setting a +Proc+ or lambda value in the +Hash+, and can reference any value stored by \Rails in the +context+ object.
42
+ # ActiveSupport::CurrentAttributes can be used to store application values. Tags with +nil+ values are
43
+ # omitted from the query comment.
35
44
  #
36
45
  # Escaping is performed on the string returned, however untrusted user input should not be used.
37
46
  #
38
47
  # Example:
39
48
  #
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
49
+ # config.active_record.query_log_tags = [
50
+ # :namespaced_controller,
51
+ # :action,
52
+ # :job,
53
+ # {
54
+ # request_id: ->(context) { context[:controller]&.request&.request_id },
55
+ # job_id: ->(context) { context[:job]&.job_id },
56
+ # tenant_id: -> { Current.tenant&.id },
57
+ # static: "value",
58
+ # },
59
+ # ]
60
+ #
61
+ # By default the name of the application, the name and action of the controller, or the name of the job are logged
62
+ # using the {SQLCommenter}[https://open-telemetry.github.io/opentelemetry-sqlcommenter/] format. This can be changed
63
+ # via {config.active_record.query_log_tags_format}[https://guides.rubyonrails.org/configuring.html#config-active-record-query-log-tags-format]
60
64
  #
61
65
  # Tag comments can be prepended to the query:
62
66
  #
@@ -65,46 +69,65 @@ module ActiveRecord
65
69
  # For applications where the content will not change during the lifetime of
66
70
  # the request or job execution, the tags can be cached for reuse in every query:
67
71
  #
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
72
  # config.active_record.cache_query_log_tags = true
73
73
  module QueryLogs
74
74
  mattr_accessor :taggings, instance_accessor: false, default: {}
75
75
  mattr_accessor :tags, instance_accessor: false, default: [ :application ]
76
76
  mattr_accessor :prepend_comment, instance_accessor: false, default: false
77
77
  mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
78
+ mattr_accessor :tags_formatter, instance_accessor: false
78
79
  thread_mattr_accessor :cached_comment, instance_accessor: false
79
80
 
80
81
  class << self
81
- def call(sql) # :nodoc:
82
- if prepend_comment
83
- "#{self.comment} #{sql}"
82
+ def call(sql, connection) # :nodoc:
83
+ comment = self.comment(connection)
84
+
85
+ if comment.blank?
86
+ sql
87
+ elsif prepend_comment
88
+ "#{comment} #{sql}"
84
89
  else
85
- "#{sql} #{self.comment}"
86
- end.strip
90
+ "#{sql} #{comment}"
91
+ end
87
92
  end
88
93
 
89
94
  def clear_cache # :nodoc:
90
95
  self.cached_comment = nil
91
96
  end
92
97
 
98
+ # Updates the formatter to be what the passed in format is.
99
+ def update_formatter(format)
100
+ self.tags_formatter =
101
+ case format
102
+ when :legacy
103
+ LegacyFormatter.new
104
+ when :sqlcommenter
105
+ SQLCommenter.new
106
+ else
107
+ raise ArgumentError, "Formatter is unsupported: #{formatter}"
108
+ end
109
+ end
110
+
93
111
  ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
94
112
 
95
113
  private
96
114
  # Returns an SQL comment +String+ containing the query log tags.
97
115
  # Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
98
- def comment
116
+ def comment(connection)
99
117
  if cache_query_log_tags
100
- self.cached_comment ||= uncached_comment
118
+ self.cached_comment ||= uncached_comment(connection)
101
119
  else
102
- uncached_comment
120
+ uncached_comment(connection)
103
121
  end
104
122
  end
105
123
 
106
- def uncached_comment
107
- content = tag_content
124
+ def formatter
125
+ self.tags_formatter || self.update_formatter(:legacy)
126
+ end
127
+
128
+ def uncached_comment(connection)
129
+ content = tag_content(connection)
130
+
108
131
  if content.present?
109
132
  "/*#{escape_sql_comment(content)}*/"
110
133
  end
@@ -113,7 +136,7 @@ module ActiveRecord
113
136
  def escape_sql_comment(content)
114
137
  # Sanitize a string to appear within a SQL comment
115
138
  # For compatibility, this also surrounding "/*+", "/*", and "*/"
116
- # charcacters, possibly with single surrounding space.
139
+ # characters, possibly with single surrounding space.
117
140
  # Then follows that by replacing any internal "*/" or "/ *" with
118
141
  # "* /" or "/ *"
119
142
  comment = content.to_s.dup
@@ -123,10 +146,11 @@ module ActiveRecord
123
146
  comment
124
147
  end
125
148
 
126
- def tag_content
149
+ def tag_content(connection)
127
150
  context = ActiveSupport::ExecutionContext.to_h
151
+ context[:connection] ||= connection
128
152
 
129
- tags.flat_map { |i| [*i] }.filter_map do |tag|
153
+ pairs = tags.flat_map { |i| [*i] }.filter_map do |tag|
130
154
  key, handler = tag
131
155
  handler ||= taggings[key]
132
156
 
@@ -141,8 +165,9 @@ module ActiveRecord
141
165
  else
142
166
  handler
143
167
  end
144
- "#{key}:#{val}" unless val.nil?
145
- end.join(",")
168
+ [key, val] unless val.nil?
169
+ end
170
+ self.formatter.format(pairs)
146
171
  end
147
172
  end
148
173
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module QueryLogs
5
+ class LegacyFormatter # :nodoc:
6
+ def initialize
7
+ @key_value_separator = ":"
8
+ end
9
+
10
+ # Formats the key value pairs into a string.
11
+ def format(pairs)
12
+ pairs.map! do |key, value|
13
+ "#{key}#{key_value_separator}#{format_value(value)}"
14
+ end.join(",")
15
+ end
16
+
17
+ private
18
+ attr_reader :key_value_separator
19
+
20
+ def format_value(value)
21
+ value
22
+ end
23
+ end
24
+
25
+ class SQLCommenter < LegacyFormatter # :nodoc:
26
+ def initialize
27
+ @key_value_separator = "="
28
+ end
29
+
30
+ def format(pairs)
31
+ pairs.sort_by!(&:first)
32
+ super
33
+ end
34
+
35
+ private
36
+ def format_value(value)
37
+ "'#{ERB::Util.url_encode(value)}'"
38
+ end
39
+ end
40
+ end
41
+ end
@@ -12,12 +12,13 @@ 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, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
15
+ :select, :reselect, :order, :regroup, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
16
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, :excluding, :without
20
+ :pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with,
21
+ :async_count, :async_average, :async_minimum, :async_maximum, :async_sum, :async_pluck, :async_pick,
21
22
  ].freeze # :nodoc:
22
23
  delegate(*QUERYING_METHODS, to: :all)
23
24
 
@@ -50,6 +51,13 @@ module ActiveRecord
50
51
  _load_from_sql(_query_by_sql(sql, binds, preparable: preparable), &block)
51
52
  end
52
53
 
54
+ # Same as <tt>#find_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise
55
+ def async_find_by_sql(sql, binds = [], preparable: nil, &block)
56
+ _query_by_sql(sql, binds, preparable: preparable, async: true).then do |result|
57
+ _load_from_sql(result, &block)
58
+ end
59
+ end
60
+
53
61
  def _query_by_sql(sql, binds = [], preparable: nil, async: false) # :nodoc:
54
62
  connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async)
55
63
  end
@@ -93,5 +101,10 @@ module ActiveRecord
93
101
  def count_by_sql(sql)
94
102
  connection.select_value(sanitize_sql(sql), "#{name} Count").to_i
95
103
  end
104
+
105
+ # Same as <tt>#count_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise
106
+ def async_count_by_sql(sql)
107
+ connection.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
108
+ end
96
109
  end
97
110
  end
@@ -34,7 +34,11 @@ module ActiveRecord
34
34
  config.active_record.sqlite3_production_warning = true
35
35
  config.active_record.query_log_tags_enabled = false
36
36
  config.active_record.query_log_tags = [ :application ]
37
+ config.active_record.query_log_tags_format = :legacy
37
38
  config.active_record.cache_query_log_tags = false
39
+ config.active_record.raise_on_assign_to_attr_readonly = false
40
+ config.active_record.belongs_to_required_validates_foreign_key = true
41
+ config.active_record.generate_secure_token_on = :create
38
42
 
39
43
  config.active_record.queues = ActiveSupport::InheritableOptions.new
40
44
 
@@ -72,6 +76,10 @@ module ActiveRecord
72
76
  require "active_record/base"
73
77
  end
74
78
 
79
+ initializer "active_record.deprecator", before: :load_environment_config do |app|
80
+ app.deprecators[:active_record] = ActiveRecord.deprecator
81
+ end
82
+
75
83
  initializer "active_record.initialize_timezone" do
76
84
  ActiveSupport.on_load(:active_record) do
77
85
  self.time_zone_aware_attributes = true
@@ -100,7 +108,7 @@ module ActiveRecord
100
108
  end
101
109
  end
102
110
 
103
- initializer "Check for cache versioning support" do
111
+ initializer "active_record.cache_versioning_support" do
104
112
  config.after_initialize do |app|
105
113
  ActiveSupport.on_load(:active_record) do
106
114
  if app.config.active_record.cache_versioning && Rails.cache
@@ -125,9 +133,15 @@ To keep using the current cache store, you can turn off cache versioning entirel
125
133
  end
126
134
  end
127
135
 
136
+ initializer "active_record.use_schema_cache_dump" do
137
+ ActiveRecord::ConnectionAdapters::SchemaReflection.use_schema_cache_dump = config.active_record.use_schema_cache_dump
138
+ end
139
+
128
140
  initializer "active_record.check_schema_cache_dump" do
129
141
  check_schema_cache_dump_version = config.active_record.check_schema_cache_dump_version
130
142
 
143
+ ActiveRecord::ConnectionAdapters::SchemaReflection.check_schema_cache_dump_version = check_schema_cache_dump_version
144
+
131
145
  if config.active_record.use_schema_cache_dump && !config.active_record.lazily_load_schema_cache
132
146
  config.after_initialize do |app|
133
147
  ActiveSupport.on_load(:active_record) do
@@ -135,10 +149,10 @@ To keep using the current cache store, you can turn off cache versioning entirel
135
149
 
136
150
  filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(
137
151
  db_config.name,
138
- schema_cache_path: db_config&.schema_cache_path
152
+ schema_cache_path: db_config.schema_cache_path
139
153
  )
140
154
 
141
- cache = ActiveRecord::ConnectionAdapters::SchemaCache.load_from(filename)
155
+ cache = ActiveRecord::ConnectionAdapters::SchemaCache._load_from(filename)
142
156
  next if cache.nil?
143
157
 
144
158
  if check_schema_cache_dump_version
@@ -150,34 +164,47 @@ To keep using the current cache store, you can turn off cache versioning entirel
150
164
  end
151
165
  next if current_version.nil?
152
166
 
153
- if cache.version != current_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}."
167
+ if cache.schema_version != current_version
168
+ warn "Ignoring #{filename} because it has expired. The current schema version is #{current_version}, but the one in the schema cache file is #{cache.schema_version}."
155
169
  next
156
170
  end
157
171
  end
158
172
 
159
173
  Rails.logger.info("Using schema cache file #{filename}")
160
- connection_pool.set_schema_cache(cache)
174
+ connection_pool.schema_reflection.set_schema_cache(cache)
161
175
  end
162
176
  end
163
177
  end
164
178
  end
165
179
 
166
180
  initializer "active_record.define_attribute_methods" do |app|
181
+ # For resiliency, it is critical that a Rails application should be
182
+ # able to boot without depending on the database (or any other service)
183
+ # being responsive.
184
+ #
185
+ # Otherwise a bad deploy adding a lot of load on the database may require to
186
+ # entirely shutdown the application so the database can recover before a fixed
187
+ # version can be deployed again.
188
+ #
189
+ # This is why this initializer tries hard not to query the database, and if it
190
+ # does, it makes sure to rescue any possible database error.
191
+ check_schema_cache_dump_version = config.active_record.check_schema_cache_dump_version
167
192
  config.after_initialize do
168
193
  ActiveSupport.on_load(:active_record) do
169
- if app.config.eager_load
194
+ # In development and test we shouldn't eagerly define attribute methods because
195
+ # db:test:prepare will trigger later and might change the schema.
196
+ #
197
+ # Additionally if `check_schema_cache_dump_version` is enabled (which is the default),
198
+ # loading the schema cache dump trigger a database connection to compare the schema
199
+ # versions.
200
+ # This means the attribute methods will be lazily defined whent the model is accessed,
201
+ # likely as part of the first few requests or jobs. This isn't good for performance
202
+ # but we unfortunately have to arbitrate between resiliency and performance, and chose
203
+ # resiliency.
204
+ if !check_schema_cache_dump_version && app.config.eager_load && !Rails.env.local?
170
205
  begin
171
206
  descendants.each do |model|
172
- # If the schema cache was loaded from a dump, we can use it without connecting
173
- schema_cache = model.connection_pool.schema_cache
174
-
175
- # If there's no connection yet, we avoid connecting.
176
- schema_cache ||= model.connected? && model.connection.schema_cache
177
-
178
- # If the schema cache doesn't have the columns
179
- # hash for the model cached, `define_attribute_methods` would trigger a query.
180
- if schema_cache && schema_cache.columns_hash?(model.table_name)
207
+ if model.connection_pool.schema_reflection.cached?(model.table_name)
181
208
  model.define_attribute_methods
182
209
  end
183
210
  end
@@ -210,6 +237,16 @@ To keep using the current cache store, you can turn off cache versioning entirel
210
237
  end
211
238
  end
212
239
 
240
+ initializer "active_record.sqlite3_adapter_strict_strings_by_default" do
241
+ config.after_initialize do
242
+ if config.active_record.sqlite3_adapter_strict_strings_by_default
243
+ ActiveSupport.on_load(:active_record_sqlite3adapter) do
244
+ self.strict_strings_by_default = true
245
+ end
246
+ end
247
+ end
248
+ end
249
+
213
250
  initializer "active_record.set_configs" do |app|
214
251
  configs = app.config.active_record
215
252
 
@@ -234,8 +271,10 @@ To keep using the current cache store, you can turn off cache versioning entirel
234
271
  :shard_resolver,
235
272
  :query_log_tags_enabled,
236
273
  :query_log_tags,
274
+ :query_log_tags_format,
237
275
  :cache_query_log_tags,
238
276
  :sqlite3_production_warning,
277
+ :sqlite3_adapter_strict_strings_by_default,
239
278
  :check_schema_cache_dump_version,
240
279
  :use_schema_cache_dump
241
280
  )
@@ -260,21 +299,23 @@ To keep using the current cache store, you can turn off cache versioning entirel
260
299
  # and then establishes the connection.
261
300
  initializer "active_record.initialize_database" do
262
301
  ActiveSupport.on_load(:active_record) do
263
- if ActiveRecord.legacy_connection_handling
264
- self.connection_handlers = { ActiveRecord.writing_role => ActiveRecord::Base.default_connection_handler }
265
- end
266
302
  self.configurations = Rails.application.config.database_configuration
267
303
 
268
304
  establish_connection
269
305
  end
270
306
  end
271
307
 
272
- # Expose database runtime to controller for logging.
308
+ # Expose database runtime for logging.
273
309
  initializer "active_record.log_runtime" do
274
310
  require "active_record/railties/controller_runtime"
275
311
  ActiveSupport.on_load(:action_controller) do
276
312
  include ActiveRecord::Railties::ControllerRuntime
277
313
  end
314
+
315
+ require "active_record/railties/job_runtime"
316
+ ActiveSupport.on_load(:active_job) do
317
+ include ActiveRecord::Railties::JobRuntime
318
+ end
278
319
  end
279
320
 
280
321
  initializer "active_record.set_reloader_hooks" do
@@ -282,7 +323,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
282
323
  ActiveSupport::Reloader.before_class_unload do
283
324
  if ActiveRecord::Base.connected?
284
325
  ActiveRecord::Base.clear_cache!
285
- ActiveRecord::Base.clear_reloadable_connections!
326
+ ActiveRecord::Base.connection_handler.clear_reloadable_connections!(:all)
286
327
  end
287
328
  end
288
329
  end
@@ -309,8 +350,8 @@ To keep using the current cache store, you can turn off cache versioning entirel
309
350
  # this connection is trivial: the rest of the pool would need to be
310
351
  # populated anyway.
311
352
 
312
- clear_active_connections!
313
- flush_idle_connections!
353
+ connection_handler.clear_active_connections!(:all)
354
+ connection_handler.flush_idle_connections!(:all)
314
355
  end
315
356
  end
316
357
  end
@@ -327,18 +368,32 @@ To keep using the current cache store, you can turn off cache versioning entirel
327
368
  end
328
369
  end
329
370
 
371
+ initializer "active_record.generated_token_verifier" do
372
+ config.after_initialize do |app|
373
+ ActiveSupport.on_load(:active_record) do
374
+ self.generated_token_verifier ||= app.message_verifier("active_record/token_for")
375
+ end
376
+ end
377
+ end
378
+
330
379
  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
380
+ auto_filtered_parameters = ActiveRecord::Encryption::AutoFilteredParameters.new(app)
336
381
 
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
382
+ config.after_initialize do |app|
383
+ ActiveRecord::Encryption.configure \
384
+ primary_key: app.credentials.dig(:active_record_encryption, :primary_key),
385
+ deterministic_key: app.credentials.dig(:active_record_encryption, :deterministic_key),
386
+ key_derivation_salt: app.credentials.dig(:active_record_encryption, :key_derivation_salt),
387
+ **config.active_record.encryption
388
+
389
+ auto_filtered_parameters.enable if ActiveRecord::Encryption.config.add_to_filter_parameters
390
+
391
+ ActiveSupport.on_load(:active_record) do
392
+ # Support extended queries for deterministic attributes and validations
393
+ if ActiveRecord::Encryption.config.extend_queries
394
+ ActiveRecord::Encryption::ExtendedDeterministicQueries.install_support
395
+ ActiveRecord::Encryption::ExtendedDeterministicUniquenessValidator.install_support
396
+ end
342
397
  end
343
398
  end
344
399
 
@@ -348,13 +403,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
348
403
  ActiveRecord::Fixture.prepend ActiveRecord::Encryption::EncryptedFixtures
349
404
  end
350
405
  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
406
  end
359
407
 
360
408
  initializer "active_record.query_log_tags_config" do |app|
@@ -363,16 +411,21 @@ To keep using the current cache store, you can turn off cache versioning entirel
363
411
  ActiveRecord.query_transformers << ActiveRecord::QueryLogs
364
412
  ActiveRecord::QueryLogs.taggings.merge!(
365
413
  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 }
414
+ pid: -> { Process.pid.to_s },
415
+ socket: ->(context) { context[:connection].pool.db_config.socket },
416
+ db_host: ->(context) { context[:connection].pool.db_config.host },
417
+ database: ->(context) { context[:connection].pool.db_config.database }
370
418
  )
419
+ ActiveRecord.disable_prepared_statements = true
371
420
 
372
421
  if app.config.active_record.query_log_tags.present?
373
422
  ActiveRecord::QueryLogs.tags = app.config.active_record.query_log_tags
374
423
  end
375
424
 
425
+ if app.config.active_record.query_log_tags_format
426
+ ActiveRecord::QueryLogs.update_formatter(app.config.active_record.query_log_tags_format)
427
+ end
428
+
376
429
  if app.config.active_record.cache_query_log_tags
377
430
  ActiveRecord::QueryLogs.cache_query_log_tags = true
378
431
  end
@@ -382,7 +435,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
382
435
 
383
436
  initializer "active_record.unregister_current_scopes_on_unload" do |app|
384
437
  config.after_initialize do
385
- unless app.config.cache_classes
438
+ if app.config.reloading_enabled?
386
439
  Rails.autoloaders.main.on_unload do |_cpath, value, _abspath|
387
440
  # Conditions are written this way to be robust against custom
388
441
  # implementations of value#is_a? or value#<.
@@ -393,5 +446,14 @@ To keep using the current cache store, you can turn off cache versioning entirel
393
446
  end
394
447
  end
395
448
  end
449
+
450
+ initializer "active_record.message_pack" do
451
+ ActiveSupport.on_load(:message_pack) do
452
+ ActiveSupport.on_load(:active_record) do
453
+ require "active_record/message_pack"
454
+ ActiveRecord::MessagePack::Extensions.install(ActiveSupport::MessagePack::CacheSerializer)
455
+ end
456
+ end
457
+ end
396
458
  end
397
459
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/module/attr_internal"
4
- require "active_record/log_subscriber"
4
+ require "active_record/runtime_registry"
5
5
 
6
6
  module ActiveRecord
7
7
  module Railties # :nodoc:
@@ -16,6 +16,11 @@ module ActiveRecord
16
16
  end
17
17
  end
18
18
 
19
+ def initialize(...) # :nodoc:
20
+ super
21
+ self.db_runtime = nil
22
+ end
23
+
19
24
  private
20
25
  attr_internal :db_runtime
21
26
 
@@ -23,16 +28,16 @@ module ActiveRecord
23
28
  # We also need to reset the runtime before each action
24
29
  # because of queries in middleware or in cases we are streaming
25
30
  # and it won't be cleaned up by the method below.
26
- ActiveRecord::LogSubscriber.reset_runtime
31
+ ActiveRecord::RuntimeRegistry.reset
27
32
  super
28
33
  end
29
34
 
30
35
  def cleanup_view_runtime
31
36
  if logger && logger.info?
32
- db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
37
+ db_rt_before_render = ActiveRecord::RuntimeRegistry.reset
33
38
  self.db_runtime = (db_runtime || 0) + db_rt_before_render
34
39
  runtime = super
35
- db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
40
+ db_rt_after_render = ActiveRecord::RuntimeRegistry.reset
36
41
  self.db_runtime += db_rt_after_render
37
42
  runtime - db_rt_after_render
38
43
  else
@@ -43,7 +48,7 @@ module ActiveRecord
43
48
  def append_info_to_payload(payload)
44
49
  super
45
50
 
46
- payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime
51
+ payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::RuntimeRegistry.reset
47
52
  end
48
53
  end
49
54
  end