activerecord 7.0.8.7 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +781 -1777
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +30 -30
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +31 -23
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +40 -9
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +35 -21
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +4 -3
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +153 -33
  47. data/lib/active_record/attributes.rb +96 -71
  48. data/lib/active_record/autosave_association.rb +81 -39
  49. data/lib/active_record/base.rb +11 -7
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +343 -91
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +229 -64
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +142 -12
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +60 -55
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +108 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -203
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -45
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +51 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +101 -105
  110. data/lib/active_record/core.rb +273 -178
  111. data/lib/active_record/counter_cache.rb +69 -35
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +56 -27
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +46 -22
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
  130. data/lib/active_record/encryption/encryptor.rb +35 -19
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +130 -28
  143. data/lib/active_record/errors.rb +154 -34
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +48 -10
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +236 -118
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +96 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +35 -10
  178. data/lib/active_record/railtie.rb +131 -87
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +147 -155
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +270 -108
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +97 -21
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +3 -2
  196. data/lib/active_record/relation/query_methods.rb +585 -109
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +15 -21
  200. data/lib/active_record/relation.rb +592 -92
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +90 -23
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +33 -11
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +23 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +108 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +3 -1
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/crud.rb +2 -0
  248. data/lib/arel/delete_manager.rb +5 -0
  249. data/lib/arel/errors.rb +10 -0
  250. data/lib/arel/factory_methods.rb +4 -0
  251. data/lib/arel/nodes/binary.rb +6 -7
  252. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  253. data/lib/arel/nodes/cte.rb +36 -0
  254. data/lib/arel/nodes/delete_statement.rb +4 -2
  255. data/lib/arel/nodes/fragments.rb +35 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  257. data/lib/arel/nodes/leading_join.rb +8 -0
  258. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  259. data/lib/arel/nodes/node.rb +115 -5
  260. data/lib/arel/nodes/sql_literal.rb +13 -0
  261. data/lib/arel/nodes/table_alias.rb +4 -0
  262. data/lib/arel/nodes/update_statement.rb +4 -2
  263. data/lib/arel/nodes.rb +6 -2
  264. data/lib/arel/predications.rb +3 -1
  265. data/lib/arel/select_manager.rb +7 -3
  266. data/lib/arel/table.rb +9 -5
  267. data/lib/arel/tree_manager.rb +8 -3
  268. data/lib/arel/update_manager.rb +7 -1
  269. data/lib/arel/visitors/dot.rb +3 -0
  270. data/lib/arel/visitors/mysql.rb +17 -5
  271. data/lib/arel/visitors/postgresql.rb +1 -12
  272. data/lib/arel/visitors/sqlite.rb +25 -0
  273. data/lib/arel/visitors/to_sql.rb +114 -34
  274. data/lib/arel/visitors/visitor.rb +2 -2
  275. data/lib/arel.rb +21 -3
  276. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  277. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  278. data/lib/rails/generators/active_record/migration.rb +3 -1
  279. data/lib/rails/generators/active_record/model/USAGE +113 -0
  280. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  281. metadata +56 -17
  282. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  283. data/lib/active_record/null_relation.rb +0 -63
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+
3
4
  module ActiveRecord
4
5
  module ConnectionAdapters # :nodoc:
5
6
  # Abstract representation of an index definition on a table. Instances of
6
7
  # this type are typically created and returned by methods in database
7
8
  # adapters. e.g. ActiveRecord::ConnectionAdapters::MySQL::SchemaStatements#indexes
8
9
  class IndexDefinition # :nodoc:
9
- attr_reader :table, :name, :unique, :columns, :lengths, :orders, :opclasses, :where, :type, :using, :comment
10
+ attr_reader :table, :name, :unique, :columns, :lengths, :orders, :opclasses, :where, :type, :using, :include, :nulls_not_distinct, :comment, :valid
10
11
 
11
12
  def initialize(
12
13
  table, name,
@@ -18,7 +19,10 @@ module ActiveRecord
18
19
  where: nil,
19
20
  type: nil,
20
21
  using: nil,
21
- comment: nil
22
+ include: nil,
23
+ nulls_not_distinct: nil,
24
+ comment: nil,
25
+ valid: true
22
26
  )
23
27
  @table = table
24
28
  @name = name
@@ -30,7 +34,14 @@ module ActiveRecord
30
34
  @where = where
31
35
  @type = type
32
36
  @using = using
37
+ @include = include
38
+ @nulls_not_distinct = nulls_not_distinct
33
39
  @comment = comment
40
+ @valid = valid
41
+ end
42
+
43
+ def valid?
44
+ @valid
34
45
  end
35
46
 
36
47
  def column_options
