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 ## Next release
(Add entries below in the order of server, console, cli, docs, others) (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: 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: 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. - server: Fixing bug in ReplaceMetadata parser - Moving from Alternative to committed-choice.

View File

@ -403,7 +403,7 @@ fromSelectArgsG selectArgsG = do
Nothing -> pure Proxy Nothing -> pure Proxy
Just {} -> refute (pure DistinctIsn'tSupported) Just {} -> refute (pure DistinctIsn'tSupported)
(argsOrderBy, joins) <- (argsOrderBy, joins) <-
runWriterT (traverse fromAnnOrderByItemG (maybe [] toList orders)) runWriterT (traverse fromAnnotatedOrderByItemG (maybe [] toList orders))
-- Any object-relation joins that we generated, we record their -- Any object-relation joins that we generated, we record their
-- generated names into a mapping. -- generated names into a mapping.
let argsExistingJoins = let argsExistingJoins =
@ -424,10 +424,10 @@ fromSelectArgsG selectArgsG = do
-- | Produce a valid ORDER BY construct, telling about any joins -- | Produce a valid ORDER BY construct, telling about any joins
-- needed on the side. -- needed on the side.
fromAnnOrderByItemG :: fromAnnotatedOrderByItemG ::
Ir.AnnOrderByItemG 'BigQuery Expression -> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) OrderBy Ir.AnnotatedOrderByItemG 'BigQuery Expression -> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) OrderBy
fromAnnOrderByItemG Ir.OrderByItemG {obiType, obiColumn, obiNulls} = do fromAnnotatedOrderByItemG Ir.OrderByItemG {obiType, obiColumn, obiNulls} = do
orderByFieldName <- unfurlAnnOrderByElement obiColumn orderByFieldName <- unfurlAnnotatedOrderByElement obiColumn
let morderByOrder = let morderByOrder =
obiType obiType
let orderByNullsOrder = 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) -- | Unfurl the nested set of object relations (tell'd in the writer)
-- that are terminated by field name (Ir.AOCColumn and -- that are terminated by field name (Ir.AOCColumn and
-- Ir.AOCArrayAggregation). -- Ir.AOCArrayAggregation).
unfurlAnnOrderByElement :: unfurlAnnotatedOrderByElement ::
Ir.AnnOrderByElement 'BigQuery Expression -> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) FieldName Ir.AnnotatedOrderByElement 'BigQuery Expression -> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) FieldName
unfurlAnnOrderByElement = unfurlAnnotatedOrderByElement =
\case \case
Ir.AOCColumn pgColumnInfo -> lift (fromPGColumnInfo pgColumnInfo) Ir.AOCColumn pgColumnInfo -> lift (fromPGColumnInfo pgColumnInfo)
Ir.AOCObjectRelation Rql.RelInfo {riMapping = mapping, riRTable = tableName} annBoolExp annOrderByElementG -> do Ir.AOCObjectRelation Rql.RelInfo {riMapping = mapping, riRTable = tableName} annBoolExp annOrderByElementG -> do
@ -477,7 +477,7 @@ unfurlAnnOrderByElement =
} }
, unfurledObjectTableAlias = Just (tableName, joinAliasEntity) , 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 Ir.AOCArrayAggregation Rql.RelInfo {riMapping = mapping, riRTable = tableName} annBoolExp annAggregateOrderBy -> do
selectFrom <- lift (lift (fromQualifiedTable tableName)) selectFrom <- lift (lift (fromQualifiedTable tableName))
let alias = aggFieldName let alias = aggFieldName

View File

@ -358,7 +358,7 @@ fromSelectArgsG selectArgsG = do
-- you can just drop the Proxy wrapper. -- you can just drop the Proxy wrapper.
let argsDistinct = Proxy let argsDistinct = Proxy
(argsOrderBy, joins) <- (argsOrderBy, joins) <-
runWriterT (traverse fromAnnOrderByItemG (maybe [] toList orders)) runWriterT (traverse fromAnnotatedOrderByItemG (maybe [] toList orders))
-- Any object-relation joins that we generated, we record their -- Any object-relation joins that we generated, we record their
-- generated names into a mapping. -- generated names into a mapping.
let argsExistingJoins = let argsExistingJoins =
@ -378,11 +378,11 @@ fromSelectArgsG selectArgsG = do
-- | Produce a valid ORDER BY construct, telling about any joins -- | Produce a valid ORDER BY construct, telling about any joins
-- needed on the side. -- needed on the side.
fromAnnOrderByItemG fromAnnotatedOrderByItemG
:: IR.AnnOrderByItemG 'MSSQL Expression :: IR.AnnotatedOrderByItemG 'MSSQL Expression
-> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) OrderBy -> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) OrderBy
fromAnnOrderByItemG IR.OrderByItemG {obiType, obiColumn = obiColumn, obiNulls} = do fromAnnotatedOrderByItemG IR.OrderByItemG {obiType, obiColumn = obiColumn, obiNulls} = do
(orderByFieldName, orderByType) <- unfurlAnnOrderByElement obiColumn (orderByFieldName, orderByType) <- unfurlAnnotatedOrderByElement obiColumn
let orderByNullsOrder = fromMaybe NullsAnyOrder obiNulls let orderByNullsOrder = fromMaybe NullsAnyOrder obiNulls
orderByOrder = fromMaybe AscOrder obiType orderByOrder = fromMaybe AscOrder obiType
pure OrderBy {..} 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) -- | Unfurl the nested set of object relations (tell'd in the writer)
-- that are terminated by field name (IR.AOCColumn and -- that are terminated by field name (IR.AOCColumn and
-- IR.AOCArrayAggregation). -- IR.AOCArrayAggregation).
unfurlAnnOrderByElement unfurlAnnotatedOrderByElement
:: IR.AnnOrderByElement 'MSSQL Expression :: IR.AnnotatedOrderByElement 'MSSQL Expression
-> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) (FieldName, Maybe TSQL.ScalarType) -> WriterT (Seq UnfurledJoin) (ReaderT EntityAlias FromIr) (FieldName, Maybe TSQL.ScalarType)
unfurlAnnOrderByElement = unfurlAnnotatedOrderByElement =
\case \case
IR.AOCColumn pgColumnInfo -> do IR.AOCColumn pgColumnInfo -> do
fieldName <- lift (fromPGColumnInfo pgColumnInfo) fieldName <- lift (fromPGColumnInfo pgColumnInfo)
@ -444,7 +444,7 @@ unfurlAnnOrderByElement =
}) })
local local
(const (EntityAlias joinAliasEntity)) (const (EntityAlias joinAliasEntity))
(unfurlAnnOrderByElement annOrderByElementG) (unfurlAnnotatedOrderByElement annOrderByElementG)
IR.AOCArrayAggregation IR.RelInfo {riMapping = mapping, riRTable = tableName} annBoolExp annAggregateOrderBy -> do IR.AOCArrayAggregation IR.RelInfo {riMapping = mapping, riRTable = tableName} annBoolExp annAggregateOrderBy -> do
selectFrom <- lift (lift (fromQualifiedTable tableName)) selectFrom <- lift (lift (fromQualifiedTable tableName))
let alias = aggFieldName let alias = aggFieldName

View File

