activerecord 6.0.0 → 7.2.3

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 (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  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 +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -1,41 +1,67 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/enumerable"
4
+
3
5
  module ActiveRecord
4
6
  class InsertAll # :nodoc:
5
7
  attr_reader :model, :connection, :inserts, :keys
6
- attr_reader :on_duplicate, :returning, :unique_by
8
+ attr_reader :on_duplicate, :update_only, :returning, :unique_by, :update_sql
7
9
 
8
- def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
9
- raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
10
+ class << self
11
+ def execute(relation, ...)
12
+ relation.model.with_connection do |c|
13
+ new(relation, c, ...).execute
14
+ end
15
+ end
16
+ end
10
17
 
11
- @model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s).to_set
12
- @on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
18
+ def initialize(relation, connection, inserts, on_duplicate:, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
19
+ @relation = relation
20
+ @model, @connection, @inserts = relation.model, connection, inserts.map(&:stringify_keys)
21
+ @on_duplicate, @update_only, @returning, @unique_by = on_duplicate, update_only, returning, unique_by
22
+ @record_timestamps = record_timestamps.nil? ? model.record_timestamps : record_timestamps
23
+
24
+ disallow_raw_sql!(on_duplicate)
25
+ disallow_raw_sql!(returning)
26
+
27
+ if @inserts.empty?
28
+ @keys = []
29
+ else
30
+ resolve_sti
31
+ resolve_attribute_aliases
32
+ @keys = @inserts.first.keys
33
+ end
34
+
35
+ @scope_attributes = relation.scope_for_create.except(@model.inheritance_column)
36
+ @keys |= @scope_attributes.keys
37
+ @keys = @keys.to_set
13
38
 
14
39
  @returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
15
40
  @returning = false if @returning == []
16
41
 
17
- @unique_by = find_unique_index_for(unique_by) if unique_by
18
- @on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
42
+ @unique_by = find_unique_index_for(@unique_by)
19
43
 
44
+ configure_on_duplicate_update_logic
20
45
  ensure_valid_options_for_connection!
21
46
  end
22
47
 
23
48
  def execute
49
+ return ActiveRecord::Result.empty if inserts.empty?
50
+
24
51
  message = +"#{model} "
25
52
  message << "Bulk " if inserts.many?
26
53
  message << (on_duplicate == :update ? "Upsert" : "Insert")
27
- connection.exec_query to_sql, message
54
+ connection.exec_insert_all to_sql, message
28
55
  end
29
56
 
30
57
  def updatable_columns
31
- keys - readonly_columns - unique_by_columns
58
+ @updatable_columns ||= keys - readonly_columns - unique_by_columns
32
59
  end
33
60
 
34
61
  def primary_keys
35
- Array(model.primary_key)
62
+ Array(@model.schema_cache.primary_keys(model.table_name))
36
63
  end
37
64
 
38
-
39
65
  def skip_duplicates?
40
66
  on_duplicate == :skip
41
67
  end
@@ -47,30 +73,103 @@ module ActiveRecord
47
73
  def map_key_with_value
48
74
  inserts.map do |attributes|
49
75
  attributes = attributes.stringify_keys
76
+ attributes.merge!(@scope_attributes)
77
+ attributes.reverse_merge!(timestamps_for_create) if record_timestamps?
78
+
50
79
  verify_attributes(attributes)
51
80
 
52
- keys.map do |key|
81
+ keys_including_timestamps.map do |key|
53
82
  yield key, attributes[key]
54
83
  end
55
84
  end
56
85
  end
57
86
 
87
+ def record_timestamps?
88
+ @record_timestamps
89
+ end
90
+
91
+ # TODO: Consider renaming this method, as it only conditionally extends keys, not always
92
+ def keys_including_timestamps
93
+ @keys_including_timestamps ||= if record_timestamps?
94
+ keys + model.all_timestamp_attributes_in_model
95
+ else
96
+ keys
97
+ end
98
+ end
99
+
58
100
  private
101
+ def has_attribute_aliases?(attributes)
102
+ attributes.keys.any? { |attribute| model.attribute_alias?(attribute) }
103
+ end
104
+
105
+ def resolve_sti
106
+ return if model.descends_from_active_record?
107
+
108
+ sti_type = model.sti_name
109
+ @inserts = @inserts.map do |insert|
110
+ insert.reverse_merge(model.inheritance_column.to_s => sti_type)
111
+ end
112
+ end
113
+
114
+ def resolve_attribute_aliases
115
+ return unless has_attribute_aliases?(@inserts.first)
116
+
117
+ @inserts = @inserts.map do |insert|
118
+ insert.transform_keys { |attribute| resolve_attribute_alias(attribute) }
119
+ end
120
+
121
+ @update_only = Array(@update_only).map { |attribute| resolve_attribute_alias(attribute) } if @update_only
122
+ @unique_by = Array(@unique_by).map { |attribute| resolve_attribute_alias(attribute) } if @unique_by
123
+ end
124
+
125
+ def resolve_attribute_alias(attribute)
126
+ model.attribute_alias(attribute) || attribute
127
+ end
128
+
129
+ def configure_on_duplicate_update_logic
130
+ if custom_update_sql_provided? && update_only.present?
131
+ raise ArgumentError, "You can't set :update_only and provide custom update SQL via :on_duplicate at the same time"
132
+ end
133
+
134
+ if update_only.present?
135
+ @updatable_columns = Array(update_only)
136
+ @on_duplicate = :update
137
+ elsif custom_update_sql_provided?
138
+ @update_sql = on_duplicate
139
+ @on_duplicate = :update
140
+ elsif @on_duplicate == :update && updatable_columns.empty?
141
+ @on_duplicate = :skip
142
+ end
143
+ end
144
+
145
+ def custom_update_sql_provided?
146
+ @custom_update_sql_provided ||= Arel.arel_node?(on_duplicate)
147
+ end
148
+
59
149
  def find_unique_index_for(unique_by)
60
- match = Array(unique_by).map(&:to_s)
150
+ if !connection.supports_insert_conflict_target?
151
+ return if unique_by.nil?
152
+
153
+ raise ArgumentError, "#{connection.class} does not support :unique_by"
154
+ end
61
155
 
62
- if index = unique_indexes.find { |i| match.include?(i.name) || i.columns == match }
156
+ name_or_columns = unique_by || model.primary_key
157
+ match = Array(name_or_columns).map(&:to_s)
158
+ sorted_match = match.sort
159
+
160
+ if index = unique_indexes.find { |i| match.include?(i.name) || Array(i.columns).sort == sorted_match }
63
161
  index
162
+ elsif match == primary_keys
163
+ unique_by.nil? ? nil : ActiveRecord::ConnectionAdapters::IndexDefinition.new(model.table_name, "#{model.table_name}_primary_key", true, match)
64
164
  else
65
- raise ArgumentError, "No unique index found for #{unique_by}"
165
+ raise ArgumentError, "No unique index found for #{name_or_columns}"
66
166
  end
67
167
  end
68
168
 
69
169
  def unique_indexes
70
- connection.schema_cache.indexes(model.table_name).select(&:unique)
170
+ @model.schema_cache.indexes(model.table_name).select(&:unique)
71
171
  end
72
172
 
73
-
74
173
  def ensure_valid_options_for_connection!
75
174
  if returning && !connection.supports_insert_returning?
76
175
  raise ArgumentError, "#{connection.class} does not support :returning"
@@ -96,7 +195,7 @@ module ActiveRecord
96
195
 
97
196
 
98
197
  def readonly_columns
99
- primary_keys + model.readonly_attributes.to_a
198
+ primary_keys + model.readonly_attributes
100
199
  end
101
200
 
102
201
  def unique_by_columns
@@ -105,36 +204,62 @@ module ActiveRecord
105
204
 
106
205
 
107
206
  def verify_attributes(attributes)
108
- if keys != attributes.keys.to_set
207
+ if keys_including_timestamps != attributes.keys.to_set
109
208
  raise ArgumentError, "All objects being inserted must have the same keys"
110
209
  end
111
210
  end
112
211
 
212
+ def disallow_raw_sql!(value)
213
+ return if !value.is_a?(String) || Arel.arel_node?(value)
214
+
215
+ raise ArgumentError, "Dangerous query method (method whose arguments are used as raw " \
216
+ "SQL) called: #{value}. " \
217
+ "Known-safe values can be passed " \
218
+ "by wrapping them in Arel.sql()."
219
+ end
220
+
221
+ def timestamps_for_create
222
+ model.all_timestamp_attributes_in_model.index_with(connection.high_precision_current_timestamp)
223
+ end
224
+
113
225
  class Builder # :nodoc:
114
226
  attr_reader :model
115
227
 
116
- delegate :skip_duplicates?, :update_duplicates?, :keys, to: :insert_all
228
+ delegate :skip_duplicates?, :update_duplicates?, :keys, :keys_including_timestamps, :record_timestamps?, to: :insert_all
117
229
 
118
230
  def initialize(insert_all)
119
231
  @insert_all, @model, @connection = insert_all, insert_all.model, insert_all.connection
120
232
  end
121
233
 
122
234
  def into
123
- "INTO #{model.quoted_table_name}(#{columns_list})"
235
+ "INTO #{model.quoted_table_name} (#{columns_list})"
124
236
  end
125
237
 
126
238
  def values_list
127
- types = extract_types_from_columns_on(model.table_name, keys: keys)
239
+ types = extract_types_from_columns_on(model.table_name, keys: keys_including_timestamps)
128
240
 
129
241
  values_list = insert_all.map_key_with_value do |key, value|
242
+ next value if Arel::Nodes::SqlLiteral === value
130
243
  connection.with_yaml_fallback(types[key].serialize(value))
131
244
  end
132
245
 
133
- Arel::InsertManager.new.create_values_list(values_list).to_sql
246
+ connection.visitor.compile(Arel::Nodes::ValuesList.new(values_list))
134
247
  end
135
248
 
136
249
  def returning
137
- format_columns(insert_all.returning) if insert_all.returning
250
+ return unless insert_all.returning
251
+
252
+ if insert_all.returning.is_a?(String)
253
+ insert_all.returning
254
+ else
255
+ Array(insert_all.returning).map do |attribute|
256
+ if model.attribute_alias?(attribute)
257
+ "#{quote_column(model.attribute_alias(attribute))} AS #{quote_column(attribute)}"
258
+ else
259
+ quote_column(attribute)
260
+ end
261
+ end.join(",")
262
+ end
138
263
  end
139
264
 
140
265
  def conflict_target
@@ -151,28 +276,52 @@ module ActiveRecord
151
276
  quote_columns(insert_all.updatable_columns)
152
277
  end
153
278
 
279
+ def touch_model_timestamps_unless(&block)
280
+ return "" unless update_duplicates? && record_timestamps?
281
+
282
+ model.timestamp_attributes_for_update_in_model.filter_map do |column_name|
283
+ if touch_timestamp_attribute?(column_name)
284
+ "#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE #{connection.high_precision_current_timestamp} END),"
285
+ end
286
+ end.join
287
+ end
288
+
289
+ def raw_update_sql
290
+ insert_all.update_sql
291
+ end
292
+
293
+ alias raw_update_sql? raw_update_sql
294
+
154
295
  private
155
296
  attr_reader :connection, :insert_all
156
297
 
298
+ def touch_timestamp_attribute?(column_name)
299
+ insert_all.updatable_columns.exclude?(column_name)
300
+ end
301
+
157
302
  def columns_list
158
- format_columns(insert_all.keys)
303
+ format_columns(insert_all.keys_including_timestamps)
159
304
  end
160
305
 
161
306
  def extract_types_from_columns_on(table_name, keys:)
162
- columns = connection.schema_cache.columns_hash(table_name)
307
+ columns = @model.schema_cache.columns_hash(table_name)
163
308
 
164
309
  unknown_column = (keys - columns.keys).first
165
310
  raise UnknownAttributeError.new(model.new, unknown_column) if unknown_column
166
311
 
167
- keys.map { |key| [ key, connection.lookup_cast_type_from_column(columns[key]) ] }.to_h
312
+ keys.index_with { |key| model.type_for_attribute(key) }
168
313
  end
169
314
 
170
315
  def format_columns(columns)
171
- quote_columns(columns).join(",")
316
+ columns.respond_to?(:map) ? quote_columns(columns).join(",") : columns
172
317
  end
173
318
 
174
319
  def quote_columns(columns)
175
- columns.map(&connection.method(:quote_column_name))
320
+ columns.map { |column| quote_column(column) }
321
+ end
322
+
323
+ def quote_column(column)
324
+ connection.quote_column_name(column)
176
325
  end
177
326
  end
178
327
  end
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  ##
11
11
  # :singleton-method:
12
12
  # Indicates the format used to generate the timestamp in the cache key, if
13
- # versioning is off. Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
13
+ # versioning is off. Accepts any of the symbols in +Time::DATE_FORMATS+.
14
14
  #
15
15
  # This is +:usec+, by default.
16
16
  class_attribute :cache_timestamp_format, instance_writer: false, default: :usec
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  # Indicates whether to use a stable #cache_key method that is accompanied
21
21
  # by a changing version in the #cache_version method.
22
22
  #
23
- # This is +true+, by default on Rails 5.2 and above.
23
+ # This is +true+, by default on \Rails 5.2 and above.
24
24
  class_attribute :cache_versioning, instance_writer: false, default: false
25
25
 
26
26
  ##
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  # Indicates whether to use a stable #cache_key method that is accompanied
29
29
  # by a changing version in the #cache_version method on collections.
30
30
  #
31
- # This is +false+, by default until Rails 6.1.
31
+ # This is +false+, by default until \Rails 6.1.
32
32
  class_attribute :collection_cache_versioning, instance_writer: false, default: false
33
33
  end
34
34
 
@@ -55,8 +55,8 @@ module ActiveRecord
55
55
  # user = User.find_by(name: 'Phusion')
56
56
  # user_path(user) # => "/users/Phusion"
57
57
  def to_param
58
- # We can't use alias_method here, because method 'id' optimizes itself on the fly.
59
- id && id.to_s # Be sure to stringify the id for routes
58
+ return unless id
59
+ Array(id).join(self.class.param_delimiter)
60
60
  end
61
61
 
62
62
  # Returns a stable cache key that can be used to identify this record.
@@ -64,7 +64,7 @@ module ActiveRecord
64
64
  # Product.new.cache_key # => "products/new"
65
65
  # Product.find(5).cache_key # => "products/5"
66
66
  #
67
- # If ActiveRecord::Base.cache_versioning is turned off, as it was in Rails 5.1 and earlier,
67
+ # If ActiveRecord::Base.cache_versioning is turned off, as it was in \Rails 5.1 and earlier,
68
68
  # the cache key will also include a version.
69
69
  #
70
70
  # Product.cache_versioning = false
@@ -79,7 +79,7 @@ module ActiveRecord
79
79
  timestamp = max_updated_column_timestamp
80
80
 
81
81
  if timestamp
82
- timestamp = timestamp.utc.to_s(cache_timestamp_format)
82
+ timestamp = timestamp.utc.to_fs(cache_timestamp_format)
83
83
  "#{model_name.cache_key}/#{id}-#{timestamp}"
84
84
  else
85
85
  "#{model_name.cache_key}/#{id}"
@@ -93,7 +93,7 @@ module ActiveRecord
93
93
  # cache_version, but this method can be overwritten to return something else.
94
94
  #
95
95
  # Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
96
- # +false+ (which it is by default until Rails 6.0).
96
+ # +false+.
97
97
  def cache_version
98
98
  return unless cache_versioning
99
99
 
@@ -101,13 +101,12 @@ module ActiveRecord
101
101
  timestamp = updated_at_before_type_cast
102
102
  if can_use_fast_cache_version?(timestamp)
103
103
  raw_timestamp_to_cache_version(timestamp)
104
+
104
105
  elsif timestamp = updated_at
105
- timestamp.utc.to_s(cache_timestamp_format)
106
- end
107
- else
108
- if self.class.has_attribute?("updated_at")
109
- raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
106
+ timestamp.utc.to_fs(cache_timestamp_format)
110
107
  end
108
+ elsif self.class.has_attribute?("updated_at")
109
+ raise ActiveModel::MissingAttributeError, "missing attribute 'updated_at' for #{self.class}"
111
110
  end
112
111
  end
113
112
 
@@ -179,7 +178,10 @@ module ActiveRecord
179
178
  def can_use_fast_cache_version?(timestamp)
180
179
  timestamp.is_a?(String) &&
181
180
  cache_timestamp_format == :usec &&
182
- default_timezone == :utc &&
181
+ # FIXME: checking out a connection for this is wasteful
182
+ # we should store/cache this information in the schema cache
183
+ # or similar.
184
+ self.class.with_connection(&:default_timezone) == :utc &&
183
185
  !updated_at_came_from_user?
184
186
  end
185
187
 
@@ -6,48 +6,159 @@ require "active_record/scoping/named"
6
6
  module ActiveRecord
7
7
  # This class is used to create a table that keeps track of values and keys such
8
8
  # as which environment migrations were run in.
9
- class InternalMetadata < ActiveRecord::Base # :nodoc:
10
- class << self
11
- def _internal?
12
- true
13
- end
9
+ #
10
+ # This is enabled by default. To disable this functionality set
11
+ # `use_metadata_table` to false in your database configuration.
12
+ class InternalMetadata # :nodoc:
13
+ class NullInternalMetadata # :nodoc:
14
+ end
15
+
16
+ attr_reader :arel_table
17
+
18
+ def initialize(pool)
19
+ @pool = pool
20
+ @arel_table = Arel::Table.new(table_name)
21
+ end
22
+
23
+ def primary_key
24
+ "key"
25
+ end
26
+
27
+ def value_key
28
+ "value"
29
+ end
14
30
 
15
- def primary_key
16
- "key"
31
+ def table_name
32
+ "#{ActiveRecord::Base.table_name_prefix}#{ActiveRecord::Base.internal_metadata_table_name}#{ActiveRecord::Base.table_name_suffix}"
33
+ end
34
+
35
+ def enabled?
36
+ @pool.db_config.use_metadata_table?
37
+ end
38
+
39
+ def []=(key, value)
40
+ return unless enabled?
41
+
42
+ @pool.with_connection do |connection|
43
+ update_or_create_entry(connection, key, value)
17
44
  end
45
+ end
18
46
 
19
- def table_name
20
- "#{table_name_prefix}#{internal_metadata_table_name}#{table_name_suffix}"
47
+ def [](key)
48
+ return unless enabled?
49
+
50
+ @pool.with_connection do |connection|
51
+ if entry = select_entry(connection, key)
52
+ entry[value_key]
53
+ end
21
54
  end
55
+ end
22
56
 
23
- def []=(key, value)
24
- find_or_initialize_by(key: key).update!(value: value)
57
+ def delete_all_entries
58
+ dm = Arel::DeleteManager.new(arel_table)
59
+
60
+ @pool.with_connection do |connection|
61
+ connection.delete(dm, "#{self.class} Destroy")
25
62
  end
63
+ end
64
+
65
+ def count
66
+ sm = Arel::SelectManager.new(arel_table)
67
+ sm.project(*Arel::Nodes::Count.new([Arel.star]))
26
68
 
27
- def [](key)
28
- where(key: key).pluck(:value).first
69
+ @pool.with_connection do |connection|
70
+ connection.select_values(sm, "#{self.class} Count").first
29
71
  end
72
+ end
73
+
74
+ def create_table_and_set_flags(environment, schema_sha1 = nil)
75
+ return unless enabled?
30
76
 
31
- def table_exists?
32
- connection.table_exists?(table_name)
77
+ @pool.with_connection do |connection|
78
+ create_table
79
+ update_or_create_entry(connection, :environment, environment)
80
+ update_or_create_entry(connection, :schema_sha1, schema_sha1) if schema_sha1
33
81
  end
82
+ end
34
83
 
35
- # Creates an internal metadata table with columns +key+ and +value+
36
- def create_table
37
- unless table_exists?
38
- key_options = connection.internal_string_options_for_primary_key
84
+ # Creates an internal metadata table with columns +key+ and +value+
85
+ def create_table
86
+ return unless enabled?
39
87
 
88
+ @pool.with_connection do |connection|
89
+ unless connection.table_exists?(table_name)
40
90
  connection.create_table(table_name, id: false) do |t|
41
- t.string :key, key_options
91
+ t.string :key, **connection.internal_string_options_for_primary_key
42
92
  t.string :value
43
93
  t.timestamps
44
94
  end
45
95
  end
46
96
  end
97
+ end
98
+
99
+ def drop_table
100
+ return unless enabled?
47
101
 
48
- def drop_table
102
+ @pool.with_connection do |connection|
49
103
  connection.drop_table table_name, if_exists: true
50
104
  end
51
105
  end
106
+
107
+ def table_exists?
108
+ @pool.schema_cache.data_source_exists?(table_name)
109
+ end
110
+
111
+ private
112
+ def update_or_create_entry(connection, key, value)
113
+ entry = select_entry(connection, key)
114
+
115
+ if entry
116
+ if entry[value_key] != value
117
+ update_entry(connection, key, value)
118
+ else
119
+ entry[value_key]
120
+ end
121
+ else
122
+ create_entry(connection, key, value)
123
+ end
124
+ end
125
+
126
+ def current_time(connection)
127
+ connection.default_timezone == :utc ? Time.now.utc : Time.now
128
+ end
129
+
130
+ def create_entry(connection, key, value)
131
+ im = Arel::InsertManager.new(arel_table)
132
+ im.insert [
133
+ [arel_table[primary_key], key],
134
+ [arel_table[value_key], value],
135
+ [arel_table[:created_at], current_time(connection)],
136
+ [arel_table[:updated_at], current_time(connection)]
137
+ ]
138
+
139
+ connection.insert(im, "#{self.class} Create", primary_key, key)
140
+ end
141
+
142
+ def update_entry(connection, key, new_value)
143
+ um = Arel::UpdateManager.new(arel_table)
144
+ um.set [
145
+ [arel_table[value_key], new_value],
146
+ [arel_table[:updated_at], current_time(connection)]
147
+ ]
148
+
149
+ um.where(arel_table[primary_key].eq(key))
150
+
151
+ connection.update(um, "#{self.class} Update")
152
+ end
153
+
154
+ def select_entry(connection, key)
155
+ sm = Arel::SelectManager.new(arel_table)
156
+ sm.project(Arel::Nodes::SqlLiteral.new("*", retryable: true))
157
+ sm.where(arel_table[primary_key].eq(Arel::Nodes::BindParam.new(key)))
158
+ sm.order(arel_table[primary_key].asc)
159
+ sm.limit = 1
160
+
161
+ connection.select_all(sm, "#{self.class} Load").first
162
+ end
52
163
  end
53
164
  end
@@ -1,47 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- module LegacyYamlAdapter
5
- def self.convert(klass, coder)
4
+ module LegacyYamlAdapter # :nodoc:
5
+ def self.convert(coder)
6
6
  return coder unless coder.is_a?(Psych::Coder)
7
7
 
8
8
  case coder["active_record_yaml_version"]
9
9
  when 1, 2 then coder
10
10
  else
11
- if coder["attributes"].is_a?(ActiveModel::AttributeSet)
12
- Rails420.convert(klass, coder)
13
- else
14
- Rails41.convert(klass, coder)
15
- end
16
- end
17
- end
18
-
19
- module Rails420
20
- def self.convert(klass, coder)
21
- attribute_set = coder["attributes"]
22
-
23
- klass.attribute_names.each do |attr_name|
24
- attribute = attribute_set[attr_name]
25
- if attribute.type.is_a?(Delegator)
26
- type_from_klass = klass.type_for_attribute(attr_name)
27
- attribute_set[attr_name] = attribute.with_type(type_from_klass)
28
- end
29
- end
30
-
31
- coder
32
- end
33
- end
34
-
35
- module Rails41
36
- def self.convert(klass, coder)
37
- attributes = klass.attributes_builder
38
- .build_from_database(coder["attributes"])
39
- new_record = coder["attributes"][klass.primary_key].blank?
40
-
41
- {
42
- "attributes" => attributes,
43
- "new_record" => new_record,
44
- }
11
+ raise("Active Record doesn't know how to load YAML with this format.")
45
12
  end
46
13
  end
47
14
  end