activerecord 7.0.0.alpha2 → 7.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +504 -10
  3. data/lib/active_record/associations/association.rb +2 -8
  4. data/lib/active_record/associations/builder/collection_association.rb +9 -2
  5. data/lib/active_record/associations/collection_association.rb +10 -2
  6. data/lib/active_record/associations/preloader/association.rb +68 -48
  7. data/lib/active_record/associations/preloader/batch.rb +3 -6
  8. data/lib/active_record/associations/preloader/through_association.rb +19 -9
  9. data/lib/active_record/associations/preloader.rb +14 -24
  10. data/lib/active_record/associations/through_association.rb +2 -2
  11. data/lib/active_record/associations.rb +16 -3
  12. data/lib/active_record/asynchronous_queries_tracker.rb +3 -0
  13. data/lib/active_record/attribute_methods/dirty.rb +9 -1
  14. data/lib/active_record/attribute_methods.rb +7 -5
  15. data/lib/active_record/autosave_association.rb +3 -3
  16. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +6 -26
  17. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -2
  18. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +18 -5
  19. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  21. data/lib/active_record/connection_adapters/abstract/quoting.rb +33 -70
  22. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +4 -0
  23. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +9 -2
  24. data/lib/active_record/connection_adapters/abstract/transaction.rb +12 -19
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +37 -8
  26. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1 -0
  27. data/lib/active_record/connection_adapters/column.rb +4 -0
  28. data/lib/active_record/connection_adapters/mysql/database_statements.rb +2 -2
  29. data/lib/active_record/connection_adapters/mysql/quoting.rb +23 -24
  30. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
  31. data/lib/active_record/connection_adapters/pool_config.rb +7 -5
  32. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  33. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -44
  34. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  35. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +18 -1
  36. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  37. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -4
  38. data/lib/active_record/connection_adapters/postgresql_adapter.rb +48 -5
  39. data/lib/active_record/connection_adapters/schema_cache.rb +3 -1
  40. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +2 -2
  41. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
  42. data/lib/active_record/connection_handling.rb +31 -19
  43. data/lib/active_record/core.rb +13 -24
  44. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  45. data/lib/active_record/database_configurations/database_config.rb +0 -9
  46. data/lib/active_record/database_configurations/hash_config.rb +40 -8
  47. data/lib/active_record/database_configurations.rb +2 -27
  48. data/lib/active_record/delegated_type.rb +19 -0
  49. data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
  50. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +1 -2
  51. data/lib/active_record/encryption/message_serializer.rb +11 -1
  52. data/lib/active_record/encryption/scheme.rb +1 -1
  53. data/lib/active_record/enum.rb +8 -1
  54. data/lib/active_record/errors.rb +1 -1
  55. data/lib/active_record/explain_registry.rb +11 -6
  56. data/lib/active_record/fixture_set/table_row.rb +1 -1
  57. data/lib/active_record/fixtures.rb +1 -9
  58. data/lib/active_record/future_result.rb +2 -2
  59. data/lib/active_record/gem_version.rb +1 -1
  60. data/lib/active_record/insert_all.rb +52 -15
  61. data/lib/active_record/integration.rb +3 -2
  62. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  63. data/lib/active_record/locking/pessimistic.rb +9 -3
  64. data/lib/active_record/log_subscriber.rb +8 -1
  65. data/lib/active_record/middleware/shard_selector.rb +60 -0
  66. data/lib/active_record/model_schema.rb +1 -28
  67. data/lib/active_record/nested_attributes.rb +11 -10
  68. data/lib/active_record/no_touching.rb +1 -1
  69. data/lib/active_record/persistence.rb +99 -21
  70. data/lib/active_record/query_logs.rb +18 -83
  71. data/lib/active_record/railtie.rb +11 -1
  72. data/lib/active_record/railties/databases.rake +4 -91
  73. data/lib/active_record/reflection.rb +22 -6
  74. data/lib/active_record/relation/calculations.rb +1 -10
  75. data/lib/active_record/relation/finder_methods.rb +0 -13
  76. data/lib/active_record/relation/query_methods.rb +5 -14
  77. data/lib/active_record/relation/record_fetch_warning.rb +5 -7
  78. data/lib/active_record/relation/where_clause.rb +2 -15
  79. data/lib/active_record/relation.rb +11 -13
  80. data/lib/active_record/result.rb +0 -5
  81. data/lib/active_record/runtime_registry.rb +10 -12
  82. data/lib/active_record/schema_dumper.rb +7 -0
  83. data/lib/active_record/scoping.rb +34 -22
  84. data/lib/active_record/suppressor.rb +11 -15
  85. data/lib/active_record/tasks/database_tasks.rb +18 -44
  86. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -1
  87. data/lib/active_record/validations/uniqueness.rb +1 -1
  88. data/lib/active_record.rb +41 -33
  89. data/lib/arel/crud.rb +12 -2
  90. data/lib/arel/delete_manager.rb +16 -0
  91. data/lib/arel/filter_predications.rb +9 -0
  92. data/lib/arel/nodes/delete_statement.rb +5 -1
  93. data/lib/arel/nodes/filter.rb +10 -0
  94. data/lib/arel/nodes/function.rb +1 -0
  95. data/lib/arel/nodes/update_statement.rb +5 -1
  96. data/lib/arel/nodes.rb +1 -0
  97. data/lib/arel/predications.rb +10 -2
  98. data/lib/arel/update_manager.rb +16 -0
  99. data/lib/arel/visitors/mysql.rb +2 -1
  100. data/lib/arel/visitors/to_sql.rb +15 -0
  101. data/lib/arel.rb +1 -0
  102. metadata +14 -10
