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