activerecord 5.2.0 → 5.2.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +214 -0
- data/lib/active_record/association_relation.rb +3 -3
- data/lib/active_record/associations.rb +9 -9
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +25 -10
- data/lib/active_record/associations/belongs_to_association.rb +14 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -1
- data/lib/active_record/associations/builder/belongs_to.rb +11 -2
- data/lib/active_record/associations/collection_association.rb +19 -15
- data/lib/active_record/associations/collection_proxy.rb +8 -34
- data/lib/active_record/associations/has_many_association.rb +9 -0
- data/lib/active_record/associations/has_many_through_association.rb +25 -1
- data/lib/active_record/associations/has_one_association.rb +8 -0
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency.rb +39 -64
- data/lib/active_record/associations/join_dependency/join_association.rb +12 -18
- data/lib/active_record/associations/join_dependency/join_part.rb +7 -0
- data/lib/active_record/associations/singular_association.rb +4 -10
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +15 -10
- data/lib/active_record/attribute_methods/read.rb +1 -1
- data/lib/active_record/autosave_association.rb +7 -2
- data/lib/active_record/callbacks.rb +4 -0
- data/lib/active_record/collection_cache_key.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +14 -8
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +19 -6
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -14
- data/lib/active_record/connection_adapters/abstract/transaction.rb +23 -14
- data/lib/active_record/connection_adapters/abstract_adapter.rb +3 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +18 -19
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +11 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -26
- data/lib/active_record/connection_adapters/postgresql/utils.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -4
- data/lib/active_record/core.rb +2 -1
- data/lib/active_record/counter_cache.rb +17 -13
- data/lib/active_record/enum.rb +1 -0
- data/lib/active_record/errors.rb +18 -12
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration.rb +1 -1
- data/lib/active_record/migration/compatibility.rb +15 -15
- data/lib/active_record/model_schema.rb +1 -1
- data/lib/active_record/persistence.rb +6 -5
- data/lib/active_record/query_cache.rb +4 -11
- data/lib/active_record/querying.rb +1 -1
- data/lib/active_record/railtie.rb +1 -3
- data/lib/active_record/relation.rb +39 -20
- data/lib/active_record/relation/calculations.rb +11 -8
- data/lib/active_record/relation/delegation.rb +30 -0
- data/lib/active_record/relation/finder_methods.rb +10 -8
- data/lib/active_record/relation/merger.rb +10 -11
- data/lib/active_record/relation/predicate_builder.rb +20 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +45 -19
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/scoping/named.rb +2 -0
- data/lib/active_record/tasks/database_tasks.rb +1 -1
- data/lib/active_record/timestamp.rb +8 -1
- data/lib/active_record/transactions.rb +23 -20
- data/lib/active_record/type/serialized.rb +4 -0
- metadata +9 -10
@@ -560,17 +560,6 @@ module ActiveRecord
|
|
560
560
|
@max_allowed_packet ||= (show_variable("max_allowed_packet") - bytes_margin)
|
561
561
|
end
|
562
562
|
|
563
|
-
def with_multi_statements
|
564
|
-
previous_flags = @config[:flags]
|
565
|
-
@config[:flags] = Mysql2::Client::MULTI_STATEMENTS
|
566
|
-
reconnect!
|
567
|
-
|
568
|
-
yield
|
569
|
-
ensure
|
570
|
-
@config[:flags] = previous_flags
|
571
|
-
reconnect!
|
572
|
-
end
|
573
|
-
|
574
563
|
def initialize_type_map(m = type_map)
|
575
564
|
super
|
576
565
|
|
@@ -815,15 +804,25 @@ module ActiveRecord
|
|
815
804
|
end
|
816
805
|
|
817
806
|
def mismatched_foreign_key(message)
|
818
|
-
|
819
|
-
|
820
|
-
|
807
|
+
match = %r/
|
808
|
+
(?:CREATE|ALTER)\s+TABLE\s*(?:`?\w+`?\.)?`?(?<table>\w+)`?.+?
|
809
|
+
FOREIGN\s+KEY\s*\(`?(?<foreign_key>\w+)`?\)\s*
|
810
|
+
REFERENCES\s*(`?(?<target_table>\w+)`?)\s*\(`?(?<primary_key>\w+)`?\)
|
811
|
+
/xmi.match(message)
|
812
|
+
|
813
|
+
options = {
|
821
814
|
message: message,
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
815
|
+
}
|
816
|
+
|
817
|
+
if match
|
818
|
+
options[:table] = match[:table]
|
819
|
+
options[:foreign_key] = match[:foreign_key]
|
820
|
+
options[:target_table] = match[:target_table]
|
821
|
+
options[:primary_key] = match[:primary_key]
|
822
|
+
options[:primary_key_column] = column_for(match[:target_table], match[:primary_key])
|
823
|
+
end
|
824
|
+
|
825
|
+
MismatchedForeignKey.new(options)
|
827
826
|
end
|
828
827
|
|
829
828
|
def integer_to_sql(limit) # :nodoc:
|
@@ -195,12 +195,12 @@ module ActiveRecord
|
|
195
195
|
if e.path == path_to_adapter
|
196
196
|
# We can assume that a non-builtin adapter was specified, so it's
|
197
197
|
# either misspelled or missing from Gemfile.
|
198
|
-
raise
|
198
|
+
raise LoadError, "Could not load the '#{spec[:adapter]}' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace
|
199
199
|
|
200
200
|
# Bubbled up from the adapter require. Prefix the exception message
|
201
201
|
# with some guidance about how to address it and reraise.
|
202
202
|
else
|
203
|
-
raise
|
203
|
+
raise LoadError, "Error loading the '#{spec[:adapter]}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
|
204
204
|
end
|
205
205
|
end
|
206
206
|
|
@@ -3,15 +3,24 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
5
|
module DetermineIfPreparableVisitor
|
6
|
-
|
6
|
+
attr_accessor :preparable
|
7
7
|
|
8
8
|
def accept(*)
|
9
9
|
@preparable = true
|
10
10
|
super
|
11
11
|
end
|
12
12
|
|
13
|
-
def visit_Arel_Nodes_In(
|
13
|
+
def visit_Arel_Nodes_In(o, collector)
|
14
14
|
@preparable = false
|
15
|
+
|
16
|
+
if Array === o.right && !o.right.empty?
|
17
|
+
o.right.delete_if do |bind|
|
18
|
+
if Arel::Nodes::BindParam === bind && Relation::QueryAttribute === bind.value
|
19
|
+
!bind.value.boundable?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
15
24
|
super
|
16
25
|
end
|
17
26
|
|
@@ -62,6 +62,42 @@ module ActiveRecord
|
|
62
62
|
@connection.next_result while @connection.more_results?
|
63
63
|
end
|
64
64
|
|
65
|
+
def supports_set_server_option?
|
66
|
+
@connection.respond_to?(:set_server_option)
|
67
|
+
end
|
68
|
+
|
69
|
+
def multi_statements_enabled?(flags)
|
70
|
+
if flags.is_a?(Array)
|
71
|
+
flags.include?("MULTI_STATEMENTS")
|
72
|
+
else
|
73
|
+
(flags & Mysql2::Client::MULTI_STATEMENTS) != 0
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def with_multi_statements
|
78
|
+
previous_flags = @config[:flags]
|
79
|
+
|
80
|
+
unless multi_statements_enabled?(previous_flags)
|
81
|
+
if supports_set_server_option?
|
82
|
+
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_ON)
|
83
|
+
else
|
84
|
+
@config[:flags] = Mysql2::Client::MULTI_STATEMENTS
|
85
|
+
reconnect!
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
yield
|
90
|
+
ensure
|
91
|
+
unless multi_statements_enabled?(previous_flags)
|
92
|
+
if supports_set_server_option?
|
93
|
+
@connection.set_server_option(Mysql2::Client::OPTION_MULTI_STATEMENTS_OFF)
|
94
|
+
else
|
95
|
+
@config[:flags] = previous_flags
|
96
|
+
reconnect!
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
65
101
|
def exec_stmt_and_free(sql, name, binds, cache_stmt: false)
|
66
102
|
# make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been
|
67
103
|
# made since we established the connection
|
@@ -80,8 +80,8 @@ module ActiveRecord
|
|
80
80
|
|
81
81
|
def new_column_from_field(table_name, field)
|
82
82
|
type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
|
83
|
-
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\(
|
84
|
-
default, default_function = nil,
|
83
|
+
if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(field[:Default])
|
84
|
+
default, default_function = nil, field[:Default]
|
85
85
|
else
|
86
86
|
default, default_function = field[:Default], nil
|
87
87
|
end
|
@@ -33,7 +33,13 @@ module ActiveRecord
|
|
33
33
|
|
34
34
|
def cast(value)
|
35
35
|
if value.is_a?(::String)
|
36
|
-
value =
|
36
|
+
value = begin
|
37
|
+
@pg_decoder.decode(value)
|
38
|
+
rescue TypeError
|
39
|
+
# malformed array string is treated as [], will raise in PG 2.0 gem
|
40
|
+
# this keeps a consistent implementation
|
41
|
+
[]
|
42
|
+
end
|
37
43
|
end
|
38
44
|
type_cast_array(value, :cast)
|
39
45
|
end
|
@@ -66,6 +72,10 @@ module ActiveRecord
|
|
66
72
|
deserialize(raw_old_value) != new_value
|
67
73
|
end
|
68
74
|
|
75
|
+
def force_equality?(value)
|
76
|
+
value.is_a?(::Array)
|
77
|
+
end
|
78
|
+
|
69
79
|
private
|
70
80
|
|
71
81
|
def type_cast_array(value, method)
|
@@ -17,6 +17,42 @@ module ActiveRecord
|
|
17
17
|
"VALIDATE CONSTRAINT #{quote_column_name(name)}"
|
18
18
|
end
|
19
19
|
|
20
|
+
def visit_ChangeColumnDefinition(o)
|
21
|
+
column = o.column
|
22
|
+
column.sql_type = type_to_sql(column.type, column.options)
|
23
|
+
quoted_column_name = quote_column_name(o.name)
|
24
|
+
|
25
|
+
change_column_sql = "ALTER COLUMN #{quoted_column_name} TYPE #{column.sql_type}".dup
|
26
|
+
|
27
|
+
options = column_options(column)
|
28
|
+
|
29
|
+
if options[:collation]
|
30
|
+
change_column_sql << " COLLATE \"#{options[:collation]}\""
|
31
|
+
end
|
32
|
+
|
33
|
+
if options[:using]
|
34
|
+
change_column_sql << " USING #{options[:using]}"
|
35
|
+
elsif options[:cast_as]
|
36
|
+
cast_as_type = type_to_sql(options[:cast_as], options)
|
37
|
+
change_column_sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
38
|
+
end
|
39
|
+
|
40
|
+
if options.key?(:default)
|
41
|
+
if options[:default].nil?
|
42
|
+
change_column_sql << ", ALTER COLUMN #{quoted_column_name} DROP DEFAULT"
|
43
|
+
else
|
44
|
+
quoted_default = quote_default_expression(options[:default], column)
|
45
|
+
change_column_sql << ", ALTER COLUMN #{quoted_column_name} SET DEFAULT #{quoted_default}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
if options.key?(:null)
|
50
|
+
change_column_sql << ", ALTER COLUMN #{quoted_column_name} #{options[:null] ? 'DROP' : 'SET'} NOT NULL"
|
51
|
+
end
|
52
|
+
|
53
|
+
change_column_sql
|
54
|
+
end
|
55
|
+
|
20
56
|
def add_column_options!(sql, options)
|
21
57
|
if options[:collation]
|
22
58
|
sql << " COLLATE \"#{options[:collation]}\""
|
@@ -124,7 +124,7 @@ module ActiveRecord
|
|
124
124
|
|
125
125
|
# add info on sort order (only desc order is explicitly specified, asc is the default)
|
126
126
|
# and non-default opclasses
|
127
|
-
expressions.scan(/(?<column>\w+)
|
127
|
+
expressions.scan(/(?<column>\w+)"?\s?(?<opclass>\w+_ops)?\s?(?<desc>DESC)?\s?(?<nulls>NULLS (?:FIRST|LAST))?/).each do |column, opclass, desc, nulls|
|
128
128
|
opclasses[column] = opclass.to_sym if opclass
|
129
129
|
if nulls
|
130
130
|
orders[column] = [desc, nulls].compact.join(" ")
|
@@ -683,34 +683,20 @@ module ActiveRecord
|
|
683
683
|
end
|
684
684
|
end
|
685
685
|
|
686
|
-
def
|
687
|
-
|
688
|
-
|
689
|
-
sql = "ALTER COLUMN #{quoted_column_name} TYPE #{sql_type}".dup
|
690
|
-
if options[:collation]
|
691
|
-
sql << " COLLATE \"#{options[:collation]}\""
|
692
|
-
end
|
693
|
-
if options[:using]
|
694
|
-
sql << " USING #{options[:using]}"
|
695
|
-
elsif options[:cast_as]
|
696
|
-
cast_as_type = type_to_sql(options[:cast_as], options)
|
697
|
-
sql << " USING CAST(#{quoted_column_name} AS #{cast_as_type})"
|
698
|
-
end
|
699
|
-
|
700
|
-
sql
|
686
|
+
def add_column_for_alter(table_name, column_name, type, options = {})
|
687
|
+
return super unless options.key?(:comment)
|
688
|
+
[super, Proc.new { change_column_comment(table_name, column_name, options[:comment]) }]
|
701
689
|
end
|
702
690
|
|
703
691
|
def change_column_for_alter(table_name, column_name, type, options = {})
|
704
|
-
|
705
|
-
|
706
|
-
sqls
|
692
|
+
td = create_table_definition(table_name)
|
693
|
+
cd = td.new_column_definition(column_name, type, options)
|
694
|
+
sqls = [schema_creation.accept(ChangeColumnDefinition.new(cd, column_name))]
|
707
695
|
sqls << Proc.new { change_column_comment(table_name, column_name, options[:comment]) } if options.key?(:comment)
|
708
696
|
sqls
|
709
697
|
end
|
710
698
|
|
711
|
-
|
712
|
-
# Changes the default value of a table column.
|
713
|
-
def change_column_default_for_alter(table_name, column_name, default_or_changes) # :nodoc:
|
699
|
+
def change_column_default_for_alter(table_name, column_name, default_or_changes)
|
714
700
|
column = column_for(table_name, column_name)
|
715
701
|
return unless column
|
716
702
|
|
@@ -725,8 +711,8 @@ module ActiveRecord
|
|
725
711
|
end
|
726
712
|
end
|
727
713
|
|
728
|
-
def change_column_null_for_alter(table_name, column_name, null, default = nil)
|
729
|
-
"ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
|
714
|
+
def change_column_null_for_alter(table_name, column_name, null, default = nil)
|
715
|
+
"ALTER COLUMN #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL"
|
730
716
|
end
|
731
717
|
|
732
718
|
def add_timestamps_for_alter(table_name, options = {})
|
@@ -751,7 +737,7 @@ module ActiveRecord
|
|
751
737
|
|
752
738
|
def data_source_sql(name = nil, type: nil)
|
753
739
|
scope = quoted_scope(name, type: type)
|
754
|
-
scope[:type] ||= "'r','v','m','f'" # (r)elation/table, (v)iew, (m)aterialized view, (f)oreign table
|
740
|
+
scope[:type] ||= "'r','v','m','p','f'" # (r)elation/table, (v)iew, (m)aterialized view, (p)artitioned table, (f)oreign table
|
755
741
|
|
756
742
|
sql = "SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace".dup
|
757
743
|
sql << " WHERE n.nspname = #{scope[:schema]}"
|
@@ -765,7 +751,7 @@ module ActiveRecord
|
|
765
751
|
type = \
|
766
752
|
case type
|
767
753
|
when "BASE TABLE"
|
768
|
-
"'r'"
|
754
|
+
"'r','p'"
|
769
755
|
when "VIEW"
|
770
756
|
"'v','m'"
|
771
757
|
when "FOREIGN TABLE"
|
@@ -68,7 +68,7 @@ module ActiveRecord
|
|
68
68
|
# * <tt>"schema_name".table_name</tt>
|
69
69
|
# * <tt>"schema.name"."table name"</tt>
|
70
70
|
def extract_schema_qualified_name(string)
|
71
|
-
schema, table = string.scan(/[^"
|
71
|
+
schema, table = string.scan(/[^".]+|"[^"]*"/)
|
72
72
|
if table.nil?
|
73
73
|
table = schema
|
74
74
|
schema = nil
|
@@ -4,6 +4,14 @@
|
|
4
4
|
gem "pg", ">= 0.18", "< 2.0"
|
5
5
|
require "pg"
|
6
6
|
|
7
|
+
# Use async_exec instead of exec_params on pg versions before 1.1
|
8
|
+
class ::PG::Connection
|
9
|
+
unless self.public_method_defined?(:async_exec_params)
|
10
|
+
remove_method :exec_params
|
11
|
+
alias exec_params async_exec
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
7
15
|
require "active_record/connection_adapters/abstract_adapter"
|
8
16
|
require "active_record/connection_adapters/statement_pool"
|
9
17
|
require "active_record/connection_adapters/postgresql/column"
|
@@ -600,7 +608,7 @@ module ActiveRecord
|
|
600
608
|
type_casted_binds = type_casted_binds(binds)
|
601
609
|
log(sql, name, binds, type_casted_binds) do
|
602
610
|
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
603
|
-
@connection.
|
611
|
+
@connection.exec_params(sql, type_casted_binds)
|
604
612
|
end
|
605
613
|
end
|
606
614
|
end
|
@@ -12,11 +12,16 @@ module ActiveRecord
|
|
12
12
|
quote_column_name(attr)
|
13
13
|
end
|
14
14
|
|
15
|
+
def quote_table_name(name)
|
16
|
+
@quoted_table_names[name] ||= super.gsub(".", "\".\"").freeze
|
17
|
+
end
|
18
|
+
|
15
19
|
def quote_column_name(name)
|
16
20
|
@quoted_column_names[name] ||= %Q("#{super.gsub('"', '""')}").freeze
|
17
21
|
end
|
18
22
|
|
19
23
|
def quoted_time(value)
|
24
|
+
value = value.change(year: 2000, month: 1, day: 1)
|
20
25
|
quoted_date(value).sub(/\A\d\d\d\d-\d\d-\d\d /, "2000-01-01 ")
|
21
26
|
end
|
22
27
|
|
@@ -7,6 +7,10 @@ module ActiveRecord
|
|
7
7
|
# Returns an array of indexes for the given table.
|
8
8
|
def indexes(table_name)
|
9
9
|
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
|
10
|
+
# Indexes SQLite creates implicitly for internal use start with "sqlite_".
|
11
|
+
# See https://www.sqlite.org/fileformat2.html#intschema
|
12
|
+
next if row["name"].starts_with?("sqlite_")
|
13
|
+
|
10
14
|
index_sql = query_value(<<-SQL, "SCHEMA")
|
11
15
|
SELECT sql
|
12
16
|
FROM sqlite_master
|
@@ -40,7 +44,7 @@ module ActiveRecord
|
|
40
44
|
where: where,
|
41
45
|
orders: orders
|
42
46
|
)
|
43
|
-
end
|
47
|
+
end.compact
|
44
48
|
end
|
45
49
|
|
46
50
|
def create_schema_dumper(options)
|
@@ -9,7 +9,7 @@ require "active_record/connection_adapters/sqlite3/schema_definitions"
|
|
9
9
|
require "active_record/connection_adapters/sqlite3/schema_dumper"
|
10
10
|
require "active_record/connection_adapters/sqlite3/schema_statements"
|
11
11
|
|
12
|
-
gem "sqlite3", "~> 1.3.6"
|
12
|
+
gem "sqlite3", "~> 1.3", ">= 1.3.6"
|
13
13
|
require "sqlite3"
|
14
14
|
|
15
15
|
module ActiveRecord
|
@@ -453,9 +453,6 @@ module ActiveRecord
|
|
453
453
|
def copy_table_indexes(from, to, rename = {})
|
454
454
|
indexes(from).each do |index|
|
455
455
|
name = index.name
|
456
|
-
# indexes sqlite creates for internal use start with `sqlite_` and
|
457
|
-
# don't need to be copied
|
458
|
-
next if name.starts_with?("sqlite_")
|
459
456
|
if to == "a#{from}"
|
460
457
|
name = "t#{name}"
|
461
458
|
elsif from == "a#{to}"
|
data/lib/active_record/core.rb
CHANGED
@@ -184,7 +184,8 @@ module ActiveRecord
|
|
184
184
|
end
|
185
185
|
|
186
186
|
def find_by(*args) # :nodoc:
|
187
|
-
return super if scope_attributes? || reflect_on_all_aggregations.any?
|
187
|
+
return super if scope_attributes? || reflect_on_all_aggregations.any? ||
|
188
|
+
columns_hash.key?(inheritance_column) && base_class != self
|
188
189
|
|
189
190
|
hash = args.first
|
190
191
|
|
@@ -47,8 +47,12 @@ module ActiveRecord
|
|
47
47
|
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
48
48
|
counter_name = reflection.counter_cache_column
|
49
49
|
|
50
|
-
updates = { counter_name
|
51
|
-
|
50
|
+
updates = { counter_name => object.send(counter_association).count(:all) }
|
51
|
+
|
52
|
+
if touch
|
53
|
+
names = touch if touch != true
|
54
|
+
updates.merge!(touch_attributes_with_time(*names))
|
55
|
+
end
|
52
56
|
|
53
57
|
unscoped.where(primary_key => object.id).update_all(updates)
|
54
58
|
end
|
@@ -68,8 +72,8 @@ module ActiveRecord
|
|
68
72
|
# * +counters+ - A Hash containing the names of the fields
|
69
73
|
# to update as keys and the amount to update the field by as values.
|
70
74
|
# * <tt>:touch</tt> option - Touch timestamp columns when updating.
|
71
|
-
#
|
72
|
-
#
|
75
|
+
# If attribute names are passed, they are updated along with updated_at/on
|
76
|
+
# attributes.
|
73
77
|
#
|
74
78
|
# ==== Examples
|
75
79
|
#
|
@@ -107,11 +111,18 @@ module ActiveRecord
|
|
107
111
|
end
|
108
112
|
|
109
113
|
if touch
|
110
|
-
|
114
|
+
names = touch if touch != true
|
115
|
+
touch_updates = touch_attributes_with_time(*names)
|
111
116
|
updates << sanitize_sql_for_assignment(touch_updates) unless touch_updates.empty?
|
112
117
|
end
|
113
118
|
|
114
|
-
|
119
|
+
if id.is_a?(Relation) && self == id.klass
|
120
|
+
relation = id
|
121
|
+
else
|
122
|
+
relation = unscoped.where!(primary_key => id)
|
123
|
+
end
|
124
|
+
|
125
|
+
relation.update_all updates.join(", ")
|
115
126
|
end
|
116
127
|
|
117
128
|
# Increment a numeric field by one, via a direct SQL update.
|
@@ -165,13 +176,6 @@ module ActiveRecord
|
|
165
176
|
def decrement_counter(counter_name, id, touch: nil)
|
166
177
|
update_counters(id, counter_name => -1, touch: touch)
|
167
178
|
end
|
168
|
-
|
169
|
-
private
|
170
|
-
def touch_updates(touch)
|
171
|
-
touch = timestamp_attributes_for_update_in_model if touch == true
|
172
|
-
touch_time = current_time_from_proper_timezone
|
173
|
-
Array(touch).map { |column| [ column, touch_time ] }.to_h
|
174
|
-
end
|
175
179
|
end
|
176
180
|
|
177
181
|
private
|