{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}

-- |
-- = Reasonably efficient PostgreSQL live queries
--
-- The module implements /query multiplexing/, which is our implementation strategy for live queries
-- (i.e. GraphQL subscriptions) made against Postgres. Fundamentally, our implementation is built
-- around polling, which is never ideal, but it’s a lot easier to implement than trying to do something
-- event-based. To minimize the resource cost of polling, we use /multiplexing/, which is essentially
-- a two-tier batching strategy.
--
-- == The high-level idea
--
-- The objective is to minimize the number of concurrent polling workers to reduce database load as
-- much as possible. A very naïve strategy would be to group identical queries together so we only have
-- one poller per /unique/ active subscription. That’s a good start, but of course, in practice, most
-- queries differ slightly. However, it happens that they very frequently /only differ in their
-- variables/ (that is, GraphQL query variables and session variables), and in those cases, we try to
-- generated parameterized SQL. This means that the same prepared SQL query can be reused, just with a
-- different set of variables.
--
-- To give a concrete example, consider the following query:
--
-- > subscription vote_count($post_id: Int!) {
-- >   vote_count(where: {post_id: {_eq: $post_id}}) {
-- >     votes
-- >   }
-- > }
--
-- No matter what the client provides for @$post_id@, we will always generate the same SQL:
--
-- > SELECT votes FROM vote_count WHERE post_id = $1
--
-- If multiple clients subscribe to @vote_count@, we can certainly reuse the same prepared query. For
-- example, imagine we had 10 concurrent subscribers, each listening on a distinct @$post_id@:
--
-- > let postIds = [3, 11, 32, 56, 13, 97, 24, 43, 109, 48]
--
-- We could iterate over @postIds@ in Haskell, executing the same prepared query 10 times:
--
-- > for postIds $ \postId ->
-- >   Q.listQE defaultTxErrorHandler preparedQuery (Identity postId) True
--
-- Sadly, that on its own isn’t good enough. The overhead of running each query is large enough that
-- Postgres becomes overwhelmed if we have to serve lots of concurrent subscribers. Therefore, what we
-- want to be able to do is somehow make one query instead of ten.
--
-- === Multiplexing
--
-- This is where multiplexing comes in. By taking advantage of Postgres
-- <https://www.postgresql.org/docs/11/queries-table-expressions.html#QUERIES-LATERAL lateral joins>,
-- we can do the iteration in Postgres rather than in Haskell, allowing us to pay the query overhead
-- just once for all ten subscribers. Essentially, lateral joins add 'map'-like functionality to SQL,
-- so we can run our query once per @$post_id@:
--
-- > SELECT results.votes
-- > FROM unnest($1::integer[]) query_variables (post_id)
-- > LEFT JOIN LATERAL (
-- >   SELECT coalesce(json_agg(votes), '[]')
-- >   FROM vote_count WHERE vote_count.post_id = query_variables.post_id
-- > ) results ON true
--
-- If we generalize this approach just a little bit more, we can apply this transformation to arbitrary
-- queries parameterized over arbitrary session and query variables!
--
-- == Implementation overview
--
-- To support query multiplexing, we maintain a tree of the following types, where @>@ should be read
-- as “contains”:
--
-- @
-- 'SubscriptionsState' > 'Poller' > 'Cohort' > 'Subscriber'
-- @
--
-- Here’s a brief summary of each type’s role:
--
--   * A 'Subscriber' is an actual client with an open websocket connection.
--
--   * A 'Cohort' is a set of 'Subscriber's that are all subscribed to the same query /with the exact
--     same variables/. (By batching these together, we can do better than multiplexing, since we can
--     just query the data once.)
--
--   * A 'Poller' is a worker thread for a single, multiplexed query. It fetches data for a set of
--     'Cohort's that all use the same parameterized query, but have different sets of variables.
--
--   * Finally, the 'SubscriptionsState' is the top-level container that holds all the active 'Poller's.
--
-- Additional details are provided by the documentation for individual bindings.
module Hasura.GraphQL.Execute.Subscription.Plan
  ( CohortId,
    dummyCohortId,
    newCohortId,
    CohortIdArray (..),
    CohortVariablesArray (..),
    CohortVariables,
    _cvCursorVariables,
    mkCohortVariables,
    ValidatedVariables (..),
    mkUnsafeValidateVariables,
    modifyCursorCohortVariables,
    ValidatedQueryVariables,
    ValidatedSyntheticVariables,
    ValidatedCursorVariables,
    SubscriptionQueryPlan (..),
    SubscriptionQueryPlanExplanation (..),
    ParameterizedSubscriptionQueryPlan (..),
    CursorVariableValues (..),
    cvSessionVariables,
    cvCursorVariables,
    cvQueryVariables,
    cvSyntheticVariables,
    unValidatedVariables,
  )
