@@ -0,0 +1,1193 @@
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+unsafeAggregate
380+ :: ByteString
381+ -> Expression params tables 'Ungrouped xty
382+ -> Expression params tables ('Grouped distincts) yty
383+unsafeAggregate fun x = UnsafeExpression $ mconcat
384+ [fun, "(", renderExpression x, ")"]
385+
386+sum_
387+ :: PGNum ty
388+ => Expression params tables 'Ungrouped ('Required (nullity ty))
389+ -> Expression params tables ('Grouped distincts) ('Required (nullity ty))
390+sum_ = unsafeAggregate "sum"
391+
392+class (KnownSymbol table, KnownSymbol column)
393+ => GroupedBy table column distincts where
394+ getGroup
395+ :: (HasTable table tables columns, HasColumn column columns ty)
396+ => Alias table
397+ -> Alias column
398+ -> Expression params tables ('Grouped distincts) ty
399+ getGroup table column = UnsafeExpression $
400+ renderAlias table <> "." <> renderAlias column
401+instance {-# OVERLAPPING #-} (KnownSymbol table, KnownSymbol column)
402+ => GroupedBy table column '[ '(table,column)]
403+instance {-# OVERLAPPABLE #-}
404+ ( KnownSymbol table
405+ , KnownSymbol column
406+ , GroupedBy table column distincts
407+ ) => GroupedBy table column (tabcol ': distincts)
408+
409+instance
410+ ( tables ~ '[table ::: columns]
411+ , HasColumn column columns ty
412+ , GroupedBy table column distincts
413+ ) => IsLabel column
414+ (Expression params tables ('Grouped distincts) ty) where
415+ fromLabel p = getGroup (Alias (proxy# :: Proxy# table)) (Alias p)
416+
417+instance
418+ ( HasTable table tables columns
419+ , HasColumn column columns ty
420+ , GroupedBy table column distincts
421+ ) => TableColumn table column
422+ (Expression params tables ('Grouped distincts) ty) where
423+ (&.) = getGroup
424+
425+{-----------------------------------------
426+tables
427+-----------------------------------------}
428+
429+newtype Table
430+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
431+ (columns :: [(Symbol,ColumnType)])
432+ = UnsafeTable { renderTable :: ByteString }
433+ deriving (Show,Eq)
434+
435+class KnownSymbol table => HasTable table tables columns
436+ | table tables -> columns where
437+ getTable :: Proxy# table -> Table tables columns
438+ getTable table = UnsafeTable $ fromString $ symbolVal' table
439+instance {-# OVERLAPPING #-} KnownSymbol table
440+ => HasTable table ((table ::: columns) ': tables) columns
441+instance {-# OVERLAPPABLE #-}
442+ (KnownSymbol table, HasTable table schema columns)
443+ => HasTable table (table' ': schema) columns
444+
445+instance HasTable table schema columns
446+ => IsLabel table (Table schema columns) where
447+ fromLabel = getTable
448+
449+data TableReference params schema tables where
450+ Table
451+ :: Aliased (Table schema) table
452+ -> TableReference params schema '[table]
453+ Subquery
454+ :: Aliased (Query schema params) table
455+ -> TableReference params schema '[table]
456+ CrossJoin
457+ :: TableReference params schema right
458+ -> TableReference params schema left
459+ -> TableReference params schema (Join left right)
460+ InnerJoin
461+ :: TableReference params schema right
462+ -> Condition params (Join left right) 'Ungrouped
463+ -> TableReference params schema left
464+ -> TableReference params schema (Join left right)
465+ LeftOuterJoin
466+ :: TableReference params schema right
467+ -> Condition params (Join left right) 'Ungrouped
468+ -> TableReference params schema left
469+ -> TableReference params schema (Join left (NullifyTables right))
470+ RightOuterJoin
471+ :: TableReference params schema right
472+ -> Condition params (Join left right) 'Ungrouped
473+ -> TableReference params schema left
474+ -> TableReference params schema (Join (NullifyTables left) right)
475+ FullOuterJoin
476+ :: TableReference params schema right
477+ -> Condition params (Join left right) 'Ungrouped
478+ -> TableReference params schema left
479+ -> TableReference params schema
480+ (Join (NullifyTables left) (NullifyTables right))
481+
482+renderTableReference :: TableReference params schema tables -> ByteString
483+renderTableReference = \case
484+ Table table -> renderAliased renderTable table
485+ Subquery selection -> renderAliased renderQuery selection
486+ CrossJoin right left -> mconcat
487+ [ renderTableReference left
488+ , " CROSS JOIN "
489+ , renderTableReference right
490+ ]
491+ InnerJoin right on left -> mconcat
492+ [ renderTableReference left
493+ , " INNER JOIN "
494+ , renderTableReference right
495+ , " ON "
496+ , renderExpression on
497+ ]
498+ LeftOuterJoin right on left -> mconcat
499+ [ renderTableReference left
500+ , " LEFT OUTER JOIN "
501+ , renderTableReference right
502+ , " ON "
503+ , renderExpression on
504+ ]
505+ RightOuterJoin right on left -> mconcat
506+ [ renderTableReference left
507+ , " RIGHT OUTER JOIN "
508+ , renderTableReference right
509+ , " ON "
510+ , renderExpression on
511+ ]
512+ FullOuterJoin right on left -> mconcat
513+ [ renderTableReference left
514+ , " FULL OUTER JOIN "
515+ , renderTableReference right
516+ , " ON "
517+ , renderExpression on
518+ ]
519+
520+data By
521+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
522+ (tabcol :: (Symbol,Symbol)) where
523+ By
524+ :: (HasTable table tables columns, HasColumn column columns ty)
525+ => (Alias table, Alias column)
526+ -> By tables '(table, column)
527+
528+by
529+ :: ( KnownSymbol table
530+ , tables ~ '[table ::: columns]
531+ , HasColumn column columns ty
532+ )
533+ => Alias column
534+ -> By tables '(table, column)
535+by column = By (Alias (proxy# :: Proxy# (table :: Symbol)), column)
536+
537+renderBy :: By tables tabcolty -> ByteString
538+renderBy (By (table, column)) = renderAlias table <> "." <> renderAlias column
539+
540+data GroupByClause tables grouping where
541+ NoGroups :: GroupByClause tables 'Ungrouped
542+ Distinct
543+ :: SListI distincts
544+ => NP (By tables) distincts
545+ -> GroupByClause tables ('Grouped distincts)
546+
547+renderGroupByClause :: GroupByClause tables grouping -> ByteString
548+renderGroupByClause = \case
549+ NoGroups -> ""
550+ Distinct Nil -> ""
551+ Distinct distincts -> " GROUP BY " <> ByteString.intercalate ", "
552+ (hcollapse (hmap (K . renderBy) distincts))
553+
554+data HavingClause params tables grouping where
555+ NoHaving :: HavingClause params tables 'Ungrouped
556+ Having
557+ :: [Condition params tables ('Grouped distincts)]
558+ -> HavingClause params tables ('Grouped distincts)
559+
560+renderHavingClause :: HavingClause params tables grouping -> ByteString
561+renderHavingClause = \case
562+ NoHaving -> ""
563+ Having [] -> ""
564+ Having conditions -> " HAVING " <> ByteString.intercalate ", "
565+ (renderExpression <$> conditions)
566+
567+data TableExpression
568+ (params :: [ColumnType])
569+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
570+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
571+ (grouping :: Grouping)
572+ = TableExpression
573+ { fromClause :: TableReference params schema tables
574+ , whereClause :: [Condition params tables 'Ungrouped]
575+ , groupByClause :: GroupByClause tables grouping
576+ , havingClause :: HavingClause params tables grouping
577+ , orderByClause :: [SortExpression params tables grouping]
578+ , limitClause :: [Word64]
579+ , offsetClause :: [Word64]
580+ }
581+
582+renderTableExpression
583+ :: TableExpression params schema tables grouping
584+ -> ByteString
585+renderTableExpression
586+ (TableExpression tables whs' grps' hvs' srts' lims' offs') = mconcat
587+ [ "FROM ", renderTableReference tables
588+ , renderWheres whs'
589+ , renderGroupByClause grps'
590+ , renderHavingClause hvs'
591+ , renderSorts srts'
592+ , renderLimits lims'
593+ , renderOffsets offs'
594+ ]
595+ where
596+ renderWheres = \case
597+ [] -> ""
598+ wh:[] -> " WHERE " <> renderExpression wh
599+ wh:whs -> " WHERE " <> renderExpression (foldr (&&*) wh whs)
600+ renderSorts = \case
601+ [] -> ""
602+ srts -> " SORT BY "
603+ <> ByteString.intercalate ", " (renderSortExpression <$> srts)
604+ renderLimits = \case
605+ [] -> ""
606+ lims -> " LIMIT " <> fromString (show (minimum lims))
607+ renderOffsets = \case
608+ [] -> ""
609+ offs -> " OFFSET " <> fromString (show (sum offs))
610+
611+from
612+ :: TableReference params schema tables
613+ -> TableExpression params schema tables 'Ungrouped
614+from tables = TableExpression tables [] NoGroups NoHaving [] [] []
615+
616+where_
617+ :: Condition params tables 'Ungrouped
618+ -> TableExpression params schema tables grouping
619+ -> TableExpression params schema tables grouping
620+where_ wh tables = tables {whereClause = wh : whereClause tables}
621+
622+group
623+ :: SListI distincts
624+ => NP (By tables) distincts
625+ -> TableExpression params schema tables 'Ungrouped
626+ -> TableExpression params schema tables ('Grouped distincts)
627+group distincts tables = TableExpression
628+ { fromClause = fromClause tables
629+ , whereClause = whereClause tables
630+ , groupByClause = Distinct distincts
631+ , havingClause = Having []
632+ , orderByClause = []
633+ , limitClause = limitClause tables
634+ , offsetClause = offsetClause tables
635+ }
636+
637+having
638+ :: Condition params tables ('Grouped distincts)
639+ -> TableExpression params schema tables ('Grouped distincts)
640+ -> TableExpression params schema tables ('Grouped distincts)
641+having hv tables = tables
642+ { havingClause = case havingClause tables of Having hvs -> Having (hv:hvs) }
643+
644+orderBy
645+ :: [SortExpression params tables grouping]
646+ -> TableExpression params schema tables grouping
647+ -> TableExpression params schema tables grouping
648+orderBy srts tables = tables {orderByClause = orderByClause tables ++ srts}
649+
650+limit
651+ :: Word64
652+ -> TableExpression params schema tables grouping
653+ -> TableExpression params schema tables grouping
654+limit lim tables = tables {limitClause = lim : limitClause tables}
655+
656+offset
657+ :: Word64
658+ -> TableExpression params schema tables grouping
659+ -> TableExpression params schema tables grouping
660+offset off tables = tables {offsetClause = off : offsetClause tables}
661+
662+{-----------------------------------------
663+type expressions
664+-----------------------------------------}
665+
666+newtype TypeExpression (ty :: ColumnType)
667+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
668+ deriving (Show,Eq)
669+
670+bool :: TypeExpression ('Required ('Null 'PGBool))
671+bool = UnsafeTypeExpression "bool"
672+int2 :: TypeExpression ('Required ('Null 'PGInt2))
673+int2 = UnsafeTypeExpression "int2"
674+smallint :: TypeExpression ('Required ('Null 'PGInt2))
675+smallint = UnsafeTypeExpression "smallint"
676+int4 :: TypeExpression ('Required ('Null 'PGInt4))
677+int4 = UnsafeTypeExpression "int4"
678+int :: TypeExpression ('Required ('Null 'PGInt4))
679+int = UnsafeTypeExpression "int"
680+integer :: TypeExpression ('Required ('Null 'PGInt4))
681+integer = UnsafeTypeExpression "integer"
682+int8 :: TypeExpression ('Required ('Null 'PGInt8))
683+int8 = UnsafeTypeExpression "int8"
684+bigint :: TypeExpression ('Required ('Null 'PGInt8))
685+bigint = UnsafeTypeExpression "bigint"
686+numeric :: TypeExpression ('Required ('Null 'PGNumeric))
687+numeric = UnsafeTypeExpression "numeric"
688+float4 :: TypeExpression ('Required ('Null 'PGFloat4))
689+float4 = UnsafeTypeExpression "float4"
690+real :: TypeExpression ('Required ('Null 'PGFloat4))
691+real = UnsafeTypeExpression "real"
692+float8 :: TypeExpression ('Required ('Null 'PGFloat8))
693+float8 = UnsafeTypeExpression "float8"
694+doublePrecision :: TypeExpression ('Required ('Null 'PGFloat8))
695+doublePrecision = UnsafeTypeExpression "double precision"
696+serial2 :: TypeExpression ('Optional ('NotNull 'PGInt2))
697+serial2 = UnsafeTypeExpression "serial2"
698+smallserial :: TypeExpression ('Optional ('NotNull 'PGInt2))
699+smallserial = UnsafeTypeExpression "smallserial"
700+serial4 :: TypeExpression ('Optional ('NotNull 'PGInt4))
701+serial4 = UnsafeTypeExpression "serial4"
702+serial :: TypeExpression ('Optional ('NotNull 'PGInt4))
703+serial = UnsafeTypeExpression "serial"
704+serial8 :: TypeExpression ('Optional ('NotNull 'PGInt8))
705+serial8 = UnsafeTypeExpression "serial8"
706+bigserial :: TypeExpression ('Optional ('NotNull 'PGInt8))
707+bigserial = UnsafeTypeExpression "bigserial"
708+money :: TypeExpression ('Required ('Null 'PGMoney))
709+money = UnsafeTypeExpression "money"
710+text :: TypeExpression ('Required ('Null 'PGText))
711+text = UnsafeTypeExpression "text"
712+char
713+ :: KnownNat n
714+ => proxy n
715+ -> TypeExpression ('Required ('Null ('PGChar n)))
716+char (_ :: proxy n) = UnsafeTypeExpression $
717+ "char(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
718+character
719+ :: KnownNat n
720+ => proxy n
721+ -> TypeExpression ('Required ('Null ('PGChar n)))
722+character (_ :: proxy n) = UnsafeTypeExpression $
723+ "character(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
724+varchar
725+ :: KnownNat n
726+ => proxy n
727+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
728+varchar (_ :: proxy n) = UnsafeTypeExpression $
729+ "varchar(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
730+characterVarying
731+ :: KnownNat n
732+ => proxy n
733+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
734+characterVarying (_ :: proxy n) = UnsafeTypeExpression $
735+ "character varying(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
736+bytea :: TypeExpression ('Required ('Null ('PGBytea)))
737+bytea = UnsafeTypeExpression "bytea"
738+timestamp :: TypeExpression ('Required ('Null ('PGTimestamp)))
739+timestamp = UnsafeTypeExpression "timestamp"
740+timestampWithTimeZone :: TypeExpression ('Required ('Null ('PGTimestampTZ)))
741+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
742+date :: TypeExpression ('Required ('Null ('PGDate)))
743+date = UnsafeTypeExpression "date"
744+time :: TypeExpression ('Required ('Null ('PGTime)))
745+time = UnsafeTypeExpression "time"
746+timeWithTimeZone :: TypeExpression ('Required ('Null ('PGTimeTZ)))
747+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
748+interval :: TypeExpression ('Required ('Null ('PGInterval)))
749+interval = UnsafeTypeExpression "interval"
750+uuid :: TypeExpression ('Required ('Null ('PGUuid)))
751+uuid = UnsafeTypeExpression "uuid"
752+json :: TypeExpression ('Required ('Null ('PGJson)))
753+json = UnsafeTypeExpression "json"
754+jsonb :: TypeExpression ('Required ('Null ('PGJsonb)))
755+jsonb = UnsafeTypeExpression "jsonb"
756+
757+notNull
758+ :: TypeExpression ('Required ('Null ty))
759+ -> TypeExpression ('Required ('NotNull ty))
760+notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <> " NOT NULL"
761+default_
762+ :: Expression '[] '[] 'Ungrouped ('Required ty)
763+ -> TypeExpression ('Required ty)
764+ -> TypeExpression ('Optional ty)
765+default_ x ty = UnsafeTypeExpression $
766+ renderTypeExpression ty <> " DEFAULT " <> renderExpression x
767+
768+class PGTyped (ty :: PGType) where
769+ pgtype :: TypeExpression ('Required ('Null ty))
770+instance PGTyped 'PGBool where pgtype = bool
771+instance PGTyped 'PGInt2 where pgtype = int2
772+instance PGTyped 'PGInt4 where pgtype = int4
773+instance PGTyped 'PGInt8 where pgtype = int8
774+instance PGTyped 'PGNumeric where pgtype = numeric
775+instance PGTyped 'PGFloat4 where pgtype = float4
776+instance PGTyped 'PGFloat8 where pgtype = float8
777+instance PGTyped 'PGMoney where pgtype = money
778+instance PGTyped 'PGText where pgtype = text
779+instance KnownNat n => PGTyped ('PGChar n) where pgtype = char (Proxy @n)
780+instance KnownNat n => PGTyped ('PGVarChar n) where pgtype = varchar (Proxy @n)
781+instance PGTyped 'PGBytea where pgtype = bytea
782+instance PGTyped 'PGTimestamp where pgtype = timestamp
783+instance PGTyped 'PGTimestampTZ where pgtype = timestampWithTimeZone
784+instance PGTyped 'PGDate where pgtype = date
785+instance PGTyped 'PGTime where pgtype = time
786+instance PGTyped 'PGTimeTZ where pgtype = timeWithTimeZone
787+instance PGTyped 'PGInterval where pgtype = interval
788+instance PGTyped 'PGUuid where pgtype = uuid
789+instance PGTyped 'PGJson where pgtype = json
790+instance PGTyped 'PGJsonb where pgtype = jsonb
791+
792+{-----------------------------------------
793+statements
794+-----------------------------------------}
795+
796+newtype Definition
797+ (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
798+ (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
799+ = UnsafeDefinition { renderDefinition :: ByteString }
800+
801+instance Category Definition where
802+ id = UnsafeDefinition ";"
803+ dd1 . dd0 = UnsafeDefinition $
804+ renderDefinition dd0 <> " " <> renderDefinition dd1
805+
806+newtype Manipulation
807+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
808+ (params :: [ColumnType])
809+ (columns :: [(Symbol,ColumnType)])
810+ = UnsafeManipulation { renderManipulation :: ByteString }
811+
812+newtype Query
813+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
814+ (params :: [ColumnType])
815+ (columns :: [(Symbol,ColumnType)])
816+ = UnsafeQuery { renderQuery :: ByteString }
817+
818+query
819+ :: Query schema params columns
820+ -> Manipulation schema params columns
821+query q = UnsafeManipulation $ renderQuery q <> ";"
822+
823+union, unionAll, intersect, intersectAll, except, exceptAll
824+ :: Query schema params columns
825+ -> Query schema params columns
826+ -> Query schema params columns
827+q1 `union` q2 = UnsafeQuery $
828+ renderQuery q1 <> " UNION " <> renderQuery q2
829+q1 `unionAll` q2 = UnsafeQuery $
830+ renderQuery q1 <> " UNION ALL " <> renderQuery q2
831+q1 `intersect` q2 = UnsafeQuery $
832+ renderQuery q1 <> " INTERSECT " <> renderQuery q2
833+q1 `intersectAll` q2 = UnsafeQuery $
834+ renderQuery q1 <> " INTERSECT ALL " <> renderQuery q2
835+q1 `except` q2 = UnsafeQuery $
836+ renderQuery q1 <> " EXCEPT " <> renderQuery q2
837+q1 `exceptAll` q2 = UnsafeQuery $
838+ renderQuery q1 <> " EXCEPT ALL " <> renderQuery q2
839+
840+{-----------------------------------------
841+SELECT statements
842+-----------------------------------------}
843+
844+select
845+ :: SListI columns
846+ => NP (Aliased (Expression params tables grouping)) columns
847+ -> TableExpression params schema tables grouping
848+ -> Query schema params columns
849+select list tabs = UnsafeQuery $
850+ "SELECT " <> renderList list <> " " <> renderTableExpression tabs
851+ where
852+ renderList
853+ = ByteString.intercalate ", "
854+ . hcollapse
855+ . hmap (K . renderAliased renderExpression)
856+
857+selectStar
858+ :: (tables ~ '[table ::: columns])
859+ => TableExpression params schema tables 'Ungrouped
860+ -> Query schema params columns
861+selectStar tabs = UnsafeQuery $ "SELECT * " <> renderTableExpression tabs
862+
863+selectDotStar
864+ :: HasTable table tables columns
865+ => Alias table
866+ -> TableExpression params schema tables 'Ungrouped
867+ -> Query schema params columns
868+selectDotStar table tables = UnsafeQuery $
869+ "SELECT " <> renderAlias table <> ".* " <> renderTableExpression tables
870+
871+{-----------------------------------------
872+CREATE statements
873+-----------------------------------------}
874+
875+createTable
876+ :: (KnownSymbol table, SListI columns)
877+ => Alias table
878+ -> NP (Aliased TypeExpression) columns
879+ -> Definition schema (Create table columns schema)
880+createTable table columns = UnsafeDefinition $ mconcat
881+ [ "CREATE TABLE "
882+ , renderAlias table
883+ , " ("
884+ , ByteString.intercalate ", " . hcollapse $
885+ hmap (K . renderColumn) columns
886+ , ");"
887+ ]
888+ where
889+ renderColumn :: Aliased TypeExpression x -> ByteString
890+ renderColumn (ty `As` column) =
891+ renderAlias column <> " " <> renderTypeExpression ty
892+
893+{-----------------------------------------
894+DROP statements
895+-----------------------------------------}
896+
897+dropTable
898+ :: KnownSymbol table
899+ => Alias table
900+ -> Definition schema (Drop table schema)
901+dropTable table = UnsafeDefinition $ "DROP TABLE " <> renderAlias table <> ";"
902+
903+{-----------------------------------------
904+ALTER statements
905+-----------------------------------------}
906+
907+alterTable
908+ :: HasTable table schema columns0
909+ => Alias table
910+ -> AlterTable columns0 columns1
911+ -> Definition schema (Alter table schema columns1)
912+alterTable table alteration = UnsafeDefinition $
913+ "ALTER TABLE "
914+ <> renderAlias table
915+ <> " "
916+ <> renderAlterTable alteration
917+ <> ";"
918+
919+alterTableRename
920+ :: (KnownSymbol table0, KnownSymbol table1)
921+ => Alias table0
922+ -> Alias table1
923+ -> Definition schema (Rename table0 table1 schema)
924+alterTableRename table0 table1 = UnsafeDefinition $
925+ "ALTER TABLE "
926+ <> renderAlias table0
927+ <> " RENAME TO "
928+ <> renderAlias table1
929+ <> ";"
930+
931+data AlterTable
932+ (columns0 :: [(Symbol, ColumnType)])
933+ (columns1 :: [(Symbol, ColumnType)]) =
934+ UnsafeAlterTable {renderAlterTable :: ByteString}
935+
936+addColumnDefault
937+ :: KnownSymbol column
938+ => Alias column
939+ -> TypeExpression ('Optional ty)
940+ -> AlterTable columns (Create column ('Optional ty) columns)
941+addColumnDefault column ty = UnsafeAlterTable $
942+ "ADD COLUMN "
943+ <> renderAlias column
944+ <> " "
945+ <> renderTypeExpression ty
946+
947+addColumnNull
948+ :: KnownSymbol column
949+ => Alias column
950+ -> TypeExpression ('Required ('Null ty))
951+ -> AlterTable columns ((column ::: 'Required ('Null ty)) ': columns)
952+addColumnNull column ty = UnsafeAlterTable $
953+ "ADD COLUMN "
954+ <> renderAlias column
955+ <> " "
956+ <> renderTypeExpression ty
957+
958+dropColumn
959+ :: KnownSymbol column
960+ => Alias column
961+ -> AlterTable columns (Drop column columns)
962+dropColumn column = UnsafeAlterTable $
963+ "DROP COLUMN " <> renderAlias column
964+
965+renameColumn
966+ :: (KnownSymbol column0, KnownSymbol column1)
967+ => Alias column0
968+ -> Alias column1
969+ -> AlterTable columns (Rename column0 column1 columns)
970+renameColumn column0 column1 = UnsafeAlterTable $
971+ "RENAME COLUMN " <> renderAlias column0
972+ <> " TO " <> renderAlias column1
973+
974+alterColumn
975+ :: (KnownSymbol column, HasColumn column columns ty0)
976+ => Alias column
977+ -> AlterColumn ty0 ty1
978+ -> AlterTable columns (Alter column columns ty1)
979+alterColumn column alteration = UnsafeAlterTable $
980+ "ALTER COLUMN " <> renderAlias column
981+ <> " " <> renderAlterColumn alteration
982+
983+data AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
984+ UnsafeAlterColumn {renderAlterColumn :: ByteString}
985+
986+setDefault
987+ :: Expression '[] '[] 'Ungrouped ('Required ty)
988+ -> AlterColumn ('Required ty) ('Optional ty)
989+setDefault expression = UnsafeAlterColumn $
990+ "SET DEFAULT " <> renderExpression expression
991+
992+dropDefault :: AlterColumn ('Optional ty) ('Required ty)
993+dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
994+
995+setNotNull
996+ :: AlterColumn (optionality ('Null ty)) (optionality ('NotNull ty))
997+setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
998+
999+dropNotNull
1000+ :: AlterColumn (optionality ('NotNull ty)) (optionality ('Null ty))
1001+dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
1002+
1003+alterType
1004+ :: PGCast ty0 ty1
1005+ => TypeExpression (optionality (nullity ty1))
1006+ -> AlterColumn (optionality (nullity ty0)) (optionality (nullity ty1))
1007+alterType ty = UnsafeAlterColumn $ "TYPE " <> renderTypeExpression ty
1008+
1009+{-----------------------------------------
1010+INSERT statements
1011+-----------------------------------------}
1012+
1013+insertInto
1014+ :: (SListI columns, HasTable table schema columns)
1015+ => Alias table
1016+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1017+ -> Manipulation schema params '[]
1018+insertInto table expressions = UnsafeManipulation $ "INSERT INTO "
1019+ <> renderAlias table
1020+ <> " (" <> ByteString.intercalate ", " aliases
1021+ <> ") VALUES ("
1022+ <> ByteString.intercalate ", " values
1023+ <> ");"
1024+ where
1025+ aliases = hcollapse $ hmap
1026+ (\ (_ `As` name) -> K (renderAlias name))
1027+ expressions
1028+ values = hcollapse $ hmap
1029+ (\ (expression `As` _) -> K (renderExpression expression))
1030+ expressions
1031+
1032+insertIntoReturning
1033+ :: (SListI columns, SListI results, HasTable table schema columns)
1034+ => Alias table
1035+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1036+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1037+ -> Manipulation schema params results
1038+insertIntoReturning table inserts results
1039+ = UnsafeManipulation . mconcat $
1040+ [ "INSERT INTO "
1041+ , renderAlias table
1042+ , " (" <> ByteString.intercalate ", " aliases
1043+ , ") VALUES ("
1044+ , ByteString.intercalate ", " values
1045+ , ") RETURNING "
1046+ , renderList results
1047+ , ";"
1048+ ]
1049+ where
1050+ aliases = hcollapse $ hmap
1051+ (\ (_ `As` name) -> K (renderAlias name))
1052+ inserts
1053+ values = hcollapse $ hmap
1054+ (\ (expression `As` _) -> K (renderExpression expression))
1055+ inserts
1056+ renderList
1057+ = ByteString.intercalate ", "
1058+ . hcollapse
1059+ . hmap (K . renderAliased renderExpression)
1060+
1061+{-----------------------------------------
1062+UPDATE statements
1063+-----------------------------------------}
1064+
1065+data UpdateExpression params table ty
1066+ = Same
1067+ | Set (Expression params table 'Ungrouped ty)
1068+
1069+renderUpdateExpression
1070+ :: Aliased (UpdateExpression params '[table ::: columns]) column
1071+ -> Maybe ByteString
1072+renderUpdateExpression = \case
1073+ Same `As` _ -> Nothing
1074+ Set expression `As` column -> Just $ mconcat
1075+ [ renderAlias column
1076+ , " = "
1077+ , renderExpression expression
1078+ ]
1079+
1080+update
1081+ :: (HasTable table schema columns, SListI columns)
1082+ => Alias table
1083+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1084+ -> Condition params '[table ::: columns] 'Ungrouped
1085+ -> Manipulation schema params '[]
1086+update table columns wh = UnsafeManipulation $ mconcat
1087+ [ "UPDATE "
1088+ , renderAlias table
1089+ , " SET "
1090+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1091+ hmap (K . renderUpdateExpression) columns
1092+ , " WHERE ", renderExpression wh, ";"
1093+ ]
1094+
1095+updateReturning
1096+ :: (SListI columns, SListI results, HasTable table schema columns)
1097+ => Alias table
1098+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1099+ -> Condition params '[table ::: columns] 'Ungrouped
1100+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1101+ -> Manipulation schema params results
1102+updateReturning table updates wh results = UnsafeManipulation $ mconcat
1103+ [ "UPDATE "
1104+ , renderAlias table
1105+ , " SET "
1106+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1107+ hmap (K . renderUpdateExpression) updates
1108+ , " WHERE ", renderExpression wh
1109+ , " RETURNING ", renderList results
1110+ , ";"
1111+ ]
1112+ where
1113+ renderList
1114+ = ByteString.intercalate ", "
1115+ . hcollapse
1116+ . hmap (K . renderAliased renderExpression)
1117+
1118+upsertInto
1119+ :: (SListI columns, HasTable table schema columns)
1120+ => Alias table
1121+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1122+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1123+ -> Condition params '[table ::: columns] 'Ungrouped
1124+ -> Manipulation schema params '[]
1125+upsertInto table inserts updates wh = UnsafeManipulation . mconcat $
1126+ [ "INSERT INTO "
1127+ , renderAlias table
1128+ , " (" <> ByteString.intercalate ", " aliases
1129+ , ") VALUES ("
1130+ , ByteString.intercalate ", " values
1131+ , ") ON CONFLICT UPDATE "
1132+ , renderAlias table
1133+ , " SET "
1134+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1135+ hmap (K . renderUpdateExpression) updates
1136+ , " WHERE ", renderExpression wh, ";"
1137+ ] where
1138+ aliases = hcollapse $ hmap
1139+ (\ (_ `As` name) -> K (renderAlias name))
1140+ inserts
1141+ values = hcollapse $ hmap
1142+ (\ (expression `As` _) -> K (renderExpression expression))
1143+ inserts
1144+
1145+upsertIntoReturning
1146+ :: (SListI columns, SListI results, HasTable table schema columns)
1147+ => Alias table
1148+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1149+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1150+ -> Condition params '[table ::: columns] 'Ungrouped
1151+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1152+ -> Manipulation schema params results
1153+upsertIntoReturning table inserts updates wh results =
1154+ UnsafeManipulation . mconcat $
1155+ [ "INSERT INTO "
1156+ , renderAlias table
1157+ , " (" <> ByteString.intercalate ", " aliases
1158+ , ") VALUES ("
1159+ , ByteString.intercalate ", " values
1160+ , ") ON CONFLICT UPDATE "
1161+ , renderAlias table
1162+ , " SET "
1163+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1164+ hmap (K . renderUpdateExpression) updates
1165+ , " WHERE ", renderExpression wh
1166+ , " RETURNING ", renderList results
1167+ , ";"
1168+ ] where
1169+ aliases = hcollapse $ hmap
1170+ (\ (_ `As` name) -> K (renderAlias name))
1171+ inserts
1172+ values = hcollapse $ hmap
1173+ (\ (expression `As` _) -> K (renderExpression expression))
1174+ inserts
1175+ renderList
1176+ = ByteString.intercalate ", "
1177+ . hcollapse
1178+ . hmap (K . renderAliased renderExpression)
1179+
1180+{-----------------------------------------
1181+DELETE statements
1182+-----------------------------------------}
1183+
1184+deleteFrom
1185+ :: HasTable table schema columns
1186+ => Alias table
1187+ -> Condition params '[table ::: columns] 'Ungrouped
1188+ -> Manipulation schema params '[]
1189+deleteFrom table wh = UnsafeManipulation $ mconcat
1190+ [ "DELETE FROM "
1191+ , renderAlias table
1192+ , " WHERE ", renderExpression wh, ";"
1193+ ]