class Arel::Visitors::Oracle

Private Instance Methods

order_hacks(o, a) click to toggle source

Hacks for the order clauses specific to Oracle

# File lib/arel/visitors/oracle.rb, line 82
def order_hacks o, a
  return o if o.orders.empty?
  return o unless o.cores.any? do |core|
    core.projections.any? do |projection|
      /FIRST_VALUE/ === projection
    end
  end
  # Previous version with join and split broke ORDER BY clause
  # if it contained functions with several arguments (separated by ',').
  #
  # orders   = o.orders.map { |x| visit x, a }.join(', ').split(',')
  orders   = o.orders.map do |x|
    string = visit x, a
    if string.include?(',')
      split_order_string(string)
    else
      string
    end
  end.flatten
  o.orders = []
  orders.each_with_index do |order, i|
    o.orders <<
      Nodes::SqlLiteral.new("alias_#{i}__#{' DESC' if /\bdesc$/i === order}")
  end
  o
end
split_order_string(string) click to toggle source

Split string by commas but count opening and closing brackets and ignore commas inside brackets.

# File lib/arel/visitors/oracle.rb, line 111
def split_order_string(string)
  array = []
  i = 0
  string.split(',').each do |part|
    if array[i]
      array[i] << ',' << part
    else
      # to ensure that array[i] will be String and not Arel::Nodes::SqlLiteral
      array[i] = '' << part
    end
    i += 1 if array[i].count('(') == array[i].count(')')
  end
  array
end
visit_Arel_Nodes_Except(o, a) click to toggle source
# File lib/arel/visitors/oracle.rb, line 64
def visit_Arel_Nodes_Except o, a
  "( #{visit o.left, a} MINUS #{visit o.right, a} )"
end
visit_Arel_Nodes_Limit(o, a) click to toggle source
# File lib/arel/visitors/oracle.rb, line 57
def visit_Arel_Nodes_Limit o, a
end
visit_Arel_Nodes_Offset(o, a) click to toggle source
# File lib/arel/visitors/oracle.rb, line 60
def visit_Arel_Nodes_Offset o, a
  "raw_rnum_ > #{visit o.expr, a}"
end
visit_Arel_Nodes_SelectStatement(o, a) click to toggle source
# File lib/arel/visitors/oracle.rb, line 6
def visit_Arel_Nodes_SelectStatement o, a
  o = order_hacks(o, a)

  # if need to select first records without ORDER BY and GROUP BY and without DISTINCT
  # then can use simple ROWNUM in WHERE clause
  if o.limit && o.orders.empty? && !o.offset && o.cores.first.set_quantifier.class.to_s !~ /Distinct/
    o.cores.last.wheres.push Nodes::LessThanOrEqual.new(
      Nodes::SqlLiteral.new('ROWNUM'), o.limit.expr
    )
    return super
  end

  if o.limit && o.offset
    o        = o.dup
    limit    = o.limit.expr.to_i
    offset   = o.offset
    o.offset = nil
    sql = super(o, a)
    return <<-eosql
        SELECT * FROM (
          SELECT raw_sql_.*, rownum raw_rnum_
          FROM (#{sql}) raw_sql_
          WHERE rownum <= #{offset.expr.to_i + limit}
        )
        WHERE #{visit offset, a}
    eosql
  end

  if o.limit
    o       = o.dup
    limit   = o.limit.expr
    return "SELECT * FROM (#{super(o, a)}) WHERE ROWNUM <= #{visit limit, a}"
  end

  if o.offset
    o        = o.dup
    offset   = o.offset
    o.offset = nil
    sql = super(o, a)
    return <<-eosql
        SELECT * FROM (
          SELECT raw_sql_.*, rownum raw_rnum_
          FROM (#{sql}) raw_sql_
        )
        WHERE #{visit offset, a}
    eosql
  end

  super
end
visit_Arel_Nodes_UpdateStatement(o, a) click to toggle source
# File lib/arel/visitors/oracle.rb, line 68
def visit_Arel_Nodes_UpdateStatement o, a
  # Oracle does not allow ORDER BY/LIMIT in UPDATEs.
  if o.orders.any? && o.limit.nil?
    # However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
    # otherwise let the user deal with the error
    o = o.dup
    o.orders = []
  end

  super
end