activerecord 6.0.0 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_record/migration/join_table"
4
3
  require "active_support/core_ext/string/access"
5
- require "active_support/deprecation"
6
- require "digest/sha2"
4
+ require "openssl"
7
5
 
8
6
  module ActiveRecord
9
7
  module ConnectionAdapters # :nodoc:
@@ -31,7 +29,7 @@ module ActiveRecord
31
29
  table_name[0...table_alias_length].tr(".", "_")
32
30
  end
33
31
 
34
- # Returns the relation names useable to back Active Record models.
32
+ # Returns the relation names usable to back Active Record models.
35
33
  # For most adapters this means all #tables and #views.
36
34
  def data_sources
37
35
  query_values(data_source_sql, "SCHEMA")
@@ -98,21 +96,19 @@ module ActiveRecord
98
96
  # # Check an index with a custom name exists
99
97
  # index_exists?(:suppliers, :company_id, name: "idx_company_id")
100
98
  #
101
- def index_exists?(table_name, column_name, options = {})
102
- column_names = Array(column_name).map(&:to_s)
103
- checks = []
104
- checks << lambda { |i| Array(i.columns) == column_names }
105
- checks << lambda { |i| i.unique } if options[:unique]
106
- checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
107
-
108
- indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
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) }
109
104
  end
110
105
 
111
106
  # Returns an array of +Column+ objects for the table specified by +table_name+.
112
107
  def columns(table_name)
113
108
  table_name = table_name.to_s
114
- column_definitions(table_name).map do |field|
115
- new_column_from_field(table_name, field)
109
+ definitions = column_definitions(table_name)
110
+ definitions.map do |field|
111
+ new_column_from_field(table_name, field, definitions)
116
112
  end
117
113
  end
118
114
 
@@ -122,6 +118,9 @@ module ActiveRecord
122
118
  # column_exists?(:suppliers, :name)
123
119
  #
124
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).
125
124
  # column_exists?(:suppliers, :name, :string)
126
125
  #
127
126
  # # Check a column exists with a specific definition
@@ -187,6 +186,9 @@ module ActiveRecord
187
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.
188
187
  #
189
188
  # A Symbol can be used to specify the type of the generated primary key column.
189
+ #
190
+ # A Hash can be used to specify the generated primary key column creation options.
191
+ # See {add_column}[rdoc-ref:ConnectionAdapters::SchemaStatements#add_column] for available options.
190
192
  # [<tt>:primary_key</tt>]
191
193
  # The name of the primary key, if one is to be added automatically.
192
194
  # Defaults to +id+. If <tt>:id</tt> is false, then this option is ignored.
@@ -258,7 +260,7 @@ module ActiveRecord
258
260
  #
259
261
  # generates:
260
262
  #
261
- # CREATE TABLE order (
263
+ # CREATE TABLE orders (
262
264
  # product_id bigint NOT NULL,
263
265
  # client_id bigint NOT NULL
264
266
  # );
@@ -291,37 +293,32 @@ module ActiveRecord
291
293
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
292
294
  #
293
295
  # See also TableDefinition#column for details on how to create columns.
294
- def create_table(table_name, **options)
295
- td = create_table_definition(table_name, options)
296
+ def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options, &block)
297
+ validate_create_table_options!(options)
298
+ validate_table_length!(table_name) unless options[:_uses_legacy_table_name]
296
299
 
297
- if options[:id] != false && !options[:as]
298
- pk = options.fetch(:primary_key) do
299
- Base.get_primary_key table_name.to_s.singularize
300
- end
301
-
302
- if pk.is_a?(Array)
303
- td.primary_keys pk
304
- else
305
- td.primary_key pk, options.fetch(:id, :primary_key), options.except(:comment)
306
- end
300
+ if force && options.key?(:if_not_exists)
301
+ raise ArgumentError, "Options `:force` and `:if_not_exists` cannot be used simultaneously."
307
302
  end
308
303
 
309
- yield td if block_given?
304
+ td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
310
305
 
311
- if options[:force]
312
- drop_table(table_name, options.merge(if_exists: true))
306
+ if force
307
+ drop_table(table_name, force: force, if_exists: true)
308
+ else
309
+ schema_cache.clear_data_source_cache!(table_name.to_s)
313
310
  end
314
311
 
315
- result = execute schema_creation.accept td
312
+ result = execute schema_creation.accept(td)
316
313
 
317
314
  unless supports_indexes_in_create?
318
315
  td.indexes.each do |column_name, index_options|
319
- add_index(table_name, column_name, index_options)
316
+ add_index(table_name, column_name, **index_options, if_not_exists: td.if_not_exists)
320
317
  end
321
318
  end
322
319
 
323
320
  if supports_comments? && !supports_comments_in_create?
324
- if table_comment = options[:comment].presence
321
+ if table_comment = td.comment.presence
325
322
  change_table_comment(table_name, table_comment)
326
323
  end
327
324
 
@@ -333,6 +330,18 @@ module ActiveRecord
333
330
  result
334
331
  end
335
332
 
333
+ # Returns a TableDefinition object containing information about the table that would be created
334
+ # if the same arguments were passed to #create_table. See #create_table for information about
335
+ # passing a +table_name+, and other additional options that can be passed.
336
+ def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
337
+ table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
338
+ table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
339
+
340
+ yield table_definition if block_given?
341
+
342
+ table_definition
343
+ end
344
+
336
345
  # Creates a new join table with the name created using the lexical order of the first two
337
346
  # arguments. These arguments can be a String or a Symbol.
338
347
  #
@@ -376,24 +385,42 @@ module ActiveRecord
376
385
 
377
386
  column_options.reverse_merge!(null: false, index: false)
378
387
 
379
- t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
388
+ t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
389
+
390
+ create_table(join_table_name, **options.merge!(id: false)) do |td|
391
+ td.references t1_ref, **column_options
392
+ td.references t2_ref, **column_options
393
+ yield td if block_given?
394
+ end
395
+ end
396
+
397
+ # Builds a TableDefinition object for a join table.
398
+ #
399
+ # This definition object contains information about the table that would be created
400
+ # if the same arguments were passed to #create_join_table. See #create_join_table for
401
+ # information about what arguments should be passed.
402
+ def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
403
+ join_table_name = find_join_table_name(table_1, table_2, options)
404
+ column_options.reverse_merge!(null: false, index: false)
405
+
406
+ t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
380
407
 
381
- create_table(join_table_name, options.merge!(id: false)) do |td|
382
- td.references t1_ref, column_options
383
- td.references t2_ref, column_options
408
+ build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
409
+ td.references t1_ref, **column_options
410
+ td.references t2_ref, **column_options
384
411
  yield td if block_given?
385
412
  end
386
413
  end
387
414
 
388
415
  # Drops the join table specified by the given arguments.
389
- # See #create_join_table for details.
416
+ # See #create_join_table and #drop_table for details.
390
417
  #
