activerecord 6.1.7 → 7.1.5

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 (311) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2030 -1020
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +17 -14
  6. data/lib/active_record/association_relation.rb +1 -11
  7. data/lib/active_record/associations/association.rb +51 -19
  8. data/lib/active_record/associations/association_scope.rb +17 -12
  9. data/lib/active_record/associations/belongs_to_association.rb +28 -9
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +11 -5
  12. data/lib/active_record/associations/builder/belongs_to.rb +40 -14
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  15. data/lib/active_record/associations/builder/has_many.rb +3 -2
  16. data/lib/active_record/associations/builder/has_one.rb +2 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  18. data/lib/active_record/associations/collection_association.rb +39 -35
  19. data/lib/active_record/associations/collection_proxy.rb +30 -15
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  21. data/lib/active_record/associations/foreign_association.rb +10 -3
  22. data/lib/active_record/associations/has_many_association.rb +28 -18
  23. data/lib/active_record/associations/has_many_through_association.rb +12 -7
  24. data/lib/active_record/associations/has_one_association.rb +20 -10
  25. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  27. data/lib/active_record/associations/join_dependency.rb +28 -20
  28. data/lib/active_record/associations/preloader/association.rb +210 -52
  29. data/lib/active_record/associations/preloader/batch.rb +48 -0
  30. data/lib/active_record/associations/preloader/branch.rb +147 -0
  31. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  32. data/lib/active_record/associations/preloader.rb +50 -121
  33. data/lib/active_record/associations/singular_association.rb +9 -3
  34. data/lib/active_record/associations/through_association.rb +25 -14
  35. data/lib/active_record/associations.rb +446 -306
  36. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  37. data/lib/active_record/attribute_assignment.rb +1 -3
  38. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  39. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  40. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  41. data/lib/active_record/attribute_methods/query.rb +31 -19
  42. data/lib/active_record/attribute_methods/read.rb +27 -12
  43. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  45. data/lib/active_record/attribute_methods/write.rb +12 -15
  46. data/lib/active_record/attribute_methods.rb +161 -40
  47. data/lib/active_record/attributes.rb +27 -38
  48. data/lib/active_record/autosave_association.rb +65 -31
  49. data/lib/active_record/base.rb +25 -2
  50. data/lib/active_record/callbacks.rb +18 -34
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -46
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
  70. data/lib/active_record/connection_adapters/column.rb +13 -0
  71. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  72. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  73. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  74. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  77. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
  78. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  79. data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
  80. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  81. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
  84. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  89. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  90. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  91. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  94. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  95. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  96. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  99. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  100. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
  101. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  102. data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
  103. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  104. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  105. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
  106. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  107. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  108. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  109. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
  110. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  111. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  112. data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
  113. data/lib/active_record/connection_adapters.rb +9 -6
  114. data/lib/active_record/connection_handling.rb +108 -137
  115. data/lib/active_record/core.rb +242 -233
  116. data/lib/active_record/counter_cache.rb +52 -27
  117. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
  118. data/lib/active_record/database_configurations/database_config.rb +21 -12
  119. data/lib/active_record/database_configurations/hash_config.rb +88 -16
  120. data/lib/active_record/database_configurations/url_config.rb +18 -12
  121. data/lib/active_record/database_configurations.rb +95 -59
  122. data/lib/active_record/delegated_type.rb +66 -20
  123. data/lib/active_record/deprecator.rb +7 -0
  124. data/lib/active_record/destroy_association_async_job.rb +4 -2
  125. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  126. data/lib/active_record/dynamic_matchers.rb +1 -1
  127. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  128. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  129. data/lib/active_record/encryption/cipher.rb +53 -0
  130. data/lib/active_record/encryption/config.rb +68 -0
  131. data/lib/active_record/encryption/configurable.rb +60 -0
  132. data/lib/active_record/encryption/context.rb +42 -0
  133. data/lib/active_record/encryption/contexts.rb +76 -0
  134. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  135. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  136. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  137. data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
  138. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  139. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  140. data/lib/active_record/encryption/encryptor.rb +155 -0
  141. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  142. data/lib/active_record/encryption/errors.rb +15 -0
  143. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  144. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  145. data/lib/active_record/encryption/key.rb +28 -0
  146. data/lib/active_record/encryption/key_generator.rb +53 -0
  147. data/lib/active_record/encryption/key_provider.rb +46 -0
  148. data/lib/active_record/encryption/message.rb +33 -0
  149. data/lib/active_record/encryption/message_serializer.rb +92 -0
  150. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  151. data/lib/active_record/encryption/properties.rb +76 -0
  152. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  153. data/lib/active_record/encryption/scheme.rb +100 -0
  154. data/lib/active_record/encryption.rb +58 -0
  155. data/lib/active_record/enum.rb +154 -63
  156. data/lib/active_record/errors.rb +172 -15
  157. data/lib/active_record/explain.rb +23 -3
  158. data/lib/active_record/explain_registry.rb +11 -6
  159. data/lib/active_record/explain_subscriber.rb +1 -1
  160. data/lib/active_record/fixture_set/file.rb +15 -1
  161. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  162. data/lib/active_record/fixture_set/render_context.rb +2 -0
  163. data/lib/active_record/fixture_set/table_row.rb +70 -14
  164. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  165. data/lib/active_record/fixtures.rb +147 -86
  166. data/lib/active_record/future_result.rb +174 -0
  167. data/lib/active_record/gem_version.rb +3 -3
  168. data/lib/active_record/inheritance.rb +81 -29
  169. data/lib/active_record/insert_all.rb +135 -22
  170. data/lib/active_record/integration.rb +11 -10
  171. data/lib/active_record/internal_metadata.rb +119 -33
  172. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  173. data/lib/active_record/locking/optimistic.rb +37 -22
  174. data/lib/active_record/locking/pessimistic.rb +15 -6
  175. data/lib/active_record/log_subscriber.rb +52 -19
  176. data/lib/active_record/marshalling.rb +59 -0
  177. data/lib/active_record/message_pack.rb +124 -0
  178. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  179. data/lib/active_record/middleware/database_selector.rb +23 -13
  180. data/lib/active_record/middleware/shard_selector.rb +62 -0
  181. data/lib/active_record/migration/command_recorder.rb +112 -14
  182. data/lib/active_record/migration/compatibility.rb +233 -46
  183. data/lib/active_record/migration/default_strategy.rb +23 -0
  184. data/lib/active_record/migration/execution_strategy.rb +19 -0
  185. data/lib/active_record/migration/join_table.rb +1 -1
  186. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  187. data/lib/active_record/migration.rb +361 -173
  188. data/lib/active_record/model_schema.rb +125 -101
  189. data/lib/active_record/nested_attributes.rb +50 -20
  190. data/lib/active_record/no_touching.rb +3 -3
  191. data/lib/active_record/normalization.rb +167 -0
  192. data/lib/active_record/persistence.rb +409 -88
  193. data/lib/active_record/promise.rb +84 -0
  194. data/lib/active_record/query_cache.rb +4 -22
  195. data/lib/active_record/query_logs.rb +174 -0
  196. data/lib/active_record/query_logs_formatter.rb +41 -0
  197. data/lib/active_record/querying.rb +29 -6
  198. data/lib/active_record/railtie.rb +220 -44
  199. data/lib/active_record/railties/controller_runtime.rb +15 -10
  200. data/lib/active_record/railties/databases.rake +188 -252
  201. data/lib/active_record/railties/job_runtime.rb +23 -0
  202. data/lib/active_record/readonly_attributes.rb +41 -3
  203. data/lib/active_record/reflection.rb +248 -81
  204. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  205. data/lib/active_record/relation/batches.rb +192 -63
  206. data/lib/active_record/relation/calculations.rb +246 -90
  207. data/lib/active_record/relation/delegation.rb +28 -14
  208. data/lib/active_record/relation/finder_methods.rb +108 -51
  209. data/lib/active_record/relation/merger.rb +22 -13
  210. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  211. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  212. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  213. data/lib/active_record/relation/predicate_builder.rb +27 -20
  214. data/lib/active_record/relation/query_attribute.rb +30 -12
  215. data/lib/active_record/relation/query_methods.rb +670 -129
  216. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  217. data/lib/active_record/relation/spawn_methods.rb +20 -3
  218. data/lib/active_record/relation/where_clause.rb +10 -19
  219. data/lib/active_record/relation.rb +287 -120
  220. data/lib/active_record/result.rb +37 -11
  221. data/lib/active_record/runtime_registry.rb +32 -13
  222. data/lib/active_record/sanitization.rb +65 -20
  223. data/lib/active_record/schema.rb +36 -22
  224. data/lib/active_record/schema_dumper.rb +73 -24
  225. data/lib/active_record/schema_migration.rb +68 -33
  226. data/lib/active_record/scoping/default.rb +72 -15
  227. data/lib/active_record/scoping/named.rb +5 -13
  228. data/lib/active_record/scoping.rb +65 -34
  229. data/lib/active_record/secure_password.rb +60 -0
  230. data/lib/active_record/secure_token.rb +21 -3
  231. data/lib/active_record/serialization.rb +6 -1
  232. data/lib/active_record/signed_id.rb +10 -8
  233. data/lib/active_record/store.rb +10 -10
  234. data/lib/active_record/suppressor.rb +13 -15
  235. data/lib/active_record/table_metadata.rb +16 -3
  236. data/lib/active_record/tasks/database_tasks.rb +251 -140
  237. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  238. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  239. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  240. data/lib/active_record/test_databases.rb +1 -1
  241. data/lib/active_record/test_fixtures.rb +117 -96
  242. data/lib/active_record/timestamp.rb +32 -19
  243. data/lib/active_record/token_for.rb +113 -0
  244. data/lib/active_record/touch_later.rb +11 -6
  245. data/lib/active_record/transactions.rb +48 -27
  246. data/lib/active_record/translation.rb +3 -3
  247. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  248. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  249. data/lib/active_record/type/internal/timezone.rb +7 -2
  250. data/lib/active_record/type/serialized.rb +9 -5
  251. data/lib/active_record/type/time.rb +4 -0
  252. data/lib/active_record/type/type_map.rb +17 -20
  253. data/lib/active_record/type.rb +1 -2
  254. data/lib/active_record/validations/absence.rb +1 -1
  255. data/lib/active_record/validations/associated.rb +4 -4
  256. data/lib/active_record/validations/numericality.rb +5 -4
  257. data/lib/active_record/validations/presence.rb +5 -28
  258. data/lib/active_record/validations/uniqueness.rb +51 -6
  259. data/lib/active_record/validations.rb +8 -4
  260. data/lib/active_record/version.rb +1 -1
  261. data/lib/active_record.rb +335 -32
  262. data/lib/arel/attributes/attribute.rb +0 -8
  263. data/lib/arel/crud.rb +28 -22
  264. data/lib/arel/delete_manager.rb +18 -4
  265. data/lib/arel/errors.rb +10 -0
  266. data/lib/arel/factory_methods.rb +4 -0
  267. data/lib/arel/filter_predications.rb +9 -0
  268. data/lib/arel/insert_manager.rb +2 -3
  269. data/lib/arel/nodes/and.rb +4 -0
  270. data/lib/arel/nodes/binary.rb +6 -1
  271. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  272. data/lib/arel/nodes/casted.rb +1 -1
  273. data/lib/arel/nodes/cte.rb +36 -0
  274. data/lib/arel/nodes/delete_statement.rb +12 -13
  275. data/lib/arel/nodes/filter.rb +10 -0
  276. data/lib/arel/nodes/fragments.rb +35 -0
  277. data/lib/arel/nodes/function.rb +1 -0
  278. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  279. data/lib/arel/nodes/insert_statement.rb +2 -2
  280. data/lib/arel/nodes/leading_join.rb +8 -0
  281. data/lib/arel/nodes/node.rb +111 -2
  282. data/lib/arel/nodes/select_core.rb +2 -2
  283. data/lib/arel/nodes/select_statement.rb +2 -2
  284. data/lib/arel/nodes/sql_literal.rb +6 -0
  285. data/lib/arel/nodes/table_alias.rb +4 -0
  286. data/lib/arel/nodes/update_statement.rb +8 -3
  287. data/lib/arel/nodes.rb +5 -0
  288. data/lib/arel/predications.rb +13 -3
  289. data/lib/arel/select_manager.rb +10 -4
  290. data/lib/arel/table.rb +9 -6
  291. data/lib/arel/tree_manager.rb +5 -13
  292. data/lib/arel/update_manager.rb +18 -4
  293. data/lib/arel/visitors/dot.rb +80 -90
  294. data/lib/arel/visitors/mysql.rb +16 -3
  295. data/lib/arel/visitors/postgresql.rb +0 -10
  296. data/lib/arel/visitors/to_sql.rb +141 -20
  297. data/lib/arel/visitors/visitor.rb +2 -2
  298. data/lib/arel.rb +18 -3
  299. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  300. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/migration.rb +3 -1
  302. data/lib/rails/generators/active_record/model/USAGE +113 -0
  303. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  304. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  305. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  306. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  307. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  308. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  309. metadata +96 -16
  310. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  311. data/lib/active_record/null_relation.rb +0 -67
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/string/access"
4
- require "digest/sha2"
4
+ require "openssl"
5
5
 