@@ -9,45 +9,46 @@ module ActiveRecord
9
9
  # Quotes the column value to help prevent
10
10
  # {SQL injection attacks}[https://en.wikipedia.org/wiki/SQL_injection].
11
11
  def quote(value)
12
- if value.is_a?(Base)
13
- ActiveSupport::Deprecation.warn(<<~MSG)
14
- Passing an Active Record object to `quote` directly is deprecated
15
- and will be no longer quoted as id value in Rails 7.0.
16
- MSG
17
- value = value.id_for_database
12
+ case value
13
+ when String, Symbol, ActiveSupport::Multibyte::Chars
14
+ "'#{quote_string(value.to_s)}'"
15
+ when true then quoted_true
16
+ when false then quoted_false
17
+ when nil then "NULL"
18
+ # BigDecimals need to be put in a non-normalized form and quoted.
19
+ when BigDecimal then value.to_s("F")
20
+ when Numeric, ActiveSupport::Duration then value.to_s
21
+ when Type::Binary::Data then quoted_binary(value)
22
+ when Type::Time::Value then "'#{quoted_time(value)}'"
23
+ when Date, Time then "'#{quoted_date(value)}'"
24
+ when Class then "'#{value}'"
25
+ else raise TypeError, "can't quote #{value.class.name}"
18
26
  end
19
-
20
- _quote(value)
21
27
  end
22
28
 
23
29
  # Cast a +value+ to a type that the database understands. For example,
24
30
  # SQLite does not understand dates, so this method will convert a Date
25
31
  # to a String.
26
- def type_cast(value, column = nil)
27
- if value.is_a?(Base)
28
- ActiveSupport::Deprecation.warn(<<~MSG)
29
- Passing an Active Record object to `type_cast` directly is deprecated
30
- and will be no longer type casted as id value in Rails 7.0.
31
- MSG
32
- value = value.id_for_database
33
- end
34
-
35
- if column
36
- ActiveSupport::Deprecation.warn(<<~MSG)
37
- Passing a column to `type_cast` is deprecated and will be removed in Rails 7.0.
38
- MSG
39
- type = lookup_cast_type_from_column(column)
40
- value = type.serialize(value)
32
+ def type_cast(value)
33
+ case value
34
+ when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
35
+ value.to_s
36
+ when true then unquoted_true
37
+ when false then unquoted_false
38
+ # BigDecimals need to be put in a non-normalized form and quoted.
39
+ when BigDecimal then value.to_s("F")
40
+ when nil, Numeric, String then value
41
+ when Type::Time::Value then quoted_time(value)
42
+ when Date, Time then quoted_date(value)
43
+ else raise TypeError, "can't cast #{value.class.name}"
41
44
  end
42
-
43
- _type_cast(value)
44
45
  end
