@@ -0,0 +1,1309 @@
1+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
6+ , FunctionalDependencies
13+ , ScopedTypeVariables
17+ , UndecidableInstances
20+module Squeal.PostgreSQL.Statement where
22+import Control.Category
23+import Data.ByteString (ByteString)
32+import GHC.OverloadedLabels
34+import Prelude hiding (RealFrac(..), id, (.))
36+import qualified Data.ByteString as ByteString
38+import Squeal.PostgreSQL.Schema
40+{-----------------------------------------
42+-----------------------------------------}
45+ (params :: [ColumnType])
46+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
47+ (grouping :: Grouping)
49+ = UnsafeExpression { renderExpression :: ByteString }
52+class (PGTyped (BaseType ty), KnownNat n)
53+ => HasParameter (n :: Nat) params ty | n params -> ty where
54+ param :: proxy n -> Expression params tables grouping ty
55+ param _ = UnsafeExpression $ parenthesized $
56+ "$" <> renderNat (Proxy @n) <+> "::"
57+ <+> renderTypeExpression (pgtype @(BaseType ty))
58+instance {-# OVERLAPPING #-} PGTyped (BaseType ty1)
59+ => HasParameter 1 (ty1:tys) ty1
60+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) params ty)
61+ => HasParameter n (ty' : params) ty
63+class KnownSymbol column => HasColumn column columns ty
64+ | column columns -> ty where
67+ -> Expression params '[table ::: columns] 'Ungrouped ty
68+ getColumn column = UnsafeExpression $ renderSymbol column
69+instance {-# OVERLAPPING #-} KnownSymbol column
70+ => HasColumn column ((column ::: optionality ty) ': tys) ('Required ty)
71+instance {-# OVERLAPPABLE #-} (KnownSymbol column, HasColumn column table ty)
72+ => HasColumn column (ty' ': table) ty
74+instance (HasColumn column columns ty, tables ~ '[table ::: columns])
75+ => IsLabel column (Expression params tables 'Ungrouped ty) where
76+ fromLabel = getColumn
78+instance (HasTable table tables columns, HasColumn column columns ty)
79+ => IsTableColumn table column (Expression params tables 'Ungrouped ty) where
80+ table &. column = UnsafeExpression $
81+ renderAlias table <> "." <> renderAlias column
83+def :: Expression params '[] grouping ('Optional (nullity ty))
84+def = UnsafeExpression "DEFAULT"
87+ :: Expression params '[] grouping ('Required (nullity ty))
88+ -> Expression params '[] grouping ('Optional (nullity ty))
89+unDef = UnsafeExpression . renderExpression
91+null :: Expression params tables grouping (optionality ('Null ty))
92+null = UnsafeExpression "NULL"
95+ :: Expression params tables grouping (optionality ('NotNull ty))
96+ -> Expression params tables grouping (optionality ('Null ty))
97+unNull = UnsafeExpression . renderExpression
100+ :: [Expression params tables grouping ('Required ('Null ty))]
101+ -> Expression params tables grouping ('Required ('NotNull ty))
102+ -> Expression params tables grouping ('Required ('NotNull ty))
103+coalesce nullxs notNullx = UnsafeExpression $
104+ "COALESCE" <> parenthesized (commaSeparated
105+ ((renderExpression <$> nullxs) <> [renderExpression notNullx]))
108+ :: Expression params tables grouping ('Required ('NotNull ty))
109+ -> Expression params tables grouping ('Required ('Null ty))
110+ -> Expression params tables grouping ('Required ('NotNull ty))
111+fromNull notNullx nullx = coalesce [nullx] notNullx
114+ :: Expression params tables grouping ('Required ('Null ty))
115+ -> Condition params tables grouping
116+isNull x = UnsafeExpression $ renderExpression x <+> "IS NULL"
119+ :: Expression params tables grouping ('Required ('Null ty))
120+ -> Condition params tables grouping
121+isn'tNull x = UnsafeExpression $ renderExpression x <+> "IS NOT NULL"
124+ :: Expression params tables grouping ('Required nullty)
125+ -> ( Expression params tables grouping ('Required ('NotNull ty))
126+ -> Expression params tables grouping ('Required nullty) )
127+ -> Expression params tables grouping ('Required ('Null ty))
128+ -> Expression params tables grouping ('Required nullty)
129+matchNull y f x = ifThenElse (isNull x) y
130+ (f (UnsafeExpression (renderExpression y)))
133+ :: Expression params tables grouping ('Required ('NotNull ty))
134+ -> Expression params tables grouping ('Required ('NotNull ty))
135+ -> Expression params tables grouping ('Required ('Null ty))
136+nullIf x y = UnsafeExpression $ parenthesized $
137+ renderExpression x <> ", " <> renderExpression y
140+ :: [Expression params tables grouping ('Required nullty)]
141+ -> Expression params table grouping ('Required nullty)
142+greatest xs = UnsafeExpression $ "GREATEST("
143+ <> commaSeparated (renderExpression <$> xs) <> ")"
146+ :: [Expression params tables grouping ('Required nullty)]
147+ -> Expression params table grouping ('Required nullty)
148+least xs = UnsafeExpression $ "LEAST("
149+ <> commaSeparated (renderExpression <$> xs) <> ")"
153+ -> Expression params tables grouping ('Required ty0)
154+ -> Expression params tables grouping ('Required ty1)
155+ -> Expression params tables grouping ('Required ty2)
156+unsafeBinaryOp op x y = UnsafeExpression $ parenthesized $
157+ renderExpression x <+> op <+> renderExpression y
161+ -> Expression params tables grouping ('Required ty0)
162+ -> Expression params tables grouping ('Required ty1)
163+unsafeUnaryOp op x = UnsafeExpression $ parenthesized $
164+ op <+> renderExpression x
168+ -> Expression params tables grouping ('Required xty)
169+ -> Expression params tables grouping ('Required yty)
170+unsafeFunction fun x = UnsafeExpression $
171+ fun <> parenthesized (renderExpression x)
174+ => Num (Expression params tables grouping ('Required (nullity ty))) where
175+ (+) = unsafeBinaryOp "+"
176+ (-) = unsafeBinaryOp "-"
177+ (*) = unsafeBinaryOp "*"
178+ abs = unsafeFunction "abs"
179+ signum = unsafeFunction "sign"
182+ . (<> if decimal (Proxy :: Proxy ty) then "." else "")
186+instance PGFloating ty => Fractional
187+ (Expression params tables grouping ('Required (nullity ty))) where
188+ (/) = unsafeBinaryOp "/"
189+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
191+instance (PGFloating ty, PGTyped ty) => Floating
192+ (Expression params tables grouping ('Required (nullity ty))) where
193+ pi = UnsafeExpression "pi()"
194+ exp = unsafeFunction "exp"
195+ log = unsafeFunction "ln"
196+ sqrt = unsafeFunction "sqrt"
197+ b ** x = UnsafeExpression $
198+ "power(" <> renderExpression b <> ", " <> renderExpression x <> ")"
199+ logBase b y = cast pgtype $ logBaseNumeric b y
202+ :: Expression params tables grouping ('Required (nullity ty))
203+ -> Expression params tables grouping ('Required (nullity ty))
204+ -> Expression params tables grouping ('Required (nullity 'PGnumeric))
205+ logBaseNumeric b' y' = UnsafeExpression $ mconcat
207+ , renderExpression (cast numeric b')
209+ , renderExpression (cast numeric y')
212+ sin = unsafeFunction "sin"
213+ cos = unsafeFunction "cos"
214+ tan = unsafeFunction "tan"
215+ asin = unsafeFunction "asin"
216+ acos = unsafeFunction "acos"
217+ atan = unsafeFunction "atan"
218+ sinh x = (exp x - exp (-x)) / 2
219+ cosh x = (exp x + exp (-x)) / 2
220+ tanh x = sinh x / cosh x
221+ asinh x = log (x + sqrt (x*x + 1))
222+ acosh x = log (x + sqrt (x*x - 1))
223+ atanh x = log ((1 + x) / (1 - x)) / 2
227+ => Expression params tables grouping ('Required (nullity float))
228+ -> Expression params tables grouping ('Required (nullity float))
229+ -> Expression params tables grouping ('Required (nullity float))
230+atan2 y x = UnsafeExpression $
231+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
234+ :: TypeExpression ('Required ('Null ty1))
235+ -> Expression params tables grouping ('Required (nullity ty0))
236+ -> Expression params tables grouping ('Required (nullity ty1))
237+cast ty x = UnsafeExpression $ parenthesized $
238+ renderExpression x <+> "::" <+> renderTypeExpression ty
242+ => Expression params tables grouping ('Required (nullity int))
243+ -> Expression params tables grouping ('Required (nullity int))
244+ -> Expression params tables grouping ('Required (nullity int))
245+div = unsafeBinaryOp "/"
249+ => Expression params tables grouping ('Required (nullity int))
250+ -> Expression params tables grouping ('Required (nullity int))
251+ -> Expression params tables grouping ('Required (nullity int))
252+mod = unsafeBinaryOp "%"
256+ => Expression params tables grouping ('Required (nullity frac))
257+ -> Expression params tables grouping ('Required (nullity frac))
258+trunc = unsafeFunction "trunc"
262+ => Expression params tables grouping ('Required (nullity frac))
263+ -> Expression params tables grouping ('Required (nullity frac))
264+round_ = unsafeFunction "round"
268+ => Expression params tables grouping ('Required (nullity frac))
269+ -> Expression params tables grouping ('Required (nullity frac))
270+ceiling_ = unsafeFunction "ceiling"
273+ (Expression params tables grouping ('Required (nullity 'PGtext))) where
274+ fromString str = UnsafeExpression $
275+ "E\'" <> fromString (escape =<< str) <> "\'"
288+type Condition params tables grouping =
289+ Expression params tables grouping ('Required ('NotNull 'PGbool))
291+true :: Condition params tables grouping
292+true = UnsafeExpression "TRUE"
294+false :: Condition params tables grouping
295+false = UnsafeExpression "FALSE"
298+ :: Condition params tables grouping
299+ -> Condition params tables grouping
300+not_ = unsafeUnaryOp "NOT"
303+ :: Condition params tables grouping
304+ -> Condition params tables grouping
305+ -> Condition params tables grouping
306+(&&*) = unsafeBinaryOp "AND"
309+ :: Condition params tables grouping
310+ -> Condition params tables grouping
311+ -> Condition params tables grouping
312+(||*) = unsafeBinaryOp "OR"
315+ :: [ ( Condition params tables grouping
316+ , Expression params tables grouping ('Required ty)
318+ -> Expression params tables grouping ('Required ty)
319+ -> Expression params tables grouping ('Required ty)
320+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
324+ [ " WHEN ", renderExpression when_
325+ , " THEN ", renderExpression then_
327+ | (when_,then_) <- whenThens
329+ , " ELSE ", renderExpression else_
334+ :: Condition params tables grouping
335+ -> Expression params tables grouping ('Required ty)
336+ -> Expression params tables grouping ('Required ty)
337+ -> Expression params tables grouping ('Required ty)
338+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
341+ :: Expression params tables grouping ('Required (nullity ty))
342+ -> Expression params tables grouping ('Required (nullity ty))
343+ -> Expression params tables grouping ('Required (nullity 'PGbool))
344+(==*) = unsafeBinaryOp "="
347+ :: Expression params tables grouping ('Required (nullity ty))
348+ -> Expression params tables grouping ('Required (nullity ty))
349+ -> Expression params tables grouping ('Required (nullity 'PGbool))
350+(/=*) = unsafeBinaryOp "<>"
353+ :: Expression params tables grouping ('Required (nullity ty))
354+ -> Expression params tables grouping ('Required (nullity ty))
355+ -> Expression params tables grouping ('Required (nullity 'PGbool))
356+(>=*) = unsafeBinaryOp ">="
359+ :: Expression params tables grouping ('Required (nullity ty))
360+ -> Expression params tables grouping ('Required (nullity ty))
361+ -> Expression params tables grouping ('Required (nullity 'PGbool))
362+(<*) = unsafeBinaryOp "<"
365+ :: Expression params tables grouping ('Required (nullity ty))
366+ -> Expression params tables grouping ('Required (nullity ty))
367+ -> Expression params tables grouping ('Required (nullity 'PGbool))
368+(<=*) = unsafeBinaryOp "<="
371+ :: Expression params tables grouping ('Required (nullity ty))
372+ -> Expression params tables grouping ('Required (nullity ty))
373+ -> Expression params tables grouping ('Required (nullity 'PGbool))
374+(>*) = unsafeBinaryOp ">"
376+data SortExpression params tables grouping where
378+ :: Expression params tables grouping ty
379+ -> SortExpression params tables grouping
381+ :: Expression params tables grouping ty
382+ -> SortExpression params tables grouping
384+renderSortExpression :: SortExpression params tables grouping -> ByteString
385+renderSortExpression = \case
386+ Asc expression -> renderExpression expression <+> "ASC"
387+ Desc expression -> renderExpression expression <+> "DESC"
389+{-----------------------------------------
391+-----------------------------------------}
394+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
395+ (tabcol :: (Symbol,Symbol)) where
397+ :: (tables ~ '[table ::: columns], HasColumn column columns ty)
399+ -> By tables '(table, column)
401+ :: (HasTable table tables columns, HasColumn column columns ty)
402+ => (Alias table, Alias column)
403+ -> By tables '(table, column)
405+renderBy :: By tables tabcolty -> ByteString
407+ By column -> renderAlias column
408+ By2 (table, column) -> renderAlias table <> "." <> renderAlias column
410+data GroupByClause tables grouping where
411+ NoGroups :: GroupByClause tables 'Ungrouped
414+ => NP (By tables) bys
415+ -> GroupByClause tables ('Grouped bys)
417+renderGroupByClause :: GroupByClause tables grouping -> ByteString
418+renderGroupByClause = \case
421+ Group bys -> " GROUP BY" <+> renderCommaSeparated renderBy bys
423+data HavingClause params tables grouping where
424+ NoHaving :: HavingClause params tables 'Ungrouped
426+ :: [Condition params tables ('Grouped bys)]
427+ -> HavingClause params tables ('Grouped bys)
429+renderHavingClause :: HavingClause params tables grouping -> ByteString
430+renderHavingClause = \case
433+ Having conditions ->
434+ " HAVING" <+> commaSeparated (renderExpression <$> conditions)
436+class (KnownSymbol table, KnownSymbol column)
437+ => GroupedBy table column bys where
439+ :: (tables ~ '[table ::: columns], HasColumn column columns ty)
441+ -> Expression params tables ('Grouped bys) ty
442+ getGroup1 column = UnsafeExpression $ renderSymbol column
444+ :: (HasTable table tables columns, HasColumn column columns ty)
447+ -> Expression params tables ('Grouped bys) ty
448+ getGroup2 table column = UnsafeExpression $
449+ renderAlias table <> "." <> renderAlias column
450+instance {-# OVERLAPPING #-} (KnownSymbol table, KnownSymbol column)
451+ => GroupedBy table column ('(table,column) ': bys)
452+instance {-# OVERLAPPABLE #-}
454+ , KnownSymbol column
455+ , GroupedBy table column bys
456+ ) => GroupedBy table column (tabcol ': bys)
459+ ( tables ~ '[table ::: columns]
460+ , HasColumn column columns ty
461+ , GroupedBy table column bys
463+ (Expression params tables ('Grouped bys) ty) where fromLabel = getGroup1
466+ ( HasTable table tables columns
467+ , HasColumn column columns ty
468+ , GroupedBy table column bys
469+ ) => IsTableColumn table column
470+ (Expression params tables ('Grouped bys) ty) where (&.) = getGroup2
474+ -> Expression params tables 'Ungrouped ('Required xty)
475+ -> Expression params tables ('Grouped bys) ('Required yty)
476+unsafeAggregate fun x = UnsafeExpression $ mconcat
477+ [fun, "(", renderExpression x, ")"]
481+ => Expression params tables 'Ungrouped ('Required (nullity ty))
482+ -> Expression params tables ('Grouped bys) ('Required (nullity ty))
483+sum_ = unsafeAggregate "sum"
487+ => Expression params tables 'Ungrouped ('Required (nullity int))
488+ -> Expression params tables ('Grouped bys) ('Required (nullity 'PGnumeric))
489+avgInt = unsafeAggregate "avg"
493+ => Expression params tables 'Ungrouped ('Required (nullity float))
494+ -> Expression params tables ('Grouped bys) ('Required (nullity 'PGfloat8))
495+avgFloat = unsafeAggregate "avg"
499+ => Expression params tables 'Ungrouped ('Required (nullity int))
500+ -> Expression params tables ('Grouped bys) ('Required (nullity int))
501+bitAnd = unsafeAggregate "bit_and"
505+ => Expression params tables 'Ungrouped ('Required (nullity int))
506+ -> Expression params tables ('Grouped bys) ('Required (nullity int))
507+bitOr = unsafeAggregate "bit_or"
510+ :: Expression params tables 'Ungrouped ('Required (nullity 'PGbool))
511+ -> Expression params tables ('Grouped bys) ('Required (nullity 'PGbool))
512+boolAnd = unsafeAggregate "bool_and"
515+ :: Expression params tables 'Ungrouped ('Required (nullity 'PGbool))
516+ -> Expression params tables ('Grouped bys) ('Required (nullity 'PGbool))
517+boolOr = unsafeAggregate "bool_or"
520+ :: Expression params table ('Grouped bys) ('Required ('NotNull 'PGint8))
521+countStar = UnsafeExpression "count(*)"
524+ :: Expression params tables 'Ungrouped ('Required ty)
525+ -> Expression params tables ('Grouped bys) ('Required ('NotNull 'PGint8))
526+count = unsafeAggregate "count"
529+ :: Expression params tables 'Ungrouped ('Required (nullity 'PGbool))
530+ -> Expression params tables ('Grouped bys) ('Required (nullity 'PGbool))
531+every = unsafeAggregate "every"
534+ :: Expression params tables 'Ungrouped ('Required ty)
535+ -> Expression params tables ('Grouped bys) ('Required ('NotNull 'PGjson))
536+jsonAgg = unsafeAggregate "json_agg"
539+ :: Expression params tables 'Ungrouped ('Required ty)
540+ -> Expression params tables ('Grouped bys) ('Required ('NotNull 'PGjsonb))
541+jsonbAgg = unsafeAggregate "jsonb_agg"
544+ :: Expression params tables 'Ungrouped ('Required ('NotNull keyty))
545+ -> Expression params tables 'Ungrouped ('Required valty)
546+ -> Expression params tables ('Grouped bys) ('Required ('NotNull 'PGjson))
547+jsonObjectAgg k v = UnsafeExpression $
548+ "json_object_agg(" <> renderExpression k
549+ <> ", " <> renderExpression v <> ")"
552+ :: Expression params tables 'Ungrouped ('Required ('NotNull keyty))
553+ -> Expression params tables 'Ungrouped ('Required valty)
554+ -> Expression params tables ('Grouped bys) ('Required ('NotNull 'PGjsonb))
555+jsonbObjectAgg k v = UnsafeExpression $
556+ "jsonb_object_agg(" <> renderExpression k
557+ <> ", " <> renderExpression v <> ")"
560+ :: Expression params tables 'Ungrouped ('Required (nullity ty))
561+ -> Expression params tables ('Grouped bys) ('Required (nullity ty))
562+max_ = unsafeAggregate "max"
565+ :: Expression params tables 'Ungrouped ('Required (nullity ty))
566+ -> Expression params tables ('Grouped bys) ('Required (nullity ty))
567+min_ = unsafeAggregate "min"
569+{-----------------------------------------
571+-----------------------------------------}
574+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
575+ (columns :: [(Symbol,ColumnType)])
576+ = UnsafeTable { renderTable :: ByteString }
579+class KnownSymbol table => HasTable table tables columns
580+ | table tables -> columns where
581+ getTable :: Proxy# table -> Table tables columns
582+ getTable table = UnsafeTable $ renderSymbol table
583+instance {-# OVERLAPPING #-} KnownSymbol table
584+ => HasTable table ((table ::: columns) ': tables) columns
585+instance {-# OVERLAPPABLE #-}
586+ (KnownSymbol table, HasTable table schema columns)
587+ => HasTable table (table' ': schema) columns
589+instance HasTable table schema columns
590+ => IsLabel table (Table schema columns) where
591+ fromLabel = getTable
593+data TableReference params schema tables where
595+ :: Aliased (Table schema) table
596+ -> TableReference params schema '[table]
598+ :: Aliased (Query schema params) table
599+ -> TableReference params schema '[table]
601+ :: TableReference params schema right
602+ -> TableReference params schema left
603+ -> TableReference params schema (Join left right)
605+ :: TableReference params schema right
606+ -> Condition params (Join left right) 'Ungrouped
607+ -> TableReference params schema left
608+ -> TableReference params schema (Join left right)
610+ :: TableReference params schema right
611+ -> Condition params (Join left right) 'Ungrouped
612+ -> TableReference params schema left
613+ -> TableReference params schema (Join left (NullifyTables right))
615+ :: TableReference params schema right
616+ -> Condition params (Join left right) 'Ungrouped
617+ -> TableReference params schema left
618+ -> TableReference params schema (Join (NullifyTables left) right)
620+ :: TableReference params schema right
621+ -> Condition params (Join left right) 'Ungrouped
622+ -> TableReference params schema left
623+ -> TableReference params schema
624+ (Join (NullifyTables left) (NullifyTables right))
626+renderTableReference :: TableReference params schema tables -> ByteString
627+renderTableReference = \case
628+ Table table -> renderAliased renderTable table
629+ Subquery selection -> renderAliased renderQuery selection
630+ CrossJoin right left ->
631+ renderTableReference left <+> "CROSS JOIN" <+> renderTableReference right
632+ InnerJoin right on left -> renderJoin "INNER JOIN" right on left
633+ LeftOuterJoin right on left -> renderJoin "LEFT OUTER JOIN" right on left
634+ RightOuterJoin right on left -> renderJoin "RIGHT OUTER JOIN" right on left
635+ FullOuterJoin right on left -> renderJoin "FULL OUTER JOIN" right on left
637+ renderJoin op right on left =
638+ renderTableReference left <+> op <+> renderTableReference right
639+ <+> "ON" <+> renderExpression on
642+ (params :: [ColumnType])
643+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
644+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
645+ (grouping :: Grouping)
647+ { fromClause :: TableReference params schema tables
648+ , whereClause :: [Condition params tables 'Ungrouped]
649+ , groupByClause :: GroupByClause tables grouping
650+ , havingClause :: HavingClause params tables grouping
651+ , orderByClause :: [SortExpression params tables grouping]
652+ , limitClause :: [Word64]
653+ , offsetClause :: [Word64]
656+renderTableExpression
657+ :: TableExpression params schema tables grouping
659+renderTableExpression
660+ (TableExpression tables whs' grps' hvs' srts' lims' offs') = mconcat
661+ [ "FROM ", renderTableReference tables
663+ , renderGroupByClause grps'
664+ , renderHavingClause hvs'
666+ , renderLimits lims'
667+ , renderOffsets offs'
670+ renderWheres = \case
672+ wh:[] -> " WHERE" <+> renderExpression wh
673+ wh:whs -> " WHERE" <+> renderExpression (foldr (&&*) wh whs)
677+ <+> commaSeparated (renderSortExpression <$> srts)
678+ renderLimits = \case
680+ lims -> " LIMIT" <+> fromString (show (minimum lims))
681+ renderOffsets = \case
683+ offs -> " OFFSET" <+> fromString (show (sum offs))
686+ :: TableReference params schema tables
687+ -> TableExpression params schema tables 'Ungrouped
688+from tables = TableExpression tables [] NoGroups NoHaving [] [] []
691+ :: Condition params tables 'Ungrouped
692+ -> TableExpression params schema tables grouping
693+ -> TableExpression params schema tables grouping
694+where_ wh tables = tables {whereClause = wh : whereClause tables}
698+ => NP (By tables) bys
699+ -> TableExpression params schema tables 'Ungrouped
700+ -> TableExpression params schema tables ('Grouped bys)
701+group bys tables = TableExpression
702+ { fromClause = fromClause tables
703+ , whereClause = whereClause tables
704+ , groupByClause = Group bys
705+ , havingClause = Having []
706+ , orderByClause = []
707+ , limitClause = limitClause tables
708+ , offsetClause = offsetClause tables
712+ :: Condition params tables ('Grouped bys)
713+ -> TableExpression params schema tables ('Grouped bys)
714+ -> TableExpression params schema tables ('Grouped bys)
715+having hv tables = tables
716+ { havingClause = case havingClause tables of Having hvs -> Having (hv:hvs) }
719+ :: [SortExpression params tables grouping]
720+ -> TableExpression params schema tables grouping
721+ -> TableExpression params schema tables grouping
722+orderBy srts tables = tables {orderByClause = orderByClause tables ++ srts}
726+ -> TableExpression params schema tables grouping
727+ -> TableExpression params schema tables grouping
728+limit lim tables = tables {limitClause = lim : limitClause tables}
732+ -> TableExpression params schema tables grouping
733+ -> TableExpression params schema tables grouping
734+offset off tables = tables {offsetClause = off : offsetClause tables}
736+{-----------------------------------------
738+-----------------------------------------}
740+newtype TypeExpression (ty :: ColumnType)
741+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
744+bool :: TypeExpression ('Required ('Null 'PGbool))
745+bool = UnsafeTypeExpression "bool"
746+int2 :: TypeExpression ('Required ('Null 'PGint2))
747+int2 = UnsafeTypeExpression "int2"
748+smallint :: TypeExpression ('Required ('Null 'PGint2))
749+smallint = UnsafeTypeExpression "smallint"
750+int4 :: TypeExpression ('Required ('Null 'PGint4))
751+int4 = UnsafeTypeExpression "int4"
752+int :: TypeExpression ('Required ('Null 'PGint4))
753+int = UnsafeTypeExpression "int"
754+integer :: TypeExpression ('Required ('Null 'PGint4))
755+integer = UnsafeTypeExpression "integer"
756+int8 :: TypeExpression ('Required ('Null 'PGint8))
757+int8 = UnsafeTypeExpression "int8"
758+bigint :: TypeExpression ('Required ('Null 'PGint8))
759+bigint = UnsafeTypeExpression "bigint"
760+numeric :: TypeExpression ('Required ('Null 'PGnumeric))
761+numeric = UnsafeTypeExpression "numeric"
762+float4 :: TypeExpression ('Required ('Null 'PGfloat4))
763+float4 = UnsafeTypeExpression "float4"
764+real :: TypeExpression ('Required ('Null 'PGfloat4))
765+real = UnsafeTypeExpression "real"
766+float8 :: TypeExpression ('Required ('Null 'PGfloat8))
767+float8 = UnsafeTypeExpression "float8"
768+doublePrecision :: TypeExpression ('Required ('Null 'PGfloat8))
769+doublePrecision = UnsafeTypeExpression "double precision"
770+serial2 :: TypeExpression ('Optional ('NotNull 'PGint2))
771+serial2 = UnsafeTypeExpression "serial2"
772+smallserial :: TypeExpression ('Optional ('NotNull 'PGint2))
773+smallserial = UnsafeTypeExpression "smallserial"
774+serial4 :: TypeExpression ('Optional ('NotNull 'PGint4))
775+serial4 = UnsafeTypeExpression "serial4"
776+serial :: TypeExpression ('Optional ('NotNull 'PGint4))
777+serial = UnsafeTypeExpression "serial"
778+serial8 :: TypeExpression ('Optional ('NotNull 'PGint8))
779+serial8 = UnsafeTypeExpression "serial8"
780+bigserial :: TypeExpression ('Optional ('NotNull 'PGint8))
781+bigserial = UnsafeTypeExpression "bigserial"
782+text :: TypeExpression ('Required ('Null 'PGtext))
783+text = UnsafeTypeExpression "text"
787+ -> TypeExpression ('Required ('Null ('PGchar n)))
788+char p = UnsafeTypeExpression $ "char(" <> renderNat p <> ")"
792+ -> TypeExpression ('Required ('Null ('PGchar n)))
793+character p = UnsafeTypeExpression $ "character(" <> renderNat p <> ")"
797+ -> TypeExpression ('Required ('Null ('PGvarchar n)))
798+varchar p = UnsafeTypeExpression $ "varchar(" <> renderNat p <> ")"
802+ -> TypeExpression ('Required ('Null ('PGvarchar n)))
803+characterVarying p = UnsafeTypeExpression $
804+ "character varying(" <> renderNat p <> ")"
805+bytea :: TypeExpression ('Required ('Null ('PGbytea)))
806+bytea = UnsafeTypeExpression "bytea"
807+timestamp :: TypeExpression ('Required ('Null ('PGtimestamp)))
808+timestamp = UnsafeTypeExpression "timestamp"
809+timestampWithTimeZone :: TypeExpression ('Required ('Null ('PGtimestamptz)))
810+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
811+date :: TypeExpression ('Required ('Null ('PGdate)))
812+date = UnsafeTypeExpression "date"
813+time :: TypeExpression ('Required ('Null ('PGtime)))
814+time = UnsafeTypeExpression "time"
815+timeWithTimeZone :: TypeExpression ('Required ('Null ('PGtimetz)))
816+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
817+interval :: TypeExpression ('Required ('Null ('PGinterval)))
818+interval = UnsafeTypeExpression "interval"
819+uuid :: TypeExpression ('Required ('Null ('PGuuid)))
820+uuid = UnsafeTypeExpression "uuid"
821+json :: TypeExpression ('Required ('Null ('PGjson)))
822+json = UnsafeTypeExpression "json"
823+jsonb :: TypeExpression ('Required ('Null ('PGjsonb)))
824+jsonb = UnsafeTypeExpression "jsonb"
827+ :: TypeExpression (optionality ('Null ty))
828+ -> TypeExpression (optionality ('NotNull ty))
829+notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <+> "NOT NULL"
831+ :: Expression '[] '[] 'Ungrouped ('Required ty)
832+ -> TypeExpression ('Required ty)
833+ -> TypeExpression ('Optional ty)
834+default_ x ty = UnsafeTypeExpression $
835+ renderTypeExpression ty <+> "DEFAULT" <+> renderExpression x
837+class PGTyped (ty :: PGType) where
838+ pgtype :: TypeExpression ('Required ('Null ty))
839+instance PGTyped 'PGbool where pgtype = bool
840+instance PGTyped 'PGint2 where pgtype = int2
841+instance PGTyped 'PGint4 where pgtype = int4
842+instance PGTyped 'PGint8 where pgtype = int8
843+instance PGTyped 'PGnumeric where pgtype = numeric
844+instance PGTyped 'PGfloat4 where pgtype = float4
845+instance PGTyped 'PGfloat8 where pgtype = float8
846+instance PGTyped 'PGtext where pgtype = text
847+instance KnownNat n => PGTyped ('PGchar n) where pgtype = char (Proxy @n)
848+instance KnownNat n => PGTyped ('PGvarchar n) where pgtype = varchar (Proxy @n)
849+instance PGTyped 'PGbytea where pgtype = bytea
850+instance PGTyped 'PGtimestamp where pgtype = timestamp
851+instance PGTyped 'PGtimestamptz where pgtype = timestampWithTimeZone
852+instance PGTyped 'PGdate where pgtype = date
853+instance PGTyped 'PGtime where pgtype = time
854+instance PGTyped 'PGtimetz where pgtype = timeWithTimeZone
855+instance PGTyped 'PGinterval where pgtype = interval
856+instance PGTyped 'PGuuid where pgtype = uuid
857+instance PGTyped 'PGjson where pgtype = json
858+instance PGTyped 'PGjsonb where pgtype = jsonb
860+{-----------------------------------------
862+-----------------------------------------}
865+ (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
866+ (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
867+ = UnsafeDefinition { renderDefinition :: ByteString }
869+instance Category Definition where
870+ id = UnsafeDefinition ";"
871+ ddl1 . ddl0 = UnsafeDefinition $
872+ renderDefinition ddl0 <+> renderDefinition ddl1
875+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
876+ (params :: [ColumnType])
877+ (columns :: [(Symbol,ColumnType)])
878+ = UnsafeManipulation { renderManipulation :: ByteString }
881+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
882+ (params :: [ColumnType])
883+ (columns :: [(Symbol,ColumnType)])
884+ = UnsafeQuery { renderQuery :: ByteString }
887+ :: Query schema params columns
888+ -> Manipulation schema params columns
889+query q = UnsafeManipulation $ renderQuery q <> ";"
891+union, unionAll, intersect, intersectAll, except, exceptAll
892+ :: Query schema params columns
893+ -> Query schema params columns
894+ -> Query schema params columns
895+q1 `union` q2 = UnsafeQuery $
896+ parenthesized (renderQuery q1)
898+ <+> parenthesized (renderQuery q2)
899+q1 `unionAll` q2 = UnsafeQuery $
900+ parenthesized (renderQuery q1)
901+ <+> "UNION" <+> "ALL"
902+ <+> parenthesized (renderQuery q2)
903+q1 `intersect` q2 = UnsafeQuery $
904+ parenthesized (renderQuery q1)
906+ <+> parenthesized (renderQuery q2)
907+q1 `intersectAll` q2 = UnsafeQuery $
908+ parenthesized (renderQuery q1)
909+ <+> "INTERSECT" <+> "ALL"
910+ <+> parenthesized (renderQuery q2)
911+q1 `except` q2 = UnsafeQuery $
912+ parenthesized (renderQuery q1)
914+ <+> parenthesized (renderQuery q2)
915+q1 `exceptAll` q2 = UnsafeQuery $
916+ parenthesized (renderQuery q1)
917+ <+> "EXCEPT" <+> "ALL"
918+ <+> parenthesized (renderQuery q2)
920+{-----------------------------------------
922+-----------------------------------------}
926+ => NP (Aliased (Expression params tables grouping)) (column ': columns)
927+ -> TableExpression params schema tables grouping
928+ -> Query schema params (column ': columns)
929+select list tabs = UnsafeQuery $
931+ <+> renderCommaSeparated (renderAliased renderExpression) list
932+ <+> renderTableExpression tabs
935+ :: (tables ~ '[table ::: columns])
936+ => TableExpression params schema tables 'Ungrouped
937+ -> Query schema params columns
938+selectStar tabs = UnsafeQuery $ "SELECT" <+> "*" <+> renderTableExpression tabs
941+ :: HasTable table tables columns
943+ -> TableExpression params schema tables 'Ungrouped
944+ -> Query schema params columns
945+selectDotStar table tables = UnsafeQuery $
946+ "SELECT" <+> renderAlias table <> ".*" <+> renderTableExpression tables
950+ => NP (Aliased (Expression params '[] 'Ungrouped)) columns
951+ -> Query schema params columns
952+selectRow row = UnsafeQuery $
953+ "SELECT" <+> renderCommaSeparated (renderAliased renderExpression) row
955+{-----------------------------------------
957+-----------------------------------------}
960+ :: (KnownSymbol table, SListI columns)
962+ -> NP (Aliased TypeExpression) (column ': columns)
963+ -> [TableConstraint schema (column ': columns)]
964+ -> Definition schema (Create table (column ': columns) schema)
965+createTable table columns constraints = UnsafeDefinition $
966+ "CREATE TABLE" <+> renderAlias table
968+ ( renderCommaSeparated renderColumnDef columns
969+ <> renderConstraints constraints )
972+ renderColumnDef :: Aliased TypeExpression x -> ByteString
973+ renderColumnDef (ty `As` column) =
974+ renderAlias column <+> renderTypeExpression ty
975+ renderConstraints :: [TableConstraint schema columns] -> ByteString
976+ renderConstraints = \case
978+ _ -> ", " <> commaSeparated (renderTableConstraint <$> constraints)
981+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
982+ (columns :: [(Symbol,ColumnType)])
983+ = UnsafeTableConstraint { renderTableConstraint :: ByteString }
986+ :: Condition '[] '[table ::: columns] 'Ungrouped
987+ -> TableConstraint schema columns
988+check condition = UnsafeTableConstraint $
989+ "CHECK" <+> parenthesized (renderExpression condition)
992+ (columns :: [(Symbol,ColumnType)])
993+ (columnty :: (Symbol,ColumnType))
996+ :: HasColumn column columns ty
998+ -> Column columns (column ::: ty)
1000+renderColumn :: Column columns columnty -> ByteString
1001+renderColumn (Column column) = renderAlias column
1004+ :: SListI subcolumns
1005+ => NP (Column columns) subcolumns
1006+ -> TableConstraint schema columns
1007+unique columns = UnsafeTableConstraint $
1008+ "UNIQUE" <+> parenthesized (renderCommaSeparated renderColumn columns)
1011+ :: (SListI subcolumns, NotNullTypes subcolumns)
1012+ => NP (Column columns) subcolumns
1013+ -> TableConstraint schema columns
1014+primaryKey columns = UnsafeTableConstraint $
1015+ "PRIMARY KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
1017+type family SameTypes columns0 columns1 :: Constraint where
1018+ SameTypes '[] '[] = ()
1019+ SameTypes ((column0 ::: ty0) ': columns0) ((column1 ::: ty1) ': columns1)
1020+ = (ty0 ~ ty1, SameTypes columns0 columns1)
1022+type family NotNullTypes columns :: Constraint where
1023+ NotNullTypes '[] = ()
1024+ NotNullTypes ((column ::: (optionality ('NotNull ty))) ': columns)
1025+ = NotNullTypes columns
1028+ :: ( HasTable reftable schema refcolumns
1029+ , SameTypes subcolumns refsubcolumns
1030+ , NotNullTypes subcolumns
1031+ , SListI subcolumns
1032+ , SListI refsubcolumns)
1033+ => NP (Column columns) subcolumns
1035+ -> NP (Column refcolumns) refsubcolumns
1036+ -> TableConstraint schema columns
1037+foreignKey columns reftable refcolumns = UnsafeTableConstraint $
1038+ "FOREIGN KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
1039+ <+> "REFERENCES" <+> renderAlias reftable
1040+ <+> parenthesized (renderCommaSeparated renderColumn refcolumns)
1042+{-----------------------------------------
1044+-----------------------------------------}
1047+ :: KnownSymbol table
1049+ -> Definition schema (Drop table schema)
1050+dropTable table = UnsafeDefinition $ "DROP TABLE" <+> renderAlias table <> ";"
1052+{-----------------------------------------
1054+-----------------------------------------}
1057+ :: HasTable table schema columns0
1059+ -> AlterTable columns0 columns1
1060+ -> Definition schema (Alter table schema columns1)
1061+alterTable table alteration = UnsafeDefinition $
1063+ <+> renderAlias table
1064+ <+> renderAlterTable alteration
1068+ :: (KnownSymbol table0, KnownSymbol table1)
1071+ -> Definition schema (Rename table0 table1 schema)
1072+alterTableRename table0 table1 = UnsafeDefinition $
1073+ "ALTER TABLE" <+> renderAlias table0
1074+ <+> "RENAME TO" <+> renderAlias table1 <> ";"
1077+ (columns0 :: [(Symbol, ColumnType)])
1078+ (columns1 :: [(Symbol, ColumnType)]) =
1079+ UnsafeAlterTable {renderAlterTable :: ByteString}
1082+ :: KnownSymbol column
1084+ -> TypeExpression ('Optional ty)
1085+ -> AlterTable columns (Create column ('Optional ty) columns)
1086+addColumnDefault column ty = UnsafeAlterTable $
1087+ "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
1090+ :: KnownSymbol column
1092+ -> TypeExpression ('Required ('Null ty))
1093+ -> AlterTable columns ((column ::: 'Required ('Null ty)) ': columns)
1094+addColumnNull column ty = UnsafeAlterTable $
1095+ "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
1098+ :: KnownSymbol column
1100+ -> AlterTable columns (Drop column columns)
1101+dropColumn column = UnsafeAlterTable $
1102+ "DROP COLUMN" <+> renderAlias column
1105+ :: (KnownSymbol column0, KnownSymbol column1)
1108+ -> AlterTable columns (Rename column0 column1 columns)
1109+renameColumn column0 column1 = UnsafeAlterTable $
1110+ "RENAME COLUMN" <+> renderAlias column0 <+> "TO" <+> renderAlias column1
1113+ :: (KnownSymbol column, HasColumn column columns ty0)
1115+ -> AlterColumn ty0 ty1
1116+ -> AlterTable columns (Alter column columns ty1)
1117+alterColumn column alteration = UnsafeAlterTable $
1118+ "ALTER COLUMN" <+> renderAlias column <+> renderAlterColumn alteration
1120+data AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
1121+ UnsafeAlterColumn {renderAlterColumn :: ByteString}
1124+ :: Expression '[] '[] 'Ungrouped ('Required ty)
1125+ -> AlterColumn ('Required ty) ('Optional ty)
1126+setDefault expression = UnsafeAlterColumn $
1127+ "SET DEFAULT" <+> renderExpression expression
1129+dropDefault :: AlterColumn ('Optional ty) ('Required ty)
1130+dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
1133+ :: AlterColumn (optionality ('Null ty)) (optionality ('NotNull ty))
1134+setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
1137+ :: AlterColumn (optionality ('NotNull ty)) (optionality ('Null ty))
1138+dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
1141+ :: TypeExpression (optionality (nullity ty1))
1142+ -> AlterColumn (optionality (nullity ty0)) (optionality (nullity ty1))
1143+alterType ty = UnsafeAlterColumn $ "TYPE " <> renderTypeExpression ty
1145+{-----------------------------------------
1147+-----------------------------------------}
1150+ :: (SListI columns, SListI results, HasTable table schema columns)
1152+ -> ValuesClause schema params columns
1153+ -> ConflictClause params columns
1154+ -> ReturningClause params columns results
1155+ -> Manipulation schema params results
1156+insertInto table insert conflict returning = UnsafeManipulation $
1157+ "INSERT" <+> "INTO" <+> renderAlias table
1158+ <+> renderValuesClause insert
1159+ <> renderConflictClause conflict
1160+ <> renderReturningClause returning
1163+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
1164+ (params :: [ColumnType])
1165+ (columns :: [(Symbol,ColumnType)])
1167+ (NP (Aliased (Expression params '[] 'Ungrouped)) columns)
1168+ [NP (Aliased (Expression params '[] 'Ungrouped)) columns]
1169+ | ValuesQuery (Query schema params columns)
1173+ => ValuesClause schema params columns
1175+renderValuesClause = \case
1177+ parenthesized (renderCommaSeparated renderAliasPart row)
1180+ (parenthesized . renderCommaSeparated renderValuePart <$> row:rows)
1182+ renderAliasPart, renderValuePart
1183+ :: Aliased (Expression params '[] 'Ungrouped) ty -> ByteString
1184+ renderAliasPart (_ `As` name) = renderAlias name
1185+ renderValuePart (value `As` _) = renderExpression value
1186+ ValuesQuery q -> renderQuery q
1188+data ReturningClause
1189+ (params :: [ColumnType])
1190+ (columns :: [(Symbol,ColumnType)])
1191+ (results :: [(Symbol,ColumnType)])
1193+ ReturningStar :: ReturningClause params columns columns
1195+ :: NP (Aliased (Expression params '[table ::: columns] 'Ungrouped))
1197+ -> ReturningClause params columns results
1199+renderReturningClause
1201+ => ReturningClause params columns results
1203+renderReturningClause = \case
1204+ ReturningStar -> " RETURNING *;"
1205+ Returning Nil -> ";"
1206+ Returning results -> " RETURNING"
1207+ <+> renderCommaSeparated (renderAliased renderExpression) results <> ";"
1209+data ConflictClause params columns where
1210+ Conflict :: ConflictClause params columns
1211+ OnConflictDoNothing :: ConflictClause params columns
1213+ :: NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1214+ -> Condition params '[table ::: columns] 'Ungrouped
1215+ -> ConflictClause params columns
1217+renderConflictClause
1219+ => ConflictClause params columns
1221+renderConflictClause = \case
1223+ OnConflictDoNothing -> " ON CONFLICT DO NOTHING"
1224+ OnConflictDoUpdate updates wh
1225+ -> " ON CONFLICT DO UPDATE SET"
1226+ <+> renderCommaSeparatedMaybe renderUpdateExpression updates
1227+ <+> "WHERE" <+> renderExpression wh
1229+{-----------------------------------------
1231+-----------------------------------------}
1233+data UpdateExpression params table ty
1235+ | Set (Expression params table 'Ungrouped ty)
1237+renderUpdateExpression
1238+ :: Aliased (UpdateExpression params '[table ::: columns]) column
1239+ -> Maybe ByteString
1240+renderUpdateExpression = \case
1241+ Same `As` _ -> Nothing
1242+ Set expression `As` column -> Just $
1243+ renderAlias column <+> "=" <+> renderExpression expression
1246+ :: (HasTable table schema columns, SListI columns, SListI results)
1248+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1249+ -> Condition params '[table ::: columns] 'Ungrouped
1250+ -> ReturningClause params columns results
1251+ -> Manipulation schema params results
1252+update table columns wh returning = UnsafeManipulation $
1254+ <+> renderAlias table
1256+ <+> renderCommaSeparatedMaybe renderUpdateExpression columns
1257+ <+> "WHERE" <+> renderExpression wh
1258+ <> renderReturningClause returning
1260+{-----------------------------------------
1262+-----------------------------------------}
1265+ :: HasTable table schema columns
1267+ -> Condition params '[table ::: columns] 'Ungrouped
1268+ -> Manipulation schema params '[]
1269+deleteFrom table wh = UnsafeManipulation $
1270+ "DELETE FROM" <+> renderAlias table
1271+ <+> "WHERE" <+> renderExpression wh <> ";"
1273+{-----------------------------------------
1274+render helper functions
1275+-----------------------------------------}
1277+parenthesized :: ByteString -> ByteString
1278+parenthesized str = "(" <> str <> ")"
1280+(<+>) :: ByteString -> ByteString -> ByteString
1281+str1 <+> str2 = str1 <> " " <> str2
1283+commaSeparated :: [ByteString] -> ByteString
1284+commaSeparated = ByteString.intercalate ", "
1286+renderCommaSeparated
1288+ => (forall x. expression x -> ByteString)
1289+ -> NP expression xs -> ByteString
1290+renderCommaSeparated render
1293+ . hmap (K . render)
1295+renderCommaSeparatedMaybe
1297+ => (forall x. expression x -> Maybe ByteString)
1298+ -> NP expression xs -> ByteString
1299+renderCommaSeparatedMaybe render
1303+ . hmap (K . render)
1305+renderSymbol :: KnownSymbol str => Proxy# str -> ByteString
1306+renderSymbol p = fromString $ symbolVal' p
1308+renderNat :: KnownNat n => proxy n -> ByteString
1309+renderNat (_ :: proxy n) = fromString (show (natVal' (proxy# :: Proxy# n)))