mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-18 13:02:11 +03:00
306162f477
### Description This PR removes `ServerConfigCtx` and `HasServerConfigCtx`. Instead, it favours different approaches: - when the code was only using one field, it passes that field explicitly (usually `SQLGenCtx` or `CheckFeatureFlag`) - when the code was using several fields, but in only one function, it inlines - for the cache build, it introduces `CacheStaticConfig` and `CacheDynamicConfig`, which are subsets of `AppEnv` and `AppContext` respectively The main goal of this is to help with the modularization of the engine: as `ServerConfigCtx` had fields whose types were imported from several unrelated parts of the engine, using it tied together parts of the engine that should not be aware of one another (such as tying together `Hasura.LogicalModel` and `Hasura.GraphQL.Schema`). The bulk of this PR is a change to the cache build, as a follow up to #8509: instead of giving the entire `ServerConfigCtx` as a incremental rule argument, we only give the new `CacheDynamicConfig` struct, which has fewer fields. The other required fields, that were coming from the `AppEnv`, are now given via the `HasCacheStaticConfig` constraint, which is a "subset" of `HasAppEnv`. (Some further work could include moving `StringifyNumbers` out of `GraphQL.Schema.Options`, given how it is used all across the codebase, including in `RQL.DML`.) PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8513 GitOrigin-RevId: 818cbcd71494e3cd946b06adbb02ca328a8a298e
219 lines
7.9 KiB
Haskell
219 lines
7.9 KiB
Haskell
{-# LANGUAGE ViewPatterns #-}
|
|
|
|
-- | The RQL query ('/v2/query')
|
|
module Hasura.Server.API.V2Query
|
|
( RQLQuery,
|
|
queryModifiesSchema,
|
|
runQuery,
|
|
)
|
|
where
|
|
|
|
import Control.Concurrent.Async.Lifted (mapConcurrently)
|
|
import Control.Lens (preview, _Right)
|
|
import Control.Monad.Trans.Control (MonadBaseControl)
|
|
import Data.Aeson
|
|
import Data.Aeson.Types (Parser)
|
|
import Data.Text qualified as T
|
|
import GHC.Generics.Extended (constrName)
|
|
import Hasura.App.State
|
|
import Hasura.Backends.BigQuery.DDL.RunSQL qualified as BigQuery
|
|
import Hasura.Backends.DataConnector.Adapter.RunSQL qualified as DataConnector
|
|
import Hasura.Backends.DataConnector.Adapter.Types (DataConnectorName, mkDataConnectorName)
|
|
import Hasura.Backends.MSSQL.DDL.RunSQL qualified as MSSQL
|
|
import Hasura.Backends.MySQL.SQL qualified as MySQL
|
|
import Hasura.Backends.Postgres.DDL.RunSQL qualified as Postgres
|
|
import Hasura.Base.Error
|
|
import Hasura.EncJSON
|
|
import Hasura.Metadata.Class
|
|
import Hasura.Prelude
|
|
import Hasura.QueryTags
|
|
import Hasura.RQL.DDL.Schema
|
|
import Hasura.RQL.DDL.Schema.Cache.Config
|
|
import Hasura.RQL.DML.Count
|
|
import Hasura.RQL.DML.Delete
|
|
import Hasura.RQL.DML.Insert
|
|
import Hasura.RQL.DML.Select
|
|
import Hasura.RQL.DML.Types
|
|
( CountQuery,
|
|
DeleteQuery,
|
|
InsertQuery,
|
|
SelectQuery,
|
|
UpdateQuery,
|
|
)
|
|
import Hasura.RQL.DML.Update
|
|
import Hasura.RQL.Types.Common
|
|
import Hasura.RQL.Types.Metadata
|
|
import Hasura.RQL.Types.SchemaCache (MetadataWithResourceVersion (MetadataWithResourceVersion))
|
|
import Hasura.RQL.Types.SchemaCache.Build
|
|
import Hasura.RQL.Types.Source
|
|
import Hasura.SQL.Backend
|
|
import Hasura.Server.Types
|
|
import Hasura.Services
|
|
import Hasura.Session
|
|
import Hasura.Tracing qualified as Tracing
|
|
import Language.GraphQL.Draft.Syntax qualified as GQL
|
|
|
|
data RQLQuery
|
|
= RQInsert !InsertQuery
|
|
| RQSelect !SelectQuery
|
|
| RQUpdate !UpdateQuery
|
|
| RQDelete !DeleteQuery
|
|
| RQCount !CountQuery
|
|
| RQRunSql !Postgres.RunSQL
|
|
| RQMssqlRunSql !MSSQL.MSSQLRunSQL
|
|
| RQCitusRunSql !Postgres.RunSQL
|
|
| RQCockroachRunSql !Postgres.RunSQL
|
|
| RQMysqlRunSql !MySQL.RunSQL
|
|
| RQBigqueryRunSql !BigQuery.BigQueryRunSQL
|
|
| RQDataConnectorRunSql !DataConnectorName !DataConnector.DataConnectorRunSQL
|
|
| RQBigqueryDatabaseInspection !BigQuery.BigQueryRunSQL
|
|
| RQBulk ![RQLQuery]
|
|
| -- | A variant of 'RQBulk' that runs a bulk of read-only queries concurrently.
|
|
-- Asserts that queries on this lists are not modifying the schema.
|
|
--
|
|
-- This is mainly used by the graphql-engine console.
|
|
RQConcurrentBulk [RQLQuery]
|
|
deriving (Generic)
|
|
|
|
-- | This instance has been written by hand so that "wildcard" prefixes of _run_sql can be delegated to data connectors.
|
|
instance FromJSON RQLQuery where
|
|
parseJSON = withObject "RQLQuery" \o -> do
|
|
t <- o .: "type"
|
|
let args :: forall a. FromJSON a => Parser a
|
|
args = o .: "args"
|
|
dcNameFromRunSql = T.stripSuffix "_run_sql" >=> GQL.mkName >=> preview _Right . mkDataConnectorName
|
|
case t of
|
|
"insert" -> RQInsert <$> args
|
|
"select" -> RQSelect <$> args
|
|
"update" -> RQUpdate <$> args
|
|
"delete" -> RQDelete <$> args
|
|
"count" -> RQCount <$> args
|
|
-- Optionally, we can specify a `pg_` prefix. This primarily makes some
|
|
-- string interpolation easier in the cross-backend tests.
|
|
"run_sql" -> RQRunSql <$> args
|
|
"pg_run_sql" -> RQRunSql <$> args
|
|
"mssql_run_sql" -> RQMssqlRunSql <$> args
|
|
"citus_run_sql" -> RQCitusRunSql <$> args
|
|
"cockroach_run_sql" -> RQCockroachRunSql <$> args
|
|
"mysql_run_sql" -> RQMysqlRunSql <$> args
|
|
"bigquery_run_sql" -> RQBigqueryRunSql <$> args
|
|
(dcNameFromRunSql -> Just t') -> RQDataConnectorRunSql t' <$> args
|
|
"bigquery_database_inspection" -> RQBigqueryDatabaseInspection <$> args
|
|
"bulk" -> RQBulk <$> args
|
|
"concurrent_bulk" -> RQConcurrentBulk <$> args
|
|
_ -> fail $ "Unrecognised RQLQuery type: " <> T.unpack t
|
|
|
|
runQuery ::
|
|
( MonadIO m,
|
|
MonadBaseControl IO m,
|
|
MonadError QErr m,
|
|
HasAppEnv m,
|
|
HasCacheStaticConfig m,
|
|
Tracing.MonadTrace m,
|
|
MonadMetadataStorage m,
|
|
MonadResolveSource m,
|
|
MonadQueryTags m,
|
|
ProvidesHasuraServices m,
|
|
UserInfoM m
|
|
) =>
|
|
AppContext ->
|
|
RebuildableSchemaCache ->
|
|
RQLQuery ->
|
|
m (EncJSON, RebuildableSchemaCache)
|
|
runQuery appContext schemaCache rqlQuery = do
|
|
AppEnv {..} <- askAppEnv
|
|
when ((appEnvEnableReadOnlyMode == ReadOnlyModeEnabled) && queryModifiesUserDB rqlQuery) $
|
|
throw400 NotSupported "Cannot run write queries when read-only mode is enabled"
|
|
|
|
let dynamicConfig = buildCacheDynamicConfig appContext
|
|
MetadataWithResourceVersion metadata currentResourceVersion <- Tracing.newSpan "fetchMetadata" $ liftEitherM fetchMetadata
|
|
((result, updatedMetadata), updatedCache, invalidations) <-
|
|
runQueryM (acSQLGenCtx appContext) rqlQuery
|
|
-- We can use defaults here unconditionally, since there is no MD export function in V2Query
|
|
& runMetadataT metadata (acMetadataDefaults appContext)
|
|
& runCacheRWT dynamicConfig schemaCache
|
|
when (queryModifiesSchema rqlQuery) $ do
|
|
case appEnvEnableMaintenanceMode of
|
|
MaintenanceModeDisabled -> do
|
|
-- set modified metadata in storage
|
|
newResourceVersion <-
|
|
Tracing.newSpan "setMetadata" $
|
|
liftEitherM $
|
|
setMetadata currentResourceVersion updatedMetadata
|
|
-- notify schema cache sync
|
|
Tracing.newSpan "notifySchemaCacheSync" $
|
|
liftEitherM $
|
|
notifySchemaCacheSync newResourceVersion appEnvInstanceId invalidations
|
|
MaintenanceModeEnabled () ->
|
|
throw500 "metadata cannot be modified in maintenance mode"
|
|
pure (result, updatedCache)
|
|
|
|
queryModifiesSchema :: RQLQuery -> Bool
|
|
queryModifiesSchema = \case
|
|
RQInsert _ -> False
|
|
RQSelect _ -> False
|
|
RQUpdate _ -> False
|
|
RQDelete _ -> False
|
|
RQCount _ -> False
|
|
RQRunSql q -> Postgres.isSchemaCacheBuildRequiredRunSQL q
|
|
RQCitusRunSql q -> Postgres.isSchemaCacheBuildRequiredRunSQL q
|
|
RQCockroachRunSql q -> Postgres.isSchemaCacheBuildRequiredRunSQL q
|
|
RQMssqlRunSql q -> MSSQL.isSchemaCacheBuildRequiredRunSQL q
|
|
RQMysqlRunSql _ -> False
|
|
RQBigqueryRunSql _ -> False
|
|
RQDataConnectorRunSql _ _ -> False
|
|
RQBigqueryDatabaseInspection _ -> False
|
|
RQBulk l -> any queryModifiesSchema l
|
|
RQConcurrentBulk l -> any queryModifiesSchema l
|
|
|
|
runQueryM ::
|
|
( MonadError QErr m,
|
|
MonadIO m,
|
|
MonadBaseControl IO m,
|
|
UserInfoM m,
|
|
CacheRWM m,
|
|
Tracing.MonadTrace m,
|
|
MetadataM m,
|
|
MonadQueryTags m
|
|
) =>
|
|
SQLGenCtx ->
|
|
RQLQuery ->
|
|
m EncJSON
|
|
runQueryM sqlGen rq = Tracing.newSpan (T.pack $ constrName rq) $ case rq of
|
|
RQInsert q -> runInsert sqlGen q
|
|
RQSelect q -> runSelect sqlGen q
|
|
RQUpdate q -> runUpdate sqlGen q
|
|
RQDelete q -> runDelete sqlGen q
|
|
RQCount q -> runCount q
|
|
RQRunSql q -> Postgres.runRunSQL @'Vanilla sqlGen q
|
|
RQMssqlRunSql q -> MSSQL.runSQL q
|
|
RQMysqlRunSql q -> MySQL.runSQL q
|
|
RQCitusRunSql q -> Postgres.runRunSQL @'Citus sqlGen q
|
|
RQCockroachRunSql q -> Postgres.runRunSQL @'Cockroach sqlGen q
|
|
RQBigqueryRunSql q -> BigQuery.runSQL q
|
|
RQDataConnectorRunSql t q -> DataConnector.runSQL t q
|
|
RQBigqueryDatabaseInspection q -> BigQuery.runDatabaseInspection q
|
|
RQBulk l -> encJFromList <$> indexedMapM (runQueryM sqlGen) l
|
|
RQConcurrentBulk l -> do
|
|
when (queryModifiesSchema rq) $
|
|
throw500 "Only read-only queries are allowed in a concurrent_bulk"
|
|
encJFromList <$> mapConcurrently (runQueryM sqlGen) l
|
|
|
|
queryModifiesUserDB :: RQLQuery -> Bool
|
|
queryModifiesUserDB = \case
|
|
RQInsert _ -> True
|
|
RQSelect _ -> False
|
|
RQUpdate _ -> True
|
|
RQDelete _ -> True
|
|
RQCount _ -> False
|
|
RQRunSql runsql -> not (Postgres.isReadOnly runsql)
|
|
RQCitusRunSql runsql -> not (Postgres.isReadOnly runsql)
|
|
RQCockroachRunSql runsql -> not (Postgres.isReadOnly runsql)
|
|
RQMssqlRunSql _ -> True
|
|
RQMysqlRunSql _ -> True
|
|
RQBigqueryRunSql _ -> True
|
|
RQDataConnectorRunSql _ _ -> True
|
|
RQBigqueryDatabaseInspection _ -> False
|
|
RQBulk q -> any queryModifiesUserDB q
|
|
RQConcurrentBulk _ -> False
|