pg-wire-0.1.0.0: src/PgWire/TypeCache.hs
-- | Pool-level cache for resolved PostgreSQL type metadata.
--
-- When multiple connections encounter the same custom OID (e.g., a PG
-- enum), this cache avoids redundant @pg_type@ round-trips. The cache
-- is shared across all connections in a pool via a 'TVar'.
--
-- The cache grows without bound but is bounded in practice by the number
-- of distinct custom types in the database (typically tens, not thousands).
-- Applications that dynamically create and drop types at high volume may
-- want to monitor 'cachedTypeCount' for unexpected growth.
module PgWire.TypeCache
( TypeCache
, TypeInfo (..)
, newTypeCache
, lookupTypeInfo
, cacheTypeInfo
, cachedTypeCount
) where
import Control.Concurrent.STM (TVar, atomically, modifyTVar', newTVarIO, readTVarIO)
import Data.ByteString (ByteString)
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
import Data.Word (Word32)
-- | Metadata about a PG type, cached at the pool level.
data TypeInfo = TypeInfo
{ tiName :: !ByteString
-- ^ Type name from @pg_type.typname@.
, tiCategory :: !Char
-- ^ Type category: @\'E\'@ enum, @\'C\'@ composite, @\'R\'@ range,
-- @\'D\'@ domain, @\'B\'@ base, @\'P\'@ pseudo, etc.
, tiArrayOid :: !Word32
-- ^ OID of the array form of this type (0 if none).
, tiBaseOid :: !Word32
-- ^ For domains: the underlying type OID. 0 otherwise.
}
deriving stock (Show, Eq)
-- | A thread-safe cache mapping OIDs to their resolved type info.
newtype TypeCache = TypeCache (TVar (Map Word32 TypeInfo))
-- | Create an empty type cache.
newTypeCache :: IO TypeCache
newTypeCache = TypeCache <$> newTVarIO Map.empty
-- | Look up cached type info for an OID.
lookupTypeInfo :: TypeCache -> Word32 -> IO (Maybe TypeInfo)
lookupTypeInfo (TypeCache tv) oid = do
m <- readTVarIO tv
pure (Map.lookup oid m)
-- | Cache type info for an OID.
cacheTypeInfo :: TypeCache -> Word32 -> TypeInfo -> IO ()
cacheTypeInfo (TypeCache tv) oid info =
atomically $ modifyTVar' tv (Map.insert oid info)
-- | Return the number of cached entries (for diagnostics).
cachedTypeCount :: TypeCache -> IO Int
cachedTypeCount (TypeCache tv) = Map.size <$> readTVarIO tv