activerecord 6.1.7 → 7.1.5

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 (311) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2030 -1020
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +51 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +39 -35
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  27. data/lib/active_record/associations/join_dependency.rb +28 -20
  28. data/lib/active_record/associations/preloader/association.rb +210 -52
  29. data/lib/active_record/associations/preloader/batch.rb +48 -0
  30. data/lib/active_record/associations/preloader/branch.rb +147 -0
  31. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  32. data/lib/active_record/associations/preloader.rb +50 -121
  33. data/lib/active_record/associations/singular_association.rb +9 -3
  34. data/lib/active_record/associations/through_association.rb +25 -14
  35. data/lib/active_record/associations.rb +446 -306
  36. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  37. data/lib/active_record/attribute_assignment.rb +1 -3
  38. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  39. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  40. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  41. data/lib/active_record/attribute_methods/query.rb +31 -19
  42. data/lib/active_record/attribute_methods/read.rb +27 -12
  43. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  45. data/lib/active_record/attribute_methods/write.rb +12 -15
  46. data/lib/active_record/attribute_methods.rb +161 -40
  47. data/lib/active_record/attributes.rb +27 -38
  48. data/lib/active_record/autosave_association.rb +65 -31
  49. data/lib/active_record/base.rb +25 -2
  50. data/lib/active_record/callbacks.rb +18 -34
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -46
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
  70. data/lib/active_record/connection_adapters/column.rb +13 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  89. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
  103. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  104. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  107. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
  110. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  111. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  112. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  113. data/lib/active_record/connection_adapters.rb +9 -6
  114. data/lib/active_record/connection_handling.rb +108 -137
  115. data/lib/active_record/core.rb +242 -233
  116. data/lib/active_record/counter_cache.rb +52 -27
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
  118. data/lib/active_record/database_configurations/database_config.rb +21 -12
  119. data/lib/active_record/database_configurations/hash_config.rb +88 -16
  120. data/lib/active_record/database_configurations/url_config.rb +18 -12
  121. data/lib/active_record/database_configurations.rb +95 -59
  122. data/lib/active_record/delegated_type.rb +66 -20
  123. data/lib/active_record/deprecator.rb +7 -0
  124. data/lib/active_record/destroy_association_async_job.rb +4 -2
  125. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  126. data/lib/active_record/dynamic_matchers.rb +1 -1
  127. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  128. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  129. data/lib/active_record/encryption/cipher.rb +53 -0
  130. data/lib/active_record/encryption/config.rb +68 -0
  131. data/lib/active_record/encryption/configurable.rb +60 -0
  132. data/lib/active_record/encryption/context.rb +42 -0
  133. data/lib/active_record/encryption/contexts.rb +76 -0
  134. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  135. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  136. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  137. data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
  138. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  139. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  140. data/lib/active_record/encryption/encryptor.rb +155 -0
  141. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  142. data/lib/active_record/encryption/errors.rb +15 -0
  143. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  144. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  145. data/lib/active_record/encryption/key.rb +28 -0
  146. data/lib/active_record/encryption/key_generator.rb +53 -0
  147. data/lib/active_record/encryption/key_provider.rb +46 -0
  148. data/lib/active_record/encryption/message.rb +33 -0
  149. data/lib/active_record/encryption/message_serializer.rb +92 -0
  150. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  151. data/lib/active_record/encryption/properties.rb +76 -0
  152. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  153. data/lib/active_record/encryption/scheme.rb +100 -0
  154. data/lib/active_record/encryption.rb +58 -0
  155. data/lib/active_record/enum.rb +154 -63
  156. data/lib/active_record/errors.rb +172 -15
  157. data/lib/active_record/explain.rb +23 -3
  158. data/lib/active_record/explain_registry.rb +11 -6
  159. data/lib/active_record/explain_subscriber.rb +1 -1
  160. data/lib/active_record/fixture_set/file.rb +15 -1
  161. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  162. data/lib/active_record/fixture_set/render_context.rb +2 -0
  163. data/lib/active_record/fixture_set/table_row.rb +70 -14
  164. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  165. data/lib/active_record/fixtures.rb +147 -86
  166. data/lib/active_record/future_result.rb +174 -0
  167. data/lib/active_record/gem_version.rb +3 -3
  168. data/lib/active_record/inheritance.rb +81 -29
  169. data/lib/active_record/insert_all.rb +135 -22
  170. data/lib/active_record/integration.rb +11 -10
  171. data/lib/active_record/internal_metadata.rb +119 -33
  172. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  173. data/lib/active_record/locking/optimistic.rb +37 -22
  174. data/lib/active_record/locking/pessimistic.rb +15 -6
  175. data/lib/active_record/log_subscriber.rb +52 -19
  176. data/lib/active_record/marshalling.rb +59 -0
  177. data/lib/active_record/message_pack.rb +124 -0
  178. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  179. data/lib/active_record/middleware/database_selector.rb +23 -13
  180. data/lib/active_record/middleware/shard_selector.rb +62 -0
  181. data/lib/active_record/migration/command_recorder.rb +112 -14
  182. data/lib/active_record/migration/compatibility.rb +233 -46
  183. data/lib/active_record/migration/default_strategy.rb +23 -0
  184. data/lib/active_record/migration/execution_strategy.rb +19 -0
  185. data/lib/active_record/migration/join_table.rb +1 -1
  186. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  187. data/lib/active_record/migration.rb +361 -173
  188. data/lib/active_record/model_schema.rb +125 -101
  189. data/lib/active_record/nested_attributes.rb +50 -20
  190. data/lib/active_record/no_touching.rb +3 -3
  191. data/lib/active_record/normalization.rb +167 -0
  192. data/lib/active_record/persistence.rb +409 -88
  193. data/lib/active_record/promise.rb +84 -0
  194. data/lib/active_record/query_cache.rb +4 -22
  195. data/lib/active_record/query_logs.rb +174 -0
  196. data/lib/active_record/query_logs_formatter.rb +41 -0
  197. data/lib/active_record/querying.rb +29 -6
  198. data/lib/active_record/railtie.rb +220 -44
  199. data/lib/active_record/railties/controller_runtime.rb +15 -10
  200. data/lib/active_record/railties/databases.rake +188 -252
  201. data/lib/active_record/railties/job_runtime.rb +23 -0
  202. data/lib/active_record/readonly_attributes.rb +41 -3
  203. data/lib/active_record/reflection.rb +248 -81
  204. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  205. data/lib/active_record/relation/batches.rb +192 -63
  206. data/lib/active_record/relation/calculations.rb +246 -90
  207. data/lib/active_record/relation/delegation.rb +28 -14
  208. data/lib/active_record/relation/finder_methods.rb +108 -51
  209. data/lib/active_record/relation/merger.rb +22 -13
  210. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  211. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  212. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  213. data/lib/active_record/relation/predicate_builder.rb +27 -20
  214. data/lib/active_record/relation/query_attribute.rb +30 -12
  215. data/lib/active_record/relation/query_methods.rb +670 -129
  216. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  217. data/lib/active_record/relation/spawn_methods.rb +20 -3
  218. data/lib/active_record/relation/where_clause.rb +10 -19
  219. data/lib/active_record/relation.rb +287 -120
  220. data/lib/active_record/result.rb +37 -11
  221. data/lib/active_record/runtime_registry.rb +32 -13
  222. data/lib/active_record/sanitization.rb +65 -20
  223. data/lib/active_record/schema.rb +36 -22
  224. data/lib/active_record/schema_dumper.rb +73 -24
  225. data/lib/active_record/schema_migration.rb +68 -33
  226. data/lib/active_record/scoping/default.rb +72 -15
  227. data/lib/active_record/scoping/named.rb +5 -13
  228. data/lib/active_record/scoping.rb +65 -34
  229. data/lib/active_record/secure_password.rb +60 -0
  230. data/lib/active_record/secure_token.rb +21 -3
  231. data/lib/active_record/serialization.rb +6 -1
  232. data/lib/active_record/signed_id.rb +10 -8
  233. data/lib/active_record/store.rb +10 -10
  234. data/lib/active_record/suppressor.rb +13 -15
  235. data/lib/active_record/table_metadata.rb +16 -3
  236. data/lib/active_record/tasks/database_tasks.rb +251 -140
  237. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  238. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  239. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  240. data/lib/active_record/test_databases.rb +1 -1
  241. data/lib/active_record/test_fixtures.rb +117 -96
  242. data/lib/active_record/timestamp.rb +32 -19
  243. data/lib/active_record/token_for.rb +113 -0
  244. data/lib/active_record/touch_later.rb +11 -6
  245. data/lib/active_record/transactions.rb +48 -27
  246. data/lib/active_record/translation.rb +3 -3
  247. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  248. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  249. data/lib/active_record/type/internal/timezone.rb +7 -2
  250. data/lib/active_record/type/serialized.rb +9 -5
  251. data/lib/active_record/type/time.rb +4 -0
  252. data/lib/active_record/type/type_map.rb +17 -20
  253. data/lib/active_record/type.rb +1 -2
  254. data/lib/active_record/validations/absence.rb +1 -1
  255. data/lib/active_record/validations/associated.rb +4 -4
  256. data/lib/active_record/validations/numericality.rb +5 -4
  257. data/lib/active_record/validations/presence.rb +5 -28
  258. data/lib/active_record/validations/uniqueness.rb +51 -6
  259. data/lib/active_record/validations.rb +8 -4
  260. data/lib/active_record/version.rb +1 -1
  261. data/lib/active_record.rb +335 -32
  262. data/lib/arel/attributes/attribute.rb +0 -8
  263. data/lib/arel/crud.rb +28 -22
  264. data/lib/arel/delete_manager.rb +18 -4
  265. data/lib/arel/errors.rb +10 -0
  266. data/lib/arel/factory_methods.rb +4 -0
  267. data/lib/arel/filter_predications.rb +9 -0
  268. data/lib/arel/insert_manager.rb +2 -3
  269. data/lib/arel/nodes/and.rb +4 -0
  270. data/lib/arel/nodes/binary.rb +6 -1
  271. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  272. data/lib/arel/nodes/casted.rb +1 -1
  273. data/lib/arel/nodes/cte.rb +36 -0
  274. data/lib/arel/nodes/delete_statement.rb +12 -13
  275. data/lib/arel/nodes/filter.rb +10 -0
  276. data/lib/arel/nodes/fragments.rb +35 -0
  277. data/lib/arel/nodes/function.rb +1 -0
  278. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  279. data/lib/arel/nodes/insert_statement.rb +2 -2
  280. data/lib/arel/nodes/leading_join.rb +8 -0
  281. data/lib/arel/nodes/node.rb +111 -2
  282. data/lib/arel/nodes/select_core.rb +2 -2
  283. data/lib/arel/nodes/select_statement.rb +2 -2
  284. data/lib/arel/nodes/sql_literal.rb +6 -0
  285. data/lib/arel/nodes/table_alias.rb +4 -0
  286. data/lib/arel/nodes/update_statement.rb +8 -3
  287. data/lib/arel/nodes.rb +5 -0
  288. data/lib/arel/predications.rb +13 -3
  289. data/lib/arel/select_manager.rb +10 -4
  290. data/lib/arel/table.rb +9 -6
  291. data/lib/arel/tree_manager.rb +5 -13
  292. data/lib/arel/update_manager.rb +18 -4
  293. data/lib/arel/visitors/dot.rb +80 -90
  294. data/lib/arel/visitors/mysql.rb +16 -3
  295. data/lib/arel/visitors/postgresql.rb +0 -10
  296. data/lib/arel/visitors/to_sql.rb +141 -20
  297. data/lib/arel/visitors/visitor.rb +2 -2
  298. data/lib/arel.rb +18 -3
  299. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  300. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/migration.rb +3 -1
  302. data/lib/rails/generators/active_record/model/USAGE +113 -0
  303. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  304. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  305. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  306. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  307. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  308. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  309. metadata +96 -16
  310. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  311. data/lib/active_record/null_relation.rb +0 -67
