activerecord 7.2.0 → 8.0.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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +189 -745
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/associations/association.rb +25 -5
  5. data/lib/active_record/associations/builder/association.rb +7 -6
  6. data/lib/active_record/associations/collection_association.rb +10 -8
  7. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  8. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  9. data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
  10. data/lib/active_record/associations/join_dependency.rb +5 -5
  11. data/lib/active_record/associations/preloader/association.rb +2 -2
  12. data/lib/active_record/associations/singular_association.rb +8 -3
  13. data/lib/active_record/associations.rb +34 -4
  14. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  15. data/lib/active_record/attribute_assignment.rb +9 -1
  16. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
  17. data/lib/active_record/attributes.rb +6 -5
  18. data/lib/active_record/autosave_association.rb +69 -27
  19. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
  20. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  21. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +23 -44
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +53 -18
  25. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  26. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  27. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +26 -5
  28. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  29. data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -25
  30. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +20 -38
  31. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  32. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  33. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +44 -46
  34. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
  35. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
  36. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
  37. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
  38. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  39. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -1
  40. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +50 -6
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +38 -90
  42. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  43. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
  44. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  45. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
  46. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
  47. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -12
  48. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  49. data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
  50. data/lib/active_record/connection_handling.rb +22 -0
  51. data/lib/active_record/core.rb +16 -9
  52. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  53. data/lib/active_record/encryption/config.rb +3 -1
  54. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  55. data/lib/active_record/encryption/encrypted_attribute_type.rb +12 -3
  56. data/lib/active_record/encryption/encryptor.rb +16 -9
  57. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  58. data/lib/active_record/encryption/key_provider.rb +1 -1
  59. data/lib/active_record/encryption/scheme.rb +8 -1
  60. data/lib/active_record/encryption.rb +2 -0
  61. data/lib/active_record/enum.rb +8 -0
  62. data/lib/active_record/errors.rb +13 -5
  63. data/lib/active_record/fixtures.rb +0 -1
  64. data/lib/active_record/future_result.rb +14 -10
  65. data/lib/active_record/gem_version.rb +3 -3
  66. data/lib/active_record/insert_all.rb +1 -1
  67. data/lib/active_record/migration/command_recorder.rb +22 -5
  68. data/lib/active_record/migration/compatibility.rb +5 -2
  69. data/lib/active_record/migration.rb +35 -33
  70. data/lib/active_record/model_schema.rb +6 -3
  71. data/lib/active_record/nested_attributes.rb +11 -2
  72. data/lib/active_record/persistence.rb +128 -130
  73. data/lib/active_record/query_logs.rb +97 -39
  74. data/lib/active_record/query_logs_formatter.rb +17 -28
  75. data/lib/active_record/querying.rb +6 -6
  76. data/lib/active_record/railtie.rb +8 -14
  77. data/lib/active_record/reflection.rb +19 -10
  78. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  79. data/lib/active_record/relation/batches.rb +135 -75
  80. data/lib/active_record/relation/calculations.rb +24 -19
  81. data/lib/active_record/relation/delegation.rb +25 -14
  82. data/lib/active_record/relation/finder_methods.rb +18 -18
  83. data/lib/active_record/relation/merger.rb +8 -8
  84. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  85. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  86. data/lib/active_record/relation/predicate_builder.rb +6 -1
  87. data/lib/active_record/relation/query_methods.rb +58 -37
  88. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  89. data/lib/active_record/relation/spawn_methods.rb +1 -1
  90. data/lib/active_record/relation.rb +72 -61
  91. data/lib/active_record/result.rb +68 -7
  92. data/lib/active_record/sanitization.rb +7 -6
  93. data/lib/active_record/schema_dumper.rb +5 -0
  94. data/lib/active_record/schema_migration.rb +2 -1
  95. data/lib/active_record/scoping/named.rb +6 -2
  96. data/lib/active_record/statement_cache.rb +12 -12
  97. data/lib/active_record/store.rb +7 -3
  98. data/lib/active_record/tasks/database_tasks.rb +36 -16
  99. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  100. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  101. data/lib/active_record/test_fixtures.rb +12 -0
  102. data/lib/active_record/token_for.rb +1 -1
  103. data/lib/active_record/validations/uniqueness.rb +9 -8
  104. data/lib/active_record.rb +15 -0
  105. data/lib/arel/collectors/bind.rb +1 -1
  106. metadata +14 -14
