{-# LANGUAGE ApplicativeDo, BangPatterns, TypeFamilies, DataKinds, PolyKinds, TypeApplications, ScopedTypeVariables, ConstraintKinds, TypeOperators, GADTs, UndecidableInstances, FlexibleContexts, AllowAmbiguousTypes #-}
module IHP.FetchPipelined
( fetchPipelined
, fetchVectorPipelined
, fetchOneOrNothingPipelined
, fetchCountPipelined
, fetchExistsPipelined
, pipeline
, Pipeline.Pipeline
) where
import IHP.Prelude
import IHP.ModelSupport
import IHP.QueryBuilder
import IHP.Hasql.FromRow (FromRowHasql(..))
import IHP.Fetch.Statement (buildQueryListStatement, buildQueryVectorStatement, buildQueryMaybeStatement, buildCountStatement, buildExistsStatement)
import qualified Hasql.Decoders as Decoders
import qualified Hasql.Encoders as Encoders
import qualified Hasql.Pipeline as Pipeline
import qualified Hasql.Session as HasqlSession
import qualified Hasql.Statement as HasqlStatement
import qualified IHP.Log as Log
import IHP.Hasql.Pool (usePoolWithRetry)
import Data.Functor.Contravariant (contramap)
import Data.Functor.Contravariant.Divisible (conquer)
fetchPipelined :: forall model table.
( Table model
, model ~ GetModelByTableName table
, KnownSymbol table
, FromRowHasql model
) => QueryBuilder table -> Pipeline.Pipeline [model]
fetchPipelined :: forall model (table :: Symbol).
(Table model, model ~ GetModelByTableName table, KnownSymbol table,
FromRowHasql model) =>
QueryBuilder table -> Pipeline [model]
fetchPipelined !QueryBuilder table
queryBuilder = () -> Statement () [model] -> Pipeline [model]
forall params result.
params -> Statement params result -> Pipeline result
Pipeline.statement () (QueryBuilder table -> Statement () [model]
forall model (table :: Symbol).
(Table model, model ~ GetModelByTableName table, KnownSymbol table,
FromRowHasql model) =>
QueryBuilder table -> Statement () [model]
buildQueryListStatement QueryBuilder table
queryBuilder)
{-# INLINE fetchPipelined #-}
fetchVectorPipelined :: forall model table.
( Table model
, model ~ GetModelByTableName table
, KnownSymbol table
, FromRowHasql model
) => QueryBuilder table -> Pipeline.Pipeline (Vector model)
fetchVectorPipelined :: forall model (table :: Symbol).
(Table model, model ~ GetModelByTableName table, KnownSymbol table,
FromRowHasql model) =>
QueryBuilder table -> Pipeline (Vector model)
fetchVectorPipelined !QueryBuilder table
queryBuilder = () -> Statement () (Vector model) -> Pipeline (Vector model)
forall params result.
params -> Statement params result -> Pipeline result
Pipeline.statement () (QueryBuilder table -> Statement () (Vector model)
forall model (table :: Symbol).
(Table model, model ~ GetModelByTableName table, KnownSymbol table,
FromRowHasql model) =>
QueryBuilder table -> Statement () (Vector model)
buildQueryVectorStatement QueryBuilder table
queryBuilder)
{-# INLINE fetchVectorPipelined #-}
fetchOneOrNothingPipelined :: forall model table.
( Table model
, model ~ GetModelByTableName table
, KnownSymbol table
, FromRowHasql model
) => QueryBuilder table -> Pipeline.Pipeline (Maybe model)
fetchOneOrNothingPipelined :: forall model (table :: Symbol).
(Table model, model ~ GetModelByTableName table, KnownSymbol table,
FromRowHasql model) =>
QueryBuilder table -> Pipeline (Maybe model)
fetchOneOrNothingPipelined !QueryBuilder table
queryBuilder = () -> Statement () (Maybe model) -> Pipeline (Maybe model)
forall params result.
params -> Statement params result -> Pipeline result
Pipeline.statement () (QueryBuilder table -> Statement () (Maybe model)
forall model (table :: Symbol).
(Table model, model ~ GetModelByTableName table, KnownSymbol table,
FromRowHasql model) =>
QueryBuilder table -> Statement () (Maybe model)
buildQueryMaybeStatement QueryBuilder table
queryBuilder)
{-# INLINE fetchOneOrNothingPipelined #-}
fetchCountPipelined :: forall table.
( KnownSymbol table
) => QueryBuilder table -> Pipeline.Pipeline Int
fetchCountPipelined :: forall (table :: Symbol).
KnownSymbol table =>
QueryBuilder table -> Pipeline Int
fetchCountPipelined !QueryBuilder table
queryBuilder = Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int64 -> Int) -> Pipeline Int64 -> Pipeline Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> () -> Statement () Int64 -> Pipeline Int64
forall params result.
params -> Statement params result -> Pipeline result
Pipeline.statement () (QueryBuilder table -> Statement () Int64
forall (table :: Symbol).
KnownSymbol table =>
QueryBuilder table -> Statement () Int64
buildCountStatement QueryBuilder table
queryBuilder)
{-# INLINE fetchCountPipelined #-}
fetchExistsPipelined :: forall table.
( KnownSymbol table
) => QueryBuilder table -> Pipeline.Pipeline Bool
fetchExistsPipelined :: forall (table :: Symbol).
KnownSymbol table =>
QueryBuilder table -> Pipeline Bool
fetchExistsPipelined !QueryBuilder table
queryBuilder = () -> Statement () Bool -> Pipeline Bool
forall params result.
params -> Statement params result -> Pipeline result
Pipeline.statement () (QueryBuilder table -> Statement () Bool
forall (table :: Symbol).
KnownSymbol table =>
QueryBuilder table -> Statement () Bool
buildExistsStatement QueryBuilder table
queryBuilder)
{-# INLINE fetchExistsPipelined #-}
pipeline :: (?modelContext :: ModelContext) => Pipeline.Pipeline a -> IO a
pipeline :: forall a. (?modelContext::ModelContext) => Pipeline a -> IO a
pipeline Pipeline a
thePipeline = do
let pool :: Pool
pool = ?modelContext::ModelContext
ModelContext
?modelContext.hasqlPool
let effectivePipeline :: Pipeline a
effectivePipeline = case (?modelContext::ModelContext
ModelContext
?modelContext.transactionRunner, ?modelContext::ModelContext
ModelContext
?modelContext.rowLevelSecurity) of
(Maybe TransactionRunner
Nothing, Just RowLevelSecurityContext { Text
rlsAuthenticatedRole :: Text
rlsAuthenticatedRole :: RowLevelSecurityContext -> Text
rlsAuthenticatedRole, Text
rlsUserId :: Text
rlsUserId :: RowLevelSecurityContext -> Text
rlsUserId }) ->
(\()
_ a
a ()
_ -> a
a)
(() -> a -> () -> a) -> Pipeline () -> Pipeline (a -> () -> a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Text, Text) -> Statement (Text, Text) () -> Pipeline ()
forall params result.
params -> Statement params result -> Pipeline result
Pipeline.statement (Text
rlsAuthenticatedRole, Text
rlsUserId) Statement (Text, Text) ()
setRLSConfigPipelineStatement
Pipeline (a -> () -> a) -> Pipeline a -> Pipeline (() -> a)
forall a b. Pipeline (a -> b) -> Pipeline a -> Pipeline b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Pipeline a
thePipeline
Pipeline (() -> a) -> Pipeline () -> Pipeline a
forall a b. Pipeline (a -> b) -> Pipeline a -> Pipeline b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> () -> Statement () () -> Pipeline ()
forall params result.
params -> Statement params result -> Pipeline result
Pipeline.statement () Statement () ()
resetRLSConfigPipelineStatement
(Maybe TransactionRunner, Maybe RowLevelSecurityContext)
_ -> Pipeline a
thePipeline
let session :: Session a
session = Pipeline a -> Session a
forall result. Pipeline result -> Session result
HasqlSession.pipeline Pipeline a
effectivePipeline
let ?context = ?context::ModelContext
?modelContext::ModelContext
ModelContext
?modelContext
let currentLogLevel :: LogLevel
currentLogLevel = ?modelContext::ModelContext
ModelContext
?modelContext.logger.level
let runQuery :: IO a
runQuery = case ?modelContext::ModelContext
ModelContext
?modelContext.transactionRunner of
Just (TransactionRunner forall a. Session a -> IO a
runner) -> Session a -> IO a
forall a. Session a -> IO a
runner Session a
session
Maybe TransactionRunner
Nothing -> Pool -> Session a -> IO a
forall a. Pool -> Session a -> IO a
usePoolWithRetry Pool
pool Session a
session
LogLevel -> Text -> IO a -> IO a
forall a.
(?context::ModelContext) =>
LogLevel -> Text -> IO a -> IO a
logQueryTiming LogLevel
currentLogLevel Text
"🔍 Pipeline" IO a
runQuery
{-# INLINABLE pipeline #-}
setRLSConfigPipelineStatement :: HasqlStatement.Statement (Text, Text) ()
setRLSConfigPipelineStatement :: Statement (Text, Text) ()
setRLSConfigPipelineStatement = Text
-> Params (Text, Text) -> Result () -> Statement (Text, Text) ()
forall params result.
Text -> Params params -> Result result -> Statement params result
HasqlStatement.preparable
Text
"SELECT set_config('role', $1, false), set_config('rls.ihp_user_id', $2, false)"
(((Text, Text) -> Text) -> Params Text -> Params (Text, Text)
forall a' a. (a' -> a) -> Params a -> Params a'
forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (Text, Text) -> Text
forall a b. (a, b) -> a
fst (NullableOrNot Value Text -> Params Text
forall a. NullableOrNot Value a -> Params a
Encoders.param (Value Text -> NullableOrNot Value Text
forall (encoder :: * -> *) a. encoder a -> NullableOrNot encoder a
Encoders.nonNullable Value Text
Encoders.text))
Params (Text, Text) -> Params (Text, Text) -> Params (Text, Text)
forall a. Semigroup a => a -> a -> a
<> ((Text, Text) -> Text) -> Params Text -> Params (Text, Text)
forall a' a. (a' -> a) -> Params a -> Params a'
forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (Text, Text) -> Text
forall a b. (a, b) -> b
snd (NullableOrNot Value Text -> Params Text
forall a. NullableOrNot Value a -> Params a
Encoders.param (Value Text -> NullableOrNot Value Text
forall (encoder :: * -> *) a. encoder a -> NullableOrNot encoder a
Encoders.nonNullable Value Text
Encoders.text)))
(Row () -> Result ()
forall a. Row a -> Result a
Decoders.singleRow (NullableOrNot Value (Maybe Text) -> Row (Maybe Text)
forall a. NullableOrNot Value a -> Row a
Decoders.column (Value Text -> NullableOrNot Value (Maybe Text)
forall (decoder :: * -> *) a.
decoder a -> NullableOrNot decoder (Maybe a)
Decoders.nullable Value Text
Decoders.text) Row (Maybe Text) -> Row (Maybe Text) -> Row (Maybe Text)
forall a b. Row a -> Row b -> Row b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> NullableOrNot Value (Maybe Text) -> Row (Maybe Text)
forall a. NullableOrNot Value a -> Row a
Decoders.column (Value Text -> NullableOrNot Value (Maybe Text)
forall (decoder :: * -> *) a.
decoder a -> NullableOrNot decoder (Maybe a)
Decoders.nullable Value Text
Decoders.text) Row (Maybe Text) -> Row () -> Row ()
forall a b. Row a -> Row b -> Row b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> () -> Row ()
forall a. a -> Row a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()))
resetRLSConfigPipelineStatement :: HasqlStatement.Statement () ()
resetRLSConfigPipelineStatement :: Statement () ()
resetRLSConfigPipelineStatement = Text -> Params () -> Result () -> Statement () ()
forall params result.
Text -> Params params -> Result result -> Statement params result
HasqlStatement.preparable
Text
"SELECT set_config('role', session_user::text, false), set_config('rls.ihp_user_id', '', false)"
Params ()
forall a. Params a
forall (f :: * -> *) a. Divisible f => f a
conquer
(Row () -> Result ()
forall a. Row a -> Result a
Decoders.singleRow (NullableOrNot Value (Maybe Text) -> Row (Maybe Text)
forall a. NullableOrNot Value a -> Row a
Decoders.column (Value Text -> NullableOrNot Value (Maybe Text)
forall (decoder :: * -> *) a.
decoder a -> NullableOrNot decoder (Maybe a)
Decoders.nullable Value Text
Decoders.text) Row (Maybe Text) -> Row (Maybe Text) -> Row (Maybe Text)
forall a b. Row a -> Row b -> Row b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> NullableOrNot Value (Maybe Text) -> Row (Maybe Text)
forall a. NullableOrNot Value a -> Row a
Decoders.column (Value Text -> NullableOrNot Value (Maybe Text)
forall (decoder :: * -> *) a.
decoder a -> NullableOrNot decoder (Maybe a)
Decoders.nullable Value Text
Decoders.text) Row (Maybe Text) -> Row () -> Row ()
forall a b. Row a -> Row b -> Row b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> () -> Row ()
forall a. a -> Row a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()))