activerecord 5.2.5 → 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 -748
- data/MIT-LICENSE +3 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +2 -1
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations.rb +16 -12
- 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.rb +15 -20
- 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/preloader.rb +32 -29
- data/lib/active_record/associations/preloader/association.rb +1 -2
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +34 -56
- 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/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.rb +184 -0
- 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/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.rb +38 -37
- data/lib/active_record/migration/command_recorder.rb +35 -5
- data/lib/active_record/migration/compatibility.rb +34 -16
- 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.rb +150 -69
- 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.rb +4 -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/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/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.rb +9 -8
- data/lib/active_record/scoping/default.rb +10 -3
- data/lib/active_record/scoping/named.rb +10 -14
- 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.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- 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/arel.rb +44 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -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.rb +67 -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/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.rb +20 -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/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- metadata +104 -26
@@ -13,33 +13,37 @@ module ActiveRecord
|
|
13
13
|
class_attribute :aggregate_reflections, instance_writer: false, default: {}
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
when :has_many
|
22
|
-
HasManyReflection
|
23
|
-
when :has_one
|
24
|
-
HasOneReflection
|
25
|
-
when :belongs_to
|
26
|
-
BelongsToReflection
|
27
|
-
else
|
28
|
-
raise "Unsupported Macro: #{macro}"
|
29
|
-
end
|
16
|
+
class << self
|
17
|
+
def create(macro, name, scope, options, ar)
|
18
|
+
reflection = reflection_class_for(macro).new(name, scope, options, ar)
|
19
|
+
options[:through] ? ThroughReflection.new(reflection) : reflection
|
20
|
+
end
|
30
21
|
|
31
|
-
|
32
|
-
|
33
|
-
|
22
|
+
def add_reflection(ar, name, reflection)
|
23
|
+
ar.clear_reflections_cache
|
24
|
+
name = name.to_s
|
25
|
+
ar._reflections = ar._reflections.except(name).merge!(name => reflection)
|
26
|
+
end
|
34
27
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
ar._reflections = ar._reflections.except(name).merge!(name => reflection)
|
39
|
-
end
|
28
|
+
def add_aggregate_reflection(ar, name, reflection)
|
29
|
+
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_s => reflection)
|
30
|
+
end
|
40
31
|
|
41
|
-
|
42
|
-
|
32
|
+
private
|
33
|
+
def reflection_class_for(macro)
|
34
|
+
case macro
|
35
|
+
when :composed_of
|
36
|
+
AggregateReflection
|
37
|
+
when :has_many
|
38
|
+
HasManyReflection
|
39
|
+
when :has_one
|
40
|
+
HasOneReflection
|
41
|
+
when :belongs_to
|
42
|
+
BelongsToReflection
|
43
|
+
else
|
44
|
+
raise "Unsupported Macro: #{macro}"
|
45
|
+
end
|
46
|
+
end
|
43
47
|
end
|
44
48
|
|
45
49
|
# \Reflection enables the ability to examine the associations and aggregations of
|
@@ -174,22 +178,26 @@ module ActiveRecord
|
|
174
178
|
scope ? [scope] : []
|
175
179
|
end
|
176
180
|
|
177
|
-
def
|
178
|
-
predicate_builder = predicate_builder(table)
|
179
|
-
scope_chain_items = join_scopes(table, predicate_builder)
|
180
|
-
klass_scope = klass_join_scope(table, predicate_builder)
|
181
|
-
|
181
|
+
def build_join_constraint(table, foreign_table)
|
182
182
|
key = join_keys.key
|
183
183
|
foreign_key = join_keys.foreign_key
|
184
184
|
|
185
|
-
|
185
|
+
constraint = table[key].eq(foreign_table[foreign_key])
|
186
186
|
|
187
|
-
if
|
188
|
-
|
187
|
+
if klass.finder_needs_type_condition?
|
188
|
+
table.create_and([constraint, klass.send(:type_condition, table)])
|
189
|
+
else
|
190
|
+
constraint
|
189
191
|
end
|
192
|
+
end
|
190
193
|
|
191
|
-
|
192
|
-
|
194
|
+
def join_scope(table, foreign_klass)
|
195
|
+
predicate_builder = predicate_builder(table)
|
196
|
+
scope_chain_items = join_scopes(table, predicate_builder)
|
197
|
+
klass_scope = klass_join_scope(table, predicate_builder)
|
198
|
+
|
199
|
+
if type
|
200
|
+
klass_scope.where!(type => foreign_klass.polymorphic_name)
|
193
201
|
end
|
194
202
|
|
195
203
|
scope_chain_items.inject(klass_scope, &:merge!)
|
@@ -413,7 +421,7 @@ module ActiveRecord
|
|
413
421
|
class AssociationReflection < MacroReflection #:nodoc:
|
414
422
|
def compute_class(name)
|
415
423
|
if polymorphic?
|
416
|
-
raise ArgumentError, "Polymorphic
|
424
|
+
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
417
425
|
end
|
418
426
|
active_record.send(:compute_type, name)
|
419
427
|
end
|
@@ -604,9 +612,21 @@ module ActiveRecord
|
|
604
612
|
|
605
613
|
# returns either +nil+ or the inverse association name that it finds.
|
606
614
|
def automatic_inverse_of
|
607
|
-
|
608
|
-
|
615
|
+
return unless can_find_inverse_of_automatically?(self)
|
616
|
+
|
617
|
+
inverse_name_candidates =
|
618
|
+
if options[:as]
|
619
|
+
[options[:as]]
|
620
|
+
else
|
621
|
+
active_record_name = active_record.name.demodulize
|
622
|
+
[active_record_name, ActiveSupport::Inflector.pluralize(active_record_name)]
|
623
|
+
end
|
609
624
|
|
625
|
+
inverse_name_candidates.map! do |candidate|
|
626
|
+
ActiveSupport::Inflector.underscore(candidate).to_sym
|
627
|
+
end
|
628
|
+
|
629
|
+
inverse_name_candidates.detect do |inverse_name|
|
610
630
|
begin
|
611
631
|
reflection = klass._reflect_on_association(inverse_name)
|
612
632
|
rescue NameError
|
@@ -615,9 +635,7 @@ module ActiveRecord
|
|
615
635
|
reflection = false
|
616
636
|
end
|
617
637
|
|
618
|
-
|
619
|
-
return inverse_name
|
620
|
-
end
|
638
|
+
valid_inverse_reflection?(reflection)
|
621
639
|
end
|
622
640
|
end
|
623
641
|
|
@@ -961,16 +979,14 @@ module ActiveRecord
|
|
961
979
|
collect_join_reflections(seed + [self])
|
962
980
|
end
|
963
981
|
|
964
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
965
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
966
982
|
protected
|
967
|
-
attr_reader :delegate_reflection
|
968
|
-
|
969
983
|
def actual_source_reflection # FIXME: this is a horrible name
|
970
984
|
source_reflection.actual_source_reflection
|
971
985
|
end
|
972
986
|
|
973
987
|
private
|
988
|
+
attr_reader :delegate_reflection
|
989
|
+
|
974
990
|
def collect_join_reflections(seed)
|
975
991
|
a = source_reflection.add_as_source seed
|
976
992
|
if options[:source_type]
|
@@ -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
|