    -----------------------------------------------------------
    -- |
    -- 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     = []
                          }