graphql-engine/server/src-lib/Hasura/Backends/MySQL/Plan.hs
Chris Done 459a7adbfb Add Types/FromIr changes for MySQL to work with the DataLoader
While it looks like a lot of work in FromIr.hs, you can rather review the type changes in `Hasura.Backends.MySQL.Types.Internal` and the changes to FromIr are only to reflect that. Essentially we're simplifying the FromIr code to not think about SQL-based joins: instead, FromIr produces fields necessary for the dataloader Plan/Execute to do their job properly.

I've done my best to ensure that all the hunks in the diff in this PR are minimal for slightly easier perusing.

I think future PRs will be more intentionally well structured, rather than created retroactively.

**Preceding PR:** #2549

**Next PR**: #2367

The tests have been run like this on my machine. I don't know more beyond that.

```
docker run -i -e "PYTEST_ADDOPTS=--color=yes" -e "TERM=xterm-256color" --net=host -v`pwd`:`pwd` -w`pwd`/server/tests-py chrisdone/hasura-pytest:b0f26f615 pytest  --hge-urls="http://localhost:8080"  --pg-urls="postgres://chinook:chinook@localhost:5432/chinook"  --backend mysql -k MySQL
```

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/2608
Co-authored-by: Abby Sassel <3883855+sassela@users.noreply.github.com>
GitOrigin-RevId: a6483335c3036963360dde7d7d7eaf10859351cb
2021-10-21 23:51:17 +00:00

68 lines
2.3 KiB
Haskell

-- | Planning MySQL queries and subscriptions.
module Hasura.Backends.MySQL.Plan
( planQuery,
queryToActionForest,
)
where
import Control.Monad.Validate
import Data.Aeson qualified as J
import Data.ByteString.Lazy (toStrict)
import Data.Text.Extended
import Data.Tree
import Hasura.Backends.MySQL.DataLoader.Plan qualified as DataLoader
import Hasura.Backends.MySQL.FromIr
import Hasura.Backends.MySQL.Types
import Hasura.Base.Error
import Hasura.GraphQL.Parser qualified as GraphQL
import Hasura.Prelude hiding (first)
import Hasura.RQL.IR
import Hasura.RQL.Types.Column qualified as RQL
import Hasura.SQL.Backend
import Hasura.Session
-- | Plan the query and then produce a forest of actions for the executor.
queryToActionForest ::
MonadError QErr m =>
UserInfo ->
QueryDB 'MySQL (Const Void) (GraphQL.UnpreparedValue 'MySQL) ->
m (DataLoader.HeadAndTail, Forest DataLoader.PlannedAction)
queryToActionForest userInfo qrf = do
select <- planQuery (_uiSession userInfo) qrf
let (!headAndTail, !plannedActionsList) =
DataLoader.runPlan
(DataLoader.planSelectHeadAndTail Nothing Nothing select)
!actionsForest = DataLoader.actionsForest id plannedActionsList
pure (headAndTail, actionsForest)
planQuery ::
MonadError QErr m =>
SessionVariables ->
QueryDB 'MySQL (Const Void) (GraphQL.UnpreparedValue 'MySQL) ->
m Select
planQuery sessionVariables queryDB = do
rootField <- traverse (prepareValueQuery sessionVariables) queryDB
sel <-
runValidate (runFromIr (fromRootField rootField))
`onLeft` (throw400 NotSupported . tshow)
pure $
sel
-- | Prepare a value without any query planning; we just execute the
-- query with the values embedded.
prepareValueQuery ::
MonadError QErr m =>
SessionVariables ->
GraphQL.UnpreparedValue 'MySQL ->
m Expression
prepareValueQuery sessionVariables =
\case
GraphQL.UVLiteral x -> pure x
GraphQL.UVSession -> pure $ ValueExpression $ BinaryValue $ toStrict $ J.encode sessionVariables
GraphQL.UVParameter _ RQL.ColumnValue {..} -> pure $ ValueExpression cvValue
GraphQL.UVSessionVar _typ sessionVariable -> do
value <-
getSessionVariableValue sessionVariable sessionVariables
`onNothing` throw400 NotFound ("missing session variable: " <>> sessionVariable)
pure $ ValueExpression $ TextValue value