@ -220,11 +220,11 @@ mkBaseTableColumnAlias :: Identifier -> PGCol -> Identifier
mkBaseTableColumnAlias pfx pgColumn = mkBaseTableColumnAlias pfx pgColumn =
pfx <> Identifier ".pg." <> toIdentifier pgColumn pfx <> Identifier ".pg." <> toIdentifier pgColumn
mkOrderByFieldName :: RelName -> FieldName mkOrderByFieldName :: ToTxt a => a -> FieldName
mkOrderByFieldName relName = mkOrderByFieldName name =
FieldName $ relNameToTxt relName <> "." <> "order_by" FieldName $ toTxt name <> "." <> "order_by"
mkAggregateOrderByAlias :: AnnAggregateOrderBy ('Postgres pgKind) -> S.Alias mkAggregateOrderByAlias :: AnnotatedAggregateOrderBy ('Postgres pgKind) -> S.Alias
mkAggregateOrderByAlias = (S.Alias . Identifier) . \case mkAggregateOrderByAlias = (S.Alias . Identifier) . \case
AAOCount -> "count" AAOCount -> "count"
AAOOp opText col -> opText <> "." <> getPGColTxt (pgiColumn col) AAOOp opText col -> opText <> "." <> getPGColTxt (pgiColumn col)
@ -284,7 +284,7 @@ withForceAggregation tyAnn e =
mkAggregateOrderByExtractorAndFields mkAggregateOrderByExtractorAndFields
:: forall pgKind :: forall pgKind
. Backend ('Postgres pgKind) . Backend ('Postgres pgKind)
=> AnnAggregateOrderBy ('Postgres pgKind) => AnnotatedAggregateOrderBy ('Postgres pgKind)
-> (S.Extractor, AggregateFields ('Postgres pgKind)) -> (S.Extractor, AggregateFields ('Postgres pgKind))
mkAggregateOrderByExtractorAndFields annAggOrderBy = mkAggregateOrderByExtractorAndFields annAggOrderBy =
case annAggOrderBy of case annAggOrderBy of
@ -309,7 +309,7 @@ mkAggregateOrderByExtractorAndFields annAggOrderBy =
alias = Just $ mkAggregateOrderByAlias annAggOrderBy alias = Just $ mkAggregateOrderByAlias annAggOrderBy
mkAnnOrderByAlias mkAnnOrderByAlias
:: Identifier -> FieldName -> SimilarArrayFields -> AnnOrderByElementG ('Postgres pgKind) v -> S.Alias :: Identifier -> FieldName -> SimilarArrayFields -> AnnotatedOrderByElement ('Postgres pgKind) v -> S.Alias
mkAnnOrderByAlias pfx parAls similarFields = \case mkAnnOrderByAlias pfx parAls similarFields = \case
AOCColumn pgColumnInfo -> AOCColumn pgColumnInfo ->
let pgColumn = pgiColumn pgColumnInfo let pgColumn = pgiColumn pgColumnInfo
@ -328,6 +328,14 @@ mkAnnOrderByAlias pfx parAls similarFields = \case
mkOrderByFieldName rn mkOrderByFieldName rn
obAls = arrPfx <> Identifier "." <> toIdentifier (mkAggregateOrderByAlias aggOrderBy) obAls = arrPfx <> Identifier "." <> toIdentifier (mkAggregateOrderByAlias aggOrderBy)
in S.Alias obAls 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 processDistinctOnColumns
:: Identifier :: Identifier
@ -349,7 +357,7 @@ mkSimilarArrayFields
:: forall pgKind v :: forall pgKind v
. (Backend ('Postgres pgKind), Eq v) . (Backend ('Postgres pgKind), Eq v)
=> AnnFieldsG ('Postgres pgKind) (Const Void) v => AnnFieldsG ('Postgres pgKind) (Const Void) v
-> Maybe (NE.NonEmpty (AnnOrderByItemG ('Postgres pgKind) v)) -> Maybe (NE.NonEmpty (AnnotatedOrderByItemG ('Postgres pgKind) v))
-> SimilarArrayFields -> SimilarArrayFields
mkSimilarArrayFields annFields maybeOrderBys = mkSimilarArrayFields annFields maybeOrderBys =
HM.fromList $ flip map allTuples $ HM.fromList $ flip map allTuples $
@ -432,7 +440,7 @@ withWriteArrayRelation action =
pure (out, (source, topExtractor, nodeExtractors)) pure (out, (source, topExtractor, nodeExtractors))
where where
updateJoinTree joinTree (source, topExtractor, nodeExtractors) = updateJoinTree joinTree (source, topExtractor, nodeExtractors) =
let arraySelectNode = ArraySelectNode [topExtractor] $ let arraySelectNode = MultiRowSelectNode [topExtractor] $
SelectNode nodeExtractors joinTree SelectNode nodeExtractors joinTree
in mempty{_jtArrayRelations = HM.singleton source arraySelectNode} in mempty{_jtArrayRelations = HM.singleton source arraySelectNode}
@ -450,24 +458,25 @@ withWriteArrayConnection action =
pure (out, (source, topExtractor, nodeExtractors)) pure (out, (source, topExtractor, nodeExtractors))
where where
updateJoinTree joinTree (source, topExtractor, nodeExtractors) = updateJoinTree joinTree (source, topExtractor, nodeExtractors) =
let arraySelectNode = ArraySelectNode [topExtractor] $ let arraySelectNode = MultiRowSelectNode [topExtractor] $
SelectNode nodeExtractors joinTree SelectNode nodeExtractors joinTree
in mempty{_jtArrayConnections = HM.singleton source arraySelectNode} in mempty{_jtArrayConnections = HM.singleton source arraySelectNode}
withWriteComputedFieldTableSet withWriteComputedFieldTableSet
:: (MonadWriter JoinTree m) :: (MonadWriter JoinTree m)
=> m ( ComputedFieldTableSetSource => m ( ComputedFieldTableSetSource
, S.Extractor
, HM.HashMap S.Alias S.SQLExp , HM.HashMap S.Alias S.SQLExp
, a , a
) )
-> m a -> m a
withWriteComputedFieldTableSet action = withWriteComputedFieldTableSet action =
withWriteJoinTree updateJoinTree $ do withWriteJoinTree updateJoinTree $ do
(source, nodeExtractors, out) <- action (source, topExtractor, nodeExtractors, out) <- action
pure (out, (source, nodeExtractors)) pure (out, (source, topExtractor, nodeExtractors))
where where
updateJoinTree joinTree (source, nodeExtractors) = updateJoinTree joinTree (source, topExtractor, nodeExtractors) =
let selectNode = SelectNode nodeExtractors joinTree let selectNode = MultiRowSelectNode [topExtractor] $ SelectNode nodeExtractors joinTree
in mempty{_jtComputedFieldTableSets = HM.singleton source selectNode} in mempty{_jtComputedFieldTableSets = HM.singleton source selectNode}
@ -557,7 +566,7 @@ processAnnAggregateSelect sourcePrefixes fieldAlias annAggSel = do
mkPermissionLimitSubQuery mkPermissionLimitSubQuery
:: Maybe Int :: Maybe Int
-> TableAggregateFields ('Postgres pgKind) -> TableAggregateFields ('Postgres pgKind)
-> Maybe (NE.NonEmpty (AnnOrderByItem ('Postgres pgKind))) -> Maybe (NE.NonEmpty (AnnotatedOrderByItem ('Postgres pgKind)))
-> PermissionLimitSubQuery -> PermissionLimitSubQuery
mkPermissionLimitSubQuery permLimit aggFields orderBys = mkPermissionLimitSubQuery permLimit aggFields orderBys =
case permLimit of case permLimit of
@ -685,7 +694,7 @@ processOrderByItems
=> Identifier => Identifier
-> FieldName -> FieldName
-> SimilarArrayFields -> SimilarArrayFields
-> NE.NonEmpty (AnnOrderByItem ('Postgres pgKind)) -> NE.NonEmpty (AnnotatedOrderByItem ('Postgres pgKind))
-> m ( [(S.Alias, S.SQLExp)] -- Order by Extractors -> m ( [(S.Alias, S.SQLExp)] -- Order by Extractors
, S.OrderByExp , S.OrderByExp
, S.SQLExp -- The cursor expression , S.SQLExp -- The cursor expression
@ -698,15 +707,15 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields orderByItems =
pure (orderByExtractors, orderByExp, cursor) pure (orderByExtractors, orderByExp, cursor)
where where
processAnnOrderByItem processAnnOrderByItem
:: AnnOrderByItem ('Postgres pgKind) :: AnnotatedOrderByItem ('Postgres pgKind)
-> m (OrderByItemG ('Postgres pgKind) (AnnOrderByElement ('Postgres pgKind) (SQLExpression ('Postgres pgKind)), (S.Alias, SQLExpression ('Postgres pgKind)))) -> m (OrderByItemG ('Postgres pgKind) (AnnotatedOrderByElement ('Postgres pgKind) (SQLExpression ('Postgres pgKind)), (S.Alias, SQLExpression ('Postgres pgKind))))
processAnnOrderByItem orderByItem = processAnnOrderByItem orderByItem =
forM orderByItem $ \ordByCol -> (ordByCol,) <$> forM orderByItem $ \ordByCol -> (ordByCol,) <$>
processAnnOrderByElement sourcePrefix' fieldAlias' ordByCol processAnnotatedOrderByElement sourcePrefix' fieldAlias' ordByCol
processAnnOrderByElement processAnnotatedOrderByElement
:: Identifier -> FieldName -> AnnOrderByElement ('Postgres pgKind) S.SQLExp -> m (S.Alias, S.SQLExp) :: Identifier -> FieldName -> AnnotatedOrderByElement ('Postgres pgKind) S.SQLExp -> m (S.Alias, S.SQLExp)
processAnnOrderByElement sourcePrefix fieldAlias annObCol = do processAnnotatedOrderByElement sourcePrefix fieldAlias annObCol = do
let ordByAlias = mkAnnOrderByAlias sourcePrefix fieldAlias similarArrayFields annObCol let ordByAlias = mkAnnOrderByAlias sourcePrefix fieldAlias similarArrayFields annObCol
(ordByAlias, ) <$> case annObCol of (ordByAlias, ) <$> case annObCol of
AOCColumn pgColInfo -> pure $ AOCColumn pgColInfo -> pure $
@ -717,7 +726,7 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields orderByItems =
relSourcePrefix = mkObjectRelationTableAlias sourcePrefix relName relSourcePrefix = mkObjectRelationTableAlias sourcePrefix relName
fieldName = mkOrderByFieldName relName fieldName = mkOrderByFieldName relName
(relOrderByAlias, relOrdByExp) <- (relOrderByAlias, relOrdByExp) <-
processAnnOrderByElement relSourcePrefix fieldName rest processAnnotatedOrderByElement relSourcePrefix fieldName rest
let selectSource = ObjectSelectSource relSourcePrefix let selectSource = ObjectSelectSource relSourcePrefix
(S.FISimple relTable Nothing) (S.FISimple relTable Nothing)
(toSQLBoolExp (S.QualTable relTable) relFilter) (toSQLBoolExp (S.QualTable relTable) relFilter)
@ -745,15 +754,38 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields orderByItems =
, S.mkQIdenExp relSourcePrefix (mkAggregateOrderByAlias aggOrderBy) , 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 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 -> S.OrderByItem
toOrderByExp orderByItemExp = toOrderByExp orderByItemExp =
let OrderByItemG obTyM expAlias obNullsM = fst . snd <$> orderByItemExp let OrderByItemG obTyM expAlias obNullsM = fst . snd <$> orderByItemExp
in S.OrderByItem (S.SEIdentifier $ toIdentifier expAlias) obTyM obNullsM in S.OrderByItem (S.SEIdentifier $ toIdentifier expAlias) obTyM obNullsM
mkCursorExp 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 -> S.SQLExp
mkCursorExp orderByItemExps = mkCursorExp orderByItemExps =
S.applyJsonBuildObj $ flip concatMap orderByItemExps $ S.applyJsonBuildObj $ flip concatMap orderByItemExps $
@ -761,6 +793,13 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields orderByItems =
let OrderByItemG _ (annObCol, (_, valExp)) _ = orderByItemExp let OrderByItemG _ (annObCol, (_, valExp)) _ = orderByItemExp
in annObColToJSONField valExp annObCol in annObColToJSONField valExp annObCol
where 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 annObColToJSONField valExp = \case
AOCColumn pgCol -> [S.SELit $ getPGColTxt $ pgiColumn pgCol, valExp] AOCColumn pgCol -> [S.SELit $ getPGColTxt $ pgiColumn pgCol, valExp]
AOCObjectRelation relInfo _ obCol -> AOCObjectRelation relInfo _ obCol ->
@ -769,14 +808,16 @@ processOrderByItems sourcePrefix' fieldAlias' similarArrayFields orderByItems =
] ]
AOCArrayAggregation relInfo _ aggOrderBy -> AOCArrayAggregation relInfo _ aggOrderBy ->
[ S.SELit $ relNameToTxt (riName relInfo) <> "_aggregate" [ S.SELit $ relNameToTxt (riName relInfo) <> "_aggregate"
, S.applyJsonBuildObj $ , S.applyJsonBuildObj $ mkAggOrderByValExp valExp aggOrderBy
case aggOrderBy of
AAOCount -> [S.SELit "count", valExp]
AAOOp opText colInfo ->
[ S.SELit opText
, S.applyJsonBuildObj [S.SELit $ getPGColTxt $ pgiColumn colInfo, valExp]
]
] ]
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 aggregateFieldsToExtractorExps
:: Identifier -> AggregateFields ('Postgres pgKind) -> [(S.Alias, S.SQLExp)] :: Identifier -> AggregateFields ('Postgres pgKind) -> [(S.Alias, S.SQLExp)]
@ -917,9 +958,11 @@ processAnnFields sourcePrefix fieldAlias similarArrFields annFields = do
(selectSource, nodeExtractors) <- (selectSource, nodeExtractors) <-
processAnnSimpleSelect (mkSourcePrefixes computedFieldSourcePrefix) processAnnSimpleSelect (mkSourcePrefixes computedFieldSourcePrefix)
fieldName PLSQNotRequired sel fieldName PLSQNotRequired sel
let computedFieldTableSetSource = let computedFieldTableSetSource = ComputedFieldTableSetSource fieldName selectSource
ComputedFieldTableSetSource fieldName selectTy selectSource extractor = asJsonAggExtr selectTy (S.toAlias fieldName) PLSQNotRequired $
_ssOrderBy selectSource
pure ( computedFieldTableSetSource pure ( computedFieldTableSetSource
, extractor
, nodeExtractors , nodeExtractors
, S.mkQIdenExp computedFieldSourcePrefix fieldName , S.mkQIdenExp computedFieldSourcePrefix fieldName
) )
@ -1027,7 +1070,6 @@ generateSQLSelect joinCondition selectSource selectNode =
map arrayConnectionToFromItem (HM.toList arrayConnections) <> map arrayConnectionToFromItem (HM.toList arrayConnections) <>
map computedFieldToFromItem (HM.toList computedFields) map computedFieldToFromItem (HM.toList computedFields)
objectRelationToFromItem objectRelationToFromItem
:: (ObjectRelationSource, SelectNode) -> S.FromItem :: (ObjectRelationSource, SelectNode) -> S.FromItem
objectRelationToFromItem (objectRelationSource, node) = objectRelationToFromItem (objectRelationSource, node) =
@ -1038,7 +1080,7 @@ generateSQLSelect joinCondition selectSource selectNode =
in S.mkLateralFromItem select alias in S.mkLateralFromItem select alias
arrayRelationToFromItem arrayRelationToFromItem
:: (ArrayRelationSource, ArraySelectNode) -> S.FromItem :: (ArrayRelationSource, MultiRowSelectNode) -> S.FromItem
arrayRelationToFromItem (arrayRelationSource, arraySelectNode) = arrayRelationToFromItem (arrayRelationSource, arraySelectNode) =
let ArrayRelationSource _ colMapping source = arrayRelationSource let ArrayRelationSource _ colMapping source = arrayRelationSource
alias = S.Alias $ _ssPrefix source alias = S.Alias $ _ssPrefix source
@ -1047,29 +1089,27 @@ generateSQLSelect joinCondition selectSource selectNode =
in S.mkLateralFromItem select alias in S.mkLateralFromItem select alias
arrayConnectionToFromItem arrayConnectionToFromItem
:: (ArrayConnectionSource, ArraySelectNode) -> S.FromItem :: (ArrayConnectionSource, MultiRowSelectNode) -> S.FromItem
arrayConnectionToFromItem (arrayConnectionSource, arraySelectNode) = arrayConnectionToFromItem (arrayConnectionSource, arraySelectNode) =
let selectWith = connectionToSelectWith baseSelectAlias arrayConnectionSource arraySelectNode let selectWith = connectionToSelectWith baseSelectAlias arrayConnectionSource arraySelectNode
alias = S.Alias $ _ssPrefix $ _acsSource arrayConnectionSource alias = S.Alias $ _ssPrefix $ _acsSource arrayConnectionSource
in S.FISelectWith (S.Lateral True) selectWith alias in S.FISelectWith (S.Lateral True) selectWith alias
computedFieldToFromItem computedFieldToFromItem
:: (ComputedFieldTableSetSource, SelectNode) -> S.FromItem :: (ComputedFieldTableSetSource, MultiRowSelectNode) -> S.FromItem
computedFieldToFromItem (computedFieldTableSource, node) = computedFieldToFromItem (computedFieldTableSource, node) =
let ComputedFieldTableSetSource fieldName selectTy source = computedFieldTableSource let ComputedFieldTableSetSource _ source = computedFieldTableSource
internalSelect = generateSQLSelect (S.BELit True) source node internalSelect = generateSQLSelect (S.BELit True) source $ _mrsnSelectNode node
extractor = asJsonAggExtr selectTy (S.toAlias fieldName) PLSQNotRequired $
_ssOrderBy source
alias = S.Alias $ _ssPrefix source alias = S.Alias $ _ssPrefix source
select = S.mkSelect select = S.mkSelect
{ S.selExtr = [extractor] { S.selExtr = _mrsnTopExtractors node
, S.selFrom = Just $ S.FromExp [S.mkSelFromItem internalSelect alias] , S.selFrom = Just $ S.FromExp [S.mkSelFromItem internalSelect alias]
} }
in S.mkLateralFromItem select alias in S.mkLateralFromItem select alias
generateSQLSelectFromArrayNode generateSQLSelectFromArrayNode
:: SelectSource :: SelectSource
-> ArraySelectNode -> MultiRowSelectNode
-> S.BoolExp -> S.BoolExp
-> S.Select -> S.Select
generateSQLSelectFromArrayNode selectSource arraySelectNode joinCondition = generateSQLSelectFromArrayNode selectSource arraySelectNode joinCondition =
@ -1078,7 +1118,7 @@ generateSQLSelectFromArrayNode selectSource arraySelectNode joinCondition =
, S.selFrom = Just $ S.FromExp [selectFrom] , S.selFrom = Just $ S.FromExp [selectFrom]
} }
where where
ArraySelectNode topExtractors selectNode = arraySelectNode MultiRowSelectNode topExtractors selectNode = arraySelectNode
selectFrom = S.mkSelFromItem selectFrom = S.mkSelFromItem
(generateSQLSelect joinCondition selectSource selectNode) $ (generateSQLSelect joinCondition selectSource selectNode) $
S.Alias $ _ssPrefix selectSource S.Alias $ _ssPrefix selectSource
@ -1095,7 +1135,7 @@ mkAggregateSelect annAggSel =
runWriter $ flip runReaderT strfyNum $ runWriter $ flip runReaderT strfyNum $
processAnnAggregateSelect sourcePrefixes rootFieldName annAggSel processAnnAggregateSelect sourcePrefixes rootFieldName annAggSel
selectNode = SelectNode nodeExtractors joinTree selectNode = SelectNode nodeExtractors joinTree
arrayNode = ArraySelectNode [topExtractor] selectNode arrayNode = MultiRowSelectNode [topExtractor] selectNode
in prefixNumToAliases $ in prefixNumToAliases $
generateSQLSelectFromArrayNode selectSource arrayNode $ S.BELit True generateSQLSelectFromArrayNode selectSource arrayNode $ S.BELit True
where where
@ -1120,7 +1160,7 @@ mkSQLSelect jsonAggSelect annSel =
selectNode = SelectNode nodeExtractors joinTree selectNode = SelectNode nodeExtractors joinTree
topExtractor = asJsonAggExtr jsonAggSelect rootFldAls permLimitSubQuery topExtractor = asJsonAggExtr jsonAggSelect rootFldAls permLimitSubQuery
$ _ssOrderBy selectSource $ _ssOrderBy selectSource
arrayNode = ArraySelectNode [topExtractor] selectNode arrayNode = MultiRowSelectNode [topExtractor] selectNode
in prefixNumToAliases $ in prefixNumToAliases $
generateSQLSelectFromArrayNode selectSource arrayNode $ S.BELit True generateSQLSelectFromArrayNode selectSource arrayNode $ S.BELit True
where where
@ -1142,7 +1182,7 @@ mkConnectionSelect connectionSelect =
runWriter $ flip runReaderT strfyNum $ runWriter $ flip runReaderT strfyNum $
processConnectionSelect sourcePrefixes rootFieldName processConnectionSelect sourcePrefixes rootFieldName
(S.Alias rootIdentifier) mempty connectionSelect (S.Alias rootIdentifier) mempty connectionSelect
selectNode = ArraySelectNode [topExtractor] $ selectNode = MultiRowSelectNode [topExtractor] $
SelectNode nodeExtractors joinTree SelectNode nodeExtractors joinTree
in prefixNumToAliasesSelectWith $ in prefixNumToAliasesSelectWith $
connectionToSelectWith (S.Alias rootIdentifier) connectionSource selectNode connectionToSelectWith (S.Alias rootIdentifier) connectionSource selectNode
@ -1350,7 +1390,7 @@ processConnectionSelect sourcePrefixes fieldAlias relAlias colMapping connection
connectionToSelectWith connectionToSelectWith
:: S.Alias :: S.Alias
-> ArrayConnectionSource -> ArrayConnectionSource
-> ArraySelectNode -> MultiRowSelectNode
-> S.SelectWithG S.Select -> S.SelectWithG S.Select
connectionToSelectWith baseSelectAlias arrayConnectionSource arraySelectNode = connectionToSelectWith baseSelectAlias arrayConnectionSource arraySelectNode =
let extractionSelect = S.mkSelect let extractionSelect = S.mkSelect
@ -1361,7 +1401,7 @@ connectionToSelectWith baseSelectAlias arrayConnectionSource arraySelectNode =
where where
ArrayConnectionSource _ columnMapping maybeSplit maybeSlice selectSource = ArrayConnectionSource _ columnMapping maybeSplit maybeSlice selectSource =
arrayConnectionSource arrayConnectionSource
ArraySelectNode topExtractors selectNode = arraySelectNode MultiRowSelectNode topExtractors selectNode = arraySelectNode
baseSelectIdentifier = Identifier "__base_select" baseSelectIdentifier = Identifier "__base_select"
splitSelectIdentifier = Identifier "__split_select" splitSelectIdentifier = Identifier "__split_select"
sliceSelectIdentifier = Identifier "__slice_select" sliceSelectIdentifier = Identifier "__slice_select"