45
46
 
46
47
  # Quote a value to be used as a bound parameter of unknown type. For example,
47
48
  # MySQL might perform dangerous castings when comparing a string to a number,
48
49
  # so this method will cast numbers to string.
49
50
  def quote_bound_value(value)
50
- _quote(value)
51
+ quote(value)
51
52
  end
52
53
 
53
54
  # If you are having to call this function, you are likely doing something
@@ -127,7 +128,7 @@ module ActiveRecord
127
128
  end
128
129
  end
129
130
 
130
- result = value.to_s(:db)
131
+ result = value.to_formatted_s(:db)
131
132
  if value.respond_to?(:usec) && value.usec > 0
132
133
  result << "." << sprintf("%06d", value.usec)
133
134
  else
@@ -203,16 +204,11 @@ module ActiveRecord
203
204
 
204
205
  private
205
206
  def type_casted_binds(binds)
206
- case binds.first
207
- when Array
208
- binds.map { |column, value| type_cast(value, column) }
209
- else
210
- binds.map do |value|
211
- if ActiveModel::Attribute === value
212
- type_cast(value.value_for_database)
213
- else
214
- type_cast(value)
215
- end
207
+ binds.map do |value|
208
+ if ActiveModel::Attribute === value
209
+ type_cast(value.value_for_database)
210
+ else
211
+ type_cast(value)
216
212
  end
217
213
  end
218
214
  end
@@ -220,39 +216,6 @@ module ActiveRecord
220
216
  def lookup_cast_type(sql_type)
221
217
  type_map.lookup(sql_type)
222
218
  end
223
-
224
- def _quote(value)
225
- case value
226
- when String, Symbol, ActiveSupport::Multibyte::Chars
227
- "'#{quote_string(value.to_s)}'"
228
- when true then quoted_true
229
- when false then quoted_false
230
- when nil then "NULL"
231
- # BigDecimals need to be put in a non-normalized form and quoted.
232
- when BigDecimal then value.to_s("F")
233
- when Numeric, ActiveSupport::Duration then value.to_s
234
- when Type::Binary::Data then quoted_binary(value)
235
- when Type::Time::Value then "'#{quoted_time(value)}'"
236
- when Date, Time then "'#{quoted_date(value)}'"
237
- when Class then "'#{value}'"
238
- else raise TypeError, "can't quote #{value.class.name}"
239
- end
240
- end
241
-
242
- def _type_cast(value)
243
- case value
244
- when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
245
- value.to_s
246
- when true then unquoted_true
247
- when false then unquoted_false
248
- # BigDecimals need to be put in a non-normalized form and quoted.
249
- when BigDecimal then value.to_s("F")
250
- when nil, Numeric, String then value
251
- when Type::Time::Value then quoted_time(value)
252
- when Date, Time then quoted_date(value)
253
- else raise TypeError, "can't cast #{value.class.name}"
254
- end
255
- end
256
219
  end
257
220
  end
258
221
  end
@@ -106,6 +106,10 @@ module ActiveRecord
106
106
  options[:on_update]
107
107
  end
108
108
 
109
+ def deferrable
110
+ options[:deferrable]
111
+ end
112
+
109
113
  def custom_primary_key?
110
114
  options[:primary_key] != default_primary_key
111
115
  end
@@ -124,6 +124,9 @@ module ActiveRecord
124
124
  # column_exists?(:suppliers, :name)
125
125
  #
126
126
  # # Check a column exists of a particular type
127
+ # #
128
+ # # This works for standard non-casted types (eg. string) but is unreliable
129
+ # # for types that may get cast to something else (eg. char, bigint).
127
130
  # column_exists?(:suppliers, :name, :string)
128
131
  #
129
132
  # # Check a column exists with a specific definition
@@ -653,7 +656,8 @@ module ActiveRecord
653
656
  # The +type+ and +options+ parameters will be ignored if present. It can be helpful
654
657
  # to provide these in a migration's +change+ method so it can be reverted.
655
658
  # In that case, +type+ and +options+ will be used by #add_column.
656
- # Indexes on the column are automatically removed.
659
+ # Depending on the database you're using, indexes using this column may be
660
+ # automatically removed or modified to remove this column from the index.
657
661
  #
