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

database connections
-}

{-# LANGUAGE
    DataKinds
  , PolyKinds
  , RankNTypes
  , TypeOperators
#-}

module Squeal.PostgreSQL.Session.Connection
  ( LibPQ.Connection
  , connectdb
  , finish
  , lowerConnection
  , SOP.K (..)
  , SOP.unK
  ) where

import Control.Monad.IO.Class
import Data.ByteString (ByteString)

import Squeal.PostgreSQL.Type.Schema

import qualified Generics.SOP as SOP
import qualified Database.PostgreSQL.LibPQ as LibPQ

-- $setup
-- >>> import Squeal.PostgreSQL

{- | Makes a new connection to the database server.

This function opens a new database connection using the parameters taken
from the string conninfo.

The passed string can be empty to use all default parameters, or it can
contain one or more parameter settings separated by whitespace.
Each parameter setting is in the form keyword = value. Spaces around the equal
sign are optional. To write an empty value or a value containing spaces,
surround it with single quotes, e.g., keyword = 'a value'. Single quotes and
backslashes within the value must be escaped with a backslash, i.e., ' and \.

To specify the schema you wish to connect with, use type application.

>>> :set -XDataKinds
>>> :set -XPolyKinds
>>> :set -XTypeOperators
>>> type DB = '["public" ::: '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint2])]]
>>> :set -XTypeApplications
>>> :set -XOverloadedStrings
>>> conn <- connectdb @DB "host=localhost port=5432 dbname=exampledb user=postgres password=postgres"

Note that, for now, squeal doesn't offer any protection from connecting
with the wrong schema!
-}
connectdb
  :: forall (db :: SchemasType) io
   . MonadIO io
  => ByteString -- ^ conninfo
  -> io (SOP.K LibPQ.Connection db)
connectdb :: ByteString -> io (K Connection db)
connectdb = (Connection -> K Connection db)
-> io Connection -> io (K Connection db)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Connection -> K Connection db
forall k a (b :: k). a -> K a b
SOP.K (io Connection -> io (K Connection db))
-> (ByteString -> io Connection)
-> ByteString
-> io (K Connection db)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO Connection -> io Connection
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Connection -> io Connection)
-> (ByteString -> IO Connection) -> ByteString -> io Connection
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> IO Connection
LibPQ.connectdb

-- | Closes the connection to the server.
finish :: MonadIO io => SOP.K LibPQ.Connection db -> io ()
finish :: K Connection db -> io ()
finish = IO () -> io ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> io ())
-> (K Connection db -> IO ()) -> K Connection db -> io ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Connection -> IO ()
LibPQ.finish (Connection -> IO ())
-> (K Connection db -> Connection) -> K Connection db -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. K Connection db -> Connection
forall k a (b :: k). K a b -> a
SOP.unK

-- | Safely `lowerConnection` to a smaller schema.
lowerConnection
  :: SOP.K LibPQ.Connection (schema ': db)
  -> SOP.K LibPQ.Connection db
lowerConnection :: K Connection (schema : db) -> K Connection db
lowerConnection (SOP.K Connection
conn) = Connection -> K Connection db
forall k a (b :: k). a -> K a b
SOP.K Connection
conn