@@ -7,9 +7,13 @@ module ActiveRecord
7
7
  class ActiveRecordError < StandardError
8
8
  end
9
9
 
10
- # Raised when trying to use a feature in Active Record which requires Active Job but the gem is not present.
11
- class ActiveJobRequiredError < ActiveRecordError
12
- end
10
+ # DEPRECATED: Previously raised when trying to use a feature in Active Record which
11
+ # requires Active Job but the gem is not present. Now raises a NameError.
12
+ include ActiveSupport::Deprecation::DeprecatedConstantAccessor
13
+ DeprecatedActiveJobRequiredError = Class.new(ActiveRecordError) # :nodoc:
14
+ deprecate_constant "ActiveJobRequiredError", "ActiveRecord::DeprecatedActiveJobRequiredError",
15
+ message: "ActiveRecord::ActiveJobRequiredError has been deprecated. If Active Job is not present, a NameError will be raised instead.",
16
+ deprecator: ActiveRecord.deprecator
13
17
 
14
18
  # Raised when the single-table inheritance mechanism fails to locate the subclass
15
19
  # (for example due to improper usage of column that
@@ -51,10 +55,31 @@ module ActiveRecord
51
55
  class AdapterNotFound < ActiveRecordError
52
56
  end
53
57
 
58
+ # Superclass for all errors raised from an Active Record adapter.
59
+ class AdapterError < ActiveRecordError
60
+ def initialize(message = nil, connection_pool: nil)
61
+ @connection_pool = connection_pool
62
+ super(message)
63
+ end
64
+
65
+ attr_reader :connection_pool
66
+ end
67
+
54
68
  # Raised when connection to the database could not been established (for example when
