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,22 +1,267 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/file/atomic"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
7
+ class SchemaReflection
8
+ class << self
9
+ attr_accessor :use_schema_cache_dump
10
+ attr_accessor :check_schema_cache_dump_version
11
+ end
12
+
13
+ self.use_schema_cache_dump = true
14
+ self.check_schema_cache_dump_version = true
15
+
16
+ def initialize(cache_path, cache = nil)
17
+ @cache = cache
18
+ @cache_path = cache_path
19
+ end
20
+
21
+ def clear!
22
+ @cache = empty_cache
23
+
24
+ nil
25
+ end
26
+
27
+ def load!(pool)
28
+ cache(pool)
29
+
30
+ self
31
+ end
32
+
33
+ def primary_keys(pool, table_name)
34
+ cache(pool).primary_keys(pool, table_name)
35
+ end
36
+
37
+ def data_source_exists?(pool, name)
38
+ cache(pool).data_source_exists?(pool, name)
39
+ end
40
+
41
+ def add(pool, name)
42
+ cache(pool).add(pool, name)
43
+ end
44
+
45
+ def data_sources(pool, name)
46
+ cache(pool).data_source_exists?(pool, name)
47
+ end
48
+
49
+ def columns(pool, table_name)
50
+ cache(pool).columns(pool, table_name)
51
+ end
52
+
53
+ def columns_hash(pool, table_name)
54
+ cache(pool).columns_hash(pool, table_name)
55
+ end
56
+
57
+ def columns_hash?(pool, table_name)
58
+ cache(pool).columns_hash?(pool, table_name)
59
+ end
60
+
61
+ def indexes(pool, table_name)
62
+ cache(pool).indexes(pool, table_name)
63
+ end
64
+
65
+ def version(pool)
66
+ cache(pool).version(pool)
67
+ end
68
+
69
+ def size(pool)
70
+ cache(pool).size
71
+ end
72
+
73
+ def clear_data_source_cache!(pool, name)
74
+ return if @cache.nil? && !possible_cache_available?
75
+
76
+ cache(pool).clear_data_source_cache!(pool, name)
77
+ end
78
+
79
+ def cached?(table_name)
80
+ if @cache.nil?
81
+ # If `check_schema_cache_dump_version` is enabled we can't load
82
+ # the schema cache dump without connecting to the database.
83
+ unless self.class.check_schema_cache_dump_version
84
+ @cache = load_cache(nil)
85
+ end
86
+ end
87
+
88
+ @cache&.cached?(table_name)
89
+ end
90
+
91
+ def dump_to(pool, filename)
92
+ fresh_cache = empty_cache
93
+ fresh_cache.add_all(pool)
94
+ fresh_cache.dump_to(filename)
95
+
96
+ @cache = fresh_cache
97
+ end
98
+
99
+ private
100
+ def empty_cache
101
+ new_cache = SchemaCache.allocate
102
+ new_cache.send(:initialize)
103
+ new_cache
104
+ end
105
+
106
+ def cache(pool)
107
+ @cache ||= load_cache(pool) || empty_cache
108
+ end
109
+
110
+ def possible_cache_available?
111
+ self.class.use_schema_cache_dump &&
112
+ @cache_path &&
113
+ File.file?(@cache_path)
114
+ end
115
+
116
+ def load_cache(pool)
117
+ # Can't load if schema dumps are disabled
118
+ return unless possible_cache_available?
119
+
120
+ # Check we can find one
121
+ return unless new_cache = SchemaCache._load_from(@cache_path)
122
+
123
+ if self.class.check_schema_cache_dump_version
124
+ begin
125
+ pool.with_connection do |connection|
126
+ current_version = connection.schema_version
127
+
128
+ if new_cache.version(connection) != current_version
129
+ warn "Ignoring #{@cache_path} because it has expired. The current schema version is #{current_version}, but the one in the schema cache file is #{new_cache.schema_version}."
130
+ return
131
+ end
132
+ end
133
+ rescue ActiveRecordError => error
134
+ warn "Failed to validate the schema cache because of #{error.class}: #{error.message}"
135
+ return
136
+ end
137
+ end
138
+
139
+ new_cache
140
+ end
141
+ end
142
+
143
+ class BoundSchemaReflection
144
+ class FakePool # :nodoc
145
+ def initialize(connection)
146
+ @connection = connection
147
+ end
148
+
149
+ def with_connection
150
+ yield @connection
151
+ end
152
+ end
153
+
154
+ class << self
155
+ def for_lone_connection(abstract_schema_reflection, connection) # :nodoc:
156
+ new(abstract_schema_reflection, FakePool.new(connection))
157
+ end
158
+ end
159
+
160
+ def initialize(abstract_schema_reflection, pool)
161
+ @schema_reflection = abstract_schema_reflection
162
+ @pool = pool
163
+ end
164
+
165
+ def clear!
166
+ @schema_reflection.clear!
167
+ end
168
+
169
+ def load!
170
+ @schema_reflection.load!(@pool)
171
+ end
172
+
173
+ def cached?(table_name)
174
+ @schema_reflection.cached?(table_name)
175
+ end
176
+
177
+ def primary_keys(table_name)
178
+ @schema_reflection.primary_keys(@pool, table_name)
179
+ end
180
+
181
+ def data_source_exists?(name)
182
+ @schema_reflection.data_source_exists?(@pool, name)
183
+ end
184
+
185
+ def add(name)
186
+ @schema_reflection.add(@pool, name)
187
+ end
188
+
189
+ def data_sources(name)
190
+ @schema_reflection.data_sources(@pool, name)
191
+ end
192
+
193
+ def columns(table_name)
194
+ @schema_reflection.columns(@pool, table_name)
195
+ end
196
+
197
+ def columns_hash(table_name)
198
+ @schema_reflection.columns_hash(@pool, table_name)
199
+ end
200
+
201
+ def columns_hash?(table_name)
202
+ @schema_reflection.columns_hash?(@pool, table_name)
203
+ end
204
+
205
+ def indexes(table_name)
206
+ @schema_reflection.indexes(@pool, table_name)
207
+ end
208
+
209
+ def version
210
+ @schema_reflection.version(@pool)
211
+ end
212
+
213
+ def size
214
+ @schema_reflection.size(@pool)
215
+ end
216
+
217
+ def clear_data_source_cache!(name)
218
+ @schema_reflection.clear_data_source_cache!(@pool, name)
219
+ end
220
+
221
+ def dump_to(filename)
222
+ @schema_reflection.dump_to(@pool, filename)
223
+ end
224
+ end
225
+
226
+ # = Active Record Connection Adapters Schema Cache
5
227
  class SchemaCache
