module Hasql.Engine.Contexts.Pipeline
( Pipeline,
run,
statement,
)
where
import Data.HashMap.Strict qualified as HashMap
import Data.HashSet qualified as HashSet
import Hasql.Codecs.RequestingOid qualified as RequestingOid
import Hasql.Codecs.Vocab qualified as Vocab
import Hasql.Codecs.Vocab.OidCache qualified as OidCache
import Hasql.Codecs.Vocab.QualifiedTypeName qualified as Vocab.QualifiedTypeName
import Hasql.Comms.Roundtrip qualified as Comms.Roundtrip
import Hasql.Engine.Errors qualified as Errors
import Hasql.Engine.PqProcedures.SelectTypeInfo qualified as PqProcedures.SelectTypeInfo
import Hasql.Engine.Statement qualified as Statement
import Hasql.Engine.Structures.StatementCache qualified as StatementCache
import Hasql.Platform.Prelude
import Hasql.Pq qualified as Pq
run ::
Pipeline a ->
Bool ->
Pq.Connection ->
OidCache.OidCache ->
StatementCache.StatementCache ->
IO
( Either Errors.SessionError a,
OidCache.OidCache,
StatementCache.StatementCache
)
run :: forall a.
Pipeline a
-> Bool
-> Connection
-> OidCache
-> StatementCache
-> IO (Either SessionError a, OidCache, StatementCache)
run (Pipeline Int
totalStatements HashSet QualifiedTypeName
unknownTypes Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache)
runPipeline) Bool
usePreparedStatements Connection
connection OidCache
oidCache StatementCache
statementCache = do
let missingTypes :: HashSet QualifiedTypeName
missingTypes = HashSet QualifiedTypeName -> OidCache -> HashSet QualifiedTypeName
OidCache.selectUnknownNames HashSet QualifiedTypeName
unknownTypes OidCache
oidCache
Either SessionError OidCache
resolvedOidCache <-
if HashSet QualifiedTypeName -> Bool
forall a. HashSet a -> Bool
HashSet.null HashSet QualifiedTypeName
missingTypes
then Either SessionError OidCache -> IO (Either SessionError OidCache)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (OidCache -> Either SessionError OidCache
forall a b. b -> Either a b
Right OidCache
oidCache)
else do
Either SessionError SelectTypeInfoResult
oidCacheUpdates <-
Connection
-> SelectTypeInfo -> IO (Either SessionError SelectTypeInfoResult)
PqProcedures.SelectTypeInfo.run Connection
connection (HashSet QualifiedTypeName -> SelectTypeInfo
PqProcedures.SelectTypeInfo.SelectTypeInfo HashSet QualifiedTypeName
missingTypes)
pure $ case Either SessionError SelectTypeInfoResult
oidCacheUpdates of
Left SessionError
err -> SessionError -> Either SessionError OidCache
forall a b. a -> Either a b
Left SessionError
err
Right SelectTypeInfoResult
oidCacheUpdates ->
let foundTypes :: HashSet QualifiedTypeName
foundTypes = SelectTypeInfoResult -> HashSet QualifiedTypeName
forall k a. HashMap k a -> HashSet k
HashMap.keysSet SelectTypeInfoResult
oidCacheUpdates
notFoundTypes :: HashSet QualifiedTypeName
notFoundTypes = HashSet QualifiedTypeName
-> HashSet QualifiedTypeName -> HashSet QualifiedTypeName
forall a. Hashable a => HashSet a -> HashSet a -> HashSet a
HashSet.difference HashSet QualifiedTypeName
missingTypes HashSet QualifiedTypeName
foundTypes
in if Bool -> Bool
not (HashSet QualifiedTypeName -> Bool
forall a. HashSet a -> Bool
HashSet.null HashSet QualifiedTypeName
notFoundTypes)
then SessionError -> Either SessionError OidCache
forall a b. a -> Either a b
Left (HashSet (Maybe Text, Text) -> SessionError
Errors.MissingTypesSessionError ((QualifiedTypeName -> (Maybe Text, Text))
-> HashSet QualifiedTypeName -> HashSet (Maybe Text, Text)
forall b a. Hashable b => (a -> b) -> HashSet a -> HashSet b
HashSet.map QualifiedTypeName -> (Maybe Text, Text)
Vocab.QualifiedTypeName.toNameTuple HashSet QualifiedTypeName
notFoundTypes))
else OidCache -> Either SessionError OidCache
forall a b. b -> Either a b
Right (OidCache
oidCache OidCache -> OidCache -> OidCache
forall a. Semigroup a => a -> a -> a
<> SelectTypeInfoResult -> OidCache
OidCache.fromHashMap SelectTypeInfoResult
oidCacheUpdates)
case Either SessionError OidCache
resolvedOidCache of
Left SessionError
err -> (Either SessionError a, OidCache, StatementCache)
-> IO (Either SessionError a, OidCache, StatementCache)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SessionError -> Either SessionError a
forall a b. a -> Either a b
Left SessionError
err, OidCache
oidCache, StatementCache
statementCache)
Right OidCache
newOidCache -> do
let (Roundtrip Context a
roundtrip, StatementCache
newStatementCache) =
Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache)
runPipeline Int
0 Bool
usePreparedStatements OidCache
newOidCache StatementCache
statementCache
contextualRoundtrip :: Roundtrip (Maybe Context) a
contextualRoundtrip = (Context -> Maybe Context)
-> Roundtrip Context a -> Roundtrip (Maybe Context) a
forall a b c. (a -> b) -> Roundtrip a c -> Roundtrip b c
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first Context -> Maybe Context
forall a. a -> Maybe a
Just Roundtrip Context a
roundtrip
Either (Error (Maybe Context)) a
executionResult <- Roundtrip (Maybe Context) a
-> Maybe Context
-> Connection
-> IO (Either (Error (Maybe Context)) a)
forall context a.
Roundtrip context a
-> context -> Connection -> IO (Either (Error context) a)
Comms.Roundtrip.toPipelineIO Roundtrip (Maybe Context) a
contextualRoundtrip Maybe Context
forall a. Maybe a
Nothing Connection
connection
let result :: Either SessionError a
result =
(Error (Maybe Context) -> SessionError)
-> Either (Error (Maybe Context)) a -> Either SessionError a
forall a b c. (a -> b) -> Either a c -> Either b c
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first
( \case
Comms.Roundtrip.ClientError Maybe Context
_context Maybe ByteString
details ->
Text -> SessionError
Errors.ConnectionSessionError (Text -> (ByteString -> Text) -> Maybe ByteString -> Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Text
"" ByteString -> Text
decodeUtf8Lenient Maybe ByteString
details)
Comms.Roundtrip.ServerError Error (Maybe Context)
recvError ->
Error (Maybe (Int, Int, ByteString, [Text], Bool)) -> SessionError
Errors.fromRecvError ((Maybe Context -> Maybe (Int, Int, ByteString, [Text], Bool))
-> Error (Maybe Context)
-> Error (Maybe (Int, Int, ByteString, [Text], Bool))
forall a b. (a -> b) -> Error a -> Error b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Context -> (Int, Int, ByteString, [Text], Bool))
-> Maybe Context -> Maybe (Int, Int, ByteString, [Text], Bool)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(Context Int
index ByteString
sql [Text]
params Bool
prepared StatementCache
_) -> (Int
totalStatements, Int
index, ByteString
sql, [Text]
params, Bool
prepared))) Error (Maybe Context)
recvError)
)
Either (Error (Maybe Context)) a
executionResult
finalStatementCache :: StatementCache
finalStatementCache =
case Either (Error (Maybe Context)) a
executionResult of
Right a
_ -> StatementCache
newStatementCache
Left Error (Maybe Context)
executionError ->
StatementCache
-> (Context -> StatementCache) -> Maybe Context -> StatementCache
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
StatementCache
statementCache
(\(Context Int
_ ByteString
_ [Text]
_ Bool
_ StatementCache
statementCache) -> StatementCache
statementCache)
(Error (Maybe Context) -> Maybe Context
forall a. Error a -> a
forall (w :: * -> *) a. Comonad w => w a -> a
extract Error (Maybe Context)
executionError)
(Either SessionError a, OidCache, StatementCache)
-> IO (Either SessionError a, OidCache, StatementCache)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either SessionError a
result, OidCache
newOidCache, StatementCache
finalStatementCache)
data Pipeline a
= Pipeline
Int
(HashSet Vocab.QualifiedTypeName)
( Int ->
Bool ->
OidCache.OidCache ->
StatementCache.StatementCache ->
(Comms.Roundtrip.Roundtrip Context a, StatementCache.StatementCache)
)
data Context
= Context
Int
ByteString
[Text]
Bool
StatementCache.StatementCache
deriving stock (Int -> Context -> ShowS
[Context] -> ShowS
Context -> String
(Int -> Context -> ShowS)
-> (Context -> String) -> ([Context] -> ShowS) -> Show Context
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Context -> ShowS
showsPrec :: Int -> Context -> ShowS
$cshow :: Context -> String
show :: Context -> String
$cshowList :: [Context] -> ShowS
showList :: [Context] -> ShowS
Show, Context -> Context -> Bool
(Context -> Context -> Bool)
-> (Context -> Context -> Bool) -> Eq Context
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Context -> Context -> Bool
== :: Context -> Context -> Bool
$c/= :: Context -> Context -> Bool
/= :: Context -> Context -> Bool
Eq)
instance Functor Pipeline where
fmap :: forall a b. (a -> b) -> Pipeline a -> Pipeline b
fmap a -> b
f (Pipeline Int
count HashSet QualifiedTypeName
unknownTypes Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache)
run) = Int
-> HashSet QualifiedTypeName
-> (Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context b, StatementCache))
-> Pipeline b
forall a.
Int
-> HashSet QualifiedTypeName
-> (Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache))
-> Pipeline a
Pipeline Int
count HashSet QualifiedTypeName
unknownTypes \Int
offset Bool
usePreparedStatements OidCache
oidCache StatementCache
cache ->
let (Roundtrip Context a
roundtrip, StatementCache
newStatementCache) = Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache)
run Int
offset Bool
usePreparedStatements OidCache
oidCache StatementCache
cache
in ((a -> b) -> Roundtrip Context a -> Roundtrip Context b
forall a b. (a -> b) -> Roundtrip Context a -> Roundtrip Context b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f Roundtrip Context a
roundtrip, StatementCache
newStatementCache)
instance Applicative Pipeline where
pure :: forall a. a -> Pipeline a
pure a
a =
Int
-> HashSet QualifiedTypeName
-> (Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache))
-> Pipeline a
forall a.
Int
-> HashSet QualifiedTypeName
-> (Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache))
-> Pipeline a
Pipeline Int
0 HashSet QualifiedTypeName
forall a. Monoid a => a
mempty (\Int
_ Bool
_ OidCache
_ StatementCache
cache -> (a -> Roundtrip Context a
forall a. a -> Roundtrip Context a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
a, StatementCache
cache))
Pipeline Int
lCount HashSet QualifiedTypeName
leftUnknownTypes Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context (a -> b), StatementCache)
lRun <*> :: forall a b. Pipeline (a -> b) -> Pipeline a -> Pipeline b
<*> Pipeline Int
rCount HashSet QualifiedTypeName
rightUnknownTypes Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache)
rRun =
let unknownTypes :: HashSet QualifiedTypeName
unknownTypes = HashSet QualifiedTypeName
leftUnknownTypes HashSet QualifiedTypeName
-> HashSet QualifiedTypeName -> HashSet QualifiedTypeName
forall a. Semigroup a => a -> a -> a
<> HashSet QualifiedTypeName
rightUnknownTypes
in Int
-> HashSet QualifiedTypeName
-> (Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context b, StatementCache))
-> Pipeline b
forall a.
Int
-> HashSet QualifiedTypeName
-> (Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache))
-> Pipeline a
Pipeline (Int
lCount Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
rCount) HashSet QualifiedTypeName
unknownTypes \Int
offset Bool
usePreparedStatements OidCache
oidCache StatementCache
statementCache ->
let (Roundtrip Context (a -> b)
lRoundtrip, StatementCache
statementCache1) = Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context (a -> b), StatementCache)
lRun Int
offset Bool
usePreparedStatements OidCache
oidCache StatementCache
statementCache
offset1 :: Int
offset1 = Int
offset Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
lCount
(Roundtrip Context a
rRoundtrip, StatementCache
statementCache2) = Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache)
rRun Int
offset1 Bool
usePreparedStatements OidCache
oidCache StatementCache
statementCache1
in (Roundtrip Context (a -> b)
lRoundtrip Roundtrip Context (a -> b)
-> Roundtrip Context a -> Roundtrip Context b
forall a b.
Roundtrip Context (a -> b)
-> Roundtrip Context a -> Roundtrip Context b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Roundtrip Context a
rRoundtrip, StatementCache
statementCache2)
statement ::
Statement.Statement params result ->
params ->
Pipeline result
statement :: forall params result.
Statement params result -> params -> Pipeline result
statement Statement params result
stmt params
params =
Int
-> HashSet QualifiedTypeName
-> (Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context result, StatementCache))
-> Pipeline result
forall a.
Int
-> HashSet QualifiedTypeName
-> (Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context a, StatementCache))
-> Pipeline a
Pipeline Int
1 (Statement params result -> HashSet QualifiedTypeName
forall params result.
Statement params result -> HashSet QualifiedTypeName
Statement.unknownTypes Statement params result
stmt) Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context result, StatementCache)
run
where
sql :: ByteString
sql = Statement params result -> ByteString
forall params result. Statement params result -> ByteString
Statement.sql Statement params result
stmt
run :: Int
-> Bool
-> OidCache
-> StatementCache
-> (Roundtrip Context result, StatementCache)
run Int
offset Bool
usePreparedStatements OidCache
oidCache =
if Bool
prepare
then StatementCache -> (Roundtrip Context result, StatementCache)
runPrepared
else StatementCache -> (Roundtrip Context result, StatementCache)
runUnprepared
where
([Word32]
oidList, [Maybe (ByteString, Bool)]
valueAndFormatList) =
Statement params result
-> OidCache -> params -> ([Word32], [Maybe (ByteString, Bool)])
forall params result.
Statement params result
-> OidCache -> params -> ([Word32], [Maybe (ByteString, Bool)])
Statement.compilePreparedStatementData Statement params result
stmt OidCache
oidCache params
params
pqOidList :: [Oid]
pqOidList =
(Word32 -> Oid) -> [Word32] -> [Oid]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (CUInt -> Oid
Pq.Oid (CUInt -> Oid) -> (Word32 -> CUInt) -> Word32 -> Oid
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Word32 -> CUInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral) [Word32]
oidList
prepare :: Bool
prepare =
Bool
usePreparedStatements Bool -> Bool -> Bool
&& Statement params result -> Bool
forall params result. Statement params result -> Bool
Statement.isPrepared Statement params result
stmt
context :: StatementCache -> Context
context StatementCache
soFarStatementCache =
Int -> ByteString -> [Text] -> Bool -> StatementCache -> Context
Context
Int
offset
ByteString
sql
(Statement params result -> params -> [Text]
forall params result. Statement params result -> params -> [Text]
Statement.printer Statement params result
stmt params
params)
Bool
prepare
StatementCache
soFarStatementCache
runPrepared :: StatementCache -> (Roundtrip Context result, StatementCache)
runPrepared StatementCache
statementCache =
(Roundtrip Context result
roundtrip, StatementCache
newStatementCache)
where
(Bool
isNew, ByteString
remoteKey, StatementCache
newStatementCache) =
case ByteString -> [Oid] -> StatementCache -> Maybe ByteString
StatementCache.lookup ByteString
sql [Oid]
pqOidList StatementCache
statementCache of
Just ByteString
remoteKey -> (Bool
False, ByteString
remoteKey, StatementCache
statementCache)
Maybe ByteString
Nothing ->
let (ByteString
remoteKey, StatementCache
newStatementCache) = ByteString
-> [Oid] -> StatementCache -> (ByteString, StatementCache)
StatementCache.insert ByteString
sql [Oid]
pqOidList StatementCache
statementCache
in (Bool
True, ByteString
remoteKey, StatementCache
newStatementCache)
roundtrip :: Roundtrip Context result
roundtrip =
Bool -> Roundtrip Context () -> Roundtrip Context ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when
Bool
isNew
(Context
-> ByteString -> ByteString -> [Oid] -> Roundtrip Context ()
forall context.
context
-> ByteString -> ByteString -> [Oid] -> Roundtrip context ()
Comms.Roundtrip.prepare (StatementCache -> Context
context StatementCache
statementCache) ByteString
remoteKey ByteString
sql [Oid]
pqOidList)
Roundtrip Context ()
-> Roundtrip Context result -> Roundtrip Context result
forall a b.
Roundtrip Context a -> Roundtrip Context b -> Roundtrip Context b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Context
-> ByteString
-> [Maybe (ByteString, Format)]
-> Format
-> ResultDecoder result
-> Roundtrip Context result
forall context a.
context
-> ByteString
-> [Maybe (ByteString, Format)]
-> Format
-> ResultDecoder a
-> Roundtrip context a
Comms.Roundtrip.queryPrepared (StatementCache -> Context
context StatementCache
newStatementCache) ByteString
remoteKey [Maybe (ByteString, Format)]
encodedParams Format
Pq.Binary ResultDecoder result
decoder'
where
encodedParams :: [Maybe (ByteString, Format)]
encodedParams =
[Maybe (ByteString, Bool)]
valueAndFormatList
[Maybe (ByteString, Bool)]
-> ([Maybe (ByteString, Bool)] -> [Maybe (ByteString, Format)])
-> [Maybe (ByteString, Format)]
forall a b. a -> (a -> b) -> b
& (Maybe (ByteString, Bool) -> Maybe (ByteString, Format))
-> [Maybe (ByteString, Bool)] -> [Maybe (ByteString, Format)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (((ByteString, Bool) -> (ByteString, Format))
-> Maybe (ByteString, Bool) -> Maybe (ByteString, Format)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(ByteString
bytes, Bool
format) -> (ByteString
bytes, Format -> Format -> Bool -> Format
forall a. a -> a -> Bool -> a
bool Format
Pq.Binary Format
Pq.Text Bool
format)))
runUnprepared :: StatementCache -> (Roundtrip Context result, StatementCache)
runUnprepared StatementCache
statementCache =
(Roundtrip Context result
roundtrip, StatementCache
statementCache)
where
roundtrip :: Roundtrip Context result
roundtrip =
Context
-> ByteString
-> [Maybe (Oid, ByteString, Format)]
-> Format
-> ResultDecoder result
-> Roundtrip Context result
forall context a.
context
-> ByteString
-> [Maybe (Oid, ByteString, Format)]
-> Format
-> ResultDecoder a
-> Roundtrip context a
Comms.Roundtrip.queryParams (StatementCache -> Context
context StatementCache
statementCache) ByteString
sql [Maybe (Oid, ByteString, Format)]
encodedParams Format
Pq.Binary ResultDecoder result
decoder'
where
encodedParams :: [Maybe (Oid, ByteString, Format)]
encodedParams =
Statement params result
-> OidCache -> params -> [Maybe (Word32, ByteString, Bool)]
forall params result.
Statement params result
-> OidCache -> params -> [Maybe (Word32, ByteString, Bool)]
Statement.compileUnpreparedStatementData Statement params result
stmt OidCache
oidCache params
params
[Maybe (Word32, ByteString, Bool)]
-> ([Maybe (Word32, ByteString, Bool)]
-> [Maybe (Oid, ByteString, Format)])
-> [Maybe (Oid, ByteString, Format)]
forall a b. a -> (a -> b) -> b
& (Maybe (Word32, ByteString, Bool)
-> Maybe (Oid, ByteString, Format))
-> [Maybe (Word32, ByteString, Bool)]
-> [Maybe (Oid, ByteString, Format)]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (((Word32, ByteString, Bool) -> (Oid, ByteString, Format))
-> Maybe (Word32, ByteString, Bool)
-> Maybe (Oid, ByteString, Format)
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(Word32
oid, ByteString
bytes, Bool
format) -> (CUInt -> Oid
Pq.Oid (Word32 -> CUInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
oid), ByteString
bytes, Format -> Format -> Bool -> Format
forall a. a -> a -> Bool -> a
bool Format
Pq.Binary Format
Pq.Text Bool
format)))
decoder' :: ResultDecoder result
decoder' =
RequestingOid (ResultDecoder result)
-> OidCache -> ResultDecoder result
forall a. RequestingOid a -> OidCache -> a
RequestingOid.toBase (Statement params result -> RequestingOid (ResultDecoder result)
forall params result.
Statement params result -> RequestingOid (ResultDecoder result)
Statement.decoder Statement params result
stmt) OidCache
oidCache