@@ -219,7 +219,7 @@ data ResolverContext = ResolverContext
219
219
}
220
220
221
221
resolveColumnField :: Column -> CoercibleField
222
- resolveColumnField col = CoercibleField (colName col) mempty (colNominalType col) Nothing (colDefault col)
222
+ resolveColumnField col = CoercibleField (colName col) mempty False (colNominalType col) Nothing (colDefault col)
223
223
224
224
resolveTableFieldName :: Table -> FieldName -> CoercibleField
225
225
resolveTableFieldName table fieldName =
@@ -228,11 +228,14 @@ resolveTableFieldName table fieldName =
228
228
229
229
resolveTableField :: Table -> Field -> CoercibleField
230
230
resolveTableField table (fieldName, [] ) = resolveTableFieldName table fieldName
231
- -- If the field is known and a JSON path is given, always assume the JSON type. But don't assume a type for entirely unknown fields.
232
231
resolveTableField table (fieldName, jp) =
233
232
case resolveTableFieldName table fieldName of
234
- cf@ CoercibleField {cfIRType= " " } -> cf{cfJsonPath= jp}
235
- cf -> cf{cfJsonPath= jp, cfIRType= " json" }
233
+ -- types that are already json/jsonb don't need to be converted with `to_jsonb` for using arrow operators `data->attr`
234
+ -- this prevents indexes not applying https://github.com/PostgREST/postgrest/issues/2594
235
+ cf@ CoercibleField {cfIRType= " json" } -> cf{cfJsonPath= jp}
236
+ cf@ CoercibleField {cfIRType= " jsonb" } -> cf{cfJsonPath= jp}
237
+ -- other types will get converted `to_jsonb(col)->attr`
238
+ cf -> cf{cfJsonPath= jp, cfToJson= True }
236
239
237
240
-- | Resolve a type within the context based on the given field name and JSON path. Although there are situations where failure to resolve a field is considered an error (see `resolveOrError`), there are also situations where we allow it (RPC calls). If it should be an error and `resolveOrError` doesn't fit, ensure to check the `cfIRType` isn't empty.
238
241
resolveTypeOrUnknown :: ResolverContext -> Field -> CoercibleField
@@ -289,7 +292,7 @@ readPlan qi@QualifiedIdentifier{..} AppConfig{configDbMaxRows} SchemaCache{dbTab
289
292
addRels qiSchema (iAction apiRequest) dbRelationships Nothing =<<
290
293
addLogicTrees ctx apiRequest =<<
291
294
addRanges apiRequest =<<
292
- addOrders apiRequest =<<
295
+ addOrders ctx apiRequest =<<
293
296
addFilters ctx apiRequest (initReadRequest ctx $ QueryParams. qsSelect $ iQueryParams apiRequest)
294
297
295
298
-- Build the initial read plan tree
@@ -526,38 +529,41 @@ addFilters ctx ApiRequest{..} rReq =
526
529
addFilterToNode =
527
530
updateNode (\ flt (Node q@ ReadPlan {from= fromTable, where_= lf} f) -> Node q{ReadPlan. where_= addFilterToLogicForest (resolveFilter ctx{qi= fromTable} flt) lf} f)
528
531
529
- addOrders :: ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
530
- addOrders ApiRequest {.. } rReq =
532
+ addOrders :: ResolverContext -> ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
533
+ addOrders ctx ApiRequest {.. } rReq =
531
534
case iAction of
532
535
ActionMutate _ -> Right rReq
533
536
_ -> foldr addOrderToNode (Right rReq) qsOrder
534
537
where
535
538
QueryParams. QueryParams {.. } = iQueryParams
536
539
537
540
addOrderToNode :: (EmbedPath , [OrderTerm ]) -> Either ApiRequestError ReadPlanTree -> Either ApiRequestError ReadPlanTree
538
- addOrderToNode = updateNode (\ o (Node q f) -> Node q{order= o} f)
541
+ addOrderToNode = updateNode (\ o (Node q f) -> Node q{order= resolveOrder ctx <$> o} f)
542
+
543
+ resolveOrder :: ResolverContext -> OrderTerm -> CoercibleOrderTerm
544
+ resolveOrder _ (OrderRelationTerm a b c d) = CoercibleOrderRelationTerm a b c d
545
+ resolveOrder ctx (OrderTerm fld dir nulls) = CoercibleOrderTerm (resolveTypeOrUnknown ctx fld) dir nulls
539
546
540
547
-- Validates that the related resource on the order is an embedded resource,
541
548
-- e.g. if `clients` is inside the `select` in /projects?order=clients(id)&select=*,clients(*),
542
549
-- and if it's a to-one relationship, it adds the right alias to the OrderRelationTerm so the generated query can succeed.
543
- -- TODO might be clearer if there's an additional intermediate type
544
550
addRelatedOrders :: ReadPlanTree -> Either ApiRequestError ReadPlanTree
545
551
addRelatedOrders (Node rp@ ReadPlan {order,from} forest) = do
546
- newOrder <- getRelOrder `traverse` order
552
+ newOrder <- newRelOrder `traverse` order
547
553
Node rp{order= newOrder} <$> addRelatedOrders `traverse` forest
548
554
where
549
- getRelOrder ot @ OrderTerm {} = Right ot
550
- getRelOrder ot @ OrderRelationTerm {otRelation } =
551
- let foundRP = rootLabel <$> find (\ (Node ReadPlan {relName, relAlias} _) -> otRelation == fromMaybe relName relAlias) forest in
555
+ newRelOrder cot @ CoercibleOrderTerm {} = Right cot
556
+ newRelOrder cot @ CoercibleOrderRelationTerm {coRelation } =
557
+ let foundRP = rootLabel <$> find (\ (Node ReadPlan {relName, relAlias} _) -> coRelation == fromMaybe relName relAlias) forest in
552
558
case foundRP of
553
559
Just ReadPlan {relName,relAlias,relAggAlias,relToParent} ->
554
560
let isToOne = relIsToOne <$> relToParent
555
561
name = fromMaybe relName relAlias in
556
562
if isToOne == Just True
557
- then Right $ ot{otRelation = relAggAlias}
563
+ then Right $ cot{coRelation = relAggAlias}
558
564
else Left $ RelatedOrderNotToOne (qiName from) name
559
565
Nothing ->
560
- Left $ NotEmbedded otRelation
566
+ Left $ NotEmbedded coRelation
561
567
562
568
-- | Searches for null filters on embeds, e.g. `projects=not.is.null` on `GET /clients?select=*,projects(*)&projects=not.is.null`
563
569
--
@@ -598,7 +604,7 @@ addRelatedOrders (Node rp@ReadPlan{order,from} forest) = do
598
604
-- where_ = [
599
605
-- CoercibleStmnt (
600
606
-- CoercibleFilter {
601
- -- field = CoercibleField {cfName = "projects", cfJsonPath = [], cfIRType = "", cfTransform = Nothing, cfDefault = Nothing},
607
+ -- field = CoercibleField {cfName = "projects", cfJsonPath = [], cfToJson=False, cfIRType = "", cfTransform = Nothing, cfDefault = Nothing},
602
608
-- opExpr = op
603
609
-- }
604
610
-- )
@@ -613,7 +619,7 @@ addRelatedOrders (Node rp@ReadPlan{order,from} forest) = do
613
619
-- Don't do anything to the filter if there's no embedding (a subtree) on projects. Assume it's a normal filter.
614
620
--
615
621
-- >>> ReadPlan.where_ . rootLabel <$> addNullEmbedFilters (readPlanTree nullOp [])
616
- -- Right [CoercibleStmnt (CoercibleFilter {field = CoercibleField {cfName = "projects", cfJsonPath = [], cfIRType = "", cfTransform = Nothing, cfDefault = Nothing}, opExpr = OpExpr True (Is TriNull)})]
622
+ -- Right [CoercibleStmnt (CoercibleFilter {field = CoercibleField {cfName = "projects", cfJsonPath = [], cfToJson = False, cfIRType = "", cfTransform = Nothing, cfDefault = Nothing}, opExpr = OpExpr True (Is TriNull)})]
617
623
--
618
624
-- If there's an embedding on projects, then change the filter to use the internal aggregate name (`clients_projects_1`) so the filter can succeed later.
619
625
--
@@ -637,7 +643,7 @@ addNullEmbedFilters (Node rp@ReadPlan{where_=curLogic} forest) = do
637
643
newNullFilters rPlans = \ case
638
644
(CoercibleExpr b lOp trees) ->
639
645
CoercibleExpr b lOp <$> (newNullFilters rPlans `traverse` trees)
640
- flt@ (CoercibleStmnt (CoercibleFilter (CoercibleField fld [] _ _ _) opExpr)) ->
646
+ flt@ (CoercibleStmnt (CoercibleFilter (CoercibleField fld [] _ _ _ _ ) opExpr)) ->
641
647
let foundRP = find (\ ReadPlan {relName, relAlias} -> fld == fromMaybe relName relAlias) rPlans in
642
648
case (foundRP, opExpr) of
643
649
(Just ReadPlan {relAggAlias}, OpExpr b (Is TriNull )) -> Right $ CoercibleStmnt $ CoercibleFilterNullEmbed b relAggAlias
@@ -726,7 +732,7 @@ mutatePlan mutation qi ApiRequest{iPreferences=Preferences{..}, ..} SchemaCache{
726
732
tbl = HM. lookup qi dbTables
727
733
pkCols = maybe mempty tablePKCols tbl
728
734
logic = map (resolveLogicTree ctx . snd ) qsLogic
729
- rootOrder = maybe [] snd $ find (\ (x, _) -> null x) qsOrder
735
+ rootOrder = resolveOrder ctx <$> maybe [] snd ( find (\ (x, _) -> null x) qsOrder)
730
736
combinedLogic = foldr (addFilterToLogicForest . resolveFilter ctx) logic qsFiltersRoot
731
737
body = payRaw <$> iPayload -- the body is assumed to be json at this stage(ApiRequest validates)
732
738
applyDefaults = preferMissing == Just ApplyDefaults
0 commit comments