6
- attr_reader :version
7
- attr_accessor :connection
228
+ def self._load_from(filename) # :nodoc:
229
+ return unless File.file?(filename)
8
230
 
9
- def initialize(conn)
10
- @connection = conn
231
+ read(filename) do |file|
232
+ if filename.include?(".dump")
233
+ Marshal.load(file)
234
+ else
235
+ if YAML.respond_to?(:unsafe_load)
236
+ YAML.unsafe_load(file)
237
+ else
238
+ YAML.load(file)
239
+ end
240
+ end
241
+ end
242
+ end
243
+
244
+ def self.read(filename, &block)
245
+ if File.extname(filename) == ".gz"
246
+ Zlib::GzipReader.open(filename) { |gz|
247
+ yield gz.read
248
+ }
249
+ else
250
+ yield File.read(filename)
251
+ end
252
+ end
253
+ private_class_method :read
11
254
 
255
+ def initialize # :nodoc:
12
256
  @columns = {}
13
257
  @columns_hash = {}
14
258
  @primary_keys = {}
15
259
  @data_sources = {}
16
260
  @indexes = {}
261
+ @version = nil
17
262
  end
18
263
 
19
- def initialize_dup(other)
264
+ def initialize_dup(other) # :nodoc:
20
265
  super
21
266
  @columns = @columns.dup