where

import Control.Lens (makeLenses)
import Data.Aeson.Extended qualified as J
import Data.Aeson.TH qualified as J
import Data.HashMap.Strict qualified as Map
import Data.HashSet qualified as Set
import Data.UUID (UUID)
import Data.UUID qualified as UUID
import Data.UUID.V4 qualified as UUID
import Database.PG.Query qualified as Q
import Database.PG.Query.PTI qualified as PTI
import Hasura.Backends.Postgres.SQL.Value
import Hasura.Prelude
import Hasura.RQL.Types.Backend
import Hasura.SQL.Backend
import Hasura.Session
import Language.GraphQL.Draft.Syntax qualified as G
import PostgreSQL.Binary.Encoding qualified as PE

----------------------------------------------------------------------------------------------------
-- Variable validation

-- | When running multiplexed queries, we have to be especially careful about user
-- input, since invalid values will cause the query to fail, causing collateral
-- damage for anyone else multiplexed into the same query.  Therefore, we
-- pre-validate variables against Postgres by executing a no-op query of the shape
--
-- > SELECT 'v1'::t1, 'v2'::t2, ..., 'vn'::tn
--
-- so if any variable values are invalid, the error will be caught early.
newtype ValidatedVariables f = ValidatedVariables {ValidatedVariables f -> f TxtEncodedVal
_unValidatedVariables :: (f TxtEncodedVal)}

deriving instance (Show (f TxtEncodedVal)) => Show (ValidatedVariables f)

deriving instance (Eq (f TxtEncodedVal)) => Eq (ValidatedVariables f)

deriving instance (Hashable (f TxtEncodedVal)) => Hashable (ValidatedVariables f)

deriving instance (J.ToJSON (f TxtEncodedVal)) => J.ToJSON (ValidatedVariables f)

deriving instance (Semigroup (f TxtEncodedVal)) => Semigroup (ValidatedVariables f)

deriving instance (Monoid (f TxtEncodedVal)) => Monoid (ValidatedVariables f)

