@@ -11,32 +11,41 @@ Squeal data manipulation language.
1111{-# LANGUAGE
1212 DeriveGeneric
1313 , FlexibleContexts
14+ , FlexibleInstances
1415 , GADTs
1516 , GeneralizedNewtypeDeriving
1617 , LambdaCase
18+ , MultiParamTypeClasses
1719 , OverloadedStrings
20+ , PatternSynonyms
1821 , RankNTypes
22+ , ScopedTypeVariables
23+ , TypeApplications
1924 , TypeInType
2025 , TypeOperators
26+ , UndecidableInstances
2127#-}
2228
2329module Squeal.PostgreSQL.Manipulation
2430 ( -- * Manipulation
25− Manipulation (UnsafeManipulation, renderManipulation)
31+ Manipulation (..)
2632 , queryStatement
27− , ColumnValue (..)
28− , ReturningClause (ReturningStar, Returning)
29− , ConflictClause (OnConflictDoRaise, OnConflictDoNothing, OnConflictDoUpdate)
33+ , QueryClause (..)
34+ , pattern Values_
35+ , DefaultAliasable (..)
36+ , ColumnExpression (..)
37+ , ReturningClause (..)
38+ , ConflictClause (..)
39+ , ConflictTarget (..)
40+ , ConflictAction (..)
3041 , UsingClause (..)
3142 -- * Insert
32− , insertRows
33− , insertRow
34− , insertRows_
35− , insertRow_
36− , insertQuery
37− , insertQuery_
43+ , insertInto
44+ , insertInto_
3845 , renderReturningClause
3946 , renderConflictClause
47+ , renderConflictTarget
48+ , renderConflictAction
4049 -- * Update
4150 , update
4251 , update_
@@ -48,6 +57,7 @@ module Squeal.PostgreSQL.Manipulation
4857
4958import Control.DeepSeq
5059import Data.ByteString hiding (foldr)
60+import GHC.TypeLits (KnownSymbol)
5161
5262import qualified Generics.SOP as SOP
5363import qualified GHC.Generics as GHC
@@ -70,7 +80,7 @@ simple insert:
7080let
7181 manipulation :: Manipulation (DBof (Public Schema)) '[] '[]
7282 manipulation =
73− insertRow_ #tab (Set 2 `as` #col1 :* Default `as` #col2)
83+ insertInto_ #tab (Values_ (2 `as` #col1 :* defaultAs #col2))
7484in printSQL manipulation
7585:}
7686INSERT INTO "tab" ("col1", "col2") VALUES (2, DEFAULT)
@@ -83,8 +93,7 @@ parameterized insert:
8393let
8494 manipulation :: Manipulation (DBof (Public Schema)) '[ 'NotNull 'PGint4, 'NotNull 'PGint4 ] '[]
8595 manipulation =
86− insertRow_ #tab
87− (Set (param @1) `as` #col1 :* Set (param @2) `as` #col2)
96+ insertInto_ #tab (Values_ (param @1 `as` #col1 :* param @2 `as` #col2))
8897in printSQL manipulation
8998:}
9099INSERT INTO "tab" ("col1", "col2") VALUES (($1 :: int4), ($2 :: int4))
@@ -95,7 +104,7 @@ returning insert:
95104let
96105 manipulation :: Manipulation (DBof (Public Schema)) '[] '["fromOnly" ::: 'NotNull 'PGint4]
97106 manipulation =
98− insertRow #tab (Set 2 `as` #col1 :* Set 3 `as` #col2)
107+ insertInto #tab (Values_ (2 `as` #col1 :* 3 `as` #col2))
99108 OnConflictDoRaise (Returning (#col1 `as` #fromOnly))
100109in printSQL manipulation
101110:}
@@ -103,29 +112,28 @@ INSERT INTO "tab" ("col1", "col2") VALUES (2, 3) RETURNING "col1" AS "fromOnly"
103112
104113upsert:
105114
106−>>> type Columns = '["col1" ::: 'NoDef :=> 'NotNull 'PGint4, "col2" ::: 'NoDef :=> 'NotNull 'PGint4]
107−>>> type Schema = '["tab" ::: 'Table ('[] :=> Columns)]
115+>>> type CustomersColumns = '["name" ::: 'NoDef :=> 'NotNull 'PGtext, "email" ::: 'NoDef :=> 'NotNull 'PGtext]
116+>>> type CustomersConstraints = '["uq" ::: 'Unique '["name"]]
117+>>> type CustomersSchema = '["customers" ::: 'Table (CustomersConstraints :=> CustomersColumns)]
108118>>> :{
109119let
110− manipulation :: Manipulation (DBof (Public Schema)) '[] '[ "sum" ::: 'NotNull 'PGint4]
120+ manipulation :: Manipulation (DBof (Public CustomersSchema)) '[] '[]
111121 manipulation =
112− insertRows #tab
113− (Set 2 `as` #col1 :* Set 4 `as` #col2)
114− [Set 6 `as` #col1 :* Set 8 `as` #col2]
115− (OnConflictDoUpdate
116− (Set 2 `as` #col1 :* Same `as` #col2)
117− [#col1 .== #col2])
118− (Returning $ (#col1 + #col2) `as` #sum)
122+ insertInto #customers
123+ (Values_ ("John Smith" `as` #name :* "john@smith.com" `as` #email))
124+ (OnConflict (OnConstraint #uq)
125+ (DoUpdate (((#excluded ! #email) <> "; " <> (#customers ! #email)) `as` #email) []))
126+ (Returning Nil)
119127in printSQL manipulation
120128:}
121−INSERT INTO "tab" ("col1", "col2") VALUES (2, 4), (6, 8) ON CONFLICT DO UPDATE SET "col1" = 2 WHERE ("col1" = "col2") RETURNING ("col1" + "col2") AS "sum"
129+INSERT INTO "customers" ("name", "email") VALUES (E'John Smith', E'john@smith.com') ON CONFLICT ON CONSTRAINT "uq" DO UPDATE SET "email" = ("excluded"."email" || (E'; ' || "customers"."email"))
122130
123131query insert:
124132
125133>>> :{
126134let
127135 manipulation :: Manipulation (DBof (Public Schema)) '[] '[]
128− manipulation = insertQuery_ #tab (selectStar (from (table #tab)))
136+ manipulation = insertInto_ #tab (Subquery (selectStar (from (table #tab))))
129137in printSQL manipulation
130138:}
131139INSERT INTO "tab" SELECT * FROM "tab" AS "tab"
@@ -135,9 +143,7 @@ update:
135143>>> :{
136144let
137145 manipulation :: Manipulation (DBof (Public Schema)) '[] '[]
138− manipulation =
139− update_ #tab (Set 2 `as` #col1 :* Same `as` #col2)
140− (#col1 ./= #col2)
146+ manipulation = update_ #tab (2 `as` #col1) (#col1 ./= #col2)
141147in printSQL manipulation
142148:}
143149UPDATE "tab" SET "col1" = 2 WHERE ("col1" <> "col2")
@@ -183,7 +189,7 @@ let
183189 manipulation :: Manipulation (DBof (Public ProductsSchema)) '[ 'NotNull 'PGdate] '[]
184190 manipulation = with
185191 (deleteFrom #products NoUsing (#date .< param @1) ReturningStar `as` #del)
186− (insertQuery_ #products_deleted (selectStar (from (common (#del)))))
192+ (insertInto_ #products_deleted (Subquery (selectStar (from (common #del)))))
187193in printSQL manipulation
188194:}
189195WITH "del" AS (DELETE FROM "products" WHERE ("date" < ($1 :: date)) RETURNING *) INSERT INTO "products_deleted" SELECT * FROM "del" AS "del"
@@ -213,138 +219,184 @@ queryStatement q = UnsafeManipulation $ renderQuery q
213219INSERT statements
214220-----------------------------------------}
215221
216−-- | Insert multiple rows.
217−--
218−-- When a table is created, it contains no data. The first thing to do
219−-- before a database can be of much use is to insert data. Data is
220−-- conceptually inserted one row at a time. Of course you can also insert
221−-- more than one row, but there is no way to insert less than one row.
222−-- Even if you know only some column values, a complete row must be created.
223−insertRows
224− :: ( SOP.SListI columns
225− , SOP.SListI results
226− , db ~ (commons :=> schemas)
222+{- |
223+When a table is created, it contains no data. The first thing to do
224+before a database can be of much use is to insert data. Data is
225+conceptually inserted one row at a time. Of course you can also insert
226+more than one row, but there is no way to insert less than one row.
227+Even if you know only some column values, a complete row must be created.
228+-}
229+insertInto
230+ :: ( db ~ (commons :=> schemas)
227231 , Has sch schemas schema
228232 , Has tab schema ('Table table)
233+ , columns ~ TableToColumns table
229234 , row ~ TableToRow table
230− , columns ~ TableToColumns table )
231− => QualifiedAlias sch tab -- ^ table to insert into
232− -> NP (Aliased (ColumnValue db '[] params)) columns -- ^ row to insert
233− -> [NP (Aliased (ColumnValue db '[] params)) columns] -- ^ more rows to insert
234− -> ConflictClause db table params
235− -- ^ what to do in case of constraint conflict
236− -> ReturningClause db params row results -- ^ results to return
237− -> Manipulation db params results
238−insertRows tab rw rws conflict returning = UnsafeManipulation $
235+ , SOP.SListI columns
236+ , SOP.SListI result )
237+ => QualifiedAlias sch tab
238+ -> QueryClause db params columns
239+ -> ConflictClause tab db params table
240+ -> ReturningClause db params row result
241+ -> Manipulation db params result
242+insertInto tab qry conflict ret = UnsafeManipulation $
239243 "INSERT" <+> "INTO" <+> renderQualifiedAlias tab
240− <+> parenthesized (renderCommaSeparated renderAliasPart rw)
241− <+> "VALUES"
242− <+> commaSeparated
243− ( parenthesized
244− . renderCommaSeparated renderColumnValuePart <$> rw:rws )
245− <> renderConflictClause conflict
246− <> renderReturningClause returning
247− where
248− renderAliasPart, renderColumnValuePart
249− :: Aliased (ColumnValue db '[] params) ty -> ByteString
250− renderAliasPart (_ `As` name) = renderAlias name
251− renderColumnValuePart (value `As` _) = case value of
252− Default -> "DEFAULT"
253− Set expression -> renderExpression expression
254−
255−-- | Insert a single row.
256−insertRow
257− :: ( SOP.SListI columns
258− , SOP.SListI results
259− , db ~ (commons :=> schemas)
260− , Has sch schemas schema
261− , Has tab schema ('Table table)
262− , row ~ TableToRow table
263− , columns ~ TableToColumns table )
264− => QualifiedAlias sch tab -- ^ table to insert into
265− -> NP (Aliased (ColumnValue db '[] params)) columns -- ^ row to insert
266− -> ConflictClause db table params
267− -- ^ what to do in case of constraint conflict
268− -> ReturningClause db params row results -- ^ results to return
269− -> Manipulation db params results
270−insertRow tab rw = insertRows tab rw []
244+ <+> renderQueryClause qry
245+ <> renderConflictClause conflict
246+ <> renderReturningClause ret
271247
272−-- | Insert multiple rows returning `Nil` and raising an error on conflicts.
273−insertRows_
274− :: ( SOP.SListI columns
275− , db ~ (commons :=> schemas)
276− , Has sch schemas schema
277− , Has tab schema ('Table table)
278− , columns ~ TableToColumns table )
279− => QualifiedAlias sch tab -- ^ table to insert into
280− -> NP (Aliased (ColumnValue db '[] params)) columns -- ^ row to insert
281− -> [NP (Aliased (ColumnValue db '[] params)) columns] -- ^ more rows to insert
282− -> Manipulation db params '[]
283−insertRows_ tab rw rws =
284− insertRows tab rw rws OnConflictDoRaise (Returning Nil)
285−
286−-- | Insert a single row returning `Nil` and raising an error on conflicts.
287−insertRow_
288− :: ( SOP.SListI columns
289− , db ~ (commons :=> schemas)
290− , Has sch schemas schema
291− , Has tab schema ('Table table)
292− , columns ~ TableToColumns table )
293− => QualifiedAlias sch tab -- ^ table to insert into
294− -> NP (Aliased (ColumnValue db '[] params)) columns -- ^ row to insert
295− -> Manipulation db params '[]
296−insertRow_ tab rw = insertRow tab rw OnConflictDoRaise (Returning Nil)
297−
298−-- | Insert a `Query`.
299−insertQuery
300− :: ( SOP.SListI columns
301− , SOP.SListI results
302− , db ~ (commons :=> schemas)
248+insertInto_
249+ :: ( db ~ (commons :=> schemas)
303250 , Has sch schemas schema
304251 , Has tab schema ('Table table)
252+ , columns ~ TableToColumns table
305253 , row ~ TableToRow table
306− , columns ~ TableToColumns table )
307− => QualifiedAlias sch tab -- ^ table to insert into
308− -> Query db params (TableToRow table)
309− -> ConflictClause db table params
310− -- ^ what to do in case of constraint conflict
311− -> ReturningClause db params row results -- ^ results to return
312− -> Manipulation db params results
313−insertQuery tab query conflict returning = UnsafeManipulation $
314− "INSERT" <+> "INTO" <+> renderQualifiedAlias tab
315− <+> renderQuery query
316− <> renderConflictClause conflict
317− <> renderReturningClause returning
318−
319−-- | Insert a `Query` returning `Nil` and raising an error on conflicts.
320−insertQuery_
321− :: ( SOP.SListI columns
322− , db ~ (commons :=> schemas)
323− , Has sch schemas schema
324− , Has tab schema ('Table table)
325− , columns ~ TableToColumns table )
326− => QualifiedAlias sch tab -- ^ table to insert into
327− -> Query db params (TableToRow table)
254+ , SOP.SListI columns )
255+ => QualifiedAlias sch tab
256+ -> QueryClause db params columns
328257 -> Manipulation db params '[]
329−insertQuery_ tab query =
330− insertQuery tab query OnConflictDoRaise (Returning Nil)
331−
332−-- | `ColumnValue`s are values to insert or update in a row.
333−-- `Same` updates with the same value.
334−-- `Default` inserts or updates with the @DEFAULT@ value.
335−-- `Set` sets a value to be an `Expression`, which can refer to
336−-- existing value in the row for an update.
337−data ColumnValue
338− (db :: DBType)
339− (columns :: RowType)
340− (params :: [NullityType])
341− (ty :: ColumnType)
258+insertInto_ tab qry =
259+ insertInto tab qry OnConflictDoRaise (Returning Nil)
260+
261+data QueryClause db params columns where
262+ Values
263+ :: SOP.SListI columns
264+ => NP (ColumnExpression db '[] 'Ungrouped params) columns
265+ -> [NP (ColumnExpression db '[] 'Ungrouped params) columns]
266+ -> QueryClause db params columns
267+ Select
268+ :: SOP.SListI columns
269+ => NP (ColumnExpression db from grp params) columns
270+ -> TableExpression db params from grp
271+ -> QueryClause db params columns
272+ Subquery
273+ :: ColumnsToRow columns ~ row
274+ => Query db params row
275+ -> QueryClause db params columns
276+
277+renderQueryClause
278+ :: QueryClause db params columns
279+ -> ByteString
280+renderQueryClause = \case
281+ Values row0 rows ->
282+ parenthesized (renderCommaSeparated renderAliasPart row0)
283+ <+> "VALUES"
284+ <+> commaSeparated
285+ ( parenthesized
286+ . renderCommaSeparated renderValuePart <$> row0 : rows )
287+ Select row0 tab ->
288+ parenthesized (renderCommaSeparatedMaybe renderAliasPartMaybe row0)
289+ <+> "SELECT"
290+ <+> renderCommaSeparatedMaybe renderValuePartMaybe row0
291+ <+> renderTableExpression tab
292+ Subquery qry -> renderQuery qry
342293 where
343− Same :: ColumnValue db (column ': columns) params ty
344− Default :: ColumnValue db columns params ('Def :=> ty)
345− Set
346− :: (forall table. Expression db '[table ::: columns] 'Ungrouped params ty)
347− -> ColumnValue db columns params (constraint :=> ty)
294+ renderAliasPartMaybe, renderValuePartMaybe
295+ :: ColumnExpression db from grp params column -> Maybe ByteString
296+ renderAliasPartMaybe = \case
297+ DefaultAs _ -> Nothing
298+ Specific (_ `As` name) -> Just $ renderAlias name
299+ renderValuePartMaybe = \case
300+ DefaultAs _ -> Nothing
301+ Specific (value `As` _) -> Just $ renderExpression value
302+ renderAliasPart, renderValuePart
303+ :: ColumnExpression db from grp params column -> ByteString
304+ renderAliasPart = \case
305+ DefaultAs name -> renderAlias name
306+ Specific (_ `As` name) -> renderAlias name
307+ renderValuePart = \case
308+ DefaultAs _ -> "DEFAULT"
309+ Specific (value `As` _) -> renderExpression value
310+
311+pattern Values_
312+ :: SOP.SListI columns
313+ => NP (ColumnExpression db '[] 'Ungrouped params) columns
314+ -> QueryClause db params columns
315+pattern Values_ vals = Values vals []
316+
317+data ColumnExpression db from grp params column where
318+ DefaultAs
319+ :: KnownSymbol col
320+ => Alias col
321+ -> ColumnExpression db from grp params (col ::: 'Def :=> ty)
322+ Specific
323+ :: Aliased (Expression db from grp params) (col ::: ty)
324+ -> ColumnExpression db from grp params (col ::: defness :=> ty)
325+
326+instance (KnownSymbol col, column ~ (col ::: defness :=> ty))
327+ => Aliasable col
328+ (Expression db from grp params ty)
329+ (ColumnExpression db from grp params column) where
330+ expression `as` col = Specific (expression `As` col)
331+instance (KnownSymbol col, columns ~ '[col ::: defness :=> ty])
332+ => Aliasable col
333+ (Expression db from grp params ty)
334+ (NP (ColumnExpression db from grp params) columns) where
335+ expression `as` col = expression `as` col :* Nil
336+instance (KnownSymbol col, column ~ (col ::: 'Def :=> ty))
337+ => DefaultAliasable col (ColumnExpression db from grp params column) where
338+ defaultAs = DefaultAs
339+instance (KnownSymbol col, columns ~ '[col ::: 'Def :=> ty])
340+ => DefaultAliasable col (NP (ColumnExpression db from grp params) columns) where
341+ defaultAs col = defaultAs col :* Nil
342+
343+instance (HasUnique tab from row, Has col row ty, colty ~ (col ::: defness :=> ty))
344+ => IsLabel col (ColumnExpression db from 'Ungrouped params colty) where
345+ fromLabel = fromLabel @col `as` Alias
346+instance (HasUnique tab from row, Has col row ty, coltys ~ '[col ::: defness :=> ty])
347+ => IsLabel col
348+ (NP (ColumnExpression db from 'Ungrouped params) coltys) where
349+ fromLabel = fromLabel @col `as` Alias
350+
351+instance (Has tab from row, Has col row ty, colty ~ (col ::: defness :=> ty))
352+ => IsQualified tab col (ColumnExpression db from 'Ungrouped params colty) where
353+ tab ! col = tab ! col `as` col
354+instance (Has tab from row, Has col row ty, coltys ~ '[col ::: defness :=> ty])
355+ => IsQualified tab col (NP (ColumnExpression db from 'Ungrouped params) coltys) where
356+ tab ! col = tab ! col `as` col
357+
358+instance
359+ ( HasUnique tab from row
360+ , Has col row ty
361+ , GroupedBy tab col bys
362+ , colty ~ (col ::: defness :=> ty)
363+ ) => IsLabel col
364+ (ColumnExpression db from ('Grouped bys) params colty) where
365+ fromLabel = fromLabel @col `as` Alias
366+instance
367+ ( HasUnique tab from row
368+ , Has col row ty
369+ , GroupedBy tab col bys
370+ , coltys ~ '[col ::: defness :=> ty]
371+ ) => IsLabel col
372+ (NP (ColumnExpression db from ('Grouped bys) params) coltys) where
373+ fromLabel = fromLabel @col `as` Alias
374+
375+instance
376+ ( Has tab from row
377+ , Has col row ty
378+ , GroupedBy tab col bys
379+ , colty ~ (col ::: defness :=> ty)
380+ ) => IsQualified tab col
381+ (ColumnExpression db from ('Grouped bys) params colty) where
382+ tab ! col = tab ! col `as` col
383+instance
384+ ( Has tab from row
385+ , Has col row ty
386+ , GroupedBy tab col bys
387+ , coltys ~ '[col ::: defness :=> ty]
388+ ) => IsQualified tab col
389+ (NP (ColumnExpression db from ('Grouped bys) params) coltys) where
390+ tab ! col = tab ! col :* Nil
391+
392+renderColumnExpression
393+ :: ColumnExpression db from grp params column
394+ -> ByteString
395+renderColumnExpression = \case
396+ DefaultAs col ->
397+ renderAlias col <+> "=" <+> "DEFAULT"
398+ Specific (expression `As` col) ->
399+ renderAlias col <+> "=" <+> renderExpression expression
348400
349401-- | A `ReturningClause` computes and return value(s) based
350402-- on each row actually inserted, updated or deleted. This is primarily
@@ -384,42 +436,61 @@ renderReturningClause = \case
384436-- `OnConflictDoNothing` simply avoids inserting a row.
385437-- `OnConflictDoUpdate` updates the existing row that conflicts with the row
386438-- proposed for insertion.
387−data ConflictClause
388− (db :: DBType)
389− (table :: TableType)
390− (params :: [NullityType]) where
391− OnConflictDoRaise :: ConflictClause db table params
392− OnConflictDoNothing :: ConflictClause db table params
393− OnConflictDoUpdate
394− :: (row ~ TableToRow table, columns ~ TableToColumns table)
395− => NP (Aliased (ColumnValue db row params)) columns
396− -> [Condition db '[t ::: row] 'Ungrouped params]
397− -> ConflictClause db table params
439+data ConflictClause tab db params table where
440+ OnConflictDoRaise :: ConflictClause tab db params table
441+ OnConflict
442+ :: ConflictTarget constraints
443+ -> ConflictAction tab db params columns
444+ -> ConflictClause tab db params (constraints :=> columns)
398445
399446-- | Render a `ConflictClause`.
400447renderConflictClause
401448 :: SOP.SListI (TableToColumns table)
402− => ConflictClause db table params
449+ => ConflictClause tab db params table
403450 -> ByteString
404451renderConflictClause = \case
405452 OnConflictDoRaise -> ""
406− OnConflictDoNothing -> " ON CONFLICT DO NOTHING"
407− OnConflictDoUpdate updates whs'
408− -> " ON CONFLICT DO UPDATE SET"
409− <+> renderCommaSeparatedMaybe renderUpdate updates
453+ OnConflict target action -> " ON CONFLICT"
454+ <+> renderConflictTarget target <+> renderConflictAction action
455+
456+data ConflictAction tab db params columns where
457+ DoNothing :: ConflictAction tab db params columns
458+ DoUpdate
459+ :: ( row ~ ColumnsToRow columns
460+ , SOP.SListI columns
461+ , columns ~ (col0 ': cols)
462+ , SOP.All (HasIn columns) subcolumns
463+ , AllUnique subcolumns )
464+ => NP (ColumnExpression db '[tab ::: row, "excluded" ::: row] 'Ungrouped params) subcolumns
465+ -> [Condition db '[tab ::: row, "excluded" ::: row] 'Ungrouped params]
466+ -> ConflictAction tab db params columns
467+
468+renderConflictAction
469+ :: ConflictAction tab db params columns
470+ -> ByteString
471+renderConflictAction = \case
472+ DoNothing -> "DO NOTHING"
473+ DoUpdate updates whs'
474+ -> "DO UPDATE SET"
475+ <+> renderCommaSeparated renderColumnExpression updates
410476 <> case whs' of
411477 [] -> ""
412478 wh:whs -> " WHERE" <+> renderExpression (foldr (.&&) wh whs)
413− where
414− renderUpdate
415− :: Aliased (ColumnValue db columns params) column
416− -> Maybe ByteString
417− renderUpdate = \case
418− Same `As` _ -> Nothing
419− Default `As` column -> Just $
420− renderAlias column <+> "=" <+> "DEFAULT"
421− Set expression `As` column -> Just $
422− renderAlias column <+> "=" <+> renderExpression expression
479+
480+-- | A `ConflictTarget` specifies the constraint violation that triggers a
481+-- `ConflictAction`.
482+data ConflictTarget constraints where
483+ OnConstraint
484+ :: Has con constraints constraint
485+ => Alias con
486+ -> ConflictTarget constraints
487+
488+-- | Render a `ConflictTarget`
489+renderConflictTarget
490+ :: ConflictTarget constraints
491+ -> ByteString
492+renderConflictTarget (OnConstraint con) =
493+ "ON" <+> "CONSTRAINT" <+> renderAlias con
423494
424495{-----------------------------------------
425496UPDATE statements
@@ -434,11 +505,13 @@ update
434505 , Has sch schemas schema
435506 , Has tab schema ('Table table)
436507 , row ~ TableToRow table
437− , columns ~ TableToColumns table )
508+ , columns ~ TableToColumns table
509+ , SOP.All (HasIn columns) subcolumns
510+ , AllUnique subcolumns )
438511 => QualifiedAlias sch tab -- ^ table to update
439− -> NP (Aliased (ColumnValue db row params)) columns
512+ -> NP (ColumnExpression db '[tab ::: row] 'Ungrouped params) subcolumns
440513 -- ^ modified values to replace old values
441− -> (forall t. Condition db '[t ::: row] 'Ungrouped params)
514+ -> Condition db '[tab ::: row] 'Ungrouped params
442515 -- ^ condition under which to perform update on a row
443516 -> ReturningClause db params row results -- ^ results to return
444517 -> Manipulation db params results
@@ -446,19 +519,9 @@ update tab columns wh returning = UnsafeManipulation $
446519 "UPDATE"
447520 <+> renderQualifiedAlias tab
448521 <+> "SET"
449− <+> renderCommaSeparatedMaybe renderUpdate columns
522+ <+> renderCommaSeparated renderColumnExpression columns
450523 <+> "WHERE" <+> renderExpression wh
451524 <> renderReturningClause returning
452− where
453− renderUpdate
454− :: Aliased (ColumnValue db columns params) column
455− -> Maybe ByteString
456− renderUpdate = \case
457− Same `As` _ -> Nothing
458− Default `As` column -> Just $
459− renderAlias column <+> "=" <+> "DEFAULT"
460− Set expression `As` column -> Just $
461− renderAlias column <+> "=" <+> renderExpression expression
462525
463526-- | Update a row returning `Nil`.
464527update_
@@ -467,11 +530,13 @@ update_
467530 , Has sch schemas schema
468531 , Has tab schema ('Table table)
469532 , row ~ TableToRow table
470− , columns ~ TableToColumns table )
533+ , columns ~ TableToColumns table
534+ , SOP.All (HasIn columns) subcolumns
535+ , AllUnique subcolumns )
471536 => QualifiedAlias sch tab -- ^ table to update
472− -> NP (Aliased (ColumnValue db row params)) columns
537+ -> NP (ColumnExpression db '[tab ::: row] 'Ungrouped params) subcolumns
473538 -- ^ modified values to replace old values
474− -> (forall t. Condition db '[t ::: row] 'Ungrouped params)
539+ -> Condition db '[tab ::: row] 'Ungrouped params
475540 -- ^ condition under which to perform update on a row
476541 -> Manipulation db params '[]
477542update_ tab columns wh = update tab columns wh (Returning Nil)