6
6
  module ActiveRecord
7
7
  module ConnectionAdapters # :nodoc:
@@ -29,7 +29,7 @@ module ActiveRecord
29
29
  table_name[0...table_alias_length].tr(".", "_")
30
30
  end
31
31
 
32
- # Returns the relation names useable to back Active Record models.
32
+ # Returns the relation names usable to back Active Record models.
33
33
  # For most adapters this means all #tables and #views.
34
34
  def data_sources
35
35
  query_values(data_source_sql, "SCHEMA")
@@ -96,25 +96,19 @@ module ActiveRecord
96
96
  # # Check an index with a custom name exists
97
97
  # index_exists?(:suppliers, :company_id, name: "idx_company_id")
98
98
  #
99
+ # # Check a valid index exists (PostgreSQL only)
100
+ # index_exists?(:suppliers, :company_id, valid: true)
101
+ #
99
102
  def index_exists?(table_name, column_name, **options)
100
- checks = []
101
-
102
- if column_name.present?
103
- column_names = Array(column_name).map(&:to_s)
104
- checks << lambda { |i| Array(i.columns) == column_names }
105
- end
106
-
107
- checks << lambda { |i| i.unique } if options[:unique]
108
- checks << lambda { |i| i.name == options[:name].to_s } if options[:name]
109
-
110
- indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
103
+ indexes(table_name).any? { |i| i.defined_for?(column_name, **options) }
111
104
  end
