mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Avoid creating extra relation
and build_arel
in _create_record
and _update_record
(#29999)
Currently `_create_record` and `_update_record` in `Persistence` are creating extra `unscoped` and calling `build_arel` in the relation. But `compile_insert` and `compile_update` can be done without those expensive operation for `SelectManager` creation. So I moved the implementation to `Persistence` to avoid creating extra relation and refactored to avoid calling `build_arel`. https://gist.github.com/kamipo/8ed73d760112cfa5f6263c9413633419 Before: ``` Warming up -------------------------------------- _update_record 150.000 i/100ms Calculating ------------------------------------- _update_record 1.548k (±12.3%) i/s - 7.650k in 5.042603s ``` After: ``` Warming up -------------------------------------- _update_record 201.000 i/100ms Calculating ------------------------------------- _update_record 2.002k (±12.8%) i/s - 9.849k in 5.027681s ``` 30% faster for STI classes.
This commit is contained in:
parent
8bfa617e99
commit
bbae710a40
2 changed files with 41 additions and 63 deletions
|
@ -165,6 +165,38 @@ module ActiveRecord
|
|||
where(primary_key => id_or_array).delete_all
|
||||
end
|
||||
|
||||
def _insert_record(values) # :nodoc:
|
||||
primary_key_value = nil
|
||||
|
||||
if primary_key && Hash === values
|
||||
arel_primary_key = arel_attribute(primary_key)
|
||||
primary_key_value = values[arel_primary_key]
|
||||
|
||||
if !primary_key_value && prefetch_primary_key?
|
||||
primary_key_value = next_sequence_value
|
||||
values[arel_primary_key] = primary_key_value
|
||||
end
|
||||
end
|
||||
|
||||
if values.empty?
|
||||
im = arel_table.compile_insert(connection.empty_insert_statement_value)
|
||||
im.into arel_table
|
||||
else
|
||||
im = arel_table.compile_insert(_substitute_values(values))
|
||||
end
|
||||
|
||||
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
|
||||
end
|
||||
|
||||
def _update_record(values, id, id_was) # :nodoc:
|
||||
bind = predicate_builder.build_bind_attribute(primary_key, id_was || id)
|
||||
um = arel_table.where(
|
||||
arel_attribute(primary_key).eq(bind)
|
||||
).compile_update(_substitute_values(values), primary_key)
|
||||
|
||||
connection.update(um, "#{self} Update")
|
||||
end
|
||||
|
||||
private
|
||||
# Called by +instantiate+ to decide which class to use for a new
|
||||
# record instance.
|
||||
|
@ -174,6 +206,13 @@ module ActiveRecord
|
|||
def discriminate_class_for_record(record)
|
||||
self
|
||||
end
|
||||
|
||||
def _substitute_values(values)
|
||||
values.map do |attr, value|
|
||||
bind = predicate_builder.build_bind_attribute(attr.name, value)
|
||||
[attr, bind]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns true if this object hasn't been saved yet -- that is, a record
|
||||
|
@ -671,7 +710,7 @@ module ActiveRecord
|
|||
rows_affected = 0
|
||||
@_trigger_update_callback = true
|
||||
else
|
||||
rows_affected = self.class.unscoped._update_record attributes_values, id, id_in_database
|
||||
rows_affected = self.class._update_record(attributes_values, id, id_in_database)
|
||||
@_trigger_update_callback = rows_affected > 0
|
||||
end
|
||||
|
||||
|
@ -685,7 +724,7 @@ module ActiveRecord
|
|||
def _create_record(attribute_names = self.attribute_names)
|
||||
attributes_values = arel_attributes_with_values_for_create(attribute_names)
|
||||
|
||||
new_id = self.class.unscoped.insert attributes_values
|
||||
new_id = self.class._insert_record(attributes_values)
|
||||
self.id ||= new_id if self.class.primary_key
|
||||
|
||||
@new_record = false
|
||||
|
|
|
@ -36,67 +36,6 @@ module ActiveRecord
|
|||
reset
|
||||
end
|
||||
|
||||
def insert(values) # :nodoc:
|
||||
primary_key_value = nil
|
||||
|
||||
if primary_key && Hash === values
|
||||
primary_key_value = values[values.keys.find { |k|
|
||||
k.name == primary_key
|
||||
}]
|
||||
|
||||
if !primary_key_value && klass.prefetch_primary_key?
|
||||
primary_key_value = klass.next_sequence_value
|
||||
values[arel_attribute(klass.primary_key)] = primary_key_value
|
||||
end
|
||||
end
|
||||
|
||||
im = arel.create_insert
|
||||
im.into @table
|
||||
|
||||
substitutes = substitute_values values
|
||||
|
||||
if values.empty? # empty insert
|
||||
im.values = Arel.sql(connection.empty_insert_statement_value)
|
||||
else
|
||||
im.insert substitutes
|
||||
end
|
||||
|
||||
@klass.connection.insert(
|
||||
im,
|
||||
"#{@klass} Create",
|
||||
primary_key || false,
|
||||
primary_key_value,
|
||||
nil,
|
||||
)
|
||||
end
|
||||
|
||||
def _update_record(values, id, id_was) # :nodoc:
|
||||
substitutes = substitute_values values
|
||||
|
||||
scope = @klass.unscoped
|
||||
|
||||
if @klass.finder_needs_type_condition?
|
||||
scope.unscope!(where: @klass.inheritance_column)
|
||||
end
|
||||
|
||||
relation = scope.where(@klass.primary_key => (id_was || id))
|
||||
um = relation
|
||||
.arel
|
||||
.compile_update(substitutes, @klass.primary_key)
|
||||
|
||||
@klass.connection.update(
|
||||
um,
|
||||
"#{@klass} Update",
|
||||
)
|
||||
end
|
||||
|
||||
def substitute_values(values) # :nodoc:
|
||||
values.map do |arel_attr, value|
|
||||
bind = predicate_builder.build_bind_attribute(arel_attr.name, value)
|
||||
[arel_attr, bind]
|
||||
end
|
||||
end
|
||||
|
||||
def arel_attribute(name) # :nodoc:
|
||||
klass.arel_attribute(name, table)
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue