activerecord 7.0.8 → 7.1.3.4

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 (231) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1554 -1452
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +16 -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 +20 -4
  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 +15 -9
  15. data/lib/active_record/associations/collection_proxy.rb +15 -10
  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 +31 -7
  22. data/lib/active_record/associations/preloader.rb +13 -10
  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 +313 -217
  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 +52 -34
  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 +74 -51
  46. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  47. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  48. data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
  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 -124
  54. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  55. data/lib/active_record/connection_adapters/abstract_adapter.rb +511 -91
  56. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +207 -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 +18 -13
  65. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -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 +14 -3
  70. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +74 -40
  71. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/quoting.rb +10 -6
  75. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  76. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  77. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  78. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +361 -60
  80. data/lib/active_record/connection_adapters/postgresql_adapter.rb +353 -192
  81. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  82. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  83. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  84. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
  85. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
  86. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
  87. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +209 -79
  88. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  89. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  90. data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
  91. data/lib/active_record/connection_adapters.rb +3 -1
  92. data/lib/active_record/connection_handling.rb +72 -95
  93. data/lib/active_record/core.rb +175 -153
  94. data/lib/active_record/counter_cache.rb +46 -25
  95. data/lib/active_record/database_configurations/database_config.rb +9 -3
  96. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  97. data/lib/active_record/database_configurations/url_config.rb +17 -11
  98. data/lib/active_record/database_configurations.rb +86 -33
  99. data/lib/active_record/delegated_type.rb +9 -4
  100. data/lib/active_record/deprecator.rb +7 -0
  101. data/lib/active_record/destroy_association_async_job.rb +2 -0
  102. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  103. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  104. data/lib/active_record/encryption/config.rb +25 -1
  105. data/lib/active_record/encryption/configurable.rb +12 -19
  106. data/lib/active_record/encryption/context.rb +10 -3
  107. data/lib/active_record/encryption/contexts.rb +5 -1
  108. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  109. data/lib/active_record/encryption/encryptable_record.rb +42 -18
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
  111. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  112. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  113. data/lib/active_record/encryption/key_generator.rb +12 -1
  114. data/lib/active_record/encryption/message_serializer.rb +2 -0
  115. data/lib/active_record/encryption/properties.rb +3 -3
  116. data/lib/active_record/encryption/scheme.rb +19 -22
  117. data/lib/active_record/encryption.rb +1 -0
  118. data/lib/active_record/enum.rb +112 -28
  119. data/lib/active_record/errors.rb +112 -18
  120. data/lib/active_record/explain.rb +23 -3
  121. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  122. data/lib/active_record/fixture_set/render_context.rb +2 -0
  123. data/lib/active_record/fixture_set/table_row.rb +29 -8
  124. data/lib/active_record/fixtures.rb +135 -71
  125. data/lib/active_record/future_result.rb +31 -5
  126. data/lib/active_record/gem_version.rb +4 -4
  127. data/lib/active_record/inheritance.rb +30 -16
  128. data/lib/active_record/insert_all.rb +57 -10
  129. data/lib/active_record/integration.rb +8 -8
  130. data/lib/active_record/internal_metadata.rb +120 -30
  131. data/lib/active_record/locking/pessimistic.rb +5 -2
  132. data/lib/active_record/log_subscriber.rb +29 -12
  133. data/lib/active_record/marshalling.rb +56 -0
  134. data/lib/active_record/message_pack.rb +124 -0
  135. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  136. data/lib/active_record/middleware/database_selector.rb +6 -8
  137. data/lib/active_record/middleware/shard_selector.rb +3 -1
  138. data/lib/active_record/migration/command_recorder.rb +104 -5
  139. data/lib/active_record/migration/compatibility.rb +139 -5
  140. data/lib/active_record/migration/default_strategy.rb +23 -0
  141. data/lib/active_record/migration/execution_strategy.rb +19 -0
  142. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  143. data/lib/active_record/migration.rb +219 -111
  144. data/lib/active_record/model_schema.rb +64 -44
  145. data/lib/active_record/nested_attributes.rb +24 -6
  146. data/lib/active_record/normalization.rb +167 -0
  147. data/lib/active_record/persistence.rb +188 -37
  148. data/lib/active_record/promise.rb +84 -0
  149. data/lib/active_record/query_cache.rb +3 -21
  150. data/lib/active_record/query_logs.rb +77 -52
  151. data/lib/active_record/query_logs_formatter.rb +41 -0
  152. data/lib/active_record/querying.rb +15 -2
  153. data/lib/active_record/railtie.rb +109 -47
  154. data/lib/active_record/railties/controller_runtime.rb +12 -6
  155. data/lib/active_record/railties/databases.rake +142 -148
  156. data/lib/active_record/railties/job_runtime.rb +23 -0
  157. data/lib/active_record/readonly_attributes.rb +32 -5
  158. data/lib/active_record/reflection.rb +174 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  160. data/lib/active_record/relation/batches.rb +190 -61
  161. data/lib/active_record/relation/calculations.rb +187 -63
  162. data/lib/active_record/relation/delegation.rb +23 -9
  163. data/lib/active_record/relation/finder_methods.rb +77 -16
  164. data/lib/active_record/relation/merger.rb +2 -0
  165. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -2
  166. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  167. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  168. data/lib/active_record/relation/predicate_builder.rb +26 -14
  169. data/lib/active_record/relation/query_attribute.rb +2 -1
  170. data/lib/active_record/relation/query_methods.rb +352 -63
  171. data/lib/active_record/relation/spawn_methods.rb +18 -1
  172. data/lib/active_record/relation.rb +91 -35
  173. data/lib/active_record/result.rb +19 -5
  174. data/lib/active_record/runtime_registry.rb +24 -1
  175. data/lib/active_record/sanitization.rb +51 -11
  176. data/lib/active_record/schema.rb +2 -3
  177. data/lib/active_record/schema_dumper.rb +46 -7
  178. data/lib/active_record/schema_migration.rb +68 -33
  179. data/lib/active_record/scoping/default.rb +15 -5
  180. data/lib/active_record/scoping/named.rb +2 -2
  181. data/lib/active_record/scoping.rb +2 -1
  182. data/lib/active_record/secure_password.rb +60 -0
  183. data/lib/active_record/secure_token.rb +21 -3
  184. data/lib/active_record/signed_id.rb +7 -5
  185. data/lib/active_record/store.rb +8 -8
  186. data/lib/active_record/suppressor.rb +3 -1
  187. data/lib/active_record/table_metadata.rb +10 -1
  188. data/lib/active_record/tasks/database_tasks.rb +127 -105
  189. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  190. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  191. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  192. data/lib/active_record/test_fixtures.rb +113 -96
  193. data/lib/active_record/timestamp.rb +27 -15
  194. data/lib/active_record/token_for.rb +113 -0
  195. data/lib/active_record/touch_later.rb +11 -6
  196. data/lib/active_record/transactions.rb +36 -10
  197. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  198. data/lib/active_record/type/internal/timezone.rb +7 -2
  199. data/lib/active_record/type/time.rb +4 -0
  200. data/lib/active_record/validations/absence.rb +1 -1
  201. data/lib/active_record/validations/numericality.rb +5 -4
  202. data/lib/active_record/validations/presence.rb +5 -28
  203. data/lib/active_record/validations/uniqueness.rb +47 -2
  204. data/lib/active_record/validations.rb +8 -4
  205. data/lib/active_record/version.rb +1 -1
  206. data/lib/active_record.rb +121 -16
  207. data/lib/arel/errors.rb +10 -0
  208. data/lib/arel/factory_methods.rb +4 -0
  209. data/lib/arel/nodes/binary.rb +6 -1
  210. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  211. data/lib/arel/nodes/cte.rb +36 -0
  212. data/lib/arel/nodes/fragments.rb +35 -0
  213. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  214. data/lib/arel/nodes/leading_join.rb +8 -0
  215. data/lib/arel/nodes/node.rb +111 -2
  216. data/lib/arel/nodes/sql_literal.rb +6 -0
  217. data/lib/arel/nodes/table_alias.rb +4 -0
  218. data/lib/arel/nodes.rb +4 -0
  219. data/lib/arel/predications.rb +2 -0
  220. data/lib/arel/table.rb +9 -5
  221. data/lib/arel/visitors/mysql.rb +8 -1
  222. data/lib/arel/visitors/to_sql.rb +81 -17
  223. data/lib/arel/visitors/visitor.rb +2 -2
  224. data/lib/arel.rb +16 -2
  225. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  226. data/lib/rails/generators/active_record/migration.rb +3 -1
  227. data/lib/rails/generators/active_record/model/USAGE +113 -0
  228. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  229. metadata +48 -12
  230. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  231. data/lib/active_record/null_relation.rb +0 -63
