activerecord 8.0.5.1 → 8.1.0.beta1

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 (157) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +421 -657
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +1 -1
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/belongs_to_association.rb +0 -2
  7. data/lib/active_record/associations/builder/association.rb +16 -5
  8. data/lib/active_record/associations/builder/belongs_to.rb +17 -4
  9. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  10. data/lib/active_record/associations/builder/has_one.rb +1 -1
  11. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  12. data/lib/active_record/associations/collection_proxy.rb +22 -4
  13. data/lib/active_record/associations/deprecation.rb +88 -0
  14. data/lib/active_record/associations/errors.rb +3 -0
  15. data/lib/active_record/associations/join_dependency.rb +4 -2
  16. data/lib/active_record/associations/preloader/batch.rb +1 -7
  17. data/lib/active_record/associations/preloader/branch.rb +1 -0
  18. data/lib/active_record/associations.rb +159 -21
  19. data/lib/active_record/attribute_methods/serialization.rb +16 -3
  20. data/lib/active_record/attribute_methods.rb +1 -1
  21. data/lib/active_record/attributes.rb +3 -0
  22. data/lib/active_record/autosave_association.rb +2 -2
  23. data/lib/active_record/base.rb +2 -3
  24. data/lib/active_record/coders/json.rb +14 -5
  25. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -3
  26. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +15 -0
  27. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
  28. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +383 -52
  29. data/lib/active_record/connection_adapters/abstract/database_statements.rb +27 -31
  30. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -18
  31. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  32. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  33. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
  34. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  35. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +88 -25
  36. data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
  37. data/lib/active_record/connection_adapters/abstract_adapter.rb +96 -49
  38. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -11
  39. data/lib/active_record/connection_adapters/column.rb +17 -4
  40. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  41. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  42. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  43. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
  44. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
  45. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
  46. data/lib/active_record/connection_adapters/postgresql/column.rb +2 -4
  47. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -23
  48. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  49. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  50. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  51. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -1
  52. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
  53. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +65 -30
  54. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +74 -38
  55. data/lib/active_record/connection_adapters/postgresql_adapter.rb +10 -12
  56. data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
  57. data/lib/active_record/connection_adapters/sqlite3/column.rb +2 -8
  58. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +37 -25
  59. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
  60. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -15
  61. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
  62. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +57 -33
  63. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
  64. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
  65. data/lib/active_record/connection_adapters.rb +1 -0
  66. data/lib/active_record/connection_handling.rb +8 -12
  67. data/lib/active_record/core.rb +5 -4
  68. data/lib/active_record/counter_cache.rb +33 -8
  69. data/lib/active_record/database_configurations/database_config.rb +5 -1
  70. data/lib/active_record/database_configurations/hash_config.rb +50 -9
  71. data/lib/active_record/database_configurations/url_config.rb +13 -3
  72. data/lib/active_record/database_configurations.rb +7 -3
  73. data/lib/active_record/delegated_type.rb +1 -1
  74. data/lib/active_record/dynamic_matchers.rb +54 -69
  75. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  76. data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
  77. data/lib/active_record/encryption/scheme.rb +1 -1
  78. data/lib/active_record/enum.rb +24 -8
  79. data/lib/active_record/errors.rb +23 -7
  80. data/lib/active_record/explain_registry.rb +0 -1
  81. data/lib/active_record/filter_attribute_handler.rb +73 -0
  82. data/lib/active_record/fixtures.rb +2 -2
  83. data/lib/active_record/future_result.rb +0 -2
  84. data/lib/active_record/gem_version.rb +3 -3
  85. data/lib/active_record/inheritance.rb +1 -1
  86. data/lib/active_record/insert_all.rb +14 -9
  87. data/lib/active_record/locking/optimistic.rb +7 -0
  88. data/lib/active_record/locking/pessimistic.rb +5 -0
  89. data/lib/active_record/log_subscriber.rb +1 -5
  90. data/lib/active_record/middleware/shard_selector.rb +34 -17
  91. data/lib/active_record/migration/command_recorder.rb +15 -2
  92. data/lib/active_record/migration/compatibility.rb +34 -24
  93. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  94. data/lib/active_record/migration.rb +26 -16
  95. data/lib/active_record/model_schema.rb +10 -7
  96. data/lib/active_record/nested_attributes.rb +2 -0
  97. data/lib/active_record/persistence.rb +34 -3
  98. data/lib/active_record/query_cache.rb +22 -15
  99. data/lib/active_record/query_logs.rb +3 -7
  100. data/lib/active_record/railtie.rb +32 -3
  101. data/lib/active_record/railties/databases.rake +16 -4
  102. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  103. data/lib/active_record/railties/job_runtime.rb +10 -11
  104. data/lib/active_record/reflection.rb +42 -3
  105. data/lib/active_record/relation/batches.rb +26 -12
  106. data/lib/active_record/relation/calculations.rb +20 -9
  107. data/lib/active_record/relation/delegation.rb +0 -1
  108. data/lib/active_record/relation/finder_methods.rb +28 -12
  109. data/lib/active_record/relation/merger.rb +2 -2
  110. data/lib/active_record/relation/predicate_builder/array_handler.rb +1 -3
  111. data/lib/active_record/relation/predicate_builder.rb +2 -2
  112. data/lib/active_record/relation/query_attribute.rb +3 -1
  113. data/lib/active_record/relation/query_methods.rb +40 -31
  114. data/lib/active_record/relation/where_clause.rb +2 -11
  115. data/lib/active_record/relation.rb +26 -14
  116. data/lib/active_record/result.rb +44 -21
  117. data/lib/active_record/sanitization.rb +2 -0
  118. data/lib/active_record/schema_dumper.rb +12 -10
  119. data/lib/active_record/scoping.rb +0 -1
  120. data/lib/active_record/signed_id.rb +43 -15
  121. data/lib/active_record/statement_cache.rb +13 -9
  122. data/lib/active_record/store.rb +44 -19
  123. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  124. data/lib/active_record/tasks/database_tasks.rb +2 -21
  125. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
  126. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -39
  127. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  128. data/lib/active_record/test_databases.rb +10 -2
  129. data/lib/active_record/test_fixtures.rb +27 -2
  130. data/lib/active_record/testing/query_assertions.rb +8 -2
  131. data/lib/active_record/timestamp.rb +4 -2
  132. data/lib/active_record/transaction.rb +2 -5
  133. data/lib/active_record/transactions.rb +32 -10
  134. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  135. data/lib/active_record/type/internal/timezone.rb +7 -0
  136. data/lib/active_record/type/json.rb +15 -2
  137. data/lib/active_record/type/serialized.rb +11 -9
  138. data/lib/active_record/type/type_map.rb +1 -1
  139. data/lib/active_record/type_caster/connection.rb +2 -1
  140. data/lib/active_record/validations/associated.rb +1 -1
  141. data/lib/active_record.rb +65 -3
  142. data/lib/arel/alias_predication.rb +2 -0
  143. data/lib/arel/crud.rb +6 -11
  144. data/lib/arel/nodes/count.rb +2 -2
  145. data/lib/arel/nodes/function.rb +4 -10
  146. data/lib/arel/nodes/named_function.rb +2 -2
  147. data/lib/arel/nodes/node.rb +1 -1
  148. data/lib/arel/nodes.rb +0 -2
  149. data/lib/arel/predications.rb +3 -1
  150. data/lib/arel/select_manager.rb +7 -2
  151. data/lib/arel/visitors/dot.rb +0 -3
  152. data/lib/arel/visitors/postgresql.rb +55 -0
  153. data/lib/arel/visitors/sqlite.rb +55 -8
  154. data/lib/arel/visitors/to_sql.rb +3 -21
  155. data/lib/arel.rb +3 -1
  156. metadata +14 -10
  157. data/lib/active_record/normalization.rb +0 -163