391
418
  # Although this command ignores the block if one is given, it can be helpful
392
419
  # to provide one in a migration's +change+ method so it can be reverted.
393
420
  # In that case, the block will be used by #create_join_table.
394
- def drop_join_table(table_1, table_2, options = {})
421
+ def drop_join_table(table_1, table_2, **options)
395
422
  join_table_name = find_join_table_name(table_1, table_2, options)
396
- drop_table(join_table_name)
423
+ drop_table(join_table_name, **options)
397
424
  end
398
425
 
399
426
  # A block for changing columns in +table+.
@@ -420,6 +447,12 @@ module ActiveRecord
420
447
  # t.column :name, :string, limit: 60
421
448
  # end
422
449
  #
450
+ # ====== Change type of a column
451
+ #
452
+ # change_table(:suppliers) do |t|
453
+ # t.change :metadata, :json
454
+ # end
455
+ #
423
456
  # ====== Add 2 integer columns
424
457
  #
425
458
  # change_table(:suppliers) do |t|
@@ -468,13 +501,13 @@ module ActiveRecord
468
501
  # end
469
502
  #
470
503
  # See also Table for details on all of the various column transformations.
471
- def change_table(table_name, options = {})
504
+ def change_table(table_name, base = self, **options)
472
505
  if supports_bulk_alter? && options[:bulk]
473
506
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
474
507
  yield update_table_definition(table_name, recorder)
475
508
  bulk_change_table(table_name, recorder.commands)
476
509
  else
477
- yield update_table_definition(table_name, self)
510
+ yield update_table_definition(table_name, base)
478
511
  end
479
512
  end
480
513
 
@@ -482,7 +515,7 @@ module ActiveRecord
482
515
  #
483
516
  # rename_table('octopuses', 'octopi')
484
517
  #
485
- def rename_table(table_name, new_name)
518
+ def rename_table(table_name, new_name, **)
486
519
  raise NotImplementedError, "rename_table is not implemented"
487
520
  end
488
521
 
@@ -498,30 +531,38 @@ module ActiveRecord
498
531
  # Although this command ignores most +options+ and the block if one is given,
499
532
  # it can be helpful to provide these in a migration's +change+ method so it can be reverted.
500
533
  # In that case, +options+ and the block will be used by #create_table.
501
- def drop_table(table_name, options = {})
534
+ def drop_table(table_name, **options)
535
+ schema_cache.clear_data_source_cache!(table_name.to_s)
502
536
  execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}"
503
537
  end
504
538
 
505
539
  # Add a new +type+ column named +column_name+ to +table_name+.
506
540
  #
541
+ # See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
542
+ #
507
543
  # The +type+ parameter is normally one of the migrations native types,
508
544
  # which is one of the following:
509
545
  # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
510
546
  # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
511
547
  # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
512
- # <tt>:binary</tt>, <tt>:boolean</tt>.
548
+ # <tt>:binary</tt>, <tt>:blob</tt>, <tt>:boolean</tt>.
513
549
  #
514
550
  # You may use a type not in this list as long as it is supported by your
515
551
  # database (for example, "polygon" in MySQL), but this will not be database
516
552
  # agnostic and should usually be avoided.
517
553
  #
518
554
  # Available options are (none of these exists by default):
555
+ # * <tt>:comment</tt> -
556
+ # Specifies the comment for the column. This option is ignored by some backends.
557
+ # * <tt>:collation</tt> -
558
+ # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column.
559
+ # If not specified, the column will have the same collation as the table.
560
+ # * <tt>:default</tt> -
561
+ # The column's default value. Use +nil+ for +NULL+.
519
562
  # * <tt>:limit</tt> -
520
563
  # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
521
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
564
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, <tt>:blob</tt>, and <tt>:integer</tt> columns.
522
565
  # This option is ignored by some backends.
523
- # * <tt>:default</tt> -
524
- # The column's default value. Use +nil+ for +NULL+.
525
566
  # * <tt>:null</tt> -
526
567
  # Allows or disallows +NULL+ values in the column.
527
568
  # * <tt>:precision</tt> -
@@ -529,11 +570,9 @@ module ActiveRecord
529
570
  # <tt>:datetime</tt>, and <tt>:time</tt> columns.
530
571
  # * <tt>:scale</tt> -
531
572
  # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
532
- # * <tt>:collation</tt> -
533
- # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
534
- # column will have the same collation as the table.
535
- # * <tt>:comment</tt> -
536
- # Specifies the comment for the column. This option is ignored by some backends.
573
+ # * <tt>:if_not_exists</tt> -
574
+ # Specifies if the column already exists to not try to re-add it. This will avoid
575
+ # duplicate column errors.
537
576
  #
538
577
  # Note: The precision is the total number of significant digits,
539
578
  # and the scale is the number of digits that can be stored following
@@ -546,7 +585,7 @@ module ActiveRecord
546
585
  # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
547
586
  # <tt>:precision</tt>, and makes no comments about the requirements of
548
587
  # <tt>:precision</tt>.
549
- # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
588
+ # * MySQL: <tt>:precision</tt> [1..65], <tt>:scale</tt> [0..30].
550
589
  # Default is (10,0).
551
590
  # * PostgreSQL: <tt>:precision</tt> [1..infinity],
552
591
  # <tt>:scale</tt> [0..infinity]. No default.
@@ -554,8 +593,6 @@ module ActiveRecord
554
593
  # but the maximum supported <tt>:precision</tt> is 16. No default.
555
594
  # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
556
595
  # Default is (38,0).
557
- # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
558
- # Default unknown.
559
596
  # * SqlServer: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
560
597
  # Default (38,0).
561
598
  #
@@ -585,21 +622,55 @@ module ActiveRecord
585
622
  # # Defines a column with a database-specific type.
586
623
  # add_column(:shapes, :triangle, 'polygon')
587
624
  # # ALTER TABLE "shapes" ADD "triangle" polygon
625
+ #
626
+ # # Ignores the method call if the column exists
627
+ # add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
588
628
  def add_column(table_name, column_name, type, **options)
589
- at = create_alter_table table_name
590
- at.add_column(column_name, type, options)
591
- execute schema_creation.accept at
629
+ add_column_def = build_add_column_definition(table_name, column_name, type, **options)
630
+ return unless add_column_def
631
+
632
+ execute schema_creation.accept(add_column_def)
633
+ end
634
+
635
+ def add_columns(table_name, *column_names, type:, **options) # :nodoc:
636
+ column_names.each do |column_name|
637
+ add_column(table_name, column_name, type, **options)
638
+ end
639
+ end
640
+
641
+ # Builds an AlterTable object for adding a column to a table.
642
+ #
643
+ # This definition object contains information about the column that would be created
644
+ # if the same arguments were passed to #add_column. See #add_column for information about
645
+ # passing a +table_name+, +column_name+, +type+ and other options that can be passed.
646
+ def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
647
+ return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
648
+
649
+ if supports_datetime_with_precision?
650
+ if type == :datetime && !options.key?(:precision)
651
+ options[:precision] = 6
652
+ end
653
+ end
654
+
655
+ alter_table = create_alter_table(table_name)
656
+ alter_table.add_column(column_name, type, **options)
657
+ alter_table
592
658
  end