@@ -3,13 +3,11 @@
3
3
  module ActiveRecord
4
4
  class DatabaseConfigurations
5
5
  # ActiveRecord::Base.configurations will return either a HashConfig or
6
- # UrlConfig respectively. It will never return a DatabaseConfig object,
6
+ # UrlConfig respectively. It will never return a +DatabaseConfig+ object,
7
7
  # as this is the parent class for the types of database configuration objects.
8
8
  class DatabaseConfig # :nodoc:
9
9
  attr_reader :env_name, :name
10
10
 
11
- attr_accessor :owner_name
12
-
13
11
  def initialize(env_name, name)
14
12
  @env_name = env_name
15
13
  @name = name
@@ -19,6 +17,10 @@ module ActiveRecord
19
17
  "#{adapter}_connection"
20
18
  end
21
19
 
20
+ def adapter_class_method
21
+ "#{adapter}_adapter_class"
22
+ end
23
+
22
24
  def host
23
25
  raise NotImplementedError
24
26
  end
@@ -51,6 +53,10 @@ module ActiveRecord
51
53
  raise NotImplementedError
52
54
  end
53
55
 
56
+ def query_cache
57
+ raise NotImplementedError
58
+ end
59
+
54
60
  def checkout_timeout
55
61
  raise NotImplementedError
