-- | Binary relational operations on 'S.Select's, that is, operations
-- which take two 'S.Select's as arguments and return a single 'S.Select'.
--
-- All the binary relational operations have the same type
-- specializations.  For example:
--
-- @
-- unionAll :: S.Select (Field a, Field b)
--          -> S.Select (Field a, Field b)
--          -> S.Select (Field a, Field b)
-- @
--
-- Assuming the @makeAdaptorAndInstance@ splice has been run for the product type @Foo@:
--
-- @
-- unionAll :: S.Select (Foo (Field a) (Field b) (Field c))
--          -> S.Select (Foo (Field a) (Field b) (Field c))
--          -> S.Select (Foo (Field a) (Field b) (Field c))
-- @
--
-- If you want to run a binary relational operator on
-- 'Select.SelectArr's you should apply 'Opaleye.Lateral.bilaterally'
-- to it, for example
--
-- @
-- 'Opaleye.Lateral.bilaterally' 'union'
--   :: 'Data.Profunctor.Product.Default' 'B.Binaryspec' fields fields
--   => 'S.SelectArr' i fields -> 'S.SelectArr' i fields -> 'S.SelectArr' i fields
-- @
--
-- `unionAll` is very close to being the @\<|\>@ operator of a
-- @Control.Applicative.Alternative@ instance but it fails to work
-- only because of the typeclass constraint it has.

