packages feed

pg-migrate-1.0.0.0: src/Database/PostgreSQL/Migrate/Runner/Connection.hs

module Database.PostgreSQL.Migrate.Runner.Connection
  ( connectionProviderFromSettings,
    checkServerVersion,
    classifyServerVersion,
  )
where

import Control.Exception (finally, mask)
import Data.Int (Int32)
import Database.PostgreSQL.Migrate.Runner.Types
import Hasql.Connection qualified as Connection
import Hasql.Connection.Settings qualified as Settings
import Hasql.Decoders qualified as Decoders
import Hasql.Encoders qualified as Encoders
import Hasql.Session qualified as Session
import Hasql.Statement (Statement)
import Hasql.Statement qualified as Statement

-- | Acquire and release a fresh Hasql connection from concrete settings.
connectionProviderFromSettings :: Settings.Settings -> ConnectionProvider
connectionProviderFromSettings settings =
  ConnectionProvider $ \action ->
    mask $ \restore -> do
      acquired <- Connection.acquire settings
      case acquired of
        Left connectionError -> pure (Left connectionError)
        Right connection ->
          Right <$> restore (action connection) `finally` Connection.release connection

checkServerVersion :: Connection.Connection -> IO (Either MigrationError Int)
checkServerVersion connection = do
  result <- Connection.use connection (Session.statement () serverVersionStatement)
  pure $ case result of
    Left sessionError -> Left (DatabaseSessionFailed sessionError)
    Right serverVersion -> classifyServerVersion serverVersion

classifyServerVersion :: Int32 -> Either MigrationError Int
classifyServerVersion serverVersionNumber
  | majorVersion `elem` [17, 18] = Right majorVersion
  | otherwise = Left (UnsupportedPostgresVersion majorVersion)
  where
    majorVersion = fromIntegral serverVersionNumber `div` 10000

serverVersionStatement :: Statement () Int32
serverVersionStatement =
  Statement.preparable
    "SELECT current_setting('server_version_num')::integer"
    Encoders.noParams
    (Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.int4)))