refactor: correlated subquery for o2m query

Improves the query costs a bit as shown in the tests and reduces code.
This commit is contained in:
steve-chavez
2022-08-06 21:42:53 -05:00
committed by Steve Chavez
parent c050b61db8
commit 3648986aa8
2 changed files with 31 additions and 38 deletions
+24 -31
View File
@@ -38,7 +38,7 @@ import Protolude
readRequestToQuery :: ReadRequest -> SQL.Snippet readRequestToQuery :: ReadRequest -> SQL.Snippet
readRequestToQuery (Node (Select colSelects mainQi tblAlias implJoins logicForest joinConditions_ ordts range, _) forest) = readRequestToQuery (Node (Select colSelects mainQi tblAlias implJoins logicForest joinConditions_ ordts range, _) forest) =
"SELECT " <> "SELECT " <>
intercalateSnippet ", " ((pgFmtSelectItem qi <$> colSelects) ++ selects) <> intercalateSnippet ", " ((pgFmtSelectItem qi <$> colSelects) ++ selects) <> " " <>
"FROM " <> SQL.sql (BS.intercalate ", " (tabl : implJs)) <> " " <> "FROM " <> SQL.sql (BS.intercalate ", " (tabl : implJs)) <> " " <>
intercalateSnippet " " joins <> " " <> intercalateSnippet " " joins <> " " <>
(if null logicForest && null joinConditions_ (if null logicForest && null joinConditions_
@@ -50,38 +50,31 @@ readRequestToQuery (Node (Select colSelects mainQi tblAlias implJoins logicFores
implJs = fromQi <$> implJoins implJs = fromQi <$> implJoins
tabl = fromQi mainQi <> maybe mempty (\a -> " AS " <> pgFmtIdent a) tblAlias tabl = fromQi mainQi <> maybe mempty (\a -> " AS " <> pgFmtIdent a) tblAlias
qi = maybe mainQi (QualifiedIdentifier mempty) tblAlias qi = maybe mainQi (QualifiedIdentifier mempty) tblAlias
(joins, selects) = foldr getJoinsSelects ([],[]) forest (selects, joins) = foldr getSelectsJoins ([],[]) forest
getJoinsSelects :: ReadRequest -> ([SQL.Snippet], [SQL.Snippet]) -> ([SQL.Snippet], [SQL.Snippet]) getSelectsJoins :: ReadRequest -> ([SQL.Snippet], [SQL.Snippet]) -> ([SQL.Snippet], [SQL.Snippet])
getJoinsSelects rr@(Node (_, (name, Just Relationship{relCardinality=card,relTable=QualifiedIdentifier{qiName=table}}, alias, _, joinType, _)) _) (joins,selects) = getSelectsJoins rr@(Node (_, (name, Just Relationship{relCardinality=card,relTable=QualifiedIdentifier{qiName=table}}, alias, _, joinType, _)) _) (selects,joins) =
let subquery = readRequestToQuery rr in let
case card of subquery = readRequestToQuery rr
M2O _ _ -> aliasOrName = fromMaybe name alias
let aliasOrName = fromMaybe name alias locTblName = table <> "_" <> aliasOrName
localTableName = pgFmtIdent $ table <> "_" <> aliasOrName localTableName = pgFmtIdent locTblName
sel = SQL.sql ("row_to_json(" <> localTableName <> ".*) AS " <> pgFmtIdent aliasOrName) internalTableName = pgFmtIdent $ "_" <> locTblName
joi = (if joinType == Just JTInner then " INNER" else " LEFT") correlatedSubquery sub al cond =
<> " JOIN LATERAL( " <> subquery <> " ) AS " <> SQL.sql localTableName <> " ON TRUE " in (if joinType == Just JTInner then "INNER" else "LEFT") <> " JOIN LATERAL ( " <> sub <> " ) AS " <> SQL.sql al <> " ON " <> cond
(joi:joins,sel:selects) (sel, joi) = case card of
_ -> case joinType of M2O _ _ ->
Just JTInner -> ( SQL.sql ("row_to_json(" <> localTableName <> ".*) AS " <> pgFmtIdent aliasOrName)
let aliasOrName = fromMaybe name alias , correlatedSubquery subquery localTableName "TRUE")
locTblName = table <> "_" <> aliasOrName
localTableName = pgFmtIdent locTblName
internalTableName = pgFmtIdent $ "_" <> locTblName
sel = SQL.sql $ localTableName <> "." <> internalTableName <> " AS " <> pgFmtIdent aliasOrName
joi = "INNER JOIN LATERAL(" <>
"SELECT json_agg(" <> SQL.sql internalTableName <> ") AS " <> SQL.sql internalTableName <>
"FROM (" <> subquery <> " ) AS " <> SQL.sql internalTableName <>
") AS " <> SQL.sql localTableName <> " ON " <> SQL.sql localTableName <> "IS NOT NULL" in
(joi:joins,sel:selects)
_ -> _ ->
let sel = "COALESCE ((" ( SQL.sql $ "COALESCE( " <> localTableName <> "." <> internalTableName <> ", '[]') AS " <> pgFmtIdent aliasOrName
<> "SELECT json_agg(" <> SQL.sql (pgFmtIdent table) <> ".*) " , correlatedSubquery (
<> "FROM (" <> subquery <> ") " <> SQL.sql (pgFmtIdent table) <> " " "SELECT json_agg(" <> SQL.sql internalTableName <> ") AS " <> SQL.sql internalTableName <>
<> "), '[]') AS " <> SQL.sql (pgFmtIdent (fromMaybe name alias)) in "FROM (" <> subquery <> " ) AS " <> SQL.sql internalTableName
(joins,sel:selects) ) localTableName $ if joinType == Just JTInner then SQL.sql localTableName <> " IS NOT NULL" else "TRUE")
getJoinsSelects (Node (_, (_, Nothing, _, _, _, _)) _) _ = ([], []) in
(sel:selects, joi:joins)
getSelectsJoins (Node (_, (_, Nothing, _, _, _, _)) _) _ = ([], [])
mutateRequestToQuery :: MutateRequest -> SQL.Snippet mutateRequestToQuery :: MutateRequest -> SQL.Snippet
mutateRequestToQuery (Insert mainQi iCols body onConflct putConditions returnings) = mutateRequestToQuery (Insert mainQi iCols body onConflct putConditions returnings) =
+7 -7
View File
@@ -14,8 +14,8 @@ import Test.Hspec hiding (pendingWith)
import Test.Hspec.Wai import Test.Hspec.Wai
import Test.Hspec.Wai.JSON import Test.Hspec.Wai.JSON
import PostgREST.Config.PgVersion (PgVersion, pgVersion120, import PostgREST.Config.PgVersion (PgVersion, pgVersion100,
pgVersion130, pgVersion100) pgVersion120, pgVersion130)
import Protolude hiding (get) import Protolude hiding (get)
import SpecHelper import SpecHelper
@@ -274,8 +274,8 @@ spec actualPgVersion = do
let totalCost = simpleBody r ^? nth 0 . key "Plan" . key "Total Cost" let totalCost = simpleBody r ^? nth 0 . key "Plan" . key "Total Cost"
liftIO $ totalCost `shouldBe` liftIO $ totalCost `shouldBe`
if actualPgVersion > pgVersion120 if actualPgVersion > pgVersion120
then Just [aesonQQ|58.26|] then Just [aesonQQ|33.25|]
else Just [aesonQQ|33.25|] else Just [aesonQQ|33.27|]
it "a many to one doesn't surpass a threshold" $ do it "a many to one doesn't surpass a threshold" $ do
r <- request methodGet "/projects?select=*,clients(*)&id=eq.1" r <- request methodGet "/projects?select=*,clients(*)&id=eq.1"
@@ -293,9 +293,9 @@ spec actualPgVersion = do
let totalCost = simpleBody r ^? nth 0 . key "Plan" . key "Total Cost" let totalCost = simpleBody r ^? nth 0 . key "Plan" . key "Total Cost"
liftIO $ totalCost `shouldBe` liftIO $ totalCost `shouldBe`
if | actualPgVersion > pgVersion120 -> Just [aesonQQ|130.44|] if | actualPgVersion > pgVersion120 -> Just [aesonQQ|69.34|]
| actualPgVersion > pgVersion100 -> Just [aesonQQ|69.34|] | actualPgVersion > pgVersion100 -> Just [aesonQQ|69.36|]
| otherwise -> Just [aesonQQ|70.79|] | otherwise -> Just [aesonQQ|70.81|]
disabledSpec :: SpecWith ((), Application) disabledSpec :: SpecWith ((), Application)
disabledSpec = disabledSpec =