55
69
  # {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
56
70
  # is given a +nil+ object).
57
- class ConnectionNotEstablished < ActiveRecordError
71
+ class ConnectionNotEstablished < AdapterError
72
+ def initialize(message = nil, connection_pool: nil)
73
+ super(message, connection_pool: connection_pool)
74
+ end
75
+
76
+ def set_pool(connection_pool)
77
+ unless @connection_pool
78
+ @connection_pool = connection_pool
79
+ end
80
+
81
+ self
82
+ end
58
83
  end
59
84
 
60
85
  # Raised when a connection could not be obtained within the connection
@@ -63,10 +88,34 @@ module ActiveRecord
63
88
  class ConnectionTimeoutError < ConnectionNotEstablished
64
89
  end
65
90
 
91
+ # Raised when connection to the database could not been established because it was not
92
+ # able to connect to the host or when the authorization failed.
93
+ class DatabaseConnectionError < ConnectionNotEstablished
94
+ def initialize(message = nil)
95
+ super(message || "Database connection error")
96
+ end
97
+
98
+ class << self
99
+ def hostname_error(hostname)
100
+ DatabaseConnectionError.new(<<~MSG)
101
+ There is an issue connecting with your hostname: #{hostname}.\n
102
+ Please check your database configuration and ensure there is a valid connection to your database.
103
+ MSG
104
+ end
105
+
106
+ def username_error(username)
107
+ DatabaseConnectionError.new(<<~MSG)
108
+ There is an issue connecting to your database with your username/password, username: #{username}.\n
109
+ Please check your database configuration to ensure the username/password are valid.
110
+ MSG
111
+ end
112
+ end
113
+ end
114
+
66
115
  # Raised when a pool was unable to get ahold of all its connections
