class Sequel::Unbinder
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)
Constants
- UNBIND_KEY_CLASSES
The key classes (first argument of the ComplexExpression) that will considered for transformation.
- UNBIND_OPS
The <tt>SQL::ComplexExpression<tt> operates that will be considered for transformation.
- UNBIND_VALUE_CLASSES
The value classes (second argument of the ComplexExpression) that will be considered for transformation.
Attributes
The hash of bind variables that were extracted from the dataset filter.
Public Class Methods
Intialize an empty binds
hash.
# File lib/sequel/ast_transformer.rb, line 154 def initialize @binds = {} end
Private Instance Methods
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 162 def bind_key(obj) case obj when Symbol obj when String obj.to_sym 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 180 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