@@ -0,0 +1,1177 @@
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+notDef = UnsafeExpression . renderExpression
96+null :: Expression params tables grouping (optionality ('Null ty))
97+null = UnsafeExpression "NULL"
100+ :: [Expression params tables grouping ('Required ('Null x))]
101+ -> Expression params tables grouping ('Required ('NotNull x))
102+ -> Expression params tables grouping ('Required ('NotNull x))
103+coalesce nulls isn'tNull = UnsafeExpression $ mconcat
105+ , ByteString.intercalate ", " $ map renderExpression nulls
106+ , ", ", renderExpression isn'tNull
112+ -> Expression params tables grouping ty0
113+ -> Expression params tables grouping ty1
114+ -> Expression params tables grouping ty2
115+unsafeBinaryOp op x y = UnsafeExpression $ mconcat
116+ ["(", renderExpression x, " ", op, " ", renderExpression y, ")"]
120+ -> Expression params tables grouping ty0
121+ -> Expression params tables grouping ty1
122+unsafeUnaryOp op x = UnsafeExpression $ mconcat
123+ ["(", op, " ", renderExpression x, ")"]
127+ -> Expression params tables grouping xty
128+ -> Expression params tables grouping yty
129+unsafeFunction fun x = UnsafeExpression $ mconcat
130+ [fun, "(", renderExpression x, ")"]
133+ => Num (Expression params tables grouping ('Required (nullity ty))) where
134+ (+) = unsafeBinaryOp "+"
135+ (-) = unsafeBinaryOp "-"
136+ (*) = unsafeBinaryOp "*"
137+ abs = unsafeFunction "abs"
138+ signum = unsafeFunction "sign"
141+ . (<> if decimal (Proxy :: Proxy ty) then "." else "")
145+instance PGFractional ty => Fractional
146+ (Expression params tables grouping ('Required (nullity ty))) where
147+ (/) = unsafeBinaryOp "/"
148+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
150+instance (PGFloating ty, PGCast 'PGNumeric ty, PGTyped ty) => Floating
151+ (Expression params tables grouping ('Required (nullity ty))) where
152+ pi = UnsafeExpression "pi()"
153+ exp = unsafeFunction "exp"
154+ log = unsafeFunction "ln"
155+ sqrt = unsafeFunction "sqrt"
156+ b ** x = UnsafeExpression $
157+ "power(" <> renderExpression b <> ", " <> renderExpression x <> ")"
158+ logBase b y = cast pgtype $ logBaseNumeric b y
161+ :: Expression params tables grouping ('Required (nullity ty))
162+ -> Expression params tables grouping ('Required (nullity ty))
163+ -> Expression params tables grouping ('Required (nullity 'PGNumeric))
164+ logBaseNumeric b' y' = UnsafeExpression $ mconcat
166+ , renderExpression b' <> "::numeric"
168+ , renderExpression y' <> "::numeric"
171+ sin = unsafeFunction "sin"
172+ cos = unsafeFunction "cos"
173+ tan = unsafeFunction "tan"
174+ asin = unsafeFunction "asin"
175+ acos = unsafeFunction "acos"
176+ atan = unsafeFunction "atan"
177+ sinh x = (exp x - exp (-x)) / 2
178+ cosh x = (exp x + exp (-x)) / 2
179+ tanh x = sinh x / cosh x
180+ asinh x = log (x + sqrt (x*x + 1))
181+ acosh x = log (x + sqrt (x*x - 1))
182+ atanh x = log ((1 + x) / (1 - x)) / 2
186+ => Expression params tables grouping ('Required (nullity float))
187+ -> Expression params tables grouping ('Required (nullity float))
188+ -> Expression params tables grouping ('Required (nullity float))
189+atan2 y x = UnsafeExpression $
190+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
192+class PGCast (ty0 :: PGType) (ty1 :: PGType) where
194+ :: TypeExpression ('Required ('Null ty1))
195+ -> Expression params tables grouping ('Required (nullity ty0))
196+ -> Expression params tables grouping ('Required (nullity ty1))
197+ cast ty x = UnsafeExpression $
198+ "(" <> renderExpression x <> "::" <> renderTypeExpression ty <> ")"
199+instance PGCast 'PGInt2 'PGInt2
200+instance PGCast 'PGInt2 'PGInt4
201+instance PGCast 'PGInt2 'PGInt8
202+instance PGCast 'PGInt4 'PGInt2
203+instance PGCast 'PGInt4 'PGInt4
204+instance PGCast 'PGInt4 'PGInt8
205+instance PGCast 'PGInt8 'PGInt2
206+instance PGCast 'PGInt8 'PGInt4
207+instance PGCast 'PGInt8 'PGInt8
208+instance PGCast 'PGInt8 'PGFloat4
209+instance PGCast 'PGInt8 'PGFloat8
210+instance PGCast 'PGInt8 'PGNumeric
213+ :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
214+ => Expression params tables grouping ('Required (nullity ty))
215+ -> Expression params tables grouping ('Required (nullity ty))
216+ -> Expression params tables grouping ('Required (nullity ty))
217+div = unsafeBinaryOp "/"
220+ :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
221+ => Expression params tables grouping ('Required (nullity ty))
222+ -> Expression params tables grouping ('Required (nullity ty))
223+ -> Expression params tables grouping ('Required (nullity ty))
224+mod = unsafeBinaryOp "%"
227+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
228+ => Expression params tables grouping ('Required (nullity frac))
229+ -> Expression params tables grouping ('Required (nullity int))
230+truncate = cast pgtype . truncate'
233+ :: Expression params tables grouping ('Required (nullity frac))
234+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
235+ truncate' = unsafeFunction "trunc"
238+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
239+ => Expression params tables grouping ('Required (nullity frac))
240+ -> Expression params tables grouping ('Required (nullity int))
241+round = cast pgtype . round'
244+ :: Expression params tables grouping ('Required (nullity frac))
245+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
246+ round' = unsafeFunction "round"
249+ :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
250+ => Expression params tables grouping ('Required (nullity frac))
251+ -> Expression params tables grouping ('Required (nullity int))
252+ceiling = cast pgtype . ceiling'
255+ :: Expression params tables grouping ('Required (nullity frac))
256+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
257+ ceiling' = unsafeFunction "ceiling"
260+ (Expression params tables grouping ('Required (nullity 'PGText))) where
261+ fromString str = UnsafeExpression $
262+ "E\'" <> fromString (escape =<< str) <> "\'"
275+type Condition params tables grouping =
276+ Expression params tables grouping ('Required ('NotNull 'PGBool))
278+true :: Condition params tables grouping
279+true = UnsafeExpression "TRUE"
281+false :: Condition params tables grouping
282+false = UnsafeExpression "FALSE"
285+ :: Condition params tables grouping
286+ -> Condition params tables grouping
287+not_ = unsafeUnaryOp "NOT"
290+ :: Condition params tables grouping
291+ -> Condition params tables grouping
292+ -> Condition params tables grouping
293+(&&*) = unsafeBinaryOp "AND"
296+ :: Condition params tables grouping
297+ -> Condition params tables grouping
298+ -> Condition params tables grouping
299+(||*) = unsafeBinaryOp "OR"
302+ :: [(Condition params tables grouping, Expression params tables grouping ty)]
303+ -> Expression params tables grouping ty
304+ -> Expression params tables grouping ty
305+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
309+ [ " WHEN ", renderExpression when_
310+ , " THEN ", renderExpression then_
312+ | (when_,then_) <- whenThens
314+ , " ELSE ", renderExpression else_
319+ :: Condition params tables grouping
320+ -> Expression params tables grouping ty
321+ -> Expression params tables grouping ty
322+ -> Expression params tables grouping ty
323+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
326+ :: Expression params tables grouping ('Required (nullity ty))
327+ -> Expression params tables grouping ('Required (nullity ty))
328+ -> Expression params tables grouping ('Required (nullity 'PGBool))
329+(==*) = unsafeBinaryOp "="
332+ :: Expression params tables grouping ('Required (nullity ty))
333+ -> Expression params tables grouping ('Required (nullity ty))
334+ -> Expression params tables grouping ('Required (nullity 'PGBool))
335+(/=*) = unsafeBinaryOp "<>"
338+ :: Expression params tables grouping ('Required (nullity ty))
339+ -> Expression params tables grouping ('Required (nullity ty))
340+ -> Expression params tables grouping ('Required (nullity 'PGBool))
341+(>=*) = unsafeBinaryOp ">="
344+ :: Expression params tables grouping ('Required (nullity ty))
345+ -> Expression params tables grouping ('Required (nullity ty))
346+ -> Expression params tables grouping ('Required (nullity 'PGBool))
347+(<*) = unsafeBinaryOp "<"
350+ :: Expression params tables grouping ('Required (nullity ty))
351+ -> Expression params tables grouping ('Required (nullity ty))
352+ -> Expression params tables grouping ('Required (nullity 'PGBool))
353+(<=*) = unsafeBinaryOp "<="
356+ :: Expression params tables grouping ('Required (nullity ty))
357+ -> Expression params tables grouping ('Required (nullity ty))
358+ -> Expression params tables grouping ('Required (nullity 'PGBool))
359+(>*) = unsafeBinaryOp ">"
361+data SortExpression params tables grouping where
363+ :: Expression params tables grouping ty
364+ -> SortExpression params tables grouping
366+ :: Expression params tables grouping ty
367+ -> SortExpression params tables grouping
369+renderSortExpression :: SortExpression params tables grouping -> ByteString
370+renderSortExpression = \case
371+ Asc expression -> renderExpression expression <> " ASC"
372+ Desc expression -> renderExpression expression <> " DESC"
374+data Aggregate (ty0 :: ColumnType) (ty1 :: ColumnType) =
375+ UnsafeAggregate { renderAggregate :: ByteString }
379+ => Aggregate ('Required (nullity ty)) ('Required (nullity ty))
380+sum_ = UnsafeAggregate "sum"
382+class (KnownSymbol table, KnownSymbol column)
383+ => GroupedBy table column distincts ty where
385+ :: (HasTable table tables columns, HasColumn column columns ty)
388+ -> Expression params tables ('Grouped distincts) ty
391+ ( HasTable table tables columns
392+ , HasColumn column columns ty
393+ , GroupedBy table column distincts ty
394+ ) => TableColumn table column
395+ (Expression params tables ('Grouped distincts) ty) where
398+class (KnownSymbol table, KnownSymbol column)
399+ => Aggregated table column distincts ty where
401+ :: (HasTable table tables columns, HasColumn column columns ty0)
405+ -> Expression params tables ('Grouped distincts) ty
408+ ( HasTable table tables columns
409+ , HasColumn column columns ty0
410+ , Aggregated table column distincts ty
411+ ) => TableColumn table column
412+ (Aggregate ty0 ty -> Expression params tables ('Grouped distincts) ty) where
415+{-----------------------------------------
417+-----------------------------------------}
420+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
421+ (columns :: [(Symbol,ColumnType)])
422+ = UnsafeTable { renderTable :: ByteString }
425+class KnownSymbol table => HasTable table tables columns
426+ | table tables -> columns where
427+ getTable :: Proxy# table -> Table tables columns
428+ getTable table = UnsafeTable $ fromString $ symbolVal' table
429+instance {-# OVERLAPPING #-} KnownSymbol table
430+ => HasTable table ((table ::: columns) ': tables) columns
431+instance {-# OVERLAPPABLE #-}
432+ (KnownSymbol table, HasTable table schema columns)
433+ => HasTable table (table' ': schema) columns
435+instance HasTable table schema columns
436+ => IsLabel table (Table schema columns) where
437+ fromLabel = getTable
439+data TableReference params schema tables where
441+ :: Aliased (Table schema) table
442+ -> TableReference params schema '[table]
444+ :: Aliased (Query schema params) table
445+ -> TableReference params schema '[table]
447+ :: TableReference params schema right
448+ -> TableReference params schema left
449+ -> TableReference params schema (Join left right)
451+ :: TableReference params schema right
452+ -> Condition params (Join left right) 'Ungrouped
453+ -> TableReference params schema left
454+ -> TableReference params schema (Join left right)
456+ :: TableReference params schema right
457+ -> Condition params (Join left right) 'Ungrouped
458+ -> TableReference params schema left
459+ -> TableReference params schema (Join left (NullifyTables right))
461+ :: TableReference params schema right
462+ -> Condition params (Join left right) 'Ungrouped
463+ -> TableReference params schema left
464+ -> TableReference params schema (Join (NullifyTables left) right)
466+ :: TableReference params schema right
467+ -> Condition params (Join left right) 'Ungrouped
468+ -> TableReference params schema left
469+ -> TableReference params schema
470+ (Join (NullifyTables left) (NullifyTables right))
472+renderTableReference :: TableReference params schema tables -> ByteString
473+renderTableReference = \case
474+ Table table -> renderAliased renderTable table
475+ Subquery selection -> renderAliased renderQuery selection
476+ CrossJoin right left -> mconcat
477+ [ renderTableReference left
479+ , renderTableReference right
481+ InnerJoin right on left -> mconcat
482+ [ renderTableReference left
484+ , renderTableReference right
486+ , renderExpression on
488+ LeftOuterJoin right on left -> mconcat
489+ [ renderTableReference left
490+ , " LEFT OUTER JOIN "
491+ , renderTableReference right
493+ , renderExpression on
495+ RightOuterJoin right on left -> mconcat
496+ [ renderTableReference left
497+ , " RIGHT OUTER JOIN "
498+ , renderTableReference right
500+ , renderExpression on
502+ FullOuterJoin right on left -> mconcat
503+ [ renderTableReference left
504+ , " FULL OUTER JOIN "
505+ , renderTableReference right
507+ , renderExpression on
510+data By tables tabcolty where
512+ :: (HasTable table tables columns, HasColumn column columns ty)
515+ -> By tables (table ::: (column ::: ty))
517+instance (HasTable table tables columns, HasColumn column columns ty)
518+ => TableColumn table column (By tables (table ::: (column ::: ty))) where
521+renderBy :: By tables tabcolty -> ByteString
522+renderBy (By table column) = renderAlias table <> "." <> renderAlias column
524+data GroupByClause tables grouping where
525+ NoGroups :: GroupByClause tables 'Ungrouped
528+ => NP (By tables) distincts
529+ -> GroupByClause tables ('Grouped distincts)
531+renderGroupByClause :: GroupByClause tables grouping -> ByteString
532+renderGroupByClause = \case
535+ Distinct distincts -> " GROUP BY " <> ByteString.intercalate ", "
536+ (hcollapse (hmap (K . renderBy) distincts))
538+data HavingClause params tables grouping where
539+ NoHaving :: HavingClause params tables 'Ungrouped
541+ :: [Condition params tables ('Grouped distincts)]
542+ -> HavingClause params tables ('Grouped distincts)
544+renderHavingClause :: HavingClause params tables grouping -> ByteString
545+renderHavingClause = \case
548+ Having conditions -> " HAVING " <> ByteString.intercalate ", "
549+ (renderExpression <$> conditions)
552+ (params :: [ColumnType])
553+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
554+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
555+ (grouping :: Grouping)
557+ { fromClause :: TableReference params schema tables
558+ , whereClause :: [Condition params tables 'Ungrouped]
559+ , groupByClause :: GroupByClause tables grouping
560+ , havingClause :: HavingClause params tables grouping
561+ , orderByClause :: [SortExpression params tables grouping]
562+ , limitClause :: [Word64]
563+ , offsetClause :: [Word64]
566+renderTableExpression
567+ :: TableExpression params schema tables grouping
569+renderTableExpression
570+ (TableExpression tables whs' grps' hvs' srts' lims' offs') = mconcat
571+ [ "FROM ", renderTableReference tables
573+ , renderGroupByClause grps'
574+ , renderHavingClause hvs'
576+ , renderLimits lims'
577+ , renderOffsets offs'
580+ renderWheres = \case
582+ wh:[] -> " WHERE " <> renderExpression wh
583+ wh:whs -> " WHERE " <> renderExpression (foldr (&&*) wh whs)
587+ <> ByteString.intercalate ", " (renderSortExpression <$> srts)
588+ renderLimits = \case
590+ lims -> " LIMIT " <> fromString (show (minimum lims))
591+ renderOffsets = \case
593+ offs -> " OFFSET " <> fromString (show (sum offs))
596+ :: TableReference params schema tables
597+ -> TableExpression params schema tables 'Ungrouped
598+from tables = TableExpression tables [] NoGroups NoHaving [] [] []
601+ :: Condition params tables 'Ungrouped
602+ -> TableExpression params schema tables grouping
603+ -> TableExpression params schema tables grouping
604+where_ wh tables = tables {whereClause = wh : whereClause tables}
608+ => NP (By tables) distincts
609+ -> TableExpression params schema tables 'Ungrouped
610+ -> TableExpression params schema tables ('Grouped distincts)
611+group distincts tables = TableExpression
612+ { fromClause = fromClause tables
613+ , whereClause = whereClause tables
614+ , groupByClause = Distinct distincts
615+ , havingClause = Having []
616+ , orderByClause = []
617+ , limitClause = limitClause tables
618+ , offsetClause = offsetClause tables
622+ :: Condition params tables ('Grouped distincts)
623+ -> TableExpression params schema tables ('Grouped distincts)
624+ -> TableExpression params schema tables ('Grouped distincts)
625+having hv tables = tables
626+ { havingClause = case havingClause tables of Having hvs -> Having (hv:hvs) }
629+ :: [SortExpression params tables grouping]
630+ -> TableExpression params schema tables grouping
631+ -> TableExpression params schema tables grouping
632+orderBy srts tables = tables {orderByClause = orderByClause tables ++ srts}
636+ -> TableExpression params schema tables grouping
637+ -> TableExpression params schema tables grouping
638+limit lim tables = tables {limitClause = lim : limitClause tables}
642+ -> TableExpression params schema tables grouping
643+ -> TableExpression params schema tables grouping
644+offset off tables = tables {offsetClause = off : offsetClause tables}
646+{-----------------------------------------
648+-----------------------------------------}
650+newtype TypeExpression (ty :: ColumnType)
651+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
654+bool :: TypeExpression ('Required ('Null 'PGBool))
655+bool = UnsafeTypeExpression "bool"
656+int2 :: TypeExpression ('Required ('Null 'PGInt2))
657+int2 = UnsafeTypeExpression "int2"
658+smallint :: TypeExpression ('Required ('Null 'PGInt2))
659+smallint = UnsafeTypeExpression "smallint"
660+int4 :: TypeExpression ('Required ('Null 'PGInt4))
661+int4 = UnsafeTypeExpression "int4"
662+int :: TypeExpression ('Required ('Null 'PGInt4))
663+int = UnsafeTypeExpression "int"
664+integer :: TypeExpression ('Required ('Null 'PGInt4))
665+integer = UnsafeTypeExpression "integer"
666+int8 :: TypeExpression ('Required ('Null 'PGInt8))
667+int8 = UnsafeTypeExpression "int8"
668+bigint :: TypeExpression ('Required ('Null 'PGInt8))
669+bigint = UnsafeTypeExpression "bigint"
670+numeric :: TypeExpression ('Required ('Null 'PGNumeric))
671+numeric = UnsafeTypeExpression "numeric"
672+float4 :: TypeExpression ('Required ('Null 'PGFloat4))
673+float4 = UnsafeTypeExpression "float4"
674+real :: TypeExpression ('Required ('Null 'PGFloat4))
675+real = UnsafeTypeExpression "real"
676+float8 :: TypeExpression ('Required ('Null 'PGFloat8))
677+float8 = UnsafeTypeExpression "float8"
678+doublePrecision :: TypeExpression ('Required ('Null 'PGFloat8))
679+doublePrecision = UnsafeTypeExpression "double precision"
680+serial2 :: TypeExpression ('Optional ('NotNull 'PGInt2))
681+serial2 = UnsafeTypeExpression "serial2"
682+smallserial :: TypeExpression ('Optional ('NotNull 'PGInt2))
683+smallserial = UnsafeTypeExpression "smallserial"
684+serial4 :: TypeExpression ('Optional ('NotNull 'PGInt4))
685+serial4 = UnsafeTypeExpression "serial4"
686+serial :: TypeExpression ('Optional ('NotNull 'PGInt4))
687+serial = UnsafeTypeExpression "serial"
688+serial8 :: TypeExpression ('Optional ('NotNull 'PGInt8))
689+serial8 = UnsafeTypeExpression "serial8"
690+bigserial :: TypeExpression ('Optional ('NotNull 'PGInt8))
691+bigserial = UnsafeTypeExpression "bigserial"
692+money :: TypeExpression ('Required ('Null 'PGMoney))
693+money = UnsafeTypeExpression "money"
694+text :: TypeExpression ('Required ('Null 'PGText))
695+text = UnsafeTypeExpression "text"
699+ -> TypeExpression ('Required ('Null ('PGChar n)))
700+char (_ :: proxy n) = UnsafeTypeExpression $
701+ "char(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
705+ -> TypeExpression ('Required ('Null ('PGChar n)))
706+character (_ :: proxy n) = UnsafeTypeExpression $
707+ "character(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
711+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
712+varchar (_ :: proxy n) = UnsafeTypeExpression $
713+ "varchar(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
717+ -> TypeExpression ('Required ('Null ('PGVarChar n)))
718+characterVarying (_ :: proxy n) = UnsafeTypeExpression $
719+ "character varying(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
720+bytea :: TypeExpression ('Required ('Null ('PGBytea)))
721+bytea = UnsafeTypeExpression "bytea"
722+timestamp :: TypeExpression ('Required ('Null ('PGTimestamp)))
723+timestamp = UnsafeTypeExpression "timestamp"
724+timestampWithTimeZone :: TypeExpression ('Required ('Null ('PGTimestampTZ)))
725+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
726+date :: TypeExpression ('Required ('Null ('PGDate)))
727+date = UnsafeTypeExpression "date"
728+time :: TypeExpression ('Required ('Null ('PGTime)))
729+time = UnsafeTypeExpression "time"
730+timeWithTimeZone :: TypeExpression ('Required ('Null ('PGTimeTZ)))
731+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
732+interval :: TypeExpression ('Required ('Null ('PGInterval)))
733+interval = UnsafeTypeExpression "interval"
734+uuid :: TypeExpression ('Required ('Null ('PGUuid)))
735+uuid = UnsafeTypeExpression "uuid"
736+json :: TypeExpression ('Required ('Null ('PGJson)))
737+json = UnsafeTypeExpression "json"
738+jsonb :: TypeExpression ('Required ('Null ('PGJsonb)))
739+jsonb = UnsafeTypeExpression "jsonb"
742+ :: TypeExpression ('Required ('Null ty))
743+ -> TypeExpression ('Required ('NotNull ty))
744+notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <> " NOT NULL"
746+ :: Expression '[] '[] 'Ungrouped ('Required ty)
747+ -> TypeExpression ('Required ty)
748+ -> TypeExpression ('Optional ty)
749+default_ x ty = UnsafeTypeExpression $
750+ renderTypeExpression ty <> " DEFAULT " <> renderExpression x
752+class PGTyped (ty :: PGType) where
753+ pgtype :: TypeExpression ('Required ('Null ty))
754+instance PGTyped 'PGBool where pgtype = bool
755+instance PGTyped 'PGInt2 where pgtype = int2
756+instance PGTyped 'PGInt4 where pgtype = int4
757+instance PGTyped 'PGInt8 where pgtype = int8
758+instance PGTyped 'PGNumeric where pgtype = numeric
759+instance PGTyped 'PGFloat4 where pgtype = float4
760+instance PGTyped 'PGFloat8 where pgtype = float8
761+instance PGTyped 'PGMoney where pgtype = money
762+instance PGTyped 'PGText where pgtype = text
763+instance KnownNat n => PGTyped ('PGChar n) where pgtype = char (Proxy @n)
764+instance KnownNat n => PGTyped ('PGVarChar n) where pgtype = varchar (Proxy @n)
765+instance PGTyped 'PGBytea where pgtype = bytea
766+instance PGTyped 'PGTimestamp where pgtype = timestamp
767+instance PGTyped 'PGTimestampTZ where pgtype = timestampWithTimeZone
768+instance PGTyped 'PGDate where pgtype = date
769+instance PGTyped 'PGTime where pgtype = time
770+instance PGTyped 'PGTimeTZ where pgtype = timeWithTimeZone
771+instance PGTyped 'PGInterval where pgtype = interval
772+instance PGTyped 'PGUuid where pgtype = uuid
773+instance PGTyped 'PGJson where pgtype = json
774+instance PGTyped 'PGJsonb where pgtype = jsonb
776+{-----------------------------------------
778+-----------------------------------------}
781+ (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
782+ (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
783+ = UnsafeDefinition { renderDefinition :: ByteString }
785+instance Category Definition where
786+ id = UnsafeDefinition ";"
787+ dd1 . dd0 = UnsafeDefinition $
788+ renderDefinition dd0 <> " " <> renderDefinition dd1
791+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
792+ (params :: [ColumnType])
793+ (columns :: [(Symbol,ColumnType)])
794+ = UnsafeManipulation { renderManipulation :: ByteString }
797+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
798+ (params :: [ColumnType])
799+ (columns :: [(Symbol,ColumnType)])
800+ = UnsafeQuery { renderQuery :: ByteString }
803+ :: Query schema params columns
804+ -> Manipulation schema params columns
805+query q = UnsafeManipulation $ renderQuery q <> ";"
807+union, unionAll, intersect, intersectAll, except, exceptAll
808+ :: Query schema params columns
809+ -> Query schema params columns
810+ -> Query schema params columns
811+q1 `union` q2 = UnsafeQuery $
812+ renderQuery q1 <> " UNION " <> renderQuery q2
813+q1 `unionAll` q2 = UnsafeQuery $
814+ renderQuery q1 <> " UNION ALL " <> renderQuery q2
815+q1 `intersect` q2 = UnsafeQuery $
816+ renderQuery q1 <> " INTERSECT " <> renderQuery q2
817+q1 `intersectAll` q2 = UnsafeQuery $
818+ renderQuery q1 <> " INTERSECT ALL " <> renderQuery q2
819+q1 `except` q2 = UnsafeQuery $
820+ renderQuery q1 <> " EXCEPT " <> renderQuery q2
821+q1 `exceptAll` q2 = UnsafeQuery $
822+ renderQuery q1 <> " EXCEPT ALL " <> renderQuery q2
824+{-----------------------------------------
826+-----------------------------------------}
830+ => NP (Aliased (Expression params tables grouping)) columns
831+ -> TableExpression params schema tables grouping
832+ -> Query schema params columns
833+select list tabs = UnsafeQuery $
834+ "SELECT " <> renderList list <> " " <> renderTableExpression tabs
837+ = ByteString.intercalate ", "
839+ . hmap (K . renderAliased renderExpression)
842+ :: (tables ~ '[table ::: columns])
843+ => TableExpression params schema tables 'Ungrouped
844+ -> Query schema params columns
845+selectStar tabs = UnsafeQuery $ "SELECT * " <> renderTableExpression tabs
848+ :: HasTable table tables columns
850+ -> TableExpression params schema tables 'Ungrouped
851+ -> Query schema params columns
852+selectDotStar table tables = UnsafeQuery $
853+ "SELECT " <> renderAlias table <> ".* " <> renderTableExpression tables
855+{-----------------------------------------
857+-----------------------------------------}
860+ :: (KnownSymbol table, SListI columns)
862+ -> NP (Aliased TypeExpression) columns
863+ -> Definition schema (Create table columns schema)
864+createTable table columns = UnsafeDefinition $ mconcat
868+ , ByteString.intercalate ", " . hcollapse $
869+ hmap (K . renderColumn) columns
873+ renderColumn :: Aliased TypeExpression x -> ByteString
874+ renderColumn (ty `As` column) =
875+ renderAlias column <> " " <> renderTypeExpression ty
877+{-----------------------------------------
879+-----------------------------------------}
882+ :: KnownSymbol table
884+ -> Definition schema (Drop table schema)
885+dropTable table = UnsafeDefinition $ "DROP TABLE " <> renderAlias table <> ";"
887+{-----------------------------------------
889+-----------------------------------------}
892+ :: HasTable table schema columns0
894+ -> AlterTable columns0 columns1
895+ -> Definition schema (Alter table schema columns1)
896+alterTable table alteration = UnsafeDefinition $
898+ <> renderAlias table
900+ <> renderAlterTable alteration
904+ :: (KnownSymbol table0, KnownSymbol table1)
907+ -> Definition schema (Rename table0 table1 schema)
908+alterTableRename table0 table1 = UnsafeDefinition $
910+ <> renderAlias table0
912+ <> renderAlias table1
916+ (columns0 :: [(Symbol, ColumnType)])
917+ (columns1 :: [(Symbol, ColumnType)]) =
918+ UnsafeAlterTable {renderAlterTable :: ByteString}
921+ :: KnownSymbol column
923+ -> TypeExpression ('Optional ty)
924+ -> AlterTable columns (Create column ('Optional ty) columns)
925+addColumnDefault column ty = UnsafeAlterTable $
927+ <> renderAlias column
929+ <> renderTypeExpression ty
932+ :: KnownSymbol column
934+ -> TypeExpression ('Required ('Null ty))
935+ -> AlterTable columns ((column ::: 'Required ('Null ty)) ': columns)
936+addColumnNull column ty = UnsafeAlterTable $
938+ <> renderAlias column
940+ <> renderTypeExpression ty
943+ :: KnownSymbol column
945+ -> AlterTable columns (Drop column columns)
946+dropColumn column = UnsafeAlterTable $
947+ "DROP COLUMN " <> renderAlias column
950+ :: (KnownSymbol column0, KnownSymbol column1)
953+ -> AlterTable columns (Rename column0 column1 columns)
954+renameColumn column0 column1 = UnsafeAlterTable $
955+ "RENAME COLUMN " <> renderAlias column0
956+ <> " TO " <> renderAlias column1
959+ :: (KnownSymbol column, HasColumn column columns ty0)
961+ -> AlterColumn ty0 ty1
962+ -> AlterTable columns (Alter column columns ty1)
963+alterColumn column alteration = UnsafeAlterTable $
964+ "ALTER COLUMN " <> renderAlias column
965+ <> " " <> renderAlterColumn alteration
967+data AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
968+ UnsafeAlterColumn {renderAlterColumn :: ByteString}
971+ :: Expression '[] '[] 'Ungrouped ('Required ty)
972+ -> AlterColumn ('Required ty) ('Optional ty)
973+setDefault expression = UnsafeAlterColumn $
974+ "SET DEFAULT " <> renderExpression expression
976+dropDefault :: AlterColumn ('Optional ty) ('Required ty)
977+dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
980+ :: AlterColumn (optionality ('Null ty)) (optionality ('NotNull ty))
981+setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
984+ :: AlterColumn (optionality ('NotNull ty)) (optionality ('Null ty))
985+dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
989+ => TypeExpression (optionality (nullity ty1))
990+ -> AlterColumn (optionality (nullity ty0)) (optionality (nullity ty1))
991+alterType ty = UnsafeAlterColumn $ "TYPE " <> renderTypeExpression ty
993+{-----------------------------------------
995+-----------------------------------------}
998+ :: (SListI columns, HasTable table schema columns)
1000+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1001+ -> Manipulation schema params '[]
1002+insertInto table expressions = UnsafeManipulation $ "INSERT INTO "
1003+ <> renderAlias table
1004+ <> " (" <> ByteString.intercalate ", " aliases
1006+ <> ByteString.intercalate ", " values
1009+ aliases = hcollapse $ hmap
1010+ (\ (_ `As` name) -> K (renderAlias name))
1012+ values = hcollapse $ hmap
1013+ (\ (expression `As` _) -> K (renderExpression expression))
1017+ :: (SListI columns, SListI results, HasTable table schema columns)
1019+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1020+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1021+ -> Manipulation schema params results
1022+insertIntoReturning table inserts results
1023+ = UnsafeManipulation . mconcat $
1025+ , renderAlias table
1026+ , " (" <> ByteString.intercalate ", " aliases
1028+ , ByteString.intercalate ", " values
1030+ , renderList results
1034+ aliases = hcollapse $ hmap
1035+ (\ (_ `As` name) -> K (renderAlias name))
1037+ values = hcollapse $ hmap
1038+ (\ (expression `As` _) -> K (renderExpression expression))
1041+ = ByteString.intercalate ", "
1043+ . hmap (K . renderAliased renderExpression)
1045+{-----------------------------------------
1047+-----------------------------------------}
1049+data UpdateExpression params table ty
1051+ | Set (Expression params table 'Ungrouped ty)
1053+renderUpdateExpression
1054+ :: Aliased (UpdateExpression params '[table ::: columns]) column
1055+ -> Maybe ByteString
1056+renderUpdateExpression = \case
1057+ Same `As` _ -> Nothing
1058+ Set expression `As` column -> Just $ mconcat
1059+ [ renderAlias column
1061+ , renderExpression expression
1065+ :: (HasTable table schema columns, SListI columns)
1067+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1068+ -> Condition params '[table ::: columns] 'Ungrouped
1069+ -> Manipulation schema params '[]
1070+update table columns wh = UnsafeManipulation $ mconcat
1072+ , renderAlias table
1074+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1075+ hmap (K . renderUpdateExpression) columns
1076+ , " WHERE ", renderExpression wh, ";"
1080+ :: (SListI columns, SListI results, HasTable table schema columns)
1082+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1083+ -> Condition params '[table ::: columns] 'Ungrouped
1084+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1085+ -> Manipulation schema params results
1086+updateReturning table updates wh results = UnsafeManipulation $ mconcat
1088+ , renderAlias table
1090+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1091+ hmap (K . renderUpdateExpression) updates
1092+ , " WHERE ", renderExpression wh
1093+ , " RETURNING ", renderList results
1098+ = ByteString.intercalate ", "
1100+ . hmap (K . renderAliased renderExpression)
1103+ :: (SListI columns, HasTable table schema columns)
1105+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1106+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1107+ -> Condition params '[table ::: columns] 'Ungrouped
1108+ -> Manipulation schema params '[]
1109+upsertInto table inserts updates wh = UnsafeManipulation . mconcat $
1111+ , renderAlias table
1112+ , " (" <> ByteString.intercalate ", " aliases
1114+ , ByteString.intercalate ", " values
1115+ , ") ON CONFLICT UPDATE "
1116+ , renderAlias table
1118+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1119+ hmap (K . renderUpdateExpression) updates
1120+ , " WHERE ", renderExpression wh, ";"
1122+ aliases = hcollapse $ hmap
1123+ (\ (_ `As` name) -> K (renderAlias name))
1125+ values = hcollapse $ hmap
1126+ (\ (expression `As` _) -> K (renderExpression expression))
1130+ :: (SListI columns, SListI results, HasTable table schema columns)
1132+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1133+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1134+ -> Condition params '[table ::: columns] 'Ungrouped
1135+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1136+ -> Manipulation schema params results
1137+upsertIntoReturning table inserts updates wh results =
1138+ UnsafeManipulation . mconcat $
1140+ , renderAlias table
1141+ , " (" <> ByteString.intercalate ", " aliases
1143+ , ByteString.intercalate ", " values
1144+ , ") ON CONFLICT UPDATE "
1145+ , renderAlias table
1147+ , ByteString.intercalate ", " . catMaybes . hcollapse $
1148+ hmap (K . renderUpdateExpression) updates
1149+ , " WHERE ", renderExpression wh
1150+ , " RETURNING ", renderList results
1153+ aliases = hcollapse $ hmap
1154+ (\ (_ `As` name) -> K (renderAlias name))
1156+ values = hcollapse $ hmap
1157+ (\ (expression `As` _) -> K (renderExpression expression))
1160+ = ByteString.intercalate ", "
1162+ . hmap (K . renderAliased renderExpression)
1164+{-----------------------------------------
1166+-----------------------------------------}
1169+ :: HasTable table schema columns
1171+ -> Condition params '[table ::: columns] 'Ungrouped
1172+ -> Manipulation schema params '[]
1173+deleteFrom table wh = UnsafeManipulation $ mconcat
1175+ , renderAlias table
1176+ , " WHERE ", renderExpression wh, ";"