psql-0.0.0: lib/PostgreSQL/Param.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Here are things dealing with query parameters.
module PostgreSQL.Param
( -- * Basics
Type (..)
, typeOid
, Info (..)
, Types.Value (..)
, Types.Oid
, Types.Format (..)
, Types.PackedParam (..)
, packParam
, toPrepared
, Types.PackedParamPrepared (..)
, packParamPrepared
-- * Class
, Param (..)
, RawText (..)
)
where
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as ByteString.Char8
import Data.Text (Text)
import Data.Text.Encoding (encodeUtf8)
import Database.PostgreSQL.LibPQ (invalidOid)
import GHC.Generics (Generic)
import qualified PostgreSQL.Types as Types
-- | Parameter type
--
-- @since 0.0.0
data Type
= InferredType
-- ^ Type is inferred on the server side
--
-- @since 0.0.0
| StaticType Types.Oid
-- ^ Explicit static type
--
-- @since 0.0.0
deriving (Show, Eq, Ord)
-- | Get the OID for the type.
--
-- @since 0.0.0
typeOid :: Type -> Types.Oid
typeOid = \case
InferredType -> invalidOid
StaticType oid -> oid
{-# INLINE typeOid #-}
-- | Static parameter information
--
-- @since 0.0.0
data Info a = Info
{ info_type :: Type
, info_typeName :: Maybe Text
-- ^ This may be used as an explicit type annotation when used in a 'Statement' or 'Template'
, info_format :: Types.Format
, info_pack :: a
}
deriving stock (Functor, Foldable, Traversable, Generic)
-- | Pack a parameter into a @postgresql-libpq@ format.
--
-- @since 0.0.0
packParam :: Info Types.Value -> Types.PackedParam
packParam paramInfo = Types.PackedParam $
case info_pack paramInfo of
Types.Null -> Nothing
Types.Value datas -> Just (oid, datas, info_format paramInfo)
where
oid = typeOid $ info_type paramInfo
{-# INLINE packParam #-}
-- | Convert 'PackedParam'.
--
-- @since 0.0.0
toPrepared :: Types.PackedParam -> Types.PackedParamPrepared
toPrepared (Types.PackedParam param) = Types.PackedParamPrepared $ do
(_, datas, format) <- param
pure (datas, format)
{-# INLINE toPrepared #-}
-- | Pack a parameter for a prepared query into a @postgresql-libpq@ format.
--
-- @since 0.0.0
packParamPrepared :: Info Types.Value -> Types.PackedParamPrepared
packParamPrepared paramInfo = Types.PackedParamPrepared $
case info_pack paramInfo of
Types.Null -> Nothing
Types.Value datas -> Just (datas, info_format paramInfo)
{-# INLINE packParamPrepared #-}
-- | @a@ can be used as a parameter
--
-- @since 0.0.0
class Param a where
-- | Parameter information
--
-- @since 0.0.0
paramInfo :: Info (a -> Types.Value)
-- | @since 0.0.0
instance Param Integer where
paramInfo = Info
{ info_type = InferredType
, info_typeName = Nothing
, info_format = Types.Text
, info_pack = Types.Value . ByteString.Char8.pack . show
}
-- | @since 0.0.0
instance Param Double where
paramInfo = Info
{ info_type = InferredType
, info_typeName = Just "float8"
, info_format = Types.Text
, info_pack = Types.Value . ByteString.Char8.pack . show
}
-- | @since 0.0.0
instance Param Text where
paramInfo = Info
{ info_type = InferredType
, info_typeName = Nothing
, info_format = Types.Text
, info_pack = Types.Value . encodeUtf8
}
-- | @since 0.0.0
instance Param Types.Oid where
paramInfo = Info
{ info_type = InferredType
, info_typeName = Just "oid"
, info_format = Types.Text
, info_pack = \(Types.Oid inner) -> Types.Value $ ByteString.Char8.pack $ show inner
}
-- | @since 0.0.0
instance Param Types.RegType where
paramInfo = Info
{ info_type = InferredType
, info_typeName = Just "regtype"
, info_format = Types.Text
, info_pack = Types.Value . encodeUtf8 . Types.unRegType
}
-- | Raw textual parameter
--
-- @since 0.0.0
newtype RawText = RawText
{ unRawText :: ByteString }
deriving (Show, Eq, Ord)
-- | @since 0.0.0
instance Param RawText where
paramInfo = Info
{ info_type = InferredType
, info_typeName = Nothing
, info_format = Types.Text
, info_pack = Types.Value . unRawText
}
-- | @since 0.0.0
instance Param Types.Value where
paramInfo = Info
{ info_type = InferredType
, info_typeName = Nothing
, info_format = Types.Text
, info_pack = id
}