activerecord 7.0.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1607 -1040
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +17 -18
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -12
  15. data/lib/active_record/associations/collection_proxy.rb +22 -12
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +27 -17
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +20 -14
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  23. data/lib/active_record/associations/preloader.rb +13 -10
  24. data/lib/active_record/associations/singular_association.rb +1 -1
  25. data/lib/active_record/associations/through_association.rb +22 -11
  26. data/lib/active_record/associations.rb +345 -219
  27. data/lib/active_record/attribute_assignment.rb +0 -2
  28. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +40 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  31. data/lib/active_record/attribute_methods/query.rb +28 -16
  32. data/lib/active_record/attribute_methods/read.rb +18 -5
  33. data/lib/active_record/attribute_methods/serialization.rb +172 -69
  34. data/lib/active_record/attribute_methods/write.rb +3 -3
  35. data/lib/active_record/attribute_methods.rb +110 -28
  36. data/lib/active_record/attributes.rb +3 -3
  37. data/lib/active_record/autosave_association.rb +56 -10
  38. data/lib/active_record/base.rb +10 -5
  39. data/lib/active_record/callbacks.rb +16 -32
  40. data/lib/active_record/coders/column_serializer.rb +61 -0
  41. data/lib/active_record/coders/json.rb +1 -1
  42. data/lib/active_record/coders/yaml_column.rb +70 -34
  43. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  45. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  46. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  47. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  48. data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
  49. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  50. data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
  51. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -129
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -106
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
  59. data/lib/active_record/connection_adapters/column.rb +9 -0
  60. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  61. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  62. data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
  63. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  64. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  65. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  66. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  67. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  69. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  70. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +3 -2
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -45
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +3 -1
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +358 -57
  83. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
  85. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
  89. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
  92. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  93. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
  94. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  95. data/lib/active_record/connection_adapters.rb +3 -1
  96. data/lib/active_record/connection_handling.rb +73 -96
  97. data/lib/active_record/core.rb +136 -148
  98. data/lib/active_record/counter_cache.rb +46 -25
  99. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
  100. data/lib/active_record/database_configurations/database_config.rb +9 -3
  101. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  102. data/lib/active_record/database_configurations/url_config.rb +17 -11
  103. data/lib/active_record/database_configurations.rb +87 -34
  104. data/lib/active_record/delegated_type.rb +9 -4
  105. data/lib/active_record/deprecator.rb +7 -0
  106. data/lib/active_record/destroy_association_async_job.rb +2 -0
  107. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  108. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  109. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  110. data/lib/active_record/encryption/config.rb +25 -1
  111. data/lib/active_record/encryption/configurable.rb +13 -14
  112. data/lib/active_record/encryption/context.rb +10 -3
  113. data/lib/active_record/encryption/contexts.rb +8 -4
  114. data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
  115. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  116. data/lib/active_record/encryption/encryptable_record.rb +38 -22
  117. data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
  118. data/lib/active_record/encryption/encryptor.rb +7 -7
  119. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  120. data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -71
  121. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  122. data/lib/active_record/encryption/key_generator.rb +12 -1
  123. data/lib/active_record/encryption/message.rb +1 -1
  124. data/lib/active_record/encryption/message_serializer.rb +2 -0
  125. data/lib/active_record/encryption/properties.rb +4 -4
  126. data/lib/active_record/encryption/scheme.rb +20 -23
  127. data/lib/active_record/encryption.rb +1 -0
  128. data/lib/active_record/enum.rb +114 -27
  129. data/lib/active_record/errors.rb +108 -15
  130. data/lib/active_record/explain.rb +23 -3
  131. data/lib/active_record/explain_subscriber.rb +1 -1
  132. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  133. data/lib/active_record/fixture_set/render_context.rb +2 -0
  134. data/lib/active_record/fixture_set/table_row.rb +29 -8
  135. data/lib/active_record/fixtures.rb +121 -73
  136. data/lib/active_record/future_result.rb +30 -5
  137. data/lib/active_record/gem_version.rb +2 -2
  138. data/lib/active_record/inheritance.rb +30 -16
  139. data/lib/active_record/insert_all.rb +55 -8
  140. data/lib/active_record/integration.rb +10 -10
  141. data/lib/active_record/internal_metadata.rb +118 -30
  142. data/lib/active_record/locking/optimistic.rb +32 -18
  143. data/lib/active_record/locking/pessimistic.rb +8 -5
  144. data/lib/active_record/log_subscriber.rb +39 -17
  145. data/lib/active_record/marshalling.rb +56 -0
  146. data/lib/active_record/message_pack.rb +124 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  148. data/lib/active_record/middleware/database_selector.rb +18 -13
  149. data/lib/active_record/middleware/shard_selector.rb +7 -5
  150. data/lib/active_record/migration/command_recorder.rb +104 -9
  151. data/lib/active_record/migration/compatibility.rb +158 -64
  152. data/lib/active_record/migration/default_strategy.rb +23 -0
  153. data/lib/active_record/migration/execution_strategy.rb +19 -0
  154. data/lib/active_record/migration.rb +271 -117
  155. data/lib/active_record/model_schema.rb +82 -50
  156. data/lib/active_record/nested_attributes.rb +23 -3
  157. data/lib/active_record/normalization.rb +159 -0
  158. data/lib/active_record/persistence.rb +200 -47
  159. data/lib/active_record/promise.rb +84 -0
  160. data/lib/active_record/query_cache.rb +3 -21
  161. data/lib/active_record/query_logs.rb +87 -51
  162. data/lib/active_record/query_logs_formatter.rb +41 -0
  163. data/lib/active_record/querying.rb +16 -3
  164. data/lib/active_record/railtie.rb +127 -61
  165. data/lib/active_record/railties/controller_runtime.rb +12 -8
  166. data/lib/active_record/railties/databases.rake +142 -143
  167. data/lib/active_record/railties/job_runtime.rb +23 -0
  168. data/lib/active_record/readonly_attributes.rb +32 -5
  169. data/lib/active_record/reflection.rb +177 -45
  170. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  171. data/lib/active_record/relation/batches.rb +190 -61
  172. data/lib/active_record/relation/calculations.rb +200 -83
  173. data/lib/active_record/relation/delegation.rb +23 -9
  174. data/lib/active_record/relation/finder_methods.rb +77 -16
  175. data/lib/active_record/relation/merger.rb +2 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  177. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  179. data/lib/active_record/relation/predicate_builder.rb +26 -14
  180. data/lib/active_record/relation/query_attribute.rb +25 -1
  181. data/lib/active_record/relation/query_methods.rb +429 -76
  182. data/lib/active_record/relation/spawn_methods.rb +18 -1
  183. data/lib/active_record/relation.rb +98 -41
  184. data/lib/active_record/result.rb +25 -9
  185. data/lib/active_record/runtime_registry.rb +10 -1
  186. data/lib/active_record/sanitization.rb +57 -16
  187. data/lib/active_record/schema.rb +36 -22
  188. data/lib/active_record/schema_dumper.rb +65 -23
  189. data/lib/active_record/schema_migration.rb +68 -33
  190. data/lib/active_record/scoping/default.rb +20 -12
  191. data/lib/active_record/scoping/named.rb +2 -2
  192. data/lib/active_record/scoping.rb +2 -1
  193. data/lib/active_record/secure_password.rb +60 -0
  194. data/lib/active_record/secure_token.rb +21 -3
  195. data/lib/active_record/serialization.rb +5 -0
  196. data/lib/active_record/signed_id.rb +9 -7
  197. data/lib/active_record/store.rb +16 -11
  198. data/lib/active_record/suppressor.rb +3 -1
  199. data/lib/active_record/table_metadata.rb +16 -3
  200. data/lib/active_record/tasks/database_tasks.rb +138 -107
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
  203. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  204. data/lib/active_record/test_fixtures.rb +123 -99
  205. data/lib/active_record/timestamp.rb +26 -14
  206. data/lib/active_record/token_for.rb +113 -0
  207. data/lib/active_record/touch_later.rb +11 -6
  208. data/lib/active_record/transactions.rb +39 -13
  209. data/lib/active_record/translation.rb +1 -1
  210. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  211. data/lib/active_record/type/internal/timezone.rb +7 -2
  212. data/lib/active_record/type/serialized.rb +8 -4
  213. data/lib/active_record/type/time.rb +4 -0
  214. data/lib/active_record/validations/absence.rb +1 -1
  215. data/lib/active_record/validations/associated.rb +3 -3
  216. data/lib/active_record/validations/numericality.rb +5 -4
  217. data/lib/active_record/validations/presence.rb +5 -28
  218. data/lib/active_record/validations/uniqueness.rb +50 -5
  219. data/lib/active_record/validations.rb +8 -4
  220. data/lib/active_record/version.rb +1 -1
  221. data/lib/active_record.rb +143 -16
  222. data/lib/arel/errors.rb +10 -0
  223. data/lib/arel/factory_methods.rb +4 -0
  224. data/lib/arel/filter_predications.rb +1 -1
  225. data/lib/arel/nodes/and.rb +4 -0
  226. data/lib/arel/nodes/binary.rb +6 -1
  227. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  228. data/lib/arel/nodes/cte.rb +36 -0
  229. data/lib/arel/nodes/filter.rb +1 -1
  230. data/lib/arel/nodes/fragments.rb +35 -0
  231. data/lib/arel/nodes/homogeneous_in.rb +0 -8
  232. data/lib/arel/nodes/leading_join.rb +8 -0
  233. data/lib/arel/nodes/node.rb +111 -2
  234. data/lib/arel/nodes/sql_literal.rb +6 -0
  235. data/lib/arel/nodes/table_alias.rb +4 -0
  236. data/lib/arel/nodes.rb +4 -0
  237. data/lib/arel/predications.rb +2 -0
  238. data/lib/arel/table.rb +9 -5
  239. data/lib/arel/visitors/mysql.rb +8 -1
  240. data/lib/arel/visitors/to_sql.rb +81 -17
  241. data/lib/arel/visitors/visitor.rb +2 -2
  242. data/lib/arel.rb +16 -2
  243. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  244. data/lib/rails/generators/active_record/migration.rb +3 -1
  245. data/lib/rails/generators/active_record/model/USAGE +113 -0
  246. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  247. metadata +50 -15
  248. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  249. 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:
