omg-activerecord 8.0.0.alpha1

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 (412) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +355 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +219 -0
  5. data/examples/performance.rb +185 -0
  6. data/examples/simple.rb +15 -0
  7. data/lib/active_record/aggregations.rb +287 -0
  8. data/lib/active_record/association_relation.rb +50 -0
  9. data/lib/active_record/associations/alias_tracker.rb +90 -0
  10. data/lib/active_record/associations/association.rb +417 -0
  11. data/lib/active_record/associations/association_scope.rb +175 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +163 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  14. data/lib/active_record/associations/builder/association.rb +170 -0
  15. data/lib/active_record/associations/builder/belongs_to.rb +160 -0
  16. data/lib/active_record/associations/builder/collection_association.rb +80 -0
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
  18. data/lib/active_record/associations/builder/has_many.rb +23 -0
  19. data/lib/active_record/associations/builder/has_one.rb +61 -0
  20. data/lib/active_record/associations/builder/singular_association.rb +48 -0
  21. data/lib/active_record/associations/collection_association.rb +535 -0
  22. data/lib/active_record/associations/collection_proxy.rb +1163 -0
  23. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  24. data/lib/active_record/associations/errors.rb +265 -0
  25. data/lib/active_record/associations/foreign_association.rb +40 -0
  26. data/lib/active_record/associations/has_many_association.rb +167 -0
  27. data/lib/active_record/associations/has_many_through_association.rb +232 -0
  28. data/lib/active_record/associations/has_one_association.rb +142 -0
  29. data/lib/active_record/associations/has_one_through_association.rb +45 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
  33. data/lib/active_record/associations/join_dependency.rb +301 -0
  34. data/lib/active_record/associations/nested_error.rb +47 -0
  35. data/lib/active_record/associations/preloader/association.rb +316 -0
  36. data/lib/active_record/associations/preloader/batch.rb +48 -0
  37. data/lib/active_record/associations/preloader/branch.rb +153 -0
  38. data/lib/active_record/associations/preloader/through_association.rb +150 -0
  39. data/lib/active_record/associations/preloader.rb +135 -0
  40. data/lib/active_record/associations/singular_association.rb +76 -0
  41. data/lib/active_record/associations/through_association.rb +132 -0
  42. data/lib/active_record/associations.rb +1897 -0
  43. data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
  44. data/lib/active_record/attribute_assignment.rb +82 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
  46. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  47. data/lib/active_record/attribute_methods/dirty.rb +262 -0
  48. data/lib/active_record/attribute_methods/primary_key.rb +158 -0
  49. data/lib/active_record/attribute_methods/query.rb +50 -0
  50. data/lib/active_record/attribute_methods/read.rb +46 -0
  51. data/lib/active_record/attribute_methods/serialization.rb +232 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
  53. data/lib/active_record/attribute_methods/write.rb +49 -0
  54. data/lib/active_record/attribute_methods.rb +542 -0
  55. data/lib/active_record/attributes.rb +307 -0
  56. data/lib/active_record/autosave_association.rb +586 -0
  57. data/lib/active_record/base.rb +338 -0
  58. data/lib/active_record/callbacks.rb +452 -0
  59. data/lib/active_record/coders/column_serializer.rb +61 -0
  60. data/lib/active_record/coders/json.rb +15 -0
  61. data/lib/active_record/coders/yaml_column.rb +95 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
  64. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
  78. data/lib/active_record/connection_adapters/column.rb +122 -0
  79. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  80. data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
  81. data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
  82. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
  83. data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
  84. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
  89. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
  90. data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
  91. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  92. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  93. data/lib/active_record/connection_adapters/postgresql/column.rb +82 -0
  94. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
  95. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
  115. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  116. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  117. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  118. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +125 -0
  119. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
  120. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  121. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  122. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
  123. data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
  124. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
  125. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
  126. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
  127. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
  128. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
  129. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
  130. data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
  131. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
  132. data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
  133. data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
  134. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  135. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
  136. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  137. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
  138. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
  139. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
  140. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
  141. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
  142. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
  143. data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
  144. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
  145. data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
  146. data/lib/active_record/connection_adapters.rb +176 -0
  147. data/lib/active_record/connection_handling.rb +413 -0
  148. data/lib/active_record/core.rb +836 -0
  149. data/lib/active_record/counter_cache.rb +230 -0
  150. data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
  151. data/lib/active_record/database_configurations/database_config.rb +104 -0
  152. data/lib/active_record/database_configurations/hash_config.rb +172 -0
  153. data/lib/active_record/database_configurations/url_config.rb +78 -0
  154. data/lib/active_record/database_configurations.rb +309 -0
  155. data/lib/active_record/delegated_type.rb +289 -0
  156. data/lib/active_record/deprecator.rb +7 -0
  157. data/lib/active_record/destroy_association_async_job.rb +38 -0
  158. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  159. data/lib/active_record/dynamic_matchers.rb +121 -0
  160. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  161. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  162. data/lib/active_record/encryption/cipher.rb +53 -0
  163. data/lib/active_record/encryption/config.rb +70 -0
  164. data/lib/active_record/encryption/configurable.rb +60 -0
  165. data/lib/active_record/encryption/context.rb +42 -0
  166. data/lib/active_record/encryption/contexts.rb +76 -0
  167. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  168. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  169. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  170. data/lib/active_record/encryption/encrypted_attribute_type.rb +184 -0
  171. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  172. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  173. data/lib/active_record/encryption/encryptor.rb +177 -0
  174. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  175. data/lib/active_record/encryption/errors.rb +15 -0
  176. data/lib/active_record/encryption/extended_deterministic_queries.rb +159 -0
  177. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  178. data/lib/active_record/encryption/key.rb +28 -0
  179. data/lib/active_record/encryption/key_generator.rb +53 -0
  180. data/lib/active_record/encryption/key_provider.rb +46 -0
  181. data/lib/active_record/encryption/message.rb +33 -0
  182. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  183. data/lib/active_record/encryption/message_serializer.rb +96 -0
  184. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  185. data/lib/active_record/encryption/properties.rb +76 -0
  186. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  187. data/lib/active_record/encryption/scheme.rb +107 -0
  188. data/lib/active_record/encryption.rb +58 -0
  189. data/lib/active_record/enum.rb +424 -0
  190. data/lib/active_record/errors.rb +614 -0
  191. data/lib/active_record/explain.rb +63 -0
  192. data/lib/active_record/explain_registry.rb +37 -0
  193. data/lib/active_record/explain_subscriber.rb +34 -0
  194. data/lib/active_record/fixture_set/file.rb +89 -0
  195. data/lib/active_record/fixture_set/model_metadata.rb +42 -0
  196. data/lib/active_record/fixture_set/render_context.rb +19 -0
  197. data/lib/active_record/fixture_set/table_row.rb +208 -0
  198. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  199. data/lib/active_record/fixtures.rb +850 -0
  200. data/lib/active_record/future_result.rb +182 -0
  201. data/lib/active_record/gem_version.rb +17 -0
  202. data/lib/active_record/inheritance.rb +366 -0
  203. data/lib/active_record/insert_all.rb +328 -0
  204. data/lib/active_record/integration.rb +209 -0
  205. data/lib/active_record/internal_metadata.rb +164 -0
  206. data/lib/active_record/legacy_yaml_adapter.rb +15 -0
  207. data/lib/active_record/locale/en.yml +48 -0
  208. data/lib/active_record/locking/optimistic.rb +228 -0
  209. data/lib/active_record/locking/pessimistic.rb +102 -0
  210. data/lib/active_record/log_subscriber.rb +149 -0
  211. data/lib/active_record/marshalling.rb +56 -0
  212. data/lib/active_record/message_pack.rb +124 -0
  213. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  214. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  215. data/lib/active_record/middleware/database_selector.rb +87 -0
  216. data/lib/active_record/middleware/shard_selector.rb +62 -0
  217. data/lib/active_record/migration/command_recorder.rb +406 -0
  218. data/lib/active_record/migration/compatibility.rb +490 -0
  219. data/lib/active_record/migration/default_strategy.rb +22 -0
  220. data/lib/active_record/migration/execution_strategy.rb +19 -0
  221. data/lib/active_record/migration/join_table.rb +16 -0
  222. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  223. data/lib/active_record/migration.rb +1626 -0
  224. data/lib/active_record/model_schema.rb +635 -0
  225. data/lib/active_record/nested_attributes.rb +633 -0
  226. data/lib/active_record/no_touching.rb +65 -0
  227. data/lib/active_record/normalization.rb +163 -0
  228. data/lib/active_record/persistence.rb +968 -0
  229. data/lib/active_record/promise.rb +84 -0
  230. data/lib/active_record/query_cache.rb +56 -0
  231. data/lib/active_record/query_logs.rb +247 -0
  232. data/lib/active_record/query_logs_formatter.rb +30 -0
  233. data/lib/active_record/querying.rb +122 -0
  234. data/lib/active_record/railtie.rb +440 -0
  235. data/lib/active_record/railties/console_sandbox.rb +5 -0
  236. data/lib/active_record/railties/controller_runtime.rb +65 -0
  237. data/lib/active_record/railties/databases.rake +641 -0
  238. data/lib/active_record/railties/job_runtime.rb +23 -0
  239. data/lib/active_record/readonly_attributes.rb +66 -0
  240. data/lib/active_record/reflection.rb +1287 -0
  241. data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
  242. data/lib/active_record/relation/batches.rb +491 -0
  243. data/lib/active_record/relation/calculations.rb +679 -0
  244. data/lib/active_record/relation/delegation.rb +154 -0
  245. data/lib/active_record/relation/finder_methods.rb +661 -0
  246. data/lib/active_record/relation/from_clause.rb +30 -0
  247. data/lib/active_record/relation/merger.rb +192 -0
  248. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  249. data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
  250. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
  251. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
  252. data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
  253. data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
  254. data/lib/active_record/relation/predicate_builder.rb +181 -0
  255. data/lib/active_record/relation/query_attribute.rb +68 -0
  256. data/lib/active_record/relation/query_methods.rb +2235 -0
  257. data/lib/active_record/relation/record_fetch_warning.rb +52 -0
  258. data/lib/active_record/relation/spawn_methods.rb +78 -0
  259. data/lib/active_record/relation/where_clause.rb +218 -0
  260. data/lib/active_record/relation.rb +1495 -0
  261. data/lib/active_record/result.rb +249 -0
  262. data/lib/active_record/runtime_registry.rb +82 -0
  263. data/lib/active_record/sanitization.rb +254 -0
  264. data/lib/active_record/schema.rb +77 -0
  265. data/lib/active_record/schema_dumper.rb +364 -0
  266. data/lib/active_record/schema_migration.rb +106 -0
  267. data/lib/active_record/scoping/default.rb +205 -0
  268. data/lib/active_record/scoping/named.rb +202 -0
  269. data/lib/active_record/scoping.rb +136 -0
  270. data/lib/active_record/secure_password.rb +60 -0
  271. data/lib/active_record/secure_token.rb +66 -0
  272. data/lib/active_record/serialization.rb +29 -0
  273. data/lib/active_record/signed_id.rb +137 -0
  274. data/lib/active_record/statement_cache.rb +164 -0
  275. data/lib/active_record/store.rb +299 -0
  276. data/lib/active_record/suppressor.rb +59 -0
  277. data/lib/active_record/table_metadata.rb +85 -0
  278. data/lib/active_record/tasks/database_tasks.rb +681 -0
  279. data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
  280. data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
  281. data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
  282. data/lib/active_record/test_databases.rb +24 -0
  283. data/lib/active_record/test_fixtures.rb +321 -0
  284. data/lib/active_record/testing/query_assertions.rb +121 -0
  285. data/lib/active_record/timestamp.rb +177 -0
  286. data/lib/active_record/token_for.rb +123 -0
  287. data/lib/active_record/touch_later.rb +70 -0
  288. data/lib/active_record/transaction.rb +132 -0
  289. data/lib/active_record/transactions.rb +523 -0
  290. data/lib/active_record/translation.rb +22 -0
  291. data/lib/active_record/type/adapter_specific_registry.rb +144 -0
  292. data/lib/active_record/type/date.rb +9 -0
  293. data/lib/active_record/type/date_time.rb +9 -0
  294. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  295. data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
  296. data/lib/active_record/type/internal/timezone.rb +22 -0
  297. data/lib/active_record/type/json.rb +30 -0
  298. data/lib/active_record/type/serialized.rb +76 -0
  299. data/lib/active_record/type/text.rb +11 -0
  300. data/lib/active_record/type/time.rb +35 -0
  301. data/lib/active_record/type/type_map.rb +58 -0
  302. data/lib/active_record/type/unsigned_integer.rb +16 -0
  303. data/lib/active_record/type.rb +83 -0
  304. data/lib/active_record/type_caster/connection.rb +33 -0
  305. data/lib/active_record/type_caster/map.rb +23 -0
  306. data/lib/active_record/type_caster.rb +9 -0
  307. data/lib/active_record/validations/absence.rb +25 -0
  308. data/lib/active_record/validations/associated.rb +65 -0
  309. data/lib/active_record/validations/length.rb +26 -0
  310. data/lib/active_record/validations/numericality.rb +36 -0
  311. data/lib/active_record/validations/presence.rb +45 -0
  312. data/lib/active_record/validations/uniqueness.rb +295 -0
  313. data/lib/active_record/validations.rb +101 -0
  314. data/lib/active_record/version.rb +10 -0
  315. data/lib/active_record.rb +616 -0
  316. data/lib/arel/alias_predication.rb +9 -0
  317. data/lib/arel/attributes/attribute.rb +33 -0
  318. data/lib/arel/collectors/bind.rb +31 -0
  319. data/lib/arel/collectors/composite.rb +46 -0
  320. data/lib/arel/collectors/plain_string.rb +20 -0
  321. data/lib/arel/collectors/sql_string.rb +27 -0
  322. data/lib/arel/collectors/substitute_binds.rb +35 -0
  323. data/lib/arel/crud.rb +48 -0
  324. data/lib/arel/delete_manager.rb +32 -0
  325. data/lib/arel/errors.rb +19 -0
  326. data/lib/arel/expressions.rb +29 -0
  327. data/lib/arel/factory_methods.rb +53 -0
  328. data/lib/arel/filter_predications.rb +9 -0
  329. data/lib/arel/insert_manager.rb +48 -0
  330. data/lib/arel/math.rb +45 -0
  331. data/lib/arel/nodes/ascending.rb +23 -0
  332. data/lib/arel/nodes/binary.rb +125 -0
  333. data/lib/arel/nodes/bind_param.rb +44 -0
  334. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  335. data/lib/arel/nodes/case.rb +55 -0
  336. data/lib/arel/nodes/casted.rb +62 -0
  337. data/lib/arel/nodes/comment.rb +29 -0
  338. data/lib/arel/nodes/count.rb +12 -0
  339. data/lib/arel/nodes/cte.rb +36 -0
  340. data/lib/arel/nodes/delete_statement.rb +44 -0
  341. data/lib/arel/nodes/descending.rb +23 -0
  342. data/lib/arel/nodes/equality.rb +15 -0
  343. data/lib/arel/nodes/extract.rb +24 -0
  344. data/lib/arel/nodes/false.rb +16 -0
  345. data/lib/arel/nodes/filter.rb +10 -0
  346. data/lib/arel/nodes/fragments.rb +35 -0
  347. data/lib/arel/nodes/full_outer_join.rb +8 -0
  348. data/lib/arel/nodes/function.rb +45 -0
  349. data/lib/arel/nodes/grouping.rb +11 -0
  350. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  351. data/lib/arel/nodes/in.rb +15 -0
  352. data/lib/arel/nodes/infix_operation.rb +92 -0
  353. data/lib/arel/nodes/inner_join.rb +8 -0
  354. data/lib/arel/nodes/insert_statement.rb +37 -0
  355. data/lib/arel/nodes/join_source.rb +20 -0
  356. data/lib/arel/nodes/leading_join.rb +8 -0
  357. data/lib/arel/nodes/matches.rb +18 -0
  358. data/lib/arel/nodes/named_function.rb +23 -0
  359. data/lib/arel/nodes/nary.rb +39 -0
  360. data/lib/arel/nodes/node.rb +161 -0
  361. data/lib/arel/nodes/node_expression.rb +13 -0
  362. data/lib/arel/nodes/ordering.rb +27 -0
  363. data/lib/arel/nodes/outer_join.rb +8 -0
  364. data/lib/arel/nodes/over.rb +15 -0
  365. data/lib/arel/nodes/regexp.rb +16 -0
  366. data/lib/arel/nodes/right_outer_join.rb +8 -0
  367. data/lib/arel/nodes/select_core.rb +67 -0
  368. data/lib/arel/nodes/select_statement.rb +41 -0
  369. data/lib/arel/nodes/sql_literal.rb +32 -0
  370. data/lib/arel/nodes/string_join.rb +11 -0
  371. data/lib/arel/nodes/table_alias.rb +35 -0
  372. data/lib/arel/nodes/terminal.rb +16 -0
  373. data/lib/arel/nodes/true.rb +16 -0
  374. data/lib/arel/nodes/unary.rb +44 -0
  375. data/lib/arel/nodes/unary_operation.rb +20 -0
  376. data/lib/arel/nodes/unqualified_column.rb +22 -0
  377. data/lib/arel/nodes/update_statement.rb +46 -0
  378. data/lib/arel/nodes/values_list.rb +9 -0
  379. data/lib/arel/nodes/window.rb +126 -0
  380. data/lib/arel/nodes/with.rb +11 -0
  381. data/lib/arel/nodes.rb +75 -0
  382. data/lib/arel/order_predications.rb +13 -0
  383. data/lib/arel/predications.rb +260 -0
  384. data/lib/arel/select_manager.rb +276 -0
  385. data/lib/arel/table.rb +121 -0
  386. data/lib/arel/tree_manager.rb +65 -0
  387. data/lib/arel/update_manager.rb +49 -0
  388. data/lib/arel/visitors/dot.rb +299 -0
  389. data/lib/arel/visitors/mysql.rb +111 -0
  390. data/lib/arel/visitors/postgresql.rb +99 -0
  391. data/lib/arel/visitors/sqlite.rb +38 -0
  392. data/lib/arel/visitors/to_sql.rb +1033 -0
  393. data/lib/arel/visitors/visitor.rb +45 -0
  394. data/lib/arel/visitors.rb +13 -0
  395. data/lib/arel/window_predications.rb +9 -0
  396. data/lib/arel.rb +73 -0
  397. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  398. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
  399. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  400. data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
  401. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
  402. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
  403. data/lib/rails/generators/active_record/migration.rb +54 -0
  404. data/lib/rails/generators/active_record/model/USAGE +113 -0
  405. data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
  406. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  407. data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
  408. data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
  409. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  410. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  411. data/lib/rails/generators/active_record.rb +19 -0
  412. metadata +505 -0
