activerecord 6.1.6 → 7.1.2

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