activerecord 6.1.7 → 7.1.0

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 (307) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1516 -1019
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +17 -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 +50 -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 +35 -31
  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.rb +26 -16
  27. data/lib/active_record/associations/preloader/association.rb +207 -52
  28. data/lib/active_record/associations/preloader/batch.rb +48 -0
  29. data/lib/active_record/associations/preloader/branch.rb +147 -0
  30. data/lib/active_record/associations/preloader/through_association.rb +50 -14
  31. data/lib/active_record/associations/preloader.rb +50 -121
  32. data/lib/active_record/associations/singular_association.rb +9 -3
  33. data/lib/active_record/associations/through_association.rb +25 -14
  34. data/lib/active_record/associations.rb +423 -289
  35. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  36. data/lib/active_record/attribute_assignment.rb +1 -3
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +61 -14
  39. data/lib/active_record/attribute_methods/primary_key.rb +78 -26
  40. data/lib/active_record/attribute_methods/query.rb +31 -19
  41. data/lib/active_record/attribute_methods/read.rb +25 -10
  42. data/lib/active_record/attribute_methods/serialization.rb +194 -37
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  44. data/lib/active_record/attribute_methods/write.rb +10 -13
  45. data/lib/active_record/attribute_methods.rb +121 -40
  46. data/lib/active_record/attributes.rb +27 -38
  47. data/lib/active_record/autosave_association.rb +61 -30
  48. data/lib/active_record/base.rb +25 -2
  49. data/lib/active_record/callbacks.rb +18 -34
  50. data/lib/active_record/coders/column_serializer.rb +61 -0
  51. data/lib/active_record/coders/json.rb +1 -1
  52. data/lib/active_record/coders/yaml_column.rb +70 -46
  53. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
  54. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -51
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -136
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +622 -149
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
  69. data/lib/active_record/connection_adapters/column.rb +13 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -11
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +18 -1
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -52
  83. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  87. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  88. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  91. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +381 -69
  98. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  99. data/lib/active_record/connection_adapters/postgresql_adapter.rb +492 -230
  100. data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
  101. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  102. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +65 -53
  103. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
  104. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
  107. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  108. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  109. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  110. data/lib/active_record/connection_adapters.rb +9 -6
  111. data/lib/active_record/connection_handling.rb +107 -136
  112. data/lib/active_record/core.rb +194 -224
  113. data/lib/active_record/counter_cache.rb +46 -25
  114. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  115. data/lib/active_record/database_configurations/database_config.rb +21 -12
  116. data/lib/active_record/database_configurations/hash_config.rb +84 -16
  117. data/lib/active_record/database_configurations/url_config.rb +18 -12
  118. data/lib/active_record/database_configurations.rb +95 -59
  119. data/lib/active_record/delegated_type.rb +61 -15
  120. data/lib/active_record/deprecator.rb +7 -0
  121. data/lib/active_record/destroy_association_async_job.rb +3 -1
  122. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  123. data/lib/active_record/dynamic_matchers.rb +1 -1
  124. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  125. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  126. data/lib/active_record/encryption/cipher.rb +53 -0
  127. data/lib/active_record/encryption/config.rb +68 -0
  128. data/lib/active_record/encryption/configurable.rb +60 -0
  129. data/lib/active_record/encryption/context.rb +42 -0
  130. data/lib/active_record/encryption/contexts.rb +76 -0
  131. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  132. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  133. data/lib/active_record/encryption/encryptable_record.rb +224 -0
  134. data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
  135. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  136. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  137. data/lib/active_record/encryption/encryptor.rb +155 -0
  138. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  139. data/lib/active_record/encryption/errors.rb +15 -0
  140. data/lib/active_record/encryption/extended_deterministic_queries.rb +172 -0
  141. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  142. data/lib/active_record/encryption/key.rb +28 -0
  143. data/lib/active_record/encryption/key_generator.rb +53 -0
  144. data/lib/active_record/encryption/key_provider.rb +46 -0
  145. data/lib/active_record/encryption/message.rb +33 -0
  146. data/lib/active_record/encryption/message_serializer.rb +92 -0
  147. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  148. data/lib/active_record/encryption/properties.rb +76 -0
  149. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  150. data/lib/active_record/encryption/scheme.rb +96 -0
  151. data/lib/active_record/encryption.rb +56 -0
  152. data/lib/active_record/enum.rb +156 -62
  153. data/lib/active_record/errors.rb +171 -15
  154. data/lib/active_record/explain.rb +23 -3
  155. data/lib/active_record/explain_registry.rb +11 -6
  156. data/lib/active_record/explain_subscriber.rb +1 -1
  157. data/lib/active_record/fixture_set/file.rb +15 -1
  158. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  159. data/lib/active_record/fixture_set/render_context.rb +2 -0
  160. data/lib/active_record/fixture_set/table_row.rb +70 -14
  161. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  162. data/lib/active_record/fixtures.rb +131 -86
  163. data/lib/active_record/future_result.rb +164 -0
  164. data/lib/active_record/gem_version.rb +3 -3
  165. data/lib/active_record/inheritance.rb +81 -29
  166. data/lib/active_record/insert_all.rb +133 -20
  167. data/lib/active_record/integration.rb +11 -10
  168. data/lib/active_record/internal_metadata.rb +117 -33
  169. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  170. data/lib/active_record/locking/optimistic.rb +36 -21
  171. data/lib/active_record/locking/pessimistic.rb +15 -6
  172. data/lib/active_record/log_subscriber.rb +52 -19
  173. data/lib/active_record/marshalling.rb +56 -0
  174. data/lib/active_record/message_pack.rb +124 -0
  175. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  176. data/lib/active_record/middleware/database_selector.rb +23 -13
  177. data/lib/active_record/middleware/shard_selector.rb +62 -0
  178. data/lib/active_record/migration/command_recorder.rb +108 -13
  179. data/lib/active_record/migration/compatibility.rb +221 -48
  180. data/lib/active_record/migration/default_strategy.rb +23 -0
  181. data/lib/active_record/migration/execution_strategy.rb +19 -0
  182. data/lib/active_record/migration/join_table.rb +1 -1
  183. data/lib/active_record/migration.rb +355 -171
  184. data/lib/active_record/model_schema.rb +116 -97
  185. data/lib/active_record/nested_attributes.rb +36 -15
  186. data/lib/active_record/no_touching.rb +3 -3
  187. data/lib/active_record/normalization.rb +159 -0
  188. data/lib/active_record/persistence.rb +405 -85
  189. data/lib/active_record/promise.rb +84 -0
  190. data/lib/active_record/query_cache.rb +3 -21
  191. data/lib/active_record/query_logs.rb +174 -0
  192. data/lib/active_record/query_logs_formatter.rb +41 -0
  193. data/lib/active_record/querying.rb +29 -6
  194. data/lib/active_record/railtie.rb +219 -43
  195. data/lib/active_record/railties/controller_runtime.rb +13 -9
  196. data/lib/active_record/railties/databases.rake +185 -249
  197. data/lib/active_record/railties/job_runtime.rb +23 -0
  198. data/lib/active_record/readonly_attributes.rb +41 -3
  199. data/lib/active_record/reflection.rb +229 -80
  200. data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
  201. data/lib/active_record/relation/batches.rb +192 -63
  202. data/lib/active_record/relation/calculations.rb +211 -90
  203. data/lib/active_record/relation/delegation.rb +27 -13
  204. data/lib/active_record/relation/finder_methods.rb +108 -51
  205. data/lib/active_record/relation/merger.rb +22 -13
  206. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  207. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  208. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  209. data/lib/active_record/relation/predicate_builder.rb +27 -20
  210. data/lib/active_record/relation/query_attribute.rb +30 -12
  211. data/lib/active_record/relation/query_methods.rb +654 -127
  212. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  213. data/lib/active_record/relation/spawn_methods.rb +20 -3
  214. data/lib/active_record/relation/where_clause.rb +10 -19
  215. data/lib/active_record/relation.rb +262 -120
  216. data/lib/active_record/result.rb +37 -11
  217. data/lib/active_record/runtime_registry.rb +18 -13
  218. data/lib/active_record/sanitization.rb +65 -20
  219. data/lib/active_record/schema.rb +36 -22
  220. data/lib/active_record/schema_dumper.rb +73 -24
  221. data/lib/active_record/schema_migration.rb +68 -33
  222. data/lib/active_record/scoping/default.rb +72 -15
  223. data/lib/active_record/scoping/named.rb +5 -13
  224. data/lib/active_record/scoping.rb +65 -34
  225. data/lib/active_record/secure_password.rb +60 -0
  226. data/lib/active_record/secure_token.rb +21 -3
  227. data/lib/active_record/serialization.rb +6 -1
  228. data/lib/active_record/signed_id.rb +10 -8
  229. data/lib/active_record/store.rb +10 -10
  230. data/lib/active_record/suppressor.rb +13 -15
  231. data/lib/active_record/table_metadata.rb +16 -3
  232. data/lib/active_record/tasks/database_tasks.rb +225 -136
  233. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  234. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  235. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  236. data/lib/active_record/test_databases.rb +1 -1
  237. data/lib/active_record/test_fixtures.rb +116 -96
  238. data/lib/active_record/timestamp.rb +28 -17
  239. data/lib/active_record/token_for.rb +113 -0
  240. data/lib/active_record/touch_later.rb +11 -6
  241. data/lib/active_record/transactions.rb +48 -27
  242. data/lib/active_record/translation.rb +3 -3
  243. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  244. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  245. data/lib/active_record/type/internal/timezone.rb +7 -2
  246. data/lib/active_record/type/serialized.rb +9 -5
  247. data/lib/active_record/type/time.rb +4 -0
  248. data/lib/active_record/type/type_map.rb +17 -20
  249. data/lib/active_record/type.rb +1 -2
  250. data/lib/active_record/validations/absence.rb +1 -1
  251. data/lib/active_record/validations/associated.rb +4 -4
  252. data/lib/active_record/validations/numericality.rb +5 -4
  253. data/lib/active_record/validations/presence.rb +5 -28
  254. data/lib/active_record/validations/uniqueness.rb +51 -6
  255. data/lib/active_record/validations.rb +8 -4
  256. data/lib/active_record/version.rb +1 -1
  257. data/lib/active_record.rb +335 -32
  258. data/lib/arel/attributes/attribute.rb +0 -8
  259. data/lib/arel/crud.rb +28 -22
  260. data/lib/arel/delete_manager.rb +18 -4
  261. data/lib/arel/errors.rb +10 -0
  262. data/lib/arel/factory_methods.rb +4 -0
  263. data/lib/arel/filter_predications.rb +9 -0
  264. data/lib/arel/insert_manager.rb +2 -3
  265. data/lib/arel/nodes/and.rb +4 -0
  266. data/lib/arel/nodes/binary.rb +6 -1
  267. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  268. data/lib/arel/nodes/casted.rb +1 -1
  269. data/lib/arel/nodes/cte.rb +36 -0
  270. data/lib/arel/nodes/delete_statement.rb +12 -13
  271. data/lib/arel/nodes/filter.rb +10 -0
  272. data/lib/arel/nodes/fragments.rb +35 -0
  273. data/lib/arel/nodes/function.rb +1 -0
  274. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  275. data/lib/arel/nodes/insert_statement.rb +2 -2
  276. data/lib/arel/nodes/leading_join.rb +8 -0
  277. data/lib/arel/nodes/node.rb +111 -2
  278. data/lib/arel/nodes/select_core.rb +2 -2
  279. data/lib/arel/nodes/select_statement.rb +2 -2
  280. data/lib/arel/nodes/sql_literal.rb +6 -0
  281. data/lib/arel/nodes/table_alias.rb +4 -0
  282. data/lib/arel/nodes/update_statement.rb +8 -3
  283. data/lib/arel/nodes.rb +5 -0
  284. data/lib/arel/predications.rb +13 -3
  285. data/lib/arel/select_manager.rb +10 -4
  286. data/lib/arel/table.rb +9 -6
  287. data/lib/arel/tree_manager.rb +0 -12
  288. data/lib/arel/update_manager.rb +18 -4
  289. data/lib/arel/visitors/dot.rb +80 -90
  290. data/lib/arel/visitors/mysql.rb +16 -3
  291. data/lib/arel/visitors/postgresql.rb +0 -10
  292. data/lib/arel/visitors/to_sql.rb +139 -19
  293. data/lib/arel/visitors/visitor.rb +2 -2
  294. data/lib/arel.rb +18 -3
  295. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  296. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  297. data/lib/rails/generators/active_record/migration.rb +3 -1
  298. data/lib/rails/generators/active_record/model/USAGE +113 -0
  299. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  300. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  301. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  302. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  303. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  304. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  305. metadata +92 -13
  306. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  307. 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,10 @@ 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