@@ -41,6 +52,16 @@ module ActiveRecord
41
52
  }
42
53
  end
43
54
 
55
+ def defined_for?(columns = nil, name: nil, unique: nil, valid: nil, include: nil, nulls_not_distinct: nil, **options)
56
+ columns = options[:column] if columns.blank?
57
+ (columns.nil? || Array(self.columns) == Array(columns).map(&:to_s)) &&
58
+ (name.nil? || self.name == name.to_s) &&
59
+ (unique.nil? || self.unique == unique) &&
60
+ (valid.nil? || self.valid == valid) &&
61
+ (include.nil? || Array(self.include) == Array(include).map(&:to_s)) &&
62
+ (nulls_not_distinct.nil? || self.nulls_not_distinct == nulls_not_distinct)
63
+ end
64
+
44
65
  private
45
66
  def concise_options(options)
46
67
  if columns.size == options.size && options.values.uniq.size == 1
@@ -56,11 +77,24 @@ module ActiveRecord
56
77
  # +columns+ attribute of said TableDefinition object, in order to be used
57
78
  # for generating a number of table creation or table changing SQL statements.
58
79
  ColumnDefinition = Struct.new(:name, :type, :options, :sql_type) do # :nodoc:
80
+ self::OPTION_NAMES = [
81
+ :limit,
82
+ :precision,
83
+ :scale,
84
+ :default,
85
+ :null,
86
+ :collation,
87
+ :comment,
88
+ :primary_key,
89
+ :if_exists,
90
+ :if_not_exists
91
+ ]
92
+
59
93
  def primary_key?
60
94
  options[:primary_key]
61
95
  end
62
96
 
63
- [:limit, :precision, :scale, :default, :null, :collation, :comment].each do |option_name|
97
+ (self::OPTION_NAMES - [:primary_key]).each do |option_name|
64
98
  module_eval <<-CODE, __FILE__, __LINE__ + 1
65
99
  def #{option_name}