View File

@ -79,20 +79,19 @@ data ArrayRelationSource
instance Hashable ArrayRelationSource instance Hashable ArrayRelationSource
deriving instance Eq ArrayRelationSource deriving instance Eq ArrayRelationSource
data ArraySelectNode data MultiRowSelectNode
= ArraySelectNode = MultiRowSelectNode
{ _asnTopExtractors :: ![PG.Extractor] { _mrsnTopExtractors :: ![PG.Extractor]
, _asnSelectNode :: !SelectNode , _mrsnSelectNode :: !SelectNode
} }
instance Semigroup ArraySelectNode where instance Semigroup MultiRowSelectNode where
ArraySelectNode lTopExtrs lSelNode <> ArraySelectNode rTopExtrs rSelNode = MultiRowSelectNode lTopExtrs lSelNode <> MultiRowSelectNode rTopExtrs rSelNode =
ArraySelectNode (lTopExtrs <> rTopExtrs) (lSelNode <> rSelNode) MultiRowSelectNode (lTopExtrs <> rTopExtrs) (lSelNode <> rSelNode)
data ComputedFieldTableSetSource data ComputedFieldTableSetSource
= ComputedFieldTableSetSource = ComputedFieldTableSetSource
{ _cftssFieldName :: !FieldName { _cftssFieldName :: !FieldName
, _cftssSelectType :: !JsonAggSelect
, _cftssSelectSource :: !SelectSource , _cftssSelectSource :: !SelectSource
} deriving (Generic) } deriving (Generic)
instance Hashable ComputedFieldTableSetSource instance Hashable ComputedFieldTableSetSource
@ -114,9 +113,9 @@ instance Hashable ArrayConnectionSource
data JoinTree data JoinTree
= JoinTree = JoinTree
{ _jtObjectRelations :: !(HM.HashMap ObjectRelationSource SelectNode) { _jtObjectRelations :: !(HM.HashMap ObjectRelationSource SelectNode)
, _jtArrayRelations :: !(HM.HashMap ArrayRelationSource ArraySelectNode) , _jtArrayRelations :: !(HM.HashMap ArrayRelationSource MultiRowSelectNode)
, _jtArrayConnections :: !(HM.HashMap ArrayConnectionSource ArraySelectNode) , _jtArrayConnections :: !(HM.HashMap ArrayConnectionSource MultiRowSelectNode)
, _jtComputedFieldTableSets :: !(HM.HashMap ComputedFieldTableSetSource SelectNode) , _jtComputedFieldTableSets :: !(HM.HashMap ComputedFieldTableSetSource MultiRowSelectNode)
} }
instance Semigroup JoinTree where 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 b -> (AnnFieldG b (Const Void) (UnpreparedValue b))
mkScalarComputedFieldSelect ScalarComputedField{..} = mkScalarComputedFieldSelect ScalarComputedField{..} =
let functionArgs = flip FunctionArgsExp mempty let functionArgs = flip FunctionArgsExp mempty
$ functionArgsWithTableRowAndSession _scfTableArgument _scfSessionArgument $ functionArgsWithTableRowAndSession UVSession _scfTableArgument _scfSessionArgument
fieldSelect = flip CFSScalar Nothing fieldSelect = flip CFSScalar Nothing
$ ComputedFieldScalarSelect _scfFunction functionArgs _scfType Nothing $ ComputedFieldScalarSelect _scfFunction functionArgs _scfType Nothing
in AFComputedField _scfXField _scfName fieldSelect 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 mapToNonEmpty :: RemoteJoinMap -> Maybe RemoteJoins