112
105
 
113
106
  # Returns an array of +Column+ objects for the table specified by +table_name+.
114
107
  def columns(table_name)
115
108
  table_name = table_name.to_s
116
- column_definitions(table_name).map do |field|
117
- 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)
118
112
  end
119
113
  end
120
114
 
@@ -124,6 +118,9 @@ module ActiveRecord
124
118
  # column_exists?(:suppliers, :name)
125
119
  #
126
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).
127
124
  # column_exists?(:suppliers, :name, :string)
128
125
  #
129
126
  # # Check a column exists with a specific definition
@@ -260,7 +257,7 @@ module ActiveRecord
260
257
  #
261
258
  # generates:
262
259
  #
263
- # CREATE TABLE order (
260
+ # CREATE TABLE orders (
264
261
  # product_id bigint NOT NULL,
265
262
  # client_id bigint NOT NULL
266
263
  # );
@@ -293,25 +290,10 @@ module ActiveRecord
293
290
  # SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
294
291
  #
295
292
  # See also TableDefinition#column for details on how to create columns.
296
- def create_table(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
297
- td = create_table_definition(table_name, **extract_table_options!(options))
298
-
299
- if id && !td.as
300
- pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
301
-
302
- if id.is_a?(Hash)
303
- options.merge!(id.except(:type))
304
- id = id.fetch(:type, :primary_key)
305
- end
306
-
307
- if pk.is_a?(Array)
308
- td.primary_keys pk
309
- else
310
- td.primary_key pk, id, **options
311
- end
312
- end
313
-
314
- yield td if block_given?
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
+ td = build_create_table_definition(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
315
297
 
316
298
  if force
317
299
  drop_table(table_name, force: force, if_exists: true)
@@ -319,7 +301,7 @@ module ActiveRecord
319
301
  schema_cache.clear_data_source_cache!(table_name.to_s)
320
302
  end
321
303
 
322
- result = execute schema_creation.accept td
304
+ result = execute schema_creation.accept(td)
323
305
 
324
306
  unless supports_indexes_in_create?
325
307
  td.indexes.each do |column_name, index_options|
@@ -340,6 +322,18 @@ module ActiveRecord
340
322
  result
341
323
  end
342
324
 
325
+ # Returns a TableDefinition object containing information about the table that would be created
326
+ # if the same arguments were passed to #create_table. See #create_table for information about
327
+ # passing a +table_name+, and other additional options that can be passed.
328
+ def build_create_table_definition(table_name, id: :primary_key, primary_key: nil, force: nil, **options)
329
+ table_definition = create_table_definition(table_name, **options.extract!(*valid_table_definition_options, :_skip_validate_options))
330
+ table_definition.set_primary_key(table_name, id, primary_key, **options.extract!(*valid_primary_key_options, :_skip_validate_options))
331
+
332
+ yield table_definition if block_given?
333
+
334
+ table_definition
335
+ end
336
+
343
337
  # Creates a new join table with the name created using the lexical order of the first two
344
338
  # arguments. These arguments can be a String or a Symbol.
345
339
  #
@@ -383,7 +377,7 @@ module ActiveRecord
383
377
 
384
378
  column_options.reverse_merge!(null: false, index: false)
385
379
 
386
- t1_ref, t2_ref = [table_1, table_2].map { |t| t.to_s.singularize }
380
+ t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
387
381
 
388
382
  create_table(join_table_name, **options.merge!(id: false)) do |td|
389
383
  td.references t1_ref, **column_options
@@ -392,15 +386,33 @@ module ActiveRecord
392
386
  end
393
387
  end
394
388
 
389
+ # Builds a TableDefinition object for a join table.
390
+ #
391
+ # This definition object contains information about the table that would be created
392
+ # if the same arguments were passed to #create_join_table. See #create_join_table for
393
+ # information about what arguments should be passed.
394
+ def build_create_join_table_definition(table_1, table_2, column_options: {}, **options) # :nodoc:
395
+ join_table_name = find_join_table_name(table_1, table_2, options)
396
+ column_options.reverse_merge!(null: false, index: false)
397
+
398
+ t1_ref, t2_ref = [table_1, table_2].map { |t| reference_name_for_table(t) }
399
+
400
+ build_create_table_definition(join_table_name, **options.merge!(id: false)) do |td|
401
+ td.references t1_ref, **column_options
402
+ td.references t2_ref, **column_options
403
+ yield td if block_given?
404
+ end
405
+ end
406
+
395
407
  # Drops the join table specified by the given arguments.
396
- # See #create_join_table for details.
408
+ # See #create_join_table and #drop_table for details.
397
409
  #
398
410
  # Although this command ignores the block if one is given, it can be helpful
399
411
  # to provide one in a migration's +change+ method so it can be reverted.
400
412
  # In that case, the block will be used by #create_join_table.
401
413
  def drop_join_table(table_1, table_2, **options)
402
414
  join_table_name = find_join_table_name(table_1, table_2, options)
403
- drop_table(join_table_name)
415
+ drop_table(join_table_name, **options)
404
416
  end
405
417
 
406
418
  # A block for changing columns in +table+.
@@ -481,13 +493,13 @@ module ActiveRecord
481
493
  # end
482
494
  #
483
495
  # See also Table for details on all of the various column transformations.
484
- def change_table(table_name, **options)
496
+ def change_table(table_name, base = self, **options)
485
497
  if supports_bulk_alter? && options[:bulk]
486
498
  recorder = ActiveRecord::Migration::CommandRecorder.new(self)
487
499
  yield update_table_definition(table_name, recorder)
488
500
  bulk_change_table(table_name, recorder.commands)
489
501
  else
490
- yield update_table_definition(table_name, self)
502
+ yield update_table_definition(table_name, base)
491
503
  end
492
504
  end
493
505
 
@@ -495,7 +507,7 @@ module ActiveRecord
495
507
  #
496
508
  # rename_table('octopuses', 'octopi')
497
509
  #
498
- def rename_table(table_name, new_name)
510
+ def rename_table(table_name, new_name, **)
499
511
  raise NotImplementedError, "rename_table is not implemented"
500
512
  end
501
513
 
@@ -518,24 +530,31 @@ module ActiveRecord
518
530
 
519
531
  # Add a new +type+ column named +column_name+ to +table_name+.
520
532
  #
533
+ # See {ActiveRecord::ConnectionAdapters::TableDefinition.column}[rdoc-ref:ActiveRecord::ConnectionAdapters::TableDefinition#column].
534
+ #
521
535
  # The +type+ parameter is normally one of the migrations native types,
522
536
  # which is one of the following:
523
537
  # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
524
538
  # <tt>:integer</tt>, <tt>:bigint</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:numeric</tt>,
525
539
  # <tt>:datetime</tt>, <tt>:time</tt>, <tt>:date</tt>,
526
- # <tt>:binary</tt>, <tt>:boolean</tt>.
540
+ # <tt>:binary</tt>, <tt>:blob</tt>, <tt>:boolean</tt>.
527
541
  #
528
542
  # You may use a type not in this list as long as it is supported by your
529
543
  # database (for example, "polygon" in MySQL), but this will not be database
530
544
  # agnostic and should usually be avoided.
531
545
  #
532
546
  # Available options are (none of these exists by default):
547
+ # * <tt>:comment</tt> -
548
+ # Specifies the comment for the column. This option is ignored by some backends.
549
+ # * <tt>:collation</tt> -
550
+ # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column.
551
+ # If not specified, the column will have the same collation as the table.
552
+ # * <tt>:default</tt> -
553
+ # The column's default value. Use +nil+ for +NULL+.
533
554
  # * <tt>:limit</tt> -
534
555
  # Requests a maximum column length. This is the number of characters for a <tt>:string</tt> column
535
- # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, and <tt>:integer</tt> columns.
556
+ # and number of bytes for <tt>:text</tt>, <tt>:binary</tt>, <tt>:blob</tt>, and <tt>:integer</tt> columns.
536
557
  # This option is ignored by some backends.
537
- # * <tt>:default</tt> -
538
- # The column's default value. Use +nil+ for +NULL+.
539
558
  # * <tt>:null</tt> -
540
559
  # Allows or disallows +NULL+ values in the column.
541
560
  # * <tt>:precision</tt> -
@@ -543,11 +562,6 @@ module ActiveRecord
543
562
  # <tt>:datetime</tt>, and <tt>:time</tt> columns.
544
563
  # * <tt>:scale</tt> -
545
564
  # Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
546
- # * <tt>:collation</tt> -
547
- # Specifies the collation for a <tt>:string</tt> or <tt>:text</tt> column. If not specified, the
548
- # column will have the same collation as the table.
549
- # * <tt>:comment</tt> -
550
- # Specifies the comment for the column. This option is ignored by some backends.
551
565
  # * <tt>:if_not_exists</tt> -
552
566
  # Specifies if the column already exists to not try to re-add it. This will avoid
553
567
  # duplicate column errors.
@@ -563,7 +577,7 @@ module ActiveRecord
563
577
  # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
564
578
  # <tt>:precision</tt>, and makes no comments about the requirements of
565
579
  # <tt>:precision</tt>.
566
- # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
580
+ # * MySQL: <tt>:precision</tt> [1..65], <tt>:scale</tt> [0..30].
567
581
  # Default is (10,0).
568
582
  # * PostgreSQL: <tt>:precision</tt> [1..infinity],
569
583
  # <tt>:scale</tt> [0..infinity]. No default.
@@ -604,11 +618,10 @@ module ActiveRecord
604
618
  # # Ignores the method call if the column exists
605
619
  # add_column(:shapes, :triangle, 'polygon', if_not_exists: true)
606
620
  def add_column(table_name, column_name, type, **options)
607
- return if options[:if_not_exists] == true && column_exists?(table_name, column_name, type)
621
+ add_column_def = build_add_column_definition(table_name, column_name, type, **options)
622
+ return unless add_column_def
608
623
 
609
- at = create_alter_table table_name
610
- at.add_column(column_name, type, **options)
611
- execute schema_creation.accept at
624
+ execute schema_creation.accept(add_column_def)
612
625
  end
613
626
 
614
627
  def add_columns(table_name, *column_names, type:, **options) # :nodoc:
@@ -617,6 +630,25 @@ module ActiveRecord
617
630
  end
618
631
  end
619
632
 
633
+ # Builds an AlterTable object for adding a column to a table.
634
+ #
635
+ # This definition object contains information about the column that would be created
636
+ # if the same arguments were passed to #add_column. See #add_column for information about
637
+ # passing a +table_name+, +column_name+, +type+ and other options that can be passed.
638
+ def build_add_column_definition(table_name, column_name, type, **options) # :nodoc:
639
+ return if options[:if_not_exists] == true && column_exists?(table_name, column_name)
640
+
641
+ if supports_datetime_with_precision?
642
+ if type == :datetime && !options.key?(:precision)
643
+ options[:precision] = 6
644
+ end
645
+ end
646
+
647
+ alter_table = create_alter_table(table_name)
648
+ alter_table.add_column(column_name, type, **options)
649
+ alter_table
650
+ end
651
+
620
652
  # Removes the given columns from the table definition.
621
653
  #
622
654
  # remove_columns(:suppliers, :qualification, :experience)
@@ -629,9 +661,8 @@ module ActiveRecord
629
661
  raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)")
630
662
  end
631
663
 
632
- column_names.each do |column_name|
633
- remove_column(table_name, column_name, type, **options)
634
- end
664
+ remove_column_fragments = remove_columns_for_alter(table_name, *column_names, type: type, **options)
665
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_fragments.join(', ')}"
635
666
  end