593
659
 
594
660
  # Removes the given columns from the table definition.
595
661
  #
596
662
  # remove_columns(:suppliers, :qualification, :experience)
597
663
  #
598
- def remove_columns(table_name, *column_names)
599
- raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
600
- column_names.each do |column_name|
601
- remove_column(table_name, column_name)
664
+ # +type+ and other column options can be passed to make migration reversible.
665
+ #
666
+ # remove_columns(:suppliers, :qualification, :experience, type: :string, null: false)
667
+ def remove_columns(table_name, *column_names, type: nil, **options)
668
+ if column_names.empty?
669
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
602
670
  end
671
+
672
+ remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
673
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(', ')}"
603
674
  end
604
675
 
605
676
  # Removes the column from the table definition.
@@ -609,9 +680,18 @@ module ActiveRecord
609
680
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
610
681
  # to provide these in a migration's +change+ method so it can be reverted.
611
682
  # In that case, +type+ and +options+ will be used by #add_column.
612
- # Indexes on the column are automatically removed.
613
- def remove_column(table_name, column_name, type = nil, options = {})
614
- execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
683
+ # Depending on the database you're using, indexes using this column may be
684
+ # automatically removed or modified to remove this column from the index.
685
+ #
686
+ # If the options provided include an +if_exists+ key, it will be used to check if the
687
+ # column does not exist. This will silently ignore the migration rather than raising
688
+ # if the column was already removed.
689
+ #
690
+ # remove_column(:suppliers, :qualification, if_exists: true)
691
+ def remove_column(table_name, column_name, type = nil, **options)
692
+ return if options[:if_exists] == true && !column_exists?(table_name, column_name)
693
+
694
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, **options)}"
615
695
  end
616
696
 
617
697
  # Changes the column's definition according to the new options.
@@ -620,7 +700,7 @@ module ActiveRecord
620
700
  # change_column(:suppliers, :name, :string, limit: 80)
621
701
  # change_column(:accounts, :description, :text)
622
702
  #
623
- def change_column(table_name, column_name, type, options = {})
703
+ def change_column(table_name, column_name, type, **options)
624
704
  raise NotImplementedError, "change_column is not implemented"
625
705
  end
626
706
 
@@ -642,6 +722,15 @@ module ActiveRecord
642
722
  raise NotImplementedError, "change_column_default is not implemented"
643
723
  end
644
724
 
725
+ # Builds a ChangeColumnDefaultDefinition object.
726
+ #
727
+ # This definition object contains information about the column change that would occur
728
+ # if the same arguments were passed to #change_column_default. See #change_column_default for
729
+ # information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
730
+ def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
731
+ raise NotImplementedError, "build_change_column_default_definition is not implemented"
732
+ end
733
+
645
734
  # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
646
735
  # indicates whether the value can be +NULL+. For example
647
736
  #
@@ -682,7 +771,17 @@ module ActiveRecord
682
771
  #
683
772
  # generates:
684
773
  #
685
- # CREATE INDEX suppliers_name_index ON suppliers(name)
774
+ # CREATE INDEX index_suppliers_on_name ON suppliers(name)
775
+ #
776
+ # ====== Creating a index which already exists
777
+ #
778
+ # add_index(:suppliers, :name, if_not_exists: true)
779
+ #
780
+ # generates:
781
+ #
782
+ # CREATE INDEX IF NOT EXISTS index_suppliers_on_name ON suppliers(name)
783
+ #
784
+ # Note: Not supported by MySQL.
686
785
  #
687
786
  # ====== Creating a unique index
688
787
  #
@@ -690,7 +789,7 @@ module ActiveRecord
690
789
  #
691
790
  # generates:
692
791
  #
693
- # CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
792
+ # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id)
694
793
  #
695
794
  # ====== Creating a named index
696
795
  #
@@ -716,11 +815,11 @@ module ActiveRecord
716
815
  #
717
816
  # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
718
817
  #
719
- # Note: SQLite doesn't support index length.
818
+ # Note: only supported by MySQL
720
819
  #
721
820
  # ====== Creating an index with a sort order (desc or asc, asc is the default)
722
821
  #
723
- # add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
822
+ # add_index(:accounts, [:branch_id, :party_id, :surname], name: 'by_branch_desc_party', order: {branch_id: :desc, party_id: :asc})
724
823
  #
725
824
  # generates:
726
825
  #
@@ -736,7 +835,17 @@ module ActiveRecord
736
835
  #
737
836
  # CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
738
837
  #
739
- # Note: Partial indexes are only supported for PostgreSQL and SQLite 3.8.0+.
838
+ # Note: Partial indexes are only supported for PostgreSQL and SQLite.
839
+ #
840
+ # ====== Creating an index that includes additional columns
841
+ #
842
+ # add_index(:accounts, :branch_id, include: :party_id)
843
+ #
844
+ # generates:
845
+ #
846
+ # CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
847
+ #
848
+ # Note: only supported by PostgreSQL.
740
849
  #
741
850
  # ====== Creating an index with a specific method
742
851
  #
@@ -775,16 +884,29 @@ module ActiveRecord
775
884
  # ====== Creating an index with a specific algorithm
776
885
  #
777
886
  # add_index(:developers, :name, algorithm: :concurrently)
778
- # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name)
887
+ # # CREATE INDEX CONCURRENTLY developers_on_name on developers (name) -- PostgreSQL
779
888
  #
780
- # Note: only supported by PostgreSQL.
889
+ # add_index(:developers, :name, algorithm: :inplace)
890
+ # # CREATE INDEX `index_developers_on_name` ON `developers` (`name`) ALGORITHM = INPLACE -- MySQL
891
+ #
892
+ # Note: only supported by PostgreSQL and MySQL.
781
893
  #
782
894
  # Concurrently adding an index is not supported in a transaction.
783
895
  #
784
896
  # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
785
- def add_index(table_name, column_name, options = {})
786
- index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
787
- execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
897
+ def add_index(table_name, column_name, **options)
898
+ create_index = build_create_index_definition(table_name, column_name, **options)
899
+ execute schema_creation.accept(create_index)
900
+ end
901
+
902
+ # Builds a CreateIndexDefinition object.
903
+ #
904
+ # This definition object contains information about the index that would be created
905
+ # if the same arguments were passed to #add_index. See #add_index for information about
906
+ # passing a +table_name+, +column_name+, and other additional options that can be passed.
907
+ def build_create_index_definition(table_name, column_name, **options) # :nodoc:
908
+ index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
909
+ CreateIndexDefinition.new(index, algorithm, if_not_exists)
788
910
  end
