@@ -0,0 +1,1666 @@
1+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
2+{-# LANGUAGE
3+ DataKinds
4+ , DeriveGeneric
5+ , DeriveDataTypeable
6+ , FlexibleContexts
7+ , FlexibleInstances
8+ , FunctionalDependencies
9+ , GADTs
10+ , GeneralizedNewtypeDeriving
11+ , LambdaCase
12+ , MagicHash
13+ , OverloadedStrings
14+ , PolyKinds
15+ , RankNTypes
16+ , ScopedTypeVariables
17+ , StandaloneDeriving
18+ , TypeApplications
19+ , TypeFamilies
20+ , TypeInType
21+ , TypeOperators
22+ , UndecidableInstances
23+#-}
24+
25+module Squeal.PostgreSQL.Statement
26+ ( -- * Column Expressions
27+ Expression (UnsafeExpression, renderExpression)
28+ , HasParameter (param)
29+ , HasColumn (getColumn)
30+ , Column (Column)
31+ , renderColumn
32+ -- ** Default
33+ , def
34+ , unDef
35+ -- ** Null
36+ , null_
37+ , unNull
38+ , coalesce
39+ , fromNull
40+ , isNull
41+ , isn'tNull
42+ , matchNull
43+ , nullIf
44+ -- ** Functions
45+ , unsafeBinaryOp
46+ , unsafeUnaryOp
47+ , unsafeFunction
48+ , atan2_
49+ , cast
50+ , div_
51+ , mod_
52+ , trunc
53+ , round_
54+ , ceiling_
55+ , greatest
56+ , least
57+ -- ** Conditions
58+ , Condition
59+ , true
60+ , false
61+ , not_
62+ , (&&*)
63+ , (||*)
64+ , caseWhenThenElse
65+ , ifThenElse
66+ , (==*)
67+ , (/=*)
68+ , (>=*)
69+ , (*<)
70+ , (*<=)
71+ , (>*)
72+ -- ** Time
73+ , currentDate
74+ , currentTime
75+ , currentTimestamp
76+ , localTime
77+ , localTimestamp
78+ -- ** Aggregation
79+ , unsafeAggregate
80+ , unsafeAggregateDistinct
81+ , sum_
82+ , sumDistinct
83+ , PGAvg (avg, avgDistinct)
84+ , bitAnd
85+ , bitOr
86+ , boolAnd
87+ , boolOr
88+ , countStar
89+ , countDistinctStar
90+ , count
91+ , every
92+ , jsonAgg
93+ , jsonbAgg
94+ , jsonObjectAgg
95+ , jsonbObjectAgg
96+ , max_
97+ , min_
98+ -- * Tables
99+ , Table (UnsafeTable, renderTable)
100+ , HasTable (getTable)
101+ -- ** Froms and Joins
102+ , FromClause (..)
103+ , renderFromClause
104+ -- ** Table Expressions
105+ , TableExpression (..)
106+ , renderTableExpression
107+ , from
108+ , where_
109+ , group
110+ , having
111+ , orderBy
112+ , limit
113+ , offset
114+ , By (By, By2)
115+ , renderBy
116+ , GroupByClause (NoGroups, Group)
117+ , renderGroupByClause
118+ , HavingClause (NoHaving, Having)
119+ , renderHavingClause
120+ , GroupedBy (getGroup1, getGroup2)
121+ , SortExpression (..)
122+ , renderSortExpression
123+ -- * TypeExpression
124+ , TypeExpression (UnsafeTypeExpression, renderTypeExpression)
125+ , PGTyped (pgtype)
126+ , bool
127+ , int2
128+ , smallint
129+ , int4
130+ , int
131+ , integer
132+ , int8
133+ , bigint
134+ , numeric
135+ , float4
136+ , real
137+ , float8
138+ , doublePrecision
139+ , serial2
140+ , smallserial
141+ , serial4
142+ , serial
143+ , serial8
144+ , bigserial
145+ , text
146+ , char
147+ , character
148+ , varchar
149+ , characterVarying
150+ , bytea
151+ , timestamp
152+ , timestampWithTimeZone
153+ , date
154+ , time
155+ , timeWithTimeZone
156+ , interval
157+ , uuid
158+ , json
159+ , jsonb
160+ , notNull
161+ , default_
162+ -- * Statements
163+ -- ** Queries
164+ , Query (UnsafeQuery, renderQuery)
165+ , select
166+ , selectDistinct
167+ , selectStar
168+ , selectDistinctStar
169+ , selectDotStar
170+ , selectDistinctDotStar
171+ , union
172+ , unionAll
173+ , intersect
174+ , intersectAll
175+ , except
176+ , exceptAll
177+ -- ** Data Definition Language
178+ , Definition (UnsafeDefinition, renderDefinition)
179+ , createTable
180+ , TableConstraint (UnsafeTableConstraint, renderTableConstraint)
181+ , check
182+ , unique
183+ , primaryKey
184+ , foreignKey
185+ , OnDelete (OnDeleteNoAction, OnDeleteRestrict, OnDeleteCascade)
186+ , renderOnDelete
187+ , OnUpdate (OnUpdateNoAction, OnUpdateRestrict, OnUpdateCascade)
188+ , renderOnUpdate
189+ , dropTable
190+ , alterTable
191+ , alterTableRename
192+ , AlterTable (UnsafeAlterTable, renderAlterTable)
193+ , addColumnDefault
194+ , addColumnNull
195+ , dropColumn
196+ , renameColumn
197+ , alterColumn
198+ , AlterColumn (UnsafeAlterColumn, renderAlterColumn)
199+ , setDefault
200+ , dropDefault
201+ , setNotNull
202+ , dropNotNull
203+ , alterType
204+ -- ** Data Manipulation Language
205+ , Manipulation (UnsafeManipulation, renderManipulation)
206+ , queryStatement
207+ , insertInto
208+ , ValuesClause (Values, ValuesQuery)
209+ , renderValuesClause
210+ , ReturningClause (ReturningStar, Returning)
211+ , renderReturningClause
212+ , ConflictClause (Conflict, OnConflictDoNothing, OnConflictDoUpdate)
213+ , renderConflictClause
214+ , UpdateExpression (Same, Set)
215+ , renderUpdateExpression
216+ , update
217+ , deleteFrom
218+ ) where
219+
220+import Control.Category
221+import Control.DeepSeq
222+import Data.ByteString (ByteString)
223+import Data.Data (Data)
224+import Data.Foldable
225+import Data.Maybe
226+import Data.Monoid hiding (All)
227+import Data.Ratio
228+import Data.String
229+import Data.Word
230+import Generics.SOP hiding (from)
231+import GHC.Exts
232+import GHC.OverloadedLabels
233+import GHC.TypeLits
234+import Prelude hiding (id, (.))
235+
236+import qualified GHC.Generics as GHC
237+import qualified Data.ByteString as ByteString
238+
239+import Squeal.PostgreSQL.Schema
240+
241+{-----------------------------------------
242+column expressions
243+-----------------------------------------}
244+
245+{- | `Expression`s are used in a variety of contexts,
246+such as in the target list of the `select` command,
247+as new column values in `insertInto` or `update`,
248+or in search `Condition`s in a number of commands.
249+
250+The expression syntax allows the calculation of
251+values from primitive expression using arithmetic, logical,
252+and other operations.
253+-}
254+newtype Expression
255+ (params :: [ColumnType])
256+ (tables :: TablesType)
257+ (grouping :: Grouping)
258+ (ty :: ColumnType)
259+ = UnsafeExpression { renderExpression :: ByteString }
260+ deriving (GHC.Generic,Show,Eq,Ord,Data,NFData)
261+
262+{- | A `HasParameter` constraint is used to indicate a value that is
263+supplied externally to a SQL statement.
264+`Squeal.PostgreSQL.PQ.manipulateParams`,
265+`Squeal.PostgreSQL.PQ.queryParams` and
266+`Squeal.PostgreSQL.PQ.traversePrepared` support specifying data values
267+separately from the SQL command string, in which case `param`s are used to
268+refer to the out-of-line data values.
269+-}
270+class (PGTyped (BaseType ty), KnownNat n)
271+ => HasParameter (n :: Nat) params ty | n params -> ty where
272+ param :: proxy n -> Expression params tables grouping ty
273+ param _ = UnsafeExpression $ parenthesized $
274+ "$" <> renderNat (Proxy @n) <+> "::"
275+ <+> renderTypeExpression (pgtype @(BaseType ty))
276+instance {-# OVERLAPPING #-} PGTyped (BaseType ty1)
277+ => HasParameter 1 (ty1:tys) ty1
278+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) params ty)
279+ => HasParameter n (ty' : params) ty
280+
281+{- | A `HasColumn` constraint indicates an unqualified column reference.
282+`getColumn` can only be unambiguous when the `TableExpression` the column
283+references is unique, in which case the column may be referenced using
284+@-XOverloadedLabels@. Otherwise, combined with a `HasTable` constraint, the
285+qualified column reference operator `!` may be used.
286+-}
287+class KnownSymbol column => HasColumn column columns ty
288+ | column columns -> ty where
289+ getColumn
290+ :: HasUnique table tables columns
291+ => Alias column
292+ -> Expression params tables 'Ungrouped ty
293+ getColumn column = UnsafeExpression $ renderAlias column
294+instance {-# OVERLAPPING #-} KnownSymbol column
295+ => HasColumn column ((column ::: optionality ty) ': tys) ('Required ty)
296+instance {-# OVERLAPPABLE #-} (KnownSymbol column, HasColumn column table ty)
297+ => HasColumn column (ty' ': table) ty
298+
299+instance (HasColumn column columns ty, HasUnique table tables columns)
300+ => IsLabel column (Expression params tables 'Ungrouped ty) where
301+ fromLabel = getColumn (Alias @column)
302+
303+instance (HasTable table tables columns, HasColumn column columns ty)
304+ => IsTableColumn table column (Expression params tables 'Ungrouped ty) where
305+ table ! column = UnsafeExpression $
306+ renderAlias table <> "." <> renderAlias column
307+
308+data Column
309+ (columns :: ColumnsType)
310+ (columnty :: (Symbol,ColumnType))
311+ where
312+ Column
313+ :: HasColumn column columns ty
314+ => Alias column
315+ -> Column columns (column ::: ty)
316+deriving instance Show (Column columns columnty)
317+deriving instance Eq (Column columns columnty)
318+deriving instance Ord (Column columns columnty)
319+
320+-- | >>> renderExpression def
321+-- "DEFAULT"
322+def :: Expression params '[] 'Ungrouped ('Optional (nullity ty))
323+def = UnsafeExpression "DEFAULT"
324+
325+-- | >>> renderExpression $ unDef false
326+-- "FALSE"
327+unDef
328+ :: Expression params '[] 'Ungrouped ('Required (nullity ty))
329+ -> Expression params '[] 'Ungrouped ('Optional (nullity ty))
330+unDef = UnsafeExpression . renderExpression
331+
332+-- | analagous to `Nothing`
333+--
334+-- >>> renderExpression $ null_
335+-- "NULL"
336+null_ :: Expression params tables grouping (optionality ('Null ty))
337+null_ = UnsafeExpression "NULL"
338+
339+-- | analagous to `Just`
340+--
341+-- >>> renderExpression $ unNull true
342+-- "TRUE"
343+unNull
344+ :: Expression params tables grouping (optionality ('NotNull ty))
345+ -> Expression params tables grouping (optionality ('Null ty))
346+unNull = UnsafeExpression . renderExpression
347+
348+-- | return the leftmost value which is not NULL
349+--
350+-- >>> renderExpression $ coalesce [null_, unNull true] false
351+-- "COALESCE(NULL, TRUE, FALSE)"
352+coalesce
353+ :: [Expression params tables grouping ('Required ('Null ty))]
354+ -> Expression params tables grouping ('Required ('NotNull ty))
355+ -> Expression params tables grouping ('Required ('NotNull ty))
356+coalesce nullxs notNullx = UnsafeExpression $
357+ "COALESCE" <> parenthesized (commaSeparated
358+ ((renderExpression <$> nullxs) <> [renderExpression notNullx]))
359+
360+-- | analagous to `fromMaybe` using @COALESCE@
361+--
362+-- >>> renderExpression $ fromNull true null_
363+-- "COALESCE(NULL, TRUE)"
364+fromNull
365+ :: Expression params tables grouping ('Required ('NotNull ty))
366+ -> Expression params tables grouping ('Required ('Null ty))
367+ -> Expression params tables grouping ('Required ('NotNull ty))
368+fromNull notNullx nullx = coalesce [nullx] notNullx
369+
370+-- | >>> renderExpression $ null_ & isNull
371+-- "NULL IS NULL"
372+isNull
373+ :: Expression params tables grouping ('Required ('Null ty))
374+ -> Condition params tables grouping
375+isNull x = UnsafeExpression $ renderExpression x <+> "IS NULL"
376+
377+-- | >>> renderExpression $ null_ & isn'tNull
378+-- "NULL IS NOT NULL"
379+isn'tNull
380+ :: Expression params tables grouping ('Required ('Null ty))
381+ -> Condition params tables grouping
382+isn'tNull x = UnsafeExpression $ renderExpression x <+> "IS NOT NULL"
383+
384+-- | analagous to `maybe` using @IS NULL@
385+--
386+-- >>> renderExpression $ matchNull true not null_
387+-- "CASE WHEN NULL IS NULL THEN TRUE ELSE (NOT TRUE) END"
388+matchNull
389+ :: Expression params tables grouping ('Required nullty)
390+ -> ( Expression params tables grouping ('Required ('NotNull ty))
391+ -> Expression params tables grouping ('Required nullty) )
392+ -> Expression params tables grouping ('Required ('Null ty))
393+ -> Expression params tables grouping ('Required nullty)
394+matchNull y f x = ifThenElse (isNull x) y
395+ (f (UnsafeExpression (renderExpression y)))
396+
397+-- | left inverse to `fromNull`
398+--
399+-- >>> renderExpression $ nullIf false (fromNull false null_)
400+-- "NULL IF (FALSE, COALESCE(NULL, FALSE))"
401+nullIf
402+ :: Expression params tables grouping ('Required ('NotNull ty))
403+ -> Expression params tables grouping ('Required ('NotNull ty))
404+ -> Expression params tables grouping ('Required ('Null ty))
405+nullIf x y = UnsafeExpression $ "NULL IF" <+> parenthesized
406+ (renderExpression x <> ", " <> renderExpression y)
407+
408+-- | >>> renderExpression $ greatest @'[_] currentTimestamp [param (Proxy @1)]
409+-- "GREATEST(CURRENT_TIMESTAMP, ($1 :: timestamp with time zone))"
410+greatest
411+ :: Expression params tables grouping ('Required nullty)
412+ -> [Expression params tables grouping ('Required nullty)]
413+ -> Expression params table grouping ('Required nullty)
414+greatest x xs = UnsafeExpression $ "GREATEST("
415+ <> commaSeparated (renderExpression <$> (x:xs)) <> ")"
416+
417+-- | >>> renderExpression $ least currentTimestamp [null_]
418+-- "LEAST(CURRENT_TIMESTAMP, NULL)"
419+least
420+ :: Expression params tables grouping ('Required nullty)
421+ -> [Expression params tables grouping ('Required nullty)]
422+ -> Expression params table grouping ('Required nullty)
423+least x xs = UnsafeExpression $ "LEAST("
424+ <> commaSeparated (renderExpression <$> (x:xs)) <> ")"
425+
426+unsafeBinaryOp
427+ :: ByteString
428+ -> Expression params tables grouping ('Required ty0)
429+ -> Expression params tables grouping ('Required ty1)
430+ -> Expression params tables grouping ('Required ty2)
431+unsafeBinaryOp op x y = UnsafeExpression $ parenthesized $
432+ renderExpression x <+> op <+> renderExpression y
433+
434+unsafeUnaryOp
435+ :: ByteString
436+ -> Expression params tables grouping ('Required ty0)
437+ -> Expression params tables grouping ('Required ty1)
438+unsafeUnaryOp op x = UnsafeExpression $ parenthesized $
439+ op <+> renderExpression x
440+
441+unsafeFunction
442+ :: ByteString
443+ -> Expression params tables grouping ('Required xty)
444+ -> Expression params tables grouping ('Required yty)
445+unsafeFunction fun x = UnsafeExpression $
446+ fun <> parenthesized (renderExpression x)
447+
448+instance PGNum ty
449+ => Num (Expression params tables grouping ('Required (nullity ty))) where
450+ (+) = unsafeBinaryOp "+"
451+ (-) = unsafeBinaryOp "-"
452+ (*) = unsafeBinaryOp "*"
453+ abs = unsafeFunction "abs"
454+ signum = unsafeFunction "sign"
455+ fromInteger
456+ = UnsafeExpression
457+ . fromString
458+ . show
459+
460+instance (PGNum ty, PGFloating ty) => Fractional
461+ (Expression params tables grouping ('Required (nullity ty))) where
462+ (/) = unsafeBinaryOp "/"
463+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
464+
465+instance (PGNum ty, PGFloating ty) => Floating
466+ (Expression params tables grouping ('Required (nullity ty))) where
467+ pi = UnsafeExpression "pi()"
468+ exp = unsafeFunction "exp"
469+ log = unsafeFunction "ln"
470+ sqrt = unsafeFunction "sqrt"
471+ b ** x = UnsafeExpression $
472+ "power(" <> renderExpression b <> ", " <> renderExpression x <> ")"
473+ logBase b y = log y / log b
474+ sin = unsafeFunction "sin"
475+ cos = unsafeFunction "cos"
476+ tan = unsafeFunction "tan"
477+ asin = unsafeFunction "asin"
478+ acos = unsafeFunction "acos"
479+ atan = unsafeFunction "atan"
480+ sinh x = (exp x - exp (-x)) / 2
481+ cosh x = (exp x + exp (-x)) / 2
482+ tanh x = sinh x / cosh x
483+ asinh x = log (x + sqrt (x*x + 1))
484+ acosh x = log (x + sqrt (x*x - 1))
485+ atanh x = log ((1 + x) / (1 - x)) / 2
486+
487+atan2_
488+ :: PGFloating float
489+ => Expression params tables grouping ('Required (nullity float))
490+ -> Expression params tables grouping ('Required (nullity float))
491+ -> Expression params tables grouping ('Required (nullity float))
492+atan2_ y x = UnsafeExpression $
493+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
494+
495+cast
496+ :: TypeExpression ('Required ('Null ty1))
497+ -> Expression params tables grouping ('Required (nullity ty0))
498+ -> Expression params tables grouping ('Required (nullity ty1))
499+cast ty x = UnsafeExpression $ parenthesized $
500+ renderExpression x <+> "::" <+> renderTypeExpression ty
501+
502+div_
503+ :: PGIntegral int
504+ => Expression params tables grouping ('Required (nullity int))
505+ -> Expression params tables grouping ('Required (nullity int))
506+ -> Expression params tables grouping ('Required (nullity int))
507+div_ = unsafeBinaryOp "/"
508+
509+mod_
510+ :: PGIntegral int
511+ => Expression params tables grouping ('Required (nullity int))
512+ -> Expression params tables grouping ('Required (nullity int))
513+ -> Expression params tables grouping ('Required (nullity int))
514+mod_ = unsafeBinaryOp "%"
515+
516+trunc
517+ :: PGFloating frac
518+ => Expression params tables grouping ('Required (nullity frac))
519+ -> Expression params tables grouping ('Required (nullity frac))
520+trunc = unsafeFunction "trunc"
521+
522+round_
523+ :: PGFloating frac
524+ => Expression params tables grouping ('Required (nullity frac))
525+ -> Expression params tables grouping ('Required (nullity frac))
526+round_ = unsafeFunction "round"
527+
528+ceiling_
529+ :: PGFloating frac
530+ => Expression params tables grouping ('Required (nullity frac))
531+ -> Expression params tables grouping ('Required (nullity frac))
532+ceiling_ = unsafeFunction "ceiling"
533+
534+instance IsString
535+ (Expression params tables grouping ('Required (nullity 'PGtext))) where
536+ fromString str = UnsafeExpression $
537+ "E\'" <> fromString (escape =<< str) <> "\'"
538+ where
539+ escape = \case
540+ '\NUL' -> "\\0"
541+ '\'' -> "''"
542+ '"' -> "\\\""
543+ '\b' -> "\\b"
544+ '\n' -> "\\n"
545+ '\r' -> "\\r"
546+ '\t' -> "\\t"
547+ '\\' -> "\\\\"
548+ c -> [c]
549+
550+type Condition params tables grouping =
551+ Expression params tables grouping ('Required ('NotNull 'PGbool))
552+
553+true :: Condition params tables grouping
554+true = UnsafeExpression "TRUE"
555+
556+false :: Condition params tables grouping
557+false = UnsafeExpression "FALSE"
558+
559+not_
560+ :: Condition params tables grouping
561+ -> Condition params tables grouping
562+not_ = unsafeUnaryOp "NOT"
563+
564+(&&*)
565+ :: Condition params tables grouping
566+ -> Condition params tables grouping
567+ -> Condition params tables grouping
568+(&&*) = unsafeBinaryOp "AND"
569+
570+(||*)
571+ :: Condition params tables grouping
572+ -> Condition params tables grouping
573+ -> Condition params tables grouping
574+(||*) = unsafeBinaryOp "OR"
575+
576+caseWhenThenElse
577+ :: [ ( Condition params tables grouping
578+ , Expression params tables grouping ('Required ty)
579+ ) ]
580+ -> Expression params tables grouping ('Required ty)
581+ -> Expression params tables grouping ('Required ty)
582+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
583+ [ "CASE"
584+ , mconcat
585+ [ mconcat
586+ [ " WHEN ", renderExpression when_
587+ , " THEN ", renderExpression then_
588+ ]
589+ | (when_,then_) <- whenThens
590+ ]
591+ , " ELSE ", renderExpression else_
592+ , " END"
593+ ]
594+
595+ifThenElse
596+ :: Condition params tables grouping
597+ -> Expression params tables grouping ('Required ty)
598+ -> Expression params tables grouping ('Required ty)
599+ -> Expression params tables grouping ('Required ty)
600+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
601+
602+(==*)
603+ :: Expression params tables grouping ('Required (nullity ty))
604+ -> Expression params tables grouping ('Required (nullity ty))
605+ -> Expression params tables grouping ('Required (nullity 'PGbool))
606+(==*) = unsafeBinaryOp "="
607+infix 4 ==*
608+
609+(/=*)
610+ :: Expression params tables grouping ('Required (nullity ty))
611+ -> Expression params tables grouping ('Required (nullity ty))
612+ -> Expression params tables grouping ('Required (nullity 'PGbool))
613+(/=*) = unsafeBinaryOp "<>"
614+infix 4 /=*
615+
616+(>=*)
617+ :: Expression params tables grouping ('Required (nullity ty))
618+ -> Expression params tables grouping ('Required (nullity ty))
619+ -> Expression params tables grouping ('Required (nullity 'PGbool))
620+(>=*) = unsafeBinaryOp ">="
621+infix 4 >=*
622+
623+(*<)
624+ :: Expression params tables grouping ('Required (nullity ty))
625+ -> Expression params tables grouping ('Required (nullity ty))
626+ -> Expression params tables grouping ('Required (nullity 'PGbool))
627+(*<) = unsafeBinaryOp "<"
628+infix 4 *<
629+
630+(*<=)
631+ :: Expression params tables grouping ('Required (nullity ty))
632+ -> Expression params tables grouping ('Required (nullity ty))
633+ -> Expression params tables grouping ('Required (nullity 'PGbool))
634+(*<=) = unsafeBinaryOp "<="
635+infix 4 *<=
636+
637+(>*)
638+ :: Expression params tables grouping ('Required (nullity ty))
639+ -> Expression params tables grouping ('Required (nullity ty))
640+ -> Expression params tables grouping ('Required (nullity 'PGbool))
641+(>*) = unsafeBinaryOp ">"
642+infix 4 >*
643+
644+currentDate
645+ :: Expression params tables grouping ('Required (nullity 'PGdate))
646+currentDate = UnsafeExpression "CURRENT_DATE"
647+
648+currentTime
649+ :: Expression params tables grouping ('Required (nullity 'PGtimetz))
650+currentTime = UnsafeExpression "CURRENT_TIME"
651+
652+currentTimestamp
653+ :: Expression params tables grouping ('Required (nullity 'PGtimestamptz))
654+currentTimestamp = UnsafeExpression "CURRENT_TIMESTAMP"
655+
656+localTime
657+ :: Expression params tables grouping ('Required (nullity 'PGtime))
658+localTime = UnsafeExpression "LOCALTIME"
659+
660+localTimestamp
661+ :: Expression params tables grouping ('Required (nullity 'PGtimestamp))
662+localTimestamp = UnsafeExpression "LOCALTIMESTAMP"
663+
664+data SortExpression params tables grouping where
665+ Asc
666+ :: Expression params tables grouping ('Required ('NotNull ty))
667+ -> SortExpression params tables grouping
668+ Desc
669+ :: Expression params tables grouping ('Required ('NotNull ty))
670+ -> SortExpression params tables grouping
671+ AscNullsFirst
672+ :: Expression params tables grouping ('Required ('Null ty))
673+ -> SortExpression params tables grouping
674+ AscNullsLast
675+ :: Expression params tables grouping ('Required ('Null ty))
676+ -> SortExpression params tables grouping
677+ DescNullsFirst
678+ :: Expression params tables grouping ('Required ('Null ty))
679+ -> SortExpression params tables grouping
680+ DescNullsLast
681+ :: Expression params tables grouping ('Required ('Null ty))
682+ -> SortExpression params tables grouping
683+deriving instance Show (SortExpression params tables grouping)
684+
685+renderSortExpression :: SortExpression params tables grouping -> ByteString
686+renderSortExpression = \case
687+ Asc expression -> renderExpression expression <+> "ASC"
688+ Desc expression -> renderExpression expression <+> "DESC"
689+ AscNullsFirst expression -> renderExpression expression
690+ <+> "ASC NULLS FIRST"
691+ DescNullsFirst expression -> renderExpression expression
692+ <+> "DESC NULLS FIRST"
693+ AscNullsLast expression -> renderExpression expression <+> "ASC NULLS LAST"
694+ DescNullsLast expression -> renderExpression expression <+> "DESC NULLS LAST"
695+
696+
697+{-----------------------------------------
698+aggregation
699+-----------------------------------------}
700+
701+data By
702+ (tables :: TablesType)
703+ (by :: (Symbol,Symbol)) where
704+ By
705+ :: (tables ~ '[table ::: columns], HasColumn column columns ty)
706+ => Alias column
707+ -> By tables '(table, column)
708+ By2
709+ :: (HasTable table tables columns, HasColumn column columns ty)
710+ => (Alias table, Alias column)
711+ -> By tables '(table, column)
712+deriving instance Show (By tables by)
713+deriving instance Eq (By tables by)
714+deriving instance Ord (By tables by)
715+
716+renderBy :: By tables tabcolty -> ByteString
717+renderBy = \case
718+ By column -> renderAlias column
719+ By2 (table, column) -> renderAlias table <> "." <> renderAlias column
720+
721+data GroupByClause tables grouping where
722+ NoGroups :: GroupByClause tables 'Ungrouped
723+ Group
724+ :: SListI bys
725+ => NP (By tables) bys
726+ -> GroupByClause tables ('Grouped bys)
727+
728+renderGroupByClause :: GroupByClause tables grouping -> ByteString
729+renderGroupByClause = \case
730+ NoGroups -> ""
731+ Group Nil -> ""
732+ Group bys -> " GROUP BY" <+> renderCommaSeparated renderBy bys
733+
734+data HavingClause params tables grouping where
735+ NoHaving :: HavingClause params tables 'Ungrouped
736+ Having
737+ :: [Condition params tables ('Grouped bys)]
738+ -> HavingClause params tables ('Grouped bys)
739+deriving instance Show (HavingClause params tables grouping)
740+deriving instance Eq (HavingClause params tables grouping)
741+deriving instance Ord (HavingClause params tables grouping)
742+
743+renderHavingClause :: HavingClause params tables grouping -> ByteString
744+renderHavingClause = \case
745+ NoHaving -> ""
746+ Having [] -> ""
747+ Having conditions ->
748+ " HAVING" <+> commaSeparated (renderExpression <$> conditions)
749+
750+class (KnownSymbol table, KnownSymbol column)
751+ => GroupedBy table column bys where
752+ getGroup1
753+ :: (tables ~ '[table ::: columns], HasColumn column columns ty)
754+ => Alias column
755+ -> Expression params tables ('Grouped bys) ty
756+ getGroup1 column = UnsafeExpression $ renderAlias column
757+ getGroup2
758+ :: (HasTable table tables columns, HasColumn column columns ty)
759+ => Alias table
760+ -> Alias column
761+ -> Expression params tables ('Grouped bys) ty
762+ getGroup2 table column = UnsafeExpression $
763+ renderAlias table <> "." <> renderAlias column
764+instance {-# OVERLAPPING #-} (KnownSymbol table, KnownSymbol column)
765+ => GroupedBy table column ('(table,column) ': bys)
766+instance {-# OVERLAPPABLE #-}
767+ ( KnownSymbol table
768+ , KnownSymbol column
769+ , GroupedBy table column bys
770+ ) => GroupedBy table column (tabcol ': bys)
771+
772+instance
773+ ( HasUnique table tables columns
774+ , HasColumn column columns ty
775+ , GroupedBy table column bys
776+ ) => IsLabel column
777+ (Expression params tables ('Grouped bys) ty) where
778+ fromLabel = getGroup1 (Alias @column)
779+
780+instance
781+ ( HasTable table tables columns
782+ , HasColumn column columns ty
783+ , GroupedBy table column bys
784+ ) => IsTableColumn table column
785+ (Expression params tables ('Grouped bys) ty) where (!) = getGroup2
786+
787+-- data AllOrDistinct = All | Distinct
788+-- deriving (GHC.Generic,Show,Eq,Ord,Data)
789+-- instance NFData AllOrDistinct
790+
791+-- renderAllOrDistinct :: AllOrDistinct -> ByteString
792+-- renderAllOrDistinct = \case
793+-- All -> "ALL"
794+-- Distinct -> "DISTINCT"
795+
796+unsafeAggregate, unsafeAggregateDistinct
797+ :: ByteString
798+ -> Expression params tables 'Ungrouped ('Required xty)
799+ -> Expression params tables ('Grouped bys) ('Required yty)
800+unsafeAggregate fun x = UnsafeExpression $ mconcat
801+ [fun, "(", renderExpression x, ")"]
802+unsafeAggregateDistinct fun x = UnsafeExpression $ mconcat
803+ [fun, "(DISTINCT ", renderExpression x, ")"]
804+
805+sum_, sumDistinct
806+ :: PGNum ty
807+ => Expression params tables 'Ungrouped ('Required (nullity ty))
808+ -> Expression params tables ('Grouped bys) ('Required (nullity ty))
809+sum_ = unsafeAggregate "sum"
810+sumDistinct = unsafeAggregateDistinct "sum"
811+
812+class PGAvg ty avg | ty -> avg where
813+ avg, avgDistinct
814+ :: Expression params tables 'Ungrouped ('Required (nullity ty))
815+ -> Expression params tables ('Grouped bys) ('Required (nullity avg))
816+ avg = unsafeAggregate "avg"
817+ avgDistinct = unsafeAggregateDistinct "avg"
818+instance PGAvg 'PGint2 'PGnumeric
819+instance PGAvg 'PGint4 'PGnumeric
820+instance PGAvg 'PGint8 'PGnumeric
821+instance PGAvg 'PGnumeric 'PGnumeric
822+instance PGAvg 'PGfloat4 'PGfloat8
823+instance PGAvg 'PGfloat8 'PGfloat8
824+instance PGAvg 'PGinterval 'PGinterval
825+
826+bitAnd, bitOr
827+ :: PGIntegral int
828+ => Expression params tables 'Ungrouped ('Required (nullity int))
829+ -> Expression params tables ('Grouped bys) ('Required (nullity int))
830+bitAnd = unsafeAggregate "bit_and"
831+bitOr = unsafeAggregate "bit_or"
832+
833+boolAnd, boolOr
834+ :: Expression params tables 'Ungrouped ('Required (nullity 'PGbool))
835+ -> Expression params tables ('Grouped bys) ('Required (nullity 'PGbool))
836+boolAnd = unsafeAggregate "bool_and"
837+boolOr = unsafeAggregate "bool_or"
838+
839+countStar, countDistinctStar
840+ :: Expression params table ('Grouped bys) ('Required ('NotNull 'PGint8))
841+countStar = UnsafeExpression $ "count(*)"
842+countDistinctStar = UnsafeExpression $ "count(DISTINCT *)"
843+
844+count
845+ :: Expression params tables 'Ungrouped ('Required ty)
846+ -> Expression params tables ('Grouped bys) ('Required ('NotNull 'PGint8))
847+count = unsafeAggregate "count"
848+
849+every
850+ :: Expression params tables 'Ungrouped ('Required (nullity 'PGbool))
851+ -> Expression params tables ('Grouped bys) ('Required (nullity 'PGbool))
852+every = unsafeAggregate "every"
853+
854+jsonAgg
855+ :: Expression params tables 'Ungrouped ('Required ty)
856+ -> Expression params tables ('Grouped bys) ('Required ('NotNull 'PGjson))
857+jsonAgg = unsafeAggregate "json_agg"
858+
859+jsonbAgg
860+ :: Expression params tables 'Ungrouped ('Required ty)
861+ -> Expression params tables ('Grouped bys) ('Required ('NotNull 'PGjsonb))
862+jsonbAgg = unsafeAggregate "jsonb_agg"
863+
864+jsonObjectAgg
865+ :: Expression params tables 'Ungrouped ('Required ('NotNull keyty))
866+ -> Expression params tables 'Ungrouped ('Required valty)
867+ -> Expression params tables ('Grouped bys) ('Required ('NotNull 'PGjson))
868+jsonObjectAgg k v = UnsafeExpression $
869+ "json_object_agg(" <> renderExpression k
870+ <> ", " <> renderExpression v <> ")"
871+
872+jsonbObjectAgg
873+ :: Expression params tables 'Ungrouped ('Required ('NotNull keyty))
874+ -> Expression params tables 'Ungrouped ('Required valty)
875+ -> Expression params tables ('Grouped bys) ('Required ('NotNull 'PGjsonb))
876+jsonbObjectAgg k v = UnsafeExpression $
877+ "jsonb_object_agg("
878+ <> renderExpression k <> ", " <> renderExpression v <> ")"
879+
880+max_, min_
881+ :: Expression params tables 'Ungrouped ('Required (nullity ty))
882+ -> Expression params tables ('Grouped bys) ('Required (nullity ty))
883+max_ = unsafeAggregate "max"
884+min_ = unsafeAggregate "min"
885+
886+{-----------------------------------------
887+tables
888+-----------------------------------------}
889+
890+newtype Table
891+ (schema :: TablesType)
892+ (columns :: ColumnsType)
893+ = UnsafeTable { renderTable :: ByteString }
894+ deriving (GHC.Generic,Show,Eq,Ord,Data,NFData)
895+
896+class KnownSymbol table => HasTable table tables columns
897+ | table tables -> columns where
898+ getTable :: Alias table -> Table tables columns
899+ getTable table = UnsafeTable $ renderAlias table
900+instance {-# OVERLAPPING #-} KnownSymbol table
901+ => HasTable table ((table ::: columns) ': tables) columns
902+instance {-# OVERLAPPABLE #-}
903+ (KnownSymbol table, HasTable table schema columns)
904+ => HasTable table (table' ': schema) columns
905+
906+instance HasTable table schema columns
907+ => IsLabel table (Table schema columns) where
908+ fromLabel = getTable (Alias @table)
909+
910+data FromClause params schema tables where
911+ Table
912+ :: Aliased (Table schema) table
913+ -> FromClause params schema '[table]
914+ Subquery
915+ :: Aliased (Query schema params) table
916+ -> FromClause params schema '[table]
917+ CrossJoin
918+ :: FromClause params schema right
919+ -> FromClause params schema left
920+ -> FromClause params schema (Join left right)
921+ InnerJoin
922+ :: FromClause params schema right
923+ -> Condition params (Join left right) 'Ungrouped
924+ -> FromClause params schema left
925+ -> FromClause params schema (Join left right)
926+ LeftOuterJoin
927+ :: FromClause params schema right
928+ -> Condition params (Join left right) 'Ungrouped
929+ -> FromClause params schema left
930+ -> FromClause params schema (Join left (NullifyTables right))
931+ RightOuterJoin
932+ :: FromClause params schema right
933+ -> Condition params (Join left right) 'Ungrouped
934+ -> FromClause params schema left
935+ -> FromClause params schema (Join (NullifyTables left) right)
936+ FullOuterJoin
937+ :: FromClause params schema right
938+ -> Condition params (Join left right) 'Ungrouped
939+ -> FromClause params schema left
940+ -> FromClause params schema
941+ (Join (NullifyTables left) (NullifyTables right))
942+
943+renderFromClause :: FromClause params schema tables -> ByteString
944+renderFromClause = \case
945+ Table table -> renderAliased renderTable table
946+ Subquery selection -> renderAliased (parenthesized . renderQuery) selection
947+ CrossJoin right left ->
948+ renderFromClause left <+> "CROSS JOIN" <+> renderFromClause right
949+ InnerJoin right on left -> renderJoin "INNER JOIN" right on left
950+ LeftOuterJoin right on left -> renderJoin "LEFT OUTER JOIN" right on left
951+ RightOuterJoin right on left -> renderJoin "RIGHT OUTER JOIN" right on left
952+ FullOuterJoin right on left -> renderJoin "FULL OUTER JOIN" right on left
953+ where
954+ renderJoin op right on left =
955+ renderFromClause left <+> op <+> renderFromClause right
956+ <+> "ON" <+> renderExpression on
957+
958+data TableExpression
959+ (params :: [ColumnType])
960+ (schema :: TablesType)
961+ (tables :: TablesType)
962+ (grouping :: Grouping)
963+ = TableExpression
964+ { fromClause :: FromClause params schema tables
965+ , whereClause :: [Condition params tables 'Ungrouped]
966+ , groupByClause :: GroupByClause tables grouping
967+ , havingClause :: HavingClause params tables grouping
968+ , orderByClause :: [SortExpression params tables grouping]
969+ , limitClause :: [Word64]
970+ , offsetClause :: [Word64]
971+ }
972+
973+renderTableExpression
974+ :: TableExpression params schema tables grouping
975+ -> ByteString
976+renderTableExpression
977+ (TableExpression tables whs' grps' hvs' srts' lims' offs') = mconcat
978+ [ "FROM ", renderFromClause tables
979+ , renderWheres whs'
980+ , renderGroupByClause grps'
981+ , renderHavingClause hvs'
982+ , renderSorts srts'
983+ , renderLimits lims'
984+ , renderOffsets offs'
985+ ]
986+ where
987+ renderWheres = \case
988+ [] -> ""
989+ wh:[] -> " WHERE" <+> renderExpression wh
990+ wh:whs -> " WHERE" <+> renderExpression (foldr (&&*) wh whs)
991+ renderSorts = \case
992+ [] -> ""
993+ srts -> " SORT BY"
994+ <+> commaSeparated (renderSortExpression <$> srts)
995+ renderLimits = \case
996+ [] -> ""
997+ lims -> " LIMIT" <+> fromString (show (minimum lims))
998+ renderOffsets = \case
999+ [] -> ""
1000+ offs -> " OFFSET" <+> fromString (show (sum offs))
1001+
1002+from
1003+ :: FromClause params schema tables
1004+ -> TableExpression params schema tables 'Ungrouped
1005+from tables = TableExpression tables [] NoGroups NoHaving [] [] []
1006+
1007+where_
1008+ :: Condition params tables 'Ungrouped
1009+ -> TableExpression params schema tables grouping
1010+ -> TableExpression params schema tables grouping
1011+where_ wh tables = tables {whereClause = wh : whereClause tables}
1012+
1013+group
1014+ :: SListI bys
1015+ => NP (By tables) bys
1016+ -> TableExpression params schema tables 'Ungrouped
1017+ -> TableExpression params schema tables ('Grouped bys)
1018+group bys tables = TableExpression
1019+ { fromClause = fromClause tables
1020+ , whereClause = whereClause tables
1021+ , groupByClause = Group bys
1022+ , havingClause = Having []
1023+ , orderByClause = []
1024+ , limitClause = limitClause tables
1025+ , offsetClause = offsetClause tables
1026+ }
1027+
1028+having
1029+ :: Condition params tables ('Grouped bys)
1030+ -> TableExpression params schema tables ('Grouped bys)
1031+ -> TableExpression params schema tables ('Grouped bys)
1032+having hv tables = tables
1033+ { havingClause = case havingClause tables of Having hvs -> Having (hv:hvs) }
1034+
1035+orderBy
1036+ :: [SortExpression params tables grouping]
1037+ -> TableExpression params schema tables grouping
1038+ -> TableExpression params schema tables grouping
1039+orderBy srts tables = tables {orderByClause = orderByClause tables ++ srts}
1040+
1041+limit
1042+ :: Word64
1043+ -> TableExpression params schema tables grouping
1044+ -> TableExpression params schema tables grouping
1045+limit lim tables = tables {limitClause = lim : limitClause tables}
1046+
1047+offset
1048+ :: Word64
1049+ -> TableExpression params schema tables grouping
1050+ -> TableExpression params schema tables grouping
1051+offset off tables = tables {offsetClause = off : offsetClause tables}
1052+
1053+{-----------------------------------------
1054+type expressions
1055+-----------------------------------------}
1056+
1057+newtype TypeExpression (ty :: ColumnType)
1058+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
1059+ deriving (GHC.Generic,Show,Eq,Ord,Data,NFData)
1060+
1061+bool :: TypeExpression ('Required ('Null 'PGbool))
1062+bool = UnsafeTypeExpression "bool"
1063+int2 :: TypeExpression ('Required ('Null 'PGint2))
1064+int2 = UnsafeTypeExpression "int2"
1065+smallint :: TypeExpression ('Required ('Null 'PGint2))
1066+smallint = UnsafeTypeExpression "smallint"
1067+int4 :: TypeExpression ('Required ('Null 'PGint4))
1068+int4 = UnsafeTypeExpression "int4"
1069+int :: TypeExpression ('Required ('Null 'PGint4))
1070+int = UnsafeTypeExpression "int"
1071+integer :: TypeExpression ('Required ('Null 'PGint4))
1072+integer = UnsafeTypeExpression "integer"
1073+int8 :: TypeExpression ('Required ('Null 'PGint8))
1074+int8 = UnsafeTypeExpression "int8"
1075+bigint :: TypeExpression ('Required ('Null 'PGint8))
1076+bigint = UnsafeTypeExpression "bigint"
1077+numeric :: TypeExpression ('Required ('Null 'PGnumeric))
1078+numeric = UnsafeTypeExpression "numeric"
1079+float4 :: TypeExpression ('Required ('Null 'PGfloat4))
1080+float4 = UnsafeTypeExpression "float4"
1081+real :: TypeExpression ('Required ('Null 'PGfloat4))
1082+real = UnsafeTypeExpression "real"
1083+float8 :: TypeExpression ('Required ('Null 'PGfloat8))
1084+float8 = UnsafeTypeExpression "float8"
1085+doublePrecision :: TypeExpression ('Required ('Null 'PGfloat8))
1086+doublePrecision = UnsafeTypeExpression "double precision"
1087+serial2 :: TypeExpression ('Optional ('NotNull 'PGint2))
1088+serial2 = UnsafeTypeExpression "serial2"
1089+smallserial :: TypeExpression ('Optional ('NotNull 'PGint2))
1090+smallserial = UnsafeTypeExpression "smallserial"
1091+serial4 :: TypeExpression ('Optional ('NotNull 'PGint4))
1092+serial4 = UnsafeTypeExpression "serial4"
1093+serial :: TypeExpression ('Optional ('NotNull 'PGint4))
1094+serial = UnsafeTypeExpression "serial"
1095+serial8 :: TypeExpression ('Optional ('NotNull 'PGint8))
1096+serial8 = UnsafeTypeExpression "serial8"
1097+bigserial :: TypeExpression ('Optional ('NotNull 'PGint8))
1098+bigserial = UnsafeTypeExpression "bigserial"
1099+text :: TypeExpression ('Required ('Null 'PGtext))
1100+text = UnsafeTypeExpression "text"
1101+char
1102+ :: KnownNat n
1103+ => proxy n
1104+ -> TypeExpression ('Required ('Null ('PGchar n)))
1105+char p = UnsafeTypeExpression $ "char(" <> renderNat p <> ")"
1106+character
1107+ :: KnownNat n
1108+ => proxy n
1109+ -> TypeExpression ('Required ('Null ('PGchar n)))
1110+character p = UnsafeTypeExpression $ "character(" <> renderNat p <> ")"
1111+varchar
1112+ :: KnownNat n
1113+ => proxy n
1114+ -> TypeExpression ('Required ('Null ('PGvarchar n)))
1115+varchar p = UnsafeTypeExpression $ "varchar(" <> renderNat p <> ")"
1116+characterVarying
1117+ :: KnownNat n
1118+ => proxy n
1119+ -> TypeExpression ('Required ('Null ('PGvarchar n)))
1120+characterVarying p = UnsafeTypeExpression $
1121+ "character varying(" <> renderNat p <> ")"
1122+bytea :: TypeExpression ('Required ('Null ('PGbytea)))
1123+bytea = UnsafeTypeExpression "bytea"
1124+timestamp :: TypeExpression ('Required ('Null ('PGtimestamp)))
1125+timestamp = UnsafeTypeExpression "timestamp"
1126+timestampWithTimeZone :: TypeExpression ('Required ('Null ('PGtimestamptz)))
1127+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
1128+date :: TypeExpression ('Required ('Null ('PGdate)))
1129+date = UnsafeTypeExpression "date"
1130+time :: TypeExpression ('Required ('Null ('PGtime)))
1131+time = UnsafeTypeExpression "time"
1132+timeWithTimeZone :: TypeExpression ('Required ('Null ('PGtimetz)))
1133+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
1134+interval :: TypeExpression ('Required ('Null ('PGinterval)))
1135+interval = UnsafeTypeExpression "interval"
1136+uuid :: TypeExpression ('Required ('Null ('PGuuid)))
1137+uuid = UnsafeTypeExpression "uuid"
1138+json :: TypeExpression ('Required ('Null ('PGjson)))
1139+json = UnsafeTypeExpression "json"
1140+jsonb :: TypeExpression ('Required ('Null ('PGjsonb)))
1141+jsonb = UnsafeTypeExpression "jsonb"
1142+
1143+notNull
1144+ :: TypeExpression (optionality ('Null ty))
1145+ -> TypeExpression (optionality ('NotNull ty))
1146+notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <+> "NOT NULL"
1147+default_
1148+ :: Expression '[] '[] 'Ungrouped ('Required ty)
1149+ -> TypeExpression ('Required ty)
1150+ -> TypeExpression ('Optional ty)
1151+default_ x ty = UnsafeTypeExpression $
1152+ renderTypeExpression ty <+> "DEFAULT" <+> renderExpression x
1153+
1154+class PGTyped (ty :: PGType) where
1155+ pgtype :: TypeExpression ('Required ('Null ty))
1156+instance PGTyped 'PGbool where pgtype = bool
1157+instance PGTyped 'PGint2 where pgtype = int2
1158+instance PGTyped 'PGint4 where pgtype = int4
1159+instance PGTyped 'PGint8 where pgtype = int8
1160+instance PGTyped 'PGnumeric where pgtype = numeric
1161+instance PGTyped 'PGfloat4 where pgtype = float4
1162+instance PGTyped 'PGfloat8 where pgtype = float8
1163+instance PGTyped 'PGtext where pgtype = text
1164+instance KnownNat n => PGTyped ('PGchar n) where pgtype = char (Proxy @n)
1165+instance KnownNat n => PGTyped ('PGvarchar n) where pgtype = varchar (Proxy @n)
1166+instance PGTyped 'PGbytea where pgtype = bytea
1167+instance PGTyped 'PGtimestamp where pgtype = timestamp
1168+instance PGTyped 'PGtimestamptz where pgtype = timestampWithTimeZone
1169+instance PGTyped 'PGdate where pgtype = date
1170+instance PGTyped 'PGtime where pgtype = time
1171+instance PGTyped 'PGtimetz where pgtype = timeWithTimeZone
1172+instance PGTyped 'PGinterval where pgtype = interval
1173+instance PGTyped 'PGuuid where pgtype = uuid
1174+instance PGTyped 'PGjson where pgtype = json
1175+instance PGTyped 'PGjsonb where pgtype = jsonb
1176+
1177+{-----------------------------------------
1178+statements
1179+-----------------------------------------}
1180+
1181+newtype Definition
1182+ (schema0 :: TablesType)
1183+ (schema1 :: TablesType)
1184+ = UnsafeDefinition { renderDefinition :: ByteString }
1185+ deriving (GHC.Generic,Show,Eq,Ord,Data,NFData)
1186+
1187+instance Category Definition where
1188+ id = UnsafeDefinition ";"
1189+ ddl1 . ddl0 = UnsafeDefinition $
1190+ renderDefinition ddl0 <+> renderDefinition ddl1
1191+
1192+newtype Manipulation
1193+ (schema :: TablesType)
1194+ (params :: [ColumnType])
1195+ (columns :: ColumnsType)
1196+ = UnsafeManipulation { renderManipulation :: ByteString }
1197+ deriving (GHC.Generic,Show,Eq,Ord,Data,NFData)
1198+
1199+newtype Query
1200+ (schema :: TablesType)
1201+ (params :: [ColumnType])
1202+ (columns :: ColumnsType)
1203+ = UnsafeQuery { renderQuery :: ByteString }
1204+ deriving (GHC.Generic,Show,Eq,Ord,Data,NFData)
1205+
1206+queryStatement
1207+ :: Query schema params columns
1208+ -> Manipulation schema params columns
1209+queryStatement q = UnsafeManipulation $ renderQuery q <> ";"
1210+
1211+union, unionAll, intersect, intersectAll, except, exceptAll
1212+ :: Query schema params columns
1213+ -> Query schema params columns
1214+ -> Query schema params columns
1215+q1 `union` q2 = UnsafeQuery $
1216+ parenthesized (renderQuery q1)
1217+ <+> "UNION"
1218+ <+> parenthesized (renderQuery q2)
1219+q1 `unionAll` q2 = UnsafeQuery $
1220+ parenthesized (renderQuery q1)
1221+ <+> "UNION" <+> "ALL"
1222+ <+> parenthesized (renderQuery q2)
1223+q1 `intersect` q2 = UnsafeQuery $
1224+ parenthesized (renderQuery q1)
1225+ <+> "INTERSECT"
1226+ <+> parenthesized (renderQuery q2)
1227+q1 `intersectAll` q2 = UnsafeQuery $
1228+ parenthesized (renderQuery q1)
1229+ <+> "INTERSECT" <+> "ALL"
1230+ <+> parenthesized (renderQuery q2)
1231+q1 `except` q2 = UnsafeQuery $
1232+ parenthesized (renderQuery q1)
1233+ <+> "EXCEPT"
1234+ <+> parenthesized (renderQuery q2)
1235+q1 `exceptAll` q2 = UnsafeQuery $
1236+ parenthesized (renderQuery q1)
1237+ <+> "EXCEPT" <+> "ALL"
1238+ <+> parenthesized (renderQuery q2)
1239+
1240+{-----------------------------------------
1241+SELECT statements
1242+-----------------------------------------}
1243+
1244+select
1245+ :: SListI columns
1246+ => NP (Aliased (Expression params tables grouping)) (column ': columns)
1247+ -> TableExpression params schema tables grouping
1248+ -> Query schema params (column ': columns)
1249+select list tabs = UnsafeQuery $
1250+ "SELECT"
1251+ <+> renderCommaSeparated (renderAliased renderExpression) list
1252+ <+> renderTableExpression tabs
1253+
1254+selectDistinct
1255+ :: SListI columns
1256+ => NP (Aliased (Expression params tables grouping)) (column ': columns)
1257+ -> TableExpression params schema tables grouping
1258+ -> Query schema params (column ': columns)
1259+selectDistinct list tabs = UnsafeQuery $
1260+ "SELECT DISTINCT"
1261+ <+> renderCommaSeparated (renderAliased renderExpression) list
1262+ <+> renderTableExpression tabs
1263+
1264+selectStar
1265+ :: tables ~ '[table ::: columns]
1266+ => TableExpression params schema tables 'Ungrouped
1267+ -> Query schema params columns
1268+selectStar tabs = UnsafeQuery $ "SELECT" <+> "*" <+> renderTableExpression tabs
1269+
1270+selectDistinctStar
1271+ :: tables ~ '[table ::: columns]
1272+ => TableExpression params schema tables 'Ungrouped
1273+ -> Query schema params columns
1274+selectDistinctStar tabs = UnsafeQuery $
1275+ "SELECT DISTINCT" <+> "*" <+> renderTableExpression tabs
1276+
1277+selectDotStar
1278+ :: HasTable table tables columns
1279+ => Alias table
1280+ -> TableExpression params schema tables 'Ungrouped
1281+ -> Query schema params columns
1282+selectDotStar table tables = UnsafeQuery $
1283+ "SELECT" <+> renderAlias table <> ".*" <+> renderTableExpression tables
1284+
1285+selectDistinctDotStar
1286+ :: HasTable table tables columns
1287+ => Alias table
1288+ -> TableExpression params schema tables 'Ungrouped
1289+ -> Query schema params columns
1290+selectDistinctDotStar table tables = UnsafeQuery $
1291+ "SELECT DISTINCT" <+> renderAlias table <> ".*"
1292+ <+> renderTableExpression tables
1293+
1294+{-----------------------------------------
1295+CREATE statements
1296+-----------------------------------------}
1297+
1298+createTable
1299+ :: (KnownSymbol table, SListI columns)
1300+ => Alias table
1301+ -> NP (Aliased TypeExpression) (column ': columns)
1302+ -> [TableConstraint schema (column ': columns)]
1303+ -> Definition schema (Create table (column ': columns) schema)
1304+createTable table columns constraints = UnsafeDefinition $
1305+ "CREATE TABLE" <+> renderAlias table
1306+ <+> parenthesized
1307+ ( renderCommaSeparated renderColumnDef columns
1308+ <> renderConstraints constraints )
1309+ <> ";"
1310+ where
1311+ renderColumnDef :: Aliased TypeExpression x -> ByteString
1312+ renderColumnDef (ty `As` column) =
1313+ renderAlias column <+> renderTypeExpression ty
1314+ renderConstraints :: [TableConstraint schema columns] -> ByteString
1315+ renderConstraints = \case
1316+ [] -> ""
1317+ _ -> ", " <> commaSeparated (renderTableConstraint <$> constraints)
1318+
1319+newtype TableConstraint
1320+ (schema :: TablesType)
1321+ (columns :: ColumnsType)
1322+ = UnsafeTableConstraint { renderTableConstraint :: ByteString }
1323+ deriving (GHC.Generic,Show,Eq,Ord,Data,NFData)
1324+
1325+check
1326+ :: Condition '[] '[table ::: columns] 'Ungrouped
1327+ -> TableConstraint schema columns
1328+check condition = UnsafeTableConstraint $
1329+ "CHECK" <+> parenthesized (renderExpression condition)
1330+
1331+renderColumn :: Column columns columnty -> ByteString
1332+renderColumn (Column column) = renderAlias column
1333+
1334+unique
1335+ :: SListI subcolumns
1336+ => NP (Column columns) subcolumns
1337+ -> TableConstraint schema columns
1338+unique columns = UnsafeTableConstraint $
1339+ "UNIQUE" <+> parenthesized (renderCommaSeparated renderColumn columns)
1340+
1341+primaryKey
1342+ :: (SListI subcolumns, AllNotNull subcolumns)
1343+ => NP (Column columns) subcolumns
1344+ -> TableConstraint schema columns
1345+primaryKey columns = UnsafeTableConstraint $
1346+ "PRIMARY KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
1347+
1348+foreignKey
1349+ :: ( HasTable reftable schema refcolumns
1350+ , SameTypes subcolumns refsubcolumns
1351+ , NotAllNull subcolumns
1352+ , SListI subcolumns
1353+ , SListI refsubcolumns)
1354+ => NP (Column columns) subcolumns
1355+ -> Alias reftable
1356+ -> NP (Column refcolumns) refsubcolumns
1357+ -> OnDelete
1358+ -> OnUpdate
1359+ -> TableConstraint schema columns
1360+foreignKey columns reftable refcolumns onDelete onUpdate =
1361+ UnsafeTableConstraint $
1362+ "FOREIGN KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
1363+ <+> "REFERENCES" <+> renderAlias reftable
1364+ <+> parenthesized (renderCommaSeparated renderColumn refcolumns)
1365+ <+> renderOnDelete onDelete
1366+ <+> renderOnUpdate onUpdate
1367+
1368+data OnDelete
1369+ = OnDeleteNoAction
1370+ | OnDeleteRestrict
1371+ | OnDeleteCascade
1372+ deriving (GHC.Generic,Show,Eq,Ord,Data)
1373+instance NFData OnDelete
1374+
1375+renderOnDelete :: OnDelete -> ByteString
1376+renderOnDelete = \case
1377+ OnDeleteNoAction -> "ON DELETE NO ACTION"
1378+ OnDeleteRestrict -> "ON DELETE RESTRICT"
1379+ OnDeleteCascade -> "ON DELETE CASCADE"
1380+
1381+data OnUpdate
1382+ = OnUpdateNoAction
1383+ | OnUpdateRestrict
1384+ | OnUpdateCascade
1385+ deriving (GHC.Generic,Show,Eq,Ord,Data)
1386+instance NFData OnUpdate
1387+
1388+renderOnUpdate :: OnUpdate -> ByteString
1389+renderOnUpdate = \case
1390+ OnUpdateNoAction -> "ON UPDATE NO ACTION"
1391+ OnUpdateRestrict -> "ON UPDATE RESTRICT"
1392+ OnUpdateCascade -> "ON UPDATE CASCADE"
1393+
1394+{-----------------------------------------
1395+DROP statements
1396+-----------------------------------------}
1397+
1398+dropTable
1399+ :: KnownSymbol table
1400+ => Alias table
1401+ -> Definition schema (Drop table schema)
1402+dropTable table = UnsafeDefinition $ "DROP TABLE" <+> renderAlias table <> ";"
1403+
1404+{-----------------------------------------
1405+ALTER statements
1406+-----------------------------------------}
1407+
1408+alterTable
1409+ :: HasTable table schema columns0
1410+ => Alias table
1411+ -> AlterTable columns0 columns1
1412+ -> Definition schema (Alter table schema columns1)
1413+alterTable table alteration = UnsafeDefinition $
1414+ "ALTER TABLE"
1415+ <+> renderAlias table
1416+ <+> renderAlterTable alteration
1417+ <> ";"
1418+
1419+alterTableRename
1420+ :: (KnownSymbol table0, KnownSymbol table1)
1421+ => Alias table0
1422+ -> Alias table1
1423+ -> Definition schema (Rename table0 table1 schema)
1424+alterTableRename table0 table1 = UnsafeDefinition $
1425+ "ALTER TABLE" <+> renderAlias table0
1426+ <+> "RENAME TO" <+> renderAlias table1 <> ";"
1427+
1428+newtype AlterTable
1429+ (columns0 :: [(Symbol, ColumnType)])
1430+ (columns1 :: [(Symbol, ColumnType)]) =
1431+ UnsafeAlterTable {renderAlterTable :: ByteString}
1432+ deriving (GHC.Generic,Show,Eq,Ord,Data,NFData)
1433+
1434+addColumnDefault
1435+ :: KnownSymbol column
1436+ => Alias column
1437+ -> TypeExpression ('Optional ty)
1438+ -> AlterTable columns (Create column ('Optional ty) columns)
1439+addColumnDefault column ty = UnsafeAlterTable $
1440+ "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
1441+
1442+addColumnNull
1443+ :: KnownSymbol column
1444+ => Alias column
1445+ -> TypeExpression ('Required ('Null ty))
1446+ -> AlterTable columns (Create column ('Required ('Null ty)) columns)
1447+addColumnNull column ty = UnsafeAlterTable $
1448+ "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
1449+
1450+dropColumn
1451+ :: KnownSymbol column
1452+ => Alias column
1453+ -> AlterTable columns (Drop column columns)
1454+dropColumn column = UnsafeAlterTable $
1455+ "DROP COLUMN" <+> renderAlias column
1456+
1457+renameColumn
1458+ :: (KnownSymbol column0, KnownSymbol column1)
1459+ => Alias column0
1460+ -> Alias column1
1461+ -> AlterTable columns (Rename column0 column1 columns)
1462+renameColumn column0 column1 = UnsafeAlterTable $
1463+ "RENAME COLUMN" <+> renderAlias column0 <+> "TO" <+> renderAlias column1
1464+
1465+alterColumn
1466+ :: (KnownSymbol column, HasColumn column columns ty0)
1467+ => Alias column
1468+ -> AlterColumn ty0 ty1
1469+ -> AlterTable columns (Alter column columns ty1)
1470+alterColumn column alteration = UnsafeAlterTable $
1471+ "ALTER COLUMN" <+> renderAlias column <+> renderAlterColumn alteration
1472+
1473+newtype AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
1474+ UnsafeAlterColumn {renderAlterColumn :: ByteString}
1475+ deriving (GHC.Generic,Show,Eq,Ord,Data,NFData)
1476+
1477+setDefault
1478+ :: Expression '[] '[] 'Ungrouped ('Required ty)
1479+ -> AlterColumn ('Required ty) ('Optional ty)
1480+setDefault expression = UnsafeAlterColumn $
1481+ "SET DEFAULT" <+> renderExpression expression
1482+
1483+dropDefault :: AlterColumn ('Optional ty) ('Required ty)
1484+dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
1485+
1486+setNotNull
1487+ :: AlterColumn (optionality ('Null ty)) (optionality ('NotNull ty))
1488+setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
1489+
1490+dropNotNull
1491+ :: AlterColumn (optionality ('NotNull ty)) (optionality ('Null ty))
1492+dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
1493+
1494+alterType
1495+ :: TypeExpression (optionality (nullity ty1))
1496+ -> AlterColumn (optionality (nullity ty0)) (optionality (nullity ty1))
1497+alterType ty = UnsafeAlterColumn $ "TYPE " <> renderTypeExpression ty
1498+
1499+{-----------------------------------------
1500+INSERT statements
1501+-----------------------------------------}
1502+
1503+insertInto
1504+ :: (SListI columns, SListI results, HasTable table schema columns)
1505+ => Alias table
1506+ -> ValuesClause schema params columns
1507+ -> ConflictClause params columns
1508+ -> ReturningClause params columns results
1509+ -> Manipulation schema params results
1510+insertInto table insert conflict returning = UnsafeManipulation $
1511+ "INSERT" <+> "INTO" <+> renderAlias table
1512+ <+> renderValuesClause insert
1513+ <> renderConflictClause conflict
1514+ <> renderReturningClause returning
1515+
1516+data ValuesClause
1517+ (schema :: TablesType)
1518+ (params :: [ColumnType])
1519+ (columns :: ColumnsType)
1520+ = Values
1521+ (NP (Aliased (Expression params '[] 'Ungrouped)) columns)
1522+ [NP (Aliased (Expression params '[] 'Ungrouped)) columns]
1523+ | ValuesQuery (Query schema params columns)
1524+
1525+renderValuesClause
1526+ :: SListI columns
1527+ => ValuesClause schema params columns
1528+ -> ByteString
1529+renderValuesClause = \case
1530+ Values row rows ->
1531+ parenthesized (renderCommaSeparated renderAliasPart row)
1532+ <+> "VALUES"
1533+ <+> commaSeparated
1534+ (parenthesized . renderCommaSeparated renderValuePart <$> row:rows)
1535+ where
1536+ renderAliasPart, renderValuePart
1537+ :: Aliased (Expression params '[] 'Ungrouped) ty -> ByteString
1538+ renderAliasPart (_ `As` name) = renderAlias name
1539+ renderValuePart (value `As` _) = renderExpression value
1540+ ValuesQuery q -> renderQuery q
1541+
1542+data ReturningClause
1543+ (params :: [ColumnType])
1544+ (columns :: ColumnsType)
1545+ (results :: ColumnsType)
1546+ where
1547+ ReturningStar :: ReturningClause params columns columns
1548+ Returning
1549+ :: NP
1550+ (Aliased (Expression params '[table ::: columns] 'Ungrouped))
1551+ results
1552+ -> ReturningClause params columns results
1553+
1554+renderReturningClause
1555+ :: SListI results
1556+ => ReturningClause params columns results
1557+ -> ByteString
1558+renderReturningClause = \case
1559+ ReturningStar -> " RETURNING *;"
1560+ Returning Nil -> ";"
1561+ Returning results -> " RETURNING"
1562+ <+> renderCommaSeparated (renderAliased renderExpression) results <> ";"
1563+
1564+data ConflictClause params columns where
1565+ Conflict :: ConflictClause params columns
1566+ OnConflictDoNothing :: ConflictClause params columns
1567+ OnConflictDoUpdate
1568+ :: NP (Aliased (UpdateExpression params columns)) columns
1569+ -> Maybe (Condition params '[table ::: columns] 'Ungrouped)
1570+ -> ConflictClause params columns
1571+
1572+renderConflictClause
1573+ :: SListI columns
1574+ => ConflictClause params columns
1575+ -> ByteString
1576+renderConflictClause = \case
1577+ Conflict -> ""
1578+ OnConflictDoNothing -> " ON CONFLICT DO NOTHING"
1579+ OnConflictDoUpdate updates whMaybe
1580+ -> " ON CONFLICT DO UPDATE SET"
1581+ <+> renderCommaSeparatedMaybe renderUpdateExpression updates
1582+ <> case whMaybe of
1583+ Nothing -> ""
1584+ Just wh -> " WHERE" <+> renderExpression wh
1585+
1586+{-----------------------------------------
1587+UPDATE statements
1588+-----------------------------------------}
1589+
1590+data UpdateExpression params columns ty
1591+ = Same
1592+ | Set (forall table. Expression params '[table ::: columns] 'Ungrouped ty)
1593+deriving instance Show (UpdateExpression params columns ty)
1594+deriving instance Eq (UpdateExpression params columns ty)
1595+deriving instance Ord (UpdateExpression params columns ty)
1596+
1597+renderUpdateExpression
1598+ :: Aliased (UpdateExpression params columns) column
1599+ -> Maybe ByteString
1600+renderUpdateExpression = \case
1601+ Same `As` _ -> Nothing
1602+ Set expression `As` column -> Just $
1603+ renderAlias column <+> "=" <+> renderExpression expression
1604+
1605+update
1606+ :: (HasTable table schema columns, SListI columns, SListI results)
1607+ => Alias table
1608+ -> NP (Aliased (UpdateExpression params columns)) columns
1609+ -> Condition params '[tab ::: columns] 'Ungrouped
1610+ -> ReturningClause params columns results
1611+ -> Manipulation schema params results
1612+update table columns wh returning = UnsafeManipulation $
1613+ "UPDATE"
1614+ <+> renderAlias table
1615+ <+> "SET"
1616+ <+> renderCommaSeparatedMaybe renderUpdateExpression columns
1617+ <+> "WHERE" <+> renderExpression wh
1618+ <> renderReturningClause returning
1619+
1620+{-----------------------------------------
1621+DELETE statements
1622+-----------------------------------------}
1623+
1624+deleteFrom
1625+ :: HasTable table schema columns
1626+ => Alias table
1627+ -> Condition params '[table ::: columns] 'Ungrouped
1628+ -> Manipulation schema params '[]
1629+deleteFrom table wh = UnsafeManipulation $
1630+ "DELETE FROM" <+> renderAlias table
1631+ <+> "WHERE" <+> renderExpression wh <> ";"
1632+
1633+{-----------------------------------------
1634+render helper functions
1635+-----------------------------------------}
1636+
1637+parenthesized :: ByteString -> ByteString
1638+parenthesized str = "(" <> str <> ")"
1639+
1640+(<+>) :: ByteString -> ByteString -> ByteString
1641+str1 <+> str2 = str1 <> " " <> str2
1642+
1643+commaSeparated :: [ByteString] -> ByteString
1644+commaSeparated = ByteString.intercalate ", "
1645+
1646+renderCommaSeparated
1647+ :: SListI xs
1648+ => (forall x. expression x -> ByteString)
1649+ -> NP expression xs -> ByteString
1650+renderCommaSeparated render
1651+ = commaSeparated
1652+ . hcollapse
1653+ . hmap (K . render)
1654+
1655+renderCommaSeparatedMaybe
1656+ :: SListI xs
1657+ => (forall x. expression x -> Maybe ByteString)
1658+ -> NP expression xs -> ByteString
1659+renderCommaSeparatedMaybe render
1660+ = commaSeparated
1661+ . catMaybes
1662+ . hcollapse
1663+ . hmap (K . render)
1664+
1665+renderNat :: KnownNat n => proxy n -> ByteString
1666+renderNat (_ :: proxy n) = fromString (show (natVal' (proxy# :: Proxy# n)))