658
662
  # If the options provided include an +if_exists+ key, it will be used to check if the
659
663
  # column does not exist. This will silently ignore the migration rather than raising
@@ -778,7 +782,7 @@ module ActiveRecord
778
782
  #
779
783
  # CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
780
784
  #
781
- # Note: SQLite doesn't support index length.
785
+ # Note: only supported by MySQL
782
786
  #
783
787
  # ====== Creating an index with a sort order (desc or asc, asc is the default)
784
788
  #
@@ -1075,6 +1079,9 @@ module ActiveRecord
1075
1079
  # duplicate column errors.
1076
1080
  # [<tt>:validate</tt>]
1077
1081
  # (PostgreSQL only) Specify whether or not the constraint should be validated. Defaults to +true+.
1082
+ # [<tt>:deferrable</tt>]
1083
+ # (PostgreSQL only) Specify whether or not the foreign key should be deferrable. Valid values are booleans or
1084
+ # +:deferred+ or +:immediate+ to specify the default behavior. Defaults to +false+.
1078
1085
  def add_foreign_key(from_table, to_table, **options)
1079
1086
  return unless supports_foreign_keys?
1080
1087
  return if options[:if_not_exists] == true && foreign_key_exists?(from_table, to_table)
@@ -333,26 +333,19 @@ module ActiveRecord
333
333
  # @connection still holds an open or invalid transaction, so we must not
334
334
  # put it back in the pool for reuse.
335
335
  @connection.throw_away! unless transaction.state.rolledback?
336
+ elsif Thread.current.status == "aborting" || (!completed && transaction.written)
337
+ # The transaction is still open but the block returned earlier.
338
+ #
339
+ # The block could return early because of a timeout or because the thread is aborting,
340
+ # so we are rolling back to make sure the timeout didn't caused the transaction to be
341
+ # committed incompletely.
342
+ rollback_transaction
336
343
  else
337
- if Thread.current.status == "aborting"
338
- rollback_transaction
339
- else
340
- if !completed && transaction.written
341
- ActiveSupport::Deprecation.warn(<<~EOW)
342
- Using `return`, `break` or `throw` to exit a transaction block is
343
- deprecated without replacement. If the `throw` came from
344
- `Timeout.timeout(duration)`, pass an exception class as a second
345
- argument so it doesn't use `throw` to abort its block. This results
346
- in the transaction being committed, but in the next release of Rails
347
- it will rollback.
348
- EOW
349
- end
350
- begin
351
- commit_transaction
352
- rescue Exception
353
- rollback_transaction(transaction) unless transaction.state.completed?
354
- raise
355
- end
344
+ begin
345
+ commit_transaction
346
+ rescue Exception
347
+ rollback_transaction(transaction) unless transaction.state.completed?
348
+ raise
356
349
  end
357
350
  end
358
351
  end
@@ -88,7 +88,7 @@ module ActiveRecord
88
88
  @logger = logger
89
89
  @config = config
90
90
  @pool = ActiveRecord::ConnectionAdapters::NullPool.new
91
- @idle_since = Concurrent.monotonic_time
91
+ @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
92
92
  @visitor = arel_visitor
93
93
  @statements = build_statement_pool
94
94
  @lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
@@ -141,9 +141,9 @@ module ActiveRecord
141
141
  def preventing_writes?
142
142
  return true if replica?
143
143
  return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord.legacy_connection_handling
144
- return false if connection_klass.nil?
144
+ return false if connection_class.nil?
145
145
 
146
- connection_klass.current_preventing_writes
146
+ connection_class.current_preventing_writes
147
147
  end
148
148
 
149
149
  def migrations_paths # :nodoc:
@@ -178,7 +178,7 @@ module ActiveRecord
178
178
  alias :prepared_statements :prepared_statements?
179
179
 
180
180
  def prepared_statements_disabled_cache # :nodoc:
181
- Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
181
+ ActiveSupport::IsolatedExecutionState[:active_record_prepared_statements_disabled_cache] ||= Set.new
182
182
  end
183
183
 
184
184
  class Version
@@ -220,8 +220,20 @@ module ActiveRecord
220
220
  @owner = Thread.current
221
221
  end
222
222
 
