-
Notifications
You must be signed in to change notification settings - Fork 2.8k
subscriptions can now be explained #2650
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,14 +14,19 @@ module Hasura.GraphQL.Execute.LiveQuery | |
| , initLiveQueriesState | ||
| , dumpLiveQueriesState | ||
|
|
||
| , LiveQueryOpG(..) | ||
|
|
||
| , LiveQueryOp | ||
| , LiveQueryOpPartial | ||
| , getLiveQueryOpPartial | ||
| , LiveQueryId | ||
| , addLiveQuery | ||
| , removeLiveQuery | ||
|
|
||
| , SubsPlan | ||
| , subsOpFromPlan | ||
| , subsOpFromPGAST | ||
|
|
||
| ) where | ||
|
|
||
| import Data.Has | ||
|
|
@@ -30,7 +35,6 @@ import qualified Control.Concurrent.STM as STM | |
| import qualified Data.Aeson as J | ||
| import qualified Data.HashMap.Strict as Map | ||
| import qualified Data.HashSet as Set | ||
| import qualified Data.Text as T | ||
| import qualified Database.PG.Query as Q | ||
| import qualified Database.PG.Query.Connection as Q | ||
| import qualified Language.GraphQL.Draft.Syntax as G | ||
|
|
@@ -48,7 +52,6 @@ import Hasura.GraphQL.Execute.LiveQuery.Types | |
| import Hasura.Prelude | ||
| import Hasura.RQL.DML.Select (asSingleRowJsonResp) | ||
| import Hasura.RQL.Types | ||
|
|
||
| import Hasura.SQL.Types | ||
| import Hasura.SQL.Value | ||
|
|
||
|
|
@@ -95,9 +98,12 @@ initLiveQueriesState (LQOpts mxOpts fallbackOpts) pgExecCtx = do | |
| <*> LQF.initLiveQueriesState fallbackOpts | ||
| return $ LiveQueriesState mxMap fallbackMap pgExecCtx | ||
|
|
||
| data LiveQueryOp | ||
| = LQMultiplexed !LQM.MxOp | ||
| | LQFallback !LQF.FallbackOp | ||
| data LiveQueryOpG f m | ||
| = LQFallback !f | ||
| | LQMultiplexed !m | ||
| deriving (Show, Eq) | ||
|
|
||
| type LiveQueryOp = LiveQueryOpG LQF.FallbackOp LQM.MxOp | ||
|
|
||
| data LiveQueryId | ||
| = LQIMultiplexed !LQM.LiveQueryId | ||
|
|
@@ -161,33 +167,56 @@ type TextEncodedVariables | |
| -- referring correctly to the values from '_subs' temporary table | ||
| -- The variables are at _subs.result_vars.variables and | ||
| -- session variables at _subs.result_vars.user | ||
| toMultiplexedQueryVar | ||
| :: (MonadState GV.AnnPGVarVals m) | ||
| => GR.UnresolvedVal -> m S.SQLExp | ||
| toMultiplexedQueryVar = \case | ||
| GR.UVPG annPGVal -> | ||
| let GR.AnnPGVal varM isNullable colTy colVal = annPGVal | ||
| in case (varM, isNullable) of | ||
| -- we don't check for nullability as | ||
| -- this is only used for reusable plans | ||
| -- the check has to be made before this | ||
| (Just var, _) -> do | ||
| modify $ Map.insert var (colTy, colVal) | ||
| return $ fromResVars (PgTypeSimple colTy) | ||
| [ "variables" | ||
| , G.unName $ G.unVariable var | ||
| ] | ||
| _ -> return $ toTxtValue colTy colVal | ||
| GR.UVSessVar ty sessVar -> | ||
| return $ fromResVars ty [ "user", T.toLower sessVar] | ||
| GR.UVSQL sqlExp -> return sqlExp | ||
| type FallbackOpPartial = (GR.QueryRootFldUnresolved, Set.HashSet G.Variable) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor, mostly unimportant comment, but: I wonder if there’s a good way to encode into the type system that this |
||
| type MultiplexedOpPartial = (GV.VarPGTypes, Q.Query, TextEncodedVariables) | ||
|
|
||
| type LiveQueryOpPartial = LiveQueryOpG FallbackOpPartial MultiplexedOpPartial | ||
|
|
||
| -- | Creates a partial live query operation, used in both | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What exactly is a “partial” live query operation—which parts of a “full” live query does it include and which parts does it lack? |
||
| -- analyze and execution of a live query | ||
| getLiveQueryOpPartial | ||
| :: ( MonadError QErr m | ||
| , MonadIO m | ||
| ) | ||
|
|
||
| -- | to validate arguments | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the syntax Haddock expects for documenting arguments is the |
||
| => PGExecCtx | ||
|
|
||
| -- | variable definitions as seen in the subscription, needed in | ||
| -- checking whether the subscription can be multiplexed or not | ||
| -> [G.VariableDefinition] | ||
|
|
||
| -- | The partially processed live query field | ||
| -> GR.QueryRootFldUnresolved | ||
|
|
||
| -> m LiveQueryOpPartial | ||
| getLiveQueryOpPartial pgExecCtx varDefs astUnresolved = do | ||
| -- collect the variables (with their types) used inside the subscription | ||
| (_, varTypes) <- flip runStateT mempty $ GR.traverseQueryRootFldAST | ||
| collectNonNullableVars astUnresolved | ||
|
|
||
| let nonConfirmingVariables = getNonConfirmingVariables varTypes | ||
|
|
||
| -- Can the subscription be multiplexed? | ||
| -- Only if all variables are non null and can be prepared | ||
| if null nonConfirmingVariables | ||
| then do | ||
| let (mxQuery, annVarVals) = LQM.resolveToMxQuery astUnresolved | ||
| -- We need to ensure that the values provided for variables | ||
| -- are correct according to Postgres. Without this check | ||
| -- an invalid value for a variable for one instance of the | ||
| -- subscription will take down the entire multiplexed query | ||
| txtEncodedVars <- validateAnnVarValsOnPg pgExecCtx annVarVals | ||
| return $ LQMultiplexed (varTypes, mxQuery, txtEncodedVars) | ||
| else | ||
| return $ LQFallback (astUnresolved, nonConfirmingVariables) | ||
| where | ||
| fromResVars ty jPath = | ||
| flip S.SETyAnn (S.mkTypeAnn ty) $ S.SEOpApp (S.SQLOp "#>>") | ||
| [ S.SEQIden $ S.QIden (S.QualIden $ Iden "_subs") | ||
| (Iden "result_vars") | ||
| , S.SEArray $ map S.SELit jPath | ||
| ] | ||
| -- get the variables which don't conifrm to the | ||
| -- 'non-null scalar' rule | ||
| getNonConfirmingVariables usedVariables = | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be “conforming” rather than “confirming”? |
||
| let queryVariables = Set.fromList $ map G._vdVariable varDefs | ||
| confirmingVariables = Map.keysSet usedVariables | ||
| in queryVariables `Set.difference` confirmingVariables | ||
|
|
||
| -- | Creates a live query operation and if possible, a reusable plan | ||
| -- | ||
|
|
@@ -216,32 +245,18 @@ subsOpFromPGAST | |
| subsOpFromPGAST pgExecCtx reqUnparsed varDefs (fldAls, astUnresolved) = do | ||
| userInfo <- asks getter | ||
|
|
||
| -- collect the variables (with their types) used inside the subscription | ||
| (_, varTypes) <- flip runStateT mempty $ GR.traverseQueryRootFldAST | ||
| collectNonNullableVars astUnresolved | ||
| liveQueryOpPartial <- getLiveQueryOpPartial pgExecCtx varDefs astUnresolved | ||
|
|
||
| -- Can the subscription be multiplexed? | ||
| -- Only if all variables are non null and can be prepared | ||
| if Set.fromList (Map.keys varTypes) == allVars | ||
| then mkMultiplexedOp userInfo varTypes | ||
| else mkFallbackOp userInfo | ||
| where | ||
| allVars = Set.fromList $ map G._vdVariable varDefs | ||
| case liveQueryOpPartial of | ||
| LQFallback _ -> mkFallbackOp userInfo | ||
| LQMultiplexed (varTypes, mxQuery, txtEncodedVars) -> | ||
| mkMultiplexedOp userInfo varTypes mxQuery txtEncodedVars | ||
|
|
||
| where | ||
| -- multiplexed subscription | ||
| mkMultiplexedOp userInfo varTypes = do | ||
| (astResolved, annVarVals) <- | ||
| flip runStateT mempty $ GR.traverseQueryRootFldAST | ||
| toMultiplexedQueryVar astUnresolved | ||
| mkMultiplexedOp userInfo varTypes mxQuery txtEncodedVars = do | ||
| let mxOpCtx = LQM.mkMxOpCtx (userRole userInfo) | ||
| (GH._grQuery reqUnparsed) fldAls $ | ||
| GR.toPGQuery astResolved | ||
|
|
||
| -- We need to ensure that the values provided for variables | ||
| -- are correct according to Postgres. Without this check | ||
| -- an invalid value for a variable for one instance of the | ||
| -- subscription will take down the entire multiplexed query | ||
| txtEncodedVars <- validateAnnVarValsOnPg pgExecCtx annVarVals | ||
| (GH._grQuery reqUnparsed) fldAls mxQuery | ||
| let mxOp = (mxOpCtx, userVars userInfo, txtEncodedVars) | ||
| return (LQMultiplexed mxOp, Just $ SubsPlan mxOpCtx varTypes) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you tell me what the
Gstands for in these types? “Grammar”? “General”?