@@ -0,0 +1,1218 @@
1+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
6+ , FunctionalDependencies
13+ , ScopedTypeVariables
17+ , UndecidableInstances
20+module Squeel.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 Squeel.PostgreSQL.Schema
40+{-----------------------------------------
42+-----------------------------------------}
45+ (params :: [ColumnType])
46+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
47+ (grouping :: Grouping)
49+ = UnsafeExpression { renderExpression :: ByteString }
52+class KnownNat n => HasParameter (n :: Nat) params ty | n params -> ty where
53+ param :: Proxy# n -> Expression params tables grouping ty
54+ param p = UnsafeExpression $ ("$" <>) $ fromString $ show $ natVal' p
55+instance {-# OVERLAPPING #-} HasParameter 1 (ty1:tys) ty1
56+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) params ty)
57+ => HasParameter n (ty' : params) ty
59+param1 :: Expression (ty1:tys) tables grouping ty1
60+param1 = param (proxy# :: Proxy# 1)
61+param2 :: Expression (ty1:ty2:tys) tables grouping ty2
62+param2 = param (proxy# :: Proxy# 2)
63+param3 :: Expression (ty1:ty2:ty3:tys) tables grouping ty3
64+param3 = param (proxy# :: Proxy# 3)
65+param4 :: Expression (ty1:ty2:ty3:ty4:tys) tables grouping ty4
66+param4 = param (proxy# :: Proxy# 4)
67+param5 :: Expression (ty1:ty2:ty3:ty4:ty5:tys) tables grouping ty5
68+param5 = param (proxy# :: Proxy# 5)
70+class KnownSymbol column => HasColumn column columns ty
71+ | column columns -> ty where
74+ -> Expression params '[table ::: columns] 'Ungrouped ty
75+ getColumn column = UnsafeExpression $ renderSymbol column
76+instance {-# OVERLAPPING #-} KnownSymbol column
77+ => HasColumn column ((column ::: optionality ty) ': tys) ('Required ty)
78+instance {-# OVERLAPPABLE #-} (KnownSymbol column, HasColumn column table ty)
79+ => HasColumn column (ty' ': table) ty
81+instance (HasColumn column columns ty, tables ~ '[table ::: columns])
82+ => IsLabel column (Expression params tables 'Ungrouped ty) where
83+ fromLabel = getColumn
85+instance (HasTable table tables columns, HasColumn column columns ty)
86+ => IsTableColumn table column (Expression params tables 'Ungrouped ty) where
87+ table &. column = UnsafeExpression $
88+ renderAlias table <> "." <> renderAlias column
90+def :: Expression params '[] grouping ('Optional (nullity ty))
91+def = UnsafeExpression "DEFAULT"
94+ :: Expression params '[] grouping ('Required (nullity ty))
95+ -> Expression params '[] grouping ('Optional (nullity ty))
96+unDef = UnsafeExpression . renderExpression
98+null :: Expression params tables grouping (optionality ('Null ty))
99+null = UnsafeExpression "NULL"
102+ :: Expression params tables grouping (optionality ('NotNull ty))
103+ -> Expression params tables grouping (optionality ('Null ty))
104+unNull = UnsafeExpression . renderExpression
107+ :: [Expression params tables grouping ('Required ('Null x))]
108+ -> Expression params tables grouping ('Required ('NotNull x))
109+ -> Expression params tables grouping ('Required ('NotNull x))
110+coalesce nulls isn'tNull = UnsafeExpression $
111+ "COALESCE" <> parenthesized (commaSeparated
112+ ((renderExpression <$> nulls) <> [renderExpression isn'tNull]))
116+ -> Expression params tables grouping ty0
117+ -> Expression params tables grouping ty1
118+ -> Expression params tables grouping ty2
119+unsafeBinaryOp op x y = UnsafeExpression $ parenthesized $
120+ renderExpression x <+> op <+> renderExpression y
124+ -> Expression params tables grouping ty0
125+ -> Expression params tables grouping ty1
126+unsafeUnaryOp op x = UnsafeExpression $ parenthesized $
127+ op <+> renderExpression x
131+ -> Expression params tables grouping xty
132+ -> Expression params tables grouping yty
133+unsafeFunction fun x = UnsafeExpression $
134+ fun <> parenthesized (renderExpression x)
137+ => Num (Expression params tables grouping ('Required (nullity ty))) where
138+ (+) = unsafeBinaryOp "+"
139+ (-) = unsafeBinaryOp "-"
140+ (*) = unsafeBinaryOp "*"
141+ abs = unsafeFunction "abs"
142+ signum = unsafeFunction "sign"
145+ . (<> if decimal (Proxy :: Proxy ty) then "." else "")
149+instance PGFractional ty => Fractional
150+ (Expression params tables grouping ('Required (nullity ty))) where
151+ (/) = unsafeBinaryOp "/"
152+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
154+instance (PGFloating ty, PGCast 'PGNumeric ty, PGTyped ty) => Floating
155+ (Expression params tables grouping ('Required (nullity ty))) where
156+ pi = UnsafeExpression "pi()"
157+ exp = unsafeFunction "exp"
158+ log = unsafeFunction "ln"
159+ sqrt = unsafeFunction "sqrt"
160+ b ** x = UnsafeExpression $
161+ "power(" <> renderExpression b <> ", " <> renderExpression x <> ")"
162+ logBase b y = cast pgtype $ logBaseNumeric b y
165+ :: Expression params tables grouping ('Required (nullity ty))
166+ -> Expression params tables grouping ('Required (nullity ty))
167+ -> Expression params tables grouping ('Required (nullity 'PGNumeric))
168+ logBaseNumeric b' y' = UnsafeExpression $ mconcat
170+ , renderExpression b' <> "::numeric"
172+ , renderExpression y' <> "::numeric"
175+ sin = unsafeFunction "sin"
176+ cos = unsafeFunction "cos"
177+ tan = unsafeFunction "tan"
178+ asin = unsafeFunction "asin"
179+ acos = unsafeFunction "acos"
180+ atan = unsafeFunction "atan"
181+ sinh x = (exp x - exp (-x)) / 2
182+ cosh x = (exp x + exp (-x)) / 2
183+ tanh x = sinh x / cosh x
184+ asinh x = log (x + sqrt (x*x + 1))
185+ acosh x = log (x + sqrt (x*x - 1))
186+ atanh x = log ((1 + x) / (1 - x)) / 2
190+ => Expression params tables grouping ('Required (nullity float))
191+ -> Expression params tables grouping ('Required (nullity float))
192+ -> Expression params tables grouping ('Required (nullity float))
193+atan2 y x = UnsafeExpression $
194+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
196+class PGCast (ty0 :: PGType) (ty1 :: PGType) where
198+ :: TypeExpression ('Required ('Null ty1))
199+ -> Expression params tables grouping ('Required (nullity ty0))
200+ -> Expression params tables grouping ('Required (nullity ty1))
201+ cast ty x = UnsafeExpression $
202+ "(" <> renderExpression x <> "::" <> renderTypeExpression ty <> ")"
203+instance PGCast 'PGInt2 'PGInt2
204+instance PGCast 'PGInt2 'PGInt4
205+instance PGCast 'PGInt2 'PGInt8
206+instance PGCast 'PGInt4 'PGInt2
207+instance PGCast 'PGInt4 'PGInt4
208+instance PGCast 'PGInt4 'PGInt8
209+instance PGCast 'PGInt8 'PGInt2
210+instance PGCast 'PGInt8 'PGInt4
211+instance PGCast 'PGInt8 'PGInt8
212+instance PGCast 'PGInt8 'PGFloat4
213+instance PGCast 'PGInt8 'PGFloat8
214+instance PGCast 'PGInt8 'PGNumeric
217+ :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
218+ => Expression params tables grouping ('Required (nullity ty))
219+ -> Expression params tables grouping ('Required (nullity ty))
220+ -> Expression params tables grouping ('Required (nullity ty))
221+div = unsafeBinaryOp "/"
224+ :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
225+ => Expression params tables grouping ('Required (nullity ty))
226+ -> Expression params tables grouping ('Required (nullity ty))
227+ -> Expression params tables grouping ('Required (nullity ty))
228+mod = unsafeBinaryOp "%"
231+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
232+ => Expression params tables grouping ('Required (nullity frac))
233+ -> Expression params tables grouping ('Required (nullity int))
234+truncate = cast pgtype . truncate'
237+ :: Expression params tables grouping ('Required (nullity frac))
238+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
239+ truncate' = unsafeFunction "trunc"
242+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
243+ => Expression params tables grouping ('Required (nullity frac))
244+ -> Expression params tables grouping ('Required (nullity int))
245+round = cast pgtype . round'
248+ :: Expression params tables grouping ('Required (nullity frac))
249+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
250+ round' = unsafeFunction "round"
253+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
254+ => Expression params tables grouping ('Required (nullity frac))
255+ -> Expression params tables grouping ('Required (nullity int))
256+ceiling = cast pgtype . ceiling'
259+ :: Expression params tables grouping ('Required (nullity frac))
260+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
261+ ceiling' = unsafeFunction "ceiling"
264+ (Expression params tables grouping ('Required (nullity 'PGText))) where
265+ fromString str = UnsafeExpression $
266+ "E\'" <> fromString (escape =<< str) <> "\'"
279+type Condition params tables grouping =
280+ Expression params tables grouping ('Required ('NotNull 'PGBool))
282+true :: Condition params tables grouping
283+true = UnsafeExpression "TRUE"
285+false :: Condition params tables grouping
286+false = UnsafeExpression "FALSE"
289+ :: Condition params tables grouping
290+ -> Condition params tables grouping
291+not_ = unsafeUnaryOp "NOT"
294+ :: Condition params tables grouping
295+ -> Condition params tables grouping
296+ -> Condition params tables grouping
297+(&&*) = unsafeBinaryOp "AND"
300+ :: Condition params tables grouping
301+ -> Condition params tables grouping
302+ -> Condition params tables grouping
303+(||*) = unsafeBinaryOp "OR"
306+ :: [(Condition params tables grouping, Expression params tables grouping ty)]
307+ -> Expression params tables grouping ty
308+ -> Expression params tables grouping ty
309+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
313+ [ " WHEN ", renderExpression when_
314+ , " THEN ", renderExpression then_
316+ | (when_,then_) <- whenThens
318+ , " ELSE ", renderExpression else_
323+ :: Condition params tables grouping
324+ -> Expression params tables grouping ty
325+ -> Expression params tables grouping ty
326+ -> Expression params tables grouping ty
327+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
330+ :: Expression params tables grouping ('Required (nullity ty))
331+ -> Expression params tables grouping ('Required (nullity ty))
332+ -> Expression params tables grouping ('Required (nullity 'PGBool))
333+(==*) = unsafeBinaryOp "="
336+ :: Expression params tables grouping ('Required (nullity ty))
337+ -> Expression params tables grouping ('Required (nullity ty))
338+ -> Expression params tables grouping ('Required (nullity 'PGBool))
339+(/=*) = unsafeBinaryOp "<>"
342+ :: Expression params tables grouping ('Required (nullity ty))
343+ -> Expression params tables grouping ('Required (nullity ty))
344+ -> Expression params tables grouping ('Required (nullity 'PGBool))
345+(>=*) = unsafeBinaryOp ">="
348+ :: Expression params tables grouping ('Required (nullity ty))
349+ -> Expression params tables grouping ('Required (nullity ty))
350+ -> Expression params tables grouping ('Required (nullity 'PGBool))
351+(<*) = unsafeBinaryOp "<"
354+ :: Expression params tables grouping ('Required (nullity ty))
355+ -> Expression params tables grouping ('Required (nullity ty))
356+ -> Expression params tables grouping ('Required (nullity 'PGBool))
357+(<=*) = unsafeBinaryOp "<="
360+ :: Expression params tables grouping ('Required (nullity ty))
361+ -> Expression params tables grouping ('Required (nullity ty))
362+ -> Expression params tables grouping ('Required (nullity 'PGBool))
363+(>*) = unsafeBinaryOp ">"
365+data SortExpression params tables grouping where
367+ :: Expression params tables grouping ty
368+ -> SortExpression params tables grouping
370+ :: Expression params tables grouping ty
371+ -> SortExpression params tables grouping
373+renderSortExpression :: SortExpression params tables grouping -> ByteString
374+renderSortExpression = \case
375+ Asc expression -> renderExpression expression <+> "ASC"
376+ Desc expression -> renderExpression expression <+> "DESC"
378+{-----------------------------------------
380+-----------------------------------------}
383+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
384+ (tabcol :: (Symbol,Symbol)) where
386+ :: (tables ~ '[table ::: columns], HasColumn column columns ty)
388+ -> By tables '(table, column)
390+ :: (HasTable table tables columns, HasColumn column columns ty)
391+ => (Alias table, Alias column)
392+ -> By tables '(table, column)
394+renderBy :: By tables tabcolty -> ByteString
396+ By column -> renderAlias column
397+ By2 (table, column) -> renderAlias table <> "." <> renderAlias column
399+data GroupByClause tables grouping where
400+ NoGroups :: GroupByClause tables 'Ungrouped
403+ => NP (By tables) bys
404+ -> GroupByClause tables ('Grouped bys)
406+renderGroupByClause :: GroupByClause tables grouping -> ByteString
407+renderGroupByClause = \case
410+ Group bys -> " GROUP BY" <+> renderCommaSeparated renderBy bys
412+data HavingClause params tables grouping where
413+ NoHaving :: HavingClause params tables 'Ungrouped
415+ :: [Condition params tables ('Grouped bys)]
416+ -> HavingClause params tables ('Grouped bys)
418+renderHavingClause :: HavingClause params tables grouping -> ByteString
419+renderHavingClause = \case
422+ Having conditions ->
423+ " HAVING" <+> commaSeparated (renderExpression <$> conditions)
425+class (KnownSymbol table, KnownSymbol column)
426+ => GroupedBy table column bys where
428+ :: (tables ~ '[table ::: columns], HasColumn column columns ty)
430+ -> Expression params tables ('Grouped bys) ty
431+ getGroup1 column = UnsafeExpression $ fromString (symbolVal' column)
433+ :: (HasTable table tables columns, HasColumn column columns ty)
436+ -> Expression params tables ('Grouped bys) ty
437+ getGroup2 table column = UnsafeExpression $
438+ renderAlias table <> "." <> renderAlias column
439+instance {-# OVERLAPPING #-} (KnownSymbol table, KnownSymbol column)
440+ => GroupedBy table column ('(table,column) ': bys)
441+instance {-# OVERLAPPABLE #-}
443+ , KnownSymbol column
444+ , GroupedBy table column bys
445+ ) => GroupedBy table column (tabcol ': bys)
448+ ( tables ~ '[table ::: columns]
449+ , HasColumn column columns ty
450+ , GroupedBy table column bys
452+ (Expression params tables ('Grouped bys) ty) where fromLabel = getGroup1
455+ ( HasTable table tables columns
456+ , HasColumn column columns ty
457+ , GroupedBy table column bys
458+ ) => IsTableColumn table column
459+ (Expression params tables ('Grouped bys) ty) where (&.) = getGroup2
463+ -> Expression params tables 'Ungrouped xty
464+ -> Expression params tables ('Grouped bys) yty
465+unsafeAggregate fun x = UnsafeExpression $ mconcat
466+ [fun, "(", renderExpression x, ")"]
470+ => Expression params tables 'Ungrouped ('Required (nullity ty))
471+ -> Expression params tables ('Grouped bys) ('Required (nullity ty))
472+sum_ = unsafeAggregate "sum"
474+{-----------------------------------------
476+-----------------------------------------}
479+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
480+ (columns :: [(Symbol,ColumnType)])
481+ = UnsafeTable { renderTable :: ByteString }
484+class KnownSymbol table => HasTable table tables columns
485+ | table tables -> columns where
486+ getTable :: Proxy# table -> Table tables columns
487+ getTable table = UnsafeTable $ fromString $ symbolVal' table
488+instance {-# OVERLAPPING #-} KnownSymbol table
489+ => HasTable table ((table ::: columns) ': tables) columns
490+instance {-# OVERLAPPABLE #-}
491+ (KnownSymbol table, HasTable table schema columns)
492+ => HasTable table (table' ': schema) columns
494+instance HasTable table schema columns
495+ => IsLabel table (Table schema columns) where
496+ fromLabel = getTable
498+data TableReference params schema tables where
500+ :: Aliased (Table schema) table
501+ -> TableReference params schema '[table]
503+ :: Aliased (Query schema params) table
504+ -> TableReference params schema '[table]
506+ :: TableReference params schema right
507+ -> TableReference params schema left
508+ -> TableReference params schema (Join left right)
510+ :: TableReference params schema right
511+ -> Condition params (Join left right) 'Ungrouped
512+ -> TableReference params schema left
513+ -> TableReference params schema (Join left right)
515+ :: TableReference params schema right
516+ -> Condition params (Join left right) 'Ungrouped
517+ -> TableReference params schema left
518+ -> TableReference params schema (Join left (NullifyTables right))
520+ :: TableReference params schema right
521+ -> Condition params (Join left right) 'Ungrouped
522+ -> TableReference params schema left
523+ -> TableReference params schema (Join (NullifyTables left) right)
525+ :: TableReference params schema right
526+ -> Condition params (Join left right) 'Ungrouped
527+ -> TableReference params schema left
528+ -> TableReference params schema
529+ (Join (NullifyTables left) (NullifyTables right))
531+renderTableReference :: TableReference params schema tables -> ByteString
532+renderTableReference = \case
533+ Table table -> renderAliased renderTable table
534+ Subquery selection -> renderAliased renderQuery selection
535+ CrossJoin right left ->
536+ renderTableReference left <+> "CROSS JOIN" <+> renderTableReference right
537+ InnerJoin right on left -> renderJoin "INNER JOIN" right on left
538+ LeftOuterJoin right on left -> renderJoin "LEFT OUTER JOIN" right on left
539+ RightOuterJoin right on left -> renderJoin "RIGHT OUTER JOIN" right on left
540+ FullOuterJoin right on left -> renderJoin "FULL OUTER JOIN" right on left
542+ renderJoin op right on left =
543+ renderTableReference left <+> op <+> renderTableReference right
544+ <+> "ON" <+> renderExpression on
547+ (params :: [ColumnType])
548+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
549+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
550+ (grouping :: Grouping)
552+ { fromClause :: TableReference params schema tables
553+ , whereClause :: [Condition params tables 'Ungrouped]
554+ , groupByClause :: GroupByClause tables grouping
555+ , havingClause :: HavingClause params tables grouping
556+ , orderByClause :: [SortExpression params tables grouping]
557+ , limitClause :: [Word64]
558+ , offsetClause :: [Word64]
561+renderTableExpression
562+ :: TableExpression params schema tables grouping
564+renderTableExpression
565+ (TableExpression tables whs' grps' hvs' srts' lims' offs') = mconcat
566+ [ "FROM ", renderTableReference tables
568+ , renderGroupByClause grps'
569+ , renderHavingClause hvs'
571+ , renderLimits lims'
572+ , renderOffsets offs'
575+ renderWheres = \case
577+ wh:[] -> " WHERE" <+> renderExpression wh
578+ wh:whs -> " WHERE" <+> renderExpression (foldr (&&*) wh whs)
582+ <+> commaSeparated (renderSortExpression <$> srts)
583+ renderLimits = \case
585+ lims -> " LIMIT" <+> fromString (show (minimum lims))
586+ renderOffsets = \case
588+ offs -> " OFFSET" <+> fromString (show (sum offs))
591+ :: TableReference params schema tables
592+ -> TableExpression params schema tables 'Ungrouped
593+from tables = TableExpression tables [] NoGroups NoHaving [] [] []
596+ :: Condition params tables 'Ungrouped
597+ -> TableExpression params schema tables grouping
598+ -> TableExpression params schema tables grouping
599+where_ wh tables = tables {whereClause = wh : whereClause tables}
603+ => NP (By tables) bys
604+ -> TableExpression params schema tables 'Ungrouped
605+ -> TableExpression params schema tables ('Grouped bys)
606+group bys tables = TableExpression
607+ { fromClause = fromClause tables
608+ , whereClause = whereClause tables
609+ , groupByClause = Group bys
610+ , havingClause = Having []
611+ , orderByClause = []
612+ , limitClause = limitClause tables
613+ , offsetClause = offsetClause tables
617+ :: Condition params tables ('Grouped bys)
618+ -> TableExpression params schema tables ('Grouped bys)
619+ -> TableExpression params schema tables ('Grouped bys)
620+having hv tables = tables
621+ { havingClause = case havingClause tables of Having hvs -> Having (hv:hvs) }
624+ :: [SortExpression params tables grouping]
625+ -> TableExpression params schema tables grouping
626+ -> TableExpression params schema tables grouping
627+orderBy srts tables = tables {orderByClause = orderByClause tables ++ srts}
631+ -> TableExpression params schema tables grouping
632+ -> TableExpression params schema tables grouping
633+limit lim tables = tables {limitClause = lim : limitClause tables}
637+ -> TableExpression params schema tables grouping
638+ -> TableExpression params schema tables grouping
639+offset off tables = tables {offsetClause = off : offsetClause tables}
641+{-----------------------------------------
643+-----------------------------------------}
645+newtype TypeExpression (ty :: ColumnType)
646+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
649+bool :: TypeExpression ('Required ('Null 'PGBool))
650+bool = UnsafeTypeExpression "bool"
651+int2 :: TypeExpression ('Required ('Null 'PGInt2))
652+int2 = UnsafeTypeExpression "int2"
653+smallint :: TypeExpression ('Required ('Null 'PGInt2))
654+smallint = UnsafeTypeExpression "smallint"
655+int4 :: TypeExpression ('Required ('Null 'PGInt4))
656+int4 = UnsafeTypeExpression "int4"
657+int :: TypeExpression ('Required ('Null 'PGInt4))
658+int = UnsafeTypeExpression "int"
659+integer :: TypeExpression ('Required ('Null 'PGInt4))
660+integer = UnsafeTypeExpression "integer"
661+int8 :: TypeExpression ('Required ('Null 'PGInt8))
662+int8 = UnsafeTypeExpression "int8"
663+bigint :: TypeExpression ('Required ('Null 'PGInt8))
664+bigint = UnsafeTypeExpression "bigint"
665+numeric :: TypeExpression ('Required ('Null 'PGNumeric))
666+numeric = UnsafeTypeExpression "numeric"
667+float4 :: TypeExpression ('Required ('Null 'PGFloat4))
668+float4 = UnsafeTypeExpression "float4"
669+real :: TypeExpression ('Required ('Null 'PGFloat4))
670+real = UnsafeTypeExpression "real"
671+float8 :: TypeExpression ('Required ('Null 'PGFloat8))
672+float8 = UnsafeTypeExpression "float8"
673+doublePrecision :: TypeExpression ('Required ('Null 'PGFloat8))
674+doublePrecision = UnsafeTypeExpression "double precision"
675+serial2 :: TypeExpression ('Optional ('NotNull 'PGInt2))
676+serial2 = UnsafeTypeExpression "serial2"
677+smallserial :: TypeExpression ('Optional ('NotNull 'PGInt2))
678+smallserial = UnsafeTypeExpression "smallserial"
679+serial4 :: TypeExpression ('Optional ('NotNull 'PGInt4))
680+serial4 = UnsafeTypeExpression "serial4"
681+serial :: TypeExpression ('Optional ('NotNull 'PGInt4))
682+serial = UnsafeTypeExpression "serial"
683+serial8 :: TypeExpression ('Optional ('NotNull 'PGInt8))
684+serial8 = UnsafeTypeExpression "serial8"
685+bigserial :: TypeExpression ('Optional ('NotNull 'PGInt8))
686+bigserial = UnsafeTypeExpression "bigserial"
687+money :: TypeExpression ('Required ('Null 'PGMoney))
688+money = UnsafeTypeExpression "money"
689+text :: TypeExpression ('Required ('Null 'PGText))
690+text = UnsafeTypeExpression "text"
694+ -> TypeExpression ('Required ('Null ('PGChar n)))
695+char (_ :: proxy n) = UnsafeTypeExpression $
696+ "char(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
700+ -> TypeExpression ('Required ('Null ('PGChar n)))
701+character (_ :: proxy n) = UnsafeTypeExpression $
702+ "character(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
706+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
707+varchar (_ :: proxy n) = UnsafeTypeExpression $
708+ "varchar(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
712+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
713+characterVarying (_ :: proxy n) = UnsafeTypeExpression $
714+ "character varying(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
715+bytea :: TypeExpression ('Required ('Null ('PGBytea)))
716+bytea = UnsafeTypeExpression "bytea"
717+timestamp :: TypeExpression ('Required ('Null ('PGTimestamp)))
718+timestamp = UnsafeTypeExpression "timestamp"
719+timestampWithTimeZone :: TypeExpression ('Required ('Null ('PGTimestampTZ)))
720+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
721+date :: TypeExpression ('Required ('Null ('PGDate)))
722+date = UnsafeTypeExpression "date"
723+time :: TypeExpression ('Required ('Null ('PGTime)))
724+time = UnsafeTypeExpression "time"
725+timeWithTimeZone :: TypeExpression ('Required ('Null ('PGTimeTZ)))
726+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
727+interval :: TypeExpression ('Required ('Null ('PGInterval)))
728+interval = UnsafeTypeExpression "interval"
729+uuid :: TypeExpression ('Required ('Null ('PGUuid)))
730+uuid = UnsafeTypeExpression "uuid"
731+json :: TypeExpression ('Required ('Null ('PGJson)))
732+json = UnsafeTypeExpression "json"
733+jsonb :: TypeExpression ('Required ('Null ('PGJsonb)))
734+jsonb = UnsafeTypeExpression "jsonb"
737+ :: TypeExpression (optionality ('Null ty))
738+ -> TypeExpression (optionality ('NotNull ty))
739+notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <+> "NOT NULL"
741+ :: Expression '[] '[] 'Ungrouped ('Required ty)
742+ -> TypeExpression ('Required ty)
743+ -> TypeExpression ('Optional ty)
744+default_ x ty = UnsafeTypeExpression $
745+ renderTypeExpression ty <+> "DEFAULT" <+> renderExpression x
747+class PGTyped (ty :: PGType) where
748+ pgtype :: TypeExpression ('Required ('Null ty))
749+instance PGTyped 'PGBool where pgtype = bool
750+instance PGTyped 'PGInt2 where pgtype = int2
751+instance PGTyped 'PGInt4 where pgtype = int4
752+instance PGTyped 'PGInt8 where pgtype = int8
753+instance PGTyped 'PGNumeric where pgtype = numeric
754+instance PGTyped 'PGFloat4 where pgtype = float4
755+instance PGTyped 'PGFloat8 where pgtype = float8
756+instance PGTyped 'PGMoney where pgtype = money
757+instance PGTyped 'PGText where pgtype = text
758+instance KnownNat n => PGTyped ('PGChar n) where pgtype = char (Proxy @n)
759+instance KnownNat n => PGTyped ('PGVarChar n) where pgtype = varchar (Proxy @n)
760+instance PGTyped 'PGBytea where pgtype = bytea
761+instance PGTyped 'PGTimestamp where pgtype = timestamp
762+instance PGTyped 'PGTimestampTZ where pgtype = timestampWithTimeZone
763+instance PGTyped 'PGDate where pgtype = date
764+instance PGTyped 'PGTime where pgtype = time
765+instance PGTyped 'PGTimeTZ where pgtype = timeWithTimeZone
766+instance PGTyped 'PGInterval where pgtype = interval
767+instance PGTyped 'PGUuid where pgtype = uuid
768+instance PGTyped 'PGJson where pgtype = json
769+instance PGTyped 'PGJsonb where pgtype = jsonb
771+{-----------------------------------------
773+-----------------------------------------}
776+ (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
777+ (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
778+ = UnsafeDefinition { renderDefinition :: ByteString }
780+instance Category Definition where
781+ id = UnsafeDefinition ";"
782+ ddl1 . ddl0 = UnsafeDefinition $
783+ renderDefinition ddl0 <+> renderDefinition ddl1
786+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
787+ (params :: [ColumnType])
788+ (columns :: [(Symbol,ColumnType)])
789+ = UnsafeManipulation { renderManipulation :: ByteString }
792+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
793+ (params :: [ColumnType])
794+ (columns :: [(Symbol,ColumnType)])
795+ = UnsafeQuery { renderQuery :: ByteString }
798+ :: Query schema params columns
799+ -> Manipulation schema params columns
800+query q = UnsafeManipulation $ renderQuery q <> ";"
802+union, unionAll, intersect, intersectAll, except, exceptAll
803+ :: Query schema params columns
804+ -> Query schema params columns
805+ -> Query schema params columns
806+q1 `union` q2 = UnsafeQuery $
807+ parenthesized (renderQuery q1)
809+ <+> parenthesized (renderQuery q2)
810+q1 `unionAll` q2 = UnsafeQuery $
811+ parenthesized (renderQuery q1)
812+ <+> "UNION" <+> "ALL"
813+ <+> parenthesized (renderQuery q2)
814+q1 `intersect` q2 = UnsafeQuery $
815+ parenthesized (renderQuery q1)
817+ <+> parenthesized (renderQuery q2)
818+q1 `intersectAll` q2 = UnsafeQuery $
819+ parenthesized (renderQuery q1)
820+ <+> "INTERSECT" <+> "ALL"
821+ <+> parenthesized (renderQuery q2)
822+q1 `except` q2 = UnsafeQuery $
823+ parenthesized (renderQuery q1)
825+ <+> parenthesized (renderQuery q2)
826+q1 `exceptAll` q2 = UnsafeQuery $
827+ parenthesized (renderQuery q1)
828+ <+> "EXCEPT" <+> "ALL"
829+ <+> parenthesized (renderQuery q2)
831+{-----------------------------------------
833+-----------------------------------------}
837+ => NP (Aliased (Expression params tables grouping)) columns
838+ -> TableExpression params schema tables grouping
839+ -> Query schema params columns
840+select list tabs = UnsafeQuery $
842+ <+> renderCommaSeparated (renderAliased renderExpression) list
843+ <+> renderTableExpression tabs
846+ :: (tables ~ '[table ::: columns])
847+ => TableExpression params schema tables 'Ungrouped
848+ -> Query schema params columns
849+selectStar tabs = UnsafeQuery $ "SELECT" <+> "*" <+> renderTableExpression tabs
852+ :: HasTable table tables columns
854+ -> TableExpression params schema tables 'Ungrouped
855+ -> Query schema params columns
856+selectDotStar table tables = UnsafeQuery $
857+ "SELECT" <+> renderAlias table <> ".*" <+> renderTableExpression tables
861+ => NP (Aliased (Expression params '[] 'Ungrouped)) columns
862+ -> Query schema params columns
863+selectRow row = UnsafeQuery $
864+ "SELECT" <+> renderCommaSeparated (renderAliased renderExpression) row
866+{-----------------------------------------
868+-----------------------------------------}
871+ :: (KnownSymbol table, SListI columns)
873+ -> NP (Aliased TypeExpression) columns
874+ -> [TableConstraint schema columns]
875+ -> Definition schema (Create table columns schema)
876+createTable table columns constraints = UnsafeDefinition $
877+ "CREATE TABLE" <+> renderAlias table
879+ ( renderCommaSeparated renderColumnDef columns
880+ <> renderConstraints constraints )
883+ renderColumnDef :: Aliased TypeExpression x -> ByteString
884+ renderColumnDef (ty `As` column) =
885+ renderAlias column <+> renderTypeExpression ty
886+ renderConstraints :: [TableConstraint schema columns] -> ByteString
887+ renderConstraints = \case
889+ _ -> ", " <> commaSeparated (renderTableConstraint <$> constraints)
892+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
893+ (columns :: [(Symbol,ColumnType)])
894+ = UnsafeTableConstraint { renderTableConstraint :: ByteString }
897+ :: Condition '[] '[table ::: columns] 'Ungrouped
898+ -> TableConstraint schema columns
899+check condition = UnsafeTableConstraint $
900+ "CHECK" <+> parenthesized (renderExpression condition)
903+ (columns :: [(Symbol,ColumnType)])
904+ (columnty :: (Symbol,ColumnType))
907+ :: HasColumn column columns ty
909+ -> Column columns (column ::: ty)
911+renderColumn :: Column columns columnty -> ByteString
912+renderColumn (Column column) = renderAlias column
915+ :: SListI subcolumns
916+ => NP (Column columns) subcolumns
917+ -> TableConstraint schema columns
918+unique columns = UnsafeTableConstraint $
919+ "UNIQUE" <+> parenthesized (renderCommaSeparated renderColumn columns)
922+ :: (SListI subcolumns, NotNullTypes subcolumns)
923+ => NP (Column columns) subcolumns
924+ -> TableConstraint schema columns
925+primaryKey columns = UnsafeTableConstraint $
926+ "PRIMARY KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
928+type family SameTypes columns0 columns1 :: Constraint where
929+ SameTypes '[] '[] = ()
930+ SameTypes ((column0 ::: ty0) ': columns0) ((column1 ::: ty1) ': columns1)
931+ = (ty0 ~ ty1, SameTypes columns0 columns1)
933+type family NotNullTypes columns :: Constraint where
934+ NotNullTypes '[] = ()
935+ NotNullTypes ((column ::: (optionality ('NotNull ty))) ': columns)
936+ = NotNullTypes columns
939+ :: ( HasTable reftable schema refcolumns
940+ , SameTypes subcolumns refsubcolumns
941+ , NotNullTypes subcolumns
943+ , SListI refsubcolumns)
944+ => NP (Column columns) subcolumns
946+ -> NP (Column refcolumns) refsubcolumns
947+ -> TableConstraint schema columns
948+foreignKey columns reftable refcolumns = UnsafeTableConstraint $
949+ "FOREIGN KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
950+ <+> "REFERENCES" <+> renderAlias reftable
951+ <+> parenthesized (renderCommaSeparated renderColumn refcolumns)
953+{-----------------------------------------
955+-----------------------------------------}
958+ :: KnownSymbol table
960+ -> Definition schema (Drop table schema)
961+dropTable table = UnsafeDefinition $ "DROP TABLE" <+> renderAlias table <> ";"
963+{-----------------------------------------
965+-----------------------------------------}
968+ :: HasTable table schema columns0
970+ -> AlterTable columns0 columns1
971+ -> Definition schema (Alter table schema columns1)
972+alterTable table alteration = UnsafeDefinition $
974+ <+> renderAlias table
975+ <+> renderAlterTable alteration
979+ :: (KnownSymbol table0, KnownSymbol table1)
982+ -> Definition schema (Rename table0 table1 schema)
983+alterTableRename table0 table1 = UnsafeDefinition $
984+ "ALTER TABLE" <+> renderAlias table0
985+ <+> "RENAME TO" <+> renderAlias table1 <> ";"
988+ (columns0 :: [(Symbol, ColumnType)])
989+ (columns1 :: [(Symbol, ColumnType)]) =
990+ UnsafeAlterTable {renderAlterTable :: ByteString}
993+ :: KnownSymbol column
995+ -> TypeExpression ('Optional ty)
996+ -> AlterTable columns (Create column ('Optional ty) columns)
997+addColumnDefault column ty = UnsafeAlterTable $
998+ "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
1001+ :: KnownSymbol column
1003+ -> TypeExpression ('Required ('Null ty))
1004+ -> AlterTable columns ((column ::: 'Required ('Null ty)) ': columns)
1005+addColumnNull column ty = UnsafeAlterTable $
1006+ "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
1009+ :: KnownSymbol column
1011+ -> AlterTable columns (Drop column columns)
1012+dropColumn column = UnsafeAlterTable $
1013+ "DROP COLUMN" <+> renderAlias column
1016+ :: (KnownSymbol column0, KnownSymbol column1)
1019+ -> AlterTable columns (Rename column0 column1 columns)
1020+renameColumn column0 column1 = UnsafeAlterTable $
1021+ "RENAME COLUMN" <+> renderAlias column0 <+> "TO" <+> renderAlias column1
1024+ :: (KnownSymbol column, HasColumn column columns ty0)
1026+ -> AlterColumn ty0 ty1
1027+ -> AlterTable columns (Alter column columns ty1)
1028+alterColumn column alteration = UnsafeAlterTable $
1029+ "ALTER COLUMN" <+> renderAlias column <+> renderAlterColumn alteration
1031+data AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
1032+ UnsafeAlterColumn {renderAlterColumn :: ByteString}
1035+ :: Expression '[] '[] 'Ungrouped ('Required ty)
1036+ -> AlterColumn ('Required ty) ('Optional ty)
1037+setDefault expression = UnsafeAlterColumn $
1038+ "SET DEFAULT" <+> renderExpression expression
1040+dropDefault :: AlterColumn ('Optional ty) ('Required ty)
1041+dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
1044+ :: AlterColumn (optionality ('Null ty)) (optionality ('NotNull ty))
1045+setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
1048+ :: AlterColumn (optionality ('NotNull ty)) (optionality ('Null ty))
1049+dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
1053+ => TypeExpression (optionality (nullity ty1))
1054+ -> AlterColumn (optionality (nullity ty0)) (optionality (nullity ty1))
1055+alterType ty = UnsafeAlterColumn $ "TYPE " <> renderTypeExpression ty
1057+{-----------------------------------------
1059+-----------------------------------------}
1062+ :: (SListI columns, SListI results, HasTable table schema columns)
1064+ -> ValuesClause schema params columns
1065+ -> ConflictClause params columns
1066+ -> ReturningClause params columns results
1067+ -> Manipulation schema params results
1068+insertInto table insert conflict returning = UnsafeManipulation $
1069+ "INSERT" <+> "INTO" <+> renderAlias table
1070+ <+> renderValuesClause insert
1071+ <> renderConflictClause conflict
1072+ <> renderReturningClause returning
1075+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
1076+ (params :: [ColumnType])
1077+ (columns :: [(Symbol,ColumnType)])
1079+ (NP (Aliased (Expression params '[] 'Ungrouped)) columns)
1080+ [NP (Aliased (Expression params '[] 'Ungrouped)) columns]
1081+ | ValuesQuery (Query schema params columns)
1085+ => ValuesClause schema params columns
1087+renderValuesClause = \case
1089+ parenthesized (renderCommaSeparated renderAliasPart row)
1092+ (parenthesized . renderCommaSeparated renderValuePart <$> row:rows)
1094+ renderAliasPart, renderValuePart
1095+ :: Aliased (Expression params '[] 'Ungrouped) ty -> ByteString
1096+ renderAliasPart (_ `As` name) = renderAlias name
1097+ renderValuePart (value `As` _) = renderExpression value
1098+ ValuesQuery q -> renderQuery q
1100+data ReturningClause
1101+ (params :: [ColumnType])
1102+ (columns :: [(Symbol,ColumnType)])
1103+ (results :: [(Symbol,ColumnType)])
1105+ ReturningNil :: ReturningClause params columns '[]
1106+ ReturningStar :: ReturningClause params columns columns
1108+ :: NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1109+ -> ReturningClause params columns results
1111+renderReturningClause
1113+ => ReturningClause params columns results
1115+renderReturningClause = \case
1116+ ReturningNil -> ";"
1117+ ReturningStar -> " RETURNING *;"
1118+ Returning results -> " RETURNING"
1119+ <+> renderCommaSeparated (renderAliased renderExpression) results <> ";"
1121+data ConflictClause params columns where
1122+ Conflict :: ConflictClause params columns
1123+ OnConflictDoNothing :: ConflictClause params columns
1125+ :: NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1126+ -> Condition params '[table ::: columns] 'Ungrouped
1127+ -> ConflictClause params columns
1129+renderConflictClause
1131+ => ConflictClause params columns
1133+renderConflictClause = \case
1135+ OnConflictDoNothing -> " ON CONFLICT DO NOTHING"
1136+ OnConflictDoUpdate updates wh
1137+ -> " ON CONFLICT DO UPDATE SET"
1138+ <+> renderCommaSeparatedMaybe renderUpdateExpression updates
1139+ <+> "WHERE" <+> renderExpression wh
1141+{-----------------------------------------
1143+-----------------------------------------}
1145+data UpdateExpression params table ty
1147+ | Set (Expression params table 'Ungrouped ty)
1149+renderUpdateExpression
1150+ :: Aliased (UpdateExpression params '[table ::: columns]) column
1151+ -> Maybe ByteString
1152+renderUpdateExpression = \case
1153+ Same `As` _ -> Nothing
1154+ Set expression `As` column -> Just $
1155+ renderAlias column <+> "=" <+> renderExpression expression
1158+ :: (HasTable table schema columns, SListI columns, SListI results)
1160+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1161+ -> Condition params '[table ::: columns] 'Ungrouped
1162+ -> ReturningClause params columns results
1163+ -> Manipulation schema params results
1164+update table columns wh returning = UnsafeManipulation $
1166+ <+> renderAlias table
1168+ <+> renderCommaSeparatedMaybe renderUpdateExpression columns
1169+ <+> "WHERE" <+> renderExpression wh
1170+ <> renderReturningClause returning
1172+{-----------------------------------------
1174+-----------------------------------------}
1177+ :: HasTable table schema columns
1179+ -> Condition params '[table ::: columns] 'Ungrouped
1180+ -> Manipulation schema params '[]
1181+deleteFrom table wh = UnsafeManipulation $
1182+ "DELETE FROM" <+> renderAlias table
1183+ <+> "WHERE" <+> renderExpression wh <> ";"
1185+{-----------------------------------------
1186+render helper functions
1187+-----------------------------------------}
1189+parenthesized :: ByteString -> ByteString
1190+parenthesized str = "(" <> str <> ")"
1192+(<+>) :: ByteString -> ByteString -> ByteString
1193+str1 <+> str2 = str1 <> " " <> str2
1195+commaSeparated :: [ByteString] -> ByteString
1196+commaSeparated = ByteString.intercalate ", "
1198+renderCommaSeparated
1200+ => (forall x. expression x -> ByteString)
1201+ -> NP expression xs -> ByteString
1202+renderCommaSeparated render
1205+ . hmap (K . render)
1207+renderCommaSeparatedMaybe
1209+ => (forall x. expression x -> Maybe ByteString)
1210+ -> NP expression xs -> ByteString
1211+renderCommaSeparatedMaybe render
1215+ . hmap (K . render)
1217+renderSymbol :: KnownSymbol str => Proxy# str -> ByteString
1218+renderSymbol p = fromString $ symbolVal' p