56
62
  end
@@ -2,7 +2,9 @@
2
2
 
3
3
  module ActiveRecord
4
4
  class DatabaseConfigurations
5
- # A HashConfig object is created for each database configuration entry that
5
+ # = Active Record Database Hash Config
6
+ #
7
+ # A +HashConfig+ object is created for each database configuration entry that
6
8
  # is created from a hash.
7
9
  #
8
10
  # A hash config:
@@ -14,19 +16,23 @@ module ActiveRecord
14
16
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
15
17
  # @env_name="development", @name="primary", @config={database: "db_name"}>
16
18
  #
17
- # ==== Options
18
- #
19
- # * <tt>:env_name</tt> - The Rails environment, i.e. "development".
20
- # * <tt>:name</tt> - The db config name. In a standard two-tier
21
- # database configuration this will default to "primary". In a multiple
22
- # database three-tier database configuration this corresponds to the name
23
- # used in the second tier, for example "primary_readonly".
24
- # * <tt>:config</tt> - The config hash. This is the hash that contains the
25
- # database adapter, name, and other important information for database
26
- # connections.
19
+ # See ActiveRecord::DatabaseConfigurations for more info.
27
20
  class HashConfig < DatabaseConfig
28
21
  attr_reader :configuration_hash
29
22
 
23
+
24
+ # Initialize a new +HashConfig+ object
25
+ #
26
+ # ==== Options
27
+ #
28
+ # * <tt>:env_name</tt> - The \Rails environment, i.e. "development".
29
+ # * <tt>:name</tt> - The db config name. In a standard two-tier
30
+ # database configuration this will default to "primary". In a multiple
31
+ # database three-tier database configuration this corresponds to the name
32
+ # used in the second tier, for example "primary_readonly".
33
+ # * <tt>:config</tt> - The config hash. This is the hash that contains the
34
+ # database adapter, name, and other important information for database
35
+ # connections.
30
36
  def initialize(env_name, name, configuration_hash)
31
37
  super(env_name, name)
32
38
  @configuration_hash = configuration_hash.symbolize_keys.freeze
@@ -74,6 +80,10 @@ module ActiveRecord
74
80
  (configuration_hash[:max_threads] || pool).to_i
75
81
  end
76
82
 
83
+ def query_cache
84
+ configuration_hash[:query_cache]
85
+ end
86
+
77
87
  def max_queue
78
88
  max_threads * 4
79
89
  end
@@ -122,7 +132,7 @@ module ActiveRecord
122
132
  # If +configuration_hash[:schema_dump]+ is set to +false+ or +nil+
123
133
  # the schema will not be dumped.
124
134
  #
125
- # If the config option is set that will be used. Otherwise Rails
135
+ # If the config option is set that will be used. Otherwise \Rails
126
136
  # will generate the filename from the database config name.
127
137
  def schema_dump(format = ActiveRecord.schema_format)
128
138
  if configuration_hash.key?(:schema_dump)
@@ -2,7 +2,9 @@
2
2
 
3
3
  module ActiveRecord
4
4
  class DatabaseConfigurations
5
- # A UrlConfig object is created for each database configuration
5
+ # = Active Record Database Url Config
6
+ #
7
+ # A +UrlConfig+ object is created for each database configuration
6
8
  # entry that is created from a URL. This can either be a URL string
7
9
  # or a hash with a URL in place of the config hash.
8
10
  #
@@ -17,20 +19,24 @@ module ActiveRecord
17
19
  # @config={adapter: "postgresql", database: "foo", host: "localhost"},
18
20
  # @url="postgres://localhost/foo">
19
21
  #
