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