@@ -55,12 +55,15 @@ module ActiveRecord
55
55
  # can be used to query the database repeatedly.
56
56
  def cacheable_query(klass, arel) # :nodoc:
57
57
  if prepared_statements
58
+ collector = collector()
59
+ collector.retryable = true
58
60
  sql, binds = visitor.compile(arel.ast, collector)
59
- query = klass.query(sql)
61
+ query = klass.query(sql, retryable: collector.retryable)
60
62
  else
61
63
  collector = klass.partial_query_collector
64
+ collector.retryable = true
62
65
  parts, binds = visitor.compile(arel.ast, collector)
63
- query = klass.partial_query(parts)
66
+ query = klass.partial_query(parts, retryable: collector.retryable)
64
67
  end
65
68
  [query, binds]
66
69
  end
@@ -110,8 +113,8 @@ module ActiveRecord
110
113
  query(...).map(&:first)
111
114
  end
112
115
 
113
- def query(...) # :nodoc:
114
- internal_exec_query(...).rows
116
+ def query(sql, name = nil, allow_retry: true, materialize_transactions: true) # :nodoc:
117
+ internal_exec_query(sql, name, allow_retry:, materialize_transactions:).rows
115
118
  end
116
119
 
117
120
  # Determines whether the SQL statement is a write query.
