activerecord 6.1.7 → 7.2.2

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 (333) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +616 -1290
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +19 -8
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  30. data/lib/active_record/associations/join_dependency.rb +28 -20
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +429 -522
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +1 -5
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +57 -54
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +19 -35
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -143
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +310 -253
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +58 -0
  165. data/lib/active_record/enum.rb +170 -62
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +59 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +145 -158
  199. data/lib/active_record/nested_attributes.rb +61 -23
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +18 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +229 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +332 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +200 -65
  216. data/lib/active_record/relation/calculations.rb +301 -112
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +870 -163
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +6 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +288 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +65 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/sqlite.rb +25 -0
  317. data/lib/arel/visitors/to_sql.rb +170 -36
  318. data/lib/arel/visitors/visitor.rb +2 -2
  319. data/lib/arel.rb +23 -4
  320. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  321. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  322. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  323. data/lib/rails/generators/active_record/migration.rb +3 -1
  324. data/lib/rails/generators/active_record/model/USAGE +113 -0
  325. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  326. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  328. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  329. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  330. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  331. metadata +103 -17
  332. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  333. data/lib/active_record/null_relation.rb +0 -67
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/deprecation"
4
+
3
5
  module ActiveRecord
6
+ include ActiveSupport::Deprecation::DeprecatedConstantAccessor
7
+
4
8
  # = Active Record Errors
5
9
  #
6
10
  # Generic Active Record exception class.
7
11
  class ActiveRecordError < StandardError
8
12
  end
9
13
 
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
13
-
14
14
  # Raised when the single-table inheritance mechanism fails to locate the subclass
15
15
  # (for example due to improper usage of column that
16
16
  # {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema::ClassMethods#inheritance_column]
@@ -51,10 +51,31 @@ module ActiveRecord
51
51
  class AdapterNotFound < ActiveRecordError
52
52
  end
53
53
 
54
+ # Superclass for all errors raised from an Active Record adapter.
55
+ class AdapterError < ActiveRecordError
56
+ def initialize(message = nil, connection_pool: nil)
57
+ @connection_pool = connection_pool
58
+ super(message)
59
+ end
60
+
61
+ attr_reader :connection_pool
62
+ end
63
+
54
64
  # Raised when connection to the database could not been established (for example when
55
- # {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
65
+ # {ActiveRecord::Base.lease_connection=}[rdoc-ref:ConnectionHandling#lease_connection]
56
66
  # is given a +nil+ object).
57
- class ConnectionNotEstablished < ActiveRecordError
67
+ class ConnectionNotEstablished < AdapterError
68
+ def initialize(message = nil, connection_pool: nil)
69
+ super(message, connection_pool: connection_pool)
70
+ end
71
+
72
+ def set_pool(connection_pool)
73
+ unless @connection_pool
74
+ @connection_pool = connection_pool
75
+ end
76
+
77
+ self
78
+ end
58
79
  end
59
80
 
60
81
  # Raised when a connection could not be obtained within the connection
@@ -63,10 +84,34 @@ module ActiveRecord
63
84
  class ConnectionTimeoutError < ConnectionNotEstablished
64
85
  end
65
86
 
87
+ # Raised when connection to the database could not been established because it was not
88
+ # able to connect to the host or when the authorization failed.
89
+ class DatabaseConnectionError < ConnectionNotEstablished
90
+ def initialize(message = nil)
91
+ super(message || "Database connection error")
92
+ end
93
+
94
+ class << self
95
+ def hostname_error(hostname)
96
+ DatabaseConnectionError.new(<<~MSG)
97
+ There is an issue connecting with your hostname: #{hostname}.\n
98
+ Please check your database configuration and ensure there is a valid connection to your database.
99
+ MSG
100
+ end
101
+
102
+ def username_error(username)
103
+ DatabaseConnectionError.new(<<~MSG)
104
+ There is an issue connecting to your database with your username/password, username: #{username}.\n
105
+ Please check your database configuration to ensure the username/password are valid.
106
+ MSG
107
+ end
108
+ end
109
+ end
110
+
66
111
  # Raised when a pool was unable to get ahold of all its connections
67
112
  # to perform a "group" action such as
68
113
  # {ActiveRecord::Base.connection_pool.disconnect!}[rdoc-ref:ConnectionAdapters::ConnectionPool#disconnect!]
69
- # or {ActiveRecord::Base.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
114
+ # or {ActiveRecord::Base.connection_handler.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
70
115
  class ExclusiveConnectionTimeoutError < ConnectionTimeoutError
71
116
  end
72
117
 
@@ -88,8 +133,18 @@ module ActiveRecord
88
133
  end
89
134
 
90
135
  # Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
