Avoid extra query attribute allocation in _insert_record/_update_record

Since a `BindParam` object always has an attribute object as the value
in the Active Record usage, so `_insert_record`/`_update_record` could
be passed attribute set instead of wrapping casted value by a query
attribute.
This commit is contained in:
Ryuta Kamizono 2021-03-01 16:20:15 +09:00
parent adc5eb66f8
commit 6ee96a8f42
3 changed files with 18 additions and 22 deletions

@ -54,7 +54,6 @@ def write_from_user(name, value)
def write_cast_value(name, value)
@attributes[name] = self[name].with_cast_value(value)
value
end
def freeze

@ -384,9 +384,7 @@ def attribute_method?(attr_name)
end
def attributes_with_values(attribute_names)
attribute_names.index_with do |name|
_read_attribute(name)
end
attribute_names.index_with { |name| @attributes[name] }
end
# Filters the primary keys and readonly attributes from the attribute names.

@ -356,27 +356,27 @@ def _insert_record(values) # :nodoc:
primary_key = self.primary_key
primary_key_value = nil
if primary_key && Hash === values
primary_key_value = values[primary_key]
if !primary_key_value && prefetch_primary_key?
if prefetch_primary_key? && primary_key
values[primary_key] ||= begin
primary_key_value = next_sequence_value
values[primary_key] = primary_key_value
_default_attributes[primary_key].with_cast_value(primary_key_value)
end
end
im = Arel::InsertManager.new
im.into(arel_table)
if values.empty?
im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key))
im.into arel_table
im.insert(connection.empty_insert_statement_value(primary_key))
else
im = arel_table.compile_insert(_substitute_values(values))
im.insert(_substitute_values(values))
end
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
end
def _update_record(values, constraints) # :nodoc:
constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
constraints = constraints.map { |name, value| predicate_builder[name, value] }
if default_scopes?(all_queries: true)
constraints << default_scoped(all_queries: true).where_clause.ast
@ -386,15 +386,16 @@ def _update_record(values, constraints) # :nodoc:
constraints << current_scope.where_clause.ast
end
um = arel_table.where(
constraints.reduce(&:and)
).compile_update(_substitute_values(values), primary_key)
um = Arel::UpdateManager.new
um.table(arel_table)
um.set(_substitute_values(values))
um.wheres = constraints
connection.update(um, "#{self} Update")
end
def _delete_record(constraints) # :nodoc:
constraints = _substitute_values(constraints).map { |attr, bind| attr.eq(bind) }
constraints = constraints.map { |name, value| predicate_builder[name, value] }
if default_scopes?(all_queries: true)
constraints << default_scoped(all_queries: true).where_clause.ast
@ -430,9 +431,7 @@ def discriminate_class_for_record(record)
def _substitute_values(values)
values.map do |name, value|
attr = arel_table[name]
bind = predicate_builder.build_bind_attribute(attr.name, value)
[attr, bind]
[ arel_table[name], Arel::Nodes::BindParam.new(value) ]
end
end
end
@ -688,8 +687,8 @@ def update_columns(attributes)
end
update_constraints = _primary_key_constraints_hash
attributes.each do |k, v|
@attributes.write_cast_value(k, v)
attributes = attributes.each_with_object({}) do |(k, v), h|
h[k] = @attributes.write_cast_value(k, v)
clear_attribute_change(k)
end