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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +239 -878
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +1 -0
- data/lib/active_record/associations/association.rb +34 -10
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +34 -4
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +2 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +0 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +33 -6
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -26
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -39
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +59 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +45 -95
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/connection_handling.rb +22 -0
- data/lib/active_record/core.rb +18 -14
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/database_configurations/hash_config.rb +8 -0
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
- data/lib/active_record/encryption/encryptor.rb +15 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +9 -22
- data/lib/active_record/errors.rb +13 -5
- data/lib/active_record/fixtures.rb +0 -2
- data/lib/active_record/future_result.rb +14 -10
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -11
- data/lib/active_record/migration/command_recorder.rb +27 -10
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +35 -38
- data/lib/active_record/model_schema.rb +3 -4
- data/lib/active_record/nested_attributes.rb +4 -6
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_logs.rb +102 -50
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +2 -26
- data/lib/active_record/railties/databases.rake +2 -17
- data/lib/active_record/reflection.rb +18 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +132 -72
- data/lib/active_record/relation/calculations.rb +40 -39
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +18 -18
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +115 -65
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation.rb +79 -61
- data/lib/active_record/result.rb +66 -4
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/statement_cache.rb +12 -12
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +1 -3
- data/lib/active_record/tasks/database_tasks.rb +48 -47
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +15 -45
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/table.rb +3 -7
- metadata +11 -12
- 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, :
|
|
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] =
|
|
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(
|
|
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
|
-
|
|
156
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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,
|
|
562
|
-
|
|
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 #{
|
|
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
|
|
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
|
|
596
|
+
sql << " BEFORE #{quote(before)}"
|
|
576
597
|
elsif after
|
|
577
|
-
sql << " 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
|
|
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
|
-
|
|
845
|
-
|
|
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?(
|
|
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
|
-
|
|
1005
|
+
raw_execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
|
|
1054
1006
|
else
|
|
1055
|
-
|
|
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
|
-
|
|
1123
|
-
|
|
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
|
-
|
|
1181
|
-
|
|
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.
|
|
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
|