activerecord 7.2.1.1 → 8.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|