20
- # ==== Options
22
+ # See ActiveRecord::DatabaseConfigurations for more info.
21
23
  #
22
- # * <tt>:env_name</tt> - The Rails environment, i.e. "development".
23
- # * <tt>:name</tt> - The db config name. In a standard two-tier
24
- # database configuration this will default to "primary". In a multiple
25
- # database three-tier database configuration this corresponds to the name
26
- # used in the second tier, for example "primary_readonly".
27
- # * <tt>:url</tt> - The database URL.
28
- # * <tt>:config</tt> - The config hash. This is the hash that contains the
29
- # database adapter, name, and other important information for database
30
- # connections.
31
24
  class UrlConfig < HashConfig
32
25
  attr_reader :url
33
26
 
27
+ # Initialize a new +UrlConfig+ object
28
+ #
29
+ # ==== Options
30
+ #
31
+ # * <tt>:env_name</tt> - The \Rails environment, i.e. "development".
32
+ # * <tt>:name</tt> - The db config name. In a standard two-tier
33
+ # database configuration this will default to "primary". In a multiple
34
+ # database three-tier database configuration this corresponds to the name
35
+ # used in the second tier, for example "primary_readonly".
36
+ # * <tt>:url</tt> - The database URL.
37
+ # * <tt>:config</tt> - The config hash. This is the hash that contains the
38
+ # database adapter, name, and other important information for database
39
+ # connections.
34
40
  def initialize(env_name, name, url, configuration_hash = {})
35
41
  super(env_name, name, configuration_hash)
36
42
 
@@ -7,15 +7,69 @@ require "active_record/database_configurations/url_config"
7
7
  require "active_record/database_configurations/connection_url_resolver"
8
8
 
9
9
  module ActiveRecord
10
- # ActiveRecord::DatabaseConfigurations returns an array of DatabaseConfig
11
- # objects (either a HashConfig or UrlConfig) that are constructed from the
12
- # application's database configuration hash or URL string.
10
+ # = Active Record Database Configurations
11
+ #
12
+ # +ActiveRecord::DatabaseConfigurations+ returns an array of +DatabaseConfig+
13
+ # objects that are constructed from the application's database
14
+ # configuration hash or URL string.
15
+ #
16
+ # The array of +DatabaseConfig+ objects in an application default to either a
17
+ # HashConfig or UrlConfig. You can retrieve your application's config by using
18
+ # ActiveRecord::Base.configurations.
19
+ #
20
+ # If you register a custom handler, objects will be created according to the
21
+ # conditions of the handler. See ::register_db_config_handler for more on
22
+ # registering custom handlers.
13
23
  class DatabaseConfigurations
14
24
  class InvalidConfigurationError < StandardError; end
15
25
 
16
26
  attr_reader :configurations
17
27
  delegate :any?, to: :configurations
18
28
 
29
+ singleton_class.attr_accessor :db_config_handlers # :nodoc:
30
+ self.db_config_handlers = [] # :nodoc:
31
+
32
+ # Allows an application to register a custom handler for database configuration
33
+ # objects. This is useful for creating a custom handler that responds to
34
+ # methods your application needs but Active Record doesn't implement. For
35
+ # example if you are using Vitess, you may want your Vitess configurations
36
+ # to respond to `sharded?`. To implement this define the following in an
37
+ # initializer:
38
+ #
39
+ # ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
40
+ # next unless config.key?(:vitess)
41
+ # VitessConfig.new(env_name, name, config)
42
+ # end
43
+ #
44
+ # Note: applications must handle the condition in which custom config should be
45
+ # created in your handler registration otherwise all objects will use the custom
46
+ # handler.
47
+ #
48
+ # Then define your +VitessConfig+ to respond to the methods your application
49
+ # needs. It is recommended that you inherit from one of the existing
50
+ # database config classes to avoid having to reimplement all methods. Custom
51
+ # config handlers should only implement methods Active Record does not.
52
+ #
53
+ # class VitessConfig < ActiveRecord::DatabaseConfigurations::UrlConfig
54
+ # def sharded?
55
+ # configuration_hash.fetch("sharded", false)
56
+ # end
57
+ # end
58
+ #
59
+ # For configs that have a +:vitess+ key, a +VitessConfig+ object will be
60
+ # created instead of a +UrlConfig+.
61
+ def self.register_db_config_handler(&block)
62
+ db_config_handlers << block
63
+ end
64
+
65
+ register_db_config_handler do |env_name, name, url, config|
66
+ if url
67
+ UrlConfig.new(env_name, name, url, config)
68
+ else
69
+ HashConfig.new(env_name, name, config)
70
+ end
71
+ end
72
+
19
73
  def initialize(configurations = {})