@@ -125,8 +161,8 @@ module ActiveRecord
125
161
 
126
162
  def defined_for?(to_table: nil, validate: nil, **options)
127
163
  (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 }
164
+ (validate.nil? || validate == self.options.fetch(:validate, validate)) &&
165
+ options.all? { |k, v| Array(self.options[k]).map(&:to_s) == Array(v).map(&:to_s) }
130
166
  end
131
167
 
132
168
  private
@@ -148,6 +184,12 @@ module ActiveRecord
148
184
  def export_name_on_schema_dump?
149
185
  !ActiveRecord::SchemaDumper.chk_ignore_pattern.match?(name) if name
150
186
  end
187
+
188
+ def defined_for?(name:, expression: nil, validate: nil, **options)
189
+ self.name == name.to_s &&
190
+ (validate.nil? || validate == self.options.fetch(:validate, validate)) &&
191
+ options.all? { |k, v| self.options[k].to_s == v.to_s }
192
+ end
151
193
  end
152
194
 
153
195
  class ReferenceDefinition # :nodoc:
@@ -171,6 +213,20 @@ module ActiveRecord
171
213
  end
172
214
  end
173
215
 
216
+ def add(table_name, connection)
217
+ columns.each do |name, type, options|
218
+ connection.add_column(table_name, name, type, **options)
219
+ end
220
+
221
+ if index
222
+ connection.add_index(table_name, column_names, **index_options(table_name))
223
+ end
224
+
225
+ if foreign_key
226
+ connection.add_foreign_key(table_name, foreign_table_name, **foreign_key_options)
227
+ end
228
+ end
229
+
174
230
  def add_to(table)