67
116
  # to perform a "group" action such as
68
117
  # {ActiveRecord::Base.connection_pool.disconnect!}[rdoc-ref:ConnectionAdapters::ConnectionPool#disconnect!]
69
- # or {ActiveRecord::Base.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
118
+ # or {ActiveRecord::Base.connection_handler.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
70
119
  class ExclusiveConnectionTimeoutError < ConnectionTimeoutError
71
120
  end
72
121
 
@@ -100,7 +149,7 @@ module ActiveRecord
100
149
  end
101
150
 
102
151
  # Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
103
- # when a call to {#destroy}[rdoc-ref:Persistence#destroy!]
152
+ # when a call to {#destroy}[rdoc-ref:Persistence#destroy]
104
153
  # would return false.
105
154
  #
106
155
  # begin
@@ -118,17 +167,36 @@ module ActiveRecord
118
167
  end
119
168
  end
120
169
 
170
+ # Raised when Active Record finds multiple records but only expected one.
171
+ class SoleRecordExceeded < ActiveRecordError
172
+ attr_reader :record
173
+
174
+ def initialize(record = nil)
175
+ @record = record
176
+ super "Wanted only one #{record&.name || "record"}"
177
+ end
178
+ end
179
+
121
180
  # Superclass for all database execution errors.
122
181
  #
123
182
  # Wraps the underlying database error as +cause+.
124
- class StatementInvalid < ActiveRecordError
125
- def initialize(message = nil, sql: nil, binds: nil)
126
- super(message || $!&.message)
183
+ class StatementInvalid < AdapterError
184
+ def initialize(message = nil, sql: nil, binds: nil, connection_pool: nil)
185
+ super(message || $!&.message, connection_pool: connection_pool)
127
186
  @sql = sql
128
187
  @binds = binds
129
188
  end
130
189
 
131
190
  attr_reader :sql, :binds
191
+
192
+ def set_query(sql, binds)
193
+ unless @sql
194
+ @sql = sql
195
+ @binds = binds
196
+ end
197
+
198
+ self
199
+ end
132
200
  end
133
201
 
134
202
  # Defunct wrapper class kept for compatibility.
@@ -155,8 +223,13 @@ module ActiveRecord
155
223
  foreign_key: nil,
156
224
  target_table: nil,
157
225
  primary_key: nil,
158
- primary_key_column: nil
226
+ primary_key_column: nil,
227
+ query_parser: nil,
228
+ connection_pool: nil
159
229
  )
230
+ @original_message = message
231
+ @query_parser = query_parser
232
+
160
233
  if table
161
234
  type = primary_key_column.bigint? ? :bigint : primary_key_column.type
162
235
  msg = <<~EOM.squish
@@ -174,7 +247,24 @@ module ActiveRecord
174
247
  if message
175
248
  msg << "\nOriginal message: #{message}"
176
249
  end