20
74
  @configurations = build_configs(configurations)
21
75
  end
@@ -23,8 +77,8 @@ module ActiveRecord
23
77
  # Collects the configs for the environment and optionally the specification
24
78
  # name passed in. To include replica configurations pass <tt>include_hidden: true</tt>.
25
79
  #
26
- # If a name is provided a single DatabaseConfig object will be
27
- # returned, otherwise an array of DatabaseConfig objects will be
80
+ # If a name is provided a single +DatabaseConfig+ object will be
81
+ # returned, otherwise an array of +DatabaseConfig+ objects will be
28
82
  # returned that corresponds with the environment and type requested.
29
83
  #
30
84
  # ==== Options
@@ -34,20 +88,14 @@ module ActiveRecord
34
88
  # * <tt>name:</tt> The db config name (i.e. primary, animals, etc.). Defaults
35
89
  # to +nil+. If no +env_name+ is specified the config for the default env and the
36
90
  # passed +name+ will be returned.
37
- # * <tt>include_replicas:</tt> Deprecated. Determines whether to include replicas in
38
- # the returned list. Most of the time we're only iterating over the write
39
- # connection (i.e. migrations don't need to run for the write and read connection).
40
- # Defaults to +false+.
91
+ # * <tt>config_key:</tt> Selects configs that contain a particular key in the configuration
92
+ # hash. Useful for selecting configs that use a custom db config handler or finding
93
+ # configs with hashes that contain a particular key.
41
94
  # * <tt>include_hidden:</tt> Determines whether to include replicas and configurations
42
- # hidden by +database_tasks: false+ in the returned list. Most of the time we're only
95
+ # hidden by <tt>database_tasks: false</tt> in the returned list. Most of the time we're only
43
96
  # iterating over the primary connections (i.e. migrations don't need to run for the
44
97
  # write and read connection). Defaults to +false+.
45
- def configs_for(env_name: nil, name: nil, include_replicas: false, include_hidden: false)
46
- if include_replicas
47
- include_hidden = include_replicas
48
- ActiveSupport::Deprecation.warn("The kwarg `include_replicas` is deprecated in favor of `include_hidden`. When `include_hidden` is passed, configurations with `replica: true` or `database_tasks: false` will be returned. `include_replicas` will be removed in Rails 7.1.")
49
- end
50
-
98
+ def configs_for(env_name: nil, name: nil, config_key: nil, include_hidden: false)
51
99
  env_name ||= default_env if name
52
100
  configs = env_with_configs(env_name)
53
101
 
@@ -57,6 +105,12 @@ module ActiveRecord
57
105
  end
58
106
  end
59
107
 
108
+ if config_key
109
+ configs = configs.select do |db_config|
110
+ db_config.configuration_hash.key?(config_key)
111
+ end
112
+ end
113
+
60
114
  if name
61
115
  configs.find do |db_config|
62
116
  db_config.name == name
@@ -66,17 +120,17 @@ module ActiveRecord
66
120
  end
67
121
  end
68
122
 
69
- # Returns a single DatabaseConfig object based on the requested environment.
123
+ # Returns a single +DatabaseConfig+ object based on the requested environment.
70
124
  #
71
125
  # If the application has multiple databases +find_db_config+ will return
72
- # the first DatabaseConfig for the environment.
126
+ # the first +DatabaseConfig+ for the environment.
73
127
  def find_db_config(env)
74
- configurations
75
- .sort_by.with_index { |db_config, i| db_config.for_current_env? ? [0, i] : [1, i] }
76
- .find do |db_config|
77
- db_config.env_name == env.to_s ||
78
- (db_config.for_current_env? && db_config.name == env.to_s)
79
- end
128
+ env = env.to_s
129
+ configurations.find do |db_config|
130
+ db_config.for_current_env? && (db_config.env_name == env || db_config.name == env)
131
+ end || configurations.find do |db_config|
132
+ db_config.env_name == env
133
+ end
80
134
  end
81
135
 
82
136
  # A primary configuration is one that is named primary or if there is
@@ -93,8 +147,6 @@ module ActiveRecord
93
147
  end
94
148
 
95
149
  # Checks if the application's configurations are empty.
96
- #
97
- # Aliased to blank?
98
150
  def empty?
99
151
  configurations.empty?
100
152
  end
@@ -219,15 +271,16 @@ module ActiveRecord
219
271
  end
220
272
 
221
273
  def build_db_config_from_hash(env_name, name, config)
