activerecord 7.2.2 → 8.0.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.

Potentially problematic release.


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

Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +239 -878
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +1 -0
  5. data/lib/active_record/associations/association.rb +34 -10
  6. data/lib/active_record/associations/builder/association.rb +7 -6
  7. data/lib/active_record/associations/collection_association.rb +10 -8
  8. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  9. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  10. data/lib/active_record/associations/preloader/association.rb +2 -2
  11. data/lib/active_record/associations/singular_association.rb +8 -3
  12. data/lib/active_record/associations.rb +34 -4
  13. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  14. data/lib/active_record/attribute_methods/primary_key.rb +2 -7
  15. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  16. data/lib/active_record/attribute_methods.rb +1 -1
  17. data/lib/active_record/autosave_association.rb +69 -27
  18. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
  19. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  20. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +0 -9
  22. data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
  23. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  24. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  25. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  26. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
  27. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +33 -6
  28. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  29. data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -26
  30. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -39
  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 +43 -45
  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/array.rb +1 -1
  38. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  39. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  40. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  41. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
  42. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
  43. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +59 -16
  44. data/lib/active_record/connection_adapters/postgresql_adapter.rb +45 -95
  45. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  46. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
  47. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  48. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
  49. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
  50. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
  51. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  52. data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
  53. data/lib/active_record/connection_adapters.rb +0 -56
  54. data/lib/active_record/connection_handling.rb +22 -0
  55. data/lib/active_record/core.rb +18 -14
  56. data/lib/active_record/database_configurations/database_config.rb +4 -0
  57. data/lib/active_record/database_configurations/hash_config.rb +8 -0
  58. data/lib/active_record/encryption/config.rb +3 -1
  59. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  60. data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
  61. data/lib/active_record/encryption/encryptor.rb +15 -8
  62. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  63. data/lib/active_record/encryption/scheme.rb +8 -1
  64. data/lib/active_record/enum.rb +9 -22
  65. data/lib/active_record/errors.rb +13 -5
  66. data/lib/active_record/fixtures.rb +0 -2
  67. data/lib/active_record/future_result.rb +14 -10
  68. data/lib/active_record/gem_version.rb +3 -3
  69. data/lib/active_record/insert_all.rb +1 -1
  70. data/lib/active_record/locking/optimistic.rb +1 -1
  71. data/lib/active_record/log_subscriber.rb +5 -11
  72. data/lib/active_record/migration/command_recorder.rb +27 -10
  73. data/lib/active_record/migration/compatibility.rb +5 -2
  74. data/lib/active_record/migration.rb +35 -38
  75. data/lib/active_record/model_schema.rb +3 -4
  76. data/lib/active_record/nested_attributes.rb +4 -6
  77. data/lib/active_record/persistence.rb +128 -130
  78. data/lib/active_record/query_logs.rb +102 -50
  79. data/lib/active_record/query_logs_formatter.rb +17 -28
  80. data/lib/active_record/querying.rb +8 -8
  81. data/lib/active_record/railtie.rb +2 -26
  82. data/lib/active_record/railties/databases.rake +2 -17
  83. data/lib/active_record/reflection.rb +18 -21
  84. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  85. data/lib/active_record/relation/batches.rb +132 -72
  86. data/lib/active_record/relation/calculations.rb +40 -39
  87. data/lib/active_record/relation/delegation.rb +25 -14
  88. data/lib/active_record/relation/finder_methods.rb +18 -18
  89. data/lib/active_record/relation/merger.rb +8 -8
  90. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  91. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  92. data/lib/active_record/relation/predicate_builder.rb +13 -0
  93. data/lib/active_record/relation/query_methods.rb +115 -65
  94. data/lib/active_record/relation/spawn_methods.rb +1 -1
  95. data/lib/active_record/relation.rb +79 -61
  96. data/lib/active_record/result.rb +66 -4
  97. data/lib/active_record/sanitization.rb +7 -6
  98. data/lib/active_record/schema_dumper.rb +5 -0
  99. data/lib/active_record/schema_migration.rb +2 -1
  100. data/lib/active_record/scoping/named.rb +5 -2
  101. data/lib/active_record/statement_cache.rb +12 -12
  102. data/lib/active_record/store.rb +7 -3
  103. data/lib/active_record/table_metadata.rb +1 -3
  104. data/lib/active_record/tasks/database_tasks.rb +48 -47
  105. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  106. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  107. data/lib/active_record/test_fixtures.rb +12 -0
  108. data/lib/active_record/token_for.rb +1 -1
  109. data/lib/active_record/validations/uniqueness.rb +8 -8
  110. data/lib/active_record.rb +15 -45
  111. data/lib/arel/collectors/bind.rb +1 -1
  112. data/lib/arel/table.rb +3 -7
  113. metadata +11 -12
  114. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -11,14 +11,11 @@ module ActiveRecord