+ generate_index_name(table_name, options[:column])
908
967
  elsif options[:name]
909
968
  options[:name]
910
969
  else
@@ -970,7 +1029,7 @@ module ActiveRecord
970
1029
  # add_reference(:products, :supplier, foreign_key: { to_table: :firms })
971
1030
  #
972
1031
  def add_reference(table_name, ref_name, **options)
973
- ReferenceDefinition.new(ref_name, **options).add_to(update_table_definition(table_name, self))
1032
+ ReferenceDefinition.new(ref_name, **options).add(table_name, self)
974
1033
  end
975
1034
  alias :add_belongs_to :add_reference
976
1035
 
@@ -990,19 +1049,21 @@ module ActiveRecord
990
1049
  # remove_reference(:products, :user, foreign_key: true)
991
1050
  #
992
1051
  def remove_reference(table_name, ref_name, foreign_key: false, polymorphic: false, **options)
1052
+ conditional_options = options.slice(:if_exists, :if_not_exists)
1053
+
993
1054
  if foreign_key
994
1055
  reference_name = Base.pluralize_table_names ? ref_name.to_s.pluralize : ref_name
995
1056
  if foreign_key.is_a?(Hash)
996
- foreign_key_options = foreign_key
1057
+ foreign_key_options = foreign_key.merge(conditional_options)
997
1058
  else
