content: -----------------------------------------------------------
  -- |
  -- Module      :  SQL
  -- Copyright   :  Daan Leijen (c) 1999, daan@cs.uu.nl
  --                HWT Group (c) 2003, haskelldb-users@lists.sourceforge.net
  -- License     :  BSD-style
  -- 
  -- Maintainer  :  haskelldb-users@lists.sourceforge.net
  -- Stability   :  experimental
  -- Portability :  non-portable
  -- 
  -- A data type for SQL.
  --
  -----------------------------------------------------------
  module Database.HaskellDB.Sql ( 
                                 SqlTable,
                                 SqlColumn,
                                 SqlName,
                                 SqlOrder(..),
                                 SqlType(..),
  
  	                       SqlSelect(..), 
  	                       SqlUpdate(..), 
  	                       SqlDelete(..), 
  	                       SqlInsert(..),
  	                       SqlCreate(..), 
  	                       SqlDrop(..),
  
                                 SqlExpr(..),
                                 Mark(..),
  
                                 newSelect, foldSqlExpr, foldSqlSelect
  	                      ) where
  
  
  -----------------------------------------------------------
  -- * SQL data type
  -----------------------------------------------------------
  
  type SqlTable = String
  
  type SqlColumn = String
  
  -- | A valid SQL name for a parameter.
  type SqlName = String
  
  data SqlOrder = SqlAsc | SqlDesc
    deriving Show
  
  data SqlType = SqlType String
               | SqlType1 String Int
               | SqlType2 String Int Int
    deriving Show
  
  data Mark = All | Columns [(SqlColumn, SqlExpr)]
    deriving Show
  
  -- | Data type for SQL SELECT statements.
  data SqlSelect  = SqlSelect { 
                               options   :: [String],                -- ^ DISTINCT, ALL etc.
  			     attrs     :: [(SqlColumn,SqlExpr)],   -- ^ result
                               tables    :: [(SqlTable,SqlSelect)],  -- ^ FROM
                               criteria  :: [SqlExpr],               -- ^ WHERE
                               groupby   :: Maybe Mark,   -- ^ GROUP BY
                               orderby   :: [(SqlExpr,SqlOrder)],    -- ^ ORDER BY
  			     extra     :: [String]                 -- ^ TOP n, etc.
                              }
                  | SqlBin   String SqlSelect SqlSelect -- ^ Binary relational operator
                  | SqlTable SqlTable -- ^ Select a whole table.
                  | SqlEmpty -- ^ Empty select.
    deriving Show
  
  -- | Transform a SqlSelect value.
  foldSqlSelect :: ([String] -> [(SqlColumn,SqlExpr)] 
                             -> [(SqlTable, t)] 
                             -> [SqlExpr] -> Maybe Mark 
                             -> [(SqlExpr,SqlOrder)] 
                             -> [String] -> t
                   , String -> t -> t -> t
                   , SqlTable -> t, t) 
                -> SqlSelect 
                -> t
  foldSqlSelect (select, bin, table, empty) = fold
    where
      fold (SqlSelect opt attr tab crit grou ord ext) = select opt attr (map (\(t, s) -> (t, fold s)) tab) crit grou ord ext
      fold (SqlBin op left right) = bin op (fold left) (fold right)
      fold (SqlTable tab) = table tab
      fold SqlEmpty = empty
  
  -- | Expressions in SQL statements.
  data SqlExpr = ColumnSqlExpr  SqlColumn
               | BinSqlExpr     String SqlExpr SqlExpr
               | PrefixSqlExpr  String SqlExpr
               | PostfixSqlExpr String SqlExpr
               | FunSqlExpr     String [SqlExpr]
               | AggrFunSqlExpr String [SqlExpr] -- ^ Aggregate functions separate from normal functions.
               | ConstSqlExpr   String
  	     | CaseSqlExpr    [(SqlExpr,SqlExpr)] SqlExpr
               | ListSqlExpr    [SqlExpr]
               | ExistsSqlExpr  SqlSelect
               | ParamSqlExpr (Maybe SqlName) SqlExpr
               | PlaceHolderSqlExpr
               | ParensSqlExpr SqlExpr
               | CastSqlExpr String SqlExpr 
    deriving Show
  
  -- | Transform a SqlExpr value.
  foldSqlExpr :: (SqlColumn -> t -- column
                 , String -> t -> t -> t -- bin
                 , String -> t -> t -- prefix
                 , String -> t -> t -- postfix
                 , String -> [t] -> t -- fun
                 , String -> [t] -> t -- aggr
                 , String -> t -- constant
  	       , [(t,t)] -> t -> t -- _case
                 , [t] -> t -- list
                 , SqlSelect -> t -- exists
                 , (Maybe SqlName) -> t -> t -- param
                 , t -- placeHolder
                 , t -> t -- parens
                 , String -> t -> t {- casts -}) 
              -> SqlExpr 
              -> t
  foldSqlExpr (column, bin, prefix, postfix, fun, aggr, constant, _case, list, exists, 
                     param, placeHolder, parens, casts) = fold
    where
      fold (ColumnSqlExpr col) = column col
      fold (BinSqlExpr op left right) = bin op (fold left) (fold right)
      fold (PrefixSqlExpr op exp) = prefix op (fold exp)
      fold (PostfixSqlExpr op exp) = postfix op (fold exp)
      fold (FunSqlExpr name exprs) = fun name (map fold exprs)
      fold (AggrFunSqlExpr name exprs) = aggr name (map fold exprs)
      fold (ConstSqlExpr c) = constant c
      fold (CaseSqlExpr cases def) = _case (map (\(e1, e2) -> (fold e1, fold e2)) cases) (fold def)
      fold (ListSqlExpr exprs) = list (map fold exprs)
      fold (ExistsSqlExpr select) = exists select
      fold (ParamSqlExpr name exp) = param name (fold exp)
      fold PlaceHolderSqlExpr = placeHolder
      fold (ParensSqlExpr exp) = parens (fold exp)
      fold (CastSqlExpr typ exp ) = casts typ (fold exp) 
  
  -- | Data type for SQL UPDATE statements.
  data SqlUpdate  = SqlUpdate SqlTable [(SqlColumn,SqlExpr)] [SqlExpr]
  
  -- | Data type for SQL DELETE statements.
  data SqlDelete  = SqlDelete SqlTable [SqlExpr]
  
  -- | Data type for SQL INSERT statements.
  data SqlInsert  = SqlInsert      SqlTable [SqlColumn] [SqlExpr]
                  | SqlInsertQuery SqlTable [SqlColumn] SqlSelect
  
  -- | Data type for SQL CREATE statements.
  data SqlCreate = SqlCreateDB String -- ^ Create a database
  	       | SqlCreateTable SqlTable [(SqlColumn,(SqlType,Bool))] -- ^ Create a table.
  
  -- | Data type representing the SQL DROP statement.
  data SqlDrop = SqlDropDB String -- ^ Delete a database
  	     | SqlDropTable SqlTable -- ^ Delete a table named SqlTable
  
  newSelect :: SqlSelect
  newSelect = SqlSelect { 
                         options   = [],
                         attrs     = [],
                         tables    = [],
                         criteria  = [],
                         groupby	 = Nothing,
                         orderby	 = [],
                         extra     = []
                        }
  
  