789
911
 
790
912
  # Removes the given index from the table.
@@ -805,6 +927,15 @@ module ActiveRecord
805
927
  #
806
928
  # remove_index :accounts, name: :by_branch_party
807
929
  #
930
+ # Removes the index on +branch_id+ named +by_branch_party+ in the +accounts+ table.
931
+ #
932
+ # remove_index :accounts, :branch_id, name: :by_branch_party
933
+ #
934
+ # Checks if the index exists before trying to remove it. Will silently ignore indexes that
935
+ # don't exist.
936
+ #
937
+ # remove_index :accounts, if_exists: true
938
+ #
808
939
  # Removes the index named +by_branch_party+ in the +accounts+ table +concurrently+.
809
940
  #
810
941
  # remove_index :accounts, name: :by_branch_party, algorithm: :concurrently
@@ -814,8 +945,11 @@ module ActiveRecord
814
945
  # Concurrently removing an index is not supported in a transaction.
815
946
  #
816
947
  # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
817
- def remove_index(table_name, options = {})
818
- index_name = index_name_for_remove(table_name, options)
948
+ def remove_index(table_name, column_name = nil, **options)
949
+ return if options[:if_exists] && !index_exists?(table_name, column_name, **options)
950
+
951
+ index_name = index_name_for_remove(table_name, column_name, options)
952
+
819
953
  execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
820
954
  end
821
955
 
@@ -826,6 +960,8 @@ module ActiveRecord
826
960
  # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
827
961
  #
828
962
  def rename_index(table_name, old_name, new_name)
963
+ old_name = old_name.to_s
964
+ new_name = new_name.to_s
829
965
  validate_index_length!(table_name, new_name)
830
966
 
831
967
  # this is a naive implementation; some DBs may support this more efficiently (PostgreSQL, for instance)
@@ -835,10 +971,14 @@ module ActiveRecord
835
971
  remove_index(table_name, name: old_name)
836
972
  end
837
973
 
838
- def index_name(table_name, options) #:nodoc:
974
+ def index_name(table_name, options) # :nodoc:
839
975
  if Hash === options
840
976
  if options[:column]
841
- "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
977
+ if options[:_uses_legacy_index_name]
978
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
979
+ else
980
+ generate_index_name(table_name, options[:column])
981
+ end
842
982
  elsif options[:name]
843
983
  options[:name]
844
984
  else
@@ -858,7 +998,6 @@ module ActiveRecord
858
998
  # Adds a reference. The reference column is a bigint by default,
859
999
  # the <tt>:type</tt> option can be used to specify a different type.
860
1000
  # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
861
- # #add_reference and #add_belongs_to are acceptable.
862
1001
  #
863
1002
  # The +options+ hash can include the following keys:
864
1003
  # [<tt>:type</tt>]
@@ -867,7 +1006,9 @@ module ActiveRecord
867
1006
  # Add an appropriate index. Defaults to true.
868
1007
  # See #add_index for usage of this option.
869
1008
  # [<tt>:foreign_key</tt>]
870
- # Add an appropriate foreign key constraint. Defaults to false.
1009
+ # Add an appropriate foreign key constraint. Defaults to false, pass true
1010
+ # to add. In case the join table can't be inferred from the association
1011
+ # pass <tt>:to_table</tt> with the appropriate table name.
871
1012
  # [<tt>:polymorphic</tt>]
872
1013
  # Whether an additional +_type+ column should be added. Defaults to false.
873
1014
  # [<tt>:null</tt>]
@@ -899,15 +1040,14 @@ module ActiveRecord
899
1040
  #
900
1041
  # ====== Create a supplier_id column and a foreign key to the firms table
901
1042
  #
902
- # add_reference(:products, :supplier, foreign_key: {to_table: :firms})
1043
+ # add_reference(:products, :supplier, foreign_key: { to_table: :firms })
903
1044
  #
904
1045
  def add_reference(table_name, ref_name, **options)
905
- ReferenceDefinition.new(ref_name, options).add_to(update_table_definition(table_name, self))
1046
+ ReferenceDefinition.new(ref_name, **options).add(table_name, self)
906
1047
  end
907
1048
  alias :add_belongs_to :add_reference
908
1049
 
909
1050
  # Removes the reference(s). Also removes a +type+ column if one exists.
910
- # #remove_reference and #remove_belongs_to are acceptable.
911
1051
  #
912
1052
  # ====== Remove the reference
913
1053
  #
@@ -922,19 +1062,21 @@ module ActiveRecord
922
1062
  # remove_reference(:products, :user, foreign_key: true)
923
1063
  #
924
1064
  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
1065
+ conditional_options = options.slice(:if_exists, :if_not_exists)
1066
+
925
1067
  if foreign_key
926
1068
  reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
927
1069
  if foreign_key.is_a?(Hash)
928
- foreign_key_options = foreign_key
1070
+ foreign_key_options = foreign_key.merge(conditional_options)
929
1071
  else
930
- foreign_key_options = { to_table: reference_name }
1072
+ foreign_key_options = { to_table: reference_name, **conditional_options }
931
1073
  end
932
1074
  foreign_key_options[:column] ||= "#{ref_name}_id"
933
- remove_foreign_key(table_name, foreign_key_options)
1075
+ remove_foreign_key(table_name, **foreign_key_options)
934
1076
  end
935
1077
 
936
- remove_column(table_name, "#{ref_name}_id")
937
- remove_column(table_name, "#{ref_name}_type") if polymorphic
1078
+ remove_column(table_name, "#{ref_name}_id", **conditional_options)
1079
+ remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
938
1080
  end
939
1081
  alias :remove_belongs_to :remove_reference
940
1082
 
@@ -959,6 +1101,10 @@ module ActiveRecord
959
1101
  #
960
1102
  # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
961
1103
  #
1104
+ # ====== Creating a foreign key, ignoring method call if the foreign key exists
1105
+ #
1106
+ # add_foreign_key(:articles, :authors, if_not_exists: true)
1107
+ #
962
1108
  # ====== Creating a foreign key on a specific column
963
1109
  #
964
1110
  # add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
@@ -967,6 +1113,16 @@ module ActiveRecord
967
1113
  #
968
1114
  # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
969
1115
  #
1116
+ # ====== Creating a composite foreign key
1117
+ #
1118
+ # Assuming "carts" table has "(shop_id, user_id)" as a primary key.
1119
+ #
1120
+ # add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
1121
+ #
1122
+ # generates:
1123
+ #
1124
+ # ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
1125
+ #
970
1126
  # ====== Creating a cascading foreign key
971
1127
  #
972
1128
  # add_foreign_key :articles, :authors, on_delete: :cascade
@@ -977,19 +1133,28 @@ module ActiveRecord
977
1133
  #