{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts #-}

module Opaleye.Binary (-- * Binary operations
                       unionAll,
                       union,
                       intersectAll,
                       intersect,
                       exceptAll,
                       except,
                       -- * Explicit versions
                       unionAllExplicit,
                       unionExplicit,
                       intersectAllExplicit,
                       intersectExplicit,
                       exceptAllExplicit,
                       exceptExplicit,
                       -- * Adaptors
                       binaryspecField,
                      ) where

import qualified Opaleye.Internal.Binary as B
import qualified Opaleye.Internal.Column
import qualified Opaleye.Internal.PrimQuery as PQ
import qualified Opaleye.Select             as S

import           Data.Profunctor.Product.Default (Default, def)

unionAll :: Default B.Binaryspec fields fields =>
            S.Select fields -> S.Select fields -> S.Select fields
unionAll :: Select fields -> Select fields -> Select fields
unionAll = Binaryspec fields fields
-> Select fields -> Select fields -> Select fields
forall fields fields'.
Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
unionAllExplicit Binaryspec fields fields
forall (p :: * -> * -> *) a b. Default p a b => p a b
def

-- | The same as 'unionAll', except that it additionally removes any
--   duplicate rows.
union :: Default B.Binaryspec fields fields =>
         S.Select fields -> S.Select fields -> S.Select fields
union :: Select fields -> Select fields -> Select fields
union = Binaryspec fields fields
-> Select fields -> Select fields -> Select fields
forall fields fields'.
Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
unionExplicit Binaryspec fields fields
forall (p :: * -> * -> *) a b. Default p a b => p a b
def

intersectAll :: Default B.Binaryspec fields fields =>
            S.Select fields -> S.Select fields -> S.Select fields
intersectAll :: Select fields -> Select fields -> Select fields
intersectAll = Binaryspec fields fields
-> Select fields -> Select fields -> Select fields
forall fields fields'.
Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
intersectAllExplicit Binaryspec fields fields
forall (p :: * -> * -> *) a b. Default p a b => p a b
def

-- | The same as 'intersectAll', except that it additionally removes any
--   duplicate rows.
intersect :: Default B.Binaryspec fields fields =>
         S.Select fields -> S.Select fields -> S.Select fields
intersect :: Select fields -> Select fields -> Select fields
intersect = Binaryspec fields fields
-> Select fields -> Select fields -> Select fields
forall fields fields'.
Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
intersectExplicit Binaryspec fields fields
forall (p :: * -> * -> *) a b. Default p a b => p a b
def

exceptAll :: Default B.Binaryspec fields fields =>
            S.Select fields -> S.Select fields -> S.Select fields
exceptAll :: Select fields -> Select fields -> Select fields
exceptAll = Binaryspec fields fields
-> Select fields -> Select fields -> Select fields
forall fields fields'.
Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
exceptAllExplicit Binaryspec fields fields
forall (p :: * -> * -> *) a b. Default p a b => p a b
def

-- | The same as 'exceptAll', except that it additionally removes any
--   duplicate rows.
except :: Default B.Binaryspec fields fields =>
         S.Select fields -> S.Select fields -> S.Select fields
except :: Select fields -> Select fields -> Select fields
except = Binaryspec fields fields
-> Select fields -> Select fields -> Select fields
forall fields fields'.
Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
exceptExplicit Binaryspec fields fields
forall (p :: * -> * -> *) a b. Default p a b => p a b
def

unionAllExplicit :: B.Binaryspec fields fields'
                 -> S.Select fields -> S.Select fields -> S.Select fields'
unionAllExplicit :: Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
unionAllExplicit = BinOp
-> Binaryspec fields fields'
-> Select fields
-> Select fields
-> Select fields'
forall columns columns'.
BinOp
-> Binaryspec columns columns'
-> Query columns
-> Query columns
-> Query columns'
B.sameTypeBinOpHelper BinOp
PQ.UnionAll

unionExplicit :: B.Binaryspec fields fields'
              -> S.Select fields -> S.Select fields -> S.Select fields'
unionExplicit :: Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
unionExplicit = BinOp
-> Binaryspec fields fields'
-> Select fields
-> Select fields
-> Select fields'
forall columns columns'.
BinOp
-> Binaryspec columns columns'
-> Query columns
-> Query columns
-> Query columns'
B.sameTypeBinOpHelper BinOp
PQ.Union

intersectAllExplicit :: B.Binaryspec fields fields'
                 -> S.Select fields -> S.Select fields -> S.Select fields'
intersectAllExplicit :: Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
intersectAllExplicit = BinOp
-> Binaryspec fields fields'
-> Select fields
-> Select fields
-> Select fields'
forall columns columns'.
BinOp
-> Binaryspec columns columns'
-> Query columns
-> Query columns
-> Query columns'
B.sameTypeBinOpHelper BinOp
PQ.IntersectAll

intersectExplicit :: B.Binaryspec fields fields'
              -> S.Select fields -> S.Select fields -> S.Select fields'
intersectExplicit :: Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
intersectExplicit = BinOp
-> Binaryspec fields fields'
-> Select fields
-> Select fields
-> Select fields'
forall columns columns'.
BinOp
-> Binaryspec columns columns'
-> Query columns
-> Query columns
-> Query columns'
B.sameTypeBinOpHelper BinOp
PQ.Intersect

exceptAllExplicit :: B.Binaryspec fields fields'
                 -> S.Select fields -> S.Select fields -> S.Select fields'
exceptAllExplicit :: Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
exceptAllExplicit = BinOp
-> Binaryspec fields fields'
-> Select fields
-> Select fields
-> Select fields'
forall columns columns'.
BinOp
-> Binaryspec columns columns'
-> Query columns
-> Query columns
-> Query columns'
B.sameTypeBinOpHelper BinOp
PQ.ExceptAll

exceptExplicit :: B.Binaryspec fields fields'
              -> S.Select fields -> S.Select fields -> S.Select fields'
exceptExplicit :: Binaryspec fields fields'
-> Select fields -> Select fields -> Select fields'
exceptExplicit = BinOp
-> Binaryspec fields fields'
-> Select fields
-> Select fields
-> Select fields'
forall columns columns'.
BinOp
-> Binaryspec columns columns'
-> Query columns
-> Query columns
-> Query columns'
B.sameTypeBinOpHelper BinOp
PQ.Except

binaryspecField :: (B.Binaryspec
                        (Opaleye.Internal.Column.Column a)
                        (Opaleye.Internal.Column.Column a))
binaryspecField :: Binaryspec (Column a) (Column a)
binaryspecField = Binaryspec (Column a) (Column a)
forall a. Binaryspec (Column a) (Column a)
B.binaryspecColumn