@@ -21,6 +21,7 @@ import qualified Data.Aeson as JSON
21
21
import qualified Data.ByteString.Char8 as BS
22
22
import qualified Data.HashMap.Strict as HM
23
23
import qualified Data.Set as S
24
+ import qualified Data.Text.Encoding as T
24
25
import qualified Hasql.DynamicStatements.Snippet as SQL
25
26
import qualified Hasql.Encoders as HE
26
27
@@ -119,6 +120,7 @@ getJoin fld node@(Node ReadPlan{relJoinType, relSpread} _) =
119
120
correlatedSubquery (selectSubqAgg <> fromSubqAgg) aggAlias joinCondition
120
121
121
122
mutatePlanToQuery :: MutatePlan -> SQL. Snippet
123
+ -- INSERT: Corresponds to HTTP POST and PUT methods
122
124
mutatePlanToQuery (Insert mainQi iCols body onConflict putConditions returnings _ applyDefaults) =
123
125
" INSERT INTO " <> fromQi mainQi <> (if null iCols then " " else " (" <> cols <> " ) " ) <>
124
126
fromJsonBodyF body iCols True False applyDefaults <>
@@ -142,6 +144,7 @@ mutatePlanToQuery (Insert mainQi iCols body onConflict putConditions returnings
142
144
cols = intercalateSnippet " , " $ pgFmtIdent . cfName <$> iCols
143
145
mergeDups = case onConflict of {Just (MergeDuplicates ,_) -> True ; _ -> False ;}
144
146
147
+ -- UPDATE: Corresponds to HTTP PATCH method
145
148
mutatePlanToQuery (Update mainQi uCols body logicForest returnings applyDefaults)
146
149
| null uCols =
147
150
-- 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
161
164
emptyBodyReturnedColumns = if null returnings then " NULL" else intercalateSnippet " , " (pgFmtColumn (QualifiedIdentifier mempty $ qiName mainQi) <$> returnings)
162
165
cols = intercalateSnippet " , " (pgFmtIdent . cfName <> const " = " <> pgFmtColumn (QualifiedIdentifier mempty " pgrst_body" ) . cfName <$> uCols)
163
166
167
+ -- DELETE: Corresponds to HTTP DELETE method
164
168
mutatePlanToQuery (Delete mainQi logicForest returnings) =
165
169
" DELETE FROM " <> fromQi mainQi <> " " <>
166
170
whereLogic <> " " <>
167
171
returningF mainQi returnings
168
172
where
169
173
whereLogic = if null logicForest then mempty else " WHERE " <> intercalateSnippet " AND " (pgFmtLogicTree mainQi <$> logicForest)
170
174
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
+
171
209
callPlanToQuery :: CallPlan -> SQL. Snippet
172
210
callPlanToQuery (FunctionCall qi params arguments returnsScalar returnsSetOfScalar filterFields returnings) =
173
211
" SELECT " <> (if returnsScalar || returnsSetOfScalar then " pgrst_call.pgrst_scalar" else returnedColumns) <> " " <>
0 commit comments