content: {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE FlexibleInstances #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  
  -- |
  -- Module      : Database.Relational.Monad.Class
  -- Copyright   : 2013-2019 Kei Hibino
  -- License     : BSD3
  --
  -- Maintainer  : ex8k.hibino@gmail.com
  -- Stability   : experimental
  -- Portability : unknown
  --
  -- This module defines query building interface classes.
  module Database.Relational.Monad.Class
         ( -- * Query interface classes
           MonadQualify (..), MonadRestrict (..),
           MonadQuery (..), MonadAggregate (..), MonadPartition (..),
  
           all', distinct,
           on, wheres, having,
         ) where
  
  import Database.Relational.Internal.ContextType (Flat, Aggregated)
  import Database.Relational.SqlSyntax (Duplication (..), AggregateKey)
  import Database.Relational.Typed.Record (Predicate, Record)
  
  import Database.Relational.Projectable (PlaceHolders)
  import Database.Relational.Monad.BaseType (ConfigureQuery, Relation)
  
  
  -- | Restrict context interface
  class (Functor m, Monad m) => MonadRestrict c m where
    -- | Add restriction to this context.
    restrict :: Predicate c -- ^ 'Record' which represent restriction
             -> m ()        -- ^ Restricted query context
  
  -- | Query building interface.
  class (Functor m, Monad m, MonadQualify ConfigureQuery m) => MonadQuery m where
    -- | Specify duplication.
    setDuplication :: Duplication -> m ()
    -- | Add restriction to last join.
    restrictJoin :: Predicate Flat -- ^ 'Record' which represent restriction
                 -> m ()           -- ^ Restricted query context
    {- Haddock BUG? -}
    -- | Join sub-query with place-holder parameter 'p'. query result is not 'Maybe'.
    query' :: Relation p r
           -> m (PlaceHolders p, Record Flat r)
    -- | Join sub-query with place-holder parameter 'p'. Query result is 'Maybe'.
    queryMaybe' :: Relation p r
                -> m (PlaceHolders p, Record Flat (Maybe r))
  
  -- | Lift interface from base qualify monad.
  class (Functor q, Monad q, Functor m, Monad m) => MonadQualify q m where
    -- | Lift from qualify monad 'q' into 'MonadQuery' m.
    --   Qualify monad qualifies table form 'SubQuery'.
    liftQualify :: q a -> m a
  
  instance (Functor q, Monad q) => MonadQualify q q where
    liftQualify = id
  
  -- | Aggregated query building interface extends 'MonadQuery'.
  class MonadQuery m => MonadAggregate m where
    -- | Add /GROUP BY/ term into context and get aggregated record.
    groupBy :: Record Flat r           -- ^ Record to add into group by
            -> m (Record Aggregated r) -- ^ Result context and aggregated record
    -- | Add /GROUP BY/ term into context and get aggregated record. Non-traditional group-by version.
    groupBy' :: AggregateKey (Record Aggregated r)  -- ^ Key to aggretate for non-traditional group-by interface
             -> m (Record Aggregated r)             -- ^ Result context and aggregated record
  
  -- | Window specification building interface.
  class Monad m => MonadPartition c m where
    -- | Add /PARTITION BY/ term into context.
    partitionBy :: Record c r -> m ()
  
  -- | Specify ALL attribute to query context.
  all' :: MonadQuery m => m ()
  all' =  setDuplication All
  
  -- | Specify DISTINCT attribute to query context.
  distinct :: MonadQuery m => m ()
  distinct =  setDuplication Distinct
  
  -- | Add restriction to last join. Record type version.
  on :: MonadQuery m => Predicate Flat -> m ()
  on =  restrictJoin
  
  -- | Add restriction to this not aggregated query.
  wheres :: MonadRestrict Flat m => Predicate Flat -> m ()
  wheres =  restrict
  
  -- | Add restriction to this aggregated query. Aggregated Record type version.
  having :: MonadRestrict Aggregated m => Predicate Aggregated -> m ()
  having =  restrict
  