@@ -17,6 +17,7 @@ Squeal data manipulation language.
17 , LambdaCase
17 , LambdaCase
18 , MultiParamTypeClasses
18 , MultiParamTypeClasses
19 , OverloadedStrings
19 , OverloadedStrings
20 , RankNTypes
21 , RankNTypes
21 , TypeFamilies
22 , TypeFamilies
22 , TypeInType
23 , TypeInType
@@ -27,22 +28,24 @@ Squeal data manipulation language.
28module Squeal.PostgreSQL.Manipulation
29module Squeal.PostgreSQL.Manipulation
29 ( -- * Manipulation
30 ( -- * Manipulation
30− Manipulation (UnsafeManipulation, renderManipulation)
31+ Manipulation (..)
31 , queryStatement
32 , queryStatement
32− , ColumnValue (..)
33+ , QueryClause (..)
35+ , DefaultAliasable (..)
36+ , ColumnExpression (..)
33− , ReturningClause (ReturningStar, Returning)
37+ , ReturningClause (..)
34− , ConflictClause (OnConflictDoRaise, OnConflictDoNothing, OnConflictDoUpdate)
38+ , ConflictClause (..)
39+ , ConflictTarget (..)
40+ , ConflictAction (..)
35 , UsingClause (..)
41 , UsingClause (..)
36− , ConflictTarget (OnConstraint, UniqueIndexOn)
37 -- * Insert
42 -- * Insert
38− , insertRows
43+ , insertInto
39− , insertRow
44+ , insertInto_
44 , renderReturningClause
45 , renderReturningClause
45 , renderConflictClause
46 , renderConflictClause
47+ , renderConflictTarget
48+ , renderConflictAction
46 -- * Update
49 -- * Update
@@ -258,126 +261,125 @@ queryStatement q = UnsafeManipulation $ renderQuery q
258INSERT statements
261INSERT statements
259-----------------------------------------}
262-----------------------------------------}
261−-- | Insert multiple rows.
264+{- |
263−-- When a table is created, it contains no data. The first thing to do
265+When a table is created, it contains no data. The first thing to do
264−-- before a database can be of much use is to insert data. Data is
266+before a database can be of much use is to insert data. Data is
265−-- conceptually inserted one row at a time. Of course you can also insert
267+conceptually inserted one row at a time. Of course you can also insert
266−-- more than one row, but there is no way to insert less than one row.
268+more than one row, but there is no way to insert less than one row.
267−-- Even if you know only some column values, a complete row must be created.
269+Even if you know only some column values, a complete row must be created.
269− :: ( SOP.SListI columns
271+insertInto
270− , SOP.SListI results
271− , Has tab schema ('Table table)
272+ :: ( Has tab schema ('Table table)
273+ , columns ~ TableToColumns table
272 , row ~ TableToRow table
274 , row ~ TableToRow table
273− , columns ~ TableToColumns table )
275+ , SOP.SListI columns
276+ , SOP.SListI result )
274− => Alias tab -- ^ table to insert into
277+ => Alias tab
275− -> NP (Aliased (ColumnValue schema '[] params)) columns -- ^ row to insert
276− -> [NP (Aliased (ColumnValue schema '[] params)) columns] -- ^ more rows to insert
278+ -> QueryClause schema params columns
277− -> ConflictClause schema table params
279+ -> ConflictClause schema params table
278− -- ^ what to do in case of constraint conflict
279− -> ReturningClause schema params row results -- ^ results to return
280+ -> ReturningClause schema params row result
280− -> Manipulation schema params results
281+ -> Manipulation schema params result
281−insertRows tab rw rws conflict returning = UnsafeManipulation $
282+insertInto tab qry conflict ret = UnsafeManipulation $
282 "INSERT" <+> "INTO" <+> renderAlias tab
283 "INSERT" <+> "INTO" <+> renderAlias tab
283− <> renderConflictAlias conflict
284+ <+> renderQueryClause qry
284− <+> parenthesized (renderCommaSeparated renderAliasPart rw)
288− . renderCommaSeparated renderColumnValuePart <$> rw:rws )
289− <> renderConflictClause conflict
285+ <> renderConflictClause conflict
290− <> renderReturningClause returning
286+ <> renderReturningClause ret
292− renderAliasPart, renderColumnValuePart
293− :: Aliased (ColumnValue schema '[] params) ty -> ByteString
294− renderAliasPart (_ `As` name) = renderAlias name
295− renderColumnValuePart (value `As` _) = case value of
296− Default -> "DEFAULT"
297− Set expression -> renderExpression expression
299−-- | Insert a single row.
301− :: ( SOP.SListI columns
302− , SOP.SListI results
303− , Has tab schema ('Table table)
304− , row ~ TableToRow table
305− , columns ~ TableToColumns table )
306− => Alias tab -- ^ table to insert into
307− -> NP (Aliased (ColumnValue schema '[] params)) columns -- ^ row to insert
308− -> ConflictClause schema table params
309− -- ^ what to do in case of constraint conflict
310− -> ReturningClause schema params row results -- ^ results to return
311− -> Manipulation schema params results
312−insertRow tab rw = insertRows tab rw []
314−-- | Insert multiple rows returning `Nil` and raising an error on conflicts.
288+insertInto_
316− :: ( SOP.SListI columns
317− , Has tab schema ('Table table)
318− , columns ~ TableToColumns table )
319− => Alias tab -- ^ table to insert into
320− -> NP (Aliased (ColumnValue schema '[] params)) columns -- ^ row to insert
321− -> [NP (Aliased (ColumnValue schema '[] params)) columns] -- ^ more rows to insert
322− -> Manipulation schema params '[]
323−insertRows_ tab rw rws =
324− insertRows tab rw rws OnConflictDoRaise (Returning Nil)
326−-- | Insert a single row returning `Nil` and raising an error on conflicts.
328− :: ( SOP.SListI columns
329− , Has tab schema ('Table table)
289+ :: ( Has tab schema ('Table table)
330− , columns ~ TableToColumns table )
290+ , columns ~ TableToColumns table
331− => Alias tab -- ^ table to insert into
332− -> NP (Aliased (ColumnValue schema '[] params)) columns -- ^ row to insert
333− -> Manipulation schema params '[]
334−insertRow_ tab rw = insertRow tab rw OnConflictDoRaise (Returning Nil)
336−-- | Insert a `Query`.
338− :: ( SOP.SListI columns
339− , SOP.SListI results
340− , Has tab schema ('Table table)
341 , row ~ TableToRow table
291 , row ~ TableToRow table
342− , columns ~ TableToColumns table )
343− => Alias tab -- ^ table to insert into
344− -> Query schema params (TableToRow table)
345− -> ConflictClause schema table params
346− -- ^ what to do in case of constraint conflict
347− -> ReturningClause schema params row results -- ^ results to return
348− -> Manipulation schema params results
349−insertQuery tab query conflict returning = UnsafeManipulation $
350− "INSERT" <+> "INTO" <+> renderAlias tab
351− <> renderConflictAlias conflict
352− <+> renderQuery query
353− <> renderConflictClause conflict
354− <> renderReturningClause returning
356−-- | Insert a `Query` returning `Nil` and raising an error on conflicts.
358− :: ( SOP.SListI columns
292+ , SOP.SListI columns )
359− , Has tab schema ('Table table)
360− , columns ~ TableToColumns table )
361− => Alias tab -- ^ table to insert into
293+ => Alias tab
362− -> Query schema params (TableToRow table)
294+ -> QueryClause schema params columns
363 -> Manipulation schema params '[]
295 -> Manipulation schema params '[]
364−insertQuery_ tab query =
296+insertInto_ tab qry =
365− insertQuery tab query OnConflictDoRaise (Returning Nil)
297+ insertInto tab qry OnConflictDoRaise (Returning Nil)
367−-- | `ColumnValue`s are values to insert or update in a row.
299+data QueryClause schema params columns where
368−-- `Default` inserts or updates with the @DEFAULT@ value.
300+ Values
369−-- `Set` sets a value to be an `Expression`, which can refer to
301+ :: SOP.SListI columns
370−-- existing value in the row for an update.
302+ => NP (ColumnExpression schema '[] 'Ungrouped params) columns
371−data ColumnValue
303+ -> [NP (ColumnExpression schema '[] 'Ungrouped params) columns]
372− (schema :: SchemaType)
304+ -> QueryClause schema params columns
373− (tables :: FromType)
305+ Select
374− (params :: [NullityType])
306+ :: SOP.SListI columns
375− (ty :: ColumnType)
307+ => NP (ColumnExpression schema from grp params) columns
308+ -> TableExpression schema params from grp
309+ -> QueryClause schema params columns
311+ :: ColumnsToRow columns ~ row
312+ => Query schema params row
313+ -> QueryClause schema params columns
316+ :: QueryClause schema params columns
318+renderQueryClause = \case
320+ parenthesized (renderCommaSeparated renderAliasPart row0)
324+ . renderCommaSeparated renderValuePart <$> row0 : rows )
326+ parenthesized (renderCommaSeparated renderAliasPart row0)
328+ <+> renderCommaSeparated renderValuePart row0
329+ <+> renderTableExpression tab
330+ Subquery qry -> renderQuery qry
377− Default :: ColumnValue schema tables params ('Def :=> ty)
332+ renderAliasPart, renderValuePart
378− Set
333+ :: ColumnExpression schema from grp params column -> ByteString
334+ renderAliasPart = \case
335+ DefaultAs name -> renderAlias name
336+ Specific (_ `As` name) -> renderAlias name
337+ renderValuePart = \case
338+ DefaultAs _ -> "DEFAULT"
339+ Specific (value `As` _) -> renderExpression value
342+ :: SOP.SListI columns
379− :: Expression schema tables 'Ungrouped params ty
343+ => NP (ColumnExpression schema '[] 'Ungrouped params) columns
380− -> ColumnValue schema tables params (constraint :=> ty)
344+ -> QueryClause schema params columns
345+pattern Values_ vals = Values vals []
347+data ColumnExpression schema from grp params column where
351+ -> ColumnExpression schema from grp params (col ::: 'Def :=> ty)
353+ :: Aliased (Expression schema from grp params) (col ::: ty)
354+ -> ColumnExpression schema from grp params (col ::: defness :=> ty)
356+instance (KnownSymbol alias, column ~ (alias ::: defness :=> ty))
358+ (Expression schema from grp params ty)
359+ (ColumnExpression schema from grp params column)
361+ expression `as` alias = Specific (expression `As` alias)
362+instance (KnownSymbol alias, columns ~ '[alias ::: defness :=> ty])
364+ (Expression schema from grp params ty)
365+ (NP (ColumnExpression schema from grp params) columns)
367+ expression `as` alias = expression `as` alias :* Nil
368+instance (KnownSymbol col, column ~ (col ::: 'Def :=> ty))
369+ => DefaultAliasable col (ColumnExpression schema from grp params column) where
370+ defaultAs = DefaultAs
371+instance (KnownSymbol col, columns ~ '[col ::: 'Def :=> ty])
372+ => DefaultAliasable col (NP (ColumnExpression schema from grp params) columns) where
373+ defaultAs col = defaultAs col :* Nil
375+renderColumnExpression
376+ :: ColumnExpression schema from grp params column
378+renderColumnExpression = \case
380+ renderAlias col <+> "=" <+> "DEFAULT"
381+ Specific (expression `As` col) ->
382+ renderAlias col <+> "=" <+> renderExpression expression
382-- | A `ReturningClause` computes and return value(s) based
384-- | A `ReturningClause` computes and return value(s) based
383-- on each row actually inserted, updated or deleted. This is primarily
385-- on each row actually inserted, updated or deleted. This is primarily
@@ -417,74 +419,61 @@ renderReturningClause = \case
417-- `OnConflictDoNothing` simply avoids inserting a row.
419-- `OnConflictDoNothing` simply avoids inserting a row.
418-- `OnConflictDoUpdate` updates the existing row that conflicts with the row
420-- `OnConflictDoUpdate` updates the existing row that conflicts with the row
419-- proposed for insertion.
421-- proposed for insertion.
420−data ConflictClause
422+data ConflictClause schema params table where
421− (schema :: SchemaType)
422− (table :: TableType)
423− (params :: [NullityType]) where
424− OnConflictDoRaise :: ConflictClause schema table params
423+ OnConflictDoRaise :: ConflictClause schema params table
425− OnConflictDoNothing :: ConflictClause schema table params
424+ OnConflict
427− :: ( row ~ TableToRow table, SOP.SListI columns, columns ~ (col0 ': cols)
428− , OrderedSubsetOf (TableToColumns table) columns )
429− => ConflictTarget table
425+ :: ConflictTarget constraints
430− -> NP (Aliased (ColumnValue schema '["existing" ::: row, "excluded" ::: row] params)) columns
426+ -> ConflictAction schema params columns
431− -> [Condition schema '["existing" ::: row, "excluded" ::: row] 'Ungrouped params]
432− -> ConflictClause schema table params
427+ -> ConflictClause schema params (constraints :=> columns)
434−-- | A `ConflictTarget` specifies the constraint violation that triggers an
435−-- `OnConflictDoUpdate`.
436−data ConflictTarget (table :: TableType) where
437− -- | An explicitly named constraint.
439− :: (Has constraintName constraints constraint)
440− => Alias constraintName
441− -> ConflictTarget '(constraints, columns)
442− -- | Match each unique index with precisely the following columns.
444− :: ( HasAll columnNames columns columnTypes
445− , SOP.All KnownSymbol columnNames )
446− => NP Alias columnNames
447− -> ConflictTarget '(constraints, columns)
449−-- | Render any table alias required for a `ConflictClause`.
450−renderConflictAlias :: ConflictClause schema table params -> ByteString
451−renderConflictAlias = \case
452− OnConflictDoUpdate _ _ _ -> " AS existing"
455-- | Render a `ConflictClause`.
429-- | Render a `ConflictClause`.
456renderConflictClause
430renderConflictClause
457 :: SOP.SListI (TableToColumns table)
431 :: SOP.SListI (TableToColumns table)
458− => ConflictClause schema table params
432+ => ConflictClause schema params table
459 -> ByteString
433 -> ByteString
460renderConflictClause = \case
434renderConflictClause = \case
461 OnConflictDoRaise -> ""
435 OnConflictDoRaise -> ""
462− OnConflictDoNothing -> " ON CONFLICT DO NOTHING"
436+ OnConflict target action -> " ON CONFLICT"
463− OnConflictDoUpdate target updates whs'
437+ <+> renderConflictTarget target <+> renderConflictAction action
464− -> " ON CONFLICT"
438+
465− <+> renderTarget target
439+data ConflictAction schema params columns where
440+ DoNothing :: ConflictAction schema params columns
442+ :: ( row ~ ColumnsToRow columns
443+ , SOP.SListI columns
444+ , columns ~ (col0 ': cols)
445+ , SOP.All (HasIn columns) subcolumns
446+ , AllUnique subcolumns )
447+ => NP (ColumnExpression schema '[t ::: row] 'Ungrouped params) subcolumns
448+ -> [Condition schema '[t ::: row] 'Ungrouped params]
449+ -> ConflictAction schema params columns
452+ :: ConflictAction schema params columns
454+renderConflictAction = \case
455+ DoNothing -> "DO NOTHING"
456+ DoUpdate updates whs'
466− <+> "DO UPDATE SET"
457+ -> "DO UPDATE SET"
467− <+> renderCommaSeparated renderUpdate updates
458+ <+> renderCommaSeparated renderColumnExpression updates
468 <> case whs' of
459 <> case whs' of
470 wh:whs -> " WHERE" <+> renderExpression (foldr (.&&) wh whs)
461 wh:whs -> " WHERE" <+> renderExpression (foldr (.&&) wh whs)
472− renderUpdate
463+-- | A `ConflictTarget` specifies the constraint violation that triggers a
473− :: Aliased (ColumnValue schema columns params) column
464+-- `ConflictAction`.
474− -> ByteString
465+data ConflictTarget constraints where
475− renderUpdate = \case
466+ OnConstraint
476− Default `As` column ->
467+ :: Has con constraints constraint
477− renderAlias column <+> "=" <+> "DEFAULT"
468+ => Alias con
478− Set expression `As` column ->
469+ -> ConflictTarget constraints
479− renderAlias column <+> "=" <+> renderExpression expression
470+
480− renderTarget
471+-- | Render a `ConflictTarget`
481− :: ConflictTarget table
473+ :: ConflictTarget constraints
482− -> ByteString
474+ -> ByteString
483− renderTarget = \case
484− OnConstraint name ->
475+renderConflictTarget (OnConstraint con) =
485− "ON CONSTRAINT" <+> renderAlias name
476+ "ON" <+> "CONSTRAINT" <+> renderAlias con
486− UniqueIndexOn columns ->
487− parenthesized (commaSeparated (renderAliases columns))
489{-----------------------------------------
478{-----------------------------------------
490UPDATE statements
479UPDATE statements
@@ -517,12 +506,13 @@ update
517 , SOP.SListI results
506 , SOP.SListI results
518 , Has tab schema ('Table table)
507 , Has tab schema ('Table table)
519 , row ~ TableToRow table
508 , row ~ TableToRow table
520− , columns ~ (col0 ': cols)
521− , OrderedSubsetOf (TableToColumns table) columns )
509+ , columns ~ TableToColumns table
510+ , SOP.All (HasIn columns) subcolumns
511+ , AllUnique subcolumns )
522 => Alias tab -- ^ table to update
512 => Alias tab -- ^ table to update
523− -> (forall t. NP (Aliased (ColumnValue schema '[t ::: row] params)) columns)
513+ -> NP (ColumnExpression schema '[tab ::: row] 'Ungrouped params) subcolumns
524 -- ^ modified values to replace old values
514 -- ^ modified values to replace old values
525− -> (forall t. Condition schema '[t ::: row] 'Ungrouped params)
515+ -> Condition schema '[tab ::: row] 'Ungrouped params
526 -- ^ condition under which to perform update on a row
516 -- ^ condition under which to perform update on a row
527 -> ReturningClause schema params row results -- ^ results to return
517 -> ReturningClause schema params row results -- ^ results to return
528 -> Manipulation schema params results
518 -> Manipulation schema params results
@@ -530,30 +520,22 @@ update tab columns wh returning = UnsafeManipulation $
531 <+> renderAlias tab
521 <+> renderAlias tab
532 <+> "SET"
522 <+> "SET"
533− <+> renderCommaSeparated renderUpdate columns
523+ <+> renderCommaSeparated renderColumnExpression columns
534 <+> "WHERE" <+> renderExpression wh
524 <+> "WHERE" <+> renderExpression wh
535 <> renderReturningClause returning
525 <> renderReturningClause returning
538− :: Aliased (ColumnValue schema columns params) column
540− renderUpdate = \case
541− Default `As` column ->
542− renderAlias column <+> "=" <+> "DEFAULT"
543− Set expression `As` column ->
544− renderAlias column <+> "=" <+> renderExpression expression
546-- | Update a row returning `Nil`.
527-- | Update a row returning `Nil`.
548 :: ( SOP.SListI columns
529 :: ( SOP.SListI columns
549 , Has tab schema ('Table table)
530 , Has tab schema ('Table table)
550 , row ~ TableToRow table
531 , row ~ TableToRow table
551− , columns ~ (col0 ': cols)
552− , OrderedSubsetOf (TableToColumns table) columns )
532+ , columns ~ TableToColumns table
533+ , SOP.All (HasIn columns) subcolumns
534+ , AllUnique subcolumns )
553 => Alias tab -- ^ table to update
535 => Alias tab -- ^ table to update
554− -> (forall t. NP (Aliased (ColumnValue schema '[t ::: row] params)) columns)
536+ -> NP (ColumnExpression schema '[tab ::: row] 'Ungrouped params) subcolumns
555 -- ^ modified values to replace old values
537 -- ^ modified values to replace old values
556− -> (forall t. Condition schema '[t ::: row] 'Ungrouped params)
538+ -> Condition schema '[tab ::: row] 'Ungrouped params
557 -- ^ condition under which to perform update on a row
539 -- ^ condition under which to perform update on a row
558 -> Manipulation schema params '[]
540 -> Manipulation schema params '[]
559update_ tab columns wh = update tab columns wh (Returning Nil)
541update_ tab columns wh = update tab columns wh (Returning Nil)