@@ -21,6 +21,7 @@ import qualified Data.Aeson as JSON
2121import qualified Data.ByteString.Char8 as BS
2222import qualified Data.HashMap.Strict as HM
2323import qualified Data.Set as S
24+ import qualified Data.Text.Encoding as T
2425import qualified Hasql.DynamicStatements.Snippet as SQL
2526import qualified Hasql.Encoders as HE
2627
@@ -119,6 +120,7 @@ getJoin fld node@(Node ReadPlan{relJoinType, relSpread} _) =
119120 correlatedSubquery (selectSubqAgg <> fromSubqAgg) aggAlias joinCondition
120121
121122mutatePlanToQuery :: MutatePlan -> SQL. Snippet
123+ -- INSERT: Corresponds to HTTP POST and PUT methods
122124mutatePlanToQuery (Insert mainQi iCols body onConflict putConditions returnings _ applyDefaults) =
123125 " INSERT INTO " <> fromQi mainQi <> (if null iCols then " " else " (" <> cols <> " ) " ) <>
124126 fromJsonBodyF body iCols True False applyDefaults <>
@@ -142,6 +144,7 @@ mutatePlanToQuery (Insert mainQi iCols body onConflict putConditions returnings
142144 cols = intercalateSnippet " , " $ pgFmtIdent . cfName <$> iCols
143145 mergeDups = case onConflict of {Just (MergeDuplicates ,_) -> True ; _ -> False ;}
144146
147+ -- UPDATE: Corresponds to HTTP PATCH method
145148mutatePlanToQuery (Update mainQi uCols body logicForest returnings applyDefaults)
146149 | null uCols =
147150 -- if there are no columns we cannot do UPDATE table SET {empty}, it'd be invalid syntax
@@ -161,13 +164,48 @@ mutatePlanToQuery (Update mainQi uCols body logicForest returnings applyDefaults
161164 emptyBodyReturnedColumns = if null returnings then " NULL" else intercalateSnippet " , " (pgFmtColumn (QualifiedIdentifier mempty $ qiName mainQi) <$> returnings)
162165 cols = intercalateSnippet " , " (pgFmtIdent . cfName <> const " = " <> pgFmtColumn (QualifiedIdentifier mempty " pgrst_body" ) . cfName <$> uCols)
163166
167+ -- DELETE: Corresponds to HTTP DELETE method
164168mutatePlanToQuery (Delete mainQi logicForest returnings) =
165169 " DELETE FROM " <> fromQi mainQi <> " " <>
166170 whereLogic <> " " <>
167171 returningF mainQi returnings
168172 where
169173 whereLogic = if null logicForest then mempty else " WHERE " <> intercalateSnippet " AND " (pgFmtLogicTree mainQi <$> logicForest)
170174
175+ -- JSON PATCH: HTTP PATCH method with custom json-patch Content-Type
176+ mutatePlanToQuery (JSONPatch mainQi body logicForest returnings) =
177+ " UPDATE " <> fromQi mainQi <> " SET "
178+ <> " (" <> intercalateSnippet " ," cols <> " )"
179+ <> " = "
180+ <> " ROW(" <> intercalateSnippet " ," vals <> " ) "
181+ <> whereLogic <> " "
182+ <> returningF mainQi returnings
183+ where
184+ -- TODO: At this stage, there must be a body. The Maybe comes from
185+ -- ApiRequest which should be refactored later to avoid 'fromJust'
186+ patchBody = fromJust body
187+
188+ cols :: [SQL. Snippet ]
189+ cols = map getFieldName patchBody
190+ where
191+ getFieldName :: JSONPatchOp -> SQL. Snippet
192+ getFieldName jspOp = SQL. sql $ T. encodeUtf8 $
193+ case jspOp of
194+ Incr field _ -> field
195+ Replace field _ -> field
196+
197+ vals :: [SQL. Snippet ]
198+ vals = map getValAndApplyOp patchBody
199+ where
200+ getValAndApplyOp :: JSONPatchOp -> SQL. Snippet
201+ getValAndApplyOp jspOp = SQL. sql $ T. encodeUtf8 $
202+ case jspOp of
203+ Incr field val -> field <> " + CAST(" <> val <> " AS INTEGER)"
204+ Replace _ val -> val
205+
206+ whereLogic = if null logicForest then mempty else " WHERE " <> intercalateSnippet " AND " (pgFmtLogicTree mainQi <$> logicForest)
207+
208+
171209callPlanToQuery :: CallPlan -> SQL. Snippet
172210callPlanToQuery (FunctionCall qi params arguments returnsScalar returnsSetOfScalar filterFields returnings) =
173211 " SELECT " <> (if returnsScalar || returnsSetOfScalar then " pgrst_call.pgrst_scalar" else returnedColumns) <> " " <>
0 commit comments