activerecord 7.2.3.1 → 8.0.5.1

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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +475 -946
  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 +1 -1
  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/join_dependency.rb +2 -2
  11. data/lib/active_record/associations/preloader/association.rb +2 -2
  12. data/lib/active_record/associations/preloader/batch.rb +7 -1
  13. data/lib/active_record/associations/singular_association.rb +8 -3
  14. data/lib/active_record/associations.rb +34 -4
  15. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  16. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  17. data/lib/active_record/attribute_methods/query.rb +34 -0
  18. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  19. data/lib/active_record/autosave_association.rb +69 -27
  20. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
  21. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  23. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +8 -17
  24. data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
  25. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  26. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  27. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  28. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
  29. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -10
  30. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  31. data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -74
  32. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -40
  33. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  34. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  35. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
  36. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
  37. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
  38. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  39. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
  40. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
  41. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  42. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  43. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  44. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  45. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
  46. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
  47. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +59 -16
  48. data/lib/active_record/connection_adapters/postgresql_adapter.rb +53 -96
  49. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  50. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  51. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
  52. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  53. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  54. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +9 -1
  55. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
  56. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  57. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  58. data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
  59. data/lib/active_record/connection_adapters.rb +0 -56
  60. data/lib/active_record/connection_handling.rb +23 -1
  61. data/lib/active_record/core.rb +29 -14
  62. data/lib/active_record/database_configurations/database_config.rb +4 -0
  63. data/lib/active_record/database_configurations/hash_config.rb +16 -2
  64. data/lib/active_record/encryption/config.rb +3 -1
  65. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  66. data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
  67. data/lib/active_record/encryption/encryptor.rb +16 -8
  68. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  69. data/lib/active_record/encryption/scheme.rb +8 -1
  70. data/lib/active_record/enum.rb +9 -22
  71. data/lib/active_record/errors.rb +13 -5
  72. data/lib/active_record/fixtures.rb +0 -2
  73. data/lib/active_record/future_result.rb +15 -9
  74. data/lib/active_record/gem_version.rb +3 -3
  75. data/lib/active_record/insert_all.rb +3 -3
  76. data/lib/active_record/locking/optimistic.rb +1 -1
  77. data/lib/active_record/log_subscriber.rb +5 -11
  78. data/lib/active_record/migration/command_recorder.rb +32 -12
  79. data/lib/active_record/migration/compatibility.rb +5 -2
  80. data/lib/active_record/migration.rb +38 -42
  81. data/lib/active_record/model_schema.rb +3 -4
  82. data/lib/active_record/nested_attributes.rb +4 -6
  83. data/lib/active_record/persistence.rb +128 -130
  84. data/lib/active_record/query_logs.rb +102 -50
  85. data/lib/active_record/query_logs_formatter.rb +17 -28
  86. data/lib/active_record/querying.rb +8 -8
  87. data/lib/active_record/railtie.rb +2 -26
  88. data/lib/active_record/railties/databases.rake +11 -35
  89. data/lib/active_record/reflection.rb +18 -21
  90. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  91. data/lib/active_record/relation/batches.rb +132 -72
  92. data/lib/active_record/relation/calculations.rb +40 -39
  93. data/lib/active_record/relation/delegation.rb +25 -14
  94. data/lib/active_record/relation/finder_methods.rb +18 -18
  95. data/lib/active_record/relation/merger.rb +8 -8
  96. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  97. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  98. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  99. data/lib/active_record/relation/predicate_builder.rb +13 -0
  100. data/lib/active_record/relation/query_methods.rb +105 -61
  101. data/lib/active_record/relation/spawn_methods.rb +7 -7
  102. data/lib/active_record/relation/where_clause.rb +1 -1
  103. data/lib/active_record/relation.rb +80 -62
  104. data/lib/active_record/result.rb +66 -4
  105. data/lib/active_record/sanitization.rb +7 -6
  106. data/lib/active_record/schema_dumper.rb +5 -0
  107. data/lib/active_record/schema_migration.rb +2 -1
  108. data/lib/active_record/scoping/named.rb +5 -2
  109. data/lib/active_record/statement_cache.rb +14 -14
  110. data/lib/active_record/store.rb +7 -3
  111. data/lib/active_record/table_metadata.rb +1 -3
  112. data/lib/active_record/tasks/database_tasks.rb +69 -60
  113. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  114. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -1
  115. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  116. data/lib/active_record/test_databases.rb +1 -1
  117. data/lib/active_record/test_fixtures.rb +12 -0
  118. data/lib/active_record/token_for.rb +1 -1
  119. data/lib/active_record/transactions.rb +5 -6
  120. data/lib/active_record/type/serialized.rb +5 -0
  121. data/lib/active_record/validations/uniqueness.rb +8 -8
  122. data/lib/active_record.rb +21 -48
  123. data/lib/arel/collectors/bind.rb +2 -2
  124. data/lib/arel/collectors/sql_string.rb +1 -1
  125. data/lib/arel/collectors/substitute_binds.rb +2 -2
  126. data/lib/arel/nodes/binary.rb +1 -1
  127. data/lib/arel/nodes/node.rb +1 -1
  128. data/lib/arel/nodes/sql_literal.rb +1 -1
  129. data/lib/arel/predications.rb +1 -3
  130. data/lib/arel/table.rb +3 -7
  131. metadata +10 -11
  132. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -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
