refactor: wrap proc logic in asJsonF

* add explicit logic for returning setof scalars
This commit is contained in:
steve-chavez
2023-04-04 19:55:10 -05:00
committed by Steve Chavez
parent d945e8c06a
commit 16f2849724
7 changed files with 41 additions and 30 deletions
+5 -2
View File
@@ -56,7 +56,8 @@ import PostgREST.SchemaCache.Identifiers (FieldName,
Schema) Schema)
import PostgREST.SchemaCache.Proc (ProcDescription (..), import PostgREST.SchemaCache.Proc (ProcDescription (..),
ProcParam (..), ProcsMap, ProcParam (..), ProcsMap,
procReturnsScalar) procReturnsScalar,
procReturnsSetOfScalar)
import PostgREST.SchemaCache.Relationship (Cardinality (..), import PostgREST.SchemaCache.Relationship (Cardinality (..),
Junction (..), Junction (..),
Relationship (..), Relationship (..),
@@ -548,6 +549,7 @@ callPlan proc ApiRequest{iPreferences=Preferences{..}} paramKeys args readReq =
, funCParams = callParams , funCParams = callParams
, funCArgs = Just args , funCArgs = Just args
, funCScalar = procReturnsScalar proc , funCScalar = procReturnsScalar proc
, funCSetOfScalar = procReturnsSetOfScalar proc
, funCReturning = inferColsEmbedNeeds readReq [] , funCReturning = inferColsEmbedNeeds readReq []
} }
where where
@@ -615,7 +617,8 @@ addFilterToLogicForest flt lf = Stmnt flt : lf
binaryField :: AppConfig -> MediaType -> Maybe ProcDescription -> ReadPlanTree -> Either ApiRequestError (Maybe FieldName) binaryField :: AppConfig -> MediaType -> Maybe ProcDescription -> ReadPlanTree -> Either ApiRequestError (Maybe FieldName)
binaryField AppConfig{configRawMediaTypes} acceptMediaType proc rpTree binaryField AppConfig{configRawMediaTypes} acceptMediaType proc rpTree
| isRawMediaType = | isRawMediaType =
if (procReturnsScalar <$> proc) == Just True if (procReturnsScalar <$> proc) == Just True ||
(procReturnsSetOfScalar <$> proc) == Just True
then Right $ Just "pgrst_scalar" then Right $ Just "pgrst_scalar"
else else
let let
+6 -5
View File
@@ -17,11 +17,12 @@ import PostgREST.SchemaCache.Proc (ProcDescription (..),
import Protolude import Protolude
data CallPlan = FunctionCall data CallPlan = FunctionCall
{ funCQi :: QualifiedIdentifier { funCQi :: QualifiedIdentifier
, funCParams :: CallParams , funCParams :: CallParams
, funCArgs :: Maybe LBS.ByteString , funCArgs :: Maybe LBS.ByteString
, funCScalar :: Bool , funCScalar :: Bool
, funCReturning :: [FieldName] , funCSetOfScalar :: Bool
, funCReturning :: [FieldName]
} }
data CallParams data CallParams
+2 -1
View File
@@ -158,7 +158,8 @@ invokeQuery proc CallReadPlan{crReadPlan, crCallPlan, crBinField} apiReq@ApiRequ
lift . SQL.statement mempty $ lift . SQL.statement mempty $
Statements.prepareCall Statements.prepareCall
(Proc.procReturnsScalar proc) (Proc.procReturnsScalar proc)
(Proc.procReturnsSingle proc) (Proc.procReturnsSingleComposite proc)
(Proc.procReturnsSetOfScalar proc)
(QueryBuilder.callPlanToQuery crCallPlan) (QueryBuilder.callPlanToQuery crCallPlan)
(QueryBuilder.readPlanToQuery crReadPlan) (QueryBuilder.readPlanToQuery crReadPlan)
(QueryBuilder.readPlanToCountQuery crReadPlan) (QueryBuilder.readPlanToCountQuery crReadPlan)
+2 -2
View File
@@ -164,8 +164,8 @@ mutatePlanToQuery (Delete mainQi logicForest range ordts returnings)
(whereRangeIdF, rangeIdF) = mutRangeF mainQi (fst . otTerm <$> ordts) (whereRangeIdF, rangeIdF) = mutRangeF mainQi (fst . otTerm <$> ordts)
callPlanToQuery :: CallPlan -> SQL.Snippet callPlanToQuery :: CallPlan -> SQL.Snippet
callPlanToQuery (FunctionCall qi params args returnsScalar returnings) = callPlanToQuery (FunctionCall qi params args returnsScalar returnsSetOfScalar returnings) =
"SELECT " <> (if returnsScalar then "pgrst_call AS pgrst_scalar " else returnedColumns) <> " " <> "SELECT " <> (if returnsScalar || returnsSetOfScalar then "pgrst_call AS pgrst_scalar " else returnedColumns) <> " " <>
fromCall fromCall
where where
fromCall = case params of fromCall = case params of
+7 -5
View File
@@ -178,16 +178,18 @@ asCsvF = asCsvHeaderF <> " || '\n' || " <> asCsvBodyF
")" ")"
asCsvBodyF = "coalesce(string_agg(substring(_postgrest_t::text, 2, length(_postgrest_t::text) - 2), '\n'), '')" asCsvBodyF = "coalesce(string_agg(substring(_postgrest_t::text, 2, length(_postgrest_t::text) - 2), '\n'), '')"
asJsonF :: Bool -> SqlFragment
asJsonF returnsScalar
| returnsScalar = "coalesce(json_agg(_postgrest_t.pgrst_scalar), '[]')"
| otherwise = "coalesce(json_agg(_postgrest_t), '[]')"
asJsonSingleF :: Bool -> SqlFragment asJsonSingleF :: Bool -> SqlFragment
asJsonSingleF returnsScalar asJsonSingleF returnsScalar
| returnsScalar = "coalesce(json_agg(_postgrest_t.pgrst_scalar)->0, 'null')" | returnsScalar = "coalesce(json_agg(_postgrest_t.pgrst_scalar)->0, 'null')"
| otherwise = "coalesce(json_agg(_postgrest_t)->0, 'null')" | otherwise = "coalesce(json_agg(_postgrest_t)->0, 'null')"
asJsonF :: Bool -> Bool -> Bool -> SqlFragment
asJsonF returnsScalar returnsSetOfScalar returnsSingleComposite
| returnsSingleComposite = "coalesce(json_agg(_postgrest_t)->0, 'null')"
| returnsScalar = "coalesce(json_agg(_postgrest_t.pgrst_scalar)->0, 'null')"
| returnsSetOfScalar = "coalesce(json_agg(_postgrest_t.pgrst_scalar), '[]')"
| otherwise = "coalesce(json_agg(_postgrest_t), '[]')"
asXmlF :: FieldName -> SqlFragment asXmlF :: FieldName -> SqlFragment
asXmlF fieldName = "coalesce(xmlagg(_postgrest_t." <> pgFmtIdent fieldName <> "), '')" asXmlF fieldName = "coalesce(xmlagg(_postgrest_t." <> pgFmtIdent fieldName <> "), '')"
+5 -6
View File
@@ -87,7 +87,7 @@ prepareWrite selectQuery mutateQuery isInsert mt rep pKeys =
| getMediaType mt == MTTextCSV = asCsvF | getMediaType mt == MTTextCSV = asCsvF
| getMediaType mt == MTGeoJSON = asGeoJsonF | getMediaType mt == MTGeoJSON = asGeoJsonF
| getMediaType mt == MTSingularJSON = asJsonSingleF False | getMediaType mt == MTSingularJSON = asJsonSingleF False
| otherwise = asJsonF False | otherwise = asJsonF False False False
selectF selectF
-- prevent using any of the column names in ?select= when no response is returned from the CTE -- prevent using any of the column names in ?select= when no response is returned from the CTE
@@ -123,17 +123,17 @@ prepareRead selectQuery countQuery countTotal mt binaryField =
| getMediaType mt == MTGeoJSON = asGeoJsonF | getMediaType mt == MTGeoJSON = asGeoJsonF
| isJust binaryField && getMediaType mt == MTTextXML = asXmlF $ fromJust binaryField | isJust binaryField && getMediaType mt == MTTextXML = asXmlF $ fromJust binaryField
| isJust binaryField = asBinaryF $ fromJust binaryField | isJust binaryField = asBinaryF $ fromJust binaryField
| otherwise = asJsonF False | otherwise = asJsonF False False False
decodeIt :: HD.Result ResultSet decodeIt :: HD.Result ResultSet
decodeIt = case mt of decodeIt = case mt of
MTPlan{} -> planRow MTPlan{} -> planRow
_ -> HD.singleRow $ standardRow True _ -> HD.singleRow $ standardRow True
prepareCall :: Bool -> Bool -> SQL.Snippet -> SQL.Snippet -> SQL.Snippet -> Bool -> prepareCall :: Bool -> Bool -> Bool -> SQL.Snippet -> SQL.Snippet -> SQL.Snippet -> Bool ->
MediaType -> Maybe FieldName -> Bool -> MediaType -> Maybe FieldName -> Bool ->
SQL.Statement () ResultSet SQL.Statement () ResultSet
prepareCall returnsScalar returnsSingle callProcQuery selectQuery countQuery countTotal mt binaryField = prepareCall returnsScalar returnsSingleComposite returnsSetOfScalar callProcQuery selectQuery countQuery countTotal mt binaryField =
SQL.dynamicallyParameterized (mtSnippet mt snippet) decodeIt SQL.dynamicallyParameterized (mtSnippet mt snippet) decodeIt
where where
snippet = snippet =
@@ -156,8 +156,7 @@ prepareCall returnsScalar returnsSingle callProcQuery selectQuery countQuery cou
| getMediaType mt == MTGeoJSON = asGeoJsonF | getMediaType mt == MTGeoJSON = asGeoJsonF
| isJust binaryField && getMediaType mt == MTTextXML = asXmlF $ fromJust binaryField | isJust binaryField && getMediaType mt == MTTextXML = asXmlF $ fromJust binaryField
| isJust binaryField = asBinaryF $ fromJust binaryField | isJust binaryField = asBinaryF $ fromJust binaryField
| returnsSingle = asJsonSingleF returnsScalar | otherwise = asJsonF returnsScalar returnsSetOfScalar returnsSingleComposite
| otherwise = asJsonF returnsScalar
decodeIt :: HD.Result ResultSet decodeIt :: HD.Result ResultSet
decodeIt = case mt of decodeIt = case mt of
+14 -9
View File
@@ -9,7 +9,8 @@ module PostgREST.SchemaCache.Proc
, ProcsMap , ProcsMap
, RetType(..) , RetType(..)
, procReturnsScalar , procReturnsScalar
, procReturnsSingle , procReturnsSetOfScalar
, procReturnsSingleComposite
, procReturnsVoid , procReturnsVoid
, procTableName , procTableName
) where ) where
@@ -71,21 +72,25 @@ type ProcsMap = HM.HashMap QualifiedIdentifier [ProcDescription]
procReturnsScalar :: ProcDescription -> Bool procReturnsScalar :: ProcDescription -> Bool
procReturnsScalar proc = case proc of procReturnsScalar proc = case proc of
ProcDescription{pdReturnType = Single (Scalar _)} -> True ProcDescription{pdReturnType = Single (Scalar _)} -> True
ProcDescription{pdReturnType = SetOf (Scalar _)} -> True _ -> False
_ -> False
procReturnsSingle :: ProcDescription -> Bool procReturnsSetOfScalar :: ProcDescription -> Bool
procReturnsSingle proc = case proc of procReturnsSetOfScalar proc = case proc of
ProcDescription{pdReturnType = Single _} -> True ProcDescription{pdReturnType = SetOf (Scalar _)} -> True
_ -> False _ -> False
procReturnsSingleComposite :: ProcDescription -> Bool
procReturnsSingleComposite proc = case proc of
ProcDescription{pdReturnType = Single (Composite _)} -> True
_ -> False
procReturnsVoid :: ProcDescription -> Bool procReturnsVoid :: ProcDescription -> Bool
procReturnsVoid proc = case proc of procReturnsVoid proc = case proc of
ProcDescription{pdReturnType = Single (Scalar True)} -> True ProcDescription{pdReturnType = Single (Scalar True)} -> True
_ -> False _ -> False
procTableName :: ProcDescription -> Maybe TableName procTableName :: ProcDescription -> Maybe TableName
procTableName proc = case pdReturnType proc of procTableName proc = case pdReturnType proc of
SetOf (Composite qi) -> Just $ qiName qi SetOf (Composite qi) -> Just $ qiName qi
Single (Composite qi) -> Just $ qiName qi Single (Composite qi) -> Just $ qiName qi
_ -> Nothing _ -> Nothing