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