@@ -319,7 +323,7 @@ module ActiveRecord
319
323
 
320
324
  # Adds a unique constraint.
321
325
  #
322
- # t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred)
326
+ # t.unique_constraint(:position, name: 'unique_position', deferrable: :deferred, nulls_not_distinct: true)
323
327
  #
324
328
  # See {connection.add_unique_constraint}[rdoc-ref:SchemaStatements#add_unique_constraint]
325
329
  def unique_constraint(...)
@@ -358,15 +362,13 @@ module ActiveRecord
358
362
 
359
363
  # = Active Record PostgreSQL Adapter Alter \Table
360
364
  class AlterTable < ActiveRecord::ConnectionAdapters::AlterTable
361
- attr_reader :constraint_validations, :exclusion_constraint_adds, :exclusion_constraint_drops, :unique_constraint_adds, :unique_constraint_drops
365
+ attr_reader :constraint_validations, :exclusion_constraint_adds, :unique_constraint_adds
362
366
 
363
367
  def initialize(td)
364
368
  super
365
369
  @constraint_validations = []
366
370
  @exclusion_constraint_adds = []
367
- @exclusion_constraint_drops = []
368
371
  @unique_constraint_adds = []
369
- @unique_constraint_drops = []
370
372
  end
371
373
 
372
374
  def validate_constraint(name)
@@ -377,17 +379,9 @@ module ActiveRecord
377
379
  @exclusion_constraint_adds << @td.new_exclusion_constraint_definition(expression, options)
378
380
  end
379
381
 
380
- def drop_exclusion_constraint(constraint_name)
381
- @exclusion_constraint_drops << constraint_name
382
- end
383
-
384
382
  def add_unique_constraint(column_name, options)
385
383
  @unique_constraint_adds << @td.new_unique_constraint_definition(column_name, options)
386
384
  end
387
-
388
- def drop_unique_constraint(unique_constraint_name)
389
- @unique_constraint_drops << unique_constraint_name
390
- end
391
385
  end
392
386
  end
393
387
  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
@@ -377,6 +381,11 @@ module ActiveRecord
377
381
  end
378
382
  end
379
383
 
384
+ def clear_cache!(new_connection: false)
385
+ super
386
+ @schema_search_path = nil if new_connection
387
+ end
388
+
380
389
  # Disconnects from the database if already connected. Otherwise, this
381
390
  # method does nothing.
382
391
  def disconnect!
@@ -406,7 +415,7 @@ module ActiveRecord
406
415
  end
407
416
 
408
417
  def set_standard_conforming_strings
409
- internal_execute("SET standard_conforming_strings = on")
418
+ internal_execute("SET standard_conforming_strings = on", "SCHEMA")
410
419
  end
411
420
 
412
421
  def supports_ddl_transactions?
@@ -480,6 +489,7 @@ module ActiveRecord
480
489
  # Set to +:cascade+ to drop dependent objects as well.
481
490
  # Defaults to false.
482
491
  def disable_extension(name, force: false)
492
+ _schema, name = name.to_s.split(".").values_at(-2, -1)
483
493
  internal_exec_query("DROP EXTENSION IF EXISTS \"#{name}\"#{' CASCADE' if force == :cascade}").tap {
484
494
  reload_type_map
485
495
  }
@@ -494,7 +504,19 @@ module ActiveRecord
494
504
  end
495
505
 
496
506
  def extensions