177
- super(msg, sql: sql, binds: binds)
250
+
251
+ super(msg, sql: sql, binds: binds, connection_pool: connection_pool)
252
+ end
253
+
254
+ def set_query(sql, binds)
255
+ if @query_parser && !@sql
256
+ self.class.new(
257
+ message: @original_message,
258
+ sql: sql,
259
+ binds: binds,
260
+ connection_pool: @connection_pool,
261
+ **@query_parser.call(sql)
262
+ ).tap do |exception|
263
+ exception.set_backtrace backtrace
264
+ end
265
+ else
266
+ super
267
+ end
178
268
  end
179
269
  end
180
270
 
@@ -190,6 +280,19 @@ module ActiveRecord
190
280
  class RangeError < StatementInvalid
191
281
  end
192
282
 
283
+ # Raised when a statement produces an SQL warning.
284
+ class SQLWarning < AdapterError
285
+ attr_reader :code, :level
286
+ attr_accessor :sql
287
+
288
+ def initialize(message = nil, code = nil, level = nil, sql = nil, connection_pool = nil)
289
+ super(message, connection_pool: connection_pool)
290
+ @code = code
291
+ @level = level
292
+ @sql = sql
293
+ end
294
+ end
295
+
193
296
  # Raised when the number of placeholders in an SQL fragment passed to
194
297
  # {ActiveRecord::Base.where}[rdoc-ref:QueryMethods#where]
195
298
  # does not match the number of values supplied.
@@ -202,6 +305,31 @@ module ActiveRecord
202
305
 
203
306
  # Raised when a given database does not exist.
204
307
  class NoDatabaseError < StatementInvalid
308
+ include ActiveSupport::ActionableError
309
+
310
+ action "Create database" do
311
+ ActiveRecord::Tasks::DatabaseTasks.create_current
312
+ end
313
+
314
+ def initialize(message = nil, connection_pool: nil)
315
+ super(message || "Database not found", connection_pool: connection_pool)
316
+ end
317
+
318
+ class << self
319
+ def db_error(db_name)
320
+ NoDatabaseError.new(<<~MSG)
321
+ We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml.
322
+
323
+ To resolve this error:
324
+
325
+ - Did you not create the database, or did you delete it? To create the database, run:
326
+
327
+ bin/rails db:create
328
+
329
+ - Has the database name changed? Verify that config/database.yml contains the correct database name.
330
+ MSG
331
+ end
332
+ end
205
333
  end
206
334
 
207
335
  # Raised when creating a database if it exists.
@@ -268,7 +396,7 @@ module ActiveRecord
268
396
  # # The system must fail on Friday so that our support department
269
397
  # # won't be out of job. We silently rollback this transaction
270
398
  # # without telling the user.
271
- # raise ActiveRecord::Rollback, "Call tech support!"
399
+ # raise ActiveRecord::Rollback
272
400
  # end
273
401
  # end
274
402
  # # ActiveRecord::Rollback is the only exception that won't be passed on
@@ -349,13 +477,26 @@ module ActiveRecord
349
477
  # * You are joining an existing open transaction
350
478
  # * You are creating a nested (savepoint) transaction
351
479
  #
352
- # The mysql2 and postgresql adapters support setting the transaction isolation level.
480
+ # The mysql2, trilogy, and postgresql adapters support setting the transaction isolation level.
353
481
  class TransactionIsolationError < ActiveRecordError
354
482
  end
355
483
 
356
484
  # TransactionRollbackError will be raised when a transaction is rolled
357
485
  # back by the database due to a serialization failure or a deadlock.
358
486
  #
487
+ # These exceptions should not be generally rescued in nested transaction
488
+ # blocks, because they have side-effects in the actual enclosing transaction
489
+ # and internal Active Record state. They can be rescued if you are above the
490
+ # root transaction block, though.
491
+ #
492
+ # In that case, beware of transactional tests, however, because they run test
493
+ # cases in their own umbrella transaction. If you absolutely need to handle
494
+ # these exceptions in tests please consider disabling transactional tests in
495
+ # the affected test class (<tt>self.use_transactional_tests = false</tt>).
496
+ #
497
+ # Due to the aforementioned side-effects, this exception should not be raised
498
+ # manually by users.
499
+ #
359
500
  # See the following:
360
501
  #
361
502
  # * https://www.postgresql.org/docs/current/static/transaction-iso.html
@@ -363,13 +504,24 @@ module ActiveRecord
363
504
  class TransactionRollbackError < StatementInvalid
364
505
  end
365
506
 
507
+ # AsynchronousQueryInsideTransactionError will be raised when attempting
508
+ # to perform an asynchronous query from inside a transaction
509
+ class AsynchronousQueryInsideTransactionError < ActiveRecordError
510
+ end
511
+
366
512
  # SerializationFailure will be raised when a transaction is rolled