998
- foreign_key_options = { to_table: reference_name }
1059
+ foreign_key_options = { to_table: reference_name, **conditional_options }
999
1060
  end
1000
1061
  foreign_key_options[:column] ||= "#{ref_name}_id"
1001
1062
  remove_foreign_key(table_name, **foreign_key_options)
1002
1063
  end
1003
1064
 
1004
- remove_column(table_name, "#{ref_name}_id")
1005
- remove_column(table_name, "#{ref_name}_type") if polymorphic
1065
+ remove_column(table_name, "#{ref_name}_id", **conditional_options)
1066
+ remove_column(table_name, "#{ref_name}_type", **conditional_options) if polymorphic
1006
1067
  end
1007
1068
  alias :remove_belongs_to :remove_reference
1008
1069
 
@@ -1027,6 +1088,10 @@ module ActiveRecord
1027
1088
  #
1028
1089
  # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_e74ce85cbc FOREIGN KEY ("author_id") REFERENCES "authors" ("id")
1029
1090
  #
1091
+ # ====== Creating a foreign key, ignoring method call if the foreign key exists
1092
+ #
1093
+ # add_foreign_key(:articles, :authors, if_not_exists: true)
1094
+ #
1030
1095
  # ====== Creating a foreign key on a specific column
1031
1096
  #