View File

@ -36,7 +36,7 @@ orderByExp
=> SourceName => SourceName
-> TableInfo b -> TableInfo b
-> SelPermInfo 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 orderByExp sourceName tableInfo selectPermissions = memoizeOn 'orderByExp (sourceName, tableInfoName tableInfo) $ do
tableGQLName <- getTableGQLName tableInfo tableGQLName <- getTableGQLName tableInfo
let name = tableGQLName <> $$(G.litName "_order_by") let name = tableGQLName <> $$(G.litName "_order_by")
@ -48,18 +48,19 @@ orderByExp sourceName tableInfo selectPermissions = memoizeOn 'orderByExp (sourc
where where
mkField mkField
:: FieldInfo b :: 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 $ mkField fieldInfo = runMaybeT $
case fieldInfo of case fieldInfo of
FIColumn columnInfo -> do FIColumn columnInfo -> do
let fieldName = pgiName columnInfo let fieldName = pgiName columnInfo
pure $ P.fieldOptional fieldName Nothing (orderByOperator @b) pure $ P.fieldOptional fieldName Nothing (orderByOperator @b)
<&> fmap (pure . mkOrderByItemG @b (IR.AOCColumn columnInfo)) . join <&> fmap (pure . mkOrderByItemG @b (IR.AOCColumn columnInfo)) . join
FIRelationship relationshipInfo -> do FIRelationship relationshipInfo -> do
remoteTableInfo <- askTableInfo @b sourceName $ riRTable relationshipInfo remoteTableInfo <- askTableInfo @b sourceName $ riRTable relationshipInfo
fieldName <- hoistMaybe $ G.mkName $ relNameToTxt $ riName relationshipInfo fieldName <- hoistMaybe $ G.mkName $ relNameToTxt $ riName relationshipInfo
perms <- MaybeT $ tableSelectPermissions remoteTableInfo perms <- MaybeT $ tableSelectPermissions remoteTableInfo
let newPerms = (fmap . fmap) partialSQLExpToUnpreparedValue $ spiFilter perms let newPerms = fmap partialSQLExpToUnpreparedValue <$> spiFilter perms
case riType relationshipInfo of case riType relationshipInfo of
ObjRel -> do ObjRel -> do
otherTableParser <- lift $ orderByExp sourceName remoteTableInfo perms otherTableParser <- lift $ orderByExp sourceName remoteTableInfo perms
@ -72,7 +73,33 @@ orderByExp sourceName tableInfo selectPermissions = memoizeOn 'orderByExp (sourc
pure $ do pure $ do
aggregationOrderBy <- join <$> P.fieldOptional aggregateFieldName Nothing (P.nullable aggregationParser) aggregationOrderBy <- join <$> P.fieldOptional aggregateFieldName Nothing (P.nullable aggregationParser)
pure $ fmap (map $ fmap $ IR.AOCArrayAggregation relationshipInfo newPerms) aggregationOrderBy 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 FIRemoteRelationship _ -> empty
@ -85,7 +112,7 @@ orderByAggregation
=> SourceName => SourceName
-> TableInfo b -> TableInfo b
-> SelPermInfo 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 orderByAggregation sourceName tableInfo selectPermissions = memoizeOn 'orderByAggregation (sourceName, tableName) do
-- WIP NOTE -- WIP NOTE
-- there is heavy duplication between this and Select.tableAggregationFields -- there is heavy duplication between this and Select.tableAggregationFields
@ -125,7 +152,7 @@ orderByAggregation sourceName tableInfo selectPermissions = memoizeOn 'orderByAg
:: G.Name :: G.Name
-> G.Name -> G.Name
-> InputFieldsParser n [(ColumnInfo b, (BasicOrderType b, NullsOrderType b))] -> 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 = parseOperator operator tableGQLName columns =
let opText = G.unName operator let opText = G.unName operator
objectName = tableGQLName <> $$(G.litName "_") <> operator <> $$(G.litName "_order_by") objectName = tableGQLName <> $$(G.litName "_") <> operator <> $$(G.litName "_order_by")