$(makeLenses 'ValidatedVariables)

type ValidatedQueryVariables = ValidatedVariables (Map.HashMap G.Name)

type ValidatedSyntheticVariables = ValidatedVariables []

type ValidatedCursorVariables = ValidatedVariables (Map.HashMap G.Name)

mkUnsafeValidateVariables :: f TxtEncodedVal -> ValidatedVariables f
mkUnsafeValidateVariables :: f TxtEncodedVal -> ValidatedVariables f
mkUnsafeValidateVariables = f TxtEncodedVal -> ValidatedVariables f
forall (f :: * -> *). f TxtEncodedVal -> ValidatedVariables f
ValidatedVariables

----------------------------------------------------------------------------------------------------
-- Cohort

newtype CohortId = CohortId {CohortId -> UUID
unCohortId :: UUID}
  deriving (Int -> CohortId -> ShowS
[CohortId] -> ShowS
CohortId -> String
(Int -> CohortId -> ShowS)
-> (CohortId -> String) -> ([CohortId] -> ShowS) -> Show CohortId
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CohortId] -> ShowS
$cshowList :: [CohortId] -> ShowS
show :: CohortId -> String
$cshow :: CohortId -> String
showsPrec :: Int -> CohortId -> ShowS
$cshowsPrec :: Int -> CohortId -> ShowS
Show, CohortId -> CohortId -> Bool
(CohortId -> CohortId -> Bool)
-> (CohortId -> CohortId -> Bool) -> Eq CohortId
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CohortId -> CohortId -> Bool
$c/= :: CohortId -> CohortId -> Bool
== :: CohortId -> CohortId -> Bool
$c== :: CohortId -> CohortId -> Bool
Eq, Int -> CohortId -> Int
CohortId -> Int
(Int -> CohortId -> Int) -> (CohortId -> Int) -> Hashable CohortId
forall a. (Int -> a -> Int) -> (a -> Int) -> Hashable a
hash :: CohortId -> Int
$chash :: CohortId -> Int
hashWithSalt :: Int -> CohortId -> Int
$chashWithSalt :: Int -> CohortId -> Int
Hashable, [CohortId] -> Value
[CohortId] -> Encoding
CohortId -> Value
CohortId -> Encoding
(CohortId -> Value)
-> (CohortId -> Encoding)
-> ([CohortId] -> Value)
-> ([CohortId] -> Encoding)
-> ToJSON CohortId
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [CohortId] -> Encoding
$ctoEncodingList :: [CohortId] -> Encoding
toJSONList :: [CohortId] -> Value
$ctoJSONList :: [CohortId] -> Value
toEncoding :: CohortId -> Encoding
$ctoEncoding :: CohortId -> Encoding
toJSON :: CohortId -> Value
$ctoJSON :: CohortId -> Value
J.ToJSON, Value -> Parser [CohortId]
Value -> Parser CohortId
(Value -> Parser CohortId)
-> (Value -> Parser [CohortId]) -> FromJSON CohortId
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [CohortId]
$cparseJSONList :: Value -> Parser [CohortId]
parseJSON :: Value -> Parser CohortId
$cparseJSON :: Value -> Parser CohortId
J.FromJSON, Maybe ByteString -> Either Text CohortId
(Maybe ByteString -> Either Text CohortId) -> FromCol CohortId
forall a. (Maybe ByteString -> Either Text a) -> FromCol a
fromCol :: Maybe ByteString -> Either Text CohortId
$cfromCol :: Maybe ByteString -> Either Text CohortId
Q.FromCol)

newCohortId :: (MonadIO m) => m CohortId
newCohortId :: m CohortId
newCohortId = UUID -> CohortId
CohortId (UUID -> CohortId) -> m UUID -> m CohortId
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO UUID -> m UUID
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO UUID
UUID.nextRandom

dummyCohortId :: CohortId
dummyCohortId :: CohortId
dummyCohortId = UUID -> CohortId
CohortId UUID
UUID.nil

data CohortVariables = CohortVariables
  { CohortVariables -> SessionVariables
_cvSessionVariables :: !SessionVariables,
    CohortVariables -> ValidatedQueryVariables
_cvQueryVariables :: !ValidatedQueryVariables,
    -- | To allow more queries to be multiplexed together, we introduce “synthetic”
    -- variables for /all/ SQL literals in a query, even if they don’t correspond to
    -- any GraphQL variable. For example, the query
    --
    -- > subscription latest_tracks($condition: tracks_bool_exp!) {
    -- >   tracks(where: $tracks_bool_exp) {
    -- >     id
    -- >     title
    -- >   }
    -- > }
    --
    -- might be executed with similar values for @$condition@, such as @{"album_id":
    -- {"_eq": "1"}}@ and @{"album_id": {"_eq": "2"}}@.
    --
    -- Normally, we wouldn’t bother parameterizing over the @1@ and @2@ literals in the
    -- resulting query because we can’t cache that query plan (since different
    -- @$condition@ values could lead to different SQL). However, for live queries, we
    -- can still take advantage of the similarity between the two queries by
    -- multiplexing them together, so we replace them with references to synthetic
    -- variables.
    CohortVariables -> ValidatedSyntheticVariables
_cvSyntheticVariables :: !ValidatedSyntheticVariables,
    -- | Cursor variables contain the latest value of the cursor.
    --   The value of the cursor variables are updated after every poll.
    --   If the value has been changed - see [Streaming subscription polling].
    --   Cursor variables are only used in the case of streaming subscriptions,
    --   for live queries it will be empty.
    CohortVariables -> ValidatedQueryVariables
_cvCursorVariables :: !ValidatedCursorVariables
  }
  deriving (Int -> CohortVariables -> ShowS
[CohortVariables] -> ShowS
CohortVariables -> String
(Int -> CohortVariables -> ShowS)
-> (CohortVariables -> String)
-> ([CohortVariables] -> ShowS)
-> Show CohortVariables
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CohortVariables] -> ShowS
$cshowList :: [CohortVariables] -> ShowS
show :: CohortVariables -> String
$cshow :: CohortVariables -> String
showsPrec :: Int -> CohortVariables -> ShowS
$cshowsPrec :: Int -> CohortVariables -> ShowS
Show, CohortVariables -> CohortVariables -> Bool
(CohortVariables -> CohortVariables -> Bool)
-> (CohortVariables -> CohortVariables -> Bool)
-> Eq CohortVariables
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CohortVariables -> CohortVariables -> Bool
$c/= :: CohortVariables -> CohortVariables -> Bool
== :: CohortVariables -> CohortVariables -> Bool
$c== :: CohortVariables -> CohortVariables -> Bool
Eq, (forall x. CohortVariables -> Rep CohortVariables x)
-> (forall x. Rep CohortVariables x -> CohortVariables)
-> Generic CohortVariables
forall x. Rep CohortVariables x -> CohortVariables
forall x. CohortVariables -> Rep CohortVariables x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep CohortVariables x -> CohortVariables
$cfrom :: forall x. CohortVariables -> Rep CohortVariables x
Generic)

