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,843 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/connection_adapters/abstract_adapter"
4
+ require "active_record/connection_adapters/statement_pool"
5
+ require "active_record/connection_adapters/sqlite3/column"
6
+ require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
7
+ require "active_record/connection_adapters/sqlite3/quoting"
8
+ require "active_record/connection_adapters/sqlite3/database_statements"
9
+ require "active_record/connection_adapters/sqlite3/schema_creation"
10
+ require "active_record/connection_adapters/sqlite3/schema_definitions"
11
+ require "active_record/connection_adapters/sqlite3/schema_dumper"
12
+ require "active_record/connection_adapters/sqlite3/schema_statements"
13
+
14
+ gem "sqlite3", ">= 2.0"
15
+ require "sqlite3"
16
+
17
+ module ActiveRecord
18
+ module ConnectionAdapters # :nodoc:
19
+ # = Active Record SQLite3 Adapter
20
+ #
21
+ # The SQLite3 adapter works with the sqlite3-ruby drivers
22
+ # (available as gem from https://rubygems.org/gems/sqlite3).
23
+ #
24
+ # Options:
25
+ #
26
+ # * <tt>:database</tt> - Path to the database file.
27
+ class SQLite3Adapter < AbstractAdapter
28
+ ADAPTER_NAME = "SQLite"
29
+
30
+ class << self
31
+ def new_client(config)
32
+ ::SQLite3::Database.new(config[:database].to_s, config)
33
+ rescue Errno::ENOENT => error
34
+ if error.message.include?("No such file or directory")
35
+ raise ActiveRecord::NoDatabaseError
36
+ else
37
+ raise
38
+ end
39
+ end
40
+
41
+ def dbconsole(config, options = {})
42
+ args = []
43
+
44
+ args << "-#{options[:mode]}" if options[:mode]
45
+ args << "-header" if options[:header]
46
+ args << File.expand_path(config.database, Rails.respond_to?(:root) ? Rails.root : nil)
47
+
48
+ find_cmd_and_exec(ActiveRecord.database_cli[:sqlite], *args)
49
+ end
50
+ end
51
+
52
+ include SQLite3::Quoting
53
+ include SQLite3::SchemaStatements
54
+ include SQLite3::DatabaseStatements
55
+
56
+ ##
57
+ # :singleton-method:
58
+ # Configure the SQLite3Adapter to be used in a strict strings mode.
59
+ # This will disable double-quoted string literals, because otherwise typos can silently go unnoticed.
60
+ # For example, it is possible to create an index for a non existing column.
61
+ # If you wish to enable this mode you can add the following line to your application.rb file:
62
+ #
63
+ # config.active_record.sqlite3_adapter_strict_strings_by_default = true
64
+ class_attribute :strict_strings_by_default, default: false
65
+
66
+ NATIVE_DATABASE_TYPES = {
67
+ primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
68
+ string: { name: "varchar" },
69
+ text: { name: "text" },
70
+ integer: { name: "integer" },
71
+ float: { name: "float" },
72
+ decimal: { name: "decimal" },
73
+ datetime: { name: "datetime" },
74
+ time: { name: "time" },
75
+ date: { name: "date" },
76
+ binary: { name: "blob" },
77
+ boolean: { name: "boolean" },
78
+ json: { name: "json" },
79
+ }
80
+
81
+ DEFAULT_PRAGMAS = {
82
+ "foreign_keys" => true,
83
+ "journal_mode" => :wal,
84
+ "synchronous" => :normal,
85
+ "mmap_size" => 134217728, # 128 megabytes
86
+ "journal_size_limit" => 67108864, # 64 megabytes
87
+ "cache_size" => 2000
88
+ }
89
+
90
+ class StatementPool < ConnectionAdapters::StatementPool # :nodoc:
91
+ alias reset clear
92
+
93
+ private
94
+ def dealloc(stmt)
95
+ stmt.close unless stmt.closed?
96
+ end
97
+ end
98
+
99
+ def initialize(...)
100
+ super
101
+
102
+ @memory_database = false
103
+ case @config[:database].to_s
104
+ when ""
105
+ raise ArgumentError, "No database file specified. Missing argument: database"
106
+ when ":memory:"
107
+ @memory_database = true
108
+ when /\Afile:/
109
+ else
110
+ # Otherwise we have a path relative to Rails.root
111
+ @config[:database] = File.expand_path(@config[:database], Rails.root) if defined?(Rails.root)
112
+ dirname = File.dirname(@config[:database])
113
+ unless File.directory?(dirname)
114
+ begin
115
+ FileUtils.mkdir_p(dirname)
116
+ rescue SystemCallError
117
+ raise ActiveRecord::NoDatabaseError.new(connection_pool: @pool)
118
+ end
119
+ end
120
+ end
121
+
122
+ @last_affected_rows = nil
123
+ @previous_read_uncommitted = nil
124
+ @config[:strict] = ConnectionAdapters::SQLite3Adapter.strict_strings_by_default unless @config.key?(:strict)
125
+ @connection_parameters = @config.merge(
126
+ database: @config[:database].to_s,
127
+ results_as_hash: true,
128
+ default_transaction_mode: :immediate,
129
+ )
130
+ end
131
+
132
+ def database_exists?
133
+ @config[:database] == ":memory:" || File.exist?(@config[:database].to_s)
134
+ end
135
+
136
+ def supports_ddl_transactions?
137
+ true
138
+ end
139
+
140
+ def supports_savepoints?
141
+ true
142
+ end
143
+
144
+ def supports_transaction_isolation?
145
+ true
146
+ end
147
+
148
+ def supports_partial_index?
149
+ true
150
+ end
151
+
152
+ def supports_expression_index?
153
+ database_version >= "3.9.0"
154
+ end
155
+
156
+ def requires_reloading?
157
+ true
158
+ end
159
+
160
+ def supports_foreign_keys?
161
+ true
162
+ end
163
+
164
+ def supports_check_constraints?
165
+ true
166
+ end
167
+
168
+ def supports_views?
169
+ true
170
+ end
171
+
172
+ def supports_datetime_with_precision?
173
+ true
174
+ end
175
+
176
+ def supports_json?
177
+ true
178
+ end
179
+
180
+ def supports_common_table_expressions?
181
+ database_version >= "3.8.3"
182
+ end
183
+
184
+ def supports_insert_returning?
185
+ database_version >= "3.35.0"
186
+ end
187
+
188
+ def supports_insert_on_conflict?
189
+ database_version >= "3.24.0"
190
+ end
191
+ alias supports_insert_on_duplicate_skip? supports_insert_on_conflict?
192
+ alias supports_insert_on_duplicate_update? supports_insert_on_conflict?
193
+ alias supports_insert_conflict_target? supports_insert_on_conflict?
194
+
195
+ def supports_concurrent_connections?
196
+ !@memory_database
197
+ end
198
+
199
+ def supports_virtual_columns?
200
+ database_version >= "3.31.0"
201
+ end
202
+
203
+ def connected?
204
+ !(@raw_connection.nil? || @raw_connection.closed?)
205
+ end
206
+
207
+ alias_method :active?, :connected?
208
+
209
+ alias :reset! :reconnect!
210
+
211
+ # Disconnects from the database if already connected. Otherwise, this
212
+ # method does nothing.
213
+ def disconnect!
214
+ super
215
+
216
+ @raw_connection&.close rescue nil
217
+ @raw_connection = nil
218
+ end
219
+
220
+ def supports_index_sort_order?
221
+ true
222
+ end
223
+
224
+ def native_database_types # :nodoc:
225
+ NATIVE_DATABASE_TYPES
226
+ end
227
+
228
+ # Returns the current database encoding format as a string, e.g. 'UTF-8'
229
+ def encoding
230
+ any_raw_connection.encoding.to_s
231
+ end
232
+
233
+ def supports_explain?
234
+ true
235
+ end
236
+
237
+ def supports_lazy_transactions?
238
+ true
239
+ end
240
+
241
+ def supports_deferrable_constraints?
242
+ true
243
+ end
244
+
245
+ # REFERENTIAL INTEGRITY ====================================
246
+
247
+ def disable_referential_integrity # :nodoc:
248
+ old_foreign_keys = query_value("PRAGMA foreign_keys")
249
+ old_defer_foreign_keys = query_value("PRAGMA defer_foreign_keys")
250
+
251
+ begin
252
+ execute("PRAGMA defer_foreign_keys = ON")
253
+ execute("PRAGMA foreign_keys = OFF")
254
+ yield
255
+ ensure
256
+ execute("PRAGMA defer_foreign_keys = #{old_defer_foreign_keys}")
257
+ execute("PRAGMA foreign_keys = #{old_foreign_keys}")
258
+ end
259
+ end
260
+
261
+ def check_all_foreign_keys_valid! # :nodoc:
262
+ sql = "PRAGMA foreign_key_check"
263
+ result = execute(sql)
264
+
265
+ unless result.blank?
266
+ tables = result.map { |row| row["table"] }
267
+ raise ActiveRecord::StatementInvalid.new("Foreign key violations found: #{tables.join(", ")}", sql: sql, connection_pool: @pool)
268
+ end
269
+ end
270
+
271
+ # SCHEMA STATEMENTS ========================================
272
+
273
+ def primary_keys(table_name) # :nodoc:
274
+ pks = table_structure(table_name).select { |f| f["pk"] > 0 }
275
+ pks.sort_by { |f| f["pk"] }.map { |f| f["name"] }
276
+ end
277
+
278
+ def remove_index(table_name, column_name = nil, **options) # :nodoc:
279
+ return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
280
+
281
+ index_name = index_name_for_remove(table_name, column_name, options)
282
+
283
+ exec_query "DROP INDEX #{quote_column_name(index_name)}"
284
+ end
285
+
286
+ VIRTUAL_TABLE_REGEX = /USING\s+(\w+)\s*\((.+)\)/i
287
+
288
+ # Returns a list of defined virtual tables
289
+ def virtual_tables
290
+ query = <<~SQL
291
+ SELECT name, sql FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL %';
292
+ SQL
293
+
294
+ exec_query(query, "SCHEMA").cast_values.each_with_object({}) do |row, memo|
295
+ table_name, sql = row[0], row[1]
296
+ _, module_name, arguments = sql.match(VIRTUAL_TABLE_REGEX).to_a
297
+ memo[table_name] = [module_name, arguments]
298
+ end.to_a
299
+ end
300
+
301
+ # Creates a virtual table
302
+ #
303
+ # Example:
304
+ # create_virtual_table :emails, :fts5, ['sender', 'title',' body']
305
+ def create_virtual_table(table_name, module_name, values)
306
+ exec_query "CREATE VIRTUAL TABLE IF NOT EXISTS #{table_name} USING #{module_name} (#{values.join(", ")})"
307
+ end
308
+
309
+ # Drops a virtual table
310
+ #
311
+ # Although this command ignores +module_name+ and +values+,
312
+ # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
313
+ # In that case, +module_name+, +values+ and +options+ will be used by #create_virtual_table.
314
+ def drop_virtual_table(table_name, module_name, values, **options)
315
+ drop_table(table_name)
316
+ end
317
+
318
+ # Renames a table.
319
+ #
320
+ # Example:
321
+ # rename_table('octopuses', 'octopi')
322
+ def rename_table(table_name, new_name, **options)
323
+ validate_table_length!(new_name) unless options[:_uses_legacy_table_name]
324
+ schema_cache.clear_data_source_cache!(table_name.to_s)
325
+ schema_cache.clear_data_source_cache!(new_name.to_s)
326
+ exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
327
+ rename_table_indexes(table_name, new_name, **options)
328
+ end
329
+
330
+ def add_column(table_name, column_name, type, **options) # :nodoc:
331
+ type = type.to_sym
332
+ if invalid_alter_table_type?(type, options)
333
+ alter_table(table_name) do |definition|
334
+ definition.column(column_name, type, **options)
335
+ end
336
+ else
337
+ super
338
+ end
339
+ end
340
+
341
+ def remove_column(table_name, column_name, type = nil, **options) # :nodoc:
342
+ alter_table(table_name) do |definition|
343
+ definition.remove_column column_name
344
+ definition.foreign_keys.delete_if { |fk| fk.column == column_name.to_s }
345
+ end
346
+ end
347
+
348
+ def remove_columns(table_name, *column_names, type: nil, **options) # :nodoc:
349
+ alter_table(table_name) do |definition|
350
+ column_names.each do |column_name|
351
+ definition.remove_column column_name
352
+ end
353
+ column_names = column_names.map(&:to_s)
354
+ definition.foreign_keys.delete_if { |fk| column_names.include?(fk.column) }
355
+ end
356
+ end
357
+
358
+ def change_column_default(table_name, column_name, default_or_changes) # :nodoc:
359
+ default = extract_new_default_value(default_or_changes)
360
+
361
+ alter_table(table_name) do |definition|
362
+ definition[column_name].default = default
363
+ end
364
+ end
365
+
366
+ def change_column_null(table_name, column_name, null, default = nil) # :nodoc:
367
+ validate_change_column_null_argument!(null)
368
+
369
+ unless null || default.nil?
370
+ internal_exec_query("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
371
+ end
372
+ alter_table(table_name) do |definition|
373
+ definition[column_name].null = null
374
+ end
375
+ end
376
+
377
+ def change_column(table_name, column_name, type, **options) # :nodoc:
378
+ alter_table(table_name) do |definition|
379
+ definition.change_column(column_name, type, **options)
380
+ end
381
+ end
382
+
383
+ def rename_column(table_name, column_name, new_column_name) # :nodoc:
384
+ column = column_for(table_name, column_name)
385
+ alter_table(table_name, rename: { column.name => new_column_name.to_s })
386
+ rename_column_indexes(table_name, column.name, new_column_name)
387
+ end
388
+
389
+ def add_timestamps(table_name, **options)
390
+ options[:null] = false if options[:null].nil?
391
+
392
+ if !options.key?(:precision)
393
+ options[:precision] = 6
394
+ end
395
+
396
+ alter_table(table_name) do |definition|
397
+ definition.column :created_at, :datetime, **options
398
+ definition.column :updated_at, :datetime, **options
399
+ end
400
+ end
401
+
402
+ def add_reference(table_name, ref_name, **options) # :nodoc:
403
+ super(table_name, ref_name, type: :integer, **options)
404
+ end
405
+ alias :add_belongs_to :add_reference
406
+
407
+ FK_REGEX = /.*FOREIGN KEY\s+\("(\w+)"\)\s+REFERENCES\s+"(\w+)"\s+\("(\w+)"\)/
408
+ DEFERRABLE_REGEX = /DEFERRABLE INITIALLY (\w+)/
409
+ def foreign_keys(table_name)
410
+ # SQLite returns 1 row for each column of composite foreign keys.
411
+ fk_info = internal_exec_query("PRAGMA foreign_key_list(#{quote(table_name)})", "SCHEMA")
412
+ # Deferred or immediate foreign keys can only be seen in the CREATE TABLE sql
413
+ fk_defs = table_structure_sql(table_name)
414
+ .select do |column_string|
415
+ column_string.start_with?("CONSTRAINT") &&
416
+ column_string.include?("FOREIGN KEY")
417
+ end
418
+ .to_h do |fk_string|
419
+ _, from, table, to = fk_string.match(FK_REGEX).to_a
420
+ _, mode = fk_string.match(DEFERRABLE_REGEX).to_a
421
+ deferred = mode&.downcase&.to_sym || false
422
+ [[table, from, to], deferred]
423
+ end
424
+
425
+ grouped_fk = fk_info.group_by { |row| row["id"] }.values.each { |group| group.sort_by! { |row| row["seq"] } }
426
+ grouped_fk.map do |group|
427
+ row = group.first
428
+ options = {
429
+ on_delete: extract_foreign_key_action(row["on_delete"]),
430
+ on_update: extract_foreign_key_action(row["on_update"]),
431
+ deferrable: fk_defs[[row["table"], row["from"], row["to"]]]
432
+ }
433
+
434
+ if group.one?
435
+ options[:column] = row["from"]
436
+ options[:primary_key] = row["to"]
437
+ else
438
+ options[:column] = group.map { |row| row["from"] }
439
+ options[:primary_key] = group.map { |row| row["to"] }
440
+ end
441
+ ForeignKeyDefinition.new(table_name, row["table"], options)
442
+ end
443
+ end
444
+
445
+ def build_insert_sql(insert) # :nodoc:
446
+ sql = +"INSERT #{insert.into} #{insert.values_list}"
447
+
448
+ if insert.skip_duplicates?
449
+ sql << " ON CONFLICT #{insert.conflict_target} DO NOTHING"
450
+ elsif insert.update_duplicates?
451
+ sql << " ON CONFLICT #{insert.conflict_target} DO UPDATE SET "
452
+ if insert.raw_update_sql?
453
+ sql << insert.raw_update_sql
454
+ else
455
+ sql << insert.touch_model_timestamps_unless { |column| "#{column} IS excluded.#{column}" }
456
+ sql << insert.updatable_columns.map { |column| "#{column}=excluded.#{column}" }.join(",")
457
+ end
458
+ end
459
+
460
+ sql << " RETURNING #{insert.returning}" if insert.returning
461
+ sql
462
+ end
463
+
464
+ def shared_cache? # :nodoc:
465
+ @config.fetch(:flags, 0).anybits?(::SQLite3::Constants::Open::SHAREDCACHE)
466
+ end
467
+
468
+ def get_database_version # :nodoc:
469
+ SQLite3Adapter::Version.new(query_value("SELECT sqlite_version(*)", "SCHEMA"))
470
+ end
471
+
472
+ def check_version # :nodoc:
473
+ if database_version < "3.8.0"
474
+ raise "Your version of SQLite (#{database_version}) is too old. Active Record supports SQLite >= 3.8."
475
+ end
476
+ end
477
+
478
+ class SQLite3Integer < Type::Integer # :nodoc:
479
+ private
480
+ def _limit
481
+ # INTEGER storage class can be stored 8 bytes value.
482
+ # See https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes
483
+ limit || 8
484
+ end
485
+ end
486
+
487
+ ActiveRecord::Type.register(:integer, SQLite3Integer, adapter: :sqlite3)
488
+
489
+ class << self
490
+ private
491
+ def initialize_type_map(m)
492
+ super
493
+ register_class_with_limit m, %r(int)i, SQLite3Integer
494
+ end
495
+ end
496
+
497
+ TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
498
+ EXTENDED_TYPE_MAPS = Concurrent::Map.new
499
+
500
+ private
501
+ # See https://www.sqlite.org/limits.html,
502
+ # the default value is 999 when not configured.
503
+ def bind_params_length
504
+ 999
505
+ end
506
+
507
+ def table_structure(table_name)
508
+ structure = table_info(table_name)
509
+ raise ActiveRecord::StatementInvalid.new("Could not find table '#{table_name}'", connection_pool: @pool) if structure.empty?
510
+ table_structure_with_collation(table_name, structure)
511
+ end
512
+ alias column_definitions table_structure
513
+
514
+ def extract_value_from_default(default)
515
+ case default
516
+ when /^null$/i
517
+ nil
518
+ # Quoted types
519
+ when /^'([^|]*)'$/m
520
+ $1.gsub("''", "'")
521
+ # Quoted types
522
+ when /^"([^|]*)"$/m
523
+ $1.gsub('""', '"')
524
+ # Numeric types
525
+ when /\A-?\d+(\.\d*)?\z/
526
+ $&
527
+ # Binary columns
528
+ when /x'(.*)'/
529
+ [ $1 ].pack("H*")
530
+ else
531
+ # Anything else is blank or some function
532
+ # and we can't know the value of that, so return nil.
533
+ nil
534
+ end
535
+ end
536
+
537
+ def extract_default_function(default_value, default)
538
+ default if has_default_function?(default_value, default)
539
+ end
540
+
541
+ def has_default_function?(default_value, default)
542
+ !default_value && %r{\w+\(.*\)|CURRENT_TIME|CURRENT_DATE|CURRENT_TIMESTAMP|\|\|}.match?(default)
543
+ end
544
+
545
+ # See: https://www.sqlite.org/lang_altertable.html
546
+ # SQLite has an additional restriction on the ALTER TABLE statement
547
+ def invalid_alter_table_type?(type, options)
548
+ type == :primary_key || options[:primary_key] ||
549
+ options[:null] == false && options[:default].nil? ||
550
+ (type == :virtual && options[:stored])
551
+ end
552
+
553
+ def alter_table(
554
+ table_name,
555
+ foreign_keys = foreign_keys(table_name),
556
+ check_constraints = check_constraints(table_name),
557
+ **options
558
+ )
559
+ altered_table_name = "a#{table_name}"
560
+
561
+ caller = lambda do |definition|
562
+ rename = options[:rename] || {}
563
+ foreign_keys.each do |fk|
564
+ if column = rename[fk.options[:column]]
565
+ fk.options[:column] = column
566
+ end
567
+ to_table = strip_table_name_prefix_and_suffix(fk.to_table)
568
+ definition.foreign_key(to_table, **fk.options)
569
+ end
570
+
571
+ check_constraints.each do |chk|
572
+ definition.check_constraint(chk.expression, **chk.options)
573
+ end
574
+
575
+ yield definition if block_given?
576
+ end
577
+
578
+ transaction do
579
+ disable_referential_integrity do
580
+ move_table(table_name, altered_table_name, options.merge(temporary: true))
581
+ move_table(altered_table_name, table_name, &caller)
582
+ end
583
+ end
584
+ end
585
+
586
+ def move_table(from, to, options = {}, &block)
587
+ copy_table(from, to, options, &block)
588
+ drop_table(from)
589
+ end
590
+
591
+ def copy_table(from, to, options = {})
592
+ from_primary_key = primary_key(from)
593
+ options[:id] = false
594
+ create_table(to, **options) do |definition|
595
+ @definition = definition
596
+ if from_primary_key.is_a?(Array)
597
+ @definition.primary_keys from_primary_key
598
+ end
599
+
600
+ columns(from).each do |column|
601
+ column_name = options[:rename] ?
602
+ (options[:rename][column.name] ||
603
+ options[:rename][column.name.to_sym] ||
604
+ column.name) : column.name
605
+
606
+ column_options = {
607
+ limit: column.limit,
608
+ precision: column.precision,
609
+ scale: column.scale,
610
+ null: column.null,
611
+ collation: column.collation,
612
+ primary_key: column_name == from_primary_key
613
+ }
614
+
615
+ if column.virtual?
616
+ column_options[:as] = column.default_function
617
+ column_options[:stored] = column.virtual_stored?
618
+ column_options[:type] = column.type
619
+ elsif column.has_default?
620
+ type = lookup_cast_type_from_column(column)
621
+ default = type.deserialize(column.default)
622
+ default = -> { column.default_function } if default.nil?
623
+
624
+ unless column.auto_increment?
625
+ column_options[:default] = default
626
+ end
627
+ end
628
+
629
+ column_type = column.virtual? ? :virtual : (column.bigint? ? :bigint : column.type)
630
+ @definition.column(column_name, column_type, **column_options)
631
+ end
632
+
633
+ yield @definition if block_given?
634
+ end
635
+ copy_table_indexes(from, to, options[:rename] || {})
636
+
637
+ columns_to_copy = @definition.columns.reject { |col| col.options.key?(:as) }.map(&:name)
638
+ copy_table_contents(from, to,
639
+ columns_to_copy,
640
+ options[:rename] || {})
641
+ end
642
+
643
+ def copy_table_indexes(from, to, rename = {})
644
+ indexes(from).each do |index|
645
+ name = index.name
646
+ if to == "a#{from}"
647
+ name = "t#{name}"
648
+ elsif from == "a#{to}"
649
+ name = name[1..-1]
650
+ end
651
+
652
+ columns = index.columns
653
+ if columns.is_a?(Array)
654
+ to_column_names = columns(to).map(&:name)
655
+ columns = columns.map { |c| rename[c] || c }.select do |column|
656
+ to_column_names.include?(column)
657
+ end
658
+ end
659
+
660
+ unless columns.empty?
661
+ # index name can't be the same
662
+ options = { name: name.gsub(/(^|_)(#{from})_/, "\\1#{to}_"), internal: true }
663
+ options[:unique] = true if index.unique
664
+ options[:where] = index.where if index.where
665
+ options[:order] = index.orders if index.orders
666
+ add_index(to, columns, **options)
667
+ end
668
+ end
669
+ end
670
+
671
+ def copy_table_contents(from, to, columns, rename = {})
672
+ column_mappings = Hash[columns.map { |name| [name, name] }]
673
+ rename.each { |a| column_mappings[a.last] = a.first }
674
+ from_columns = columns(from).collect(&:name)
675
+ columns = columns.find_all { |col| from_columns.include?(column_mappings[col]) }
676
+ from_columns_to_copy = columns.map { |col| column_mappings[col] }
677
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ","
678
+ quoted_from_columns = from_columns_to_copy.map { |col| quote_column_name(col) } * ","
679
+
680
+ internal_exec_query("INSERT INTO #{quote_table_name(to)} (#{quoted_columns})
681
+ SELECT #{quoted_from_columns} FROM #{quote_table_name(from)}")
682
+ end
683
+
684
+ def translate_exception(exception, message:, sql:, binds:)
685
+ # SQLite 3.8.2 returns a newly formatted error message:
686
+ # UNIQUE constraint failed: *table_name*.*column_name*
687
+ # Older versions of SQLite return:
688
+ # column *column_name* is not unique
689
+ if exception.message.match?(/(column(s)? .* (is|are) not unique|UNIQUE constraint failed: .*)/i)
690
+ RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool)
691
+ elsif exception.message.match?(/(.* may not be NULL|NOT NULL constraint failed: .*)/i)
692
+ NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
693
+ elsif exception.message.match?(/FOREIGN KEY constraint failed/i)
694
+ InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool)
695
+ elsif exception.message.match?(/called on a closed database/i)
696
+ ConnectionNotEstablished.new(exception, connection_pool: @pool)
697
+ elsif exception.is_a?(::SQLite3::BusyException)
698
+ StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool)
699
+ else
700
+ super
701
+ end
702
+ end
703
+
704
+ COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i
705
+ PRIMARY_KEY_AUTOINCREMENT_REGEX = /.*"(\w+)".+PRIMARY KEY AUTOINCREMENT/i
706
+ GENERATED_ALWAYS_AS_REGEX = /.*"(\w+)".+GENERATED ALWAYS AS \((.+)\) (?:STORED|VIRTUAL)/i
707
+
708
+ def table_structure_with_collation(table_name, basic_structure)
709
+ collation_hash = {}
710
+ auto_increments = {}
711
+ generated_columns = {}
712
+
713
+ column_strings = table_structure_sql(table_name, basic_structure.map { |column| column["name"] })
714
+
715
+ if column_strings.any?
716
+ column_strings.each do |column_string|
717
+ # This regex will match the column name and collation type and will save
718
+ # the value in $1 and $2 respectively.
719
+ collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
720
+ auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
721
+ generated_columns[$1] = $2 if GENERATED_ALWAYS_AS_REGEX =~ column_string
722
+ end
723
+
724
+ basic_structure.map do |column|
725
+ column = column.to_h
726
+
727
+ column_name = column["name"]
728
+
729
+ if collation_hash.has_key? column_name
730
+ column["collation"] = collation_hash[column_name]
731
+ end
732
+
733
+ if auto_increments.has_key?(column_name)
734
+ column["auto_increment"] = true
735
+ end
736
+
737
+ if generated_columns.has_key?(column_name)
738
+ column["dflt_value"] = generated_columns[column_name]
739
+ end
740
+
741
+ column
742
+ end
743
+ else
744
+ basic_structure.to_a
745
+ end
746
+ end
747
+
748
+ UNQUOTED_OPEN_PARENS_REGEX = /\((?![^'"]*['"][^'"]*$)/
749
+ FINAL_CLOSE_PARENS_REGEX = /\);*\z/
750
+
751
+ def table_structure_sql(table_name, column_names = nil)
752
+ unless column_names
753
+ column_info = table_info(table_name)
754
+ column_names = column_info.map { |column| column["name"] }
755
+ end
756
+
757
+ sql = <<~SQL
758
+ SELECT sql FROM
759
+ (SELECT * FROM sqlite_master UNION ALL
760
+ SELECT * FROM sqlite_temp_master)
761
+ WHERE type = 'table' AND name = #{quote(table_name)}
762
+ SQL
763
+
764
+ # Result will have following sample string
765
+ # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
766
+ # "password_digest" varchar COLLATE "NOCASE",
767
+ # "o_id" integer,
768
+ # CONSTRAINT "fk_rails_78146ddd2e" FOREIGN KEY ("o_id") REFERENCES "os" ("id"));
769
+ result = query_value(sql, "SCHEMA")
770
+
771
+ return [] unless result
772
+
773
+ # Splitting with left parentheses and discarding the first part will return all
774
+ # columns separated with comma(,).
775
+ result.partition(UNQUOTED_OPEN_PARENS_REGEX)
776
+ .last
777
+ .sub(FINAL_CLOSE_PARENS_REGEX, "")
778
+ # column definitions can have a comma in them, so split on commas followed
779
+ # by a space and a column name in quotes or followed by the keyword CONSTRAINT
780
+ .split(/,(?=\s(?:CONSTRAINT|"(?:#{Regexp.union(column_names).source})"))/i)
781
+ .map(&:strip)
782
+ end
783
+
784
+ def table_info(table_name)
785
+ if supports_virtual_columns?
786
+ internal_exec_query("PRAGMA table_xinfo(#{quote_table_name(table_name)})", "SCHEMA")
787
+ else
788
+ internal_exec_query("PRAGMA table_info(#{quote_table_name(table_name)})", "SCHEMA")
789
+ end
790
+ end
791
+
792
+ def arel_visitor
793
+ Arel::Visitors::SQLite.new(self)
794
+ end
795
+
796
+ def build_statement_pool
797
+ StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
798
+ end
799
+
800
+ def connect
801
+ @raw_connection = self.class.new_client(@connection_parameters)
802
+ rescue ConnectionNotEstablished => ex
803
+ raise ex.set_pool(@pool)
804
+ end
805
+
806
+ def reconnect
807
+ if active?
808
+ @raw_connection.rollback rescue nil
809
+ else
810
+ connect
811
+ end
812
+ end
813
+
814
+ def configure_connection
815
+ if @config[:timeout] && @config[:retries]
816
+ raise ArgumentError, "Cannot specify both timeout and retries arguments"
817
+ elsif @config[:timeout]
818
+ timeout = self.class.type_cast_config_to_integer(@config[:timeout])
819
+ raise TypeError, "timeout must be integer, not #{timeout}" unless timeout.is_a?(Integer)
820
+ @raw_connection.busy_handler_timeout = timeout
821
+ elsif @config[:retries]
822
+ ActiveRecord.deprecator.warn(<<~MSG)
823
+ The retries option is deprecated and will be removed in Rails 8.1. Use timeout instead.
824
+ MSG
825
+ retries = self.class.type_cast_config_to_integer(@config[:retries])
826
+ raw_connection.busy_handler { |count| count <= retries }
827
+ end
828
+
829
+ super
830
+
831
+ pragmas = @config.fetch(:pragmas, {}).stringify_keys
832
+ DEFAULT_PRAGMAS.merge(pragmas).each do |pragma, value|
833
+ if ::SQLite3::Pragmas.method_defined?("#{pragma}=")
834
+ @raw_connection.public_send("#{pragma}=", value)
835
+ else
836
+ warn "Unknown SQLite pragma: #{pragma}"
837
+ end
838
+ end
839
+ end
840
+ end
841
+ ActiveSupport.run_load_hooks(:active_record_sqlite3adapter, SQLite3Adapter)
842
+ end
843
+ end