2021-04-12 13:18:29 +03:00
|
|
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
|
|
|
|
|
|
module Hasura.Backends.BigQuery.Instances.Execute () where
|
|
|
|
|
2021-06-28 16:29:48 +03:00
|
|
|
import Data.Aeson qualified as Aeson
|
2021-11-24 19:21:59 +03:00
|
|
|
import Data.Aeson.Text qualified as Aeson
|
2022-07-19 04:51:42 +03:00
|
|
|
import Data.Environment qualified as Env
|
2021-11-24 19:21:59 +03:00
|
|
|
import Data.HashMap.Strict qualified as Map
|
2021-06-28 16:29:48 +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
|
|
|
|
import Hasura.Backends.BigQuery.Execute qualified as DataLoader
|
|
|
|
import Hasura.Backends.BigQuery.FromIr qualified as BigQuery
|
2021-04-12 13:18:29 +03:00
|
|
|
import Hasura.Backends.BigQuery.Plan
|
2021-06-28 16:29:48 +03:00
|
|
|
import Hasura.Backends.BigQuery.ToQuery qualified as ToQuery
|
|
|
|
import Hasura.Backends.BigQuery.Types qualified as BigQuery
|
2021-05-11 18:18:31 +03:00
|
|
|
import Hasura.Base.Error
|
2021-06-28 16:29:48 +03:00
|
|
|
import Hasura.Base.Error qualified as E
|
2021-05-05 19:20:04 +03:00
|
|
|
import Hasura.EncJSON
|
2021-04-12 13:18:29 +03:00
|
|
|
import Hasura.GraphQL.Execute.Backend
|
2021-10-29 17:42:07 +03:00
|
|
|
import Hasura.GraphQL.Namespace (RootFieldAlias)
|
2022-07-14 20:57:28 +03:00
|
|
|
import Hasura.GraphQL.Schema.Options qualified as Options
|
2021-05-11 18:18:31 +03:00
|
|
|
import Hasura.Prelude
|
2021-11-24 19:21:59 +03:00
|
|
|
import Hasura.QueryTags
|
|
|
|
( emptyQueryTagsComment,
|
|
|
|
)
|
2021-06-11 06:26:50 +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
|
2022-05-25 13:24:41 +03:00
|
|
|
import Hasura.RQL.Types.Function
|
2021-06-28 16:29:48 +03:00
|
|
|
import Hasura.SQL.AnyBackend qualified as AB
|
2022-04-27 16:57:28 +03:00
|
|
|
import Hasura.SQL.Backend
|
2021-04-12 13:18:29 +03:00
|
|
|
import Hasura.Session
|
2023-01-25 10:12:53 +03:00
|
|
|
import Language.GraphQL.Draft.Syntax qualified as G
|
|
|
|
import Network.HTTP.Types qualified as HTTP
|
2021-05-05 19:20:04 +03:00
|
|
|
|
2021-04-12 13:18:29 +03:00
|
|
|
instance BackendExecute 'BigQuery where
|
2021-05-05 19:20:04 +03:00
|
|
|
type PreparedQuery 'BigQuery = Text
|
2021-04-12 13:18:29 +03:00
|
|
|
type MultiplexedQuery 'BigQuery = Void
|
Allow backend execution to happen on the base app monad.
### Description
Each Backend executes queries against the database in a slightly different stack: Postgres uses its own `TXeT`, MSSQL uses a variant of it, BigQuery is simply in `ExceptT QErr IO`... To accommodate those variations, we had originally introduced an `ExecutionMonad b` type family in `BackendExecute`, allowing each backend to describe its own stack. It was then up to that backend's `BackendTransport` instance to implement running said stack, and converting the result back into our main app monad.
However, this was not without complications: `TraceT` is one of them: as it usually needs to be on the top of the stack, converting from one stack to the other implies the use `interpTraceT`, which is quite monstrous. Furthermore, as part of the Entitlement Services work, we're trying to move to a "Services" architecture in which the entire engine runs in one base monad, that delegates features and dependencies to monad constraints; and as a result we'd like to minimize the number of different monad stacks we have to maintain and translate from and to in the codebase.
To improve things, this PR changes `ExecutionMonad b` from an _absolute_ stack to a _relative_ one: i.e.: what needs to be stacked on top of our base monad for the execution. In `Transport`, we then only need to pop the top of the stack, and voila. This greatly simplifies the implementation of the backends, as there's no longer any need to do any stack transformation: MySQL's implementation becomes a `runIdentityT`! This also removes most mentions of `TraceT` from the execution code since it's no longer required: we can rely on the base monad's existing `MonadTrace` constraint.
To continue encapsulating monadic actions in `DBStepInfo` and avoid threading a bunch of `forall` all over the place, this PR introduces a small local helper: `OnBaseMonad`. One only downside of all this is that this requires adding `MonadBaseControl IO m` constraint all over the place: previously, we would run directly on `IO` and lift, and would therefore not need to bring that constraint all the way.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7789
GitOrigin-RevId: e9b2e431c5c47fa9851abf87545c0415ff6d1a12
2023-02-09 17:38:33 +03:00
|
|
|
type ExecutionMonad 'BigQuery = IdentityT
|
2021-04-12 13:18:29 +03:00
|
|
|
|
2021-05-05 19:20:04 +03:00
|
|
|
mkDBQueryPlan = bqDBQueryPlan
|
|
|
|
mkDBMutationPlan = bqDBMutationPlan
|
2023-01-25 10:12:53 +03:00
|
|
|
mkLiveQuerySubscriptionPlan _ _ _ _ _ _ _ =
|
2022-04-07 17:41:43 +03:00
|
|
|
throw500 "Cannot currently perform subscriptions on BigQuery sources."
|
2023-01-25 10:12:53 +03:00
|
|
|
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-05-05 19:20:04 +03:00
|
|
|
bqDBQueryPlan ::
|
2021-04-12 13:18:29 +03:00
|
|
|
forall m.
|
2021-05-11 18:18:31 +03:00
|
|
|
( MonadError E.QErr m
|
2021-04-12 13:18:29 +03:00
|
|
|
) =>
|
2021-06-11 06:26:50 +03:00
|
|
|
UserInfo ->
|
2022-07-19 04:51:42 +03:00
|
|
|
Env.Environment ->
|
2021-04-12 13:18:29 +03:00
|
|
|
SourceName ->
|
|
|
|
SourceConfig 'BigQuery ->
|
2021-12-07 16:12:02 +03:00
|
|
|
QueryDB 'BigQuery Void (UnpreparedValue 'BigQuery) ->
|
2023-01-25 10:12:53 +03:00
|
|
|
[HTTP.Header] ->
|
|
|
|
Maybe G.Name ->
|
2021-06-11 06:26:50 +03:00
|
|
|
m (DBStepInfo 'BigQuery)
|
2023-01-25 10:12:53 +03:00
|
|
|
bqDBQueryPlan userInfo _env 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
|
Allow backend execution to happen on the base app monad.
### Description
Each Backend executes queries against the database in a slightly different stack: Postgres uses its own `TXeT`, MSSQL uses a variant of it, BigQuery is simply in `ExceptT QErr IO`... To accommodate those variations, we had originally introduced an `ExecutionMonad b` type family in `BackendExecute`, allowing each backend to describe its own stack. It was then up to that backend's `BackendTransport` instance to implement running said stack, and converting the result back into our main app monad.
However, this was not without complications: `TraceT` is one of them: as it usually needs to be on the top of the stack, converting from one stack to the other implies the use `interpTraceT`, which is quite monstrous. Furthermore, as part of the Entitlement Services work, we're trying to move to a "Services" architecture in which the entire engine runs in one base monad, that delegates features and dependencies to monad constraints; and as a result we'd like to minimize the number of different monad stacks we have to maintain and translate from and to in the codebase.
To improve things, this PR changes `ExecutionMonad b` from an _absolute_ stack to a _relative_ one: i.e.: what needs to be stacked on top of our base monad for the execution. In `Transport`, we then only need to pop the top of the stack, and voila. This greatly simplifies the implementation of the backends, as there's no longer any need to do any stack transformation: MySQL's implementation becomes a `runIdentityT`! This also removes most mentions of `TraceT` from the execution code since it's no longer required: we can rely on the base monad's existing `MonadTrace` constraint.
To continue encapsulating monadic actions in `DBStepInfo` and avoid threading a bunch of `forall` all over the place, this PR introduces a small local helper: `OnBaseMonad`. One only downside of all this is that this requires adding `MonadBaseControl IO m` constraint all over the place: previously, we would run directly on `IO` and lift, and would therefore not need to bring that constraint all the way.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7789
GitOrigin-RevId: e9b2e431c5c47fa9851abf87545c0415ff6d1a12
2023-02-09 17:38:33 +03:00
|
|
|
let action = OnBaseMonad do
|
2021-04-12 13:18:29 +03:00
|
|
|
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-06-27 17:32:31 +03:00
|
|
|
Left err -> throw500WithDetail (DataLoader.executeProblemMessage DataLoader.HideDetails err) $ Aeson.toJSON err
|
2021-06-15 11:58:21 +03:00
|
|
|
Right recordSet -> pure $! recordSetToEncJSON (BigQuery.selectCardinality select) recordSet
|
2023-01-25 10:12:53 +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
|
2022-07-29 17:05:03 +03:00
|
|
|
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 ->
|
2021-04-12 13:18:29 +03:00
|
|
|
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.
|
2022-07-29 17:05:03 +03:00
|
|
|
DataLoader.RecordOutputValue record -> encJFromRecord record
|
2021-04-12 13:18:29 +03:00
|
|
|
|
|
|
|
-- mutation
|
|
|
|
|
2021-05-05 19:20:04 +03:00
|
|
|
bqDBMutationPlan ::
|
2021-04-12 13:18:29 +03:00
|
|
|
forall m.
|
2021-05-11 18:18:31 +03:00
|
|
|
( MonadError E.QErr m
|
2021-04-12 13:18:29 +03:00
|
|
|
) =>
|
2021-06-11 06:26:50 +03:00
|
|
|
UserInfo ->
|
2023-01-17 03:33:56 +03:00
|
|
|
Env.Environment ->
|
2022-07-14 20:57:28 +03:00
|
|
|
Options.StringifyNumbers ->
|
2021-04-12 13:18:29 +03:00
|
|
|
SourceName ->
|
|
|
|
SourceConfig 'BigQuery ->
|
2021-12-07 16:12:02 +03:00
|
|
|
MutationDB 'BigQuery Void (UnpreparedValue 'BigQuery) ->
|
2023-01-25 10:12:53 +03:00
|
|
|
[HTTP.Header] ->
|
|
|
|
Maybe G.Name ->
|
2021-06-11 06:26:50 +03:00
|
|
|
m (DBStepInfo 'BigQuery)
|
2023-01-25 10:12:53 +03:00
|
|
|
bqDBMutationPlan _userInfo _environment _stringifyNum _sourceName _sourceConfig _mrf _headers _gName =
|
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
|
|
|
|
|
|
|
|
bqDBQueryExplain ::
|
2021-05-11 18:18:31 +03:00
|
|
|
MonadError E.QErr m =>
|
2021-10-29 17:42:07 +03:00
|
|
|
RootFieldAlias ->
|
2021-05-05 19:20:04 +03:00
|
|
|
UserInfo ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig 'BigQuery ->
|
2021-12-07 16:12:02 +03:00
|
|
|
QueryDB 'BigQuery Void (UnpreparedValue 'BigQuery) ->
|
2023-01-25 10:12:53 +03:00
|
|
|
[HTTP.Header] ->
|
|
|
|
Maybe G.Name ->
|
2021-05-05 19:20:04 +03:00
|
|
|
m (AB.AnyBackend DBStepInfo)
|
2023-01-25 10:12:53 +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-05-05 19:20:04 +03:00
|
|
|
pure $
|
|
|
|
AB.mkAnyBackend $
|
2023-01-25 10:12:53 +03:00
|
|
|
DBStepInfo @'BigQuery
|
|
|
|
sourceName
|
|
|
|
sourceConfig
|
|
|
|
Nothing
|
Allow backend execution to happen on the base app monad.
### Description
Each Backend executes queries against the database in a slightly different stack: Postgres uses its own `TXeT`, MSSQL uses a variant of it, BigQuery is simply in `ExceptT QErr IO`... To accommodate those variations, we had originally introduced an `ExecutionMonad b` type family in `BackendExecute`, allowing each backend to describe its own stack. It was then up to that backend's `BackendTransport` instance to implement running said stack, and converting the result back into our main app monad.
However, this was not without complications: `TraceT` is one of them: as it usually needs to be on the top of the stack, converting from one stack to the other implies the use `interpTraceT`, which is quite monstrous. Furthermore, as part of the Entitlement Services work, we're trying to move to a "Services" architecture in which the entire engine runs in one base monad, that delegates features and dependencies to monad constraints; and as a result we'd like to minimize the number of different monad stacks we have to maintain and translate from and to in the codebase.
To improve things, this PR changes `ExecutionMonad b` from an _absolute_ stack to a _relative_ one: i.e.: what needs to be stacked on top of our base monad for the execution. In `Transport`, we then only need to pop the top of the stack, and voila. This greatly simplifies the implementation of the backends, as there's no longer any need to do any stack transformation: MySQL's implementation becomes a `runIdentityT`! This also removes most mentions of `TraceT` from the execution code since it's no longer required: we can rely on the base monad's existing `MonadTrace` constraint.
To continue encapsulating monadic actions in `DBStepInfo` and avoid threading a bunch of `forall` all over the place, this PR introduces a small local helper: `OnBaseMonad`. One only downside of all this is that this requires adding `MonadBaseControl IO m` constraint all over the place: previously, we would run directly on `IO` and lift, and would therefore not need to bring that constraint all the way.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7789
GitOrigin-RevId: e9b2e431c5c47fa9851abf87545c0415ff6d1a12
2023-02-09 17:38:33 +03:00
|
|
|
( OnBaseMonad $
|
|
|
|
pure $
|
|
|
|
encJFromJValue $
|
|
|
|
ExplainPlan
|
|
|
|
fieldName
|
|
|
|
(Just $ textSQL)
|
|
|
|
(Just $ T.lines $ textSQL)
|
2023-01-25 10:12:53 +03:00
|
|
|
)
|
|
|
|
()
|
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 =
|
|
|
|
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
|
|
|
|
-- @
|
|
|
|
bqDBRemoteRelationshipPlan ::
|
|
|
|
forall m.
|
|
|
|
( MonadError QErr m
|
|
|
|
) =>
|
|
|
|
UserInfo ->
|
|
|
|
SourceName ->
|
|
|
|
SourceConfig 'BigQuery ->
|
|
|
|
-- | List of json objects, each of which becomes a row of the table.
|
|
|
|
NonEmpty Aeson.Object ->
|
|
|
|
-- | The above objects have this schema
|
|
|
|
--
|
|
|
|
-- XXX: What is this for/what does this mean?
|
|
|
|
HashMap FieldName (Column 'BigQuery, ScalarType 'BigQuery) ->
|
|
|
|
-- | This is a field name from the lhs that *has* to be selected in the
|
|
|
|
-- response along with the relationship.
|
|
|
|
FieldName ->
|
2021-12-07 16:12:02 +03:00
|
|
|
(FieldName, SourceRelationshipSelection 'BigQuery Void UnpreparedValue) ->
|
2023-01-25 10:12:53 +03:00
|
|
|
[HTTP.Header] ->
|
|
|
|
Maybe G.Name ->
|
2022-12-19 17:03:13 +03:00
|
|
|
Options.StringifyNumbers ->
|
2021-09-22 13:43:05 +03:00
|
|
|
m (DBStepInfo 'BigQuery)
|
2023-01-25 10:12:53 +03:00
|
|
|
bqDBRemoteRelationshipPlan userInfo sourceName sourceConfig lhs lhsSchema argumentId relationship reqHeaders operationName stringifyNumbers = do
|
|
|
|
flip runReaderT emptyQueryTagsComment $ bqDBQueryPlan userInfo Env.emptyEnvironment sourceName sourceConfig rootSelection reqHeaders operationName
|
2021-11-24 19:21:59 +03:00
|
|
|
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)
|
2022-05-25 13:24:41 +03:00
|
|
|
( FunctionArgsExp
|
|
|
|
[BigQuery.AEInput rowsArgument]
|
2021-11-24 19:21:59 +03:00
|
|
|
mempty
|
|
|
|
)
|
|
|
|
(Just recordSetDefinitionList)
|
|
|
|
|
|
|
|
rootSelection =
|
|
|
|
convertRemoteSourceRelationship
|
|
|
|
(fst <$> joinColumnMapping)
|
|
|
|
jsonToRecordSet
|
|
|
|
(BigQuery.ColumnName $ getFieldNameTxt argumentId)
|
|
|
|
(ColumnScalar BigQuery.IntegerScalarType)
|
|
|
|
relationship
|
2022-12-19 17:03:13 +03:00
|
|
|
stringifyNumbers
|