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
@@ -4,8 +4,228 @@ require "active_support/core_ext/file/atomic"
4
4
 
5
5
  module ActiveRecord
6
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
7
227
  class SchemaCache
8
- def self.load_from(filename)
228
+ def self._load_from(filename) # :nodoc:
9
229
  return unless File.file?(filename)
10
230
 
11
231
  read(filename) do |file|
@@ -32,20 +252,16 @@ module ActiveRecord
32
252
  end
33
253
  private_class_method :read
34
254
 
35
- attr_reader :version
36
- attr_accessor :connection
37
-
38
- def initialize(conn)
39
- @connection = conn
40
-
255
+ def initialize # :nodoc:
41
256
  @columns = {}
42
257
  @columns_hash = {}
43
258
  @primary_keys = {}
44
259
  @data_sources = {}
45
260
  @indexes = {}
261
+ @version = nil
46
262
  end
47
263
 
48
- def initialize_dup(other)
264
+ def initialize_dup(other) # :nodoc:
49
265
  super
50
266
  @columns = @columns.dup
51
267
  @columns_hash = @columns_hash.dup
@@ -54,97 +270,114 @@ module ActiveRecord
54
270
  @indexes = @indexes.dup
55
271
  end
56
272
 
57
- def encode_with(coder)
58
- reset_version!
59
-
60
- coder["columns"] = @columns
61
- coder["primary_keys"] = @primary_keys
62
- coder["data_sources"] = @data_sources
63
- coder["indexes"] = @indexes
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
64
278
  coder["version"] = @version
65
- coder["database_version"] = database_version
66
279
  end
67
280
 
68
- def init_with(coder)
281
+ def init_with(coder) # :nodoc:
69
282
  @columns = coder["columns"]
283
+ @columns_hash = coder["columns_hash"]
70
284
  @primary_keys = coder["primary_keys"]
71
285
  @data_sources = coder["data_sources"]
72
286
  @indexes = coder["indexes"] || {}
73
287
  @version = coder["version"]
74
- @database_version = coder["database_version"]
75
288
 
76
- derive_columns_hash_and_deduplicate_values
289
+ unless coder["deduplicated"]
290
+ derive_columns_hash_and_deduplicate_values
291
+ end
77
292
  end
78
293
 
79
- def primary_keys(table_name)
294
+ def cached?(table_name)
295
+ @columns.key?(table_name)
296
+ end
297
+
298
+ def primary_keys(pool, table_name)
80
299
  @primary_keys.fetch(table_name) do
81
- if data_source_exists?(table_name)
82
- @primary_keys[deep_deduplicate(table_name)] = deep_deduplicate(connection.primary_key(table_name))
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
83
304
  end
84
305
  end
85
306
  end
86
307
 
87
308
  # A cached lookup for table existence.
88
- def data_source_exists?(name)
89
- 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
+
90
318
  return @data_sources[name] if @data_sources.key? name
91
319
 