@@ -351,7 +354,7 @@ module ActiveRecord
351
354
  # :args: (requires_new: nil, isolation: nil, &block)
352
355
  def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
353
356
  if !requires_new && current_transaction.joinable?
354
- if isolation
357
+ if isolation && current_transaction.isolation != isolation
355
358
  raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
356
359
  end
357
360
  yield current_transaction.user_transaction
@@ -499,20 +502,6 @@ module ActiveRecord
499
502
  "DEFAULT VALUES"
500
503
  end
501
504
 
502
- # Sanitizes the given LIMIT parameter in order to prevent SQL injection.
503
- #
504
- # The +limit+ may be anything that can evaluate to a string via #to_s. It
505
- # should look like an integer, or an Arel SQL literal.
506
- #
507
- # Returns Integer and Arel::Nodes::SqlLiteral limits as is.
508
- def sanitize_limit(limit)
509
- if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
510
- limit
511
- else
512
- Integer(limit)
513
- end
514
- end
515
-
516
505
  # Fixture value is quoted by Arel, however scalar values
517
506
  # are not quotable. In this case we want to convert
518
507
  # the column value to YAML.
@@ -547,13 +536,24 @@ module ActiveRecord
547
536
  cast_result(internal_execute(...))
548
537
  end
549
538
 
539
+ def default_insert_value(column) # :nodoc:
540
+ DEFAULT_INSERT_VALUE
541
+ end
542
+
550
543
  private
544
+ DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
545
+ private_constant :DEFAULT_INSERT_VALUE
546
+
551
547
  # Lowest level way to execute a query. Doesn't check for illegal writes, doesn't annotate queries, yields a native result object.
552
548
  def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false)
553
549
  type_casted_binds = type_casted_binds(binds)
554
- log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
550
+ log(sql, name, binds, type_casted_binds, async: async, allow_retry: allow_retry) do |notification_payload|
555
551
  with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
556
- perform_query(conn, sql, binds, type_casted_binds, prepare: prepare, notification_payload: notification_payload, batch: batch)
552
+ result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
553
+ perform_query(conn, sql, binds, type_casted_binds, prepare: prepare, notification_payload: notification_payload, batch: batch)
554
+ end
555
+ handle_warnings(result, sql)
556
+ result
557
557
  end
558
558
  end
559
559
  end
@@ -562,6 +562,9 @@ module ActiveRecord
562
562
  raise NotImplementedError
563
563
  end
564
564
 