367
513
  # back by the database due to a serialization failure.
514
+ #
515
+ # This is a subclass of TransactionRollbackError, please make sure to check
516
+ # its documentation to be aware of its caveats.
368
517
  class SerializationFailure < TransactionRollbackError
369
518
  end
370
519
 
371
520
  # Deadlocked will be raised when a transaction is rolled
372
521
  # back by the database when a deadlock is encountered.
522
+ #
523
+ # This is a subclass of TransactionRollbackError, please make sure to check
524
+ # its documentation to be aware of its caveats.
373
525
  class Deadlocked < TransactionRollbackError
374
526
  end
375
527
 
@@ -398,6 +550,11 @@ module ActiveRecord
398
550
  class AdapterTimeout < QueryAborted
399
551
  end
400
552
 
553
+ # ConnectionFailed will be raised when the network connection to the
554
+ # database fails while sending a query or waiting for its result.
555
+ class ConnectionFailed < QueryAborted
556
+ end
557
+
401
558
  # UnknownAttributeReference is raised when an unknown and potentially unsafe
402
559
  # value is passed to a query method. For example, passing a non column name
403
560
  # value to a relation's #order method might cause this exception.
@@ -409,12 +566,12 @@ module ActiveRecord
409
566
  #
410
567
  # For example, the following code would raise this exception:
411
568
  #
412
- # Post.order("length(title)").first
569
+ # Post.order("REPLACE(title, 'misc', 'zzzz') asc").pluck(:id)
413
570
  #
414
571
  # The desired result can be accomplished by wrapping the known-safe string
415
572
  # in Arel.sql:
416
573
  #
417
- # Post.order(Arel.sql("length(title)")).first
574
+ # Post.order(Arel.sql("REPLACE(title, 'misc', 'zzzz') asc")).pluck(:id)
418
575
  #
419
576
  # Again, such a workaround should *not* be used when passing user-provided
420
577
  # values, such as request parameters or model attributes to query methods.
@@ -16,15 +16,15 @@ module ActiveRecord
16
16
 
17
17
  # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
18
18
  # Returns a formatted string ready to be logged.
19
- def exec_explain(queries) # :nodoc:
19
+ def exec_explain(queries, options = []) # :nodoc:
20
20
  str = queries.map do |sql, binds|
21
- msg = +"EXPLAIN for: #{sql}"
21
+ msg = +"#{build_explain_clause(options)} #{sql}"
22
22
  unless binds.empty?
23
23
  msg << " "
24
24
  msg << binds.map { |attr| render_bind(attr) }.inspect
25
25
  end
26
26
  msg << "\n"
27
- msg << connection.explain(sql, binds)
27
+ msg << connection_explain(sql, binds, options)
28
28
  end.join("\n")
29
29
 
30
30
  # Overriding inspect to be more human readable, especially in the console.
@@ -50,5 +50,25 @@ module ActiveRecord
50
50
 
51
51
  [attr&.name, value]
52
52
  end
53
+
54
+ def build_explain_clause(options = [])
55
+ if connection.respond_to?(:build_explain_clause, true)
56
+ connection.build_explain_clause(options)
57
+ else
58
+ "EXPLAIN for:"
59
+ end
60
+ end
61
+
62
+ def connection_explain(sql, binds, options)
63
+ if connection.method(:explain).parameters.size == 2
64
+ ActiveRecord.deprecator.warn(<<~MSG.squish)
65
+ The current database adapter, #{connection.adapter_name}, does not support explain options.
66
+ To remove this warning, the adapter must implement `build_explain_clause(options = [])`.
67
+ MSG
68
+ connection.explain(sql, binds)
69
+ else
70
+ connection.explain(sql, binds, options)
71
+ end
72
+ end
53
73
  end
54
74
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/per_thread_registry"
3
+ require "active_support/core_ext/module/delegation"
4
4
 
5
5
  module ActiveRecord
6
6
  # This is a thread locals registry for EXPLAIN. For example
@@ -8,13 +8,18 @@ module ActiveRecord
8
8
  # ActiveRecord::ExplainRegistry.queries
9
9
  #
10
10
  # returns the collected queries local to the current thread.
11
- #
12
- # See the documentation of ActiveSupport::PerThreadRegistry
13
- # for further details.
14
11
  class ExplainRegistry # :nodoc:
15
- extend ActiveSupport::PerThreadRegistry
12
+ class << self
13
+ delegate :reset, :collect, :collect=, :collect?, :queries, to: :instance
14
+
15
+ private
16
+ def instance
17
+ ActiveSupport::IsolatedExecutionState[:active_record_explain_registry] ||= new
18
+ end
19
+ end
16
20
 
