postgresql-replicant-0.2.0.1: src/Database/PostgreSQL/Replicant/Connection.hs
{-# LANGUAGE Strict #-}
{-
Module : Database.PostgreSQL.Replicant.Connection
Description : Create replication handling connections to PostgreSQL
A ReplicantConnection is different from a regular Connection because
it uses a special mode that can send replication commands that regular
Connection objects cannots send.
-}
module Database.PostgreSQL.Replicant.Connection
( -- * Types
ReplicantConnection
-- * Constructor
, connect
, getConnection
, unsafeCreateConnection
)
where
import Control.Concurrent
import Control.Exception
import Database.PostgreSQL.LibPQ
import Network.Socket.KeepAlive
import System.Posix.Types
import Database.PostgreSQL.Replicant.Exception
import Database.PostgreSQL.Replicant.Settings
import Database.PostgreSQL.Replicant.Util
newtype ReplicantConnection
= ReplicantConnection { getConnection :: Connection }
deriving Eq
data ConnectResult
= ConnectSuccess
| ConnectFailure
deriving (Eq, Show)
-- | Connect to the PostgreSQL server in replication mode
connect :: PgSettings -> IO ReplicantConnection
connect settings = do
conn <- connectStart $ pgConnectionString settings
mFd <- socket conn
sockFd <- maybeThrow
(ReplicantException "withLogicalStream: could not get socket fd") mFd
pollResult <- pollConnectStart conn sockFd
case pollResult of
ConnectFailure -> throwIO
$ ReplicantException "withLogicalStream: Unable to connect to the database"
ConnectSuccess -> pure $ ReplicantConnection conn
pollConnectStart :: Connection -> Fd -> IO ConnectResult
pollConnectStart conn fd@(Fd cint) = do
pollStatus <- connectPoll conn
case pollStatus of
PollingReading -> do
threadWaitRead fd
pollConnectStart conn fd
PollingWriting -> do
threadWaitWrite fd
pollConnectStart conn fd
PollingOk -> do
_ <- setKeepAlive cint $ KeepAlive True 60 2
pure ConnectSuccess
PollingFailed -> pure ConnectFailure
-- | Unsafe function for wrapping regular libpq Connection. This is
-- unsafe because the Connection needs to be set up to send
-- replication commands. Improperly constructed connections can lead
-- to runtime exceptions.
unsafeCreateConnection :: Connection -> ReplicantConnection
unsafeCreateConnection = ReplicantConnection