Unbinder
is used to take a dataset filter and return a
modified version that unbinds already bound values and returns a dataset
with bound value placeholders and a hash of bind values. You can then
prepare the dataset and use the bound variables to execute it with the same
values.
This class only does a limited form of unbinding where the variable names
and values can be associated unambiguously. The only cases it handles are
<tt>SQL::ComplexExpression<tt> with an operator in
UNBIND_OPS
, a first argument that's an instance of a
member of UNBIND_KEY_CLASSES
, and a second argument that's
an instance of a member of UNBIND_VALUE_CLASSES
.
So it can handle cases like:
DB.filter(:a=>1).exclude(:b=>2).where{c > 3}
But it cannot handle cases like:
DB.filter(:a + 1 < 0)
The key classes (first argument of the ComplexExpression) that will considered for transformation.
The <tt>SQL::ComplexExpression<tt> operates that will be considered for transformation.
The value classes (second argument of the ComplexExpression) that will be considered for transformation.
The hash of bind variables that were extracted from the dataset filter.
Intialize an empty binds
hash.
# File lib/sequel/ast_transformer.rb, line 150 def initialize @binds = {} end
Create a suitable bound variable key for the object, which should be an
instance of one of the UNBIND_KEY_CLASSES
.
# File lib/sequel/ast_transformer.rb, line 158 def bind_key(obj) case obj when Symbol, String obj when SQL::Identifier bind_key(obj.value) when SQL::QualifiedIdentifier :"#{bind_key(obj.table)}.#{bind_key(obj.column)}" else raise Error, "unhandled object in Sequel::Unbinder#bind_key: #{obj}" end end
Handle SQL::ComplexExpression
instances with suitable ops and
arguments, substituting the value with a bound variable placeholder and
assigning it an entry in the binds
hash with a matching key.
# File lib/sequel/ast_transformer.rb, line 174 def v(o) if o.is_a?(SQL::ComplexExpression) && UNBIND_OPS.include?(o.op) l, r = o.args l = l.value if l.is_a?(Sequel::SQL::Wrapper) r = r.value if r.is_a?(Sequel::SQL::Wrapper) if UNBIND_KEY_CLASSES.any?{|c| l.is_a?(c)} && UNBIND_VALUE_CLASSES.any?{|c| r.is_a?(c)} && !r.is_a?(LiteralString) key = bind_key(l) if (old = binds[key]) && old != r raise UnbindDuplicate, "two different values for #{key.inspect}: #{[r, old].inspect}" end binds[key] = r SQL::ComplexExpression.new(o.op, l, :"$#{key}") else super end else super end end