activerecord 7.0.4.3 → 7.0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +318 -7
- data/README.rdoc +2 -2
- data/lib/active_record/associations/collection_association.rb +1 -1
- data/lib/active_record/associations/collection_proxy.rb +5 -0
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations.rb +15 -6
- data/lib/active_record/attribute_methods/read.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +0 -4
- data/lib/active_record/attribute_methods.rb +5 -7
- data/lib/active_record/callbacks.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +20 -16
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +2 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +4 -17
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +15 -9
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +13 -2
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -5
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +5 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -4
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/locking/optimistic.rb +32 -18
- data/lib/active_record/middleware/database_selector.rb +3 -3
- data/lib/active_record/migration/command_recorder.rb +1 -2
- data/lib/active_record/migration/compatibility.rb +19 -54
- data/lib/active_record/migration.rb +53 -4
- data/lib/active_record/persistence.rb +7 -5
- data/lib/active_record/railties/controller_runtime.rb +3 -4
- data/lib/active_record/reflection.rb +8 -0
- data/lib/active_record/relation/calculations.rb +50 -23
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -1
- data/lib/active_record/relation/query_attribute.rb +23 -0
- data/lib/active_record/relation/query_methods.rb +35 -8
- data/lib/active_record/result.rb +6 -4
- data/lib/active_record/schema_dumper.rb +4 -0
- data/lib/active_record/store.rb +1 -1
- data/lib/active_record/table_metadata.rb +6 -2
- data/lib/active_record/transactions.rb +3 -3
- data/lib/active_record/type/serialized.rb +8 -4
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/filter.rb +1 -1
- metadata +13 -13
|
@@ -551,6 +551,41 @@ module ActiveRecord
|
|
|
551
551
|
|
|
552
552
|
# This must be defined before the inherited hook, below
|
|
553
553
|
class Current < Migration # :nodoc:
|
|
554
|
+
def create_table(table_name, **options)
|
|
555
|
+
if block_given?
|
|
556
|
+
super { |t| yield compatible_table_definition(t) }
|
|
557
|
+
else
|
|
558
|
+
super
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def change_table(table_name, **options)
|
|
563
|
+
if block_given?
|
|
564
|
+
super { |t| yield compatible_table_definition(t) }
|
|
565
|
+
else
|
|
566
|
+
super
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def create_join_table(table_1, table_2, **options)
|
|
571
|
+
if block_given?
|
|
572
|
+
super { |t| yield compatible_table_definition(t) }
|
|
573
|
+
else
|
|
574
|
+
super
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
def drop_table(table_name, **options)
|
|
579
|
+
if block_given?
|
|
580
|
+
super { |t| yield compatible_table_definition(t) }
|
|
581
|
+
else
|
|
582
|
+
super
|
|
583
|
+
end
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def compatible_table_definition(t)
|
|
587
|
+
t
|
|
588
|
+
end
|
|
554
589
|
end
|
|
555
590
|
|
|
556
591
|
def self.inherited(subclass) # :nodoc:
|
|
@@ -916,9 +951,7 @@ module ActiveRecord
|
|
|
916
951
|
end
|
|
917
952
|
|
|
918
953
|
def method_missing(method, *arguments, &block)
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
say_with_time "#{method}(#{arg_list})" do
|
|
954
|
+
say_with_time "#{method}(#{format_arguments(arguments)})" do
|
|
922
955
|
unless connection.respond_to? :revert
|
|
923
956
|
unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
|
|
924
957
|
arguments[0] = proper_table_name(arguments.first, table_name_options)
|
|
@@ -1026,6 +1059,22 @@ module ActiveRecord
|
|
|
1026
1059
|
end
|
|
1027
1060
|
end
|
|
1028
1061
|
|
|
1062
|
+
def format_arguments(arguments)
|
|
1063
|
+
arg_list = arguments[0...-1].map(&:inspect)
|
|
1064
|
+
last_arg = arguments.last
|
|
1065
|
+
if last_arg.is_a?(Hash)
|
|
1066
|
+
last_arg = last_arg.reject { |k, _v| internal_option?(k) }
|
|
1067
|
+
arg_list << last_arg.inspect unless last_arg.empty?
|
|
1068
|
+
else
|
|
1069
|
+
arg_list << last_arg.inspect
|
|
1070
|
+
end
|
|
1071
|
+
arg_list.join(", ")
|
|
1072
|
+
end
|
|
1073
|
+
|
|
1074
|
+
def internal_option?(option_name)
|
|
1075
|
+
option_name.start_with?("_")
|
|
1076
|
+
end
|
|
1077
|
+
|
|
1029
1078
|
def command_recorder
|
|
1030
1079
|
CommandRecorder.new(connection)
|
|
1031
1080
|
end
|
|
@@ -1338,7 +1387,7 @@ module ActiveRecord
|
|
|
1338
1387
|
# Stores the current environment in the database.
|
|
1339
1388
|
def record_environment
|
|
1340
1389
|
return if down?
|
|
1341
|
-
ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Base.connection.
|
|
1390
|
+
ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Base.connection.pool.db_config.env_name
|
|
1342
1391
|
end
|
|
1343
1392
|
|
|
1344
1393
|
def ran?(migration)
|
|
@@ -564,7 +564,7 @@ module ActiveRecord
|
|
|
564
564
|
end
|
|
565
565
|
|
|
566
566
|
# Returns true if this object was just created -- that is, prior to the last
|
|
567
|
-
#
|
|
567
|
+
# update or delete, the object didn't exist in the database and new_record? would have
|
|
568
568
|
# returned true.
|
|
569
569
|
def previously_new_record?
|
|
570
570
|
@previously_new_record
|
|
@@ -663,6 +663,7 @@ module ActiveRecord
|
|
|
663
663
|
def delete
|
|
664
664
|
_delete_row if persisted?
|
|
665
665
|
@destroyed = true
|
|
666
|
+
@previously_new_record = false
|
|
666
667
|
freeze
|
|
667
668
|
end
|
|
668
669
|
|
|
@@ -682,6 +683,7 @@ module ActiveRecord
|
|
|
682
683
|
true
|
|
683
684
|
end
|
|
684
685
|
@destroyed = true
|
|
686
|
+
@previously_new_record = false
|
|
685
687
|
freeze
|
|
686
688
|
end
|
|
687
689
|
|
|
@@ -813,7 +815,7 @@ module ActiveRecord
|
|
|
813
815
|
verify_readonly_attribute(name) || name
|
|
814
816
|
end
|
|
815
817
|
|
|
816
|
-
update_constraints =
|
|
818
|
+
update_constraints = _query_constraints_hash
|
|
817
819
|
attributes = attributes.each_with_object({}) do |(k, v), h|
|
|
818
820
|
h[k] = @attributes.write_cast_value(k, v)
|
|
819
821
|
clear_attribute_change(k)
|
|
@@ -1028,7 +1030,7 @@ module ActiveRecord
|
|
|
1028
1030
|
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
|
1029
1031
|
end
|
|
1030
1032
|
|
|
1031
|
-
def
|
|
1033
|
+
def _query_constraints_hash
|
|
1032
1034
|
{ @primary_key => id_in_database }
|
|
1033
1035
|
end
|
|
1034
1036
|
|
|
@@ -1041,7 +1043,7 @@ module ActiveRecord
|
|
|
1041
1043
|
end
|
|
1042
1044
|
|
|
1043
1045
|
def _delete_row
|
|
1044
|
-
self.class._delete_record(
|
|
1046
|
+
self.class._delete_record(_query_constraints_hash)
|
|
1045
1047
|
end
|
|
1046
1048
|
|
|
1047
1049
|
def _touch_row(attribute_names, time)
|
|
@@ -1057,7 +1059,7 @@ module ActiveRecord
|
|
|
1057
1059
|
def _update_row(attribute_names, attempted_action = "update")
|
|
1058
1060
|
self.class._update_record(
|
|
1059
1061
|
attributes_with_values(attribute_names),
|
|
1060
|
-
|
|
1062
|
+
_query_constraints_hash
|
|
1061
1063
|
)
|
|
1062
1064
|
end
|
|
1063
1065
|
|
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def cleanup_view_runtime
|
|
31
|
-
if logger && logger.info?
|
|
31
|
+
if logger && logger.info?
|
|
32
32
|
db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
|
|
33
33
|
self.db_runtime = (db_runtime || 0) + db_rt_before_render
|
|
34
34
|
runtime = super
|
|
@@ -42,9 +42,8 @@ module ActiveRecord
|
|
|
42
42
|
|
|
43
43
|
def append_info_to_payload(payload)
|
|
44
44
|
super
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
end
|
|
45
|
+
|
|
46
|
+
payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime
|
|
48
47
|
end
|
|
49
48
|
end
|
|
50
49
|
end
|
|
@@ -485,6 +485,10 @@ module ActiveRecord
|
|
|
485
485
|
foreign_key
|
|
486
486
|
end
|
|
487
487
|
|
|
488
|
+
def join_primary_type
|
|
489
|
+
type
|
|
490
|
+
end
|
|
491
|
+
|
|
488
492
|
def join_foreign_key
|
|
489
493
|
active_record_primary_key
|
|
490
494
|
end
|
|
@@ -588,6 +592,10 @@ module ActiveRecord
|
|
|
588
592
|
options[:polymorphic]
|
|
589
593
|
end
|
|
590
594
|
|
|
595
|
+
def polymorphic_name
|
|
596
|
+
active_record.polymorphic_name
|
|
597
|
+
end
|
|
598
|
+
|
|
591
599
|
def add_as_source(seed)
|
|
592
600
|
seed
|
|
593
601
|
end
|
|
@@ -4,6 +4,47 @@ require "active_support/core_ext/enumerable"
|
|
|
4
4
|
|
|
5
5
|
module ActiveRecord
|
|
6
6
|
module Calculations
|
|
7
|
+
class ColumnAliasTracker # :nodoc:
|
|
8
|
+
def initialize(connection)
|
|
9
|
+
@connection = connection
|
|
10
|
+
@aliases = Hash.new(0)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def alias_for(field)
|
|
14
|
+
aliased_name = column_alias_for(field)
|
|
15
|
+
|
|
16
|
+
if @aliases[aliased_name] == 0
|
|
17
|
+
@aliases[aliased_name] = 1
|
|
18
|
+
aliased_name
|
|
19
|
+
else
|
|
20
|
+
# Update the count
|
|
21
|
+
count = @aliases[aliased_name] += 1
|
|
22
|
+
"#{truncate(aliased_name)}_#{count}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
# Converts the given field to the value that the database adapter returns as
|
|
28
|
+
# a usable column name:
|
|
29
|
+
#
|
|
30
|
+
# column_alias_for("users.id") # => "users_id"
|
|
31
|
+
# column_alias_for("sum(id)") # => "sum_id"
|
|
32
|
+
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
|
33
|
+
# column_alias_for("count(*)") # => "count_all"
|
|
34
|
+
def column_alias_for(field)
|
|
35
|
+
column_alias = +field
|
|
36
|
+
column_alias.gsub!(/\*/, "all")
|
|
37
|
+
column_alias.gsub!(/\W+/, " ")
|
|
38
|
+
column_alias.strip!
|
|
39
|
+
column_alias.gsub!(/ +/, "_")
|
|
40
|
+
@connection.table_alias_for(column_alias)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def truncate(name)
|
|
44
|
+
name.slice(0, @connection.table_alias_length - 2)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
7
48
|
# Count the records.
|
|
8
49
|
#
|
|
9
50
|
# Person.count
|
|
@@ -86,7 +127,7 @@ module ActiveRecord
|
|
|
86
127
|
def sum(identity_or_column = nil, &block)
|
|
87
128
|
if block_given?
|
|
88
129
|
values = map(&block)
|
|
89
|
-
if identity_or_column.nil? && (values.first.is_a?(Numeric) || values.first(1) == [])
|
|
130
|
+
if identity_or_column.nil? && (values.first.is_a?(Numeric) || values.first(1) == [] || values.first.respond_to?(:coerce))
|
|
90
131
|
identity_or_column = 0
|
|
91
132
|
end
|
|
92
133
|
|
|
@@ -336,14 +377,16 @@ module ActiveRecord
|
|
|
336
377
|
end
|
|
337
378
|
group_fields = arel_columns(group_fields)
|
|
338
379
|
|
|
380
|
+
column_alias_tracker = ColumnAliasTracker.new(connection)
|
|
381
|
+
|
|
339
382
|
group_aliases = group_fields.map { |field|
|
|
340
383
|
field = connection.visitor.compile(field) if Arel.arel_node?(field)
|
|
341
|
-
|
|
384
|
+
column_alias_tracker.alias_for(field.to_s.downcase)
|
|
342
385
|
}
|
|
343
386
|
group_columns = group_aliases.zip(group_fields)
|
|
344
387
|
|
|
345
388
|
column = aggregate_column(column_name)
|
|
346
|
-
column_alias =
|
|
389
|
+
column_alias = column_alias_tracker.alias_for("#{operation} #{column_name.to_s.downcase}")
|
|
347
390
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
|
348
391
|
select_value.as(connection.quote_column_name(column_alias))
|
|
349
392
|
|
|
@@ -372,9 +415,10 @@ module ActiveRecord
|
|
|
372
415
|
end
|
|
373
416
|
|
|
374
417
|
key_types = group_columns.each_with_object({}) do |(aliaz, col_name), types|
|
|
375
|
-
types[aliaz] =
|
|
376
|
-
|
|
377
|
-
|
|
418
|
+
types[aliaz] = col_name.try(:type_caster) ||
|
|
419
|
+
type_for(col_name) do
|
|
420
|
+
calculated_data.column_types.fetch(aliaz, Type.default_value)
|
|
421
|
+
end
|
|
378
422
|
end
|
|
379
423
|
|
|
380
424
|
hash_rows = calculated_data.cast_values(key_types).map! do |row|
|
|
@@ -398,23 +442,6 @@ module ActiveRecord
|
|
|
398
442
|
end
|
|
399
443
|
end
|
|
400
444
|
|
|
401
|
-
# Converts the given field to the value that the database adapter returns as
|
|
402
|
-
# a usable column name:
|
|
403
|
-
#
|
|
404
|
-
# column_alias_for("users.id") # => "users_id"
|
|
405
|
-
# column_alias_for("sum(id)") # => "sum_id"
|
|
406
|
-
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
|
|
407
|
-
# column_alias_for("count(*)") # => "count_all"
|
|
408
|
-
def column_alias_for(field)
|
|
409
|
-
column_alias = +field
|
|
410
|
-
column_alias.gsub!(/\*/, "all")
|
|
411
|
-
column_alias.gsub!(/\W+/, " ")
|
|
412
|
-
column_alias.strip!
|
|
413
|
-
column_alias.gsub!(/ +/, "_")
|
|
414
|
-
|
|
415
|
-
connection.table_alias_for(column_alias)
|
|
416
|
-
end
|
|
417
|
-
|
|
418
445
|
def type_for(field, &block)
|
|
419
446
|
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
|
|
420
447
|
@klass.type_for_attribute(field_name, &block)
|
|
@@ -18,7 +18,10 @@ module ActiveRecord
|
|
|
18
18
|
def ids
|
|
19
19
|
case value
|
|
20
20
|
when Relation
|
|
21
|
-
|
|
21
|
+
relation = value
|
|
22
|
+
relation = relation.select(primary_key) if select_clause?
|
|
23
|
+
relation = relation.where(primary_type => polymorphic_name) if polymorphic_clause?
|
|
24
|
+
relation
|
|
22
25
|
when Array
|
|
23
26
|
value.map { |v| convert_to_id(v) }
|
|
24
27
|
else
|
|
@@ -30,6 +33,22 @@ module ActiveRecord
|
|
|
30
33
|
associated_table.join_primary_key
|
|
31
34
|
end
|
|
32
35
|
|
|
36
|
+
def primary_type
|
|
37
|
+
associated_table.join_primary_type
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def polymorphic_name
|
|
41
|
+
associated_table.polymorphic_name_association
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def select_clause?
|
|
45
|
+
value.select_values.empty?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def polymorphic_clause?
|
|
49
|
+
primary_type && !value.where_values_hash.has_key?(primary_type)
|
|
50
|
+
end
|
|
51
|
+
|
|
33
52
|
def convert_to_id(value)
|
|
34
53
|
if value.respond_to?(primary_key)
|
|
35
54
|
value.public_send(primary_key)
|
|
@@ -5,6 +5,20 @@ require "active_model/attribute"
|
|
|
5
5
|
module ActiveRecord
|
|
6
6
|
class Relation
|
|
7
7
|
class QueryAttribute < ActiveModel::Attribute # :nodoc:
|
|
8
|
+
def initialize(...)
|
|
9
|
+
super
|
|
10
|
+
|
|
11
|
+
# The query attribute value may be mutated before we actually "compile" the query.
|
|
12
|
+
# To avoid that if the type uses a serializer we eagerly compute the value for database
|
|
13
|
+
if value_before_type_cast.is_a?(StatementCache::Substitute)
|
|
14
|
+
# we don't need to serialize StatementCache::Substitute
|
|
15
|
+
elsif @type.serialized?
|
|
16
|
+
value_for_database
|
|
17
|
+
elsif @type.mutable? # If the type is simply mutable, we deep_dup it.
|
|
18
|
+
@value_before_type_cast = @value_before_type_cast.deep_dup
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
8
22
|
def type_cast(value)
|
|
9
23
|
value
|
|
10
24
|
end
|
|
@@ -35,6 +49,15 @@ module ActiveRecord
|
|
|
35
49
|
@_unboundable
|
|
36
50
|
end
|
|
37
51
|
|
|
52
|
+
def ==(other)
|
|
53
|
+
super && value_for_database == other.value_for_database
|
|
54
|
+
end
|
|
55
|
+
alias eql? ==
|
|
56
|
+
|
|
57
|
+
def hash
|
|
58
|
+
[self.class, name, value_for_database, type].hash
|
|
59
|
+
end
|
|
60
|
+
|
|
38
61
|
private
|
|
39
62
|
def infinity?(value)
|
|
40
63
|
value.respond_to?(:infinite?) && value.infinite?
|
|
@@ -77,7 +77,11 @@ module ActiveRecord
|
|
|
77
77
|
associations.each do |association|
|
|
78
78
|
reflection = scope_association_reflection(association)
|
|
79
79
|
@scope.joins!(association)
|
|
80
|
-
|
|
80
|
+
if reflection.options[:class_name]
|
|
81
|
+
self.not(association => { reflection.association_primary_key => nil })
|
|
82
|
+
else
|
|
83
|
+
self.not(reflection.table_name => { reflection.association_primary_key => nil })
|
|
84
|
+
end
|
|
81
85
|
end
|
|
82
86
|
|
|
83
87
|
@scope
|
|
@@ -105,7 +109,11 @@ module ActiveRecord
|
|
|
105
109
|
associations.each do |association|
|
|
106
110
|
reflection = scope_association_reflection(association)
|
|
107
111
|
@scope.left_outer_joins!(association)
|
|
108
|
-
|
|
112
|
+
if reflection.options[:class_name]
|
|
113
|
+
@scope.where!(association => { reflection.association_primary_key => nil })
|
|
114
|
+
else
|
|
115
|
+
@scope.where!(reflection.table_name => { reflection.association_primary_key => nil })
|
|
116
|
+
end
|
|
109
117
|
end
|
|
110
118
|
|
|
111
119
|
@scope
|
|
@@ -289,7 +297,7 @@ module ActiveRecord
|
|
|
289
297
|
# You can also use one or more strings, which will be used unchanged as SELECT fields.
|
|
290
298
|
#
|
|
291
299
|
# Model.select('field AS field_one', 'other_field AS field_two')
|
|
292
|
-
# # => [#<Model id: nil,
|
|
300
|
+
# # => [#<Model id: nil, field_one: "value", field_two: "value">]
|
|
293
301
|
#
|
|
294
302
|
# If an alias was specified, it will be accessible from the resulting objects:
|
|
295
303
|
#
|
|
@@ -436,13 +444,16 @@ module ActiveRecord
|
|
|
436
444
|
self
|
|
437
445
|
end
|
|
438
446
|
|
|
439
|
-
# Allows to specify an order by a specific set of values.
|
|
440
|
-
# adapter this will either use a CASE statement or a built-in function.
|
|
447
|
+
# Allows to specify an order by a specific set of values.
|
|
441
448
|
#
|
|
442
449
|
# User.in_order_of(:id, [1, 5, 3])
|
|
443
450
|
# # SELECT "users".* FROM "users"
|
|
444
|
-
# # ORDER BY FIELD("users"."id", 1, 5, 3)
|
|
445
451
|
# # WHERE "users"."id" IN (1, 5, 3)
|
|
452
|
+
# # ORDER BY CASE
|
|
453
|
+
# # WHEN "users"."id" = 1 THEN 1
|
|
454
|
+
# # WHEN "users"."id" = 5 THEN 2
|
|
455
|
+
# # WHEN "users"."id" = 3 THEN 3
|
|
456
|
+
# # END ASC
|
|
446
457
|
#
|
|
447
458
|
def in_order_of(column, values)
|
|
448
459
|
klass.disallow_raw_sql!([column], permit: connection.column_name_with_order_matcher)
|
|
@@ -454,9 +465,16 @@ module ActiveRecord
|
|
|
454
465
|
values = values.map { |value| type_caster.type_cast_for_database(column, value) }
|
|
455
466
|
arel_column = column.is_a?(Symbol) ? order_column(column.to_s) : column
|
|
456
467
|
|
|
468
|
+
where_clause =
|
|
469
|
+
if values.include?(nil)
|
|
470
|
+
arel_column.in(values.compact).or(arel_column.eq(nil))
|
|
471
|
+
else
|
|
472
|
+
arel_column.in(values)
|
|
473
|
+
end
|
|
474
|
+
|
|
457
475
|
spawn
|
|
458
|
-
.order!(
|
|
459
|
-
.where!(
|
|
476
|
+
.order!(build_case_for_value_position(arel_column, values))
|
|
477
|
+
.where!(where_clause)
|
|
460
478
|
end
|
|
461
479
|
|
|
462
480
|
# Replaces any existing order defined on the relation with the specified order.
|
|
@@ -1661,6 +1679,15 @@ module ActiveRecord
|
|
|
1661
1679
|
end
|
|
1662
1680
|
end
|
|
1663
1681
|
|
|
1682
|
+
def build_case_for_value_position(column, values)
|
|
1683
|
+
node = Arel::Nodes::Case.new
|
|
1684
|
+
values.each.with_index(1) do |value, order|
|
|
1685
|
+
node.when(column.eq(value)).then(order)
|
|
1686
|
+
end
|
|
1687
|
+
|
|
1688
|
+
Arel::Nodes::Ascending.new(node)
|
|
1689
|
+
end
|
|
1690
|
+
|
|
1664
1691
|
def resolve_arel_attributes(attrs)
|
|
1665
1692
|
attrs.flat_map do |attr|
|
|
1666
1693
|
case attr
|
data/lib/active_record/result.rb
CHANGED
|
@@ -108,7 +108,7 @@ module ActiveRecord
|
|
|
108
108
|
type = if type_overrides.is_a?(Array)
|
|
109
109
|
type_overrides.first
|
|
110
110
|
else
|
|
111
|
-
column_type(columns.first, type_overrides)
|
|
111
|
+
column_type(columns.first, 0, type_overrides)
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
rows.map do |(value)|
|
|
@@ -118,7 +118,7 @@ module ActiveRecord
|
|
|
118
118
|
types = if type_overrides.is_a?(Array)
|
|
119
119
|
type_overrides
|
|
120
120
|
else
|
|
121
|
-
columns.map { |name| column_type(name, type_overrides) }
|
|
121
|
+
columns.map.with_index { |name, i| column_type(name, i, type_overrides) }
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
rows.map do |values|
|
|
@@ -135,9 +135,11 @@ module ActiveRecord
|
|
|
135
135
|
end
|
|
136
136
|
|
|
137
137
|
private
|
|
138
|
-
def column_type(name, type_overrides
|
|
138
|
+
def column_type(name, index, type_overrides)
|
|
139
139
|
type_overrides.fetch(name) do
|
|
140
|
-
column_types.fetch(
|
|
140
|
+
column_types.fetch(index) do
|
|
141
|
+
column_types.fetch(name, Type.default_value)
|
|
142
|
+
end
|
|
141
143
|
end
|
|
142
144
|
end
|
|
143
145
|
|
|
@@ -292,6 +292,10 @@ module ActiveRecord
|
|
|
292
292
|
end
|
|
293
293
|
|
|
294
294
|
def remove_prefix_and_suffix(table)
|
|
295
|
+
# This method appears at the top when profiling active_record test cases run.
|
|
296
|
+
# Avoid costly calculation when there are no prefix and suffix.
|
|
297
|
+
return table if @options[:table_name_prefix].blank? && @options[:table_name_suffix].blank?
|
|
298
|
+
|
|
295
299
|
prefix = Regexp.escape(@options[:table_name_prefix].to_s)
|
|
296
300
|
suffix = Regexp.escape(@options[:table_name_suffix].to_s)
|
|
297
301
|
table.sub(/\A#{prefix}(.+)#{suffix}\z/, "\\1")
|
data/lib/active_record/store.rb
CHANGED
|
@@ -70,7 +70,7 @@ module ActiveRecord
|
|
|
70
70
|
#
|
|
71
71
|
# The stored attribute names can be retrieved using {.stored_attributes}[rdoc-ref:rdoc-ref:ClassMethods#stored_attributes].
|
|
72
72
|
#
|
|
73
|
-
# User.stored_attributes[:settings] # [:color, :homepage, :two_factor_auth, :login_retry]
|
|
73
|
+
# User.stored_attributes[:settings] # => [:color, :homepage, :two_factor_auth, :login_retry]
|
|
74
74
|
#
|
|
75
75
|
# == Overwriting default accessors
|
|
76
76
|
#
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module ActiveRecord
|
|
4
4
|
class TableMetadata # :nodoc:
|
|
5
|
-
delegate :join_primary_key, :join_foreign_key, :join_foreign_type, to: :reflection
|
|
5
|
+
delegate :join_primary_key, :join_primary_type, :join_foreign_key, :join_foreign_type, to: :reflection
|
|
6
6
|
|
|
7
7
|
def initialize(klass, arel_table, reflection = nil)
|
|
8
8
|
@klass = klass
|
|
@@ -19,7 +19,7 @@ module ActiveRecord
|
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def has_column?(column_name)
|
|
22
|
-
klass&.columns_hash
|
|
22
|
+
klass&.columns_hash&.key?(column_name)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def associated_with?(table_name)
|
|
@@ -54,6 +54,10 @@ module ActiveRecord
|
|
|
54
54
|
reflection&.polymorphic?
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
def polymorphic_name_association
|
|
58
|
+
reflection&.polymorphic_name
|
|
59
|
+
end
|
|
60
|
+
|
|
57
61
|
def through_association?
|
|
58
62
|
reflection&.through_reflection?
|
|
59
63
|
end
|
|
@@ -336,9 +336,9 @@ module ActiveRecord
|
|
|
336
336
|
@_trigger_update_callback = @_trigger_destroy_callback = false if force_restore_state
|
|
337
337
|
end
|
|
338
338
|
|
|
339
|
-
# Executes
|
|
340
|
-
# status flag. If the status is true the transaction is committed,
|
|
341
|
-
# a ROLLBACK is issued. In any case the status flag is returned.
|
|
339
|
+
# Executes a block within a transaction and captures its return value as a
|
|
340
|
+
# status flag. If the status is true, the transaction is committed,
|
|
341
|
+
# otherwise a ROLLBACK is issued. In any case, the status flag is returned.
|
|
342
342
|
#
|
|
343
343
|
# This method is available within the context of an ActiveRecord::Base
|
|
344
344
|
# instance.
|
|
@@ -55,6 +55,10 @@ module ActiveRecord
|
|
|
55
55
|
coder.respond_to?(:object_class) && value.is_a?(coder.object_class)
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
def serialized? # :nodoc:
|
|
59
|
+
true
|
|
60
|
+
end
|
|
61
|
+
|
|
58
62
|
private
|
|
59
63
|
def default_value?(value)
|
|
60
64
|
value == coder.load(nil)
|
|
@@ -63,11 +67,11 @@ module ActiveRecord
|
|
|
63
67
|
def encoded(value)
|
|
64
68
|
return if default_value?(value)
|
|
65
69
|
payload = coder.dump(value)
|
|
66
|
-
if payload && binary?
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
if payload && @subtype.binary?
|
|
71
|
+
ActiveModel::Type::Binary::Data.new(payload)
|
|
72
|
+
else
|
|
73
|
+
payload
|
|
69
74
|
end
|
|
70
|
-
payload
|
|
71
75
|
end
|
|
72
76
|
end
|
|
73
77
|
end
|
data/lib/arel/nodes/and.rb
CHANGED