636
667
 
637
668
  # Removes the column from the table definition.
@@ -641,7 +672,8 @@ module ActiveRecord
641
672
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
642
673
  # to provide these in a migration's +change+ method so it can be reverted.
643
674
  # In that case, +type+ and +options+ will be used by #add_column.
644
- # Indexes on the column are automatically removed.
675
+ # Depending on the database you're using, indexes using this column may be
676
+ # automatically removed or modified to remove this column from the index.
645
677
  #
646
678
  # If the options provided include an +if_exists+ key, it will be used to check if the
647
679
  # column does not exist. This will silently ignore the migration rather than raising
@@ -682,6 +714,15 @@ module ActiveRecord
682
714
  raise NotImplementedError, "change_column_default is not implemented"
683
715
  end
684
716
 
717
+ # Builds a ChangeColumnDefaultDefinition object.
718
+ #
719
+ # This definition object contains information about the column change that would occur
720
+ # if the same arguments were passed to #change_column_default. See #change_column_default for
721
+ # information about passing a +table_name+, +column_name+, +type+ and other options that can be passed.
722
+ def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc:
723
+ raise NotImplementedError, "build_change_column_default_definition is not implemented"
724
+ end
725
+
685
726
  # Sets or removes a <tt>NOT NULL</tt> constraint on a column. The +null+ flag