497
- internal_exec_query("SELECT extname FROM pg_extension", "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values
507
+ query = <<~SQL
508
+ SELECT
509
+ pg_extension.extname,
510
+ n.nspname AS schema
511
+ FROM pg_extension
512
+ JOIN pg_namespace n ON pg_extension.extnamespace = n.oid
513
+ SQL
514
+
515
+ internal_exec_query(query, "SCHEMA", allow_retry: true, materialize_transactions: false).cast_values.map do |row|
516
+ name, schema = row[0], row[1]
517
+ schema = nil if schema == current_schema
518
+ [schema, name].compact.join(".")
519
+ end
498
520
  end
499
521
 
500
522
  # Returns a list of defined enum types, and their values.
@@ -559,30 +581,34 @@ module ActiveRecord
559
581
  end
560
582
 
561
583
  # Rename an existing enum type to something else.
562
- def rename_enum(name, options = {})
563
- to = options.fetch(:to) { raise ArgumentError, ":to is required" }
584
+ def rename_enum(name, new_name = nil, **options)
585
+ new_name ||= options.fetch(:to) do
586
+ raise ArgumentError, "rename_enum requires two from/to name positional arguments."
587
+ end
564
588
 
565
- exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
589
+ exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}").tap { reload_type_map }
566
590
  end
567
591
 
568
592
  # Add enum value to an existing enum type.
569
- def add_enum_value(type_name, value, options = {})
593
+ def add_enum_value(type_name, value, **options)
570
594
  before, after = options.values_at(:before, :after)
571
- sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE '#{value}'"
595
+ sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE"
596
+ sql << " IF NOT EXISTS" if options[:if_not_exists]
597
+ sql << " #{quote(value)}"
572
598
 
573
599
  if before && after
574
600
  raise ArgumentError, "Cannot have both :before and :after at the same time"
575
601
  elsif before
576
- sql << " BEFORE '#{before}'"
602
+ sql << " BEFORE #{quote(before)}"
577
603
  elsif after
578
- sql << " AFTER '#{after}'"
604
+ sql << " AFTER #{quote(after)}"
579
605
  end
580
606
 
581
607
  execute(sql).tap { reload_type_map }
582
608
  end
583
609
 
584
610
  # Rename enum value on an existing enum type.
585
- def rename_enum_value(type_name, options = {})
611
+ def rename_enum_value(type_name, **options)
586
612
  unless database_version >= 10_00_00 # >= 10.0
587
613
  raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
588
614
  end
@@ -590,12 +616,12 @@ module ActiveRecord
590
616
  from = options.fetch(:from) { raise ArgumentError, ":from is required" }
591
617
  to = options.fetch(:to) { raise ArgumentError, ":to is required" }
592
618
 
593
- execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE '#{from}' TO '#{to}'").tap {
619
+ execute("ALTER TYPE #{quote_table_name(type_name)} RENAME VALUE #{quote(from)} TO #{quote(to)}").tap {
594
620
  reload_type_map
595
621
  }
596
622
  end
597
623
 
598
- # Returns the configured supported identifier length supported by PostgreSQL
624
+ # Returns the configured maximum supported identifier length supported by PostgreSQL
599
625
  def max_identifier_length
600
626
  @max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
601
627
  end
@@ -846,9 +872,8 @@ module ActiveRecord
846
872
  def load_additional_types(oids = nil)
847
873
  initializer = OID::TypeMapInitializer.new(type_map)
848
874
  load_types_queries(initializer, oids) do |query|
849
- execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |records|
850
- initializer.run(records)
851
- end
875
+ records = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
876
+ initializer.run(records)
852
877
  end
853
878
  end
854
879
 
@@ -869,73 +894,6 @@ module ActiveRecord
869
894
 
870
895
  FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
871
896
 
872
- def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
873
- sql = transform_query(sql)
874
- check_if_write_query(sql)
875
-
876
- if !prepare || without_prepared_statement?(binds)
877
- result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
878
- else
879
- result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
880
- end
881
- begin
882
- ret = yield result
883
- ensure
884
- result.clear
885
- end
886
- ret
887
- end
888
-
889
- def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
890
- mark_transaction_written_if_write(sql)
891
-
892
- # make sure we carry over any changes to ActiveRecord.default_timezone that have been
893
- # made since we established the connection
894
- update_typemap_for_default_timezone
895
-
896
- type_casted_binds = type_casted_binds(binds)
897
- log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
898
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
899
- result = conn.exec_params(sql, type_casted_binds)
900
- verified!
901
- notification_payload[:row_count] = result.count
902
- result
903
- end
904
- end
905
- end
906
-
907
- def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
908
- mark_transaction_written_if_write(sql)
909
-
910
- update_typemap_for_default_timezone
911
-
912
- with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
913
- stmt_key = prepare_statement(sql, binds, conn)
914
- type_casted_binds = type_casted_binds(binds)
915
-
916
- log(sql, name, binds, type_casted_binds, stmt_key, async: async) do |notification_payload|
917
- result = conn.exec_prepared(stmt_key, type_casted_binds)
918
- verified!
919
- notification_payload[:row_count] = result.count
920
- result
921
- end
922
- end
923
- rescue ActiveRecord::StatementInvalid => e
924
- raise unless is_cached_plan_failure?(e)
925
-
926
- # Nothing we can do if we are in a transaction because all commands
927
- # will raise InFailedSQLTransaction
928
- if in_transaction?
929
- raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message, connection_pool: @pool)
930
- else
931
- @lock.synchronize do
932
- # outside of transactions we can simply flush this query and retry
933
- @statements.delete sql_key(sql)
934
- end
935
- retry
936
- end
937
- end
938
-
939
897
  # Annoyingly, the code for prepared statements whose return value may