@@ -0,0 +1,328 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/enumerable"
4
+
5
+ module ActiveRecord
6
+ class InsertAll # :nodoc:
7
+ attr_reader :model, :connection, :inserts, :keys
8
+ attr_reader :on_duplicate, :update_only, :returning, :unique_by, :update_sql
9
+
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
17
+
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
38
+
39
+ @returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
40
+ @returning = false if @returning == []
41
+
42
+ @unique_by = find_unique_index_for(@unique_by)
43
+
44
+ configure_on_duplicate_update_logic
45
+ ensure_valid_options_for_connection!
46
+ end
47
+
48
+ def execute
49
+ return ActiveRecord::Result.empty if inserts.empty?
50
+
51
+ message = +"#{model} "
52
+ message << "Bulk " if inserts.many?
53
+ message << (on_duplicate == :update ? "Upsert" : "Insert")
54
+ connection.exec_insert_all to_sql, message
55
+ end
56
+
57
+ def updatable_columns
58
+ @updatable_columns ||= keys - readonly_columns - unique_by_columns
59
+ end
60
+
61
+ def primary_keys
62
+ Array(@model.schema_cache.primary_keys(model.table_name))
63
+ end
64
+
65
+ def skip_duplicates?
66
+ on_duplicate == :skip
67
+ end
68
+
69
+ def update_duplicates?
70
+ on_duplicate == :update
71
+ end
72
+
73
+ def map_key_with_value
74
+ inserts.map do |attributes|
75
+ attributes = attributes.stringify_keys
76
+ attributes.merge!(@scope_attributes)
77
+ attributes.reverse_merge!(timestamps_for_create) if record_timestamps?
78
+
79
+ verify_attributes(attributes)
80
+
81
+ keys_including_timestamps.map do |key|
82
+ yield key, attributes[key]
83
+ end
84
+ end
85
+ end
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
+
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
+
149
+ def find_unique_index_for(unique_by)
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
155
+
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 }
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)
164
+ else
165
+ raise ArgumentError, "No unique index found for #{name_or_columns}"
166
+ end
167
+ end
168
+
169
+ def unique_indexes
170
+ @model.schema_cache.indexes(model.table_name).select(&:unique)
171
+ end
172
+
173
+ def ensure_valid_options_for_connection!
174
+ if returning && !connection.supports_insert_returning?
175
+ raise ArgumentError, "#{connection.class} does not support :returning"
176
+ end
177
+
178
+ if skip_duplicates? && !connection.supports_insert_on_duplicate_skip?
179
+ raise ArgumentError, "#{connection.class} does not support skipping duplicates"
180
+ end
181
+
182
+ if update_duplicates? && !connection.supports_insert_on_duplicate_update?
183
+ raise ArgumentError, "#{connection.class} does not support upsert"
184
+ end
185
+
186
+ if unique_by && !connection.supports_insert_conflict_target?
187
+ raise ArgumentError, "#{connection.class} does not support :unique_by"
188
+ end
189
+ end
190
+
191
+
192
+ def to_sql
193
+ connection.build_insert_sql(ActiveRecord::InsertAll::Builder.new(self))
194
+ end
195
+
196
+
197
+ def readonly_columns
198
+ primary_keys + model.readonly_attributes
199
+ end
200
+
201
+ def unique_by_columns
202
+ Array(unique_by&.columns)
203
+ end
204
+
205
+
206
+ def verify_attributes(attributes)
207
+ if keys_including_timestamps != attributes.keys.to_set
208
+ raise ArgumentError, "All objects being inserted must have the same keys"
209
+ end
210
+ end
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
+
225
+ class Builder # :nodoc:
226
+ attr_reader :model
227
+
228
+ delegate :skip_duplicates?, :update_duplicates?, :keys, :keys_including_timestamps, :record_timestamps?, to: :insert_all
229
+
230
+ def initialize(insert_all)
231
+ @insert_all, @model, @connection = insert_all, insert_all.model, insert_all.connection
232
+ end
233
+
234
+ def into
235
+ "INTO #{model.quoted_table_name} (#{columns_list})"
236
+ end
237
+
238
+ def values_list
239
+ types = extract_types_from_columns_on(model.table_name, keys: keys_including_timestamps)
240
+
241
+ values_list = insert_all.map_key_with_value do |key, value|
242
+ next value if Arel::Nodes::SqlLiteral === value
243
+ connection.with_yaml_fallback(types[key].serialize(value))
244
+ end
245
+
246
+ connection.visitor.compile(Arel::Nodes::ValuesList.new(values_list))
247
+ end
248
+
249
+ def 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
263
+ end
264
+
265
+ def conflict_target
266
+ if index = insert_all.unique_by
267
+ sql = +"(#{format_columns(index.columns)})"
268
+ sql << " WHERE #{index.where}" if index.where
269
+ sql
270
+ elsif update_duplicates?
271
+ "(#{format_columns(insert_all.primary_keys)})"
272
+ end
273
+ end
274
+
275
+ def updatable_columns
276
+ quote_columns(insert_all.updatable_columns)
277
+ end
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
+
295
+ private
296
+ attr_reader :connection, :insert_all
297
+
298
+ def touch_timestamp_attribute?(column_name)
299
+ insert_all.updatable_columns.exclude?(column_name)
300
+ end
301
+
302
+ def columns_list
303
+ format_columns(insert_all.keys_including_timestamps)
304
+ end
305
+
306
+ def extract_types_from_columns_on(table_name, keys:)
307
+ columns = @model.schema_cache.columns_hash(table_name)
308
+
309
+ unknown_column = (keys - columns.keys).first
310
+ raise UnknownAttributeError.new(model.new, unknown_column) if unknown_column
311
+
312
+ keys.index_with { |key| model.type_for_attribute(key) }
313
+ end
314
+
315
+ def format_columns(columns)
316
+ columns.respond_to?(:map) ? quote_columns(columns).join(",") : columns
317
+ end
318
+
319
+ def quote_columns(columns)
320
+ columns.map { |column| quote_column(column) }
321
+ end
322
+
323
+ def quote_column(column)
324
+ connection.quote_column_name(column)
325
+ end
326
+ end
327
+ end
328
+ end
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/filters"
4
+
5
+ module ActiveRecord
6
+ module Integration
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ ##
11
+ # :singleton-method:
12
+ # Indicates the format used to generate the timestamp in the cache key, if
13
+ # versioning is off. Accepts any of the symbols in +Time::DATE_FORMATS+.
14
+ #
15
+ # This is +:usec+, by default.
16
+ class_attribute :cache_timestamp_format, instance_writer: false, default: :usec
17
+
18
+ ##
19
+ # :singleton-method:
20
+ # Indicates whether to use a stable #cache_key method that is accompanied
21
+ # by a changing version in the #cache_version method.
22
+ #
23
+ # This is +true+, by default on \Rails 5.2 and above.
24
+ class_attribute :cache_versioning, instance_writer: false, default: false
25
+
26
+ ##
27
+ # :singleton-method:
28
+ # Indicates whether to use a stable #cache_key method that is accompanied
29
+ # by a changing version in the #cache_version method on collections.
30
+ #
31
+ # This is +false+, by default until \Rails 6.1.
32
+ class_attribute :collection_cache_versioning, instance_writer: false, default: false
33
+ end
34
+
35
+ # Returns a +String+, which Action Pack uses for constructing a URL to this
36
+ # object. The default implementation returns this record's id as a +String+,
37
+ # or +nil+ if this record's unsaved.
38
+ #
39
+ # For example, suppose that you have a User model, and that you have a
40
+ # <tt>resources :users</tt> route. Normally, +user_path+ will
41
+ # construct a path with the user object's 'id' in it:
42
+ #
43
+ # user = User.find_by(name: 'Phusion')
44
+ # user_path(user) # => "/users/1"
45
+ #
46
+ # You can override +to_param+ in your model to make +user_path+ construct
47
+ # a path using the user's name instead of the user's id:
48
+ #
49
+ # class User < ActiveRecord::Base
50
+ # def to_param # overridden
51
+ # name
52
+ # end
53
+ # end
54
+ #
55
+ # user = User.find_by(name: 'Phusion')
56
+ # user_path(user) # => "/users/Phusion"
57
+ def to_param
58
+ return unless id
59
+ Array(id).join(self.class.param_delimiter)
60
+ end
61
+
62
+ # Returns a stable cache key that can be used to identify this record.
63
+ #
64
+ # Product.new.cache_key # => "products/new"
65
+ # Product.find(5).cache_key # => "products/5"
66
+ #
67
+ # If ActiveRecord::Base.cache_versioning is turned off, as it was in \Rails 5.1 and earlier,
68
+ # the cache key will also include a version.
69
+ #
70
+ # Product.cache_versioning = false
71
+ # Product.find(5).cache_key # => "products/5-20071224150000" (updated_at available)
72
+ def cache_key
73
+ if new_record?
74
+ "#{model_name.cache_key}/new"
75
+ else
76
+ if cache_version
77
+ "#{model_name.cache_key}/#{id}"
78
+ else
79
+ timestamp = max_updated_column_timestamp
80
+
81
+ if timestamp
82
+ timestamp = timestamp.utc.to_fs(cache_timestamp_format)
83
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
84
+ else
85
+ "#{model_name.cache_key}/#{id}"
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ # Returns a cache version that can be used together with the cache key to form
92
+ # a recyclable caching scheme. By default, the #updated_at column is used for the
93
+ # cache_version, but this method can be overwritten to return something else.
94
+ #
95
+ # Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
96
+ # +false+.
97
+ def cache_version
98
+ return unless cache_versioning
99
+
100
+ if has_attribute?("updated_at")
101
+ timestamp = updated_at_before_type_cast
102
+ if can_use_fast_cache_version?(timestamp)
103
+ raw_timestamp_to_cache_version(timestamp)
104
+
105
+ elsif timestamp = updated_at
106
+ timestamp.utc.to_fs(cache_timestamp_format)
107
+ end
108
+ elsif self.class.has_attribute?("updated_at")
109
+ raise ActiveModel::MissingAttributeError, "missing attribute 'updated_at' for #{self.class}"
110
+ end
111
+ end
112
+
113
+ # Returns a cache key along with the version.
114
+ def cache_key_with_version
115
+ if version = cache_version
116
+ "#{cache_key}-#{version}"
117
+ else
118
+ cache_key
119
+ end
120
+ end
121
+
122
+ module ClassMethods
123
+ # Defines your model's +to_param+ method to generate "pretty" URLs
124
+ # using +method_name+, which can be any attribute or method that
125
+ # responds to +to_s+.
126
+ #
127
+ # class User < ActiveRecord::Base
128
+ # to_param :name
129
+ # end
130
+ #
131
+ # user = User.find_by(name: 'Fancy Pants')
132
+ # user.id # => 123
133
+ # user_path(user) # => "/users/123-fancy-pants"
134
+ #
135
+ # Values longer than 20 characters will be truncated. The value
136
+ # is truncated word by word.
137
+ #
138
+ # user = User.find_by(name: 'David Heinemeier Hansson')
139
+ # user.id # => 125
140
+ # user_path(user) # => "/users/125-david-heinemeier"
141
+ #
142
+ # Because the generated param begins with the record's +id+, it is
143
+ # suitable for passing to +find+. In a controller, for example:
144
+ #
145
+ # params[:id] # => "123-fancy-pants"
146
+ # User.find(params[:id]).id # => 123
147
+ def to_param(method_name = nil)
148
+ if method_name.nil?
149
+ super()
150
+ else
151
+ define_method :to_param do
152
+ if (default = super()) &&
153
+ (result = send(method_name).to_s).present? &&
154
+ (param = result.squish.parameterize.truncate(20, separator: /-/, omission: "")).present?
155
+ "#{default}-#{param}"
156
+ else
157
+ default
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ def collection_cache_key(collection = all, timestamp_column = :updated_at) # :nodoc:
164
+ collection.send(:compute_cache_key, timestamp_column)
165
+ end
166
+ end
167
+
168
+ private
169
+ # Detects if the value before type cast
170
+ # can be used to generate a cache_version.
171
+ #
172
+ # The fast cache version only works with a
173
+ # string value directly from the database.
174
+ #
175
+ # We also must check if the timestamp format has been changed
176
+ # or if the timezone is not set to UTC then
177
+ # we cannot apply our transformations correctly.
178
+ def can_use_fast_cache_version?(timestamp)
179
+ timestamp.is_a?(String) &&
180
+ cache_timestamp_format == :usec &&
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 &&
185
+ !updated_at_came_from_user?
186
+ end
187
+
188
+ # Converts a raw database string to `:usec`
189
+ # format.
190
+ #
191
+ # Example:
192
+ #
193
+ # timestamp = "2018-10-15 20:02:15.266505"
194
+ # raw_timestamp_to_cache_version(timestamp)
195
+ # # => "20181015200215266505"
196
+ #
197
+ # PostgreSQL truncates trailing zeros,
198
+ # https://github.com/postgres/postgres/commit/3e1beda2cde3495f41290e1ece5d544525810214
199
+ # to account for this we pad the output with zeros
200
+ def raw_timestamp_to_cache_version(timestamp)
201
+ key = timestamp.delete("- :.")
202
+ if key.length < 20
203
+ key.ljust(20, "0")
204
+ else
205
+ key
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/scoping/default"
4
+ require "active_record/scoping/named"
5
+
6
+ module ActiveRecord
7
+ # This class is used to create a table that keeps track of values and keys such
8
+ # as which environment migrations were run in.
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
30
+
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)
44
+ end
45
+ end
46
+
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
54
+ end
55
+ end
56
+
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")
62
+ end
63
+ end
64
+
65
+ def count
66
+ sm = Arel::SelectManager.new(arel_table)
67
+ sm.project(*Arel::Nodes::Count.new([Arel.star]))
68
+
69
+ @pool.with_connection do |connection|
70
+ connection.select_values(sm, "#{self.class} Count").first
71
+ end
72
+ end
73
+
74
+ def create_table_and_set_flags(environment, schema_sha1 = nil)
75
+ return unless enabled?
76
+
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
81
+ end
82
+ end
83
+
84
+ # Creates an internal metadata table with columns +key+ and +value+
85
+ def create_table
86
+ return unless enabled?
87
+
88
+ @pool.with_connection do |connection|
89
+ unless connection.table_exists?(table_name)
90
+ connection.create_table(table_name, id: false) do |t|
91
+ t.string :key, **connection.internal_string_options_for_primary_key
92
+ t.string :value
93
+ t.timestamps
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ def drop_table
100
+ return unless enabled?
101
+
102
+ @pool.with_connection do |connection|
103
+ connection.drop_table table_name, if_exists: true
104
+ end
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
163
+ end
164
+ end