@@ -27,6 +27,7 @@ module Squeal.PostgreSQL.Manipulation
2727 , ColumnValue (..)
2828 , ReturningClause (ReturningStar, Returning)
2929 , ConflictClause (OnConflictDoRaise, OnConflictDoNothing, OnConflictDoUpdate)
30+ , ConflictTarget (OnConstraint, UniqueIndexOn)
3031 -- * Insert
3132 , insertRows
3233 , insertRow
@@ -46,6 +47,7 @@ module Squeal.PostgreSQL.Manipulation
4647
4748import Control.DeepSeq
4849import Data.ByteString hiding (foldr)
50+import GHC.TypeLits (KnownSymbol)
4951
5052import qualified Generics.SOP as SOP
5153import qualified GHC.Generics as GHC
@@ -120,12 +122,13 @@ let
120122 (Set 2 `as` #col1 :* Set 4 `as` #col2)
121123 [Set 6 `as` #col1 :* Set 8 `as` #col2]
122124 (OnConflictDoUpdate
125+ (UniqueIndexOn #col1)
123126 (Set 2 `as` #col1 :* Same `as` #col2)
124127 [#col1 .== #col2])
125128 (Returning $ (#col1 + #col2) `as` #sum)
126129in printSQL manipulation
127130:}
128−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"
131+INSERT INTO "tab" ("col1", "col2") VALUES (2, 4), (6, 8) ON CONFLICT ("col1") DO UPDATE SET "col1" = 2 WHERE ("col1" = "col2") RETURNING ("col1" + "col2") AS "sum"
129132
130133query insert:
131134
@@ -387,10 +390,26 @@ data ConflictClause
387390 OnConflictDoNothing :: ConflictClause schema table params
388391 OnConflictDoUpdate
389392 :: (row ~ TableToRow table, columns ~ TableToColumns table)
390− => NP (Aliased (ColumnValue schema row params)) columns
393+ => ConflictTarget table
394+ -> NP (Aliased (ColumnValue schema row params)) columns
391395 -> [Condition schema '[t ::: row] 'Ungrouped params]
392396 -> ConflictClause schema table params
393397
398+-- | A 'ConflictTarget' specifies the constraint violation that triggers an
399+-- 'OnConflictDoUpdate'.
400+data ConflictTarget (table :: TableType) where
401+ -- | An explicitly named constraint.
402+ OnConstraint
403+ :: (Has constraintName constraints constraint)
404+ => Alias constraintName
405+ -> ConflictTarget '(constraints, columns)
406+ -- | Match each unique index with precisely the following columns.
407+ UniqueIndexOn
408+ :: ( HasAll columnNames columns columnTypes
409+ , SOP.All KnownSymbol columnNames )
410+ => NP Alias columnNames
411+ -> ConflictTarget '(constraints, columns)
412+
394413-- | Render a `ConflictClause`.
395414renderConflictClause
396415 :: SOP.SListI (TableToColumns table)
@@ -399,8 +418,10 @@ renderConflictClause
399418renderConflictClause = \case
400419 OnConflictDoRaise -> ""
401420 OnConflictDoNothing -> " ON CONFLICT DO NOTHING"
402− OnConflictDoUpdate updates whs'
403− -> " ON CONFLICT DO UPDATE SET"
421+ OnConflictDoUpdate target updates whs'
422+ -> " ON CONFLICT"
423+ <+> renderTarget target
424+ <+> "DO UPDATE SET"
404425 <+> renderCommaSeparatedMaybe renderUpdate updates
405426 <> case whs' of
406427 [] -> ""
@@ -415,6 +436,14 @@ renderConflictClause = \case
415436 renderAlias column <+> "=" <+> "DEFAULT"
416437 Set expression `As` column -> Just $
417438 renderAlias column <+> "=" <+> renderExpression expression
439+ renderTarget
440+ :: ConflictTarget table
441+ -> ByteString
442+ renderTarget = \case
443+ OnConstraint name ->
444+ "ON CONSTRAINT" <+> renderAlias name
445+ UniqueIndexOn columns ->
446+ parenthesized (commaSeparated (renderAliases columns))
418447
419448{-----------------------------------------
420449UPDATE statements