@@ -12,16 +12,8 @@ module ActiveRecord
12
12
 
13
13
  # Queries the database and returns the results in an Array-like object
14
14
  def query(sql, name = nil) # :nodoc:
15
- mark_transaction_written_if_write(sql)
16
-
17
- log(sql, name) do |notification_payload|
18
- with_raw_connection do |conn|
19
- result = conn.async_exec(sql).map_types!(@type_map_for_results).values
20
- verified!
21
- notification_payload[:row_count] = result.count
22
- result
23
- end
24
- end
15
+ result = internal_execute(sql, name)
16
+ result.map_types!(@type_map_for_results).values
25
17
  end
26
18
 
27
19
  READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
@@ -50,36 +42,6 @@ module ActiveRecord
50
42
  @notice_receiver_sql_warnings = []
51
43
  end
52
44
 
53
- def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
54
- log(sql, name, async: async) do |notification_payload|
55
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
56
- result = conn.async_exec(sql)
57
- verified!
58
- handle_warnings(result)
59
- notification_payload[:row_count] = result.count
60
- result
61
- end
62
- end
63
- end
64
-
65
- def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
66
- execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
67
- types = {}
68
- fields = result.fields
69
- fields.each_with_index do |fname, i|
70
- ftype = result.ftype i
71
- fmod = result.fmod i
72
- types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
73
- end
74
- build_result(columns: fields, rows: result.values, column_types: types.freeze)
75
- end
76
- end
77
-
78
- def exec_delete(sql, name = nil, binds = []) # :nodoc:
79
- execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
80
- end
81
- alias :exec_update :exec_delete
82
-
83
45
  def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
84
46
  if use_insert_returning? || pk == false
85
47
  super
@@ -170,8 +132,68 @@ module ActiveRecord
170
132
  rescue PG::Error
171
133
  end
172
134
 
173
- def execute_batch(statements, name = nil)
174
- execute(combine_multi_statements(statements))
135
+ def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
136
+ update_typemap_for_default_timezone
137
+ result = if prepare
138
+ begin
139
+ stmt_key = prepare_statement(sql, binds, raw_connection)
140
+ notification_payload[:statement_name] = stmt_key
141
+ raw_connection.exec_prepared(stmt_key, type_casted_binds)
142
+ rescue PG::FeatureNotSupported => error
143
+ if is_cached_plan_failure?(error)
144
+ # Nothing we can do if we are in a transaction because all commands
145
+ # will raise InFailedSQLTransaction
146
+ if in_transaction?
147
+ raise PreparedStatementCacheExpired.new(error.message, connection_pool: @pool)
148
+ else
149
+ @lock.synchronize do
150
+ # outside of transactions we can simply flush this query and retry
151
+ @statements.delete sql_key(sql)
152
+ end
153
+ retry
154
+ end
155
+ end
156
+
157
+ raise
158
+ end
159
+ elsif binds.nil? || binds.empty?
160
+ raw_connection.async_exec(sql)
161
+ else
162
+ raw_connection.exec_params(sql, type_casted_binds)
163
+ end
164
+
165
+ verified!
166
+ handle_warnings(result)
167
+ notification_payload[:row_count] = result.count
168
+ result
169
+ end
170
+
171
+ def cast_result(result)
172
+ if result.fields.empty?
173
+ result.clear
174
+ return ActiveRecord::Result.empty
175
+ end
176
+
177
+ types = {}
178
+ fields = result.fields
179
+ fields.each_with_index do |fname, i|
180
+ ftype = result.ftype i
181
+ fmod = result.fmod i
182
+ types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
183
+ end
184
+ ar_result = ActiveRecord::Result.new(fields, result.values, types.freeze)
185
+ result.clear
186
+ ar_result
187
+ end
188
+
189
+ def affected_rows(result)
190
+ affected_rows = result.cmd_tuples
191
+ result.clear
192
+ affected_rows
193
+ end
194
+
195
+ def execute_batch(statements, name = nil, **kwargs)
196
+ raw_execute(combine_multi_statements(statements), name, batch: true, **kwargs)
175
197
  end
