packages feed

hpgsql-simple-compat-0.1.0.0: src/Database/PostgreSQL/Simple/Notification.hs

-----------------------------------------------------------------------------

-----------------------------------------------------------------------------

-- |
-- Module      :  Database.PostgreSQL.Simple.Notification
-- Copyright   :  (c) 2011-2015 Leon P Smith
--                (c) 2012 Joey Adams
-- License     :  BSD3
--
-- Maintainer  :  leon@melding-monads.com
-- Stability   :  experimental
--
-- Support for receiving asynchronous notifications via PostgreSQL's
-- Listen/Notify mechanism.  See
-- <https://www.postgresql.org/docs/9.5/static/sql-notify.html> for more
-- information.
--
-- Note that on Windows,  @getNotification@ currently uses a polling loop
-- of 1 second to check for more notifications,  due to some inadequacies
-- in GHC's IO implementation and interface on that platform.   See GHC
-- issue #7353 for more information.  While this workaround is less than
-- ideal,  notifications are still better than polling the database directly.
-- Notifications do not create any extra work for the backend,  and are
-- likely cheaper on the client side as well.
--
-- <https://hackage.haskell.org/trac/ghc/ticket/7353>
module Database.PostgreSQL.Simple.Notification
  ( Notification (..),
    getNotification,
    getNotificationNonBlocking,
    getBackendPID,
  )
where

import qualified Data.ByteString as B
import Data.Text.Encoding (encodeUtf8)
import Database.PostgreSQL.Simple.Internal
import qualified Hpgsql.Connection
import qualified Hpgsql.Notification
import System.Posix.Types (CPid)

data Notification = Notification
  { notificationPid :: {-# UNPACK #-} !CPid,
    notificationChannel :: {-# UNPACK #-} !B.ByteString,
    notificationData :: {-# UNPACK #-} !B.ByteString
  }
  deriving (Show, Eq)

-- | Returns a single notification.   If no notifications are available,
--   'getNotification' blocks until one arrives.
--
--   It is safe to call 'getNotification' on a connection that is concurrently
--   being used for other purposes,   note however that PostgreSQL does not
--   deliver notifications while a connection is inside a transaction.
getNotification :: Connection -> IO Notification
getNotification conn = fromHpgsqlNotification <$> Hpgsql.Notification.getNotification (hpgConn conn)

-- | Non-blocking variant of 'getNotification'.   Returns a single notification,
-- if available.   If no notifications are available,  returns 'Nothing'.
getNotificationNonBlocking :: Connection -> IO (Maybe Notification)
getNotificationNonBlocking conn = fmap fromHpgsqlNotification <$> Hpgsql.Notification.getNotificationNonBlocking (hpgConn conn)

fromHpgsqlNotification :: Hpgsql.Notification.NotificationResponse -> Notification
fromHpgsqlNotification Hpgsql.Notification.NotificationResponse {..} =
  Notification
    { notificationPid = fromIntegral notifierPid,
      notificationChannel = encodeUtf8 channelName,
      notificationData = encodeUtf8 notifPayload
    }

-- | Returns the process 'CPid' of the backend server process
-- handling this connection.
--
-- The backend PID is useful for debugging purposes and for comparison
-- to NOTIFY messages (which include the PID of the notifying backend
-- process). Note that the PID belongs to a process executing on the
-- database server host, not the local host!
getBackendPID :: Connection -> IO CPid
getBackendPID conn = pure $ fromIntegral $ Hpgsql.Connection.getBackendPid (hpgConn conn)