1032
1097
  # add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id"
@@ -1035,6 +1100,16 @@ module ActiveRecord
1035
1100
  #
1036
1101
  # ALTER TABLE "articles" ADD CONSTRAINT fk_rails_58ca3d3a82 FOREIGN KEY ("author_id") REFERENCES "users" ("lng_id")
1037
1102
  #
1103
+ # ====== Creating a composite foreign key
1104
+ #
1105
+ # Assuming "carts" table has "(shop_id, user_id)" as a primary key.
1106
+ #
1107
+ # add_foreign_key :orders, :carts, primary_key: [:shop_id, :user_id]
1108
+ #
1109
+ # generates:
1110
+ #
1111
+ # ALTER TABLE "orders" ADD CONSTRAINT fk_rails_6f5e4cb3a4 FOREIGN KEY ("cart_shop_id", "cart_user_id") REFERENCES "carts" ("shop_id", "user_id")
1112
+ #
1038
1113
  # ====== Creating a cascading foreign key
1039
1114
  #
1040
1115
  # add_foreign_key :articles, :authors, on_delete: :cascade
@@ -1045,19 +1120,28 @@ module ActiveRecord
1045
1120
  #
1046
1121
  # The +options+ hash can include the following keys:
1047
1122
  # [<tt>:column</tt>]
1048
- # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>
1123
+ # The foreign key column name on +from_table+. Defaults to <tt>to_table.singularize + "_id"</tt>.
1124
+ # Pass an array to create a composite foreign key.
1049
1125
  # [<tt>:primary_key</tt>]
1050
1126
  # The primary key column name on +to_table+. Defaults to +id+.
1127
+ # Pass an array to create a composite foreign key.
1051
1128
  # [<tt>:name</tt>]
1052
1129
  # The constraint name. Defaults to <tt>fk_rails_<identifier></tt>.
1053
1130
  # [<tt>:on_delete</tt>]
1054
- # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
1131
+ # Action that happens <tt>ON DELETE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
1055
1132
  # [<tt>:on_update</tt>]
1056
- # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+ and +:restrict+
1133
+ # Action that happens <tt>ON UPDATE</tt>. Valid values are +:nullify+, +:cascade+, and +:restrict+
1134
+ # [<tt>:if_not_exists</tt>]
1135
+ # Specifies if the foreign key already exists to not try to re-add it. This will avoid
1136
+ # duplicate column errors.
1057
1137
  # [<tt>:validate</tt>]
1058
1138
  # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1139
+ # [<tt>:deferrable</tt>]
1140
+ # (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
1141
+ # +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
1059
1142
  def add_foreign_key(from_table, to_table, **options)
1060
- return unless supports_foreign_keys?
1143
+ return unless use_foreign_keys?
1144
+ return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table, **options.slice(:column))
1061
1145
 
1062
1146
  options = foreign_key_options(from_table, to_table, options)
1063
1147
  at = create_alter_table from_table
@@ -1087,12 +1171,18 @@ module ActiveRecord
1087
1171
  #
1088
1172
  # remove_foreign_key :accounts, name: :special_fk_name
1089
1173
  #
1174
+ # Checks if the foreign key exists before trying to remove it. Will silently ignore indexes that
1175
+ # don't exist.
1176
+ #
1177
+ # remove_foreign_key :accounts, :branches, if_exists: true
1178
+ #
1090
1179
  # The +options+ hash accepts the same keys as SchemaStatements#add_foreign_key
1091
1180
  # with an addition of
1092
1181
  # [<tt>:to_table</tt>]
1093
1182
  # The name of the table that contains the referenced primary key.
1094
1183
  def remove_foreign_key(from_table, to_table = nil, **options)