175
231
  columns.each do |name, type, options|
176
232
  table.column(name, type, **options)
@@ -192,8 +248,12 @@ module ActiveRecord
192
248
  value.is_a?(Hash) ? value : {}
193
249
  end
194
250
 
251
+ def conditional_options
252
+ options.slice(:if_exists, :if_not_exists)
253
+ end
254
+
195
255
  def polymorphic_options
196
- as_options(polymorphic).merge(options.slice(:null, :first, :after))
256
+ as_options(polymorphic).merge(conditional_options).merge(options.slice(:null, :first, :after))
197
257
  end
198
258
 
199
259
  def polymorphic_index_name(table_name)
@@ -201,13 +261,17 @@ module ActiveRecord
201
261
  end
202
262
 
203
263
  def index_options(table_name)
204
- index_options = as_options(index)
264
+ index_options = as_options(index).merge(conditional_options)
265
+
266
+ # legacy reference index names are used on versions 6.0 and earlier
267
+ return index_options if options[:_uses_legacy_reference_index_name]
268
+
205
269
  index_options[:name] ||= polymorphic_index_name(table_name) if polymorphic
206
270
  index_options
207
271
  end
208
272
 
209
273
  def foreign_key_options
210
- as_options(foreign_key).merge(column: column_name)
274
+ as_options(foreign_key).merge(column: column_name, **conditional_options)
211
275
  end
