omg-activerecord 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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,1883 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/access"
4
+ require "openssl"
5
+
6
+ module ActiveRecord
7
+ module ConnectionAdapters # :nodoc:
8
+ module SchemaStatements
9
+ include ActiveRecord::Migration::JoinTable
10
+
11
+ # Returns a hash of mappings from the abstract data types to the native
12
+ # database types. See TableDefinition#column for details on the recognized
13
+ # abstract data types.
14
+ def native_database_types
15
+ {}
16
+ end
17
+
18
+ def table_options(table_name)
19
+ nil
20
+ end
21
+
22
+ # Returns the table comment that's stored in database metadata.
23
+ def table_comment(table_name)
24
+ nil
25
+ end
26
+
27
+ # Truncates a table alias according to the limits of the current adapter.
28
+ def table_alias_for(table_name)
29
+ table_name[0...table_alias_length].tr(".", "_")
30
+ end
31
+
32
+ # Returns the relation names usable to back Active Record models.
33
+ # For most adapters this means all #tables and #views.
34
+ def data_sources
35
+ query_values(data_source_sql, "SCHEMA")
36
+ rescue NotImplementedError
37
+ tables | views
38
+ end
39
+
40
+ # Checks to see if the data source +name+ exists on the database.
41
+ #
42
+ # data_source_exists?(:ebooks)
43
+ #
44
+ def data_source_exists?(name)
45
+ query_values(data_source_sql(name), "SCHEMA").any? if name.present?
46
+ rescue NotImplementedError
47
+ data_sources.include?(name.to_s)
48
+ end
49
+
50
+ # Returns an array of table names defined in the database.
51
+ def tables
52
+ query_values(data_source_sql(type: "BASE TABLE"), "SCHEMA")
53
+ end
54
+
55
+ # Checks to see if the table +table_name+ exists on the database.
56
+ #
57
+ # table_exists?(:developers)
58
+ #
59
+ def table_exists?(table_name)
60
+ query_values(data_source_sql(table_name, type: "BASE TABLE"), "SCHEMA").any? if table_name.present?
61
+ rescue NotImplementedError
62
+ tables.include?(table_name.to_s)
63
+ end
64
+
65
+ # Returns an array of view names defined in the database.
66
+ def views
67
+ query_values(data_source_sql(type: "VIEW"), "SCHEMA")
68
+ end
69
+
70
+ # Checks to see if the view +view_name+ exists on the database.
71
+ #
72
+ # view_exists?(:ebooks)
73
+ #
74
+ def view_exists?(view_name)
75
+ query_values(data_source_sql(view_name, type: "VIEW"), "SCHEMA").any? if view_name.present?
76
+ rescue NotImplementedError
77
+ views.include?(view_name.to_s)
78
+ end
79
+
80
+ # Returns an array of indexes for the given table.
81
+ def indexes(table_name)
82
+ raise NotImplementedError, "#indexes is not implemented"
83
+ end
84
+
85
+ # Checks to see if an index exists on a table for a given index definition.
86
+ #
87
+ # # Check an index exists
88
+ # index_exists?(:suppliers, :company_id)
89
+ #
90
+ # # Check an index on multiple columns exists
91
+ # index_exists?(:suppliers, [:company_id, :company_type])
92
+ #
93
+ # # Check a unique index exists
94
+ # index_exists?(:suppliers, :company_id, unique: true)
95
+ #
96
+ # # Check an index with a custom name exists
97
+ # index_exists?(:suppliers, :company_id, name: "idx_company_id")
98
+ #
99
+ # # Check a valid index exists (PostgreSQL only)
100
+ # index_exists?(:suppliers, :company_id, valid: true)
101
+ #
102
+ def index_exists?(table_name, column_name, **options)
103
+ indexes(table_name).any? { |i| i.defined_for?(column_name, **options) }
104
+ end
105
+
106
+ # Returns an array of +Column+ objects for the table specified by +table_name+.
107
+ def columns(table_name)
108
+ table_name = table_name.to_s
109
+ definitions = column_definitions(table_name)
110
+ definitions.map do |field|
111
+ new_column_from_field(table_name, field, definitions)
112
+ end
113
+ end
114
+
115
+ # Checks to see if a column exists in a given table.
116
+ #
117
+ # # Check a column exists
118
+ # column_exists?(:suppliers, :name)
119
+ #
120
+ # # Check a column exists of a particular type
121
+ # #
122
+ # # This works for standard non-casted types (eg. string) but is unreliable
123
+ # # for types that may get cast to something else (eg. char, bigint).
124
+ # column_exists?(:suppliers, :name, :string)
125
+ #
126
+ # # Check a column exists with a specific definition
127
+ # column_exists?(:suppliers, :name, :string, limit: 100)
128
+ # column_exists?(:suppliers, :name, :string, default: 'default')
129
+ # column_exists?(:suppliers, :name, :string, null: false)
130
+ # column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
131
+ #
132
+ def column_exists?(table_name, column_name, type = nil, **options)
133
+ column_name = column_name.to_s
134
+ checks = []
135
+ checks << lambda { |c| c.name == column_name }
136
+ checks << lambda { |c| c.type == type.to_sym rescue nil } if type
137
+ column_options_keys.each do |attr|
138
+ checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr)
139
+ end
140
+
141
+ columns(table_name).any? { |c| checks.all? { |check| check[c] } }
142
+ end
143
+
144
+ # Returns just a table's primary key
145
+ def primary_key(table_name)
146
+ pk = primary_keys(table_name)
147
+ pk = pk.first unless pk.size > 1
148
+ pk
149
+ end
150
+
151
+ # Creates a new table with the name +table_name+. +table_name+ may either
152
+ # be a String or a Symbol.
153
+ #
154
+ # There are two ways to work with #create_table. You can use the block
155
+ # form or the regular form, like this:
156
+ #
157
+ # === Block form
158
+ #
159
+ # # create_table() passes a TableDefinition object to the block.
160
+ # # This form will not only create the table, but also columns for the
161
+ # # table.
162
+ #
163
+ # create_table(:suppliers) do |t|
164
+ # t.column :name, :string, limit: 60
165
+ # # Other fields here
166
+ # end
167
+ #
168
+ # === Block form, with shorthand
169
+ #
170
+ # # You can also use the column types as method calls, rather than calling the column method.
171
+ # create_table(:suppliers) do |t|
172
+ # t.string :name, limit: 60
173
+ # # Other fields here
174
+ # end
175
+ #
176
+ # === Regular form
177
+ #
178
+ # # Creates a table called 'suppliers' with no columns.
179
+ # create_table(:suppliers)
180
+ # # Add a column to 'suppliers'.
181
+ # add_column(:suppliers, :name, :string, {limit: 60})
182
+ #
183
+ # The +options+ hash can include the following keys:
184
+ # [<tt>:id</tt>]
185
+ # Whether to automatically add a primary key column. Defaults to true.
186
+ # Join tables for {ActiveRecord::Base.has_and_belongs_to_many}[rdoc-ref:Associations::ClassMethods#has_and_belongs_to_many] should set it to false.
187
+ #
188
+ # A Symbol can be used to specify the type of the generated primary key column.
189
+ # [<tt>:primary_key</tt>]
190
+ # The name of the primary key, if one is to be added automatically.
191
+ # Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
192
+ #
193
+ # If an array is passed, a composite primary key will be created.
194
+ #
195
+ # Note that Active Record models will automatically detect their
196
+ # primary key. This can be avoided by using
197
+ # {self.primary_key=}[rdoc-ref:AttributeMethods::PrimaryKey::ClassMethods#primary_key=] on the model
198
+ # to define the key explicitly.
199
+ #
200
+ # [<tt>:options</tt>]
201
+ # Any extra options you want appended to the table definition.
202
+ # [<tt>:temporary</tt>]
203
+ # Make a temporary table.
204
+ # [<tt>:force</tt>]
205
+ # Set to true to drop the table before creating it.
206
+ # Set to +:cascade+ to drop dependent objects as well.
207
+ # Defaults to false.
208
+ # [<tt>:if_not_exists</tt>]
209
+ # Set to true to avoid raising an error when the table already exists.
210
+ # Defaults to false.
211
+ # [<tt>:as</tt>]
212
+ # SQL to use to generate the table. When this option is used, the block is
213
+ # ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
214
+ #
215
+ # ====== Add a backend specific option to the generated SQL (MySQL)
216
+ #
217
+ # create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8mb4')
218
+ #
219
+ # generates:
220
+ #
221
+ # CREATE TABLE suppliers (
222
+ # id bigint auto_increment PRIMARY KEY
223
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
224
+ #
225
+ # ====== Rename the primary key column
226
+ #
227
+ # create_table(:objects, primary_key: 'guid') do |t|
228
+ # t.column :name, :string, limit: 80
229
+ # end
230
+ #
231
+ # generates:
232
+ #
233
+ # CREATE TABLE objects (
234
+ # guid bigint auto_increment PRIMARY KEY,
235
+ # name varchar(80)
236
+ # )
237
+ #
238
+ # ====== Change the primary key column type
239
+ #
240
+ # create_table(:tags, id: :string) do |t|
241
+ # t.column :label, :string
242
+ # end
243
+ #
244
+ # generates:
245
+ #
246
+ # CREATE TABLE tags (
247
+ # id varchar PRIMARY KEY,
248
+ # label varchar
249
+ # )
250
+ #
251
+ # ====== Create a composite primary key
252
+ #
253
+ # create_table(:orders, primary_key: [:product_id, :client_id]) do |t|
254
+ # t.belongs_to :product
255
+ # t.belongs_to :client
256
+ # end
257
+ #
258
+ # generates:
259
+ #
260
+ # CREATE TABLE orders (
261
+ # product_id bigint NOT NULL,
262
+ # client_id bigint NOT NULL
263
+ # );
264
+ #
265
+ # ALTER TABLE ONLY "orders"
266
+ # ADD CONSTRAINT orders_pkey PRIMARY KEY (product_id, client_id);
267
+ #
268
+ # ====== Do not add a primary key column
269
+ #
270
+ # create_table(:categories_suppliers, id: false) do |t|
271
+ # t.column :category_id, :bigint
272
+ # t.column :supplier_id, :bigint
273
+ # end
274
+ #
275
+ # generates:
276
+ #
277
+ # CREATE TABLE categories_suppliers (
278
+ # category_id bigint,
279
+ # supplier_id bigint
280
+ # )
281
+ #
282
+ # ====== Create a temporary table based on a query
283
+ #
284
+ # create_table(:long_query, temporary: true,
285
+ # as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
286
+ #
287
+ # generates:
288
+ #
289
+ # CREATE TEMPORARY TABLE long_query AS
290
+ # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
291
+ #
292
+ # See also TableDefinition#column for details on how to create columns.
293
+ def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
294
+ validate_create_table_options!(options)
295
+ validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
296
+
297
+ if force && options.key?(:if_not_exists)
298
+ raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
299
+ end
300
+
301
+ td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
302
+
303
+ if force
304
+ drop_table(table_name, force: force, if_exists: true)
305
+ else
306
+ schema_cache.clear_data_source_cache!(table_name.to_s)
307
+ end
308
+
309
+ result = execute schema_creation.accept(td)
310
+
311
+ unless supports_indexes_in_create?
312
+ td.indexes.each do |column_name, index_options|
313
+ add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
314
+ end
315
+ end
316
+
317
+ if supports_comments? && !supports_comments_in_create?
318
+ if table_comment = td.comment.presence
319
+ change_table_comment(table_name, table_comment)
320
+ end
321
+
322
+ td.columns.each do |column|
323
+ change_column_comment(table_name, column.name, column.comment) if column.comment.present?
324
+ end
325
+ end
326
+
327
+ result
328
+ end
329
+
330
+ # Returns a TableDefinition object containing information about the table that would be created
331
+ # if the same arguments were passed to #create_table. See #create_table for information about
332
+ # passing a +table_name+, and other additional options that can be passed.
333
+ def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
334
+ table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
335
+ table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
336
+
337
+ yield table_definition if block_given?
338
+
339
+ table_definition
340
+ end
341
+
342
+ # Creates a new join table with the name created using the lexical order of the first two
343
+ # arguments. These arguments can be a String or a Symbol.
344
+ #
345
+ # # Creates a table called 'assemblies_parts' with no id.
346
+ # create_join_table(:assemblies, :parts)
347
+ #
348
+ # # Creates a table called 'paper_boxes_papers' with no id.
349
+ # create_join_table('papers', 'paper_boxes')
350
+ #
351
+ # A duplicate prefix is combined into a single prefix. This is useful for
352
+ # namespaced models like Music::Artist and Music::Record:
353
+ #
354
+ # # Creates a table called 'music_artists_records' with no id.
355
+ # create_join_table('music_artists', 'music_records')
356
+ #
357
+ # You can pass an +options+ hash which can include the following keys:
358
+ # [<tt>:table_name</tt>]
359
+ # Sets the table name, overriding the default.
360
+ # [<tt>:column_options</tt>]
361
+ # Any extra options you want appended to the columns definition.
362
+ # [<tt>:options</tt>]
363
+ # Any extra options you want appended to the table definition.
364
+ # [<tt>:temporary</tt>]
365
+ # Make a temporary table.
366
+ # [<tt>:force</tt>]
367
+ # Set to true to drop the table before creating it.
368
+ # Defaults to false.
369
+ #
370
+ # Note that #create_join_table does not create any indices by default; you can use
371
+ # its block form to do so yourself:
372
+ #
373
+ # create_join_table :products, :categories do |t|
374
+ # t.index :product_id
375
+ # t.index :category_id
376
+ # end
377
+ #
378
+ # ====== Add a backend specific option to the generated SQL (MySQL)
379
+ #
380
+ # create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
381
+ #
382
+ # generates:
383
+ #
384
+ # CREATE TABLE assemblies_parts (
385
+ # assembly_id bigint NOT NULL,
386
+ # part_id bigint NOT NULL,
387
+ # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
388
+ #
389
+ def create_join_table(table_1, table_2, column_options: {}, **options)
390
+ join_table_name = find_join_table_name(table_1, table_2, options)
391
+
392
+ column_options.reverse_merge!(null: false, index: false)
393
+
394
+ t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
395
+
396
+ create_table(join_table_name, **options.merge!(id: false)) do |td|
397
+ td.references t1_ref, **column_options
398
+ td.references t2_ref, **column_options
399
+ yield td if block_given?
400
+ end
401
+ end
402
+
403
+ # Builds a TableDefinition object for a join table.
404
+ #
405
+ # This definition object contains information about the table that would be created
406
+ # if the same arguments were passed to #create_join_table. See #create_join_table for
407
+ # information about what arguments should be passed.
408
+ def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
409
+ join_table_name = find_join_table_name(table_1, table_2, options)
410
+ column_options.reverse_merge!(null: false, index: false)
411
+
412
+ t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
413
+
414
+ build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
415
+ td.references t1_ref, **column_options
416
+ td.references t2_ref, **column_options
417
+ yield td if block_given?
418
+ end
419
+ end
420
+
421
+ # Drops the join table specified by the given arguments.
422
+ # See #create_join_table and #drop_table for details.
423
+ #
424
+ # Although this command ignores the block if one is given, it can be helpful
425
+ # to provide one in a migration's +change+ method so it can be reverted.
426
+ # In that case, the block will be used by #create_join_table.
427
+ def drop_join_table(table_1, table_2, **options)
428
+ join_table_name = find_join_table_name(table_1, table_2, options)
429
+ drop_table(join_table_name, **options)
430
+ end
431
+
432
+ # A block for changing columns in +table+.
433
+ #
434
+ # # change_table() yields a Table instance
435
+ # change_table(:suppliers) do |t|
436
+ # t.column :name, :string, limit: 60
437
+ # # Other column alterations here
438
+ # end
439
+ #
440
+ # The +options+ hash can include the following keys:
441
+ # [<tt>:bulk</tt>]
442
+ # Set this to true to make this a bulk alter query, such as
443
+ #
444
+ # ALTER TABLE `users` ADD COLUMN age INT, ADD COLUMN birthdate DATETIME ...
445
+ #
446
+ # Defaults to false.
447
+ #
448
+ # Only supported on the MySQL and PostgreSQL adapter, ignored elsewhere.
449
+ #
450
+ # ====== Add a column
451
+ #
452
+ # change_table(:suppliers) do |t|
453
+ # t.column :name, :string, limit: 60
454
+ # end
455
+ #
456
+ # ====== Change type of a column
457
+ #
458
+ # change_table(:suppliers) do |t|
459
+ # t.change :metadata, :json
460
+ # end
461
+ #
462
+ # ====== Add 2 integer columns
463
+ #
464
+ # change_table(:suppliers) do |t|
465
+ # t.integer :width, :height, null: false, default: 0
466
+ # end
467
+ #
468
+ # ====== Add created_at/updated_at columns
469
+ #
470
+ # change_table(:suppliers) do |t|
471
+ # t.timestamps
472
+ # end
473
+ #
474
+ # ====== Add a foreign key column
475
+ #
476
+ # change_table(:suppliers) do |t|
477
+ # t.references :company
478
+ # end
479
+ #
480
+ # Creates a <tt>company_id(bigint)</tt> column.
481
+ #
482
+ # ====== Add a polymorphic foreign key column
483
+ #
484
+ # change_table(:suppliers) do |t|
485
+ # t.belongs_to :company, polymorphic: true
486
+ # end
487
+ #
488
+ # Creates <tt>company_type(varchar)</tt> and <tt>company_id(bigint)</tt> columns.
489
+ #
490
+ # ====== Remove a column
491
+ #
492
+ # change_table(:suppliers) do |t|
493
+ # t.remove :company
494
+ # end
495
+ #
496
+ # ====== Remove several columns
497
+ #
498
+ # change_table(:suppliers) do |t|
499
+ # t.remove :company_id
500
+ # t.remove :width, :height
501
+ # end
502
+ #
503
+ # ====== Remove an index
504
+ #
505
+ # change_table(:suppliers) do |t|
506
+ # t.remove_index :company_id
507
+ # end
508
+ #
509
+ # See also Table for details on all of the various column transformations.
510
+ def change_table(table_name, base = self, **options)
511
+ if supports_bulk_alter? && options[:bulk]
512
+ recorder = ActiveRecord::Migration::CommandRecorder.new(self)
513
+ yield update_table_definition(table_name, recorder)
514
+ bulk_change_table(table_name, recorder.commands)
515
+ else
516
+ yield update_table_definition(table_name, base)
517
+ end
518
+ end
519
+
520
+ # Renames a table.
521
+ #
522
+ # rename_table('octopuses', 'octopi')
523
+ #
524
+ def rename_table(table_name, new_name, **)
525
+ raise NotImplementedError, "rename_table is not implemented"
526
+ end
527
+
528
+ # Drops a table or tables from the database.
529
+ #
530
+ # [<tt>:force</tt>]
531
+ # Set to +:cascade+ to drop dependent objects as well.
532
+ # Defaults to false.
533
+ # [<tt>:if_exists</tt>]
534
+ # Set to +true+ to only drop the table if it exists.
535
+ # Defaults to false.
536
+ #
537
+ # Although this command ignores most +options+ and the block if one is given,
538
+ # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
539
+ # In that case, +options+ and the block will be used by #create_table except if you provide more than one table which is not supported.
540
+ def drop_table(*table_names, **options)
541
+ table_names.each do |table_name|
542
+ schema_cache.clear_data_source_cache!(table_name.to_s)
543
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
544
+ end
545
+ end
546
+
547
+ # Add a new +type+ column named +column_name+ to +table_name+.
548
+ #
549
+ # See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
550
+ #
551
+ # The +type+ parameter is normally one of the migrations native types,
552
+ # which is one of the following:
553
+ # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
554
+ # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
555
+ # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
556
+ # <tt>:binary</tt>, <tt>:blob</tt>, <tt>:boolean</tt>.
557
+ #
558
+ # You may use a type not in this list as long as it is supported by your
559
+ # database (for example, "polygon" in MySQL), but this will not be database
560
+ # agnostic and should usually be avoided.
561
+ #
562
+ # Available options are (none of these exists by default):
563
+ # * <tt>:comment</tt> -
564
+ # Specifies the comment for the column. This option is ignored by some backends.
565
+ # * <tt>:collation</tt> -
566
+ # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column.
567
+ # If not specified, the column will have the same collation as the table.
568
+ # * <tt>:default</tt> -
569
+ # The column's default value. Use +nil+ for +NULL+.
570
+ # * <tt>:limit</tt> -
571
+ # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
572
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, <tt>:blob</tt>, and <tt>:integer</tt> columns.
573
+ # This option is ignored by some backends.
574
+ # * <tt>:null</tt> -
575
+ # Allows or disallows +NULL+ values in the column.
576
+ # * <tt>:precision</tt> -
577
+ # Specifies the precision for the <tt>:decimal</tt>, <tt>:numeric</tt>,
578
+ # <tt>:datetime</tt>, and <tt>:time</tt> columns.
579
+ # * <tt>:scale</tt> -
580
+ # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
581
+ # * <tt>:if_not_exists</tt> -
582
+ # Specifies if the column already exists to not try to re-add it. This will avoid
583
+ # duplicate column errors.
584
+ #
585
+ # Note: The precision is the total number of significant digits,
586
+ # and the scale is the number of digits that can be stored following
587
+ # the decimal point. For example, the number 123.45 has a precision of 5
588
+ # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
589
+ # range from -999.99 to 999.99.
590
+ #
591
+ # Please be aware of different RDBMS implementations behavior with
592
+ # <tt>:decimal</tt> columns:
593
+ # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
594
+ # <tt>:precision</tt>, and makes no comments about the requirements of
595
+ # <tt>:precision</tt>.
596
+ # * MySQL: <tt>:precision</tt> [1..65], <tt>:scale</tt> [0..30].
597
+ # Default is (10,0).
598
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
599
+ # <tt>:scale</tt> [0..infinity]. No default.
600
+ # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
601
+ # but the maximum supported <tt>:precision</tt> is 16. No default.
602
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
603
+ # Default is (38,0).
604
+ # * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
605
+ # Default (38,0).
606
+ #
607
+ # == Examples
608
+ #
609
+ # add_column(:users, :picture, :binary, limit: 2.megabytes)
610
+ # # ALTER TABLE "users" ADD "picture" blob(2097152)
611
+ #
612
+ # add_column(:articles, :status, :string, limit: 20, default: 'draft', null: false)
613
+ # # ALTER TABLE "articles" ADD "status" varchar(20) DEFAULT 'draft' NOT NULL
614
+ #
615
+ # add_column(:answers, :bill_gates_money, :decimal, precision: 15, scale: 2)
616
+ # # ALTER TABLE "answers" ADD "bill_gates_money" decimal(15,2)
617
+ #
618
+ # add_column(:measurements, :sensor_reading, :decimal, precision: 30, scale: 20)
619
+ # # ALTER TABLE "measurements" ADD "sensor_reading" decimal(30,20)
620
+ #
621
+ # # While :scale defaults to zero on most databases, it
622
+ # # probably wouldn't hurt to include it.
623
+ # add_column(:measurements, :huge_integer, :decimal, precision: 30)
624
+ # # ALTER TABLE "measurements" ADD "huge_integer" decimal(30)
625
+ #
626
+ # # Defines a column that stores an array of a type.
627
+ # add_column(:users, :skills, :text, array: true)
628
+ # # ALTER TABLE "users" ADD "skills" text[]
629
+ #
630
+ # # Defines a column with a database-specific type.
631
+ # add_column(:shapes, :triangle, 'polygon')
632
+ # # ALTER TABLE "shapes" ADD "triangle" polygon
633
+ #
634
+ # # Ignores the method call if the column exists
635
+ # add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
636
+ def add_column(table_name, column_name, type, **options)
637
+ add_column_def = build_add_column_definition(table_name, column_name, type, **options)
638
+ return unless add_column_def
639
+
640
+ execute schema_creation.accept(add_column_def)
641
+ end
642
+
643
+ def add_columns(table_name, *column_names, type:, **options) # :nodoc:
644
+ column_names.each do |column_name|
645
+ add_column(table_name, column_name, type, **options)
646
+ end
647
+ end
648
+
649
+ # Builds an AlterTable object for adding a column to a table.
650
+ #
651
+ # This definition object contains information about the column that would be created
652
+ # if the same arguments were passed to #add_column. See #add_column for information about
653
+ # passing a +table_name+, +column_name+, +type+ and other options that can be passed.
654
+ def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
655
+ return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
656
+
657
+ if supports_datetime_with_precision?
658
+ if type == :datetime && !options.key?(:precision)
659
+ options[:precision] = 6
660
+ end
661
+ end
662
+
663
+ alter_table = create_alter_table(table_name)
664
+ alter_table.add_column(column_name, type, **options)
665
+ alter_table
666
+ end
667
+
668
+ # Removes the given columns from the table definition.
669
+ #
670
+ # remove_columns(:suppliers, :qualification, :experience)
671
+ #
672
+ # +type+ and other column options can be passed to make migration reversible.
673
+ #
674
+ # remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
675
+ def remove_columns(table_name, *column_names, type: nil, **options)
676
+ if column_names.empty?
677
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
678
+ end
679
+
680
+ remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
681
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(', ')}"
682
+ end
683
+
684
+ # Removes the column from the table definition.
685
+ #
686
+ # remove_column(:suppliers, :qualification)
687
+ #
688
+ # The +type+ and +options+ parameters will be ignored if present. It can be helpful
689
+ # to provide these in a migration's +change+ method so it can be reverted.
690
+ # In that case, +type+ and +options+ will be used by #add_column.
691
+ # Depending on the database you're using, indexes using this column may be
692
+ # automatically removed or modified to remove this column from the index.
693
+ #
694
+ # If the options provided include an +if_exists+ key, it will be used to check if the
695
+ # column does not exist. This will silently ignore the migration rather than raising
696
+ # if the column was already used.
697
+ #
698
+ # remove_column(:suppliers, :qualification, if_exists: true)
699
+ def remove_column(table_name, column_name, type = nil, **options)
700
+ return if options[:if_exists] == true && !column_exists?(table_name, column_name)
701
+
702
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
703
+ end
704
+
705
+ # Changes the column's definition according to the new options.
706
+ # See TableDefinition#column for details of the options you can use.
707
+ #
708
+ # change_column(:suppliers, :name, :string, limit: 80)
709
+ # change_column(:accounts, :description, :text)
710
+ #
711
+ def change_column(table_name, column_name, type, **options)
712
+ raise NotImplementedError, "change_column is not implemented"
713
+ end
714
+
715
+ # Sets a new default value for a column:
716
+ #
717
+ # change_column_default(:suppliers, :qualification, 'new')
718
+ # change_column_default(:accounts, :authorized, 1)
719
+ #
720
+ # Setting the default to +nil+ effectively drops the default:
721
+ #
722
+ # change_column_default(:users, :email, nil)
723
+ #
724
+ # Passing a hash containing +:from+ and +:to+ will make this change
725
+ # reversible in migration:
726
+ #
727
+ # change_column_default(:posts, :state, from: nil, to: "draft")
728
+ #
729
+ def change_column_default(table_name, column_name, default_or_changes)
730
+ raise NotImplementedError, "change_column_default is not implemented"
731
+ end
732
+
733
+ # Builds a ChangeColumnDefaultDefinition object.
734
+ #
735
+ # This definition object contains information about the column change that would occur
736
+ # if the same arguments were passed to #change_column_default. See #change_column_default for
737
+ # information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
738
+ def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
739
+ raise NotImplementedError, "build_change_column_default_definition is not implemented"
740
+ end
741
+
742
+ # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
743
+ # indicates whether the value can be +NULL+. For example
744
+ #
745
+ # change_column_null(:users, :nickname, false)
746
+ #
747
+ # says nicknames cannot be +NULL+ (adds the constraint), whereas
748
+ #
749
+ # change_column_null(:users, :nickname, true)
750
+ #
751
+ # allows them to be +NULL+ (drops the constraint).
752
+ #
753
+ # The method accepts an optional fourth argument to replace existing
754
+ # <tt>NULL</tt>s with some other value. Use that one when enabling the
755
+ # constraint if needed, since otherwise those rows would not be valid.
756
+ #
757
+ # Please note the fourth argument does not set a column's default.
758
+ def change_column_null(table_name, column_name, null, default = nil)
759
+ raise NotImplementedError, "change_column_null is not implemented"
760
+ end
761
+
762
+ # Renames a column.
763
+ #
764
+ # rename_column(:suppliers, :description, :name)
765
+ #
766
+ def rename_column(table_name, column_name, new_column_name)
767
+ raise NotImplementedError, "rename_column is not implemented"
768
+ end
769
+
770
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
771
+ # an Array of Symbols.
772
+ #
773
+ # The index will be named after the table and the column name(s), unless
774
+ # you pass <tt>:name</tt> as an option.
775
+ #
776
+ # ====== Creating a simple index
777
+ #
778
+ # add_index(:suppliers, :name)
779
+ #
780
+ # generates:
781
+ #
782
+ # CREATE INDEX index_suppliers_on_name ON suppliers(name)
783
+ #
784
+ # ====== Creating a index which already exists
785
+ #
786
+ # add_index(:suppliers, :name, if_not_exists: true)
787
+ #
788
+ # generates:
789
+ #
790
+ # CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)
791
+ #
792
+ # Note: Not supported by MySQL.
793
+ #
794
+ # ====== Creating a unique index
795
+ #
796
+ # add_index(:accounts, [:branch_id, :party_id], unique: true)
797
+ #
798
+ # generates:
799
+ #
800
+ # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
801
+ #
802
+ # ====== Creating a named index
803
+ #
804
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
805
+ #
806
+ # generates:
807
+ #
808
+ # CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
809
+ #
810
+ # ====== Creating an index with specific key length
811
+ #
812
+ # add_index(:accounts, :name, name: 'by_name', length: 10)
813
+ #
814
+ # generates:
815
+ #
816
+ # CREATE INDEX by_name ON accounts(name(10))
817
+ #
818
+ # ====== Creating an index with specific key lengths for multiple keys
819
+ #
820
+ # add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
821
+ #
822
+ # generates:
823
+ #
824
+ # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
825
+ #
826
+ # Note: only supported by MySQL
827
+ #
828
+ # ====== Creating an index with a sort order (desc or asc, asc is the default)
829
+ #
830
+ # add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})
831
+ #
832
+ # generates:
833
+ #
834
+ # CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
835
+ #
836
+ # Note: MySQL only supports index order from 8.0.1 onwards (earlier versions accepted the syntax but ignored it).
837
+ #
838
+ # ====== Creating a partial index
839
+ #
840
+ # add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
841
+ #
842
+ # generates:
843
+ #
844
+ # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
845
+ #
846
+ # Note: Partial indexes are only supported for PostgreSQL and SQLite.
847
+ #
848
+ # ====== Creating an index that includes additional columns
849
+ #
850
+ # add_index(:accounts, :branch_id, include: :party_id)
851
+ #
852
+ # generates:
853
+ #
854
+ # CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
855
+ #
856
+ # Note: only supported by PostgreSQL.
857
+ #
858
+ # ====== Creating an index with a specific method
859
+ #
860
+ # add_index(:developers, :name, using: 'btree')
861
+ #
862
+ # generates:
863
+ #
864
+ # CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
865
+ # CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
866
+ #
867
+ # Note: only supported by PostgreSQL and MySQL
868
+ #
869
+ # ====== Creating an index with a specific operator class
870
+ #
871
+ # add_index(:developers, :name, using: 'gist', opclass: :gist_trgm_ops)
872
+ # # CREATE INDEX developers_on_name ON developers USING gist (name gist_trgm_ops) -- PostgreSQL
873
+ #
874
+ # add_index(:developers, [:name, :city], using: 'gist', opclass: { city: :gist_trgm_ops })
875
+ # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name, city gist_trgm_ops) -- PostgreSQL
876
+ #
877
+ # add_index(:developers, [:name, :city], using: 'gist', opclass: :gist_trgm_ops)
878
+ # # CREATE INDEX developers_on_name_and_city ON developers USING gist (name gist_trgm_ops, city gist_trgm_ops) -- PostgreSQL
879
+ #
880
+ # Note: only supported by PostgreSQL
881
+ #
882
+ # ====== Creating an index with a specific type
883
+ #
884
+ # add_index(:developers, :name, type: :fulltext)
885
+ #
886
+ # generates:
887
+ #
888
+ # CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
889
+ #
890
+ # Note: only supported by MySQL.
891
+ #
892
+ # ====== Creating an index with a specific algorithm
893
+ #
894
+ # add_index(:developers, :name, algorithm: :concurrently)
895
+ # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL
896
+ #
897
+ # add_index(:developers, :name, algorithm: :inplace)
898
+ # # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
899
+ #
900
+ # Note: only supported by PostgreSQL and MySQL.
901
+ #
902
+ # Concurrently adding an index is not supported in a transaction.
903
+ #
904
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
905
+ def add_index(table_name, column_name, **options)
906
+ create_index = build_create_index_definition(table_name, column_name, **options)
907
+ execute schema_creation.accept(create_index)
908
+ end
909
+
910
+ # Builds a CreateIndexDefinition object.
911
+ #
912
+ # This definition object contains information about the index that would be created
913
+ # if the same arguments were passed to #add_index. See #add_index for information about
914
+ # passing a +table_name+, +column_name+, and other additional options that can be passed.
915
+ def build_create_index_definition(table_name, column_name, **options) # :nodoc:
916
+ index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
917
+ CreateIndexDefinition.new(index, algorithm, if_not_exists)
918
+ end
919
+
920
+ # Removes the given index from the table.
921
+ #
922
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
923
+ #
924
+ # remove_index :accounts, :branch_id
925
+ #
926
+ # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
927
+ #
928
+ # remove_index :accounts, column: :branch_id
929
+ #
930
+ # Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
931
+ #
932
+ # remove_index :accounts, column: [:branch_id, :party_id]
933
+ #
934
+ # Removes the index named +by_branch_party+ in the +accounts+ table.
935
+ #
936
+ # remove_index :accounts, name: :by_branch_party
937
+ #
938
+ # Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.
939
+ #
940
+ # remove_index :accounts, :branch_id, name: :by_branch_party
941
+ #
942
+ # Checks if the index exists before trying to remove it. Will silently ignore indexes that
943
+ # don't exist.
944
+ #
945
+ # remove_index :accounts, if_exists: true
946
+ #
947
+ # Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
948
+ #
949
+ # remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
950
+ #
951
+ # Note: only supported by PostgreSQL.
952
+ #
953
+ # Concurrently removing an index is not supported in a transaction.
954
+ #
955
+ # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
956
+ def remove_index(table_name, column_name = nil, **options)
957
+ return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
958
+
959
+ index_name = index_name_for_remove(table_name, column_name, options)
960
+
961
+ execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
962
+ end
963
+
964
+ # Renames an index.
965
+ #
966
+ # Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:
967
+ #
968
+ # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
969
+ #
970
+ def rename_index(table_name, old_name, new_name)
971
+ old_name = old_name.to_s
972
+ new_name = new_name.to_s
973
+ validate_index_length!(table_name, new_name)
974
+
975
+ # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
976
+ old_index_def = indexes(table_name).detect { |i| i.name == old_name }
977
+ return unless old_index_def
978
+ add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
979
+ remove_index(table_name, name: old_name)
980
+ end
981
+
982
+ def index_name(table_name, options) # :nodoc:
983
+ if Hash === options
984
+ if options[:column]
985
+ if options[:_uses_legacy_index_name]
986
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
987
+ else
988
+ generate_index_name(table_name, options[:column])
989
+ end
990
+ elsif options[:name]
991
+ options[:name]
992
+ else
993
+ raise ArgumentError, "You must specify the index name"
994
+ end
995
+ else
996
+ index_name(table_name, index_name_options(options))
997
+ end
998
+ end
999
+
1000
+ # Verifies the existence of an index with a given name.
1001
+ def index_name_exists?(table_name, index_name)
1002
+ index_name = index_name.to_s
1003
+ indexes(table_name).detect { |i| i.name == index_name }
1004
+ end
1005
+
1006
+ # Adds a reference. The reference column is a bigint by default,
1007
+ # the <tt>:type</tt> option can be used to specify a different type.
1008
+ # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
1009
+ #
1010
+ # The +options+ hash can include the following keys:
1011
+ # [<tt>:type</tt>]
1012
+ # The reference column type. Defaults to +:bigint+.
1013
+ # [<tt>:index</tt>]
1014
+ # Add an appropriate index. Defaults to true.
1015
+ # See #add_index for usage of this option.
1016
+ # [<tt>:foreign_key</tt>]
1017
+ # Add an appropriate foreign key constraint. Defaults to false, pass true
1018
+ # to add. In case the join table can't be inferred from the association
1019
+ # pass <tt>:to_table</tt> with the appropriate table name.
1020
+ # [<tt>:polymorphic</tt>]
1021
+ # Whether an additional +_type+ column should be added. Defaults to false.
1022
+ # [<tt>:null</tt>]
1023
+ # Whether the column allows nulls. Defaults to true.
1024
+ #
1025
+ # ====== Create a user_id bigint column without an index
1026
+ #
1027
+ # add_reference(:products, :user, index: false)
1028
+ #
1029
+ # ====== Create a user_id string column
1030
+ #
1031
+ # add_reference(:products, :user, type: :string)
1032
+ #
1033
+ # ====== Create supplier_id, supplier_type columns
1034
+ #
1035
+ # add_reference(:products, :supplier, polymorphic: true)
1036
+ #
1037
+ # ====== Create a supplier_id column with a unique index
1038
+ #
1039
+ # add_reference(:products, :supplier, index: { unique: true })
1040
+ #
1041
+ # ====== Create a supplier_id column with a named index
1042
+ #
1043
+ # add_reference(:products, :supplier, index: { name: "my_supplier_index" })
1044
+ #
1045
+ # ====== Create a supplier_id column and appropriate foreign key
1046
+ #
1047
+ # add_reference(:products, :supplier, foreign_key: true)
1048
+ #
1049
+ # ====== Create a supplier_id column and a foreign key to the firms table
1050
+ #
1051
+ # add_reference(:products, :supplier, foreign_key: { to_table: :firms })
1052
+ #
1053
+ def add_reference(table_name, ref_name, **options)
1054
+ ReferenceDefinition.new(ref_name, **options).add(table_name, self)
1055
+ end
1056
+ alias :add_belongs_to :add_reference
1057
+
1058
+ # Removes the reference(s). Also removes a +type+ column if one exists.
1059
+ #
1060
+ # ====== Remove the reference
1061
+ #
1062
+ # remove_reference(:products, :user, index: false)
1063
+ #
1064
+ # ====== Remove polymorphic reference
1065
+ #
1066
+ # remove_reference(:products, :supplier, polymorphic: true)
1067
+ #
1068
+ # ====== Remove the reference with a foreign key
1069
+ #
1070
+ # remove_reference(:products, :user, foreign_key: true)
1071
+ #
1072
+ def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
1073
+ conditional_options = options.slice(:if_exists, :if_not_exists)
1074
+
1075
+ if foreign_key
1076
+ reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
1077
+ if foreign_key.is_a?(Hash)
1078
+ foreign_key_options = foreign_key.merge(conditional_options)
1079
+ else
1080
+ foreign_key_options = { to_table: reference_name, **conditional_options }
1081
+ end
1082
+ foreign_key_options[:column] ||= "#{ref_name}_id"
1083
+ remove_foreign_key(table_name, **foreign_key_options)
1084
+ end
1085
+
1086
+ remove_column(table_name, "#{ref_name}_id", **conditional_options)
1087
+ remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
1088
+ end
1089
+ alias :remove_belongs_to :remove_reference
1090
+
1091
+ # Returns an array of foreign keys for the given table.
1092
+ # The foreign keys are represented as ForeignKeyDefinition objects.
1093
+ def foreign_keys(table_name)
1094
+ raise NotImplementedError, "foreign_keys is not implemented"
1095
+ end
1096
+
1097
+ # Adds a new foreign key. +from_table+ is the table with the key column,
1098
+ # +to_table+ contains the referenced primary key.
1099
+ #
1100
+ # The foreign key will be named after the following pattern: <tt>fk_rails_<identifier></tt>.
1101
+ # +identifier+ is a 10 character long string which is deterministically generated from the
1102
+ # +from_table+ and +column+. A custom name can be specified with the <tt>:name</tt> option.
1103
+ #
1104
+ # ====== Creating a simple foreign key
1105
+ #
1106
+ # add_foreign_key :articles, :authors
1107
+ #
1108
+ # generates:
1109
+ #
1110
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
1111
+ #
1112
+ # ====== Creating a foreign key, ignoring method call if the foreign key exists
1113
+ #
1114
+ # add_foreign_key(:articles, :authors, if_not_exists: true)
1115
+ #
1116
+ # ====== Creating a foreign key on a specific column
1117
+ #
1118
+ # add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
1119
+ #
1120
+ # generates:
1121
+ #
1122
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
1123
+ #
1124
+ # ====== Creating a composite foreign key
1125
+ #
1126
+ # Assuming "carts" table has "(shop_id, user_id)" as a primary key.
1127
+ #
1128
+ # add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
1129
+ #
1130
+ # generates:
1131
+ #
1132
+ # ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
1133
+ #
1134
+ # ====== Creating a cascading foreign key
1135
+ #
1136
+ # add_foreign_key :articles, :authors, on_delete: :cascade
1137
+ #
1138
+ # generates:
1139
+ #
1140
+ # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id") ON DELETE CASCADE
1141
+ #
1142
+ # The +options+ hash can include the following keys:
1143
+ # [<tt>:column</tt>]
1144
+ # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
1145
+ # Pass an array to create a composite foreign key.
1146
+ # [<tt>:primary_key</tt>]
1147
+ # The primary key column name on +to_table+. Defaults to +id+.
1148
+ # Pass an array to create a composite foreign key.
1149
+ # [<tt>:name</tt>]
1150
+ # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
1151
+ # [<tt>:on_delete</tt>]
1152
+ # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
1153
+ # [<tt>:on_update</tt>]
1154
+ # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
1155
+ # [<tt>:if_not_exists</tt>]
1156
+ # Specifies if the foreign key already exists to not try to re-add it. This will avoid
1157
+ # duplicate column errors.
1158
+ # [<tt>:validate</tt>]
1159
+ # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1160
+ # [<tt>:deferrable</tt>]
1161
+ # (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
1162
+ # +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
1163
+ def add_foreign_key(from_table, to_table, **options)
1164
+ return unless use_foreign_keys?
1165
+ return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
1166
+
1167
+ options = foreign_key_options(from_table, to_table, options)
1168
+ at = create_alter_table from_table
1169
+ at.add_foreign_key to_table, options
1170
+
1171
+ execute schema_creation.accept(at)
1172
+ end
1173
+
1174
+ # Removes the given foreign key from the table. Any option parameters provided
1175
+ # will be used to re-add the foreign key in case of a migration rollback.
1176
+ # It is recommended that you provide any options used when creating the foreign
1177
+ # key so that the migration can be reverted properly.
1178
+ #
1179
+ # Removes the foreign key on +accounts.branch_id+.
1180
+ #
1181
+ # remove_foreign_key :accounts, :branches
1182
+ #
1183
+ # Removes the foreign key on +accounts.owner_id+.
1184
+ #
1185
+ # remove_foreign_key :accounts, column: :owner_id
1186
+ #
1187
+ # Removes the foreign key on +accounts.owner_id+.
1188
+ #
1189
+ # remove_foreign_key :accounts, to_table: :owners
1190
+ #
1191
+ # Removes the foreign key named +special_fk_name+ on the +accounts+ table.
1192
+ #
1193
+ # remove_foreign_key :accounts, name: :special_fk_name
1194
+ #
1195
+ # Checks if the foreign key exists before trying to remove it. Will silently ignore indexes that
1196
+ # don't exist.
1197
+ #
1198
+ # remove_foreign_key :accounts, :branches, if_exists: true
1199
+ #
1200
+ # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
1201
+ # with an addition of
1202
+ # [<tt>:to_table</tt>]
1203
+ # The name of the table that contains the referenced primary key.
1204
+ def remove_foreign_key(from_table, to_table = nil, **options)
1205
+ return unless use_foreign_keys?
1206
+ return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
1207
+
1208
+ fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
1209
+
1210
+ at = create_alter_table from_table
1211
+ at.drop_foreign_key fk_name_to_delete
1212
+
1213
+ execute schema_creation.accept(at)
1214
+ end
1215
+
1216
+ # Checks to see if a foreign key exists on a table for a given foreign key definition.
1217
+ #
1218
+ # # Checks to see if a foreign key exists.
1219
+ # foreign_key_exists?(:accounts, :branches)
1220
+ #
1221
+ # # Checks to see if a foreign key on a specified column exists.
1222
+ # foreign_key_exists?(:accounts, column: :owner_id)
1223
+ #
1224
+ # # Checks to see if a foreign key with a custom name exists.
1225
+ # foreign_key_exists?(:accounts, name: "special_fk_name")
1226
+ #
1227
+ def foreign_key_exists?(from_table, to_table = nil, **options)
1228
+ foreign_key_for(from_table, to_table: to_table, **options).present?
1229
+ end
1230
+
1231
+ def foreign_key_column_for(table_name, column_name) # :nodoc:
1232
+ name = strip_table_name_prefix_and_suffix(table_name)
1233
+ "#{name.singularize}_#{column_name}"
1234
+ end
1235
+
1236
+ def foreign_key_options(from_table, to_table, options) # :nodoc:
1237
+ options = options.dup
1238
+
1239
+ if options[:primary_key].is_a?(Array)
1240
+ options[:column] ||= options[:primary_key].map do |pk_column|
1241
+ foreign_key_column_for(to_table, pk_column)
1242
+ end
1243
+ else
1244
+ options[:column] ||= foreign_key_column_for(to_table, "id")
1245
+ end
1246
+
1247
+ options[:name] ||= foreign_key_name(from_table, options)
1248
+
1249
+ if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
1250
+ if Array(options[:primary_key]).size != Array(options[:column]).size
1251
+ raise ArgumentError, <<~MSG.squish
1252
+ For composite primary keys, specify :column and :primary_key, where
1253
+ :column must reference all the :primary_key columns from #{to_table.inspect}
1254
+ MSG
1255
+ end
1256
+ end
1257
+
1258
+ options
1259
+ end
1260
+
1261
+ # Returns an array of check constraints for the given table.
1262
+ # The check constraints are represented as CheckConstraintDefinition objects.
1263
+ def check_constraints(table_name)
1264
+ raise NotImplementedError
1265
+ end
1266
+
1267
+ # Adds a new check constraint to the table. +expression+ is a String
1268
+ # representation of verifiable boolean condition.
1269
+ #
1270
+ # add_check_constraint :products, "price > 0", name: "price_check"
1271
+ #
1272
+ # generates:
1273
+ #
1274
+ # ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
1275
+ #
1276
+ # The +options+ hash can include the following keys:
1277
+ # [<tt>:name</tt>]
1278
+ # The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
1279
+ # [<tt>:if_not_exists</tt>]
1280
+ # Silently ignore if the constraint already exists, rather than raise an error.
1281
+ # [<tt>:validate</tt>]
1282
+ # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1283
+ def add_check_constraint(table_name, expression, if_not_exists: false, **options)
1284
+ return unless supports_check_constraints?
1285
+
1286
+ options = check_constraint_options(table_name, expression, options)
1287
+ return if if_not_exists && check_constraint_exists?(table_name, **options)
1288
+
1289
+ at = create_alter_table(table_name)
1290
+ at.add_check_constraint(expression, options)
1291
+
1292
+ execute schema_creation.accept(at)
1293
+ end
1294
+
1295
+ def check_constraint_options(table_name, expression, options) # :nodoc:
1296
+ options = options.dup
1297
+ options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
1298
+ options
1299
+ end
1300
+
1301
+ # Removes the given check constraint from the table. Removing a check constraint
1302
+ # that does not exist will raise an error.
1303
+ #
1304
+ # remove_check_constraint :products, name: "price_check"
1305
+ #
1306
+ # To silently ignore a non-existent check constraint rather than raise an error,
1307
+ # use the +if_exists+ option.
1308
+ #
1309
+ # remove_check_constraint :products, name: "price_check", if_exists: true
1310
+ #
1311
+ # The +expression+ parameter will be ignored if present. It can be helpful
1312
+ # to provide this in a migration's +change+ method so it can be reverted.
1313
+ # In that case, +expression+ will be used by #add_check_constraint.
1314
+ def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
1315
+ return unless supports_check_constraints?
1316
+
1317
+ return if if_exists && !check_constraint_exists?(table_name, **options)
1318
+
1319
+ chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
1320
+
1321
+ at = create_alter_table(table_name)
1322
+ at.drop_check_constraint(chk_name_to_delete)
1323
+
1324
+ execute schema_creation.accept(at)
1325
+ end
1326
+
1327
+
1328
+ # Checks to see if a check constraint exists on a table for a given check constraint definition.
1329
+ #
1330
+ # check_constraint_exists?(:products, name: "price_check")
1331
+ #
1332
+ def check_constraint_exists?(table_name, **options)
1333
+ if !options.key?(:name) && !options.key?(:expression)
1334
+ raise ArgumentError, "At least one of :name or :expression must be supplied"
1335
+ end
1336
+ check_constraint_for(table_name, **options).present?
1337
+ end
1338
+
1339
+ def dump_schema_information # :nodoc:
1340
+ versions = pool.schema_migration.versions
1341
+ insert_versions_sql(versions) if versions.any?
1342
+ end
1343
+
1344
+ def internal_string_options_for_primary_key # :nodoc:
1345
+ { primary_key: true }
1346
+ end
1347
+
1348
+ def assume_migrated_upto_version(version)
1349
+ version = version.to_i
1350
+ sm_table = quote_table_name(pool.schema_migration.table_name)
1351
+
1352
+ migration_context = pool.migration_context
1353
+ migrated = migration_context.get_all_versions
1354
+ versions = migration_context.migrations.map(&:version)
1355
+
1356
+ unless migrated.include?(version)
1357
+ execute "INSERT INTO #{sm_table} (version) VALUES (#{quote(version)})"
1358
+ end
1359
+
1360
+ inserting = (versions - migrated).select { |v| v < version }
1361
+ if inserting.any?
1362
+ if (duplicate = inserting.detect { |v| inserting.count(v) > 1 })
1363
+ raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
1364
+ end
1365
+ execute insert_versions_sql(inserting)
1366
+ end
1367
+ end
1368
+
1369
+ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) # :nodoc:
1370
+ type = type.to_sym if type
1371
+ if native = native_database_types[type]
1372
+ column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
1373
+
1374
+ if type == :decimal # ignore limit, use precision and scale
1375
+ scale ||= native[:scale]
1376
+
1377
+ if precision ||= native[:precision]
1378
+ if scale
1379
+ column_type_sql << "(#{precision},#{scale})"
1380
+ else
1381
+ column_type_sql << "(#{precision})"
1382
+ end
1383
+ elsif scale
1384
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
1385
+ end
1386
+
1387
+ elsif [:datetime, :timestamp, :time, :interval].include?(type) && precision ||= native[:precision]
1388
+ if (0..6) === precision
1389
+ column_type_sql << "(#{precision})"
1390
+ else
1391
+ raise ArgumentError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6"
1392
+ end
1393
+ elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
1394
+ column_type_sql << "(#{limit})"
1395
+ end
1396
+
1397
+ column_type_sql
1398
+ else
1399
+ type.to_s
1400
+ end
1401
+ end
1402
+
1403
+ # Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
1404
+ # PostgreSQL, MySQL, and Oracle override this for custom DISTINCT syntax - they
1405
+ # require the order columns appear in the SELECT.
1406
+ #
1407
+ # columns_for_distinct("posts.id", ["posts.created_at desc"])
1408
+ #
1409
+ def columns_for_distinct(columns, orders) # :nodoc:
1410
+ columns
1411
+ end
1412
+
1413
+ def distinct_relation_for_primary_key(relation) # :nodoc:
1414
+ primary_key_columns = Array(relation.primary_key).map do |column|
1415
+ visitor.compile(relation.table[column])
1416
+ end
1417
+
1418
+ values = columns_for_distinct(
1419
+ primary_key_columns,
1420
+ relation.order_values
1421
+ )
1422
+
1423
+ limited = relation.reselect(values).distinct!
1424
+ limited_ids = select_rows(limited.arel, "SQL").map do |results|
1425
+ results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
1426
+ end
1427
+
1428
+ if limited_ids.empty?
1429
+ relation.none!
1430
+ else
1431
+ relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
1432
+ end
1433
+
1434
+ relation.limit_value = relation.offset_value = nil
1435
+ relation
1436
+ end
1437
+
1438
+ # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
1439
+ # Additional options (like +:null+) are forwarded to #add_column.
1440
+ #
1441
+ # add_timestamps(:suppliers, null: true)
1442
+ #
1443
+ def add_timestamps(table_name, **options)
1444
+ fragments = add_timestamps_for_alter(table_name, **options)
1445
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
1446
+ end
1447
+
1448
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
1449
+ #
1450
+ # remove_timestamps(:suppliers)
1451
+ #
1452
+ def remove_timestamps(table_name, **options)
1453
+ remove_columns table_name, :updated_at, :created_at
1454
+ end
1455
+
1456
+ def update_table_definition(table_name, base) # :nodoc:
1457
+ Table.new(table_name, base)
1458
+ end
1459
+
1460
+ def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
1461
+ options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
1462
+
1463
+ column_names = index_column_names(column_name)
1464
+
1465
+ index_name = name&.to_s
1466
+ index_name ||= index_name(table_name, column_names)
1467
+
1468
+ validate_index_length!(table_name, index_name, internal)
1469
+
1470
+ index = IndexDefinition.new(
1471
+ table_name, index_name,
1472
+ options[:unique],
1473
+ column_names,
1474
+ lengths: options[:length] || {},
1475
+ orders: options[:order] || {},
1476
+ opclasses: options[:opclass] || {},
1477
+ where: options[:where],
1478
+ type: options[:type],
1479
+ using: options[:using],
1480
+ include: options[:include],
1481
+ nulls_not_distinct: options[:nulls_not_distinct],
1482
+ comment: options[:comment]
1483
+ )
1484
+
1485
+ [index, index_algorithm(options[:algorithm]), if_not_exists]
1486
+ end
1487
+
1488
+ def index_algorithm(algorithm) # :nodoc:
1489
+ index_algorithms.fetch(algorithm) do
1490
+ raise ArgumentError, "Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}"
1491
+ end if algorithm
1492
+ end
1493
+
1494
+ def quoted_columns_for_index(column_names, options) # :nodoc:
1495
+ quoted_columns = column_names.each_with_object({}) do |name, result|
1496
+ result[name.to_sym] = quote_column_name(name).dup
1497
+ end
1498
+ add_options_for_index_columns(quoted_columns, **options).values.join(", ")
1499
+ end
1500
+
1501
+ def options_include_default?(options)
1502
+ options.include?(:default) && !(options[:null] == false && options[:default].nil?)
1503
+ end
1504
+
1505
+ # Changes the comment for a table or removes it if +nil+.
1506
+ #
1507
+ # Passing a hash containing +:from+ and +:to+ will make this change
1508
+ # reversible in migration:
1509
+ #
1510
+ # change_table_comment(:posts, from: "old_comment", to: "new_comment")
1511
+ def change_table_comment(table_name, comment_or_changes)
1512
+ raise NotImplementedError, "#{self.class} does not support changing table comments"
1513
+ end
1514
+
1515
+ # Changes the comment for a column or removes it if +nil+.
1516
+ #
1517
+ # Passing a hash containing +:from+ and +:to+ will make this change
1518
+ # reversible in migration:
1519
+ #
1520
+ # change_column_comment(:posts, :state, from: "old_comment", to: "new_comment")
1521
+ def change_column_comment(table_name, column_name, comment_or_changes)
1522
+ raise NotImplementedError, "#{self.class} does not support changing column comments"
1523
+ end
1524
+
1525
+ def create_schema_dumper(options) # :nodoc:
1526
+ SchemaDumper.create(self, options)
1527
+ end
1528
+
1529
+ def use_foreign_keys?
1530
+ supports_foreign_keys? && foreign_keys_enabled?
1531
+ end
1532
+
1533
+ # Returns an instance of SchemaCreation, which can be used to visit a schema definition
1534
+ # object and return DDL.
1535
+ def schema_creation # :nodoc:
1536
+ SchemaCreation.new(self)
1537
+ end
1538
+
1539
+ def bulk_change_table(table_name, operations) # :nodoc:
1540
+ sql_fragments = []
1541
+ non_combinable_operations = []
1542
+
1543
+ operations.each do |command, args|
1544
+ table, arguments = args.shift, args
1545
+ method = :"#{command}_for_alter"
1546
+
1547
+ if respond_to?(method, true)
1548
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1549
+ sql_fragments.concat(sqls)
1550
+ non_combinable_operations.concat(procs)
1551
+ else
1552
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1553
+ non_combinable_operations.each(&:call)
1554
+ sql_fragments = []
1555
+ non_combinable_operations = []
1556
+ send(command, table, *arguments)
1557
+ end
1558
+ end
1559
+
1560
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1561
+ non_combinable_operations.each(&:call)
1562
+ end
1563
+
1564
+ def valid_table_definition_options # :nodoc:
1565
+ [:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
1566
+ end
1567
+
1568
+ def valid_column_definition_options # :nodoc:
1569
+ ColumnDefinition::OPTION_NAMES
1570
+ end
1571
+
1572
+ def valid_primary_key_options # :nodoc:
1573
+ [:limit, :default, :precision]
1574
+ end
1575
+
1576
+ # Returns the maximum length of an index name in bytes.
1577
+ def max_index_name_size
1578
+ 62
1579
+ end
1580
+
1581
+ private
1582
+ def generate_index_name(table_name, column)
1583
+ name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
1584
+ return name if name.bytesize <= max_index_name_size
1585
+
1586
+ # Fallback to short version, add hash to ensure uniqueness
1587
+ hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
1588
+ name = "idx_on_#{Array(column) * '_'}"
1589
+
1590
+ short_limit = max_index_name_size - hashed_identifier.bytesize
1591
+ short_name = name.mb_chars.limit(short_limit).to_s
1592
+
1593
+ "#{short_name}#{hashed_identifier}"
1594
+ end
1595
+
1596
+ def validate_change_column_null_argument!(value)
1597
+ unless value == true || value == false
1598
+ raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
1599
+ end
1600
+ end
1601
+
1602
+ def column_options_keys
1603
+ [:limit, :precision, :scale, :default, :null, :collation, :comment]
1604
+ end
1605
+
1606
+ def add_index_sort_order(quoted_columns, **options)
1607
+ orders = options_for_index_columns(options[:order])
1608
+ quoted_columns.each do |name, column|
1609
+ column << " #{orders[name].upcase}" if orders[name].present?
1610
+ end
1611
+ end
1612
+
1613
+ def options_for_index_columns(options)
1614
+ if options.is_a?(Hash)
1615
+ options.symbolize_keys
1616
+ else
1617
+ Hash.new { |hash, column| hash[column] = options }
1618
+ end
1619
+ end
1620
+
1621
+ # Overridden by the MySQL adapter for supporting index lengths and by
1622
+ # the PostgreSQL adapter for supporting operator classes.
1623
+ def add_options_for_index_columns(quoted_columns, **options)
1624
+ if supports_index_sort_order?
1625
+ quoted_columns = add_index_sort_order(quoted_columns, **options)
1626
+ end
1627
+
1628
+ quoted_columns
1629
+ end
1630
+
1631
+ def index_name_for_remove(table_name, column_name, options)
1632
+ return options[:name] if can_remove_index_by_name?(column_name, options)
1633
+
1634
+ checks = []
1635
+
1636
+ if !options.key?(:name) && expression_column_name?(column_name)
1637
+ options[:name] = index_name(table_name, column_name)
1638
+ column_names = []
1639
+ else
1640
+ column_names = index_column_names(column_name || options[:column])
1641
+ end
1642
+
1643
+ checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1644
+
1645
+ if column_names.present? && !(options.key?(:name) && expression_column_name?(column_names))
1646
+ checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
1647
+ end
1648
+
1649
+ raise ArgumentError, "No name or columns specified" if checks.none?
1650
+
1651
+ matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
1652
+
1653
+ if matching_indexes.count > 1
1654
+ raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
1655
+ "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1656
+ elsif matching_indexes.none?
1657
+ raise ArgumentError, "No indexes found on #{table_name} with the options provided."
1658
+ else
1659
+ matching_indexes.first.name
1660
+ end
1661
+ end
1662
+
1663
+ def rename_table_indexes(table_name, new_name, **options)
1664
+ indexes(new_name).each do |index|
1665
+ generated_index_name = index_name(table_name, column: index.columns, **options)
1666
+ if generated_index_name == index.name
1667
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns, **options)
1668
+ end
1669
+ end
1670
+ end
1671
+
1672
+ def rename_column_indexes(table_name, column_name, new_column_name)
1673
+ column_name, new_column_name = column_name.to_s, new_column_name.to_s
1674
+ indexes(table_name).each do |index|
1675
+ next unless index.columns.include?(new_column_name)
1676
+ old_columns = index.columns.dup
1677
+ old_columns[old_columns.index(new_column_name)] = column_name
1678
+ generated_index_name = index_name(table_name, column: old_columns)
1679
+ if generated_index_name == index.name
1680
+ rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
1681
+ end
1682
+ end
1683
+ end
1684
+
1685
+ def create_table_definition(name, **options)
1686
+ TableDefinition.new(self, name, **options)
1687
+ end
1688
+
1689
+ def create_alter_table(name)
1690
+ AlterTable.new create_table_definition(name)
1691
+ end
1692
+
1693
+ def validate_create_table_options!(options)
1694
+ unless options[:_skip_validate_options]
1695
+ options
1696
+ .except(:_uses_legacy_table_name, :_skip_validate_options)
1697
+ .assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
1698
+ end
1699
+ end
1700
+
1701
+ def fetch_type_metadata(sql_type)
1702
+ cast_type = lookup_cast_type(sql_type)
1703
+ SqlTypeMetadata.new(
1704
+ sql_type: sql_type,
1705
+ type: cast_type.type,
1706
+ limit: cast_type.limit,
1707
+ precision: cast_type.precision,
1708
+ scale: cast_type.scale,
1709
+ )
1710
+ end
1711
+
1712
+ def index_column_names(column_names)
1713
+ if expression_column_name?(column_names)
1714
+ column_names
1715
+ else
1716
+ Array(column_names)
1717
+ end
1718
+ end
1719
+
1720
+ def index_name_options(column_names)
1721
+ if expression_column_name?(column_names)
1722
+ column_names = column_names.scan(/\w+/).join("_")
1723
+ end
1724
+
1725
+ { column: column_names }
1726
+ end
1727
+
1728
+ # Try to identify whether the given column name is an expression
1729
+ def expression_column_name?(column_name)
1730
+ column_name.is_a?(String) && /\W/.match?(column_name)
1731
+ end
1732
+
1733
+ def strip_table_name_prefix_and_suffix(table_name)
1734
+ prefix = Base.table_name_prefix
1735
+ suffix = Base.table_name_suffix
1736
+ table_name.to_s =~ /#{prefix}(.+)#{suffix}/ ? $1 : table_name.to_s
1737
+ end
1738
+
1739
+ def foreign_key_name(table_name, options)
1740
+ options.fetch(:name) do
1741
+ columns = Array(options.fetch(:column)).map(&:to_s)
1742
+ identifier = "#{table_name}_#{columns * '_and_'}_fk"
1743
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1744
+
1745
+ "fk_rails_#{hashed_identifier}"
1746
+ end
1747
+ end
1748
+
1749
+ def foreign_key_for(from_table, **options)
1750
+ return unless use_foreign_keys?
1751
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
1752
+ end
1753
+
1754
+ def foreign_key_for!(from_table, to_table: nil, **options)
1755
+ foreign_key_for(from_table, to_table: to_table, **options) ||
1756
+ raise(ArgumentError, "Table '#{from_table}' has no foreign key for #{to_table || options}")
1757
+ end
1758
+
1759
+ def extract_foreign_key_action(specifier)
1760
+ case specifier
1761
+ when "CASCADE"; :cascade
1762
+ when "SET NULL"; :nullify
1763
+ when "RESTRICT"; :restrict
1764
+ end
1765
+ end
1766
+
1767
+ def foreign_keys_enabled?
1768
+ @config.fetch(:foreign_keys, true)
1769
+ end
1770
+
1771
+ def check_constraint_name(table_name, **options)
1772
+ options.fetch(:name) do
1773
+ expression = options.fetch(:expression)
1774
+ identifier = "#{table_name}_#{expression}_chk"
1775
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1776
+
1777
+ "chk_rails_#{hashed_identifier}"
1778
+ end
1779
+ end
1780
+
1781
+ def check_constraint_for(table_name, **options)
1782
+ return unless supports_check_constraints?
1783
+ chk_name = check_constraint_name(table_name, **options)
1784
+ check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
1785
+ end
1786
+
1787
+ def check_constraint_for!(table_name, expression: nil, **options)
1788
+ check_constraint_for(table_name, expression: expression, **options) ||
1789
+ raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
1790
+ end
1791
+
1792
+ def validate_index_length!(table_name, new_name, internal = false)
1793
+ if new_name.length > index_name_length
1794
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
1795
+ end
1796
+ end
1797
+
1798
+ def validate_table_length!(table_name)
1799
+ if table_name.length > table_name_length
1800
+ raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
1801
+ end
1802
+ end
1803
+
1804
+ def extract_new_default_value(default_or_changes)
1805
+ if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1806
+ default_or_changes[:to]
1807
+ else
1808
+ default_or_changes
1809
+ end
1810
+ end
1811
+ alias :extract_new_comment_value :extract_new_default_value
1812
+
1813
+ def can_remove_index_by_name?(column_name, options)
1814
+ column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
1815
+ end
1816
+
1817
+ def reference_name_for_table(table_name)
1818
+ table_name.to_s.singularize
1819
+ end
1820
+
1821
+ def add_column_for_alter(table_name, column_name, type, **options)
1822
+ td = create_table_definition(table_name)
1823
+ cd = td.new_column_definition(column_name, type, **options)
1824
+ schema_creation.accept(AddColumnDefinition.new(cd))
1825
+ end
1826
+
1827
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
1828
+ cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
1829
+ schema_creation.accept(cd)
1830
+ end
1831
+
1832
+ def rename_column_sql(table_name, column_name, new_column_name)
1833
+ "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1834
+ end
1835
+
1836
+ def remove_column_for_alter(table_name, column_name, type = nil, **options)
1837
+ "DROP COLUMN #{quote_column_name(column_name)}"
1838
+ end
1839
+
1840
+ def remove_columns_for_alter(table_name, *column_names, **options)
1841
+ column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
1842
+ end
1843
+
1844
+ def add_timestamps_for_alter(table_name, **options)
1845
+ options[:null] = false if options[:null].nil?
1846
+
1847
+ if !options.key?(:precision) && supports_datetime_with_precision?
1848
+ options[:precision] = 6
1849
+ end
1850
+
1851
+ [
1852
+ add_column_for_alter(table_name, :created_at, :datetime, **options),
1853
+ add_column_for_alter(table_name, :updated_at, :datetime, **options)
1854
+ ]
1855
+ end
1856
+
1857
+ def remove_timestamps_for_alter(table_name, **options)
1858
+ remove_columns_for_alter(table_name, :updated_at, :created_at)
1859
+ end
1860
+
1861
+ def insert_versions_sql(versions)
1862
+ sm_table = quote_table_name(pool.schema_migration.table_name)
1863
+
1864
+ if versions.is_a?(Array)
1865
+ sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1866
+ sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
1867
+ sql << ";"
1868
+ sql
1869
+ else
1870
+ "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"
1871
+ end
1872
+ end
1873
+
1874
+ def data_source_sql(name = nil, type: nil)
1875
+ raise NotImplementedError
1876
+ end
1877
+
1878
+ def quoted_scope(name = nil, type: nil)
1879
+ raise NotImplementedError
1880
+ end
1881
+ end
1882
+ end
1883
+ end