refactor: aliasing logic to addRels

* also shorten query builder alias logic
This commit is contained in:
steve-chavez
2022-10-19 19:49:43 -05:00
committed by Steve Chavez
parent 5f5a0c7764
commit c8b39c6cde
3 changed files with 37 additions and 37 deletions
+28 -29
View File
@@ -95,38 +95,27 @@ readPlan :: QualifiedIdentifier -> AppConfig -> SchemaCache -> ApiRequest -> Eit
readPlan qi@QualifiedIdentifier{..} AppConfig{configDbMaxRows} SchemaCache{dbRelationships} apiRequest =
mapLeft ApiRequestError $
treeRestrictRange configDbMaxRows (iAction apiRequest) =<<
addRels qiSchema dbRelationships Nothing =<<
addRels qiSchema (iAction apiRequest) dbRelationships Nothing =<<
addLogicTrees apiRequest =<<
addRanges apiRequest =<<
addOrders apiRequest =<<
addFilters apiRequest (initReadRequest rootName rootAlias qsSelect)
where
QueryParams.QueryParams{..} = iQueryParams apiRequest
(rootName, rootAlias) = case iAction apiRequest of
ActionRead _ -> (qi, Nothing)
-- the CTE we use for non-read cases has a sourceCTEName(see Statements.hs) as the WITH name so we use the table name as an alias so findRel can find the right relationship
_ -> (QualifiedIdentifier mempty $ decodeUtf8 sourceCTEName, Just qiName)
addFilters apiRequest (initReadRequest qi $ QueryParams.qsSelect $ iQueryParams apiRequest)
-- Build the initial tree with a Depth attribute so when a self join occurs we
-- can differentiate the parent and child tables by having an alias like
-- "table_depth", this is related to
-- http://github.com/PostgREST/postgrest/issues/987.
initReadRequest :: QualifiedIdentifier -> Maybe Alias -> [Tree SelectItem] -> ReadPlanTree
initReadRequest rootQi rootAlias =
foldr (treeEntry rootDepth) initial
-- Build the initial read plan tree
initReadRequest :: QualifiedIdentifier -> [Tree SelectItem] -> ReadPlanTree
initReadRequest qi@QualifiedIdentifier{..} =
foldr (treeEntry rootDepth) $ Node defReadPlan{from=qi, relName=qiName, depth=rootDepth} []
where
rootDepth = 0
rootSchema = qiSchema rootQi
rootName = qiName rootQi
initial = Node (ReadPlan [] rootQi rootAlias [] [] allRange rootName Nothing [] Nothing Nothing Nothing rootDepth) []
defReadPlan = ReadPlan [] (QualifiedIdentifier mempty mempty) Nothing [] [] allRange mempty Nothing [] Nothing Nothing Nothing rootDepth
treeEntry :: Depth -> Tree SelectItem -> ReadPlanTree -> ReadPlanTree
treeEntry depth (Node fld@((fn, _),_,alias, hint, joinType) fldForest) (Node q rForest) =
treeEntry depth (Node fld@((fldName, _),_,alias, hint, joinType) fldForest) (Node q rForest) =
let nxtDepth = succ depth in
case fldForest of
[] -> Node q{select=fld:select q} rForest
_ -> Node q $
foldr (treeEntry nxtDepth)
(Node (ReadPlan [] (QualifiedIdentifier rootSchema fn) Nothing [] [] allRange fn Nothing [] alias hint joinType nxtDepth) [])
(Node defReadPlan{from=QualifiedIdentifier qiSchema fldName, relName=fldName, relAlias=alias, relHint=hint, relJoinType=joinType, depth=nxtDepth} [])
fldForest:rForest
-- | Enforces the `max-rows` config on the result
@@ -138,29 +127,39 @@ treeRestrictRange maxRows _ request = pure $ nodeRestrictRange maxRows <$> reque
nodeRestrictRange m q@ReadPlan{range_=r} = q{range_=restrictRange m r }
-- add relationships to the nodes of the tree by traversing the forest while keeping track of the parentNode
addRels :: Schema -> RelationshipsMap -> Maybe ReadPlanTree -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
addRels schema allRels parentNode (Node query@ReadPlan{from=tbl,relName,relHint,depth} forest) =
addRels :: Schema -> Action -> RelationshipsMap -> Maybe ReadPlanTree -> ReadPlanTree -> Either ApiRequestError ReadPlanTree
addRels schema action allRels parentNode (Node rPlan@ReadPlan{from=tbl,relName,relHint,depth} forest) =
case parentNode of
Just (Node ReadPlan{from=parentNodeQi, fromAlias=aliasQi} _) ->
Just (Node ReadPlan{from=parentNodeQi, fromAlias} _) ->
let newFrom r = if qiName tbl == relName then relForeignTable r else tbl
newReadPlan = (\r ->
if not $ relIsSelf r -- add alias if self rel
then query{from=newFrom r, relToParent=Just r, relJoinConds=getJoinConditions Nothing aliasQi r}
then rPlan{from=newFrom r, relToParent=Just r, relJoinConds=getJoinConditions Nothing fromAlias r}
else
let selfAlias = Just (qiName (newFrom r) <> "_" <> show depth) in
query{from=newFrom r, relToParent=Just r, fromAlias=selfAlias, relJoinConds=getJoinConditions selfAlias aliasQi r}
rPlan{from=newFrom r, relToParent=Just r, fromAlias=selfAlias, relJoinConds=getJoinConditions selfAlias fromAlias r}
) <$> 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
then fromMaybe (qiName parentNodeQi) aliasQi
then fromMaybe (qiName parentNodeQi) fromAlias
else qiName parentNodeQi
rel = findRel schema allRels origin relName relHint
in
Node <$> newReadPlan <*> (updateForest . hush $ Node <$> newReadPlan <*> pure forest)
_ ->
Node query <$> updateForest (Just $ Node query forest)
Nothing -> -- root case
let
newFrom = QualifiedIdentifier mempty $ decodeUtf8 sourceCTEName
newAlias = Just (qiName $ from rPlan)
newReadPlan = case action of
-- the CTE for mutations/rpc is used as WITH sourceCTEName .. SELECT .. FROM sourceCTEName as alias,
-- we use the table name as an alias so findRel can find the right relationship.
ActionMutate _ -> rPlan{from=newFrom, fromAlias=newAlias}
ActionInvoke _ -> rPlan{from=newFrom, fromAlias=newAlias}
_ -> rPlan
in
Node newReadPlan <$> updateForest (Just $ Node newReadPlan forest)
where
updateForest :: Maybe ReadPlanTree -> Either ApiRequestError [ReadPlanTree]
updateForest rq = addRels schema allRels rq `traverse` forest
updateForest rq = addRels schema action allRels rq `traverse` forest
getJoinConditions :: Maybe Alias -> Maybe Alias -> Relationship -> [JoinCondition]
getJoinConditions _ _ ComputedRelationship{} = []
+3
View File
@@ -43,6 +43,9 @@ data ReadPlan = ReadPlan
, relHint :: Maybe Hint
, relJoinType :: Maybe JoinType
, depth :: Depth
-- depth is used bc when a self join occurs we
-- need to differentiate the parent from the child tables by having an alias like
-- "table_depth". See http://github.com/PostgREST/postgrest/issues/987.
}
deriving (Eq)
+6 -8
View File
@@ -56,13 +56,11 @@ readPlanToQuery (Node ReadPlan{select,from=mainQi,fromAlias,where_=logicForest,o
getSelectsJoins :: ReadPlanTree -> ([SQL.Snippet], [SQL.Snippet]) -> ([SQL.Snippet], [SQL.Snippet])
getSelectsJoins (Node ReadPlan{relToParent=Nothing} _) _ = ([], [])
getSelectsJoins rr@(Node ReadPlan{relName=name, relToParent=Just rel, relAlias=alias, relJoinType=joinType} _) (selects,joins) =
getSelectsJoins rr@(Node ReadPlan{relName, relToParent=Just rel, relAlias, relJoinType=joinType, depth} _) (selects,joins) =
let
subquery = readPlanToQuery rr
aliasOrName = fromMaybe name alias
locTblName = qiName (relTable rel) <> "_" <> aliasOrName
localTableName = pgFmtIdent locTblName
internalTableName = pgFmtIdent $ "_" <> locTblName
aliasOrName = fromMaybe relName relAlias
localTableName = pgFmtIdent $ qiName (relTable rel) <> "_" <> aliasOrName <> "_" <> show depth
correlatedSubquery sub al cond =
(if joinType == Just JTInner then "INNER" else "LEFT") <> " JOIN LATERAL ( " <> sub <> " ) AS " <> SQL.sql al <> " ON " <> cond
isToOne = case rel of
@@ -75,10 +73,10 @@ getSelectsJoins rr@(Node ReadPlan{relName=name, relToParent=Just rel, relAlias=a
( SQL.sql ("row_to_json(" <> localTableName <> ".*) AS " <> pgFmtIdent aliasOrName)
, correlatedSubquery subquery localTableName "TRUE")
else
( SQL.sql $ "COALESCE( " <> localTableName <> "." <> internalTableName <> ", '[]') AS " <> pgFmtIdent aliasOrName
( SQL.sql $ "COALESCE( " <> localTableName <> "." <> localTableName <> ", '[]') AS " <> pgFmtIdent aliasOrName
, correlatedSubquery (
"SELECT json_agg(" <> SQL.sql internalTableName <> ") AS " <> SQL.sql internalTableName <>
"FROM (" <> subquery <> " ) AS " <> SQL.sql internalTableName
"SELECT json_agg(" <> SQL.sql localTableName <> ") AS " <> SQL.sql localTableName <>
"FROM (" <> subquery <> " ) AS " <> SQL.sql localTableName
) localTableName $ if joinType == Just JTInner then SQL.sql localTableName <> " IS NOT NULL" else "TRUE")
in
(sel:selects, joi:joins)