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 (Node (Select colSelects mainQi tblAlias implJoins logicForest joinConditions_ ordts range, _) forest) =
|
||||
"SELECT " <>
|
||||
intercalateSnippet ", " ((pgFmtSelectItem qi <$> colSelects) ++ selects) <>
|
||||
intercalateSnippet ", " ((pgFmtSelectItem qi <$> colSelects) ++ selects) <> " " <>
|
||||
"FROM " <> SQL.sql (BS.intercalate ", " (tabl : implJs)) <> " " <>
|
||||
intercalateSnippet " " joins <> " " <>
|
||||
(if null logicForest && null joinConditions_
|
||||
@@ -50,38 +50,31 @@ readRequestToQuery (Node (Select colSelects mainQi tblAlias implJoins logicFores
|
||||
implJs = fromQi <$> implJoins
|
||||
tabl = fromQi mainQi <> maybe mempty (\a -> " AS " <> pgFmtIdent a) 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])
|
||||
getJoinsSelects rr@(Node (_, (name, Just Relationship{relCardinality=card,relTable=QualifiedIdentifier{qiName=table}}, alias, _, joinType, _)) _) (joins,selects) =
|
||||
let subquery = readRequestToQuery rr in
|
||||
case card of
|
||||
M2O _ _ ->
|
||||
let aliasOrName = fromMaybe name alias
|
||||
localTableName = pgFmtIdent $ table <> "_" <> aliasOrName
|
||||
sel = SQL.sql ("row_to_json(" <> localTableName <> ".*) AS " <> pgFmtIdent aliasOrName)
|
||||
joi = (if joinType == Just JTInner then " INNER" else " LEFT")
|
||||
<> " JOIN LATERAL( " <> subquery <> " ) AS " <> SQL.sql localTableName <> " ON TRUE " in
|
||||
(joi:joins,sel:selects)
|
||||
_ -> case joinType of
|
||||
Just JTInner ->
|
||||
let aliasOrName = fromMaybe name alias
|
||||
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)
|
||||
getSelectsJoins :: ReadRequest -> ([SQL.Snippet], [SQL.Snippet]) -> ([SQL.Snippet], [SQL.Snippet])
|
||||
getSelectsJoins rr@(Node (_, (name, Just Relationship{relCardinality=card,relTable=QualifiedIdentifier{qiName=table}}, alias, _, joinType, _)) _) (selects,joins) =
|
||||
let
|
||||
subquery = readRequestToQuery rr
|
||||
aliasOrName = fromMaybe name alias
|
||||
locTblName = table <> "_" <> aliasOrName
|
||||
localTableName = pgFmtIdent locTblName
|
||||
internalTableName = pgFmtIdent $ "_" <> locTblName
|
||||
correlatedSubquery sub al cond =
|
||||
(if joinType == Just JTInner then "INNER" else "LEFT") <> " JOIN LATERAL ( " <> sub <> " ) AS " <> SQL.sql al <> " ON " <> cond
|
||||
(sel, joi) = case card of
|
||||
M2O _ _ ->
|
||||
( SQL.sql ("row_to_json(" <> localTableName <> ".*) AS " <> pgFmtIdent aliasOrName)
|
||||
, correlatedSubquery subquery localTableName "TRUE")
|
||||
_ ->
|
||||
let sel = "COALESCE (("
|
||||
<> "SELECT json_agg(" <> SQL.sql (pgFmtIdent table) <> ".*) "
|
||||
<> "FROM (" <> subquery <> ") " <> SQL.sql (pgFmtIdent table) <> " "
|
||||
<> "), '[]') AS " <> SQL.sql (pgFmtIdent (fromMaybe name alias)) in
|
||||
(joins,sel:selects)
|
||||
getJoinsSelects (Node (_, (_, Nothing, _, _, _, _)) _) _ = ([], [])
|
||||
( SQL.sql $ "COALESCE( " <> localTableName <> "." <> internalTableName <> ", '[]') AS " <> pgFmtIdent aliasOrName
|
||||
, correlatedSubquery (
|
||||
"SELECT json_agg(" <> SQL.sql internalTableName <> ") AS " <> SQL.sql internalTableName <>
|
||||
"FROM (" <> subquery <> " ) AS " <> SQL.sql internalTableName
|
||||
) localTableName $ if joinType == Just JTInner then SQL.sql localTableName <> " IS NOT NULL" else "TRUE")
|
||||
in
|
||||
(sel:selects, joi:joins)
|
||||
getSelectsJoins (Node (_, (_, Nothing, _, _, _, _)) _) _ = ([], [])
|
||||
|
||||
mutateRequestToQuery :: MutateRequest -> SQL.Snippet
|
||||
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.JSON
|
||||
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion120,
|
||||
pgVersion130, pgVersion100)
|
||||
import PostgREST.Config.PgVersion (PgVersion, pgVersion100,
|
||||
pgVersion120, pgVersion130)
|
||||
import Protolude hiding (get)
|
||||
import SpecHelper
|
||||
|
||||
@@ -274,8 +274,8 @@ spec actualPgVersion = do
|
||||
let totalCost = simpleBody r ^? nth 0 . key "Plan" . key "Total Cost"
|
||||
liftIO $ totalCost `shouldBe`
|
||||
if actualPgVersion > pgVersion120
|
||||
then Just [aesonQQ|58.26|]
|
||||
else Just [aesonQQ|33.25|]
|
||||
then Just [aesonQQ|33.25|]
|
||||
else Just [aesonQQ|33.27|]
|
||||
|
||||
it "a many to one doesn't surpass a threshold" $ do
|
||||
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"
|
||||
liftIO $ totalCost `shouldBe`
|
||||
if | actualPgVersion > pgVersion120 -> Just [aesonQQ|130.44|]
|
||||
| actualPgVersion > pgVersion100 -> Just [aesonQQ|69.34|]
|
||||
| otherwise -> Just [aesonQQ|70.79|]
|
||||
if | actualPgVersion > pgVersion120 -> Just [aesonQQ|69.34|]
|
||||
| actualPgVersion > pgVersion100 -> Just [aesonQQ|69.36|]
|
||||
| otherwise -> Just [aesonQQ|70.81|]
|
||||
|
||||
disabledSpec :: SpecWith ((), Application)
|
||||
disabledSpec =
|
||||
|
||||
Reference in New Issue
Block a user