-- | Planning T-SQL queries and subscriptions.
module Hasura.Backends.BigQuery.Plan
  ( planNoPlan,
  )
where

import Control.Monad.Validate
import Data.Aeson.Text
import Data.Text.Extended
import Data.Text.Lazy qualified as LT
import Hasura.Backends.BigQuery.FromIr as BigQuery
import Hasura.Backends.BigQuery.Types
import Hasura.Base.Error qualified as E
import Hasura.Prelude
import Hasura.RQL.IR
import Hasura.RQL.Types.Column qualified as RQL
import Hasura.SQL.Backend
import Hasura.SQL.Types
import Hasura.Session

--------------------------------------------------------------------------------
-- Top-level planner

planNoPlan ::
  MonadError E.QErr m =>
  FromIrConfig ->
  UserInfo ->
  QueryDB 'BigQuery Void (UnpreparedValue 'BigQuery) ->
  m Select
planNoPlan :: FromIrConfig
-> UserInfo
-> QueryDB 'BigQuery Void (UnpreparedValue 'BigQuery)
-> m Select
planNoPlan FromIrConfig
fromIrConfig UserInfo
userInfo QueryDB 'BigQuery Void (UnpreparedValue 'BigQuery)
queryDB = do
  QueryDB 'BigQuery Void Expression
rootField <- (UnpreparedValue 'BigQuery -> m Expression)
-> QueryDB 'BigQuery Void (UnpreparedValue 'BigQuery)
-> m (QueryDB 'BigQuery Void Expression)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse (SessionVariables -> UnpreparedValue 'BigQuery -> m Expression
forall (m :: * -> *).
MonadError QErr m =>
SessionVariables -> UnpreparedValue 'BigQuery -> m Expression
prepareValueNoPlan (UserInfo -> SessionVariables
_uiSession UserInfo
userInfo)) QueryDB 'BigQuery Void (UnpreparedValue 'BigQuery)
queryDB
  Validate (NonEmpty Error) Select -> Either (NonEmpty Error) Select
forall e a. Validate e a -> Either e a
runValidate (FromIrConfig -> FromIr Select -> Validate (NonEmpty Error) Select
forall a. FromIrConfig -> FromIr a -> Validate (NonEmpty Error) a
BigQuery.runFromIr FromIrConfig
fromIrConfig (QueryDB 'BigQuery Void Expression -> FromIr Select
BigQuery.fromRootField QueryDB 'BigQuery Void Expression
rootField))
    Either (NonEmpty Error) Select
-> (NonEmpty Error -> m Select) -> m Select
forall (m :: * -> *) e a.
Applicative m =>
Either e a -> (e -> m a) -> m a
`onLeft` (Code -> Text -> m Select
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
E.throw400 Code
E.NotSupported (Text -> m Select)
-> (NonEmpty Error -> Text) -> NonEmpty Error -> m Select
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NonEmpty Error -> Text
forall a. Show a => a -> Text
tshow :: NonEmpty Error -> Text))

--------------------------------------------------------------------------------
-- Resolving values

-- | Prepare a value without any query planning; we just execute the
-- query with the values embedded.
prepareValueNoPlan ::
  (MonadError E.QErr m) =>
  SessionVariables ->
  UnpreparedValue 'BigQuery ->
  m Expression
prepareValueNoPlan :: SessionVariables -> UnpreparedValue 'BigQuery -> m Expression
prepareValueNoPlan SessionVariables
sessionVariables =
  \case
    UVLiteral SQLExpression 'BigQuery
x -> Expression -> m Expression
forall (f :: * -> *) a. Applicative f => a -> f a
pure SQLExpression 'BigQuery
Expression
x
    UnpreparedValue 'BigQuery
UVSession -> Expression -> m Expression
forall (f :: * -> *) a. Applicative f => a -> f a
pure Expression
globalSessionExpression
    -- To be honest, I'm not sure if it's indeed the JSON_VALUE operator we need here...
    UVSessionVar SessionVarType 'BigQuery
typ SessionVariable
text ->
      case SessionVarType 'BigQuery
typ of
        CollectableTypeScalar ScalarType 'BigQuery
scalarType ->
          Expression -> m Expression
forall (f :: * -> *) a. Applicative f => a -> f a
pure
            ( Expression -> ScalarType -> Expression
CastExpression
                ( Expression -> JsonPath -> Expression
JsonValueExpression
                    Expression
globalSessionExpression
                    (JsonPath -> Text -> JsonPath
FieldPath JsonPath
RootPath (SessionVariable -> Text
forall a. ToTxt a => a -> Text
toTxt SessionVariable
text))
                )
                ScalarType 'BigQuery
ScalarType
scalarType
            )
        CollectableTypeArray {} ->
          QErr -> m Expression
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (QErr -> m Expression) -> QErr -> m Expression
forall a b. (a -> b) -> a -> b
$ Text -> QErr
E.internalError Text
"Cannot currently prepare array types in BigQuery."
    UVParameter Maybe VariableInfo
_ RQL.ColumnValue {ScalarValue 'BigQuery
ColumnType 'BigQuery
cvValue :: forall (b :: BackendType). ColumnValue b -> ScalarValue b
cvType :: forall (b :: BackendType). ColumnValue b -> ColumnType b
cvValue :: ScalarValue 'BigQuery
cvType :: ColumnType 'BigQuery
..} -> Expression -> m Expression
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Value -> Expression
ValueExpression ScalarValue 'BigQuery
Value
cvValue)
  where
    globalSessionExpression :: Expression
globalSessionExpression =
      Value -> Expression
ValueExpression
        (Text -> Value
StringValue (Text -> Text
LT.toStrict (SessionVariables -> Text
forall a. ToJSON a => a -> Text
encodeToLazyText SessionVariables
sessionVariables)))