92
- @data_sources[deep_deduplicate(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
93
323
  end
94
324
 
95
325
  # Add internal cache for table with +table_name+.
96
- def add(table_name)
97
- if data_source_exists?(table_name)
98
- primary_keys(table_name)
99
- columns(table_name)
100
- columns_hash(table_name)
101
- 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
102
334
  end
103
335
  end
104
336
 
105
- def data_sources(name)
106
- @data_sources[name]
107
- end
108
-
109
337
  # Get the columns for a table
110
- def 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
+
111
343
  @columns.fetch(table_name) do
112
- @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
344
+ pool.with_connection do |connection|
345
+ @columns[deep_deduplicate(table_name)] = deep_deduplicate(connection.columns(table_name))
346
+ end
113
347
  end
114
348
  end
115
349
 
116
350
  # Get the columns for a table as a hash, key is the column name
117
351
  # value is the column object.
118
- def columns_hash(table_name)
352
+ def columns_hash(pool, table_name)
119
353
  @columns_hash.fetch(table_name) do
120
- @columns_hash[deep_deduplicate(table_name)] = columns(table_name).index_by(&:name).freeze
354
+ @columns_hash[deep_deduplicate(table_name)] = columns(pool, table_name).index_by(&:name).freeze
121
355
  end
122
356
  end
123
357
 
124
358
  # Checks whether the columns hash is already cached for a table.
125
- def columns_hash?(table_name)
359
+ def columns_hash?(_pool, table_name)
126
360
  @columns_hash.key?(table_name)
127
361
  end
128
362
 
129
- def indexes(table_name)
363
+ def indexes(pool, table_name)
130
364
  @indexes.fetch(table_name) do
131
- @indexes[deep_deduplicate(table_name)] = deep_deduplicate(connection.indexes(table_name))
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
132
372
  end
133
373
  end
134
374
 
135
- def database_version # :nodoc:
136
- @database_version ||= connection.get_database_version
375
+ def version(pool)
376
+ @version ||= pool.with_connection(&:schema_version)
137
377
  end
138
378
 
139
- # Clears out internal caches
140
- def clear!
141
- @columns.clear
142
- @columns_hash.clear
143
- @primary_keys.clear
144
- @data_sources.clear
145
- @indexes.clear
146
- @version = nil
147
- @database_version = nil
379
+ def schema_version
380
+ @version
148
381
  end
149
382
 
150
383
  def size
@@ -152,7 +385,7 @@ module ActiveRecord
152
385
  end
153
386
 
154
387
  # Clear out internal caches for the data source +name+.
155
- def clear_data_source_cache!(name)
388
+ def clear_data_source_cache!(_connection, name)
156
389
  @columns.delete name
157
390
  @columns_hash.delete name
158
391
  @primary_keys.delete name
@@ -160,9 +393,17 @@ module ActiveRecord
160
393
  @indexes.delete name
161
394
  end
162
395
 
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
+
163
406
  def dump_to(filename)
164
- clear!
165
- connection.data_sources.each { |table| add(table) }
166
407
  open(filename) { |f|
167
408
  if filename.include?(".dump")
168
409
  f.write(Marshal.dump(self))
@@ -172,22 +413,30 @@ module ActiveRecord
172
413
  }
173
414
  end
174
415
 
175
- def marshal_dump
176
- reset_version!
177
-
178
- [@version, @columns, {}, @primary_keys, @data_sources, @indexes, database_version]
416
+ def marshal_dump # :nodoc:
417
+ [@version, @columns, {}, @primary_keys, @data_sources, @indexes]
179
418
  end
180
419
 
181
- def marshal_load(array)
182
- @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, @database_version = array
420
+ def marshal_load(array) # :nodoc:
421
+ @version, @columns, _columns_hash, @primary_keys, @data_sources, @indexes, _database_version = array
183
422
  @indexes ||= {}
184
423
 
185
424
  derive_columns_hash_and_deduplicate_values
186
425
  end
187
426
 
188
427
  private
189
- def reset_version!
190
- @version = connection.migration_context.current_version
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
191
440
  end
192
441
 
193
442
  def derive_columns_hash_and_deduplicate_values
@@ -198,51 +447,26 @@ module ActiveRecord
198
447
  @indexes = deep_deduplicate(@indexes)
199
448
  end
200
449
 
201
- if RUBY_VERSION < "2.7"
202
- def deep_deduplicate(value)
203
- case value
204
- when Hash
205
- value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
206
- when Array
207
- value.map { |i| deep_deduplicate(i) }
208
- when String
209
- if value.tainted?
210
- # Ruby 2.6 and 2.7 have slightly different implementations of the String#-@ method.
211
- # In Ruby 2.6, the receiver of the String#-@ method is modified under certain
212
- # circumstances, and this was later identified as a bug
213
- # (https://bugs.ruby-lang.org/issues/15926) and only fixed in Ruby 2.7.
214
- value = value.dup
215
- end
216
- -value
217
- when Deduplicable
218
- -value
219
- else
220
- value
221
- end
222
- end
223
- else
224
- def deep_deduplicate(value)
225
- case value
226
- when Hash
227
- value.transform_keys { |k| deep_deduplicate(k) }.transform_values { |v| deep_deduplicate(v) }
228
- when Array
229
- value.map { |i| deep_deduplicate(i) }
230
- when String, Deduplicable
231
- -value
232
- else
233
- value
234
- end
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
235
460
  end
236
461
  end
237
462
 
238
- def prepare_data_sources
239
- connection.data_sources.each { |source| @data_sources[source] = true }
240
- end
241
-
242
463
  def open(filename)
464
+ FileUtils.mkdir_p(File.dirname(filename))
465
+
243
466
  File.atomic_write(filename) do |file|
244
467
  if File.extname(filename) == ".gz"
245
468
  zipper = Zlib::GzipWriter.new file
469
+ zipper.mtime = 0
246
470
  yield zipper
247
471
  zipper.flush
248
472
  zipper.close
@@ -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