alf-tool/alf-sql

View on GitHub
lib/alf/sql/builder.rb

Summary

Maintainability
A
2 hrs
Test Coverage
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