91
- # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
92
- # methods when a record is invalid and cannot be saved.
136
+ # {ActiveRecord::Base.update_attribute!}[rdoc-ref:Persistence#update_attribute!]
137
+ # methods when a record failed to validate or cannot be saved due to any of the
138
+ # <tt>before_*</tt> callbacks throwing +:abort+. See
139
+ # ActiveRecord::Callbacks for further details.
140
+ #
141
+ # class Product < ActiveRecord::Base
142
+ # before_save do
143
+ # throw :abort if price < 0
144
+ # end
145
+ # end
146
+ #
147
+ # Product.create! # => raises an ActiveRecord::RecordNotSaved
93
148
  class RecordNotSaved < ActiveRecordError
94
149
  attr_reader :record
95
150
 
@@ -100,15 +155,17 @@ module ActiveRecord
100
155
  end
101
156
 
102
157
  # Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
103
- # when a call to {#destroy}[rdoc-ref:Persistence#destroy!]
104
- # would return false.
158
+ # when a record cannot be destroyed due to any of the
159
+ # <tt>before_destroy</tt> callbacks throwing +:abort+. See
160
+ # ActiveRecord::Callbacks for further details.
105
161
  #
106
- # begin
107
- # complex_operation_that_internally_calls_destroy!
108
- # rescue ActiveRecord::RecordNotDestroyed => invalid
109
- # puts invalid.record.errors
162
+ # class User < ActiveRecord::Base
163
+ # before_destroy do
164
+ # throw :abort if still_active?
165
+ # end
110
166
  # end
111
167
  #
168
+ # User.first.destroy! # => raises an ActiveRecord::RecordNotDestroyed
112
169
  class RecordNotDestroyed < ActiveRecordError
113
170
  attr_reader :record
114
171
 
@@ -118,17 +175,36 @@ module ActiveRecord
118
175
  end
119
176
  end
120
177
 
178
+ # Raised when Active Record finds multiple records but only expected one.
179
+ class SoleRecordExceeded < ActiveRecordError
180
+ attr_reader :record
181
+
182
+ def initialize(record = nil)
183
+ @record = record
184
+ super "Wanted only one #{record&.name || "record"}"
185
+ end
186
+ end
187
+
121
188
  # Superclass for all database execution errors.
122
189
  #
123
190
  # Wraps the underlying database error as +cause+.
124
- class StatementInvalid < ActiveRecordError
125
- def initialize(message = nil, sql: nil, binds: nil)
126
- super(message || $!&.message)
191
+ class StatementInvalid < AdapterError
192
+ def initialize(message = nil, sql: nil, binds: nil, connection_pool: nil)
193
+ super(message || $!&.message, connection_pool: connection_pool)
127
194
  @sql = sql
128
195
  @binds = binds
129
196
  end
130
197
 
131
198
  attr_reader :sql, :binds
199
+
200
+ def set_query(sql, binds)
201
+ unless @sql
202
+ @sql = sql
203
+ @binds = binds
204
+ end
205
+
206
+ self
207
+ end
132
208
  end
133
209
 
134
210
  # Defunct wrapper class kept for compatibility.
@@ -155,8 +231,13 @@ module ActiveRecord
155
231
  foreign_key: nil,
156
232
  target_table: nil,
157
233
  primary_key: nil,
158
- primary_key_column: nil
234
+ primary_key_column: nil,
235
+ query_parser: nil,
236
+ connection_pool: nil
159
237
  )
238
+ @original_message = message
239
+ @query_parser = query_parser
240
+
160
241
  if table
161
242
  type = primary_key_column.bigint? ? :bigint : primary_key_column.type
162
243
  msg = <<~EOM.squish
@@ -174,7 +255,24 @@ module ActiveRecord
174
255
  if message
175
256
  msg << "\nOriginal message: #{message}"
176
257
  end
177
- super(msg, sql: sql, binds: binds)
258
+
259
+ super(msg, sql: sql, binds: binds, connection_pool: connection_pool)
260
+ end
261
+
262
+ def set_query(sql, binds)
263
+ if @query_parser && !@sql
264
+ self.class.new(
265
+ message: @original_message,
266
+ sql: sql,
267
+ binds: binds,
268
+ connection_pool: @connection_pool,
269
+ **@query_parser.call(sql)
270
+ ).tap do |exception|
271
+ exception.set_backtrace backtrace
272
+ end
273
+ else
274
+ super
275
+ end
178
276
  end
179
277
  end
180
278
 
@@ -190,6 +288,19 @@ module ActiveRecord
190
288
  class RangeError < StatementInvalid
191
289
  end
192
290
 