223
- def connection_klass # :nodoc:
224
- @pool.connection_klass
223
+ def connection_class # :nodoc:
224
+ @pool.connection_class
225
+ end
226
+
227
+ # The role (ie :writing) for the current connection. In a
228
+ # non-multi role application, `:writing` is returned.
229
+ def role
230
+ @pool.role
231
+ end
232
+
233
+ # The shard (ie :default) for the current connection. In
234
+ # a non-sharded application, `:default` is returned.
235
+ def shard
236
+ @pool.shard
225
237
  end
226
238
 
227
239
  def schema_cache
@@ -242,7 +254,7 @@ module ActiveRecord
242
254
  "Current thread: #{Thread.current}."
243
255
  end
244
256
 
245
- @idle_since = Concurrent.monotonic_time
257
+ @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
246
258
  @owner = nil
247
259
  else
248
260
  raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
@@ -265,7 +277,7 @@ module ActiveRecord
265
277
  # Seconds since this connection was returned to the pool
266
278
  def seconds_idle # :nodoc:
267
279
  return 0 if in_use?
268
- Concurrent.monotonic_time - @idle_since
280
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
269
281
  end
270
282
 
271
283
  def unprepared_statement
@@ -363,6 +375,11 @@ module ActiveRecord
363
375
  false
364
376
  end
365
377
 
378
+ # Does this adapter support creating deferrable constraints?
379
+ def supports_deferrable_constraints?
380
+ false
381
+ end
382
+
366
383
  # Does this adapter support creating check constraints?
367
384
  def supports_check_constraints?
368
385
  false
@@ -454,6 +471,10 @@ module ActiveRecord
454
471
  def enable_extension(name)
455
472
  end
456
473
 
474
+ # This is meant to be implemented by the adapters that support custom enum types
475
+ def create_enum(*) # :nodoc:
476
+ end
477
+
457
478
  def advisory_locks_enabled? # :nodoc:
458
479
  supports_advisory_locks? && @advisory_locks_enabled
459
480
  end
@@ -632,6 +653,14 @@ module ActiveRecord
632
653
  def check_version # :nodoc:
633
654
  end
634
655
 
656
+ # Returns the version identifier of the schema currently available in
657
+ # the database. This is generally equal to the number of the highest-
658
+ # numbered migration that has been executed, or 0 if no schema
659
+ # information is present / the database is empty.
660
+ def schema_version
661
+ migration_context.current_version
662
+ end
663
+
635
664
  def field_ordered_value(column, values) # :nodoc:
636
665
  node = Arel::Nodes::Case.new(column)
637
666
  values.each.with_index(1) do |value, order|
@@ -31,6 +31,7 @@ module ActiveRecord
31
31
  string: { name: "varchar", limit: 255 },
32
32
  text: { name: "text" },
33
33
  integer: { name: "int", limit: 4 },
34
+ bigint: { name: "bigint" },
34
35
  float: { name: "float", limit: 24 },
35
36
  decimal: { name: "decimal" },
36
37
  datetime: { name: "datetime" },
@@ -87,6 +87,10 @@ module ActiveRecord
87
87
  comment.hash
88
88
  end
89
89
 
90
+ def virtual?
91
+ false
92
+ end
93
+
90
94
  private
91
95
  def deduplicated
92
96
  @name = -name
@@ -30,9 +30,9 @@ module ActiveRecord
30
30
 
31
31
  def explain(arel, binds = [])
32
32
  sql = "EXPLAIN #{to_sql(arel, binds)}"
33
- start = Concurrent.monotonic_time
33
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
34
34
  result = exec_query(sql, "EXPLAIN", binds)
35
- elapsed = Concurrent.monotonic_time - start
35
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
36
36
 
37
37
  MySQL::ExplainPrettyPrinter.new.pp(result, elapsed)
38
38
  end
@@ -9,15 +9,15 @@ module ActiveRecord
9
9
  def quote_bound_value(value)
10
10
  case value
11
11
  when Numeric
12
- _quote(value.to_s)
12
+ quote(value.to_s)
13
13
  when BigDecimal
14
- _quote(value.to_s("F"))
14
+ quote(value.to_s("F"))
15
15
  when true
16
16
  "'1'"