686
727
  # indicates whether the value can be +NULL+. For example
687
728
  #
@@ -766,7 +807,7 @@ module ActiveRecord
766
807
  #
767
808
  # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
768
809
  #
769
- # Note: SQLite doesn't support index length.
810
+ # Note: only supported by MySQL
770
811
  #
771
812
  # ====== Creating an index with a sort order (desc or asc, asc is the default)
772
813
  #
@@ -788,6 +829,16 @@ module ActiveRecord
788
829
  #
789
830
  # Note: Partial indexes are only supported for PostgreSQL and SQLite.
790
831
  #
832
+ # ====== Creating an index that includes additional columns
833
+ #
834
+ # add_index(:accounts, :branch_id, include: :party_id)
835
+ #
836
+ # generates:
837
+ #
838
+ # CREATE INDEX index_accounts_on_branch_id ON accounts USING btree(branch_id) INCLUDE (party_id)
839
+ #
840
+ # Note: only supported by PostgreSQL.
841
+ #
791
842
  # ====== Creating an index with a specific method
792
843
  #
793
844
  # add_index(:developers, :name, using: 'btree')
@@ -833,12 +884,20 @@ module ActiveRecord
833
884
  #
834
885
  # For more information see the {"Transactional Migrations" section}[rdoc-ref:Migration].
835
886
  def add_index(table_name, column_name, **options)
836
- index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
837
-
838
- create_index = CreateIndexDefinition.new(index, algorithm, if_not_exists)
887
+ create_index = build_create_index_definition(table_name, column_name, **options)
839
888
  execute schema_creation.accept(create_index)
840
889
  end
841
890
 
891
+ # Builds a CreateIndexDefinition object.
892
+ #
893
+ # This definition object contains information about the index that would be created
894
+ # if the same arguments were passed to #add_index. See #add_index for information about
895
+ # passing a +table_name+, +column_name+, and other additional options that can be passed.
896
+ def build_create_index_definition(table_name, column_name, **options) # :nodoc:
897
+ index, algorithm, if_not_exists = add_index_options(table_name, column_name, **options)
898
+ CreateIndexDefinition.new(index, algorithm, if_not_exists)
899
+ end
900
+
842
901
  # Removes the given index from the table.
843
902
  #
844
903
  # Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
@@ -901,10 +960,14 @@ module ActiveRecord
901
960
  remove_index(table_name, name: old_name)
902
961
  end
903
962
 
904
- def index_name(table_name, options) #:nodoc:
963
+ def index_name(table_name, options) # :nodoc:
905
964
  if Hash === options
906
965
  if options[:column]
907
- "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
966
+ if options[:_uses_legacy_index_name]
967
+ "index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
968
+ else
969
+ generate_index_name(table_name, options[:column])
970
+ end
908
971
  elsif options[:name]
909
972
  options[:name]
910
973
  else
@@ -924,7 +987,6 @@ module ActiveRecord
924
987
  # Adds a reference. The reference column is a bigint by default,
925
988
  # the <tt>:type</tt> option can be used to specify a different type.
926
989
  # Optionally adds a +_type+ column, if <tt>:polymorphic</tt> option is provided.
927
- # #add_reference and #add_belongs_to are acceptable.
928
990
  #
929
991
  # The +options+ hash can include the following keys:
930
992
  # [<tt>:type</tt>]
@@ -970,12 +1032,11 @@ module ActiveRecord
970
1032
  # add_reference(:products, :supplier, foreign_key: { to_table: :firms })
971
1033
  #
972
1034
  def add_reference(table_name, ref_name, **options)
973
- ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
1035
+ ReferenceDefinition.new(ref_name, **options).add(table_name, self)
974
1036
  end
975
1037
  alias :add_belongs_to :add_reference
976
1038
 
977
1039
  # Removes the reference(s). Also removes a +type+ column if one exists.
978
- # #remove_reference and #remove_belongs_to are acceptable.
979
1040
  #
980
1041
  # ====== Remove the reference
981
1042
  #
@@ -990,19 +1051,21 @@ module ActiveRecord
990
1051
  # remove_reference(:products, :user, foreign_key: true)
991
1052
  #
992
1053
  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
1054
+ conditional_options = options.slice(:if_exists, :if_not_exists)
1055
+
993
1056
  if foreign_key
994
1057
  reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
995
1058
  if foreign_key.is_a?(Hash)
996
- foreign_key_options = foreign_key
1059
+ foreign_key_options = foreign_key.merge(conditional_options)
997
1060
  else
998
- foreign_key_options = { to_table: reference_name }
1061
+ foreign_key_options = { to_table: reference_name, **conditional_options }
999
1062
  end
1000
1063
  foreign_key_options[:column] ||= "#{ref_name}_id"
1001
1064
  remove_foreign_key(table_name, **foreign_key_options)
1002
1065
  end
1003
1066
 
1004
- remove_column(table_name, "#{ref_name}_id")
1005
- remove_column(table_name, "#{ref_name}_type") if polymorphic
1067
+ remove_column(table_name, "#{ref_name}_id", **conditional_options)
1068
+ remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
1006
1069
  end
1007
1070
  alias :remove_belongs_to :remove_reference
1008
1071
 
@@ -1027,6 +1090,10 @@ module ActiveRecord
1027
1090
  #
1028
1091
  # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
1029
1092
  #
1093
+ # ====== Creating a foreign key, ignoring method call if the foreign key exists
1094
+ #
1095
+ # add_foreign_key(:articles, :authors, if_not_exists: true)
1096
+ #
1030
1097
  # ====== Creating a foreign key on a specific column
1031
1098
  #
1032
1099
  # add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
@@ -1035,6 +1102,16 @@ module ActiveRecord
1035
1102
  #
1036
1103
  # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
1037
1104
  #
1105
+ # ====== Creating a composite foreign key
1106
+ #
1107
+ # Assuming "carts" table has "(shop_id, user_id)" as a primary key.
1108
+ #
1109
+ # add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
1110
+ #
1111
+ # generates:
1112
+ #
1113
+ # ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
1114
+ #
1038
1115
  # ====== Creating a cascading foreign key
1039
1116
  #
1040
1117
  # add_foreign_key :articles, :authors, on_delete: :cascade
@@ -1045,19 +1122,28 @@ module ActiveRecord
1045
1122
  #
1046
1123
  # The +options+ hash can include the following keys:
1047
1124
  # [<tt>:column</tt>]
