refactor: simplify join conditions logic

This commit is contained in:
steve-chavez
2022-10-19 19:49:43 -05:00
committed by Steve Chavez
parent 922da4520f
commit 5f5a0c7764
5 changed files with 52 additions and 58 deletions
+1 -9
View File
@@ -9,7 +9,6 @@ module PostgREST.ApiRequest.Types
, Field , Field
, Filter(..) , Filter(..)
, Hint , Hint
, JoinCondition(..)
, JoinType(..) , JoinType(..)
, JsonOperand(..) , JsonOperand(..)
, JsonOperation(..) , JsonOperation(..)
@@ -33,8 +32,7 @@ module PostgREST.ApiRequest.Types
) where ) where
import PostgREST.MediaType (MediaType (..)) import PostgREST.MediaType (MediaType (..))
import PostgREST.SchemaCache.Identifiers (FieldName, import PostgREST.SchemaCache.Identifiers (FieldName)
QualifiedIdentifier)
import PostgREST.SchemaCache.Proc (ProcDescription (..)) import PostgREST.SchemaCache.Proc (ProcDescription (..))
import PostgREST.SchemaCache.Relationship (Relationship) import PostgREST.SchemaCache.Relationship (Relationship)
@@ -72,12 +70,6 @@ data RangeError
type NodeName = Text type NodeName = Text
type Depth = Integer type Depth = Integer
data JoinCondition =
JoinCondition
(QualifiedIdentifier, FieldName)
(QualifiedIdentifier, FieldName)
deriving (Eq)
data OrderTerm = OrderTerm data OrderTerm = OrderTerm
{ otTerm :: Field { otTerm :: Field
, otDirection :: Maybe OrderDirection , otDirection :: Maybe OrderDirection
+21 -26
View File
@@ -95,7 +95,7 @@ readPlan :: QualifiedIdentifier -> AppConfig -> SchemaCache -> ApiRequest -> Eit
readPlan qi@QualifiedIdentifier{..} AppConfig{configDbMaxRows} SchemaCache{dbRelationships} apiRequest = readPlan qi@QualifiedIdentifier{..} AppConfig{configDbMaxRows} SchemaCache{dbRelationships} apiRequest =
mapLeft ApiRequestError $ mapLeft ApiRequestError $
treeRestrictRange configDbMaxRows (iAction apiRequest) =<< treeRestrictRange configDbMaxRows (iAction apiRequest) =<<
augmentRequestWithJoin qiSchema dbRelationships =<< addRels qiSchema dbRelationships Nothing =<<
addLogicTrees apiRequest =<< addLogicTrees apiRequest =<<
addRanges apiRequest =<< addRanges apiRequest =<<
addOrders apiRequest =<< addOrders apiRequest =<<
@@ -118,7 +118,7 @@ initReadRequest rootQi rootAlias =
rootDepth = 0 rootDepth = 0
rootSchema = qiSchema rootQi rootSchema = qiSchema rootQi
rootName = qiName rootQi rootName = qiName rootQi
initial = Node (ReadPlan [] rootQi rootAlias [] [] [] allRange rootName Nothing Nothing Nothing Nothing rootDepth) [] initial = Node (ReadPlan [] rootQi rootAlias [] [] allRange rootName Nothing [] Nothing Nothing Nothing rootDepth) []
treeEntry :: Depth -> Tree SelectItem -> ReadPlanTree -> ReadPlanTree treeEntry :: Depth -> Tree SelectItem -> ReadPlanTree -> ReadPlanTree
treeEntry depth (Node fld@((fn, _),_,alias, hint, joinType) fldForest) (Node q rForest) = treeEntry depth (Node fld@((fn, _),_,alias, hint, joinType) fldForest) (Node q rForest) =
let nxtDepth = succ depth in let nxtDepth = succ depth in
@@ -126,7 +126,7 @@ initReadRequest rootQi rootAlias =
[] -> Node q{select=fld:select q} rForest [] -> Node q{select=fld:select q} rForest
_ -> Node q $ _ -> Node q $
foldr (treeEntry nxtDepth) foldr (treeEntry nxtDepth)
(Node (ReadPlan [] (QualifiedIdentifier rootSchema fn) Nothing [] [] [] allRange fn Nothing alias hint joinType nxtDepth) []) (Node (ReadPlan [] (QualifiedIdentifier rootSchema fn) Nothing [] [] allRange fn Nothing [] alias hint joinType nxtDepth) [])
fldForest:rForest fldForest:rForest
-- | Enforces the `max-rows` config on the result -- | Enforces the `max-rows` config on the result
@@ -137,19 +137,18 @@ treeRestrictRange maxRows _ request = pure $ nodeRestrictRange maxRows <$> reque
nodeRestrictRange :: Maybe Integer -> ReadPlan -> ReadPlan nodeRestrictRange :: Maybe Integer -> ReadPlan -> ReadPlan
nodeRestrictRange m q@ReadPlan{range_=r} = q{range_=restrictRange m r } nodeRestrictRange m q@ReadPlan{range_=r} = q{range_=restrictRange m r }
augmentRequestWithJoin :: Schema -> RelationshipsMap -> ReadPlanTree -> Either ApiRequestError ReadPlanTree -- add relationships to the nodes of the tree by traversing the forest while keeping track of the parentNode
augmentRequestWithJoin schema allRels request =
addJoinConditions Nothing <$> addRels schema allRels Nothing request
addRels :: Schema -> RelationshipsMap -> Maybe ReadPlanTree -> ReadPlanTree -> Either ApiRequestError ReadPlanTree addRels :: Schema -> RelationshipsMap -> Maybe ReadPlanTree -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
addRels schema allRels parentNode (Node query@ReadPlan{from=tbl,relName,relHint,depth} forest) = addRels schema allRels parentNode (Node query@ReadPlan{from=tbl,relName,relHint,depth} forest) =
case parentNode of case parentNode of
Just (Node ReadPlan{from=parentNodeQi, fromAlias=aliasQi} _) -> Just (Node ReadPlan{from=parentNodeQi, fromAlias=aliasQi} _) ->
let newFrom r = if qiName tbl == relName then relForeignTable r else tbl let newFrom r = if qiName tbl == relName then relForeignTable r else tbl
newReadPlan = (\r -> newReadPlan = (\r ->
if not $ relIsSelf r -- add alias if self rel TODO consolidate aliasing in another function if not $ relIsSelf r -- add alias if self rel
then query{from=newFrom r, relToParent=Just r} then query{from=newFrom r, relToParent=Just r, relJoinConds=getJoinConditions Nothing aliasQi r}
else query{from=newFrom r, relToParent=Just r, fromAlias=Just (qiName (newFrom r) <> "_" <> show depth)} else
let selfAlias = Just (qiName (newFrom r) <> "_" <> show depth) in
query{from=newFrom r, relToParent=Just r, fromAlias=selfAlias, relJoinConds=getJoinConditions selfAlias aliasQi r}
) <$> rel ) <$> rel
origin = if depth == 1 -- Only on depth 1 we check if the root(depth 0) has an alias so the sourceCTEName alias can be found as a relationship origin = if depth == 1 -- Only on depth 1 we check if the root(depth 0) has an alias so the sourceCTEName alias can be found as a relationship
then fromMaybe (qiName parentNodeQi) aliasQi then fromMaybe (qiName parentNodeQi) aliasQi
@@ -163,25 +162,21 @@ addRels schema allRels parentNode (Node query@ReadPlan{from=tbl,relName,relHint,
updateForest :: Maybe ReadPlanTree -> Either ApiRequestError [ReadPlanTree] updateForest :: Maybe ReadPlanTree -> Either ApiRequestError [ReadPlanTree]
updateForest rq = addRels schema allRels rq `traverse` forest updateForest rq = addRels schema allRels rq `traverse` forest
-- applies aliasing to join conditions TODO refactor, this should go into the querybuilder module getJoinConditions :: Maybe Alias -> Maybe Alias -> Relationship -> [JoinCondition]
addJoinConditions :: Maybe Alias -> ReadPlanTree -> ReadPlanTree getJoinConditions _ _ ComputedRelationship{} = []
addJoinConditions _ (Node node@ReadPlan{fromAlias=tblAlias, relToParent=Nothing} forest) = Node node (addJoinConditions tblAlias <$> forest) getJoinConditions tblAlias parentAlias Relationship{relTable=qi,relForeignTable=fQi,relCardinality=card} =
addJoinConditions _ (Node node@ReadPlan{fromAlias=tblAlias, relToParent=Just ComputedRelationship{}} forest) = Node node (addJoinConditions tblAlias <$> forest) case card of
addJoinConditions previousAlias (Node query@ReadPlan{fromAlias=tblAlias, relToParent=Just Relationship{relTable=qi,relForeignTable=fQi,relCardinality=card}} forest) = M2M (Junction QualifiedIdentifier{qiName=jtn} _ _ jcols1 jcols2) ->
Node query{joinConditions=joinConds} (addJoinConditions tblAlias <$> forest) (toJoinCondition Nothing Nothing ftN jtn <$> jcols2) ++ (toJoinCondition parentAlias tblAlias tN jtn <$> jcols1)
O2M _ cols ->
toJoinCondition parentAlias tblAlias tN ftN <$> cols
M2O _ cols ->
toJoinCondition parentAlias tblAlias tN ftN <$> cols
O2O _ cols ->
toJoinCondition parentAlias tblAlias tN ftN <$> cols
where where
QualifiedIdentifier{qiSchema=tSchema, qiName=tN} = qi QualifiedIdentifier{qiSchema=tSchema, qiName=tN} = qi
QualifiedIdentifier{qiName=ftN} = fQi QualifiedIdentifier{qiName=ftN} = fQi
joinConds =
case card of
M2M (Junction QualifiedIdentifier{qiName=jtn} _ _ jcols1 jcols2) ->
(toJoinCondition Nothing Nothing ftN jtn <$> jcols2) ++ (toJoinCondition previousAlias tblAlias tN jtn <$> jcols1)
O2M _ cols ->
toJoinCondition previousAlias tblAlias tN ftN <$> cols
M2O _ cols ->
toJoinCondition previousAlias tblAlias tN ftN <$> cols
O2O _ cols ->
toJoinCondition previousAlias tblAlias tN ftN <$> cols
toJoinCondition :: Maybe Alias -> Maybe Alias -> Text -> Text -> (FieldName, FieldName) -> JoinCondition toJoinCondition :: Maybe Alias -> Maybe Alias -> Text -> Text -> (FieldName, FieldName) -> JoinCondition
toJoinCondition prAl newAl tb ftb (c, fc) = toJoinCondition prAl newAl tb ftb (c, fc) =
let qi1 = QualifiedIdentifier tSchema ftb let qi1 = QualifiedIdentifier tSchema ftb
+23 -16
View File
@@ -3,14 +3,15 @@ module PostgREST.Plan.ReadPlan
( ReadPlanTree ( ReadPlanTree
, ReadPlan(..) , ReadPlan(..)
, fstFieldNames , fstFieldNames
, JoinCondition(..)
) where ) where
import Data.Tree (Tree (..)) import Data.Tree (Tree (..))
import PostgREST.ApiRequest.Types (Alias, Depth, Hint, import PostgREST.ApiRequest.Types (Alias, Depth, Hint,
JoinCondition, JoinType, JoinType, LogicTree,
LogicTree, NodeName, NodeName, OrderTerm,
OrderTerm, SelectItem) SelectItem)
import PostgREST.RangeQuery (NonnegRange) import PostgREST.RangeQuery (NonnegRange)
import PostgREST.SchemaCache.Identifiers (FieldName, import PostgREST.SchemaCache.Identifiers (FieldName,
QualifiedIdentifier) QualifiedIdentifier)
@@ -21,21 +22,27 @@ import Protolude
type ReadPlanTree = Tree ReadPlan type ReadPlanTree = Tree ReadPlan
data JoinCondition =
JoinCondition
(QualifiedIdentifier, FieldName)
(QualifiedIdentifier, FieldName)
deriving (Eq)
data ReadPlan = ReadPlan data ReadPlan = ReadPlan
{ select :: [SelectItem] { select :: [SelectItem]
, from :: QualifiedIdentifier , from :: QualifiedIdentifier
, fromAlias :: Maybe Alias , fromAlias :: Maybe Alias
-- ^ A table alias is used in case of self joins -- ^ A table alias is used in case of self joins
, where_ :: [LogicTree] , where_ :: [LogicTree]
, joinConditions :: [JoinCondition] , order :: [OrderTerm]
, order :: [OrderTerm] , range_ :: NonnegRange
, range_ :: NonnegRange , relName :: NodeName
, relName :: NodeName , relToParent :: Maybe Relationship
, relToParent :: Maybe Relationship , relJoinConds :: [JoinCondition]
, relAlias :: Maybe Alias , relAlias :: Maybe Alias
, relHint :: Maybe Hint , relHint :: Maybe Hint
, relJoinType :: Maybe JoinType , relJoinType :: Maybe JoinType
, depth :: Depth , depth :: Depth
} }
deriving (Eq) deriving (Eq)
+6 -6
View File
@@ -39,14 +39,14 @@ import PostgREST.RangeQuery (allRange)
import Protolude import Protolude
readPlanToQuery :: ReadPlanTree -> SQL.Snippet readPlanToQuery :: ReadPlanTree -> SQL.Snippet
readPlanToQuery (Node ReadPlan{select,from=mainQi,fromAlias,where_=logicForest,joinConditions, order, range_=readRange, relToParent} forest) = readPlanToQuery (Node ReadPlan{select,from=mainQi,fromAlias,where_=logicForest,order, range_=readRange, relToParent, relJoinConds} forest) =
"SELECT " <> "SELECT " <>
intercalateSnippet ", " ((pgFmtSelectItem qi <$> select) ++ selects) <> " " <> intercalateSnippet ", " ((pgFmtSelectItem qi <$> select) ++ selects) <> " " <>
fromFrag <> " " <> fromFrag <> " " <>
intercalateSnippet " " joins <> " " <> intercalateSnippet " " joins <> " " <>
(if null logicForest && null joinConditions (if null logicForest && null relJoinConds
then mempty then mempty
else "WHERE " <> intercalateSnippet " AND " (map (pgFmtLogicTree qi) logicForest ++ map pgFmtJoinCondition joinConditions)) <> " " <> else "WHERE " <> intercalateSnippet " AND " (map (pgFmtLogicTree qi) logicForest ++ map pgFmtJoinCondition relJoinConds)) <> " " <>
orderF qi order <> " " <> orderF qi order <> " " <>
limitOffsetF readRange limitOffsetF readRange
where where
@@ -225,14 +225,14 @@ callPlanToQuery (FunctionCall qi params args returnsScalar multipleCall returnin
-- See https://github.com/PostgREST/postgrest/issues/2009#issuecomment-977473031 -- See https://github.com/PostgREST/postgrest/issues/2009#issuecomment-977473031
-- Only for the nodes that have an INNER JOIN linked to the root level. -- Only for the nodes that have an INNER JOIN linked to the root level.
readPlanToCountQuery :: ReadPlanTree -> SQL.Snippet readPlanToCountQuery :: ReadPlanTree -> SQL.Snippet
readPlanToCountQuery (Node ReadPlan{from=mainQi, fromAlias=tblAlias, where_=logicForest, joinConditions=joinConditions_, relToParent=rel} forest) = readPlanToCountQuery (Node ReadPlan{from=mainQi, fromAlias=tblAlias, where_=logicForest, relToParent=rel, relJoinConds} forest) =
"SELECT 1 " <> fromFrag <> "SELECT 1 " <> fromFrag <>
(if null logicForest && null joinConditions_ && null subQueries (if null logicForest && null relJoinConds && null subQueries
then mempty then mempty
else " WHERE " ) <> else " WHERE " ) <>
intercalateSnippet " AND " ( intercalateSnippet " AND " (
map (pgFmtLogicTree qi) logicForest ++ map (pgFmtLogicTree qi) logicForest ++
map pgFmtJoinCondition joinConditions_ ++ map pgFmtJoinCondition relJoinConds ++
subQueries subQueries
) )
where where
+1 -1
View File
@@ -58,7 +58,6 @@ import Text.InterpolatedString.Perl6 (qc)
import PostgREST.ApiRequest.Types (Alias, Field, Filter (..), import PostgREST.ApiRequest.Types (Alias, Field, Filter (..),
FtsOperator (..), FtsOperator (..),
JoinCondition (..),
JsonOperand (..), JsonOperand (..),
JsonOperation (..), JsonOperation (..),
JsonPath, JsonPath,
@@ -72,6 +71,7 @@ import PostgREST.ApiRequest.Types (Alias, Field, Filter (..),
TrileanVal (..)) TrileanVal (..))
import PostgREST.MediaType (MTPlanFormat (..), import PostgREST.MediaType (MTPlanFormat (..),
MTPlanOption (..)) MTPlanOption (..))
import PostgREST.Plan.ReadPlan (JoinCondition (..))
import PostgREST.RangeQuery (NonnegRange, allRange, import PostgREST.RangeQuery (NonnegRange, allRange,
rangeLimit, rangeOffset) rangeLimit, rangeOffset)
import PostgREST.SchemaCache.Identifiers (FieldName, import PostgREST.SchemaCache.Identifiers (FieldName,