instance Hashable CohortVariables

$(makeLenses 'CohortVariables)

modifyCursorCohortVariables ::
  ValidatedCursorVariables ->
  CohortVariables ->
  CohortVariables
modifyCursorCohortVariables :: ValidatedQueryVariables -> CohortVariables -> CohortVariables
modifyCursorCohortVariables ValidatedQueryVariables
validatedCursorVariables CohortVariables
cohortVariables =
  CohortVariables
cohortVariables {_cvCursorVariables :: ValidatedQueryVariables
_cvCursorVariables = ValidatedQueryVariables
validatedCursorVariables}

-- | Builds a cohort's variables by only using the session variables that
-- are required for the subscription
mkCohortVariables ::
  Set.HashSet SessionVariable ->
  SessionVariables ->
  ValidatedQueryVariables ->
  ValidatedSyntheticVariables ->
  ValidatedCursorVariables ->
  CohortVariables
mkCohortVariables :: HashSet SessionVariable
-> SessionVariables
-> ValidatedQueryVariables
-> ValidatedSyntheticVariables
-> ValidatedQueryVariables
-> CohortVariables
mkCohortVariables HashSet SessionVariable
requiredSessionVariables SessionVariables
sessionVariableValues =
  SessionVariables
-> ValidatedQueryVariables
-> ValidatedSyntheticVariables
-> ValidatedQueryVariables
-> CohortVariables
CohortVariables (SessionVariables
 -> ValidatedQueryVariables
 -> ValidatedSyntheticVariables
 -> ValidatedQueryVariables
 -> CohortVariables)
-> SessionVariables
-> ValidatedQueryVariables
-> ValidatedSyntheticVariables
-> ValidatedQueryVariables
-> CohortVariables
forall a b. (a -> b) -> a -> b
$
    (SessionVariable -> Text -> Bool)
-> SessionVariables -> SessionVariables
filterSessionVariables
      (\SessionVariable
k Text
_ -> SessionVariable -> HashSet SessionVariable -> Bool
forall a. (Eq a, Hashable a) => a -> HashSet a -> Bool
Set.member SessionVariable
k HashSet SessionVariable
requiredSessionVariables)
      SessionVariables
sessionVariableValues

instance J.ToJSON CohortVariables where
  toJSON :: CohortVariables -> Value
toJSON (CohortVariables SessionVariables
sessionVars ValidatedQueryVariables
queryVars ValidatedSyntheticVariables
syntheticVars ValidatedQueryVariables
cursorVars) =
    [Pair] -> Value
J.object
      [ Key
"session" Key -> SessionVariables -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
J..= SessionVariables
sessionVars,
        Key
"query" Key -> ValidatedQueryVariables -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
J..= ValidatedQueryVariables
queryVars,
        Key
"synthetic" Key -> ValidatedSyntheticVariables -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
J..= ValidatedSyntheticVariables
syntheticVars,
        Key
"cursor" Key -> ValidatedQueryVariables -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
J..= ValidatedQueryVariables
cursorVars
      ]

-- These types exist only to use the Postgres array encoding.
newtype CohortIdArray = CohortIdArray {CohortIdArray -> [CohortId]
unCohortIdArray :: [CohortId]}
  deriving (Int -> CohortIdArray -> ShowS
[CohortIdArray] -> ShowS
CohortIdArray -> String
(Int -> CohortIdArray -> ShowS)
-> (CohortIdArray -> String)
-> ([CohortIdArray] -> ShowS)
-> Show CohortIdArray
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CohortIdArray] -> ShowS
$cshowList :: [CohortIdArray] -> ShowS
show :: CohortIdArray -> String
$cshow :: CohortIdArray -> String
showsPrec :: Int -> CohortIdArray -> ShowS
$cshowsPrec :: Int -> CohortIdArray -> ShowS
Show, CohortIdArray -> CohortIdArray -> Bool
(CohortIdArray -> CohortIdArray -> Bool)
-> (CohortIdArray -> CohortIdArray -> Bool) -> Eq CohortIdArray
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CohortIdArray -> CohortIdArray -> Bool
$c/= :: CohortIdArray -> CohortIdArray -> Bool
== :: CohortIdArray -> CohortIdArray -> Bool
$c== :: CohortIdArray -> CohortIdArray -> Bool
Eq)