1048
- # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>
1125
+ # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
1126
+ # Pass an array to create a composite foreign key.
1049
1127
  # [<tt>:primary_key</tt>]
1050
1128
  # The primary key column name on +to_table+. Defaults to +id+.
1129
+ # Pass an array to create a composite foreign key.
1051
1130
  # [<tt>:name</tt>]
1052
1131
  # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
1053
1132
  # [<tt>:on_delete</tt>]
1054
- # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
1133
+ # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
1055
1134
  # [<tt>:on_update</tt>]
1056
- # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
1135
+ # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
1136
+ # [<tt>:if_not_exists</tt>]
1137
+ # Specifies if the foreign key already exists to not try to re-add it. This will avoid
1138
+ # duplicate column errors.
1057
1139
  # [<tt>:validate</tt>]
1058
1140
  # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1141
+ # [<tt>:deferrable</tt>]
1142
+ # (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
1143
+ # +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
1059
1144
  def add_foreign_key(from_table, to_table, **options)
1060
- return unless supports_foreign_keys?
1145
+ return unless use_foreign_keys?
1146
+ return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
1061
1147
 
1062
1148
  options = foreign_key_options(from_table, to_table, options)
1063
1149
  at = create_alter_table from_table
@@ -1087,12 +1173,18 @@ module ActiveRecord
1087
1173
  #
1088
1174
  # remove_foreign_key :accounts, name: :special_fk_name
1089
1175
  #
1176
+ # Checks if the foreign key exists before trying to remove it. Will silently ignore indexes that
1177
+ # don't exist.
1178
+ #
1179
+ # remove_foreign_key :accounts, :branches, if_exists: true
1180
+ #
1090
1181
  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
1091
1182
  # with an addition of
1092
1183
  # [<tt>:to_table</tt>]
1093
1184
  # The name of the table that contains the referenced primary key.
1094
1185
  def remove_foreign_key(from_table, to_table = nil, **options)
1095
- return unless supports_foreign_keys?
1186
+ return unless use_foreign_keys?
1187
+ return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
1096
1188
 
1097
1189
  fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
1098
1190
 
@@ -1117,15 +1209,33 @@ module ActiveRecord
1117
1209
  foreign_key_for(from_table, to_table: to_table, **options).present?
1118
1210
  end
1119
1211
 
1120
- def foreign_key_column_for(table_name) # :nodoc:
1212
+ def foreign_key_column_for(table_name, column_name) # :nodoc:
1121
1213
  name = strip_table_name_prefix_and_suffix(table_name)
1122
- "#{name.singularize}_id"
1214
+ "#{name.singularize}_#{column_name}"
1123
1215
  end
1124
1216
 
1125
1217
  def foreign_key_options(from_table, to_table, options) # :nodoc:
1126
1218
  options = options.dup
1127
- options[:column] ||= foreign_key_column_for(to_table)
1219
+
1220
+ if options[:primary_key].is_a?(Array)
1221
+ options[:column] ||= options[:primary_key].map do |pk_column|
1222
+ foreign_key_column_for(to_table, pk_column)
1223
+ end
1224
+ else
1225
+ options[:column] ||= foreign_key_column_for(to_table, "id")
1226
+ end
1227
+
1128
1228
  options[:name] ||= foreign_key_name(from_table, options)
1229
+
1230
+ if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
1231
+ if Array(options[:primary_key]).size != Array(options[:column]).size
1232
+ raise ArgumentError, <<~MSG.squish
1233
+ For composite primary keys, specify :column and :primary_key, where
1234
+ :column must reference all the :primary_key columns from #{to_table.inspect}
1235
+ MSG
1236
+ end
1237
+ end
1238
+
1129
1239
  options
1130
1240
  end
1131
1241
 
@@ -1147,12 +1257,16 @@ module ActiveRecord
1147
1257
  # The +options+ hash can include the following keys:
1148
1258
  # [<tt>:name</tt>]
1149
1259
  # The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
1260
+ # [<tt>:if_not_exists</tt>]
1261
+ # Silently ignore if the constraint already exists, rather than raise an error.
1150
1262
  # [<tt>:validate</tt>]
1151
1263
  # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1152
- def add_check_constraint(table_name, expression, **options)
1264
+ def add_check_constraint(table_name, expression, if_not_exists: false, **options)
1153
1265
  return unless supports_check_constraints?
1154
1266
 
1155
1267
  options = check_constraint_options(table_name, expression, options)
1268
+ return if if_not_exists && check_constraint_exists?(table_name, **options)
1269
+
1156
1270
  at = create_alter_table(table_name)
1157
1271
  at.add_check_constraint(expression, options)
1158
1272
 
@@ -1165,16 +1279,24 @@ module ActiveRecord
1165
1279
  options
1166
1280
  end
1167
1281
 
1168
- # Removes the given check constraint from the table.
1282
+ # Removes the given check constraint from the table. Removing a check constraint
1283
+ # that does not exist will raise an error.
1169
1284
  #
1170
1285
  # remove_check_constraint :products, name: "price_check"
1171
1286
  #
1287
+ # To silently ignore a non-existent check constraint rather than raise an error,
1288
+ # use the +if_exists+ option.
1289
+ #
1290
+ # remove_check_constraint :products, name: "price_check", if_exists: true
1291
+ #
1172
1292
  # The +expression+ parameter will be ignored if present. It can be helpful
1173
1293
  # to provide this in a migration's +change+ method so it can be reverted.
1174
1294
  # In that case, +expression+ will be used by #add_check_constraint.
1175
- def remove_check_constraint(table_name, expression = nil, **options)
1295
+ def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
1176
1296
  return unless supports_check_constraints?
1177
1297
 
1298
+ return if if_exists && !check_constraint_exists?(table_name, **options)
1299
+
1178
1300
  chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
1179
1301
 
1180
1302
  at = create_alter_table(table_name)
@@ -1183,8 +1305,20 @@ module ActiveRecord
1183
1305
  execute schema_creation.accept(at)
1184
1306
  end
1185
1307
 
1308
+
1309
+ # Checks to see if a check constraint exists on a table for a given check constraint definition.
1310
+ #
1311
+ # check_constraint_exists?(:products, name: "price_check")
1312
+ #
1313
+ def check_constraint_exists?(table_name, **options)
1314
+ if !options.key?(:name) && !options.key?(:expression)
1315
+ raise ArgumentError, "At least one of :name or :expression must be supplied"
1316
+ end
1317
+ check_constraint_for(table_name, **options).present?
1318
+ end
1319
+
1186
1320
  def dump_schema_information # :nodoc:
1187
- versions = schema_migration.all_versions
1321
+ versions = schema_migration.versions
1188
1322
  insert_versions_sql(versions) if versions.any?
1189
1323
  end
1190
1324
 
@@ -1256,20 +1390,39 @@ module ActiveRecord
1256
1390
  columns
1257
1391
  end