View File

@ -625,7 +625,7 @@ tableOrderByArg
=> SourceName => SourceName
-> TableInfo b -> TableInfo b
-> SelPermInfo 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 tableOrderByArg sourceName tableInfo selectPermissions = do
orderByParser <- orderByExp sourceName tableInfo selectPermissions orderByParser <- orderByExp sourceName tableInfo selectPermissions
pure $ do pure $ do
@ -738,7 +738,7 @@ tableConnectionArgs pkeyColumns sourceName tableInfo selectPermissions = do
where where
base64Text = base64Decode <$> P.string 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) = appendPrimaryKeyOrderBy orderBys@(h NE.:| t) =
let orderByColumnNames = let orderByColumnNames =
orderBys ^.. traverse . to IR.obiColumn . IR._AOCColumn . to pgiColumn orderBys ^.. traverse . to IR.obiColumn . IR._AOCColumn . to pgiColumn
@ -748,7 +748,7 @@ tableConnectionArgs pkeyColumns sourceName tableInfo selectPermissions = do
in h NE.:| (t <> pkeyOrderBys) in h NE.:| (t <> pkeyOrderBys)
parseConnectionSplit parseConnectionSplit
:: Maybe (NonEmpty (IR.AnnOrderByItemG b (UnpreparedValue b))) :: Maybe (NonEmpty (IR.AnnotatedOrderByItemG b (UnpreparedValue b)))
-> IR.ConnectionSplitKind -> IR.ConnectionSplitKind
-> BL.ByteString -> BL.ByteString
-> n (NonEmpty (IR.ConnectionSplit b (UnpreparedValue b))) -> n (NonEmpty (IR.ConnectionSplit b (UnpreparedValue b)))
@ -774,11 +774,15 @@ tableConnectionArgs pkeyColumns sourceName tableInfo selectPermissions = do
pgValue <- liftQErr $ parseScalarValueColumnType columnType orderByItemValue pgValue <- liftQErr $ parseScalarValueColumnType columnType orderByItemValue
let unresolvedValue = UVParameter Nothing $ ColumnValue columnType pgValue let unresolvedValue = UVParameter Nothing $ ColumnValue columnType pgValue
pure $ IR.ConnectionSplit splitKind unresolvedValue $ pure $ IR.ConnectionSplit splitKind unresolvedValue $
IR.OrderByItemG orderType (() <$ annObCol) nullsOrder IR.OrderByItemG orderType annObCol nullsOrder
where where
throwInvalidCursor = parseError "the \"after\" or \"before\" cursor is invalid" throwInvalidCursor = parseError "the \"after\" or \"before\" cursor is invalid"
liftQErr = either (parseError . qeError) pure . runExcept 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 getPathFromOrderBy = \case
IR.AOCColumn pgColInfo -> IR.AOCColumn pgColInfo ->
let pathElement = J.Key $ toTxt $ pgiColumn pgColInfo let pathElement = J.Key $ toTxt $ pgiColumn pgColInfo
@ -788,15 +792,24 @@ tableConnectionArgs pkeyColumns sourceName tableInfo selectPermissions = do
in pathElement : getPathFromOrderBy obCol in pathElement : getPathFromOrderBy obCol
IR.AOCArrayAggregation relInfo _ aggOb -> IR.AOCArrayAggregation relInfo _ aggOb ->
let fieldName = J.Key $ relNameToTxt (riName relInfo) <> "_aggregate" let fieldName = J.Key $ relNameToTxt (riName relInfo) <> "_aggregate"
in fieldName : case aggOb of in fieldName : mkAggregateOrderByPath aggOb
IR.AAOCount -> [J.Key "count"] IR.AOCComputedField cfob ->
IR.AAOOp t col -> [J.Key t, J.Key $ toTxt $ pgiColumn col] 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 getOrderByColumnType = \case
IR.AOCColumn pgColInfo -> pgiType pgColInfo IR.AOCColumn pgColInfo -> pgiType pgColInfo
IR.AOCObjectRelation _ _ obCol -> getOrderByColumnType obCol IR.AOCObjectRelation _ _ obCol -> getOrderByColumnType obCol
IR.AOCArrayAggregation _ _ aggOb -> IR.AOCArrayAggregation _ _ aggOb -> aggregateOrderByColumnType aggOb
case aggOb of 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.AAOCount -> ColumnScalar (aggregateOrderByCountType @b)
IR.AAOOp _ colInfo -> pgiType colInfo IR.AAOOp _ colInfo -> pgiType colInfo
@ -1311,7 +1324,7 @@ functionArgs functionTrackedAs (toList -> inputArgs) = do
tablePermissionsInfo :: Backend b => SelPermInfo b -> TablePerms b tablePermissionsInfo :: Backend b => SelPermInfo b -> TablePerms b
tablePermissionsInfo selectPermissions = IR.TablePerm tablePermissionsInfo selectPermissions = IR.TablePerm
{ IR._tpFilter = (fmap . fmap) partialSQLExpToUnpreparedValue $ spiFilter selectPermissions { IR._tpFilter = fmap partialSQLExpToUnpreparedValue <$> spiFilter selectPermissions
, IR._tpLimit = spiLimit selectPermissions , IR._tpLimit = spiLimit selectPermissions
} }

