class Seaquel::ExpressionConverter

Attributes

quoter[R]

Public Class Methods

new(quoter) click to toggle source
# File lib/seaquel/expression_converter.rb, line 9
def initialize quoter
  @quoter = quoter
end

Public Instance Methods

sql(node) click to toggle source
# File lib/seaquel/expression_converter.rb, line 152
def sql node
  # p [:sql, node]
  case node
    when nil
      bit('NULL')
    when Array
      sql_bits = node.map { |el| sql(el).at(0) }
      bit("(" + sql_bits.join(', ') + ")")
    when String
      bit(quote_string(node))
    when Fixnum
      bit(quote_number(node))
    when true,false
      node ? bit('TRUE') : bit('FALSE')
  else
    node.visit(self)
  end
end
visit_alias(name, to) click to toggle source
# File lib/seaquel/expression_converter.rb, line 61
def visit_alias name, to
  prec = precedence(:as)
  bit("#{sql(to).at(prec)} AS \"#{name}\"")
end
visit_assign(left, right) click to toggle source
# File lib/seaquel/expression_converter.rb, line 22
def visit_assign left, right
  if left.kind_of? AST::Column
    bit(
      [left.as_column_reference(quoter), '=', sql(right).at(0)].join)
  else
    bit(
      [sql(left).at(0), '=', sql(right).at(0)].join)
  end
end
visit_binding(pos) click to toggle source
# File lib/seaquel/expression_converter.rb, line 139
def visit_binding pos
  bit("$#{pos}")
end
visit_binop(op, left, right) click to toggle source
# File lib/seaquel/expression_converter.rb, line 66
def visit_binop op, left, right
  prec = precedence(op)

  if right.nil?
    op = :is if op==:eq
    op = :isnot if op==:noteq
  end

  if op==:in && right.kind_of?(Range)
    raise "Ranges excluding the right side are not permitted in Seaquel." \
      if right.exclude_end?

    return bit([
      sql(left).at(0), 
      'BETWEEN', 
      right.begin, 
      'AND', 
      right.end].join(' '))
  end

  if binops.has_key?(op)
    symbol = binops[op]

    return bit(
      [sql(left).at(prec), symbol, sql(right).at(prec)].join, 
      prec)
  end

  raise "No such operation (#{op.inspect})."
end
visit_column(col) click to toggle source
# File lib/seaquel/expression_converter.rb, line 135
def visit_column col
  bit(col.as_full_reference(quoter))
end
visit_column_list(elements) click to toggle source
# File lib/seaquel/expression_converter.rb, line 44
def visit_column_list elements
  # Column lists only contain columns.
  bit(elements.map { |e| e.as_column_reference(quoter) }.join(', '))
end
visit_funcall(name, args) click to toggle source
# File lib/seaquel/expression_converter.rb, line 143
def visit_funcall name, args
  arglist = args.map { |arg| sql(arg).toplevel }.join(', ')
  bit("#{name}(#{arglist})")
end
visit_joinop(op, exps) click to toggle source
# File lib/seaquel/expression_converter.rb, line 97
def visit_joinop op, exps
  # Shortcut if we join one element only.
  if exps.size == 1
    el = exps.first
    return sql(el)
  end

  prec = precedence(op)
  parts = exps.map { |e| sql(e).at(prec) }

  sql = case op
    when :and
      parts.join(' AND ')
    when :or
      parts.join(' OR ')
  else
    raise "AF: JoinOp called for #{op.inspect}, but has no branch for it."
  end

  bit(sql, prec)
end
visit_list(elements) click to toggle source
# File lib/seaquel/expression_converter.rb, line 40
def visit_list elements
  bit(elements.map { |e| sql(e).at(0) }.join(', '))
end
visit_literal(literal) click to toggle source
# File lib/seaquel/expression_converter.rb, line 36
def visit_literal literal
  bit(literal)
end
visit_new_statement(*args) click to toggle source
# File lib/seaquel/expression_converter.rb, line 32
def visit_new_statement *args
  ::Seaquel::Generator.new(self).compact_sql
end
visit_node(node) click to toggle source

Gets called whenever the expression contains a statement-type AST::Node.

# File lib/seaquel/expression_converter.rb, line 15
def visit_node node
  bit(node.to_sql, 0)
end
visit_order(order, expr) click to toggle source
# File lib/seaquel/expression_converter.rb, line 148
def visit_order order, expr
  bit("#{sql(expr).at(0)} #{order.upcase}")
end
visit_table(table) click to toggle source
# File lib/seaquel/expression_converter.rb, line 49
def visit_table table
  bit(table.quote(quoter))
end
visit_table_alias(table, name) click to toggle source
# File lib/seaquel/expression_converter.rb, line 53
def visit_table_alias table, name
  bit([
    sql(table).at(0), 
    "AS", 
    quoter.identifier(name)
    ].join(' '))
end
visit_unary(op, exp) click to toggle source
# File lib/seaquel/expression_converter.rb, line 119
def visit_unary op, exp
  raise "No such unary operation #{op.inspect}." \
    unless unaryops.has_key?(op)

  symbol, prefix = unaryops[op]
  prec = precedences[op]

  parts = []

  parts << symbol if prefix 
  parts << sql(exp).at(0)
  parts << symbol unless prefix 

  bit(parts.join, prec)
end

Private Instance Methods

binops() click to toggle source
# File lib/seaquel/expression_converter.rb, line 180
def binops
  @binops ||= {
    :eq   => '=',
    :gt   => '>', 
    :gteq => '>=', 
    :lt   => '<', 
    :lteq => '<=', 
    :is   => ' IS ', 
    :isnot => ' IS NOT ', 
    :noteq => '!=', 
    :in   => ' IN ', 
    :like => ' LIKE ', 
    :not_like => ' NOT LIKE ', 
    :ilike => ' ILIKE ', 
    :not_ilike => ' NOT ILIKE ', 
    :similar_to => ' SIMILAR TO ',
    :not_similar_to => ' NOT SIMILAR TO ', 
    :plus => '+', 
    :minus => '-', 
    :div => '/', 
    :times => '*', 
    :mod => '%', 
    :pow => '^', 
    :sqrt => '|/', 
    :cbrt => '||/',
    :bitand => '&', 
    :bitor => '|', 
    :bitxor => '#', 
    :sleft => '<<', 
    :sright => '>>'
  }
end
bit(str, precedence=:inf) click to toggle source
# File lib/seaquel/expression_converter.rb, line 172
def bit str, precedence=:inf
  Bit.new(str, precedence)
end
precedence(op) click to toggle source
# File lib/seaquel/expression_converter.rb, line 176
def precedence op
  precedences[op]
end
precedences() click to toggle source
# File lib/seaquel/expression_converter.rb, line 222
def precedences
  @precedences ||= begin
    prec = 1 # 0 reserved for simple values
    precs = Hash.new(0)

    assign = -> (*list) {
      list.map { |e| precs[e] = prec }
      prec += 1
    }
    
    # By listing something above something else, it gets a lower precedence
    # assigned. List from lower to higher precedences. Equivalence classes
    # by extending the argument list.
    assign[:not]
    assign[:as, :is, :isnot]
    assign[:or]
    assign[:and]
    assign[:eq, :gt, :gteq, :lt, :lteq]
    assign[:plus, :minus]
    assign[:times, :div]

    precs
  end
end
unaryops() click to toggle source
# File lib/seaquel/expression_converter.rb, line 213
def unaryops
  @unaryops ||= {
            # sym,   prefix?
    :not => ['NOT ', true], 
    :bitnot => ['~', true],
    :fact => ['!', false],
  }
end