22
267
  @columns_hash = @columns_hash.dup
@@ -25,87 +270,114 @@ module ActiveRecord
25
270
  @indexes = @indexes.dup
26
271
  end
27
272
 
28
- def encode_with(coder)
29
- coder["columns"] = @columns
30
- coder["columns_hash"] = @columns_hash
31
- coder["primary_keys"] = @primary_keys
32
- coder["data_sources"] = @data_sources
33
- coder["indexes"] = @indexes
34
- coder["version"] = connection.migration_context.current_version
35
- coder["database_version"] = database_version
273
+ def encode_with(coder) # :nodoc:
274
+ coder["columns"] = @columns.sort.to_h
275
+ coder["primary_keys"] = @primary_keys.sort.to_h
276
+ coder["data_sources"] = @data_sources.sort.to_h
277
+ coder["indexes"] = @indexes.sort.to_h
278
+ coder["version"] = @version
36
279
  end
37
280
 
38
- def init_with(coder)
281
+ def init_with(coder) # :nodoc:
39
282
  @columns = coder["columns"]
40
283
  @columns_hash = coder["columns_hash"]
41
284
  @primary_keys = coder["primary_keys"]
42
285
  @data_sources = coder["data_sources"]
43
286
  @indexes = coder["indexes"] || {}
44
287
  @version = coder["version"]
45
- @database_version = coder["database_version"]
288
+
289
+ unless coder["deduplicated"]
290
+ derive_columns_hash_and_deduplicate_values
291
+ end
46
292
  end
47
293
 
48
- def primary_keys(table_name)
49
- @primary_keys[table_name] ||= data_source_exists?(table_name) ? connection.primary_key(table_name) : nil
294
+ def cached?(table_name)
295
+ @columns.key?(table_name)
296
+ end
297
+
298
+ def primary_keys(pool, table_name)
299
+ @primary_keys.fetch(table_name) do
300
+ pool.with_connection do |connection|
301
+ if data_source_exists?(pool, table_name)
302
+ @primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
303
+ end
304
+ end
305
+ end
50
306
  end
51
307
 
52
308
  # A cached lookup for table existence.
53
- def data_source_exists?(name)
54
- prepare_data_sources if @data_sources.empty?
309
+ def data_source_exists?(pool, name)
310
+ return if ignored_table?(name)
311
+
312
+ if @data_sources.empty?
313
+ tables_to_cache(pool).each do |source|
314
+ @data_sources[source] = true
315
+ end
316
+ end
317
+
55
318
  return @data_sources[name] if @data_sources.key? name
56
319
 
57
- @data_sources[name] = connection.data_source_exists?(name)
320
+ @data_sources[deep_deduplicate(name)] = pool.with_connection do |connection|
321
+ connection.data_source_exists?(name)
322
+ end
58
323
  end
59
324
 
60
325
  # Add internal cache for table with +table_name+.
61
- def add(table_name)
62
- if data_source_exists?(table_name)
63
- primary_keys(table_name)
64
- columns(table_name)
65
- columns_hash(table_name)
66
- indexes(table_name)
326
+ def add(pool, table_name)
327
+ pool.with_connection do
328
+ if data_source_exists?(pool, table_name)
329
+ primary_keys(pool, table_name)
330
+ columns(pool, table_name)
331
+ columns_hash(pool, table_name)
332
+ indexes(pool, table_name)
333
+ end
67
334
  end
68
335
  end
69
336
 
70
- def data_sources(name)
71
- @data_sources[name]
72
- end
73
-
74
337
  # Get the columns for a table
