activerecord 8.0.3 → 8.1.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +427 -522
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/join_dependency.rb +2 -0
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations.rb +159 -21
- data/lib/active_record/attribute_methods/serialization.rb +16 -3
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +1 -1
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +15 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +382 -51
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +26 -30
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -18
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -22
- data/lib/active_record/connection_adapters/abstract/transaction.rb +16 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +66 -14
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -11
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -23
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +65 -30
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +74 -38
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +10 -5
- data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +37 -25
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -30
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/core.rb +5 -4
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +50 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +24 -8
- data/lib/active_record/errors.rb +23 -7
- data/lib/active_record/explain_registry.rb +0 -1
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +12 -7
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +1 -5
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +14 -1
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +26 -16
- data/lib/active_record/model_schema.rb +10 -7
- data/lib/active_record/nested_attributes.rb +2 -0
- data/lib/active_record/persistence.rb +34 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +3 -7
- data/lib/active_record/railtie.rb +32 -3
- data/lib/active_record/railties/databases.rake +16 -4
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +42 -3
- data/lib/active_record/relation/batches.rb +26 -12
- data/lib/active_record/relation/calculations.rb +20 -9
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +27 -11
- data/lib/active_record/relation/merger.rb +2 -2
- data/lib/active_record/relation/predicate_builder.rb +2 -2
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +39 -29
- data/lib/active_record/relation/where_clause.rb +1 -10
- data/lib/active_record/relation.rb +25 -13
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema_dumper.rb +12 -10
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +43 -15
- data/lib/active_record/statement_cache.rb +13 -9
- data/lib/active_record/store.rb +44 -19
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +2 -21
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +10 -2
- data/lib/active_record/test_fixtures.rb +27 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +32 -10
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +11 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record.rb +65 -3
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +3 -21
- data/lib/arel.rb +3 -1
- metadata +13 -9
- data/lib/active_record/normalization.rb +0 -163
@@ -425,10 +425,14 @@ module ActiveRecord
|
|
425
425
|
|
426
426
|
def _klass(class_name) # :nodoc:
|
427
427
|
if active_record.name.demodulize == class_name
|
428
|
-
|
428
|
+
begin
|
429
|
+
compute_class("::#{class_name}")
|
430
|
+
rescue NameError
|
431
|
+
compute_class(class_name)
|
432
|
+
end
|
433
|
+
else
|
434
|
+
compute_class(class_name)
|
429
435
|
end
|
430
|
-
|
431
|
-
compute_class(class_name)
|
432
436
|
end
|
433
437
|
|
434
438
|
def compute_class(name)
|
@@ -516,6 +520,8 @@ module ActiveRecord
|
|
516
520
|
|
517
521
|
def initialize(name, scope, options, active_record)
|
518
522
|
super
|
523
|
+
|
524
|
+
@validated = false
|
519
525
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
520
526
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
521
527
|
@join_table = nil
|
@@ -534,6 +540,8 @@ module ActiveRecord
|
|
534
540
|
options[:query_constraints] = options.delete(:foreign_key)
|
535
541
|
end
|
536
542
|
|
543
|
+
@deprecated = !!options[:deprecated]
|
544
|
+
|
537
545
|
ensure_option_not_given_as_class!(:class_name)
|
538
546
|
end
|
539
547
|
|
@@ -616,6 +624,8 @@ module ActiveRecord
|
|
616
624
|
end
|
617
625
|
|
618
626
|
def check_validity!
|
627
|
+
return if @validated
|
628
|
+
|
619
629
|
check_validity_of_inverse!
|
620
630
|
|
621
631
|
if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
|
@@ -625,6 +635,8 @@ module ActiveRecord
|
|
625
635
|
raise CompositePrimaryKeyMismatchError.new(self)
|
626
636
|
end
|
627
637
|
end
|
638
|
+
|
639
|
+
@validated = true
|
628
640
|
end
|
629
641
|
|
630
642
|
def check_eager_loadable!
|
@@ -742,6 +754,10 @@ module ActiveRecord
|
|
742
754
|
Array(options[:extend])
|
743
755
|
end
|
744
756
|
|
757
|
+
def deprecated?
|
758
|
+
@deprecated
|
759
|
+
end
|
760
|
+
|
745
761
|
private
|
746
762
|
# Attempts to find the inverse association name automatically.
|
747
763
|
# If it cannot find a suitable inverse association name, it returns
|
@@ -975,6 +991,8 @@ module ActiveRecord
|
|
975
991
|
|
976
992
|
def initialize(delegate_reflection)
|
977
993
|
super()
|
994
|
+
|
995
|
+
@validated = false
|
978
996
|
@delegate_reflection = delegate_reflection
|
979
997
|
@klass = delegate_reflection.options[:anonymous_class]
|
980
998
|
@source_reflection_name = delegate_reflection.options[:source]
|
@@ -1138,6 +1156,8 @@ module ActiveRecord
|
|
1138
1156
|
end
|
1139
1157
|
|
1140
1158
|
def check_validity!
|
1159
|
+
return if @validated
|
1160
|
+
|
1141
1161
|
if through_reflection.nil?
|
1142
1162
|
raise HasManyThroughAssociationNotFoundError.new(active_record, self)
|
1143
1163
|
end
|
@@ -1175,6 +1195,8 @@ module ActiveRecord
|
|
1175
1195
|
end
|
1176
1196
|
|
1177
1197
|
check_validity_of_inverse!
|
1198
|
+
|
1199
|
+
@validated = true
|
1178
1200
|
end
|
1179
1201
|
|
1180
1202
|
def constraints
|
@@ -1195,6 +1217,10 @@ module ActiveRecord
|
|
1195
1217
|
collect_join_reflections(seed + [self])
|
1196
1218
|
end
|
1197
1219
|
|
1220
|
+
def deprecated_nested_reflections
|
1221
|
+
@deprecated_nested_reflections ||= collect_deprecated_nested_reflections
|
1222
|
+
end
|
1223
|
+
|
1198
1224
|
protected
|
1199
1225
|
def actual_source_reflection # FIXME: this is a horrible name
|
1200
1226
|
source_reflection.actual_source_reflection
|
@@ -1219,6 +1245,19 @@ module ActiveRecord
|
|
1219
1245
|
options[:source_type] || source_reflection.class_name
|
1220
1246
|
end
|
1221
1247
|
|
1248
|
+
def collect_deprecated_nested_reflections
|
1249
|
+
result = []
|
1250
|
+
[through_reflection, source_reflection].each do |reflection|
|
1251
|
+
result << reflection if reflection.deprecated?
|
1252
|
+
# Both the through and the source reflections could be through
|
1253
|
+
# themselves. Nesting can go an arbitrary number of levels down.
|
1254
|
+
if reflection.through_reflection?
|
1255
|
+
result.concat(reflection.deprecated_nested_reflections)
|
1256
|
+
end
|
1257
|
+
end
|
1258
|
+
result
|
1259
|
+
end
|
1260
|
+
|
1222
1261
|
delegate_methods = AssociationReflection.public_instance_methods -
|
1223
1262
|
public_instance_methods
|
1224
1263
|
|
@@ -435,35 +435,49 @@ module ActiveRecord
|
|
435
435
|
if load
|
436
436
|
records = batch_relation.records
|
437
437
|
values = records.pluck(*cursor)
|
438
|
-
|
438
|
+
values_size = values.size
|
439
|
+
values_last = values.last
|
440
|
+
yielded_relation = where(cursor => values).order(batch_orders.to_h)
|
439
441
|
yielded_relation.load_records(records)
|
440
442
|
elsif (empty_scope && use_ranges != false) || use_ranges
|
441
|
-
|
443
|
+
# Efficiently peak at the last value for the next batch using offset and limit.
|
444
|
+
values_size = batch_limit
|
445
|
+
values_last = batch_relation.offset(batch_limit - 1).pick(*cursor)
|
446
|
+
|
447
|
+
# If the last value is not found using offset, there is at most one more batch of size < batch_limit.
|
448
|
+
# Retry by getting the whole list of remaining values so that we have the exact size and last value.
|
449
|
+
unless values_last
|
450
|
+
values = batch_relation.pluck(*cursor)
|
451
|
+
values_size = values.size
|
452
|
+
values_last = values.last
|
453
|
+
end
|
442
454
|
|
443
|
-
|
444
|
-
if
|
445
|
-
yielded_relation = apply_finish_limit(batch_relation, cursor,
|
446
|
-
yielded_relation = yielded_relation.except(:limit
|
455
|
+
# Finally, build the yielded relation if at least one value found.
|
456
|
+
if values_last
|
457
|
+
yielded_relation = apply_finish_limit(batch_relation, cursor, values_last, batch_orders)
|
458
|
+
yielded_relation = yielded_relation.except(:limit).reorder(batch_orders.to_h)
|
447
459
|
yielded_relation.skip_query_cache!(false)
|
448
460
|
end
|
449
461
|
else
|
450
462
|
values = batch_relation.pluck(*cursor)
|
451
|
-
|
463
|
+
values_size = values.size
|
464
|
+
values_last = values.last
|
465
|
+
yielded_relation = where(cursor => values).order(batch_orders.to_h)
|
452
466
|
end
|
453
467
|
|
454
|
-
break if
|
468
|
+
break if values_size == 0
|
455
469
|
|
456
|
-
if
|
470
|
+
if [values_last].flatten.any?(nil)
|
457
471
|
raise ArgumentError, "Not all of the batch cursor columns were included in the custom select clause "\
|
458
472
|
"or some columns contain nil."
|
459
473
|
end
|
460
474
|
|
461
475
|
yield yielded_relation
|
462
476
|
|
463
|
-
break if
|
477
|
+
break if values_size < batch_limit
|
464
478
|
|
465
479
|
if limit_value
|
466
|
-
remaining -=
|
480
|
+
remaining -= values_size
|
467
481
|
|
468
482
|
if remaining == 0
|
469
483
|
# Saves a useless iteration when the limit is a multiple of the
|
@@ -481,7 +495,7 @@ module ActiveRecord
|
|
481
495
|
end
|
482
496
|
operators << (last_order == :desc ? :lt : :gt)
|
483
497
|
|
484
|
-
cursor_value =
|
498
|
+
cursor_value = values_last
|
485
499
|
batch_relation = batch_condition(relation, cursor, cursor_value, operators)
|
486
500
|
end
|
487
501
|
|
@@ -286,6 +286,11 @@ module ActiveRecord
|
|
286
286
|
# # SELECT DATEDIFF(updated_at, created_at) FROM people
|
287
287
|
# # => ['0', '27761', '173']
|
288
288
|
#
|
289
|
+
# Be aware that #pluck ignores any previous select clauses
|
290
|
+
#
|
291
|
+
# Person.select(:name).pluck(:id)
|
292
|
+
# # SELECT people.id FROM people
|
293
|
+
#
|
289
294
|
# See also #ids.
|
290
295
|
def pluck(*column_names)
|
291
296
|
if @none
|
@@ -314,7 +319,7 @@ module ActiveRecord
|
|
314
319
|
columns = relation.arel_columns(column_names)
|
315
320
|
relation.select_values = columns
|
316
321
|
result = skip_query_cache_if_necessary do
|
317
|
-
if where_clause.contradiction?
|
322
|
+
if where_clause.contradiction? && !possible_aggregation?(column_names)
|
318
323
|
ActiveRecord::Result.empty(async: @async)
|
319
324
|
else
|
320
325
|
model.with_connection do |c|
|
@@ -417,7 +422,7 @@ module ActiveRecord
|
|
417
422
|
when :all
|
418
423
|
Arel.star
|
419
424
|
else
|
420
|
-
arel_column(column_name)
|
425
|
+
arel_column(column_name.to_s)
|
421
426
|
end
|
422
427
|
end
|
423
428
|
|
@@ -461,6 +466,16 @@ module ActiveRecord
|
|
461
466
|
column_name.is_a?(::String) && /\bDISTINCT[\s(]/i.match?(column_name)
|
462
467
|
end
|
463
468
|
|
469
|
+
def possible_aggregation?(column_names)
|
470
|
+
column_names.all? do |column_name|
|
471
|
+
if column_name.is_a?(String)
|
472
|
+
column_name.include?("(")
|
473
|
+
else
|
474
|
+
Arel.arel_node?(column_name)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
464
479
|
def operation_over_aggregate_column(column, operation, distinct)
|
465
480
|
operation == "count" ? column.count(distinct) : column.public_send(operation)
|
466
481
|
end
|
@@ -512,7 +527,6 @@ module ActiveRecord
|
|
512
527
|
|
513
528
|
def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
|
514
529
|
group_fields = group_values
|
515
|
-
group_fields = group_fields.uniq if group_fields.size > 1
|
516
530
|
|
517
531
|
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
518
532
|
association = model._reflect_on_association(group_fields.first)
|
@@ -535,7 +549,7 @@ module ActiveRecord
|
|
535
549
|
column = relation.aggregate_column(column_name)
|
536
550
|
column_alias = column_alias_tracker.alias_for("#{operation} #{column_name.to_s.downcase}")
|
537
551
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
538
|
-
select_value.as(model.adapter_class.quote_column_name(column_alias))
|
552
|
+
select_value = select_value.as(model.adapter_class.quote_column_name(column_alias))
|
539
553
|
|
540
554
|
select_values = [select_value]
|
541
555
|
select_values += self.select_values unless having_clause.empty?
|
@@ -662,6 +676,7 @@ module ActiveRecord
|
|
662
676
|
if column_name == :all
|
663
677
|
column_alias = Arel.star
|
664
678
|
relation.select_values = [ Arel.sql(FinderMethods::ONE_AS_ONE) ] unless distinct
|
679
|
+
relation.unscope!(:order)
|
665
680
|
else
|
666
681
|
column_alias = Arel.sql("count_column")
|
667
682
|
relation.select_values = [ relation.aggregate_column(column_name).as(column_alias) ]
|
@@ -670,11 +685,7 @@ module ActiveRecord
|
|
670
685
|
subquery_alias = Arel.sql("subquery_for_count", retryable: true)
|
671
686
|
select_value = operation_over_aggregate_column(column_alias, "count", false)
|
672
687
|
|
673
|
-
|
674
|
-
relation.unscope(:order).build_subquery(subquery_alias, select_value)
|
675
|
-
else
|
676
|
-
relation.build_subquery(subquery_alias, select_value)
|
677
|
-
end
|
688
|
+
relation.build_subquery(subquery_alias, select_value)
|
678
689
|
end
|
679
690
|
end
|
680
691
|
end
|
@@ -141,7 +141,7 @@ module ActiveRecord
|
|
141
141
|
#
|
142
142
|
# Product.where(["price = %?", price]).sole
|
143
143
|
def sole
|
144
|
-
found, undesired =
|
144
|
+
found, undesired = take(2)
|
145
145
|
|
146
146
|
if found.nil?
|
147
147
|
raise_record_not_found_exception!
|
@@ -442,7 +442,7 @@ module ActiveRecord
|
|
442
442
|
if distinct_value && offset_value
|
443
443
|
relation = except(:order).limit!(1)
|
444
444
|
else
|
445
|
-
relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
|
445
|
+
relation = except(:select, :distinct, :order)._select!(Arel.sql(ONE_AS_ONE, retryable: true)).limit!(1)
|
446
446
|
end
|
447
447
|
|
448
448
|
case conditions
|
@@ -639,24 +639,40 @@ module ActiveRecord
|
|
639
639
|
end
|
640
640
|
|
641
641
|
def ordered_relation
|
642
|
-
if order_values.empty?
|
643
|
-
|
642
|
+
if order_values.empty?
|
643
|
+
if !_order_columns.empty?
|
644
|
+
return order(_order_columns.map { |column| table[column].asc })
|
645
|
+
end
|
646
|
+
|
647
|
+
if ActiveRecord.raise_on_missing_required_finder_order_columns
|
648
|
+
raise MissingRequiredOrderError, <<~MSG.squish
|
649
|
+
Relation has no order values, and #{model} has no order columns to use as a default.
|
650
|
+
Set at least one of `implicit_order_column`, `query_constraints` or `primary_key` on
|
651
|
+
the model when no `order `is specified on the relation.
|
652
|
+
MSG
|
653
|
+
else
|
654
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
655
|
+
Calling order dependent finder methods (e.g. `#first`, `#second`) without `order` values on the relation,
|
656
|
+
and on a model (#{model}) that does not have any order columns (`implicit_order_column`, `query_constraints`,
|
657
|
+
or `primary_key`) to fall back on is deprecated and will raise `ActiveRecord::MissingRequiredOrderError`
|
658
|
+
in Rails 8.2.
|
659
|
+
MSG
|
660
|
+
|
661
|
+
self
|
662
|
+
end
|
644
663
|
else
|
645
664
|
self
|
646
665
|
end
|
647
666
|
end
|
648
667
|
|
649
668
|
def _order_columns
|
650
|
-
|
669
|
+
columns = Array(model.implicit_order_column)
|
651
670
|
|
652
|
-
|
653
|
-
oc << model.query_constraints_list if model.query_constraints_list
|
671
|
+
return columns.compact if columns.length.positive? && columns.last.nil?
|
654
672
|
|
655
|
-
|
656
|
-
oc << model.primary_key
|
657
|
-
end
|
673
|
+
columns += Array(model.query_constraints_list || model.primary_key)
|
658
674
|
|
659
|
-
|
675
|
+
columns.uniq.compact
|
660
676
|
end
|
661
677
|
end
|
662
678
|
end
|
@@ -85,9 +85,9 @@ module ActiveRecord
|
|
85
85
|
return if other.select_values.empty?
|
86
86
|
|
87
87
|
if other.model == relation.model
|
88
|
-
relation.select_values
|
88
|
+
relation.select_values += other.select_values if relation.select_values != other.select_values
|
89
89
|
else
|
90
|
-
relation.select_values
|
90
|
+
relation.select_values += other.instance_eval do
|
91
91
|
arel_columns(select_values)
|
92
92
|
end
|
93
93
|
end
|
@@ -37,7 +37,7 @@ module ActiveRecord
|
|
37
37
|
|
38
38
|
# Define how a class is converted to Arel nodes when passed to +where+.
|
39
39
|
# The handler can be any object that responds to +call+, and will be used
|
40
|
-
# for any value that
|
40
|
+
# for any value that <tt>===</tt> the class given. For example:
|
41
41
|
#
|
42
42
|
# MyCustomDateRange = Struct.new(:start, :end)
|
43
43
|
# handler = proc do |column, range|
|
@@ -82,7 +82,7 @@ module ActiveRecord
|
|
82
82
|
attr_writer :table
|
83
83
|
|
84
84
|
def expand_from_hash(attributes, &block)
|
85
|
-
return ["1=0"] if attributes.empty?
|
85
|
+
return [Arel.sql("1=0", retryable: true)] if attributes.empty?
|
86
86
|
|
87
87
|
attributes.flat_map do |key, value|
|
88
88
|
if key.is_a?(Array) && key.size == 1
|
@@ -15,7 +15,9 @@ module ActiveRecord
|
|
15
15
|
elsif @type.serialized?
|
16
16
|
value_for_database
|
17
17
|
elsif @type.mutable? # If the type is simply mutable, we deep_dup it.
|
18
|
-
|
18
|
+
unless @value_before_type_cast.frozen?
|
19
|
+
@value_before_type_cast = @value_before_type_cast.deep_dup
|
20
|
+
end
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
@@ -426,7 +426,7 @@ module ActiveRecord
|
|
426
426
|
end
|
427
427
|
|
428
428
|
def _select!(*fields) # :nodoc:
|
429
|
-
self.select_values
|
429
|
+
self.select_values += fields
|
430
430
|
self
|
431
431
|
end
|
432
432
|
|
@@ -576,7 +576,7 @@ module ActiveRecord
|
|
576
576
|
end
|
577
577
|
|
578
578
|
def group!(*args) # :nodoc:
|
579
|
-
self.group_values
|
579
|
+
self.group_values |= args
|
580
580
|
self
|
581
581
|
end
|
582
582
|
|
@@ -809,7 +809,7 @@ module ActiveRecord
|
|
809
809
|
end
|
810
810
|
|
811
811
|
def unscope!(*args) # :nodoc:
|
812
|
-
self.unscope_values
|
812
|
+
self.unscope_values |= args
|
813
813
|
|
814
814
|
args.each do |scope|
|
815
815
|
case scope
|
@@ -1213,6 +1213,7 @@ module ActiveRecord
|
|
1213
1213
|
end
|
1214
1214
|
|
1215
1215
|
def limit!(value) # :nodoc:
|
1216
|
+
value = Integer(value) unless value.nil?
|
1216
1217
|
self.limit_value = value
|
1217
1218
|
self
|
1218
1219
|
end
|
@@ -1465,7 +1466,7 @@ module ActiveRecord
|
|
1465
1466
|
modules << Module.new(&block) if block
|
1466
1467
|
modules.flatten!
|
1467
1468
|
|
1468
|
-
self.extending_values
|
1469
|
+
self.extending_values |= modules
|
1469
1470
|
extend(*extending_values) if extending_values.any?
|
1470
1471
|
|
1471
1472
|
self
|
@@ -1533,7 +1534,7 @@ module ActiveRecord
|
|
1533
1534
|
|
1534
1535
|
# Like #annotate, but modifies relation in place.
|
1535
1536
|
def annotate!(*args) # :nodoc:
|
1536
|
-
self.annotate_values
|
1537
|
+
self.annotate_values |= args
|
1537
1538
|
self
|
1538
1539
|
end
|
1539
1540
|
|
@@ -1592,7 +1593,7 @@ module ActiveRecord
|
|
1592
1593
|
|
1593
1594
|
# Returns the Arel object associated with the relation.
|
1594
1595
|
def arel(aliases = nil) # :nodoc:
|
1595
|
-
@arel ||=
|
1596
|
+
@arel ||= build_arel(aliases)
|
1596
1597
|
end
|
1597
1598
|
|
1598
1599
|
def construct_join_dependency(associations, join_type) # :nodoc:
|
@@ -1626,7 +1627,7 @@ module ActiveRecord
|
|
1626
1627
|
elsif opts.include?("?")
|
1627
1628
|
parts = [build_bound_sql_literal(opts, rest)]
|
1628
1629
|
else
|
1629
|
-
parts = [model.sanitize_sql(
|
1630
|
+
parts = [Arel.sql(model.sanitize_sql([opts, *rest]))]
|
1630
1631
|
end
|
1631
1632
|
when Hash
|
1632
1633
|
opts = opts.transform_keys do |key|
|
@@ -1653,13 +1654,12 @@ module ActiveRecord
|
|
1653
1654
|
end
|
1654
1655
|
alias :build_having_clause :build_where_clause
|
1655
1656
|
|
1656
|
-
def async!
|
1657
|
+
def async! # :nodoc:
|
1657
1658
|
@async = true
|
1658
1659
|
self
|
1659
1660
|
end
|
1660
1661
|
|
1661
|
-
|
1662
|
-
def arel_columns(columns)
|
1662
|
+
def arel_columns(columns) # :nodoc:
|
1663
1663
|
columns.flat_map do |field|
|
1664
1664
|
case field
|
1665
1665
|
when Symbol, String
|
@@ -1747,32 +1747,27 @@ module ActiveRecord
|
|
1747
1747
|
raise UnmodifiableRelation if @loaded || @arel
|
1748
1748
|
end
|
1749
1749
|
|
1750
|
-
def build_arel(
|
1750
|
+
def build_arel(aliases)
|
1751
1751
|
arel = Arel::SelectManager.new(table)
|
1752
1752
|
|
1753
1753
|
build_joins(arel.join_sources, aliases)
|
1754
1754
|
|
1755
1755
|
arel.where(where_clause.ast) unless where_clause.empty?
|
1756
1756
|
arel.having(having_clause.ast) unless having_clause.empty?
|
1757
|
-
arel.take(build_cast_value("LIMIT",
|
1757
|
+
arel.take(build_cast_value("LIMIT", limit_value)) if limit_value
|
1758
1758
|
arel.skip(build_cast_value("OFFSET", offset_value.to_i)) if offset_value
|
1759
|
-
arel.group(*arel_columns(group_values
|
1759
|
+
arel.group(*arel_columns(group_values)) unless group_values.empty?
|
1760
1760
|
|
1761
1761
|
build_order(arel)
|
1762
1762
|
build_with(arel)
|
1763
1763
|
build_select(arel)
|
1764
1764
|
|
1765
1765
|
arel.optimizer_hints(*optimizer_hints_values) unless optimizer_hints_values.empty?
|
1766
|
+
arel.comment(*annotate_values) unless annotate_values.empty?
|
1766
1767
|
arel.distinct(distinct_value)
|
1767
1768
|
arel.from(build_from) unless from_clause.empty?
|
1768
1769
|
arel.lock(lock_value) if lock_value
|
1769
1770
|
|
1770
|
-
unless annotate_values.empty?
|
1771
|
-
annotates = annotate_values
|
1772
|
-
annotates = annotates.uniq if annotates.size > 1
|
1773
|
-
arel.comment(*annotates)
|
1774
|
-
end
|
1775
|
-
|
1776
1771
|
arel
|
1777
1772
|
end
|
1778
1773
|
|
@@ -1990,7 +1985,7 @@ module ActiveRecord
|
|
1990
1985
|
def arel_column(field)
|
1991
1986
|
field = field.name if is_symbol = field.is_a?(Symbol)
|
1992
1987
|
|
1993
|
-
field = model.attribute_aliases[field] || field
|
1988
|
+
field = model.attribute_aliases[field] || field
|
1994
1989
|
from = from_clause.name || from_clause.value
|
1995
1990
|
|
1996
1991
|
if model.columns_hash.key?(field) && (!from || table_name_matches?(from))
|
@@ -2001,8 +1996,10 @@ module ActiveRecord
|
|
2001
1996
|
yield field
|
2002
1997
|
elsif Arel.arel_node?(field)
|
2003
1998
|
field
|
1999
|
+
elsif is_symbol
|
2000
|
+
Arel.sql(model.adapter_class.quote_table_name(field), retryable: true)
|
2004
2001
|
else
|
2005
|
-
Arel.sql(
|
2002
|
+
Arel.sql(field)
|
2006
2003
|
end
|
2007
2004
|
end
|
2008
2005
|
|
@@ -2014,9 +2011,15 @@ module ActiveRecord
|
|
2014
2011
|
|
2015
2012
|
def reverse_sql_order(order_query)
|
2016
2013
|
if order_query.empty?
|
2017
|
-
|
2018
|
-
|
2019
|
-
|
2014
|
+
if !_reverse_order_columns.empty?
|
2015
|
+
return _reverse_order_columns.map { |column| table[column].desc }
|
2016
|
+
end
|
2017
|
+
|
2018
|
+
raise IrreversibleOrderError, <<~MSG.squish
|
2019
|
+
Relation has no order values, and #{model} has no order columns to use as a default.
|
2020
|
+
Set at least one of `implicit_order_column`, or `primary_key` on the model when no
|
2021
|
+
`order `is specified on the relation.
|
2022
|
+
MSG
|
2020
2023
|
end
|
2021
2024
|
|
2022
2025
|
order_query.flat_map do |o|
|
@@ -2041,6 +2044,13 @@ module ActiveRecord
|
|
2041
2044
|
end
|
2042
2045
|
end
|
2043
2046
|
|
2047
|
+
def _reverse_order_columns
|
2048
|
+
roc = []
|
2049
|
+
roc << model.implicit_order_column if model.implicit_order_column
|
2050
|
+
roc << model.primary_key if model.primary_key
|
2051
|
+
roc.flatten.uniq.compact
|
2052
|
+
end
|
2053
|
+
|
2044
2054
|
def does_not_support_reverse?(order)
|
2045
2055
|
# Account for String subclasses like Arel::Nodes::SqlLiteral that
|
2046
2056
|
# override methods like #count.
|
@@ -2267,11 +2277,11 @@ module ActiveRecord
|
|
2267
2277
|
values = other.values
|
2268
2278
|
STRUCTURAL_VALUE_METHODS.reject do |method|
|
2269
2279
|
v1, v2 = @values[method], values[method]
|
2270
|
-
|
2271
|
-
|
2272
|
-
|
2273
|
-
|
2274
|
-
|
2280
|
+
|
2281
|
+
# `and`/`or` are focused to combine where-like clauses, so it relaxes
|
2282
|
+
# the difference when other's multi values are uninitialized.
|
2283
|
+
next true if v1.is_a?(Array) && v2.nil?
|
2284
|
+
|
2275
2285
|
v1 == v2
|
2276
2286
|
end
|
2277
2287
|
end
|
@@ -176,8 +176,6 @@ module ActiveRecord
|
|
176
176
|
end
|
177
177
|
|
178
178
|
def except_predicates(columns)
|
179
|
-
return predicates if columns.empty?
|
180
|
-
|
181
179
|
attrs = columns.extract! { |node| node.is_a?(Arel::Attribute) }
|
182
180
|
non_attrs = columns.extract! { |node| node.is_a?(Arel::Predications) }
|
183
181
|
|
@@ -194,7 +192,7 @@ module ActiveRecord
|
|
194
192
|
non_empty_predicates.map do |node|
|
195
193
|
case node
|
196
194
|
when Arel::Nodes::SqlLiteral, ::String
|
197
|
-
|
195
|
+
Arel::Nodes::Grouping.new(node)
|
198
196
|
else node
|
199
197
|
end
|
200
198
|
end
|
@@ -205,13 +203,6 @@ module ActiveRecord
|
|
205
203
|
predicates - ARRAY_WITH_EMPTY_STRING
|
206
204
|
end
|
207
205
|
|
208
|
-
def wrap_sql_literal(node)
|
209
|
-
if ::String === node
|
210
|
-
node = Arel.sql(node)
|
211
|
-
end
|
212
|
-
Arel::Nodes::Grouping.new(node)
|
213
|
-
end
|
214
|
-
|
215
206
|
def extract_node_value(node)
|
216
207
|
if node.respond_to?(:value_before_type_cast)
|
217
208
|
node.value_before_type_cast
|