@@ -0,0 +1,1198 @@
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 (PGTyped (BaseType ty), KnownNat n)
53+ => HasParameter (n :: Nat) params ty | n params -> ty where
54+ param :: proxy n -> Expression params tables grouping ty
55+ param _ = UnsafeExpression $ parenthesized $
56+ "$" <> paramNum <+> "::" <+> renderTypeExpression (pgtype @(BaseType ty))
58+ paramNum = fromString $ show $ natVal (Proxy @n)
59+instance {-# OVERLAPPING #-} PGTyped (BaseType ty1)
60+ => HasParameter 1 (ty1:tys) ty1
61+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) params ty)
62+ => HasParameter n (ty' : params) ty
64+class KnownSymbol column => HasColumn column columns ty
65+ | column columns -> ty where
68+ -> Expression params '[table ::: columns] 'Ungrouped ty
69+ getColumn column = UnsafeExpression $ renderSymbol column
70+instance {-# OVERLAPPING #-} KnownSymbol column
71+ => HasColumn column ((column ::: optionality ty) ': tys) ('Required ty)
72+instance {-# OVERLAPPABLE #-} (KnownSymbol column, HasColumn column table ty)
73+ => HasColumn column (ty' ': table) ty
75+instance (HasColumn column columns ty, tables ~ '[table ::: columns])
76+ => IsLabel column (Expression params tables 'Ungrouped ty) where
77+ fromLabel = getColumn
79+instance (HasTable table tables columns, HasColumn column columns ty)
80+ => IsTableColumn table column (Expression params tables 'Ungrouped ty) where
81+ table &. column = UnsafeExpression $
82+ renderAlias table <> "." <> renderAlias column
84+def :: Expression params '[] grouping ('Optional (nullity ty))
85+def = UnsafeExpression "DEFAULT"
88+ :: Expression params '[] grouping ('Required (nullity ty))
89+ -> Expression params '[] grouping ('Optional (nullity ty))
90+unDef = UnsafeExpression . renderExpression
92+null :: Expression params tables grouping (optionality ('Null ty))
93+null = UnsafeExpression "NULL"
96+ :: Expression params tables grouping (optionality ('NotNull ty))
97+ -> Expression params tables grouping (optionality ('Null ty))
98+unNull = UnsafeExpression . renderExpression
101+ :: [Expression params tables grouping ('Required ('Null x))]
102+ -> Expression params tables grouping ('Required ('NotNull x))
103+ -> Expression params tables grouping ('Required ('NotNull x))
104+coalesce nulls isn'tNull = UnsafeExpression $
105+ "COALESCE" <> parenthesized (commaSeparated
106+ ((renderExpression <$> nulls) <> [renderExpression isn'tNull]))
110+ -> Expression params tables grouping ty0
111+ -> Expression params tables grouping ty1
112+ -> Expression params tables grouping ty2
113+unsafeBinaryOp op x y = UnsafeExpression $ parenthesized $
114+ renderExpression x <+> op <+> renderExpression y
118+ -> Expression params tables grouping ty0
119+ -> Expression params tables grouping ty1
120+unsafeUnaryOp op x = UnsafeExpression $ parenthesized $
121+ op <+> renderExpression x
125+ -> Expression params tables grouping xty
126+ -> Expression params tables grouping yty
127+unsafeFunction fun x = UnsafeExpression $
128+ fun <> parenthesized (renderExpression x)
131+ => Num (Expression params tables grouping ('Required (nullity ty))) where
132+ (+) = unsafeBinaryOp "+"
133+ (-) = unsafeBinaryOp "-"
134+ (*) = unsafeBinaryOp "*"
135+ abs = unsafeFunction "abs"
136+ signum = unsafeFunction "sign"
139+ . (<> if decimal (Proxy :: Proxy ty) then "." else "")
143+instance PGFloating ty => Fractional
144+ (Expression params tables grouping ('Required (nullity ty))) where
145+ (/) = unsafeBinaryOp "/"
146+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
148+instance (PGFloating ty, PGTyped ty) => Floating
149+ (Expression params tables grouping ('Required (nullity ty))) where
150+ pi = UnsafeExpression "pi()"
151+ exp = unsafeFunction "exp"
152+ log = unsafeFunction "ln"
153+ sqrt = unsafeFunction "sqrt"
154+ b ** x = UnsafeExpression $
155+ "power(" <> renderExpression b <> ", " <> renderExpression x <> ")"
156+ logBase b y = cast pgtype $ logBaseNumeric b y
159+ :: Expression params tables grouping ('Required (nullity ty))
160+ -> Expression params tables grouping ('Required (nullity ty))
161+ -> Expression params tables grouping ('Required (nullity 'PGnumeric))
162+ logBaseNumeric b' y' = UnsafeExpression $ mconcat
164+ , renderExpression (cast numeric b')
166+ , renderExpression (cast numeric y')
169+ sin = unsafeFunction "sin"
170+ cos = unsafeFunction "cos"
171+ tan = unsafeFunction "tan"
172+ asin = unsafeFunction "asin"
173+ acos = unsafeFunction "acos"
174+ atan = unsafeFunction "atan"
175+ sinh x = (exp x - exp (-x)) / 2
176+ cosh x = (exp x + exp (-x)) / 2
177+ tanh x = sinh x / cosh x
178+ asinh x = log (x + sqrt (x*x + 1))
179+ acosh x = log (x + sqrt (x*x - 1))
180+ atanh x = log ((1 + x) / (1 - x)) / 2
184+ => Expression params tables grouping ('Required (nullity float))
185+ -> Expression params tables grouping ('Required (nullity float))
186+ -> Expression params tables grouping ('Required (nullity float))
187+atan2 y x = UnsafeExpression $
188+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
191+ :: TypeExpression ('Required ('Null ty1))
192+ -> Expression params tables grouping ('Required (nullity ty0))
193+ -> Expression params tables grouping ('Required (nullity ty1))
194+cast ty x = UnsafeExpression $
195+ "(" <> renderExpression x <> " :: " <> renderTypeExpression ty <> ")"
199+ => Expression params tables grouping ('Required (nullity ty))
200+ -> Expression params tables grouping ('Required (nullity ty))
201+ -> Expression params tables grouping ('Required (nullity ty))
202+div = unsafeBinaryOp "/"
206+ => Expression params tables grouping ('Required (nullity ty))
207+ -> Expression params tables grouping ('Required (nullity ty))
208+ -> Expression params tables grouping ('Required (nullity ty))
209+mod = unsafeBinaryOp "%"
212+ :: (PGFloating frac, PGTyped int)
213+ => Expression params tables grouping ('Required (nullity frac))
214+ -> Expression params tables grouping ('Required (nullity int))
215+truncate = cast pgtype . truncate'
218+ :: Expression params tables grouping ('Required (nullity frac))
219+ -> Expression params tables grouping ('Required (nullity frac))
220+ truncate' = unsafeFunction "trunc"
223+ :: (PGFloating frac, PGTyped int)
224+ => Expression params tables grouping ('Required (nullity frac))
225+ -> Expression params tables grouping ('Required (nullity int))
226+round = cast pgtype . round'
229+ :: Expression params tables grouping ('Required (nullity frac))
230+ -> Expression params tables grouping ('Required (nullity frac))
231+ round' = unsafeFunction "round"
234+ :: (PGFloating frac, PGTyped int)
235+ => Expression params tables grouping ('Required (nullity frac))
236+ -> Expression params tables grouping ('Required (nullity int))
237+ceiling = cast pgtype . ceiling'
240+ :: Expression params tables grouping ('Required (nullity frac))
241+ -> Expression params tables grouping ('Required (nullity frac))
242+ ceiling' = unsafeFunction "ceiling"
245+ (Expression params tables grouping ('Required (nullity 'PGtext))) where
246+ fromString str = UnsafeExpression $
247+ "E\'" <> fromString (escape =<< str) <> "\'"
260+type Condition params tables grouping =
261+ Expression params tables grouping ('Required ('NotNull 'PGbool))
263+true :: Condition params tables grouping
264+true = UnsafeExpression "TRUE"
266+false :: Condition params tables grouping
267+false = UnsafeExpression "FALSE"
270+ :: Condition params tables grouping
271+ -> Condition params tables grouping
272+not_ = unsafeUnaryOp "NOT"
275+ :: Condition params tables grouping
276+ -> Condition params tables grouping
277+ -> Condition params tables grouping
278+(&&*) = unsafeBinaryOp "AND"
281+ :: Condition params tables grouping
282+ -> Condition params tables grouping
283+ -> Condition params tables grouping
284+(||*) = unsafeBinaryOp "OR"
287+ :: [(Condition params tables grouping, Expression params tables grouping ty)]
288+ -> Expression params tables grouping ty
289+ -> Expression params tables grouping ty
290+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
294+ [ " WHEN ", renderExpression when_
295+ , " THEN ", renderExpression then_
297+ | (when_,then_) <- whenThens
299+ , " ELSE ", renderExpression else_
304+ :: Condition params tables grouping
305+ -> Expression params tables grouping ty
306+ -> Expression params tables grouping ty
307+ -> Expression params tables grouping ty
308+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
311+ :: Expression params tables grouping ('Required (nullity ty))
312+ -> Expression params tables grouping ('Required (nullity ty))
313+ -> Expression params tables grouping ('Required (nullity 'PGbool))
314+(==*) = unsafeBinaryOp "="
317+ :: Expression params tables grouping ('Required (nullity ty))
318+ -> Expression params tables grouping ('Required (nullity ty))
319+ -> Expression params tables grouping ('Required (nullity 'PGbool))
320+(/=*) = unsafeBinaryOp "<>"
323+ :: Expression params tables grouping ('Required (nullity ty))
324+ -> Expression params tables grouping ('Required (nullity ty))
325+ -> Expression params tables grouping ('Required (nullity 'PGbool))
326+(>=*) = unsafeBinaryOp ">="
329+ :: Expression params tables grouping ('Required (nullity ty))
330+ -> Expression params tables grouping ('Required (nullity ty))
331+ -> Expression params tables grouping ('Required (nullity 'PGbool))
332+(<*) = unsafeBinaryOp "<"
335+ :: Expression params tables grouping ('Required (nullity ty))
336+ -> Expression params tables grouping ('Required (nullity ty))
337+ -> Expression params tables grouping ('Required (nullity 'PGbool))
338+(<=*) = unsafeBinaryOp "<="
341+ :: Expression params tables grouping ('Required (nullity ty))
342+ -> Expression params tables grouping ('Required (nullity ty))
343+ -> Expression params tables grouping ('Required (nullity 'PGbool))
344+(>*) = unsafeBinaryOp ">"
346+data SortExpression params tables grouping where
348+ :: Expression params tables grouping ty
349+ -> SortExpression params tables grouping
351+ :: Expression params tables grouping ty
352+ -> SortExpression params tables grouping
354+renderSortExpression :: SortExpression params tables grouping -> ByteString
355+renderSortExpression = \case
356+ Asc expression -> renderExpression expression <+> "ASC"
357+ Desc expression -> renderExpression expression <+> "DESC"
359+{-----------------------------------------
361+-----------------------------------------}
364+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
365+ (tabcol :: (Symbol,Symbol)) where
367+ :: (tables ~ '[table ::: columns], HasColumn column columns ty)
369+ -> By tables '(table, column)
371+ :: (HasTable table tables columns, HasColumn column columns ty)
372+ => (Alias table, Alias column)
373+ -> By tables '(table, column)
375+renderBy :: By tables tabcolty -> ByteString
377+ By column -> renderAlias column
378+ By2 (table, column) -> renderAlias table <> "." <> renderAlias column
380+data GroupByClause tables grouping where
381+ NoGroups :: GroupByClause tables 'Ungrouped
384+ => NP (By tables) bys
385+ -> GroupByClause tables ('Grouped bys)
387+renderGroupByClause :: GroupByClause tables grouping -> ByteString
388+renderGroupByClause = \case
391+ Group bys -> " GROUP BY" <+> renderCommaSeparated renderBy bys
393+data HavingClause params tables grouping where
394+ NoHaving :: HavingClause params tables 'Ungrouped
396+ :: [Condition params tables ('Grouped bys)]
397+ -> HavingClause params tables ('Grouped bys)
399+renderHavingClause :: HavingClause params tables grouping -> ByteString
400+renderHavingClause = \case
403+ Having conditions ->
404+ " HAVING" <+> commaSeparated (renderExpression <$> conditions)
406+class (KnownSymbol table, KnownSymbol column)
407+ => GroupedBy table column bys where
409+ :: (tables ~ '[table ::: columns], HasColumn column columns ty)
411+ -> Expression params tables ('Grouped bys) ty
412+ getGroup1 column = UnsafeExpression $ fromString (symbolVal' column)
414+ :: (HasTable table tables columns, HasColumn column columns ty)
417+ -> Expression params tables ('Grouped bys) ty
418+ getGroup2 table column = UnsafeExpression $
419+ renderAlias table <> "." <> renderAlias column
420+instance {-# OVERLAPPING #-} (KnownSymbol table, KnownSymbol column)
421+ => GroupedBy table column ('(table,column) ': bys)
422+instance {-# OVERLAPPABLE #-}
424+ , KnownSymbol column
425+ , GroupedBy table column bys
426+ ) => GroupedBy table column (tabcol ': bys)
429+ ( tables ~ '[table ::: columns]
430+ , HasColumn column columns ty
431+ , GroupedBy table column bys
433+ (Expression params tables ('Grouped bys) ty) where fromLabel = getGroup1
436+ ( HasTable table tables columns
437+ , HasColumn column columns ty
438+ , GroupedBy table column bys
439+ ) => IsTableColumn table column
440+ (Expression params tables ('Grouped bys) ty) where (&.) = getGroup2
444+ -> Expression params tables 'Ungrouped xty
445+ -> Expression params tables ('Grouped bys) yty
446+unsafeAggregate fun x = UnsafeExpression $ mconcat
447+ [fun, "(", renderExpression x, ")"]
451+ => Expression params tables 'Ungrouped ('Required (nullity ty))
452+ -> Expression params tables ('Grouped bys) ('Required (nullity ty))
453+sum_ = unsafeAggregate "sum"
455+{-----------------------------------------
457+-----------------------------------------}
460+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
461+ (columns :: [(Symbol,ColumnType)])
462+ = UnsafeTable { renderTable :: ByteString }
465+class KnownSymbol table => HasTable table tables columns
466+ | table tables -> columns where
467+ getTable :: Proxy# table -> Table tables columns
468+ getTable table = UnsafeTable $ fromString $ symbolVal' table
469+instance {-# OVERLAPPING #-} KnownSymbol table
470+ => HasTable table ((table ::: columns) ': tables) columns
471+instance {-# OVERLAPPABLE #-}
472+ (KnownSymbol table, HasTable table schema columns)
473+ => HasTable table (table' ': schema) columns
475+instance HasTable table schema columns
476+ => IsLabel table (Table schema columns) where
477+ fromLabel = getTable
479+data TableReference params schema tables where
481+ :: Aliased (Table schema) table
482+ -> TableReference params schema '[table]
484+ :: Aliased (Query schema params) table
485+ -> TableReference params schema '[table]
487+ :: TableReference params schema right
488+ -> TableReference params schema left
489+ -> TableReference params schema (Join left right)
491+ :: TableReference params schema right
492+ -> Condition params (Join left right) 'Ungrouped
493+ -> TableReference params schema left
494+ -> TableReference params schema (Join left right)
496+ :: TableReference params schema right
497+ -> Condition params (Join left right) 'Ungrouped
498+ -> TableReference params schema left
499+ -> TableReference params schema (Join left (NullifyTables right))
501+ :: TableReference params schema right
502+ -> Condition params (Join left right) 'Ungrouped
503+ -> TableReference params schema left
504+ -> TableReference params schema (Join (NullifyTables left) right)
506+ :: TableReference params schema right
507+ -> Condition params (Join left right) 'Ungrouped
508+ -> TableReference params schema left
509+ -> TableReference params schema
510+ (Join (NullifyTables left) (NullifyTables right))
512+renderTableReference :: TableReference params schema tables -> ByteString
513+renderTableReference = \case
514+ Table table -> renderAliased renderTable table
515+ Subquery selection -> renderAliased renderQuery selection
516+ CrossJoin right left ->
517+ renderTableReference left <+> "CROSS JOIN" <+> renderTableReference right
518+ InnerJoin right on left -> renderJoin "INNER JOIN" right on left
519+ LeftOuterJoin right on left -> renderJoin "LEFT OUTER JOIN" right on left
520+ RightOuterJoin right on left -> renderJoin "RIGHT OUTER JOIN" right on left
521+ FullOuterJoin right on left -> renderJoin "FULL OUTER JOIN" right on left
523+ renderJoin op right on left =
524+ renderTableReference left <+> op <+> renderTableReference right
525+ <+> "ON" <+> renderExpression on
528+ (params :: [ColumnType])
529+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
530+ (tables :: [(Symbol,[(Symbol,ColumnType)])])
531+ (grouping :: Grouping)
533+ { fromClause :: TableReference params schema tables
534+ , whereClause :: [Condition params tables 'Ungrouped]
535+ , groupByClause :: GroupByClause tables grouping
536+ , havingClause :: HavingClause params tables grouping
537+ , orderByClause :: [SortExpression params tables grouping]
538+ , limitClause :: [Word64]
539+ , offsetClause :: [Word64]
542+renderTableExpression
543+ :: TableExpression params schema tables grouping
545+renderTableExpression
546+ (TableExpression tables whs' grps' hvs' srts' lims' offs') = mconcat
547+ [ "FROM ", renderTableReference tables
549+ , renderGroupByClause grps'
550+ , renderHavingClause hvs'
552+ , renderLimits lims'
553+ , renderOffsets offs'
556+ renderWheres = \case
558+ wh:[] -> " WHERE" <+> renderExpression wh
559+ wh:whs -> " WHERE" <+> renderExpression (foldr (&&*) wh whs)
563+ <+> commaSeparated (renderSortExpression <$> srts)
564+ renderLimits = \case
566+ lims -> " LIMIT" <+> fromString (show (minimum lims))
567+ renderOffsets = \case
569+ offs -> " OFFSET" <+> fromString (show (sum offs))
572+ :: TableReference params schema tables
573+ -> TableExpression params schema tables 'Ungrouped
574+from tables = TableExpression tables [] NoGroups NoHaving [] [] []
577+ :: Condition params tables 'Ungrouped
578+ -> TableExpression params schema tables grouping
579+ -> TableExpression params schema tables grouping
580+where_ wh tables = tables {whereClause = wh : whereClause tables}
584+ => NP (By tables) bys
585+ -> TableExpression params schema tables 'Ungrouped
586+ -> TableExpression params schema tables ('Grouped bys)
587+group bys tables = TableExpression
588+ { fromClause = fromClause tables
589+ , whereClause = whereClause tables
590+ , groupByClause = Group bys
591+ , havingClause = Having []
592+ , orderByClause = []
593+ , limitClause = limitClause tables
594+ , offsetClause = offsetClause tables
598+ :: Condition params tables ('Grouped bys)
599+ -> TableExpression params schema tables ('Grouped bys)
600+ -> TableExpression params schema tables ('Grouped bys)
601+having hv tables = tables
602+ { havingClause = case havingClause tables of Having hvs -> Having (hv:hvs) }
605+ :: [SortExpression params tables grouping]
606+ -> TableExpression params schema tables grouping
607+ -> TableExpression params schema tables grouping
608+orderBy srts tables = tables {orderByClause = orderByClause tables ++ srts}
612+ -> TableExpression params schema tables grouping
613+ -> TableExpression params schema tables grouping
614+limit lim tables = tables {limitClause = lim : limitClause tables}
618+ -> TableExpression params schema tables grouping
619+ -> TableExpression params schema tables grouping
620+offset off tables = tables {offsetClause = off : offsetClause tables}
622+{-----------------------------------------
624+-----------------------------------------}
626+newtype TypeExpression (ty :: ColumnType)
627+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
630+bool :: TypeExpression ('Required ('Null 'PGbool))
631+bool = UnsafeTypeExpression "bool"
632+int2 :: TypeExpression ('Required ('Null 'PGint2))
633+int2 = UnsafeTypeExpression "int2"
634+smallint :: TypeExpression ('Required ('Null 'PGint2))
635+smallint = UnsafeTypeExpression "smallint"
636+int4 :: TypeExpression ('Required ('Null 'PGint4))
637+int4 = UnsafeTypeExpression "int4"
638+int :: TypeExpression ('Required ('Null 'PGint4))
639+int = UnsafeTypeExpression "int"
640+integer :: TypeExpression ('Required ('Null 'PGint4))
641+integer = UnsafeTypeExpression "integer"
642+int8 :: TypeExpression ('Required ('Null 'PGint8))
643+int8 = UnsafeTypeExpression "int8"
644+bigint :: TypeExpression ('Required ('Null 'PGint8))
645+bigint = UnsafeTypeExpression "bigint"
646+numeric :: TypeExpression ('Required ('Null 'PGnumeric))
647+numeric = UnsafeTypeExpression "numeric"
648+float4 :: TypeExpression ('Required ('Null 'PGfloat4))
649+float4 = UnsafeTypeExpression "float4"
650+real :: TypeExpression ('Required ('Null 'PGfloat4))
651+real = UnsafeTypeExpression "real"
652+float8 :: TypeExpression ('Required ('Null 'PGfloat8))
653+float8 = UnsafeTypeExpression "float8"
654+doublePrecision :: TypeExpression ('Required ('Null 'PGfloat8))
655+doublePrecision = UnsafeTypeExpression "double precision"
656+serial2 :: TypeExpression ('Optional ('NotNull 'PGint2))
657+serial2 = UnsafeTypeExpression "serial2"
658+smallserial :: TypeExpression ('Optional ('NotNull 'PGint2))
659+smallserial = UnsafeTypeExpression "smallserial"
660+serial4 :: TypeExpression ('Optional ('NotNull 'PGint4))
661+serial4 = UnsafeTypeExpression "serial4"
662+serial :: TypeExpression ('Optional ('NotNull 'PGint4))
663+serial = UnsafeTypeExpression "serial"
664+serial8 :: TypeExpression ('Optional ('NotNull 'PGint8))
665+serial8 = UnsafeTypeExpression "serial8"
666+bigserial :: TypeExpression ('Optional ('NotNull 'PGint8))
667+bigserial = UnsafeTypeExpression "bigserial"
668+text :: TypeExpression ('Required ('Null 'PGtext))
669+text = UnsafeTypeExpression "text"
673+ -> TypeExpression ('Required ('Null ('PGchar n)))
674+char (_ :: proxy n) = UnsafeTypeExpression $
675+ "char(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
679+ -> TypeExpression ('Required ('Null ('PGchar n)))
680+character (_ :: proxy n) = UnsafeTypeExpression $
681+ "character(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
685+ -> TypeExpression ('Required ('Null ('PGvarchar n)))
686+varchar (_ :: proxy n) = UnsafeTypeExpression $
687+ "varchar(" <> fromString (show (natVal' (proxy# :: Proxy# n))) <> ")"
691+ -> TypeExpression ('Required ('Null ('PGvarchar n)))
692+characterVarying p = UnsafeTypeExpression $
693+ "character varying(" <> renderNat p <> ")"
694+bytea :: TypeExpression ('Required ('Null ('PGbytea)))
695+bytea = UnsafeTypeExpression "bytea"
696+timestamp :: TypeExpression ('Required ('Null ('PGtimestamp)))
697+timestamp = UnsafeTypeExpression "timestamp"
698+timestampWithTimeZone :: TypeExpression ('Required ('Null ('PGtimestamptz)))
699+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
700+date :: TypeExpression ('Required ('Null ('PGdate)))
701+date = UnsafeTypeExpression "date"
702+time :: TypeExpression ('Required ('Null ('PGtime)))
703+time = UnsafeTypeExpression "time"
704+timeWithTimeZone :: TypeExpression ('Required ('Null ('PGtimetz)))
705+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
706+interval :: TypeExpression ('Required ('Null ('PGinterval)))
707+interval = UnsafeTypeExpression "interval"
708+uuid :: TypeExpression ('Required ('Null ('PGuuid)))
709+uuid = UnsafeTypeExpression "uuid"
710+json :: TypeExpression ('Required ('Null ('PGjson)))
711+json = UnsafeTypeExpression "json"
712+jsonb :: TypeExpression ('Required ('Null ('PGjsonb)))
713+jsonb = UnsafeTypeExpression "jsonb"
716+ :: TypeExpression (optionality ('Null ty))
717+ -> TypeExpression (optionality ('NotNull ty))
718+notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <+> "NOT NULL"
720+ :: Expression '[] '[] 'Ungrouped ('Required ty)
721+ -> TypeExpression ('Required ty)
722+ -> TypeExpression ('Optional ty)
723+default_ x ty = UnsafeTypeExpression $
724+ renderTypeExpression ty <+> "DEFAULT" <+> renderExpression x
726+class PGTyped (ty :: PGType) where
727+ pgtype :: TypeExpression ('Required ('Null ty))
728+instance PGTyped 'PGbool where pgtype = bool
729+instance PGTyped 'PGint2 where pgtype = int2
730+instance PGTyped 'PGint4 where pgtype = int4
731+instance PGTyped 'PGint8 where pgtype = int8
732+instance PGTyped 'PGnumeric where pgtype = numeric
733+instance PGTyped 'PGfloat4 where pgtype = float4
734+instance PGTyped 'PGfloat8 where pgtype = float8
735+instance PGTyped 'PGtext where pgtype = text
736+instance KnownNat n => PGTyped ('PGchar n) where pgtype = char (Proxy @n)
737+instance KnownNat n => PGTyped ('PGvarchar n) where pgtype = varchar (Proxy @n)
738+instance PGTyped 'PGbytea where pgtype = bytea
739+instance PGTyped 'PGtimestamp where pgtype = timestamp
740+instance PGTyped 'PGtimestamptz where pgtype = timestampWithTimeZone
741+instance PGTyped 'PGdate where pgtype = date
742+instance PGTyped 'PGtime where pgtype = time
743+instance PGTyped 'PGtimetz where pgtype = timeWithTimeZone
744+instance PGTyped 'PGinterval where pgtype = interval
745+instance PGTyped 'PGuuid where pgtype = uuid
746+instance PGTyped 'PGjson where pgtype = json
747+instance PGTyped 'PGjsonb where pgtype = jsonb
749+{-----------------------------------------
751+-----------------------------------------}
754+ (schema0 :: [(Symbol,[(Symbol,ColumnType)])])
755+ (schema1 :: [(Symbol,[(Symbol,ColumnType)])])
756+ = UnsafeDefinition { renderDefinition :: ByteString }
758+instance Category Definition where
759+ id = UnsafeDefinition ";"
760+ ddl1 . ddl0 = UnsafeDefinition $
761+ renderDefinition ddl0 <+> renderDefinition ddl1
764+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
765+ (params :: [ColumnType])
766+ (columns :: [(Symbol,ColumnType)])
767+ = UnsafeManipulation { renderManipulation :: ByteString }
770+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
771+ (params :: [ColumnType])
772+ (columns :: [(Symbol,ColumnType)])
773+ = UnsafeQuery { renderQuery :: ByteString }
776+ :: Query schema params columns
777+ -> Manipulation schema params columns
778+query q = UnsafeManipulation $ renderQuery q <> ";"
780+union, unionAll, intersect, intersectAll, except, exceptAll
781+ :: Query schema params columns
782+ -> Query schema params columns
783+ -> Query schema params columns
784+q1 `union` q2 = UnsafeQuery $
785+ parenthesized (renderQuery q1)
787+ <+> parenthesized (renderQuery q2)
788+q1 `unionAll` q2 = UnsafeQuery $
789+ parenthesized (renderQuery q1)
790+ <+> "UNION" <+> "ALL"
791+ <+> parenthesized (renderQuery q2)
792+q1 `intersect` q2 = UnsafeQuery $
793+ parenthesized (renderQuery q1)
795+ <+> parenthesized (renderQuery q2)
796+q1 `intersectAll` q2 = UnsafeQuery $
797+ parenthesized (renderQuery q1)
798+ <+> "INTERSECT" <+> "ALL"
799+ <+> parenthesized (renderQuery q2)
800+q1 `except` q2 = UnsafeQuery $
801+ parenthesized (renderQuery q1)
803+ <+> parenthesized (renderQuery q2)
804+q1 `exceptAll` q2 = UnsafeQuery $
805+ parenthesized (renderQuery q1)
806+ <+> "EXCEPT" <+> "ALL"
807+ <+> parenthesized (renderQuery q2)
809+{-----------------------------------------
811+-----------------------------------------}
815+ => NP (Aliased (Expression params tables grouping)) columns
816+ -> TableExpression params schema tables grouping
817+ -> Query schema params columns
818+select list tabs = UnsafeQuery $
820+ <+> renderCommaSeparated (renderAliased renderExpression) list
821+ <+> renderTableExpression tabs
824+ :: (tables ~ '[table ::: columns])
825+ => TableExpression params schema tables 'Ungrouped
826+ -> Query schema params columns
827+selectStar tabs = UnsafeQuery $ "SELECT" <+> "*" <+> renderTableExpression tabs
830+ :: HasTable table tables columns
832+ -> TableExpression params schema tables 'Ungrouped
833+ -> Query schema params columns
834+selectDotStar table tables = UnsafeQuery $
835+ "SELECT" <+> renderAlias table <> ".*" <+> renderTableExpression tables
839+ => NP (Aliased (Expression params '[] 'Ungrouped)) columns
840+ -> Query schema params columns
841+selectRow row = UnsafeQuery $
842+ "SELECT" <+> renderCommaSeparated (renderAliased renderExpression) row
844+{-----------------------------------------
846+-----------------------------------------}
849+ :: (KnownSymbol table, SListI columns)
851+ -> NP (Aliased TypeExpression) columns
852+ -> [TableConstraint schema columns]
853+ -> Definition schema (Create table columns schema)
854+createTable table columns constraints = UnsafeDefinition $
855+ "CREATE TABLE" <+> renderAlias table
857+ ( renderCommaSeparated renderColumnDef columns
858+ <> renderConstraints constraints )
861+ renderColumnDef :: Aliased TypeExpression x -> ByteString
862+ renderColumnDef (ty `As` column) =
863+ renderAlias column <+> renderTypeExpression ty
864+ renderConstraints :: [TableConstraint schema columns] -> ByteString
865+ renderConstraints = \case
867+ _ -> ", " <> commaSeparated (renderTableConstraint <$> constraints)
870+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
871+ (columns :: [(Symbol,ColumnType)])
872+ = UnsafeTableConstraint { renderTableConstraint :: ByteString }
875+ :: Condition '[] '[table ::: columns] 'Ungrouped
876+ -> TableConstraint schema columns
877+check condition = UnsafeTableConstraint $
878+ "CHECK" <+> parenthesized (renderExpression condition)
881+ (columns :: [(Symbol,ColumnType)])
882+ (columnty :: (Symbol,ColumnType))
885+ :: HasColumn column columns ty
887+ -> Column columns (column ::: ty)
889+renderColumn :: Column columns columnty -> ByteString
890+renderColumn (Column column) = renderAlias column
893+ :: SListI subcolumns
894+ => NP (Column columns) subcolumns
895+ -> TableConstraint schema columns
896+unique columns = UnsafeTableConstraint $
897+ "UNIQUE" <+> parenthesized (renderCommaSeparated renderColumn columns)
900+ :: (SListI subcolumns, NotNullTypes subcolumns)
901+ => NP (Column columns) subcolumns
902+ -> TableConstraint schema columns
903+primaryKey columns = UnsafeTableConstraint $
904+ "PRIMARY KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
906+type family SameTypes columns0 columns1 :: Constraint where
907+ SameTypes '[] '[] = ()
908+ SameTypes ((column0 ::: ty0) ': columns0) ((column1 ::: ty1) ': columns1)
909+ = (ty0 ~ ty1, SameTypes columns0 columns1)
911+type family NotNullTypes columns :: Constraint where
912+ NotNullTypes '[] = ()
913+ NotNullTypes ((column ::: (optionality ('NotNull ty))) ': columns)
914+ = NotNullTypes columns
917+ :: ( HasTable reftable schema refcolumns
918+ , SameTypes subcolumns refsubcolumns
919+ , NotNullTypes subcolumns
921+ , SListI refsubcolumns)
922+ => NP (Column columns) subcolumns
924+ -> NP (Column refcolumns) refsubcolumns
925+ -> TableConstraint schema columns
926+foreignKey columns reftable refcolumns = UnsafeTableConstraint $
927+ "FOREIGN KEY" <+> parenthesized (renderCommaSeparated renderColumn columns)
928+ <+> "REFERENCES" <+> renderAlias reftable
929+ <+> parenthesized (renderCommaSeparated renderColumn refcolumns)
931+{-----------------------------------------
933+-----------------------------------------}
936+ :: KnownSymbol table
938+ -> Definition schema (Drop table schema)
939+dropTable table = UnsafeDefinition $ "DROP TABLE" <+> renderAlias table <> ";"
941+{-----------------------------------------
943+-----------------------------------------}
946+ :: HasTable table schema columns0
948+ -> AlterTable columns0 columns1
949+ -> Definition schema (Alter table schema columns1)
950+alterTable table alteration = UnsafeDefinition $
952+ <+> renderAlias table
953+ <+> renderAlterTable alteration
957+ :: (KnownSymbol table0, KnownSymbol table1)
960+ -> Definition schema (Rename table0 table1 schema)
961+alterTableRename table0 table1 = UnsafeDefinition $
962+ "ALTER TABLE" <+> renderAlias table0
963+ <+> "RENAME TO" <+> renderAlias table1 <> ";"
966+ (columns0 :: [(Symbol, ColumnType)])
967+ (columns1 :: [(Symbol, ColumnType)]) =
968+ UnsafeAlterTable {renderAlterTable :: ByteString}
971+ :: KnownSymbol column
973+ -> TypeExpression ('Optional ty)
974+ -> AlterTable columns (Create column ('Optional ty) columns)
975+addColumnDefault column ty = UnsafeAlterTable $
976+ "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
979+ :: KnownSymbol column
981+ -> TypeExpression ('Required ('Null ty))
982+ -> AlterTable columns ((column ::: 'Required ('Null ty)) ': columns)
983+addColumnNull column ty = UnsafeAlterTable $
984+ "ADD COLUMN" <+> renderAlias column <+> renderTypeExpression ty
987+ :: KnownSymbol column
989+ -> AlterTable columns (Drop column columns)
990+dropColumn column = UnsafeAlterTable $
991+ "DROP COLUMN" <+> renderAlias column
994+ :: (KnownSymbol column0, KnownSymbol column1)
997+ -> AlterTable columns (Rename column0 column1 columns)
998+renameColumn column0 column1 = UnsafeAlterTable $
999+ "RENAME COLUMN" <+> renderAlias column0 <+> "TO" <+> renderAlias column1
1002+ :: (KnownSymbol column, HasColumn column columns ty0)
1004+ -> AlterColumn ty0 ty1
1005+ -> AlterTable columns (Alter column columns ty1)
1006+alterColumn column alteration = UnsafeAlterTable $
1007+ "ALTER COLUMN" <+> renderAlias column <+> renderAlterColumn alteration
1009+data AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
1010+ UnsafeAlterColumn {renderAlterColumn :: ByteString}
1013+ :: Expression '[] '[] 'Ungrouped ('Required ty)
1014+ -> AlterColumn ('Required ty) ('Optional ty)
1015+setDefault expression = UnsafeAlterColumn $
1016+ "SET DEFAULT" <+> renderExpression expression
1018+dropDefault :: AlterColumn ('Optional ty) ('Required ty)
1019+dropDefault = UnsafeAlterColumn $ "DROP DEFAULT"
1022+ :: AlterColumn (optionality ('Null ty)) (optionality ('NotNull ty))
1023+setNotNull = UnsafeAlterColumn $ "SET NOT NULL"
1026+ :: AlterColumn (optionality ('NotNull ty)) (optionality ('Null ty))
1027+dropNotNull = UnsafeAlterColumn $ "DROP NOT NULL"
1030+ :: TypeExpression (optionality (nullity ty1))
1031+ -> AlterColumn (optionality (nullity ty0)) (optionality (nullity ty1))
1032+alterType ty = UnsafeAlterColumn $ "TYPE " <> renderTypeExpression ty
1034+{-----------------------------------------
1036+-----------------------------------------}
1039+ :: (SListI columns, SListI results, HasTable table schema columns)
1041+ -> ValuesClause schema params columns
1042+ -> ConflictClause params columns
1043+ -> ReturningClause params columns results
1044+ -> Manipulation schema params results
1045+insertInto table insert conflict returning = UnsafeManipulation $
1046+ "INSERT" <+> "INTO" <+> renderAlias table
1047+ <+> renderValuesClause insert
1048+ <> renderConflictClause conflict
1049+ <> renderReturningClause returning
1052+ (schema :: [(Symbol,[(Symbol,ColumnType)])])
1053+ (params :: [ColumnType])
1054+ (columns :: [(Symbol,ColumnType)])
1056+ (NP (Aliased (Expression params '[] 'Ungrouped)) columns)
1057+ [NP (Aliased (Expression params '[] 'Ungrouped)) columns]
1058+ | ValuesQuery (Query schema params columns)
1062+ => ValuesClause schema params columns
1064+renderValuesClause = \case
1066+ parenthesized (renderCommaSeparated renderAliasPart row)
1069+ (parenthesized . renderCommaSeparated renderValuePart <$> row:rows)
1071+ renderAliasPart, renderValuePart
1072+ :: Aliased (Expression params '[] 'Ungrouped) ty -> ByteString
1073+ renderAliasPart (_ `As` name) = renderAlias name
1074+ renderValuePart (value `As` _) = renderExpression value
1075+ ValuesQuery q -> renderQuery q
1077+data ReturningClause
1078+ (params :: [ColumnType])
1079+ (columns :: [(Symbol,ColumnType)])
1080+ (results :: [(Symbol,ColumnType)])
1082+ ReturningNil :: ReturningClause params columns '[]
1083+ ReturningStar :: ReturningClause params columns columns
1085+ :: NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
1086+ -> ReturningClause params columns results
1088+renderReturningClause
1090+ => ReturningClause params columns results
1092+renderReturningClause = \case
1093+ ReturningNil -> ";"
1094+ ReturningStar -> " RETURNING *;"
1095+ Returning results -> " RETURNING"
1096+ <+> renderCommaSeparated (renderAliased renderExpression) results <> ";"
1098+data ConflictClause params columns where
1099+ Conflict :: ConflictClause params columns
1100+ OnConflictDoNothing :: ConflictClause params columns
1102+ :: NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1103+ -> Condition params '[table ::: columns] 'Ungrouped
1104+ -> ConflictClause params columns
1106+renderConflictClause
1108+ => ConflictClause params columns
1110+renderConflictClause = \case
1112+ OnConflictDoNothing -> " ON CONFLICT DO NOTHING"
1113+ OnConflictDoUpdate updates wh
1114+ -> " ON CONFLICT DO UPDATE SET"
1115+ <+> renderCommaSeparatedMaybe renderUpdateExpression updates
1116+ <+> "WHERE" <+> renderExpression wh
1118+{-----------------------------------------
1120+-----------------------------------------}
1122+data UpdateExpression params table ty
1124+ | Set (Expression params table 'Ungrouped ty)
1126+renderUpdateExpression
1127+ :: Aliased (UpdateExpression params '[table ::: columns]) column
1128+ -> Maybe ByteString
1129+renderUpdateExpression = \case
1130+ Same `As` _ -> Nothing
1131+ Set expression `As` column -> Just $
1132+ renderAlias column <+> "=" <+> renderExpression expression
1135+ :: (HasTable table schema columns, SListI columns, SListI results)
1137+ -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
1138+ -> Condition params '[table ::: columns] 'Ungrouped
1139+ -> ReturningClause params columns results
1140+ -> Manipulation schema params results
1141+update table columns wh returning = UnsafeManipulation $
1143+ <+> renderAlias table
1145+ <+> renderCommaSeparatedMaybe renderUpdateExpression columns
1146+ <+> "WHERE" <+> renderExpression wh
1147+ <> renderReturningClause returning
1149+{-----------------------------------------
1151+-----------------------------------------}
1154+ :: HasTable table schema columns
1156+ -> Condition params '[table ::: columns] 'Ungrouped
1157+ -> Manipulation schema params '[]
1158+deleteFrom table wh = UnsafeManipulation $
1159+ "DELETE FROM" <+> renderAlias table
1160+ <+> "WHERE" <+> renderExpression wh <> ";"
1162+{-----------------------------------------
1163+render helper functions
1164+-----------------------------------------}
1166+parenthesized :: ByteString -> ByteString
1167+parenthesized str = "(" <> str <> ")"
1169+(<+>) :: ByteString -> ByteString -> ByteString
1170+str1 <+> str2 = str1 <> " " <> str2
1172+commaSeparated :: [ByteString] -> ByteString
1173+commaSeparated = ByteString.intercalate ", "
1175+renderCommaSeparated
1177+ => (forall x. expression x -> ByteString)
1178+ -> NP expression xs -> ByteString
1179+renderCommaSeparated render
1182+ . hmap (K . render)
1184+renderCommaSeparatedMaybe
1186+ => (forall x. expression x -> Maybe ByteString)
1187+ -> NP expression xs -> ByteString
1188+renderCommaSeparatedMaybe render
1192+ . hmap (K . render)
1194+renderSymbol :: KnownSymbol str => Proxy# str -> ByteString
1195+renderSymbol p = fromString $ symbolVal' p
1197+renderNat :: KnownNat n => proxy n -> ByteString
1198+renderNat (_ :: proxy n) = fromString (show (natVal' (proxy# :: Proxy# n)))