content: {-# LANGUAGE FlexibleInstances #-}
  -----------------------------------------------------------
  -- |
  -- Module      :  HaskellDB
  -- Copyright   :  Daan Leijen (c) 1999, daan@cs.uu.nl
  --                HWT Group (c) 2003, haskelldb-users@lists.sourceforge.net
  -- License     :  BSD-style
  -- 
  -- Maintainer  :  "Justin Bailey" <jgbailey@gmail.com>
  -- Stability   :  experimental
  -- Portability :  non portable
  --
  -- HaskellDB is a Haskell library for expressing database queries and
  -- operations in a type safe and declarative way. HaskellDB compiles a
  -- relational algebra-like syntax into SQL, submits the operations to the
  -- database for processing, and returns the results as ordinary Haskell
  -- values.
  --
  -- This is the main module that the user should 
  -- import. Beside this module, the user should import a
  -- particular database binding (ie. "Database.HaskellDB.HSQL.ODBC") 
  -- and database definitions.
  --
  -- HaskellDB was originally written by Daan Leijen and it's 
  -- design is described in the paper Domain Specific Embedded 
  -- Compilers, Daan Leijen and Erik Meijer. 2nd USENIX 
  -- Conference on Domain-Specific Languages (DSL), Austin, 
  -- USA, October 1999 (<http://www.usenix.org/events/dsl99/>).
  --
  -- This new version of HaskellDB was produced as a student project at
  -- Chalmers University of Technology in Gothenburg, Sweden. The aim of the
  -- project was to make HaskellDB a practically useful database library.
  -- That work is described in 
  -- Student Paper: HaskellDB Improved, 
  -- Björn Bringert, Anders Höckersten, Conny Andersson, Martin Andersson, 
  -- Mary Bergman, Victor Blomqvist and Torbjörn Martin. 
  -- In Proceedings of the ACM SIGPLAN 2004 Haskell Workshop, Snowbird, Utah, 
  -- USA, September 22, 2004.
  -- (<http://haskelldb.sourceforge.net/haskelldb.pdf>)
  --
  -----------------------------------------------------------
  module Database.HaskellDB
    ( Rel, Attr, Expr, ExprAggr, Table, Query, OrderExpr
    -- * Records
    , HasField, Record, Select, ( # ), ( << ), (<<-), (!), (!.)
  
    -- * Relational operators
    , restrict, table, project, unique
    , union, intersect, divide, minus
    , copy, copyAll, subQuery
  
    -- * Query expressions
    , (.==.) , (.<>.), (.<.), (.<=.), (.>.), (.>=.)
    , (.&&.) , (.||.)
    , (.*.) , (./.), (.+.), (.-.), (.%.), (.++.)
    , _not, like, _in, cat, _length
    , isNull, notNull, fromNull, fromVal
    , constant, constVal, constNull, constExpr
    , param, namedParam, Args, func
    , queryParams, Param, cast, coerce
    , literal, toStr
    , count, _sum, _max, _min, avg
    , stddev, stddevP, variance, varianceP
    , asc, desc, order
    , top, offset, _case , _default
  
    -- * Database operations
    , Database				-- abstract
    , query, recCat
    , insert, delete, update, insertQuery
    , tables, describe, transaction
            
    -- * Debugging
    , showQuery, showQueryUnOpt, showSql, showSqlUnOpt
    ) where
  
  import Database.HaskellDB.HDBRec
  
  -- PrimQuery type is imported so that haddock can find it.
  import Database.HaskellDB.PrimQuery (PrimQuery)
  import Database.HaskellDB.Sql (SqlSelect(SqlSelect, SqlBin), SqlExpr(..), SqlName)
  import qualified Database.HaskellDB.Sql as S (SqlSelect(..), Mark(..))
  import Database.HaskellDB.Sql.Generate (sqlQuery)
  import Database.HaskellDB.Sql.Default (defaultSqlGenerator)
  import Database.HaskellDB.Sql.Print (ppSql)
  import Database.HaskellDB.Optimize  (optimize)
  import Database.HaskellDB.Query
  import Database.HaskellDB.Database
  import Text.PrettyPrint.HughesPJ (Doc)
  import Data.Foldable (foldr')
  
  -- | Represents a query parameter. Left parameters are indexed
  -- by position, while right parameters are named.
  type Param = Either Int String
  
  -- | Shows the optimized SQL for the query.
  instance Show (Query (Rel r)) where
    showsPrec _ query = shows (showSql query)
  
  -- | Shows the optimized 'PrimQuery'.
  showQuery :: Query (Rel r) -> String
  showQuery = show . optimize . runQuery
  
  -- | Shows the unoptimized 'PrimQuery'.
  showQueryUnOpt :: Query (Rel r) -> String
  showQueryUnOpt = show . runQuery
  
  -- | Shows the optimized SQL query.
  showSql :: Query (Rel r) -> String
  showSql = show . ppSql . sqlQuery defaultSqlGenerator . optimize . runQuery 
  
  -- | Shows the unoptimized SQL query.
  showSqlUnOpt :: Query (Rel r) -> String
  showSqlUnOpt = show . ppSql . sqlQuery defaultSqlGenerator . runQuery
  
  -- | Get paramaters from a query in order.
  queryParams :: Query (Rel r) -> [Param]
  queryParams q = snd . indexParams . selectParams . toSelect $ q
    where
      -- Use foldr so we don't have to reverse parameter list built.
      indexParams = foldr' renumber (1, [])
      renumber (Just n) (idx, ps) = (idx, Right n : ps)
      renumber Nothing (idx, ps) = (idx + 1, Left idx : ps)
      toSelect = sqlQuery defaultSqlGenerator . optimize . runQuery 
      -- | All parameters that are in the select, in the textual order
      -- they will appear.
      selectParams :: SqlSelect -> [Maybe SqlName]
      selectParams select@(SqlSelect  { S.attrs = a, S.tables = t, S.criteria = c, S.groupby = g, S.orderby = o})
          = (attrParams a ++) . (tableParams t ++) . (criteriaParams c ++) .
              (groupByParams g ++) . orderByParams $ o
        where
          attrParams = getParams (exprParams . snd) 
          tableParams = getParams (selectParams . snd) 
          criteriaParams = getParams exprParams
          groupByParams Nothing = [] 
          groupByParams (Just S.All) = []
          groupByParams (Just (S.Columns cols)) = getParams (exprParams . snd) cols
          orderByParams = getParams (exprParams . fst) 
          getParams :: (a -> [Maybe SqlName]) -> [a] -> [Maybe SqlName]
          getParams f = concatMap f
          -- | All parameters in the expression, in the textual order
          -- in which they will appear.
          exprParams :: SqlExpr -> [Maybe SqlName]
          exprParams (ColumnSqlExpr _) = [] 
          exprParams (ConstSqlExpr _) = []
          exprParams (ParamSqlExpr p _) = [p] 
          exprParams (BinSqlExpr _ l r) = exprParams l ++ exprParams r
          exprParams (PrefixSqlExpr _ e) = exprParams e
          exprParams (PostfixSqlExpr _ e) = exprParams e
          exprParams (FunSqlExpr _ es) = (concatMap exprParams es)
          exprParams (AggrFunSqlExpr _ es) = (concatMap exprParams es)
          exprParams (CaseSqlExpr es e) =
            let caseExprs = concatMap (\(l, r) -> exprParams l ++ exprParams r) es
            in caseExprs ++ exprParams e
          exprParams (ListSqlExpr es) = concatMap exprParams es
          exprParams (ExistsSqlExpr select) = selectParams select
          exprParams PlaceHolderSqlExpr = []
          exprParams (ParensSqlExpr e) = exprParams e
          exprParams (CastSqlExpr _ e) = exprParams e
      selectParams (SqlBin _ l r) = selectParams l ++ selectParams r
      selectParams _ = []
  