mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
server/postgres: support computed fields in order by
https://github.com/hasura/graphql-engine-mono/pull/1793 GitOrigin-RevId: e0396c0d4d96fc8f9bdbd7567193933db5b295a6
This commit is contained in:
parent
e4155e4c5b
commit
5cfac6ea87
@ -3,6 +3,7 @@
|
||||
## Next release
|
||||
(Add entries below in the order of server, console, cli, docs, others)
|
||||
|
||||
- server: support computed fields in query 'order_by' (close #7103)
|
||||
- server: log warning if there are errors while executing clean up actions after "drop source" (previously it would throw an error)
|
||||
- server: Fixed a bug where MSSQL and BigQuery would ignore environment variables set from the console
|
||||
- server: Fixing bug in ReplaceMetadata parser - Moving from Alternative to committed-choice.
|
||||
|
@ -403,7 +403,7 @@ fromSelectArgsG selectArgsG = do
|
||||
Nothing -> pure Proxy
|
||||
Just {} -> refute (pure DistinctIsn'tSupported)
|
||||
(argsOrderBy, joins) <-
|
||||
runWriterT (traverse fromAnnOrderByItemG (maybe [] toList orders))
|
||||
runWriterT (traverse fromAnnotatedOrderByItemG (maybe [] toList orders))
|
||||
-- Any object-relation joins that we generated, we record their
|
||||
-- generated names into a mapping.
|
||||
let argsExistingJoins =
|
||||
@ -424,10 +424,10 @@ fromSelectArgsG selectArgsG = do
|
||||
|
||||
-- | Produce a valid ORDER BY construct, telling about any joins
|
||||
-- needed on the side.
|
||||
fromAnnOrderByItemG ::
|
||||
Ir.AnnOrderByItemG 'BigQuery Expression -> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) OrderBy
|
||||
fromAnnOrderByItemG Ir.OrderByItemG {obiType, obiColumn, obiNulls} = do
|
||||
orderByFieldName <- unfurlAnnOrderByElement obiColumn
|
||||
fromAnnotatedOrderByItemG ::
|
||||
Ir.AnnotatedOrderByItemG 'BigQuery Expression -> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) OrderBy
|
||||
fromAnnotatedOrderByItemG Ir.OrderByItemG {obiType, obiColumn, obiNulls} = do
|
||||
orderByFieldName <- unfurlAnnotatedOrderByElement obiColumn
|
||||
let morderByOrder =
|
||||
obiType
|
||||
let orderByNullsOrder =
|
||||
@ -439,9 +439,9 @@ fromAnnOrderByItemG Ir.OrderByItemG {obiType, obiColumn, obiNulls} = do
|
||||
-- | Unfurl the nested set of object relations (tell'd in the writer)
|
||||
-- that are terminated by field name (Ir.AOCColumn and
|
||||
-- Ir.AOCArrayAggregation).
|
||||
unfurlAnnOrderByElement ::
|
||||
Ir.AnnOrderByElement 'BigQuery Expression -> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) FieldName
|
||||
unfurlAnnOrderByElement =
|
||||
unfurlAnnotatedOrderByElement ::
|
||||
Ir.AnnotatedOrderByElement 'BigQuery Expression -> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) FieldName
|
||||
unfurlAnnotatedOrderByElement =
|
||||
\case
|
||||
Ir.AOCColumn pgColumnInfo -> lift (fromPGColumnInfo pgColumnInfo)
|
||||
Ir.AOCObjectRelation Rql.RelInfo {riMapping = mapping, riRTable = tableName} annBoolExp annOrderByElementG -> do
|
||||
@ -477,7 +477,7 @@ unfurlAnnOrderByElement =
|
||||
}
|
||||
, unfurledObjectTableAlias = Just (tableName, joinAliasEntity)
|
||||
})
|
||||
local (const joinAliasEntity) (unfurlAnnOrderByElement annOrderByElementG)
|
||||
local (const joinAliasEntity) (unfurlAnnotatedOrderByElement annOrderByElementG)
|
||||
Ir.AOCArrayAggregation Rql.RelInfo {riMapping = mapping, riRTable = tableName} annBoolExp annAggregateOrderBy -> do
|
||||
selectFrom <- lift (lift (fromQualifiedTable tableName))
|
||||
let alias = aggFieldName
|
||||
|
@ -358,7 +358,7 @@ fromSelectArgsG selectArgsG = do
|
||||
-- you can just drop the Proxy wrapper.
|
||||
let argsDistinct = Proxy
|
||||
(argsOrderBy, joins) <-
|
||||
runWriterT (traverse fromAnnOrderByItemG (maybe [] toList orders))
|
||||
runWriterT (traverse fromAnnotatedOrderByItemG (maybe [] toList orders))
|
||||
-- Any object-relation joins that we generated, we record their
|
||||
-- generated names into a mapping.
|
||||
let argsExistingJoins =
|
||||
@ -378,11 +378,11 @@ fromSelectArgsG selectArgsG = do
|
||||
|
||||
-- | Produce a valid ORDER BY construct, telling about any joins
|
||||
-- needed on the side.
|
||||
fromAnnOrderByItemG
|
||||
:: IR.AnnOrderByItemG 'MSSQL Expression
|
||||
fromAnnotatedOrderByItemG
|
||||
:: IR.AnnotatedOrderByItemG 'MSSQL Expression
|
||||
-> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) OrderBy
|
||||
fromAnnOrderByItemG IR.OrderByItemG {obiType, obiColumn = obiColumn, obiNulls} = do
|
||||
(orderByFieldName, orderByType) <- unfurlAnnOrderByElement obiColumn
|
||||
fromAnnotatedOrderByItemG IR.OrderByItemG {obiType, obiColumn = obiColumn, obiNulls} = do
|
||||
(orderByFieldName, orderByType) <- unfurlAnnotatedOrderByElement obiColumn
|
||||
let orderByNullsOrder = fromMaybe NullsAnyOrder obiNulls
|
||||
orderByOrder = fromMaybe AscOrder obiType
|
||||
pure OrderBy {..}
|
||||
@ -390,10 +390,10 @@ fromAnnOrderByItemG IR.OrderByItemG {obiType, obiColumn = obiColumn, obiNulls} =
|
||||
-- | Unfurl the nested set of object relations (tell'd in the writer)
|
||||
-- that are terminated by field name (IR.AOCColumn and
|
||||
-- IR.AOCArrayAggregation).
|
||||
unfurlAnnOrderByElement
|
||||
:: IR.AnnOrderByElement 'MSSQL Expression
|
||||
unfurlAnnotatedOrderByElement
|
||||
:: IR.AnnotatedOrderByElement 'MSSQL Expression
|
||||
-> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) (FieldName, Maybe TSQL.ScalarType)
|
||||
unfurlAnnOrderByElement =
|
||||
unfurlAnnotatedOrderByElement =
|
||||
\case
|
||||
IR.AOCColumn pgColumnInfo -> do
|
||||
fieldName <- lift (fromPGColumnInfo pgColumnInfo)
|
||||
@ -444,7 +444,7 @@ unfurlAnnOrderByElement =
|
||||
})
|
||||
local
|
||||
(const (EntityAlias joinAliasEntity))
|
||||
(unfurlAnnOrderByElement annOrderByElementG)
|
||||
(unfurlAnnotatedOrderByElement annOrderByElementG)
|
||||
IR.AOCArrayAggregation IR.RelInfo {riMapping = mapping, riRTable = tableName} annBoolExp annAggregateOrderBy -> do
|
||||
selectFrom <- lift (lift (fromQualifiedTable tableName))
|
||||
let alias = aggFieldName
|
||||
|
@ -220,11 +220,11 @@ mkBaseTableColumnAlias :: Identifier -> PGCol -> Identifier
|
||||
mkBaseTableColumnAlias pfx pgColumn =
|
||||
pfx <> Identifier ".pg." <> toIdentifier pgColumn
|
||||
|
||||
mkOrderByFieldName :: RelName -> FieldName
|
||||
mkOrderByFieldName relName =
|
||||
FieldName $ relNameToTxt relName <> "." <> "order_by"
|
||||
mkOrderByFieldName :: ToTxt a => a -> FieldName
|
||||
mkOrderByFieldName name =
|
||||
FieldName $ toTxt name <> "." <> "order_by"
|
||||
|
||||
mkAggregateOrderByAlias :: AnnAggregateOrderBy ('Postgres pgKind) -> S.Alias
|
||||
mkAggregateOrderByAlias :: AnnotatedAggregateOrderBy ('Postgres pgKind) -> S.Alias
|
||||
mkAggregateOrderByAlias = (S.Alias . Identifier) . \case
|
||||
AAOCount -> "count"
|
||||
AAOOp opText col -> opText <> "." <> getPGColTxt (pgiColumn col)
|
||||
@ -284,7 +284,7 @@ withForceAggregation tyAnn e =
|
||||
mkAggregateOrderByExtractorAndFields
|
||||
:: forall pgKind
|
||||
. Backend ('Postgres pgKind)
|
||||
=> AnnAggregateOrderBy ('Postgres pgKind)
|
||||
=> AnnotatedAggregateOrderBy ('Postgres pgKind)
|
||||
-> (S.Extractor, AggregateFields ('Postgres pgKind))
|
||||
mkAggregateOrderByExtractorAndFields annAggOrderBy =
|
||||
case annAggOrderBy of
|
||||
@ -309,7 +309,7 @@ mkAggregateOrderByExtractorAndFields annAggOrderBy =
|
||||
alias = Just $ mkAggregateOrderByAlias annAggOrderBy
|
||||
|
||||
mkAnnOrderByAlias
|
||||
:: Identifier -> FieldName -> SimilarArrayFields -> AnnOrderByElementG ('Postgres pgKind) v -> S.Alias
|
||||
:: Identifier -> FieldName -> SimilarArrayFields -> AnnotatedOrderByElement ('Postgres pgKind) v -> S.Alias
|
||||
mkAnnOrderByAlias pfx parAls similarFields = \case
|
||||
AOCColumn pgColumnInfo ->
|
||||
let pgColumn = pgiColumn pgColumnInfo
|
||||
@ -328,6 +328,14 @@ mkAnnOrderByAlias pfx parAls similarFields = \case
|
||||
mkOrderByFieldName rn
|
||||
obAls = arrPfx <> Identifier "." <> toIdentifier (mkAggregateOrderByAlias aggOrderBy)
|
||||
in S.Alias obAls
|
||||
AOCComputedField cfOrderBy ->
|
||||
let fieldName = fromComputedField $ _cfobName cfOrderBy
|
||||
in case _cfobOrderByElement cfOrderBy of
|
||||
CFOBEScalar _ -> S.Alias $ mkComputedFieldTableAlias pfx fieldName
|
||||
CFOBETableAggregation _ _ aggOrderBy ->
|
||||
let cfPfx = mkComputedFieldTableAlias pfx fieldName
|
||||
obAls = cfPfx <> Identifier "." <> toIdentifier (mkAggregateOrderByAlias aggOrderBy)
|
||||
in S.Alias obAls
|
||||
|
||||
processDistinctOnColumns
|
||||
:: Identifier
|
||||
@ -349,7 +357,7 @@ mkSimilarArrayFields
|
||||
:: forall pgKind v
|
||||
. (Backend ('Postgres pgKind), Eq v)
|
||||
=> AnnFieldsG ('Postgres pgKind) (Const Void) v
|
||||
-> Maybe (NE.NonEmpty (AnnOrderByItemG ('Postgres pgKind) v))
|
||||
-> Maybe (NE.NonEmpty (AnnotatedOrderByItemG ('Postgres pgKind) v))
|
||||
-> SimilarArrayFields
|
||||
mkSimilarArrayFields annFields maybeOrderBys =
|
||||
HM.fromList $ flip map allTuples $
|
||||
@ -432,7 +440,7 @@ withWriteArrayRelation action =
|
||||
pure (out, (source, topExtractor, nodeExtractors))
|
||||
where
|
||||
updateJoinTree joinTree (source, topExtractor, nodeExtractors) =
|
||||
let arraySelectNode = ArraySelectNode [topExtractor] $
|
||||
let arraySelectNode = MultiRowSelectNode [topExtractor] $
|
||||
SelectNode nodeExtractors joinTree
|
||||
in mempty{_jtArrayRelations = HM.singleton source arraySelectNode}
|
||||
|
||||
@ -450,24 +458,25 @@ withWriteArrayConnection action =
|
||||
pure (out, (source, topExtractor, nodeExtractors))
|
||||
where
|
||||
updateJoinTree joinTree (source, topExtractor, nodeExtractors) =
|
||||
let arraySelectNode = ArraySelectNode [topExtractor] $
|
||||
let arraySelectNode = MultiRowSelectNode [topExtractor] $
|
||||
SelectNode nodeExtractors joinTree
|
||||
in mempty{_jtArrayConnections = HM.singleton source arraySelectNode}
|
||||
|
||||
withWriteComputedFieldTableSet
|
||||
:: (MonadWriter JoinTree m)
|
||||
=> m ( ComputedFieldTableSetSource
|
||||
, S.Extractor
|
||||
, HM.HashMap S.Alias S.SQLExp
|
||||
, a
|
||||
)
|
||||
-> m a
|
||||
withWriteComputedFieldTableSet action =
|
||||
withWriteJoinTree updateJoinTree $ do
|
||||
(source, nodeExtractors, out) <- action
|
||||
pure (out, (source, nodeExtractors))
|
||||
(source, topExtractor, nodeExtractors, out) <- action
|
||||
pure (out, (source, topExtractor, nodeExtractors))
|
||||
where
|
||||
updateJoinTree joinTree (source, nodeExtractors) =
|
||||
let selectNode = SelectNode nodeExtractors joinTree
|
||||
updateJoinTree joinTree (source, topExtractor, nodeExtractors) =
|
||||
let selectNode = MultiRowSelectNode [topExtractor] $ SelectNode nodeExtractors joinTree
|
||||
in mempty{_jtComputedFieldTableSets = HM.singleton source selectNode}
|
||||
|
||||
|
||||
@ -557,7 +566,7 @@ processAnnAggregateSelect sourcePrefixes fieldAlias annAggSel = do
|
||||
mkPermissionLimitSubQuery
|
||||
:: Maybe Int
|
||||
-> TableAggregateFields ('Postgres pgKind)
|
||||
-> Maybe (NE.NonEmpty (AnnOrderByItem ('Postgres pgKind)))
|
||||
-> Maybe (NE.NonEmpty (AnnotatedOrderByItem ('Postgres pgKind)))
|
||||
-> PermissionLimitSubQuery
|
||||
mkPermissionLimitSubQuery permLimit aggFields orderBys =
|
||||
case permLimit of
|
||||
@ -685,7 +694,7 @@ processOrderByItems
|
||||
=> Identifier
|
||||
-> FieldName
|
||||
-> SimilarArrayFields
|
||||
-> NE.NonEmpty (AnnOrderByItem ('Postgres pgKind))
|
||||
-> NE.NonEmpty (AnnotatedOrderByItem ('Postgres pgKind))
|
||||
-> m ( [(S.Alias, S.SQLExp)] -- Order by Extractors
|
||||
, S.OrderByExp
|
||||
, S.SQLExp -- The cursor expression
|
||||
@ -698,15 +707,15 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields orderByItems =
|
||||
pure (orderByExtractors, orderByExp, cursor)
|
||||
where
|
||||
processAnnOrderByItem
|
||||
:: AnnOrderByItem ('Postgres pgKind)
|
||||
-> m (OrderByItemG ('Postgres pgKind) (AnnOrderByElement ('Postgres pgKind) (SQLExpression ('Postgres pgKind)), (S.Alias, SQLExpression ('Postgres pgKind))))
|
||||
:: AnnotatedOrderByItem ('Postgres pgKind)
|
||||
-> m (OrderByItemG ('Postgres pgKind) (AnnotatedOrderByElement ('Postgres pgKind) (SQLExpression ('Postgres pgKind)), (S.Alias, SQLExpression ('Postgres pgKind))))
|
||||
processAnnOrderByItem orderByItem =
|
||||
forM orderByItem $ \ordByCol -> (ordByCol,) <$>
|
||||
processAnnOrderByElement sourcePrefix' fieldAlias' ordByCol
|
||||
processAnnotatedOrderByElement sourcePrefix' fieldAlias' ordByCol
|
||||
|
||||
processAnnOrderByElement
|
||||
:: Identifier -> FieldName -> AnnOrderByElement ('Postgres pgKind) S.SQLExp -> m (S.Alias, S.SQLExp)
|
||||
processAnnOrderByElement sourcePrefix fieldAlias annObCol = do
|
||||
processAnnotatedOrderByElement
|
||||
:: Identifier -> FieldName -> AnnotatedOrderByElement ('Postgres pgKind) S.SQLExp -> m (S.Alias, S.SQLExp)
|
||||
processAnnotatedOrderByElement sourcePrefix fieldAlias annObCol = do
|
||||
let ordByAlias = mkAnnOrderByAlias sourcePrefix fieldAlias similarArrayFields annObCol
|
||||
(ordByAlias, ) <$> case annObCol of
|
||||
AOCColumn pgColInfo -> pure $
|
||||
@ -717,7 +726,7 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields orderByItems =
|
||||
relSourcePrefix = mkObjectRelationTableAlias sourcePrefix relName
|
||||
fieldName = mkOrderByFieldName relName
|
||||
(relOrderByAlias, relOrdByExp) <-
|
||||
processAnnOrderByElement relSourcePrefix fieldName rest
|
||||
processAnnotatedOrderByElement relSourcePrefix fieldName rest
|
||||
let selectSource = ObjectSelectSource relSourcePrefix
|
||||
(S.FISimple relTable Nothing)
|
||||
(toSQLBoolExp (S.QualTable relTable) relFilter)
|
||||
@ -745,15 +754,38 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields orderByItems =
|
||||
, S.mkQIdenExp relSourcePrefix (mkAggregateOrderByAlias aggOrderBy)
|
||||
)
|
||||
|
||||
AOCComputedField ComputedFieldOrderBy{..} ->
|
||||
case _cfobOrderByElement of
|
||||
CFOBEScalar _ -> do
|
||||
let functionArgs = fromTableRowArgs sourcePrefix _cfobFunctionArgsExp
|
||||
functionExp = S.FunctionExp _cfobFunction functionArgs Nothing
|
||||
pure $ S.SEFunction functionExp
|
||||
CFOBETableAggregation _ tableFilter aggOrderBy -> withWriteComputedFieldTableSet $ do
|
||||
let fieldName = mkOrderByFieldName _cfobName
|
||||
computedFieldSourcePrefix = mkComputedFieldTableAlias sourcePrefix fieldName
|
||||
(topExtractor, fields) = mkAggregateOrderByExtractorAndFields aggOrderBy
|
||||
fromItem = selectFromToFromItem sourcePrefix $
|
||||
FromFunction _cfobFunction _cfobFunctionArgsExp Nothing
|
||||
functionQual = S.QualifiedIdentifier (functionToIdentifier _cfobFunction) Nothing
|
||||
selectSource = SelectSource computedFieldSourcePrefix fromItem Nothing
|
||||
(toSQLBoolExp functionQual tableFilter)
|
||||
Nothing Nothing Nothing
|
||||
source = ComputedFieldTableSetSource fieldName selectSource
|
||||
pure ( source
|
||||
, topExtractor
|
||||
, HM.fromList $ aggregateFieldsToExtractorExps computedFieldSourcePrefix fields
|
||||
, S.mkQIdenExp computedFieldSourcePrefix (mkAggregateOrderByAlias aggOrderBy)
|
||||
)
|
||||
|
||||
toOrderByExp
|
||||
:: OrderByItemG ('Postgres pgKind) (AnnOrderByElement ('Postgres pgKind) (SQLExpression ('Postgres pgKind)), (S.Alias, SQLExpression ('Postgres pgKind)))
|
||||
:: OrderByItemG ('Postgres pgKind) (AnnotatedOrderByElement ('Postgres pgKind) (SQLExpression ('Postgres pgKind)), (S.Alias, SQLExpression ('Postgres pgKind)))
|
||||
-> S.OrderByItem
|
||||
toOrderByExp orderByItemExp =
|
||||
let OrderByItemG obTyM expAlias obNullsM = fst . snd <$> orderByItemExp
|
||||
in S.OrderByItem (S.SEIdentifier $ toIdentifier expAlias) obTyM obNullsM
|
||||
|
||||
mkCursorExp
|
||||
:: [OrderByItemG ('Postgres pgKind) (AnnOrderByElement ('Postgres pgKind) (SQLExpression ('Postgres pgKind)), (S.Alias, SQLExpression ('Postgres pgKind)))]
|
||||
:: [OrderByItemG ('Postgres pgKind) (AnnotatedOrderByElement ('Postgres pgKind) (SQLExpression ('Postgres pgKind)), (S.Alias, SQLExpression ('Postgres pgKind)))]
|
||||
-> S.SQLExp
|
||||
mkCursorExp orderByItemExps =
|
||||
S.applyJsonBuildObj $ flip concatMap orderByItemExps $
|
||||
@ -761,6 +793,13 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields orderByItems =
|
||||
let OrderByItemG _ (annObCol, (_, valExp)) _ = orderByItemExp
|
||||
in annObColToJSONField valExp annObCol
|
||||
where
|
||||
mkAggOrderByValExp valExp = \case
|
||||
AAOCount -> [S.SELit "count", valExp]
|
||||
AAOOp opText colInfo ->
|
||||
[ S.SELit opText
|
||||
, S.applyJsonBuildObj [S.SELit $ getPGColTxt $ pgiColumn colInfo, valExp]
|
||||
]
|
||||
|
||||
annObColToJSONField valExp = \case
|
||||
AOCColumn pgCol -> [S.SELit $ getPGColTxt $ pgiColumn pgCol, valExp]
|
||||
AOCObjectRelation relInfo _ obCol ->
|
||||
@ -769,14 +808,16 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields orderByItems =
|
||||
]
|
||||
AOCArrayAggregation relInfo _ aggOrderBy ->
|
||||
[ S.SELit $ relNameToTxt (riName relInfo) <> "_aggregate"
|
||||
, S.applyJsonBuildObj $
|
||||
case aggOrderBy of
|
||||
AAOCount -> [S.SELit "count", valExp]
|
||||
AAOOp opText colInfo ->
|
||||
[ S.SELit opText
|
||||
, S.applyJsonBuildObj [S.SELit $ getPGColTxt $ pgiColumn colInfo, valExp]
|
||||
]
|
||||
, S.applyJsonBuildObj $ mkAggOrderByValExp valExp aggOrderBy
|
||||
]
|
||||
AOCComputedField cfOrderBy ->
|
||||
let fieldNameText = computedFieldNameToText $ _cfobName cfOrderBy
|
||||
in case _cfobOrderByElement cfOrderBy of
|
||||
CFOBEScalar _ -> [S.SELit fieldNameText, valExp]
|
||||
CFOBETableAggregation _ _ aggOrderBy ->
|
||||
[ S.SELit $ fieldNameText <> "_aggregate"
|
||||
, S.applyJsonBuildObj $ mkAggOrderByValExp valExp aggOrderBy
|
||||
]
|
||||
|
||||
aggregateFieldsToExtractorExps
|
||||
:: Identifier -> AggregateFields ('Postgres pgKind) -> [(S.Alias, S.SQLExp)]
|
||||
@ -917,9 +958,11 @@ processAnnFields sourcePrefix fieldAlias similarArrFields annFields = do
|
||||
(selectSource, nodeExtractors) <-
|
||||
processAnnSimpleSelect (mkSourcePrefixes computedFieldSourcePrefix)
|
||||
fieldName PLSQNotRequired sel
|
||||
let computedFieldTableSetSource =
|
||||
ComputedFieldTableSetSource fieldName selectTy selectSource
|
||||
let computedFieldTableSetSource = ComputedFieldTableSetSource fieldName selectSource
|
||||
extractor = asJsonAggExtr selectTy (S.toAlias fieldName) PLSQNotRequired $
|
||||
_ssOrderBy selectSource
|
||||
pure ( computedFieldTableSetSource
|
||||
, extractor
|
||||
, nodeExtractors
|
||||
, S.mkQIdenExp computedFieldSourcePrefix fieldName
|
||||
)
|
||||
@ -1027,7 +1070,6 @@ generateSQLSelect joinCondition selectSource selectNode =
|
||||
map arrayConnectionToFromItem (HM.toList arrayConnections) <>
|
||||
map computedFieldToFromItem (HM.toList computedFields)
|
||||
|
||||
|
||||
objectRelationToFromItem
|
||||
:: (ObjectRelationSource, SelectNode) -> S.FromItem
|
||||
objectRelationToFromItem (objectRelationSource, node) =
|
||||
@ -1038,7 +1080,7 @@ generateSQLSelect joinCondition selectSource selectNode =
|
||||
in S.mkLateralFromItem select alias
|
||||
|
||||
arrayRelationToFromItem
|
||||
:: (ArrayRelationSource, ArraySelectNode) -> S.FromItem
|
||||
:: (ArrayRelationSource, MultiRowSelectNode) -> S.FromItem
|
||||
arrayRelationToFromItem (arrayRelationSource, arraySelectNode) =
|
||||
let ArrayRelationSource _ colMapping source = arrayRelationSource
|
||||
alias = S.Alias $ _ssPrefix source
|
||||
@ -1047,29 +1089,27 @@ generateSQLSelect joinCondition selectSource selectNode =
|
||||
in S.mkLateralFromItem select alias
|
||||
|
||||
arrayConnectionToFromItem
|
||||
:: (ArrayConnectionSource, ArraySelectNode) -> S.FromItem
|
||||
:: (ArrayConnectionSource, MultiRowSelectNode) -> S.FromItem
|
||||
arrayConnectionToFromItem (arrayConnectionSource, arraySelectNode) =
|
||||
let selectWith = connectionToSelectWith baseSelectAlias arrayConnectionSource arraySelectNode
|
||||
alias = S.Alias $ _ssPrefix $ _acsSource arrayConnectionSource
|
||||
in S.FISelectWith (S.Lateral True) selectWith alias
|
||||
|
||||
computedFieldToFromItem
|
||||
:: (ComputedFieldTableSetSource, SelectNode) -> S.FromItem
|
||||
:: (ComputedFieldTableSetSource, MultiRowSelectNode) -> S.FromItem
|
||||
computedFieldToFromItem (computedFieldTableSource, node) =
|
||||
let ComputedFieldTableSetSource fieldName selectTy source = computedFieldTableSource
|
||||
internalSelect = generateSQLSelect (S.BELit True) source node
|
||||
extractor = asJsonAggExtr selectTy (S.toAlias fieldName) PLSQNotRequired $
|
||||
_ssOrderBy source
|
||||
let ComputedFieldTableSetSource _ source = computedFieldTableSource
|
||||
internalSelect = generateSQLSelect (S.BELit True) source $ _mrsnSelectNode node
|
||||
alias = S.Alias $ _ssPrefix source
|
||||
select = S.mkSelect
|
||||
{ S.selExtr = [extractor]
|
||||
{ S.selExtr = _mrsnTopExtractors node
|
||||
, S.selFrom = Just $ S.FromExp [S.mkSelFromItem internalSelect alias]
|
||||
}
|
||||
in S.mkLateralFromItem select alias
|
||||
|
||||
generateSQLSelectFromArrayNode
|
||||
:: SelectSource
|
||||
-> ArraySelectNode
|
||||
-> MultiRowSelectNode
|
||||
-> S.BoolExp
|
||||
-> S.Select
|
||||
generateSQLSelectFromArrayNode selectSource arraySelectNode joinCondition =
|
||||
@ -1078,7 +1118,7 @@ generateSQLSelectFromArrayNode selectSource arraySelectNode joinCondition =
|
||||
, S.selFrom = Just $ S.FromExp [selectFrom]
|
||||
}
|
||||
where
|
||||
ArraySelectNode topExtractors selectNode = arraySelectNode
|
||||
MultiRowSelectNode topExtractors selectNode = arraySelectNode
|
||||
selectFrom = S.mkSelFromItem
|
||||
(generateSQLSelect joinCondition selectSource selectNode) $
|
||||
S.Alias $ _ssPrefix selectSource
|
||||
@ -1095,7 +1135,7 @@ mkAggregateSelect annAggSel =
|
||||
runWriter $ flip runReaderT strfyNum $
|
||||
processAnnAggregateSelect sourcePrefixes rootFieldName annAggSel
|
||||
selectNode = SelectNode nodeExtractors joinTree
|
||||
arrayNode = ArraySelectNode [topExtractor] selectNode
|
||||
arrayNode = MultiRowSelectNode [topExtractor] selectNode
|
||||
in prefixNumToAliases $
|
||||
generateSQLSelectFromArrayNode selectSource arrayNode $ S.BELit True
|
||||
where
|
||||
@ -1120,7 +1160,7 @@ mkSQLSelect jsonAggSelect annSel =
|
||||
selectNode = SelectNode nodeExtractors joinTree
|
||||
topExtractor = asJsonAggExtr jsonAggSelect rootFldAls permLimitSubQuery
|
||||
$ _ssOrderBy selectSource
|
||||
arrayNode = ArraySelectNode [topExtractor] selectNode
|
||||
arrayNode = MultiRowSelectNode [topExtractor] selectNode
|
||||
in prefixNumToAliases $
|
||||
generateSQLSelectFromArrayNode selectSource arrayNode $ S.BELit True
|
||||
where
|
||||
@ -1142,7 +1182,7 @@ mkConnectionSelect connectionSelect =
|
||||
runWriter $ flip runReaderT strfyNum $
|
||||
processConnectionSelect sourcePrefixes rootFieldName
|
||||
(S.Alias rootIdentifier) mempty connectionSelect
|
||||
selectNode = ArraySelectNode [topExtractor] $
|
||||
selectNode = MultiRowSelectNode [topExtractor] $
|
||||
SelectNode nodeExtractors joinTree
|
||||
in prefixNumToAliasesSelectWith $
|
||||
connectionToSelectWith (S.Alias rootIdentifier) connectionSource selectNode
|
||||
@ -1350,7 +1390,7 @@ processConnectionSelect sourcePrefixes fieldAlias relAlias colMapping connection
|
||||
connectionToSelectWith
|
||||
:: S.Alias
|
||||
-> ArrayConnectionSource
|
||||
-> ArraySelectNode
|
||||
-> MultiRowSelectNode
|
||||
-> S.SelectWithG S.Select
|
||||
connectionToSelectWith baseSelectAlias arrayConnectionSource arraySelectNode =
|
||||
let extractionSelect = S.mkSelect
|
||||
@ -1361,7 +1401,7 @@ connectionToSelectWith baseSelectAlias arrayConnectionSource arraySelectNode =
|
||||
where
|
||||
ArrayConnectionSource _ columnMapping maybeSplit maybeSlice selectSource =
|
||||
arrayConnectionSource
|
||||
ArraySelectNode topExtractors selectNode = arraySelectNode
|
||||
MultiRowSelectNode topExtractors selectNode = arraySelectNode
|
||||
baseSelectIdentifier = Identifier "__base_select"
|
||||
splitSelectIdentifier = Identifier "__split_select"
|
||||
sliceSelectIdentifier = Identifier "__slice_select"
|
||||
|
@ -79,20 +79,19 @@ data ArrayRelationSource
|
||||
instance Hashable ArrayRelationSource
|
||||
deriving instance Eq ArrayRelationSource
|
||||
|
||||
data ArraySelectNode
|
||||
= ArraySelectNode
|
||||
{ _asnTopExtractors :: ![PG.Extractor]
|
||||
, _asnSelectNode :: !SelectNode
|
||||
data MultiRowSelectNode
|
||||
= MultiRowSelectNode
|
||||
{ _mrsnTopExtractors :: ![PG.Extractor]
|
||||
, _mrsnSelectNode :: !SelectNode
|
||||
}
|
||||
|
||||
instance Semigroup ArraySelectNode where
|
||||
ArraySelectNode lTopExtrs lSelNode <> ArraySelectNode rTopExtrs rSelNode =
|
||||
ArraySelectNode (lTopExtrs <> rTopExtrs) (lSelNode <> rSelNode)
|
||||
instance Semigroup MultiRowSelectNode where
|
||||
MultiRowSelectNode lTopExtrs lSelNode <> MultiRowSelectNode rTopExtrs rSelNode =
|
||||
MultiRowSelectNode (lTopExtrs <> rTopExtrs) (lSelNode <> rSelNode)
|
||||
|
||||
data ComputedFieldTableSetSource
|
||||
= ComputedFieldTableSetSource
|
||||
{ _cftssFieldName :: !FieldName
|
||||
, _cftssSelectType :: !JsonAggSelect
|
||||
, _cftssSelectSource :: !SelectSource
|
||||
} deriving (Generic)
|
||||
instance Hashable ComputedFieldTableSetSource
|
||||
@ -114,9 +113,9 @@ instance Hashable ArrayConnectionSource
|
||||
data JoinTree
|
||||
= JoinTree
|
||||
{ _jtObjectRelations :: !(HM.HashMap ObjectRelationSource SelectNode)
|
||||
, _jtArrayRelations :: !(HM.HashMap ArrayRelationSource ArraySelectNode)
|
||||
, _jtArrayConnections :: !(HM.HashMap ArrayConnectionSource ArraySelectNode)
|
||||
, _jtComputedFieldTableSets :: !(HM.HashMap ComputedFieldTableSetSource SelectNode)
|
||||
, _jtArrayRelations :: !(HM.HashMap ArrayRelationSource MultiRowSelectNode)
|
||||
, _jtArrayConnections :: !(HM.HashMap ArrayConnectionSource MultiRowSelectNode)
|
||||
, _jtComputedFieldTableSets :: !(HM.HashMap ComputedFieldTableSetSource MultiRowSelectNode)
|
||||
}
|
||||
|
||||
instance Semigroup JoinTree where
|
||||
|
@ -322,19 +322,10 @@ transformAnnFields path fields = do
|
||||
mkScalarComputedFieldSelect :: ScalarComputedField b -> (AnnFieldG b (Const Void) (UnpreparedValue b))
|
||||
mkScalarComputedFieldSelect ScalarComputedField{..} =
|
||||
let functionArgs = flip FunctionArgsExp mempty
|
||||
$ functionArgsWithTableRowAndSession _scfTableArgument _scfSessionArgument
|
||||
$ functionArgsWithTableRowAndSession UVSession _scfTableArgument _scfSessionArgument
|
||||
fieldSelect = flip CFSScalar Nothing
|
||||
$ ComputedFieldScalarSelect _scfFunction functionArgs _scfType Nothing
|
||||
in AFComputedField _scfXField _scfName fieldSelect
|
||||
where
|
||||
functionArgsWithTableRowAndSession
|
||||
:: FunctionTableArgument
|
||||
-> Maybe FunctionSessionArgument
|
||||
-> [ArgumentExp b (UnpreparedValue b)]
|
||||
functionArgsWithTableRowAndSession _ Nothing = [AETableRow Nothing] -- No session argument
|
||||
functionArgsWithTableRowAndSession (FTAFirst) _ = [AETableRow Nothing, AESession UVSession]
|
||||
functionArgsWithTableRowAndSession (FTANamed _ 0) _ = [AETableRow Nothing, AESession UVSession] -- Index is 0 implies table argument is first
|
||||
functionArgsWithTableRowAndSession _ _ = [AESession UVSession, AETableRow Nothing]
|
||||
|
||||
|
||||
mapToNonEmpty :: RemoteJoinMap -> Maybe RemoteJoins
|
||||
|
@ -36,7 +36,7 @@ orderByExp
|
||||
=> SourceName
|
||||
-> TableInfo b
|
||||
-> SelPermInfo b
|
||||
-> m (Parser 'Input n [IR.AnnOrderByItemG b (UnpreparedValue b)])
|
||||
-> m (Parser 'Input n [IR.AnnotatedOrderByItemG b (UnpreparedValue b)])
|
||||
orderByExp sourceName tableInfo selectPermissions = memoizeOn 'orderByExp (sourceName, tableInfoName tableInfo) $ do
|
||||
tableGQLName <- getTableGQLName tableInfo
|
||||
let name = tableGQLName <> $$(G.litName "_order_by")
|
||||
@ -48,18 +48,19 @@ orderByExp sourceName tableInfo selectPermissions = memoizeOn 'orderByExp (sourc
|
||||
where
|
||||
mkField
|
||||
:: FieldInfo b
|
||||
-> m (Maybe (InputFieldsParser n (Maybe [IR.AnnOrderByItemG b (UnpreparedValue b)])))
|
||||
-> m (Maybe (InputFieldsParser n (Maybe [IR.AnnotatedOrderByItemG b (UnpreparedValue b)])))
|
||||
mkField fieldInfo = runMaybeT $
|
||||
case fieldInfo of
|
||||
FIColumn columnInfo -> do
|
||||
let fieldName = pgiName columnInfo
|
||||
pure $ P.fieldOptional fieldName Nothing (orderByOperator @b)
|
||||
<&> fmap (pure . mkOrderByItemG @b (IR.AOCColumn columnInfo)) . join
|
||||
|
||||
FIRelationship relationshipInfo -> do
|
||||
remoteTableInfo <- askTableInfo @b sourceName $ riRTable relationshipInfo
|
||||
fieldName <- hoistMaybe $ G.mkName $ relNameToTxt $ riName relationshipInfo
|
||||
perms <- MaybeT $ tableSelectPermissions remoteTableInfo
|
||||
let newPerms = (fmap . fmap) partialSQLExpToUnpreparedValue $ spiFilter perms
|
||||
let newPerms = fmap partialSQLExpToUnpreparedValue <$> spiFilter perms
|
||||
case riType relationshipInfo of
|
||||
ObjRel -> do
|
||||
otherTableParser <- lift $ orderByExp sourceName remoteTableInfo perms
|
||||
@ -72,7 +73,33 @@ orderByExp sourceName tableInfo selectPermissions = memoizeOn 'orderByExp (sourc
|
||||
pure $ do
|
||||
aggregationOrderBy <- join <$> P.fieldOptional aggregateFieldName Nothing (P.nullable aggregationParser)
|
||||
pure $ fmap (map $ fmap $ IR.AOCArrayAggregation relationshipInfo newPerms) aggregationOrderBy
|
||||
FIComputedField _ -> empty
|
||||
|
||||
FIComputedField ComputedFieldInfo{..} -> do
|
||||
let ComputedFieldFunction{..} = _cfiFunction
|
||||
mkComputedFieldOrderBy =
|
||||
let functionArgs = flip IR.FunctionArgsExp mempty
|
||||
$ IR.functionArgsWithTableRowAndSession P.UVSession _cffTableArgument _cffSessionArgument
|
||||
in IR.ComputedFieldOrderBy _cfiXComputedFieldInfo _cfiName _cffName functionArgs
|
||||
fieldName <- hoistMaybe $ G.mkName $ toTxt _cfiName
|
||||
guard $ _cffInputArgs == mempty -- No input arguments other than table row and session argument
|
||||
case _cfiReturnType of
|
||||
CFRScalar scalarType -> do
|
||||
let computedFieldOrderBy = mkComputedFieldOrderBy $ IR.CFOBEScalar scalarType
|
||||
pure $ P.fieldOptional fieldName Nothing (orderByOperator @b)
|
||||
<&> fmap (pure . mkOrderByItemG @b (IR.AOCComputedField computedFieldOrderBy)) . join
|
||||
CFRSetofTable table -> do
|
||||
let aggregateFieldName = fieldName <> $$(G.litName "_aggregate")
|
||||
tableInfo' <- askTableInfo @b sourceName table
|
||||
perms <- MaybeT $ tableSelectPermissions tableInfo'
|
||||
let newPerms = fmap partialSQLExpToUnpreparedValue <$> spiFilter perms
|
||||
aggregationParser <- lift $ orderByAggregation sourceName tableInfo' perms
|
||||
pure $ do
|
||||
aggregationOrderBy <- join <$> P.fieldOptional aggregateFieldName Nothing (P.nullable aggregationParser)
|
||||
pure $ fmap (map $ fmap $ IR.AOCComputedField
|
||||
. mkComputedFieldOrderBy
|
||||
. IR.CFOBETableAggregation table newPerms
|
||||
) aggregationOrderBy
|
||||
|
||||
FIRemoteRelationship _ -> empty
|
||||
|
||||
|
||||
@ -85,7 +112,7 @@ orderByAggregation
|
||||
=> SourceName
|
||||
-> TableInfo b
|
||||
-> SelPermInfo b
|
||||
-> m (Parser 'Input n [IR.OrderByItemG b (IR.AnnAggregateOrderBy b)])
|
||||
-> m (Parser 'Input n [IR.OrderByItemG b (IR.AnnotatedAggregateOrderBy b)])
|
||||
orderByAggregation sourceName tableInfo selectPermissions = memoizeOn 'orderByAggregation (sourceName, tableName) do
|
||||
-- WIP NOTE
|
||||
-- there is heavy duplication between this and Select.tableAggregationFields
|
||||
@ -125,7 +152,7 @@ orderByAggregation sourceName tableInfo selectPermissions = memoizeOn 'orderByAg
|
||||
:: G.Name
|
||||
-> G.Name
|
||||
-> InputFieldsParser n [(ColumnInfo b, (BasicOrderType b, NullsOrderType b))]
|
||||
-> InputFieldsParser n (Maybe [IR.OrderByItemG b (IR.AnnAggregateOrderBy b)])
|
||||
-> InputFieldsParser n (Maybe [IR.OrderByItemG b (IR.AnnotatedAggregateOrderBy b)])
|
||||
parseOperator operator tableGQLName columns =
|
||||
let opText = G.unName operator
|
||||
objectName = tableGQLName <> $$(G.litName "_") <> operator <> $$(G.litName "_order_by")
|
||||
|
@ -625,7 +625,7 @@ tableOrderByArg
|
||||
=> SourceName
|
||||
-> TableInfo b
|
||||
-> SelPermInfo b
|
||||
-> m (InputFieldsParser n (Maybe (NonEmpty (IR.AnnOrderByItemG b (UnpreparedValue b)))))
|
||||
-> m (InputFieldsParser n (Maybe (NonEmpty (IR.AnnotatedOrderByItemG b (UnpreparedValue b)))))
|
||||
tableOrderByArg sourceName tableInfo selectPermissions = do
|
||||
orderByParser <- orderByExp sourceName tableInfo selectPermissions
|
||||
pure $ do
|
||||
@ -738,7 +738,7 @@ tableConnectionArgs pkeyColumns sourceName tableInfo selectPermissions = do
|
||||
where
|
||||
base64Text = base64Decode <$> P.string
|
||||
|
||||
appendPrimaryKeyOrderBy :: NonEmpty (IR.AnnOrderByItemG b v) -> NonEmpty (IR.AnnOrderByItemG b v)
|
||||
appendPrimaryKeyOrderBy :: NonEmpty (IR.AnnotatedOrderByItemG b v) -> NonEmpty (IR.AnnotatedOrderByItemG b v)
|
||||
appendPrimaryKeyOrderBy orderBys@(h NE.:| t) =
|
||||
let orderByColumnNames =
|
||||
orderBys ^.. traverse . to IR.obiColumn . IR._AOCColumn . to pgiColumn
|
||||
@ -748,7 +748,7 @@ tableConnectionArgs pkeyColumns sourceName tableInfo selectPermissions = do
|
||||
in h NE.:| (t <> pkeyOrderBys)
|
||||
|
||||
parseConnectionSplit
|
||||
:: Maybe (NonEmpty (IR.AnnOrderByItemG b (UnpreparedValue b)))
|
||||
:: Maybe (NonEmpty (IR.AnnotatedOrderByItemG b (UnpreparedValue b)))
|
||||
-> IR.ConnectionSplitKind
|
||||
-> BL.ByteString
|
||||
-> n (NonEmpty (IR.ConnectionSplit b (UnpreparedValue b)))
|
||||
@ -774,11 +774,15 @@ tableConnectionArgs pkeyColumns sourceName tableInfo selectPermissions = do
|
||||
pgValue <- liftQErr $ parseScalarValueColumnType columnType orderByItemValue
|
||||
let unresolvedValue = UVParameter Nothing $ ColumnValue columnType pgValue
|
||||
pure $ IR.ConnectionSplit splitKind unresolvedValue $
|
||||
IR.OrderByItemG orderType (() <$ annObCol) nullsOrder
|
||||
IR.OrderByItemG orderType annObCol nullsOrder
|
||||
where
|
||||
throwInvalidCursor = parseError "the \"after\" or \"before\" cursor is invalid"
|
||||
liftQErr = either (parseError . qeError) pure . runExcept
|
||||
|
||||
mkAggregateOrderByPath = \case
|
||||
IR.AAOCount -> [J.Key "count"]
|
||||
IR.AAOOp t col -> [J.Key t, J.Key $ toTxt $ pgiColumn col]
|
||||
|
||||
getPathFromOrderBy = \case
|
||||
IR.AOCColumn pgColInfo ->
|
||||
let pathElement = J.Key $ toTxt $ pgiColumn pgColInfo
|
||||
@ -788,15 +792,24 @@ tableConnectionArgs pkeyColumns sourceName tableInfo selectPermissions = do
|
||||
in pathElement : getPathFromOrderBy obCol
|
||||
IR.AOCArrayAggregation relInfo _ aggOb ->
|
||||
let fieldName = J.Key $ relNameToTxt (riName relInfo) <> "_aggregate"
|
||||
in fieldName : case aggOb of
|
||||
IR.AAOCount -> [J.Key "count"]
|
||||
IR.AAOOp t col -> [J.Key t, J.Key $ toTxt $ pgiColumn col]
|
||||
in fieldName : mkAggregateOrderByPath aggOb
|
||||
IR.AOCComputedField cfob ->
|
||||
let fieldNameText = computedFieldNameToText $ IR._cfobName cfob
|
||||
in case IR._cfobOrderByElement cfob of
|
||||
IR.CFOBEScalar _ -> [J.Key fieldNameText]
|
||||
IR.CFOBETableAggregation _ _ aggOb ->
|
||||
J.Key (fieldNameText <> "_aggregate") : mkAggregateOrderByPath aggOb
|
||||
|
||||
getOrderByColumnType = \case
|
||||
IR.AOCColumn pgColInfo -> pgiType pgColInfo
|
||||
IR.AOCObjectRelation _ _ obCol -> getOrderByColumnType obCol
|
||||
IR.AOCArrayAggregation _ _ aggOb ->
|
||||
case aggOb of
|
||||
IR.AOCArrayAggregation _ _ aggOb -> aggregateOrderByColumnType aggOb
|
||||
IR.AOCComputedField cfob ->
|
||||
case IR._cfobOrderByElement cfob of
|
||||
IR.CFOBEScalar scalarType -> ColumnScalar scalarType
|
||||
IR.CFOBETableAggregation _ _ aggOb -> aggregateOrderByColumnType aggOb
|
||||
where
|
||||
aggregateOrderByColumnType = \case
|
||||
IR.AAOCount -> ColumnScalar (aggregateOrderByCountType @b)
|
||||
IR.AAOOp _ colInfo -> pgiType colInfo
|
||||
|
||||
@ -1311,7 +1324,7 @@ functionArgs functionTrackedAs (toList -> inputArgs) = do
|
||||
|
||||
tablePermissionsInfo :: Backend b => SelPermInfo b -> TablePerms b
|
||||
tablePermissionsInfo selectPermissions = IR.TablePerm
|
||||
{ IR._tpFilter = (fmap . fmap) partialSQLExpToUnpreparedValue $ spiFilter selectPermissions
|
||||
{ IR._tpFilter = fmap partialSQLExpToUnpreparedValue <$> spiFilter selectPermissions
|
||||
, IR._tpLimit = spiLimit selectPermissions
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ convSelCol
|
||||
-> SelCol
|
||||
-> m [ExtCol ('Postgres 'Vanilla)]
|
||||
convSelCol _ _ (SCExtSimple cn) =
|
||||
return [ECSimple cn]
|
||||
pure [ECSimple cn]
|
||||
convSelCol fieldInfoMap _ (SCExtRel rn malias selQ) = do
|
||||
-- Point to the name key
|
||||
let pgWhenRelErr = "only relationships can be expanded"
|
||||
@ -56,7 +56,7 @@ convSelCol fieldInfoMap _ (SCExtRel rn malias selQ) = do
|
||||
let (RelInfo _ _ _ relTab _ _ _) = relInfo
|
||||
(rfim, rspi) <- fetchRelDet rn relTab
|
||||
resolvedSelQ <- resolveStar rfim rspi selQ
|
||||
return [ECRel rn malias resolvedSelQ]
|
||||
pure [ECRel rn malias resolvedSelQ]
|
||||
convSelCol fieldInfoMap spi (SCStar wildcard) =
|
||||
convWildcard fieldInfoMap spi wildcard
|
||||
|
||||
@ -68,7 +68,7 @@ convWildcard
|
||||
-> m [ExtCol ('Postgres 'Vanilla)]
|
||||
convWildcard fieldInfoMap selPermInfo wildcard =
|
||||
case wildcard of
|
||||
Star -> return simpleCols
|
||||
Star -> pure simpleCols
|
||||
(StarDot wc) -> (simpleCols ++) <$> (catMaybes <$> relExtCols wc)
|
||||
where
|
||||
cols = spiCols selPermInfo
|
||||
@ -85,7 +85,7 @@ convWildcard fieldInfoMap selPermInfo wildcard =
|
||||
|
||||
forM mRelSelPerm $ \relSelPermInfo -> do
|
||||
rExtCols <- convWildcard (_tciFieldInfoMap $ _tiCoreInfo relTabInfo) relSelPermInfo wc
|
||||
return $ ECRel relName Nothing $
|
||||
pure $ ECRel relName Nothing $
|
||||
SelectG rExtCols Nothing Nothing Nothing Nothing
|
||||
|
||||
relExtCols wc = mapM (mkRelCol wc) relColInfos
|
||||
@ -99,13 +99,13 @@ resolveStar
|
||||
resolveStar fim selPermInfo (SelectG selCols mWh mOb mLt mOf) = do
|
||||
procOverrides <- fmap (concat . catMaybes) $ withPathK "columns" $
|
||||
indexedForM selCols $ \selCol -> case selCol of
|
||||
(SCStar _) -> return Nothing
|
||||
(SCStar _) -> pure Nothing
|
||||
_ -> Just <$> convSelCol fim selPermInfo selCol
|
||||
everything <- case wildcards of
|
||||
[] -> return []
|
||||
[] -> pure []
|
||||
_ -> convWildcard fim selPermInfo $ maximum wildcards
|
||||
let extCols = unionBy equals procOverrides everything
|
||||
return $ SelectG extCols mWh mOb mLt mOf
|
||||
pure $ SelectG extCols mWh mOb mLt mOf
|
||||
where
|
||||
wildcards = lefts $ map mkEither selCols
|
||||
|
||||
@ -121,7 +121,7 @@ convOrderByElem
|
||||
=> SessVarBldr ('Postgres 'Vanilla) m
|
||||
-> (FieldInfoMap (FieldInfo ('Postgres 'Vanilla)), SelPermInfo ('Postgres 'Vanilla))
|
||||
-> OrderByCol
|
||||
-> m (AnnOrderByElement ('Postgres 'Vanilla) S.SQLExp)
|
||||
-> m (AnnotatedOrderByElement ('Postgres 'Vanilla) S.SQLExp)
|
||||
convOrderByElem sessVarBldr (flds, spi) = \case
|
||||
OCPG fldName -> do
|
||||
fldInfo <- askFieldInfo flds fldName
|
||||
@ -134,7 +134,7 @@ convOrderByElem sessVarBldr (flds, spi) = \case
|
||||
[ fldName <<> " has type 'geometry'"
|
||||
, " and cannot be used in order_by"
|
||||
]
|
||||
else return $ AOCColumn colInfo
|
||||
else pure $ AOCColumn colInfo
|
||||
FIRelationship _ -> throw400 UnexpectedPayload $ mconcat
|
||||
[ fldName <<> " is a"
|
||||
, " relationship and should be expanded"
|
||||
@ -165,8 +165,7 @@ convOrderByElem sessVarBldr (flds, spi) = \case
|
||||
]
|
||||
(relFim, relSelPermInfo) <- fetchRelDet (riName relInfo) (riRTable relInfo)
|
||||
resolvedSelFltr <- convAnnBoolExpPartialSQL sessVarBldr $ spiFilter relSelPermInfo
|
||||
AOCObjectRelation relInfo resolvedSelFltr <$>
|
||||
convOrderByElem sessVarBldr (relFim, relSelPermInfo) rest
|
||||
AOCObjectRelation relInfo resolvedSelFltr <$> convOrderByElem sessVarBldr (relFim, relSelPermInfo) rest
|
||||
FIRemoteRelationship {} ->
|
||||
throw400 UnexpectedPayload (mconcat [ fldName <<> " is a remote field" ])
|
||||
|
||||
@ -195,11 +194,11 @@ convSelectQ table fieldInfoMap selPermInfo selQ sessVarBldr prepValBldr = do
|
||||
(colInfo, caseBoolExpMaybe) <- convExtSimple fieldInfoMap selPermInfo pgCol
|
||||
resolvedCaseBoolExp <-
|
||||
traverse (convAnnColumnCaseBoolExpPartialSQL sessVarBldr) caseBoolExpMaybe
|
||||
return (fromCol @('Postgres 'Vanilla) pgCol, mkAnnColumnField colInfo resolvedCaseBoolExp Nothing)
|
||||
pure (fromCol @('Postgres 'Vanilla) pgCol, mkAnnColumnField colInfo resolvedCaseBoolExp Nothing)
|
||||
(ECRel relName mAlias relSelQ) -> do
|
||||
annRel <- convExtRel fieldInfoMap relName mAlias
|
||||
relSelQ sessVarBldr prepValBldr
|
||||
return ( fromRel $ fromMaybe relName mAlias
|
||||
pure ( fromRel $ fromMaybe relName mAlias
|
||||
, either AFObjectRelation AFArrayRelation annRel
|
||||
)
|
||||
|
||||
@ -221,7 +220,7 @@ convSelectQ table fieldInfoMap selPermInfo selQ sessVarBldr prepValBldr = do
|
||||
tabArgs = SelectArgs wClause annOrdByM mQueryLimit (fromIntegral <$> mQueryOffset) Nothing
|
||||
|
||||
strfyNum <- stringifyNum . _sccSQLGenCtx <$> askServerConfigCtx
|
||||
return $ AnnSelectG annFlds tabFrom tabPerm tabArgs strfyNum
|
||||
pure $ AnnSelectG annFlds tabFrom tabPerm tabArgs strfyNum
|
||||
|
||||
where
|
||||
mQueryOffset = sqOffset selQ
|
||||
@ -264,10 +263,10 @@ convExtRel fieldInfoMap relName mAlias selQ sessVarBldr prepValBldr = do
|
||||
case relTy of
|
||||
ObjRel -> do
|
||||
when misused $ throw400 UnexpectedPayload objRelMisuseMsg
|
||||
return $ Left $ AnnRelationSelectG (fromMaybe relName mAlias) colMapping $
|
||||
pure $ Left $ AnnRelationSelectG (fromMaybe relName mAlias) colMapping $
|
||||
AnnObjectSelectG (_asnFields annSel) relTab $ _tpFilter $ _asnPerm annSel
|
||||
ArrRel ->
|
||||
return $ Right $ ASSimple $ AnnRelationSelectG (fromMaybe relName mAlias)
|
||||
pure $ Right $ ASSimple $ AnnRelationSelectG (fromMaybe relName mAlias)
|
||||
colMapping annSel
|
||||
where
|
||||
pgWhenRelErr = "only relationships can be expanded"
|
||||
|
@ -96,9 +96,9 @@ data ConnectionSplit (b :: BackendType) v
|
||||
= ConnectionSplit
|
||||
{ _csKind :: !ConnectionSplitKind
|
||||
, _csValue :: !v
|
||||
, _csOrderBy :: !(OrderByItemG b (AnnOrderByElementG b ()))
|
||||
, _csOrderBy :: !(OrderByItemG b (AnnotatedOrderByElement b v))
|
||||
} deriving (Functor, Generic, Foldable, Traversable)
|
||||
instance (Backend b, Hashable (ColumnInfo b), Hashable v) => Hashable (ConnectionSplit b v)
|
||||
instance (Backend b, Hashable (ColumnInfo b), Hashable v, Hashable (BooleanOperators b v)) => Hashable (ConnectionSplit b v)
|
||||
|
||||
data ConnectionSlice
|
||||
= SliceFirst !Int
|
||||
@ -133,7 +133,7 @@ type SelectFrom b = SelectFromG b (SQLExpression b)
|
||||
data SelectArgsG (b :: BackendType) v
|
||||
= SelectArgs
|
||||
{ _saWhere :: !(Maybe (AnnBoolExp b v))
|
||||
, _saOrderBy :: !(Maybe (NE.NonEmpty (AnnOrderByItemG b v)))
|
||||
, _saOrderBy :: !(Maybe (NE.NonEmpty (AnnotatedOrderByItemG b v)))
|
||||
, _saLimit :: !(Maybe Int)
|
||||
, _saOffset :: !(Maybe Int64)
|
||||
, _saDistinct :: !(Maybe (NE.NonEmpty (Column b)))
|
||||
@ -159,24 +159,51 @@ noSelectArgs = SelectArgs Nothing Nothing Nothing Nothing Nothing
|
||||
|
||||
-- Order by argument
|
||||
|
||||
data AnnOrderByElementG (b :: BackendType) v
|
||||
= AOCColumn !(ColumnInfo b)
|
||||
| AOCObjectRelation !(RelInfo b) !v !(AnnOrderByElementG b v)
|
||||
| AOCArrayAggregation !(RelInfo b) !v !(AnnAggregateOrderBy b)
|
||||
-- | The order by element for a computed field based on its return type
|
||||
data ComputedFieldOrderByElement (b :: BackendType) v
|
||||
= CFOBEScalar !(ScalarType b)
|
||||
-- ^ Sort by the scalar computed field
|
||||
| CFOBETableAggregation !(TableName b)
|
||||
!(AnnBoolExp b v) -- ^ Permission filter of the retuning table
|
||||
!(AnnotatedAggregateOrderBy b)
|
||||
-- ^ Sort by aggregation fields of table rows returned by computed field
|
||||
deriving (Generic, Functor, Foldable, Traversable)
|
||||
deriving instance (Backend b, Eq v) => Eq (AnnOrderByElementG b v)
|
||||
instance (Backend b, Hashable v) => Hashable (AnnOrderByElementG b v)
|
||||
deriving instance (Backend b, Eq v, Eq (BooleanOperators b v)) => Eq (ComputedFieldOrderByElement b v)
|
||||
instance (Backend b, Hashable v, Hashable (BooleanOperators b v)) => Hashable (ComputedFieldOrderByElement b v)
|
||||
|
||||
data AnnAggregateOrderBy (b :: BackendType)
|
||||
data ComputedFieldOrderBy (b :: BackendType) v
|
||||
= ComputedFieldOrderBy
|
||||
{ _cfobXField :: !(XComputedField b)
|
||||
, _cfobName :: !ComputedFieldName
|
||||
, _cfobFunction :: !(FunctionName b)
|
||||
, _cfobFunctionArgsExp :: !(FunctionArgsExpTableRow b v)
|
||||
, _cfobOrderByElement :: !(ComputedFieldOrderByElement b v)
|
||||
} deriving (Generic, Functor, Foldable, Traversable)
|
||||
deriving instance (Backend b, Eq v, Eq (BooleanOperators b v)) => Eq (ComputedFieldOrderBy b v)
|
||||
instance (Backend b, Hashable v, Hashable (BooleanOperators b v)) => Hashable (ComputedFieldOrderBy b v)
|
||||
|
||||
data AnnotatedOrderByElement (b :: BackendType) v
|
||||
= AOCColumn !(ColumnInfo b)
|
||||
| AOCObjectRelation !(RelInfo b)
|
||||
!(AnnBoolExp b v) -- ^ Permission filter of the remote table to which the relationship is defined
|
||||
!(AnnotatedOrderByElement b v)
|
||||
| AOCArrayAggregation !(RelInfo b)
|
||||
!(AnnBoolExp b v) -- ^ Permission filter of the remote table to which the relationship is defined
|
||||
!(AnnotatedAggregateOrderBy b)
|
||||
| AOCComputedField !(ComputedFieldOrderBy b v)
|
||||
deriving (Generic, Functor, Foldable, Traversable)
|
||||
deriving instance (Backend b, Eq v, Eq (BooleanOperators b v)) => Eq (AnnotatedOrderByElement b v)
|
||||
instance (Backend b, Hashable v, Hashable (BooleanOperators b v)) => Hashable (AnnotatedOrderByElement b v)
|
||||
|
||||
data AnnotatedAggregateOrderBy (b :: BackendType)
|
||||
= AAOCount
|
||||
| AAOOp !Text !(ColumnInfo b)
|
||||
deriving (Generic)
|
||||
deriving instance (Backend b) => Eq (AnnAggregateOrderBy b)
|
||||
instance (Backend b) => Hashable (AnnAggregateOrderBy b)
|
||||
deriving instance (Backend b) => Eq (AnnotatedAggregateOrderBy b)
|
||||
instance (Backend b) => Hashable (AnnotatedAggregateOrderBy b)
|
||||
|
||||
type AnnOrderByElement b v = AnnOrderByElementG b (AnnBoolExp b v)
|
||||
type AnnOrderByItemG b v = OrderByItemG b (AnnOrderByElement b v)
|
||||
type AnnOrderByItem b = AnnOrderByItemG b (SQLExpression b)
|
||||
type AnnotatedOrderByItemG b v = OrderByItemG b (AnnotatedOrderByElement b v)
|
||||
type AnnotatedOrderByItem b = AnnotatedOrderByItemG b (SQLExpression b)
|
||||
|
||||
|
||||
-- Fields
|
||||
@ -466,6 +493,17 @@ type FunctionArgExp b = FunctionArgsExpG (SQLExpression b)
|
||||
emptyFunctionArgsExp :: FunctionArgsExpG a
|
||||
emptyFunctionArgsExp = FunctionArgsExp [] HM.empty
|
||||
|
||||
functionArgsWithTableRowAndSession
|
||||
:: v
|
||||
-> FunctionTableArgument
|
||||
-> Maybe FunctionSessionArgument
|
||||
-> [ArgumentExp b v]
|
||||
functionArgsWithTableRowAndSession _ _ Nothing = [AETableRow Nothing] -- No session argument
|
||||
functionArgsWithTableRowAndSession sess (FTAFirst) _ = [AETableRow Nothing, AESession sess]
|
||||
functionArgsWithTableRowAndSession sess (FTANamed _ 0) _ = [AETableRow Nothing, AESession sess] -- Index is 0 implies table argument is first
|
||||
functionArgsWithTableRowAndSession sess _ _ = [AESession sess, AETableRow Nothing]
|
||||
|
||||
|
||||
-- | If argument positional index is less than or equal to length of
|
||||
-- 'positional' arguments then insert the value in 'positional' arguments else
|
||||
-- insert the value with argument name in 'named' arguments
|
||||
@ -488,4 +526,4 @@ insertFunctionArg argName idx value (FunctionArgsExp positional named) =
|
||||
|
||||
$(makeLenses ''AnnSelectG)
|
||||
$(makePrisms ''AnnFieldG)
|
||||
$(makePrisms ''AnnOrderByElementG)
|
||||
$(makePrisms ''AnnotatedOrderByElement)
|
||||
|
@ -0,0 +1,21 @@
|
||||
description: Fetch tracks order by their size which is a computed field
|
||||
url: /v1/graphql
|
||||
status: 200
|
||||
query:
|
||||
query: |
|
||||
query {
|
||||
Track(order_by: {size: desc}, limit: 2){
|
||||
name
|
||||
bytes
|
||||
size
|
||||
}
|
||||
}
|
||||
response:
|
||||
data:
|
||||
Track:
|
||||
- name: Restless
|
||||
bytes: 9836284
|
||||
size: 9
|
||||
- name: Mistress
|
||||
bytes: 7521946
|
||||
size: 7
|
@ -0,0 +1,31 @@
|
||||
description: Fetch author with order by using computed field aggregate returns set of articles with a search query via session variable
|
||||
url: /v1/graphql
|
||||
status: 200
|
||||
headers:
|
||||
X-Hasura-Role: admin
|
||||
X-Hasura-Search: '1'
|
||||
query:
|
||||
query: |
|
||||
query{
|
||||
author(order_by: {get_articles_aggregate: {count: desc}}){
|
||||
id
|
||||
name
|
||||
get_articles{
|
||||
id
|
||||
title
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
response:
|
||||
data:
|
||||
author:
|
||||
- id: 1
|
||||
name: Author 1
|
||||
get_articles:
|
||||
- id: 1
|
||||
title: Article 1
|
||||
content: Sample article content 1
|
||||
- id: 2
|
||||
name: Author 2
|
||||
get_articles: []
|
@ -11,11 +11,13 @@ args:
|
||||
phone INTEGER,
|
||||
address TEXT
|
||||
);
|
||||
|
||||
create table author(
|
||||
id serial primary key,
|
||||
name text unique,
|
||||
contact_id INTEGER REFERENCES contact(id)
|
||||
);
|
||||
|
||||
CREATE TABLE article (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title TEXT,
|
||||
@ -24,14 +26,27 @@ args:
|
||||
is_published BOOLEAN,
|
||||
published_on TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE FUNCTION get_articles(hasura_session json, author_row author)
|
||||
RETURNS SETOF article AS $$
|
||||
SELECT *
|
||||
FROM article
|
||||
WHERE author_id = author_row.id AND
|
||||
( title ilike ('%' || (hasura_session ->> 'x-hasura-search') || '%')
|
||||
OR content ilike ('%' || (hasura_session ->> 'x-hasura-search') || '%')
|
||||
)
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
insert into contact (phone)
|
||||
values
|
||||
(1234567890),
|
||||
(1234567891);
|
||||
|
||||
insert into author (name, contact_id)
|
||||
values
|
||||
('Author 1', 2),
|
||||
('Author 2', 1);
|
||||
|
||||
insert into article (title,content,author_id,is_published)
|
||||
values
|
||||
(
|
||||
@ -56,6 +71,7 @@ args:
|
||||
album_id INTEGER NOT NULL PRIMARY KEY,
|
||||
title TEXT NOT NULL
|
||||
);
|
||||
|
||||
create table "Track" (
|
||||
track_id INTEGER NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
@ -63,8 +79,10 @@ args:
|
||||
milliseconds INTEGER NOT NULL,
|
||||
bytes INTEGER NOT NULL
|
||||
);
|
||||
|
||||
insert into "Album" (album_id, title)
|
||||
values ( 1, 'Big Ones' ), (2, 'Face Lift');
|
||||
|
||||
insert into "Track"
|
||||
(track_id, name, album_id, milliseconds, bytes)
|
||||
values
|
||||
@ -75,16 +93,24 @@ args:
|
||||
( 5, 'Random', 2, 1094732, 6032547) ,
|
||||
( 6, 'Good One', 2, 346208, 6732987) ,
|
||||
( 7, 'Mistress', 2, 420985, 7521946);
|
||||
|
||||
CREATE FUNCTION track_size_mb(track_row "Track")
|
||||
RETURNS INTEGER AS $$
|
||||
SELECT (track_row.bytes / 1048576)
|
||||
$$ LANGUAGE SQL STABLE;
|
||||
|
||||
CREATE table "Tag" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
album_id INTEGER NOT NULL REFERENCES "Album"(album_id)
|
||||
);
|
||||
|
||||
INSERT INTO "Tag" (name, album_id)
|
||||
VALUES
|
||||
( 'Rock', 1),
|
||||
( 'Folk', 1),
|
||||
( 'Hip Hop', 2);
|
||||
|
||||
CREATE TABLE employee (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
@ -109,6 +135,16 @@ args:
|
||||
schema: public
|
||||
name: article
|
||||
|
||||
# Computed field to author table
|
||||
- type: add_computed_field
|
||||
args:
|
||||
table: author
|
||||
name: get_articles
|
||||
definition:
|
||||
function: get_articles
|
||||
table_argument: author_row
|
||||
session_argument: hasura_session
|
||||
|
||||
#Object relationship
|
||||
- type: create_object_relationship
|
||||
args:
|
||||
@ -161,6 +197,14 @@ args:
|
||||
table: Track
|
||||
column: album_id
|
||||
|
||||
# Create computed field for Track table
|
||||
- type: add_computed_field
|
||||
args:
|
||||
table: Track
|
||||
name: size
|
||||
definition:
|
||||
function: track_size_mb
|
||||
|
||||
# Insert values in Album and Track
|
||||
- type: track_table
|
||||
args:
|
||||
|
@ -3,9 +3,11 @@ args:
|
||||
- type: run_sql
|
||||
args:
|
||||
sql: |
|
||||
DROP FUNCTION get_articles(json, author);
|
||||
DROP TABLE article;
|
||||
DROP TABLE author;
|
||||
DROP TABLE contact;
|
||||
DROP FUNCTION track_size_mb("Track");
|
||||
DROP TABLE "Track";
|
||||
DROP TABLE "Tag";
|
||||
DROP TABLE "Album";
|
||||
|
@ -894,6 +894,12 @@ class TestGraphQLQueryOrderBy:
|
||||
def test_album_order_by_tracks_tags(self, hge_ctx, transport):
|
||||
check_query_f(hge_ctx, self.dir() + '/album_order_by_tracks_tags.yaml', transport)
|
||||
|
||||
def test_Track_order_by_size(self, hge_ctx, transport):
|
||||
check_query_f(hge_ctx, self.dir() + '/Track_order_by_size.yaml', transport)
|
||||
|
||||
def test_author_order_by_get_articles_aggregate(self, hge_ctx, transport):
|
||||
check_query_f(hge_ctx, self.dir() + '/author_order_by_get_articles_aggregate.yaml', transport)
|
||||
|
||||
@classmethod
|
||||
def dir(cls):
|
||||
return 'queries/graphql_query/order_by'
|
||||
|
Loading…
Reference in New Issue
Block a user