@@ -0,0 +1,1197 @@
1+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
6+ , FunctionalDependencies
11+ , ScopedTypeVariables
15+ , UndecidableInstances
18+module Squeel.PostgreSQL.Statement where
20+import Control.Category
21+import Data.ByteString (ByteString)
30+import GHC.OverloadedLabels
32+import Prelude hiding (RealFrac(..), id, (.))
34+import qualified Data.ByteString as ByteString
36+import Squeel.PostgreSQL.Schema
38+{-----------------------------------------
40+-----------------------------------------}
43+ (params :: [ColumnType])
44+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
45+ (grouping :: Grouping)
47+ = UnsafeExpression { renderExpression :: ByteString }
50+class KnownNat n => HasParameter (n :: Nat) params ty | n params -> ty where
51+ param :: Proxy# n -> Expression params tables grouping ty
52+ param p = UnsafeExpression $ ("$" <>) $ fromString $ show $ natVal' p
53+instance {-# OVERLAPPING #-} HasParameter 1 (ty1:tys) ty1
54+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) params ty)
55+ => HasParameter n (ty' : params) ty
57+param1 :: Expression (ty1:tys) tables grouping ty1
58+param1 = param (proxy# :: Proxy# 1)
59+param2 :: Expression (ty1:ty2:tys) tables grouping ty2
60+param2 = param (proxy# :: Proxy# 2)
61+param3 :: Expression (ty1:ty2:ty3:tys) tables grouping ty3
62+param3 = param (proxy# :: Proxy# 3)
63+param4 :: Expression (ty1:ty2:ty3:ty4:tys) tables grouping ty4
64+param4 = param (proxy# :: Proxy# 4)
65+param5 :: Expression (ty1:ty2:ty3:ty4:ty5:tys) tables grouping ty5
66+param5 = param (proxy# :: Proxy# 5)
68+class KnownSymbol column => HasColumn column columns ty
69+ | column columns -> ty where
72+ -> Expression params '[table ::: columns] 'Ungrouped ty
73+ getColumn column = UnsafeExpression $ fromString $ symbolVal' column
74+instance {-# OVERLAPPING #-} KnownSymbol column
75+ => HasColumn column ((column ::: optionality ty) ': tys) ('Required ty)
76+instance {-# OVERLAPPABLE #-} (KnownSymbol column, HasColumn column table ty)
77+ => HasColumn column (ty' ': table) ty
79+instance (HasColumn column columns ty, tables ~ '[table ::: columns])
80+ => IsLabel column (Expression params tables 'Ungrouped ty) where
81+ fromLabel = getColumn
83+instance (HasTable table tables columns, HasColumn column columns ty)
84+ => TableColumn table column (Expression params tables 'Ungrouped ty) where
85+ table &. column = UnsafeExpression $
86+ renderAlias table <> "." <> renderAlias column
88+def :: Expression params '[] grouping ('Optional (nullity ty))
89+def = UnsafeExpression "DEFAULT"
92+ :: Expression params '[] grouping ('Required (nullity ty))
93+ -> Expression params '[] grouping ('Optional (nullity ty))
94+unDef = UnsafeExpression . renderExpression
96+null :: Expression params tables grouping (optionality ('Null ty))
97+null = UnsafeExpression "NULL"
100+ :: Expression params tables grouping (optionality ('NotNull ty))
101+ -> Expression params tables grouping (optionality ('Null ty))
102+unNull = UnsafeExpression . renderExpression
105+ :: [Expression params tables grouping ('Required ('Null x))]
106+ -> Expression params tables grouping ('Required ('NotNull x))
107+ -> Expression params tables grouping ('Required ('NotNull x))
108+coalesce nulls isn'tNull = UnsafeExpression $ mconcat
110+ , ByteString.intercalate ", " $ map renderExpression nulls
111+ , ", ", renderExpression isn'tNull
117+ -> Expression params tables grouping ty0
118+ -> Expression params tables grouping ty1
119+ -> Expression params tables grouping ty2
120+unsafeBinaryOp op x y = UnsafeExpression $ mconcat
121+ ["(", renderExpression x, " ", op, " ", renderExpression y, ")"]
125+ -> Expression params tables grouping ty0
126+ -> Expression params tables grouping ty1
127+unsafeUnaryOp op x = UnsafeExpression $ mconcat
128+ ["(", op, " ", renderExpression x, ")"]
132+ -> Expression params tables grouping xty
133+ -> Expression params tables grouping yty
134+unsafeFunction fun x = UnsafeExpression $ mconcat
135+ [fun, "(", renderExpression x, ")"]
138+ => Num (Expression params tables grouping ('Required (nullity ty))) where
139+ (+) = unsafeBinaryOp "+"
140+ (-) = unsafeBinaryOp "-"
141+ (*) = unsafeBinaryOp "*"
142+ abs = unsafeFunction "abs"
143+ signum = unsafeFunction "sign"
146+ . (<> if decimal (Proxy :: Proxy ty) then "." else "")
150+instance PGFractional ty => Fractional
151+ (Expression params tables grouping ('Required (nullity ty))) where
152+ (/) = unsafeBinaryOp "/"
153+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
155+instance (PGFloating ty, PGCast 'PGNumeric ty, PGTyped ty) => Floating
156+ (Expression params tables grouping ('Required (nullity ty))) where
157+ pi = UnsafeExpression "pi()"
158+ exp = unsafeFunction "exp"
159+ log = unsafeFunction "ln"
160+ sqrt = unsafeFunction "sqrt"
161+ b ** x = UnsafeExpression $
162+ "power(" <> renderExpression b <> ", " <> renderExpression x <> ")"
163+ logBase b y = cast pgtype $ logBaseNumeric b y
166+ :: Expression params tables grouping ('Required (nullity ty))
167+ -> Expression params tables grouping ('Required (nullity ty))
168+ -> Expression params tables grouping ('Required (nullity 'PGNumeric))
169+ logBaseNumeric b' y' = UnsafeExpression $ mconcat
171+ , renderExpression b' <> "::numeric"
173+ , renderExpression y' <> "::numeric"
176+ sin = unsafeFunction "sin"
177+ cos = unsafeFunction "cos"
178+ tan = unsafeFunction "tan"
179+ asin = unsafeFunction "asin"
180+ acos = unsafeFunction "acos"
181+ atan = unsafeFunction "atan"
182+ sinh x = (exp x - exp (-x)) / 2
183+ cosh x = (exp x + exp (-x)) / 2
184+ tanh x = sinh x / cosh x
185+ asinh x = log (x + sqrt (x*x + 1))
186+ acosh x = log (x + sqrt (x*x - 1))
187+ atanh x = log ((1 + x) / (1 - x)) / 2
191+ => Expression params tables grouping ('Required (nullity float))
192+ -> Expression params tables grouping ('Required (nullity float))
193+ -> Expression params tables grouping ('Required (nullity float))
194+atan2 y x = UnsafeExpression $
195+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
197+class PGCast (ty0 :: PGType) (ty1 :: PGType) where
199+ :: TypeExpression ('Required ('Null ty1))
200+ -> Expression params tables grouping ('Required (nullity ty0))
201+ -> Expression params tables grouping ('Required (nullity ty1))
202+ cast ty x = UnsafeExpression $
203+ "(" <> renderExpression x <> "::" <> renderTypeExpression ty <> ")"
204+instance PGCast 'PGInt2 'PGInt2
205+instance PGCast 'PGInt2 'PGInt4
206+instance PGCast 'PGInt2 'PGInt8
207+instance PGCast 'PGInt4 'PGInt2
208+instance PGCast 'PGInt4 'PGInt4
209+instance PGCast 'PGInt4 'PGInt8
210+instance PGCast 'PGInt8 'PGInt2
211+instance PGCast 'PGInt8 'PGInt4
212+instance PGCast 'PGInt8 'PGInt8
213+instance PGCast 'PGInt8 'PGFloat4
214+instance PGCast 'PGInt8 'PGFloat8
215+instance PGCast 'PGInt8 'PGNumeric
218+ :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
219+ => Expression params tables grouping ('Required (nullity ty))
220+ -> Expression params tables grouping ('Required (nullity ty))
221+ -> Expression params tables grouping ('Required (nullity ty))
222+div = unsafeBinaryOp "/"
225+ :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
226+ => Expression params tables grouping ('Required (nullity ty))
227+ -> Expression params tables grouping ('Required (nullity ty))
228+ -> Expression params tables grouping ('Required (nullity ty))
229+mod = unsafeBinaryOp "%"
232+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
233+ => Expression params tables grouping ('Required (nullity frac))
234+ -> Expression params tables grouping ('Required (nullity int))
235+truncate = cast pgtype . truncate'
238+ :: Expression params tables grouping ('Required (nullity frac))
239+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
240+ truncate' = unsafeFunction "trunc"
243+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
244+ => Expression params tables grouping ('Required (nullity frac))
245+ -> Expression params tables grouping ('Required (nullity int))
246+round = cast pgtype . round'
249+ :: Expression params tables grouping ('Required (nullity frac))
250+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
251+ round' = unsafeFunction "round"
254+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
255+ => Expression params tables grouping ('Required (nullity frac))
256+ -> Expression params tables grouping ('Required (nullity int))
257+ceiling = cast pgtype . ceiling'
260+ :: Expression params tables grouping ('Required (nullity frac))
261+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
262+ ceiling' = unsafeFunction "ceiling"
265+ (Expression params tables grouping ('Required (nullity 'PGText))) where
266+ fromString str = UnsafeExpression $
267+ "E\'" <> fromString (escape =<< str) <> "\'"
280+type Condition params tables grouping =
281+ Expression params tables grouping ('Required ('NotNull 'PGBool))
283+true :: Condition params tables grouping
284+true = UnsafeExpression "TRUE"
286+false :: Condition params tables grouping
287+false = UnsafeExpression "FALSE"
290+ :: Condition params tables grouping
291+ -> Condition params tables grouping
292+not_ = unsafeUnaryOp "NOT"
295+ :: Condition params tables grouping
296+ -> Condition params tables grouping
297+ -> Condition params tables grouping
298+(&&*) = unsafeBinaryOp "AND"
301+ :: Condition params tables grouping
302+ -> Condition params tables grouping
303+ -> Condition params tables grouping
304+(||*) = unsafeBinaryOp "OR"
307+ :: [(Condition params tables grouping, Expression params tables grouping ty)]
308+ -> Expression params tables grouping ty
309+ -> Expression params tables grouping ty
310+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
314+ [ " WHEN ", renderExpression when_
315+ , " THEN ", renderExpression then_
317+ | (when_,then_) <- whenThens
319+ , " ELSE ", renderExpression else_
324+ :: Condition params tables grouping
325+ -> Expression params tables grouping ty
326+ -> Expression params tables grouping ty
327+ -> Expression params tables grouping ty
328+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
331+ :: Expression params tables grouping ('Required (nullity ty))
332+ -> Expression params tables grouping ('Required (nullity ty))
333+ -> Expression params tables grouping ('Required (nullity 'PGBool))
334+(==*) = unsafeBinaryOp "="
337+ :: Expression params tables grouping ('Required (nullity ty))
338+ -> Expression params tables grouping ('Required (nullity ty))
339+ -> Expression params tables grouping ('Required (nullity 'PGBool))
340+(/=*) = unsafeBinaryOp "<>"
343+ :: Expression params tables grouping ('Required (nullity ty))
344+ -> Expression params tables grouping ('Required (nullity ty))
345+ -> Expression params tables grouping ('Required (nullity 'PGBool))
346+(>=*) = unsafeBinaryOp ">="
349+ :: Expression params tables grouping ('Required (nullity ty))
350+ -> Expression params tables grouping ('Required (nullity ty))
351+ -> Expression params tables grouping ('Required (nullity 'PGBool))
352+(<*) = unsafeBinaryOp "<"
355+ :: Expression params tables grouping ('Required (nullity ty))
356+ -> Expression params tables grouping ('Required (nullity ty))
357+ -> Expression params tables grouping ('Required (nullity 'PGBool))
358+(<=*) = unsafeBinaryOp "<="
361+ :: Expression params tables grouping ('Required (nullity ty))
362+ -> Expression params tables grouping ('Required (nullity ty))
363+ -> Expression params tables grouping ('Required (nullity 'PGBool))
364+(>*) = unsafeBinaryOp ">"
366+data SortExpression params tables grouping where
368+ :: Expression params tables grouping ty
369+ -> SortExpression params tables grouping
371+ :: Expression params tables grouping ty
372+ -> SortExpression params tables grouping
374+renderSortExpression :: SortExpression params tables grouping -> ByteString
375+renderSortExpression = \case
376+ Asc expression -> renderExpression expression <> " ASC"
377+ Desc expression -> renderExpression expression <> " DESC"
379+{-----------------------------------------
381+-----------------------------------------}
384+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
385+ (tabcol :: (Symbol,Symbol)) where
387+ :: (HasTable table tables columns, HasColumn column columns ty)
388+ => (Alias table, Alias column)
389+ -> By tables '(table, column)
392+ :: ( KnownSymbol table
393+ , tables ~ '[table ::: columns]
394+ , HasColumn column columns ty
397+ -> By tables '(table, column)
398+by column = By (Alias (proxy# :: Proxy# (table :: Symbol)), column)
400+renderBy :: By tables tabcolty -> ByteString
401+renderBy (By (table, column)) = renderAlias table <> "." <> renderAlias column
403+data GroupByClause tables grouping where
404+ NoGroups :: GroupByClause tables 'Ungrouped
407+ => NP (By tables) bys
408+ -> GroupByClause tables ('Grouped bys)
410+renderGroupByClause :: GroupByClause tables grouping -> ByteString
411+renderGroupByClause = \case
414+ Group bys -> " GROUP BY " <> ByteString.intercalate ", "
415+ (hcollapse (hmap (K . renderBy) bys))
417+data HavingClause params tables grouping where
418+ NoHaving :: HavingClause params tables 'Ungrouped
420+ :: [Condition params tables ('Grouped bys)]
421+ -> HavingClause params tables ('Grouped bys)
423+renderHavingClause :: HavingClause params tables grouping -> ByteString
424+renderHavingClause = \case
427+ Having conditions -> " HAVING " <> ByteString.intercalate ", "
428+ (renderExpression <$> conditions)
430+class (KnownSymbol table, KnownSymbol column)
431+ => GroupedBy table column bys where
433+ :: (HasTable table tables columns, HasColumn column columns ty)
436+ -> Expression params tables ('Grouped bys) ty
437+ getGroup table column = UnsafeExpression $
438+ renderAlias table <> "." <> renderAlias column
439+instance {-# OVERLAPPING #-} (KnownSymbol table, KnownSymbol column)
440+ => GroupedBy table column '[ '(table,column)]
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
453+ fromLabel p = getGroup (Alias (proxy# :: Proxy# table)) (Alias p)
456+ ( HasTable table tables columns
457+ , HasColumn column columns ty
458+ , GroupedBy table column bys
459+ ) => TableColumn table column
460+ (Expression params tables ('Grouped bys) ty) where
465+ -> Expression params tables 'Ungrouped xty
466+ -> Expression params tables ('Grouped bys) yty
467+unsafeAggregate fun x = UnsafeExpression $ mconcat
468+ [fun, "(", renderExpression x, ")"]
472+ => Expression params tables 'Ungrouped ('Required (nullity ty))
473+ -> Expression params tables ('Grouped bys) ('Required (nullity ty))
474+sum_ = unsafeAggregate "sum"
476+{-----------------------------------------
478+-----------------------------------------}
481+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
482+ (columns :: [(Symbol,ColumnType)])
483+ = UnsafeTable { renderTable :: ByteString }
486+class KnownSymbol table => HasTable table tables columns
487+ | table tables -> columns where
488+ getTable :: Proxy# table -> Table tables columns
489+ getTable table = UnsafeTable $ fromString $ symbolVal' table
490+instance {-# OVERLAPPING #-} KnownSymbol table
491+ => HasTable table ((table ::: columns) ': tables) columns
492+instance {-# OVERLAPPABLE #-}
493+ (KnownSymbol table, HasTable table schema columns)
494+ => HasTable table (table' ': schema) columns
496+instance HasTable table schema columns
497+ => IsLabel table (Table schema columns) where
498+ fromLabel = getTable
500+data TableReference params schema tables where
502+ :: Aliased (Table schema) table
503+ -> TableReference params schema '[table]
505+ :: Aliased (Query schema params) table
506+ -> TableReference params schema '[table]
508+ :: TableReference params schema right
509+ -> TableReference params schema left
510+ -> TableReference params schema (Join left right)
512+ :: TableReference params schema right
513+ -> Condition params (Join left right) 'Ungrouped
514+ -> TableReference params schema left
515+ -> TableReference params schema (Join left right)
517+ :: TableReference params schema right
518+ -> Condition params (Join left right) 'Ungrouped
519+ -> TableReference params schema left
520+ -> TableReference params schema (Join left (NullifyTables right))
522+ :: TableReference params schema right
523+ -> Condition params (Join left right) 'Ungrouped
524+ -> TableReference params schema left
525+ -> TableReference params schema (Join (NullifyTables left) right)
527+ :: TableReference params schema right
528+ -> Condition params (Join left right) 'Ungrouped
529+ -> TableReference params schema left
530+ -> TableReference params schema
531+ (Join (NullifyTables left) (NullifyTables right))
533+renderTableReference :: TableReference params schema tables -> ByteString
534+renderTableReference = \case
535+ Table table -> renderAliased renderTable table
536+ Subquery selection -> renderAliased renderQuery selection
537+ CrossJoin right left -> mconcat
538+ [ renderTableReference left
540+ , renderTableReference right
542+ InnerJoin right on left -> mconcat
543+ [ renderTableReference left
545+ , renderTableReference right
547+ , renderExpression on
549+ LeftOuterJoin right on left -> mconcat
550+ [ renderTableReference left
551+ , " LEFT OUTER JOIN "
552+ , renderTableReference right
554+ , renderExpression on
556+ RightOuterJoin right on left -> mconcat
557+ [ renderTableReference left
558+ , " RIGHT OUTER JOIN "
559+ , renderTableReference right
561+ , renderExpression on
563+ FullOuterJoin right on left -> mconcat
564+ [ renderTableReference left
565+ , " FULL OUTER JOIN "
566+ , renderTableReference right
568+ , renderExpression on
572+ (params :: [ColumnType])
573+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
574+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
575+ (grouping :: Grouping)
577+ { fromClause :: TableReference params schema tables
578+ , whereClause :: [Condition params tables 'Ungrouped]
579+ , groupByClause :: GroupByClause tables grouping
580+ , havingClause :: HavingClause params tables grouping
581+ , orderByClause :: [SortExpression params tables grouping]
582+ , limitClause :: [Word64]
583+ , offsetClause :: [Word64]
586+renderTableExpression
587+ :: TableExpression params schema tables grouping
589+renderTableExpression
590+ (TableExpression tables whs' grps' hvs' srts' lims' offs') = mconcat
591+ [ "FROM ", renderTableReference tables
593+ , renderGroupByClause grps'
594+ , renderHavingClause hvs'
596+ , renderLimits lims'
597+ , renderOffsets offs'
600+ renderWheres = \case
602+ wh:[] -> " WHERE " <> renderExpression wh
603+ wh:whs -> " WHERE " <> renderExpression (foldr (&&*) wh whs)
607+ <> ByteString.intercalate ", " (renderSortExpression <$> srts)
608+ renderLimits = \case
610+ lims -> " LIMIT " <> fromString (show (minimum lims))
611+ renderOffsets = \case
613+ offs -> " OFFSET " <> fromString (show (sum offs))
616+ :: TableReference params schema tables
617+ -> TableExpression params schema tables 'Ungrouped
618+from tables = TableExpression tables [] NoGroups NoHaving [] [] []
621+ :: Condition params tables 'Ungrouped
622+ -> TableExpression params schema tables grouping
623+ -> TableExpression params schema tables grouping
624+where_ wh tables = tables {whereClause = wh : whereClause tables}
628+ => NP (By tables) bys
629+ -> TableExpression params schema tables 'Ungrouped
630+ -> TableExpression params schema tables ('Grouped bys)
631+group bys tables = TableExpression
632+ { fromClause = fromClause tables
633+ , whereClause = whereClause tables
634+ , groupByClause = Group bys
635+ , havingClause = Having []
636+ , orderByClause = []
637+ , limitClause = limitClause tables
638+ , offsetClause = offsetClause tables
642+ :: Condition params tables ('Grouped bys)
643+ -> TableExpression params schema tables ('Grouped bys)
644+ -> TableExpression params schema tables ('Grouped bys)
645+having hv tables = tables
646+ { havingClause = case havingClause tables of Having hvs -> Having (hv:hvs) }
649+ :: [SortExpression params tables grouping]
650+ -> TableExpression params schema tables grouping
651+ -> TableExpression params schema tables grouping
652+orderBy srts tables = tables {orderByClause = orderByClause tables ++ srts}
656+ -> TableExpression params schema tables grouping
657+ -> TableExpression params schema tables grouping
658+limit lim tables = tables {limitClause = lim : limitClause tables}
662+ -> TableExpression params schema tables grouping
663+ -> TableExpression params schema tables grouping
664+offset off tables = tables {offsetClause = off : offsetClause tables}
666+{-----------------------------------------
668+-----------------------------------------}
670+newtype TypeExpression (ty :: ColumnType)
671+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
674+bool :: TypeExpression ('Required ('Null 'PGBool))
675+bool = UnsafeTypeExpression "bool"
676+int2 :: TypeExpression ('Required ('Null 'PGInt2))
677+int2 = UnsafeTypeExpression "int2"
678+smallint :: TypeExpression ('Required ('Null 'PGInt2))
679+smallint = UnsafeTypeExpression "smallint"
680+int4 :: TypeExpression ('Required ('Null 'PGInt4))
681+int4 = UnsafeTypeExpression "int4"
682+int :: TypeExpression ('Required ('Null 'PGInt4))
683+int = UnsafeTypeExpression "int"
684+integer :: TypeExpression ('Required ('Null 'PGInt4))
685+integer = UnsafeTypeExpression "integer"
686+int8 :: TypeExpression ('Required ('Null 'PGInt8))
687+int8 = UnsafeTypeExpression "int8"
688+bigint :: TypeExpression ('Required ('Null 'PGInt8))
689+bigint = UnsafeTypeExpression "bigint"
690+numeric :: TypeExpression ('Required ('Null 'PGNumeric))
691+numeric = UnsafeTypeExpression "numeric"
692+float4 :: TypeExpression ('Required ('Null 'PGFloat4))
693+float4 = UnsafeTypeExpression "float4"
694+real :: TypeExpression ('Required ('Null 'PGFloat4))
695+real = UnsafeTypeExpression "real"
696+float8 :: TypeExpression ('Required ('Null 'PGFloat8))
697+float8 = UnsafeTypeExpression "float8"
698+doublePrecision :: TypeExpression ('Required ('Null 'PGFloat8))
699+doublePrecision = UnsafeTypeExpression "double precision"
700+serial2 :: TypeExpression ('Optional ('NotNull 'PGInt2))
701+serial2 = UnsafeTypeExpression "serial2"
702+smallserial :: TypeExpression ('Optional ('NotNull 'PGInt2))
703+smallserial = UnsafeTypeExpression "smallserial"
704+serial4 :: TypeExpression ('Optional ('NotNull 'PGInt4))
705+serial4 = UnsafeTypeExpression "serial4"
706+serial :: TypeExpression ('Optional ('NotNull 'PGInt4))
707+serial = UnsafeTypeExpression "serial"
708+serial8 :: TypeExpression ('Optional ('NotNull 'PGInt8))
709+serial8 = UnsafeTypeExpression "serial8"
710+bigserial :: TypeExpression ('Optional ('NotNull 'PGInt8))
711+bigserial = UnsafeTypeExpression "bigserial"
712+money :: TypeExpression ('Required ('Null 'PGMoney))
713+money = UnsafeTypeExpression "money"
714+text :: TypeExpression ('Required ('Null 'PGText))
715+text = UnsafeTypeExpression "text"
719+ -> TypeExpression ('Required ('Null ('PGChar n)))
720+char (_ :: proxy n) = UnsafeTypeExpression $
721+ "char(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
725+ -> TypeExpression ('Required ('Null ('PGChar n)))
726+character (_ :: proxy n) = UnsafeTypeExpression $
727+ "character(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
731+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
732+varchar (_ :: proxy n) = UnsafeTypeExpression $
733+ "varchar(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
737+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
738+characterVarying (_ :: proxy n) = UnsafeTypeExpression $
739+ "character varying(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
740+bytea :: TypeExpression ('Required ('Null ('PGBytea)))
741+bytea = UnsafeTypeExpression "bytea"
742+timestamp :: TypeExpression ('Required ('Null ('PGTimestamp)))
743+timestamp = UnsafeTypeExpression "timestamp"
744+timestampWithTimeZone :: TypeExpression ('Required ('Null ('PGTimestampTZ)))
745+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
746+date :: TypeExpression ('Required ('Null ('PGDate)))
747+date = UnsafeTypeExpression "date"
748+time :: TypeExpression ('Required ('Null ('PGTime)))
749+time = UnsafeTypeExpression "time"
750+timeWithTimeZone :: TypeExpression ('Required ('Null ('PGTimeTZ)))
751+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
752+interval :: TypeExpression ('Required ('Null ('PGInterval)))
753+interval = UnsafeTypeExpression "interval"
754+uuid :: TypeExpression ('Required ('Null ('PGUuid)))
755+uuid = UnsafeTypeExpression "uuid"
756+json :: TypeExpression ('Required ('Null ('PGJson)))
757+json = UnsafeTypeExpression "json"
758+jsonb :: TypeExpression ('Required ('Null ('PGJsonb)))
759+jsonb = UnsafeTypeExpression "jsonb"
762+ :: TypeExpression ('Required ('Null ty))
763+ -> TypeExpression ('Required ('NotNull ty))
764+notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <> " NOT NULL"
766+ :: Expression '[] '[] 'Ungrouped ('Required ty)
767+ -> TypeExpression ('Required ty)
768+ -> TypeExpression ('Optional ty)
769+default_ x ty = UnsafeTypeExpression $
770+ renderTypeExpression ty <> " DEFAULT " <> renderExpression x
772+class PGTyped (ty :: PGType) where
773+ pgtype :: TypeExpression ('Required ('Null ty))
774+instance PGTyped 'PGBool where pgtype = bool
775+instance PGTyped 'PGInt2 where pgtype = int2
776+instance PGTyped 'PGInt4 where pgtype = int4
777+instance PGTyped 'PGInt8 where pgtype = int8
778+instance PGTyped 'PGNumeric where pgtype = numeric
779+instance PGTyped 'PGFloat4 where pgtype = float4
780+instance PGTyped 'PGFloat8 where pgtype = float8
781+instance PGTyped 'PGMoney where pgtype = money
782+instance PGTyped 'PGText where pgtype = text
783+instance KnownNat n => PGTyped ('PGChar n) where pgtype = char (Proxy @n)
784+instance KnownNat n => PGTyped ('PGVarChar n) where pgtype = varchar (Proxy @n)
785+instance PGTyped 'PGBytea where pgtype = bytea
786+instance PGTyped 'PGTimestamp where pgtype = timestamp
787+instance PGTyped 'PGTimestampTZ where pgtype = timestampWithTimeZone
788+instance PGTyped 'PGDate where pgtype = date
789+instance PGTyped 'PGTime where pgtype = time
790+instance PGTyped 'PGTimeTZ where pgtype = timeWithTimeZone
791+instance PGTyped 'PGInterval where pgtype = interval
792+instance PGTyped 'PGUuid where pgtype = uuid
793+instance PGTyped 'PGJson where pgtype = json
794+instance PGTyped 'PGJsonb where pgtype = jsonb
796+{-----------------------------------------
798+-----------------------------------------}
801+ (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
802+ (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
803+ = UnsafeDefinition { renderDefinition :: ByteString }
805+instance Category Definition where
806+ id = UnsafeDefinition ";"
807+ dd1 . dd0 = UnsafeDefinition $
808+ renderDefinition dd0 <> " " <> renderDefinition dd1
811+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
812+ (params :: [ColumnType])
813+ (columns :: [(Symbol,ColumnType)])
814+ = UnsafeManipulation { renderManipulation :: ByteString }
817+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
818+ (params :: [ColumnType])
819+ (columns :: [(Symbol,ColumnType)])
820+ = UnsafeQuery { renderQuery :: ByteString }
823+ :: Query schema params columns
824+ -> Manipulation schema params columns
825+query q = UnsafeManipulation $ renderQuery q <> ";"
827+union, unionAll, intersect, intersectAll, except, exceptAll
828+ :: Query schema params columns
829+ -> Query schema params columns
830+ -> Query schema params columns
831+q1 `union` q2 = UnsafeQuery $
832+ "(" <> renderQuery q1 <> ") UNION (" <> renderQuery q2 <> ")"
833+q1 `unionAll` q2 = UnsafeQuery $
834+ "(" <> renderQuery q1 <> ") UNION ALL " <> renderQuery q2
835+q1 `intersect` q2 = UnsafeQuery $
836+ "(" <> renderQuery q1 <> ") INTERSECT " <> renderQuery q2
837+q1 `intersectAll` q2 = UnsafeQuery $
838+ "(" <> renderQuery q1 <> ") INTERSECT ALL " <> renderQuery q2
839+q1 `except` q2 = UnsafeQuery $
840+ "(" <> renderQuery q1 <> ") EXCEPT " <> renderQuery q2
841+q1 `exceptAll` q2 = UnsafeQuery $
842+ "(" <> renderQuery q1 <> ") EXCEPT ALL " <> renderQuery q2
844+{-----------------------------------------
846+-----------------------------------------}
850+ => NP (Aliased (Expression params tables grouping)) columns
851+ -> TableExpression params schema tables grouping
852+ -> Query schema params columns
853+select list tabs = UnsafeQuery $
854+ "SELECT " <> renderList list <> " " <> renderTableExpression tabs
857+ = ByteString.intercalate ", "
859+ . hmap (K . renderAliased renderExpression)
862+ :: (tables ~ '[table ::: columns])
863+ => TableExpression params schema tables 'Ungrouped
864+ -> Query schema params columns
865+selectStar tabs = UnsafeQuery $ "SELECT * " <> renderTableExpression tabs
868+ :: HasTable table tables columns
870+ -> TableExpression params schema tables 'Ungrouped
871+ -> Query schema params columns
872+selectDotStar table tables = UnsafeQuery $
873+ "SELECT " <> renderAlias table <> ".* " <> renderTableExpression tables
875+{-----------------------------------------
877+-----------------------------------------}
880+ :: (KnownSymbol table, SListI columns)
882+ -> NP (Aliased TypeExpression) columns
883+ -> Definition schema (Create table columns schema)
884+createTable table columns = UnsafeDefinition $ mconcat
888+ , ByteString.intercalate ", " . hcollapse $
889+ hmap (K . renderColumn) columns
893+ renderColumn :: Aliased TypeExpression x -> ByteString
894+ renderColumn (ty `As` column) =
895+ renderAlias column <> " " <> renderTypeExpression ty
897+{-----------------------------------------
899+-----------------------------------------}
902+ :: KnownSymbol table
904+ -> Definition schema (Drop table schema)
905+dropTable table = UnsafeDefinition $ "DROP TABLE " <> renderAlias table <> ";"
907+{-----------------------------------------
909+-----------------------------------------}
912+ :: HasTable table schema columns0
914+ -> AlterTable columns0 columns1
915+ -> Definition schema (Alter table schema columns1)
916+alterTable table alteration = UnsafeDefinition $
918+ <> renderAlias table
920+ <> renderAlterTable alteration
924+ :: (KnownSymbol table0, KnownSymbol table1)
927+ -> Definition schema (Rename table0 table1 schema)
928+alterTableRename table0 table1 = UnsafeDefinition $
930+ <> renderAlias table0
932+ <> renderAlias table1
936+ (columns0 :: [(Symbol, ColumnType)])
937+ (columns1 :: [(Symbol, ColumnType)]) =
938+ UnsafeAlterTable {renderAlterTable :: ByteString}
941+ :: KnownSymbol column
943+ -> TypeExpression ('Optional ty)
944+ -> AlterTable columns (Create column ('Optional ty) columns)
945+addColumnDefault column ty = UnsafeAlterTable $
947+ <> renderAlias column
949+ <> renderTypeExpression ty
952+ :: KnownSymbol column
954+ -> TypeExpression ('Required ('Null ty))
955+ -> AlterTable columns ((column ::: 'Required ('Null ty)) ': columns)
956+addColumnNull column ty = UnsafeAlterTable $
958+ <> renderAlias column
960+ <> renderTypeExpression ty
963+ :: KnownSymbol column
965+ -> AlterTable columns (Drop column columns)
966+dropColumn column = UnsafeAlterTable $
967+ "DROP COLUMN " <> renderAlias column
970+ :: (KnownSymbol column0, KnownSymbol column1)
973+ -> AlterTable columns (Rename column0 column1 columns)
974+renameColumn column0 column1 = UnsafeAlterTable $
975+ "RENAME COLUMN " <> renderAlias column0
976+ <> " TO " <> renderAlias column1
979+ :: (KnownSymbol column, HasColumn column columns ty0)
981+ -> AlterColumn ty0 ty1
982+ -> AlterTable columns (Alter column columns ty1)
983+alterColumn column alteration = UnsafeAlterTable $
984+ "ALTER COLUMN " <> renderAlias column
985+ <> " " <> renderAlterColumn alteration
987+data AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
988+ UnsafeAlterColumn {renderAlterColumn :: ByteString}
991+ :: Expression '[] '[] 'Ungrouped ('Required ty)
992+ -> AlterColumn ('Required ty) ('Optional ty)
993+setDefault expression = UnsafeAlterColumn $
994+ "SET DEFAULT " <> renderExpression expression
996+dropDefault :: AlterColumn ('Optional ty) ('Required ty)
997+dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
1000+ :: AlterColumn (optionality ('Null ty)) (optionality ('NotNull ty))
1001+setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
1004+ :: AlterColumn (optionality ('NotNull ty)) (optionality ('Null ty))
1005+dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
1009+ => TypeExpression (optionality (nullity ty1))
1010+ -> AlterColumn (optionality (nullity ty0)) (optionality (nullity ty1))
1011+alterType ty = UnsafeAlterColumn $ "TYPE " <> renderTypeExpression ty
1013+{-----------------------------------------
1015+-----------------------------------------}
1018+ :: (SListI columns, HasTable table schema columns)
1020+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1021+ -> Manipulation schema params '[]
1022+insertInto table expressions = UnsafeManipulation $ "INSERT INTO "
1023+ <> renderAlias table
1024+ <> " (" <> ByteString.intercalate ", " aliases
1026+ <> ByteString.intercalate ", " values
1029+ aliases = hcollapse $ hmap
1030+ (\ (_ `As` name) -> K (renderAlias name))
1032+ values = hcollapse $ hmap
1033+ (\ (expression `As` _) -> K (renderExpression expression))
1037+ :: (SListI columns, SListI results, HasTable table schema columns)
1039+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1040+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1041+ -> Manipulation schema params results
1042+insertIntoReturning table inserts results
1043+ = UnsafeManipulation . mconcat $
1045+ , renderAlias table
1046+ , " (" <> ByteString.intercalate ", " aliases
1048+ , ByteString.intercalate ", " values
1050+ , renderList results
1054+ aliases = hcollapse $ hmap
1055+ (\ (_ `As` name) -> K (renderAlias name))
1057+ values = hcollapse $ hmap
1058+ (\ (expression `As` _) -> K (renderExpression expression))
1061+ = ByteString.intercalate ", "
1063+ . hmap (K . renderAliased renderExpression)
1065+{-----------------------------------------
1067+-----------------------------------------}
1069+data UpdateExpression params table ty
1071+ | Set (Expression params table 'Ungrouped ty)
1073+renderUpdateExpression
1074+ :: Aliased (UpdateExpression params '[table ::: columns]) column
1075+ -> Maybe ByteString
1076+renderUpdateExpression = \case
1077+ Same `As` _ -> Nothing
1078+ Set expression `As` column -> Just $ mconcat
1079+ [ renderAlias column
1081+ , renderExpression expression
1085+ :: (HasTable table schema columns, SListI columns)
1087+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1088+ -> Condition params '[table ::: columns] 'Ungrouped
1089+ -> Manipulation schema params '[]
1090+update table columns wh = UnsafeManipulation $ mconcat
1092+ , renderAlias table
1094+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1095+ hmap (K . renderUpdateExpression) columns
1096+ , " WHERE ", renderExpression wh, ";"
1100+ :: (SListI columns, SListI results, HasTable table schema columns)
1102+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1103+ -> Condition params '[table ::: columns] 'Ungrouped
1104+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1105+ -> Manipulation schema params results
1106+updateReturning table updates wh results = UnsafeManipulation $ mconcat
1108+ , renderAlias table
1110+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1111+ hmap (K . renderUpdateExpression) updates
1112+ , " WHERE ", renderExpression wh
1113+ , " RETURNING ", renderList results
1118+ = ByteString.intercalate ", "
1120+ . hmap (K . renderAliased renderExpression)
1123+ :: (SListI columns, HasTable table schema columns)
1125+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1126+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1127+ -> Condition params '[table ::: columns] 'Ungrouped
1128+ -> Manipulation schema params '[]
1129+upsertInto table inserts updates wh = UnsafeManipulation . mconcat $
1131+ , renderAlias table
1132+ , " (" <> ByteString.intercalate ", " aliases
1134+ , ByteString.intercalate ", " values
1135+ , ") ON CONFLICT UPDATE "
1136+ , renderAlias table
1138+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1139+ hmap (K . renderUpdateExpression) updates
1140+ , " WHERE ", renderExpression wh, ";"
1142+ aliases = hcollapse $ hmap
1143+ (\ (_ `As` name) -> K (renderAlias name))
1145+ values = hcollapse $ hmap
1146+ (\ (expression `As` _) -> K (renderExpression expression))
1150+ :: (SListI columns, SListI results, HasTable table schema columns)
1152+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1153+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1154+ -> Condition params '[table ::: columns] 'Ungrouped
1155+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1156+ -> Manipulation schema params results
1157+upsertIntoReturning table inserts updates wh results =
1158+ UnsafeManipulation . mconcat $
1160+ , renderAlias table
1161+ , " (" <> ByteString.intercalate ", " aliases
1163+ , ByteString.intercalate ", " values
1164+ , ") ON CONFLICT UPDATE "
1165+ , renderAlias table
1167+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1168+ hmap (K . renderUpdateExpression) updates
1169+ , " WHERE ", renderExpression wh
1170+ , " RETURNING ", renderList results
1173+ aliases = hcollapse $ hmap
1174+ (\ (_ `As` name) -> K (renderAlias name))
1176+ values = hcollapse $ hmap
1177+ (\ (expression `As` _) -> K (renderExpression expression))
1180+ = ByteString.intercalate ", "
1182+ . hmap (K . renderAliased renderExpression)
1184+{-----------------------------------------
1186+-----------------------------------------}
1189+ :: HasTable table schema columns
1191+ -> Condition params '[table ::: columns] 'Ungrouped
1192+ -> Manipulation schema params '[]
1193+deleteFrom table wh = UnsafeManipulation $ mconcat
1195+ , renderAlias table
1196+ , " WHERE ", renderExpression wh, ";"