@@ -0,0 +1,1177 @@
1+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
2+{-# LANGUAGE
3+ DataKinds
4+ , FlexibleContexts
5+ , FlexibleInstances
6+ , FunctionalDependencies
7+ , GADTs
8+ , LambdaCase
9+ , MagicHash
10+ , OverloadedStrings
11+ , ScopedTypeVariables
12+ , TypeApplications
13+ , TypeFamilies
14+ , TypeOperators
15+ , UndecidableInstances
16+#-}
17+
18+module Squeel.PostgreSQL.Statement where
19+
20+import Control.Category
21+import Data.ByteString (ByteString)
22+import Data.Foldable
23+import Data.Maybe
24+import Data.Monoid
25+import Data.Ratio
26+import Data.String
27+import Data.Word
28+import Generics.SOP
29+import GHC.Exts
30+import GHC.OverloadedLabels
31+import GHC.TypeLits
32+import Prelude hiding (RealFrac(..), id, (.))
33+
34+import qualified Data.ByteString as ByteString
35+
36+import Squeel.PostgreSQL.Schema
37+
38+{-----------------------------------------
39+column expressions
40+-----------------------------------------}
41+
42+newtype Expression
43+ (params :: [ColumnType])
44+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
45+ (grouping :: Grouping)
46+ (ty :: ColumnType)
47+ = UnsafeExpression { renderExpression :: ByteString }
48+ deriving (Show,Eq)
49+
50+class KnownNat n => HasParameter (n :: Nat) params ty | n params -> ty where
51+ param :: Proxy# n -> Expression params tables grouping ty
52+ param p = UnsafeExpression $ ("$" <>) $ fromString $ show $ natVal' p
53+instance {-# OVERLAPPING #-} HasParameter 1 (ty1:tys) ty1
54+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) params ty)
55+ => HasParameter n (ty' : params) ty
56+
57+param1 :: Expression (ty1:tys) tables grouping ty1
58+param1 = param (proxy# :: Proxy# 1)
59+param2 :: Expression (ty1:ty2:tys) tables grouping ty2
60+param2 = param (proxy# :: Proxy# 2)
61+param3 :: Expression (ty1:ty2:ty3:tys) tables grouping ty3
62+param3 = param (proxy# :: Proxy# 3)
63+param4 :: Expression (ty1:ty2:ty3:ty4:tys) tables grouping ty4
64+param4 = param (proxy# :: Proxy# 4)
65+param5 :: Expression (ty1:ty2:ty3:ty4:ty5:tys) tables grouping ty5
66+param5 = param (proxy# :: Proxy# 5)
67+
68+class KnownSymbol column => HasColumn column columns ty
69+ | column columns -> ty where
70+ getColumn
71+ :: Proxy# column
72+ -> Expression params '[table ::: columns] 'Ungrouped ty
73+ getColumn column = UnsafeExpression $ fromString $ symbolVal' column
74+instance {-# OVERLAPPING #-} KnownSymbol column
75+ => HasColumn column ((column ::: optionality ty) ': tys) ('Required ty)
76+instance {-# OVERLAPPABLE #-} (KnownSymbol column, HasColumn column table ty)
77+ => HasColumn column (ty' ': table) ty
78+
79+instance (HasColumn column columns ty, tables ~ '[table ::: columns])
80+ => IsLabel column (Expression params tables 'Ungrouped ty) where
81+ fromLabel = getColumn
82+
83+instance (HasTable table tables columns, HasColumn column columns ty)
84+ => TableColumn table column (Expression params tables 'Ungrouped ty) where
85+ table &. column = UnsafeExpression $
86+ renderAlias table <> "." <> renderAlias column
87+
88+def :: Expression params '[] grouping ('Optional (nullity ty))
89+def = UnsafeExpression "DEFAULT"
90+
91+notDef
92+ :: Expression params '[] grouping ('Required (nullity ty))
93+ -> Expression params '[] grouping ('Optional (nullity ty))
94+notDef = UnsafeExpression . renderExpression
95+
96+null :: Expression params tables grouping (optionality ('Null ty))
97+null = UnsafeExpression "NULL"
98+
99+coalesce
100+ :: [Expression params tables grouping ('Required ('Null x))]
101+ -> Expression params tables grouping ('Required ('NotNull x))
102+ -> Expression params tables grouping ('Required ('NotNull x))
103+coalesce nulls isn'tNull = UnsafeExpression $ mconcat
104+ [ "COALESCE("
105+ , ByteString.intercalate ", " $ map renderExpression nulls
106+ , ", ", renderExpression isn'tNull
107+ , ")"
108+ ]
109+
110+unsafeBinaryOp
111+ :: ByteString
112+ -> Expression params tables grouping ty0
113+ -> Expression params tables grouping ty1
114+ -> Expression params tables grouping ty2
115+unsafeBinaryOp op x y = UnsafeExpression $ mconcat
116+ ["(", renderExpression x, " ", op, " ", renderExpression y, ")"]
117+
118+unsafeUnaryOp
119+ :: ByteString
120+ -> Expression params tables grouping ty0
121+ -> Expression params tables grouping ty1
122+unsafeUnaryOp op x = UnsafeExpression $ mconcat
123+ ["(", op, " ", renderExpression x, ")"]
124+
125+unsafeFunction
126+ :: ByteString
127+ -> Expression params tables grouping xty
128+ -> Expression params tables grouping yty
129+unsafeFunction fun x = UnsafeExpression $ mconcat
130+ [fun, "(", renderExpression x, ")"]
131+
132+instance PGNum ty
133+ => Num (Expression params tables grouping ('Required (nullity ty))) where
134+ (+) = unsafeBinaryOp "+"
135+ (-) = unsafeBinaryOp "-"
136+ (*) = unsafeBinaryOp "*"
137+ abs = unsafeFunction "abs"
138+ signum = unsafeFunction "sign"
139+ fromInteger
140+ = UnsafeExpression
141+ . (<> if decimal (Proxy :: Proxy ty) then "." else "")
142+ . fromString
143+ . show
144+
145+instance PGFractional ty => Fractional
146+ (Expression params tables grouping ('Required (nullity ty))) where
147+ (/) = unsafeBinaryOp "/"
148+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
149+
150+instance (PGFloating ty, PGCast 'PGNumeric ty, PGTyped ty) => Floating
151+ (Expression params tables grouping ('Required (nullity ty))) where
152+ pi = UnsafeExpression "pi()"
153+ exp = unsafeFunction "exp"
154+ log = unsafeFunction "ln"
155+ sqrt = unsafeFunction "sqrt"
156+ b ** x = UnsafeExpression $
157+ "power(" <> renderExpression b <> ", " <> renderExpression x <> ")"
158+ logBase b y = cast pgtype $ logBaseNumeric b y
159+ where
160+ logBaseNumeric
161+ :: Expression params tables grouping ('Required (nullity ty))
162+ -> Expression params tables grouping ('Required (nullity ty))
163+ -> Expression params tables grouping ('Required (nullity 'PGNumeric))
164+ logBaseNumeric b' y' = UnsafeExpression $ mconcat
165+ [ "log("
166+ , renderExpression b' <> "::numeric"
167+ , ", "
168+ , renderExpression y' <> "::numeric"
169+ , ")"
170+ ]
171+ sin = unsafeFunction "sin"
172+ cos = unsafeFunction "cos"
173+ tan = unsafeFunction "tan"
174+ asin = unsafeFunction "asin"
175+ acos = unsafeFunction "acos"
176+ atan = unsafeFunction "atan"
177+ sinh x = (exp x - exp (-x)) / 2
178+ cosh x = (exp x + exp (-x)) / 2
179+ tanh x = sinh x / cosh x
180+ asinh x = log (x + sqrt (x*x + 1))
181+ acosh x = log (x + sqrt (x*x - 1))
182+ atanh x = log ((1 + x) / (1 - x)) / 2
183+
184+atan2
185+ :: PGFloating float
186+ => Expression params tables grouping ('Required (nullity float))
187+ -> Expression params tables grouping ('Required (nullity float))
188+ -> Expression params tables grouping ('Required (nullity float))
189+atan2 y x = UnsafeExpression $
190+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
191+
192+class PGCast (ty0 :: PGType) (ty1 :: PGType) where
193+ cast
194+ :: TypeExpression ('Required ('Null ty1))
195+ -> Expression params tables grouping ('Required (nullity ty0))
196+ -> Expression params tables grouping ('Required (nullity ty1))
197+ cast ty x = UnsafeExpression $
198+ "(" <> renderExpression x <> "::" <> renderTypeExpression ty <> ")"
199+instance PGCast 'PGInt2 'PGInt2
200+instance PGCast 'PGInt2 'PGInt4
201+instance PGCast 'PGInt2 'PGInt8
202+instance PGCast 'PGInt4 'PGInt2
203+instance PGCast 'PGInt4 'PGInt4
204+instance PGCast 'PGInt4 'PGInt8
205+instance PGCast 'PGInt8 'PGInt2
206+instance PGCast 'PGInt8 'PGInt4
207+instance PGCast 'PGInt8 'PGInt8
208+instance PGCast 'PGInt8 'PGFloat4
209+instance PGCast 'PGInt8 'PGFloat8
210+instance PGCast 'PGInt8 'PGNumeric
211+
212+div
213+ :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
214+ => Expression params tables grouping ('Required (nullity ty))
215+ -> Expression params tables grouping ('Required (nullity ty))
216+ -> Expression params tables grouping ('Required (nullity ty))
217+div = unsafeBinaryOp "/"
218+
219+mod
220+ :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
221+ => Expression params tables grouping ('Required (nullity ty))
222+ -> Expression params tables grouping ('Required (nullity ty))
223+ -> Expression params tables grouping ('Required (nullity ty))
224+mod = unsafeBinaryOp "%"
225+
226+truncate
227+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
228+ => Expression params tables grouping ('Required (nullity frac))
229+ -> Expression params tables grouping ('Required (nullity int))
230+truncate = cast pgtype . truncate'
231+ where
232+ truncate'
233+ :: Expression params tables grouping ('Required (nullity frac))
234+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
235+ truncate' = unsafeFunction "trunc"
236+
237+round
238+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
239+ => Expression params tables grouping ('Required (nullity frac))
240+ -> Expression params tables grouping ('Required (nullity int))
241+round = cast pgtype . round'
242+ where
243+ round'
244+ :: Expression params tables grouping ('Required (nullity frac))
245+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
246+ round' = unsafeFunction "round"
247+
248+ceiling
249+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
250+ => Expression params tables grouping ('Required (nullity frac))
251+ -> Expression params tables grouping ('Required (nullity int))
252+ceiling = cast pgtype . ceiling'
253+ where
254+ ceiling'
255+ :: Expression params tables grouping ('Required (nullity frac))
256+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
257+ ceiling' = unsafeFunction "ceiling"
258+
259+instance IsString
260+ (Expression params tables grouping ('Required (nullity 'PGText))) where
261+ fromString str = UnsafeExpression $
262+ "E\'" <> fromString (escape =<< str) <> "\'"
263+ where
264+ escape = \case
265+ '\NUL' -> "\\0"
266+ '\'' -> "''"
267+ '"' -> "\\\""
268+ '\b' -> "\\b"
269+ '\n' -> "\\n"
270+ '\r' -> "\\r"
271+ '\t' -> "\\t"
272+ '\\' -> "\\\\"
273+ c -> [c]
274+
275+type Condition params tables grouping =
276+ Expression params tables grouping ('Required ('NotNull 'PGBool))
277+
278+true :: Condition params tables grouping
279+true = UnsafeExpression "TRUE"
280+
281+false :: Condition params tables grouping
282+false = UnsafeExpression "FALSE"
283+
284+not_
285+ :: Condition params tables grouping
286+ -> Condition params tables grouping
287+not_ = unsafeUnaryOp "NOT"
288+
289+(&&*)
290+ :: Condition params tables grouping
291+ -> Condition params tables grouping
292+ -> Condition params tables grouping
293+(&&*) = unsafeBinaryOp "AND"
294+
295+(||*)
296+ :: Condition params tables grouping
297+ -> Condition params tables grouping
298+ -> Condition params tables grouping
299+(||*) = unsafeBinaryOp "OR"
300+
301+caseWhenThenElse
302+ :: [(Condition params tables grouping, Expression params tables grouping ty)]
303+ -> Expression params tables grouping ty
304+ -> Expression params tables grouping ty
305+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
306+ [ "CASE"
307+ , mconcat
308+ [ mconcat
309+ [ " WHEN ", renderExpression when_
310+ , " THEN ", renderExpression then_
311+ ]
312+ | (when_,then_) <- whenThens
313+ ]
314+ , " ELSE ", renderExpression else_
315+ , " END"
316+ ]
317+
318+ifThenElse
319+ :: Condition params tables grouping
320+ -> Expression params tables grouping ty
321+ -> Expression params tables grouping ty
322+ -> Expression params tables grouping ty
323+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
324+
325+(==*)
326+ :: Expression params tables grouping ('Required (nullity ty))
327+ -> Expression params tables grouping ('Required (nullity ty))
328+ -> Expression params tables grouping ('Required (nullity 'PGBool))
329+(==*) = unsafeBinaryOp "="
330+
331+(/=*)
332+ :: Expression params tables grouping ('Required (nullity ty))
333+ -> Expression params tables grouping ('Required (nullity ty))
334+ -> Expression params tables grouping ('Required (nullity 'PGBool))
335+(/=*) = unsafeBinaryOp "<>"
336+
337+(>=*)
338+ :: Expression params tables grouping ('Required (nullity ty))
339+ -> Expression params tables grouping ('Required (nullity ty))
340+ -> Expression params tables grouping ('Required (nullity 'PGBool))
341+(>=*) = unsafeBinaryOp ">="
342+
343+(<*)
344+ :: Expression params tables grouping ('Required (nullity ty))
345+ -> Expression params tables grouping ('Required (nullity ty))
346+ -> Expression params tables grouping ('Required (nullity 'PGBool))
347+(<*) = unsafeBinaryOp "<"
348+
349+(<=*)
350+ :: Expression params tables grouping ('Required (nullity ty))
351+ -> Expression params tables grouping ('Required (nullity ty))
352+ -> Expression params tables grouping ('Required (nullity 'PGBool))
353+(<=*) = unsafeBinaryOp "<="
354+
355+(>*)
356+ :: Expression params tables grouping ('Required (nullity ty))
357+ -> Expression params tables grouping ('Required (nullity ty))
358+ -> Expression params tables grouping ('Required (nullity 'PGBool))
359+(>*) = unsafeBinaryOp ">"
360+
361+data SortExpression params tables grouping where
362+ Asc
363+ :: Expression params tables grouping ty
364+ -> SortExpression params tables grouping
365+ Desc
366+ :: Expression params tables grouping ty
367+ -> SortExpression params tables grouping
368+
369+renderSortExpression :: SortExpression params tables grouping -> ByteString
370+renderSortExpression = \case
371+ Asc expression -> renderExpression expression <> " ASC"
372+ Desc expression -> renderExpression expression <> " DESC"
373+
374+data Aggregate (ty0 :: ColumnType) (ty1 :: ColumnType) =
375+ UnsafeAggregate { renderAggregate :: ByteString }
376+
377+sum_
378+ :: PGNum ty
379+ => Aggregate ('Required (nullity ty)) ('Required (nullity ty))
380+sum_ = UnsafeAggregate "sum"
381+
382+class (KnownSymbol table, KnownSymbol column)
383+ => GroupedBy table column distincts ty where
384+ getGroup
385+ :: (HasTable table tables columns, HasColumn column columns ty)
386+ => Alias table
387+ -> Alias column
388+ -> Expression params tables ('Grouped distincts) ty
389+
390+instance
391+ ( HasTable table tables columns
392+ , HasColumn column columns ty
393+ , GroupedBy table column distincts ty
394+ ) => TableColumn table column
395+ (Expression params tables ('Grouped distincts) ty) where
396+ (&.) = getGroup
397+
398+class (KnownSymbol table, KnownSymbol column)
399+ => Aggregated table column distincts ty where
400+ getAggregate
401+ :: (HasTable table tables columns, HasColumn column columns ty0)
402+ => Alias table
403+ -> Alias column
404+ -> Aggregate ty0 ty
405+ -> Expression params tables ('Grouped distincts) ty
406+
407+instance
408+ ( HasTable table tables columns
409+ , HasColumn column columns ty0
410+ , Aggregated table column distincts ty
411+ ) => TableColumn table column
412+ (Aggregate ty0 ty -> Expression params tables ('Grouped distincts) ty) where
413+ (&.) = getAggregate
414+
415+{-----------------------------------------
416+tables
417+-----------------------------------------}
418+
419+newtype Table
420+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
421+ (columns :: [(Symbol,ColumnType)])
422+ = UnsafeTable { renderTable :: ByteString }
423+ deriving (Show,Eq)
424+
425+class KnownSymbol table => HasTable table tables columns
426+ | table tables -> columns where
427+ getTable :: Proxy# table -> Table tables columns
428+ getTable table = UnsafeTable $ fromString $ symbolVal' table
429+instance {-# OVERLAPPING #-} KnownSymbol table
430+ => HasTable table ((table ::: columns) ': tables) columns
431+instance {-# OVERLAPPABLE #-}
432+ (KnownSymbol table, HasTable table schema columns)
433+ => HasTable table (table' ': schema) columns
434+
435+instance HasTable table schema columns
436+ => IsLabel table (Table schema columns) where
437+ fromLabel = getTable
438+
439+data TableReference params schema tables where
440+ Table
441+ :: Aliased (Table schema) table
442+ -> TableReference params schema '[table]
443+ Subquery
444+ :: Aliased (Query schema params) table
445+ -> TableReference params schema '[table]
446+ CrossJoin
447+ :: TableReference params schema right
448+ -> TableReference params schema left
449+ -> TableReference params schema (Join left right)
450+ InnerJoin
451+ :: TableReference params schema right
452+ -> Condition params (Join left right) 'Ungrouped
453+ -> TableReference params schema left
454+ -> TableReference params schema (Join left right)
455+ LeftOuterJoin
456+ :: TableReference params schema right
457+ -> Condition params (Join left right) 'Ungrouped
458+ -> TableReference params schema left
459+ -> TableReference params schema (Join left (NullifyTables right))
460+ RightOuterJoin
461+ :: TableReference params schema right
462+ -> Condition params (Join left right) 'Ungrouped
463+ -> TableReference params schema left
464+ -> TableReference params schema (Join (NullifyTables left) right)
465+ FullOuterJoin
466+ :: TableReference params schema right
467+ -> Condition params (Join left right) 'Ungrouped
468+ -> TableReference params schema left
469+ -> TableReference params schema
470+ (Join (NullifyTables left) (NullifyTables right))
471+
472+renderTableReference :: TableReference params schema tables -> ByteString
473+renderTableReference = \case
474+ Table table -> renderAliased renderTable table
475+ Subquery selection -> renderAliased renderQuery selection
476+ CrossJoin right left -> mconcat
477+ [ renderTableReference left
478+ , " CROSS JOIN "
479+ , renderTableReference right
480+ ]
481+ InnerJoin right on left -> mconcat
482+ [ renderTableReference left
483+ , " INNER JOIN "
484+ , renderTableReference right
485+ , " ON "
486+ , renderExpression on
487+ ]
488+ LeftOuterJoin right on left -> mconcat
489+ [ renderTableReference left
490+ , " LEFT OUTER JOIN "
491+ , renderTableReference right
492+ , " ON "
493+ , renderExpression on
494+ ]
495+ RightOuterJoin right on left -> mconcat
496+ [ renderTableReference left
497+ , " RIGHT OUTER JOIN "
498+ , renderTableReference right
499+ , " ON "
500+ , renderExpression on
501+ ]
502+ FullOuterJoin right on left -> mconcat
503+ [ renderTableReference left
504+ , " FULL OUTER JOIN "
505+ , renderTableReference right
506+ , " ON "
507+ , renderExpression on
508+ ]
509+
510+data By tables tabcolty where
511+ By
512+ :: (HasTable table tables columns, HasColumn column columns ty)
513+ => Alias table
514+ -> Alias column
515+ -> By tables (table ::: (column ::: ty))
516+
517+instance (HasTable table tables columns, HasColumn column columns ty)
518+ => TableColumn table column (By tables (table ::: (column ::: ty))) where
519+ (&.) = By
520+
521+renderBy :: By tables tabcolty -> ByteString
522+renderBy (By table column) = renderAlias table <> "." <> renderAlias column
523+
524+data GroupByClause tables grouping where
525+ NoGroups :: GroupByClause tables 'Ungrouped
526+ Distinct
527+ :: SListI distincts
528+ => NP (By tables) distincts
529+ -> GroupByClause tables ('Grouped distincts)
530+
531+renderGroupByClause :: GroupByClause tables grouping -> ByteString
532+renderGroupByClause = \case
533+ NoGroups -> ""
534+ Distinct Nil -> ""
535+ Distinct distincts -> " GROUP BY " <> ByteString.intercalate ", "
536+ (hcollapse (hmap (K . renderBy) distincts))
537+
538+data HavingClause params tables grouping where
539+ NoHaving :: HavingClause params tables 'Ungrouped
540+ Having
541+ :: [Condition params tables ('Grouped distincts)]
542+ -> HavingClause params tables ('Grouped distincts)
543+
544+renderHavingClause :: HavingClause params tables grouping -> ByteString
545+renderHavingClause = \case
546+ NoHaving -> ""
547+ Having [] -> ""
548+ Having conditions -> " HAVING " <> ByteString.intercalate ", "
549+ (renderExpression <$> conditions)
550+
551+data TableExpression
552+ (params :: [ColumnType])
553+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
554+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
555+ (grouping :: Grouping)
556+ = TableExpression
557+ { fromClause :: TableReference params schema tables
558+ , whereClause :: [Condition params tables 'Ungrouped]
559+ , groupByClause :: GroupByClause tables grouping
560+ , havingClause :: HavingClause params tables grouping
561+ , orderByClause :: [SortExpression params tables grouping]
562+ , limitClause :: [Word64]
563+ , offsetClause :: [Word64]
564+ }
565+
566+renderTableExpression
567+ :: TableExpression params schema tables grouping
568+ -> ByteString
569+renderTableExpression
570+ (TableExpression tables whs' grps' hvs' srts' lims' offs') = mconcat
571+ [ "FROM ", renderTableReference tables
572+ , renderWheres whs'
573+ , renderGroupByClause grps'
574+ , renderHavingClause hvs'
575+ , renderSorts srts'
576+ , renderLimits lims'
577+ , renderOffsets offs'
578+ ]
579+ where
580+ renderWheres = \case
581+ [] -> ""
582+ wh:[] -> " WHERE " <> renderExpression wh
583+ wh:whs -> " WHERE " <> renderExpression (foldr (&&*) wh whs)
584+ renderSorts = \case
585+ [] -> ""
586+ srts -> " SORT BY "
587+ <> ByteString.intercalate ", " (renderSortExpression <$> srts)
588+ renderLimits = \case
589+ [] -> ""
590+ lims -> " LIMIT " <> fromString (show (minimum lims))
591+ renderOffsets = \case
592+ [] -> ""
593+ offs -> " OFFSET " <> fromString (show (sum offs))
594+
595+from
596+ :: TableReference params schema tables
597+ -> TableExpression params schema tables 'Ungrouped
598+from tables = TableExpression tables [] NoGroups NoHaving [] [] []
599+
600+where_
601+ :: Condition params tables 'Ungrouped
602+ -> TableExpression params schema tables grouping
603+ -> TableExpression params schema tables grouping
604+where_ wh tables = tables {whereClause = wh : whereClause tables}
605+
606+group
607+ :: SListI distincts
608+ => NP (By tables) distincts
609+ -> TableExpression params schema tables 'Ungrouped
610+ -> TableExpression params schema tables ('Grouped distincts)
611+group distincts tables = TableExpression
612+ { fromClause = fromClause tables
613+ , whereClause = whereClause tables
614+ , groupByClause = Distinct distincts
615+ , havingClause = Having []
616+ , orderByClause = []
617+ , limitClause = limitClause tables
618+ , offsetClause = offsetClause tables
619+ }
620+
621+having
622+ :: Condition params tables ('Grouped distincts)
623+ -> TableExpression params schema tables ('Grouped distincts)
624+ -> TableExpression params schema tables ('Grouped distincts)
625+having hv tables = tables
626+ { havingClause = case havingClause tables of Having hvs -> Having (hv:hvs) }
627+
628+orderBy
629+ :: [SortExpression params tables grouping]
630+ -> TableExpression params schema tables grouping
631+ -> TableExpression params schema tables grouping
632+orderBy srts tables = tables {orderByClause = orderByClause tables ++ srts}
633+
634+limit
635+ :: Word64
636+ -> TableExpression params schema tables grouping
637+ -> TableExpression params schema tables grouping
638+limit lim tables = tables {limitClause = lim : limitClause tables}
639+
640+offset
641+ :: Word64
642+ -> TableExpression params schema tables grouping
643+ -> TableExpression params schema tables grouping
644+offset off tables = tables {offsetClause = off : offsetClause tables}
645+
646+{-----------------------------------------
647+type expressions
648+-----------------------------------------}
649+
650+newtype TypeExpression (ty :: ColumnType)
651+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
652+ deriving (Show,Eq)
653+
654+bool :: TypeExpression ('Required ('Null 'PGBool))
655+bool = UnsafeTypeExpression "bool"
656+int2 :: TypeExpression ('Required ('Null 'PGInt2))
657+int2 = UnsafeTypeExpression "int2"
658+smallint :: TypeExpression ('Required ('Null 'PGInt2))
659+smallint = UnsafeTypeExpression "smallint"
660+int4 :: TypeExpression ('Required ('Null 'PGInt4))
661+int4 = UnsafeTypeExpression "int4"
662+int :: TypeExpression ('Required ('Null 'PGInt4))
663+int = UnsafeTypeExpression "int"
664+integer :: TypeExpression ('Required ('Null 'PGInt4))
665+integer = UnsafeTypeExpression "integer"
666+int8 :: TypeExpression ('Required ('Null 'PGInt8))
667+int8 = UnsafeTypeExpression "int8"
668+bigint :: TypeExpression ('Required ('Null 'PGInt8))
669+bigint = UnsafeTypeExpression "bigint"
670+numeric :: TypeExpression ('Required ('Null 'PGNumeric))
671+numeric = UnsafeTypeExpression "numeric"
672+float4 :: TypeExpression ('Required ('Null 'PGFloat4))
673+float4 = UnsafeTypeExpression "float4"
674+real :: TypeExpression ('Required ('Null 'PGFloat4))
675+real = UnsafeTypeExpression "real"
676+float8 :: TypeExpression ('Required ('Null 'PGFloat8))
677+float8 = UnsafeTypeExpression "float8"
678+doublePrecision :: TypeExpression ('Required ('Null 'PGFloat8))
679+doublePrecision = UnsafeTypeExpression "double precision"
680+serial2 :: TypeExpression ('Optional ('NotNull 'PGInt2))
681+serial2 = UnsafeTypeExpression "serial2"
682+smallserial :: TypeExpression ('Optional ('NotNull 'PGInt2))
683+smallserial = UnsafeTypeExpression "smallserial"
684+serial4 :: TypeExpression ('Optional ('NotNull 'PGInt4))
685+serial4 = UnsafeTypeExpression "serial4"
686+serial :: TypeExpression ('Optional ('NotNull 'PGInt4))
687+serial = UnsafeTypeExpression "serial"
688+serial8 :: TypeExpression ('Optional ('NotNull 'PGInt8))
689+serial8 = UnsafeTypeExpression "serial8"
690+bigserial :: TypeExpression ('Optional ('NotNull 'PGInt8))
691+bigserial = UnsafeTypeExpression "bigserial"
692+money :: TypeExpression ('Required ('Null 'PGMoney))
693+money = UnsafeTypeExpression "money"
694+text :: TypeExpression ('Required ('Null 'PGText))
695+text = UnsafeTypeExpression "text"
696+char
697+ :: KnownNat n
698+ => proxy n
699+ -> TypeExpression ('Required ('Null ('PGChar n)))
700+char (_ :: proxy n) = UnsafeTypeExpression $
701+ "char(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
702+character
703+ :: KnownNat n
704+ => proxy n
705+ -> TypeExpression ('Required ('Null ('PGChar n)))
706+character (_ :: proxy n) = UnsafeTypeExpression $
707+ "character(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
708+varchar
709+ :: KnownNat n
710+ => proxy n
711+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
712+varchar (_ :: proxy n) = UnsafeTypeExpression $
713+ "varchar(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
714+characterVarying
715+ :: KnownNat n
716+ => proxy n
717+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
718+characterVarying (_ :: proxy n) = UnsafeTypeExpression $
719+ "character varying(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
720+bytea :: TypeExpression ('Required ('Null ('PGBytea)))
721+bytea = UnsafeTypeExpression "bytea"
722+timestamp :: TypeExpression ('Required ('Null ('PGTimestamp)))
723+timestamp = UnsafeTypeExpression "timestamp"
724+timestampWithTimeZone :: TypeExpression ('Required ('Null ('PGTimestampTZ)))
725+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
726+date :: TypeExpression ('Required ('Null ('PGDate)))
727+date = UnsafeTypeExpression "date"
728+time :: TypeExpression ('Required ('Null ('PGTime)))
729+time = UnsafeTypeExpression "time"
730+timeWithTimeZone :: TypeExpression ('Required ('Null ('PGTimeTZ)))
731+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
732+interval :: TypeExpression ('Required ('Null ('PGInterval)))
733+interval = UnsafeTypeExpression "interval"
734+uuid :: TypeExpression ('Required ('Null ('PGUuid)))
735+uuid = UnsafeTypeExpression "uuid"
736+json :: TypeExpression ('Required ('Null ('PGJson)))
737+json = UnsafeTypeExpression "json"
738+jsonb :: TypeExpression ('Required ('Null ('PGJsonb)))
739+jsonb = UnsafeTypeExpression "jsonb"
740+
741+notNull
742+ :: TypeExpression ('Required ('Null ty))
743+ -> TypeExpression ('Required ('NotNull ty))
744+notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <> " NOT NULL"
745+default_
746+ :: Expression '[] '[] 'Ungrouped ('Required ty)
747+ -> TypeExpression ('Required ty)
748+ -> TypeExpression ('Optional ty)
749+default_ x ty = UnsafeTypeExpression $
750+ renderTypeExpression ty <> " DEFAULT " <> renderExpression x
751+
752+class PGTyped (ty :: PGType) where
753+ pgtype :: TypeExpression ('Required ('Null ty))
754+instance PGTyped 'PGBool where pgtype = bool
755+instance PGTyped 'PGInt2 where pgtype = int2
756+instance PGTyped 'PGInt4 where pgtype = int4
757+instance PGTyped 'PGInt8 where pgtype = int8
758+instance PGTyped 'PGNumeric where pgtype = numeric
759+instance PGTyped 'PGFloat4 where pgtype = float4
760+instance PGTyped 'PGFloat8 where pgtype = float8
761+instance PGTyped 'PGMoney where pgtype = money
762+instance PGTyped 'PGText where pgtype = text
763+instance KnownNat n => PGTyped ('PGChar n) where pgtype = char (Proxy @n)
764+instance KnownNat n => PGTyped ('PGVarChar n) where pgtype = varchar (Proxy @n)
765+instance PGTyped 'PGBytea where pgtype = bytea
766+instance PGTyped 'PGTimestamp where pgtype = timestamp
767+instance PGTyped 'PGTimestampTZ where pgtype = timestampWithTimeZone
768+instance PGTyped 'PGDate where pgtype = date
769+instance PGTyped 'PGTime where pgtype = time
770+instance PGTyped 'PGTimeTZ where pgtype = timeWithTimeZone
771+instance PGTyped 'PGInterval where pgtype = interval
772+instance PGTyped 'PGUuid where pgtype = uuid
773+instance PGTyped 'PGJson where pgtype = json
774+instance PGTyped 'PGJsonb where pgtype = jsonb
775+
776+{-----------------------------------------
777+statements
778+-----------------------------------------}
779+
780+newtype Definition
781+ (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
782+ (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
783+ = UnsafeDefinition { renderDefinition :: ByteString }
784+
785+instance Category Definition where
786+ id = UnsafeDefinition ";"
787+ dd1 . dd0 = UnsafeDefinition $
788+ renderDefinition dd0 <> " " <> renderDefinition dd1
789+
790+newtype Manipulation
791+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
792+ (params :: [ColumnType])
793+ (columns :: [(Symbol,ColumnType)])
794+ = UnsafeManipulation { renderManipulation :: ByteString }
795+
796+newtype Query
797+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
798+ (params :: [ColumnType])
799+ (columns :: [(Symbol,ColumnType)])
800+ = UnsafeQuery { renderQuery :: ByteString }
801+
802+query
803+ :: Query schema params columns
804+ -> Manipulation schema params columns
805+query q = UnsafeManipulation $ renderQuery q <> ";"
806+
807+union, unionAll, intersect, intersectAll, except, exceptAll
808+ :: Query schema params columns
809+ -> Query schema params columns
810+ -> Query schema params columns
811+q1 `union` q2 = UnsafeQuery $
812+ renderQuery q1 <> " UNION " <> renderQuery q2
813+q1 `unionAll` q2 = UnsafeQuery $
814+ renderQuery q1 <> " UNION ALL " <> renderQuery q2
815+q1 `intersect` q2 = UnsafeQuery $
816+ renderQuery q1 <> " INTERSECT " <> renderQuery q2
817+q1 `intersectAll` q2 = UnsafeQuery $
818+ renderQuery q1 <> " INTERSECT ALL " <> renderQuery q2
819+q1 `except` q2 = UnsafeQuery $
820+ renderQuery q1 <> " EXCEPT " <> renderQuery q2
821+q1 `exceptAll` q2 = UnsafeQuery $
822+ renderQuery q1 <> " EXCEPT ALL " <> renderQuery q2
823+
824+{-----------------------------------------
825+SELECT statements
826+-----------------------------------------}
827+
828+select
829+ :: SListI columns
830+ => NP (Aliased (Expression params tables grouping)) columns
831+ -> TableExpression params schema tables grouping
832+ -> Query schema params columns
833+select list tabs = UnsafeQuery $
834+ "SELECT " <> renderList list <> " " <> renderTableExpression tabs
835+ where
836+ renderList
837+ = ByteString.intercalate ", "
838+ . hcollapse
839+ . hmap (K . renderAliased renderExpression)
840+
841+selectStar
842+ :: (tables ~ '[table ::: columns])
843+ => TableExpression params schema tables 'Ungrouped
844+ -> Query schema params columns
845+selectStar tabs = UnsafeQuery $ "SELECT * " <> renderTableExpression tabs
846+
847+selectDotStar
848+ :: HasTable table tables columns
849+ => Alias table
850+ -> TableExpression params schema tables 'Ungrouped
851+ -> Query schema params columns
852+selectDotStar table tables = UnsafeQuery $
853+ "SELECT " <> renderAlias table <> ".* " <> renderTableExpression tables
854+
855+{-----------------------------------------
856+CREATE statements
857+-----------------------------------------}
858+
859+createTable
860+ :: (KnownSymbol table, SListI columns)
861+ => Alias table
862+ -> NP (Aliased TypeExpression) columns
863+ -> Definition schema (Create table columns schema)
864+createTable table columns = UnsafeDefinition $ mconcat
865+ [ "CREATE TABLE "
866+ , renderAlias table
867+ , " ("
868+ , ByteString.intercalate ", " . hcollapse $
869+ hmap (K . renderColumn) columns
870+ , ");"
871+ ]
872+ where
873+ renderColumn :: Aliased TypeExpression x -> ByteString
874+ renderColumn (ty `As` column) =
875+ renderAlias column <> " " <> renderTypeExpression ty
876+
877+{-----------------------------------------
878+DROP statements
879+-----------------------------------------}
880+
881+dropTable
882+ :: KnownSymbol table
883+ => Alias table
884+ -> Definition schema (Drop table schema)
885+dropTable table = UnsafeDefinition $ "DROP TABLE " <> renderAlias table <> ";"
886+
887+{-----------------------------------------
888+ALTER statements
889+-----------------------------------------}
890+
891+alterTable
892+ :: HasTable table schema columns0
893+ => Alias table
894+ -> AlterTable columns0 columns1
895+ -> Definition schema (Alter table schema columns1)
896+alterTable table alteration = UnsafeDefinition $
897+ "ALTER TABLE "
898+ <> renderAlias table
899+ <> " "
900+ <> renderAlterTable alteration
901+ <> ";"
902+
903+alterTableRename
904+ :: (KnownSymbol table0, KnownSymbol table1)
905+ => Alias table0
906+ -> Alias table1
907+ -> Definition schema (Rename table0 table1 schema)
908+alterTableRename table0 table1 = UnsafeDefinition $
909+ "ALTER TABLE "
910+ <> renderAlias table0
911+ <> " RENAME TO "
912+ <> renderAlias table1
913+ <> ";"
914+
915+data AlterTable
916+ (columns0 :: [(Symbol, ColumnType)])
917+ (columns1 :: [(Symbol, ColumnType)]) =
918+ UnsafeAlterTable {renderAlterTable :: ByteString}
919+
920+addColumnDefault
921+ :: KnownSymbol column
922+ => Alias column
923+ -> TypeExpression ('Optional ty)
924+ -> AlterTable columns (Create column ('Optional ty) columns)
925+addColumnDefault column ty = UnsafeAlterTable $
926+ "ADD COLUMN "
927+ <> renderAlias column
928+ <> " "
929+ <> renderTypeExpression ty
930+
931+addColumnNull
932+ :: KnownSymbol column
933+ => Alias column
934+ -> TypeExpression ('Required ('Null ty))
935+ -> AlterTable columns ((column ::: 'Required ('Null ty)) ': columns)
936+addColumnNull column ty = UnsafeAlterTable $
937+ "ADD COLUMN "
938+ <> renderAlias column
939+ <> " "
940+ <> renderTypeExpression ty
941+
942+dropColumn
943+ :: KnownSymbol column
944+ => Alias column
945+ -> AlterTable columns (Drop column columns)
946+dropColumn column = UnsafeAlterTable $
947+ "DROP COLUMN " <> renderAlias column
948+
949+renameColumn
950+ :: (KnownSymbol column0, KnownSymbol column1)
951+ => Alias column0
952+ -> Alias column1
953+ -> AlterTable columns (Rename column0 column1 columns)
954+renameColumn column0 column1 = UnsafeAlterTable $
955+ "RENAME COLUMN " <> renderAlias column0
956+ <> " TO " <> renderAlias column1
957+
958+alterColumn
959+ :: (KnownSymbol column, HasColumn column columns ty0)
960+ => Alias column
961+ -> AlterColumn ty0 ty1
962+ -> AlterTable columns (Alter column columns ty1)
963+alterColumn column alteration = UnsafeAlterTable $
964+ "ALTER COLUMN " <> renderAlias column
965+ <> " " <> renderAlterColumn alteration
966+
967+data AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
968+ UnsafeAlterColumn {renderAlterColumn :: ByteString}
969+
970+setDefault
971+ :: Expression '[] '[] 'Ungrouped ('Required ty)
972+ -> AlterColumn ('Required ty) ('Optional ty)
973+setDefault expression = UnsafeAlterColumn $
974+ "SET DEFAULT " <> renderExpression expression
975+
976+dropDefault :: AlterColumn ('Optional ty) ('Required ty)
977+dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
978+
979+setNotNull
980+ :: AlterColumn (optionality ('Null ty)) (optionality ('NotNull ty))
981+setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
982+
983+dropNotNull
984+ :: AlterColumn (optionality ('NotNull ty)) (optionality ('Null ty))
985+dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
986+
987+alterType
988+ :: PGCast ty0 ty1
989+ => TypeExpression (optionality (nullity ty1))
990+ -> AlterColumn (optionality (nullity ty0)) (optionality (nullity ty1))
991+alterType ty = UnsafeAlterColumn $ "TYPE " <> renderTypeExpression ty
992+
993+{-----------------------------------------
994+INSERT statements
995+-----------------------------------------}
996+
997+insertInto
998+ :: (SListI columns, HasTable table schema columns)
999+ => Alias table
1000+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1001+ -> Manipulation schema params '[]
1002+insertInto table expressions = UnsafeManipulation $ "INSERT INTO "
1003+ <> renderAlias table
1004+ <> " (" <> ByteString.intercalate ", " aliases
1005+ <> ") VALUES ("
1006+ <> ByteString.intercalate ", " values
1007+ <> ");"
1008+ where
1009+ aliases = hcollapse $ hmap
1010+ (\ (_ `As` name) -> K (renderAlias name))
1011+ expressions
1012+ values = hcollapse $ hmap
1013+ (\ (expression `As` _) -> K (renderExpression expression))
1014+ expressions
1015+
1016+insertIntoReturning
1017+ :: (SListI columns, SListI results, HasTable table schema columns)
1018+ => Alias table
1019+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1020+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1021+ -> Manipulation schema params results
1022+insertIntoReturning table inserts results
1023+ = UnsafeManipulation . mconcat $
1024+ [ "INSERT INTO "
1025+ , renderAlias table
1026+ , " (" <> ByteString.intercalate ", " aliases
1027+ , ") VALUES ("
1028+ , ByteString.intercalate ", " values
1029+ , ") RETURNING "
1030+ , renderList results
1031+ , ";"
1032+ ]
1033+ where
1034+ aliases = hcollapse $ hmap
1035+ (\ (_ `As` name) -> K (renderAlias name))
1036+ inserts
1037+ values = hcollapse $ hmap
1038+ (\ (expression `As` _) -> K (renderExpression expression))
1039+ inserts
1040+ renderList
1041+ = ByteString.intercalate ", "
1042+ . hcollapse
1043+ . hmap (K . renderAliased renderExpression)
1044+
1045+{-----------------------------------------
1046+UPDATE statements
1047+-----------------------------------------}
1048+
1049+data UpdateExpression params table ty
1050+ = Same
1051+ | Set (Expression params table 'Ungrouped ty)
1052+
1053+renderUpdateExpression
1054+ :: Aliased (UpdateExpression params '[table ::: columns]) column
1055+ -> Maybe ByteString
1056+renderUpdateExpression = \case
1057+ Same `As` _ -> Nothing
1058+ Set expression `As` column -> Just $ mconcat
1059+ [ renderAlias column
1060+ , " = "
1061+ , renderExpression expression
1062+ ]
1063+
1064+update
1065+ :: (HasTable table schema columns, SListI columns)
1066+ => Alias table
1067+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1068+ -> Condition params '[table ::: columns] 'Ungrouped
1069+ -> Manipulation schema params '[]
1070+update table columns wh = UnsafeManipulation $ mconcat
1071+ [ "UPDATE "
1072+ , renderAlias table
1073+ , " SET "
1074+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1075+ hmap (K . renderUpdateExpression) columns
1076+ , " WHERE ", renderExpression wh, ";"
1077+ ]
1078+
1079+updateReturning
1080+ :: (SListI columns, SListI results, HasTable table schema columns)
1081+ => Alias table
1082+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1083+ -> Condition params '[table ::: columns] 'Ungrouped
1084+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1085+ -> Manipulation schema params results
1086+updateReturning table updates wh results = UnsafeManipulation $ mconcat
1087+ [ "UPDATE "
1088+ , renderAlias table
1089+ , " SET "
1090+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1091+ hmap (K . renderUpdateExpression) updates
1092+ , " WHERE ", renderExpression wh
1093+ , " RETURNING ", renderList results
1094+ , ";"
1095+ ]
1096+ where
1097+ renderList
1098+ = ByteString.intercalate ", "
1099+ . hcollapse
1100+ . hmap (K . renderAliased renderExpression)
1101+
1102+upsertInto
1103+ :: (SListI columns, HasTable table schema columns)
1104+ => Alias table
1105+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1106+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1107+ -> Condition params '[table ::: columns] 'Ungrouped
1108+ -> Manipulation schema params '[]
1109+upsertInto table inserts updates wh = UnsafeManipulation . mconcat $
1110+ [ "INSERT INTO "
1111+ , renderAlias table
1112+ , " (" <> ByteString.intercalate ", " aliases
1113+ , ") VALUES ("
1114+ , ByteString.intercalate ", " values
1115+ , ") ON CONFLICT UPDATE "
1116+ , renderAlias table
1117+ , " SET "
1118+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1119+ hmap (K . renderUpdateExpression) updates
1120+ , " WHERE ", renderExpression wh, ";"
1121+ ] where
1122+ aliases = hcollapse $ hmap
1123+ (\ (_ `As` name) -> K (renderAlias name))
1124+ inserts
1125+ values = hcollapse $ hmap
1126+ (\ (expression `As` _) -> K (renderExpression expression))
1127+ inserts
1128+
1129+upsertIntoReturning
1130+ :: (SListI columns, SListI results, HasTable table schema columns)
1131+ => Alias table
1132+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1133+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1134+ -> Condition params '[table ::: columns] 'Ungrouped
1135+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1136+ -> Manipulation schema params results
1137+upsertIntoReturning table inserts updates wh results =
1138+ UnsafeManipulation . mconcat $
1139+ [ "INSERT INTO "
1140+ , renderAlias table
1141+ , " (" <> ByteString.intercalate ", " aliases
1142+ , ") VALUES ("
1143+ , ByteString.intercalate ", " values
1144+ , ") ON CONFLICT UPDATE "
1145+ , renderAlias table
1146+ , " SET "
1147+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1148+ hmap (K . renderUpdateExpression) updates
1149+ , " WHERE ", renderExpression wh
1150+ , " RETURNING ", renderList results
1151+ , ";"
1152+ ] where
1153+ aliases = hcollapse $ hmap
1154+ (\ (_ `As` name) -> K (renderAlias name))
1155+ inserts
1156+ values = hcollapse $ hmap
1157+ (\ (expression `As` _) -> K (renderExpression expression))
1158+ inserts
1159+ renderList
1160+ = ByteString.intercalate ", "
1161+ . hcollapse
1162+ . hmap (K . renderAliased renderExpression)
1163+
1164+{-----------------------------------------
1165+DELETE statements
1166+-----------------------------------------}
1167+
1168+deleteFrom
1169+ :: HasTable table schema columns
1170+ => Alias table
1171+ -> Condition params '[table ::: columns] 'Ungrouped
1172+ -> Manipulation schema params '[]
1173+deleteFrom table wh = UnsafeManipulation $ mconcat
1174+ [ "DELETE FROM "
1175+ , renderAlias table
1176+ , " WHERE ", renderExpression wh, ";"
1177+ ]