module Alf module Sql class Builder DISTINCT = Grammar.sexpr([:set_quantifier, "distinct"]) ALL = Grammar.sexpr([:set_quantifier, "all"]) IS_TABLE_DEE = Grammar.sexpr([:select_list, [:select_item, [:literal, true], [:column_name, "is_table_dee"]]]) SELECT_STAR = Grammar.sexpr([:select_star]) def self.builder(meth) old = instance_method(meth) define_method(meth) do |*args, &bl| Grammar.sexpr(old.bind(self).call(*args, &bl)) end end def initialize(start = 0) @next_qualifier = (start || 0) end def distinct DISTINCT end builder :distinct def all ALL end builder :all def name_intro(name, sexpr) [:name_intro, table_name(name), sexpr] end builder :name_intro def select_all(heading, name, qualifier = next_qualifier!) [ :select_exp, all, select_list(heading, qualifier), from_clause(name, qualifier) ] end builder :select_all def select_is_table_dee(subquery) [ :select_exp, all, is_table_dee, [:where_clause, exists(subquery)] ] end builder :select_is_table_dee def is_table_dee IS_TABLE_DEE end builder :is_table_dee def select_list(heading, qualifier) attrs = heading.to_attr_list.to_a attrs.map{|a| select_item(qualifier, a) }.unshift(:select_list) end builder :select_list def select_item(qualifier, name, as = name) [:select_item, qualified_name(qualifier, name.to_s), column_name(as.to_s)] end builder :select_item def select_star SELECT_STAR end builder :select_star def from_clause(table_name, qualifier) [:from_clause, table_as(table_name, qualifier) ] end builder :from_clause def table_as(table, qualifier) table = case table when String, Symbol then table_name(table) else table end [:table_as, table, range_var_name(qualifier) ] end builder :table_as def qualified_name(qualifier, name) [:qualified_name, range_var_name(qualifier), column_name(name) ] end builder :qualified_name def range_var_name(qualifier) [:range_var_name, qualifier] end builder :range_var_name def column_name(name) [:column_name, name] end builder :column_name def table_name(name) [:table_name, name] end builder :table_name def exists(subquery) Predicate::Grammar.sexpr [ :exists, subquery ] end def order_by_clause(ordering, &desaliaser) ordering.to_a.map{|(s,d)| if s.composite? raise NotSupportedError, "SQL order by does not support composite selectors" end name = s.outcoerce.to_s name = (desaliaser && desaliaser[name]) || column_name(name) [:order_by_term, name, d.to_s] }.unshift(:order_by_clause) end builder :order_by_clause def limit_clause(limit) [:limit_clause, limit] end builder :limit_clause def offset_clause(limit) [:offset_clause, limit] end builder :offset_clause def from_self(sexpr) Processor::FromSelf.new(self).call(sexpr) end public def next_qualifier! "t#{@next_qualifier += 1}" end end end end