212
276
 
213
277
  def columns
@@ -276,13 +340,15 @@ module ActiveRecord
276
340
  end
277
341
  end
278
342
 
343
+ # = Active Record Connection Adapters \Table \Definition
344
+ #
279
345
  # Represents the schema of an SQL table in an abstract way. This class
280
346
  # provides methods for manipulating the schema representation.
281
347
  #
282
348
  # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
283
349
  # is actually of this type:
284
350
  #
285
- # class SomeMigration < ActiveRecord::Migration[7.0]
351
+ # class SomeMigration < ActiveRecord::Migration[7.1]
286
352
  # def up
287
353
  # create_table :foo do |t|
288
354
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -323,6 +389,23 @@ module ActiveRecord
323
389
  @comment = comment
324
390
  end
325
391
 
392
+ def set_primary_key(table_name, id, primary_key, **options)
393
+ if id && !as
394
+ pk = primary_key || Base.get_primary_key(table_name.to_s.singularize)
395
+
396
+ if id.is_a?(Hash)
397
+ options.merge!(id.except(:type))
398
+ id = id.fetch(:type, :primary_key)
399
+ end
400
+
401
+ if pk.is_a?(Array)
402
+ primary_keys(pk)
403
+ else
404
+ primary_key(pk, id, **options)
405
+ end
406
+ end
407
+ end
408
+
326
409
  def primary_keys(name = nil) # :nodoc:
327
410
  @primary_keys = PrimaryKeyDefinition.new(name) if name
328
411
  @primary_keys
@@ -407,20 +490,7 @@ module ActiveRecord
407
490
  name = name.to_s
408
491
  type = type.to_sym if type
409
492
 
410
- if @columns_hash[name]
411
- if @columns_hash[name].primary_key?
412
- raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
413
- else
414
- raise ArgumentError, "you can't define an already defined column '#{name}'."
415
- end
416
- end
417
-
418
- if @conn.supports_datetime_with_precision?
419
- if type == :datetime && !options.key?(:precision)
420
- options[:precision] = 6
421
- end
422
- end
423
-
493
+ raise_on_duplicate_column(name)
424
494
  @columns_hash[name] = new_column_definition(name, type, **options)
425
495
 
426
496
  if index
@@ -487,6 +557,13 @@ module ActiveRecord
487
557
  type = integer_like_primary_key_type(type, options)
488
558
  end
489
559
  type = aliased_types(type.to_s, type)
560
+
561
+ if @conn.supports_datetime_with_precision?
562
+ if type == :datetime && !options.key?(:precision)
563
+ options[:precision] = 6
564
+ end
565
+ end
566
+
490
567
  options[:primary_key] ||= type == :primary_key
491
568
  options[:null] = false if options[:primary_key]
492
569
  create_column_definition(name, type, options)
@@ -506,7 +583,15 @@ module ActiveRecord
506
583
  end
507
584
 
508
585
  private
586
+ def valid_column_definition_options
587
+ @conn.valid_column_definition_options
588
+ end
589
+
509
590
  def create_column_definition(name, type, options)