instance Q.ToPrepArg CohortIdArray where
  toPrepVal :: CohortIdArray -> PrepArg
toPrepVal (CohortIdArray [CohortId]
l) = Oid -> ([UUID] -> Encoding) -> [UUID] -> PrepArg
forall a. Oid -> (a -> Encoding) -> a -> PrepArg
Q.toPrepValHelper Oid
PTI.unknown [UUID] -> Encoding
encoder ([UUID] -> PrepArg) -> [UUID] -> PrepArg
forall a b. (a -> b) -> a -> b
$ (CohortId -> UUID) -> [CohortId] -> [UUID]
forall a b. (a -> b) -> [a] -> [b]
map CohortId -> UUID
unCohortId [CohortId]
l
    where
      encoder :: [UUID] -> Encoding
encoder = Word32 -> Array -> Encoding
PE.array Word32
2950 (Array -> Encoding) -> ([UUID] -> Array) -> [UUID] -> Encoding
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall b. (b -> UUID -> b) -> b -> [UUID] -> b)
-> (UUID -> Array) -> [UUID] -> Array
forall a c.
(forall b. (b -> a -> b) -> b -> c -> b)
-> (a -> Array) -> c -> Array
PE.dimensionArray forall b. (b -> UUID -> b) -> b -> [UUID] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Encoding -> Array
PE.encodingArray (Encoding -> Array) -> (UUID -> Encoding) -> UUID -> Array
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UUID -> Encoding
PE.uuid)

newtype CohortVariablesArray = CohortVariablesArray {CohortVariablesArray -> [CohortVariables]
unCohortVariablesArray :: [CohortVariables]}
  deriving (Int -> CohortVariablesArray -> ShowS
[CohortVariablesArray] -> ShowS
CohortVariablesArray -> String
(Int -> CohortVariablesArray -> ShowS)
-> (CohortVariablesArray -> String)
-> ([CohortVariablesArray] -> ShowS)
-> Show CohortVariablesArray
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CohortVariablesArray] -> ShowS
$cshowList :: [CohortVariablesArray] -> ShowS
show :: CohortVariablesArray -> String
$cshow :: CohortVariablesArray -> String
showsPrec :: Int -> CohortVariablesArray -> ShowS
$cshowsPrec :: Int -> CohortVariablesArray -> ShowS
Show, CohortVariablesArray -> CohortVariablesArray -> Bool
(CohortVariablesArray -> CohortVariablesArray -> Bool)
-> (CohortVariablesArray -> CohortVariablesArray -> Bool)
-> Eq CohortVariablesArray
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CohortVariablesArray -> CohortVariablesArray -> Bool
$c/= :: CohortVariablesArray -> CohortVariablesArray -> Bool
== :: CohortVariablesArray -> CohortVariablesArray -> Bool
$c== :: CohortVariablesArray -> CohortVariablesArray -> Bool
Eq)