978
1134
  # The +options+ hash can include the following keys:
979
1135
  # [<tt>:column</tt>]
980
- # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>
1136
+ # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
1137
+ # Pass an array to create a composite foreign key.
981
1138
  # [<tt>:primary_key</tt>]
982
1139
  # The primary key column name on +to_table+. Defaults to +id+.
1140
+ # Pass an array to create a composite foreign key.
983
1141
  # [<tt>:name</tt>]
984
1142
  # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
985
1143
  # [<tt>:on_delete</tt>]
986
- # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
1144
+ # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
987
1145
  # [<tt>:on_update</tt>]
988
- # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
1146
+ # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
1147
+ # [<tt>:if_not_exists</tt>]
1148
+ # Specifies if the foreign key already exists to not try to re-add it. This will avoid
1149
+ # duplicate column errors.
989
1150
  # [<tt>:validate</tt>]
990
1151
  # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
991
- def add_foreign_key(from_table, to_table, options = {})
992
- return unless supports_foreign_keys?
1152
+ # [<tt>:deferrable</tt>]
1153
+ # (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
1154
+ # +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
1155
+ def add_foreign_key(from_table, to_table, **options)
1156
+ return unless use_foreign_keys?
1157
+ return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
993
1158
 
994
1159
  options = foreign_key_options(from_table, to_table, options)
995
1160
  at = create_alter_table from_table
@@ -1019,12 +1184,18 @@ module ActiveRecord
1019
1184
  #
1020
1185
  # remove_foreign_key :accounts, name: :special_fk_name
1021
1186
  #
1187
+ # Checks if the foreign key exists before trying to remove it. Will silently ignore indexes that
1188
+ # don't exist.
1189
+ #
1190
+ # remove_foreign_key :accounts, :branches, if_exists: true
1191
+ #
1022
1192
  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
1023
1193
  # with an addition of
1024
1194
  # [<tt>:to_table</tt>]
1025
1195
  # The name of the table that contains the referenced primary key.
1026
1196
  def remove_foreign_key(from_table, to_table = nil, **options)
1027
- return unless supports_foreign_keys?
1197
+ return unless use_foreign_keys?
1198
+ return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
1028
1199
 
1029
1200
  fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
1030
1201
 
@@ -1049,20 +1220,116 @@ module ActiveRecord
1049
1220
  foreign_key_for(from_table, to_table: to_table, **options).present?
1050
1221
  end
1051
1222
 
1052
- def foreign_key_column_for(table_name) # :nodoc:
1223
+ def foreign_key_column_for(table_name, column_name) # :nodoc:
1053
1224
  name = strip_table_name_prefix_and_suffix(table_name)
1054
- "#{name.singularize}_id"
1225
+ "#{name.singularize}_#{column_name}"
1055
1226
  end
1056
1227
 
1057
1228
  def foreign_key_options(from_table, to_table, options) # :nodoc:
1058
1229
  options = options.dup
1059
- options[:column] ||= foreign_key_column_for(to_table)
1230
+
1231
+ if options[:primary_key].is_a?(Array)
1232
+ options[:column] ||= options[:primary_key].map do |pk_column|
1233
+ foreign_key_column_for(to_table, pk_column)
1234
+ end
1235
+ else
1236
+ options[:column] ||= foreign_key_column_for(to_table, "id")
1237
+ end
1238
+
1060
1239
  options[:name] ||= foreign_key_name(from_table, options)
1240
+
1241
+ if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
1242
+ if Array(options[:primary_key]).size != Array(options[:column]).size
1243
+ raise ArgumentError, <<~MSG.squish
1244
+ For composite primary keys, specify :column and :primary_key, where
1245
+ :column must reference all the :primary_key columns from #{to_table.inspect}
1246
+ MSG
1247
+ end
1248
+ end
1249
+
1061
1250
  options
1062
1251
  end
1063
1252
 
1253
+ # Returns an array of check constraints for the given table.
1254
+ # The check constraints are represented as CheckConstraintDefinition objects.
1255
+ def check_constraints(table_name)
1256
+ raise NotImplementedError
1257
+ end
1258
+
1259
+ # Adds a new check constraint to the table. +expression+ is a String
1260
+ # representation of verifiable boolean condition.
1261
+ #
1262
+ # add_check_constraint :products, "price > 0", name: "price_check"
1263
+ #
1264
+ # generates:
1265
+ #
1266
+ # ALTER TABLE "products" ADD CONSTRAINT price_check CHECK (price > 0)
1267
+ #
1268
+ # The +options+ hash can include the following keys:
1269
+ # [<tt>:name</tt>]
1270
+ # The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
1271
+ # [<tt>:if_not_exists</tt>]
1272
+ # Silently ignore if the constraint already exists, rather than raise an error.
1273
+ # [<tt>:validate</tt>]
1274
+ # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1275
+ def add_check_constraint(table_name, expression, if_not_exists: false, **options)
1276
+ return unless supports_check_constraints?
1277
+
1278
+ options = check_constraint_options(table_name, expression, options)
1279
+ return if if_not_exists && check_constraint_exists?(table_name, **options)
1280
+
1281
+ at = create_alter_table(table_name)
1282
+ at.add_check_constraint(expression, options)
1283
+
1284
+ execute schema_creation.accept(at)
1285
+ end
1286
+
1287
+ def check_constraint_options(table_name, expression, options) # :nodoc:
1288
+ options = options.dup
1289
+ options[:name] ||= check_constraint_name(table_name, expression: expression, **options)
1290
+ options
1291
+ end
1292
+
1293
+ # Removes the given check constraint from the table. Removing a check constraint
1294
+ # that does not exist will raise an error.
1295
+ #
1296
+ # remove_check_constraint :products, name: "price_check"
1297
+ #
1298
+ # To silently ignore a non-existent check constraint rather than raise an error,
1299
+ # use the +if_exists+ option.
1300
+ #
1301
+ # remove_check_constraint :products, name: "price_check", if_exists: true
1302
+ #
1303
+ # The +expression+ parameter will be ignored if present. It can be helpful
1304
+ # to provide this in a migration's +change+ method so it can be reverted.
1305
+ # In that case, +expression+ will be used by #add_check_constraint.
1306
+ def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
1307
+ return unless supports_check_constraints?
1308
+
1309
+ return if if_exists && !check_constraint_exists?(table_name, **options)
1310
+
1311
+ chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
1312
+
1313
+ at = create_alter_table(table_name)
1314
+ at.drop_check_constraint(chk_name_to_delete)
1315
+
1316
+ execute schema_creation.accept(at)
1317
+ end
1318
+
1319
+
1320
+ # Checks to see if a check constraint exists on a table for a given check constraint definition.
1321
+ #
1322
+ # check_constraint_exists?(:products, name: "price_check")
1323
+ #
1324
+ def check_constraint_exists?(table_name, **options)
1325
+ if !options.key?(:name) && !options.key?(:expression)
1326
+ raise ArgumentError, "At least one of :name or :expression must be supplied"
1327
+ end
1328
+ check_constraint_for(table_name, **options).present?
1329
+ end
1330
+
1064
1331
  def dump_schema_information # :nodoc:
1065
- versions = schema_migration.all_versions
1332
+ versions = pool.schema_migration.versions
1066
1333
  insert_versions_sql(versions) if versions.any?
1067
1334
  end
1068
1335
 
@@ -1070,16 +1337,11 @@ module ActiveRecord
1070
1337
  { primary_key: true }
1071
1338
  end
1072
1339
 
1073
- def assume_migrated_upto_version(version, migrations_paths = nil)
1074
- unless migrations_paths.nil?
1075
- ActiveSupport::Deprecation.warn(<<~MSG.squish)
1076
- Passing migrations_paths to #assume_migrated_upto_version is deprecated and will be removed in Rails 6.1.
1077
- MSG
1078
- end
1079
-
1340
+ def assume_migrated_upto_version(version)
1080
1341
  version = version.to_i
1081
- sm_table = quote_table_name(schema_migration.table_name)
1342
+ sm_table = quote_table_name(pool.schema_migration.table_name)
1082
1343
 
1344
+ migration_context = pool.migration_context
1083
1345
  migrated = migration_context.get_all_versions
1084
1346
  versions = migration_context.migrations.map(&:version)
1085
1347
 
@@ -1140,65 +1402,92 @@ module ActiveRecord
1140
1402
  columns
1141
1403
  end
1142
1404
 
1405
+ def distinct_relation_for_primary_key(relation) # :nodoc:
1406
+ primary_key_columns = Array(relation.primary_key).map do |column|
1407
+ visitor.compile(relation.table[column])
1408
+ end
1409
+
1410
+ values = columns_for_distinct(
1411
+ primary_key_columns,
1412
+ relation.order_values
1413
+ )
1414
+
1415
+ limited = relation.reselect(values).distinct!
1416
+ limited_ids = select_rows(limited.arel, "SQL").map do |results|
1417
+ results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
1418
+ end
1419
+
1420
+ if limited_ids.empty?
1421
+ relation.none!
1422
+ else
1423
+ relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
1424
+ end
1425
+
1426
+ relation.limit_value = relation.offset_value = nil
1427
+ relation
1428
+ end
1429
+
1143
1430
  # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
1144
1431
  # Additional options (like +:null+) are forwarded to #add_column.
1145
1432
  #
1146
1433
  # add_timestamps(:suppliers, null: true)
1147
1434
  #
1148
- def add_timestamps(table_name, options = {})
1149
- options[:null] = false if options[:null].nil?
1150
-
1151
- if !options.key?(:precision) && supports_datetime_with_precision?
1152
- options[:precision] = 6
1153
- end
1154
-
1155
- add_column table_name, :created_at, :datetime, options
1156
- add_column table_name, :updated_at, :datetime, options
1435
+ def add_timestamps(table_name, **options)
1436
+ fragments = add_timestamps_for_alter(table_name, **options)
1437
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
1157
1438
  end
1158
1439
 
1159
1440
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
1160
1441
  #
1161
1442
  # remove_timestamps(:suppliers)
1162
1443
  #
1163
- def remove_timestamps(table_name, options = {})
1164
- remove_column table_name, :updated_at
1165
- remove_column table_name, :created_at
1444
+ def remove_timestamps(table_name, **options)
1445
+ remove_columns table_name, :updated_at, :created_at
1166
1446
  end
1167
1447
 
1168
- def update_table_definition(table_name, base) #:nodoc:
1448
+ def update_table_definition(table_name, base) # :nodoc:
1169
1449
  Table.new(table_name, base)
1170
1450
  end
1171
1451
 
1172
- def add_index_options(table_name, column_name, comment: nil, **options) # :nodoc:
1173
- column_names = index_column_names(column_name)
1452
+ def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
1453
+ options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
1174
1454
 
1175
- options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type, :opclass)
1455
+ column_names = index_column_names(column_name)
1176
1456
 
1177
- index_type = options[:type].to_s if options.key?(:type)
1178
- index_type ||= options[:unique] ? "UNIQUE" : ""
1179
- index_name = options[:name].to_s if options.key?(:name)
1457
+ index_name = name&.to_s
1180
1458
  index_name ||= index_name(table_name, column_names)
1181
1459
 
1182
- if options.key?(:algorithm)
1183
- algorithm = index_algorithms.fetch(options[:algorithm]) {
1184
- raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
1185
- }
1186
- end
1187
-
1188
- using = "USING #{options[:using]}" if options[:using].present?
1189
-
1190
- if supports_partial_index?
1191
- index_options = options[:where] ? " WHERE #{options[:where]}" : ""
1192
- end
1460
+ validate_index_length!(table_name, index_name, internal)
1461
+
1462
+ index = IndexDefinition.new(
1463
+ table_name, index_name,
1464
+ options[:unique],
1465
+ column_names,
1466
+ lengths: options[:length] || {},
1467
+ orders: options[:order] || {},
1468
+ opclasses: options[:opclass] || {},
1469
+ where: options[:where],
1470
+ type: options[:type],
1471
+ using: options[:using],
1472
+ include: options[:include],
1473
+ nulls_not_distinct: options[:nulls_not_distinct],
1474
+ comment: options[:comment]
1475
+ )
1476
+
1477
+ [index, index_algorithm(options[:algorithm]), if_not_exists]
1478
+ end
1193
1479
 
1194
- validate_index_length!(table_name, index_name, options.fetch(:internal, false))
1480
+ def index_algorithm(algorithm) # :nodoc:
1481
+ index_algorithms.fetch(algorithm) do
1482
+ raise ArgumentError, "Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}"
1483
+ end if algorithm
1484
+ end
1195
1485
 
1196
- if data_source_exists?(table_name) && index_name_exists?(table_name, index_name)
1197
- raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
1486
+ def quoted_columns_for_index(column_names, options) # :nodoc:
1487
+ quoted_columns = column_names.each_with_object({}) do |name, result|
1488
+ result[name.to_sym] = quote_column_name(name).dup
1198
1489
  end
1199
- index_columns = quoted_columns_for_index(column_names, options).join(", ")
1200
-
1201
- [index_name, index_type, index_columns, index_options, algorithm, using, comment]
1490
+ add_options_for_index_columns(quoted_columns, **options).values.join(", ")
1202
1491
  end
1203
1492
 
1204
1493
  def options_include_default?(options)
@@ -1229,7 +1518,79 @@ module ActiveRecord
1229
1518
  SchemaDumper.create(self, options)
1230
1519
  end
1231
1520
 