591
+ unless options[:_skip_validate_options]
592
+ options.except(:_uses_legacy_reference_index_name, :_skip_validate_options).assert_valid_keys(valid_column_definition_options)
593
+ end
594
+
510
595
  ColumnDefinition.new(name, type, options)
511
596
  end
512
597
 
@@ -521,6 +606,16 @@ module ActiveRecord
521
606
  def integer_like_primary_key_type(type, options)
522
607
  type
523
608
  end
609
+
610
+ def raise_on_duplicate_column(name)
611
+ if @columns_hash[name]
612
+ if @columns_hash[name].primary_key?
613
+ 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."
614
+ else
615
+ raise ArgumentError, "you can't define an already defined column '#{name}' on '#{@name}'."
616
+ end
617
+ end
618
+ end
524
619
  end
525
620
 
526
621
  class AlterTable # :nodoc:
@@ -562,6 +657,8 @@ module ActiveRecord
562
657
  end
563
658
  end
564
659
 
660
+ # = Active Record Connection Adapters \Table
661
+ #
565
662
  # Represents an SQL table in an abstract way for updating a table.
566
663
  # Also see TableDefinition and {connection.create_table}[rdoc-ref:SchemaStatements#create_table]
567
664
  #
@@ -622,6 +719,7 @@ module ActiveRecord
622
719
  #
623
720
  # See TableDefinition#column for details of the options you can use.
624
721
  def column(column_name, type, index: nil, **options)
722
+ raise_on_if_exist_options(options)
625
723
  @base.add_column(name, column_name, type, **options)
626
724
  if index
627
725
  index_options = index.is_a?(Hash) ? index : {}
@@ -647,6 +745,7 @@ module ActiveRecord
647
745
  #
648
746
  # See {connection.add_index}[rdoc-ref:SchemaStatements#add_index] for details of the options you can use.
649
747
  def index(column_name, **options)
748
+ raise_on_if_exist_options(options)
650
749
  @base.add_index(name, column_name, **options)
651
750
  end
652
751
 
@@ -657,8 +756,8 @@ module ActiveRecord
657
756
  # end
658
757
  #
659
758
  # See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?]
660
- def index_exists?(column_name, options = {})
661
- @base.index_exists?(name, column_name, options)
759
+ def index_exists?(column_name, **options)
760
+ @base.index_exists?(name, column_name, **options)
662
761
  end
663
762
 
664
763
  # Renames the given index on the table.
@@ -676,6 +775,7 @@ module ActiveRecord
676
775
  #
677
776
  # See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
678
777
  def timestamps(**options)
778
+ raise_on_if_exist_options(options)
679
779
  @base.add_timestamps(name, **options)
680
780
  end
681
781
 
@@ -686,6 +786,7 @@ module ActiveRecord
686
786
  #
687
787
  # See TableDefinition#column for details of the options you can use.
688
788
  def change(column_name, type, **options)
789
+ raise_on_if_exist_options(options)
689
790
  @base.change_column(name, column_name, type, **options)
690
791
  end
691
792
 
@@ -717,6 +818,7 @@ module ActiveRecord
717
818
  #
718
819
  # See {connection.remove_columns}[rdoc-ref:SchemaStatements#remove_columns]
719
820
  def remove(*column_names, **options)
821
+ raise_on_if_exist_options(options)
720
822
  @base.remove_columns(name, *column_names, **options)
721
823
  end
722
824
 
@@ -729,6 +831,7 @@ module ActiveRecord
729
831
  #
730
832
  # See {connection.remove_index}[rdoc-ref:SchemaStatements#remove_index]
731
833
  def remove_index(column_name = nil, **options)
834
+ raise_on_if_exist_options(options)
732
835
  @base.remove_index(name, column_name, **options)
733
836
  end
734
837
 
@@ -757,6 +860,7 @@ module ActiveRecord
757
860
  #
758
861
  # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
759
862
  def references(*args, **options)
863
+ raise_on_if_exist_options(options)
760
864
  args.each do |ref_name|
761
865
  @base.add_reference(name, ref_name, **options)
762
866
  end
@@ -770,6 +874,7 @@ module ActiveRecord
770
874
  #
771
875
  # See {connection.remove_reference}[rdoc-ref:SchemaStatements#remove_reference]