11
11
  sql = super
12
12
  sql << o.constraint_validations.map { |fk| visit_ValidateConstraint fk }.join(" ")
13
13
  sql << o.exclusion_constraint_adds.map { |con| visit_AddExclusionConstraint con }.join(" ")
14
- sql << o.exclusion_constraint_drops.map { |con| visit_DropExclusionConstraint con }.join(" ")
15
14
  sql << o.unique_constraint_adds.map { |con| visit_AddUniqueConstraint con }.join(" ")
16
- sql << o.unique_constraint_drops.map { |con| visit_DropUniqueConstraint con }.join(" ")
17
15
  end
18
16
 
19
17
  def visit_AddForeignKey(o)
20
18
  super.dup.tap do |sql|
21
- sql << " DEFERRABLE INITIALLY #{o.options[:deferrable].to_s.upcase}" if o.deferrable
22
19
  sql << " NOT VALID" unless o.validate?
23
20
  end
24
21
  end
@@ -55,6 +52,7 @@ module ActiveRecord
55
52
  sql = ["CONSTRAINT"]
56
53
  sql << quote_column_name(o.name)
57
54
  sql << "UNIQUE"
55
+ sql << "NULLS NOT DISTINCT" if supports_nulls_not_distinct? && o.nulls_not_distinct
58
56
 
59
57
  if o.using_index
60
58
  sql << "USING INDEX #{quote_column_name(o.using_index)}"
@@ -73,18 +71,10 @@ module ActiveRecord
73
71
  "ADD #{accept(o)}"
74
72
  end
75
73
 
76
- def visit_DropExclusionConstraint(name)
77
- "DROP CONSTRAINT #{quote_column_name(name)}"
78
- end
79
-
80
74
  def visit_AddUniqueConstraint(o)
81
75
  "ADD #{accept(o)}"
82
76
  end
83
77
 
84
- def visit_DropUniqueConstraint(name)
85
- "DROP CONSTRAINT #{quote_column_name(name)}"
86
- end
87
-
88
78
  def visit_ChangeColumnDefinition(o)
89
79
  column = o.column
90
80
  column.sql_type = type_to_sql(column.type, **column.options)
@@ -224,6 +224,10 @@ module ActiveRecord
224
224
  options[:using_index]
225
225
  end
226
226
 
227
+ def nulls_not_distinct
228
+ options[:nulls_not_distinct]
229
+ end
230
+
227
231
  def export_name_on_schema_dump?
228
232
  !ActiveRecord::SchemaDumper.unique_ignore_pattern.match?(name) if name
229
233
  end
@@ -317,7 +321,7 @@ module ActiveRecord
317
321
 
318
322
  # Adds a unique constraint.
319
323
  #
320
- # t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred)
324
+ # t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred, nulls_not_distinct: true)
321
325
  #
322
326
  # See {connection.add_unique_constraint}[rdoc-ref:SchemaStatements#add_unique_constraint]
323
327
  def unique_constraint(*args)
@@ -356,15 +360,13 @@ module ActiveRecord
356
360
 
357
361
  # = Active Record PostgreSQL Adapter Alter \Table
358
362
  class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
359
- attr_reader :constraint_validations, :exclusion_constraint_adds, :exclusion_constraint_drops, :unique_constraint_adds, :unique_constraint_drops
363
+ attr_reader :constraint_validations, :exclusion_constraint_adds, :unique_constraint_adds
360
364
 
361
365
  def initialize(td)
362
366
  super
363
367
  @constraint_validations = []
364
368
  @exclusion_constraint_adds = []
365
- @exclusion_constraint_drops = []
366
369
  @unique_constraint_adds = []
367
- @unique_constraint_drops = []
368
370
  end
369
371
 
370
372
  def validate_constraint(name)
@@ -375,17 +377,9 @@ module ActiveRecord
375
377
  @exclusion_constraint_adds << @td.new_exclusion_constraint_definition(expression, options)
376
378
  end
377
379
 
378
- def drop_exclusion_constraint(constraint_name)
379
- @exclusion_constraint_drops << constraint_name
380
- end
381
-
382
380
  def add_unique_constraint(column_name, options)
383
381
  @unique_constraint_adds << @td.new_unique_constraint_definition(column_name, options)
384
382
  end
385
-
386
- def drop_unique_constraint(unique_constraint_name)
387
- @unique_constraint_drops << unique_constraint_name
388
- end
389
383
  end
390
384
  end
391
385
  end
@@ -68,6 +68,7 @@ module ActiveRecord
68
68
  "t.unique_constraint #{unique_constraint.column.inspect}"
69
69
  ]
70
70
 