176
198
 
177
199
  def build_truncate_statements(table_names)
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  # TODO: Remove when IPAddr#== compares IPAddr#prefix. See
32
32
  # https://github.com/ruby/ipaddr/issues/21
33
33
  def changed?(old_value, new_value, _new_value_before_type_cast)
34
- super || !old_value.nil? && old_value.prefix != new_value.prefix
34
+ !old_value.eql?(new_value) || !old_value.nil? && old_value.prefix != new_value.prefix
35
35
  end
36
36
 
37
37
  def cast_value(value)
@@ -25,6 +25,10 @@ module ActiveRecord
25
25
  build_point(x, y)
26
26
  when ::Array
27
27
  build_point(*value)
28
+ when ::Hash
29
+ return if value.blank?
30
+
31
+ build_point(*values_array_from_hash(value))
28
32
  else
29
33
  value
30
34
  end
@@ -36,6 +40,8 @@ module ActiveRecord
36
40
  "(#{number_for_point(value.x)},#{number_for_point(value.y)})"
37
41
  when ::Array
38
42
  serialize(build_point(*value))
43
+ when ::Hash
44
+ serialize(build_point(*values_array_from_hash(value)))
39
45
  else
40
46
  super
41
47
  end
@@ -57,6 +63,10 @@ module ActiveRecord
57
63
  def build_point(x, y)
58
64
  ActiveRecord::Point.new(Float(x), Float(y))
59
65
  end
66
+
67
+ def values_array_from_hash(value)
68
+ [value.values_at(:x, "x").compact.first, value.values_at(:y, "y").compact.first]
69
+ end
60
70
  end
61
71
  end
62
72
  end
@@ -18,7 +18,6 @@ module ActiveRecord
18
18
 
19
19
  def visit_AddForeignKey(o)
20
20
  super.dup.tap do |sql|
21
- sql << " DEFERRABLE INITIALLY #{o.options[:deferrable].to_s.upcase}" if o.deferrable
22
21
  sql << " NOT VALID" unless o.validate?
23
22
  end
24
23
  end
@@ -54,9 +54,9 @@ module ActiveRecord
54
54
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
55
55
  end
56
56
 
57
- def drop_table(table_name, **options) # :nodoc:
58
- schema_cache.clear_data_source_cache!(table_name.to_s)
59
- execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
57
+ def drop_table(*table_names, **options) # :nodoc:
58
+ table_names.each { |table_name| schema_cache.clear_data_source_cache!(table_name.to_s) }
59
+ execute "DROP TABLE#{' IF EXISTS' if options[:if_exists]} #{table_names.map { |table_name| quote_table_name(table_name) }.join(', ')}#{' CASCADE' if options[:force] == :cascade}"
60
60
  end
61
61
 
62
62
  # Returns true if schema exists.
@@ -152,9 +152,23 @@ module ActiveRecord
152
152
  end
153
153
 
154
154
  def table_options(table_name) # :nodoc:
155
- if comment = table_comment(table_name)
156
- { comment: comment }
155
+ options = {}
156
+
157
+ comment = table_comment(table_name)
158
+
159
+ options[:comment] = comment if comment
160
+
161
+ inherited_table_names = inherited_table_names(table_name).presence
162
+
163
+ options[:options] = "INHERITS (#{inherited_table_names.join(", ")})" if inherited_table_names
164
+
165
+ if !options[:options] && supports_native_partitioning?
166
+ partition_definition = table_partition_definition(table_name)
167
+
168
+ options[:options] = "PARTITION BY #{partition_definition}" if partition_definition
157
169
  end
