2021-07-15 15:44:26 +03:00
|
|
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
|
|
|
2021-11-04 19:08:33 +03:00
|
|
|
module Hasura.Backends.MySQL.Instances.Execute () where
|
2021-07-15 15:44:26 +03:00
|
|
|
|
2021-10-18 19:04:30 +03:00
|
|
|
import Data.Aeson as J
|
2021-10-23 14:42:20 +03:00
|
|
|
import Data.Bifunctor
|
|
|
|
import Data.Coerce
|
2021-10-18 19:04:30 +03:00
|
|
|
import Data.HashMap.Strict.InsOrd qualified as OMap
|
|
|
|
import Data.Text qualified as T
|
2021-10-23 14:42:20 +03:00
|
|
|
import Data.Text.Encoding qualified as T
|
2021-10-18 19:04:30 +03:00
|
|
|
import Data.Tree
|
2021-10-23 14:42:20 +03:00
|
|
|
import Database.MySQL.Base (fetchFields, query, storeResult)
|
|
|
|
import Hasura.Backends.MySQL.Connection
|
2021-10-18 19:04:30 +03:00
|
|
|
import Hasura.Backends.MySQL.DataLoader.Execute (OutputValue (..), RecordSet (..))
|
|
|
|
import Hasura.Backends.MySQL.DataLoader.Execute qualified as DataLoader
|
|
|
|
import Hasura.Backends.MySQL.DataLoader.Plan qualified as DataLoader
|
|
|
|
import Hasura.Backends.MySQL.Plan
|
2021-10-23 14:42:20 +03:00
|
|
|
import Hasura.Backends.MySQL.ToQuery as ToQuery
|
|
|
|
import Hasura.Backends.MySQL.Types qualified as MySQL
|
2021-10-18 19:04:30 +03:00
|
|
|
import Hasura.Base.Error
|
|
|
|
import Hasura.EncJSON
|
2021-09-24 01:56:37 +03:00
|
|
|
import Hasura.GraphQL.Execute.Backend
|
2021-10-29 17:42:07 +03:00
|
|
|
import Hasura.GraphQL.Namespace
|
2021-10-18 19:04:30 +03:00
|
|
|
import Hasura.GraphQL.Parser
|
2021-10-23 14:42:20 +03:00
|
|
|
import Hasura.Prelude hiding (first, second)
|
2021-10-18 19:04:30 +03:00
|
|
|
import Hasura.RQL.IR
|
|
|
|
import Hasura.RQL.Types
|
2021-10-23 14:42:20 +03:00
|
|
|
import Hasura.SQL.AnyBackend qualified as AB
|
2021-10-18 19:04:30 +03:00
|
|
|
import Hasura.Session
|
2021-10-23 14:42:20 +03:00
|
|
|
import Hasura.Tracing qualified as Tracing
|
2021-07-15 15:44:26 +03:00
|
|
|
|
|
|
|
instance BackendExecute 'MySQL where
|
2021-09-24 01:56:37 +03:00
|
|
|
type PreparedQuery 'MySQL = Text
|
2021-07-15 15:44:26 +03:00
|
|
|
type MultiplexedQuery 'MySQL = Void
|
2021-10-23 14:42:20 +03:00
|
|
|
type ExecutionMonad 'MySQL = Tracing.TraceT (ExceptT QErr IO)
|
2021-09-24 01:56:37 +03:00
|
|
|
mkDBQueryPlan = mysqlDBQueryPlan
|
|
|
|
mkDBMutationPlan = error "mkDBMutationPlan: MySQL backend does not support this operation yet."
|
2021-08-04 14:42:24 +03:00
|
|
|
mkDBSubscriptionPlan _ _ _ _ = error "mkDBSubscriptionPlan: MySQL backend does not support this operation yet."
|
2021-10-23 14:42:20 +03:00
|
|
|
mkDBQueryExplain = mysqlDBQueryExplain
|
2021-09-24 01:56:37 +03:00
|
|
|
mkLiveQueryExplain _ = error "mkLiveQueryExplain: MySQL backend does not support this operation yet."
|
2021-10-18 19:04:30 +03:00
|
|
|
mkDBRemoteRelationshipPlan = error "mkDBRemoteRelationshipPlan: MySQL does not support this operation yet."
|
2021-09-22 13:43:05 +03:00
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
mysqlDBQueryPlan ::
|
|
|
|
forall m.
|
|
|
|
( MonadError QErr m
|
|
|
|
) =>
|
|
|
|
UserInfo ->
|
2021-10-18 19:04:30 +03:00
|
|
|
SourceName ->
|
|
|
|
SourceConfig 'MySQL ->
|
2021-09-24 01:56:37 +03:00
|
|
|
QueryDB 'MySQL (Const Void) (UnpreparedValue 'MySQL) ->
|
|
|
|
m (DBStepInfo 'MySQL)
|
2021-09-23 15:37:56 +03:00
|
|
|
mysqlDBQueryPlan userInfo sourceName sourceConfig qrf = do
|
2021-10-18 19:04:30 +03:00
|
|
|
(headAndTail, actionsForest) <- queryToActionForest userInfo qrf
|
|
|
|
pure
|
|
|
|
( DBStepInfo
|
|
|
|
@'MySQL
|
|
|
|
sourceName
|
|
|
|
sourceConfig
|
|
|
|
(Just (T.pack (drawForest (fmap (fmap show) actionsForest))))
|
|
|
|
( do
|
|
|
|
result <-
|
|
|
|
DataLoader.runExecute
|
|
|
|
sourceConfig
|
|
|
|
headAndTail
|
|
|
|
(DataLoader.execute actionsForest)
|
|
|
|
either
|
|
|
|
(throw500WithDetail "MySQL DataLoader Error" . toJSON . show)
|
|
|
|
(pure . encJFromRecordSet)
|
|
|
|
result
|
|
|
|
)
|
|
|
|
)
|
2021-09-22 13:43:05 +03:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
2021-10-18 19:04:30 +03:00
|
|
|
-- Encoding for Hasura's GraphQL JSON representation
|
2021-09-22 13:43:05 +03:00
|
|
|
|
2021-10-23 14:42:20 +03:00
|
|
|
mysqlDBQueryExplain ::
|
|
|
|
MonadError QErr m =>
|
2021-10-29 17:42:07 +03:00
|
|
|
RootFieldAlias ->
|
2021-10-23 14:42:20 +03:00
|
|
|
UserInfo ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig 'MySQL ->
|
|
|
|
QueryDB 'MySQL (Const Void) (UnpreparedValue 'MySQL) ->
|
|
|
|
m (AB.AnyBackend DBStepInfo)
|
|
|
|
mysqlDBQueryExplain fieldName userInfo sourceName sourceConfig qrf = do
|
|
|
|
select :: MySQL.Select <- planQuery (_uiSession userInfo) qrf
|
|
|
|
let sqlQuery = selectSQLTextForQuery select
|
|
|
|
sqlQueryText = (T.decodeUtf8 . unQuery . toQueryPretty) (ToQuery.fromSelect select)
|
|
|
|
explainResult =
|
|
|
|
withMySQLPool
|
|
|
|
(MySQL.scConnectionPool sourceConfig)
|
|
|
|
( \conn -> do
|
|
|
|
query conn ("EXPLAIN FORMAT=JSON " <> (unQuery sqlQuery))
|
|
|
|
result <- storeResult conn
|
|
|
|
fields <- fetchFields result
|
|
|
|
rows <- fetchAllRows result
|
|
|
|
let texts = concat $ parseTextRows fields rows
|
|
|
|
pure $ encJFromJValue $ ExplainPlan fieldName (Just sqlQueryText) (Just texts)
|
|
|
|
)
|
|
|
|
pure $
|
|
|
|
AB.mkAnyBackend $
|
|
|
|
DBStepInfo @'MySQL sourceName sourceConfig Nothing explainResult
|
|
|
|
|
|
|
|
selectSQLTextForQuery :: MySQL.Select -> ToQuery.Query
|
|
|
|
selectSQLTextForQuery select = toQueryFlat $ ToQuery.fromSelect select
|
|
|
|
|
2021-10-18 19:04:30 +03:00
|
|
|
encJFromRecordSet :: RecordSet -> EncJSON
|
|
|
|
encJFromRecordSet RecordSet {rows} =
|
|
|
|
encJFromList
|
|
|
|
( map
|
|
|
|
( encJFromAssocList
|
|
|
|
. map (first coerce . second encJFromOutputValue)
|
|
|
|
. OMap.toList
|
|
|
|
)
|
|
|
|
(toList rows)
|
|
|
|
)
|
|
|
|
|
|
|
|
encJFromOutputValue :: DataLoader.OutputValue -> EncJSON
|
|
|
|
encJFromOutputValue =
|
|
|
|
\case
|
|
|
|
ArrayOutputValue array -> encJFromList (map encJFromOutputValue (toList array))
|
|
|
|
RecordOutputValue m ->
|
|
|
|
encJFromAssocList
|
|
|
|
. map (first coerce . second encJFromOutputValue)
|
|
|
|
. OMap.toList
|
|
|
|
$ m
|
|
|
|
ScalarOutputValue value -> encJFromJValue value
|
|
|
|
NullOutputValue {} -> encJFromJValue J.Null
|