1521
+ def use_foreign_keys?
1522
+ supports_foreign_keys? && foreign_keys_enabled?
1523
+ end
1524
+
1525
+ # Returns an instance of SchemaCreation, which can be used to visit a schema definition
1526
+ # object and return DDL.
1527
+ def schema_creation # :nodoc:
1528
+ SchemaCreation.new(self)
1529
+ end
1530
+
1531
+ def bulk_change_table(table_name, operations) # :nodoc:
1532
+ sql_fragments = []
1533
+ non_combinable_operations = []
1534
+
1535
+ operations.each do |command, args|
1536
+ table, arguments = args.shift, args
1537
+ method = :"#{command}_for_alter"
1538
+
1539
+ if respond_to?(method, true)
1540
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1541
+ sql_fragments.concat(sqls)
1542
+ non_combinable_operations.concat(procs)
1543
+ else
1544
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1545
+ non_combinable_operations.each(&:call)
1546
+ sql_fragments = []
1547
+ non_combinable_operations = []
1548
+ send(command, table, *arguments)
1549
+ end
1550
+ end
1551
+
1552
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1553
+ non_combinable_operations.each(&:call)
1554
+ end
1555
+
1556
+ def valid_table_definition_options # :nodoc:
1557
+ [:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
1558
+ end
1559
+
1560
+ def valid_column_definition_options # :nodoc:
1561
+ ColumnDefinition::OPTION_NAMES
1562
+ end
1563
+
1564
+ def valid_primary_key_options # :nodoc:
1565
+ [:limit, :default, :precision]
1566
+ end
1567
+
1568
+ # Returns the maximum length of an index name in bytes.
1569
+ def max_index_name_size
1570
+ 62
1571
+ end
1572
+
1232
1573
  private
1574
+ def generate_index_name(table_name, column)
1575
+ name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
1576
+ return name if name.bytesize <= max_index_name_size
1577
+
1578
+ # Fallback to short version, add hash to ensure uniqueness
1579
+ hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
1580
+ name = "idx_on_#{Array(column) * '_'}"
1581
+
1582
+ short_limit = max_index_name_size - hashed_identifier.bytesize
1583
+ short_name = name.mb_chars.limit(short_limit).to_s
1584
+
1585
+ "#{short_name}#{hashed_identifier}"
1586
+ end
1587
+
1588
+ def validate_change_column_null_argument!(value)
1589
+ unless value == true || value == false
1590
+ raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
1591
+ end
1592
+ end
1593
+
1233
1594
  def column_options_keys
1234
1595
  [:limit, :precision, :scale, :default, :null, :collation, :comment]
1235
1596
  end
@@ -1253,32 +1614,27 @@ module ActiveRecord
1253
1614
  # the PostgreSQL adapter for supporting operator classes.
1254
1615
  def add_options_for_index_columns(quoted_columns, **options)
1255
1616
  if supports_index_sort_order?
1256
- quoted_columns = add_index_sort_order(quoted_columns, options)
1617
+ quoted_columns = add_index_sort_order(quoted_columns, **options)
1257
1618
  end
1258
1619
 
1259
1620
  quoted_columns
1260
1621
  end
1261
1622
 
1262
- def quoted_columns_for_index(column_names, **options)
1263
- return [column_names] if column_names.is_a?(String)
1264
-
1265
- quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }]
1266
- add_options_for_index_columns(quoted_columns, options).values
1267
- end
1268
-
1269
- def index_name_for_remove(table_name, options = {})
1270
- return options[:name] if can_remove_index_by_name?(options)
1623
+ def index_name_for_remove(table_name, column_name, options)
1624
+ return options[:name] if can_remove_index_by_name?(column_name, options)
1271
1625
 
1272
1626
  checks = []
1273
1627
 
1274
- if options.is_a?(Hash)
1275
- checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1276
- column_names = index_column_names(options[:column])
1628
+ if !options.key?(:name) && expression_column_name?(column_name)
1629
+ options[:name] = index_name(table_name, column_name)
1630
+ column_names = []
1277
1631
  else
1278
- column_names = index_column_names(options)
1632
+ column_names = index_column_names(column_name || options[:column])
1279
1633
  end
1280
1634
 
1281
- if column_names.present?
1635
+ checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1636
+
1637
+ if column_names.present? && !(options.key?(:name) && expression_column_name?(column_names))
1282
1638
  checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
1283
1639
  end
1284
1640
 
@@ -1288,7 +1644,7 @@ module ActiveRecord
1288
1644
 
1289
1645
  if matching_indexes.count > 1
1290
1646
  raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
1291
- "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1647
+ "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1292
1648
  elsif matching_indexes.none?
1293
1649
  raise ArgumentError, "No indexes found on #{table_name} with the options provided."
1294
1650
  else
@@ -1296,11 +1652,11 @@ module ActiveRecord
1296
1652
  end
1297
1653
  end
1298
1654
 
1299
- def rename_table_indexes(table_name, new_name)
1655
+ def rename_table_indexes(table_name, new_name, **options)
1300
1656
  indexes(new_name).each do |index|
1301
- generated_index_name = index_name(table_name, column: index.columns)
1657
+ generated_index_name = index_name(table_name, column: index.columns, **options)
1302
1658
  if generated_index_name == index.name
1303
- rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
1659
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns, **options)
1304
1660
  end
1305
1661
  end
1306
1662
  end
@@ -1318,18 +1674,22 @@ module ActiveRecord
1318
1674
  end
1319
1675
  end
1320
1676
 
1321
- def schema_creation
1322
- SchemaCreation.new(self)
1323
- end
1324
-
1325
- def create_table_definition(*args)
1326
- TableDefinition.new(self, *args)
1677
+ def create_table_definition(name, **options)
1678
+ TableDefinition.new(self, name, **options)
1327
1679
  end
1328
1680
 
1329
1681
  def create_alter_table(name)
1330
1682
  AlterTable.new create_table_definition(name)
1331
1683
  end
1332
1684
 
1685
+ def validate_create_table_options!(options)
1686
+ unless options[:_skip_validate_options]
1687
+ options
1688
+ .except(:_uses_legacy_table_name, :_skip_validate_options)
1689
+ .assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
1690
+ end
1691
+ end
1692
+
1333
1693
  def fetch_type_metadata(sql_type)
1334
1694
  cast_type = lookup_cast_type(sql_type)