291
+ # Raised when a statement produces an SQL warning.
292
+ class SQLWarning < AdapterError
293
+ attr_reader :code, :level
294
+ attr_accessor :sql
295
+
296
+ def initialize(message = nil, code = nil, level = nil, sql = nil, connection_pool = nil)
297
+ super(message, connection_pool: connection_pool)
298
+ @code = code
299
+ @level = level
300
+ @sql = sql
301
+ end
302
+ end
303
+
193
304
  # Raised when the number of placeholders in an SQL fragment passed to
194
305
  # {ActiveRecord::Base.where}[rdoc-ref:QueryMethods#where]
195
306
  # does not match the number of values supplied.
@@ -202,6 +313,31 @@ module ActiveRecord
202
313
 
203
314
  # Raised when a given database does not exist.
204
315
  class NoDatabaseError < StatementInvalid
316
+ include ActiveSupport::ActionableError
317
+
318
+ action "Create database" do
319
+ ActiveRecord::Tasks::DatabaseTasks.create_current
320
+ end
321
+
322
+ def initialize(message = nil, connection_pool: nil)
323
+ super(message || "Database not found", connection_pool: connection_pool)
324
+ end
325
+
326
+ class << self
327
+ def db_error(db_name)
328
+ NoDatabaseError.new(<<~MSG)
329
+ We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml.
330
+
331
+ To resolve this error:
332
+
333
+ - Did you not create the database, or did you delete it? To create the database, run:
334
+
335
+ bin/rails db:create
336
+
337
+ - Has the database name changed? Verify that config/database.yml contains the correct database name.
338
+ MSG
339
+ end
340
+ end
205
341
  end
206
342
 
207
343
  # Raised when creating a database if it exists.
@@ -246,6 +382,12 @@ module ActiveRecord
246
382
  end
247
383
 
248
384
  # Raised on attempt to lazily load records that are marked as strict loading.
385
+ #
386
+ # You can resolve this error by eager loading marked records before accessing
387
+ # them. The
388
+ # {Eager Loading Associations}[https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations]
389
+ # guide covers solutions, such as using
390
+ # {ActiveRecord::Base.includes}[rdoc-ref:QueryMethods#includes].
249
391
  class StrictLoadingViolationError < ActiveRecordError
250
392
  end
251
393
 
@@ -268,7 +410,7 @@ module ActiveRecord
268
410
  # # The system must fail on Friday so that our support department
269
411
  # # won't be out of job. We silently rollback this transaction
270
412
  # # without telling the user.
271
- # raise ActiveRecord::Rollback, "Call tech support!"
413
+ # raise ActiveRecord::Rollback
272
414
  # end
273
415
  # end
274
416
  # # ActiveRecord::Rollback is the only exception that won't be passed on
@@ -338,10 +480,15 @@ module ActiveRecord
338
480
  # relation.loaded? # => true
339
481
  #
340
482
  # # Methods which try to mutate a loaded relation fail.
341
- # relation.where!(title: 'TODO') # => ActiveRecord::ImmutableRelation
342
- # relation.limit!(5) # => ActiveRecord::ImmutableRelation
343
- class ImmutableRelation < ActiveRecordError
483
+ # relation.where!(title: 'TODO') # => ActiveRecord::UnmodifiableRelation
484
+ # relation.limit!(5) # => ActiveRecord::UnmodifiableRelation
485
+ class UnmodifiableRelation < ActiveRecordError
344
486
  end
487
+ deprecate_constant(
488
+ :ImmutableRelation,
489
+ "ActiveRecord::UnmodifiableRelation",
490
+ deprecator: ActiveRecord.deprecator
491
+ )
345
492
 
346
493
  # TransactionIsolationError will be raised under the following conditions:
347
494
  #
@@ -349,13 +496,26 @@ module ActiveRecord
349
496
  # * You are joining an existing open transaction
350
497
  # * You are creating a nested (savepoint) transaction
351
498
  #
352
- # The mysql2 and postgresql adapters support setting the transaction isolation level.
499
+ # The mysql2, trilogy, and postgresql adapters support setting the transaction isolation level.
353
500
  class TransactionIsolationError < ActiveRecordError
354
501
  end
355
502
 
356
503
  # TransactionRollbackError will be raised when a transaction is rolled
357
504
  # back by the database due to a serialization failure or a deadlock.
358
505
  #
506
+ # These exceptions should not be generally rescued in nested transaction
507
+ # blocks, because they have side-effects in the actual enclosing transaction
508
+ # and internal Active Record state. They can be rescued if you are above the
509
+ # root transaction block, though.
510
+ #
511
+ # In that case, beware of transactional tests, however, because they run test
512
+ # cases in their own umbrella transaction. If you absolutely need to handle
513
+ # these exceptions in tests please consider disabling transactional tests in
514
+ # the affected test class (<tt>self.use_transactional_tests = false</tt>).
515
+ #
516
+ # Due to the aforementioned side-effects, this exception should not be raised
517
+ # manually by users.
518
+ #
359
519
  # See the following:
360
520
  #
361
521
  # * https://www.postgresql.org/docs/current/static/transaction-iso.html
@@ -363,13 +523,24 @@ module ActiveRecord
363
523
  class TransactionRollbackError < StatementInvalid
364
524
  end
365
525
 
526
+ # AsynchronousQueryInsideTransactionError will be raised when attempting
527
+ # to perform an asynchronous query from inside a transaction
528
+ class AsynchronousQueryInsideTransactionError < ActiveRecordError
529
+ end
530
+
366
531
  # SerializationFailure will be raised when a transaction is rolled
367
532
  # back by the database due to a serialization failure.
533
+ #
534
+ # This is a subclass of TransactionRollbackError, please make sure to check
535
+ # its documentation to be aware of its caveats.
368
536
  class SerializationFailure < TransactionRollbackError
369
537
  end
370
538
 
371
539
  # Deadlocked will be raised when a transaction is rolled
372
540
  # back by the database when a deadlock is encountered.
541
+ #
542
+ # This is a subclass of TransactionRollbackError, please make sure to check
543
+ # its documentation to be aware of its caveats.
373
544
  class Deadlocked < TransactionRollbackError
374
545
  end
375
546
 
@@ -398,6 +569,11 @@ module ActiveRecord
398
569
  class AdapterTimeout < QueryAborted
399
570
  end
400
571
 
572
+ # ConnectionFailed will be raised when the network connection to the
573
+ # database fails while sending a query or waiting for its result.
574
+ class ConnectionFailed < QueryAborted
575
+ end
576
+
401
577
  # UnknownAttributeReference is raised when an unknown and potentially unsafe
402
578
  # value is passed to a query method. For example, passing a non column name
403
579
  # value to a relation's #order method might cause this exception.
@@ -409,15 +585,22 @@ module ActiveRecord
409
585
  #
410
586
  # For example, the following code would raise this exception:
411
587
  #
412
- # Post.order("length(title)").first
588
+ # Post.order("REPLACE(title, 'misc', 'zzzz') asc").pluck(:id)
413
589
  #
414
590
  # The desired result can be accomplished by wrapping the known-safe string
415
591
  # in Arel.sql:
416
592
  #
417
- # Post.order(Arel.sql("length(title)")).first
593
+ # Post.order(Arel.sql("REPLACE(title, 'misc', 'zzzz') asc")).pluck(:id)
418
594
  #
419
595
  # Again, such a workaround should *not* be used when passing user-provided
420
596
  # values, such as request parameters or model attributes to query methods.
421
597
  class UnknownAttributeReference < ActiveRecordError
422
598
  end
599
+
600
+ # DatabaseVersionError will be raised when the database version is not supported, or when
601
+ # the database version cannot be determined.
602
+ class DatabaseVersionError < ActiveRecordError
603
+ end
423
604
  end
605
+
606
+ require "active_record/associations/errors"
@@ -16,17 +16,18 @@ 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:
20
- str = queries.map do |sql, binds|
21
- msg = +"EXPLAIN for: #{sql}"
22
- unless binds.empty?
23
- msg << " "
24
- msg << binds.map { |attr| render_bind(attr) }.inspect
25
- end
26
- msg << "\n"
27
- msg << connection.explain(sql, binds)
28
- end.join("\n")
29
-
19
+ def exec_explain(queries, options = []) # :nodoc:
20
+ str = with_connection do |c|
21
+ queries.map do |sql, binds|
22
+ msg = +"#{build_explain_clause(c, options)} #{sql}"
23
+ unless binds.empty?
24
+ msg << " "
25
+ msg << binds.map { |attr| render_bind(c, attr) }.inspect
26
+ end
27
+ msg << "\n"
28
+ msg << c.explain(sql, binds, options)
29
+ end.join("\n")
30
+ end
30
31
  # Overriding inspect to be more human readable, especially in the console.
31
32
  def str.inspect
32
33
  self
@@ -36,7 +37,7 @@ module ActiveRecord
36
37
  end
37
38
 
38
39
  private
39
- def render_bind(attr)
40
+ def render_bind(connection, attr)
40
41
  if ActiveModel::Attribute === attr
41
42
  value = if attr.type.binary? && attr.value
42
43
  "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
@@ -50,5 +51,13 @@ module ActiveRecord
50
51
 
51
52
  [attr&.name, value]
52
53
  end
54
+
55
+ def build_explain_clause(connection, options = [])
56
+ if connection.respond_to?(:build_explain_clause, true)
57
+ connection.build_explain_clause(options)
58
+ else
59
+ "EXPLAIN for:"
60
+ end
61
+ end
53
62
  end
54
63
  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