565
+ def handle_warnings(raw_result, sql)
566
+ end
567
+
565
568
  # Receive a native adapter result object and returns an ActiveRecord::Result object.
566
569
  def cast_result(raw_result)
567
570
  raise NotImplementedError
@@ -597,13 +600,6 @@ module ActiveRecord
597
600
  end
598
601
  end
599
602
 
600
- DEFAULT_INSERT_VALUE = Arel.sql("DEFAULT").freeze
601
- private_constant :DEFAULT_INSERT_VALUE
602
-
603
- def default_insert_value(column)
604
- DEFAULT_INSERT_VALUE
605
- end
606
-
607
603
  def build_fixture_sql(fixtures, table_name)
608
604
  columns = schema_cache.columns_hash(table_name).reject { |_, column| supports_virtual_columns? && column.virtual? }
609
605
 
@@ -617,8 +613,8 @@ module ActiveRecord
617
613
 
618
614
  columns.map do |name, column|
619
615
  if fixture.key?(name)
620
- type = lookup_cast_type_from_column(column)
621
- with_yaml_fallback(type.serialize(fixture[name]))
616
+ # TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
617
+ with_yaml_fallback(column.fetch_cast_type(self).serialize(fixture[name]))
622
618
  else
623
619
  default_insert_value(column)
624
620
  end
@@ -738,7 +734,7 @@ module ActiveRecord
738
734
  end
739
735
 
740
736
  def extract_table_ref_from_insert_sql(sql)
741
- if sql =~ /into\s("[-A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im
737
+ if sql =~ /into\s("[A-Za-z0-9_."\[\]\s]+"|[A-Za-z0-9_."\[\]]+)\s*/im
742
738
  $1.delete('"').strip
743
739
  end
744
740
  end
@@ -31,6 +31,18 @@ module ActiveRecord
31
31
  end
32
32
  end
33
33
 
34
+ # This is the actual query cache store.
35
+ #
36
+ # It has an internal hash whose keys are either SQL strings, or arrays of
37
+ # two elements [SQL string, binds], if there are binds. The hash values
38
+ # are their corresponding ActiveRecord::Result objects.
39
+ #
40
+ # Keeping the hash size under max size is achieved with LRU eviction.
41
+ #
42
+ # The store gets passed a version object, which is shared among the query
43
+ # cache stores of a given connection pool (see ConnectionPoolConfiguration
44
+ # down below). The version value may be externally changed as a way to
45
+ # signal cache invalidation, that is why all methods have a guard for it.
34
46
  class Store # :nodoc:
35
47
  attr_accessor :enabled, :dirties
36
48
  alias_method :enabled?, :enabled
@@ -94,6 +106,12 @@ module ActiveRecord
94
106
  end
95
107
  end
96
108
 
109
+ # Each connection pool has one of these registries. They map execution
110
+ # contexts to query cache stores.
111
+ #
112
+ # The keys of the internal map are threads or fibers (whatever
113
+ # ActiveSupport::IsolatedExecutionState.context returns), and their
114
+ # associated values are their respective query cache stores.
97
115
  class QueryCacheRegistry # :nodoc:
98
116
  def initialize
99
117
  @mutex = Mutex.new
@@ -191,26 +209,15 @@ module ActiveRecord
191
209
  end
192
210
  end
193
211
 
212
+ attr_accessor :query_cache
213
+
194
214
  def initialize(*)
195
215
  super
196
216
  @query_cache = nil
197
217
  end
198
218
 
199
- attr_writer :query_cache
200
-
201
- def query_cache
202
- if @pinned && @owner != ActiveSupport::IsolatedExecutionState.context
203
- # With transactional tests, if the connection is pinned, any thread
204
- # other than the one that pinned the connection need to go through the
205
- # query cache pool, so each thread get a different cache.
206
- pool.query_cache
207
- else
208
- @query_cache
209
- end
210
- end
211
-
212
219
  def query_cache_enabled
