refactor: move snippet logic inside Statements.hs

Some snippet logic was unnecessarily at the Query.hs level.

This is better as we can see what inputs we need to form the main
queries.
This commit is contained in:
steve-chavez
2025-09-02 16:26:15 -05:00
committed by Steve Chavez
parent 0844743c30
commit b2ce322d56
2 changed files with 52 additions and 57 deletions
+11 -37
View File
@@ -45,8 +45,7 @@ import PostgREST.ApiRequest.Preferences (PreferCount (..),
PreferHandling (..), PreferHandling (..),
PreferMaxAffected (..), PreferMaxAffected (..),
PreferTransaction (..), PreferTransaction (..),
Preferences (..), Preferences (..))
shouldCount)
import PostgREST.ApiRequest.Types (Mutation (..)) import PostgREST.ApiRequest.Types (Mutation (..))
import PostgREST.Auth.Types (AuthResult (..)) import PostgREST.Auth.Types (AuthResult (..))
import PostgREST.Config (AppConfig (..), import PostgREST.Config (AppConfig (..),
@@ -59,7 +58,6 @@ import PostgREST.Plan (ActionPlan (..),
DbActionPlan (..), DbActionPlan (..),
InfoPlan (..), InfoPlan (..),
InspectPlan (..)) InspectPlan (..))
import PostgREST.Plan.MutatePlan (MutatePlan (..))
import PostgREST.SchemaCache (SchemaCache (..)) import PostgREST.SchemaCache (SchemaCache (..))
import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..)) import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..))
import PostgREST.SchemaCache.Routine (Routine (..), RoutineMap) import PostgREST.SchemaCache.Routine (Routine (..), RoutineMap)
@@ -139,18 +137,9 @@ actionQuery (DbCrud plan@WrappedReadPlan{..}) conf@AppConfig{..} apiReq@ApiReque
(mainActionQuery, mainSQLQuery) (mainActionQuery, mainSQLQuery)
where where
countQuery = QueryBuilder.readPlanToCountQuery wrReadPlan countQuery = QueryBuilder.readPlanToCountQuery wrReadPlan
result@(SQL.Statement mainSQLQuery _ _ _) = SQL.dynamicallyParameterized (Statements.prepareRead result@(SQL.Statement mainSQLQuery _ _ _) = SQL.dynamicallyParameterized
(QueryBuilder.readPlanToQuery wrReadPlan) (Statements.mainRead wrReadPlan countQuery preferCount configDbMaxRows wrMedia wrHandler)
(if preferCount == Just EstimatedCount then decodeIt configDbPreparedStatements
-- LIMIT maxRows + 1 so we can determine below that maxRows was surpassed
QueryBuilder.limitedQuery countQuery ((+ 1) <$> configDbMaxRows)
else
countQuery
)
(shouldCount preferCount)
wrMedia
wrHandler
) decodeIt configDbPreparedStatements
mainActionQuery = do mainActionQuery = do
resultSet <- lift $ SQL.statement mempty result resultSet <- lift $ SQL.statement mempty result
failNotSingular wrMedia resultSet failNotSingular wrMedia resultSet
@@ -165,19 +154,9 @@ actionQuery (DbCrud plan@WrappedReadPlan{..}) conf@AppConfig{..} apiReq@ApiReque
actionQuery (DbCrud plan@MutateReadPlan{..}) conf@AppConfig{..} apiReq@ApiRequest{iPreferences=Preferences{..}} _ = actionQuery (DbCrud plan@MutateReadPlan{..}) conf@AppConfig{..} apiReq@ApiRequest{iPreferences=Preferences{..}} _ =
(mainActionQuery, mainSQLQuery) (mainActionQuery, mainSQLQuery)
where where
(isPut, isInsert, pkCols) = case mrMutatePlan of result@(SQL.Statement mainSQLQuery _ _ _) = SQL.dynamicallyParameterized
Insert{where_,insPkCols} -> ((not . null) where_, True, insPkCols) (Statements.mainWrite mrReadPlan mrMutatePlan mrMedia mrHandler preferRepresentation preferResolution)
_ -> (False,False, mempty); decodeIt configDbPreparedStatements
result@(SQL.Statement mainSQLQuery _ _ _) = SQL.dynamicallyParameterized (Statements.prepareWrite
(QueryBuilder.readPlanToQuery mrReadPlan)
(QueryBuilder.mutatePlanToQuery mrMutatePlan)
isInsert
isPut
mrMedia
mrHandler
preferRepresentation
preferResolution
pkCols) decodeIt configDbPreparedStatements
failMutation resultSet = case mrMutation of failMutation resultSet = case mrMutation of
MutationCreate -> do MutationCreate -> do
failNotSingular mrMedia resultSet failNotSingular mrMedia resultSet
@@ -203,14 +182,9 @@ actionQuery (DbCrud plan@MutateReadPlan{..}) conf@AppConfig{..} apiReq@ApiReques
actionQuery (DbCall plan@CallReadPlan{..}) conf@AppConfig{..} apiReq@ApiRequest{iPreferences=Preferences{..}} _ = actionQuery (DbCall plan@CallReadPlan{..}) conf@AppConfig{..} apiReq@ApiRequest{iPreferences=Preferences{..}} _ =
(mainActionQuery, mainSQLQuery) (mainActionQuery, mainSQLQuery)
where where
result@(SQL.Statement mainSQLQuery _ _ _) = SQL.dynamicallyParameterized (Statements.prepareCall result@(SQL.Statement mainSQLQuery _ _ _) = SQL.dynamicallyParameterized
crProc (Statements.mainCall crProc crCallPlan crReadPlan preferCount crMedia crHandler)
(QueryBuilder.callPlanToQuery crCallPlan) decodeIt configDbPreparedStatements
(QueryBuilder.readPlanToQuery crReadPlan)
(QueryBuilder.readPlanToCountQuery crReadPlan)
(shouldCount preferCount)
crMedia
crHandler) decodeIt configDbPreparedStatements
mainActionQuery = do mainActionQuery = do
resultSet <- lift $ SQL.statement mempty result resultSet <- lift $ SQL.statement mempty result
@@ -275,7 +249,7 @@ resultSetWTotal AppConfig{..} ApiRequest{iPreferences=Preferences{..}} rs@RSStan
where where
explain = explain =
lift . SQL.statement mempty $ lift . SQL.statement mempty $
SQL.dynamicallyParameterized (Statements.preparePlanRows countQuery) SQL.dynamicallyParameterized (Statements.postExplain countQuery)
decodeIt decodeIt
configDbPreparedStatements configDbPreparedStatements
+41 -20
View File
@@ -1,12 +1,13 @@
{-# LANGUAGE NamedFieldPuns #-}
{-| {-|
Module : PostgREST.Query.Statements Module : PostgREST.Query.Statements
Description : PostgREST SQL statements. Description : PostgREST main queries
-} -}
module PostgREST.Query.Statements module PostgREST.Query.Statements
( prepareWrite ( mainWrite
, prepareRead , mainRead
, prepareCall , mainCall
, preparePlanRows , postExplain
) where ) where
import qualified Hasql.DynamicStatements.Snippet as SQL import qualified Hasql.DynamicStatements.Snippet as SQL
@@ -14,15 +15,19 @@ import qualified Hasql.DynamicStatements.Snippet as SQL
import PostgREST.ApiRequest.Preferences import PostgREST.ApiRequest.Preferences
import PostgREST.MediaType (MTVndPlanFormat (..), import PostgREST.MediaType (MTVndPlanFormat (..),
MediaType (..)) MediaType (..))
import PostgREST.Plan.CallPlan
import PostgREST.Plan.MutatePlan as MTPlan
import PostgREST.Plan.ReadPlan
import PostgREST.Query.QueryBuilder
import PostgREST.Query.SqlFragment import PostgREST.Query.SqlFragment
import PostgREST.SchemaCache.Routine (MediaHandler (..), Routine, import PostgREST.SchemaCache.Routine (MediaHandler (..), Routine,
funcReturnsSingle) funcReturnsSingle)
import Protolude import Protolude
prepareWrite :: SQL.Snippet -> SQL.Snippet -> Bool -> Bool -> MediaType -> MediaHandler -> mainWrite :: ReadPlanTree -> MutatePlan -> MediaType -> MediaHandler ->
Maybe PreferRepresentation -> Maybe PreferResolution -> [Text] -> SQL.Snippet Maybe PreferRepresentation -> Maybe PreferResolution -> SQL.Snippet
prepareWrite selectQuery mutateQuery isInsert isPut mt handler rep resolution pKeys = mtSnippet mt snippet mainWrite rPlan mtplan mt handler rep resolution = mtSnippet mt snippet
where where
checkUpsert snip = if isInsert && (isPut || resolution == Just MergeDuplicates) then snip else "''" checkUpsert snip = if isInsert && (isPut || resolution == Just MergeDuplicates) then snip else "''"
pgrstInsertedF = checkUpsert "nullif(current_setting('pgrst.inserted', true),'')::int" pgrstInsertedF = checkUpsert "nullif(current_setting('pgrst.inserted', true),'')::int"
@@ -42,7 +47,7 @@ prepareWrite selectQuery mutateQuery isInsert isPut mt handler rep resolution pK
if isInsert && rep == Just HeadersOnly if isInsert && rep == Just HeadersOnly
then then
"CASE WHEN pg_catalog.count(_postgrest_t) = 1 " <> "CASE WHEN pg_catalog.count(_postgrest_t) = 1 " <>
"THEN coalesce(" <> locationF pKeys <> ", " <> noLocationF <> ") " <> "THEN coalesce(" <> locationF pkCols <> ", " <> noLocationF <> ") " <>
"ELSE " <> noLocationF <> " " <> "ELSE " <> noLocationF <> " " <>
"END" "END"
else noLocationF else noLocationF
@@ -52,8 +57,15 @@ prepareWrite selectQuery mutateQuery isInsert isPut mt handler rep resolution pK
| handler == NoAgg = "SELECT * FROM " <> sourceCTE | handler == NoAgg = "SELECT * FROM " <> sourceCTE
| otherwise = selectQuery | otherwise = selectQuery
prepareRead :: SQL.Snippet -> SQL.Snippet -> Bool -> MediaType -> MediaHandler -> SQL.Snippet selectQuery = readPlanToQuery rPlan
prepareRead selectQuery countQuery countTotal mt handler = mtSnippet mt snippet mutateQuery = mutatePlanToQuery mtplan
(isPut, isInsert, pkCols) = case mtplan of
MTPlan.Insert{MTPlan.where_,insPkCols} -> ((not . null) where_, True, insPkCols)
_ -> (False,False, mempty);
mainRead :: ReadPlanTree -> SQL.Snippet -> Maybe PreferCount -> Maybe Integer ->
MediaType -> MediaHandler -> SQL.Snippet
mainRead rPlan countQuery pCount maxRows mt handler = mtSnippet mt snippet
where where
snippet = snippet =
"WITH " <> sourceCTE <> " AS ( " <> selectQuery <> " ) " <> "WITH " <> sourceCTE <> " AS ( " <> selectQuery <> " ) " <>
@@ -67,12 +79,18 @@ prepareRead selectQuery countQuery countTotal mt handler = mtSnippet mt snippet
"''" <> " AS response_inserted " <> "''" <> " AS response_inserted " <>
"FROM ( SELECT * FROM " <> sourceCTE <> " ) _postgrest_t" "FROM ( SELECT * FROM " <> sourceCTE <> " ) _postgrest_t"
(countCTEF, countResultF) = countF countQuery countTotal (countCTEF, countResultF) = countF countQ $ shouldCount pCount
selectQuery = readPlanToQuery rPlan
countQ =
if pCount == Just EstimatedCount then
-- LIMIT maxRows + 1 so we can determine below that maxRows was surpassed
limitedQuery countQuery ((+ 1) <$> maxRows)
else
countQuery
mainCall :: Routine -> CallPlan -> ReadPlanTree -> Maybe PreferCount ->
prepareCall :: Routine -> SQL.Snippet -> SQL.Snippet -> SQL.Snippet -> Bool -> MediaType -> MediaHandler -> SQL.Snippet
MediaType -> MediaHandler -> SQL.Snippet mainCall rout cPlan rPlan pCount mt handler = mtSnippet mt snippet
prepareCall rout callProcQuery selectQuery countQuery countTotal mt handler = mtSnippet mt snippet
where where
snippet = snippet =
"WITH " <> sourceCTE <> " AS (" <> callProcQuery <> ") " <> "WITH " <> sourceCTE <> " AS (" <> callProcQuery <> ") " <>
@@ -88,11 +106,14 @@ prepareCall rout callProcQuery selectQuery countQuery countTotal mt handler = mt
"''" <> " AS response_inserted " <> "''" <> " AS response_inserted " <>
"FROM (" <> selectQuery <> ") _postgrest_t" "FROM (" <> selectQuery <> ") _postgrest_t"
(countCTEF, countResultF) = countF countQuery countTotal (countCTEF, countResultF) = countF countQuery $ shouldCount pCount
selectQuery = readPlanToQuery rPlan
callProcQuery = callPlanToQuery cPlan
countQuery = readPlanToCountQuery rPlan
-- This occurs after the main query runs, that's why it's prefixed with "post"
preparePlanRows :: SQL.Snippet -> SQL.Snippet postExplain :: SQL.Snippet -> SQL.Snippet
preparePlanRows = explainF PlanJSON mempty postExplain = explainF PlanJSON mempty
mtSnippet :: MediaType -> SQL.Snippet -> SQL.Snippet mtSnippet :: MediaType -> SQL.Snippet -> SQL.Snippet
mtSnippet mediaType snippet = case mediaType of mtSnippet mediaType snippet = case mediaType of