170
+
171
+ options
158
172
  end
159
173
 
160
174
  # Returns a comment stored in database for given table
@@ -172,6 +186,36 @@ module ActiveRecord
172
186
  end
173
187
  end
174
188
 
189
+ # Returns the partition definition of a given table
190
+ def table_partition_definition(table_name) # :nodoc:
191
+ scope = quoted_scope(table_name, type: "BASE TABLE")
192
+
193
+ query_value(<<~SQL, "SCHEMA")
194
+ SELECT pg_catalog.pg_get_partkeydef(c.oid)
195
+ FROM pg_catalog.pg_class c
196
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
197
+ WHERE c.relname = #{scope[:name]}
198
+ AND c.relkind IN (#{scope[:type]})
199
+ AND n.nspname = #{scope[:schema]}
200
+ SQL
201
+ end
202
+
203
+ # Returns the inherited table name of a given table
204
+ def inherited_table_names(table_name) # :nodoc:
205
+ scope = quoted_scope(table_name, type: "BASE TABLE")
206
+
207
+ query_values(<<~SQL, "SCHEMA")
208
+ SELECT parent.relname
209
+ FROM pg_catalog.pg_inherits i
210
+ JOIN pg_catalog.pg_class child ON i.inhrelid = child.oid
211
+ JOIN pg_catalog.pg_class parent ON i.inhparent = parent.oid
212
+ LEFT JOIN pg_namespace n ON n.oid = child.relnamespace
213
+ WHERE child.relname = #{scope[:name]}
214
+ AND child.relkind IN (#{scope[:type]})
215
+ AND n.nspname = #{scope[:schema]}
216
+ SQL
217
+ end
218
+
175
219
  # Returns the current database name.
176
220
  def current_database
177
221
  query_value("SELECT current_database()", "SCHEMA")
@@ -250,7 +294,7 @@ module ActiveRecord
250
294
 
251
295
  # Set the client message level.
252
296
  def client_min_messages=(level)
253
- internal_execute("SET client_min_messages TO '#{level}'")
297
+ internal_execute("SET client_min_messages TO '#{level}'", "SCHEMA")
254
298
  end
255
299
 
256
300
  # Returns the sequence name for a table's primary key or some other specified key.
@@ -86,7 +86,7 @@ module ActiveRecord
86
86
  "-c #{name}=#{value.to_s.gsub(/[ \\]/, '\\\\\0')}" unless value == ":default" || value == :default
87
87
  end.join(" ")
88
88
  end
89
- find_cmd_and_exec("psql", config.database)
89
+ find_cmd_and_exec(ActiveRecord.database_cli[:postgresql], config.database)
90
90
  end
91
91
  end
92
92
 
@@ -284,6 +284,10 @@ module ActiveRecord
284
284
  database_version >= 15_00_00 # >= 15.0
285
285
  end
286
286
 
287
+ def supports_native_partitioning? # :nodoc:
288
+ database_version >= 10_00_00 # >= 10.0
289
+ end
290
+
287
291
  def index_algorithms
288
292
  { concurrently: "CONCURRENTLY" }
289
293
  end
@@ -405,7 +409,7 @@ module ActiveRecord
405
409
  end
406
410
 
407
411
  def set_standard_conforming_strings
408
- internal_execute("SET standard_conforming_strings = on")
412
+ internal_execute("SET standard_conforming_strings = on", "SCHEMA")
409
413
  end
410
414
 
411
415
  def supports_ddl_transactions?
@@ -479,6 +483,7 @@ module ActiveRecord
479
483
  # Set to +:cascade+ to drop dependent objects as well.
480
484
  # Defaults to false.
481
485
  def disable_extension(name, force: false)
486
+ _schema, name = name.to_s.split(".").values_at(-2, -1)
482
487
  internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
483
488
  reload_type_map
484
489
  }
@@ -493,7 +498,19 @@ module ActiveRecord
493
498
  end
494
499
 
495
500
  def extensions
496
- internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
501
+ query = <<~SQL
502
+ SELECT
503
+ pg_extension.extname,
504
+ n.nspname AS schema
505
+ FROM pg_extension
506
+ JOIN pg_namespace n ON pg_extension.extnamespace = n.oid
507
+ SQL
508
+
509
+ internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.map do |row|
510
+ name, schema = row[0], row[1]
511
+ schema = nil if schema == current_schema
512
+ [schema, name].compact.join(".")
513
+ end
497
514
  end
498
515
 
499
516
  # Returns a list of defined enum types, and their values.
@@ -558,16 +575,18 @@ module ActiveRecord
558
575
  end
559
576
 
560
577
  # Rename an existing enum type to something else.
561
- def rename_enum(name, options = {})
578
+ def rename_enum(name, **options)
562
579
  to = options.fetch(:to) { raise ArgumentError, ":to is required" }
563
580
 
564
581
  exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
565
582
  end
566
583
 
567
584
  # Add enum value to an existing enum type.
568
- def add_enum_value(type_name, value, options = {})
585
+ def add_enum_value(type_name, value, **options)
569
586
  before, after = options.values_at(:before, :after)
570
- sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
587
+ sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE"
588
+ sql << " IF NOT EXISTS" if options[:if_not_exists]
589
+ sql << " '#{value}'"
571
590
 
572
591
  if before && after
573
592
  raise ArgumentError, "Cannot have both :before and :after at the same time"
@@ -581,7 +600,7 @@ module ActiveRecord
581
600
  end
582
601
 
583
602
  # Rename enum value on an existing enum type.
584
- def rename_enum_value(type_name, options = {})
603
+ def rename_enum_value(type_name, **options)
585
604
  unless database_version >= 10_00_00 # >= 10.0
586
605
  raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
587
606
  end
@@ -841,9 +860,8 @@ module ActiveRecord
841
860
  def load_additional_types(oids = nil)
842
861
  initializer = OID::TypeMapInitializer.new(type_map)
843
862
  load_types_queries(initializer, oids) do |query|
844
- execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
845
- initializer.run(records)
846
- end
863
+ records = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
864
+ initializer.run(records)
847
865
  end
848
866
  end
849
867
 
@@ -864,73 +882,6 @@ module ActiveRecord
864
882
 
865
883
  FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
866
884
 
867
- def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
868
- sql = transform_query(sql)
869
- check_if_write_query(sql)
870
-
871
- if !prepare || without_prepared_statement?(binds)
872
- result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
873
- else
874
- result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
875
- end
876
- begin
877
- ret = yield result
878
- ensure
879
- result.clear
880
- end
881
- ret
882
- end
883
-
884
- def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
885
- mark_transaction_written_if_write(sql)
886
-
887
- # make sure we carry over any changes to ActiveRecord.default_timezone that have been
888
- # made since we established the connection
889
- update_typemap_for_default_timezone
890
-
891
- type_casted_binds = type_casted_binds(binds)
892
- log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
893
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
894
- result = conn.exec_params(sql, type_casted_binds)
895
- verified!
896
- notification_payload[:row_count] = result.count
897
- result
898
- end
899
- end
900
- end
901
-
902
- def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
903
- mark_transaction_written_if_write(sql)
904
-
905
- update_typemap_for_default_timezone
906
-
907
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
908
- stmt_key = prepare_statement(sql, binds, conn)
909
- type_casted_binds = type_casted_binds(binds)
910
-
911
- log(sql, name, binds, type_casted_binds, stmt_key, async: async) do |notification_payload|
912
- result = conn.exec_prepared(stmt_key, type_casted_binds)
913
- verified!
914
- notification_payload[:row_count] = result.count
915
- result
916
- end
917
- end
918
- rescue ActiveRecord::StatementInvalid => e
919
- raise unless is_cached_plan_failure?(e)
920
-
921
- # Nothing we can do if we are in a transaction because all commands
922
- # will raise InFailedSQLTransaction
923
- if in_transaction?
924
- raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message, connection_pool: @pool)
925
- else
926
- @lock.synchronize do
927
- # outside of transactions we can simply flush this query and retry
928
- @statements.delete sql_key(sql)
929
- end
930
- retry
931
- end
932
- end
933
-
934
885
  # Annoyingly, the code for prepared statements whose return value may
