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