graphql-engine/server/src-lib/Hasura/Backends/Postgres/Instances/Transport.hs
David Overton aac64f2c81 Source typename customization (close graphql-engine#6974)
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/1616
GitOrigin-RevId: f7eefd2367929209aa77895ea585e96a99a78d47
2021-10-29 14:43:14 +00:00

169 lines
5.3 KiB
Haskell

{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Hasura.Backends.Postgres.Instances.Transport
( runPGMutationTransaction,
)
where
import Data.Aeson qualified as J
import Data.ByteString qualified as B
import Data.HashMap.Strict.InsOrd qualified as OMap
import Data.Text.Extended
import Database.PG.Query qualified as Q
import Hasura.Backends.Postgres.Execute.LiveQuery qualified as PGL
import Hasura.Backends.Postgres.Instances.Execute qualified as EQ
import Hasura.Backends.Postgres.SQL.Value
import Hasura.Backends.Postgres.Translate.Select (PostgresAnnotatedFieldJSON)
import Hasura.Base.Error
import Hasura.EncJSON
import Hasura.GraphQL.Execute.Backend
import Hasura.GraphQL.Execute.LiveQuery.Plan
import Hasura.GraphQL.Logging
import Hasura.GraphQL.Namespace
( RootFieldAlias,
RootFieldMap,
mkUnNamespacedRootFieldAlias,
)
import Hasura.GraphQL.Transport.Backend
import Hasura.GraphQL.Transport.HTTP.Protocol
import Hasura.Logging qualified as L
import Hasura.Prelude
import Hasura.RQL.Types
import Hasura.Server.Types (RequestId)
import Hasura.Session
import Hasura.Tracing
import Hasura.Tracing qualified as Tracing
import Language.GraphQL.Draft.Syntax qualified as G
instance
( Backend ('Postgres pgKind),
PostgresAnnotatedFieldJSON pgKind
) =>
BackendTransport ('Postgres pgKind)
where
runDBQuery = runPGQuery
runDBMutation = runPGMutation
runDBSubscription = runPGSubscription
runDBQueryExplain = runPGQueryExplain
runPGQuery ::
( MonadIO m,
MonadError QErr m,
MonadQueryLog m,
MonadTrace m
) =>
RequestId ->
GQLReqUnparsed ->
RootFieldAlias ->
UserInfo ->
L.Logger L.Hasura ->
SourceConfig ('Postgres pgKind) ->
Tracing.TraceT (Q.TxET QErr IO) EncJSON ->
Maybe EQ.PreparedSql ->
-- | Also return the time spent in the PG query; for telemetry.
m (DiffTime, EncJSON)
runPGQuery reqId query fieldName _userInfo logger sourceConfig tx genSql = do
-- log the generated SQL and the graphql query
logQueryLog logger $ mkQueryLog query fieldName genSql reqId
withElapsedTime $
trace ("Postgres Query for root field " <>> fieldName) $
Tracing.interpTraceT (runQueryTx $ _pscExecCtx sourceConfig) tx
runPGMutation ::
( MonadIO m,
MonadError QErr m,
MonadQueryLog m,
MonadTrace m
) =>
RequestId ->
GQLReqUnparsed ->
RootFieldAlias ->
UserInfo ->
L.Logger L.Hasura ->
SourceConfig ('Postgres pgKind) ->
Tracing.TraceT (Q.TxET QErr IO) EncJSON ->
Maybe EQ.PreparedSql ->
m (DiffTime, EncJSON)
runPGMutation reqId query fieldName userInfo logger sourceConfig tx _genSql = do
-- log the graphql query
logQueryLog logger $ mkQueryLog query fieldName Nothing reqId
ctx <- Tracing.currentContext
withElapsedTime $
trace ("Postgres Mutation for root field " <>> fieldName) $
Tracing.interpTraceT
( liftEitherM . liftIO . runExceptT
. runTx (_pscExecCtx sourceConfig) Q.ReadWrite
. withTraceContext ctx
. withUserInfo userInfo
)
tx
runPGSubscription ::
MonadIO m =>
SourceConfig ('Postgres pgKind) ->
MultiplexedQuery ('Postgres pgKind) ->
[(CohortId, CohortVariables)] ->
m (DiffTime, Either QErr [(CohortId, B.ByteString)])
runPGSubscription sourceConfig query variables =
withElapsedTime $
runExceptT $
runQueryTx (_pscExecCtx sourceConfig) $
PGL.executeMultiplexedQuery query variables
runPGQueryExplain ::
forall pgKind m.
( MonadIO m,
MonadError QErr m
) =>
DBStepInfo ('Postgres pgKind) ->
m EncJSON
runPGQueryExplain (DBStepInfo _ sourceConfig _ action) =
-- All Postgres transport functions use the same monad stack: the ExecutionMonad defined in the
-- matching instance of BackendExecute. However, Explain doesn't need tracing! Rather than
-- introducing a separate "ExplainMonad", we simply use @runTraceTWithReporter@ to remove the
-- TraceT.
runQueryTx (_pscExecCtx sourceConfig) $ runTraceTWithReporter noReporter "explain" $ action
mkQueryLog ::
GQLReqUnparsed ->
RootFieldAlias ->
Maybe EQ.PreparedSql ->
RequestId ->
QueryLog
mkQueryLog gqlQuery fieldName preparedSql requestId =
QueryLog gqlQuery ((fieldName,) <$> generatedQuery) requestId QueryLogKindDatabase
where
generatedQuery =
preparedSql <&> \(EQ.PreparedSql query args) ->
GeneratedQuery (Q.getQueryText query) (J.toJSON $ pgScalarValueToJson . snd <$> args)
-- ad-hoc transaction optimisation
-- see Note [Backwards-compatible transaction optimisation]
runPGMutationTransaction ::
( MonadIO m,
MonadError QErr m,
MonadQueryLog m,
MonadTrace m
) =>
RequestId ->
GQLReqUnparsed ->
UserInfo ->
L.Logger L.Hasura ->
SourceConfig ('Postgres pgKind) ->
RootFieldMap (DBStepInfo ('Postgres pgKind)) ->
m (DiffTime, RootFieldMap EncJSON)
runPGMutationTransaction reqId query userInfo logger sourceConfig mutations = do
logQueryLog logger $ mkQueryLog query (mkUnNamespacedRootFieldAlias $$(G.litName "transaction")) Nothing reqId
ctx <- Tracing.currentContext
withElapsedTime $ do
Tracing.interpTraceT
( liftEitherM . liftIO . runExceptT
. runTx (_pscExecCtx sourceConfig) Q.ReadWrite
. withTraceContext ctx
. withUserInfo userInfo
)
$ flip OMap.traverseWithKey mutations \fieldName dbsi ->
trace ("Postgres Mutation for root field " <>> fieldName) $ dbsiAction dbsi