@@ -0,0 +1,1208 @@
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+ :: (HasTable table tables columns, HasColumn column columns ty)
387+ => (Alias table, Alias column)
388+ -> By tables '(table, column)
390+renderBy :: By tables tabcolty -> ByteString
391+renderBy (By (table, column)) = renderAlias table <> "." <> renderAlias column
393+data GroupByClause tables grouping where
394+ NoGroups :: GroupByClause tables 'Ungrouped
397+ => NP (By tables) bys
398+ -> GroupByClause tables ('Grouped bys)
400+renderGroupByClause :: GroupByClause tables grouping -> ByteString
401+renderGroupByClause = \case
404+ Group bys -> " GROUP BY" <+> renderCommaSeparated renderBy bys
406+data HavingClause params tables grouping where
407+ NoHaving :: HavingClause params tables 'Ungrouped
409+ :: [Condition params tables ('Grouped bys)]
410+ -> HavingClause params tables ('Grouped bys)
412+renderHavingClause :: HavingClause params tables grouping -> ByteString
413+renderHavingClause = \case
416+ Having conditions ->
417+ " HAVING" <+> commaSeparated (renderExpression <$> conditions)
419+class (KnownSymbol table, KnownSymbol column)
420+ => GroupedBy table column bys where
422+ :: (tables ~ '[table ::: columns], HasColumn column columns ty)
424+ -> Expression params tables ('Grouped bys) ty
425+ getGroup1 column = UnsafeExpression $ fromString (symbolVal' column)
427+ :: (HasTable table tables columns, HasColumn column columns ty)
430+ -> Expression params tables ('Grouped bys) ty
431+ getGroup2 table column = UnsafeExpression $
432+ renderAlias table <> "." <> renderAlias column
433+instance {-# OVERLAPPING #-} (KnownSymbol table, KnownSymbol column)
434+ => GroupedBy table column ('(table,column) ': bys)
435+instance {-# OVERLAPPABLE #-}
437+ , KnownSymbol column
438+ , GroupedBy table column bys
439+ ) => GroupedBy table column (tabcol ': bys)
442+ ( tables ~ '[table ::: columns]
443+ , HasColumn column columns ty
444+ , GroupedBy table column bys
446+ (Expression params tables ('Grouped bys) ty) where fromLabel = getGroup1
449+ ( HasTable table tables columns
450+ , HasColumn column columns ty
451+ , GroupedBy table column bys
452+ ) => IsTableColumn table column
453+ (Expression params tables ('Grouped bys) ty) where (&.) = getGroup2
457+ -> Expression params tables 'Ungrouped xty
458+ -> Expression params tables ('Grouped bys) yty
459+unsafeAggregate fun x = UnsafeExpression $ mconcat
460+ [fun, "(", renderExpression x, ")"]
464+ => Expression params tables 'Ungrouped ('Required (nullity ty))
465+ -> Expression params tables ('Grouped bys) ('Required (nullity ty))
466+sum_ = unsafeAggregate "sum"
468+{-----------------------------------------
470+-----------------------------------------}
473+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
474+ (columns :: [(Symbol,ColumnType)])
475+ = UnsafeTable { renderTable :: ByteString }
478+class KnownSymbol table => HasTable table tables columns
479+ | table tables -> columns where
480+ getTable :: Proxy# table -> Table tables columns
481+ getTable table = UnsafeTable $ fromString $ symbolVal' table
482+instance {-# OVERLAPPING #-} KnownSymbol table
483+ => HasTable table ((table ::: columns) ': tables) columns
484+instance {-# OVERLAPPABLE #-}
485+ (KnownSymbol table, HasTable table schema columns)
486+ => HasTable table (table' ': schema) columns
488+instance HasTable table schema columns
489+ => IsLabel table (Table schema columns) where
490+ fromLabel = getTable
492+data TableReference params schema tables where
494+ :: Aliased (Table schema) table
495+ -> TableReference params schema '[table]
497+ :: Aliased (Query schema params) table
498+ -> TableReference params schema '[table]
500+ :: TableReference params schema right
501+ -> TableReference params schema left
502+ -> TableReference params schema (Join left right)
504+ :: TableReference params schema right
505+ -> Condition params (Join left right) 'Ungrouped
506+ -> TableReference params schema left
507+ -> TableReference params schema (Join left right)
509+ :: TableReference params schema right
510+ -> Condition params (Join left right) 'Ungrouped
511+ -> TableReference params schema left
512+ -> TableReference params schema (Join left (NullifyTables right))
514+ :: TableReference params schema right
515+ -> Condition params (Join left right) 'Ungrouped
516+ -> TableReference params schema left
517+ -> TableReference params schema (Join (NullifyTables left) right)
519+ :: TableReference params schema right
520+ -> Condition params (Join left right) 'Ungrouped
521+ -> TableReference params schema left
522+ -> TableReference params schema
523+ (Join (NullifyTables left) (NullifyTables right))
525+renderTableReference :: TableReference params schema tables -> ByteString
526+renderTableReference = \case
527+ Table table -> renderAliased renderTable table
528+ Subquery selection -> renderAliased renderQuery selection
529+ CrossJoin right left ->
530+ renderTableReference left <+> "CROSS JOIN" <+> renderTableReference right
531+ InnerJoin right on left -> renderJoin "INNER JOIN" right on left
532+ LeftOuterJoin right on left -> renderJoin "LEFT OUTER JOIN" right on left
533+ RightOuterJoin right on left -> renderJoin "RIGHT OUTER JOIN" right on left
534+ FullOuterJoin right on left -> renderJoin "FULL OUTER JOIN" right on left
536+ renderJoin op right on left =
537+ renderTableReference left <+> op <+> renderTableReference right
538+ <+> "ON" <+> renderExpression on
541+ (params :: [ColumnType])
542+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
543+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
544+ (grouping :: Grouping)
546+ { fromClause :: TableReference params schema tables
547+ , whereClause :: [Condition params tables 'Ungrouped]
548+ , groupByClause :: GroupByClause tables grouping
549+ , havingClause :: HavingClause params tables grouping
550+ , orderByClause :: [SortExpression params tables grouping]
551+ , limitClause :: [Word64]
552+ , offsetClause :: [Word64]
555+renderTableExpression
556+ :: TableExpression params schema tables grouping
558+renderTableExpression
559+ (TableExpression tables whs' grps' hvs' srts' lims' offs') = mconcat
560+ [ "FROM ", renderTableReference tables
562+ , renderGroupByClause grps'
563+ , renderHavingClause hvs'
565+ , renderLimits lims'
566+ , renderOffsets offs'
569+ renderWheres = \case
571+ wh:[] -> " WHERE" <+> renderExpression wh
572+ wh:whs -> " WHERE" <+> renderExpression (foldr (&&*) wh whs)
576+ <+> commaSeparated (renderSortExpression <$> srts)
577+ renderLimits = \case
579+ lims -> " LIMIT" <+> fromString (show (minimum lims))
580+ renderOffsets = \case
582+ offs -> " OFFSET" <+> fromString (show (sum offs))
585+ :: TableReference params schema tables
586+ -> TableExpression params schema tables 'Ungrouped
587+from tables = TableExpression tables [] NoGroups NoHaving [] [] []
590+ :: Condition params tables 'Ungrouped
591+ -> TableExpression params schema tables grouping
592+ -> TableExpression params schema tables grouping
593+where_ wh tables = tables {whereClause = wh : whereClause tables}
597+ => NP (By tables) bys
598+ -> TableExpression params schema tables 'Ungrouped
599+ -> TableExpression params schema tables ('Grouped bys)
600+group bys tables = TableExpression
601+ { fromClause = fromClause tables
602+ , whereClause = whereClause tables
603+ , groupByClause = Group bys
604+ , havingClause = Having []
605+ , orderByClause = []
606+ , limitClause = limitClause tables
607+ , offsetClause = offsetClause tables
611+ :: Condition params tables ('Grouped bys)
612+ -> TableExpression params schema tables ('Grouped bys)
613+ -> TableExpression params schema tables ('Grouped bys)
614+having hv tables = tables
615+ { havingClause = case havingClause tables of Having hvs -> Having (hv:hvs) }
618+ :: [SortExpression params tables grouping]
619+ -> TableExpression params schema tables grouping
620+ -> TableExpression params schema tables grouping
621+orderBy srts tables = tables {orderByClause = orderByClause tables ++ srts}
625+ -> TableExpression params schema tables grouping
626+ -> TableExpression params schema tables grouping
627+limit lim tables = tables {limitClause = lim : limitClause tables}
631+ -> TableExpression params schema tables grouping
632+ -> TableExpression params schema tables grouping
633+offset off tables = tables {offsetClause = off : offsetClause tables}
635+{-----------------------------------------
637+-----------------------------------------}
639+newtype TypeExpression (ty :: ColumnType)
640+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
643+bool :: TypeExpression ('Required ('Null 'PGBool))
644+bool = UnsafeTypeExpression "bool"
645+int2 :: TypeExpression ('Required ('Null 'PGInt2))
646+int2 = UnsafeTypeExpression "int2"
647+smallint :: TypeExpression ('Required ('Null 'PGInt2))
648+smallint = UnsafeTypeExpression "smallint"
649+int4 :: TypeExpression ('Required ('Null 'PGInt4))
650+int4 = UnsafeTypeExpression "int4"
651+int :: TypeExpression ('Required ('Null 'PGInt4))
652+int = UnsafeTypeExpression "int"
653+integer :: TypeExpression ('Required ('Null 'PGInt4))
654+integer = UnsafeTypeExpression "integer"
655+int8 :: TypeExpression ('Required ('Null 'PGInt8))
656+int8 = UnsafeTypeExpression "int8"
657+bigint :: TypeExpression ('Required ('Null 'PGInt8))
658+bigint = UnsafeTypeExpression "bigint"
659+numeric :: TypeExpression ('Required ('Null 'PGNumeric))
660+numeric = UnsafeTypeExpression "numeric"
661+float4 :: TypeExpression ('Required ('Null 'PGFloat4))
662+float4 = UnsafeTypeExpression "float4"
663+real :: TypeExpression ('Required ('Null 'PGFloat4))
664+real = UnsafeTypeExpression "real"
665+float8 :: TypeExpression ('Required ('Null 'PGFloat8))
666+float8 = UnsafeTypeExpression "float8"
667+doublePrecision :: TypeExpression ('Required ('Null 'PGFloat8))
668+doublePrecision = UnsafeTypeExpression "double precision"
669+serial2 :: TypeExpression ('Optional ('NotNull 'PGInt2))
670+serial2 = UnsafeTypeExpression "serial2"
671+smallserial :: TypeExpression ('Optional ('NotNull 'PGInt2))
672+smallserial = UnsafeTypeExpression "smallserial"
673+serial4 :: TypeExpression ('Optional ('NotNull 'PGInt4))
674+serial4 = UnsafeTypeExpression "serial4"
675+serial :: TypeExpression ('Optional ('NotNull 'PGInt4))
676+serial = UnsafeTypeExpression "serial"
677+serial8 :: TypeExpression ('Optional ('NotNull 'PGInt8))
678+serial8 = UnsafeTypeExpression "serial8"
679+bigserial :: TypeExpression ('Optional ('NotNull 'PGInt8))
680+bigserial = UnsafeTypeExpression "bigserial"
681+money :: TypeExpression ('Required ('Null 'PGMoney))
682+money = UnsafeTypeExpression "money"
683+text :: TypeExpression ('Required ('Null 'PGText))
684+text = UnsafeTypeExpression "text"
688+ -> TypeExpression ('Required ('Null ('PGChar n)))
689+char (_ :: proxy n) = UnsafeTypeExpression $
690+ "char(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
694+ -> TypeExpression ('Required ('Null ('PGChar n)))
695+character (_ :: proxy n) = UnsafeTypeExpression $
696+ "character(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
700+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
701+varchar (_ :: proxy n) = UnsafeTypeExpression $
702+ "varchar(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
706+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
707+characterVarying (_ :: proxy n) = UnsafeTypeExpression $
708+ "character varying(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
709+bytea :: TypeExpression ('Required ('Null ('PGBytea)))
710+bytea = UnsafeTypeExpression "bytea"
711+timestamp :: TypeExpression ('Required ('Null ('PGTimestamp)))
712+timestamp = UnsafeTypeExpression "timestamp"
713+timestampWithTimeZone :: TypeExpression ('Required ('Null ('PGTimestampTZ)))
714+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
715+date :: TypeExpression ('Required ('Null ('PGDate)))
716+date = UnsafeTypeExpression "date"
717+time :: TypeExpression ('Required ('Null ('PGTime)))
718+time = UnsafeTypeExpression "time"
719+timeWithTimeZone :: TypeExpression ('Required ('Null ('PGTimeTZ)))
720+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
721+interval :: TypeExpression ('Required ('Null ('PGInterval)))
722+interval = UnsafeTypeExpression "interval"
723+uuid :: TypeExpression ('Required ('Null ('PGUuid)))
724+uuid = UnsafeTypeExpression "uuid"
725+json :: TypeExpression ('Required ('Null ('PGJson)))
726+json = UnsafeTypeExpression "json"
727+jsonb :: TypeExpression ('Required ('Null ('PGJsonb)))
728+jsonb = UnsafeTypeExpression "jsonb"
731+ :: TypeExpression (optionality ('Null ty))
732+ -> TypeExpression (optionality ('NotNull ty))
733+notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <+> "NOT NULL"
735+ :: Expression '[] '[] 'Ungrouped ('Required ty)
736+ -> TypeExpression ('Required ty)
737+ -> TypeExpression ('Optional ty)
738+default_ x ty = UnsafeTypeExpression $
739+ renderTypeExpression ty <+> "DEFAULT" <+> renderExpression x
741+class PGTyped (ty :: PGType) where
742+ pgtype :: TypeExpression ('Required ('Null ty))
743+instance PGTyped 'PGBool where pgtype = bool
744+instance PGTyped 'PGInt2 where pgtype = int2
745+instance PGTyped 'PGInt4 where pgtype = int4
746+instance PGTyped 'PGInt8 where pgtype = int8
747+instance PGTyped 'PGNumeric where pgtype = numeric
748+instance PGTyped 'PGFloat4 where pgtype = float4
749+instance PGTyped 'PGFloat8 where pgtype = float8
750+instance PGTyped 'PGMoney where pgtype = money
751+instance PGTyped 'PGText where pgtype = text
752+instance KnownNat n => PGTyped ('PGChar n) where pgtype = char (Proxy @n)
753+instance KnownNat n => PGTyped ('PGVarChar n) where pgtype = varchar (Proxy @n)
754+instance PGTyped 'PGBytea where pgtype = bytea
755+instance PGTyped 'PGTimestamp where pgtype = timestamp
756+instance PGTyped 'PGTimestampTZ where pgtype = timestampWithTimeZone
757+instance PGTyped 'PGDate where pgtype = date
758+instance PGTyped 'PGTime where pgtype = time
759+instance PGTyped 'PGTimeTZ where pgtype = timeWithTimeZone
760+instance PGTyped 'PGInterval where pgtype = interval
761+instance PGTyped 'PGUuid where pgtype = uuid
762+instance PGTyped 'PGJson where pgtype = json
763+instance PGTyped 'PGJsonb where pgtype = jsonb
765+{-----------------------------------------
767+-----------------------------------------}
770+ (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
771+ (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
772+ = UnsafeDefinition { renderDefinition :: ByteString }
774+instance Category Definition where
775+ id = UnsafeDefinition ";"
776+ ddl1 . ddl0 = UnsafeDefinition $
777+ renderDefinition ddl0 <+> renderDefinition ddl1
780+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
781+ (params :: [ColumnType])
782+ (columns :: [(Symbol,ColumnType)])
783+ = UnsafeManipulation { renderManipulation :: ByteString }
786+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
787+ (params :: [ColumnType])
788+ (columns :: [(Symbol,ColumnType)])
789+ = UnsafeQuery { renderQuery :: ByteString }
792+ :: Query schema params columns
793+ -> Manipulation schema params columns
794+query q = UnsafeManipulation $ renderQuery q <> ";"
796+union, unionAll, intersect, intersectAll, except, exceptAll
797+ :: Query schema params columns
798+ -> Query schema params columns
799+ -> Query schema params columns
800+q1 `union` q2 = UnsafeQuery $
801+ parenthesized (renderQuery q1)
803+ <+> parenthesized (renderQuery q2)
804+q1 `unionAll` q2 = UnsafeQuery $
805+ parenthesized (renderQuery q1)
806+ <+> "UNION" <+> "ALL"
807+ <+> parenthesized (renderQuery q2)
808+q1 `intersect` q2 = UnsafeQuery $
809+ parenthesized (renderQuery q1)
811+ <+> parenthesized (renderQuery q2)
812+q1 `intersectAll` q2 = UnsafeQuery $
813+ parenthesized (renderQuery q1)
814+ <+> "INTERSECT" <+> "ALL"
815+ <+> parenthesized (renderQuery q2)
816+q1 `except` q2 = UnsafeQuery $
817+ parenthesized (renderQuery q1)
819+ <+> parenthesized (renderQuery q2)
820+q1 `exceptAll` q2 = UnsafeQuery $
821+ parenthesized (renderQuery q1)
822+ <+> "EXCEPT" <+> "ALL"
823+ <+> parenthesized (renderQuery q2)
825+{-----------------------------------------
827+-----------------------------------------}
831+ => NP (Aliased (Expression params tables grouping)) columns
832+ -> TableExpression params schema tables grouping
833+ -> Query schema params columns
834+select list tabs = UnsafeQuery $
836+ <+> renderCommaSeparated (renderAliased renderExpression) list
837+ <+> renderTableExpression tabs
840+ :: (tables ~ '[table ::: columns])
841+ => TableExpression params schema tables 'Ungrouped
842+ -> Query schema params columns
843+selectStar tabs = UnsafeQuery $ "SELECT" <+> "*" <+> renderTableExpression tabs
846+ :: HasTable table tables columns
848+ -> TableExpression params schema tables 'Ungrouped
849+ -> Query schema params columns
850+selectDotStar table tables = UnsafeQuery $
851+ "SELECT" <+> renderAlias table <> ".*" <+> renderTableExpression tables
855+ => NP (Aliased (Expression params '[] 'Ungrouped)) columns
856+ -> Query schema params columns
857+selectRow row = UnsafeQuery $
858+ "SELECT" <+> renderCommaSeparated (renderAliased renderExpression) row
860+{-----------------------------------------
862+-----------------------------------------}
865+ :: (KnownSymbol table, SListI columns)
867+ -> NP (Aliased TypeExpression) columns
868+ -> [TableConstraint schema columns]
869+ -> Definition schema (Create table columns schema)
870+createTable table columns constraints = UnsafeDefinition $
871+ "CREATE TABLE" <+> renderAlias table
873+ ( renderCommaSeparated renderColumnDef columns
874+ <> renderConstraints constraints )
877+ renderColumnDef :: Aliased TypeExpression x -> ByteString
878+ renderColumnDef (ty `As` column) =
879+ renderAlias column <+> renderTypeExpression ty
880+ renderConstraints :: [TableConstraint schema columns] -> ByteString
881+ renderConstraints = \case
883+ _ -> ", " <> commaSeparated (renderTableConstraint <$> constraints)
886+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
887+ (columns :: [(Symbol,ColumnType)])
888+ = UnsafeTableConstraint { renderTableConstraint :: ByteString }
891+ :: Condition '[] '[table ::: columns] 'Ungrouped
892+ -> TableConstraint schema columns
893+check condition = UnsafeTableConstraint $
894+ "CHECK" <+> renderExpression condition
897+ (columns :: [(Symbol,ColumnType)])
898+ (columnty :: (Symbol,ColumnType))
899+ = UnsafeColumn {renderColumn :: ByteString}
900+instance HasColumn column columns ty
901+ => IsLabel column (Column columns (column ::: ty)) where
902+ fromLabel symbol = UnsafeColumn (renderSymbol symbol)
905+ :: SListI subcolumns
906+ => NP (Column columns) subcolumns
907+ -> TableConstraint schema columns
908+unique columns = UnsafeTableConstraint $
909+ "UNIQUE" <+> renderCommaSeparated renderColumn columns
912+ :: (SListI subcolumns, NotNullTypes subcolumns)
913+ => NP (Column columns) subcolumns
914+ -> TableConstraint schema columns
915+primaryKey columns = UnsafeTableConstraint $
916+ "PRIMARY KEY" <+> renderCommaSeparated renderColumn columns
918+type family SameTypes columns0 columns1 :: Constraint where
919+ SameTypes '[] '[] = ()
920+ SameTypes ((column0 ::: ty0) ': columns0) ((column1 ::: ty1) ': columns1)
921+ = (ty0 ~ ty1, SameTypes columns0 columns1)
923+type family NotNullTypes columns :: Constraint where
924+ NotNullTypes '[] = ()
925+ NotNullTypes ((column ::: (optionality ('NotNull ty))) ': columns)
926+ = NotNullTypes columns
929+ :: ( HasTable reftable schema refcolumns
930+ , SameTypes subcolumns refsubcolumns
931+ , NotNullTypes subcolumns
933+ , SListI refsubcolumns)
934+ => NP (Column columns) subcolumns
936+ -> NP (Column refcolumns) refsubcolumns
937+ -> TableConstraint schema columns
938+foreignKey columns reftable refcolumns = UnsafeTableConstraint $
939+ "FOREIGN KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
940+ <+> "REFERENCES" <+> renderAlias reftable
941+ <+> parenthesized (renderCommaSeparated renderColumn refcolumns)
943+{-----------------------------------------
945+-----------------------------------------}
948+ :: KnownSymbol table
950+ -> Definition schema (Drop table schema)
951+dropTable table = UnsafeDefinition $ "DROP TABLE" <+> renderAlias table <> ";"
953+{-----------------------------------------
955+-----------------------------------------}
958+ :: HasTable table schema columns0
960+ -> AlterTable columns0 columns1
961+ -> Definition schema (Alter table schema columns1)
962+alterTable table alteration = UnsafeDefinition $
964+ <+> renderAlias table
965+ <+> renderAlterTable alteration
969+ :: (KnownSymbol table0, KnownSymbol table1)
972+ -> Definition schema (Rename table0 table1 schema)
973+alterTableRename table0 table1 = UnsafeDefinition $
974+ "ALTER TABLE" <+> renderAlias table0
975+ <+> "RENAME TO" <+> renderAlias table1 <> ";"
978+ (columns0 :: [(Symbol, ColumnType)])
979+ (columns1 :: [(Symbol, ColumnType)]) =
980+ UnsafeAlterTable {renderAlterTable :: ByteString}
983+ :: KnownSymbol column
985+ -> TypeExpression ('Optional ty)
986+ -> AlterTable columns (Create column ('Optional ty) columns)
987+addColumnDefault column ty = UnsafeAlterTable $
988+ "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
991+ :: KnownSymbol column
993+ -> TypeExpression ('Required ('Null ty))
994+ -> AlterTable columns ((column ::: 'Required ('Null ty)) ': columns)
995+addColumnNull column ty = UnsafeAlterTable $
996+ "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
999+ :: KnownSymbol column
1001+ -> AlterTable columns (Drop column columns)
1002+dropColumn column = UnsafeAlterTable $
1003+ "DROP COLUMN" <+> renderAlias column
1006+ :: (KnownSymbol column0, KnownSymbol column1)
1009+ -> AlterTable columns (Rename column0 column1 columns)
1010+renameColumn column0 column1 = UnsafeAlterTable $
1011+ "RENAME COLUMN" <+> renderAlias column0 <+> "TO" <+> renderAlias column1
1014+ :: (KnownSymbol column, HasColumn column columns ty0)
1016+ -> AlterColumn ty0 ty1
1017+ -> AlterTable columns (Alter column columns ty1)
1018+alterColumn column alteration = UnsafeAlterTable $
1019+ "ALTER COLUMN" <+> renderAlias column <+> renderAlterColumn alteration
1021+data AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
1022+ UnsafeAlterColumn {renderAlterColumn :: ByteString}
1025+ :: Expression '[] '[] 'Ungrouped ('Required ty)
1026+ -> AlterColumn ('Required ty) ('Optional ty)
1027+setDefault expression = UnsafeAlterColumn $
1028+ "SET DEFAULT" <+> renderExpression expression
1030+dropDefault :: AlterColumn ('Optional ty) ('Required ty)
1031+dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
1034+ :: AlterColumn (optionality ('Null ty)) (optionality ('NotNull ty))
1035+setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
1038+ :: AlterColumn (optionality ('NotNull ty)) (optionality ('Null ty))
1039+dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
1043+ => TypeExpression (optionality (nullity ty1))
1044+ -> AlterColumn (optionality (nullity ty0)) (optionality (nullity ty1))
1045+alterType ty = UnsafeAlterColumn $ "TYPE " <> renderTypeExpression ty
1047+{-----------------------------------------
1049+-----------------------------------------}
1052+ :: (SListI columns, SListI results, HasTable table schema columns)
1054+ -> ValuesClause schema params columns
1055+ -> ConflictClause params columns
1056+ -> ReturningClause params columns results
1057+ -> Manipulation schema params results
1058+insertInto table insert conflict returning = UnsafeManipulation $
1059+ "INSERT" <+> "INTO" <+> renderAlias table
1060+ <+> renderValuesClause insert
1061+ <> renderConflictClause conflict
1062+ <> renderReturningClause returning
1065+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
1066+ (params :: [ColumnType])
1067+ (columns :: [(Symbol,ColumnType)])
1069+ (NP (Aliased (Expression params '[] 'Ungrouped)) columns)
1070+ [NP (Aliased (Expression params '[] 'Ungrouped)) columns]
1071+ | ValuesQuery (Query schema params columns)
1075+ => ValuesClause schema params columns
1077+renderValuesClause = \case
1079+ parenthesized (renderCommaSeparated renderAliasPart row)
1082+ (parenthesized . renderCommaSeparated renderValuePart <$> row:rows)
1084+ renderAliasPart, renderValuePart
1085+ :: Aliased (Expression params '[] 'Ungrouped) ty -> ByteString
1086+ renderAliasPart (_ `As` name) = renderAlias name
1087+ renderValuePart (value `As` _) = renderExpression value
1088+ ValuesQuery q -> renderQuery q
1090+data ReturningClause
1091+ (params :: [ColumnType])
1092+ (columns :: [(Symbol,ColumnType)])
1093+ (results :: [(Symbol,ColumnType)])
1095+ ReturningNil :: ReturningClause params columns '[]
1096+ ReturningStar :: ReturningClause params columns columns
1098+ :: NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1099+ -> ReturningClause params columns results
1101+renderReturningClause
1103+ => ReturningClause params columns results
1105+renderReturningClause = \case
1106+ ReturningNil -> ";"
1107+ ReturningStar -> " RETURNING *;"
1108+ Returning results -> " RETURNING"
1109+ <+> renderCommaSeparated (renderAliased renderExpression) results <> ";"
1111+data ConflictClause params columns where
1112+ Conflict :: ConflictClause params columns
1113+ OnConflictDoNothing :: ConflictClause params columns
1115+ :: NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1116+ -> Condition params '[table ::: columns] 'Ungrouped
1117+ -> ConflictClause params columns
1119+renderConflictClause
1121+ => ConflictClause params columns
1123+renderConflictClause = \case
1125+ OnConflictDoNothing -> " ON CONFLICT DO NOTHING"
1126+ OnConflictDoUpdate updates wh
1127+ -> " ON CONFLICT DO UPDATE SET"
1128+ <+> renderCommaSeparatedMaybe renderUpdateExpression updates
1129+ <+> "WHERE" <+> renderExpression wh
1131+{-----------------------------------------
1133+-----------------------------------------}
1135+data UpdateExpression params table ty
1137+ | Set (Expression params table 'Ungrouped ty)
1139+renderUpdateExpression
1140+ :: Aliased (UpdateExpression params '[table ::: columns]) column
1141+ -> Maybe ByteString
1142+renderUpdateExpression = \case
1143+ Same `As` _ -> Nothing
1144+ Set expression `As` column -> Just $
1145+ renderAlias column <+> "=" <+> renderExpression expression
1148+ :: (HasTable table schema columns, SListI columns, SListI results)
1150+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1151+ -> Condition params '[table ::: columns] 'Ungrouped
1152+ -> ReturningClause params columns results
1153+ -> Manipulation schema params results
1154+update table columns wh returning = UnsafeManipulation $
1156+ <+> renderAlias table
1158+ <+> renderCommaSeparatedMaybe renderUpdateExpression columns
1159+ <+> "WHERE" <+> renderExpression wh
1160+ <> renderReturningClause returning
1162+{-----------------------------------------
1164+-----------------------------------------}
1167+ :: HasTable table schema columns
1169+ -> Condition params '[table ::: columns] 'Ungrouped
1170+ -> Manipulation schema params '[]
1171+deleteFrom table wh = UnsafeManipulation $
1172+ "DELETE FROM" <+> renderAlias table
1173+ <+> "WHERE" <+> renderExpression wh <> ";"
1175+{-----------------------------------------
1176+render helper functions
1177+-----------------------------------------}
1179+parenthesized :: ByteString -> ByteString
1180+parenthesized str = "(" <> str <> ")"
1182+(<+>) :: ByteString -> ByteString -> ByteString
1183+str1 <+> str2 = str1 <> " " <> str2
1185+commaSeparated :: [ByteString] -> ByteString
1186+commaSeparated = ByteString.intercalate ", "
1188+renderCommaSeparated
1190+ => (forall x. expression x -> ByteString)
1191+ -> NP expression xs -> ByteString
1192+renderCommaSeparated render
1195+ . hmap (K . render)
1197+renderCommaSeparatedMaybe
1199+ => (forall x. expression x -> Maybe ByteString)
1200+ -> NP expression xs -> ByteString
1201+renderCommaSeparatedMaybe render
1205+ . hmap (K . render)
1207+renderSymbol :: KnownSymbol str => Proxy# str -> ByteString
1208+renderSymbol p = fromString $ symbolVal' p