class Hotdog::Expression::BinaryExpressionNode
Attributes
left[R]
op[R]
right[R]
Public Class Methods
new(op, left, right, options={})
click to toggle source
# File lib/hotdog/expression/semantics.rb, line 170 def initialize(op, left, right, options={}) case (op || "or").to_s when "&&", "&", "AND", "and" @op = :AND when ",", "||", "|", "OR", "or" @op = :OR when "^", "XOR", "xor" @op = :XOR else raise(SyntaxError.new("unknown binary operator: #{op.inspect}")) end @left = left @right = right @options = {} end
Public Instance Methods
==(other)
click to toggle source
# File lib/hotdog/expression/semantics.rb, line 364 def ==(other) self.class === other and @op == other.op and @left == other.left and @right == other.right end
dump(options={})
click to toggle source
# File lib/hotdog/expression/semantics.rb, line 368 def dump(options={}) {left: @left.dump(options), binary_op: @op.to_s, right: @right.dump(options)} end
evaluate(environment, options={})
click to toggle source
# File lib/hotdog/expression/semantics.rb, line 186 def evaluate(environment, options={}) case @op when :AND left_values = @left.evaluate(environment, options).tap do |values| environment.logger.debug("lhs(#{values.length})") end if left_values.empty? [] else right_values = @right.evaluate(environment, options).tap do |values| environment.logger.debug("rhs(#{values.length})") end if right_values.empty? [] else # workaround for "too many terms in compound SELECT" min, max = environment.execute("SELECT MIN(id), MAX(id) FROM hosts LIMIT 1;").first.to_a sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT (min / ((sqlite_limit_compound_select - 2) / 2)).upto(max / ((sqlite_limit_compound_select - 2) / 2)).flat_map { |i| range = (((sqlite_limit_compound_select - 2) / 2) * i)...(((sqlite_limit_compound_select - 2) / 2) * (i + 1)) left_selected = left_values.select { |n| range === n } right_selected = right_values.select { |n| range === n } if 0 < left_selected.length and 0 < right_selected.length q = "SELECT id FROM hosts " \ "WHERE ? <= id AND id < ? AND ( id IN (%s) AND id IN (%s) );" environment.execute(q % [left_selected.map { "?" }.join(", "), right_selected.map { "?" }.join(", ")], [range.first, range.last] + left_selected + right_selected).map { |row| row.first } else [] end }.tap do |values| environment.logger.debug("lhs(#{left_values.length}) AND rhs(#{right_values.length}) => #{values.length}") end end end when :OR left_values = @left.evaluate(environment, options).tap do |values| environment.logger.debug("lhs(#{values.length})") end right_values = @right.evaluate(environment, options).tap do |values| environment.logger.debug("rhs(#{values.length})") end if left_values.empty? right_values else if right_values.empty? [] else # workaround for "too many terms in compound SELECT" min, max = environment.execute("SELECT MIN(id), MAX(id) FROM hosts LIMIT 1;").first.to_a sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT (min / ((sqlite_limit_compound_select - 2) / 2)).upto(max / ((sqlite_limit_compound_select - 2) / 2)).flat_map { |i| range = (((sqlite_limit_compound_select - 2) / 2) * i)...(((sqlite_limit_compound_select - 2) / 2) * (i + 1)) left_selected = left_values.select { |n| range === n } right_selected = right_values.select { |n| range === n } if 0 < left_selected.length or 0 < right_selected.length q = "SELECT id FROM hosts " \ "WHERE ? <= id AND id < ? AND ( id IN (%s) OR id IN (%s) );" environment.execute(q % [left_selected.map { "?" }.join(", "), right_selected.map { "?" }.join(", ")], [range.first, range.last] + left_selected + right_selected).map { |row| row.first } else [] end }.tap do |values| environment.logger.debug("lhs(#{left_values.length}) OR rhs(#{right_values.length}) => #{values.length}") end end end when :XOR left_values = @left.evaluate(environment, options).tap do |values| environment.logger.debug("lhs(#{values.length})") end right_values = @right.evaluate(environment, options).tap do |values| environment.logger.debug("rhs(#{values.length})") end if left_values.empty? right_values else if right_values.empty? [] else # workaround for "too many terms in compound SELECT" min, max = environment.execute("SELECT MIN(id), MAX(id) FROM hosts LIMIT 1;").first.to_a sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT (min / ((sqlite_limit_compound_select - 2) / 4)).upto(max / ((sqlite_limit_compound_select - 2) / 4)).flat_map { |i| range = (((sqlite_limit_compound_select - 2) / 4) * i)...(((sqlite_limit_compound_select - 2) / 4) * (i + 1)) left_selected = left_values.select { |n| range === n } right_selected = right_values.select { |n| range === n } if 0 < left_selected.length or 0 < right_selected.length q = "SELECT id FROM hosts " \ "WHERE ? <= id AND id < ? AND NOT (id IN (%s) AND id IN (%s)) AND ( id IN (%s) OR id IN (%s) );" lq = left_selected.map { "?" }.join(", ") rq = right_selected.map { "?" }.join(", ") environment.execute(q % [lq, rq, lq, rq], [range.first, range.last] + left_selected + right_selected + left_selected + right_selected).map { |row| row.first } else [] end }.tap do |values| environment.logger.debug("lhs(#{left_values.length}) XOR rhs(#{right_values.length}) => #{values.length}") end end end else [] end end
optimize(options={})
click to toggle source
# File lib/hotdog/expression/semantics.rb, line 291 def optimize(options={}) o_left = @left.optimize(options) o_right = @right.optimize(options) case op when :AND case o_left when EverythingNode o_right when NothingNode o_left else if o_left == o_right o_left else BinaryExpressionNode.new( op, o_left, o_right, ).optimize1(options) end end when :OR case o_left when EverythingNode o_left when NothingNode o_right else if o_left == o_right o_left else if MultinaryExpressionNode === o_left if o_left.op == op o_left.merge(o_right, fallback: self) else BinaryExpressionNode.new( op, o_left, o_right, ).optimize1(options) end else if MultinaryExpressionNode === o_right if o_right.op == op o_right.merge(o_left, fallback: self) else BinaryExpressionNode.new( op, o_left, o_right, ).optimize1(options) end else MultinaryExpressionNode.new(op, [o_left, o_right], fallback: self) end end end end when :XOR if o_left == o_right NothingNode.new(options) else BinaryExpressionNode.new( op, o_left, o_right, ).optimize1(options) end else self.dup end end
Protected Instance Methods
optimize1(options)
click to toggle source
# File lib/hotdog/expression/semantics.rb, line 373 def optimize1(options) if TagExpressionNode === left and TagExpressionNode === right lq = left.maybe_query(options) lv = left.condition_values(options) rq = right.maybe_query(options) rv = right.condition_values(options) sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT if lq and rq and lv.length + rv.length <= sqlite_limit_compound_select case op when :AND q = "#{lq.sub(/\s*;\s*\z/, "")} INTERSECT #{rq.sub(/\s*;\s*\z/, "")};" QueryExpressionNode.new(q, lv + rv, fallback: self) when :OR q = "#{lq.sub(/\s*;\s*\z/, "")} UNION #{rq.sub(/\s*;\s*\z/, "")};" QueryExpressionNode.new(q, lv + rv, fallback: self) when :XOR q = "#{lq.sub(/\s*;\s*\z/, "")} UNION #{rq.sub(/\s*;\s*\z/, "")} " \ "EXCEPT #{lq.sub(/\s*;\s*\z/, "")} " \ "INTERSECT #{rq.sub(/\s*;\s*\z/, "")};" QueryExpressionNode.new(q, lv + rv, fallback: self) else self.dup end else self.dup end else self.dup end end