module Bmg::Sql::Support

Public Instance Methods

_order_all(tables, joins, result) click to toggle source
# File lib/bmg/sql/support/from_clause_orderer.rb, line 110
def _order_all(tables, joins, result)
  if tables.empty? and joins.empty?
    # end or recusion
    result
  elsif tables.empty?
    # Why will this never happen exactly??
    raise NotImplementedError, "Orphan joins: `#{joins.inspect}`"
  else
    # Greedy strategy: we take the first table in the list and keep the
    # rest for recursion later
    table, tables_tail = tables.first, tables[1..-1]

    # Split the remaining joins in two lists: those referencing only
    # introduced tables, and those making forward references
    on, joins_tail = split_joins(joins, table, tables_tail)

    # Decide which kind of join it is, according to the result and
    # the number of join clauses that will be used
    join_kind = if result.empty?
      :base
    elsif table.last == :left_join
      :left_join
    elsif on.empty?
      :cross_join
    else
      :inner_join
    end

    # Compute the AND([eq]) predicate on selected join clauses
    predicate = on.inject(nil){|p,clause|
      p.nil? ? clause : Predicate::Factory.and(p, clause)
    }

    # Recurse with that new clause in the result
    clause = [ join_kind, table[0], predicate ]
    _order_all(tables_tail, joins_tail, result + [clause])
  end
end
split_joins(joins, table, tables_tail) click to toggle source

Given a list of join `ti.attri = tj.attrj` clauses, a newly introduced ti `table`, and a set of non-yet-introduced tables `tables_tail`,…

… split the joins in two sublists: those making references to table `ti` and making no reference to non introduced tables, and the others.

# File lib/bmg/sql/support/from_clause_orderer.rb, line 154
def split_joins(joins, table, tables_tail)
  joins.partition{|j|
    uses?(j, table[0]) && !tables_tail.find{|t|
      uses?(j, t[0])
    }
  }
end
uses?(condition, table) click to toggle source

Returns whether the join conditions references the given table

# File lib/bmg/sql/support/from_clause_orderer.rb, line 163
def uses?(condition, table)
  name = table.as_name.to_s
  left_name = var_name(condition[1])
  right_name = var_name(condition[2])
  (left_name == name) or (right_name == name)
end
var_name(qualified) click to toggle source

Given a `ti.attri` expression (AST node), returns `ti`

# File lib/bmg/sql/support/from_clause_orderer.rb, line 171
def var_name(qualified)
  case qualified.first
  when :qualified_identifier then qualified[1].to_s
  when :qualified_name       then qualified[1][1].to_s
  else
    raise NotImplementedError, "Unexpected qualified name `#{qualified.inspect}`"
  end
end