2021-02-12 06:04:09 +03:00
|
|
|
module Hasura.GraphQL.Execute.Backend where
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
import Control.Monad.Trans.Control (MonadBaseControl)
|
|
|
|
import Data.Aeson qualified as J
|
|
|
|
import Data.Aeson.Casing qualified as J
|
|
|
|
import Data.Aeson.Ordered qualified as JO
|
|
|
|
import Data.Kind (Type)
|
|
|
|
import Data.Tagged
|
|
|
|
import Data.Text.Extended
|
|
|
|
import Data.Text.NonEmpty (mkNonEmptyTextUnsafe)
|
|
|
|
import Database.PG.Query qualified as Q
|
|
|
|
import Hasura.Base.Error
|
|
|
|
import Hasura.EncJSON
|
|
|
|
import Hasura.GraphQL.Execute.Action.Types (ActionExecutionPlan)
|
|
|
|
import Hasura.GraphQL.Execute.LiveQuery.Plan
|
|
|
|
import Hasura.GraphQL.Execute.RemoteJoin.Types
|
2021-10-29 17:42:07 +03:00
|
|
|
import Hasura.GraphQL.Namespace (RootFieldAlias, RootFieldMap)
|
2021-09-24 01:56:37 +03:00
|
|
|
import Hasura.GraphQL.Parser hiding (Type)
|
|
|
|
import Hasura.GraphQL.Transport.HTTP.Protocol qualified as GH
|
|
|
|
import Hasura.Metadata.Class
|
|
|
|
import Hasura.Prelude
|
|
|
|
import Hasura.QueryTags
|
|
|
|
import Hasura.RQL.DDL.Schema.Cache (CacheRWT)
|
|
|
|
import Hasura.RQL.IR
|
|
|
|
import Hasura.RQL.Types.Action
|
|
|
|
import Hasura.RQL.Types.Backend
|
|
|
|
import Hasura.RQL.Types.Column (ColumnType, fromCol)
|
|
|
|
import Hasura.RQL.Types.Common
|
|
|
|
import Hasura.RQL.Types.QueryTags (QueryTagsConfig)
|
|
|
|
import Hasura.RQL.Types.RemoteSchema
|
2021-10-29 17:42:07 +03:00
|
|
|
import Hasura.RQL.Types.ResultCustomization
|
2021-09-24 01:56:37 +03:00
|
|
|
import Hasura.RQL.Types.Run (RunT (..))
|
|
|
|
import Hasura.RQL.Types.SchemaCache.Build (MetadataT (..))
|
|
|
|
import Hasura.SQL.AnyBackend qualified as AB
|
|
|
|
import Hasura.SQL.Backend
|
|
|
|
import Hasura.Session
|
|
|
|
import Hasura.Tracing (TraceT)
|
|
|
|
import Language.GraphQL.Draft.Syntax qualified as G
|
|
|
|
import Network.HTTP.Types qualified as HTTP
|
2021-02-12 06:04:09 +03:00
|
|
|
|
|
|
|
-- | This typeclass enacapsulates how a given backend translates a root field into an execution
|
|
|
|
-- plan. For now, each root field maps to one execution step, but in the future, when we have
|
|
|
|
-- a client-side dataloader, each root field might translate into a multi-step plan.
|
2021-09-24 01:56:37 +03:00
|
|
|
class
|
|
|
|
( Backend b,
|
|
|
|
ToTxt (MultiplexedQuery b),
|
|
|
|
Monad (ExecutionMonad b)
|
|
|
|
) =>
|
|
|
|
BackendExecute (b :: BackendType)
|
|
|
|
where
|
2021-02-12 06:04:09 +03:00
|
|
|
-- generated query information
|
2021-09-24 01:56:37 +03:00
|
|
|
type PreparedQuery b :: Type
|
2021-02-20 16:45:49 +03:00
|
|
|
type MultiplexedQuery b :: Type
|
2021-09-24 01:56:37 +03:00
|
|
|
type ExecutionMonad b :: Type -> Type
|
2021-02-12 06:04:09 +03:00
|
|
|
|
|
|
|
-- execution plan generation
|
2021-09-24 01:56:37 +03:00
|
|
|
mkDBQueryPlan ::
|
|
|
|
forall m.
|
|
|
|
( MonadError QErr m,
|
|
|
|
MonadQueryTags m,
|
|
|
|
MonadReader QueryTagsComment m
|
|
|
|
) =>
|
|
|
|
UserInfo ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig b ->
|
|
|
|
QueryDB b (Const Void) (UnpreparedValue b) ->
|
|
|
|
m (DBStepInfo b)
|
|
|
|
mkDBMutationPlan ::
|
|
|
|
forall m.
|
|
|
|
( MonadError QErr m,
|
|
|
|
MonadQueryTags m,
|
|
|
|
MonadReader QueryTagsComment m
|
|
|
|
) =>
|
|
|
|
UserInfo ->
|
|
|
|
Bool ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig b ->
|
|
|
|
MutationDB b (Const Void) (UnpreparedValue b) ->
|
|
|
|
m (DBStepInfo b)
|
|
|
|
mkDBSubscriptionPlan ::
|
|
|
|
forall m.
|
|
|
|
( MonadError QErr m,
|
|
|
|
MonadIO m,
|
2021-10-22 17:49:15 +03:00
|
|
|
MonadBaseControl IO m,
|
2021-09-24 01:56:37 +03:00
|
|
|
MonadReader QueryTagsComment m
|
|
|
|
) =>
|
|
|
|
UserInfo ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig b ->
|
2021-10-29 17:42:07 +03:00
|
|
|
Maybe G.Name ->
|
|
|
|
RootFieldMap (QueryDB b (Const Void) (UnpreparedValue b)) ->
|
2021-09-24 01:56:37 +03:00
|
|
|
m (LiveQueryPlan b (MultiplexedQuery b))
|
|
|
|
mkDBQueryExplain ::
|
|
|
|
forall m.
|
|
|
|
( MonadError QErr m
|
|
|
|
) =>
|
2021-10-29 17:42:07 +03:00
|
|
|
RootFieldAlias ->
|
2021-09-24 01:56:37 +03:00
|
|
|
UserInfo ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig b ->
|
|
|
|
QueryDB b (Const Void) (UnpreparedValue b) ->
|
|
|
|
m (AB.AnyBackend DBStepInfo)
|
|
|
|
mkLiveQueryExplain ::
|
|
|
|
( MonadError QErr m,
|
|
|
|
MonadIO m,
|
|
|
|
MonadBaseControl IO m
|
|
|
|
) =>
|
|
|
|
LiveQueryPlan b (MultiplexedQuery b) ->
|
|
|
|
m LiveQueryPlanExplanation
|
|
|
|
|
|
|
|
mkDBRemoteRelationshipPlan ::
|
|
|
|
forall m.
|
|
|
|
( MonadError QErr m,
|
|
|
|
MonadQueryTags m
|
|
|
|
) =>
|
|
|
|
UserInfo ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig b ->
|
|
|
|
-- | List of json objects, each of which becomes a row of the table.
|
|
|
|
NonEmpty J.Object ->
|
|
|
|
-- | The above objects have this schema.
|
|
|
|
HashMap FieldName (Column b, ScalarType b) ->
|
|
|
|
-- | This is a field name from the lhs that *has* to be selected in the
|
2021-09-22 13:43:05 +03:00
|
|
|
-- response along with the relationship.
|
2021-09-24 01:56:37 +03:00
|
|
|
FieldName ->
|
|
|
|
(FieldName, SourceRelationshipSelection b (Const Void) UnpreparedValue) ->
|
|
|
|
m (DBStepInfo b)
|
2021-09-22 13:43:05 +03:00
|
|
|
|
|
|
|
-- | This is a helper function to convert a remote source's relationship to a
|
|
|
|
-- normal relationship to a temporary table. This function can be used to
|
|
|
|
-- implement executeRemoteRelationship function in databases which support
|
|
|
|
-- constructing a temporary table for a list of json objects.
|
2021-09-24 01:56:37 +03:00
|
|
|
convertRemoteSourceRelationship ::
|
|
|
|
forall b.
|
|
|
|
(Backend b) =>
|
|
|
|
-- | Join columns for the relationship
|
|
|
|
HashMap (Column b) (Column b) ->
|
|
|
|
-- | The LHS of the join, this is the expression which selects from json
|
2021-09-22 13:43:05 +03:00
|
|
|
-- objects
|
2021-09-24 01:56:37 +03:00
|
|
|
SelectFromG b (UnpreparedValue b) ->
|
|
|
|
-- | This is the __argument__ id column, that needs to be added to the response
|
2021-09-22 13:43:05 +03:00
|
|
|
-- This is used by by the remote joins processing logic to convert the
|
|
|
|
-- response from upstream to join indices
|
2021-09-24 01:56:37 +03:00
|
|
|
Column b ->
|
|
|
|
-- | This is the type of the __argument__ id column
|
|
|
|
ColumnType b ->
|
|
|
|
-- | The relationship column and its name (how it should be selected in the
|
2021-09-22 13:43:05 +03:00
|
|
|
-- response)
|
2021-09-24 01:56:37 +03:00
|
|
|
(FieldName, SourceRelationshipSelection b (Const Void) UnpreparedValue) ->
|
|
|
|
QueryDB b (Const Void) (UnpreparedValue b)
|
|
|
|
convertRemoteSourceRelationship
|
|
|
|
columnMapping
|
|
|
|
selectFrom
|
|
|
|
argumentIdColumn
|
|
|
|
argumentIdColumnType
|
2021-09-22 13:43:05 +03:00
|
|
|
(relationshipName, relationship) =
|
2021-09-24 01:56:37 +03:00
|
|
|
QDBMultipleRows simpleSelect
|
|
|
|
where
|
|
|
|
-- TODO: FieldName should have also been a wrapper around NonEmptyText
|
|
|
|
relName = RelName $ mkNonEmptyTextUnsafe $ getFieldNameTxt relationshipName
|
|
|
|
|
|
|
|
relationshipField = case relationship of
|
|
|
|
SourceRelationshipObject s ->
|
|
|
|
AFObjectRelation $ AnnRelationSelectG relName columnMapping s
|
|
|
|
SourceRelationshipArray s ->
|
|
|
|
AFArrayRelation $ ASSimple $ AnnRelationSelectG relName columnMapping s
|
|
|
|
SourceRelationshipArrayAggregate s ->
|
|
|
|
AFArrayRelation $ ASAggregate $ AnnRelationSelectG relName columnMapping s
|
|
|
|
|
|
|
|
argumentIdField =
|
|
|
|
( fromCol @b argumentIdColumn,
|
|
|
|
AFColumn $
|
|
|
|
AnnColumnField
|
|
|
|
{ _acfColumn = argumentIdColumn,
|
|
|
|
_acfType = argumentIdColumnType,
|
|
|
|
_acfAsText = False,
|
|
|
|
_acfOp = Nothing,
|
|
|
|
_acfCaseBoolExpression = Nothing
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
simpleSelect =
|
|
|
|
AnnSelectG
|
|
|
|
{ _asnFields = [argumentIdField, (relationshipName, relationshipField)],
|
|
|
|
_asnFrom = selectFrom,
|
|
|
|
_asnPerm = TablePerm annBoolExpTrue Nothing,
|
|
|
|
_asnArgs = noSelectArgs,
|
|
|
|
_asnStrfyNum = False
|
|
|
|
}
|
2021-09-22 13:43:05 +03:00
|
|
|
|
2021-04-01 23:40:31 +03:00
|
|
|
data DBStepInfo b = DBStepInfo
|
2021-09-24 01:56:37 +03:00
|
|
|
{ dbsiSourceName :: SourceName,
|
|
|
|
dbsiSourceConfig :: SourceConfig b,
|
|
|
|
dbsiPreparedQuery :: Maybe (PreparedQuery b),
|
|
|
|
dbsiAction :: ExecutionMonad b EncJSON
|
2021-04-01 23:40:31 +03:00
|
|
|
}
|
2021-02-12 06:04:09 +03:00
|
|
|
|
2021-04-13 14:10:08 +03:00
|
|
|
-- | The result of an explain query: for a given root field (denoted by its name): the generated SQL
|
|
|
|
-- query, and the detailed explanation obtained from the database (if any). We mostly use this type
|
|
|
|
-- as an intermediary step, and immediately tranform any value we obtain into an equivalent JSON
|
|
|
|
-- representation.
|
2021-09-24 01:56:37 +03:00
|
|
|
data ExplainPlan = ExplainPlan
|
2021-10-29 17:42:07 +03:00
|
|
|
{ _fpField :: !RootFieldAlias,
|
2021-09-24 01:56:37 +03:00
|
|
|
_fpSql :: !(Maybe Text),
|
|
|
|
_fpPlan :: !(Maybe [Text])
|
|
|
|
}
|
|
|
|
deriving (Show, Eq, Generic)
|
2021-04-13 14:10:08 +03:00
|
|
|
|
|
|
|
instance J.ToJSON ExplainPlan where
|
|
|
|
toJSON = J.genericToJSON $ J.aesonPrefix J.camelCase
|
|
|
|
|
2021-02-12 06:04:09 +03:00
|
|
|
-- | One execution step to processing a GraphQL query (e.g. one root field).
|
2021-02-20 16:45:49 +03:00
|
|
|
data ExecutionStep where
|
2021-06-11 06:26:50 +03:00
|
|
|
-- | A query to execute against the database
|
2021-09-24 01:56:37 +03:00
|
|
|
ExecStepDB ::
|
|
|
|
HTTP.ResponseHeaders ->
|
|
|
|
AB.AnyBackend DBStepInfo ->
|
|
|
|
Maybe RemoteJoins ->
|
|
|
|
ExecutionStep
|
2021-06-11 06:26:50 +03:00
|
|
|
-- | Execute an action
|
2021-09-24 01:56:37 +03:00
|
|
|
ExecStepAction ::
|
|
|
|
ActionExecutionPlan ->
|
|
|
|
ActionsInfo ->
|
|
|
|
Maybe RemoteJoins ->
|
|
|
|
ExecutionStep
|
2021-06-11 06:26:50 +03:00
|
|
|
-- | A graphql query to execute against a remote schema
|
2021-09-24 01:56:37 +03:00
|
|
|
ExecStepRemote ::
|
|
|
|
!RemoteSchemaInfo ->
|
2021-10-29 17:42:07 +03:00
|
|
|
!ResultCustomizer ->
|
2021-09-24 01:56:37 +03:00
|
|
|
!GH.GQLReqOutgoing ->
|
|
|
|
ExecutionStep
|
2021-06-11 06:26:50 +03:00
|
|
|
-- | Output a plain JSON object
|
2021-09-24 01:56:37 +03:00
|
|
|
ExecStepRaw ::
|
|
|
|
JO.Value ->
|
|
|
|
ExecutionStep
|
2021-03-13 17:40:50 +03:00
|
|
|
|
2021-02-12 06:04:09 +03:00
|
|
|
-- | The series of steps that need to be executed for a given query. For now, those steps are all
|
|
|
|
-- independent. In the future, when we implement a client-side dataloader and generalized joins,
|
|
|
|
-- this will need to be changed into an annotated tree.
|
2021-10-29 17:42:07 +03:00
|
|
|
type ExecutionPlan = RootFieldMap ExecutionStep
|
2021-07-29 11:29:12 +03:00
|
|
|
|
|
|
|
class (Monad m) => MonadQueryTags m where
|
|
|
|
-- | Creates Query Tags. These are appended to the Generated SQL.
|
|
|
|
-- Helps users to use native database monitoring tools to get some 'application-context'.
|
2021-09-24 01:56:37 +03:00
|
|
|
createQueryTags ::
|
|
|
|
QueryTagsAttributes -> Maybe QueryTagsConfig -> Tagged m QueryTagsComment
|
2021-07-29 11:29:12 +03:00
|
|
|
|
|
|
|
instance (MonadQueryTags m) => MonadQueryTags (ReaderT r m) where
|
2021-09-23 15:37:56 +03:00
|
|
|
createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (ReaderT r m) QueryTagsComment
|
2021-07-29 11:29:12 +03:00
|
|
|
|
|
|
|
instance (MonadQueryTags m) => MonadQueryTags (ExceptT e m) where
|
2021-09-23 15:37:56 +03:00
|
|
|
createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (ExceptT e m) QueryTagsComment
|
2021-07-29 11:29:12 +03:00
|
|
|
|
|
|
|
instance (MonadQueryTags m) => MonadQueryTags (TraceT m) where
|
2021-09-23 15:37:56 +03:00
|
|
|
createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (TraceT m) QueryTagsComment
|
2021-07-29 11:29:12 +03:00
|
|
|
|
|
|
|
instance (MonadQueryTags m) => MonadQueryTags (MetadataStorageT m) where
|
2021-09-23 15:37:56 +03:00
|
|
|
createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (MetadataStorageT m) QueryTagsComment
|
2021-07-29 11:29:12 +03:00
|
|
|
|
2021-09-15 23:45:49 +03:00
|
|
|
instance (MonadQueryTags m) => MonadQueryTags (Q.TxET QErr m) where
|
2021-09-23 15:37:56 +03:00
|
|
|
createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (Q.TxET QErr m) QueryTagsComment
|
2021-07-29 11:29:12 +03:00
|
|
|
|
|
|
|
instance (MonadQueryTags m) => MonadQueryTags (MetadataT m) where
|
2021-09-23 15:37:56 +03:00
|
|
|
createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (MetadataT m) QueryTagsComment
|
2021-07-29 11:29:12 +03:00
|
|
|
|
|
|
|
instance (MonadQueryTags m) => MonadQueryTags (CacheRWT m) where
|
2021-09-23 15:37:56 +03:00
|
|
|
createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (CacheRWT m) QueryTagsComment
|
2021-07-29 11:29:12 +03:00
|
|
|
|
|
|
|
instance (MonadQueryTags m) => MonadQueryTags (RunT m) where
|
2021-09-23 15:37:56 +03:00
|
|
|
createQueryTags qtSourceConfig attr = retag (createQueryTags @m qtSourceConfig attr) :: Tagged (RunT m) QueryTagsComment
|