2021-02-12 06:04:09 +03:00
|
|
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
|
|
|
2021-02-23 20:37:27 +03:00
|
|
|
module Hasura.Backends.Postgres.Instances.Execute () where
|
2021-02-12 06:04:09 +03:00
|
|
|
|
|
|
|
import Hasura.Prelude
|
|
|
|
|
2021-02-20 16:45:49 +03:00
|
|
|
import qualified Data.Environment as Env
|
|
|
|
import qualified Data.HashSet as Set
|
|
|
|
import qualified Data.Sequence as Seq
|
|
|
|
import qualified Language.GraphQL.Draft.Syntax as G
|
|
|
|
import qualified Network.HTTP.Client as HTTP
|
|
|
|
import qualified Network.HTTP.Types as HTTP
|
|
|
|
|
|
|
|
import qualified Hasura.Backends.Postgres.Execute.LiveQuery as PGL
|
|
|
|
import qualified Hasura.Backends.Postgres.Execute.Mutation as PGE
|
|
|
|
import qualified Hasura.RQL.IR.Delete as IR
|
|
|
|
import qualified Hasura.RQL.IR.Insert as IR
|
|
|
|
import qualified Hasura.RQL.IR.Returning as IR
|
|
|
|
import qualified Hasura.RQL.IR.Select as IR
|
|
|
|
import qualified Hasura.RQL.IR.Update as IR
|
|
|
|
import qualified Hasura.Tracing as Tracing
|
2021-02-12 06:04:09 +03:00
|
|
|
|
|
|
|
import Hasura.Backends.Postgres.Connection
|
|
|
|
import Hasura.EncJSON
|
|
|
|
import Hasura.GraphQL.Context
|
|
|
|
import Hasura.GraphQL.Execute.Backend
|
|
|
|
import Hasura.GraphQL.Execute.Common
|
|
|
|
import Hasura.GraphQL.Execute.Insert
|
2021-02-20 16:45:49 +03:00
|
|
|
import Hasura.GraphQL.Execute.LiveQuery.Plan
|
2021-02-12 06:04:09 +03:00
|
|
|
import Hasura.GraphQL.Execute.Prepare
|
|
|
|
import Hasura.GraphQL.Parser
|
|
|
|
import Hasura.RQL.Types
|
2021-02-20 16:45:49 +03:00
|
|
|
import Hasura.Server.Version (HasVersion)
|
2021-02-12 06:04:09 +03:00
|
|
|
import Hasura.Session
|
|
|
|
|
|
|
|
|
2021-02-20 16:45:49 +03:00
|
|
|
instance BackendExecute 'Postgres where
|
|
|
|
type PreparedQuery 'Postgres = PreparedSql
|
|
|
|
type MultiplexedQuery 'Postgres = PGL.MultiplexedQuery
|
|
|
|
type ExecutionMonad 'Postgres = Tracing.TraceT (LazyTxT QErr IO)
|
2021-02-12 06:04:09 +03:00
|
|
|
getRemoteJoins = concatMap (toList . snd) . maybe [] toList . _psRemoteJoins
|
|
|
|
|
|
|
|
mkDBQueryPlan = pgDBQueryPlan
|
|
|
|
mkDBMutationPlan = pgDBMutationPlan
|
2021-02-20 16:45:49 +03:00
|
|
|
mkDBSubscriptionPlan = pgDBSubscriptionPlan
|
2021-02-12 06:04:09 +03:00
|
|
|
|
|
|
|
|
|
|
|
-- query
|
|
|
|
|
|
|
|
pgDBQueryPlan
|
2021-02-20 16:45:49 +03:00
|
|
|
:: forall m .
|
2021-02-12 06:04:09 +03:00
|
|
|
( MonadError QErr m
|
|
|
|
, HasVersion
|
|
|
|
)
|
|
|
|
=> Env.Environment
|
|
|
|
-> HTTP.Manager
|
|
|
|
-> [HTTP.Header]
|
|
|
|
-> UserInfo
|
|
|
|
-> [G.Directive G.Name]
|
|
|
|
-> SourceConfig 'Postgres
|
|
|
|
-> QueryDB 'Postgres (UnpreparedValue 'Postgres)
|
2021-02-20 16:45:49 +03:00
|
|
|
-> m ExecutionStep
|
2021-02-12 06:04:09 +03:00
|
|
|
pgDBQueryPlan env manager reqHeaders userInfo _directives sourceConfig qrf = do
|
|
|
|
(preparedQuery, PlanningSt _ _ planVals expectedVariables) <- flip runStateT initPlanningSt $ traverseQueryDB prepareWithPlan qrf
|
|
|
|
validateSessionVariables expectedVariables $ _uiSession userInfo
|
|
|
|
let (action, preparedSQL) = mkCurPlanTx env manager reqHeaders userInfo $ irToRootFieldPlan planVals preparedQuery
|
|
|
|
pure $ ExecStepDB sourceConfig preparedSQL [] action
|
|
|
|
|
|
|
|
|
|
|
|
-- mutation
|
|
|
|
|
|
|
|
convertDelete
|
2021-02-20 16:45:49 +03:00
|
|
|
:: forall m .
|
2021-02-12 06:04:09 +03:00
|
|
|
( MonadError QErr m
|
|
|
|
, HasVersion
|
|
|
|
)
|
|
|
|
=> Env.Environment
|
|
|
|
-> SessionVariables
|
|
|
|
-> PGE.MutationRemoteJoinCtx
|
|
|
|
-> IR.AnnDelG 'Postgres (UnpreparedValue 'Postgres)
|
|
|
|
-> Bool
|
2021-02-20 16:45:49 +03:00
|
|
|
-> m (Tracing.TraceT (LazyTxT QErr IO) EncJSON)
|
2021-02-12 06:04:09 +03:00
|
|
|
convertDelete env usrVars remoteJoinCtx deleteOperation stringifyNum = do
|
|
|
|
let (preparedDelete, expectedVariables) = flip runState Set.empty $ IR.traverseAnnDel prepareWithoutPlan deleteOperation
|
|
|
|
validateSessionVariables expectedVariables usrVars
|
|
|
|
pure $ PGE.execDeleteQuery env stringifyNum (Just remoteJoinCtx) (preparedDelete, Seq.empty)
|
|
|
|
|
|
|
|
convertUpdate
|
2021-02-20 16:45:49 +03:00
|
|
|
:: forall m.
|
2021-02-12 06:04:09 +03:00
|
|
|
( MonadError QErr m
|
|
|
|
, HasVersion
|
|
|
|
)
|
|
|
|
=> Env.Environment
|
|
|
|
-> SessionVariables
|
|
|
|
-> PGE.MutationRemoteJoinCtx
|
|
|
|
-> IR.AnnUpdG 'Postgres (UnpreparedValue 'Postgres)
|
|
|
|
-> Bool
|
2021-02-20 16:45:49 +03:00
|
|
|
-> m (Tracing.TraceT (LazyTxT QErr IO) EncJSON)
|
2021-02-12 06:04:09 +03:00
|
|
|
convertUpdate env usrVars remoteJoinCtx updateOperation stringifyNum = do
|
|
|
|
let (preparedUpdate, expectedVariables) = flip runState Set.empty $ IR.traverseAnnUpd prepareWithoutPlan updateOperation
|
|
|
|
if null $ IR.uqp1OpExps updateOperation
|
|
|
|
then pure $ pure $ IR.buildEmptyMutResp $ IR.uqp1Output preparedUpdate
|
|
|
|
else do
|
|
|
|
validateSessionVariables expectedVariables usrVars
|
|
|
|
pure $ PGE.execUpdateQuery env stringifyNum (Just remoteJoinCtx) (preparedUpdate, Seq.empty)
|
|
|
|
|
|
|
|
convertInsert
|
2021-02-20 16:45:49 +03:00
|
|
|
:: forall m.
|
2021-02-12 06:04:09 +03:00
|
|
|
( MonadError QErr m
|
|
|
|
, HasVersion
|
|
|
|
)
|
|
|
|
=> Env.Environment
|
|
|
|
-> SessionVariables
|
|
|
|
-> PGE.MutationRemoteJoinCtx
|
|
|
|
-> IR.AnnInsert 'Postgres (UnpreparedValue 'Postgres)
|
|
|
|
-> Bool
|
2021-02-20 16:45:49 +03:00
|
|
|
-> m (Tracing.TraceT (LazyTxT QErr IO) EncJSON)
|
2021-02-12 06:04:09 +03:00
|
|
|
convertInsert env usrVars remoteJoinCtx insertOperation stringifyNum = do
|
|
|
|
let (preparedInsert, expectedVariables) = flip runState Set.empty $ traverseAnnInsert prepareWithoutPlan insertOperation
|
|
|
|
validateSessionVariables expectedVariables usrVars
|
|
|
|
pure $ convertToSQLTransaction env preparedInsert remoteJoinCtx Seq.empty stringifyNum
|
|
|
|
|
|
|
|
-- | A pared-down version of 'Query.convertQuerySelSet', for use in execution of
|
|
|
|
-- special case of SQL function mutations (see 'MDBFunction').
|
|
|
|
convertFunction
|
2021-02-20 16:45:49 +03:00
|
|
|
:: forall m.
|
2021-02-12 06:04:09 +03:00
|
|
|
( MonadError QErr m
|
|
|
|
, HasVersion
|
|
|
|
)
|
|
|
|
=> Env.Environment
|
|
|
|
-> UserInfo
|
|
|
|
-> HTTP.Manager
|
|
|
|
-> HTTP.RequestHeaders
|
|
|
|
-> JsonAggSelect
|
|
|
|
-> IR.AnnSimpleSelG 'Postgres (UnpreparedValue 'Postgres)
|
|
|
|
-- ^ VOLATILE function as 'SelectExp'
|
2021-02-20 16:45:49 +03:00
|
|
|
-> m (Tracing.TraceT (LazyTxT QErr IO) EncJSON)
|
2021-02-12 06:04:09 +03:00
|
|
|
convertFunction env userInfo manager reqHeaders jsonAggSelect unpreparedQuery = do
|
|
|
|
-- Transform the RQL AST into a prepared SQL query
|
|
|
|
(preparedQuery, PlanningSt _ _ planVals expectedVariables)
|
|
|
|
<- flip runStateT initPlanningSt
|
|
|
|
$ IR.traverseAnnSimpleSelect prepareWithPlan unpreparedQuery
|
|
|
|
validateSessionVariables expectedVariables $ _uiSession userInfo
|
|
|
|
let queryResultFn =
|
|
|
|
case jsonAggSelect of
|
|
|
|
JASMultipleRows -> QDBMultipleRows
|
|
|
|
JASSingleObject -> QDBSingleRow
|
|
|
|
pure $!
|
|
|
|
fst $ -- forget (Maybe PreparedSql)
|
|
|
|
mkCurPlanTx env manager reqHeaders userInfo $
|
|
|
|
irToRootFieldPlan planVals $ queryResultFn preparedQuery
|
|
|
|
|
|
|
|
pgDBMutationPlan
|
2021-02-20 16:45:49 +03:00
|
|
|
:: forall m.
|
2021-02-12 06:04:09 +03:00
|
|
|
( MonadError QErr m
|
|
|
|
, HasVersion
|
|
|
|
)
|
|
|
|
=> Env.Environment
|
|
|
|
-> HTTP.Manager
|
|
|
|
-> [HTTP.Header]
|
|
|
|
-> UserInfo
|
|
|
|
-> Bool
|
|
|
|
-> SourceConfig 'Postgres
|
|
|
|
-> MutationDB 'Postgres (UnpreparedValue 'Postgres)
|
2021-02-20 16:45:49 +03:00
|
|
|
-> m ExecutionStep
|
2021-02-12 06:04:09 +03:00
|
|
|
pgDBMutationPlan env manager reqHeaders userInfo stringifyNum sourceConfig mrf =
|
|
|
|
ExecStepDB sourceConfig Nothing [] <$> case mrf of
|
|
|
|
MDBInsert s -> convertInsert env userSession remoteJoinCtx s stringifyNum
|
|
|
|
MDBUpdate s -> convertUpdate env userSession remoteJoinCtx s stringifyNum
|
|
|
|
MDBDelete s -> convertDelete env userSession remoteJoinCtx s stringifyNum
|
|
|
|
MDBFunction returnsSet s -> convertFunction env userInfo manager reqHeaders returnsSet s
|
|
|
|
where
|
|
|
|
userSession = _uiSession userInfo
|
|
|
|
remoteJoinCtx = (manager, reqHeaders, userInfo)
|
2021-02-20 16:45:49 +03:00
|
|
|
|
|
|
|
|
2021-02-23 20:37:27 +03:00
|
|
|
-- subscription
|
2021-02-20 16:45:49 +03:00
|
|
|
|
|
|
|
pgDBSubscriptionPlan
|
2021-02-23 20:37:27 +03:00
|
|
|
:: forall m.
|
|
|
|
( MonadError QErr m
|
|
|
|
, MonadIO m
|
|
|
|
)
|
2021-02-20 16:45:49 +03:00
|
|
|
=> UserInfo
|
|
|
|
-> SourceConfig 'Postgres
|
|
|
|
-> InsOrdHashMap G.Name (QueryDB 'Postgres (UnpreparedValue 'Postgres))
|
|
|
|
-> m (LiveQueryPlan 'Postgres (MultiplexedQuery 'Postgres))
|
|
|
|
pgDBSubscriptionPlan userInfo sourceConfig unpreparedAST = do
|
|
|
|
(preparedAST, PGL.QueryParametersInfo{..}) <- flip runStateT mempty $
|
|
|
|
for unpreparedAST $ traverseQueryDB PGL.resolveMultiplexedValue
|
|
|
|
let multiplexedQuery = PGL.mkMultiplexedQuery preparedAST
|
|
|
|
roleName = _uiRole userInfo
|
|
|
|
parameterizedPlan = ParameterizedLiveQueryPlan roleName multiplexedQuery
|
|
|
|
|
|
|
|
-- We need to ensure that the values provided for variables are correct according to Postgres.
|
|
|
|
-- Without this check an invalid value for a variable for one instance of the subscription will
|
|
|
|
-- take down the entire multiplexed query.
|
|
|
|
validatedQueryVars <- PGL.validateVariables (_pscExecCtx sourceConfig) _qpiReusableVariableValues
|
|
|
|
validatedSyntheticVars <- PGL.validateVariables (_pscExecCtx sourceConfig) $ toList _qpiSyntheticVariableValues
|
|
|
|
|
|
|
|
-- TODO validatedQueryVars validatedSyntheticVars
|
|
|
|
let cohortVariables = mkCohortVariables
|
|
|
|
_qpiReferencedSessionVariables
|
|
|
|
(_uiSession userInfo)
|
|
|
|
validatedQueryVars
|
|
|
|
validatedSyntheticVars
|
|
|
|
|
|
|
|
pure $ LiveQueryPlan parameterizedPlan sourceConfig cohortVariables
|