213
- query_cache&.enabled?
220
+ @query_cache&.enabled?
214
221
  end
215
222
 
216
223
  # Enable the query cache within the block.
@@ -249,8 +256,8 @@ module ActiveRecord
249
256
 
250
257
  # If arel is locked this is a SELECT ... FOR UPDATE or somesuch.
251
258
  # Such queries should not be cached.
252
- if query_cache_enabled && !(arel.respond_to?(:locked) && arel.locked)
253
- sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable)
259
+ if @query_cache&.enabled? && !(arel.respond_to?(:locked) && arel.locked)
260
+ sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable, allow_retry)
254
261
 
255
262
  if async
256
263
  result = lookup_sql_cache(sql, name, binds) || super(sql, name, binds, preparable: preparable, async: async, allow_retry: allow_retry)
@@ -273,7 +280,7 @@ module ActiveRecord
273
280
 
274
281
  result = nil
275
282
  @lock.synchronize do
276
- result = query_cache[key]
283
+ result = @query_cache[key]
277
284
  end
278
285
 
279
286
  if result
@@ -292,7 +299,7 @@ module ActiveRecord
292
299
  hit = true
293
300
 
294
301
  @lock.synchronize do
295
- result = query_cache.compute_if_absent(key) do
302
+ result = @query_cache.compute_if_absent(key) do
296
303
  hit = false
297
304
  yield
298
305
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/big_decimal/conversions"
4
- require "active_support/multibyte/chars"
5
4
 
6
5
  module ActiveRecord
7
6
  module ConnectionAdapters # :nodoc:
@@ -84,7 +83,8 @@ module ActiveRecord
84
83
  when Type::Time::Value then "'#{quoted_time(value)}'"
85
84
  when Date, Time then "'#{quoted_date(value)}'"
86
85
  when Class then "'#{value}'"
87
- else raise TypeError, "can't quote #{value.class.name}"
86
+ else
87
+ raise TypeError, "can't quote #{value.class.name}"
88
88
  end
89
89
  end
90
90
 
@@ -93,7 +93,7 @@ module ActiveRecord
93
93
  # to a String.
94
94
  def type_cast(value)
95
95
  case value
96
- when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
96
+ when Symbol, Type::Binary::Data, ActiveSupport::Multibyte::Chars
97
97
  value.to_s
98
98
  when true then unquoted_true
99
99
  when false then unquoted_false
@@ -102,7 +102,8 @@ module ActiveRecord
102
102
  when nil, Numeric, String then value
103
103
  when Type::Time::Value then quoted_time(value)
104
104
  when Date, Time then quoted_date(value)
105
- else raise TypeError, "can't cast #{value.class.name}"
105
+ else
106
+ raise TypeError, "can't cast #{value.class.name}"
106
107
  end
107
108
  end
108
109
 
@@ -113,19 +114,6 @@ module ActiveRecord
113
114
  value
114
115
  end
115
116
 
116
- # If you are having to call this function, you are likely doing something
117
- # wrong. The column does not have sufficient type information if the user
118
- # provided a custom type on the class level either explicitly (via
119
- # Attributes::ClassMethods#attribute) or implicitly (via
120
- # AttributeMethods::Serialization::ClassMethods#serialize, +time_zone_aware_attributes+).
121
- # In almost all cases, the sql type should only be used to change quoting behavior, when the primitive to
122
- # represent the type doesn't sufficiently reflect the differences
123
- # (varchar vs binary) for example. The type used to get this primitive
124
- # should have been provided before reaching the connection adapter.
125
- def lookup_cast_type_from_column(column) # :nodoc:
126
- lookup_cast_type(column.sql_type)
127
- end
128
-
129
117
  # Quotes a string, escaping any ' (single quote) and \ (backslash)
130
118
  # characters.
131
119
  def quote_string(s)
@@ -158,7 +146,9 @@ module ActiveRecord
158
146
  if value.is_a?(Proc)