935
886
  # have changed is FEATURE_NOT_SUPPORTED.
936
887
  #
@@ -940,8 +891,7 @@ module ActiveRecord
940
891
  #
941
892
  # Check here for more details:
942
893
  # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
943
- def is_cached_plan_failure?(e)
944
- pgerror = e.cause
894
+ def is_cached_plan_failure?(pgerror)
945
895
  pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
946
896
  pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
947
897
  rescue
@@ -1020,16 +970,16 @@ module ActiveRecord
1020
970
  variables = @config.fetch(:variables, {}).stringify_keys
1021
971
 
1022
972
  # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
1023
- internal_execute("SET intervalstyle = iso_8601")
973
+ internal_execute("SET intervalstyle = iso_8601", "SCHEMA")
1024
974
 
1025
975
  # SET statements from :variables config hash
1026
976
  # https://www.postgresql.org/docs/current/static/sql-set.html
1027
977
  variables.map do |k, v|
1028
978
  if v == ":default" || v == :default
1029
979
  # Sets the value to the global or compile default
1030
- internal_execute("SET SESSION #{k} TO DEFAULT")
980
+ internal_execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
1031
981
  elsif !v.nil?
1032
- internal_execute("SET SESSION #{k} TO #{quote(v)}")
982
+ internal_execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
1033
983
  end