17
17
  when false
18
18
  "'0'"
19
19
  else
20
- _quote(value)
20
+ quote(value)
21
21
  end
22
22
  end
23
23
 
@@ -49,6 +49,26 @@ module ActiveRecord
49
49
  "x'#{value.hex}'"
50
50
  end
51
51
 
52
+ # Override +type_cast+ we pass to mysql2 Date and Time objects instead
53
+ # of Strings since mysql2 is able to handle those classes more efficiently.
54
+ def type_cast(value) # :nodoc:
55
+ case value
56
+ when ActiveSupport::TimeWithZone
57
+ # We need to check explicitly for ActiveSupport::TimeWithZone because
58
+ # we need to transform it to Time objects but we don't want to
59
+ # transform Time objects to themselves.
60
+ if ActiveRecord.default_timezone == :utc
61
+ value.getutc
62
+ else
63
+ value.getlocal
64
+ end
65
+ when Date, Time
66
+ value
67
+ else
68
+ super
69
+ end
70
+ end
71
+
52
72
  def column_name_matcher
53
73
  COLUMN_NAME
54
74
  end
@@ -84,27 +104,6 @@ module ActiveRecord
84
104
  /ix
85
105
 
86
106
  private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
87
-
88
- private
89
- # Override +_type_cast+ we pass to mysql2 Date and Time objects instead
90
- # of Strings since mysql2 is able to handle those classes more efficiently.
91
- def _type_cast(value)
92
- case value
93
- when ActiveSupport::TimeWithZone
94
- # We need to check explicitly for ActiveSupport::TimeWithZone because
95
- # we need to transform it to Time objects but we don't want to
96
- # transform Time objects to themselves.
97
- if ActiveRecord.default_timezone == :utc
98
- value.getutc
99
- else
100
- value.getlocal
101
- end
102
- when Date, Time
103
- value
104
- else
105
- super
106
- end
107
- end
108
107
  end
109
108
  end
110
109
  end
@@ -206,7 +206,7 @@ module ActiveRecord
206
206
  def data_source_sql(name = nil, type: nil)
207
207
  scope = quoted_scope(name, type: type)
208
208
 
209
- sql = +"SELECT table_name FROM (SELECT * FROM information_schema.tables "
209
+ sql = +"SELECT table_name FROM (SELECT table_name, table_type FROM information_schema.tables "
210
210
  sql << " WHERE table_schema = #{scope[:schema]}) _subquery"
211
211
  if scope[:type] || scope[:name]
212
212
  conditions = []
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  class PoolConfig # :nodoc:
6
6
  include Mutex_m
7
7
 
8
- attr_reader :db_config, :connection_klass
8
+ attr_reader :db_config, :connection_class, :role, :shard
9
9
  attr_accessor :schema_cache
10
10
 
11
11
  INSTANCES = ObjectSpace::WeakMap.new
@@ -17,19 +17,21 @@ module ActiveRecord
17
17
  end
18
18
  end
19
19
 
20
- def initialize(connection_klass, db_config)
20
+ def initialize(connection_class, db_config, role, shard)
21
21
  super()
22
- @connection_klass = connection_klass
22
+ @connection_class = connection_class
23
23
  @db_config = db_config
24
+ @role = role
25
+ @shard = shard
24
26
  @pool = nil
25
27
  INSTANCES[self] = self
26
28
  end
27
29
 
28
30
  def connection_specification_name
29
- if connection_klass.primary_class?
31
+ if connection_class.primary_class?
30
32
  "ActiveRecord::Base"
31
33
  else
32
- connection_klass.name
34
+ connection_class.name
33
35
  end
34
36
  end
35
37
 
@@ -1,25 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/object/blank"
4
+
3
5
  module ActiveRecord
4
6
  module ConnectionAdapters
5
7
  module PostgreSQL
6
8
  class Column < ConnectionAdapters::Column # :nodoc:
7
9
  delegate :oid, :fmod, to: :sql_type_metadata
8
10
 
9
- def initialize(*, serial: nil, **)
11
+ def initialize(*, serial: nil, generated: nil, **)
10
12
  super
11
13
  @serial = serial
14
+ @generated = generated
12
15
  end
13
16
 
14
17
  def serial?