View File

@ -47,7 +47,7 @@ convSelCol
-> SelCol -> SelCol
-> m [ExtCol ('Postgres 'Vanilla)] -> m [ExtCol ('Postgres 'Vanilla)]
convSelCol _ _ (SCExtSimple cn) = convSelCol _ _ (SCExtSimple cn) =
return [ECSimple cn] pure [ECSimple cn]
convSelCol fieldInfoMap _ (SCExtRel rn malias selQ) = do convSelCol fieldInfoMap _ (SCExtRel rn malias selQ) = do
-- Point to the name key -- Point to the name key
let pgWhenRelErr = "only relationships can be expanded" let pgWhenRelErr = "only relationships can be expanded"
@ -56,7 +56,7 @@ convSelCol fieldInfoMap _ (SCExtRel rn malias selQ) = do
let (RelInfo _ _ _ relTab _ _ _) = relInfo let (RelInfo _ _ _ relTab _ _ _) = relInfo
(rfim, rspi) <- fetchRelDet rn relTab (rfim, rspi) <- fetchRelDet rn relTab
resolvedSelQ <- resolveStar rfim rspi selQ resolvedSelQ <- resolveStar rfim rspi selQ
return [ECRel rn malias resolvedSelQ] pure [ECRel rn malias resolvedSelQ]
convSelCol fieldInfoMap spi (SCStar wildcard) = convSelCol fieldInfoMap spi (SCStar wildcard) =
convWildcard fieldInfoMap spi wildcard convWildcard fieldInfoMap spi wildcard
@ -68,7 +68,7 @@ convWildcard
-> m [ExtCol ('Postgres 'Vanilla)] -> m [ExtCol ('Postgres 'Vanilla)]
convWildcard fieldInfoMap selPermInfo wildcard = convWildcard fieldInfoMap selPermInfo wildcard =
case wildcard of case wildcard of
Star -> return simpleCols Star -> pure simpleCols
(StarDot wc) -> (simpleCols ++) <$> (catMaybes <$> relExtCols wc) (StarDot wc) -> (simpleCols ++) <$> (catMaybes <$> relExtCols wc)
where where
cols = spiCols selPermInfo cols = spiCols selPermInfo
@ -85,7 +85,7 @@ convWildcard fieldInfoMap selPermInfo wildcard =
forM mRelSelPerm $ \relSelPermInfo -> do forM mRelSelPerm $ \relSelPermInfo -> do
rExtCols <- convWildcard (_tciFieldInfoMap $ _tiCoreInfo relTabInfo) relSelPermInfo wc rExtCols <- convWildcard (_tciFieldInfoMap $ _tiCoreInfo relTabInfo) relSelPermInfo wc
return $ ECRel relName Nothing $ pure $ ECRel relName Nothing $
SelectG rExtCols Nothing Nothing Nothing Nothing SelectG rExtCols Nothing Nothing Nothing Nothing
relExtCols wc = mapM (mkRelCol wc) relColInfos relExtCols wc = mapM (mkRelCol wc) relColInfos
@ -99,13 +99,13 @@ resolveStar
resolveStar fim selPermInfo (SelectG selCols mWh mOb mLt mOf) = do resolveStar fim selPermInfo (SelectG selCols mWh mOb mLt mOf) = do
procOverrides <- fmap (concat . catMaybes) $ withPathK "columns" $ procOverrides <- fmap (concat . catMaybes) $ withPathK "columns" $
indexedForM selCols $ \selCol -> case selCol of indexedForM selCols $ \selCol -> case selCol of
(SCStar _) -> return Nothing (SCStar _) -> pure Nothing
_ -> Just <$> convSelCol fim selPermInfo selCol _ -> Just <$> convSelCol fim selPermInfo selCol
everything <- case wildcards of everything <- case wildcards of
[] -> return [] [] -> pure []
_ -> convWildcard fim selPermInfo $ maximum wildcards _ -> convWildcard fim selPermInfo $ maximum wildcards
let extCols = unionBy equals procOverrides everything let extCols = unionBy equals procOverrides everything
return $ SelectG extCols mWh mOb mLt mOf pure $ SelectG extCols mWh mOb mLt mOf
where where
wildcards = lefts $ map mkEither selCols wildcards = lefts $ map mkEither selCols
@ -121,7 +121,7 @@ convOrderByElem
=> SessVarBldr ('Postgres 'Vanilla) m => SessVarBldr ('Postgres 'Vanilla) m
-> (FieldInfoMap (FieldInfo ('Postgres 'Vanilla)), SelPermInfo ('Postgres 'Vanilla)) -> (FieldInfoMap (FieldInfo ('Postgres 'Vanilla)), SelPermInfo ('Postgres 'Vanilla))
-> OrderByCol -> OrderByCol
-> m (AnnOrderByElement ('Postgres 'Vanilla) S.SQLExp) -> m (AnnotatedOrderByElement ('Postgres 'Vanilla) S.SQLExp)
convOrderByElem sessVarBldr (flds, spi) = \case convOrderByElem sessVarBldr (flds, spi) = \case
OCPG fldName -> do OCPG fldName -> do
fldInfo <- askFieldInfo flds fldName fldInfo <- askFieldInfo flds fldName
@ -134,7 +134,7 @@ convOrderByElem sessVarBldr (flds, spi) = \case
[ fldName <<> " has type 'geometry'" [ fldName <<> " has type 'geometry'"
, " and cannot be used in order_by" , " and cannot be used in order_by"
] ]
else return $ AOCColumn colInfo else pure $ AOCColumn colInfo
FIRelationship _ -> throw400 UnexpectedPayload $ mconcat FIRelationship _ -> throw400 UnexpectedPayload $ mconcat
[ fldName <<> " is a" [ fldName <<> " is a"
, " relationship and should be expanded" , " relationship and should be expanded"
@ -165,8 +165,7 @@ convOrderByElem sessVarBldr (flds, spi) = \case
] ]
(relFim, relSelPermInfo) <- fetchRelDet (riName relInfo) (riRTable relInfo) (relFim, relSelPermInfo) <- fetchRelDet (riName relInfo) (riRTable relInfo)
resolvedSelFltr <- convAnnBoolExpPartialSQL sessVarBldr $ spiFilter relSelPermInfo resolvedSelFltr <- convAnnBoolExpPartialSQL sessVarBldr $ spiFilter relSelPermInfo
AOCObjectRelation relInfo resolvedSelFltr <$> AOCObjectRelation relInfo resolvedSelFltr <$> convOrderByElem sessVarBldr (relFim, relSelPermInfo) rest
convOrderByElem sessVarBldr (relFim, relSelPermInfo) rest
FIRemoteRelationship {} -> FIRemoteRelationship {} ->
throw400 UnexpectedPayload (mconcat [ fldName <<> " is a remote field" ]) 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 (colInfo, caseBoolExpMaybe) <- convExtSimple fieldInfoMap selPermInfo pgCol
resolvedCaseBoolExp <- resolvedCaseBoolExp <-
traverse (convAnnColumnCaseBoolExpPartialSQL sessVarBldr) caseBoolExpMaybe 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 (ECRel relName mAlias relSelQ) -> do
annRel <- convExtRel fieldInfoMap relName mAlias annRel <- convExtRel fieldInfoMap relName mAlias
relSelQ sessVarBldr prepValBldr relSelQ sessVarBldr prepValBldr
return ( fromRel $ fromMaybe relName mAlias pure ( fromRel $ fromMaybe relName mAlias
, either AFObjectRelation AFArrayRelation annRel , either AFObjectRelation AFArrayRelation annRel
) )
@ -221,7 +220,7 @@ convSelectQ table fieldInfoMap selPermInfo selQ sessVarBldr prepValBldr = do
tabArgs = SelectArgs wClause annOrdByM mQueryLimit (fromIntegral <$> mQueryOffset) Nothing tabArgs = SelectArgs wClause annOrdByM mQueryLimit (fromIntegral <$> mQueryOffset) Nothing
strfyNum <- stringifyNum . _sccSQLGenCtx <$> askServerConfigCtx strfyNum <- stringifyNum . _sccSQLGenCtx <$> askServerConfigCtx
return $ AnnSelectG annFlds tabFrom tabPerm tabArgs strfyNum pure $ AnnSelectG annFlds tabFrom tabPerm tabArgs strfyNum
where where
mQueryOffset = sqOffset selQ mQueryOffset = sqOffset selQ
@ -264,10 +263,10 @@ convExtRel fieldInfoMap relName mAlias selQ sessVarBldr prepValBldr = do
case relTy of case relTy of
ObjRel -> do ObjRel -> do
when misused $ throw400 UnexpectedPayload objRelMisuseMsg 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 AnnObjectSelectG (_asnFields annSel) relTab $ _tpFilter $ _asnPerm annSel
ArrRel -> ArrRel ->
return $ Right $ ASSimple $ AnnRelationSelectG (fromMaybe relName mAlias) pure $ Right $ ASSimple $ AnnRelationSelectG (fromMaybe relName mAlias)
colMapping annSel colMapping annSel
where where
pgWhenRelErr = "only relationships can be expanded" pgWhenRelErr = "only relationships can be expanded"