17
- attr_accessor :queries, :collect
21
+ attr_accessor :collect
22
+ attr_reader :queries
18
23
 
19
24
  def initialize
20
25
  reset
@@ -21,7 +21,7 @@ module ActiveRecord
21
21
  # On the other hand, we want to monitor the performance of our real database
22
22
  # queries, not the performance of the access to the query cache.
23
23
  IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN)
24
- EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
24
+ EXPLAINED_SQLS = /\A\s*(\/\*.*\*\/)?\s*(with|select|update|delete|insert)\b/i
25
25
  def ignore_payload?(payload)
26
26
  payload[:exception] ||
27
27
  payload[:cached] ||
@@ -41,7 +41,7 @@ module ActiveRecord
41
41
  @config_row ||= begin
42
42
  row = raw_rows.find { |fixture_name, _| fixture_name == "_fixture" }
43
43
  if row
44
- row.last
44
+ validate_config_row(row.last)
45
45
  else
46
46
  { 'model_class': nil, 'ignore': nil }
47
47
  end
@@ -58,6 +58,20 @@ module ActiveRecord
58
58
  end
59
59
  end
60
60
 
61
+ def validate_config_row(data)
62
+ unless Hash === data
63
+ raise Fixture::FormatError, "Invalid `_fixture` section: `_fixture` must be a hash: #{@file}"
64
+ end
65
+
66
+ begin
67
+ data.assert_valid_keys("model_class", "ignore")
68
+ rescue ArgumentError => error
69
+ raise Fixture::FormatError, "Invalid `_fixture` section: #{error.message}: #{@file}"
70
+ end
71
+
72
+ data
73
+ end
74
+
61
75
  # Validate our unmarshalled data.
62
76
  def validate(data)
63
77
  unless Hash === data || YAML::Omap === data
@@ -12,12 +12,22 @@ module ActiveRecord
12
12
  end
13
13
 
14
14
  def primary_key_type
15
- @primary_key_type ||= @model_class && @model_class.type_for_attribute(@model_class.primary_key).type
15
+ @primary_key_type ||= @model_class && column_type(@model_class.primary_key)
16
16
  end
17
17
 
18
- def has_primary_key_column?
19
- @has_primary_key_column ||= primary_key_name &&
20
- @model_class.columns.any? { |col| col.name == primary_key_name }
18
+ def column_type(column_name)
19
+ @column_type ||= {}
20
+ return @column_type[column_name] if @column_type.key?(column_name)
21
+
22
+ @column_type[column_name] = @model_class && @model_class.type_for_attribute(column_name).type
23
+ end
24
+
25
+ def has_column?(column_name)
26
+ column_names.include?(column_name)
27
+ end
28
+
29
+ def column_names
30
+ @column_names ||= @model_class ? @model_class.columns.map(&:name).to_set : Set.new
21
31
  end
22
32
 
23
33
  def timestamp_column_names
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "base64"
4
+
3
5
  # NOTE: This class has to be defined in compact style in
4
6
  # order for rendering context subclassing to work correctly.
5
7
  class ActiveRecord::FixtureSet::RenderContext # :nodoc:
@@ -33,6 +33,33 @@ module ActiveRecord
33
33
  def join_table
34
34
  @association.through_reflection.table_name
35
35
  end
