activerecord 5.2.7 → 6.0.0.beta1
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 +299 -778
- data/MIT-LICENSE +3 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations/association.rb +35 -19
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/belongs_to.rb +14 -50
- data/lib/active_record/associations/builder/collection_association.rb +3 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/collection_association.rb +11 -25
- data/lib/active_record/associations/collection_proxy.rb +32 -6
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +25 -18
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/join_dependency.rb +15 -20
- data/lib/active_record/associations/preloader/association.rb +1 -2
- data/lib/active_record/associations/preloader.rb +32 -29
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/associations.rb +16 -12
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods/dirty.rb +64 -26
- data/lib/active_record/attribute_methods/primary_key.rb +8 -7
- data/lib/active_record/attribute_methods/read.rb +16 -48
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +15 -16
- data/lib/active_record/attribute_methods.rb +34 -56
- data/lib/active_record/autosave_association.rb +7 -21
- data/lib/active_record/base.rb +2 -2
- data/lib/active_record/callbacks.rb +3 -17
- data/lib/active_record/collection_cache_key.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
- data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
- data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
- data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
- data/lib/active_record/connection_handling.rb +132 -26
- data/lib/active_record/core.rb +76 -43
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +74 -0
- data/lib/active_record/database_configurations.rb +184 -0
- data/lib/active_record/enum.rb +22 -7
- data/lib/active_record/errors.rb +24 -21
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +140 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +12 -2
- data/lib/active_record/integration.rb +56 -16
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +2 -2
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/migration/command_recorder.rb +35 -5
- data/lib/active_record/migration/compatibility.rb +34 -16
- data/lib/active_record/migration.rb +38 -37
- data/lib/active_record/model_schema.rb +30 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +18 -7
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +19 -11
- data/lib/active_record/railtie.rb +71 -42
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +94 -43
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +38 -28
- data/lib/active_record/relation/delegation.rb +4 -13
- data/lib/active_record/relation/finder_methods.rb +12 -25
- data/lib/active_record/relation/merger.rb +2 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/query_attribute.rb +15 -12
- data/lib/active_record/relation/query_methods.rb +29 -52
- data/lib/active_record/relation/where_clause.rb +4 -0
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/relation.rb +150 -69
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +2 -39
- data/lib/active_record/schema.rb +1 -10
- data/lib/active_record/schema_dumper.rb +12 -6
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +10 -3
- data/lib/active_record/scoping/named.rb +10 -14
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/statement_cache.rb +32 -5
- data/lib/active_record/store.rb +39 -8
- data/lib/active_record/table_metadata.rb +1 -4
- data/lib/active_record/tasks/database_tasks.rb +89 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +38 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/transactions.rb +3 -22
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type_caster/connection.rb +1 -6
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +13 -25
- data/lib/active_record.rb +2 -1
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +63 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values.rb +16 -0
- data/lib/arel/nodes/values_list.rb +24 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +67 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/depth_first.rb +199 -0
- data/lib/arel/visitors/dot.rb +292 -0
- data/lib/arel/visitors/ibm_db.rb +21 -0
- data/lib/arel/visitors/informix.rb +56 -0
- data/lib/arel/visitors/mssql.rb +143 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +67 -0
- data/lib/arel/visitors/postgresql.rb +116 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +913 -0
- data/lib/arel/visitors/visitor.rb +42 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +44 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- metadata +104 -26
@@ -9,6 +9,7 @@ module ActiveRecord
|
|
9
9
|
|
10
10
|
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
|
11
11
|
:reverse_order, :distinct, :create_with, :skip_query_cache]
|
12
|
+
|
12
13
|
CLAUSE_METHODS = [:where, :having, :from]
|
13
14
|
INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
|
14
15
|
|
@@ -18,6 +19,7 @@ module ActiveRecord
|
|
18
19
|
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
|
19
20
|
|
20
21
|
attr_reader :table, :klass, :loaded, :predicate_builder
|
22
|
+
attr_accessor :skip_preloading_value
|
21
23
|
alias :model :klass
|
22
24
|
alias :loaded? :loaded
|
23
25
|
alias :locked? :lock_value
|
@@ -41,6 +43,17 @@ module ActiveRecord
|
|
41
43
|
klass.arel_attribute(name, table)
|
42
44
|
end
|
43
45
|
|
46
|
+
def bind_attribute(name, value) # :nodoc:
|
47
|
+
if reflection = klass._reflect_on_association(name)
|
48
|
+
name = reflection.foreign_key
|
49
|
+
value = value.read_attribute(reflection.klass.primary_key) unless value.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
attr = arel_attribute(name)
|
53
|
+
bind = predicate_builder.build_bind_attribute(attr.name, value)
|
54
|
+
yield attr, bind
|
55
|
+
end
|
56
|
+
|
44
57
|
# Initializes new record from relation while maintaining the current
|
45
58
|
# scope.
|
46
59
|
#
|
@@ -54,7 +67,7 @@ module ActiveRecord
|
|
54
67
|
# user = users.new { |user| user.name = 'Oscar' }
|
55
68
|
# user.name # => Oscar
|
56
69
|
def new(attributes = nil, &block)
|
57
|
-
scoping { klass.new(
|
70
|
+
scoping { klass.new(attributes, &block) }
|
58
71
|
end
|
59
72
|
|
60
73
|
alias build new
|
@@ -79,11 +92,7 @@ module ActiveRecord
|
|
79
92
|
# users.create(name: nil) # validation on name
|
80
93
|
# # => #<User id: nil, name: nil, ...>
|
81
94
|
def create(attributes = nil, &block)
|
82
|
-
|
83
|
-
attributes.collect { |attr| create(attr, &block) }
|
84
|
-
else
|
85
|
-
scoping { klass.create(values_for_create(attributes), &block) }
|
86
|
-
end
|
95
|
+
scoping { klass.create(attributes, &block) }
|
87
96
|
end
|
88
97
|
|
89
98
|
# Similar to #create, but calls
|
@@ -93,11 +102,7 @@ module ActiveRecord
|
|
93
102
|
# Expects arguments in the same format as
|
94
103
|
# {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
|
95
104
|
def create!(attributes = nil, &block)
|
96
|
-
|
97
|
-
attributes.collect { |attr| create!(attr, &block) }
|
98
|
-
else
|
99
|
-
scoping { klass.create!(values_for_create(attributes), &block) }
|
100
|
-
end
|
105
|
+
scoping { klass.create!(attributes, &block) }
|
101
106
|
end
|
102
107
|
|
103
108
|
def first_or_create(attributes = nil, &block) # :nodoc:
|
@@ -143,23 +148,12 @@ module ActiveRecord
|
|
143
148
|
# failed due to validation errors it won't be persisted, you get what
|
144
149
|
# #create returns in such situation.
|
145
150
|
#
|
146
|
-
# Please note
|
151
|
+
# Please note <b>this method is not atomic</b>, it runs first a SELECT, and if
|
147
152
|
# there are no results an INSERT is attempted. If there are other threads
|
148
153
|
# or processes there is a race condition between both calls and it could
|
149
154
|
# be the case that you end up with two similar records.
|
150
155
|
#
|
151
|
-
#
|
152
|
-
# application, but in the particular case in which rows have a UNIQUE
|
153
|
-
# constraint an exception may be raised, just retry:
|
154
|
-
#
|
155
|
-
# begin
|
156
|
-
# CreditAccount.transaction(requires_new: true) do
|
157
|
-
# CreditAccount.find_or_create_by(user_id: user.id)
|
158
|
-
# end
|
159
|
-
# rescue ActiveRecord::RecordNotUnique
|
160
|
-
# retry
|
161
|
-
# end
|
162
|
-
#
|
156
|
+
# If this might be a problem for your application, please see #create_or_find_by.
|
163
157
|
def find_or_create_by(attributes, &block)
|
164
158
|
find_by(attributes) || create(attributes, &block)
|
165
159
|
end
|
@@ -171,6 +165,47 @@ module ActiveRecord
|
|
171
165
|
find_by(attributes) || create!(attributes, &block)
|
172
166
|
end
|
173
167
|
|
168
|
+
# Attempts to create a record with the given attributes in a table that has a unique constraint
|
169
|
+
# on one or several of its columns. If a row already exists with one or several of these
|
170
|
+
# unique constraints, the exception such an insertion would normally raise is caught,
|
171
|
+
# and the existing record with those attributes is found using #find_by!.
|
172
|
+
#
|
173
|
+
# This is similar to #find_or_create_by, but avoids the problem of stale reads between the SELECT
|
174
|
+
# and the INSERT, as that method needs to first query the table, then attempt to insert a row
|
175
|
+
# if none is found.
|
176
|
+
#
|
177
|
+
# There are several drawbacks to #create_or_find_by, though:
|
178
|
+
#
|
179
|
+
# * The underlying table must have the relevant columns defined with unique constraints.
|
180
|
+
# * A unique constraint violation may be triggered by only one, or at least less than all,
|
181
|
+
# of the given attributes. This means that the subsequent #find_by! may fail to find a
|
182
|
+
# matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
|
183
|
+
# rather than a record with the given attributes.
|
184
|
+
# * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
|
185
|
+
# we actually have another race condition between INSERT -> SELECT, which can be triggered
|
186
|
+
# if a DELETE between those two statements is run by another client. But for most applications,
|
187
|
+
# that's a significantly less likely condition to hit.
|
188
|
+
# * It relies on exception handling to handle control flow, which may be marginally slower.
|
189
|
+
#
|
190
|
+
# This method will return a record if all given attributes are covered by unique constraints
|
191
|
+
# (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
|
192
|
+
# and failed due to validation errors it won't be persisted, you get what #create returns in
|
193
|
+
# such situation.
|
194
|
+
def create_or_find_by(attributes, &block)
|
195
|
+
transaction(requires_new: true) { create(attributes, &block) }
|
196
|
+
rescue ActiveRecord::RecordNotUnique
|
197
|
+
find_by!(attributes)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Like #create_or_find_by, but calls
|
201
|
+
# {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
|
202
|
+
# is raised if the created record is invalid.
|
203
|
+
def create_or_find_by!(attributes, &block)
|
204
|
+
transaction(requires_new: true) { create!(attributes, &block) }
|
205
|
+
rescue ActiveRecord::RecordNotUnique
|
206
|
+
find_by!(attributes)
|
207
|
+
end
|
208
|
+
|
174
209
|
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
|
175
210
|
# instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
|
176
211
|
def find_or_initialize_by(attributes, &block)
|
@@ -185,7 +220,7 @@ module ActiveRecord
|
|
185
220
|
# are needed by the next ones when eager loading is going on.
|
186
221
|
#
|
187
222
|
# Please see further details in the
|
188
|
-
# {Active Record Query Interface guide}[
|
223
|
+
# {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
|
189
224
|
def explain
|
190
225
|
exec_explain(collecting_queries_for_explain { exec_queries })
|
191
226
|
end
|
@@ -277,10 +312,7 @@ module ActiveRecord
|
|
277
312
|
# Please check unscoped if you want to remove all previous scopes (including
|
278
313
|
# the default_scope) during the execution of a block.
|
279
314
|
def scoping
|
280
|
-
|
281
|
-
yield
|
282
|
-
ensure
|
283
|
-
klass.current_scope = previous unless @delegate_to_klass
|
315
|
+
@delegate_to_klass ? yield : klass._scoping(self) { yield }
|
284
316
|
end
|
285
317
|
|
286
318
|
def _exec_scope(*args, &block) # :nodoc:
|
@@ -321,17 +353,17 @@ module ActiveRecord
|
|
321
353
|
end
|
322
354
|
|
323
355
|
stmt = Arel::UpdateManager.new
|
324
|
-
|
325
|
-
stmt.
|
326
|
-
stmt.
|
327
|
-
|
328
|
-
|
329
|
-
|
356
|
+
stmt.table(arel.join_sources.empty? ? table : arel.source)
|
357
|
+
stmt.key = arel_attribute(primary_key)
|
358
|
+
stmt.take(arel.limit)
|
359
|
+
stmt.offset(arel.offset)
|
360
|
+
stmt.order(*arel.orders)
|
361
|
+
stmt.wheres = arel.constraints
|
362
|
+
|
363
|
+
if updates.is_a?(Hash)
|
364
|
+
stmt.set _substitute_values(updates)
|
330
365
|
else
|
331
|
-
stmt.
|
332
|
-
stmt.take(arel.limit)
|
333
|
-
stmt.order(*arel.orders)
|
334
|
-
stmt.wheres = arel.constraints
|
366
|
+
stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
|
335
367
|
end
|
336
368
|
|
337
369
|
@klass.connection.update stmt, "#{@klass} Update All"
|
@@ -345,6 +377,59 @@ module ActiveRecord
|
|
345
377
|
end
|
346
378
|
end
|
347
379
|
|
380
|
+
def update_counters(counters) # :nodoc:
|
381
|
+
touch = counters.delete(:touch)
|
382
|
+
|
383
|
+
updates = {}
|
384
|
+
counters.each do |counter_name, value|
|
385
|
+
attr = arel_attribute(counter_name)
|
386
|
+
bind = predicate_builder.build_bind_attribute(attr.name, value.abs)
|
387
|
+
expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attr), 0)
|
388
|
+
expr = value < 0 ? expr - bind : expr + bind
|
389
|
+
updates[counter_name] = expr.expr
|
390
|
+
end
|
391
|
+
|
392
|
+
if touch
|
393
|
+
names = touch if touch != true
|
394
|
+
touch_updates = klass.touch_attributes_with_time(*names)
|
395
|
+
updates.merge!(touch_updates) unless touch_updates.empty?
|
396
|
+
end
|
397
|
+
|
398
|
+
update_all updates
|
399
|
+
end
|
400
|
+
|
401
|
+
# Touches all records in the current relation without instantiating records first with the updated_at/on attributes
|
402
|
+
# set to the current time or the time specified.
|
403
|
+
# This method can be passed attribute names and an optional time argument.
|
404
|
+
# If attribute names are passed, they are updated along with updated_at/on attributes.
|
405
|
+
# If no time argument is passed, the current time is used as default.
|
406
|
+
#
|
407
|
+
# === Examples
|
408
|
+
#
|
409
|
+
# # Touch all records
|
410
|
+
# Person.all.touch_all
|
411
|
+
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"
|
412
|
+
#
|
413
|
+
# # Touch multiple records with a custom attribute
|
414
|
+
# Person.all.touch_all(:created_at)
|
415
|
+
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"
|
416
|
+
#
|
417
|
+
# # Touch multiple records with a specified time
|
418
|
+
# Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
|
419
|
+
# # => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"
|
420
|
+
#
|
421
|
+
# # Touch records with scope
|
422
|
+
# Person.where(name: 'David').touch_all
|
423
|
+
# # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
|
424
|
+
def touch_all(*names, time: nil)
|
425
|
+
if klass.locking_enabled?
|
426
|
+
names << { time: time }
|
427
|
+
update_counters(klass.locking_column => 1, touch: names)
|
428
|
+
else
|
429
|
+
update_all klass.touch_attributes_with_time(*names, time: time)
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
348
433
|
# Destroys the records by instantiating each
|
349
434
|
# record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
|
350
435
|
# Each object's callbacks are executed (including <tt>:dependent</tt> association options).
|
@@ -398,13 +483,12 @@ module ActiveRecord
|
|
398
483
|
end
|
399
484
|
|
400
485
|
stmt = Arel::DeleteManager.new
|
401
|
-
stmt.from(table)
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
end
|
486
|
+
stmt.from(arel.join_sources.empty? ? table : arel.source)
|
487
|
+
stmt.key = arel_attribute(primary_key)
|
488
|
+
stmt.take(arel.limit)
|
489
|
+
stmt.offset(arel.offset)
|
490
|
+
stmt.order(*arel.orders)
|
491
|
+
stmt.wheres = arel.constraints
|
408
492
|
|
409
493
|
affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
|
410
494
|
|
@@ -530,6 +614,16 @@ module ActiveRecord
|
|
530
614
|
ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
|
531
615
|
end
|
532
616
|
|
617
|
+
def preload_associations(records) # :nodoc:
|
618
|
+
preload = preload_values
|
619
|
+
preload += includes_values unless eager_loading?
|
620
|
+
preloader = nil
|
621
|
+
preload.each do |associations|
|
622
|
+
preloader ||= build_preloader
|
623
|
+
preloader.preload records, associations
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
533
627
|
protected
|
534
628
|
|
535
629
|
def load_records(records)
|
@@ -538,9 +632,15 @@ module ActiveRecord
|
|
538
632
|
end
|
539
633
|
|
540
634
|
private
|
541
|
-
|
542
|
-
|
543
|
-
|
635
|
+
def _substitute_values(values)
|
636
|
+
values.map do |name, value|
|
637
|
+
attr = arel_attribute(name)
|
638
|
+
unless Arel.arel_node?(value)
|
639
|
+
type = klass.type_for_attribute(attr.name)
|
640
|
+
value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
|
641
|
+
end
|
642
|
+
[attr, value]
|
643
|
+
end
|
544
644
|
end
|
545
645
|
|
546
646
|
def exec_queries(&block)
|
@@ -560,13 +660,7 @@ module ActiveRecord
|
|
560
660
|
klass.find_by_sql(arel, &block).freeze
|
561
661
|
end
|
562
662
|
|
563
|
-
|
564
|
-
preload += includes_values unless eager_loading?
|
565
|
-
preloader = nil
|
566
|
-
preload.each do |associations|
|
567
|
-
preloader ||= build_preloader
|
568
|
-
preloader.preload @records, associations
|
569
|
-
end
|
663
|
+
preload_associations(@records) unless skip_preloading_value
|
570
664
|
|
571
665
|
@records.each(&:readonly!) if readonly_value
|
572
666
|
|
@@ -612,18 +706,5 @@ module ActiveRecord
|
|
612
706
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
613
707
|
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ["raw_sql_"]
|
614
708
|
end
|
615
|
-
|
616
|
-
def values_for_create(attributes = nil)
|
617
|
-
result = attributes ? where_values_hash.merge!(attributes) : where_values_hash
|
618
|
-
|
619
|
-
# NOTE: if there are same keys in both create_with and result, create_with should be used.
|
620
|
-
# This is to make sure nested attributes don't get passed to the klass.new,
|
621
|
-
# while keeping the precedence of the duplicate keys in create_with.
|
622
|
-
create_with_value.stringify_keys.each do |k, v|
|
623
|
-
result[k] = v if result.key?(k)
|
624
|
-
end
|
625
|
-
|
626
|
-
result
|
627
|
-
end
|
628
709
|
end
|
629
710
|
end
|
data/lib/active_record/result.rb
CHANGED
@@ -21,7 +21,7 @@ module ActiveRecord
|
|
21
21
|
# ]
|
22
22
|
#
|
23
23
|
# # Get an array of hashes representing the result (column => value):
|
24
|
-
# result.
|
24
|
+
# result.to_a
|
25
25
|
# # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
|
26
26
|
# {"id" => 2, "title" => "title_2", "body" => "body_2"},
|
27
27
|
# ...
|
@@ -43,6 +43,11 @@ module ActiveRecord
|
|
43
43
|
@column_types = column_types
|
44
44
|
end
|
45
45
|
|
46
|
+
# Returns true if this result set includes the column named +name+
|
47
|
+
def includes_column?(name)
|
48
|
+
@columns.include? name
|
49
|
+
end
|
50
|
+
|
46
51
|
# Returns the number of elements in the rows array.
|
47
52
|
def length
|
48
53
|
@rows.length
|
@@ -60,9 +65,12 @@ module ActiveRecord
|
|
60
65
|
end
|
61
66
|
end
|
62
67
|
|
63
|
-
# Returns an array of hashes representing each row record.
|
64
68
|
def to_hash
|
65
|
-
|
69
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
70
|
+
`ActiveRecord::Result#to_hash` has been renamed to `to_a`.
|
71
|
+
`to_hash` is deprecated and will be removed in Rails 6.1.
|
72
|
+
MSG
|
73
|
+
to_a
|
66
74
|
end
|
67
75
|
|
68
76
|
alias :map! :map
|
@@ -78,6 +86,8 @@ module ActiveRecord
|
|
78
86
|
hash_rows
|
79
87
|
end
|
80
88
|
|
89
|
+
alias :to_a :to_ary
|
90
|
+
|
81
91
|
def [](idx)
|
82
92
|
hash_rows[idx]
|
83
93
|
end
|
@@ -97,12 +107,21 @@ module ActiveRecord
|
|
97
107
|
end
|
98
108
|
|
99
109
|
def cast_values(type_overrides = {}) # :nodoc:
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
110
|
+
if columns.one?
|
111
|
+
# Separated to avoid allocating an array per row
|
112
|
+
|
113
|
+
type = column_type(columns.first, type_overrides)
|
104
114
|
|
105
|
-
|
115
|
+
rows.map do |(value)|
|
116
|
+
type.deserialize(value)
|
117
|
+
end
|
118
|
+
else
|
119
|
+
types = columns.map { |name| column_type(name, type_overrides) }
|
120
|
+
|
121
|
+
rows.map do |values|
|
122
|
+
Array.new(values.size) { |i| types[i].deserialize(values[i]) }
|
123
|
+
end
|
124
|
+
end
|
106
125
|
end
|
107
126
|
|
108
127
|
def initialize_copy(other)
|
@@ -125,7 +144,9 @@ module ActiveRecord
|
|
125
144
|
begin
|
126
145
|
# We freeze the strings to prevent them getting duped when
|
127
146
|
# used as keys in ActiveRecord::Base's @attributes hash
|
128
|
-
columns = @columns.map
|
147
|
+
columns = @columns.map(&:-@)
|
148
|
+
length = columns.length
|
149
|
+
|
129
150
|
@rows.map { |row|
|
130
151
|
# In the past we used Hash[columns.zip(row)]
|
131
152
|
# though elegant, the verbose way is much more efficient
|
@@ -134,8 +155,6 @@ module ActiveRecord
|
|
134
155
|
hash = {}
|
135
156
|
|
136
157
|
index = 0
|
137
|
-
length = columns.length
|
138
|
-
|
139
158
|
while index < length
|
140
159
|
hash[columns[index]] = row[index]
|
141
160
|
index += 1
|
@@ -61,8 +61,8 @@ module ActiveRecord
|
|
61
61
|
# # => "id ASC"
|
62
62
|
def sanitize_sql_for_order(condition)
|
63
63
|
if condition.is_a?(Array) && condition.first.to_s.include?("?")
|
64
|
-
|
65
|
-
|
64
|
+
disallow_raw_sql!([condition.first],
|
65
|
+
permit: AttributeMethods::ClassMethods::COLUMN_NAME_WITH_ORDER
|
66
66
|
)
|
67
67
|
|
68
68
|
# Ensure we aren't dealing with a subclass of String that might
|
@@ -134,43 +134,6 @@ module ActiveRecord
|
|
134
134
|
end
|
135
135
|
|
136
136
|
private
|
137
|
-
# Accepts a hash of SQL conditions and replaces those attributes
|
138
|
-
# that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
|
139
|
-
# relationship with their expanded aggregate attribute values.
|
140
|
-
#
|
141
|
-
# Given:
|
142
|
-
#
|
143
|
-
# class Person < ActiveRecord::Base
|
144
|
-
# composed_of :address, class_name: "Address",
|
145
|
-
# mapping: [%w(address_street street), %w(address_city city)]
|
146
|
-
# end
|
147
|
-
#
|
148
|
-
# Then:
|
149
|
-
#
|
150
|
-
# { address: Address.new("813 abc st.", "chicago") }
|
151
|
-
# # => { address_street: "813 abc st.", address_city: "chicago" }
|
152
|
-
def expand_hash_conditions_for_aggregates(attrs) # :doc:
|
153
|
-
expanded_attrs = {}
|
154
|
-
attrs.each do |attr, value|
|
155
|
-
if aggregation = reflect_on_aggregation(attr.to_sym)
|
156
|
-
mapping = aggregation.mapping
|
157
|
-
mapping.each do |field_attr, aggregate_attr|
|
158
|
-
expanded_attrs[field_attr] = if value.is_a?(Array)
|
159
|
-
value.map { |it| it.send(aggregate_attr) }
|
160
|
-
elsif mapping.size == 1 && !value.respond_to?(aggregate_attr)
|
161
|
-
value
|
162
|
-
else
|
163
|
-
value.send(aggregate_attr)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
else
|
167
|
-
expanded_attrs[attr] = value
|
168
|
-
end
|
169
|
-
end
|
170
|
-
expanded_attrs
|
171
|
-
end
|
172
|
-
deprecate :expand_hash_conditions_for_aggregates
|
173
|
-
|
174
137
|
def replace_bind_variables(statement, values)
|
175
138
|
raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
|
176
139
|
bound = values.dup
|
data/lib/active_record/schema.rb
CHANGED
@@ -51,20 +51,11 @@ module ActiveRecord
|
|
51
51
|
|
52
52
|
if info[:version].present?
|
53
53
|
ActiveRecord::SchemaMigration.create_table
|
54
|
-
connection.assume_migrated_upto_version(info[:version]
|
54
|
+
connection.assume_migrated_upto_version(info[:version])
|
55
55
|
end
|
56
56
|
|
57
57
|
ActiveRecord::InternalMetadata.create_table
|
58
58
|
ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
|
59
59
|
end
|
60
|
-
|
61
|
-
private
|
62
|
-
# Returns the migrations paths.
|
63
|
-
#
|
64
|
-
# ActiveRecord::Schema.new.migrations_paths
|
65
|
-
# # => ["db/migrate"] # Rails migration path by default.
|
66
|
-
def migrations_paths
|
67
|
-
ActiveRecord::Migrator.migrations_paths
|
68
|
-
end
|
69
60
|
end
|
70
61
|
end
|
@@ -17,6 +17,12 @@ module ActiveRecord
|
|
17
17
|
# Only strings are accepted if ActiveRecord::Base.schema_format == :sql.
|
18
18
|
cattr_accessor :ignore_tables, default: []
|
19
19
|
|
20
|
+
##
|
21
|
+
# :singleton-method:
|
22
|
+
# Specify a custom regular expression matching foreign keys which name
|
23
|
+
# should not be dumped to db/schema.rb.
|
24
|
+
cattr_accessor :fk_ignore_pattern, default: /^fk_rails_[0-9a-f]{10}$/
|
25
|
+
|
20
26
|
class << self
|
21
27
|
def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
|
22
28
|
connection.create_schema_dumper(generate_options(config)).dump(stream)
|
@@ -65,11 +71,11 @@ module ActiveRecord
|
|
65
71
|
# of editing this file, please use the migrations feature of Active Record to
|
66
72
|
# incrementally modify your database, and then regenerate this schema definition.
|
67
73
|
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
# from scratch.
|
72
|
-
#
|
74
|
+
# This file is the source Rails uses to define your schema when running `rails
|
75
|
+
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
76
|
+
# be faster and is potentially less error prone than running all of your
|
77
|
+
# migrations from scratch. Old migrations may fail to apply correctly if those
|
78
|
+
# migrations use external dependencies or application code.
|
73
79
|
#
|
74
80
|
# It's strongly recommended that you check this file into your version control system.
|
75
81
|
|
@@ -210,7 +216,7 @@ HEADER
|
|
210
216
|
parts << "primary_key: #{foreign_key.primary_key.inspect}"
|
211
217
|
end
|
212
218
|
|
213
|
-
if foreign_key.
|
219
|
+
if foreign_key.export_name_on_schema_dump?
|
214
220
|
parts << "name: #{foreign_key.name.inspect}"
|
215
221
|
end
|
216
222
|
|
@@ -31,7 +31,14 @@ module ActiveRecord
|
|
31
31
|
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
|
32
32
|
# }
|
33
33
|
def unscoped
|
34
|
-
block_given? ? relation
|
34
|
+
block_given? ? _scoping(relation) { yield } : relation
|
35
|
+
end
|
36
|
+
|
37
|
+
def _scoping(relation) # :nodoc:
|
38
|
+
previous, self.current_scope = current_scope(true), relation
|
39
|
+
yield
|
40
|
+
ensure
|
41
|
+
self.current_scope = previous
|
35
42
|
end
|
36
43
|
|
37
44
|
# Are there attributes associated with this scope?
|
@@ -86,8 +93,8 @@ module ActiveRecord
|
|
86
93
|
# # Should return a scope, you can call 'super' here etc.
|
87
94
|
# end
|
88
95
|
# end
|
89
|
-
def default_scope(scope = nil
|
90
|
-
scope =
|
96
|
+
def default_scope(scope = nil) # :doc:
|
97
|
+
scope = Proc.new if block_given?
|
91
98
|
|
92
99
|
if scope.is_a?(Relation) || !scope.respond_to?(:call)
|
93
100
|
raise ArgumentError,
|
@@ -24,13 +24,13 @@ module ActiveRecord
|
|
24
24
|
# You can define a scope that applies to all finders using
|
25
25
|
# {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
|
26
26
|
def all
|
27
|
-
|
27
|
+
scope = current_scope
|
28
28
|
|
29
|
-
if
|
30
|
-
if self ==
|
31
|
-
|
29
|
+
if scope
|
30
|
+
if self == scope.klass
|
31
|
+
scope.clone
|
32
32
|
else
|
33
|
-
relation.merge!(
|
33
|
+
relation.merge!(scope)
|
34
34
|
end
|
35
35
|
else
|
36
36
|
default_scoped
|
@@ -38,9 +38,7 @@ module ActiveRecord
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def scope_for_association(scope = relation) # :nodoc:
|
41
|
-
|
42
|
-
|
43
|
-
if current_scope && current_scope.empty_scope?
|
41
|
+
if current_scope&.empty_scope?
|
44
42
|
scope
|
45
43
|
else
|
46
44
|
default_scoped(scope)
|
@@ -181,16 +179,14 @@ module ActiveRecord
|
|
181
179
|
extension = Module.new(&block) if block
|
182
180
|
|
183
181
|
if body.respond_to?(:to_proc)
|
184
|
-
singleton_class.
|
185
|
-
scope = all
|
186
|
-
scope = scope._exec_scope(*args, &body)
|
182
|
+
singleton_class.define_method(name) do |*args|
|
183
|
+
scope = all._exec_scope(*args, &body)
|
187
184
|
scope = scope.extending(extension) if extension
|
188
185
|
scope
|
189
186
|
end
|
190
187
|
else
|
191
|
-
singleton_class.
|
192
|
-
scope = all
|
193
|
-
scope = scope.scoping { body.call(*args) || scope }
|
188
|
+
singleton_class.define_method(name) do |*args|
|
189
|
+
scope = body.call(*args) || all
|
194
190
|
scope = scope.extending(extension) if extension
|
195
191
|
scope
|
196
192
|
end
|
@@ -12,14 +12,6 @@ module ActiveRecord
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module ClassMethods # :nodoc:
|
15
|
-
def current_scope(skip_inherited_scope = false)
|
16
|
-
ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
|
17
|
-
end
|
18
|
-
|
19
|
-
def current_scope=(scope)
|
20
|
-
ScopeRegistry.set_value_for(:current_scope, self, scope)
|
21
|
-
end
|
22
|
-
|
23
15
|
# Collects attributes from scopes that should be applied when creating
|
24
16
|
# an AR instance for the particular class this is called on.
|
25
17
|
def scope_attributes
|
@@ -30,6 +22,15 @@ module ActiveRecord
|
|
30
22
|
def scope_attributes?
|
31
23
|
current_scope
|
32
24
|
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def current_scope(skip_inherited_scope = false)
|
28
|
+
ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
|
29
|
+
end
|
30
|
+
|
31
|
+
def current_scope=(scope)
|
32
|
+
ScopeRegistry.set_value_for(:current_scope, self, scope)
|
33
|
+
end
|
33
34
|
end
|
34
35
|
|
35
36
|
def populate_with_current_scope_attributes # :nodoc:
|