71
+ parts << "nulls_not_distinct: #{unique_constraint.nulls_not_distinct.inspect}" if unique_constraint.nulls_not_distinct
71
72
  parts << "deferrable: #{unique_constraint.deferrable.inspect}" if unique_constraint.deferrable
72
73
 
73
74
  if unique_constraint.export_name_on_schema_dump?
@@ -91,7 +92,7 @@ module ActiveRecord
91
92
  spec = { type: schema_type(column).inspect }.merge!(spec)
92
93
  end
93
94
 
94
- spec[:enum_type] = "\"#{column.sql_type}\"" if column.enum?
95
+ spec[:enum_type] = column.sql_type.inspect if column.enum?
95
96
 
96
97
  spec
97
98
  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.
@@ -654,7 +698,7 @@ module ActiveRecord
654
698
  scope = quoted_scope(table_name)
655
699
 
656
700
  unique_info = internal_exec_query(<<~SQL, "SCHEMA", allow_retry: true, materialize_transactions: false)
657
- SELECT c.conname, c.conrelid, c.conkey, c.condeferrable, c.condeferred
701
+ SELECT c.conname, c.conrelid, c.conkey, c.condeferrable, c.condeferred, pg_get_constraintdef(c.oid) AS constraintdef
658
702
  FROM pg_constraint c
659
703
  JOIN pg_class t ON c.conrelid = t.oid
660
704
  JOIN pg_namespace n ON n.oid = c.connamespace
@@ -667,10 +711,12 @@ module ActiveRecord
667
711
  conkey = row["conkey"].delete("{}").split(",").map(&:to_i)
668
712
  columns = column_names_from_column_numbers(row["conrelid"], conkey)
669
713
 
714
+ nulls_not_distinct = row["constraintdef"].start_with?("UNIQUE NULLS NOT DISTINCT")
670
715
  deferrable = extract_constraint_deferrable(row["condeferrable"], row["condeferred"])
671
716
 
672
717
  options = {
673
718
  name: row["conname"],
719
+ nulls_not_distinct: nulls_not_distinct,
674
720
  deferrable: deferrable
675
721
  }
676
722
 
@@ -722,15 +768,12 @@ module ActiveRecord
722
768
  def remove_exclusion_constraint(table_name, expression = nil, **options)
723
769
  excl_name_to_delete = exclusion_constraint_for!(table_name, expression: expression, **options).name
724
770
 
725
- at = create_alter_table(table_name)
726
- at.drop_exclusion_constraint(excl_name_to_delete)
727
-
728
- execute schema_creation.accept(at)
771
+ remove_constraint(table_name, excl_name_to_delete)
729
772
  end
730
773
 
731
774
  # Adds a new unique constraint to the table.
732
775
  #
733
- # add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_position"
776
+ # add_unique_constraint :sections, [:position], deferrable: :deferred, name: "unique_position", nulls_not_distinct: true
734
777
  #
735
778
  # generates:
736
779
  #
@@ -747,6 +790,9 @@ module ActiveRecord
747
790
  # Specify whether or not the unique constraint should be deferrable. Valid values are +false+ or +:immediate+ or +:deferred+ to specify the default behavior. Defaults to +false+.
748
791
  # [<tt>:using_index</tt>]
749
792
  # To specify an existing unique index name. Defaults to +nil+.
793
+ # [<tt>:nulls_not_distinct</tt>]
794
+ # Create a unique constraint where NULLs are treated equally.
795
+ # Note: only supported by PostgreSQL version 15.0.0 and greater.
750
796
  def add_unique_constraint(table_name, column_name = nil, **options)
751
797
  options = unique_constraint_options(table_name, column_name, options)
752
798
  at = create_alter_table(table_name)
@@ -777,10 +823,7 @@ module ActiveRecord
777
823
  def remove_unique_constraint(table_name, column_name = nil, **options)
778
824
  unique_name_to_delete = unique_constraint_for!(table_name, column: column_name, **options).name
779
825
 
780
- at = create_alter_table(table_name)
781
- at.drop_unique_constraint(unique_name_to_delete)
782
-
783
- execute schema_creation.accept(at)
826
+ remove_constraint(table_name, unique_name_to_delete)
784
827
  end
785
828
 
786
829
  # Maps logical Rails types to PostgreSQL-specific data types.
@@ -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,30 +575,34 @@ module ActiveRecord
558
575
  end
559
576
 
560
577
  # Rename an existing enum type to something else.
561
- def rename_enum(name, options = {})
562
- to = options.fetch(:to) { raise ArgumentError, ":to is required" }
578
+ def rename_enum(name, new_name = nil, **options)
579
+ new_name ||= options.fetch(:to) do
580
+ raise ArgumentError, "rename_enum requires two from/to name positional arguments."
581
+ end
563
582
 