36
+
37
+ def timestamp_column_names
38
+ @association.through_reflection.klass.all_timestamp_attributes_in_model
39
+ end
40
+ end
41
+
42
+ class PrimaryKeyError < StandardError # :nodoc:
43
+ def initialize(label, association, value)
44
+ super(<<~MSG)
45
+ Unable to set #{association.name} to #{value} because the association has a
46
+ custom primary key (#{association.join_primary_key}) that does not match the
47
+ associated table's primary key (#{association.klass.primary_key}).
48
+
49
+ To fix this, change your fixture from
50
+
51
+ #{label}:
52
+ #{association.name}: #{value}
53
+
54
+ to
55
+
56
+ #{label}:
57
+ #{association.foreign_key}: **value**
58
+
59
+ where **value** is the #{association.join_primary_key} value for the
60
+ associated #{association.klass.name} record.
61
+ MSG
62
+ end
36
63
  end
37
64
 
38
65
  def initialize(fixture, table_rows:, label:, now:)
@@ -60,7 +87,7 @@ module ActiveRecord
60
87
  return unless model_class
61
88
  fill_timestamps
62
89
  interpolate_label
63
- generate_primary_key
90
+ model_class.composite_primary_key? ? generate_composite_primary_key : generate_primary_key
64
91
  resolve_enums
65
92
  resolve_sti_reflections
66
93
  end
@@ -90,16 +117,28 @@ module ActiveRecord
90
117
  end
91
118
 
92
119
  def generate_primary_key
93
- # generate a primary key if necessary
94
- if model_metadata.has_primary_key_column? && !@row.include?(model_metadata.primary_key_name)
95
- @row[model_metadata.primary_key_name] = ActiveRecord::FixtureSet.identify(
96
- @label, model_metadata.primary_key_type
97
- )
120
+ pk = model_metadata.primary_key_name
121
+
122
+ unless column_defined?(pk)
123
+ @row[pk] = ActiveRecord::FixtureSet.identify(@label, model_metadata.column_type(pk))
124
+ end
125
+ end
126
+
127
+ def generate_composite_primary_key
128
+ composite_key = ActiveRecord::FixtureSet.composite_identify(@label, model_metadata.primary_key_name)
129
+ composite_key.each do |column, value|
130
+ next if column_defined?(column)
131
+
132
+ @row[column] = value
98
133
  end
99
134
  end
100
135
 
136
+ def column_defined?(col)
137
+ !model_metadata.has_column?(col) || @row.include?(col)
138
+ end
139
+
101
140
  def resolve_enums
102
- model_class.defined_enums.each do |name, values|
141
+ reflection_class.defined_enums.each do |name, values|
103
142
  if @row.include?(name)
104
143
  @row[name] = values.fetch(@row[name], @row[name])
105
144
  end
@@ -115,13 +154,26 @@ module ActiveRecord
115
154
  fk_name = association.join_foreign_key
116
155
 
117
156
  if association.name.to_s != fk_name && value = @row.delete(association.name.to_s)
118
- if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
119
- # support polymorphic belongs_to as "label (Type)"
120
- @row[association.join_foreign_type] = $1
157
+ if association.polymorphic?
158
+ if value.sub!(/\s*\(([^)]*)\)\s*$/, "")
159
+ # support polymorphic belongs_to as "label (Type)"
160
+ @row[association.join_foreign_type] = $1
161
+ end
162
+ elsif association.join_primary_key != association.klass.primary_key
163
+ raise PrimaryKeyError.new(@label, association, value)
121
164
  end
122
165
 
123
- fk_type = reflection_class.type_for_attribute(fk_name).type
124
- @row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
166
+ if fk_name.is_a?(Array)
167
+ composite_key = ActiveRecord::FixtureSet.composite_identify(value, fk_name)
168
+ composite_key.each do |column, value|
169
+ next if column_defined?(column)
170
+
171
+ @row[column] = value
172
+ end
173
+ else
174
+ fk_type = reflection_class.type_for_attribute(fk_name).type
175
+ @row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
176
+ end
125
177
  end
126
178
  when :has_many
127
179
  if association.options[:through]
@@ -141,8 +193,12 @@ module ActiveRecord
141
193
 
142
194
  targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
143
195
  joins = targets.map do |target|
144
- { lhs_key => @row[model_metadata.primary_key_name],
145
- rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
196
+ join = { lhs_key => @row[model_metadata.primary_key_name],
197
+ rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
198
+ association.timestamp_column_names.each do |col|
199
+ join[col] = @now
200
+ end
201
+ join
146
202
  end
147
203
  @table_rows.tables[table_name].concat(joins)
148
204
  end
@@ -6,7 +6,7 @@ require "active_record/fixture_set/model_metadata"
6
6
  module ActiveRecord
7
7
  class FixtureSet
8
8
  class TableRows # :nodoc:
9
- def initialize(table_name, model_class:, fixtures:, config:)
9
+ def initialize(table_name, model_class:, fixtures:)
10
10
  @model_class = model_class
11
11
 
12
12
  # track any join tables we need to insert later
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  # ensure this table is loaded before any HABTM associations
16
16
  @tables[table_name] = nil
17
17
 
18
- build_table_rows_from(table_name, fixtures, config)
18
+ build_table_rows_from(table_name, fixtures)
19
19
  end
20
20
 
21
21
  attr_reader :tables, :model_class
@@ -29,8 +29,8 @@ module ActiveRecord
29
29
  end
30
30
 
31
31
  private
32
- def build_table_rows_from(table_name, fixtures, config)
33
- now = config.default_timezone == :utc ? Time.now.utc : Time.now
32
+ def build_table_rows_from(table_name, fixtures)
33
+ now = ActiveRecord.default_timezone == :utc ? Time.now.utc : Time.now
34
34
 
35
35
  @tables[table_name] = fixtures.map do |label, fixture|
36
36
  TableRow.new(