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