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:
committed by
Steve Chavez
parent
c050b61db8
commit
3648986aa8
@@ -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) =
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
Reference in New Issue
Block a user