75
- def columns(table_name)
76
- @columns[table_name] ||= connection.columns(table_name)
338
+ def columns(pool, table_name)
339
+ if ignored_table?(table_name)
340
+ raise ActiveRecord::StatementInvalid.new("Table '#{table_name}' doesn't exist", connection_pool: pool)
341
+ end
342
+
343
+ @columns.fetch(table_name) do
344
+ pool.with_connection do |connection|
345
+ @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
346
+ end
347
+ end
77
348
  end
78
349
 
79
350
  # Get the columns for a table as a hash, key is the column name
80
351
  # value is the column object.
81
- def columns_hash(table_name)
82
- @columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
83
- [col.name, col]
84
- }]
352
+ def columns_hash(pool, table_name)
353
+ @columns_hash.fetch(table_name) do
354
+ @columns_hash[deep_deduplicate(table_name)] = columns(pool, table_name).index_by(&:name).freeze
355
+ end
85
356
  end
86
357
 
87
358
  # Checks whether the columns hash is already cached for a table.
88
- def columns_hash?(table_name)
359
+ def columns_hash?(_pool, table_name)
89
360
  @columns_hash.key?(table_name)
90
361
  end
91
362
 
92
- def indexes(table_name)
93
- @indexes[table_name] ||= connection.indexes(table_name)
363
+ def indexes(pool, table_name)
364
+ @indexes.fetch(table_name) do
365
+ pool.with_connection do |connection|
366
+ if data_source_exists?(pool, table_name)
367
+ @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
368
+ else
369
+ []
370
+ end
371
+ end
372
+ end
94
373
  end
95
374
 
96
- def database_version # :nodoc:
97
- @database_version ||= connection.get_database_version
375
+ def version(pool)
376
+ @version ||= pool.with_connection(&:schema_version)
98
377
  end
99
378
 
100
- # Clears out internal caches
101
- def clear!
102
- @columns.clear
103
- @columns_hash.clear
104
- @primary_keys.clear
105
- @data_sources.clear
106
- @indexes.clear
107
- @version = nil
108
- @database_version = nil
379
+ def schema_version
380
+ @version
109
381
  end
110
382
 
111
383
  def size
@@ -113,7 +385,7 @@ module ActiveRecord
113
385
  end
114
386
 
115
387
  # Clear out internal caches for the data source +name+.
116
- def clear_data_source_cache!(name)
388
+ def clear_data_source_cache!(_connection, name)
117
389
  @columns.delete name
118
390
  @columns_hash.delete name
119
391
  @primary_keys.delete name
@@ -121,20 +393,87 @@ module ActiveRecord
121
393
  @indexes.delete name
122
394
  end
123
395
 
124
- def marshal_dump
125
- # if we get current version during initialization, it happens stack over flow.
126
- @version = connection.migration_context.current_version
127
- [@version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, database_version]
396
+ def add_all(pool) # :nodoc:
397
+ pool.with_connection do
398
+ tables_to_cache(pool).each do |table|
399
+ add(pool, table)
400
+ end
401
+
402
+ version(pool)
403
+ end
404
+ end
405
+
406
+ def dump_to(filename)
407
+ open(filename) { |f|
408
+ if filename.include?(".dump")
409
+ f.write(Marshal.dump(self))
410
+ else
411
+ f.write(YAML.dump(self))
412
+ end
413
+ }
128
414
  end
129
415
 
130
- def marshal_load(array)
131
- @version, @columns, @columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
132
- @indexes = @indexes || {}
416
+ def marshal_dump # :nodoc:
417
+ [@version, @columns, {}, @primary_keys, @data_sources, @indexes]
418
+ end
419
+
420
+ def marshal_load(array) # :nodoc:
421
+ @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, _database_version = array
422
+ @indexes ||= {}
423
+
424
+ derive_columns_hash_and_deduplicate_values
133
425
  end
134
426
 
135
427
  private
