2021-02-23 20:37:27 +03:00
|
|
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
|
|
|
2021-04-20 19:57:14 +03:00
|
|
|
module Hasura.Backends.MSSQL.Instances.Execute (MultiplexedQuery'(..), multiplexRootReselect) where
|
2021-02-23 20:37:27 +03:00
|
|
|
|
|
|
|
import Hasura.Prelude
|
|
|
|
|
2021-04-20 19:57:14 +03:00
|
|
|
import qualified Data.Aeson.Extended as J
|
2021-02-23 20:37:27 +03:00
|
|
|
import qualified Data.Environment as Env
|
2021-04-20 19:57:14 +03:00
|
|
|
import qualified Data.List.NonEmpty as NE
|
|
|
|
import qualified Data.Text.Extended as T
|
2021-02-23 20:37:27 +03:00
|
|
|
import qualified Database.ODBC.SQLServer as ODBC
|
|
|
|
import qualified Language.GraphQL.Draft.Syntax as G
|
|
|
|
import qualified Network.HTTP.Client as HTTP
|
|
|
|
import qualified Network.HTTP.Types as HTTP
|
|
|
|
|
|
|
|
|
2021-03-15 16:02:58 +03:00
|
|
|
import qualified Hasura.SQL.AnyBackend as AB
|
|
|
|
|
2021-02-25 21:15:55 +03:00
|
|
|
import Hasura.Backends.MSSQL.Connection
|
2021-04-20 19:57:14 +03:00
|
|
|
import Hasura.Backends.MSSQL.FromIr as TSQL
|
2021-02-23 20:37:27 +03:00
|
|
|
import Hasura.Backends.MSSQL.Plan
|
2021-04-20 19:57:14 +03:00
|
|
|
import Hasura.Backends.MSSQL.SQL.Value (toTxtEncodedVal)
|
2021-02-23 20:37:27 +03:00
|
|
|
import Hasura.Backends.MSSQL.ToQuery
|
2021-04-20 19:57:14 +03:00
|
|
|
import Hasura.Backends.MSSQL.Types as TSQL
|
2021-02-23 20:37:27 +03:00
|
|
|
import Hasura.EncJSON
|
|
|
|
import Hasura.GraphQL.Context
|
|
|
|
import Hasura.GraphQL.Execute.Backend
|
|
|
|
import Hasura.GraphQL.Execute.LiveQuery.Plan
|
|
|
|
import Hasura.GraphQL.Parser
|
|
|
|
import Hasura.RQL.Types
|
|
|
|
import Hasura.Session
|
|
|
|
|
|
|
|
|
|
|
|
instance BackendExecute 'MSSQL where
|
|
|
|
type PreparedQuery 'MSSQL = Text
|
2021-04-20 19:57:14 +03:00
|
|
|
type MultiplexedQuery 'MSSQL = MultiplexedQuery'
|
2021-02-25 21:15:55 +03:00
|
|
|
type ExecutionMonad 'MSSQL = ExceptT QErr IO
|
2021-02-23 20:37:27 +03:00
|
|
|
getRemoteJoins = const []
|
|
|
|
|
|
|
|
mkDBQueryPlan = msDBQueryPlan
|
|
|
|
mkDBMutationPlan = msDBMutationPlan
|
|
|
|
mkDBSubscriptionPlan = msDBSubscriptionPlan
|
2021-04-13 14:10:08 +03:00
|
|
|
mkDBQueryExplain = msDBQueryExplain
|
|
|
|
mkLiveQueryExplain = msDBLiveQueryExplain
|
2021-02-23 20:37:27 +03:00
|
|
|
|
|
|
|
|
2021-04-20 19:57:14 +03:00
|
|
|
-- multiplexed query
|
|
|
|
newtype MultiplexedQuery' = MultiplexedQuery' Reselect
|
2021-02-23 20:37:27 +03:00
|
|
|
|
2021-04-20 19:57:14 +03:00
|
|
|
instance T.ToTxt MultiplexedQuery' where
|
|
|
|
toTxt (MultiplexedQuery' reselect) = T.toTxt $ toQueryPretty $ fromReselect reselect
|
2021-02-23 20:37:27 +03:00
|
|
|
|
|
|
|
|
|
|
|
-- query
|
|
|
|
|
|
|
|
msDBQueryPlan
|
|
|
|
:: forall m.
|
|
|
|
( MonadError QErr m
|
|
|
|
)
|
|
|
|
=> Env.Environment
|
|
|
|
-> HTTP.Manager
|
|
|
|
-> [HTTP.Header]
|
|
|
|
-> UserInfo
|
|
|
|
-> [G.Directive G.Name]
|
2021-04-01 23:40:31 +03:00
|
|
|
-> SourceName
|
2021-02-23 20:37:27 +03:00
|
|
|
-> SourceConfig 'MSSQL
|
|
|
|
-> QueryDB 'MSSQL (UnpreparedValue 'MSSQL)
|
|
|
|
-> m ExecutionStep
|
2021-04-01 23:40:31 +03:00
|
|
|
msDBQueryPlan _env _manager _reqHeaders userInfo _directives sourceName sourceConfig qrf = do
|
2021-02-23 20:37:27 +03:00
|
|
|
select <- fromSelect <$> planNoPlan userInfo qrf
|
|
|
|
let queryString = ODBC.renderQuery $ toQueryPretty select
|
2021-02-25 21:15:55 +03:00
|
|
|
pool = _mscConnectionPool sourceConfig
|
|
|
|
odbcQuery = encJFromText <$> runJSONPathQuery pool (toQueryFlat select)
|
2021-03-15 16:02:58 +03:00
|
|
|
pure
|
|
|
|
$ ExecStepDB []
|
|
|
|
. AB.mkAnyBackend
|
2021-04-01 23:40:31 +03:00
|
|
|
$ DBStepInfo sourceName sourceConfig (Just queryString) odbcQuery
|
2021-02-23 20:37:27 +03:00
|
|
|
|
2021-04-13 14:10:08 +03:00
|
|
|
msDBQueryExplain
|
2021-04-20 19:57:14 +03:00
|
|
|
:: MonadError QErr m
|
2021-04-13 14:10:08 +03:00
|
|
|
=> G.Name
|
|
|
|
-> UserInfo
|
|
|
|
-> SourceName
|
|
|
|
-> SourceConfig 'MSSQL
|
|
|
|
-> QueryDB 'MSSQL (UnpreparedValue 'MSSQL)
|
|
|
|
-> m (AB.AnyBackend DBStepInfo)
|
|
|
|
msDBQueryExplain fieldName userInfo sourceName sourceConfig qrf = do
|
|
|
|
select <- withExplain . fromSelect <$> planNoPlan userInfo qrf
|
|
|
|
let queryString = ODBC.renderQuery $ toQueryPretty select
|
|
|
|
pool = _mscConnectionPool sourceConfig
|
|
|
|
-- TODO: execute `select` in separate batch
|
|
|
|
-- https://github.com/hasura/graphql-engine-mono/issues/1024
|
|
|
|
odbcQuery = runJSONPathQuery pool (toQueryFlat select) <&> \explainInfo ->
|
|
|
|
encJFromJValue $ ExplainPlan fieldName (Just queryString) (Just [explainInfo])
|
|
|
|
pure
|
|
|
|
$ AB.mkAnyBackend
|
|
|
|
$ DBStepInfo sourceName sourceConfig Nothing odbcQuery
|
|
|
|
|
|
|
|
msDBLiveQueryExplain
|
2021-04-20 19:57:14 +03:00
|
|
|
:: MonadError QErr m
|
2021-04-13 14:10:08 +03:00
|
|
|
=> LiveQueryPlan 'MSSQL (MultiplexedQuery 'MSSQL) -> m LiveQueryPlanExplanation
|
2021-04-20 19:57:14 +03:00
|
|
|
msDBLiveQueryExplain (LiveQueryPlan plan _sourceConfig variables) = do
|
|
|
|
let query = _plqpQuery plan
|
|
|
|
-- TODO: execute `select` in separate batch
|
|
|
|
-- https://github.com/hasura/graphql-engine-mono/issues/1024
|
|
|
|
-- select = withExplain $ QueryPrinter query
|
|
|
|
-- pool = _mscConnectionPool sourceConfig
|
|
|
|
-- explainInfo <- runJSONPathQuery pool (toQueryFlat select)
|
|
|
|
pure $ LiveQueryPlanExplanation (T.toTxt query) [] variables
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- Producing the correct SQL-level list comprehension to multiplex a query
|
|
|
|
|
|
|
|
-- Problem description:
|
|
|
|
--
|
|
|
|
-- Generate a query that repeats the same query N times but with
|
|
|
|
-- certain slots replaced:
|
|
|
|
--
|
|
|
|
-- [ Select x y | (x,y) <- [..] ]
|
|
|
|
--
|
|
|
|
|
|
|
|
multiplexRootReselect
|
|
|
|
:: [(CohortId, CohortVariables)]
|
|
|
|
-> TSQL.Reselect
|
|
|
|
-> TSQL.Select
|
|
|
|
multiplexRootReselect variables rootReselect =
|
|
|
|
Select
|
|
|
|
{ selectTop = NoTop
|
|
|
|
, selectProjections =
|
|
|
|
[ FieldNameProjection
|
|
|
|
Aliased
|
|
|
|
{ aliasedThing =
|
|
|
|
TSQL.FieldName
|
|
|
|
{fieldNameEntity = rowAlias, fieldName = resultIdAlias}
|
|
|
|
, aliasedAlias = resultIdAlias
|
|
|
|
}
|
|
|
|
, ExpressionProjection
|
|
|
|
Aliased
|
|
|
|
{ aliasedThing =
|
|
|
|
ColumnExpression
|
|
|
|
(TSQL.FieldName
|
|
|
|
{ fieldNameEntity = resultAlias
|
|
|
|
, fieldName = TSQL.jsonFieldName
|
|
|
|
})
|
|
|
|
, aliasedAlias = resultAlias
|
|
|
|
}
|
|
|
|
]
|
|
|
|
, selectFrom =
|
|
|
|
FromOpenJson
|
|
|
|
Aliased
|
|
|
|
{ aliasedThing =
|
|
|
|
OpenJson
|
|
|
|
{ openJsonExpression =
|
|
|
|
ValueExpression (ODBC.TextValue $ lbsToTxt $ J.encode variables)
|
|
|
|
, openJsonWith =
|
|
|
|
NE.fromList
|
|
|
|
[ UuidField resultIdAlias (Just $ IndexPath RootPath 0)
|
|
|
|
, JsonField resultVarsAlias (Just $ IndexPath RootPath 1)
|
|
|
|
]
|
|
|
|
}
|
|
|
|
, aliasedAlias = rowAlias
|
|
|
|
}
|
|
|
|
, selectJoins =
|
|
|
|
[ Join
|
|
|
|
{ joinSource = JoinReselect rootReselect
|
|
|
|
, joinJoinAlias =
|
|
|
|
JoinAlias
|
|
|
|
{ joinAliasEntity = resultAlias
|
|
|
|
, joinAliasField = Just TSQL.jsonFieldName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
, selectWhere = Where mempty
|
|
|
|
, selectFor =
|
|
|
|
JsonFor ForJson {jsonCardinality = JsonArray, jsonRoot = NoRoot}
|
|
|
|
, selectOrderBy = Nothing
|
|
|
|
, selectOffset = Nothing
|
|
|
|
}
|
|
|
|
|
2021-04-13 14:10:08 +03:00
|
|
|
|
2021-02-23 20:37:27 +03:00
|
|
|
-- mutation
|
|
|
|
|
|
|
|
msDBMutationPlan
|
|
|
|
:: forall m.
|
|
|
|
( MonadError QErr m
|
|
|
|
)
|
|
|
|
=> Env.Environment
|
|
|
|
-> HTTP.Manager
|
|
|
|
-> [HTTP.Header]
|
|
|
|
-> UserInfo
|
|
|
|
-> Bool
|
2021-04-01 23:40:31 +03:00
|
|
|
-> SourceName
|
2021-02-23 20:37:27 +03:00
|
|
|
-> SourceConfig 'MSSQL
|
|
|
|
-> MutationDB 'MSSQL (UnpreparedValue 'MSSQL)
|
|
|
|
-> m ExecutionStep
|
2021-04-01 23:40:31 +03:00
|
|
|
msDBMutationPlan _env _manager _reqHeaders _userInfo _stringifyNum _sourceName _sourceConfig _mrf =
|
2021-02-23 20:37:27 +03:00
|
|
|
throw500 "mutations are not supported in MSSQL; this should be unreachable"
|
|
|
|
|
|
|
|
|
|
|
|
-- subscription
|
|
|
|
|
|
|
|
msDBSubscriptionPlan
|
|
|
|
:: forall m.
|
|
|
|
( MonadError QErr m
|
|
|
|
)
|
|
|
|
=> UserInfo
|
2021-04-01 23:40:31 +03:00
|
|
|
-> SourceName
|
2021-02-23 20:37:27 +03:00
|
|
|
-> SourceConfig 'MSSQL
|
|
|
|
-> InsOrdHashMap G.Name (QueryDB 'MSSQL (UnpreparedValue 'MSSQL))
|
|
|
|
-> m (LiveQueryPlan 'MSSQL (MultiplexedQuery 'MSSQL))
|
2021-04-20 19:57:14 +03:00
|
|
|
msDBSubscriptionPlan UserInfo {_uiSession, _uiRole} _sourceName sourceConfig rootFields = do
|
|
|
|
(reselect, prepareState) <- planMultiplex rootFields _uiSession
|
|
|
|
let PrepareState{sessionVariables, namedArguments, positionalArguments} = prepareState
|
|
|
|
-- TODO: call MSSQL validateVariables
|
|
|
|
-- We need to ensure that the values provided for variables are correct according to MSSQL.
|
|
|
|
-- Without this check an invalid value for a variable for one instance of the subscription will
|
|
|
|
-- take down the entire multiplexed query.
|
|
|
|
let cohortVariables = mkCohortVariables
|
|
|
|
sessionVariables
|
|
|
|
_uiSession
|
|
|
|
(toTxtEncodedVal namedArguments)
|
|
|
|
(toTxtEncodedVal positionalArguments)
|
|
|
|
let parameterizedPlan = ParameterizedLiveQueryPlan _uiRole $ MultiplexedQuery' reselect
|
2021-02-23 20:37:27 +03:00
|
|
|
pure
|
2021-04-20 19:57:14 +03:00
|
|
|
$ LiveQueryPlan parameterizedPlan sourceConfig cohortVariables
|