activerecord 7.0.5.1 → 7.0.7
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 +112 -2
- data/README.rdoc +2 -2
- data/lib/active_record/associations/has_one_association.rb +0 -4
- data/lib/active_record/associations/singular_association.rb +5 -7
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +0 -17
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +8 -6
- data/lib/active_record/connection_adapters/mysql/quoting.rb +5 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.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/sqlite3/quoting.rb +5 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +2 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -0
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/locking/optimistic.rb +32 -18
- 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/predicate_builder/association_query_value.rb +20 -1
- data/lib/active_record/relation/predicate_builder.rb +1 -2
- data/lib/active_record/relation/query_attribute.rb +23 -0
- data/lib/active_record/relation/query_methods.rb +34 -7
- data/lib/active_record/result.rb +6 -4
- data/lib/active_record/table_metadata.rb +5 -1
- data/lib/active_record/transactions.rb +3 -3
- data/lib/active_record/type/serialized.rb +4 -0
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbd03f9e73d8cd308ad7eef63e3ece5612be70272833ffaa9f0c9f1251a1a0b6
|
4
|
+
data.tar.gz: c3efeab424b1d87a4c977ff53c0f97f60cfa89bd6d37723d24bd682c84f7f52a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07caf53afe430ac4a57d208fd7f46cc4430749bc6ee6d8b3666c934e37cde92d3ae2553c3ab0f3c31d18ccedcf5efc89540863ba777c7cea2eefa6e3badad17a
|
7
|
+
data.tar.gz: d9fbd502b44cdf8d53b3ff6c75f58f98aec5580bc829be6bd9511da7302ec1483ec747c9871839951d7fb37a0485c44d44de24f3bbab432db2a69d23e8c1565a
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,118 @@
|
|
1
|
+
## Rails 7.0.7 (August 09, 2023) ##
|
2
|
+
|
3
|
+
* Restores functionality to the missing method when using enums and fixes.
|
4
|
+
|
5
|
+
*paulreece*
|
6
|
+
|
7
|
+
* Fix `StatementCache::Substitute` with serialized type.
|
8
|
+
|
9
|
+
*ywenc*
|
10
|
+
|
11
|
+
* Fix `:db_runtime` on notification payload when application have multiple databases.
|
12
|
+
|
13
|
+
*Eileen M. Uchitelle*
|
14
|
+
|
15
|
+
* Correctly dump check constraints for MySQL 8.0.16+.
|
16
|
+
|
17
|
+
*Steve Hill*
|
18
|
+
|
19
|
+
* Fix `ActiveRecord::QueryMethods#in_order_of` to include `nil`s, to match the
|
20
|
+
behavior of `Enumerable#in_order_of`.
|
21
|
+
|
22
|
+
For example, `Post.in_order_of(:title, [nil, "foo"])` will now include posts
|
23
|
+
with `nil` titles, the same as `Post.all.to_a.in_order_of(:title, [nil, "foo"])`.
|
24
|
+
|
25
|
+
*fatkodima*
|
26
|
+
|
27
|
+
* Revert "Fix autosave associations with validations added on `:base` of the associated objects."
|
28
|
+
|
29
|
+
This change intended to remove the :base attribute from the message,
|
30
|
+
but broke many assumptions which key these errors were stored.
|
31
|
+
|
32
|
+
*zzak*
|
33
|
+
|
34
|
+
* Fix `#previously_new_record?` to return true for destroyed records.
|
35
|
+
|
36
|
+
Before, if a record was created and then destroyed, `#previously_new_record?` would return true.
|
37
|
+
Now, any UPDATE or DELETE to a record is considered a change, and will result in `#previously_new_record?`
|
38
|
+
returning false.
|
39
|
+
|
40
|
+
*Adrianna Chang*
|
41
|
+
|
42
|
+
* Revert breaking changes to `has_one` relationship deleting the old record before the new one is validated.
|
43
|
+
|
44
|
+
*zzak*
|
45
|
+
|
46
|
+
* Fix support for Active Record instances being uses in queries.
|
47
|
+
|
48
|
+
As of `7.0.5`, query arguments were deep duped to avoid mutations impacting
|
49
|
+
the query cache, but this had the adverse effect to clearing the primary key when
|
50
|
+
the query argument contained an `ActiveRecord::Base` instance.
|
51
|
+
|
52
|
+
This broke the `noticed` gem.
|
53
|
+
|
54
|
+
*Jean Boussier*
|
55
|
+
|
56
|
+
|
57
|
+
## Rails 7.0.6 (June 29, 2023) ##
|
58
|
+
|
59
|
+
* Fix autosave associations with validations added on `:base` of the associated objects.
|
60
|
+
|
61
|
+
*fatkodima*
|
62
|
+
|
63
|
+
* Fix result with anonymous PostgreSQL columns of different type from json.
|
64
|
+
|
65
|
+
*Oleksandr Avoiants*
|
66
|
+
|
67
|
+
* Preserve timestamp when setting an `ActiveSupport::TimeWithZone` value to `timestamptz` attribute.
|
68
|
+
|
69
|
+
*fatkodima*
|
70
|
+
|
71
|
+
* Fix assignment into an `has_one` relationship deleting the old record before the new one is validated.
|
72
|
+
|
73
|
+
*Jean Boussier*
|
74
|
+
|
75
|
+
* Fix where on association with has_one/has_many polymorphic relations.
|
76
|
+
|
77
|
+
Before:
|
78
|
+
```ruby
|
79
|
+
Treasure.where(price_estimates: PriceEstimate.all)
|
80
|
+
#=> SELECT (...) WHERE "treasures"."id" IN (SELECT "price_estimates"."estimate_of_id" FROM "price_estimates")
|
81
|
+
```
|
82
|
+
|
83
|
+
Later:
|
84
|
+
```ruby
|
85
|
+
Treasure.where(price_estimates: PriceEstimate.all)
|
86
|
+
#=> SELECT (...) WHERE "treasures"."id" IN (SELECT "price_estimates"."estimate_of_id" FROM "price_estimates" WHERE "price_estimates"."estimate_of_type" = 'Treasure')
|
87
|
+
```
|
88
|
+
|
89
|
+
*Lázaro Nixon*
|
90
|
+
|
91
|
+
* Fix decrementing counter caches on optimistically locked record deletion
|
92
|
+
|
93
|
+
*fatkodima*
|
94
|
+
|
95
|
+
* Ensure binary-destined values have binary encoding during type cast.
|
96
|
+
|
97
|
+
*Matthew Draper*
|
98
|
+
|
99
|
+
* Preserve existing column default functions when altering table in SQLite.
|
100
|
+
|
101
|
+
*fatkodima*
|
102
|
+
|
103
|
+
* Remove table alias added when using `where.missing` or `where.associated`.
|
104
|
+
|
105
|
+
*fatkodima*
|
106
|
+
|
107
|
+
* Fix `Enumerable#in_order_of` to only flatten first level to preserve nesting.
|
108
|
+
|
109
|
+
*Miha Rekar*
|
110
|
+
|
111
|
+
|
1
112
|
## Rails 7.0.5.1 (June 26, 2023) ##
|
2
113
|
|
3
114
|
* No changes.
|
4
115
|
|
5
|
-
|
6
116
|
## Rails 7.0.5 (May 24, 2023) ##
|
7
117
|
|
8
118
|
* Type cast `#attribute_changed?` `:from` and `:to` options.
|
@@ -81,7 +191,7 @@
|
|
81
191
|
|
82
192
|
*Shota Toguchi*, *Yusaku Ono*
|
83
193
|
|
84
|
-
*
|
194
|
+
* Fix erroneous nil default precision on virtual datetime columns.
|
85
195
|
|
86
196
|
Prior to this change, virtual datetime columns did not have the same
|
87
197
|
default precision as regular datetime columns, resulting in the following
|
data/README.rdoc
CHANGED
@@ -192,7 +192,7 @@ The latest version of Active Record can be installed with RubyGems:
|
|
192
192
|
|
193
193
|
$ gem install activerecord
|
194
194
|
|
195
|
-
Source code can be downloaded as part of the Rails project on GitHub:
|
195
|
+
Source code can be downloaded as part of the \Rails project on GitHub:
|
196
196
|
|
197
197
|
* https://github.com/rails/rails/tree/main/activerecord
|
198
198
|
|
@@ -210,7 +210,7 @@ API documentation is at:
|
|
210
210
|
|
211
211
|
* https://api.rubyonrails.org
|
212
212
|
|
213
|
-
Bug reports for the Ruby on Rails project can be filed here:
|
213
|
+
Bug reports for the Ruby on \Rails project can be filed here:
|
214
214
|
|
215
215
|
* https://github.com/rails/rails/issues
|
216
216
|
|
@@ -54,13 +54,11 @@ module ActiveRecord
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def _create_record(attributes, raise_error = false, &block)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
record
|
63
|
-
end
|
57
|
+
record = build_record(attributes, &block)
|
58
|
+
saved = record.save
|
59
|
+
set_new_record(record)
|
60
|
+
raise RecordInvalid.new(record) if !saved && raise_error
|
61
|
+
record
|
64
62
|
end
|
65
63
|
end
|
66
64
|
end
|
@@ -71,14 +71,6 @@ module ActiveRecord
|
|
71
71
|
/\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
|
72
72
|
end
|
73
73
|
|
74
|
-
def self.quoted_column_names # :nodoc:
|
75
|
-
@quoted_column_names ||= {}
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.quoted_table_names # :nodoc:
|
79
|
-
@quoted_table_names ||= {}
|
80
|
-
end
|
81
|
-
|
82
74
|
def initialize(connection, logger = nil, config = {}) # :nodoc:
|
83
75
|
super()
|
84
76
|
|
@@ -665,15 +657,6 @@ module ActiveRecord
|
|
665
657
|
migration_context.current_version
|
666
658
|
end
|
667
659
|
|
668
|
-
def field_ordered_value(column, values) # :nodoc:
|
669
|
-
node = Arel::Nodes::Case.new(column)
|
670
|
-
values.each.with_index(1) do |value, order|
|
671
|
-
node.when(value).then(order)
|
672
|
-
end
|
673
|
-
|
674
|
-
Arel::Nodes::Ascending.new(node.else(values.length + 1))
|
675
|
-
end
|
676
|
-
|
677
660
|
class << self
|
678
661
|
private
|
679
662
|
def initialize_type_map(m)
|
@@ -138,11 +138,6 @@ module ActiveRecord
|
|
138
138
|
true
|
139
139
|
end
|
140
140
|
|
141
|
-
def field_ordered_value(column, values) # :nodoc:
|
142
|
-
field = Arel::Nodes::NamedFunction.new("FIELD", [column, values.reverse.map { |value| Arel::Nodes.build_quoted(value) }])
|
143
|
-
Arel::Nodes::Descending.new(field)
|
144
|
-
end
|
145
|
-
|
146
141
|
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
|
147
142
|
query_value("SELECT GET_LOCK(#{quote(lock_name.to_s)}, #{timeout})") == 1
|
148
143
|
end
|
@@ -438,7 +433,8 @@ module ActiveRecord
|
|
438
433
|
name: row["name"]
|
439
434
|
}
|
440
435
|
expression = row["expression"]
|
441
|
-
expression = expression[1..-2]
|
436
|
+
expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")")
|
437
|
+
expression = strip_whitespace_characters(expression)
|
442
438
|
CheckConstraintDefinition.new(table_name, expression, options)
|
443
439
|
end
|
444
440
|
else
|
@@ -619,6 +615,12 @@ module ActiveRecord
|
|
619
615
|
end
|
620
616
|
|
621
617
|
private
|
618
|
+
def strip_whitespace_characters(expression)
|
619
|
+
expression = expression.gsub(/\\n|\\\\/, "")
|
620
|
+
expression = expression.gsub(/\s{2,}/, " ")
|
621
|
+
expression
|
622
|
+
end
|
623
|
+
|
622
624
|
def text_type?(type)
|
623
625
|
TYPE_MAP.lookup(type).is_a?(Type::String) || TYPE_MAP.lookup(type).is_a?(Type::Text)
|
624
626
|
end
|
@@ -6,6 +6,9 @@ module ActiveRecord
|
|
6
6
|
module ConnectionAdapters
|
7
7
|
module MySQL
|
8
8
|
module Quoting # :nodoc:
|
9
|
+
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
10
|
+
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
11
|
+
|
9
12
|
def quote_bound_value(value)
|
10
13
|
case value
|
11
14
|
when Rational
|
@@ -24,11 +27,11 @@ module ActiveRecord
|
|
24
27
|
end
|
25
28
|
|
26
29
|
def quote_column_name(name)
|
27
|
-
|
30
|
+
QUOTED_COLUMN_NAMES[name] ||= "`#{super.gsub('`', '``')}`"
|
28
31
|
end
|
29
32
|
|
30
33
|
def quote_table_name(name)
|
31
|
-
|
34
|
+
QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "`.`").freeze
|
32
35
|
end
|
33
36
|
|
34
37
|
def unquoted_true
|
@@ -57,7 +57,7 @@ module ActiveRecord
|
|
57
57
|
fields.each_with_index do |fname, i|
|
58
58
|
ftype = result.ftype i
|
59
59
|
fmod = result.fmod i
|
60
|
-
types[fname] = get_oid_type(ftype, fmod, fname)
|
60
|
+
types[fname] = types[i] = get_oid_type(ftype, fmod, fname)
|
61
61
|
end
|
62
62
|
build_result(columns: fields, rows: result.values, column_types: types)
|
63
63
|
end
|
@@ -13,7 +13,7 @@ module ActiveRecord
|
|
13
13
|
return if value.blank?
|
14
14
|
|
15
15
|
time = super
|
16
|
-
return time
|
16
|
+
return time if time.is_a?(ActiveSupport::TimeWithZone) || !time.acts_like?(:time)
|
17
17
|
|
18
18
|
# While in UTC mode, the PG gem may not return times back in "UTC" even if they were provided to Postgres in UTC.
|
19
19
|
# We prefer times always in UTC, so here we convert back.
|
@@ -4,6 +4,9 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module PostgreSQL
|
6
6
|
module Quoting
|
7
|
+
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
8
|
+
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
9
|
+
|
7
10
|
class IntegerOutOf64BitRange < StandardError
|
8
11
|
def initialize(msg)
|
9
12
|
super(msg)
|
@@ -81,7 +84,7 @@ module ActiveRecord
|
|
81
84
|
# - "schema.name".table_name
|
82
85
|
# - "schema.name"."table.name"
|
83
86
|
def quote_table_name(name) # :nodoc:
|
84
|
-
|
87
|
+
QUOTED_TABLE_NAMES[name] ||= Utils.extract_schema_qualified_name(name.to_s).quoted.freeze
|
85
88
|
end
|
86
89
|
|
87
90
|
# Quotes schema names for use in SQL queries.
|
@@ -95,7 +98,7 @@ module ActiveRecord
|
|
95
98
|
|
96
99
|
# Quotes column names for use in SQL queries.
|
97
100
|
def quote_column_name(name) # :nodoc:
|
98
|
-
|
101
|
+
QUOTED_COLUMN_NAMES[name] ||= PG::Connection.quote_ident(super).freeze
|
99
102
|
end
|
100
103
|
|
101
104
|
# Quote date/time values for use in SQL input.
|
@@ -4,6 +4,9 @@ module ActiveRecord
|
|
4
4
|
module ConnectionAdapters
|
5
5
|
module SQLite3
|
6
6
|
module Quoting # :nodoc:
|
7
|
+
QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
|
8
|
+
QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
|
9
|
+
|
7
10
|
def quote_string(s)
|
8
11
|
@connection.class.quote(s)
|
9
12
|
end
|
@@ -13,11 +16,11 @@ module ActiveRecord
|
|
13
16
|
end
|
14
17
|
|
15
18
|
def quote_table_name(name)
|
16
|
-
|
19
|
+
QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "\".\"").freeze
|
17
20
|
end
|
18
21
|
|
19
22
|
def quote_column_name(name)
|
20
|
-
|
23
|
+
QUOTED_COLUMN_NAMES[name] ||= %Q("#{super.gsub('"', '""')}")
|
21
24
|
end
|
22
25
|
|
23
26
|
def quoted_time(value)
|
@@ -84,11 +84,11 @@ module ActiveRecord
|
|
84
84
|
table_sql = query_value(<<-SQL, "SCHEMA")
|
85
85
|
SELECT sql
|
86
86
|
FROM sqlite_master
|
87
|
-
WHERE name = #{
|
87
|
+
WHERE name = #{quote(table_name)} AND type = 'table'
|
88
88
|
UNION ALL
|
89
89
|
SELECT sql
|
90
90
|
FROM sqlite_temp_master
|
91
|
-
WHERE name = #{
|
91
|
+
WHERE name = #{quote(table_name)} AND type = 'table'
|
92
92
|
SQL
|
93
93
|
|
94
94
|
table_sql.to_s.scan(/CONSTRAINT\s+(?<name>\w+)\s+CHECK\s+\((?<expression>(:?[^()]|\(\g<expression>\))+)\)/i).map do |name, expression|
|
@@ -480,6 +480,7 @@ module ActiveRecord
|
|
480
480
|
if column.has_default?
|
481
481
|
type = lookup_cast_type_from_column(column)
|
482
482
|
default = type.deserialize(column.default)
|
483
|
+
default = -> { column.default_function } if default.nil?
|
483
484
|
end
|
484
485
|
|
485
486
|
@definition.column(column_name, column.type,
|
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Locking
|
5
|
-
# == What is Optimistic Locking
|
5
|
+
# == What is \Optimistic \Locking
|
6
6
|
#
|
7
7
|
# Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of
|
8
8
|
# conflicts with the data. It does this by checking whether another process has made changes to a record since
|
9
|
-
# it was opened, an
|
9
|
+
# it was opened, an ActiveRecord::StaleObjectError exception is thrown if that has occurred
|
10
10
|
# and the update is ignored.
|
11
11
|
#
|
12
|
-
# Check out
|
12
|
+
# Check out +ActiveRecord::Locking::Pessimistic+ for an alternative.
|
13
13
|
#
|
14
14
|
# == Usage
|
15
15
|
#
|
@@ -69,6 +69,11 @@ module ActiveRecord
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
def initialize_dup(other) # :nodoc:
|
73
|
+
super
|
74
|
+
_clear_locking_column if locking_enabled?
|
75
|
+
end
|
76
|
+
|
72
77
|
private
|
73
78
|
def _create_record(attribute_names = self.attribute_names)
|
74
79
|
if locking_enabled?
|
@@ -91,8 +96,7 @@ module ActiveRecord
|
|
91
96
|
locking_column = self.class.locking_column
|
92
97
|
lock_attribute_was = @attributes[locking_column]
|
93
98
|
|
94
|
-
update_constraints =
|
95
|
-
update_constraints[locking_column] = _lock_value_for_database(locking_column)
|
99
|
+
update_constraints = _query_constraints_hash
|
96
100
|
|
97
101
|
attribute_names = attribute_names.dup if attribute_names.frozen?
|
98
102
|
attribute_names << locking_column
|
@@ -118,16 +122,9 @@ module ActiveRecord
|
|
118
122
|
end
|
119
123
|
|
120
124
|
def destroy_row
|
121
|
-
|
122
|
-
|
123
|
-
locking_column = self.class.locking_column
|
125
|
+
affected_rows = super
|
124
126
|
|
125
|
-
|
126
|
-
delete_constraints[locking_column] = _lock_value_for_database(locking_column)
|
127
|
-
|
128
|
-
affected_rows = self.class._delete_record(delete_constraints)
|
129
|
-
|
130
|
-
if affected_rows != 1
|
127
|
+
if locking_enabled? && affected_rows != 1
|
131
128
|
raise ActiveRecord::StaleObjectError.new(self, "destroy")
|
132
129
|
end
|
133
130
|
|
@@ -142,6 +139,18 @@ module ActiveRecord
|
|
142
139
|
end
|
143
140
|
end
|
144
141
|
|
142
|
+
def _clear_locking_column
|
143
|
+
self[self.class.locking_column] = nil
|
144
|
+
clear_attribute_change(self.class.locking_column)
|
145
|
+
end
|
146
|
+
|
147
|
+
def _query_constraints_hash
|
148
|
+
return super unless locking_enabled?
|
149
|
+
|
150
|
+
locking_column = self.class.locking_column
|
151
|
+
super.merge(locking_column => _lock_value_for_database(locking_column))
|
152
|
+
end
|
153
|
+
|
145
154
|
module ClassMethods
|
146
155
|
DEFAULT_LOCKING_COLUMN = "lock_version"
|
147
156
|
|
@@ -159,10 +168,7 @@ module ActiveRecord
|
|
159
168
|
end
|
160
169
|
|
161
170
|
# The version column used for optimistic locking. Defaults to +lock_version+.
|
162
|
-
|
163
|
-
@locking_column = DEFAULT_LOCKING_COLUMN unless defined?(@locking_column)
|
164
|
-
@locking_column
|
165
|
-
end
|
171
|
+
attr_reader :locking_column
|
166
172
|
|
167
173
|
# Reset the column used for optimistic locking back to the +lock_version+ default.
|
168
174
|
def reset_locking_column
|
@@ -182,6 +188,14 @@ module ActiveRecord
|
|
182
188
|
end
|
183
189
|
super
|
184
190
|
end
|
191
|
+
|
192
|
+
private
|
193
|
+
def inherited(base)
|
194
|
+
super
|
195
|
+
base.class_eval do
|
196
|
+
@locking_column = DEFAULT_LOCKING_COLUMN
|
197
|
+
end
|
198
|
+
end
|
185
199
|
end
|
186
200
|
end
|
187
201
|
|
@@ -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
|
@@ -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)
|
@@ -65,8 +65,7 @@ module ActiveRecord
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def build_bind_attribute(column_name, value)
|
68
|
-
|
69
|
-
Relation::QueryAttribute.new(column_name, type.immutable_value(value), type)
|
68
|
+
Relation::QueryAttribute.new(column_name, value, table.type(column_name))
|
70
69
|
end
|
71
70
|
|
72
71
|
def resolve_arel_attribute(table_name, column_name, &block)
|
@@ -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
|
@@ -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
|
|
@@ -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
|
@@ -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.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.0.
|
4
|
+
version: 7.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 7.0.
|
19
|
+
version: 7.0.7
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 7.0.
|
26
|
+
version: 7.0.7
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activemodel
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - '='
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 7.0.
|
33
|
+
version: 7.0.7
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - '='
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 7.0.
|
40
|
+
version: 7.0.7
|
41
41
|
description: Databases on Rails. Build a persistent domain model by mapping database
|
42
42
|
tables to Ruby classes. Strong conventions for associations, validations, aggregations,
|
43
43
|
migrations, and testing come baked-in.
|
@@ -434,10 +434,10 @@ licenses:
|
|
434
434
|
- MIT
|
435
435
|
metadata:
|
436
436
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
437
|
-
changelog_uri: https://github.com/rails/rails/blob/v7.0.
|
438
|
-
documentation_uri: https://api.rubyonrails.org/v7.0.
|
437
|
+
changelog_uri: https://github.com/rails/rails/blob/v7.0.7/activerecord/CHANGELOG.md
|
438
|
+
documentation_uri: https://api.rubyonrails.org/v7.0.7/
|
439
439
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
440
|
-
source_code_uri: https://github.com/rails/rails/tree/v7.0.
|
440
|
+
source_code_uri: https://github.com/rails/rails/tree/v7.0.7/activerecord
|
441
441
|
rubygems_mfa_required: 'true'
|
442
442
|
post_install_message:
|
443
443
|
rdoc_options:
|
@@ -456,7 +456,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
456
456
|
- !ruby/object:Gem::Version
|
457
457
|
version: '0'
|
458
458
|
requirements: []
|
459
|
-
rubygems_version: 3.
|
459
|
+
rubygems_version: 3.4.10
|
460
460
|
signing_key:
|
461
461
|
specification_version: 4
|
462
462
|
summary: Object-relational mapper framework (part of Rails).
|