66
100
  options[:#{option_name}]
@@ -81,6 +115,8 @@ module ActiveRecord
81
115
 
82
116
  ChangeColumnDefinition = Struct.new(:column, :name) # :nodoc:
83
117
 
118
+ ChangeColumnDefaultDefinition = Struct.new(:column, :default) # :nodoc:
119
+
84
120
  CreateIndexDefinition = Struct.new(:index, :algorithm, :if_not_exists) # :nodoc:
85
121
 
86
122
  PrimaryKeyDefinition = Struct.new(:name) # :nodoc:
@@ -124,9 +160,11 @@ module ActiveRecord
124
160
  end
125
161
 
126
162
  def defined_for?(to_table: nil, validate: nil, **options)
163
+ options = options.slice(*self.options.keys)
164
+
127
165
  (to_table.nil? || to_table.to_s == self.to_table) &&
128
- (validate.nil? || validate == options.fetch(:validate, validate)) &&
129
- options.all? { |k, v| self.options[k].to_s == v.to_s }
166
+ (validate.nil? || validate == self.options.fetch(:validate, validate)) &&
167
+ options.all? { |k, v| Array(self.options[k]).map(&:to_s) == Array(v).map(&:to_s) }
130
168
  end
131
169
 
132
170
  private
@@ -148,6 +186,14 @@ module ActiveRecord
148
186
  def export_name_on_schema_dump?
149
187
  !ActiveRecord::SchemaDumper.chk_ignore_pattern.match?(name) if name
150
188
  end
189
+
190
+ def defined_for?(name:, expression: nil, validate: nil, **options)
191
+ options = options.slice(*self.options.keys)
192
+
193
+ self.name == name.to_s &&
194
+ (validate.nil? || validate == self.options.fetch(:validate, validate)) &&
195
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
196
+ end
151
197
  end
152
198
 
153
199
  class ReferenceDefinition # :nodoc:
@@ -171,6 +217,20 @@ module ActiveRecord
171
217
  end
172
218
  end
173
219
 
220
+ def add(table_name, connection)
221
+ columns.each do |name, type, options|
222
+ connection.add_column(table_name, name, type, **options)
223
+ end
224
+
225
+ if index
226
+ connection.add_index(table_name, column_names, **index_options(table_name))
227
+ end
228
+
229
+ if foreign_key
230
+ connection.add_foreign_key(table_name, foreign_table_name, **foreign_key_options)
231
+ end
232
+ end
233
+
174
234
  def add_to(table)
175
235
  columns.each do |name, type, options|
176
236
  table.column(name, type, **options)
@@ -192,8 +252,12 @@ module ActiveRecord
192
252
  value.is_a?(Hash) ? value : {}
193
253
  end
194
254
 
255
+ def conditional_options
256
+ options.slice(:if_exists, :if_not_exists)
257
+ end
258
+
195
259
  def polymorphic_options
196
- as_options(polymorphic).merge(options.slice(:null, :first, :after))
260
+ as_options(polymorphic).merge(conditional_options).merge(options.slice(:null, :first, :after))
197
261
  end
198
262
 
199
263
  def polymorphic_index_name(table_name)
@@ -201,7 +265,7 @@ module ActiveRecord
201
265
  end
202
266
 
203
267
  def index_options(table_name)
204
- index_options = as_options(index)
268
+ index_options = as_options(index).merge(conditional_options)
205
269
 
206
270
  # legacy reference index names are used on versions 6.0 and earlier
207
271
  return index_options if options[:_uses_legacy_reference_index_name]
@@ -211,7 +275,7 @@ module ActiveRecord
211
275
  end
212
276
 
213
277
  def foreign_key_options
214
- as_options(foreign_key).merge(column: column_name)
278
+ as_options(foreign_key).merge(column: column_name, **conditional_options)
215
279
  end
216
280
 
217
281
  def columns
@@ -280,13 +344,15 @@ module ActiveRecord
280
344
  end
281
345
  end
282
346
 
347
+ # = Active Record Connection Adapters \Table \Definition
348
+ #
283
349
  # Represents the schema of an SQL table in an abstract way. This class
284
350
  # provides methods for manipulating the schema representation.
285
351
  #
286
352
  # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
287
353
  # is actually of this type:
288
354
  #
289
- # class SomeMigration < ActiveRecord::Migration[7.0]
355
+ # class SomeMigration < ActiveRecord::Migration[7.2]
290
356
  # def up
291
357
  # create_table :foo do |t|
292
358
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -327,6 +393,23 @@ module ActiveRecord
327
393
  @comment = comment
328
394
  end
329
395
 
396
+ def set_primary_key(table_name, id, primary_key, **options)
397
+ if id && !as
398
+ pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
399
+
400
+ if id.is_a?(Hash)
401
+ options.merge!(id.except(:type))
402
+ id = id.fetch(:type, :primary_key)
403
+ end
404
+
405
+ if pk.is_a?(Array)
406
+ primary_keys(pk)
407
+ else
408
+ primary_key(pk, id, **options)
409
+ end
410
+ end
411
+ end
412
+
330
413
  def primary_keys(name = nil) # :nodoc:
331
414
  @primary_keys = PrimaryKeyDefinition.new(name) if name
332
415
  @primary_keys
@@ -352,7 +435,7 @@ module ActiveRecord
352
435
  #
353
436
  # == Examples
354
437
  #
355
- # # Assuming +td+ is an instance of TableDefinition
438
+ # # Assuming `td` is an instance of TableDefinition
356
439
  # td.column(:granted, :boolean, index: true)
357
440
  #
358
441
  # == Short-hand examples
@@ -504,7 +587,15 @@ module ActiveRecord
504
587
  end
505
588
 
506
589
  private
590
+ def valid_column_definition_options
591
+ @conn.valid_column_definition_options
592
+ end
593
+
507
594
  def create_column_definition(name, type, options)
595
+ unless options[:_skip_validate_options]
596
+ options.except(:_uses_legacy_reference_index_name, :_skip_validate_options).assert_valid_keys(valid_column_definition_options)
597
+ end
598
+
508
599
  ColumnDefinition.new(name, type, options)
509
600
  end
510
601
 
@@ -523,9 +614,9 @@ module ActiveRecord
523
614
  def raise_on_duplicate_column(name)
524
615
  if @columns_hash[name]
525
616
  if @columns_hash[name].primary_key?
526
- raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
617
+ raise ArgumentError, "you can't redefine the primary key column '#{name}' on '#{@name}'. To define a custom primary key, pass { id: false } to create_table."
527
618
  else
528
- raise ArgumentError, "you can't define an already defined column '#{name}'."
619
+ raise ArgumentError, "you can't define an already defined column '#{name}' on '#{@name}'."
529
620
  end
530
621
  end
531
622
  end
@@ -570,6 +661,8 @@ module ActiveRecord
570
661
  end
571
662
  end
572
663
 
664
+ # = Active Record Connection Adapters \Table
665
+ #
573
666
  # Represents an SQL table in an abstract way for updating a table.
574
667
  # Also see TableDefinition and {connection.create_table}[rdoc-ref:SchemaStatements#create_table]
575
668
  #
@@ -630,6 +723,7 @@ module ActiveRecord
630
723
  #
631
724
  # See TableDefinition#column for details of the options you can use.
632
725
  def column(column_name, type, index: nil, **options)
726
+ raise_on_if_exist_options(options)
633
727
  @base.add_column(name, column_name, type, **options)
634
728
  if index
635
729
  index_options = index.is_a?(Hash) ? index : {}
@@ -655,6 +749,7 @@ module ActiveRecord
655
749
  #
656
750
  # See {connection.add_index}[rdoc-ref:SchemaStatements#add_index] for details of the options you can use.
657
751
  def index(column_name, **options)
752
+ raise_on_if_exist_options(options)
658
753
  @base.add_index(name, column_name, **options)
659
754
  end
660
755
 
@@ -684,6 +779,7 @@ module ActiveRecord
684
779
  #
685
780
  # See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
686
781
  def timestamps(**options)
782
+ raise_on_if_exist_options(options)
687
783
  @base.add_timestamps(name, **options)
688
784
  end
689
785
 
@@ -694,6 +790,7 @@ module ActiveRecord
694
790
  #
695
791
  # See TableDefinition#column for details of the options you can use.
696
792
  def change(column_name, type, **options)
793
+ raise_on_if_exist_options(options)
697
794
  @base.change_column(name, column_name, type, **options)
698
795
  end
699
796
 
@@ -725,6 +822,7 @@ module ActiveRecord
725
822
  #
726
823
  # See {connection.remove_columns}[rdoc-ref:SchemaStatements#remove_columns]
727
824
  def remove(*column_names, **options)
825
+ raise_on_if_exist_options(options)
728
826
  @base.remove_columns(name, *column_names, **options)
729
827
  end
730
828
 
@@ -737,6 +835,7 @@ module ActiveRecord
737
835
  #
738
836
  # See {connection.remove_index}[rdoc-ref:SchemaStatements#remove_index]
739
837
  def remove_index(column_name = nil, **options)
838
+ raise_on_if_exist_options(options)
740
839
  @base.remove_index(name, column_name, **options)
741
840
  end
742
841
 
@@ -765,6 +864,7 @@ module ActiveRecord
765
864
  #
766
865
  # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
767
866
  def references(*args, **options)
867
+ raise_on_if_exist_options(options)
768
868
  args.each do |ref_name|
769
869
  @base.add_reference(name, ref_name, **options)
770
870
  end
@@ -778,6 +878,7 @@ module ActiveRecord
778
878
  #
779
879
  # See {connection.remove_reference}[rdoc-ref:SchemaStatements#remove_reference]
780
880
  def remove_references(*args, **options)
881
+ raise_on_if_exist_options(options)
781
882
  args.each do |ref_name|
782
883
  @base.remove_reference(name, ref_name, **options)
783
884
  end
@@ -791,6 +892,7 @@ module ActiveRecord
791
892
  #
792
893
  # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
793
894
  def foreign_key(*args, **options)
895
+ raise_on_if_exist_options(options)
794
896
  @base.add_foreign_key(name, *args, **options)
795
897
  end
796
898
 
@@ -801,6 +903,7 @@ module ActiveRecord
801
903
  #
802
904
  # See {connection.remove_foreign_key}[rdoc-ref:SchemaStatements#remove_foreign_key]
803
905
  def remove_foreign_key(*args, **options)
906
+ raise_on_if_exist_options(options)
804
907
  @base.remove_foreign_key(name, *args, **options)
805
908
  end
806
909
 
@@ -830,6 +933,33 @@ module ActiveRecord
830
933
  def remove_check_constraint(*args, **options)
831
934
  @base.remove_check_constraint(name, *args, **options)
832
935
  end
936
+
937
+ # Checks if a check_constraint exists on a table.
938
+ #
939
+ # unless t.check_constraint_exists?(name: "price_check")
940
+ # t.check_constraint("price > 0", name: "price_check")
941
+ # end
942
+ #
943
+ # See {connection.check_constraint_exists?}[rdoc-ref:SchemaStatements#check_constraint_exists?]
944
+ def check_constraint_exists?(*args, **options)
945
+ @base.check_constraint_exists?(name, *args, **options)
946
+ end
947
+
948
+ private
949
+ def raise_on_if_exist_options(options)
950
+ unrecognized_option = options.keys.find do |key|
951
+ key == :if_exists || key == :if_not_exists
952
+ end
953
+ if unrecognized_option
954
+ conditional = unrecognized_option == :if_exists ? "if" : "unless"
955
+ message = <<~TXT
956
+ Option #{unrecognized_option} will be ignored. If you are calling an expression like
957
+ `t.column(.., #{unrecognized_option}: true)` from inside a change_table block, try a
958
+ conditional clause instead, as in `t.column(..) #{conditional} t.column_exists?(..)`
959
+ TXT
960
+ raise ArgumentError.new(message)
961
+ end
962
+ end
833
963
  end
834
964
  end
835
965
  end