159
147
  value.call
160
148
  else
161
- value = lookup_cast_type(column.sql_type).serialize(value)
149
+ # TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
150
+ cast_type = column.fetch_cast_type(self)
151
+ value = cast_type.serialize(value)
162
152
  quote(value)
163
153
  end
164
154
  end
@@ -208,10 +198,10 @@ module ActiveRecord
208
198
  end
209
199
 
210
200
  def sanitize_as_sql_comment(value) # :nodoc:
211
- # Sanitize a string to appear within a SQL comment
201
+ # Sanitize a string to appear within an SQL comment
212
202
  # For compatibility, this also surrounding "/*+", "/*", and "*/"
213
203
  # charcacters, possibly with single surrounding space.
214
- # Then follows that by replacing any internal "*/" or "/ *" with
204
+ # Then follows that by replacing any internal "*/" or "/*" with
215
205
  # "* /" or "/ *"
216
206
  comment = value.to_s.dup
217
207
  comment.gsub!(%r{\A\s*/\*\+?\s?|\s?\*/\s*\Z}, "")
@@ -220,6 +210,11 @@ module ActiveRecord
220
210
  comment
221
211
  end
222
212
 
213
+ def lookup_cast_type(sql_type) # :nodoc:
214
+ # TODO: Make this method private after we release 8.1.
215
+ type_map.lookup(sql_type)
216
+ end
217
+
223
218
  private
224
219
  def type_casted_binds(binds)
225
220
  binds&.map do |value|
@@ -230,10 +225,6 @@ module ActiveRecord
230
225
  end
231
226
  end
232
227
  end
233
-
234
- def lookup_cast_type(sql_type)
235
- type_map.lookup(sql_type)
236
- end
237
228
  end
238
229
  end
239
230
  end
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  :options_include_default?, :supports_indexes_in_create?, :use_foreign_keys?,
18
18
  :quoted_columns_for_index, :supports_partial_index?, :supports_check_constraints?,
19
19
  :supports_index_include?, :supports_exclusion_constraints?, :supports_unique_constraints?,
20
- :supports_nulls_not_distinct?,
20
+ :supports_nulls_not_distinct?, :lookup_cast_type,
21
21
  to: :@conn, private: true
22
22
 
23
23
  private
@@ -148,7 +148,7 @@ module ActiveRecord
148
148
  end
149
149
 
150
150
  def add_column_options!(sql, options)
151
- sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
151
+ sql << " DEFAULT #{quote_default_expression_for_column_definition(options[:default], options[:column])}" if options_include_default?(options)
152
152
  # must explicitly check for :null to allow change_column to work on migrations
153
153
  if options[:null] == false
154
154
  sql << " NOT NULL"
@@ -162,6 +162,11 @@ module ActiveRecord
162
162
  sql
163
163
  end
164
164
 
165
+ def quote_default_expression_for_column_definition(default, column_definition)
166
+ column_definition.cast_type = lookup_cast_type(column_definition.sql_type)
167
+ quote_default_expression(default, column_definition)
168
+ end
169
+
165
170
  def to_sql(sql)
166
171
  sql = sql.to_sql if sql.respond_to?(:to_sql)
167
172
  sql
@@ -75,7 +75,7 @@ module ActiveRecord
75
75
  # are typically created by methods in TableDefinition, and added to the
76
76
  # +columns+ attribute of said TableDefinition object, in order to be used
77
77
  # for generating a number of table creation or table changing SQL statements.