15
18
  @serial
16
19
  end
17
20
 
21
+ def virtual?
22
+ # We assume every generated column is virtual, no matter the concrete type
23
+ @generated.present?
24
+ end
25
+
26
+ def has_default?
27
+ super && !virtual?
28
+ end
29
+
18
30
  def array
19
31
  sql_type_metadata.sql_type.end_with?("[]")
20
32
  end
21
33
  alias :array? :array
22
34
 
35
+ def enum?
36
+ type == :enum
37
+ end
38
+
23
39
  def sql_type
24
40
  super.delete_suffix("[]")
25
41
  end
@@ -16,6 +16,31 @@ module ActiveRecord
16
16
  @connection.unescape_bytea(value) if value
17
17
  end
18
18
 
19
+ def quote(value) # :nodoc:
20
+ case value
21
+ when OID::Xml::Data
22
+ "xml '#{quote_string(value.to_s)}'"
23
+ when OID::Bit::Data
24
+ if value.binary?
25
+ "B'#{value}'"
26
+ elsif value.hex?
27
+ "X'#{value}'"
28
+ end
29
+ when Numeric
30
+ if value.finite?
31
+ super
32
+ else
33
+ "'#{value}'"
34
+ end
35
+ when OID::Array::Data
36
+ quote(encode_array(value))
37
+ when Range
38
+ quote(encode_range(value))
39
+ else
40
+ super
41
+ end
42
+ end
43
+
19
44
  # Quotes strings for use in SQL input.
20
45
  def quote_string(s) # :nodoc:
21
46
  PG::Connection.escape(s)
@@ -74,6 +99,24 @@ module ActiveRecord
74
99
  end
75
100
  end
76
101
 
102
+ def type_cast(value) # :nodoc:
103
+ case value
104
+ when Type::Binary::Data
105
+ # Return a bind param hash with format as binary.
106
+ # See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
107
+ # for more information
108
+ { value: value.to_s, format: 1 }
109
+ when OID::Xml::Data, OID::Bit::Data
110
+ value.to_s
111
+ when OID::Array::Data
112
+ encode_array(value)
113
+ when Range
114
+ encode_range(value)
115
+ else
116
+ super
117
+ end
118
+ end
119
+
77
120
  def lookup_cast_type_from_column(column) # :nodoc:
78
121
  type_map.lookup(column.oid, column.fmod, column.sql_type)
79
122
  end
@@ -120,49 +163,6 @@ module ActiveRecord
120
163
  super(query_value("SELECT #{quote(sql_type)}::regtype::oid", "SCHEMA").to_i)
121
164
  end
122
165
 
123
- def _quote(value)
124
- case value
125
- when OID::Xml::Data
126
- "xml '#{quote_string(value.to_s)}'"
127
- when OID::Bit::Data
128
- if value.binary?
129
- "B'#{value}'"
130
- elsif value.hex?
131
- "X'#{value}'"
132
- end
133
- when Numeric
134
- if value.finite?
135
- super
136
- else
137
- "'#{value}'"
138
- end
139
- when OID::Array::Data
140
- _quote(encode_array(value))
141
- when Range
142
- _quote(encode_range(value))
143
- else
144
- super
145
- end
146
- end
147
-
148
- def _type_cast(value)
149
- case value
150
- when Type::Binary::Data
151
- # Return a bind param hash with format as binary.
152
- # See https://deveiate.org/code/pg/PG/Connection.html#method-i-exec_prepared-doc
153
- # for more information
154
- { value: value.to_s, format: 1 }
155
- when OID::Xml::Data, OID::Bit::Data
156
- value.to_s
157
- when OID::Array::Data
158
- encode_array(value)
159
- when Range
160
- encode_range(value)
161
- else
162
- super
163
- end
164
- end
165
-
166
166
  def encode_array(array_data)
167
167
  encoder = array_data.encoder
168
168
  values = type_cast_array(array_data.values)
@@ -188,7 +188,7 @@ module ActiveRecord
188
188
  def type_cast_array(values)
189
189
  case values
190
190
  when ::Array then values.map { |item| type_cast_array(item) }
191
- else _type_cast(values)
191
+ else type_cast(values)
192
192
  end
193
193
  end
194
194