instance Q.ToPrepArg CohortVariablesArray where
  toPrepVal :: CohortVariablesArray -> PrepArg
toPrepVal (CohortVariablesArray [CohortVariables]
l) =
    Oid -> ([Value] -> Encoding) -> [Value] -> PrepArg
forall a. Oid -> (a -> Encoding) -> a -> PrepArg
Q.toPrepValHelper Oid
PTI.unknown [Value] -> Encoding
encoder ((CohortVariables -> Value) -> [CohortVariables] -> [Value]
forall a b. (a -> b) -> [a] -> [b]
map CohortVariables -> Value
forall a. ToJSON a => a -> Value
J.toJSON [CohortVariables]
l)
    where
      encoder :: [Value] -> Encoding
encoder = Word32 -> Array -> Encoding
PE.array Word32
114 (Array -> Encoding) -> ([Value] -> Array) -> [Value] -> Encoding
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall b. (b -> Value -> b) -> b -> [Value] -> b)
-> (Value -> Array) -> [Value] -> Array
forall a c.
(forall b. (b -> a -> b) -> b -> c -> b)
-> (a -> Array) -> c -> Array
PE.dimensionArray forall b. (b -> Value -> b) -> b -> [Value] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Encoding -> Array
PE.encodingArray (Encoding -> Array) -> (Value -> Encoding) -> Value -> Array
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Value -> Encoding
PE.json_ast)

----------------------------------------------------------------------------------------------------
-- Live query plans

-- | A self-contained, ready-to-execute subscription plan. Contains enough information
-- to find an existing poller that this can be added to /or/ to create a new poller
-- if necessary.
data SubscriptionQueryPlan (b :: BackendType) q = SubscriptionQueryPlan
  { SubscriptionQueryPlan b q -> ParameterizedSubscriptionQueryPlan b q
_sqpParameterizedPlan :: !(ParameterizedSubscriptionQueryPlan b q),
    SubscriptionQueryPlan b q -> SourceConfig b
_sqpSourceConfig :: !(SourceConfig b),
    SubscriptionQueryPlan b q -> CohortVariables
_sqpVariables :: !CohortVariables,
    -- | We need to know if the source has a namespace so that we can wrap it around
    -- the response from the DB
    SubscriptionQueryPlan b q -> Maybe Name
_sqpNamespace :: !(Maybe G.Name)
  }

data ParameterizedSubscriptionQueryPlan (b :: BackendType) q = ParameterizedSubscriptionQueryPlan
  { ParameterizedSubscriptionQueryPlan b q -> RoleName
_plqpRole :: !RoleName,
    ParameterizedSubscriptionQueryPlan b q -> q
_plqpQuery :: !q
  }
  deriving (Int -> ParameterizedSubscriptionQueryPlan b q -> ShowS
[ParameterizedSubscriptionQueryPlan b q] -> ShowS
ParameterizedSubscriptionQueryPlan b q -> String
(Int -> ParameterizedSubscriptionQueryPlan b q -> ShowS)
-> (ParameterizedSubscriptionQueryPlan b q -> String)
-> ([ParameterizedSubscriptionQueryPlan b q] -> ShowS)
-> Show (ParameterizedSubscriptionQueryPlan b q)
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall (b :: BackendType) q.
Show q =>
Int -> ParameterizedSubscriptionQueryPlan b q -> ShowS
forall (b :: BackendType) q.
Show q =>
[ParameterizedSubscriptionQueryPlan b q] -> ShowS
forall (b :: BackendType) q.
Show q =>
ParameterizedSubscriptionQueryPlan b q -> String
showList :: [ParameterizedSubscriptionQueryPlan b q] -> ShowS
$cshowList :: forall (b :: BackendType) q.
Show q =>
[ParameterizedSubscriptionQueryPlan b q] -> ShowS
show :: ParameterizedSubscriptionQueryPlan b q -> String
$cshow :: forall (b :: BackendType) q.
Show q =>
ParameterizedSubscriptionQueryPlan b q -> String
showsPrec :: Int -> ParameterizedSubscriptionQueryPlan b q -> ShowS
$cshowsPrec :: forall (b :: BackendType) q.
Show q =>
Int -> ParameterizedSubscriptionQueryPlan b q -> ShowS
Show)