78
- ColumnDefinition = Struct.new(:name, :type, :options, :sql_type) do # :nodoc:
78
+ ColumnDefinition = Struct.new(:name, :type, :options, :sql_type, :cast_type) do # :nodoc:
79
79
  self::OPTION_NAMES = [
80
80
  :limit,
81
81
  :precision,
@@ -108,6 +108,10 @@ module ActiveRecord
108
108
  def aliased_types(name, fallback)
109
109
  "timestamp" == name ? :datetime : fallback
110
110
  end
111
+
112
+ def fetch_cast_type(connection)
113
+ cast_type
114
+ end
111
115
  end
112
116
 
113
117
  AddColumnDefinition = Struct.new(:column) # :nodoc:
@@ -303,44 +307,32 @@ module ActiveRecord
303
307
  module ColumnMethods
304
308
  extend ActiveSupport::Concern
305
309
 
306
- # Appends a primary key definition to the table definition.
307
- # Can be called multiple times, but this is probably not a good idea.
308
- def primary_key(name, type = :primary_key, **options)
309
- column(name, type, **options.merge(primary_key: true))
310
- end
311
-
312
- ##
313
- # :method: column
314
- # :call-seq: column(name, type, **options)
315
- #
316
- # Appends a column or columns of a specified type.
317
- #
318
- # t.string(:goat)
319
- # t.string(:goat, :sheep)
320
- #
321
- # See TableDefinition#column
322
-
323
- included do
324
- define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
325
- :float, :integer, :json, :string, :text, :time, :timestamp, :virtual
326
-
327
- alias :blob :binary
328
- alias :numeric :decimal
329
- end
330
-
331
310
  class_methods do
332
- def define_column_methods(*column_types) # :nodoc:
333
- column_types.each do |column_type|
334
- module_eval <<-RUBY, __FILE__, __LINE__ + 1
311
+ private
312
+ def define_column_methods(*column_types) # :nodoc:
313
+ column_types.each do |column_type|
314
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
335
315
  def #{column_type}(*names, **options)
336
316
  raise ArgumentError, "Missing column name(s) for #{column_type}" if names.empty?
337
317
  names.each { |name| column(name, :#{column_type}, **options) }
338
318
  end
339
- RUBY
319
+ RUBY
320
+ end
340
321
  end
341
- end
342
- private :define_column_methods
343
322
  end
323
+ extend ClassMethods
324
+
325
+ # Appends a primary key definition to the table definition.
326
+ # Can be called multiple times, but this is probably not a good idea.
327
+ def primary_key(name, type = :primary_key, **options)
328
+ column(name, type, **options, primary_key: true)
329
+ end
330
+
331
+ define_column_methods :bigint, :binary, :boolean, :date, :datetime, :decimal,
332
+ :float, :integer, :json, :string, :text, :time, :timestamp, :virtual
333
+
334
+ alias :blob :binary
335
+ alias :numeric :decimal
344
336
  end
345
337
 
346
338
  # = Active Record Connection Adapters \Table \Definition
@@ -351,7 +343,7 @@ module ActiveRecord
351
343
  # Inside migration files, the +t+ object in {create_table}[rdoc-ref:SchemaStatements#create_table]
352
344
  # is actually of this type:
353
345
  #
354
- # class SomeMigration < ActiveRecord::Migration[8.0]
346
+ # class SomeMigration < ActiveRecord::Migration[8.1]
355
347
  # def up
356
348
  # create_table :foo do |t|
357
349
  # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
@@ -765,7 +757,7 @@ module ActiveRecord
765
757
  # end
766
758
  #
767
759
  # See {connection.index_exists?}[rdoc-ref:SchemaStatements#index_exists?]
768
- def index_exists?(column_name, **options)
760
+ def index_exists?(column_name = nil, **options)
769
761
  @base.index_exists?(name, column_name, **options)
770
762
  end
771
763
 
@@ -85,7 +85,8 @@ module ActiveRecord
85
85
 
86
86
  def schema_default(column)
87
87
  return unless column.has_default?
88
- type = @connection.lookup_cast_type_from_column(column)
88
+ # TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
89
+ type = column.fetch_cast_type(@connection)
89
90
  default = type.deserialize(column.default)
90
91
  if default.nil?
91
92
  schema_expression(column)