222
- if config.has_key?(:url)
223
- url = config[:url]
224
- config_without_url = config.dup
225
- config_without_url.delete :url
274
+ url = config[:url]
275
+ config_without_url = config.dup
276
+ config_without_url.delete :url
226
277
 
227
- UrlConfig.new(env_name, name, url, config_without_url)
228
- else
229
- HashConfig.new(env_name, name, config)
278
+ DatabaseConfigurations.db_config_handlers.reverse_each do |handler|
279
+ config = handler.call(env_name, name, url, config_without_url)
280
+ return config if config
230
281
  end
282
+
283
+ nil
231
284
  end
232
285
 
233
286
  def merge_db_environment_variables(current_env, configs)
@@ -3,7 +3,7 @@
3
3
  require "active_support/core_ext/string/inquiry"
4
4
 
5
5
  module ActiveRecord
6
- # == Delegated types
6
+ # = Delegated types
7
7
  #
8
8
  # Class hierarchies can map to relational database tables in many ways. Active Record, for example, offers
9
9
  # purely abstract classes, where the superclass doesn't persist any attributes, and single-table inheritance,
@@ -138,7 +138,7 @@ module ActiveRecord
138
138
  #
139
139
  # Now you can list a bunch of entries, call <tt>Entry#title</tt>, and polymorphism will provide you with the answer.
140
140
  #
141
- # == Nested Attributes
141
+ # == Nested \Attributes
142
142
  #
143
143
  # Enabling nested attributes on a delegated_type association allows you to
144
144
  # create the entry and message in one go:
@@ -192,6 +192,11 @@ module ActiveRecord
192
192
  # +role+ with an "_id" suffix. So a class that defines a
193
193
  # <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_id" as
194
194
  # the default <tt>:foreign_key</tt>.
195
+ # [:foreign_type]
196
+ # Specify the column used to store the associated object's type. By default this is inferred to be the passed
197
+ # +role+ with a "_type" suffix. A class that defines a
198
+ # <tt>delegated_type :entryable, types: %w[ Message Comment ]</tt> association will use "entryable_type" as
199
+ # the default <tt>:foreign_type</tt>.
195
200
  # [:primary_key]
196
201
  # Specify the method that returns the primary key of associated object used for the convenience methods.
197
202
  # By default this is +id+.
@@ -211,11 +216,11 @@ module ActiveRecord
211
216
  private
212
217
  def define_delegated_type_methods(role, types:, options:)
213
218
  primary_key = options[:primary_key] || "id"
214
- role_type = "#{role}_type"
219
+ role_type = options[:foreign_type] || "#{role}_type"
215
220
  role_id = options[:foreign_key] || "#{role}_id"
216
221
 
217
222
  define_method "#{role}_class" do
218
- public_send("#{role}_type").constantize
223
+ public_send(role_type).constantize
219
224
  end
220
225
 
221
226
  define_method "#{role}_name" do
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ def self.deprecator # :nodoc:
5
+ @deprecator ||= ActiveSupport::Deprecation.new
6
+ end
7
+ end
@@ -4,6 +4,8 @@ module ActiveRecord
4
4
  class DestroyAssociationAsyncError < StandardError
5
5
  end
6
6
 
7
+ # = Active Record Destroy Association Async Job
8
+ #
7
9
  # Job to destroy the records associated with a destroyed record in background.
8
10
  class DestroyAssociationAsyncJob < ActiveJob::Base
9
11
  queue_as { ActiveRecord.queues[:destroy] }
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Encryption
5
+ class AutoFilteredParameters
6
+ def initialize(app)
7
+ @app = app
8
+ @attributes_by_class = Concurrent::Map.new
9
+ @collecting = true
10
+
11
+ install_collecting_hook
12
+ end
13
+
14
+ def enable
15
+ apply_collected_attributes
16
+ @collecting = false
17
+ end
18
+
19
+ private
20
+ attr_reader :app
21
+
22
+ def install_collecting_hook
23
+ ActiveRecord::Encryption.on_encrypted_attribute_declared do |klass, attribute|
24
+ attribute_was_declared(klass, attribute)
25
+ end
26
+ end
27
+
28
+ def attribute_was_declared(klass, attribute)
29
+ if collecting?
30
+ collect_for_later(klass, attribute)
31
+ else
32
+ apply_filter(klass, attribute)
33
+ end
34
+ end
35
+
36
+ def apply_collected_attributes
37
+ @attributes_by_class.each do |klass, attributes|
38
+ attributes.each do |attribute|
39
+ apply_filter(klass, attribute)
40
+ end
41
+ end
42
+ end
43
+
44
+ def collecting?
45
+ @collecting
46
+ end
47
+
48
+ def collect_for_later(klass, attribute)
49
+ @attributes_by_class[klass] ||= Concurrent::Array.new
50
+ @attributes_by_class[klass] << attribute
51
+ end
52
+
53
+ def apply_filter(klass, attribute)
54
+ filter = [("#{klass.model_name.element}" if klass.name), attribute.to_s].compact.join(".")
55
+ unless excluded_from_filter_parameters?(filter)
56
+ app.config.filter_parameters << filter unless app.config.filter_parameters.include?(filter)
57
+ klass.filter_attributes += [ attribute ]
58
+ end
59
+ end
60
+
61
+ def excluded_from_filter_parameters?(filter_parameter)
62
+ ActiveRecord::Encryption.config.excluded_from_filter_parameters.find { |excluded_filter| excluded_filter.to_s == filter_parameter }
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "openssl"
4
- require "base64"
5
4
 