1335
1695
  SqlTypeMetadata.new(
@@ -1342,7 +1702,7 @@ module ActiveRecord
1342
1702
  end
1343
1703
 
1344
1704
  def index_column_names(column_names)
1345
- if column_names.is_a?(String) && /\W/.match?(column_names)
1705
+ if expression_column_name?(column_names)
1346
1706
  column_names
1347
1707
  else
1348
1708
  Array(column_names)
@@ -1350,13 +1710,18 @@ module ActiveRecord
1350
1710
  end
1351
1711
 
1352
1712
  def index_name_options(column_names)
1353
- if column_names.is_a?(String) && /\W/.match?(column_names)
1713
+ if expression_column_name?(column_names)
1354
1714
  column_names = column_names.scan(/\w+/).join("_")
1355
1715
  end
1356
1716
 
1357
1717
  { column: column_names }
1358
1718
  end
1359
1719
 
1720
+ # Try to identify whether the given column name is an expression
1721
+ def expression_column_name?(column_name)
1722
+ column_name.is_a?(String) && /\W/.match?(column_name)
1723
+ end
1724
+
1360
1725
  def strip_table_name_prefix_and_suffix(table_name)
1361
1726
  prefix = Base.table_name_prefix
1362
1727
  suffix = Base.table_name_suffix
@@ -1365,16 +1730,17 @@ module ActiveRecord
1365
1730
 
1366
1731
  def foreign_key_name(table_name, options)
1367
1732
  options.fetch(:name) do
1368
- identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1369
- hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1733
+ columns = Array(options.fetch(:column)).map(&:to_s)
1734
+ identifier = "#{table_name}_#{columns * '_and_'}_fk"
1735
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1370
1736
 
1371
1737
  "fk_rails_#{hashed_identifier}"
1372
1738
  end
1373
1739
  end
1374
1740
 
1375
1741
  def foreign_key_for(from_table, **options)
1376
- return unless supports_foreign_keys?
1377
- foreign_keys(from_table).detect { |fk| fk.defined_for?(options) }
1742
+ return unless use_foreign_keys?
1743
+ foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
1378
1744
  end
1379
1745
 
1380
1746
  def foreign_key_for!(from_table, to_table: nil, **options)
@@ -1390,11 +1756,40 @@ module ActiveRecord
1390
1756
  end
1391
1757
  end
1392
1758
 
1759
+ def foreign_keys_enabled?
1760
+ @config.fetch(:foreign_keys, true)
1761
+ end
1762
+
1763
+ def check_constraint_name(table_name, **options)
1764
+ options.fetch(:name) do
1765
+ expression = options.fetch(:expression)
1766
+ identifier = "#{table_name}_#{expression}_chk"
1767
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1768
+
1769
+ "chk_rails_#{hashed_identifier}"
1770
+ end
1771
+ end
1772
+
1773
+ def check_constraint_for(table_name, **options)
1774
+ return unless supports_check_constraints?
1775
+ chk_name = check_constraint_name(table_name, **options)
1776
+ check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
1777
+ end
1778
+
1779
+ def check_constraint_for!(table_name, expression: nil, **options)
1780
+ check_constraint_for(table_name, expression: expression, **options) ||
1781
+ raise(ArgumentError, "Table '#{table_name}' has no check constraint for #{expression || options}")
1782
+ end
1783
+
1393
1784
  def validate_index_length!(table_name, new_name, internal = false)
1394
- max_index_length = internal ? index_name_length : allowed_index_name_length
1785
+ if new_name.length > index_name_length
1786
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
1787
+ end
1788
+ end
1395
1789
 
1396
- if new_name.length > max_index_length
1397
- raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
1790
+ def validate_table_length!(table_name)
1791
+ if table_name.length > table_name_length
1792
+ raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
1398
1793
  end
1399
1794
  end
1400
1795
 
@@ -1407,56 +1802,61 @@ module ActiveRecord
1407
1802
  end
1408
1803
  alias :extract_new_comment_value :extract_new_default_value
1409
1804
 
1410
- def can_remove_index_by_name?(options)
1411
- options.is_a?(Hash) && options.key?(:name) && options.except(:name, :algorithm).empty?
1805
+ def can_remove_index_by_name?(column_name, options)
1806
+ column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
1412
1807
  end
1413
1808
 
1414
- def bulk_change_table(table_name, operations)
1415
- sql_fragments = []
1416
- non_combinable_operations = []
1417
-
1418
- operations.each do |command, args|
1419
- table, arguments = args.shift, args
1420
- method = :"#{command}_for_alter"
1421
-
1422
- if respond_to?(method, true)
1423
- sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1424
- sql_fragments << sqls
1425
- non_combinable_operations.concat(procs)
1426
- else
1427
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1428
- non_combinable_operations.each(&:call)
1429
- sql_fragments = []
1430
- non_combinable_operations = []
1431
- send(command, table, *arguments)
1432
- end
1433
- end
1434
-
1435
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1436
- non_combinable_operations.each(&:call)
1809
+ def reference_name_for_table(table_name)
1810
+ table_name.to_s.singularize
1437
1811
  end
1438
1812
 
1439
- def add_column_for_alter(table_name, column_name, type, options = {})
1813
+ def add_column_for_alter(table_name, column_name, type, **options)
1440
1814
  td = create_table_definition(table_name)
1441
- cd = td.new_column_definition(column_name, type, options)
1815
+ cd = td.new_column_definition(column_name, type, **options)
1442
1816
  schema_creation.accept(AddColumnDefinition.new(cd))
1443
1817
  end
1444
1818
 
1445
- def remove_column_for_alter(table_name, column_name, type = nil, options = {})
1819
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
1820
+ cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
1821
+ schema_creation.accept(cd)
1822
+ end
1823
+
1824
+ def rename_column_sql(table_name, column_name, new_column_name)
1825
+ "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1826
+ end
1827
+
1828
+ def remove_column_for_alter(table_name, column_name, type = nil, **options)
1446
1829
  "DROP COLUMN #{quote_column_name(column_name)}"
1447
1830
  end
1448
1831
 
1449
- def remove_columns_for_alter(table_name, *column_names)
1832
+ def remove_columns_for_alter(table_name, *column_names, **options)
1450
1833
  column_names.map { |column_name| remove_column_for_alter(table_name, column_name) }
1451
1834
  end
1452
1835
 
1836
+ def add_timestamps_for_alter(table_name, **options)
1837
+ options[:null] = false if options[:null].nil?
1838
+
1839
+ if !options.key?(:precision) && supports_datetime_with_precision?
1840
+ options[:precision] = 6
1841
+ end
1842
+
1843
+ [
1844
+ add_column_for_alter(table_name, :created_at, :datetime, **options),
1845
+ add_column_for_alter(table_name, :updated_at, :datetime, **options)
1846
+ ]
1847
+ end
1848
+
1849
+ def remove_timestamps_for_alter(table_name, **options)
1850
+ remove_columns_for_alter(table_name, :updated_at, :created_at)
1851
+ end
1852
+
1453
1853
  def insert_versions_sql(versions)
1454
- sm_table = quote_table_name(schema_migration.table_name)
1854
+ sm_table = quote_table_name(pool.schema_migration.table_name)
1455
1855
 
1456
1856
  if versions.is_a?(Array)
1457
1857
  sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1458
- sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1459
- sql << ";\n\n"
1858
+ sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
1859
+ sql << ";"
1460
1860
  sql
1461
1861
  else
1462
1862
  "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"