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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +475 -946
- 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 +1 -1
- 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/join_dependency.rb +2 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- 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 +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
- 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 +8 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
- 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 +37 -10
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +67 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -40
- 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 +50 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
- 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 +53 -96
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +9 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- 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 +23 -1
- data/lib/active_record/core.rb +29 -14
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/database_configurations/hash_config.rb +16 -2
- 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 +16 -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 +15 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/insert_all.rb +3 -3
- 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 +32 -12
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +38 -42
- 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 +11 -35
- 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/array_handler.rb +3 -1
- 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 +105 -61
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +1 -1
- data/lib/active_record/relation.rb +80 -62
- 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 +14 -14
- 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 +69 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transactions.rb +5 -6
- data/lib/active_record/type/serialized.rb +5 -0
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +21 -48
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/table.rb +3 -7
- metadata +10 -11
- 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, :
|
|
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] =
|
|
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
|
|
@@ -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
|
-
|
|
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,
|
|
563
|
-
|
|
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 #{
|
|
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
|
|
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
|
|
602
|
+
sql << " BEFORE #{quote(before)}"
|
|
577
603
|
elsif after
|
|
578
|
-
sql << " 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
|
|
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
|
-
|
|
850
|
-
|
|
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?(
|
|
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
|
-
|
|
1017
|
+
raw_execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
|
|
1059
1018
|
else
|
|
1060
|
-
|
|
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
|
-
|
|
1128
|
-
|
|
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
|
-
|
|
1186
|
-
|
|
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.
|
|
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
|