@@ -219,7 +219,7 @@ data ResolverContext = ResolverContext
219219 }
220220
221221resolveColumnField :: 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)
223223
224224resolveTableFieldName :: Table -> FieldName -> CoercibleField
225225resolveTableFieldName table fieldName =
@@ -228,11 +228,14 @@ resolveTableFieldName table fieldName =
228228
229229resolveTableField :: Table -> Field -> CoercibleField
230230resolveTableField 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.
232231resolveTableField table (fieldName, jp) =
233232 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 }
236239
237240-- | 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.
238241resolveTypeOrUnknown :: ResolverContext -> Field -> CoercibleField
@@ -289,7 +292,7 @@ readPlan qi@QualifiedIdentifier{..} AppConfig{configDbMaxRows} SchemaCache{dbTab
289292 addRels qiSchema (iAction apiRequest) dbRelationships Nothing =<<
290293 addLogicTrees ctx apiRequest =<<
291294 addRanges apiRequest =<<
292- addOrders apiRequest =<<
295+ addOrders ctx apiRequest =<<
293296 addFilters ctx apiRequest (initReadRequest ctx $ QueryParams. qsSelect $ iQueryParams apiRequest)
294297
295298-- Build the initial read plan tree
@@ -526,38 +529,41 @@ addFilters ctx ApiRequest{..} rReq =
526529 addFilterToNode =
527530 updateNode (\ flt (Node q@ ReadPlan {from= fromTable, where_= lf} f) -> Node q{ReadPlan. where_= addFilterToLogicForest (resolveFilter ctx{qi= fromTable} flt) lf} f)
528531
529- addOrders :: ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
530- addOrders ApiRequest {.. } rReq =
532+ addOrders :: ResolverContext -> ApiRequest -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
533+ addOrders ctx ApiRequest {.. } rReq =
531534 case iAction of
532535 ActionMutate _ -> Right rReq
533536 _ -> foldr addOrderToNode (Right rReq) qsOrder
534537 where
535538 QueryParams. QueryParams {.. } = iQueryParams
536539
537540 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
539546
540547-- Validates that the related resource on the order is an embedded resource,
541548-- e.g. if `clients` is inside the `select` in /projects?order=clients(id)&select=*,clients(*),
542549-- 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
544550addRelatedOrders :: ReadPlanTree -> Either ApiRequestError ReadPlanTree
545551addRelatedOrders (Node rp@ ReadPlan {order,from} forest) = do
546- newOrder <- getRelOrder `traverse` order
552+ newOrder <- newRelOrder `traverse` order
547553 Node rp{order= newOrder} <$> addRelatedOrders `traverse` forest
548554 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
552558 case foundRP of
553559 Just ReadPlan {relName,relAlias,relAggAlias,relToParent} ->
554560 let isToOne = relIsToOne <$> relToParent
555561 name = fromMaybe relName relAlias in
556562 if isToOne == Just True
557- then Right $ ot{otRelation = relAggAlias}
563+ then Right $ cot{coRelation = relAggAlias}
558564 else Left $ RelatedOrderNotToOne (qiName from) name
559565 Nothing ->
560- Left $ NotEmbedded otRelation
566+ Left $ NotEmbedded coRelation
561567
562568-- | Searches for null filters on embeds, e.g. `projects=not.is.null` on `GET /clients?select=*,projects(*)&projects=not.is.null`
563569--
@@ -598,7 +604,7 @@ addRelatedOrders (Node rp@ReadPlan{order,from} forest) = do
598604-- where_ = [
599605-- CoercibleStmnt (
600606-- 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},
602608-- opExpr = op
603609-- }
604610-- )
@@ -613,7 +619,7 @@ addRelatedOrders (Node rp@ReadPlan{order,from} forest) = do
613619-- Don't do anything to the filter if there's no embedding (a subtree) on projects. Assume it's a normal filter.
614620--
615621-- >>> 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)})]
617623--
618624-- 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.
619625--
@@ -637,7 +643,7 @@ addNullEmbedFilters (Node rp@ReadPlan{where_=curLogic} forest) = do
637643 newNullFilters rPlans = \ case
638644 (CoercibleExpr b lOp trees) ->
639645 CoercibleExpr b lOp <$> (newNullFilters rPlans `traverse` trees)
640- flt@ (CoercibleStmnt (CoercibleFilter (CoercibleField fld [] _ _ _) opExpr)) ->
646+ flt@ (CoercibleStmnt (CoercibleFilter (CoercibleField fld [] _ _ _ _ ) opExpr)) ->
641647 let foundRP = find (\ ReadPlan {relName, relAlias} -> fld == fromMaybe relName relAlias) rPlans in
642648 case (foundRP, opExpr) of
643649 (Just ReadPlan {relAggAlias}, OpExpr b (Is TriNull )) -> Right $ CoercibleStmnt $ CoercibleFilterNullEmbed b relAggAlias
@@ -726,7 +732,7 @@ mutatePlan mutation qi ApiRequest{iPreferences=Preferences{..}, ..} SchemaCache{
726732 tbl = HM. lookup qi dbTables
727733 pkCols = maybe mempty tablePKCols tbl
728734 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)
730736 combinedLogic = foldr (addFilterToLogicForest . resolveFilter ctx) logic qsFiltersRoot
731737 body = payRaw <$> iPayload -- the body is assumed to be json at this stage(ApiRequest validates)
732738 applyDefaults = preferMissing == Just ApplyDefaults
0 commit comments