Avoid extra BindParam allocation to generate placeholder in queries

In the Active Record usage, a `BindParam` object always has an attribute
object as the value. A `BindParam` object is used to call `add_bind` to
generate placeholder if `prepared_statements: true`.

Since Arel is a part of Active Record now, I think that we can regard an
`ActiveModel::Attribute` object as boundable without wrapping it by a
`BindParam` to avoid extra `BindParam` allocation.
This commit is contained in:
Ryuta Kamizono 2021-03-01 18:14:43 +09:00
parent a8c462d37c
commit 64ca6f608e
7 changed files with 15 additions and 20 deletions

@ -368,7 +368,7 @@ def _insert_record(values) # :nodoc:
if values.empty?
im.insert(connection.empty_insert_statement_value(primary_key))
else
im.insert(_substitute_values(values))
im.insert(values.transform_keys { |name| arel_table[name] })
end
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
@ -386,7 +386,7 @@ def _update_record(values, constraints) # :nodoc:
end
um = Arel::UpdateManager.new(arel_table)
um.set(_substitute_values(values))
um.set(values.transform_keys { |name| arel_table[name] })
um.wheres = constraints
connection.update(um, "#{self} Update")
@ -425,12 +425,6 @@ def instantiate_instance_of(klass, attributes, column_types = {}, &block)
def discriminate_class_for_record(record)
self
end
def _substitute_values(values)
values.map do |name, value|
[ arel_table[name], Arel::Nodes::BindParam.new(value) ]
end
end
end
# Returns true if this object hasn't been saved yet -- that is, a record

@ -65,8 +65,7 @@ def build(attribute, value, operator = nil)
end
def build_bind_attribute(column_name, value)
attr = Relation::QueryAttribute.new(column_name, value, table.type(column_name))
Arel::Nodes::BindParam.new(attr)
Relation::QueryAttribute.new(column_name, value, table.type(column_name))
end
def resolve_arel_attribute(table_name, column_name, &block)

@ -1263,8 +1263,7 @@ def build_arel(aliases)
end
def build_cast_value(name, value)
cast_value = ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
Arel::Nodes::BindParam.new(cast_value)
ActiveModel::Attribute.with_cast_value(name, value, Type.default_value)
end
def build_from

@ -224,11 +224,10 @@ def wrap_sql_literal(node)
end
def extract_node_value(node)
case node
when Array
node.map { |v| extract_node_value(v) }
when Arel::Nodes::BindParam, Arel::Nodes::Casted, Arel::Nodes::Quoted
if node.respond_to?(:value_before_type_cast)
node.value_before_type_cast
elsif Array === node
node.map { |v| extract_node_value(v) }
end
end
end

@ -47,7 +47,7 @@ def infinite?
def self.build_quoted(other, attribute = nil)
case other
when Arel::Nodes::Node, Arel::Attributes::Attribute, Arel::Table, Arel::SelectManager, Arel::Nodes::SqlLiteral
when Arel::Nodes::Node, Arel::Attributes::Attribute, Arel::Table, Arel::SelectManager, Arel::Nodes::SqlLiteral, ActiveModel::Attribute
other
else
case attribute

@ -52,7 +52,7 @@ def between(other)
else
left = quoted_node(other.begin)
right = quoted_node(other.end)
Nodes::Between.new(self, left.and(right))
Nodes::Between.new(self, Nodes::And.new([left, right]))
end
end

@ -103,7 +103,7 @@ def visit_Arel_Nodes_ValuesList(o, collector)
row.each_with_index do |value, k|
collector << ", " unless k == 0
case value
when Nodes::SqlLiteral, Nodes::BindParam
when Nodes::SqlLiteral, Nodes::BindParam, ActiveModel::Attribute
collector = visit(value, collector)
else
collector << quote(value).to_s
@ -585,7 +585,7 @@ def visit_Arel_Nodes_Or(o, collector)
def visit_Arel_Nodes_Assignment(o, collector)
case o.right
when Arel::Nodes::Node, Arel::Attributes::Attribute
when Arel::Nodes::Node, Arel::Attributes::Attribute, ActiveModel::Attribute
collector = visit o.left, collector
collector << " = "
visit o.right, collector
@ -695,6 +695,10 @@ def visit_Arel_Attributes_Attribute(o, collector)
def bind_block; BIND_BLOCK; end
def visit_ActiveModel_Attribute(o, collector)
collector.add_bind(o, &bind_block)
end
def visit_Arel_Nodes_BindParam(o, collector)
collector.add_bind(o.value, &bind_block)
end