hunk.code_window:     let annRelBoolExp = cBoolExp nesAnn
           innerBoolExp = S.BEBin S.AndOp backCompExp annRelBoolExp
       S.mkExists relTN innerBoolExp
   inBoolExpBuilder :: S.SQLExp -> [S.SQLExp] -> S.BoolExp
   inBoolExpBuilder qualColExp rhsExps =
     foldr (S.BEBin S.OrOp) (S.BELit False) eqExps
     where
       eqExps = map (equalsBoolExpBuilder qualColExp) rhsExps
   -- txtValParser
   --   :: (MonadError QErr m)
   --   => ValueParser m (AnnValOpExpG S.SQLExp)
   -- txtValParser =
   --   undefined
  
hunk.labels: keep keep keep keep replace replace replace replace replace replace keep keep keep keep keep
hunk.file_path: server/src-lib/Hasura/RQL/GBoolExp.hs
hunk.type: replace
file: {-# LANGUAGE FlexibleContexts  #-}
  {-# LANGUAGE LambdaCase        #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE TemplateHaskell   #-}
  
  module Hasura.GraphQL.Explain
    ( explainGQLQuery
    , GQLExplain
    ) where
  
  import qualified Data.Aeson                             as J
  import qualified Data.Aeson.Casing                      as J
  import qualified Data.Aeson.TH                          as J
  import qualified Data.HashMap.Strict                    as Map
  import qualified Database.PG.Query                      as Q
  import qualified Language.GraphQL.Draft.Syntax          as G
  import qualified Text.Builder                           as TB
  import qualified Data.ByteString.Lazy                   as BL
  
  import           Hasura.GraphQL.Resolve.Context
  import           Hasura.GraphQL.Schema
  import           Hasura.GraphQL.Validate.Field
  import           Hasura.Prelude
  import           Hasura.RQL.DML.Internal
  import           Hasura.RQL.Types
  import           Hasura.SQL.Types
  import           Hasura.SQL.Value
  
  import qualified Hasura.GraphQL.Resolve.Select          as RS
  import qualified Hasura.GraphQL.Transport.HTTP.Protocol as GH
  import qualified Hasura.GraphQL.Validate                as GV
  import qualified Hasura.RQL.DML.Select                  as RS
  import qualified Hasura.Server.Query                    as RQ
  
  data GQLExplain
    = GQLExplain
    { _gqeQuery :: !GH.GraphQLRequest
    , _gqeUser  :: !(Maybe (Map.HashMap Text Text))
    } deriving (Show, Eq)
  
  $(J.deriveJSON (J.aesonDrop 4 J.camelCase){J.omitNothingFields=True}
    ''GQLExplain
   )
  
  data FieldPlan
    = FieldPlan
    { _fpField :: !G.Name
    , _fpSql   :: !(Maybe Text)
    , _fpPlan  :: !(Maybe [Text])
    } deriving (Show, Eq)
  
  $(J.deriveJSON (J.aesonDrop 3 J.camelCase) ''FieldPlan)
  
  type Explain =
    (ReaderT (FieldMap, OrdByCtx) (Except QErr))
  
  runExplain
    :: (MonadError QErr m)
    => (FieldMap, OrdByCtx) -> Explain a -> m a
  runExplain ctx m =
    either throwError return $ runExcept $ runReaderT m ctx
  
  explainField
    :: UserInfo -> GCtx -> Field -> Q.TxE QErr FieldPlan
  explainField userInfo gCtx fld =
    case fName of
      "__type"     -> return $ FieldPlan fName Nothing Nothing
      "__schema"   -> return $ FieldPlan fName Nothing Nothing
      "__typename" -> return $ FieldPlan fName Nothing Nothing
      _            -> do
        opCxt <- getOpCtx fName
        sel <- runExplain (fldMap, orderByCtx) $ case opCxt of
          OCSelect tn permFilter permLimit hdrs -> do
            validateHdrs hdrs
            RS.mkSQLSelect False <$>
              RS.fromField txtConverter tn permFilter permLimit fld
          OCSelectPkey tn permFilter hdrs -> do
            validateHdrs hdrs
            RS.mkSQLSelect True <$>
              RS.fromFieldByPKey txtConverter tn permFilter fld
          OCSelectAgg tn permFilter permLimit hdrs -> do
            validateHdrs hdrs
            RS.mkAggSelect <$>
              RS.fromAggField txtConverter tn permFilter permLimit fld
          _ -> throw500 "unexpected mut field info for explain"
  
        let selectSQL = TB.run $ toSQL sel
            withExplain = "EXPLAIN (FORMAT TEXT) " <> selectSQL
        planLines <- liftTx $ map runIdentity <$>
          Q.listQE dmlTxErrorHandler (Q.fromText withExplain) () True
        return $ FieldPlan fName (Just selectSQL) $ Just planLines
    where
      fName = _fName fld
      txtConverter = return . txtEncoder . snd
      opCtxMap = _gOpCtxMap gCtx
      fldMap = _gFields gCtx
      orderByCtx = _gOrdByCtx gCtx
  
      getOpCtx f =
        onNothing (Map.lookup f opCtxMap) $ throw500 $
        "lookup failed: opctx: " <> showName f
  
      validateHdrs hdrs = do
        let receivedHdrs = userVars userInfo
        forM_ hdrs $ \hdr ->
          unless (isJust $ getVarVal hdr receivedHdrs) $
          throw400 NotFound $ hdr <<> " header is expected but not found"
  
  explainGQLQuery
    :: (MonadError QErr m, MonadIO m)
    => Q.PGPool
    -> Q.TxIsolation
    -> GCtxMap
    -> GQLExplain
    -> m BL.ByteString
  explainGQLQuery pool iso gCtxMap (GQLExplain query userVarsRaw)= do
    (opTy, selSet) <- runReaderT (GV.validateGQ query) gCtx
    unless (opTy == G.OperationTypeQuery) $
      throw400 InvalidParams "only queries can be explained"
    let tx = mapM (explainField userInfo gCtx) (toList selSet)
    plans <- liftIO (runExceptT $ runTx tx) >>= liftEither
    return $ J.encode plans
    where
      usrVars = mkUserVars $ maybe [] Map.toList userVarsRaw
      userInfo = mkUserInfo (fromMaybe adminRole $ roleFromVars usrVars) usrVars
      gCtx = getGCtx (userRole userInfo) gCtxMap
      runTx tx =
        Q.runTx pool (iso, Nothing) $
        RQ.setHeadersTx (userVars userInfo) >> tx
  