1095
- return unless supports_foreign_keys?
1184
+ return unless use_foreign_keys?
1185
+ return if options.delete(:if_exists) == true && !foreign_key_exists?(from_table, to_table)
1096
1186
 
1097
1187
  fk_name_to_delete = foreign_key_for!(from_table, to_table: to_table, **options).name
1098
1188
 
@@ -1117,15 +1207,33 @@ module ActiveRecord
1117
1207
  foreign_key_for(from_table, to_table: to_table, **options).present?
1118
1208
  end
1119
1209
 
1120
- def foreign_key_column_for(table_name) # :nodoc:
1210
+ def foreign_key_column_for(table_name, column_name) # :nodoc:
1121
1211
  name = strip_table_name_prefix_and_suffix(table_name)
1122
- "#{name.singularize}_id"
1212
+ "#{name.singularize}_#{column_name}"
1123
1213
  end
1124
1214
 
1125
1215
  def foreign_key_options(from_table, to_table, options) # :nodoc:
1126
1216
  options = options.dup
1127
- options[:column] ||= foreign_key_column_for(to_table)
1217
+
1218
+ if options[:primary_key].is_a?(Array)
1219
+ options[:column] ||= options[:primary_key].map do |pk_column|
1220
+ foreign_key_column_for(to_table, pk_column)
1221
+ end
1222
+ else
1223
+ options[:column] ||= foreign_key_column_for(to_table, "id")
1224
+ end
1225
+
1128
1226
  options[:name] ||= foreign_key_name(from_table, options)
1227
+
1228
+ if options[:column].is_a?(Array) || options[:primary_key].is_a?(Array)
1229
+ if Array(options[:primary_key]).size != Array(options[:column]).size
1230
+ raise ArgumentError, <<~MSG.squish
1231
+ For composite primary keys, specify :column and :primary_key, where
1232
+ :column must reference all the :primary_key columns from #{to_table.inspect}
1233
+ MSG
1234
+ end
1235
+ end
1236
+
1129
1237
  options
1130
1238
  end
1131
1239
 
@@ -1147,12 +1255,16 @@ module ActiveRecord
1147
1255
  # The +options+ hash can include the following keys:
1148
1256
  # [<tt>:name</tt>]
1149
1257
  # The constraint name. Defaults to <tt>chk_rails_<identifier></tt>.
1258
+ # [<tt>:if_not_exists</tt>]
1259
+ # Silently ignore if the constraint already exists, rather than raise an error.
1150
1260
  # [<tt>:validate</tt>]
1151
1261
  # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1152
- def add_check_constraint(table_name, expression, **options)
1262
+ def add_check_constraint(table_name, expression, if_not_exists: false, **options)
1153
1263
  return unless supports_check_constraints?
1154
1264
 
1155
1265
  options = check_constraint_options(table_name, expression, options)
1266
+ return if if_not_exists && check_constraint_exists?(table_name, **options)
1267
+
1156
1268
  at = create_alter_table(table_name)
1157
1269
  at.add_check_constraint(expression, options)
1158
1270
 
@@ -1165,16 +1277,24 @@ module ActiveRecord
1165
1277
  options
1166
1278
  end
1167
1279
 
1168
- # Removes the given check constraint from the table.
1280
+ # Removes the given check constraint from the table. Removing a check constraint
1281
+ # that does not exist will raise an error.
1169
1282
  #
1170
1283
  # remove_check_constraint :products, name: "price_check"
1171
1284
  #
1285
+ # To silently ignore a non-existent check constraint rather than raise an error,
1286
+ # use the +if_exists+ option.
1287
+ #
1288
+ # remove_check_constraint :products, name: "price_check", if_exists: true
1289
+ #
1172
1290
  # The +expression+ parameter will be ignored if present. It can be helpful
1173
1291
  # to provide this in a migration's +change+ method so it can be reverted.
1174
1292
  # In that case, +expression+ will be used by #add_check_constraint.
1175
- def remove_check_constraint(table_name, expression = nil, **options)
1293
+ def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
1176
1294
  return unless supports_check_constraints?
1177
1295
 
1296
+ return if if_exists && !check_constraint_exists?(table_name, **options)
1297
+
1178
1298
  chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name
1179
1299
 
1180
1300
  at = create_alter_table(table_name)
@@ -1183,8 +1303,20 @@ module ActiveRecord
1183
1303
  execute schema_creation.accept(at)
1184
1304
  end
1185
1305
 
1306
+
1307
+ # Checks to see if a check constraint exists on a table for a given check constraint definition.
1308
+ #
1309
+ # check_constraint_exists?(:products, name: "price_check")
1310
+ #
1311
+ def check_constraint_exists?(table_name, **options)
1312
+ if !options.key?(:name) && !options.key?(:expression)
1313
+ raise ArgumentError, "At least one of :name or :expression must be supplied"
1314
+ end
1315
+ check_constraint_for(table_name, **options).present?
1316
+ end
1317
+
1186
1318
  def dump_schema_information # :nodoc:
1187
- versions = schema_migration.all_versions
1319
+ versions = schema_migration.versions
1188
1320
  insert_versions_sql(versions) if versions.any?
1189
1321
  end
1190
1322
 
@@ -1256,20 +1388,39 @@ module ActiveRecord
1256
1388
  columns
1257
1389
  end
1258
1390
 
1391
+ def distinct_relation_for_primary_key(relation) # :nodoc:
1392
+ primary_key_columns = Array(relation.primary_key).map do |column|
1393
+ visitor.compile(relation.table[column])
1394
+ end
1395
+
1396
+ values = columns_for_distinct(
1397
+ primary_key_columns,
1398
+ relation.order_values
1399
+ )
1400
+
1401
+ limited = relation.reselect(values).distinct!
1402
+ limited_ids = select_rows(limited.arel, "SQL").map do |results|
1403
+ results.last(Array(relation.primary_key).length) # ignores order values for MySQL and Postgres
1404
+ end
1405
+
1406
+ if limited_ids.empty?
1407
+ relation.none!
1408
+ else
1409
+ relation.where!(**Array(relation.primary_key).zip(limited_ids.transpose).to_h)
1410
+ end
1411
+
1412
+ relation.limit_value = relation.offset_value = nil
1413
+ relation
1414
+ end
1415
+
1259
1416
  # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+.
1260
1417
  # Additional options (like +:null+) are forwarded to #add_column.
1261
1418
  #
1262
1419
  # add_timestamps(:suppliers, null: true)
1263
1420
  #
1264
1421
  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
1422
+ fragments = add_timestamps_for_alter(table_name, **options)
1423
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{fragments.join(', ')}"
1273
1424
  end
1274
1425
 
1275
1426
  # Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
@@ -1277,16 +1428,15 @@ module ActiveRecord
1277
1428
  # remove_timestamps(:suppliers)
1278
1429
  #
1279
1430
  def remove_timestamps(table_name, **options)
1280
- remove_column table_name, :updated_at
1281
- remove_column table_name, :created_at
1431
+ remove_columns table_name, :updated_at, :created_at
1282
1432
  end
1283
1433
 
1284
- def update_table_definition(table_name, base) #:nodoc:
1434
+ def update_table_definition(table_name, base) # :nodoc:
1285
1435
  Table.new(table_name, base)
1286
1436
  end
1287
1437
 
1288
1438
  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)
1439
+ options.assert_valid_keys(:unique, :length, :order, :opclass, :where, :type, :using, :comment, :algorithm, :include, :nulls_not_distinct)
1290
1440
 
1291
1441
  column_names = index_column_names(column_name)
1292
1442
 
@@ -1305,6 +1455,8 @@ module ActiveRecord
1305
1455
  where: options[:where],
1306
1456
  type: options[:type],
1307
1457
  using: options[:using],
1458
+ include: options[:include],
1459
+ nulls_not_distinct: options[:nulls_not_distinct],
1308
1460
  comment: options[:comment]
1309
1461
  )
1310
1462
 
@@ -1352,7 +1504,79 @@ module ActiveRecord
1352
1504
  SchemaDumper.create(self, options)
1353
1505
  end
1354
1506
 
1507
+ def use_foreign_keys?
1508
+ supports_foreign_keys? && foreign_keys_enabled?
1509
+ end
1510
+
1511
+ # Returns an instance of SchemaCreation, which can be used to visit a schema definition
1512
+ # object and return DDL.
1513
+ def schema_creation # :nodoc:
1514
+ SchemaCreation.new(self)
1515
+ end
1516
+
1517
+ def bulk_change_table(table_name, operations) # :nodoc:
1518
+ sql_fragments = []
1519
+ non_combinable_operations = []
1520
+
1521
+ operations.each do |command, args|
1522
+ table, arguments = args.shift, args
1523
+ method = :"#{command}_for_alter"
1524
+
1525
+ if respond_to?(method, true)
1526
+ sqls, procs = Array(send(method, table, *arguments)).partition { |v| v.is_a?(String) }
1527
+ sql_fragments.concat(sqls)
1528
+ non_combinable_operations.concat(procs)
1529
+ else
1530
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1531
+ non_combinable_operations.each(&:call)
1532
+ sql_fragments = []
1533
+ non_combinable_operations = []
1534
+ send(command, table, *arguments)
1535
+ end
1536
+ end
1537
+
1538
+ execute "ALTER TABLE #{quote_table_name(table_name)} #{sql_fragments.join(", ")}" unless sql_fragments.empty?
1539
+ non_combinable_operations.each(&:call)
1540
+ end
1541
+
1542
+ def valid_table_definition_options # :nodoc:
1543
+ [:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation]
1544
+ end
1545
+
1546
+ def valid_column_definition_options # :nodoc:
1547
+ ColumnDefinition::OPTION_NAMES
1548
+ end
1549
+
1550
+ def valid_primary_key_options # :nodoc:
1551
+ [:limit, :default, :precision]
1552
+ end
1553
+
1554
+ # Returns the maximum length of an index name in bytes.
1555
+ def max_index_name_size
1556
+ 62
1557
+ end
1558
+
1355
1559
  private
