activerecord 7.1.1 → 7.1.5
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 +496 -0
- data/README.rdoc +1 -0
- data/lib/active_record/associations/association.rb +2 -1
- data/lib/active_record/associations/belongs_to_association.rb +4 -4
- data/lib/active_record/associations/collection_association.rb +4 -4
- data/lib/active_record/associations/has_many_association.rb +2 -2
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +10 -12
- data/lib/active_record/associations/preloader/association.rb +4 -1
- data/lib/active_record/associations.rb +21 -15
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +15 -11
- data/lib/active_record/attribute_methods/read.rb +3 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -0
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +47 -7
- data/lib/active_record/autosave_association.rb +5 -2
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +19 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +5 -3
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +8 -4
- data/lib/active_record/connection_adapters/abstract_adapter.rb +27 -19
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +37 -13
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +2 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +3 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -6
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +32 -33
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +1 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +25 -21
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +49 -10
- data/lib/active_record/counter_cache.rb +7 -3
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/database_configurations/hash_config.rb +6 -2
- data/lib/active_record/delegated_type.rb +7 -7
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -1
- data/lib/active_record/encryption/encrypted_attribute_type.rb +6 -2
- data/lib/active_record/encryption/extended_deterministic_queries.rb +0 -15
- data/lib/active_record/encryption/scheme.rb +8 -4
- data/lib/active_record/encryption.rb +2 -0
- data/lib/active_record/enum.rb +6 -9
- data/lib/active_record/errors.rb +5 -4
- data/lib/active_record/fixtures.rb +16 -0
- data/lib/active_record/future_result.rb +10 -0
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/insert_all.rb +3 -3
- data/lib/active_record/internal_metadata.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/marshalling.rb +4 -1
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +1 -1
- data/lib/active_record/migration/compatibility.rb +14 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +9 -5
- data/lib/active_record/model_schema.rb +12 -7
- data/lib/active_record/nested_attributes.rb +16 -5
- data/lib/active_record/normalization.rb +8 -0
- data/lib/active_record/persistence.rb +6 -5
- data/lib/active_record/promise.rb +1 -1
- data/lib/active_record/query_cache.rb +1 -1
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/railtie.rb +14 -14
- data/lib/active_record/railties/controller_runtime.rb +2 -1
- data/lib/active_record/railties/databases.rake +7 -7
- data/lib/active_record/reflection.rb +21 -3
- data/lib/active_record/relation/calculations.rb +28 -1
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/query_methods.rb +21 -7
- data/lib/active_record/relation.rb +30 -5
- data/lib/active_record/result.rb +1 -1
- data/lib/active_record/runtime_registry.rb +15 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/secure_token.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +35 -13
- data/lib/active_record/test_fixtures.rb +1 -0
- data/lib/active_record/timestamp.rb +3 -1
- data/lib/active_record.rb +2 -2
- data/lib/arel/nodes/homogeneous_in.rb +1 -1
- data/lib/arel/tree_manager.rb +5 -1
- data/lib/arel/visitors/to_sql.rb +2 -1
- metadata +14 -13
|
@@ -336,7 +336,7 @@ module ActiveRecord
|
|
|
336
336
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
337
337
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
|
338
338
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
|
339
|
-
rename_table_indexes(table_name, new_name)
|
|
339
|
+
rename_table_indexes(table_name, new_name, **options)
|
|
340
340
|
end
|
|
341
341
|
|
|
342
342
|
# Drops a table from the database.
|
|
@@ -635,18 +635,38 @@ module ActiveRecord
|
|
|
635
635
|
end
|
|
636
636
|
|
|
637
637
|
def build_insert_sql(insert) # :nodoc:
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
sql
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
638
|
+
no_op_column = quote_column_name(insert.keys.first)
|
|
639
|
+
|
|
640
|
+
# MySQL 8.0.19 replaces `VALUES(<expression>)` clauses with row and column alias names, see https://dev.mysql.com/worklog/task/?id=6312 .
|
|
641
|
+
# then MySQL 8.0.20 deprecates the `VALUES(<expression>)` see https://dev.mysql.com/worklog/task/?id=13325 .
|
|
642
|
+
if supports_insert_raw_alias_syntax?
|
|
643
|
+
values_alias = quote_table_name("#{insert.model.table_name}_values")
|
|
644
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
|
|
645
|
+
|
|
646
|
+
if insert.skip_duplicates?
|
|
647
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
|
|
648
|
+
elsif insert.update_duplicates?
|
|
649
|
+
if insert.raw_update_sql?
|
|
650
|
+
sql = +"INSERT #{insert.into} #{insert.values_list} ON DUPLICATE KEY UPDATE #{insert.raw_update_sql}"
|
|
651
|
+
else
|
|
652
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
|
653
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
|
|
654
|
+
sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
|
|
655
|
+
end
|
|
656
|
+
end
|
|
657
|
+
else
|
|
658
|
+
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
|
659
|
+
|
|
660
|
+
if insert.skip_duplicates?
|
|
661
|
+
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
|
|
662
|
+
elsif insert.update_duplicates?
|
|
663
|
+
sql << " ON DUPLICATE KEY UPDATE "
|
|
664
|
+
if insert.raw_update_sql?
|
|
665
|
+
sql << insert.raw_update_sql
|
|
666
|
+
else
|
|
667
|
+
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
|
|
668
|
+
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
|
669
|
+
end
|
|
650
670
|
end
|
|
651
671
|
end
|
|
652
672
|
|
|
@@ -853,6 +873,10 @@ module ActiveRecord
|
|
|
853
873
|
"DROP INDEX #{quote_column_name(index_name)}"
|
|
854
874
|
end
|
|
855
875
|
|
|
876
|
+
def supports_insert_raw_alias_syntax?
|
|
877
|
+
!mariadb? && database_version >= "8.0.19"
|
|
878
|
+
end
|
|
879
|
+
|
|
856
880
|
def supports_rename_index?
|
|
857
881
|
if mariadb?
|
|
858
882
|
database_version >= "10.5.2"
|
|
@@ -155,7 +155,7 @@ module ActiveRecord
|
|
|
155
155
|
end
|
|
156
156
|
|
|
157
157
|
def valid_primary_key_options
|
|
158
|
-
super + [:unsigned]
|
|
158
|
+
super + [:unsigned, :auto_increment]
|
|
159
159
|
end
|
|
160
160
|
|
|
161
161
|
def create_table_definition(name, **options)
|
|
@@ -185,6 +185,7 @@ module ActiveRecord
|
|
|
185
185
|
default, default_function = nil, default
|
|
186
186
|
elsif type_metadata.extra == "DEFAULT_GENERATED"
|
|
187
187
|
default = +"(#{default})" unless default.start_with?("(")
|
|
188
|
+
default = default.gsub("\\'", "'")
|
|
188
189
|
default, default_function = nil, default
|
|
189
190
|
elsif type_metadata.type == :text && default&.start_with?("'")
|
|
190
191
|
# strip and unescape quotes
|
|
@@ -98,6 +98,7 @@ module ActiveRecord
|
|
|
98
98
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
99
99
|
sync_timezone_changes(conn)
|
|
100
100
|
result = conn.query(sql)
|
|
101
|
+
verified!
|
|
101
102
|
handle_warnings(sql)
|
|
102
103
|
result
|
|
103
104
|
end
|
|
@@ -126,6 +127,8 @@ module ActiveRecord
|
|
|
126
127
|
result = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
|
127
128
|
stmt.execute(*type_casted_binds)
|
|
128
129
|
end
|
|
130
|
+
verified!
|
|
131
|
+
result
|
|
129
132
|
rescue ::Mysql2::Error => e
|
|
130
133
|
if cache_stmt
|
|
131
134
|
@statements.delete(sql)
|
|
@@ -131,7 +131,7 @@ module ActiveRecord
|
|
|
131
131
|
#++
|
|
132
132
|
|
|
133
133
|
def active?
|
|
134
|
-
|
|
134
|
+
!(@raw_connection.nil? || @raw_connection.closed?) && @lock.synchronize { @raw_connection&.ping } || false
|
|
135
135
|
end
|
|
136
136
|
|
|
137
137
|
alias :reset! :reconnect!
|
|
@@ -139,15 +139,19 @@ module ActiveRecord
|
|
|
139
139
|
# Disconnects from the database if already connected.
|
|
140
140
|
# Otherwise, this method does nothing.
|
|
141
141
|
def disconnect!
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
@lock.synchronize do
|
|
143
|
+
super
|
|
144
|
+
@raw_connection&.close
|
|
145
|
+
@raw_connection = nil
|
|
146
|
+
end
|
|
145
147
|
end
|
|
146
148
|
|
|
147
149
|
def discard! # :nodoc:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
@lock.synchronize do
|
|
151
|
+
super
|
|
152
|
+
@raw_connection&.automatic_close = false
|
|
153
|
+
@raw_connection = nil
|
|
154
|
+
end
|
|
151
155
|
end
|
|
152
156
|
|
|
153
157
|
private
|
|
@@ -162,9 +166,11 @@ module ActiveRecord
|
|
|
162
166
|
end
|
|
163
167
|
|
|
164
168
|
def reconnect
|
|
165
|
-
@
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
@lock.synchronize do
|
|
170
|
+
@raw_connection&.close
|
|
171
|
+
@raw_connection = nil
|
|
172
|
+
connect
|
|
173
|
+
end
|
|
168
174
|
end
|
|
169
175
|
|
|
170
176
|
def configure_connection
|
|
@@ -16,7 +16,9 @@ module ActiveRecord
|
|
|
16
16
|
|
|
17
17
|
log(sql, name) do
|
|
18
18
|
with_raw_connection do |conn|
|
|
19
|
-
conn.async_exec(sql).map_types!(@type_map_for_results).values
|
|
19
|
+
result = conn.async_exec(sql).map_types!(@type_map_for_results).values
|
|
20
|
+
verified!
|
|
21
|
+
result
|
|
20
22
|
end
|
|
21
23
|
end
|
|
22
24
|
end
|
|
@@ -51,6 +53,7 @@ module ActiveRecord
|
|
|
51
53
|
log(sql, name, async: async) do
|
|
52
54
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
53
55
|
result = conn.async_exec(sql)
|
|
56
|
+
verified!
|
|
54
57
|
handle_warnings(result)
|
|
55
58
|
result
|
|
56
59
|
end
|
|
@@ -28,6 +28,12 @@ module ActiveRecord
|
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
# TODO: Remove when IPAddr#== compares IPAddr#prefix. See
|
|
32
|
+
# https://github.com/ruby/ipaddr/issues/21
|
|
33
|
+
def changed?(old_value, new_value, _new_value_before_type_cast)
|
|
34
|
+
!old_value.eql?(new_value) || !old_value.nil? && old_value.prefix != new_value.prefix
|
|
35
|
+
end
|
|
36
|
+
|
|
31
37
|
def cast_value(value)
|
|
32
38
|
if value.nil?
|
|
33
39
|
nil
|
|
@@ -27,9 +27,10 @@ module ActiveRecord
|
|
|
27
27
|
value = value.sub(/^\((.+)\)$/, '-\1') # (4)
|
|
28
28
|
case value
|
|
29
29
|
when /^-?\D*+[\d,]+\.\d{2}$/ # (1)
|
|
30
|
-
value.
|
|
30
|
+
value.delete!("^-0-9.")
|
|
31
31
|
when /^-?\D*+[\d.]+,\d{2}$/ # (2)
|
|
32
|
-
value.
|
|
32
|
+
value.delete!("^-0-9,")
|
|
33
|
+
value.tr!(",", ".")
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
super(value)
|
|
@@ -247,6 +247,8 @@ module ActiveRecord
|
|
|
247
247
|
|
|
248
248
|
# Returns the sequence name for a table's primary key or some other specified key.
|
|
249
249
|
def default_sequence_name(table_name, pk = "id") # :nodoc:
|
|
250
|
+
return nil if pk.is_a?(Array)
|
|
251
|
+
|
|
250
252
|
result = serial_sequence(table_name, pk)
|
|
251
253
|
return nil unless result
|
|
252
254
|
Utils.extract_schema_qualified_name(result).to_s
|
|
@@ -341,7 +343,7 @@ module ActiveRecord
|
|
|
341
343
|
JOIN pg_namespace nsp ON (t.relnamespace = nsp.oid)
|
|
342
344
|
WHERE t.oid = #{quote(quote_table_name(table))}::regclass
|
|
343
345
|
AND cons.contype = 'p'
|
|
344
|
-
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
|
|
346
|
+
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate|gen_random_uuid'
|
|
345
347
|
SQL
|
|
346
348
|
end
|
|
347
349
|
|
|
@@ -385,15 +387,22 @@ module ActiveRecord
|
|
|
385
387
|
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
|
386
388
|
pk, seq = pk_and_sequence_for(new_name)
|
|
387
389
|
if pk
|
|
388
|
-
|
|
389
|
-
|
|
390
|
+
# PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
|
|
391
|
+
# truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
|
|
392
|
+
max_pkey_prefix = max_identifier_length - "_pkey".size
|
|
393
|
+
idx = "#{table_name[0, max_pkey_prefix]}_pkey"
|
|
394
|
+
new_idx = "#{new_name[0, max_pkey_prefix]}_pkey"
|
|
390
395
|
execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}"
|
|
391
|
-
|
|
392
|
-
|
|
396
|
+
|
|
397
|
+
# PostgreSQL automatically creates a sequence for PRIMARY KEY with name consisting of
|
|
398
|
+
# truncated table name and "#{primary_key}_seq" suffix fitting into max_identifier_length number of characters.
|
|
399
|
+
max_seq_prefix = max_identifier_length - "_#{pk}_seq".size
|
|
400
|
+
if seq && seq.identifier == "#{table_name[0, max_seq_prefix]}_#{pk}_seq"
|
|
401
|
+
new_seq = "#{new_name[0, max_seq_prefix]}_#{pk}_seq"
|
|
393
402
|
execute "ALTER TABLE #{seq.quoted} RENAME TO #{quote_table_name(new_seq)}"
|
|
394
403
|
end
|
|
395
404
|
end
|
|
396
|
-
rename_table_indexes(table_name, new_name)
|
|
405
|
+
rename_table_indexes(table_name, new_name, **options)
|
|
397
406
|
end
|
|
398
407
|
|
|
399
408
|
def add_column(table_name, column_name, type, **options) # :nodoc:
|
|
@@ -108,10 +108,11 @@ module ActiveRecord
|
|
|
108
108
|
# but significantly increases the risk of data loss if the database
|
|
109
109
|
# crashes. As a result, this should not be used in production
|
|
110
110
|
# environments. If you would like all created tables to be unlogged in
|
|
111
|
-
# the test environment you can add the following
|
|
112
|
-
# file:
|
|
111
|
+
# the test environment you can add the following to your test.rb file:
|
|
113
112
|
#
|
|
114
|
-
#
|
|
113
|
+
# ActiveSupport.on_load(:active_record_postgresqladapter) do
|
|
114
|
+
# self.create_unlogged_tables = true
|
|
115
|
+
# end
|
|
115
116
|
class_attribute :create_unlogged_tables, default: false
|
|
116
117
|
|
|
117
118
|
##
|
|
@@ -277,6 +278,10 @@ module ActiveRecord
|
|
|
277
278
|
database_version >= 12_00_00 # >= 12.0
|
|
278
279
|
end
|
|
279
280
|
|
|
281
|
+
def supports_identity_columns? # :nodoc:
|
|
282
|
+
database_version >= 10_00_00 # >= 10.0
|
|
283
|
+
end
|
|
284
|
+
|
|
280
285
|
def supports_nulls_not_distinct?
|
|
281
286
|
database_version >= 15_00_00 # >= 15.0
|
|
282
287
|
end
|
|
@@ -535,7 +540,7 @@ module ActiveRecord
|
|
|
535
540
|
END
|
|
536
541
|
$$;
|
|
537
542
|
SQL
|
|
538
|
-
internal_exec_query(query)
|
|
543
|
+
internal_exec_query(query).tap { reload_type_map }
|
|
539
544
|
end
|
|
540
545
|
|
|
541
546
|
# Drops an enum type.
|
|
@@ -551,7 +556,7 @@ module ActiveRecord
|
|
|
551
556
|
query = <<~SQL
|
|
552
557
|
DROP TYPE#{' IF EXISTS' if options[:if_exists]} #{quote_table_name(name)};
|
|
553
558
|
SQL
|
|
554
|
-
internal_exec_query(query)
|
|
559
|
+
internal_exec_query(query).tap { reload_type_map }
|
|
555
560
|
end
|
|
556
561
|
|
|
557
562
|
# Rename an existing enum type to something else.
|
|
@@ -596,14 +601,6 @@ module ActiveRecord
|
|
|
596
601
|
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
|
|
597
602
|
end
|
|
598
603
|
|
|
599
|
-
# Returns the maximum length of a table name.
|
|
600
|
-
def table_name_length
|
|
601
|
-
# PostgreSQL automatically creates an index for PRIMARY KEY with name consisting of
|
|
602
|
-
# truncated table name and "_pkey" suffix fitting into max_identifier_length number of characters.
|
|
603
|
-
# We allow smaller table names to be able to correctly rename this index when renaming the table.
|
|
604
|
-
max_identifier_length - "_pkey".length
|
|
605
|
-
end
|
|
606
|
-
|
|
607
604
|
# Set the authorized user for this session
|
|
608
605
|
def session_auth=(user)
|
|
609
606
|
clear_cache!
|
|
@@ -894,7 +891,9 @@ module ActiveRecord
|
|
|
894
891
|
type_casted_binds = type_casted_binds(binds)
|
|
895
892
|
log(sql, name, binds, type_casted_binds, async: async) do
|
|
896
893
|
with_raw_connection do |conn|
|
|
897
|
-
conn.exec_params(sql, type_casted_binds)
|
|
894
|
+
result = conn.exec_params(sql, type_casted_binds)
|
|
895
|
+
verified!
|
|
896
|
+
result
|
|
898
897
|
end
|
|
899
898
|
end
|
|
900
899
|
end
|
|
@@ -904,12 +903,14 @@ module ActiveRecord
|
|
|
904
903
|
|
|
905
904
|
update_typemap_for_default_timezone
|
|
906
905
|
|
|
907
|
-
stmt_key = prepare_statement(sql, binds)
|
|
908
|
-
type_casted_binds = type_casted_binds(binds)
|
|
909
|
-
|
|
910
906
|
with_raw_connection do |conn|
|
|
907
|
+
stmt_key = prepare_statement(sql, binds, conn)
|
|
908
|
+
type_casted_binds = type_casted_binds(binds)
|
|
909
|
+
|
|
911
910
|
log(sql, name, binds, type_casted_binds, stmt_key, async: async) do
|
|
912
|
-
conn.exec_prepared(stmt_key, type_casted_binds)
|
|
911
|
+
result = conn.exec_prepared(stmt_key, type_casted_binds)
|
|
912
|
+
verified!
|
|
913
|
+
result
|
|
913
914
|
end
|
|
914
915
|
end
|
|
915
916
|
rescue ActiveRecord::StatementInvalid => e
|
|
@@ -957,22 +958,20 @@ module ActiveRecord
|
|
|
957
958
|
|
|
958
959
|
# Prepare the statement if it hasn't been prepared, return
|
|
959
960
|
# the statement key.
|
|
960
|
-
def prepare_statement(sql, binds)
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
raise translate_exception_class(e, sql, binds)
|
|
969
|
-
end
|
|
970
|
-
# Clear the queue
|
|
971
|
-
conn.get_last_result
|
|
972
|
-
@statements[sql_key] = nextkey
|
|
961
|
+
def prepare_statement(sql, binds, conn)
|
|
962
|
+
sql_key = sql_key(sql)
|
|
963
|
+
unless @statements.key? sql_key
|
|
964
|
+
nextkey = @statements.next_key
|
|
965
|
+
begin
|
|
966
|
+
conn.prepare nextkey, sql
|
|
967
|
+
rescue => e
|
|
968
|
+
raise translate_exception_class(e, sql, binds)
|
|
973
969
|
end
|
|
974
|
-
|
|
970
|
+
# Clear the queue
|
|
971
|
+
conn.get_last_result
|
|
972
|
+
@statements[sql_key] = nextkey
|
|
975
973
|
end
|
|
974
|
+
@statements[sql_key]
|
|
976
975
|
end
|
|
977
976
|
|
|
978
977
|
# Connects to a PostgreSQL server and sets up the adapter depending on the
|
|
@@ -1076,7 +1075,7 @@ module ActiveRecord
|
|
|
1076
1075
|
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
|
|
1077
1076
|
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
|
|
1078
1077
|
c.collname, col_description(a.attrelid, a.attnum) AS comment,
|
|
1079
|
-
|
|
1078
|
+
#{supports_identity_columns? ? 'attidentity' : quote('')} AS identity,
|
|
1080
1079
|
#{supports_virtual_columns? ? 'attgenerated' : quote('')} as attgenerated
|
|
1081
1080
|
FROM pg_attribute a
|
|
1082
1081
|
LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
|
|
@@ -50,6 +50,7 @@ module ActiveRecord
|
|
|
50
50
|
stmt.bind_params(type_casted_binds)
|
|
51
51
|
records = stmt.to_a
|
|
52
52
|
end
|
|
53
|
+
verified!
|
|
53
54
|
|
|
54
55
|
build_result(columns: cols, rows: records)
|
|
55
56
|
end
|
|
@@ -76,7 +77,9 @@ module ActiveRecord
|
|
|
76
77
|
def begin_db_transaction # :nodoc:
|
|
77
78
|
log("begin transaction", "TRANSACTION") do
|
|
78
79
|
with_raw_connection(allow_retry: true, materialize_transactions: false) do |conn|
|
|
79
|
-
conn.transaction
|
|
80
|
+
result = conn.transaction
|
|
81
|
+
verified!
|
|
82
|
+
result
|
|
80
83
|
end
|
|
81
84
|
end
|
|
82
85
|
end
|
|
@@ -112,7 +115,9 @@ module ActiveRecord
|
|
|
112
115
|
def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: false)
|
|
113
116
|
log(sql, name, async: async) do
|
|
114
117
|
with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn|
|
|
115
|
-
conn.execute(sql)
|
|
118
|
+
result = conn.execute(sql)
|
|
119
|
+
verified!
|
|
120
|
+
result
|
|
116
121
|
end
|
|
117
122
|
end
|
|
118
123
|
end
|
|
@@ -133,7 +138,9 @@ module ActiveRecord
|
|
|
133
138
|
|
|
134
139
|
log(sql, name) do
|
|
135
140
|
with_raw_connection do |conn|
|
|
136
|
-
conn.execute_batch2(sql)
|
|
141
|
+
result = conn.execute_batch2(sql)
|
|
142
|
+
verified!
|
|
143
|
+
result
|
|
137
144
|
end
|
|
138
145
|
end
|
|
139
146
|
end
|
|
@@ -11,7 +11,7 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
|
|
|
11
11
|
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
|
12
12
|
require "active_record/connection_adapters/sqlite3/schema_statements"
|
|
13
13
|
|
|
14
|
-
gem "sqlite3", "
|
|
14
|
+
gem "sqlite3", ">= 1.4"
|
|
15
15
|
require "sqlite3"
|
|
16
16
|
|
|
17
17
|
module ActiveRecord
|
|
@@ -286,7 +286,7 @@ module ActiveRecord
|
|
|
286
286
|
schema_cache.clear_data_source_cache!(table_name.to_s)
|
|
287
287
|
schema_cache.clear_data_source_cache!(new_name.to_s)
|
|
288
288
|
exec_query "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
|
|
289
|
-
rename_table_indexes(table_name, new_name)
|
|
289
|
+
rename_table_indexes(table_name, new_name, **options)
|
|
290
290
|
end
|
|
291
291
|
|
|
292
292
|
def add_column(table_name, column_name, type, **options) # :nodoc:
|
|
@@ -57,7 +57,7 @@ module ActiveRecord
|
|
|
57
57
|
def new_client(config)
|
|
58
58
|
config[:ssl_mode] = parse_ssl_mode(config[:ssl_mode]) if config[:ssl_mode]
|
|
59
59
|
::Trilogy.new(config)
|
|
60
|
-
rescue ::Trilogy::
|
|
60
|
+
rescue ::Trilogy::Error => error
|
|
61
61
|
raise translate_connect_error(config, error)
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -99,6 +99,18 @@ module ActiveRecord
|
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
+
def initialize(...)
|
|
103
|
+
super
|
|
104
|
+
|
|
105
|
+
if @config[:prepared_statements]
|
|
106
|
+
raise ArgumentError, "Trilogy currently doesn't support prepared statements. Remove `prepared_statements: true` from your database configuration."
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Trilogy ignore `socket` if `host is set. We want the opposite to allow
|
|
110
|
+
# configuring UNIX domain sockets via `DATABASE_URL`.
|
|
111
|
+
@config.delete(:host) if @config[:socket]
|
|
112
|
+
end
|
|
113
|
+
|
|
102
114
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
|
103
115
|
|
|
104
116
|
def supports_json?
|
|
@@ -132,7 +144,7 @@ module ActiveRecord
|
|
|
132
144
|
end
|
|
133
145
|
|
|
134
146
|
def active?
|
|
135
|
-
|
|
147
|
+
!(@raw_connection.nil? || @raw_connection.closed?) && @lock.synchronize { @raw_connection&.ping } || false
|
|
136
148
|
rescue ::Trilogy::Error
|
|
137
149
|
false
|
|
138
150
|
end
|
|
@@ -140,18 +152,18 @@ module ActiveRecord
|
|
|
140
152
|
alias reset! reconnect!
|
|
141
153
|
|
|
142
154
|
def disconnect!
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
155
|
+
@lock.synchronize do
|
|
156
|
+
super
|
|
157
|
+
@raw_connection&.close
|
|
158
|
+
@raw_connection = nil
|
|
147
159
|
end
|
|
148
160
|
end
|
|
149
161
|
|
|
150
162
|
def discard!
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
163
|
+
@lock.synchronize do
|
|
164
|
+
super
|
|
165
|
+
@raw_connection&.discard!
|
|
166
|
+
@raw_connection = nil
|
|
155
167
|
end
|
|
156
168
|
end
|
|
157
169
|
|
|
@@ -181,23 +193,15 @@ module ActiveRecord
|
|
|
181
193
|
exception.error_code if exception.respond_to?(:error_code)
|
|
182
194
|
end
|
|
183
195
|
|
|
184
|
-
def connection
|
|
185
|
-
@raw_connection
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def connection=(conn)
|
|
189
|
-
@raw_connection = conn
|
|
190
|
-
end
|
|
191
|
-
|
|
192
196
|
def connect
|
|
193
|
-
|
|
197
|
+
@raw_connection = self.class.new_client(@config)
|
|
194
198
|
rescue ConnectionNotEstablished => ex
|
|
195
199
|
raise ex.set_pool(@pool)
|
|
196
200
|
end
|
|
197
201
|
|
|
198
202
|
def reconnect
|
|
199
|
-
|
|
200
|
-
|
|
203
|
+
@raw_connection&.close
|
|
204
|
+
@raw_connection = nil
|
|
201
205
|
connect
|
|
202
206
|
end
|
|
203
207
|
|
|
@@ -256,7 +256,7 @@ module ActiveRecord
|
|
|
256
256
|
|
|
257
257
|
attr_writer :connection_specification_name
|
|
258
258
|
|
|
259
|
-
#
|
|
259
|
+
# Returns the connection specification name from the current class or its parent.
|
|
260
260
|
def connection_specification_name
|
|
261
261
|
if !defined?(@connection_specification_name) || @connection_specification_name.nil?
|
|
262
262
|
return self == Base ? Base.name : superclass.connection_specification_name
|
data/lib/active_record/core.rb
CHANGED
|
@@ -15,9 +15,10 @@ module ActiveRecord
|
|
|
15
15
|
##
|
|
16
16
|
# :singleton-method:
|
|
17
17
|
#
|
|
18
|
-
# Accepts a logger conforming to the interface of Log4r
|
|
19
|
-
# passed on to any new database
|
|
20
|
-
#
|
|
18
|
+
# Accepts a logger conforming to the interface of Log4r or the default
|
|
19
|
+
# Ruby +Logger+ class, which is then passed on to any new database
|
|
20
|
+
# connections made. You can retrieve this logger by calling +logger+ on
|
|
21
|
+
# either an Active Record model class or an Active Record model instance.
|
|
21
22
|
class_attribute :logger, instance_writer: false
|
|
22
23
|
|
|
23
24
|
class_attribute :_destroy_association_async_job, instance_accessor: false, default: "ActiveRecord::DestroyAssociationAsyncJob"
|
|
@@ -271,10 +272,25 @@ module ActiveRecord
|
|
|
271
272
|
elsif reflection.belongs_to? && !reflection.polymorphic?
|
|
272
273
|
key = reflection.join_foreign_key
|
|
273
274
|
pkey = reflection.join_primary_key
|
|
274
|
-
|
|
275
|
+
|
|
276
|
+
if pkey.is_a?(Array)
|
|
277
|
+
if pkey.all? { |attribute| value.respond_to?(attribute) }
|
|
278
|
+
value = pkey.map do |attribute|
|
|
279
|
+
if attribute == "id"
|
|
280
|
+
value.id_value
|
|
281
|
+
else
|
|
282
|
+
value.public_send(attribute)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
composite_primary_key = true
|
|
286
|
+
end
|
|
287
|
+
else
|
|
288
|
+
value = value.public_send(pkey) if value.respond_to?(pkey)
|
|
289
|
+
end
|
|
275
290
|
end
|
|
276
291
|
|
|
277
|
-
if !
|
|
292
|
+
if !composite_primary_key &&
|
|
293
|
+
(!columns_hash.key?(key) || StatementCache.unsupported_value?(value))
|
|
278
294
|
return super
|
|
279
295
|
end
|
|
280
296
|
|
|
@@ -330,7 +346,7 @@ module ActiveRecord
|
|
|
330
346
|
|
|
331
347
|
# Returns a string like 'Post(id:integer, title:string, body:text)'
|
|
332
348
|
def inspect # :nodoc:
|
|
333
|
-
if self == Base
|
|
349
|
+
if self == Base || singleton_class?
|
|
334
350
|
super
|
|
335
351
|
elsif abstract_class?
|
|
336
352
|
"#{super}(abstract)"
|
|
@@ -380,8 +396,8 @@ module ActiveRecord
|
|
|
380
396
|
@arel_table = nil
|
|
381
397
|
@predicate_builder = nil
|
|
382
398
|
@inspection_filter = nil
|
|
383
|
-
@filter_attributes
|
|
384
|
-
@generated_association_methods
|
|
399
|
+
@filter_attributes ||= nil
|
|
400
|
+
@generated_association_methods ||= nil
|
|
385
401
|
end
|
|
386
402
|
end
|
|
387
403
|
|
|
@@ -401,12 +417,18 @@ module ActiveRecord
|
|
|
401
417
|
|
|
402
418
|
def cached_find_by(keys, values)
|
|
403
419
|
statement = cached_find_by_statement(keys) { |params|
|
|
404
|
-
wheres = keys.index_with
|
|
420
|
+
wheres = keys.index_with do |key|
|
|
421
|
+
if key.is_a?(Array)
|
|
422
|
+
[key.map { params.bind }]
|
|
423
|
+
else
|
|
424
|
+
params.bind
|
|
425
|
+
end
|
|
426
|
+
end
|
|
405
427
|
where(wheres).limit(1)
|
|
406
428
|
}
|
|
407
429
|
|
|
408
430
|
begin
|
|
409
|
-
statement.execute(values, connection).first
|
|
431
|
+
statement.execute(values.flatten, connection).first
|
|
410
432
|
rescue TypeError
|
|
411
433
|
raise ActiveRecord::StatementInvalid
|
|
412
434
|
end
|
|
@@ -544,6 +566,10 @@ module ActiveRecord
|
|
|
544
566
|
# Returns a hash of the given methods with their names as keys and returned
|
|
545
567
|
# values as values.
|
|
546
568
|
#
|
|
569
|
+
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
|
570
|
+
# topic.slice(:title, :author_name)
|
|
571
|
+
# => { "title" => "Budget", "author_name" => "Jason" }
|
|
572
|
+
#
|
|
547
573
|
#--
|
|
548
574
|
# Implemented by ActiveModel::Access#slice.
|
|
549
575
|
|
|
@@ -554,6 +580,10 @@ module ActiveRecord
|
|
|
554
580
|
#
|
|
555
581
|
# Returns an array of the values returned by the given methods.
|
|
556
582
|
#
|
|
583
|
+
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
|
584
|
+
# topic.values_at(:title, :author_name)
|
|
585
|
+
# => ["Budget", "Jason"]
|
|
586
|
+
#
|
|
557
587
|
#--
|
|
558
588
|
# Implemented by ActiveModel::Access#values_at.
|
|
559
589
|
|
|
@@ -671,7 +701,16 @@ module ActiveRecord
|
|
|
671
701
|
@strict_loading_mode == :n_plus_one_only
|
|
672
702
|
end
|
|
673
703
|
|
|
704
|
+
# Returns +true+ if the record uses strict_loading with +:all+ mode enabled.
|
|
705
|
+
def strict_loading_all?
|
|
706
|
+
@strict_loading_mode == :all
|
|
707
|
+
end
|
|
708
|
+
|
|
674
709
|
# Marks this record as read only.
|
|
710
|
+
#
|
|
711
|
+
# customer = Customer.first
|
|
712
|
+
# customer.readonly!
|
|
713
|
+
# customer.save # Raises an ActiveRecord::ReadOnlyRecord
|
|
675
714
|
def readonly!
|
|
676
715
|
@readonly = true
|
|
677
716
|
end
|