1034
984
  end
1035
985
 
@@ -1050,9 +1000,9 @@ module ActiveRecord
1050
1000
  # If using Active Record's time zone support configure the connection
1051
1001
  # to return TIMESTAMP WITH ZONE types in UTC.
1052
1002
  if default_timezone == :utc
1053
- internal_execute("SET SESSION timezone TO 'UTC'")
1003
+ raw_execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
1054
1004
  else
1055
- internal_execute("SET SESSION timezone TO DEFAULT")
1005
+ raw_execute("SET SESSION timezone TO DEFAULT", "SCHEMA")
1056
1006
  end
1057
1007
  end
1058
1008
 
@@ -1119,9 +1069,8 @@ module ActiveRecord
1119
1069
  AND castsource = #{quote column.sql_type}::regtype
1120
1070
  )
1121
1071
  SQL
1122
- execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1123
- result.getvalue(0, 0)
1124
- end
1072
+ result = internal_execute(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
1073
+ result.getvalue(0, 0)
1125
1074
  end
1126
1075
  end
1127
1076
  end
@@ -1177,9 +1126,8 @@ module ActiveRecord
1177
1126
  FROM pg_type as t
1178
1127
  WHERE t.typname IN (%s)
1179
1128
  SQL
1180
- coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1181
- result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
1182
- end
1129
+ result = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
1130
+ coders = result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
1183
1131
 
1184
1132
  map = PG::TypeMapByOid.new
1185
1133
  coders.each { |coder| map.add_coder(coder) }
@@ -434,9 +434,7 @@ module ActiveRecord
434
434
  end
435
435
 
436
436
  def ignored_table?(table_name)
437
- ActiveRecord.schema_cache_ignored_tables.any? do |ignored|
438
- ignored === table_name
439
- end
437
+ ActiveRecord.schema_cache_ignored_table?(table_name)
440
438
  end
441
439
 
442
440
  def derive_columns_hash_and_deduplicate_values