View File

@ -96,9 +96,9 @@ data ConnectionSplit (b :: BackendType) v
= ConnectionSplit = ConnectionSplit
{ _csKind :: !ConnectionSplitKind { _csKind :: !ConnectionSplitKind
, _csValue :: !v , _csValue :: !v
, _csOrderBy :: !(OrderByItemG b (AnnOrderByElementG b ())) , _csOrderBy :: !(OrderByItemG b (AnnotatedOrderByElement b v))
} deriving (Functor, Generic, Foldable, Traversable) } 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 data ConnectionSlice
= SliceFirst !Int = SliceFirst !Int
@ -133,7 +133,7 @@ type SelectFrom b = SelectFromG b (SQLExpression b)
data SelectArgsG (b :: BackendType) v data SelectArgsG (b :: BackendType) v
= SelectArgs = SelectArgs
{ _saWhere :: !(Maybe (AnnBoolExp b v)) { _saWhere :: !(Maybe (AnnBoolExp b v))
, _saOrderBy :: !(Maybe (NE.NonEmpty (AnnOrderByItemG b v))) , _saOrderBy :: !(Maybe (NE.NonEmpty (AnnotatedOrderByItemG b v)))
, _saLimit :: !(Maybe Int) , _saLimit :: !(Maybe Int)
, _saOffset :: !(Maybe Int64) , _saOffset :: !(Maybe Int64)
, _saDistinct :: !(Maybe (NE.NonEmpty (Column b))) , _saDistinct :: !(Maybe (NE.NonEmpty (Column b)))
@ -159,24 +159,51 @@ noSelectArgs = SelectArgs Nothing Nothing Nothing Nothing Nothing
-- Order by argument -- Order by argument
data AnnOrderByElementG (b :: BackendType) v -- | The order by element for a computed field based on its return type
= AOCColumn !(ColumnInfo b) data ComputedFieldOrderByElement (b :: BackendType) v
| AOCObjectRelation !(RelInfo b) !v !(AnnOrderByElementG b v) = CFOBEScalar !(ScalarType b)
| AOCArrayAggregation !(RelInfo b) !v !(AnnAggregateOrderBy 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 (Generic, Functor, Foldable, Traversable)
deriving instance (Backend b, Eq v) => Eq (AnnOrderByElementG b v) deriving instance (Backend b, Eq v, Eq (BooleanOperators b v)) => Eq (ComputedFieldOrderByElement b v)
instance (Backend b, Hashable v) => Hashable (AnnOrderByElementG 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 = AAOCount
| AAOOp !Text !(ColumnInfo b) | AAOOp !Text !(ColumnInfo b)
deriving (Generic) deriving (Generic)
deriving instance (Backend b) => Eq (AnnAggregateOrderBy b) deriving instance (Backend b) => Eq (AnnotatedAggregateOrderBy b)
instance (Backend b) => Hashable (AnnAggregateOrderBy b) instance (Backend b) => Hashable (AnnotatedAggregateOrderBy b)
type AnnOrderByElement b v = AnnOrderByElementG b (AnnBoolExp b v) type AnnotatedOrderByItemG b v = OrderByItemG b (AnnotatedOrderByElement b v)
type AnnOrderByItemG b v = OrderByItemG b (AnnOrderByElement b v) type AnnotatedOrderByItem b = AnnotatedOrderByItemG b (SQLExpression b)
type AnnOrderByItem b = AnnOrderByItemG b (SQLExpression b)
-- Fields -- Fields
@ -466,6 +493,17 @@ type FunctionArgExp b = FunctionArgsExpG (SQLExpression b)
emptyFunctionArgsExp :: FunctionArgsExpG a emptyFunctionArgsExp :: FunctionArgsExpG a
emptyFunctionArgsExp = FunctionArgsExp [] HM.empty 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 -- | If argument positional index is less than or equal to length of
-- 'positional' arguments then insert the value in 'positional' arguments else -- 'positional' arguments then insert the value in 'positional' arguments else
-- insert the value with argument name in 'named' arguments -- insert the value with argument name in 'named' arguments
@ -488,4 +526,4 @@ insertFunctionArg argName idx value (FunctionArgsExp positional named) =
$(makeLenses ''AnnSelectG) $(makeLenses ''AnnSelectG)
$(makePrisms ''AnnFieldG) $(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, phone INTEGER,
address TEXT address TEXT
); );
create table author( create table author(
id serial primary key, id serial primary key,
name text unique, name text unique,
contact_id INTEGER REFERENCES contact(id) contact_id INTEGER REFERENCES contact(id)
); );
CREATE TABLE article ( CREATE TABLE article (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
title TEXT, title TEXT,
@ -24,14 +26,27 @@ args:
is_published BOOLEAN, is_published BOOLEAN,
published_on TIMESTAMP 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) insert into contact (phone)
values values
(1234567890), (1234567890),
(1234567891); (1234567891);
insert into author (name, contact_id) insert into author (name, contact_id)
values values
('Author 1', 2), ('Author 1', 2),
('Author 2', 1); ('Author 2', 1);
insert into article (title,content,author_id,is_published) insert into article (title,content,author_id,is_published)
values values
( (
@ -56,6 +71,7 @@ args:
album_id INTEGER NOT NULL PRIMARY KEY, album_id INTEGER NOT NULL PRIMARY KEY,
title TEXT NOT NULL title TEXT NOT NULL
); );
create table "Track" ( create table "Track" (
track_id INTEGER NOT NULL PRIMARY KEY, track_id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
@ -63,8 +79,10 @@ args:
milliseconds INTEGER NOT NULL, milliseconds INTEGER NOT NULL,
bytes INTEGER NOT NULL bytes INTEGER NOT NULL
); );
insert into "Album" (album_id, title) insert into "Album" (album_id, title)
values ( 1, 'Big Ones' ), (2, 'Face Lift'); values ( 1, 'Big Ones' ), (2, 'Face Lift');
insert into "Track" insert into "Track"
(track_id, name, album_id, milliseconds, bytes) (track_id, name, album_id, milliseconds, bytes)
values values
@ -75,16 +93,24 @@ args:
( 5, 'Random', 2, 1094732, 6032547) , ( 5, 'Random', 2, 1094732, 6032547) ,
( 6, 'Good One', 2, 346208, 6732987) , ( 6, 'Good One', 2, 346208, 6732987) ,
( 7, 'Mistress', 2, 420985, 7521946); ( 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" ( CREATE table "Tag" (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
album_id INTEGER NOT NULL REFERENCES "Album"(album_id) album_id INTEGER NOT NULL REFERENCES "Album"(album_id)
); );
INSERT INTO "Tag" (name, album_id) INSERT INTO "Tag" (name, album_id)
VALUES VALUES
( 'Rock', 1), ( 'Rock', 1),
( 'Folk', 1), ( 'Folk', 1),
( 'Hip Hop', 2); ( 'Hip Hop', 2);
CREATE TABLE employee ( CREATE TABLE employee (
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE, name TEXT NOT NULL UNIQUE,
@ -109,6 +135,16 @@ args:
schema: public schema: public
name: article 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 #Object relationship
- type: create_object_relationship - type: create_object_relationship
args: args:
@ -161,6 +197,14 @@ args:
table: Track table: Track
column: album_id 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 # Insert values in Album and Track
- type: track_table - type: track_table
args: args:

View File

@ -3,9 +3,11 @@ args:
- type: run_sql - type: run_sql
args: args:
sql: | sql: |
DROP FUNCTION get_articles(json, author);
DROP TABLE article; DROP TABLE article;
DROP TABLE author; DROP TABLE author;
DROP TABLE contact; DROP TABLE contact;
DROP FUNCTION track_size_mb("Track");
DROP TABLE "Track"; DROP TABLE "Track";
DROP TABLE "Tag"; DROP TABLE "Tag";
DROP TABLE "Album"; DROP TABLE "Album";

View File

@ -894,6 +894,12 @@ class TestGraphQLQueryOrderBy:
def test_album_order_by_tracks_tags(self, hge_ctx, transport): 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) 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 @classmethod
def dir(cls): def dir(cls):
return 'queries/graphql_query/order_by' return 'queries/graphql_query/order_by'