564
- exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
583
+ exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}").tap { reload_type_map }
565
584
  end
566
585
 
567
586
  # Add enum value to an existing enum type.
568
- def add_enum_value(type_name, value, options = {})
587
+ def add_enum_value(type_name, value, **options)
569
588
  before, after = options.values_at(:before, :after)
570
- sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
589
+ sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE"
590
+ sql << " IF NOT EXISTS" if options[:if_not_exists]
591
+ sql << " #{quote(value)}"
571
592
 
572
593
  if before && after
573
594
  raise ArgumentError, "Cannot have both :before and :after at the same time"
574
595
  elsif before
575
- sql << " BEFORE '#{before}'"
596
+ sql << " BEFORE #{quote(before)}"
576
597
  elsif after
577
- sql << " AFTER '#{after}'"
598
+ sql << " AFTER #{quote(after)}"
578
599
  end
579
600
 
580
601
  execute(sql).tap { reload_type_map }
581
602
  end
582
603
 
583
604
  # Rename enum value on an existing enum type.
584
- def rename_enum_value(type_name, options = {})
605
+ def rename_enum_value(type_name, **options)
585
606
  unless database_version >= 10_00_00 # >= 10.0
586
607
  raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
587
608
  end
@@ -589,7 +610,7 @@ module ActiveRecord
589
610
  from = options.fetch(:from) { raise ArgumentError, ":from is required" }
590
611
  to = options.fetch(:to) { raise ArgumentError, ":to is required" }
591
612
 
592
- execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
613
+ execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE #{quote(from)} TO #{quote(to)}").tap {
593
614
  reload_type_map
594
615
  }
595
616
  end
@@ -841,9 +862,8 @@ module ActiveRecord
841
862
  def load_additional_types(oids = nil)
842
863
  initializer = OID::TypeMapInitializer.new(type_map)
843
864
  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
865
+ records = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
866
+ initializer.run(records)
847
867
  end
848
868
  end
849
869
 
@@ -864,73 +884,6 @@ module ActiveRecord
864
884
 
865
885
  FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
866
886
 
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
887
  # Annoyingly, the code for prepared statements whose return value may
935
888
  # have changed is FEATURE_NOT_SUPPORTED.
936
889
  #
@@ -940,8 +893,7 @@ module ActiveRecord
940
893
  #
941
894
  # Check here for more details:
942
895
  # 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
896
+ def is_cached_plan_failure?(pgerror)
945
897
  pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
946
898
  pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
947
899
  rescue
@@ -1020,16 +972,16 @@ module ActiveRecord
1020
972
  variables = @config.fetch(:variables, {}).stringify_keys
1021
973
 
1022
974
  # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
1023
- internal_execute("SET intervalstyle = iso_8601")
975
+ internal_execute("SET intervalstyle = iso_8601", "SCHEMA")
1024
976
 
1025
977
  # SET statements from :variables config hash
1026
978
  # https://www.postgresql.org/docs/current/static/sql-set.html
1027
979
  variables.map do |k, v|
1028
980
  if v == ":default" || v == :default
1029
981
  # Sets the value to the global or compile default
1030
- internal_execute("SET SESSION #{k} TO DEFAULT")
982
+ internal_execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
1031
983
  elsif !v.nil?
1032
- internal_execute("SET SESSION #{k} TO #{quote(v)}")
984
+ internal_execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
1033
985
  end
1034
986
  end
1035
987
 
@@ -1050,9 +1002,9 @@ module ActiveRecord
1050
1002
  # If using Active Record's time zone support configure the connection
1051
1003
  # to return TIMESTAMP WITH ZONE types in UTC.
1052
1004
  if default_timezone == :utc
1053
- internal_execute("SET SESSION timezone TO 'UTC'")
1005
+ raw_execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
1054
1006
  else
1055
- internal_execute("SET SESSION timezone TO DEFAULT")
1007
+ raw_execute("SET SESSION timezone TO DEFAULT", "SCHEMA")
1056
1008
  end
1057
1009
  end
1058
1010
 
@@ -1119,9 +1071,8 @@ module ActiveRecord
1119
1071
  AND castsource = #{quote column.sql_type}::regtype
1120
1072
  )
1121
1073
  SQL
1122
- execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1123
- result.getvalue(0, 0)
1124
- end
1074
+ result = internal_execute(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
1075
+ result.getvalue(0, 0)
1125
1076
  end
1126
1077
  end
1127
1078
  end
@@ -1177,9 +1128,8 @@ module ActiveRecord
1177
1128
  FROM pg_type as t
1178
1129
  WHERE t.typname IN (%s)
1179
1130
  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
1131
+ result = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
1132
+ coders = result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
1183
1133
 
1184
1134
  map = PG::TypeMapByOid.new
1185
1135
  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