136
- def prepare_data_sources
137
- connection.data_sources.each { |source| @data_sources[source] = true }
428
+ def tables_to_cache(pool)
429
+ pool.with_connection do |connection|
430
+ connection.data_sources.reject do |table|
431
+ ignored_table?(table)
432
+ end
433
+ end
434
+ end
435
+
436
+ def ignored_table?(table_name)
437
+ ActiveRecord.schema_cache_ignored_tables.any? do |ignored|
438
+ ignored === table_name
439
+ end
440
+ end
441
+
442
+ def derive_columns_hash_and_deduplicate_values
443
+ @columns = deep_deduplicate(@columns)
444
+ @columns_hash = @columns.transform_values { |columns| columns.index_by(&:name) }
445
+ @primary_keys = deep_deduplicate(@primary_keys)
446
+ @data_sources = deep_deduplicate(@data_sources)
447
+ @indexes = deep_deduplicate(@indexes)
448
+ end
449
+
450
+ def deep_deduplicate(value)
451
+ case value
452
+ when Hash
453
+ value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
454
+ when Array
455
+ value.map { |i| deep_deduplicate(i) }
456
+ when String, Deduplicable
457
+ -value
458
+ else
459
+ value
460
+ end
461
+ end
462
+
463
+ def open(filename)
464
+ FileUtils.mkdir_p(File.dirname(filename))
465
+
466
+ File.atomic_write(filename) do |file|
467
+ if File.extname(filename) == ".gz"
468
+ zipper = Zlib::GzipWriter.new file
469
+ zipper.mtime = 0
470
+ yield zipper
471
+ zipper.flush
472
+ zipper.close
473
+ else
474
+ yield file
475
+ end
476
+ end
138
477
  end
139
478
  end
140
479
  end
@@ -4,6 +4,8 @@ module ActiveRecord
4
4
  # :stopdoc:
5
5
  module ConnectionAdapters
6
6
  class SqlTypeMetadata
7
+ include Deduplicable
8
+
7
9
  attr_reader :sql_type, :type, :limit, :precision, :scale
8
10
 
9
11
  def initialize(sql_type: nil, type: nil, limit: nil, precision: nil, scale: nil)
@@ -32,6 +34,12 @@ module ActiveRecord
32
34
  precision.hash >> 1 ^
33
35
  scale.hash >> 2
34
36
  end
37
+
38
+ private
39
+ def deduplicated
40
+ @sql_type = -sql_type
41
+ super
42
+ end
35
43
  end
36
44
  end
37
45
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module SQLite3
6
+ class Column < ConnectionAdapters::Column # :nodoc:
7
+ attr_reader :rowid
8
+
9
+ def initialize(*, auto_increment: nil, rowid: false, generated_type: nil, **)
10
+ super
11
+ @auto_increment = auto_increment
12
+ @rowid = rowid
13
+ @generated_type = generated_type
14
+ end
15
+
16
+ def auto_increment?
17
+ @auto_increment
18
+ end
19
+
20
+ def auto_incremented_by_db?
21
+ auto_increment? || rowid
22
+ end
23
+
24
+ def virtual?
25
+ !@generated_type.nil?
26
+ end
27
+
28
+ def virtual_stored?
29
+ virtual? && @generated_type == :stored
30
+ end
31
+
32
+ def has_default?
33
+ super && !virtual?
34
+ end
35
+
36
+ def init_with(coder)
37
+ @auto_increment = coder["auto_increment"]
38
+ super
39
+ end
40
+
41
+ def encode_with(coder)
42
+ coder["auto_increment"] = @auto_increment
43
+ super
44
+ end
45
+
46
+ def ==(other)
47
+ other.is_a?(Column) &&
48
+ super &&
49
+ auto_increment? == other.auto_increment?
50
+ end
51
+ alias :eql? :==
52
+
53
+ def hash
54
+ Column.hash ^
55
+ super.hash ^
56
+ auto_increment?.hash ^
57
+ rowid.hash
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end