{-|
Module: Squeal.PostgreSQL.Session.Statement
Description: statements
Copyright: (c) Eitan Chatav, 2019
Maintainer: eitan@morphism.tech
Stability: experimental

A top-level `Statement` type wraps a `Squeal.PostgreSQL.Query.Query`
or `Squeal.PostgreSQL.Manipulation.Manipulation`
together with an `EncodeParams` and a `DecodeRow`.
-}

{-# LANGUAGE
    DataKinds
  , DeriveFunctor
  , DeriveFoldable
  , DeriveGeneric
  , DeriveTraversable
  , FlexibleContexts
  , GADTs
  , RankNTypes
#-}

module Squeal.PostgreSQL.Session.Statement
  ( Statement (..)
  , query
  , manipulation
  ) where

import Data.Functor.Contravariant
import Data.Profunctor (Profunctor (..))

import qualified Generics.SOP as SOP

import Squeal.PostgreSQL.Manipulation
import Squeal.PostgreSQL.Session.Decode
import Squeal.PostgreSQL.Session.Encode
import Squeal.PostgreSQL.Session.Oid
import Squeal.PostgreSQL.Query
import Squeal.PostgreSQL.Render

-- | A `Statement` consists of a `Squeal.PostgreSQL.Statement.Manipulation`
-- or a `Squeal.PostgreSQL.Session.Statement.Query` that can be run
-- in a `Squeal.PostgreSQL.Session.Monad.MonadPQ`.
data Statement db x y where
  -- | Constructor for a data manipulation language statement
  Manipulation
    :: (SOP.All (OidOfNull db) params, SOP.SListI row)
    => EncodeParams db params x -- ^ encoding of parameters
    -> DecodeRow row y -- ^ decoding of returned rows
    -> Manipulation '[] db params row
    -- ^ `Squeal.PostgreSQL.Manipulation.Insert.insertInto`,
    -- `Squeal.PostgreSQL.Manipulation.Update.update`,
    -- or `Squeal.PostgreSQL.Manipulation.Delete.deleteFrom`, ...
    -> Statement db x y
  -- | Constructor for a structured query language statement
  Query
    :: (SOP.All (OidOfNull db) params, SOP.SListI row)
    => EncodeParams db params x -- ^ encoding of parameters
    -> DecodeRow row y -- ^ decoding of returned rows
    -> Query '[] '[] db params row
    -- ^ `Squeal.PostgreSQL.Query.Select.select`,
    -- `Squeal.PostgreSQL.Query.Values.values`, ...
    -> Statement db x y

instance Profunctor (Statement db) where
  lmap :: (a -> b) -> Statement db b c -> Statement db a c
lmap a -> b
f (Manipulation EncodeParams db params b
encode DecodeRow row c
decode Manipulation '[] db params row
q) =
    EncodeParams db params a
-> DecodeRow row c
-> Manipulation '[] db params row
-> Statement db a c
forall (db :: SchemasType) (params :: [NullType]) (row :: RowType)
       x y.
(All (OidOfNull db) params, SListI row) =>
EncodeParams db params x
-> DecodeRow row y
-> Manipulation '[] db params row
-> Statement db x y
Manipulation ((a -> b) -> EncodeParams db params b -> EncodeParams db params a
forall (f :: * -> *) a b. Contravariant f => (a -> b) -> f b -> f a
contramap a -> b
f EncodeParams db params b
encode) DecodeRow row c
decode Manipulation '[] db params row
q
  lmap a -> b
f (Query EncodeParams db params b
encode DecodeRow row c
decode Query '[] '[] db params row
q) =
    EncodeParams db params a
-> DecodeRow row c
-> Query '[] '[] db params row
-> Statement db a c
forall (db :: SchemasType) (params :: [NullType]) (row :: RowType)
       x y.
(All (OidOfNull db) params, SListI row) =>
EncodeParams db params x
-> DecodeRow row y
-> Query '[] '[] db params row
-> Statement db x y
Query ((a -> b) -> EncodeParams db params b -> EncodeParams db params a
forall (f :: * -> *) a b. Contravariant f => (a -> b) -> f b -> f a
contramap a -> b
f EncodeParams db params b
encode) DecodeRow row c
decode Query '[] '[] db params row
q
  rmap :: (b -> c) -> Statement db a b -> Statement db a c
rmap b -> c
f (Manipulation EncodeParams db params a
encode DecodeRow row b
decode Manipulation '[] db params row
q) =
    EncodeParams db params a
-> DecodeRow row c
-> Manipulation '[] db params row
-> Statement db a c
forall (db :: SchemasType) (params :: [NullType]) (row :: RowType)
       x y.
(All (OidOfNull db) params, SListI row) =>
EncodeParams db params x
-> DecodeRow row y
-> Manipulation '[] db params row
-> Statement db x y
Manipulation EncodeParams db params a
encode ((b -> c) -> DecodeRow row b -> DecodeRow row c
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap b -> c
f DecodeRow row b
decode) Manipulation '[] db params row
q
  rmap b -> c
f (Query EncodeParams db params a
encode DecodeRow row b
decode Query '[] '[] db params row
q) =
    EncodeParams db params a
-> DecodeRow row c
-> Query '[] '[] db params row
-> Statement db a c
forall (db :: SchemasType) (params :: [NullType]) (row :: RowType)
       x y.
(All (OidOfNull db) params, SListI row) =>
EncodeParams db params x
-> DecodeRow row y
-> Query '[] '[] db params row
-> Statement db x y
Query EncodeParams db params a
encode ((b -> c) -> DecodeRow row b -> DecodeRow row c
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap b -> c
f DecodeRow row b
decode) Query '[] '[] db params row
q
  dimap :: (a -> b) -> (c -> d) -> Statement db b c -> Statement db a d
dimap a -> b
f c -> d
g (Manipulation EncodeParams db params b
encode DecodeRow row c
decode Manipulation '[] db params row
q) =
    EncodeParams db params a
-> DecodeRow row d
-> Manipulation '[] db params row
-> Statement db a d
forall (db :: SchemasType) (params :: [NullType]) (row :: RowType)
       x y.
(All (OidOfNull db) params, SListI row) =>
EncodeParams db params x
-> DecodeRow row y
-> Manipulation '[] db params row
-> Statement db x y
Manipulation ((a -> b) -> EncodeParams db params b -> EncodeParams db params a
forall (f :: * -> *) a b. Contravariant f => (a -> b) -> f b -> f a
contramap a -> b
f EncodeParams db params b
encode) ((c -> d) -> DecodeRow row c -> DecodeRow row d
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap c -> d
g DecodeRow row c
decode) Manipulation '[] db params row
q
  dimap a -> b
f c -> d
g (Query EncodeParams db params b
encode DecodeRow row c
decode Query '[] '[] db params row
q) =
    EncodeParams db params a
-> DecodeRow row d
-> Query '[] '[] db params row
-> Statement db a d
forall (db :: SchemasType) (params :: [NullType]) (row :: RowType)
       x y.
(All (OidOfNull db) params, SListI row) =>
EncodeParams db params x
-> DecodeRow row y
-> Query '[] '[] db params row
-> Statement db x y
Query ((a -> b) -> EncodeParams db params b -> EncodeParams db params a
forall (f :: * -> *) a b. Contravariant f => (a -> b) -> f b -> f a
contramap a -> b
f EncodeParams db params b
encode) ((c -> d) -> DecodeRow row c -> DecodeRow row d
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap c -> d
g DecodeRow row c
decode) Query '[] '[] db params row
q

instance Functor (Statement db x) where fmap :: (a -> b) -> Statement db x a -> Statement db x b
fmap = (a -> b) -> Statement db x a -> Statement db x b
forall (p :: * -> * -> *) b c a.
Profunctor p =>
(b -> c) -> p a b -> p a c
rmap

instance RenderSQL (Statement db x y) where
  renderSQL :: Statement db x y -> ByteString
renderSQL (Manipulation EncodeParams db params x
_ DecodeRow row y
_ Manipulation '[] db params row
q) = Manipulation '[] db params row -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL Manipulation '[] db params row
q
  renderSQL (Query EncodeParams db params x
_ DecodeRow row y
_ Query '[] '[] db params row
q) = Query '[] '[] db params row -> ByteString
forall sql. RenderSQL sql => sql -> ByteString
renderSQL Query '[] '[] db params row
q

-- | Smart constructor for a structured query language statement
query ::
  ( GenericParams db params x xs
  , GenericRow row y ys
  ) => Query '[] '[] db params row
    -- ^ `Squeal.PostgreSQL.Query.Select.select`,
    -- `Squeal.PostgreSQL.Query.Values.values`, ...
    -> Statement db x y
query :: Query '[] '[] db params row -> Statement db x y
query = EncodeParams db params x
-> DecodeRow row y
-> Query '[] '[] db params row
-> Statement db x y
forall (db :: SchemasType) (params :: [NullType]) (row :: RowType)
       x y.
(All (OidOfNull db) params, SListI row) =>
EncodeParams db params x
-> DecodeRow row y
-> Query '[] '[] db params row
-> Statement db x y
Query EncodeParams db params x
forall (db :: SchemasType) (params :: [NullType]) x (xs :: [*]).
GenericParams db params x xs =>
EncodeParams db params x
genericParams DecodeRow row y
forall (row :: RowType) y (ys :: RecordCode).
GenericRow row y ys =>
DecodeRow row y
genericRow

-- | Smart constructor for a data manipulation language statement
manipulation ::
  ( GenericParams db params x xs
  , GenericRow row y ys
  ) => Manipulation '[] db params row
    -- ^ `Squeal.PostgreSQL.Manipulation.Insert.insertInto`,
    -- `Squeal.PostgreSQL.Manipulation.Update.update`,
    -- or `Squeal.PostgreSQL.Manipulation.Delete.deleteFrom`, ...
    -> Statement db x y
manipulation :: Manipulation '[] db params row -> Statement db x y
manipulation = EncodeParams db params x
-> DecodeRow row y
-> Manipulation '[] db params row
-> Statement db x y
forall (db :: SchemasType) (params :: [NullType]) (row :: RowType)
       x y.
(All (OidOfNull db) params, SListI row) =>
EncodeParams db params x
-> DecodeRow row y
-> Manipulation '[] db params row
-> Statement db x y
Manipulation EncodeParams db params x
forall (db :: SchemasType) (params :: [NullType]) x (xs :: [*]).
GenericParams db params x xs =>
EncodeParams db params x
genericParams DecodeRow row y
forall (row :: RowType) y (ys :: RecordCode).
GenericRow row y ys =>
DecodeRow row y
genericRow