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:
Rakesh Emmadi 2021-07-27 21:57:28 +05:30 committed by hasura-bot
parent e4155e4c5b
commit 5cfac6ea87
15 changed files with 348 additions and 136 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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
}

View File

@ -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"

View File

@ -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)

View File

@ -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

View File

@ -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: []

View File

@ -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:

View File

@ -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";

View File

@ -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'