2021-04-12 13:18:29 +03:00
|
|
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
|
|
|
|
|
|
module Hasura.Backends.BigQuery.Instances.Execute () where
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
import Data.Aeson qualified as Aeson
|
2021-11-24 19:21:59 +03:00
|
|
|
import Data.Aeson.Text qualified as Aeson
|
|
|
|
import Data.HashMap.Strict qualified as Map
|
2021-09-24 01:56:37 +03:00
|
|
|
import Data.HashMap.Strict.InsOrd qualified as OMap
|
|
|
|
import Data.Text qualified as T
|
|
|
|
import Data.Text.Lazy qualified as LT
|
|
|
|
import Data.Text.Lazy.Builder qualified as LT
|
|
|
|
import Data.Vector qualified as V
|
2022-03-30 16:53:14 +03:00
|
|
|
import Hasura.Backends.BigQuery.Execute (executeProblemMessage)
|
2021-09-24 01:56:37 +03:00
|
|
|
import Hasura.Backends.BigQuery.Execute qualified as DataLoader
|
|
|
|
import Hasura.Backends.BigQuery.FromIr qualified as BigQuery
|
|
|
|
import Hasura.Backends.BigQuery.Plan
|
|
|
|
import Hasura.Backends.BigQuery.ToQuery qualified as ToQuery
|
|
|
|
import Hasura.Backends.BigQuery.Types qualified as BigQuery
|
|
|
|
import Hasura.Base.Error
|
|
|
|
import Hasura.Base.Error qualified as E
|
|
|
|
import Hasura.EncJSON
|
|
|
|
import Hasura.GraphQL.Execute.Backend
|
2021-10-29 17:42:07 +03:00
|
|
|
import Hasura.GraphQL.Namespace (RootFieldAlias)
|
2021-09-24 01:56:37 +03:00
|
|
|
import Hasura.GraphQL.Parser
|
|
|
|
import Hasura.Prelude
|
2021-11-24 19:21:59 +03:00
|
|
|
import Hasura.QueryTags
|
|
|
|
( emptyQueryTagsComment,
|
|
|
|
)
|
2021-09-24 01:56:37 +03:00
|
|
|
import Hasura.RQL.IR
|
2021-11-24 19:21:59 +03:00
|
|
|
import Hasura.RQL.IR.Select qualified as IR
|
2022-04-27 16:57:28 +03:00
|
|
|
import Hasura.RQL.Types.Backend
|
|
|
|
import Hasura.RQL.Types.Column
|
|
|
|
import Hasura.RQL.Types.Common
|
2021-09-24 01:56:37 +03:00
|
|
|
import Hasura.SQL.AnyBackend qualified as AB
|
2022-04-27 16:57:28 +03:00
|
|
|
import Hasura.SQL.Backend
|
2021-09-24 01:56:37 +03:00
|
|
|
import Hasura.Session
|
|
|
|
import Hasura.Tracing qualified as Tracing
|
2021-05-05 19:20:04 +03:00
|
|
|
|
2021-04-12 13:18:29 +03:00
|
|
|
instance BackendExecute 'BigQuery where
|
2021-09-24 01:56:37 +03:00
|
|
|
type PreparedQuery 'BigQuery = Text
|
2021-04-12 13:18:29 +03:00
|
|
|
type MultiplexedQuery 'BigQuery = Void
|
2021-09-24 01:56:37 +03:00
|
|
|
type ExecutionMonad 'BigQuery = Tracing.TraceT (ExceptT QErr IO)
|
2021-04-12 13:18:29 +03:00
|
|
|
|
2021-05-05 19:20:04 +03:00
|
|
|
mkDBQueryPlan = bqDBQueryPlan
|
|
|
|
mkDBMutationPlan = bqDBMutationPlan
|
2022-04-07 17:41:43 +03:00
|
|
|
mkLiveQuerySubscriptionPlan _ _ _ _ _ =
|
|
|
|
throw500 "Cannot currently perform subscriptions on BigQuery sources."
|
|
|
|
mkDBStreamingSubscriptionPlan _ _ _ _ =
|
2021-09-22 13:43:05 +03:00
|
|
|
throw500 "Cannot currently perform subscriptions on BigQuery sources."
|
2021-05-05 19:20:04 +03:00
|
|
|
mkDBQueryExplain = bqDBQueryExplain
|
2022-03-21 13:39:49 +03:00
|
|
|
mkSubscriptionExplain _ =
|
2021-09-22 13:43:05 +03:00
|
|
|
throw500 "Cannot currently retrieve query execution plans on BigQuery sources."
|
|
|
|
|
|
|
|
-- NOTE: Currently unimplemented!.
|
|
|
|
--
|
|
|
|
-- This function is just a stub for future implementation; for now it just
|
|
|
|
-- throws a 500 error.
|
|
|
|
mkDBRemoteRelationshipPlan =
|
|
|
|
bqDBRemoteRelationshipPlan
|
2021-05-05 19:20:04 +03:00
|
|
|
|
2021-04-12 13:18:29 +03:00
|
|
|
-- query
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
bqDBQueryPlan ::
|
|
|
|
forall m.
|
|
|
|
( MonadError E.QErr m
|
|
|
|
) =>
|
|
|
|
UserInfo ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig 'BigQuery ->
|
2021-12-07 16:12:02 +03:00
|
|
|
QueryDB 'BigQuery Void (UnpreparedValue 'BigQuery) ->
|
2021-09-24 01:56:37 +03:00
|
|
|
m (DBStepInfo 'BigQuery)
|
2021-09-23 15:37:56 +03:00
|
|
|
bqDBQueryPlan userInfo sourceName sourceConfig qrf = do
|
2021-07-29 11:29:12 +03:00
|
|
|
-- TODO (naveen): Append query tags to the query
|
2021-06-25 16:35:39 +03:00
|
|
|
select <- planNoPlan (BigQuery.bigQuerySourceConfigToFromIrConfig sourceConfig) userInfo qrf
|
2021-04-12 13:18:29 +03:00
|
|
|
let action = do
|
|
|
|
result <-
|
|
|
|
DataLoader.runExecute
|
|
|
|
sourceConfig
|
2021-06-28 16:29:48 +03:00
|
|
|
(DataLoader.executeSelect select)
|
2021-04-12 13:18:29 +03:00
|
|
|
case result of
|
2022-03-30 16:53:14 +03:00
|
|
|
Left err -> throw500WithDetail (executeProblemMessage err) $ Aeson.toJSON err
|
2021-06-15 11:58:21 +03:00
|
|
|
Right recordSet -> pure $! recordSetToEncJSON (BigQuery.selectCardinality select) recordSet
|
2021-06-28 16:29:48 +03:00
|
|
|
pure $ DBStepInfo @'BigQuery sourceName sourceConfig (Just (selectSQLTextForExplain select)) action
|
2021-04-12 13:18:29 +03:00
|
|
|
|
|
|
|
-- | Convert the dataloader's 'RecordSet' type to JSON.
|
2021-06-15 11:58:21 +03:00
|
|
|
recordSetToEncJSON :: BigQuery.Cardinality -> DataLoader.RecordSet -> EncJSON
|
|
|
|
recordSetToEncJSON cardinality DataLoader.RecordSet {rows} =
|
|
|
|
case cardinality of
|
|
|
|
BigQuery.One
|
|
|
|
| Just row <- rows V.!? 0 -> encJFromRecord row
|
|
|
|
| otherwise -> encJFromList (toList (fmap encJFromRecord rows))
|
|
|
|
BigQuery.Many -> encJFromList (toList (fmap encJFromRecord rows))
|
2021-04-12 13:18:29 +03:00
|
|
|
where
|
|
|
|
encJFromRecord =
|
|
|
|
encJFromInsOrdHashMap . fmap encJFromOutputValue . OMap.mapKeys coerce
|
|
|
|
encJFromOutputValue outputValue =
|
|
|
|
case outputValue of
|
|
|
|
DataLoader.NullOutputValue -> encJFromJValue Aeson.Null
|
|
|
|
DataLoader.DecimalOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.BigDecimalOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.FloatOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.TextOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.BytesOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.DateOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.TimestampOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.TimeOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.DatetimeOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.GeographyOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.BoolOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.IntegerOutputValue !i -> encJFromJValue i
|
|
|
|
DataLoader.ArrayOutputValue !vector ->
|
|
|
|
encJFromList (toList (fmap encJFromOutputValue vector))
|
|
|
|
-- Really, the case below shouldn't be happening. But if it
|
|
|
|
-- does, it's not a problem either. The output will just have
|
|
|
|
-- a record in it.
|
|
|
|
DataLoader.RecordOutputValue !record -> encJFromRecord record
|
|
|
|
|
|
|
|
-- mutation
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
bqDBMutationPlan ::
|
|
|
|
forall m.
|
|
|
|
( MonadError E.QErr m
|
|
|
|
) =>
|
|
|
|
UserInfo ->
|
2022-02-23 23:17:58 +03:00
|
|
|
StringifyNumbers ->
|
2021-09-24 01:56:37 +03:00
|
|
|
SourceName ->
|
|
|
|
SourceConfig 'BigQuery ->
|
2021-12-07 16:12:02 +03:00
|
|
|
MutationDB 'BigQuery Void (UnpreparedValue 'BigQuery) ->
|
2021-09-24 01:56:37 +03:00
|
|
|
m (DBStepInfo 'BigQuery)
|
2021-09-23 15:37:56 +03:00
|
|
|
bqDBMutationPlan _userInfo _stringifyNum _sourceName _sourceConfig _mrf =
|
2021-04-12 13:18:29 +03:00
|
|
|
throw500 "mutations are not supported in BigQuery; this should be unreachable"
|
2021-05-05 19:20:04 +03:00
|
|
|
|
|
|
|
-- explain
|
|
|
|
|
2021-09-24 01:56:37 +03:00
|
|
|
bqDBQueryExplain ::
|
|
|
|
MonadError E.QErr m =>
|
2021-10-29 17:42:07 +03:00
|
|
|
RootFieldAlias ->
|
2021-09-24 01:56:37 +03:00
|
|
|
UserInfo ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig 'BigQuery ->
|
2021-12-07 16:12:02 +03:00
|
|
|
QueryDB 'BigQuery Void (UnpreparedValue 'BigQuery) ->
|
2021-09-24 01:56:37 +03:00
|
|
|
m (AB.AnyBackend DBStepInfo)
|
2021-05-05 19:20:04 +03:00
|
|
|
bqDBQueryExplain fieldName userInfo sourceName sourceConfig qrf = do
|
2021-06-28 16:29:48 +03:00
|
|
|
select <- planNoPlan (BigQuery.bigQuerySourceConfigToFromIrConfig sourceConfig) userInfo qrf
|
|
|
|
let textSQL = selectSQLTextForExplain select
|
2021-09-24 01:56:37 +03:00
|
|
|
pure $
|
|
|
|
AB.mkAnyBackend $
|
|
|
|
DBStepInfo @'BigQuery sourceName sourceConfig Nothing $
|
|
|
|
pure $
|
|
|
|
encJFromJValue $
|
|
|
|
ExplainPlan
|
|
|
|
fieldName
|
|
|
|
(Just $ textSQL)
|
|
|
|
(Just $ T.lines $ textSQL)
|
2021-06-28 16:29:48 +03:00
|
|
|
|
|
|
|
-- | Get the SQL text for a select, with parameters left as $1, $2, .. holes.
|
|
|
|
selectSQLTextForExplain :: BigQuery.Select -> Text
|
|
|
|
selectSQLTextForExplain =
|
2021-09-24 01:56:37 +03:00
|
|
|
LT.toStrict
|
|
|
|
. LT.toLazyText
|
|
|
|
. fst
|
|
|
|
. ToQuery.renderBuilderPretty
|
|
|
|
. ToQuery.fromSelect
|
2021-09-22 13:43:05 +03:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- Remote Relationships (e.g. DB-to-DB Joins, remote schema joins, etc.)
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- | Construct an action (i.e. 'DBStepInfo') which can marshal some remote
|
|
|
|
-- relationship information into a form that BigQuery can query against.
|
|
|
|
--
|
|
|
|
-- XXX: Currently unimplemented; the Postgres implementation uses
|
|
|
|
-- @jsonb_to_recordset@ to query the remote relationship, however this
|
|
|
|
-- functionality doesn't exist in BigQuery.
|
|
|
|
--
|
|
|
|
-- NOTE: The following typeclass constraints will be necessary when implementing
|
|
|
|
-- this function for real:
|
|
|
|
--
|
|
|
|
-- @
|
|
|
|
-- MonadQueryTags m
|
|
|
|
-- Backend 'BigQuery
|
|
|
|
-- @
|
2021-09-24 01:56:37 +03:00
|
|
|
bqDBRemoteRelationshipPlan ::
|
|
|
|
forall m.
|
|
|
|
( MonadError QErr m
|
|
|
|
) =>
|
|
|
|
UserInfo ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig 'BigQuery ->
|
2021-09-22 13:43:05 +03:00
|
|
|
-- | List of json objects, each of which becomes a row of the table.
|
2021-09-24 01:56:37 +03:00
|
|
|
NonEmpty Aeson.Object ->
|
2021-09-22 13:43:05 +03:00
|
|
|
-- | The above objects have this schema
|
|
|
|
--
|
|
|
|
-- XXX: What is this for/what does this mean?
|
2021-09-24 01:56:37 +03:00
|
|
|
HashMap FieldName (Column 'BigQuery, ScalarType 'BigQuery) ->
|
2021-09-22 13:43:05 +03:00
|
|
|
-- | This is a field name from the lhs that *has* to be selected in the
|
|
|
|
-- response along with the relationship.
|
2021-09-24 01:56:37 +03:00
|
|
|
FieldName ->
|
2021-12-07 16:12:02 +03:00
|
|
|
(FieldName, SourceRelationshipSelection 'BigQuery Void UnpreparedValue) ->
|
2021-09-24 01:56:37 +03:00
|
|
|
m (DBStepInfo 'BigQuery)
|
2021-11-24 19:21:59 +03:00
|
|
|
bqDBRemoteRelationshipPlan userInfo sourceName sourceConfig lhs lhsSchema argumentId relationship = do
|
|
|
|
flip runReaderT emptyQueryTagsComment $ bqDBQueryPlan userInfo sourceName sourceConfig rootSelection
|
|
|
|
where
|
|
|
|
coerceToColumn = BigQuery.ColumnName . getFieldNameTxt
|
|
|
|
joinColumnMapping = mapKeys coerceToColumn lhsSchema
|
|
|
|
|
|
|
|
rowsArgument :: UnpreparedValue 'BigQuery
|
|
|
|
rowsArgument =
|
|
|
|
UVParameter Nothing $
|
|
|
|
ColumnValue (ColumnScalar BigQuery.StringScalarType) $
|
|
|
|
BigQuery.StringValue . LT.toStrict $ Aeson.encodeToLazyText lhs
|
|
|
|
|
|
|
|
recordSetDefinitionList =
|
|
|
|
(coerceToColumn argumentId, BigQuery.IntegerScalarType) : Map.toList (fmap snd joinColumnMapping)
|
|
|
|
|
|
|
|
jsonToRecordSet :: IR.SelectFromG ('BigQuery) (UnpreparedValue 'BigQuery)
|
|
|
|
jsonToRecordSet =
|
|
|
|
IR.FromFunction
|
2022-05-04 17:52:29 +03:00
|
|
|
(BigQuery.FunctionName "unnest" Nothing)
|
2021-11-24 19:21:59 +03:00
|
|
|
( IR.FunctionArgsExp
|
|
|
|
[IR.AEInput rowsArgument]
|
|
|
|
mempty
|
|
|
|
)
|
|
|
|
(Just recordSetDefinitionList)
|
|
|
|
|
|
|
|
rootSelection =
|
|
|
|
convertRemoteSourceRelationship
|
|
|
|
(fst <$> joinColumnMapping)
|
|
|
|
jsonToRecordSet
|
|
|
|
(BigQuery.ColumnName $ getFieldNameTxt argumentId)
|
|
|
|
(ColumnScalar BigQuery.IntegerScalarType)
|
|
|
|
relationship
|