1258
1392
 
1393
+ def distinct_relation_for_primary_key(relation) # :nodoc:
1394
+ primary_key_columns = Array(relation.primary_key).map do |column|
1395
+ visitor.compile(relation.table[column])
1396
+ end
1397
+
1398
+ values = columns_for_distinct(
1399
+ primary_key_columns,
1400
+ relation.order_values
1401
+ )
1402
+
1403
+ limited = relation.reselect(values).distinct!
1404
+ limited_ids = select_rows(limited.arel, "SQL").map do |results|
1405
+ results.last(Array(relation.primary_key).length) # ignores order values for MySQL and PostgreSQL
1406
+ end
1407
+
1408
+ if limited_ids.empty?
1409
+ relation.none!
1410
+ else
1411
+ relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
1412
+ end
1413
+
1414
+ relation.limit_value = relation.offset_value = nil
1415
+ relation
1416
+ end
1417
+
1259
1418
  # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
1260
1419
  # Additional options (like +:null+) are forwarded to #add_column.
1261
1420
  #
1262
1421
  # add_timestamps(:suppliers, null: true)
1263
1422
  #
1264
1423
  def add_timestamps(table_name, **options)
1265
- options[:null] = false if options[:null].nil?
1266
-
1267
- if !options.key?(:precision) && supports_datetime_with_precision?
1268
- options[:precision] = 6
1269
- end
1270
-
1271
- add_column table_name, :created_at, :datetime, **options
1272
- add_column table_name, :updated_at, :datetime, **options
1424
+ fragments = add_timestamps_for_alter(table_name, **options)
1425
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
1273
1426
  end
1274
1427
 
1275
1428
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
@@ -1277,16 +1430,15 @@ module ActiveRecord
1277
1430
  # remove_timestamps(:suppliers)
1278
1431
  #
1279
1432
  def remove_timestamps(table_name, **options)
1280
- remove_column table_name, :updated_at
1281
- remove_column table_name, :created_at
1433
+ remove_columns table_name, :updated_at, :created_at
1282
1434
  end
1283
1435
 
1284
- def update_table_definition(table_name, base) #:nodoc:
1436
+ def update_table_definition(table_name, base) # :nodoc:
1285
1437
  Table.new(table_name, base)
1286
1438
  end
1287
1439
 
1288
1440
  def add_index_options(table_name, column_name, name: nil, if_not_exists: false, internal: false, **options) # :nodoc:
1289
- options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm)
1441
+ options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
1290
1442
 
1291
1443
  column_names = index_column_names(column_name)
1292
1444
 
@@ -1305,6 +1457,8 @@ module ActiveRecord
1305
1457
  where: options[:where],
1306
1458
  type: options[:type],
1307
1459
  using: options[:using],
1460
+ include: options[:include],
1461
+ nulls_not_distinct: options[:nulls_not_distinct],
1308
1462
  comment: options[:comment]
1309
1463
  )
1310
1464
 
@@ -1352,7 +1506,79 @@ module ActiveRecord
1352
1506
  SchemaDumper.create(self, options)
1353
1507
  end
1354
1508
 