940
898
  # have changed is FEATURE_NOT_SUPPORTED.
941
899
  #
@@ -945,8 +903,7 @@ module ActiveRecord
945
903
  #
946
904
  # Check here for more details:
947
905
  # https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
948
- def is_cached_plan_failure?(e)
949
- pgerror = e.cause
906
+ def is_cached_plan_failure?(pgerror)
950
907
  pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
951
908
  pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
952
909
  rescue
@@ -1025,22 +982,24 @@ module ActiveRecord
1025
982
  variables = @config.fetch(:variables, {}).stringify_keys
1026
983
 
1027
984
  # Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
1028
- internal_execute("SET intervalstyle = iso_8601")
985
+ internal_execute("SET intervalstyle = iso_8601", "SCHEMA")
1029
986
 
1030
987
  # SET statements from :variables config hash
1031
988
  # https://www.postgresql.org/docs/current/static/sql-set.html
1032
989
  variables.map do |k, v|
1033
990
  if v == ":default" || v == :default
1034
991
  # Sets the value to the global or compile default
1035
- internal_execute("SET SESSION #{k} TO DEFAULT")
992
+ internal_execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
1036
993
  elsif !v.nil?
1037
- internal_execute("SET SESSION #{k} TO #{quote(v)}")
994
+ internal_execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
1038
995
  end
1039
996
  end
1040
997
 
1041
998
  add_pg_encoders
1042
999
  add_pg_decoders
1043
1000
 
1001
+ schema_search_path # populate cache
1002
+
1044
1003
  reload_type_map
1045
1004
  end
1046
1005
 
@@ -1055,9 +1014,9 @@ module ActiveRecord
1055
1014
  # If using Active Record's time zone support configure the connection
1056
1015
  # to return TIMESTAMP WITH ZONE types in UTC.
1057
1016
  if default_timezone == :utc
1058
- internal_execute("SET SESSION timezone TO 'UTC'")
1017
+ raw_execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
1059
1018
  else
1060
- internal_execute("SET SESSION timezone TO DEFAULT")
1019
+ raw_execute("SET SESSION timezone TO DEFAULT", "SCHEMA")
1061
1020
  end
1062
1021
  end
1063
1022
 
@@ -1124,9 +1083,8 @@ module ActiveRecord
1124
1083
  AND castsource = #{quote column.sql_type}::regtype
1125
1084
  )
1126
1085
  SQL
1127
- execute_and_clear(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1128
- result.getvalue(0, 0)
1129
- end
1086
+ result = internal_execute(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
1087
+ result.getvalue(0, 0)
1130
1088
  end
1131
1089
  end
1132
1090
  end
@@ -1182,9 +1140,8 @@ module ActiveRecord
1182
1140
  FROM pg_type as t
1183
1141
  WHERE t.typname IN (%s)
1184
1142
  SQL
1185
- coders = execute_and_clear(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false) do |result|
1186
- result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
1187
- end
1143
+ result = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
1144
+ coders = result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
1188
1145
 
1189
1146
  map = PG::TypeMapByOid.new
1190
1147
  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
@@ -46,7 +46,9 @@ module ActiveRecord
46
46
  def ==(other)
47
47
  other.is_a?(Column) &&
48
48
  super &&
49
- auto_increment? == other.auto_increment?
49
+ auto_increment? == other.auto_increment? &&
50
+ rowid == other.rowid &&
51
+ generated_type == other.generated_type
50
52
  end
51
53
  alias :eql? :==
52
54
 
@@ -54,8 +56,12 @@ module ActiveRecord
54
56
  Column.hash ^
55
57
  super.hash ^
56
58
  auto_increment?.hash ^
57
- rowid.hash
59
+ rowid.hash ^
60
+ virtual?.hash
58
61
  end
62
+
63
+ protected
64
+ attr_reader :generated_type
59
65
  end
60
66
  end
61
67
  end