activerecord 7.2.0 → 8.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +189 -745
- data/README.rdoc +1 -1
- data/lib/active_record/associations/association.rb +25 -5
- 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/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +5 -5
- 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_assignment.rb +9 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
- data/lib/active_record/attributes.rb +6 -5
- 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 +23 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +53 -18
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +26 -5
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -25
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +20 -38
- 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 +44 -46
- 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/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +50 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +38 -90
- 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 +55 -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_handling.rb +22 -0
- data/lib/active_record/core.rb +16 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +12 -3
- data/lib/active_record/encryption/encryptor.rb +16 -9
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +8 -0
- data/lib/active_record/errors.rb +13 -5
- data/lib/active_record/fixtures.rb +0 -1
- 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/migration/command_recorder.rb +22 -5
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +35 -33
- data/lib/active_record/model_schema.rb +6 -3
- data/lib/active_record/nested_attributes.rb +11 -2
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_logs.rb +97 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +6 -6
- data/lib/active_record/railtie.rb +8 -14
- data/lib/active_record/reflection.rb +19 -10
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +135 -75
- data/lib/active_record/relation/calculations.rb +24 -19
- 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 +6 -1
- data/lib/active_record/relation/query_methods.rb +58 -37
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation.rb +72 -61
- data/lib/active_record/result.rb +68 -7
- 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 +6 -2
- data/lib/active_record/statement_cache.rb +12 -12
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +36 -16
- 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 +9 -8
- data/lib/active_record.rb +15 -0
- data/lib/arel/collectors/bind.rb +1 -1
- metadata +14 -14
@@ -12,16 +12,8 @@ module ActiveRecord
|
|
12
12
|
|
13
13
|
# Queries the database and returns the results in an Array-like object
|
14
14
|
def query(sql, name = nil) # :nodoc:
|
15
|
-
|
16
|
-
|
17
|
-
log(sql, name) do |notification_payload|
|
18
|
-
with_raw_connection do |conn|
|
19
|
-
result = conn.async_exec(sql).map_types!(@type_map_for_results).values
|
20
|
-
verified!
|
21
|
-
notification_payload[:row_count] = result.count
|
22
|
-
result
|
23
|
-
end
|
24
|
-
end
|
15
|
+
result = internal_execute(sql, name)
|
16
|
+
result.map_types!(@type_map_for_results).values
|
25
17
|
end
|
26
18
|
|
27
19
|
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(
|
@@ -50,36 +42,6 @@ module ActiveRecord
|
|
50
42
|
@notice_receiver_sql_warnings = []
|
51
43
|
end
|
52
44
|
|
53
|
-
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true)
|
54
|
-
log(sql, name, async: async) do |notification_payload|
|
55
|
-
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
56
|
-
result = conn.async_exec(sql)
|
57
|
-
verified!
|
58
|
-
handle_warnings(result)
|
59
|
-
notification_payload[:row_count] = result.count
|
60
|
-
result
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true) # :nodoc:
|
66
|
-
execute_and_clear(sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |result|
|
67
|
-
types = {}
|
68
|
-
fields = result.fields
|
69
|
-
fields.each_with_index do |fname, i|
|
70
|
-
ftype = result.ftype i
|
71
|
-
fmod = result.fmod i
|
72
|
-
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
73
|
-
end
|
74
|
-
build_result(columns: fields, rows: result.values, column_types: types.freeze)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def exec_delete(sql, name = nil, binds = []) # :nodoc:
|
79
|
-
execute_and_clear(sql, name, binds) { |result| result.cmd_tuples }
|
80
|
-
end
|
81
|
-
alias :exec_update :exec_delete
|
82
|
-
|
83
45
|
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) # :nodoc:
|
84
46
|
if use_insert_returning? || pk == false
|
85
47
|
super
|
@@ -170,8 +132,68 @@ module ActiveRecord
|
|
170
132
|
rescue PG::Error
|
171
133
|
end
|
172
134
|
|
173
|
-
def
|
174
|
-
|
135
|
+
def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch: false)
|
136
|
+
update_typemap_for_default_timezone
|
137
|
+
result = if prepare
|
138
|
+
begin
|
139
|
+
stmt_key = prepare_statement(sql, binds, raw_connection)
|
140
|
+
notification_payload[:statement_name] = stmt_key
|
141
|
+
raw_connection.exec_prepared(stmt_key, type_casted_binds)
|
142
|
+
rescue PG::FeatureNotSupported => error
|
143
|
+
if is_cached_plan_failure?(error)
|
144
|
+
# Nothing we can do if we are in a transaction because all commands
|
145
|
+
# will raise InFailedSQLTransaction
|
146
|
+
if in_transaction?
|
147
|
+
raise PreparedStatementCacheExpired.new(error.message, connection_pool: @pool)
|
148
|
+
else
|
149
|
+
@lock.synchronize do
|
150
|
+
# outside of transactions we can simply flush this query and retry
|
151
|
+
@statements.delete sql_key(sql)
|
152
|
+
end
|
153
|
+
retry
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
raise
|
158
|
+
end
|
159
|
+
elsif binds.nil? || binds.empty?
|
160
|
+
raw_connection.async_exec(sql)
|
161
|
+
else
|
162
|
+
raw_connection.exec_params(sql, type_casted_binds)
|
163
|
+
end
|
164
|
+
|
165
|
+
verified!
|
166
|
+
handle_warnings(result)
|
167
|
+
notification_payload[:row_count] = result.count
|
168
|
+
result
|
169
|
+
end
|
170
|
+
|
171
|
+
def cast_result(result)
|
172
|
+
if result.fields.empty?
|
173
|
+
result.clear
|
174
|
+
return ActiveRecord::Result.empty
|
175
|
+
end
|
176
|
+
|
177
|
+
types = {}
|
178
|
+
fields = result.fields
|
179
|
+
fields.each_with_index do |fname, i|
|
180
|
+
ftype = result.ftype i
|
181
|
+
fmod = result.fmod i
|
182
|
+
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
183
|
+
end
|
184
|
+
ar_result = ActiveRecord::Result.new(fields, result.values, types.freeze)
|
185
|
+
result.clear
|
186
|
+
ar_result
|
187
|
+
end
|
188
|
+
|
189
|
+
def affected_rows(result)
|
190
|
+
affected_rows = result.cmd_tuples
|
191
|
+
result.clear
|
192
|
+
affected_rows
|
193
|
+
end
|
194
|
+
|
195
|
+
def execute_batch(statements, name = nil, **kwargs)
|
196
|
+
raw_execute(combine_multi_statements(statements), name, batch: true, **kwargs)
|
175
197
|
end
|
176
198
|
|
177
199
|
def build_truncate_statements(table_names)
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
# TODO: Remove when IPAddr#== compares IPAddr#prefix. See
|
32
32
|
# https://github.com/ruby/ipaddr/issues/21
|
33
33
|
def changed?(old_value, new_value, _new_value_before_type_cast)
|
34
|
-
|
34
|
+
!old_value.eql?(new_value) || !old_value.nil? && old_value.prefix != new_value.prefix
|
35
35
|
end
|
36
36
|
|
37
37
|
def cast_value(value)
|
@@ -25,6 +25,10 @@ module ActiveRecord
|
|
25
25
|
build_point(x, y)
|
26
26
|
when ::Array
|
27
27
|
build_point(*value)
|
28
|
+
when ::Hash
|
29
|
+
return if value.blank?
|
30
|
+
|
31
|
+
build_point(*values_array_from_hash(value))
|
28
32
|
else
|
29
33
|
value
|
30
34
|
end
|
@@ -36,6 +40,8 @@ module ActiveRecord
|
|
36
40
|
"(#{number_for_point(value.x)},#{number_for_point(value.y)})"
|
37
41
|
when ::Array
|
38
42
|
serialize(build_point(*value))
|
43
|
+
when ::Hash
|
44
|
+
serialize(build_point(*values_array_from_hash(value)))
|
39
45
|
else
|
40
46
|
super
|
41
47
|
end
|
@@ -57,6 +63,10 @@ module ActiveRecord
|
|
57
63
|
def build_point(x, y)
|
58
64
|
ActiveRecord::Point.new(Float(x), Float(y))
|
59
65
|
end
|
66
|
+
|
67
|
+
def values_array_from_hash(value)
|
68
|
+
[value.values_at(:x, "x").compact.first, value.values_at(:y, "y").compact.first]
|
69
|
+
end
|
60
70
|
end
|
61
71
|
end
|
62
72
|
end
|
@@ -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.
|
@@ -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,16 +575,18 @@ module ActiveRecord
|
|
558
575
|
end
|
559
576
|
|
560
577
|
# Rename an existing enum type to something else.
|
561
|
-
def rename_enum(name, options
|
578
|
+
def rename_enum(name, **options)
|
562
579
|
to = options.fetch(:to) { raise ArgumentError, ":to is required" }
|
563
580
|
|
564
581
|
exec_query("ALTER TYPE #{quote_table_name(name)} RENAME TO #{to}").tap { reload_type_map }
|
565
582
|
end
|
566
583
|
|
567
584
|
# Add enum value to an existing enum type.
|
568
|
-
def add_enum_value(type_name, value, options
|
585
|
+
def add_enum_value(type_name, value, **options)
|
569
586
|
before, after = options.values_at(:before, :after)
|
570
|
-
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE
|
587
|
+
sql = +"ALTER TYPE #{quote_table_name(type_name)} ADD VALUE"
|
588
|
+
sql << " IF NOT EXISTS" if options[:if_not_exists]
|
589
|
+
sql << " '#{value}'"
|
571
590
|
|
572
591
|
if before && after
|
573
592
|
raise ArgumentError, "Cannot have both :before and :after at the same time"
|
@@ -581,7 +600,7 @@ module ActiveRecord
|
|
581
600
|
end
|
582
601
|
|
583
602
|
# Rename enum value on an existing enum type.
|
584
|
-
def rename_enum_value(type_name, options
|
603
|
+
def rename_enum_value(type_name, **options)
|
585
604
|
unless database_version >= 10_00_00 # >= 10.0
|
586
605
|
raise ArgumentError, "Renaming enum values is only supported in PostgreSQL 10 or later"
|
587
606
|
end
|
@@ -841,9 +860,8 @@ module ActiveRecord
|
|
841
860
|
def load_additional_types(oids = nil)
|
842
861
|
initializer = OID::TypeMapInitializer.new(type_map)
|
843
862
|
load_types_queries(initializer, oids) do |query|
|
844
|
-
|
845
|
-
|
846
|
-
end
|
863
|
+
records = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
864
|
+
initializer.run(records)
|
847
865
|
end
|
848
866
|
end
|
849
867
|
|
@@ -864,73 +882,6 @@ module ActiveRecord
|
|
864
882
|
|
865
883
|
FEATURE_NOT_SUPPORTED = "0A000" # :nodoc:
|
866
884
|
|
867
|
-
def execute_and_clear(sql, name, binds, prepare: false, async: false, allow_retry: false, materialize_transactions: true)
|
868
|
-
sql = transform_query(sql)
|
869
|
-
check_if_write_query(sql)
|
870
|
-
|
871
|
-
if !prepare || without_prepared_statement?(binds)
|
872
|
-
result = exec_no_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
873
|
-
else
|
874
|
-
result = exec_cache(sql, name, binds, async: async, allow_retry: allow_retry, materialize_transactions: materialize_transactions)
|
875
|
-
end
|
876
|
-
begin
|
877
|
-
ret = yield result
|
878
|
-
ensure
|
879
|
-
result.clear
|
880
|
-
end
|
881
|
-
ret
|
882
|
-
end
|
883
|
-
|
884
|
-
def exec_no_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
885
|
-
mark_transaction_written_if_write(sql)
|
886
|
-
|
887
|
-
# make sure we carry over any changes to ActiveRecord.default_timezone that have been
|
888
|
-
# made since we established the connection
|
889
|
-
update_typemap_for_default_timezone
|
890
|
-
|
891
|
-
type_casted_binds = type_casted_binds(binds)
|
892
|
-
log(sql, name, binds, type_casted_binds, async: async) do |notification_payload|
|
893
|
-
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
894
|
-
result = conn.exec_params(sql, type_casted_binds)
|
895
|
-
verified!
|
896
|
-
notification_payload[:row_count] = result.count
|
897
|
-
result
|
898
|
-
end
|
899
|
-
end
|
900
|
-
end
|
901
|
-
|
902
|
-
def exec_cache(sql, name, binds, async:, allow_retry:, materialize_transactions:)
|
903
|
-
mark_transaction_written_if_write(sql)
|
904
|
-
|
905
|
-
update_typemap_for_default_timezone
|
906
|
-
|
907
|
-
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
908
|
-
stmt_key = prepare_statement(sql, binds, conn)
|
909
|
-
type_casted_binds = type_casted_binds(binds)
|
910
|
-
|
911
|
-
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do |notification_payload|
|
912
|
-
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
913
|
-
verified!
|
914
|
-
notification_payload[:row_count] = result.count
|
915
|
-
result
|
916
|
-
end
|
917
|
-
end
|
918
|
-
rescue ActiveRecord::StatementInvalid => e
|
919
|
-
raise unless is_cached_plan_failure?(e)
|
920
|
-
|
921
|
-
# Nothing we can do if we are in a transaction because all commands
|
922
|
-
# will raise InFailedSQLTransaction
|
923
|
-
if in_transaction?
|
924
|
-
raise ActiveRecord::PreparedStatementCacheExpired.new(e.cause.message, connection_pool: @pool)
|
925
|
-
else
|
926
|
-
@lock.synchronize do
|
927
|
-
# outside of transactions we can simply flush this query and retry
|
928
|
-
@statements.delete sql_key(sql)
|
929
|
-
end
|
930
|
-
retry
|
931
|
-
end
|
932
|
-
end
|
933
|
-
|
934
885
|
# Annoyingly, the code for prepared statements whose return value may
|
935
886
|
# have changed is FEATURE_NOT_SUPPORTED.
|
936
887
|
#
|
@@ -940,8 +891,7 @@ module ActiveRecord
|
|
940
891
|
#
|
941
892
|
# Check here for more details:
|
942
893
|
# https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
|
943
|
-
def is_cached_plan_failure?(
|
944
|
-
pgerror = e.cause
|
894
|
+
def is_cached_plan_failure?(pgerror)
|
945
895
|
pgerror.result.result_error_field(PG::PG_DIAG_SQLSTATE) == FEATURE_NOT_SUPPORTED &&
|
946
896
|
pgerror.result.result_error_field(PG::PG_DIAG_SOURCE_FUNCTION) == "RevalidateCachedQuery"
|
947
897
|
rescue
|
@@ -1020,16 +970,16 @@ module ActiveRecord
|
|
1020
970
|
variables = @config.fetch(:variables, {}).stringify_keys
|
1021
971
|
|
1022
972
|
# Set interval output format to ISO 8601 for ease of parsing by ActiveSupport::Duration.parse
|
1023
|
-
internal_execute("SET intervalstyle = iso_8601")
|
973
|
+
internal_execute("SET intervalstyle = iso_8601", "SCHEMA")
|
1024
974
|
|
1025
975
|
# SET statements from :variables config hash
|
1026
976
|
# https://www.postgresql.org/docs/current/static/sql-set.html
|
1027
977
|
variables.map do |k, v|
|
1028
978
|
if v == ":default" || v == :default
|
1029
979
|
# Sets the value to the global or compile default
|
1030
|
-
internal_execute("SET SESSION #{k} TO DEFAULT")
|
980
|
+
internal_execute("SET SESSION #{k} TO DEFAULT", "SCHEMA")
|
1031
981
|
elsif !v.nil?
|
1032
|
-
internal_execute("SET SESSION #{k} TO #{quote(v)}")
|
982
|
+
internal_execute("SET SESSION #{k} TO #{quote(v)}", "SCHEMA")
|
1033
983
|
end
|
1034
984
|
end
|
1035
985
|
|
@@ -1050,9 +1000,9 @@ module ActiveRecord
|
|
1050
1000
|
# If using Active Record's time zone support configure the connection
|
1051
1001
|
# to return TIMESTAMP WITH ZONE types in UTC.
|
1052
1002
|
if default_timezone == :utc
|
1053
|
-
|
1003
|
+
raw_execute("SET SESSION timezone TO 'UTC'", "SCHEMA")
|
1054
1004
|
else
|
1055
|
-
|
1005
|
+
raw_execute("SET SESSION timezone TO DEFAULT", "SCHEMA")
|
1056
1006
|
end
|
1057
1007
|
end
|
1058
1008
|
|
@@ -1119,9 +1069,8 @@ module ActiveRecord
|
|
1119
1069
|
AND castsource = #{quote column.sql_type}::regtype
|
1120
1070
|
)
|
1121
1071
|
SQL
|
1122
|
-
|
1123
|
-
|
1124
|
-
end
|
1072
|
+
result = internal_execute(sql, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
1073
|
+
result.getvalue(0, 0)
|
1125
1074
|
end
|
1126
1075
|
end
|
1127
1076
|
end
|
@@ -1177,9 +1126,8 @@ module ActiveRecord
|
|
1177
1126
|
FROM pg_type as t
|
1178
1127
|
WHERE t.typname IN (%s)
|
1179
1128
|
SQL
|
1180
|
-
|
1181
|
-
|
1182
|
-
end
|
1129
|
+
result = internal_execute(query, "SCHEMA", [], allow_retry: true, materialize_transactions: false)
|
1130
|
+
coders = result.filter_map { |row| construct_coder(row, coders_by_name[row["typname"]]) }
|
1183
1131
|
|
1184
1132
|
map = PG::TypeMapByOid.new
|
1185
1133
|
coders.each { |coder| map.add_coder(coder) }
|
@@ -434,9 +434,7 @@ module ActiveRecord
|
|
434
434
|
end
|
435
435
|
|
436
436
|
def ignored_table?(table_name)
|
437
|
-
ActiveRecord.
|
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
|