1509
+ def use_foreign_keys?
1510
+ supports_foreign_keys? && foreign_keys_enabled?
1511
+ end
1512
+
1513
+ # Returns an instance of SchemaCreation, which can be used to visit a schema definition
1514
+ # object and return DDL.
1515
+ def schema_creation # :nodoc:
1516
+ SchemaCreation.new(self)
1517
+ end
1518
+
1519
+ def bulk_change_table(table_name, operations) # :nodoc:
1520
+ sql_fragments = []
1521
+ non_combinable_operations = []
1522
+
1523
+ operations.each do |command, args|
1524
+ table, arguments = args.shift, args
1525
+ method = :"#{command}_for_alter"
1526
+
1527
+ if respond_to?(method, true)
1528
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1529
+ sql_fragments.concat(sqls)
1530
+ non_combinable_operations.concat(procs)
1531
+ else
1532
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1533
+ non_combinable_operations.each(&:call)
1534
+ sql_fragments = []
1535
+ non_combinable_operations = []
1536
+ send(command, table, *arguments)
1537
+ end
1538
+ end
1539
+
1540
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1541
+ non_combinable_operations.each(&:call)
1542
+ end
1543
+
1544
+ def valid_table_definition_options # :nodoc:
1545
+ [:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
1546
+ end
1547
+
1548
+ def valid_column_definition_options # :nodoc:
1549
+ ColumnDefinition::OPTION_NAMES
1550
+ end
1551
+
1552
+ def valid_primary_key_options # :nodoc:
1553
+ [:limit, :default, :precision]
1554
+ end
1555
+
1556
+ # Returns the maximum length of an index name in bytes.
1557
+ def max_index_name_size
1558
+ 62
1559
+ end
1560
+
1355
1561
  private
1562
+ def generate_index_name(table_name, column)
1563
+ name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
1564
+ return name if name.bytesize <= max_index_name_size
1565
+
1566
+ # Fallback to short version, add hash to ensure uniqueness
1567
+ hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
1568
+ name = "idx_on_#{Array(column) * '_'}"
1569
+
1570
+ short_limit = max_index_name_size - hashed_identifier.bytesize
1571
+ short_name = name.mb_chars.limit(short_limit).to_s
1572
+
1573
+ "#{short_name}#{hashed_identifier}"
1574
+ end
1575
+
1576
+ def validate_change_column_null_argument!(value)
1577
+ unless value == true || value == false
1578
+ raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
1579
+ end
1580
+ end
1581
+
1356
1582
  def column_options_keys
1357
1583
  [:limit, :precision, :scale, :default, :null, :collation, :comment]
1358
1584
  end
@@ -1387,7 +1613,7 @@ module ActiveRecord
1387
1613
 
1388
1614
  checks = []
1389
1615
 
1390
- if !options.key?(:name) && column_name.is_a?(String) && /\W/.match?(column_name)
1616
+ if !options.key?(:name) && expression_column_name?(column_name)
1391
1617
  options[:name] = index_name(table_name, column_name)
1392
1618
  column_names = []
1393
1619
  else
@@ -1396,7 +1622,7 @@ module ActiveRecord
1396
1622
 
1397
1623
  checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1398
1624
 
1399
- if column_names.present?
1625
+ if column_names.present? && !(options.key?(:name) && expression_column_name?(column_names))
1400
1626
  checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
1401
1627
  end
1402
1628
 
@@ -1406,7 +1632,7 @@ module ActiveRecord
1406
1632
 
1407
1633
  if matching_indexes.count > 1
1408
1634
  raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
1409
- "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1635
+ "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1410
1636
  elsif matching_indexes.none?
1411
1637
  raise ArgumentError, "No indexes found on #{table_name} with the options provided."
1412
1638
  else
@@ -1414,11 +1640,11 @@ module ActiveRecord
1414
1640
  end
1415
1641
  end
1416
1642
 
1417
- def rename_table_indexes(table_name, new_name)
1643
+ def rename_table_indexes(table_name, new_name, **options)
1418
1644
  indexes(new_name).each do |index|
1419
- generated_index_name = index_name(table_name, column: index.columns)
1645
+ generated_index_name = index_name(table_name, column: index.columns, **options)
1420
1646
  if generated_index_name == index.name
1421
- rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
1647
+ rename_index new_name, generated_index_name, index_name(new_name, column: index.columns, **options)
1422
1648
  end
1423
1649
  end
1424
1650
  end
@@ -1436,10 +1662,6 @@ module ActiveRecord
1436
1662
  end
1437
1663
  end
1438
1664
 
1439
- def schema_creation
1440
- SchemaCreation.new(self)
1441
- end
1442
-
1443
1665
  def create_table_definition(name, **options)
1444
1666
  TableDefinition.new(self, name, **options)
1445
1667
  end
@@ -1448,8 +1670,12 @@ module ActiveRecord
1448
1670
  AlterTable.new create_table_definition(name)
1449
1671
  end
1450
1672
 
1451
- def extract_table_options!(options)
1452
- options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
1673
+ def validate_create_table_options!(options)
1674
+ unless options[:_skip_validate_options]
1675
+ options
1676
+ .except(:_uses_legacy_table_name, :_skip_validate_options)
1677
+ .assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
1678
+ end
1453
1679
  end
1454
1680
 
1455
1681
  def fetch_type_metadata(sql_type)
@@ -1464,7 +1690,7 @@ module ActiveRecord
1464
1690
  end
1465
1691
 
1466
1692
  def index_column_names(column_names)
1467
- if column_names.is_a?(String) && /\W/.match?(column_names)
1693
+ if expression_column_name?(column_names)
1468
1694
  column_names
1469
1695
  else
1470
1696
  Array(column_names)
@@ -1472,13 +1698,18 @@ module ActiveRecord
1472
1698
  end
1473
1699
 
1474
1700
  def index_name_options(column_names)
1475
- if column_names.is_a?(String) && /\W/.match?(column_names)
1701
+ if expression_column_name?(column_names)
1476
1702
  column_names = column_names.scan(/\w+/).join("_")
1477
1703
  end
1478
1704
 
1479
1705
  { column: column_names }
1480
1706
  end
1481
1707
 
1708
+ # Try to identify whether the given column name is an expression
1709
+ def expression_column_name?(column_name)
1710
+ column_name.is_a?(String) && /\W/.match?(column_name)
1711
+ end
1712
+
1482
1713
  def strip_table_name_prefix_and_suffix(table_name)
1483
1714
  prefix = Base.table_name_prefix
1484
1715
  suffix = Base.table_name_suffix
@@ -1487,15 +1718,16 @@ module ActiveRecord
1487
1718
 
1488
1719
  def foreign_key_name(table_name, options)
1489
1720
  options.fetch(:name) do
1490
- identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1491
- hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1721
+ columns = Array(options.fetch(:column)).map(&:to_s)
1722
+ identifier = "#{table_name}_#{columns * '_and_'}_fk"
1723
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1492
1724
 
1493
1725
  "fk_rails_#{hashed_identifier}"
1494
1726
  end
1495
1727
  end
1496
1728
 
1497
1729
  def foreign_key_for(from_table, **options)
1498
- return unless supports_foreign_keys?
1730
+ return unless use_foreign_keys?
1499
1731
  foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
1500
1732
  end
1501
1733
 
@@ -1512,11 +1744,15 @@ module ActiveRecord
1512
1744
  end
1513
1745
  end
1514
1746
 
1747
+ def foreign_keys_enabled?
1748
+ @config.fetch(:foreign_keys, true)
1749
+ end
1750
+
1515
1751
  def check_constraint_name(table_name, **options)
1516
1752
  options.fetch(:name) do
1517
1753
  expression = options.fetch(:expression)
1518
1754
  identifier = "#{table_name}_#{expression}_chk"
1519
- hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1755
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1520
1756
 
1521
1757
  "chk_rails_#{hashed_identifier}"
1522
1758
  end
@@ -1525,7 +1761,7 @@ module ActiveRecord
1525
1761
  def check_constraint_for(table_name, **options)
1526
1762
  return unless supports_check_constraints?
1527
1763
  chk_name = check_constraint_name(table_name, **options)
1528
- check_constraints(table_name).detect { |chk| chk.name == chk_name }
1764
+ check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
1529
1765
  end
1530
1766
 
1531
1767
  def check_constraint_for!(table_name, expression: nil, **options)
@@ -1539,6 +1775,12 @@ module ActiveRecord
1539
1775
  end
1540
1776
  end
1541
1777
 
1778
+ def validate_table_length!(table_name)
1779
+ if table_name.length > table_name_length
1780
+ raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
1781
+ end
1782
+ end
1783
+
1542
1784
  def extract_new_default_value(default_or_changes)
1543
1785
  if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1544
1786
  default_or_changes[:to]
@@ -1552,29 +1794,8 @@ module ActiveRecord
1552
1794
  column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
1553
1795
  end
1554
1796
 
1555
- def bulk_change_table(table_name, operations)
1556
- sql_fragments = []
1557
- non_combinable_operations = []
1558
-
1559
- operations.each do |command, args|
1560
- table, arguments = args.shift, args
1561
- method = :"#{command}_for_alter"
1562
-
1563
- if respond_to?(method, true)
1564
- sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1565
- sql_fragments << sqls
1566
- non_combinable_operations.concat(procs)
1567
- else
1568
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1569
- non_combinable_operations.each(&:call)
1570
- sql_fragments = []
1571
- non_combinable_operations = []
1572
- send(command, table, *arguments)
1573
- end
1574
- end
1575
-
1576
- execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1577
- non_combinable_operations.each(&:call)
1797
+ def reference_name_for_table(table_name)
1798
+ table_name.to_s.singularize
1578
1799
  end
1579
1800
 
1580
1801
  def add_column_for_alter(table_name, column_name, type, **options)
@@ -1583,6 +1804,11 @@ module ActiveRecord
1583
1804
  schema_creation.accept(AddColumnDefinition.new(cd))
1584
1805
  end
1585
1806
 
1807
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
1808
+ cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
1809
+ schema_creation.accept(cd)
1810
+ end
1811
+
1586
1812
  def rename_column_sql(table_name, column_name, new_column_name)
1587
1813
  "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1588
1814
  end
@@ -1617,8 +1843,8 @@ module ActiveRecord
1617
1843
 
1618
1844
  if versions.is_a?(Array)
1619
1845
  sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1620
- sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1621
- sql << ";\n\n"
1846
+ sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
1847
+ sql << ";"
1622
1848
  sql
1623
1849
  else
1624
1850
  "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"