$(J.deriveToJSON hasuraJSON ''ParameterizedSubscriptionQueryPlan)

data SubscriptionQueryPlanExplanation = SubscriptionQueryPlanExplanation
  { SubscriptionQueryPlanExplanation -> Text
_sqpeSql :: !Text,
    SubscriptionQueryPlanExplanation -> [Text]
_sqpePlan :: ![Text],
    SubscriptionQueryPlanExplanation -> CohortVariables
_sqpeVariables :: !CohortVariables
  }
  deriving (Int -> SubscriptionQueryPlanExplanation -> ShowS
[SubscriptionQueryPlanExplanation] -> ShowS
SubscriptionQueryPlanExplanation -> String
(Int -> SubscriptionQueryPlanExplanation -> ShowS)
-> (SubscriptionQueryPlanExplanation -> String)
-> ([SubscriptionQueryPlanExplanation] -> ShowS)
-> Show SubscriptionQueryPlanExplanation
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SubscriptionQueryPlanExplanation] -> ShowS
$cshowList :: [SubscriptionQueryPlanExplanation] -> ShowS
show :: SubscriptionQueryPlanExplanation -> String
$cshow :: SubscriptionQueryPlanExplanation -> String
showsPrec :: Int -> SubscriptionQueryPlanExplanation -> ShowS
$cshowsPrec :: Int -> SubscriptionQueryPlanExplanation -> ShowS
Show)

$(J.deriveToJSON hasuraJSON ''SubscriptionQueryPlanExplanation)

--------------------------------------------------------------------------
--- Streaming Subscriptions

newtype CursorVariableValues = CursorVariableValues (HashMap G.Name TxtEncodedVal)
  deriving (Value -> Parser [CursorVariableValues]
Value -> Parser CursorVariableValues
(Value -> Parser CursorVariableValues)
-> (Value -> Parser [CursorVariableValues])
-> FromJSON CursorVariableValues
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [CursorVariableValues]
$cparseJSONList :: Value -> Parser [CursorVariableValues]
parseJSON :: Value -> Parser CursorVariableValues
$cparseJSON :: Value -> Parser CursorVariableValues
J.FromJSON, [CursorVariableValues] -> Value
[CursorVariableValues] -> Encoding
CursorVariableValues -> Value
CursorVariableValues -> Encoding
(CursorVariableValues -> Value)
-> (CursorVariableValues -> Encoding)
-> ([CursorVariableValues] -> Value)
-> ([CursorVariableValues] -> Encoding)
-> ToJSON CursorVariableValues
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [CursorVariableValues] -> Encoding
$ctoEncodingList :: [CursorVariableValues] -> Encoding
toJSONList :: [CursorVariableValues] -> Value
$ctoJSONList :: [CursorVariableValues] -> Value
toEncoding :: CursorVariableValues -> Encoding
$ctoEncoding :: CursorVariableValues -> Encoding
toJSON :: CursorVariableValues -> Value
$ctoJSON :: CursorVariableValues -> Value
J.ToJSON, CursorVariableValues -> CursorVariableValues -> Bool
(CursorVariableValues -> CursorVariableValues -> Bool)
-> (CursorVariableValues -> CursorVariableValues -> Bool)
-> Eq CursorVariableValues
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CursorVariableValues -> CursorVariableValues -> Bool
$c/= :: CursorVariableValues -> CursorVariableValues -> Bool
== :: CursorVariableValues -> CursorVariableValues -> Bool
$c== :: CursorVariableValues -> CursorVariableValues -> Bool
Eq, Int -> CursorVariableValues -> ShowS
[CursorVariableValues] -> ShowS
CursorVariableValues -> String
(Int -> CursorVariableValues -> ShowS)
-> (CursorVariableValues -> String)
-> ([CursorVariableValues] -> ShowS)
-> Show CursorVariableValues
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CursorVariableValues] -> ShowS
$cshowList :: [CursorVariableValues] -> ShowS
show :: CursorVariableValues -> String
$cshow :: CursorVariableValues -> String
showsPrec :: Int -> CursorVariableValues -> ShowS
$cshowsPrec :: Int -> CursorVariableValues -> ShowS
Show)