772
876
  def remove_references(*args, **options)
877
+ raise_on_if_exist_options(options)
773
878
  args.each do |ref_name|
774
879
  @base.remove_reference(name, ref_name, **options)
775
880
  end
@@ -783,6 +888,7 @@ module ActiveRecord
783
888
  #
784
889
  # See {connection.add_foreign_key}[rdoc-ref:SchemaStatements#add_foreign_key]
785
890
  def foreign_key(*args, **options)
891
+ raise_on_if_exist_options(options)
786
892
  @base.add_foreign_key(name, *args, **options)
787
893
  end
788
894
 
@@ -793,6 +899,7 @@ module ActiveRecord
793
899
  #
794
900
  # See {connection.remove_foreign_key}[rdoc-ref:SchemaStatements#remove_foreign_key]
795
901
  def remove_foreign_key(*args, **options)
902
+ raise_on_if_exist_options(options)
796
903
  @base.remove_foreign_key(name, *args, **options)
797
904
  end
798
905
 
@@ -810,8 +917,8 @@ module ActiveRecord
810
917
  # t.check_constraint("price > 0", name: "price_check")
811
918
  #
812
919
  # See {connection.add_check_constraint}[rdoc-ref:SchemaStatements#add_check_constraint]
813
- def check_constraint(*args)
814
- @base.add_check_constraint(name, *args)
920
+ def check_constraint(*args, **options)
921
+ @base.add_check_constraint(name, *args, **options)
815
922
  end
816
923
 
817
924
  # Removes the given check constraint from the table.
@@ -819,9 +926,36 @@ module ActiveRecord
819
926
  # t.remove_check_constraint(name: "price_check")
820
927
  #
821
928
  # See {connection.remove_check_constraint}[rdoc-ref:SchemaStatements#remove_check_constraint]
822
- def remove_check_constraint(*args)
823
- @base.remove_check_constraint(name, *args)
929
+ def remove_check_constraint(*args, **options)
930
+ @base.remove_check_constraint(name, *args, **options)
824
931
  end
932
+
933
+ # Checks if a check_constraint exists on a table.
934
+ #
935
+ # unless t.check_constraint_exists?(name: "price_check")
936
+ # t.check_constraint("price > 0", name: "price_check")
937
+ # end
938
+ #
939
+ # See {connection.check_constraint_exists?}[rdoc-ref:SchemaStatements#check_constraint_exists?]
940
+ def check_constraint_exists?(*args, **options)
941
+ @base.check_constraint_exists?(name, *args, **options)
942
+ end
943
+
944
+ private
945
+ def raise_on_if_exist_options(options)
946
+ unrecognized_option = options.keys.find do |key|
947
+ key == :if_exists || key == :if_not_exists
948
+ end
949
+ if unrecognized_option
950
+ conditional = unrecognized_option == :if_exists ? "if" : "unless"
951
+ message = <<~TXT
952
+ Option #{unrecognized_option} will be ignored. If you are calling an expression like
953
+ `t.column(.., #{unrecognized_option}: true)` from inside a change_table block, try a
954
+ conditional clause instead, as in `t.column(..) #{conditional} t.column_exists?(..)`
955
+ TXT
956
+ raise ArgumentError.new(message)
957
+ end
958
+ end
825
959
  end
826
960
  end
827
961
  end
@@ -3,6 +3,8 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters # :nodoc:
5
5
  class SchemaDumper < SchemaDumper # :nodoc:
6
+ DEFAULT_DATETIME_PRECISION = 6 # :nodoc:
7
+
6
8
  def self.create(connection, options)
7
9
  new(connection, options)
8
10
  end
@@ -63,7 +65,18 @@ module ActiveRecord
63
65
  end
64
66
 
65
67
  def schema_precision(column)
66
- column.precision.inspect if column.precision
68
+ if column.type == :datetime
69
+ case column.precision
70
+ when nil
71
+ "nil"
72
+ when DEFAULT_DATETIME_PRECISION
73
+ nil
74
+ else
75
+ column.precision.inspect
76
+ end
77
+ elsif column.precision
78
+ column.precision.inspect
79
+ end
67
80
  end
68
81
 
69
82
  def schema_scale(column)