1560
+ def generate_index_name(table_name, column)
1561
+ name = "index_#{table_name}_on_#{Array(column) * '_and_'}"
1562
+ return name if name.bytesize <= max_index_name_size
1563
+
1564
+ # Fallback to short version, add hash to ensure uniqueness
1565
+ hashed_identifier = "_" + OpenSSL::Digest::SHA256.hexdigest(name).first(10)
1566
+ name = "idx_on_#{Array(column) * '_'}"
1567
+
1568
+ short_limit = max_index_name_size - hashed_identifier.bytesize
1569
+ short_name = name.mb_chars.limit(short_limit).to_s
1570
+
1571
+ "#{short_name}#{hashed_identifier}"
1572
+ end
1573
+
1574
+ def validate_change_column_null_argument!(value)
1575
+ unless value == true || value == false
1576
+ raise ArgumentError, "change_column_null expects a boolean value (true for NULL, false for NOT NULL). Got: #{value.inspect}"
1577
+ end
1578
+ end
1579
+
1356
1580
  def column_options_keys
1357
1581
  [:limit, :precision, :scale, :default, :null, :collation, :comment]
1358
1582
  end
@@ -1387,7 +1611,7 @@ module ActiveRecord
1387
1611
 
1388
1612
  checks = []
1389
1613
 
1390
- if !options.key?(:name) && column_name.is_a?(String) && /\W/.match?(column_name)
1614
+ if !options.key?(:name) && expression_column_name?(column_name)
1391
1615
  options[:name] = index_name(table_name, column_name)
1392
1616
  column_names = []
1393
1617
  else
@@ -1396,7 +1620,7 @@ module ActiveRecord
1396
1620
 
1397
1621
  checks << lambda { |i| i.name == options[:name].to_s } if options.key?(:name)
1398
1622
 
1399
- if column_names.present?
1623
+ if column_names.present? && !(options.key?(:name) && expression_column_name?(column_names))
1400
1624
  checks << lambda { |i| index_name(table_name, i.columns) == index_name(table_name, column_names) }
1401
1625
  end
1402
1626
 
@@ -1406,7 +1630,7 @@ module ActiveRecord
1406
1630
 
1407
1631
  if matching_indexes.count > 1
1408
1632
  raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
1409
- "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1633
+ "Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
1410
1634
  elsif matching_indexes.none?
1411
1635
  raise ArgumentError, "No indexes found on #{table_name} with the options provided."
1412
1636
  else
@@ -1436,10 +1660,6 @@ module ActiveRecord
1436
1660
  end
1437
1661
  end
1438
1662
 
1439
- def schema_creation
1440
- SchemaCreation.new(self)
1441
- end
1442
-
1443
1663
  def create_table_definition(name, **options)
1444
1664
  TableDefinition.new(self, name, **options)
1445
1665
  end
@@ -1448,8 +1668,12 @@ module ActiveRecord
1448
1668
  AlterTable.new create_table_definition(name)
1449
1669
  end
1450
1670
 
1451
- def extract_table_options!(options)
1452
- options.extract!(:temporary, :if_not_exists, :options, :as, :comment, :charset, :collation)
1671
+ def validate_create_table_options!(options)
1672
+ unless options[:_skip_validate_options]
1673
+ options
1674
+ .except(:_uses_legacy_table_name, :_skip_validate_options)
1675
+ .assert_valid_keys(valid_table_definition_options, valid_primary_key_options)
1676
+ end
1453
1677
  end
1454
1678
 
1455
1679
  def fetch_type_metadata(sql_type)
@@ -1464,7 +1688,7 @@ module ActiveRecord
1464
1688
  end
1465
1689
 
1466
1690
  def index_column_names(column_names)
1467
- if column_names.is_a?(String) && /\W/.match?(column_names)
1691
+ if expression_column_name?(column_names)
1468
1692
  column_names
1469
1693
  else
1470
1694
  Array(column_names)
@@ -1472,13 +1696,18 @@ module ActiveRecord
1472
1696
  end
1473
1697
 
1474
1698
  def index_name_options(column_names)
1475
- if column_names.is_a?(String) && /\W/.match?(column_names)
1699
+ if expression_column_name?(column_names)
1476
1700
  column_names = column_names.scan(/\w+/).join("_")
1477
1701
  end
1478
1702
 
1479
1703
  { column: column_names }
1480
1704
  end
1481
1705
 
