mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 20:41:49 +03:00
4ccc830bb8
## Description We go through the module `Hasura.Backends.MSSQL.FromIr` and split it into separate self-contained units, which we document. Note that this PR has a slightly opinionated follow-up PR #3909 . ### Related Issues Fix #3666 ### Solution and Design The module `FromIr` has given rise to: * `FromIr.Expression` * `FromIr.Query` * `FromIr.Delete` * `FromIr.Insert` * `FromIr.Update` * `FromIr.SelectIntoTempTable` And `Execute.MutationResponse` has become `FromIr.MutationResponse` (after some slight adaptation of types). PR-URL: https://github.com/hasura/graphql-engine-mono/pull/3908 GitOrigin-RevId: 364acf1bcdf74f2e19464c31cdded12bd8e9aa59
98 lines
3.4 KiB
Haskell
98 lines
3.4 KiB
Haskell
-- | The modules in the @Hasura.Backends.MSSQL.FromIr@ namespace translates the
|
|
-- RQL IR into TSQL, the SQL dialect of MSSQL, as defined in abstract syntax in
|
|
-- "Hasura.Backends.MSSQL.Types".
|
|
--
|
|
-- The translation happens in the @FromIr@ monad, which manages identifier
|
|
-- scoping and error collection.
|
|
--
|
|
-- The actual rendering of this AST into TSQL text happens in
|
|
-- "Hasura.Backends.MSSQL.ToQuery".
|
|
module Hasura.Backends.MSSQL.FromIr
|
|
( -- * The central Monad
|
|
FromIr,
|
|
runFromIr,
|
|
Error (..),
|
|
|
|
-- * Name generation
|
|
NameTemplate (..),
|
|
generateAlias,
|
|
)
|
|
where
|
|
|
|
import Control.Monad.Validate
|
|
import Control.Monad.Validate qualified as V
|
|
import Data.Map.Strict (Map)
|
|
import Data.Map.Strict qualified as M
|
|
import Data.Text qualified as T
|
|
import Hasura.Backends.MSSQL.Instances.Types ()
|
|
import Hasura.Backends.MSSQL.Types.Internal as TSQL
|
|
import Hasura.Base.Error (QErr, throw500)
|
|
import Hasura.Prelude
|
|
import Hasura.RQL.IR qualified as IR
|
|
import Hasura.SQL.Backend
|
|
|
|
-- | The central Monad used throughout for all conversion functions.
|
|
--
|
|
-- It has the following features:
|
|
--
|
|
-- * It's a 'MonadValidate', so it'll continue going when it encounters 'Error's
|
|
-- to accumulate as many as possible.
|
|
--
|
|
-- * It has a facility for generating fresh, unique aliases, which lets the
|
|
-- translation output retain a resemblance with source names without the
|
|
-- translation process needing to be bothered about potential name shadowing.
|
|
-- See 'generateAlias'.
|
|
newtype FromIr a = FromIr
|
|
{ unFromIr :: StateT (Map Text Int) (Validate (NonEmpty Error)) a
|
|
}
|
|
deriving (Functor, Applicative, Monad, MonadValidate (NonEmpty Error))
|
|
|
|
-- | Run a 'FromIr' action, throwing errors that have been collected using the
|
|
-- supplied action.
|
|
runFromIr :: MonadError QErr m => FromIr a -> m a
|
|
runFromIr = flip onLeft (throw500 . tshow) . V.runValidate . flip evalStateT mempty . unFromIr
|
|
|
|
-- | Errors that may happen during translation.
|
|
data Error
|
|
= UnsupportedOpExpG (IR.OpExpG 'MSSQL Expression)
|
|
| FunctionNotSupported
|
|
deriving (Show, Eq)
|
|
|
|
-- | Hints about the type of entity that 'generateAlias' is producing an alias
|
|
-- for.
|
|
data NameTemplate
|
|
= ArrayRelationTemplate Text
|
|
| ArrayAggregateTemplate Text
|
|
| ObjectRelationTemplate Text
|
|
| TableTemplate Text
|
|
| ForOrderAlias Text
|
|
|
|
-- | Generate a fresh alias for a given entity to remove ambiguity and naming
|
|
-- conflicts between scopes at the TSQL level.
|
|
--
|
|
-- Names are generated in the form @type_name_occurrence@, where:
|
|
--
|
|
-- * @type@ hints at the type of entity,
|
|
-- * @name@ refers to the source name being aliased, and
|
|
-- * @occurrence@ is an integer counter that distinguishes each occurrence of @type_name@.
|
|
--
|
|
-- Example outputs:
|
|
--
|
|
-- > do
|
|
-- > "ar_articles_1" <- generateAlias (ArrayRelationTemplate "articles")
|
|
-- > "ar_articles_2" <- generateAlias (ArrayRelationTemplate "articles")
|
|
-- > "t_users_1" <- generateAlias (TableTemplate "users")
|
|
generateAlias :: NameTemplate -> FromIr Text
|
|
generateAlias template = do
|
|
FromIr (modify' (M.insertWith (+) rendered 1))
|
|
occurrence <- M.findWithDefault 1 rendered <$> FromIr get
|
|
pure (rendered <> tshow occurrence)
|
|
where
|
|
rendered = T.take 20 $
|
|
case template of
|
|
ArrayRelationTemplate sample -> "ar_" <> sample
|
|
ArrayAggregateTemplate sample -> "aa_" <> sample
|
|
ObjectRelationTemplate sample -> "or_" <> sample
|
|
TableTemplate sample -> "t_" <> sample
|
|
ForOrderAlias sample -> "order_" <> sample
|