6
5
  module ActiveRecord
7
6
  module Encryption
@@ -80,6 +79,10 @@ module ActiveRecord
80
79
  raise ActiveRecord::Encryption::Errors::Decryption
81
80
  end
82
81
 
82
+ def inspect # :nodoc:
83
+ "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
84
+ end
85
+
83
86
  private
84
87
  def generate_iv(cipher, clear_text)
85
88
  if @deterministic
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "openssl"
4
+
3
5
  module ActiveRecord
4
6
  module Encryption
5
7
  # Container of configuration options
6
8
  class Config
7
- attr_accessor :primary_key, :deterministic_key, :store_key_references, :key_derivation_salt,
9
+ attr_accessor :primary_key, :deterministic_key, :store_key_references, :key_derivation_salt, :hash_digest_class,
8
10
  :support_unencrypted_data, :encrypt_fixtures, :validate_column_size, :add_to_filter_parameters,
9
11
  :excluded_from_filter_parameters, :extend_queries, :previous_schemes, :forced_encoding_for_deterministic_encryption
10
12
 
@@ -21,6 +23,27 @@ module ActiveRecord
21
23
  end
22
24
  end
23
25
 
26
+ def support_sha1_for_non_deterministic_encryption=(value)
27
+ if value && has_primary_key?
28
+ sha1_key_generator = ActiveRecord::Encryption::KeyGenerator.new(hash_digest_class: OpenSSL::Digest::SHA1)
29
+ sha1_key_provider = ActiveRecord::Encryption::DerivedSecretKeyProvider.new(primary_key, key_generator: sha1_key_generator)
30
+ add_previous_scheme key_provider: sha1_key_provider
31
+ end
32
+ end
33
+
34
+ %w(key_derivation_salt primary_key deterministic_key).each do |key|
35
+ silence_redefinition_of_method "has_#{key}?"
36
+ define_method("has_#{key}?") do
37
+ instance_variable_get(:"@#{key}").presence
38
+ end
39
+
40
+ silence_redefinition_of_method key
41
+ define_method(key) do
42
+ public_send("has_#{key}?") or
43
+ raise Errors::Configuration, "Missing Active Record encryption credential: active_record_encryption.#{key}"
44
+ end
45
+ end
46
+
24
47
  private
25
48
  def set_defaults
26
49
  self.store_key_references = false
@@ -31,6 +54,7 @@ module ActiveRecord
31
54
  self.excluded_from_filter_parameters = []
32
55
  self.previous_schemes = []
33
56
  self.forced_encoding_for_deterministic_encryption = Encoding::UTF_8
57
+ self.hash_digest_class = OpenSSL::Digest::SHA1
34
58
 
35
59
  # TODO: Setting to false for now as the implementation is a bit experimental
36
60
  self.extend_queries = false
@@ -17,24 +17,29 @@ module ActiveRecord
17
17
  delegate name, to: :context
18
18
  end
19
19
 
20
- def configure(primary_key:, deterministic_key:, key_derivation_salt:, **properties) # :nodoc:
20
+ def configure(primary_key: nil, deterministic_key: nil, key_derivation_salt: nil, **properties) # :nodoc:
21
21
  config.primary_key = primary_key
22
22
  config.deterministic_key = deterministic_key
23
23
  config.key_derivation_salt = key_derivation_salt
24
24
 
25
- context.key_provider = ActiveRecord::Encryption::DerivedSecretKeyProvider.new(primary_key)
25
+ # Set the default for this property here instead of in +Config#set_defaults+ as this needs
26
+ # to happen *after* the keys have been set.
27
+ properties[:support_sha1_for_non_deterministic_encryption] = true if properties[:support_sha1_for_non_deterministic_encryption].nil?
26
28
 
27
29
  properties.each do |name, value|