1706
+ # Try to identify whether the given column name is an expression
1707
+ def expression_column_name?(column_name)
1708
+ column_name.is_a?(String) && /\W/.match?(column_name)
1709
+ end
1710
+
1482
1711
  def strip_table_name_prefix_and_suffix(table_name)
1483
1712
  prefix = Base.table_name_prefix
1484
1713
  suffix = Base.table_name_suffix
@@ -1487,15 +1716,16 @@ module ActiveRecord
1487
1716
 
1488
1717
  def foreign_key_name(table_name, options)
1489
1718
  options.fetch(:name) do
1490
- identifier = "#{table_name}_#{options.fetch(:column)}_fk"
1491
- hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1719
+ columns = Array(options.fetch(:column)).map(&:to_s)
1720
+ identifier = "#{table_name}_#{columns * '_and_'}_fk"
1721
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1492
1722
 
1493
1723
  "fk_rails_#{hashed_identifier}"
1494
1724
  end
1495
1725
  end
1496
1726
 
1497
1727
  def foreign_key_for(from_table, **options)
1498
- return unless supports_foreign_keys?
1728
+ return unless use_foreign_keys?
1499
1729
  foreign_keys(from_table).detect { |fk| fk.defined_for?(**options) }
1500
1730
  end
1501
1731
 
@@ -1512,11 +1742,15 @@ module ActiveRecord
1512
1742
  end
1513
1743
  end
1514
1744
 
1745
+ def foreign_keys_enabled?
1746
+ @config.fetch(:foreign_keys, true)
1747
+ end
1748
+
1515
1749
  def check_constraint_name(table_name, **options)
1516
1750
  options.fetch(:name) do
1517
1751
  expression = options.fetch(:expression)
1518
1752
  identifier = "#{table_name}_#{expression}_chk"
1519
- hashed_identifier = Digest::SHA256.hexdigest(identifier).first(10)
1753
+ hashed_identifier = OpenSSL::Digest::SHA256.hexdigest(identifier).first(10)
1520
1754
 
1521
1755
  "chk_rails_#{hashed_identifier}"
1522
1756
  end
@@ -1525,7 +1759,7 @@ module ActiveRecord
1525
1759
  def check_constraint_for(table_name, **options)
1526
1760
  return unless supports_check_constraints?
1527
1761
  chk_name = check_constraint_name(table_name, **options)
1528
- check_constraints(table_name).detect { |chk| chk.name == chk_name }
1762
+ check_constraints(table_name).detect { |chk| chk.defined_for?(name: chk_name, **options) }
1529
1763
  end
1530
1764
 
1531
1765
  def check_constraint_for!(table_name, expression: nil, **options)
@@ -1539,6 +1773,12 @@ module ActiveRecord
1539
1773
  end
1540
1774
  end
1541
1775
 
1776
+ def validate_table_length!(table_name)
1777
+ if table_name.length > table_name_length
1778
+ raise ArgumentError, "Table name '#{table_name}' is too long; the limit is #{table_name_length} characters"
1779
+ end
1780
+ end
1781
+
1542
1782
  def extract_new_default_value(default_or_changes)
1543
1783
  if default_or_changes.is_a?(Hash) && default_or_changes.has_key?(:from) && default_or_changes.has_key?(:to)
1544
1784
  default_or_changes[:to]
@@ -1552,29 +1792,8 @@ module ActiveRecord
1552
1792
  column_name.nil? && options.key?(:name) && options.except(:name, :algorithm).empty?
1553
1793
  end
1554
1794
 
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)
1795
+ def reference_name_for_table(table_name)
1796
+ table_name.to_s.singularize
1578
1797
  end
1579
1798
 
1580
1799
  def add_column_for_alter(table_name, column_name, type, **options)
@@ -1583,6 +1802,11 @@ module ActiveRecord
1583
1802
  schema_creation.accept(AddColumnDefinition.new(cd))
1584
1803
  end
1585
1804
 
1805
+ def change_column_default_for_alter(table_name, column_name, default_or_changes)
1806
+ cd = build_change_column_default_definition(table_name, column_name, default_or_changes)
1807
+ schema_creation.accept(cd)
1808
+ end
1809
+
1586
1810
  def rename_column_sql(table_name, column_name, new_column_name)
1587
1811
  "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
1588
1812
  end
@@ -1617,8 +1841,8 @@ module ActiveRecord
1617
1841
 
1618
1842
  if versions.is_a?(Array)
1619
1843
  sql = +"INSERT INTO #{sm_table} (version) VALUES\n"
1620
- sql << versions.map { |v| "(#{quote(v)})" }.join(",\n")
1621
- sql << ";\n\n"
1844
+ sql << versions.reverse.map { |v| "(#{quote(v)})" }.join(",\n")
1845
+ sql << ";"
1622
1846
  sql
1623
1847
  else
1624
1848
  "INSERT INTO #{sm_table} (version) VALUES (#{quote(versions)});"