activerecord 7.2.1.1 → 8.0.0.rc1
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 +220 -756
- 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 +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +4 -4
- 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/primary_key.rb +2 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -12
- data/lib/active_record/attributes.rb +1 -2
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/callbacks.rb +1 -1
- 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 +26 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -4
- 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 +34 -7
- 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 +28 -42
- 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/cidr.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 +0 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +54 -14
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +45 -97
- 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 +28 -18
- 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 +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/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 +54 -75
- 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 +4 -4
- 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/marshalling.rb +4 -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 -38
- data/lib/active_record/model_schema.rb +4 -6
- data/lib/active_record/nested_attributes.rb +11 -2
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_cache.rb +0 -4
- 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 +9 -38
- data/lib/active_record/railties/databases.rake +1 -1
- data/lib/active_record/reflection.rb +23 -23
- 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 +41 -40
- 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 +14 -1
- data/lib/active_record/relation/query_methods.rb +122 -71
- 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 +40 -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/testing/query_assertions.rb +2 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +9 -8
- data/lib/active_record.rb +15 -45
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/sqlite.rb +25 -0
- metadata +10 -11
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -68,19 +68,26 @@ module ActiveRecord
|
|
68
68
|
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
|
69
69
|
include SignedId::RelationMethods, TokenFor::RelationMethods
|
70
70
|
|
71
|
-
attr_reader :table, :
|
71
|
+
attr_reader :table, :model, :loaded, :predicate_builder
|
72
72
|
attr_accessor :skip_preloading_value
|
73
|
-
alias :
|
73
|
+
alias :klass :model
|
74
74
|
alias :loaded? :loaded
|
75
75
|
alias :locked? :lock_value
|
76
76
|
|
77
|
-
def initialize(
|
78
|
-
|
77
|
+
def initialize(model, table: nil, predicate_builder: nil, values: {})
|
78
|
+
if table
|
79
|
+
predicate_builder ||= model.predicate_builder.with(TableMetadata.new(model, table))
|
80
|
+
else
|
81
|
+
table = model.arel_table
|
82
|
+
predicate_builder ||= model.predicate_builder
|
83
|
+
end
|
84
|
+
|
85
|
+
@model = model
|
79
86
|
@table = table
|
80
87
|
@values = values
|
81
88
|
@loaded = false
|
82
89
|
@predicate_builder = predicate_builder
|
83
|
-
@
|
90
|
+
@delegate_to_model = false
|
84
91
|
@future_result = nil
|
85
92
|
@records = nil
|
86
93
|
@async = false
|
@@ -93,7 +100,7 @@ module ActiveRecord
|
|
93
100
|
end
|
94
101
|
|
95
102
|
def bind_attribute(name, value) # :nodoc:
|
96
|
-
if reflection =
|
103
|
+
if reflection = model._reflect_on_association(name)
|
97
104
|
name = reflection.foreign_key
|
98
105
|
value = value.read_attribute(reflection.association_primary_key) unless value.nil?
|
99
106
|
end
|
@@ -430,14 +437,14 @@ module ActiveRecord
|
|
430
437
|
# Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
|
431
438
|
def cache_key(timestamp_column = "updated_at")
|
432
439
|
@cache_keys ||= {}
|
433
|
-
@cache_keys[timestamp_column] ||=
|
440
|
+
@cache_keys[timestamp_column] ||= model.collection_cache_key(self, timestamp_column)
|
434
441
|
end
|
435
442
|
|
436
443
|
def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
|
437
444
|
query_signature = ActiveSupport::Digest.hexdigest(to_sql)
|
438
|
-
key = "#{
|
445
|
+
key = "#{model.model_name.cache_key}/query-#{query_signature}"
|
439
446
|
|
440
|
-
if collection_cache_versioning
|
447
|
+
if model.collection_cache_versioning
|
441
448
|
key
|
442
449
|
else
|
443
450
|
"#{key}-#{compute_cache_version(timestamp_column)}"
|
@@ -456,7 +463,7 @@ module ActiveRecord
|
|
456
463
|
#
|
457
464
|
# SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
|
458
465
|
def cache_version(timestamp_column = :updated_at)
|
459
|
-
if collection_cache_versioning
|
466
|
+
if model.collection_cache_versioning
|
460
467
|
@cache_versions ||= {}
|
461
468
|
@cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
|
462
469
|
end
|
@@ -475,7 +482,7 @@ module ActiveRecord
|
|
475
482
|
|
476
483
|
with_connection do |c|
|
477
484
|
column = c.visitor.compile(table[timestamp_column])
|
478
|
-
select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
|
485
|
+
select_values = "COUNT(*) AS #{model.adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
|
479
486
|
|
480
487
|
if collection.has_limit_or_offset?
|
481
488
|
query = collection.select("#{column} AS collection_cache_key_timestamp")
|
@@ -492,7 +499,7 @@ module ActiveRecord
|
|
492
499
|
size, timestamp = c.select_rows(arel, nil).first
|
493
500
|
|
494
501
|
if size
|
495
|
-
column_type =
|
502
|
+
column_type = model.type_for_attribute(timestamp_column)
|
496
503
|
timestamp = column_type.deserialize(timestamp)
|
497
504
|
else
|
498
505
|
size = 0
|
@@ -501,7 +508,7 @@ module ActiveRecord
|
|
501
508
|
end
|
502
509
|
|
503
510
|
if timestamp
|
504
|
-
"#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
|
511
|
+
"#{size}-#{timestamp.utc.to_fs(model.cache_timestamp_format)}"
|
505
512
|
else
|
506
513
|
"#{size}"
|
507
514
|
end
|
@@ -532,7 +539,7 @@ module ActiveRecord
|
|
532
539
|
# Please check unscoped if you want to remove all previous scopes (including
|
533
540
|
# the default_scope) during the execution of a block.
|
534
541
|
def scoping(all_queries: nil, &block)
|
535
|
-
registry =
|
542
|
+
registry = model.scope_registry
|
536
543
|
if global_scope?(registry) && all_queries == false
|
537
544
|
raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
|
538
545
|
elsif already_in_scope?(registry)
|
@@ -543,11 +550,11 @@ module ActiveRecord
|
|
543
550
|
end
|
544
551
|
|
545
552
|
def _exec_scope(...) # :nodoc:
|
546
|
-
@
|
547
|
-
registry =
|
553
|
+
@delegate_to_model = true
|
554
|
+
registry = model.scope_registry
|
548
555
|
_scoping(nil, registry) { instance_exec(...) || self }
|
549
556
|
ensure
|
550
|
-
@
|
557
|
+
@delegate_to_model = false
|
551
558
|
end
|
552
559
|
|
553
560
|
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
@@ -584,30 +591,30 @@ module ActiveRecord
|
|
584
591
|
return 0 if @none
|
585
592
|
|
586
593
|
if updates.is_a?(Hash)
|
587
|
-
if
|
588
|
-
!updates.key?(
|
589
|
-
!updates.key?(
|
590
|
-
attr = table[
|
594
|
+
if model.locking_enabled? &&
|
595
|
+
!updates.key?(model.locking_column) &&
|
596
|
+
!updates.key?(model.locking_column.to_sym)
|
597
|
+
attr = table[model.locking_column]
|
591
598
|
updates[attr.name] = _increment_attribute(attr)
|
592
599
|
end
|
593
600
|
values = _substitute_values(updates)
|
594
601
|
else
|
595
|
-
values = Arel.sql(
|
602
|
+
values = Arel.sql(model.sanitize_sql_for_assignment(updates, table.name))
|
596
603
|
end
|
597
604
|
|
598
|
-
|
605
|
+
model.with_connection do |c|
|
599
606
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
600
607
|
arel.source.left = table
|
601
608
|
|
602
609
|
group_values_arel_columns = arel_columns(group_values.uniq)
|
603
610
|
having_clause_ast = having_clause.ast unless having_clause.empty?
|
604
|
-
key = if
|
611
|
+
key = if model.composite_primary_key?
|
605
612
|
primary_key.map { |pk| table[pk] }
|
606
613
|
else
|
607
614
|
table[primary_key]
|
608
615
|
end
|
609
616
|
stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
|
610
|
-
c.update(stmt, "#{
|
617
|
+
c.update(stmt, "#{model} Update All").tap { reset }
|
611
618
|
end
|
612
619
|
end
|
613
620
|
|
@@ -615,7 +622,7 @@ module ActiveRecord
|
|
615
622
|
if id == :all
|
616
623
|
each { |record| record.update(attributes) }
|
617
624
|
else
|
618
|
-
|
625
|
+
model.update(id, attributes)
|
619
626
|
end
|
620
627
|
end
|
621
628
|
|
@@ -623,7 +630,7 @@ module ActiveRecord
|
|
623
630
|
if id == :all
|
624
631
|
each { |record| record.update!(attributes) }
|
625
632
|
else
|
626
|
-
|
633
|
+
model.update!(id, attributes)
|
627
634
|
end
|
628
635
|
end
|
629
636
|
|
@@ -929,7 +936,7 @@ module ActiveRecord
|
|
929
936
|
names = touch if touch != true
|
930
937
|
names = Array.wrap(names)
|
931
938
|
options = names.extract_options!
|
932
|
-
touch_updates =
|
939
|
+
touch_updates = model.touch_attributes_with_time(*names, **options)
|
933
940
|
updates.merge!(touch_updates) unless touch_updates.empty?
|
934
941
|
end
|
935
942
|
|
@@ -960,7 +967,7 @@ module ActiveRecord
|
|
960
967
|
# Person.where(name: 'David').touch_all
|
961
968
|
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
|
962
969
|
def touch_all(*names, time: nil)
|
963
|
-
update_all
|
970
|
+
update_all model.touch_attributes_with_time(*names, time: time)
|
964
971
|
end
|
965
972
|
|
966
973
|
# Destroys the records by instantiating each
|
@@ -1012,20 +1019,20 @@ module ActiveRecord
|
|
1012
1019
|
raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
|
1013
1020
|
end
|
1014
1021
|
|
1015
|
-
|
1022
|
+
model.with_connection do |c|
|
1016
1023
|
arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
|
1017
1024
|
arel.source.left = table
|
1018
1025
|
|
1019
1026
|
group_values_arel_columns = arel_columns(group_values.uniq)
|
1020
1027
|
having_clause_ast = having_clause.ast unless having_clause.empty?
|
1021
|
-
key = if
|
1028
|
+
key = if model.composite_primary_key?
|
1022
1029
|
primary_key.map { |pk| table[pk] }
|
1023
1030
|
else
|
1024
1031
|
table[primary_key]
|
1025
1032
|
end
|
1026
1033
|
stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
|
1027
1034
|
|
1028
|
-
c.delete(stmt, "#{
|
1035
|
+
c.delete(stmt, "#{model} Delete All").tap { reset }
|
1029
1036
|
end
|
1030
1037
|
end
|
1031
1038
|
|
@@ -1124,9 +1131,6 @@ module ActiveRecord
|
|
1124
1131
|
# for queries to actually be executed concurrently. Otherwise it defaults to
|
1125
1132
|
# executing them in the foreground.
|
1126
1133
|
#
|
1127
|
-
# +load_async+ will also fall back to executing in the foreground in the test environment when transactional
|
1128
|
-
# fixtures are enabled.
|
1129
|
-
#
|
1130
1134
|
# If the query was actually executed in the background, the Active Record logs will show
|
1131
1135
|
# it by prefixing the log line with <tt>ASYNC</tt>:
|
1132
1136
|
#
|
@@ -1136,7 +1140,7 @@ module ActiveRecord
|
|
1136
1140
|
return load if !c.async_enabled?
|
1137
1141
|
|
1138
1142
|
unless loaded?
|
1139
|
-
result = exec_main_query(async: c.current_transaction.
|
1143
|
+
result = exec_main_query(async: !c.current_transaction.joinable?)
|
1140
1144
|
|
1141
1145
|
if result.is_a?(Array)
|
1142
1146
|
@records = result
|
@@ -1150,6 +1154,16 @@ module ActiveRecord
|
|
1150
1154
|
self
|
1151
1155
|
end
|
1152
1156
|
|
1157
|
+
def then(&block) # :nodoc:
|
1158
|
+
if @future_result
|
1159
|
+
@future_result.then do
|
1160
|
+
yield self
|
1161
|
+
end
|
1162
|
+
else
|
1163
|
+
super
|
1164
|
+
end
|
1165
|
+
end
|
1166
|
+
|
1153
1167
|
# Returns <tt>true</tt> if the relation was scheduled on the background
|
1154
1168
|
# thread pool.
|
1155
1169
|
def scheduled?
|
@@ -1180,7 +1194,7 @@ module ActiveRecord
|
|
1180
1194
|
def reset
|
1181
1195
|
@future_result&.cancel
|
1182
1196
|
@future_result = nil
|
1183
|
-
@
|
1197
|
+
@delegate_to_model = false
|
1184
1198
|
@to_sql = @arel = @loaded = @should_eager_load = nil
|
1185
1199
|
@offsets = @take = nil
|
1186
1200
|
@cache_keys = nil
|
@@ -1200,7 +1214,7 @@ module ActiveRecord
|
|
1200
1214
|
relation.to_sql
|
1201
1215
|
end
|
1202
1216
|
else
|
1203
|
-
|
1217
|
+
model.with_connection do |conn|
|
1204
1218
|
conn.unprepared_statement { conn.to_sql(arel) }
|
1205
1219
|
end
|
1206
1220
|
end
|
@@ -1210,12 +1224,12 @@ module ActiveRecord
|
|
1210
1224
|
#
|
1211
1225
|
# User.where(name: 'Oscar').where_values_hash
|
1212
1226
|
# # => {name: "Oscar"}
|
1213
|
-
def where_values_hash(relation_table_name =
|
1227
|
+
def where_values_hash(relation_table_name = model.table_name) # :nodoc:
|
1214
1228
|
where_clause.to_h(relation_table_name)
|
1215
1229
|
end
|
1216
1230
|
|
1217
1231
|
def scope_for_create
|
1218
|
-
hash = where_clause.to_h(
|
1232
|
+
hash = where_clause.to_h(model.table_name, equality_only: true)
|
1219
1233
|
create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
|
1220
1234
|
hash
|
1221
1235
|
end
|
@@ -1261,6 +1275,10 @@ module ActiveRecord
|
|
1261
1275
|
records.blank?
|
1262
1276
|
end
|
1263
1277
|
|
1278
|
+
def readonly?
|
1279
|
+
readonly_value
|
1280
|
+
end
|
1281
|
+
|
1264
1282
|
def values
|
1265
1283
|
@values.dup
|
1266
1284
|
end
|
@@ -1279,7 +1297,7 @@ module ActiveRecord
|
|
1279
1297
|
end
|
1280
1298
|
|
1281
1299
|
def empty_scope? # :nodoc:
|
1282
|
-
@values ==
|
1300
|
+
@values == model.unscoped.values
|
1283
1301
|
end
|
1284
1302
|
|
1285
1303
|
def has_limit_or_offset? # :nodoc:
|
@@ -1287,7 +1305,7 @@ module ActiveRecord
|
|
1287
1305
|
end
|
1288
1306
|
|
1289
1307
|
def alias_tracker(joins = [], aliases = nil) # :nodoc:
|
1290
|
-
ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
|
1308
|
+
ActiveRecord::Associations::AliasTracker.create(model.connection_pool, table.name, joins, aliases)
|
1291
1309
|
end
|
1292
1310
|
|
1293
1311
|
class StrictLoadingScope # :nodoc:
|
@@ -1317,46 +1335,46 @@ module ActiveRecord
|
|
1317
1335
|
|
1318
1336
|
private
|
1319
1337
|
def already_in_scope?(registry)
|
1320
|
-
@
|
1338
|
+
@delegate_to_model && registry.current_scope(model, true)
|
1321
1339
|
end
|
1322
1340
|
|
1323
1341
|
def global_scope?(registry)
|
1324
|
-
registry.global_current_scope(
|
1342
|
+
registry.global_current_scope(model, true)
|
1325
1343
|
end
|
1326
1344
|
|
1327
1345
|
def current_scope_restoring_block(&block)
|
1328
|
-
current_scope =
|
1346
|
+
current_scope = model.current_scope(true)
|
1329
1347
|
-> record do
|
1330
|
-
|
1348
|
+
model.current_scope = current_scope
|
1331
1349
|
yield record if block_given?
|
1332
1350
|
end
|
1333
1351
|
end
|
1334
1352
|
|
1335
1353
|
def _new(attributes, &block)
|
1336
|
-
|
1354
|
+
model.new(attributes, &block)
|
1337
1355
|
end
|
1338
1356
|
|
1339
1357
|
def _create(attributes, &block)
|
1340
|
-
|
1358
|
+
model.create(attributes, &block)
|
1341
1359
|
end
|
1342
1360
|
|
1343
1361
|
def _create!(attributes, &block)
|
1344
|
-
|
1362
|
+
model.create!(attributes, &block)
|
1345
1363
|
end
|
1346
1364
|
|
1347
1365
|
def _scoping(scope, registry, all_queries = false)
|
1348
|
-
previous = registry.current_scope(
|
1349
|
-
registry.set_current_scope(
|
1366
|
+
previous = registry.current_scope(model, true)
|
1367
|
+
registry.set_current_scope(model, scope)
|
1350
1368
|
|
1351
1369
|
if all_queries
|
1352
|
-
previous_global = registry.global_current_scope(
|
1353
|
-
registry.set_global_current_scope(
|
1370
|
+
previous_global = registry.global_current_scope(model, true)
|
1371
|
+
registry.set_global_current_scope(model, scope)
|
1354
1372
|
end
|
1355
1373
|
yield
|
1356
1374
|
ensure
|
1357
|
-
registry.set_current_scope(
|
1375
|
+
registry.set_current_scope(model, previous)
|
1358
1376
|
if all_queries
|
1359
|
-
registry.set_global_current_scope(
|
1377
|
+
registry.set_global_current_scope(model, previous_global)
|
1360
1378
|
end
|
1361
1379
|
end
|
1362
1380
|
|
@@ -1368,7 +1386,7 @@ module ActiveRecord
|
|
1368
1386
|
value = Arel::Nodes::Grouping.new(value)
|
1369
1387
|
end
|
1370
1388
|
else
|
1371
|
-
type =
|
1389
|
+
type = model.type_for_attribute(attr.name)
|
1372
1390
|
value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
|
1373
1391
|
end
|
1374
1392
|
[attr, value]
|
@@ -1415,7 +1433,7 @@ module ActiveRecord
|
|
1415
1433
|
if where_clause.contradiction?
|
1416
1434
|
[].freeze
|
1417
1435
|
elsif eager_loading?
|
1418
|
-
|
1436
|
+
model.with_connection do |c|
|
1419
1437
|
apply_join_dependency do |relation, join_dependency|
|
1420
1438
|
if relation.null_relation?
|
1421
1439
|
[].freeze
|
@@ -1427,8 +1445,8 @@ module ActiveRecord
|
|
1427
1445
|
end
|
1428
1446
|
end
|
1429
1447
|
else
|
1430
|
-
|
1431
|
-
|
1448
|
+
model.with_connection do |c|
|
1449
|
+
model._query_by_sql(c, arel, async: async)
|
1432
1450
|
end
|
1433
1451
|
end
|
1434
1452
|
end
|
@@ -1441,13 +1459,13 @@ module ActiveRecord
|
|
1441
1459
|
@_join_dependency = nil
|
1442
1460
|
records
|
1443
1461
|
else
|
1444
|
-
|
1462
|
+
model._load_from_sql(rows, &block).freeze
|
1445
1463
|
end
|
1446
1464
|
end
|
1447
1465
|
|
1448
1466
|
def skip_query_cache_if_necessary(&block)
|
1449
1467
|
if skip_query_cache_value
|
1450
|
-
uncached(&block)
|
1468
|
+
model.uncached(&block)
|
1451
1469
|
else
|
1452
1470
|
yield
|
1453
1471
|
end
|
data/lib/active_record/result.rb
CHANGED
@@ -36,6 +36,59 @@ module ActiveRecord
|
|
36
36
|
class Result
|
37
37
|
include Enumerable
|
38
38
|
|
39
|
+
class IndexedRow
|
40
|
+
def initialize(column_indexes, row)
|
41
|
+
@column_indexes = column_indexes
|
42
|
+
@row = row
|
43
|
+
end
|
44
|
+
|
45
|
+
def size
|
46
|
+
@column_indexes.size
|
47
|
+
end
|
48
|
+
alias_method :length, :size
|
49
|
+
|
50
|
+
def each_key(&block)
|
51
|
+
@column_indexes.each_key(&block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def keys
|
55
|
+
@column_indexes.keys
|
56
|
+
end
|
57
|
+
|
58
|
+
def ==(other)
|
59
|
+
if other.is_a?(Hash)
|
60
|
+
to_hash == other
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def key?(column)
|
67
|
+
@column_indexes.key?(column)
|
68
|
+
end
|
69
|
+
|
70
|
+
def fetch(column)
|
71
|
+
if index = @column_indexes[column]
|
72
|
+
@row[index]
|
73
|
+
elsif block_given?
|
74
|
+
yield
|
75
|
+
else
|
76
|
+
raise KeyError, "key not found: #{column.inspect}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def [](column)
|
81
|
+
if index = @column_indexes[column]
|
82
|
+
@row[index]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_h
|
87
|
+
@column_indexes.transform_values { |index| @row[index] }
|
88
|
+
end
|
89
|
+
alias_method :to_hash, :to_h
|
90
|
+
end
|
91
|
+
|
39
92
|
attr_reader :columns, :rows, :column_types
|
40
93
|
|
41
94
|
def self.empty(async: false) # :nodoc:
|
@@ -67,7 +120,9 @@ module ActiveRecord
|
|
67
120
|
end
|
68
121
|
|
69
122
|
# Calls the given block once for each element in row collection, passing
|
70
|
-
# row as parameter.
|
123
|
+
# row as parameter. Each row is a Hash-like, read only object.
|
124
|
+
#
|
125
|
+
# To get real hashes, use +.to_a.each+.
|
71
126
|
#
|
72
127
|
# Returns an +Enumerator+ if no block is given.
|
73
128
|
def each(&block)
|
@@ -134,14 +189,14 @@ module ActiveRecord
|
|
134
189
|
end
|
135
190
|
|
136
191
|
def initialize_copy(other)
|
137
|
-
@
|
138
|
-
@rows = rows.dup
|
192
|
+
@rows = rows.dup
|
139
193
|
@column_types = column_types.dup
|
140
194
|
@hash_rows = nil
|
141
195
|
end
|
142
196
|
|
143
197
|
def freeze # :nodoc:
|
144
198
|
hash_rows.freeze
|
199
|
+
indexed_rows.freeze
|
145
200
|
super
|
146
201
|
end
|
147
202
|
|
@@ -154,7 +209,14 @@ module ActiveRecord
|
|
154
209
|
hash[columns[index]] = index
|
155
210
|
index += 1
|
156
211
|
end
|
157
|
-
hash
|
212
|
+
hash.freeze
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def indexed_rows # :nodoc:
|
217
|
+
@indexed_rows ||= begin
|
218
|
+
columns = column_indexes
|
219
|
+
@rows.map { |row| IndexedRow.new(columns, row) }.freeze
|
158
220
|
end
|
159
221
|
end
|
160
222
|
|
@@ -105,12 +105,13 @@ module ActiveRecord
|
|
105
105
|
# sanitize_sql_hash_for_assignment({ status: nil, group_id: 1 }, "posts")
|
106
106
|
# # => "`posts`.`status` = NULL, `posts`.`group_id` = 1"
|
107
107
|
def sanitize_sql_hash_for_assignment(attrs, table)
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
108
|
+
with_connection do |c|
|
109
|
+
attrs.map do |attr, value|
|
110
|
+
type = type_for_attribute(attr)
|
111
|
+
value = type.serialize(type.cast(value))
|
112
|
+
"#{c.quote_table_name_for_assignment(table, attr)} = #{c.quote(value)}"
|
113
|
+
end.join(", ")
|
114
|
+
end
|
114
115
|
end
|
115
116
|
|
116
117
|
# Sanitizes a +string+ so that it is safe to use within an SQL
|
@@ -63,6 +63,7 @@ module ActiveRecord
|
|
63
63
|
extensions(stream)
|
64
64
|
types(stream)
|
65
65
|
tables(stream)
|
66
|
+
virtual_tables(stream)
|
66
67
|
trailer(stream)
|
67
68
|
stream
|
68
69
|
end
|
@@ -126,6 +127,10 @@ module ActiveRecord
|
|
126
127
|
def schemas(stream)
|
127
128
|
end
|
128
129
|
|
130
|
+
# virtual tables are only supported by SQLite
|
131
|
+
def virtual_tables(stream)
|
132
|
+
end
|
133
|
+
|
129
134
|
def tables(stream)
|
130
135
|
sorted_tables = @connection.tables.sort
|
131
136
|
|
@@ -34,7 +34,8 @@ module ActiveRecord
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def delete_all_versions
|
37
|
-
|
37
|
+
# Eagerly check in connection to avoid checking in/out many times in the called method.
|
38
|
+
@pool.with_connection do
|
38
39
|
versions.each do |version|
|
39
40
|
delete_version(version)
|
40
41
|
end
|
@@ -23,7 +23,7 @@ module ActiveRecord
|
|
23
23
|
scope = current_scope
|
24
24
|
|
25
25
|
if scope
|
26
|
-
if self == scope.
|
26
|
+
if self == scope.model
|
27
27
|
scope.clone
|
28
28
|
else
|
29
29
|
relation.merge!(scope)
|
@@ -191,7 +191,10 @@ module ActiveRecord
|
|
191
191
|
private
|
192
192
|
def singleton_method_added(name)
|
193
193
|
super
|
194
|
-
|
194
|
+
# Most Kernel extends are both singleton and instance methods so
|
195
|
+
# respond_to is a fast check, but we don't want to define methods
|
196
|
+
# only on the module (ex. Module#name)
|
197
|
+
generate_relation_method(name) if Kernel.respond_to?(name) && (Kernel.method_defined?(name) || Kernel.private_method_defined?(name)) && !ActiveRecord::Relation.method_defined?(name)
|
195
198
|
end
|
196
199
|
end
|
197
200
|
end
|
@@ -133,23 +133,26 @@ module ActiveRecord
|
|
133
133
|
relation = (callable || block).call Params.new
|
134
134
|
query_builder, binds = connection.cacheable_query(self, relation.arel)
|
135
135
|
bind_map = BindMap.new(binds)
|
136
|
-
new(query_builder, bind_map, relation.
|
136
|
+
new(query_builder, bind_map, relation.model)
|
137
137
|
end
|
138
138
|
|
139
|
-
def initialize(query_builder, bind_map,
|
139
|
+
def initialize(query_builder, bind_map, model)
|
140
140
|
@query_builder = query_builder
|
141
141
|
@bind_map = bind_map
|
142
|
-
@
|
142
|
+
@model = model
|
143
143
|
end
|
144
144
|
|
145
|
-
def execute(params, connection, allow_retry: false, &block)
|
146
|
-
bind_values = bind_map.bind params
|
145
|
+
def execute(params, connection, allow_retry: false, async: false, &block)
|
146
|
+
bind_values = @bind_map.bind params
|
147
|
+
sql = @query_builder.sql_for bind_values, connection
|
147
148
|
|
148
|
-
|
149
|
-
|
150
|
-
|
149
|
+
if async
|
150
|
+
@model.async_find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
|
151
|
+
else
|
152
|
+
@model.find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
|
153
|
+
end
|
151
154
|
rescue ::RangeError
|
152
|
-
[]
|
155
|
+
async ? Promise.wrap([]) : []
|
153
156
|
end
|
154
157
|
|
155
158
|
def self.unsupported_value?(value)
|
@@ -157,8 +160,5 @@ module ActiveRecord
|
|
157
160
|
when NilClass, Array, Range, Hash, Relation, Base then true
|
158
161
|
end
|
159
162
|
end
|
160
|
-
|
161
|
-
private
|
162
|
-
attr_reader :query_builder, :bind_map, :klass
|
163
163
|
end
|
164
164
|
end
|
data/lib/active_record/store.rb
CHANGED
@@ -25,8 +25,8 @@ module ActiveRecord
|
|
25
25
|
# You can set custom coder to encode/decode your serialized attributes to/from different formats.
|
26
26
|
# JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
|
27
27
|
#
|
28
|
-
# NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+,
|
29
|
-
# +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
|
28
|
+
# NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+, MySQL 5.7+
|
29
|
+
# +json+, or SQLite 3.38+ +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
|
30
30
|
# Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
|
31
31
|
# the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
|
32
32
|
# using a symbol.
|
@@ -217,7 +217,11 @@ module ActiveRecord
|
|
217
217
|
end
|
218
218
|
|
219
219
|
def store_accessor_for(store_attribute)
|
220
|
-
type_for_attribute(store_attribute).
|
220
|
+
type_for_attribute(store_attribute).tap do |type|
|
221
|
+
unless type.respond_to?(:accessor)
|
222
|
+
raise ConfigurationError, "the column '#{store_attribute}' has not been configured as a store. Please make sure the column is declared serializable via 'ActiveRecord.store' or, if your database supports it, use a structured column type like hstore or json."
|
223
|
+
end
|
224
|
+
end.accessor
|
221
225
|
end
|
222
226
|
|
223
227
|
class HashAccessor # :nodoc:
|
@@ -69,9 +69,7 @@ module ActiveRecord
|
|
69
69
|
|
70
70
|
def predicate_builder
|
71
71
|
if klass
|
72
|
-
|
73
|
-
predicate_builder.instance_variable_set(:@table, self)
|
74
|
-
predicate_builder
|
72
|
+
klass.predicate_builder.with(self)
|
75
73
|
else
|
76
74
|
PredicateBuilder.new(self)
|
77
75
|
end
|