28
- [:context, :config].each do |configurable_object_name|
29
- configurable_object = ActiveRecord::Encryption.send(configurable_object_name)
30
- configurable_object.send "#{name}=", value if configurable_object.respond_to?("#{name}=")
31
- end
30
+ ActiveRecord::Encryption.config.send "#{name}=", value if ActiveRecord::Encryption.config.respond_to?("#{name}=")
31
+ end
32
+
33
+ ActiveRecord::Encryption.reset_default_context
34
+
35
+ properties.each do |name, value|
36
+ ActiveRecord::Encryption.context.send "#{name}=", value if ActiveRecord::Encryption.context.respond_to?("#{name}=")
32
37
  end
33
38
  end
34
39
 
35
40
  # Register callback to be invoked when an encrypted attribute is declared.
36
41
  #
37
- # === Example:
42
+ # === Example
38
43
  #
39
44
  # ActiveRecord::Encryption.on_encrypted_attribute_declared do |klass, attribute_name|
40
45
  # ...
@@ -49,18 +54,6 @@ module ActiveRecord
49
54
  block.call(klass, name)
50
55
  end
51
56
  end
52
-
53
- def install_auto_filtered_parameters_hook(application) # :nodoc:
54
- ActiveRecord::Encryption.on_encrypted_attribute_declared do |klass, encrypted_attribute_name|
55
- filter_parameter = [("#{klass.model_name.element}" if klass.name), encrypted_attribute_name.to_s].compact.join(".")
56
- application.config.filter_parameters << filter_parameter unless excluded_from_filter_parameters?(filter_parameter)
57
- end
58
- end
59
-
60
- private
61
- def excluded_from_filter_parameters?(filter_parameter)
62
- ActiveRecord::Encryption.config.excluded_from_filter_parameters.find { |excluded_filter| excluded_filter.to_s == filter_parameter }
63
- end
64
57
  end
65
58
  end
66
59
  end
@@ -12,9 +12,7 @@ module ActiveRecord
12
12
  class Context
13
13
  PROPERTIES = %i[ key_provider key_generator cipher message_serializer encryptor frozen_encryption ]
14
14
 
15
- PROPERTIES.each do |name|
16
- attr_accessor name
17
- end
15
+ attr_accessor(*PROPERTIES)
18
16
 
19
17
  def initialize
20
18
  set_defaults
@@ -22,6 +20,11 @@ module ActiveRecord
22
20
 
23
21
  alias frozen_encryption? frozen_encryption
24
22
 
23
+ silence_redefinition_of_method :key_provider
24
+ def key_provider
25
+ @key_provider ||= build_default_key_provider
26
+ end
27
+
25
28
  private
26
29
  def set_defaults
27
30
  self.frozen_encryption = false
@@ -30,6 +33,10 @@ module ActiveRecord
30
33
  self.encryptor = ActiveRecord::Encryption::Encryptor.new
31
34
  self.message_serializer = ActiveRecord::Encryption::MessageSerializer.new
32
35
  end
36
+
37
+ def build_default_key_provider
38
+ ActiveRecord::Encryption::DerivedSecretKeyProvider.new(ActiveRecord::Encryption.config.primary_key)
39
+ end
33
40
  end
34
41
  end
35
42
  end
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  extend ActiveSupport::Concern
15
15
 
16
16
  included do
17
- mattr_reader :default_context, default: Context.new
17
+ mattr_accessor :default_context, default: Context.new
18
18
  thread_mattr_accessor :custom_contexts
19
19
  end
20
20
 
@@ -66,6 +66,10 @@ module ActiveRecord
66
66
  def current_custom_context
67
67
  self.custom_contexts&.last
68
68
  end
69
+
70
+ def reset_default_context
71
+ self.default_context = Context.new
72
+ end
69
73
  end
70
74
  end
71
75
  end
@@ -4,9 +4,15 @@ module ActiveRecord
4
4
  module Encryption
5
5
  # A KeyProvider that derives keys from passwords.
6
6
  class DerivedSecretKeyProvider < KeyProvider
7
- def initialize(passwords)
8
- super(Array(passwords).collect { |password| Key.derive_from(password) })
7
+ def initialize(passwords, key_generator: ActiveRecord::Encryption.key_generator)
8
+ super(Array(passwords).collect { |password| derive_key_from(password, using: key_generator) })
9
9
  end
10
+
11
+ private
12
+ def derive_key_from(password, using: key_generator)
13
+ secret = using.derive_key_from(password)
14
+ ActiveRecord::Encryption::Key.new(secret)
15
+ end
10
16
  end
11
17
  end
12
18
  end