require 'swift/adapter'

module Swift
  class Adapter

    # Abstract SQL Adapter.
    #
    # @abstract
    class Sql < Adapter
      def tables
        raise NotImplementedError
      end

      def fields table
        result = execute("select * from #{table} limit 0")
        Hash[result.fields.map(&:to_sym).zip(result.field_types)]
      end

      protected
        def returning?
          raise NotImplementedError
        end

        def prepare_cached scheme, name, &block
          @prepared               ||= Hash.new{|h,k| h[k] = Hash.new}
          @prepared[scheme][name] ||= prepare(scheme, yield)
        end

        def prepare_get scheme
          prepare_cached(scheme, :get) do
            where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
            "select * from #{scheme.store} where #{where} limit 1"
          end
        end

        def prepare_create scheme
          prepare_cached(scheme, :create) do
            values    = (['?'] * scheme.header.insertable.size).join(', ')
            returning = "returning #{scheme.header.serial}" if scheme.header.serial and returning?
            "insert into #{scheme.store} (#{scheme.header.insertable.join(', ')}) values (#{values}) #{returning}"
          end
        end

        def prepare_update scheme
          prepare_cached(scheme, :update) do
            set   = scheme.header.updatable.map{|field| "#{field} = ?"}.join(', ')
            where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
            "update #{scheme.store} set #{set} where #{where}"
          end
        end

        def prepare_delete scheme
          prepare_cached(scheme, :delete) do
            where = scheme.header.keys.map{|key| "#{key} = ?"}.join(' and ')
            "delete from #{scheme.store} where #{where}"
          end
        end

        def field_definition attribute
          "#{attribute.field} " + field_type(attribute)
        end

        def field_type attribute
          case attribute
            when Type::String     then 'text'
            when Type::Integer    then attribute.serial ? 'serial' : 'integer'
            when Type::Float      then 'float'
            when Type::BigDecimal then 'numeric'
            when Type::Time       then 'timestamp' # deprecated
            when Type::DateTime   then 'timestamp'
            when Type::Date       then 'date'
            when Type::Boolean    then 'boolean'
            when Type::IO         then 'blob'
            else 'text'
          end
        end
    end # Sql
  end # Adapter
end # Swift