@@ -1,13 +1,14 @@
11{-|
2−Module: Squeal.PostgreSQL.Expression
2+Module: Squeal.PostgreSQL.Query
33Description: Squeal expressions
4−Copyright: (c) Eitan Chatav, 2019
4+Copyright: (c) Eitan Chatav, 2017
55Maintainer: eitan@morphism.tech
66Stability: experimental
77
88Squeal expressions are the atoms used to build statements.
99-}
1010
11+{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
1112{-# LANGUAGE
1213 AllowAmbiguousTypes
1314 , ConstraintKinds
@@ -15,13 +16,11 @@ Squeal expressions are the atoms used to build statements.
1516 , FlexibleContexts
1617 , FlexibleInstances
1718 , FunctionalDependencies
18− , GADTs
1919 , GeneralizedNewtypeDeriving
2020 , LambdaCase
2121 , MagicHash
2222 , OverloadedStrings
2323 , ScopedTypeVariables
24− , StandaloneDeriving
2524 , TypeApplications
2625 , TypeFamilies
2726 , TypeInType
@@ -32,351 +31,486 @@ Squeal expressions are the atoms used to build statements.
3231
3332module Squeal.PostgreSQL.Expression
3433 ( -- * Expression
35− Expression (..)
36− , Expr
37− , type (-->)
38− , FunctionDB
39− , unsafeFunction
40− , function
41− , unsafeLeftOp
42− , leftOp
43− , unsafeRightOp
44− , rightOp
45− , Operator
46− , OperatorDB
34+ Expression (UnsafeExpression, renderExpression)
35+ , HasParameter (parameter)
36+ , param
37+ -- ** Null
38+ , null_
39+ , notNull
40+ , coalesce
41+ , fromNull
42+ , isNull
43+ , isNotNull
44+ , matchNull
45+ , nullIf
46+ -- ** Collections
47+ , array
48+ , row
49+ -- ** Functions
4750 , unsafeBinaryOp
48− , binaryOp
49− , FunctionVar
50− , unsafeFunctionVar
51− , type (--->)
52− , FunctionNDB
53− , unsafeFunctionN
54− , functionN
55− , PGSubset (..)
56− , PGIntersect (..)
51+ , unsafeUnaryOp
52+ , unsafeFunction
53+ , atan2_
54+ , cast
55+ , quot_
56+ , rem_
57+ , trunc
58+ , round_
59+ , ceiling_
60+ , greatest
61+ , least
62+ -- ** Conditions
63+ , Condition
64+ , true
65+ , false
66+ , not_
67+ , (.&&)
68+ , (.||)
69+ , caseWhenThenElse
70+ , ifThenElse
71+ , (.==)
72+ , (./=)
73+ , (.>=)
74+ , (.<)
75+ , (.<=)
76+ , (.>)
77+ -- ** Time
78+ , currentDate
79+ , currentTime
80+ , currentTimestamp
81+ , localTime
82+ , localTimestamp
83+ -- ** Text
84+ , lower
85+ , upper
86+ , charLength
87+ , like
88+ -- ** json or jsonb operators
89+ , PGarray
90+ , PGarrayOf
91+ , (.->)
92+ , (.->>)
93+ , (.#>)
94+ , (.#>>)
95+ -- *** jsonb only operators
96+ , (.@>)
97+ , (.<@)
98+ , (.?)
99+ , (.?|)
100+ , (.?&)
101+ , (.||.)
102+ , (.-.)
103+ , (#-.)
104+ -- *** Functions
105+ , toJson
106+ , toJsonb
107+ , arrayToJson
108+ , rowToJson
109+ -- ** Aggregation
110+ , unsafeAggregate, unsafeAggregateDistinct
111+ , sum_, sumDistinct
112+ , PGAvg (avg, avgDistinct)
113+ , bitAnd, bitOr, boolAnd, boolOr
114+ , bitAndDistinct, bitOrDistinct, boolAndDistinct, boolOrDistinct
115+ , countStar
116+ , count, countDistinct
117+ , every, everyDistinct
118+ , max_, maxDistinct, min_, minDistinct
119+ -- * Types
120+ , TypeExpression (UnsafeTypeExpression, renderTypeExpression)
121+ , PGTyped (pgtype)
122+ , bool
123+ , int2
124+ , smallint
125+ , int4
126+ , int
127+ , integer
128+ , int8
129+ , bigint
130+ , numeric
131+ , float4
132+ , real
133+ , float8
134+ , doublePrecision
135+ , text
136+ , char
137+ , character
138+ , varchar
139+ , characterVarying
140+ , bytea
141+ , timestamp
142+ , timestampWithTimeZone
143+ , date
144+ , time
145+ , timeWithTimeZone
146+ , interval
147+ , uuid
148+ , inet
149+ , json
150+ , jsonb
151+ , vararray
152+ , fixarray
57153 -- * Re-export
58154 , (&)
59− , K (..)
60− , unK
155+ , NP ((:*), Nil)
61156 ) where
62157
63158import Control.Category
64159import Control.DeepSeq
65160import Data.ByteString (ByteString)
66161import Data.Function ((&))
67−import Data.Semigroup hiding (All)
162+import Data.Semigroup
163+import Data.Ratio
68164import Data.String
69−import Generics.SOP hiding (All, from)
165+import Generics.SOP hiding (from)
166+import GHC.Exts (Constraint)
70167import GHC.OverloadedLabels
71168import GHC.TypeLits
72−import Numeric
73169import Prelude hiding (id, (.))
74170
75171import qualified GHC.Generics as GHC
76172
77−import Squeal.PostgreSQL.Alias
78−import Squeal.PostgreSQL.List
79173import Squeal.PostgreSQL.Render
80174import Squeal.PostgreSQL.Schema
81175
82−-- $setup
83−-- >>> import Squeal.PostgreSQL
84−
85176{-----------------------------------------
86177column expressions
87178-----------------------------------------}
88179
89180{- | `Expression`s are used in a variety of contexts,
90−such as in the target `Squeal.PostgreSQL.Query.List` of the
91−`Squeal.PostgreSQL.Query.select` command,
181+such as in the target list of the `Squeal.PostgreSQL.Query.select` command,
92182as new column values in `Squeal.PostgreSQL.Manipulation.insertRow` or
93183`Squeal.PostgreSQL.Manipulation.update`,
94−or in search `Squeal.PostgreSQL.Logic.Condition`s in a number of commands.
184+or in search `Condition`s in a number of commands.
95185
96186The expression syntax allows the calculation of
97187values from primitive expression using arithmetic, logical,
98188and other operations.
99−
100−The type parameters of `Expression` are
101−
102−* @outer :: @ `FromType`, the @from@ clauses of any outer queries in which
103− the `Expression` is a correlated subquery expression;
104−* @commons :: @ `FromType`, the `Squeal.PostgreSQL.Query.CommonTableExpression`s
105− that are in scope for the `Expression`;
106−* @grp :: @ `Grouping`, the `Grouping` of the @from@ clause which may limit
107− which columns may be referenced by alias;
108−* @schemas :: @ `SchemasType`, the schemas of your database that are in
109− scope for the `Expression`;
110−* @from :: @ `FromType`, the @from@ clause which the `Expression` may use
111− to reference columns by alias;
112−* @ty :: @ `NullityType`, the type of the `Expression`.
113189-}
114190newtype Expression
115− (outer :: FromType)
116− (commons :: FromType)
117− (grp :: Grouping)
118− (schemas :: SchemasType)
191+ (schema :: SchemaType)
192+ (relations :: RelationsType)
193+ (grouping :: Grouping)
119194 (params :: [NullityType])
120− (from :: FromType)
121195 (ty :: NullityType)
122196 = UnsafeExpression { renderExpression :: ByteString }
123197 deriving (GHC.Generic,Show,Eq,Ord,NFData)
124−instance RenderSQL (Expression outer commons grp schemas params from ty) where
198+
199+instance RenderSQL (Expression schema relations grouping params ty) where
125200 renderSQL = renderExpression
126201
127−-- | An `Expr` is a closed `Expression`.
128−-- It is a F@RankNType@ but don't be scared.
129−-- Think of it as an expression which sees no
130−-- namespaces, so you can't use parameters
131−-- or alias references. It can be used as
132−-- a simple piece of more complex `Expression`s.
133−type Expr x
134− = forall outer commons grp schemas params from
135− . Expression outer commons grp schemas params from x
136− -- ^ cannot reference aliases
137−
138−-- | A @RankNType@ for binary operators.
139−type Operator x1 x2 y
140− = forall outer commons grp schemas params from
141− . Expression outer commons grp schemas params from x1
142− -- ^ left input
143− -> Expression outer commons grp schemas params from x2
144− -- ^ right input
145− -> Expression outer commons grp schemas params from y
146− -- ^ output
147−
148−type OperatorDB schemas x1 x2 y
149− = forall outer commons grp params from
150− . Expression outer commons grp schemas params from x1
151− -- ^ left input
152− -> Expression outer commons grp schemas params from x2
153− -- ^ right input
154− -> Expression outer commons grp schemas params from y
155− -- ^ output
156−
157−-- | A @RankNType@ for functions with a single argument.
158−-- These could be either function calls or unary operators.
159−-- This is a subtype of the usual Haskell function type `Prelude.->`,
160−-- indeed a subcategory as it is closed under the usual
161−-- `Prelude..` and `Prelude.id`.
162−type (-->) x y
163− = forall outer commons grp schemas params from
164− . Expression outer commons grp schemas params from x
165− -- ^ input
166− -> Expression outer commons grp schemas params from y
167− -- ^ output
168−
169−type FunctionDB schemas x y
170− = forall outer commons grp params from
171− . Expression outer commons grp schemas params from x
172− -- ^ input
173− -> Expression outer commons grp schemas params from y
174− -- ^ output
175−
176−{- | A @RankNType@ for functions with a fixed-length list of heterogeneous arguments.
177−Use the `*:` operator to end your argument lists, like so.
178−
179−>>> printSQL (unsafeFunctionN "fun" (true :* false :* localTime *: true))
180−fun(TRUE, FALSE, LOCALTIME, TRUE)
202+{- | A `HasParameter` constraint is used to indicate a value that is
203+supplied externally to a SQL statement.
204+`Squeal.PostgreSQL.PQ.manipulateParams`,
205+`Squeal.PostgreSQL.PQ.queryParams` and
206+`Squeal.PostgreSQL.PQ.traversePrepared` support specifying data values
207+separately from the SQL command string, in which case `param`s are used to
208+refer to the out-of-line data values.
181209-}
182−type (--->) xs y
183− = forall outer commons grp schemas params from
184− . NP (Expression outer commons grp schemas params from) xs
185− -- ^ inputs
186− -> Expression outer commons grp schemas params from y
187− -- ^ output
188−
189−type FunctionNDB schemas xs y
190− = forall outer commons grp params from
191− . NP (Expression outer commons grp schemas params from) xs
192− -- ^ inputs
193− -> Expression outer commons grp schemas params from y
194− -- ^ output
195−
196−{- | A @RankNType@ for functions with a variable-length list of
197−homogeneous arguments and at least 1 more argument.
198−-}
199−type FunctionVar x0 x1 y
200− = forall outer commons grp schemas params from
201− . [Expression outer commons grp schemas params from x0]
202− -- ^ inputs
203− -> Expression outer commons grp schemas params from x1
204− -- ^ must have at least 1 input
205− -> Expression outer commons grp schemas params from y
206− -- ^ output
207−
208−{- | >>> printSQL (unsafeFunctionVar "greatest" [true, null_] false)
209−greatest(TRUE, NULL, FALSE)
210−-}
211−unsafeFunctionVar :: ByteString -> FunctionVar x0 x1 y
212−unsafeFunctionVar fun xs x = UnsafeExpression $ fun <> parenthesized
213− (commaSeparated (renderSQL <$> xs) <> ", " <> renderSQL x)
214−
215−instance (HasUnique tab (Join outer from) row, Has col row ty)
216− => IsLabel col (Expression outer commons 'Ungrouped schemas params from ty) where
217− fromLabel = UnsafeExpression $ renderSQL (Alias @col)
218−instance (HasUnique tab (Join outer from) row, Has col row ty, tys ~ '[ty])
219− => IsLabel col (NP (Expression outer commons 'Ungrouped schemas params from) tys) where
220− fromLabel = fromLabel @col :* Nil
221−instance (HasUnique tab (Join outer from) row, Has col row ty, column ~ (col ::: ty))
222− => IsLabel col
223− (Aliased (Expression outer commons 'Ungrouped schemas params from) column) where
224− fromLabel = fromLabel @col `As` Alias
225−instance (HasUnique tab (Join outer from) row, Has col row ty, columns ~ '[col ::: ty])
226− => IsLabel col
227− (NP (Aliased (Expression outer commons 'Ungrouped schemas params from)) columns) where
228− fromLabel = fromLabel @col :* Nil
229−
230−instance (Has tab (Join outer from) row, Has col row ty)
231− => IsQualified tab col (Expression outer commons 'Ungrouped schemas params from ty) where
232− tab ! col = UnsafeExpression $
233− renderSQL tab <> "." <> renderSQL col
234−instance (Has tab (Join outer from) row, Has col row ty, tys ~ '[ty])
235− => IsQualified tab col (NP (Expression outer commons 'Ungrouped schemas params from) tys) where
236− tab ! col = tab ! col :* Nil
237−instance (Has tab (Join outer from) row, Has col row ty, column ~ (col ::: ty))
238− => IsQualified tab col
239− (Aliased (Expression outer commons 'Ungrouped schemas params from) column) where
240− tab ! col = tab ! col `As` col
241−instance (Has tab (Join outer from) row, Has col row ty, columns ~ '[col ::: ty])
242− => IsQualified tab col
243− (NP (Aliased (Expression outer commons 'Ungrouped schemas params from)) columns) where
244− tab ! col = tab ! col :* Nil
210+class KnownNat n => HasParameter
211+ (n :: Nat)
212+ (schema :: SchemaType)
213+ (params :: [NullityType])
214+ (ty :: NullityType)
215+ | n params -> ty where
216+ -- | `parameter` takes a `Nat` using type application and a `TypeExpression`.
217+ --
218+ -- >>> let expr = parameter @1 int4 :: Expression sch rels grp '[ 'Null 'PGint4] ('Null 'PGint4)
219+ -- >>> printSQL expr
220+ -- ($1 :: int4)
221+ parameter
222+ :: TypeExpression schema (PGTypeOf ty)
223+ -> Expression schema relations grouping params ty
224+ parameter ty = UnsafeExpression $ parenthesized $
225+ "$" <> renderNat (Proxy @n) <+> "::"
226+ <+> renderTypeExpression ty
227+instance {-# OVERLAPPING #-} HasParameter 1 schema (ty1:tys) ty1
228+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) schema params ty)
229+ => HasParameter n schema (ty' : params) ty
230+
231+-- | `param` takes a `Nat` using type application and for basic types,
232+-- infers a `TypeExpression`.
233+--
234+-- >>> let expr = param @1 :: Expression sch rels grp '[ 'Null 'PGint4] ('Null 'PGint4)
235+-- >>> printSQL expr
236+-- ($1 :: int4)
237+param
238+ :: forall n schema params relations grouping ty
239+ . (PGTyped schema (PGTypeOf ty), HasParameter n schema params ty)
240+ => Expression schema relations grouping params ty -- ^ param
241+param = parameter @n pgtype
242+
243+instance (HasUnique relation relations columns, Has column columns ty)
244+ => IsLabel column (Expression schema relations 'Ungrouped params ty) where
245+ fromLabel = UnsafeExpression $ renderAlias (Alias @column)
246+instance (HasUnique relation relations columns, Has column columns ty)
247+ => IsLabel column
248+ (Aliased (Expression schema relations 'Ungrouped params) (column ::: ty)) where
249+ fromLabel = fromLabel @column `As` Alias @column
250+instance (HasUnique relation relations columns, Has column columns ty)
251+ => IsLabel column
252+ (NP (Aliased (Expression schema relations 'Ungrouped params)) '[column ::: ty]) where
253+ fromLabel = fromLabel @column :* Nil
254+
255+instance (Has relation relations columns, Has column columns ty)
256+ => IsQualified relation column (Expression schema relations 'Ungrouped params ty) where
257+ relation ! column = UnsafeExpression $
258+ renderAlias relation <> "." <> renderAlias column
259+instance (Has relation relations columns, Has column columns ty)
260+ => IsQualified relation column
261+ (Aliased (Expression schema relations 'Ungrouped params) (column ::: ty)) where
262+ relation ! column = relation ! column `As` column
263+instance (Has relation relations columns, Has column columns ty)
264+ => IsQualified relation column
265+ (NP (Aliased (Expression schema relations 'Ungrouped params)) '[column ::: ty]) where
266+ relation ! column = relation ! column :* Nil
245267
246268instance
247− ( HasUnique tab (Join outer from) row
248− , Has col row ty
249− , GroupedBy tab col bys
250− ) => IsLabel col
251− (Expression outer commons ('Grouped bys) schemas params from ty) where
252− fromLabel = UnsafeExpression $ renderSQL (Alias @col)
253−instance
254− ( HasUnique tab (Join outer from) row
255− , Has col row ty
256− , GroupedBy tab col bys
257− , tys ~ '[ty]
258− ) => IsLabel col
259− (NP (Expression outer commons ('Grouped bys) schemas params from) tys) where
260− fromLabel = fromLabel @col :* Nil
269+ ( HasUnique relation relations columns
270+ , Has column columns ty
271+ , GroupedBy relation column bys
272+ ) => IsLabel column
273+ (Expression schema relations ('Grouped bys) params ty) where
274+ fromLabel = UnsafeExpression $ renderAlias (Alias @column)
261275instance
262− ( HasUnique tab (Join outer from) row
263− , Has col row ty
264− , GroupedBy tab col bys
265− , column ~ (col ::: ty)
266− ) => IsLabel col
267− (Aliased (Expression outer commons ('Grouped bys) schemas params from) column) where
268− fromLabel = fromLabel @col `As` Alias
276+ ( HasUnique relation relations columns
277+ , Has column columns ty
278+ , GroupedBy relation column bys
279+ ) => IsLabel column
280+ ( Aliased (Expression schema relations ('Grouped bys) params)
281+ (column ::: ty) ) where
282+ fromLabel = fromLabel @column `As` Alias @column
269283instance
270− ( HasUnique tab (Join outer from) row
271− , Has col row ty
272− , GroupedBy tab col bys
273− , columns ~ '[col ::: ty]
274− ) => IsLabel col
275− (NP (Aliased (Expression outer commons ('Grouped bys) schemas params from)) columns) where
276− fromLabel = fromLabel @col :* Nil
284+ ( HasUnique relation relations columns
285+ , Has column columns ty
286+ , GroupedBy relation column bys
287+ ) => IsLabel column
288+ ( NP (Aliased (Expression schema relations ('Grouped bys) params))
289+ '[column ::: ty] ) where
290+ fromLabel = fromLabel @column :* Nil
277291
278292instance
279− ( Has tab (Join outer from) row
280− , Has col row ty
281− , GroupedBy tab col bys
282− ) => IsQualified tab col
283− (Expression outer commons ('Grouped bys) schemas params from ty) where
284− tab ! col = UnsafeExpression $
285− renderSQL tab <> "." <> renderSQL col
293+ ( Has relation relations columns
294+ , Has column columns ty
295+ , GroupedBy relation column bys
296+ ) => IsQualified relation column
297+ (Expression schema relations ('Grouped bys) params ty) where
298+ relation ! column = UnsafeExpression $
299+ renderAlias relation <> "." <> renderAlias column
286300instance
287− ( Has tab (Join outer from) row
288− , Has col row ty
289− , GroupedBy tab col bys
290− , tys ~ '[ty]
291− ) => IsQualified tab col
292− (NP (Expression outer commons ('Grouped bys) schemas params from) tys) where
293− tab ! col = tab ! col :* Nil
301+ ( Has relation relations columns
302+ , Has column columns ty
303+ , GroupedBy relation column bys
304+ ) => IsQualified relation column
305+ (Aliased (Expression schema relations ('Grouped bys) params)
306+ (column ::: ty)) where
307+ relation ! column = relation ! column `As` column
294308instance
295− ( Has tab (Join outer from) row
296− , Has col row ty
297− , GroupedBy tab col bys
298− , column ~ (col ::: ty)
299− ) => IsQualified tab col
300− (Aliased (Expression outer commons ('Grouped bys) schemas params from) column) where
301− tab ! col = tab ! col `As` col
302−instance
303− ( Has tab (Join outer from) row
304− , Has col row ty
305− , GroupedBy tab col bys
306− , columns ~ '[col ::: ty]
307− ) => IsQualified tab col
308− (NP (Aliased (Expression outer commons ('Grouped bys) schemas params from)) columns) where
309− tab ! col = tab ! col :* Nil
309+ ( Has relation relations columns
310+ , Has column columns ty
311+ , GroupedBy relation column bys
312+ ) => IsQualified relation column
313+ ( NP (Aliased (Expression schema relations ('Grouped bys) params))
314+ '[column ::: ty]) where
315+ relation ! column = relation ! column :* Nil
316+
317+-- | analagous to `Nothing`
318+--
319+-- >>> printSQL null_
320+-- NULL
321+null_ :: Expression schema rels grouping params ('Null ty)
322+null_ = UnsafeExpression "NULL"
323+
324+-- | analagous to `Just`
325+--
326+-- >>> printSQL $ notNull true
327+-- TRUE
328+notNull
329+ :: Expression schema rels grouping params ('NotNull ty)
330+ -> Expression schema rels grouping params ('Null ty)
331+notNull = UnsafeExpression . renderExpression
332+
333+-- | return the leftmost value which is not NULL
334+--
335+-- >>> printSQL $ coalesce [null_, notNull true] false
336+-- COALESCE(NULL, TRUE, FALSE)
337+coalesce
338+ :: [Expression schema relations grouping params ('Null ty)]
339+ -- ^ @NULL@s may be present
340+ -> Expression schema relations grouping params ('NotNull ty)
341+ -- ^ @NULL@ is absent
342+ -> Expression schema relations grouping params ('NotNull ty)
343+coalesce nullxs notNullx = UnsafeExpression $
344+ "COALESCE" <> parenthesized (commaSeparated
345+ ((renderExpression <$> nullxs) <> [renderExpression notNullx]))
346+
347+-- | analagous to `Data.Maybe.fromMaybe` using @COALESCE@
348+--
349+-- >>> printSQL $ fromNull true null_
350+-- COALESCE(NULL, TRUE)
351+fromNull
352+ :: Expression schema relations grouping params ('NotNull ty)
353+ -- ^ what to convert @NULL@ to
354+ -> Expression schema relations grouping params ('Null ty)
355+ -> Expression schema relations grouping params ('NotNull ty)
356+fromNull notNullx nullx = coalesce [nullx] notNullx
357+
358+-- | >>> printSQL $ null_ & isNull
359+-- NULL IS NULL
360+isNull
361+ :: Expression schema relations grouping params ('Null ty)
362+ -- ^ possibly @NULL@
363+ -> Condition schema relations grouping params
364+isNull x = UnsafeExpression $ renderExpression x <+> "IS NULL"
365+
366+-- | >>> printSQL $ null_ & isNotNull
367+-- NULL IS NOT NULL
368+isNotNull
369+ :: Expression schema relations grouping params ('Null ty)
370+ -- ^ possibly @NULL@
371+ -> Condition schema relations grouping params
372+isNotNull x = UnsafeExpression $ renderExpression x <+> "IS NOT NULL"
373+
374+-- | analagous to `maybe` using @IS NULL@
375+--
376+-- >>> printSQL $ matchNull true not_ null_
377+-- CASE WHEN NULL IS NULL THEN TRUE ELSE (NOT NULL) END
378+matchNull
379+ :: Expression schema relations grouping params (nullty)
380+ -- ^ what to convert @NULL@ to
381+ -> ( Expression schema relations grouping params ('NotNull ty)
382+ -> Expression schema relations grouping params (nullty) )
383+ -- ^ function to perform when @NULL@ is absent
384+ -> Expression schema relations grouping params ('Null ty)
385+ -> Expression schema relations grouping params (nullty)
386+matchNull y f x = ifThenElse (isNull x) y
387+ (f (UnsafeExpression (renderExpression x)))
388+
389+{-| right inverse to `fromNull`, if its arguments are equal then
390+`nullIf` gives @NULL@.
391+
392+>>> :set -XTypeApplications -XDataKinds
393+>>> let expr = nullIf false (param @1) :: Expression schema rels grp '[ 'NotNull 'PGbool] ('Null 'PGbool)
394+>>> printSQL expr
395+NULL IF (FALSE, ($1 :: bool))
396+-}
397+nullIf
398+ :: Expression schema relations grouping params ('NotNull ty)
399+ -- ^ @NULL@ is absent
400+ -> Expression schema relations grouping params ('NotNull ty)
401+ -- ^ @NULL@ is absent
402+ -> Expression schema relations grouping params ('Null ty)
403+nullIf x y = UnsafeExpression $ "NULL IF" <+> parenthesized
404+ (renderExpression x <> ", " <> renderExpression y)
405+
406+-- | >>> printSQL $ array [null_, notNull false, notNull true]
407+-- ARRAY[NULL, FALSE, TRUE]
408+array
409+ :: [Expression schema relations grouping params ('Null ty)]
410+ -- ^ array elements
411+ -> Expression schema relations grouping params (nullity ('PGvararray ty))
412+array xs = UnsafeExpression $
413+ "ARRAY[" <> commaSeparated (renderExpression <$> xs) <> "]"
310414
311415instance (KnownSymbol label, label `In` labels) => IsPGlabel label
312− (Expression outer commons grp schemas params from (null ('PGenum labels))) where
313− label = UnsafeExpression $ renderSQL (PGlabel @label)
416+ (Expression schema relations grouping params (nullity ('PGenum labels))) where
417+ label = UnsafeExpression $ renderLabel (PGlabel @label)
418+
419+-- | A row constructor is an expression that builds a row value
420+-- (also called a composite value) using values for its member fields.
421+--
422+-- >>> type Complex = PGcomposite '["real" ::: 'PGfloat8, "imaginary" ::: 'PGfloat8]
423+-- >>> let i = row (0 `As` #real :* 1 `As` #imaginary :* Nil) :: Expression '[] '[] 'Ungrouped '[] ('NotNull Complex)
424+-- >>> printSQL i
425+-- ROW(0, 1)
426+row
427+ :: SListI (Nulls fields)
428+ => NP (Aliased (Expression schema relations grouping params)) (Nulls fields)
429+ -- ^ zero or more expressions for the row field values
430+ -> Expression schema relations grouping params (nullity ('PGcomposite fields))
431+row exprs = UnsafeExpression $ "ROW" <> parenthesized
432+ (renderCommaSeparated (\ (expr `As` _) -> renderExpression expr) exprs)
433+
434+instance Has field fields ty => IsLabel field
435+ ( Expression schema relation grouping params (nullity ('PGcomposite fields))
436+ -> Expression schema relation grouping params ('Null ty) ) where
437+ fromLabel expr = UnsafeExpression $
438+ parenthesized (renderExpression expr) <> "." <>
439+ fromString (symbolVal (Proxy @field))
440+
441+instance Semigroup
442+ (Expression schema relations grouping params (nullity ('PGvararray ty))) where
443+ (<>) = unsafeBinaryOp "||"
444+
445+instance Monoid
446+ (Expression schema relations grouping params (nullity ('PGvararray ty))) where
447+ mempty = array []
448+ mappend = (<>)
449+
450+-- | >>> let expr = greatest currentTimestamp [param @1] :: Expression sch rels grp '[ 'NotNull 'PGtimestamptz] ('NotNull 'PGtimestamptz)
451+-- >>> printSQL expr
452+-- GREATEST(CURRENT_TIMESTAMP, ($1 :: timestamp with time zone))
453+greatest
454+ :: Expression schema relations grouping params (nullty)
455+ -- ^ needs at least 1 argument
456+ -> [Expression schema relations grouping params (nullty)]
457+ -- ^ or more
458+ -> Expression schema relations grouping params (nullty)
459+greatest x xs = UnsafeExpression $ "GREATEST("
460+ <> commaSeparated (renderExpression <$> (x:xs)) <> ")"
461+
462+-- | >>> printSQL $ least currentTimestamp [null_]
463+-- LEAST(CURRENT_TIMESTAMP, NULL)
464+least
465+ :: Expression schema relations grouping params (nullty)
466+ -- ^ needs at least 1 argument
467+ -> [Expression schema relations grouping params (nullty)]
468+ -- ^ or more
469+ -> Expression schema relations grouping params (nullty)
470+least x xs = UnsafeExpression $ "LEAST("
471+ <> commaSeparated (renderExpression <$> (x:xs)) <> ")"
314472
315473-- | >>> printSQL $ unsafeBinaryOp "OR" true false
316474-- (TRUE OR FALSE)
317−unsafeBinaryOp :: ByteString -> Operator ty0 ty1 ty2
475+unsafeBinaryOp
476+ :: ByteString
477+ -- ^ operator
478+ -> Expression schema relations grouping params (ty0)
479+ -> Expression schema relations grouping params (ty1)
480+ -> Expression schema relations grouping params (ty2)
318481unsafeBinaryOp op x y = UnsafeExpression $ parenthesized $
319− renderSQL x <+> op <+> renderSQL y
482+ renderExpression x <+> op <+> renderExpression y
320483
321−binaryOp
322− :: forall op sch schemas schema x y z.
323− ( Has sch schemas schema
324− , Has op schema ('Operator ('BinaryOp x y z)) )
325− => OperatorDB schemas x y z
326−binaryOp = unsafeBinaryOp $ renderSymbol @op
327−
328−-- | >>> printSQL $ unsafeLeftOp "NOT" true
484+-- | >>> printSQL $ unsafeUnaryOp "NOT" true
329485-- (NOT TRUE)
330−unsafeLeftOp :: ByteString -> x --> y
331−unsafeLeftOp op x = UnsafeExpression $ parenthesized $ op <+> renderSQL x
332−
333−leftOp
334− :: forall op sch schemas schema x y.
335− ( Has sch schemas schema
336− , Has op schema ('Operator ('LeftOp x y)) )
337− => FunctionDB schemas x y
338−leftOp = unsafeLeftOp $ renderSymbol @op
339−
340−-- | >>> printSQL $ true & unsafeRightOp "IS NOT TRUE"
341−-- (TRUE IS NOT TRUE)
342−unsafeRightOp :: ByteString -> x --> y
343−unsafeRightOp op x = UnsafeExpression $ parenthesized $ renderSQL x <+> op
344−
345−rightOp
346− :: forall op sch schemas schema x y.
347− ( Has sch schemas schema
348− , Has op schema ('Operator ('RightOp x y)) )
349− => FunctionDB schemas x y
350−rightOp = unsafeRightOp $ renderSymbol @op
486+unsafeUnaryOp
487+ :: ByteString
488+ -- ^ operator
489+ -> Expression schema relations grouping params (ty0)
490+ -> Expression schema relations grouping params (ty1)
491+unsafeUnaryOp op x = UnsafeExpression $ parenthesized $
492+ op <+> renderExpression x
351493
352494-- | >>> printSQL $ unsafeFunction "f" true
353495-- f(TRUE)
354−unsafeFunction :: ByteString -> x --> y
496+unsafeFunction
497+ :: ByteString
498+ -- ^ function
499+ -> Expression schema relations grouping params (xty)
500+ -> Expression schema relations grouping params (yty)
355501unsafeFunction fun x = UnsafeExpression $
356− fun <> parenthesized (renderSQL x)
357−
358−function
359− :: (Has sch schemas schema, Has fun schema ('Function ('[x] :=> 'Returns y)))
360− => QualifiedAlias sch fun
361− -> FunctionDB schemas x y
362−function = unsafeFunction . renderSQL
363−
364−-- | >>> printSQL $ unsafeFunctionN "f" (currentTime :* localTimestamp :* false *: literal 'a')
365−-- f(CURRENT_TIME, LOCALTIMESTAMP, FALSE, E'a')
366−unsafeFunctionN :: SListI xs => ByteString -> xs ---> y
367−unsafeFunctionN fun xs = UnsafeExpression $
368− fun <> parenthesized (renderCommaSeparated renderSQL xs)
369−
370−functionN
371− :: ( Has sch schemas schema
372− , Has fun schema ('Function (xs :=> 'Returns y))
373− , SListI xs )
374− => QualifiedAlias sch fun
375− -> FunctionNDB schemas xs y
376−functionN = unsafeFunctionN . renderSQL
377−
378−instance ty `In` PGNum
379− => Num (Expression outer commons grp schemas params from (null ty)) where
502+ fun <> parenthesized (renderExpression x)
503+
504+unsafeVariadicFunction
505+ :: ByteString
506+ -- ^ function
507+ -> [Expression schema relations grouping params xty]
508+ -> Expression schema relations grouping params (yty)
509+unsafeVariadicFunction fun x = UnsafeExpression $
510+ fun <> parenthesized (commaSeparated (map renderExpression x))
511+
512+instance PGNum ty
513+ => Num (Expression schema relations grouping params (nullity ty)) where
380514 (+) = unsafeBinaryOp "+"
381515 (-) = unsafeBinaryOp "-"
382516 (*) = unsafeBinaryOp "*"
@@ -387,24 +521,19 @@ instance ty `In` PGNum
387521 . fromString
388522 . show
389523
390−instance (ty `In` PGNum, ty `In` PGFloating) => Fractional
391− (Expression outer commons grp schemas params from (null ty)) where
524+instance (PGNum ty, PGFloating ty) => Fractional
525+ (Expression schema relations grouping params (nullity ty)) where
392526 (/) = unsafeBinaryOp "/"
393− fromRational
394− = UnsafeExpression
395− . fromString
396− . ($ "")
397− . showFFloat Nothing
398− . fromRat @Double
527+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
399528
400−instance (ty `In` PGNum, ty `In` PGFloating) => Floating
401− (Expression outer commons grp schemas params from (null ty)) where
529+instance (PGNum ty, PGFloating ty) => Floating
530+ (Expression schema relations grouping params (nullity ty)) where
402531 pi = UnsafeExpression "pi()"
403532 exp = unsafeFunction "exp"
404533 log = unsafeFunction "ln"
405534 sqrt = unsafeFunction "sqrt"
406535 b ** x = UnsafeExpression $
407− "power(" <> renderSQL b <> ", " <> renderSQL x <> ")"
536+ "power(" <> renderExpression b <> ", " <> renderExpression x <> ")"
408537 logBase b y = log y / log b
409538 sin = unsafeFunction "sin"
410539 cos = unsafeFunction "cos"
@@ -419,54 +548,916 @@ instance (ty `In` PGNum, ty `In` PGFloating) => Floating
419548 acosh x = log (x + sqrt (x*x - 1))
420549 atanh x = log ((1 + x) / (1 - x)) / 2
421550
422−-- | Contained by operators
423−class PGSubset ty where
424− (@>) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
425− (@>) = unsafeBinaryOp "@>"
426− (<@) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
427− (<@) = unsafeBinaryOp "<@"
428−instance PGSubset 'PGjsonb
429−instance PGSubset 'PGtsquery
430−instance PGSubset ('PGvararray ty)
431−instance PGSubset ('PGrange ty)
432−
433−class PGIntersect ty where
434− (@&&) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
435− (@&&) = unsafeBinaryOp "&&"
436−instance PGIntersect ('PGvararray ty)
437−instance PGIntersect ('PGrange ty)
551+-- | >>> :{
552+-- let
553+-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
554+-- expression = atan2_ pi 2
555+-- in printSQL expression
556+-- :}
557+-- atan2(pi(), 2)
558+atan2_
559+ :: PGFloating float
560+ => Expression schema relations grouping params (nullity float)
561+ -- ^ numerator
562+ -> Expression schema relations grouping params (nullity float)
563+ -- ^ denominator
564+ -> Expression schema relations grouping params (nullity float)
565+atan2_ y x = UnsafeExpression $
566+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
567+
568+-- When a `cast` is applied to an `Expression` of a known type, it
569+-- represents a run-time type conversion. The cast will succeed only if a
570+-- suitable type conversion operation has been defined.
571+--
572+-- | >>> printSQL $ true & cast int4
573+-- (TRUE :: int4)
574+cast
575+ :: TypeExpression schema ty1
576+ -- ^ type to cast as
577+ -> Expression schema relations grouping params (nullity ty0)
578+ -- ^ value to convert
579+ -> Expression schema relations grouping params (nullity ty1)
580+cast ty x = UnsafeExpression $ parenthesized $
581+ renderExpression x <+> "::" <+> renderTypeExpression ty
582+
583+-- | integer division, truncates the result
584+--
585+-- >>> :{
586+-- let
587+-- expression :: Expression schema relations grouping params (nullity 'PGint2)
588+-- expression = 5 `quot_` 2
589+-- in printSQL expression
590+-- :}
591+-- (5 / 2)
592+quot_
593+ :: PGIntegral int
594+ => Expression schema relations grouping params (nullity int)
595+ -- ^ numerator
596+ -> Expression schema relations grouping params (nullity int)
597+ -- ^ denominator
598+ -> Expression schema relations grouping params (nullity int)
599+quot_ = unsafeBinaryOp "/"
600+
601+-- | remainder upon integer division
602+--
603+-- >>> :{
604+-- let
605+-- expression :: Expression schema relations grouping params (nullity 'PGint2)
606+-- expression = 5 `rem_` 2
607+-- in printSQL expression
608+-- :}
609+-- (5 % 2)
610+rem_
611+ :: PGIntegral int
612+ => Expression schema relations grouping params (nullity int)
613+ -- ^ numerator
614+ -> Expression schema relations grouping params (nullity int)
615+ -- ^ denominator
616+ -> Expression schema relations grouping params (nullity int)
617+rem_ = unsafeBinaryOp "%"
618+
619+-- | >>> :{
620+-- let
621+-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
622+-- expression = trunc pi
623+-- in printSQL expression
624+-- :}
625+-- trunc(pi())
626+trunc
627+ :: PGFloating frac
628+ => Expression schema relations grouping params (nullity frac)
629+ -- ^ fractional number
630+ -> Expression schema relations grouping params (nullity frac)
631+trunc = unsafeFunction "trunc"
632+
633+-- | >>> :{
634+-- let
635+-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
636+-- expression = round_ pi
637+-- in printSQL expression
638+-- :}
639+-- round(pi())
640+round_
641+ :: PGFloating frac
642+ => Expression schema relations grouping params (nullity frac)
643+ -- ^ fractional number
644+ -> Expression schema relations grouping params (nullity frac)
645+round_ = unsafeFunction "round"
646+
647+-- | >>> :{
648+-- let
649+-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
650+-- expression = ceiling_ pi
651+-- in printSQL expression
652+-- :}
653+-- ceiling(pi())
654+ceiling_
655+ :: PGFloating frac
656+ => Expression schema relations grouping params (nullity frac)
657+ -- ^ fractional number
658+ -> Expression schema relations grouping params (nullity frac)
659+ceiling_ = unsafeFunction "ceiling"
660+
661+-- | A `Condition` is a boolean valued `Expression`. While SQL allows
662+-- conditions to have @NULL@, Squeal instead chooses to disallow @NULL@,
663+-- forcing one to handle the case of @NULL@ explicitly to produce
664+-- a `Condition`.
665+type Condition schema relations grouping params =
666+ Expression schema relations grouping params ('NotNull 'PGbool)
667+
668+-- | >>> printSQL true
669+-- TRUE
670+true :: Condition schema relations grouping params
671+true = UnsafeExpression "TRUE"
672+
673+-- | >>> printSQL false
674+-- FALSE
675+false :: Condition schema relations grouping params
676+false = UnsafeExpression "FALSE"
677+
678+-- | >>> printSQL $ not_ true
679+-- (NOT TRUE)
680+not_
681+ :: Condition schema relations grouping params
682+ -> Condition schema relations grouping params
683+not_ = unsafeUnaryOp "NOT"
684+
685+-- | >>> printSQL $ true .&& false
686+-- (TRUE AND FALSE)
687+(.&&)
688+ :: Condition schema relations grouping params
689+ -> Condition schema relations grouping params
690+ -> Condition schema relations grouping params
691+(.&&) = unsafeBinaryOp "AND"
692+
693+-- | >>> printSQL $ true .|| false
694+-- (TRUE OR FALSE)
695+(.||)
696+ :: Condition schema relations grouping params
697+ -> Condition schema relations grouping params
698+ -> Condition schema relations grouping params
699+(.||) = unsafeBinaryOp "OR"
700+
701+-- | >>> :{
702+-- let
703+-- expression :: Expression schema relations grouping params (nullity 'PGint2)
704+-- expression = caseWhenThenElse [(true, 1), (false, 2)] 3
705+-- in printSQL expression
706+-- :}
707+-- CASE WHEN TRUE THEN 1 WHEN FALSE THEN 2 ELSE 3 END
708+caseWhenThenElse
709+ :: [ ( Condition schema relations grouping params
710+ , Expression schema relations grouping params (ty)
711+ ) ]
712+ -- ^ whens and thens
713+ -> Expression schema relations grouping params (ty)
714+ -- ^ else
715+ -> Expression schema relations grouping params (ty)
716+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
717+ [ "CASE"
718+ , mconcat
719+ [ mconcat
720+ [ " WHEN ", renderExpression when_
721+ , " THEN ", renderExpression then_
722+ ]
723+ | (when_,then_) <- whenThens
724+ ]
725+ , " ELSE ", renderExpression else_
726+ , " END"
727+ ]
728+
729+-- | >>> :{
730+-- let
731+-- expression :: Expression schema relations grouping params (nullity 'PGint2)
732+-- expression = ifThenElse true 1 0
733+-- in printSQL expression
734+-- :}
735+-- CASE WHEN TRUE THEN 1 ELSE 0 END
736+ifThenElse
737+ :: Condition schema relations grouping params
738+ -> Expression schema relations grouping params (ty) -- ^ then
739+ -> Expression schema relations grouping params (ty) -- ^ else
740+ -> Expression schema relations grouping params (ty)
741+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
742+
743+-- | Comparison operations like `.==`, `./=`, `.>`, `.>=`, `.<` and `.<=`
744+-- will produce @NULL@s if one of their arguments is @NULL@.
745+--
746+-- >>> printSQL $ notNull true .== null_
747+-- (TRUE = NULL)
748+(.==)
749+ :: Expression schema relations grouping params (nullity ty) -- ^ lhs
750+ -> Expression schema relations grouping params (nullity ty) -- ^ rhs
751+ -> Expression schema relations grouping params (nullity 'PGbool)
752+(.==) = unsafeBinaryOp "="
753+infix 4 .==
754+
755+-- | >>> printSQL $ notNull true ./= null_
756+-- (TRUE <> NULL)
757+(./=)
758+ :: Expression schema relations grouping params (nullity ty) -- ^ lhs
759+ -> Expression schema relations grouping params (nullity ty) -- ^ rhs
760+ -> Expression schema relations grouping params (nullity 'PGbool)
761+(./=) = unsafeBinaryOp "<>"
762+infix 4 ./=
763+
764+-- | >>> printSQL $ notNull true .>= null_
765+-- (TRUE >= NULL)
766+(.>=)
767+ :: Expression schema relations grouping params (nullity ty) -- ^ lhs
768+ -> Expression schema relations grouping params (nullity ty) -- ^ rhs
769+ -> Expression schema relations grouping params (nullity 'PGbool)
770+(.>=) = unsafeBinaryOp ">="
771+infix 4 .>=
772+
773+-- | >>> printSQL $ notNull true .< null_
774+-- (TRUE < NULL)
775+(.<)
776+ :: Expression schema relations grouping params (nullity ty) -- ^ lhs
777+ -> Expression schema relations grouping params (nullity ty) -- ^ rhs
778+ -> Expression schema relations grouping params (nullity 'PGbool)
779+(.<) = unsafeBinaryOp "<"
780+infix 4 .<
781+
782+-- | >>> printSQL $ notNull true .<= null_
783+-- (TRUE <= NULL)
784+(.<=)
785+ :: Expression schema relations grouping params (nullity ty) -- ^ lhs
786+ -> Expression schema relations grouping params (nullity ty) -- ^ rhs
787+ -> Expression schema relations grouping params (nullity 'PGbool)
788+(.<=) = unsafeBinaryOp "<="
789+infix 4 .<=
790+
791+-- | >>> printSQL $ notNull true .> null_
792+-- (TRUE > NULL)
793+(.>)
794+ :: Expression schema relations grouping params (nullity ty) -- ^ lhs
795+ -> Expression schema relations grouping params (nullity ty) -- ^ rhs
796+ -> Expression schema relations grouping params (nullity 'PGbool)
797+(.>) = unsafeBinaryOp ">"
798+infix 4 .>
799+
800+-- | >>> printSQL currentDate
801+-- CURRENT_DATE
802+currentDate
803+ :: Expression schema relations grouping params (nullity 'PGdate)
804+currentDate = UnsafeExpression "CURRENT_DATE"
805+
806+-- | >>> printSQL currentTime
807+-- CURRENT_TIME
808+currentTime
809+ :: Expression schema relations grouping params (nullity 'PGtimetz)
810+currentTime = UnsafeExpression "CURRENT_TIME"
811+
812+-- | >>> printSQL currentTimestamp
813+-- CURRENT_TIMESTAMP
814+currentTimestamp
815+ :: Expression schema relations grouping params (nullity 'PGtimestamptz)
816+currentTimestamp = UnsafeExpression "CURRENT_TIMESTAMP"
817+
818+-- | >>> printSQL localTime
819+-- LOCALTIME
820+localTime
821+ :: Expression schema relations grouping params (nullity 'PGtime)
822+localTime = UnsafeExpression "LOCALTIME"
823+
824+-- | >>> printSQL localTimestamp
825+-- LOCALTIMESTAMP
826+localTimestamp
827+ :: Expression schema relations grouping params (nullity 'PGtimestamp)
828+localTimestamp = UnsafeExpression "LOCALTIMESTAMP"
829+
830+{-----------------------------------------
831+text
832+-----------------------------------------}
438833
439834instance IsString
440− (Expression outer commons grp schemas params from (null 'PGtext)) where
835+ (Expression schema relations grouping params (nullity 'PGtext)) where
441836 fromString str = UnsafeExpression $
442837 "E\'" <> fromString (escape =<< str) <> "\'"
443−instance IsString
444− (Expression outer commons grp schemas params from (null 'PGtsvector)) where
445− fromString str = UnsafeExpression . parenthesized . (<> " :: tsvector") $
446− "E\'" <> fromString (escape =<< str) <> "\'"
447−instance IsString
448− (Expression outer commons grp schemas params from (null 'PGtsquery)) where
449− fromString str = UnsafeExpression . parenthesized . (<> " :: tsquery") $
450− "E\'" <> fromString (escape =<< str) <> "\'"
838+ where
839+ escape = \case
840+ '\NUL' -> "\\0"
841+ '\'' -> "''"
842+ '"' -> "\\\""
843+ '\b' -> "\\b"
844+ '\n' -> "\\n"
845+ '\r' -> "\\r"
846+ '\t' -> "\\t"
847+ '\\' -> "\\\\"
848+ c -> [c]
451849
452850instance Semigroup
453− (Expression outer commons grp schemas params from (null ('PGvararray ty))) where
454− (<>) = unsafeBinaryOp "||"
455−instance Semigroup
456− (Expression outer commons grp schemas params from (null 'PGjsonb)) where
457− (<>) = unsafeBinaryOp "||"
458−instance Semigroup
459− (Expression outer commons grp schemas params from (null 'PGtext)) where
460− (<>) = unsafeBinaryOp "||"
461−instance Semigroup
462− (Expression outer commons grp schemas params from (null 'PGtsvector)) where
851+ (Expression schema relations grouping params (nullity 'PGtext)) where
463852 (<>) = unsafeBinaryOp "||"
464853
465854instance Monoid
466− (Expression outer commons grp schemas params from (null 'PGtext)) where
467− mempty = fromString ""
468− mappend = (<>)
469−instance Monoid
470− (Expression outer commons grp schemas params from (null 'PGtsvector)) where
855+ (Expression schema relations grouping params (nullity 'PGtext)) where
471856 mempty = fromString ""
472857 mappend = (<>)
858+
859+-- | >>> printSQL $ lower "ARRRGGG"
860+-- lower(E'ARRRGGG')
861+lower
862+ :: Expression schema relations grouping params (nullity 'PGtext)
863+ -- ^ string to lower case
864+ -> Expression schema relations grouping params (nullity 'PGtext)
865+lower = unsafeFunction "lower"
866+
867+-- | >>> printSQL $ upper "eeee"
868+-- upper(E'eeee')
869+upper
870+ :: Expression schema relations grouping params (nullity 'PGtext)
871+ -- ^ string to upper case
872+ -> Expression schema relations grouping params (nullity 'PGtext)
873+upper = unsafeFunction "upper"
874+
875+-- | >>> printSQL $ charLength "four"
876+-- char_length(E'four')
877+charLength
878+ :: Expression schema relations grouping params (nullity 'PGtext)
879+ -- ^ string to measure
880+ -> Expression schema relations grouping params (nullity 'PGint4)
881+charLength = unsafeFunction "char_length"
882+
883+-- | The `like` expression returns true if the @string@ matches
884+-- the supplied @pattern@. If @pattern@ does not contain percent signs
885+-- or underscores, then the pattern only represents the string itself;
886+-- in that case `like` acts like the equals operator. An underscore (_)
887+-- in pattern stands for (matches) any single character; a percent sign (%)
888+-- matches any sequence of zero or more characters.
889+--
890+-- >>> printSQL $ "abc" `like` "a%"
891+-- (E'abc' LIKE E'a%')
892+like
893+ :: Expression schema relations grouping params (nullity 'PGtext)
894+ -- ^ string
895+ -> Expression schema relations grouping params (nullity 'PGtext)
896+ -- ^ pattern
897+ -> Expression schema relations grouping params (nullity 'PGbool)
898+like = unsafeBinaryOp "LIKE"
899+
900+{-----------------------------------------
901+json and jsonb operators
902+see https://www.postgresql.org/docs/10/static/functions-json.html
903+-----------------------------------------}
904+
905+type IsJSONKey key = key `In` '[ 'PGint2, 'PGint4, 'PGtext ]
906+type IsJSON json = json `In` '[ 'PGjson, 'PGjsonb ]
907+
908+type Placeholder k
909+ = 'Text "(_"
910+ ':<>: 'Text "::"
911+ ':<>: 'ShowType k ':<>:
912+ 'Text ")"
913+
914+type ErrArrayOf arr ty = arr ':<>: 'Text " " ':<>: ty
915+type ErrPGfixarrayOf t = ErrArrayOf ('ShowType 'PGfixarray ':<>: 'Text " " ':<>: Placeholder Nat) t
916+type ErrPGvararrayOf t = ErrArrayOf ('ShowType 'PGvararray) t
917+
918+type family PGarray arr :: Constraint where
919+ PGarray ('PGvararray x) = ()
920+ PGarray ('PGfixarray n x) = ()
921+ PGarray val = TypeError
922+ ('Text "Unsatisfied PGarrayOf constraint. Expected either: "
923+ ':$$: 'Text " • " ':<>: ErrPGvararrayOf (Placeholder PGType)
924+ ':$$: 'Text " • " ':<>: ErrPGfixarrayOf (Placeholder PGType)
925+ ':$$: 'Text "But got: " ':<>: 'ShowType val)
926+
927+type family PGarrayOf arr ty :: Constraint where
928+ PGarrayOf ('PGvararray x) ty = x ~ ty
929+ PGarrayOf ('PGfixarray n x) ty = x ~ ty
930+ PGarrayOf val ty = TypeError
931+ ('Text "Unsatisfied PGarrayOf constraint. Expected either: "
932+ ':$$: 'Text " • " ':<>: ErrPGvararrayOf ('ShowType ty)
933+ ':$$: 'Text " • " ':<>: ErrPGfixarrayOf ('ShowType ty)
934+ ':$$: 'Text "But got: " ':<>: 'ShowType val)
935+
936+type IsPGtextArray arr = PGarrayOf arr 'PGtext
937+
938+-- | Get JSON value (object field or array element) at a key.
939+(.->)
940+ :: (IsJSON json, IsJSONKey key)
941+ => Expression schema relations grouping params (jnull json)
942+ -> Expression schema relations grouping params (knull key)
943+ -> Expression schema relations grouping params ('Null json)
944+(.->) = unsafeBinaryOp "->"
945+
946+-- | Get JSON value (object field or array element) at a key, as text.
947+(.->>)
948+ :: (IsJSON json, IsJSONKey key)
949+ => Expression schema relations grouping params (jnull json)
950+ -> Expression schema relations grouping params (knull key)
951+ -> Expression schema relations grouping params ('Null 'PGtext)
952+(.->>) = unsafeBinaryOp "->>"
953+
954+-- | Get JSON value at a specified path.
955+(.#>)
956+ :: (IsJSON json, IsPGtextArray path)
957+ => Expression schema relations grouping params (jnull json)
958+ -> Expression schema relations grouping params (knull path)
959+ -> Expression schema relations grouping params ('Null json)
960+(.#>) = unsafeBinaryOp "#>"
961+
962+-- | Get JSON value at a specified path as text.
963+(.#>>)
964+ :: (IsJSON json, IsPGtextArray path)
965+ => Expression schema relations grouping params (jnull json)
966+ -> Expression schema relations grouping params (knull path)
967+ -> Expression schema relations grouping params ('Null 'PGtext)
968+(.#>>) = unsafeBinaryOp "#>>"
969+
970+-- Additional jsonb operators
971+
972+-- | Or operator for nulls; if either argument is null, then the result is null.
973+type family Null2 a b where
974+ Null2 'Null a = 'Null
975+ Null2 a 'Null = 'Null
976+ Null2 'NotNull 'NotNull = 'NotNull
977+
978+-- | See 'Null2'
979+type family NullOfN xs where
980+ NullOfN ('Null ty:xs) = 'Null
981+ NullOfN (x:xs) = NullOfN xs
982+ NullOfN '[] = 'NotNull
983+
984+-- | Does the left JSON value contain the right JSON path/value entries at the
985+-- top level?
986+(.@>)
987+ :: Expression schema relations grouping params (null0 'PGjsonb)
988+ -> Expression schema relations grouping params (null1 'PGjsonb)
989+ -> Expression schema relations grouping params (Null2 null0 null1 'PGbool)
990+(.@>) = unsafeBinaryOp "@>"
991+
992+-- | Are the left JSON path/value entries contained at the top level within the
993+-- right JSON value?
994+(.<@)
995+ :: Expression schema relations grouping params (null0 'PGjsonb)
996+ -> Expression schema relations grouping params (null1 'PGjsonb)
997+ -> Expression schema relations grouping params (Null2 null0 null1 'PGbool)
998+(.<@) = unsafeBinaryOp "<@"
999+
1000+-- | Does the string exist as a top-level key within the JSON value?
1001+(.?)
1002+ :: Expression schema relations grouping params (null0 'PGjsonb)
1003+ -> Expression schema relations grouping params (null1 'PGtext)
1004+ -> Expression schema relations grouping params (Null2 null0 null1 'PGbool)
1005+(.?) = unsafeBinaryOp "?"
1006+
1007+-- | Do any of these array strings exist as top-level keys?
1008+(.?|)
1009+ :: Expression schema relations grouping params (null0 'PGjsonb)
1010+ -> Expression schema relations grouping params (null1 ('PGvararray 'PGtext))
1011+ -> Expression schema relations grouping params (Null2 null0 null1 'PGbool)
1012+(.?|) = unsafeBinaryOp "?|"
1013+
1014+-- | Do all of these array strings exist as top-level keys?
1015+(.?&)
1016+ :: Expression schema relations grouping params (null0 'PGjsonb)
1017+ -> Expression schema relations grouping params (null1 ('PGvararray 'PGtext))
1018+ -> Expression schema relations grouping params (Null2 null0 null1 'PGbool)
1019+(.?&) = unsafeBinaryOp "?&"
1020+
1021+-- | Concatenate two jsonb values into a new jsonb value
1022+(.||.)
1023+ :: Expression schema relations grouping params (null0 'PGjsonb)
1024+ -> Expression schema relations grouping params (null1 'PGjsonb)
1025+ -> Expression schema relations grouping params (Null2 null0 null1 'PGjsonb)
1026+(.||.) = unsafeBinaryOp "||"
1027+
1028+-- | Delete a key or keys from a JSON object, or remove an array element.
1029+--
1030+-- If the right operand is..
1031+--
1032+-- @ text @: Delete key/value pair or string element from left operand. Key/value pairs
1033+-- are matched based on their key value.
1034+--
1035+-- @ text[] @: Delete multiple key/value pairs or string elements
1036+-- from left operand. Key/value pairs are matched based on their key value.
1037+--
1038+-- @ integer @: Delete the array element with specified index (Negative integers
1039+-- count from the end). Throws an error if top level container is not an array.
1040+(.-.)
1041+ :: (key `In` '[ 'PGtext, 'PGvararray 'PGtext, 'PGint4, 'PGint2 ]) -- hlint error without parens here
1042+ => Expression schema relations grouping params (null0 'PGjsonb)
1043+ -> Expression schema relations grouping params (null1 key)
1044+ -> Expression schema relations grouping params (Null2 null0 null1 'PGjsonb)
1045+(.-.) = unsafeBinaryOp "-"
1046+
1047+-- | Delete the field or element with specified path (for JSON arrays, negative
1048+-- integers count from the end)
1049+(#-.)
1050+ :: IsPGtextArray arrayty
1051+ => Expression schema relations grouping params (null0 'PGjsonb)
1052+ -> Expression schema relations grouping params (null1 arrayty)
1053+ -> Expression schema relations grouping params (Null2 null0 null1 'PGjsonb)
1054+(#-.) = unsafeBinaryOp "#-"
1055+
1056+toJson
1057+ :: Expression schema relations grouping params (nullity ty)
1058+ -> Expression schema relations grouping params (nullity 'PGjson)
1059+toJson = unsafeFunction "to_json"
1060+
1061+toJsonb
1062+ :: Expression schema relations grouping params (nullity ty)
1063+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1064+toJsonb = unsafeUnaryOp "to_jsonb"
1065+
1066+arrayToJson
1067+ :: PGarray arr
1068+ => Expression schema relations grouping params (nullity arr)
1069+ -> Expression schema relations grouping params (nullity 'PGjson)
1070+arrayToJson = unsafeFunction "array_to_json"
1071+
1072+rowToJson
1073+ :: Expression schema relations grouping params (nullity ('PGcomposite ty))
1074+ -> Expression schema relations grouping params (nullity 'PGjson)
1075+rowToJson = unsafeFunction "row_to_json"
1076+
1077+-- TODO: jsonBuildArray, jsonbBuildArray, jsonObject, jsonbObject
1078+-- probably something like
1079+-- :: NP (Expression s r g p) xs -> Expression s r g p ('NotNull 'PGjson[b])
1080+
1081+{-----------------------------------------
1082+aggregation
1083+-----------------------------------------}
1084+
1085+-- | escape hatch to define aggregate functions
1086+unsafeAggregate
1087+ :: ByteString -- ^ aggregate function
1088+ -> Expression schema relations 'Ungrouped params (xty)
1089+ -> Expression schema relations ('Grouped bys) params (yty)
1090+unsafeAggregate fun x = UnsafeExpression $ mconcat
1091+ [fun, "(", renderExpression x, ")"]
1092+
1093+-- | escape hatch to define aggregate functions over distinct values
1094+unsafeAggregateDistinct
1095+ :: ByteString -- ^ aggregate function
1096+ -> Expression schema relations 'Ungrouped params (xty)
1097+ -> Expression schema relations ('Grouped bys) params (yty)
1098+unsafeAggregateDistinct fun x = UnsafeExpression $ mconcat
1099+ [fun, "(DISTINCT ", renderExpression x, ")"]
1100+
1101+-- | >>> :{
1102+-- let
1103+-- expression :: Expression schema '[tab ::: '["col" ::: 'Null 'PGnumeric]] ('Grouped bys) params ('Null 'PGnumeric)
1104+-- expression = sum_ #col
1105+-- in printSQL expression
1106+-- :}
1107+-- sum("col")
1108+sum_
1109+ :: PGNum ty
1110+ => Expression schema relations 'Ungrouped params (nullity ty)
1111+ -- ^ what to sum
1112+ -> Expression schema relations ('Grouped bys) params (nullity ty)
1113+sum_ = unsafeAggregate "sum"
1114+
1115+-- | >>> :{
1116+-- let
1117+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGnumeric]] ('Grouped bys) params (nullity 'PGnumeric)
1118+-- expression = sumDistinct #col
1119+-- in printSQL expression
1120+-- :}
1121+-- sum(DISTINCT "col")
1122+sumDistinct
1123+ :: PGNum ty
1124+ => Expression schema relations 'Ungrouped params (nullity ty)
1125+ -- ^ what to sum
1126+ -> Expression schema relations ('Grouped bys) params (nullity ty)
1127+sumDistinct = unsafeAggregateDistinct "sum"
1128+
1129+-- | A constraint for `PGType`s that you can take averages of and the resulting
1130+-- `PGType`.
1131+class PGAvg ty avg | ty -> avg where
1132+ avg, avgDistinct
1133+ :: Expression schema relations 'Ungrouped params (nullity ty)
1134+ -- ^ what to average
1135+ -> Expression schema relations ('Grouped bys) params (nullity avg)
1136+ avg = unsafeAggregate "avg"
1137+ avgDistinct = unsafeAggregateDistinct "avg"
1138+instance PGAvg 'PGint2 'PGnumeric
1139+instance PGAvg 'PGint4 'PGnumeric
1140+instance PGAvg 'PGint8 'PGnumeric
1141+instance PGAvg 'PGnumeric 'PGnumeric
1142+instance PGAvg 'PGfloat4 'PGfloat8
1143+instance PGAvg 'PGfloat8 'PGfloat8
1144+instance PGAvg 'PGinterval 'PGinterval
1145+
1146+-- | >>> :{
1147+-- let
1148+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1149+-- expression = bitAnd #col
1150+-- in printSQL expression
1151+-- :}
1152+-- bit_and("col")
1153+bitAnd
1154+ :: PGIntegral int
1155+ => Expression schema relations 'Ungrouped params (nullity int)
1156+ -- ^ what to aggregate
1157+ -> Expression schema relations ('Grouped bys) params (nullity int)
1158+bitAnd = unsafeAggregate "bit_and"
1159+
1160+-- | >>> :{
1161+-- let
1162+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1163+-- expression = bitOr #col
1164+-- in printSQL expression
1165+-- :}
1166+-- bit_or("col")
1167+bitOr
1168+ :: PGIntegral int
1169+ => Expression schema relations 'Ungrouped params (nullity int)
1170+ -- ^ what to aggregate
1171+ -> Expression schema relations ('Grouped bys) params (nullity int)
1172+bitOr = unsafeAggregate "bit_or"
1173+
1174+-- | >>> :{
1175+-- let
1176+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1177+-- expression = bitAndDistinct #col
1178+-- in printSQL expression
1179+-- :}
1180+-- bit_and(DISTINCT "col")
1181+bitAndDistinct
1182+ :: PGIntegral int
1183+ => Expression schema relations 'Ungrouped params (nullity int)
1184+ -- ^ what to aggregate
1185+ -> Expression schema relations ('Grouped bys) params (nullity int)
1186+bitAndDistinct = unsafeAggregateDistinct "bit_and"
1187+
1188+-- | >>> :{
1189+-- let
1190+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1191+-- expression = bitOrDistinct #col
1192+-- in printSQL expression
1193+-- :}
1194+-- bit_or(DISTINCT "col")
1195+bitOrDistinct
1196+ :: PGIntegral int
1197+ => Expression schema relations 'Ungrouped params (nullity int)
1198+ -- ^ what to aggregate
1199+ -> Expression schema relations ('Grouped bys) params (nullity int)
1200+bitOrDistinct = unsafeAggregateDistinct "bit_or"
1201+
1202+-- | >>> :{
1203+-- let
1204+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1205+-- expression = boolAnd #col
1206+-- in printSQL expression
1207+-- :}
1208+-- bool_and("col")
1209+boolAnd
1210+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1211+ -- ^ what to aggregate
1212+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1213+boolAnd = unsafeAggregate "bool_and"
1214+
1215+-- | >>> :{
1216+-- let
1217+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1218+-- expression = boolOr #col
1219+-- in printSQL expression
1220+-- :}
1221+-- bool_or("col")
1222+boolOr
1223+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1224+ -- ^ what to aggregate
1225+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1226+boolOr = unsafeAggregate "bool_or"
1227+
1228+-- | >>> :{
1229+-- let
1230+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1231+-- expression = boolAndDistinct #col
1232+-- in printSQL expression
1233+-- :}
1234+-- bool_and(DISTINCT "col")
1235+boolAndDistinct
1236+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1237+ -- ^ what to aggregate
1238+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1239+boolAndDistinct = unsafeAggregateDistinct "bool_and"
1240+
1241+-- | >>> :{
1242+-- let
1243+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1244+-- expression = boolOrDistinct #col
1245+-- in printSQL expression
1246+-- :}
1247+-- bool_or(DISTINCT "col")
1248+boolOrDistinct
1249+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1250+ -- ^ what to aggregate
1251+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1252+boolOrDistinct = unsafeAggregateDistinct "bool_or"
1253+
1254+-- | A special aggregation that does not require an input
1255+--
1256+-- >>> printSQL countStar
1257+-- count(*)
1258+countStar
1259+ :: Expression schema relations ('Grouped bys) params ('NotNull 'PGint8)
1260+countStar = UnsafeExpression $ "count(*)"
1261+
1262+-- | >>> :{
1263+-- let
1264+-- expression :: Expression schema '[tab ::: '["col" ::: nullity ty]] (Grouped bys) params ('NotNull 'PGint8)
1265+-- expression = count #col
1266+-- in printSQL expression
1267+-- :}
1268+-- count("col")
1269+count
1270+ :: Expression schema relations 'Ungrouped params ty
1271+ -- ^ what to count
1272+ -> Expression schema relations ('Grouped bys) params ('NotNull 'PGint8)
1273+count = unsafeAggregate "count"
1274+
1275+-- | >>> :{
1276+-- let
1277+-- expression :: Expression schema '[tab ::: '["col" ::: nullity ty]] (Grouped bys) params ('NotNull 'PGint8)
1278+-- expression = countDistinct #col
1279+-- in printSQL expression
1280+-- :}
1281+-- count(DISTINCT "col")
1282+countDistinct
1283+ :: Expression schema relations 'Ungrouped params ty
1284+ -- ^ what to count
1285+ -> Expression schema relations ('Grouped bys) params ('NotNull 'PGint8)
1286+countDistinct = unsafeAggregateDistinct "count"
1287+
1288+-- | synonym for `boolAnd`
1289+--
1290+-- >>> :{
1291+-- let
1292+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1293+-- expression = every #col
1294+-- in printSQL expression
1295+-- :}
1296+-- every("col")
1297+every
1298+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1299+ -- ^ what to aggregate
1300+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1301+every = unsafeAggregate "every"
1302+
1303+-- | synonym for `boolAndDistinct`
1304+--
1305+-- >>> :{
1306+-- let
1307+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1308+-- expression = everyDistinct #col
1309+-- in printSQL expression
1310+-- :}
1311+-- every(DISTINCT "col")
1312+everyDistinct
1313+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1314+ -- ^ what to aggregate
1315+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1316+everyDistinct = unsafeAggregateDistinct "every"
1317+
1318+-- | minimum and maximum aggregation
1319+max_, min_, maxDistinct, minDistinct
1320+ :: Expression schema relations 'Ungrouped params (nullity ty)
1321+ -- ^ what to aggregate
1322+ -> Expression schema relations ('Grouped bys) params (nullity ty)
1323+max_ = unsafeAggregate "max"
1324+min_ = unsafeAggregate "min"
1325+maxDistinct = unsafeAggregateDistinct "max"
1326+minDistinct = unsafeAggregateDistinct "min"
1327+
1328+{-----------------------------------------
1329+type expressions
1330+-----------------------------------------}
1331+
1332+-- | `TypeExpression`s are used in `cast`s and `createTable` commands.
1333+newtype TypeExpression (schema :: SchemaType) (ty :: PGType)
1334+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
1335+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
1336+
1337+instance (Has alias schema ('Typedef ty))
1338+ => IsLabel alias (TypeExpression schema ty) where
1339+ fromLabel = UnsafeTypeExpression (renderAlias (fromLabel @alias))
1340+
1341+-- | logical Boolean (true/false)
1342+bool :: TypeExpression schema 'PGbool
1343+bool = UnsafeTypeExpression "bool"
1344+-- | signed two-byte integer
1345+int2, smallint :: TypeExpression schema 'PGint2
1346+int2 = UnsafeTypeExpression "int2"
1347+smallint = UnsafeTypeExpression "smallint"
1348+-- | signed four-byte integer
1349+int4, int, integer :: TypeExpression schema 'PGint4
1350+int4 = UnsafeTypeExpression "int4"
1351+int = UnsafeTypeExpression "int"
1352+integer = UnsafeTypeExpression "integer"
1353+-- | signed eight-byte integer
1354+int8, bigint :: TypeExpression schema 'PGint8
1355+int8 = UnsafeTypeExpression "int8"
1356+bigint = UnsafeTypeExpression "bigint"
1357+-- | arbitrary precision numeric type
1358+numeric :: TypeExpression schema 'PGnumeric
1359+numeric = UnsafeTypeExpression "numeric"
1360+-- | single precision floating-point number (4 bytes)
1361+float4, real :: TypeExpression schema 'PGfloat4
1362+float4 = UnsafeTypeExpression "float4"
1363+real = UnsafeTypeExpression "real"
1364+-- | double precision floating-point number (8 bytes)
1365+float8, doublePrecision :: TypeExpression schema 'PGfloat8
1366+float8 = UnsafeTypeExpression "float8"
1367+doublePrecision = UnsafeTypeExpression "double precision"
1368+-- | variable-length character string
1369+text :: TypeExpression schema 'PGtext
1370+text = UnsafeTypeExpression "text"
1371+-- | fixed-length character string
1372+char, character
1373+ :: (KnownNat n, 1 <= n)
1374+ => proxy n
1375+ -> TypeExpression schema ('PGchar n)
1376+char p = UnsafeTypeExpression $ "char(" <> renderNat p <> ")"
1377+character p = UnsafeTypeExpression $ "character(" <> renderNat p <> ")"
1378+-- | variable-length character string
1379+varchar, characterVarying
1380+ :: (KnownNat n, 1 <= n)
1381+ => proxy n
1382+ -> TypeExpression schema ('PGvarchar n)
1383+varchar p = UnsafeTypeExpression $ "varchar(" <> renderNat p <> ")"
1384+characterVarying p = UnsafeTypeExpression $
1385+ "character varying(" <> renderNat p <> ")"
1386+-- | binary data ("byte array")
1387+bytea :: TypeExpression schema 'PGbytea
1388+bytea = UnsafeTypeExpression "bytea"
1389+-- | date and time (no time zone)
1390+timestamp :: TypeExpression schema 'PGtimestamp
1391+timestamp = UnsafeTypeExpression "timestamp"
1392+-- | date and time, including time zone
1393+timestampWithTimeZone :: TypeExpression schema 'PGtimestamptz
1394+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
1395+-- | calendar date (year, month, day)
1396+date :: TypeExpression schema 'PGdate
1397+date = UnsafeTypeExpression "date"
1398+-- | time of day (no time zone)
1399+time :: TypeExpression schema 'PGtime
1400+time = UnsafeTypeExpression "time"
1401+-- | time of day, including time zone
1402+timeWithTimeZone :: TypeExpression schema 'PGtimetz
1403+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
1404+-- | time span
1405+interval :: TypeExpression schema 'PGinterval
1406+interval = UnsafeTypeExpression "interval"
1407+-- | universally unique identifier
1408+uuid :: TypeExpression schema 'PGuuid
1409+uuid = UnsafeTypeExpression "uuid"
1410+-- | IPv4 or IPv6 host address
1411+inet :: TypeExpression schema 'PGinet
1412+inet = UnsafeTypeExpression "inet"
1413+-- | textual JSON data
1414+json :: TypeExpression schema 'PGjson
1415+json = UnsafeTypeExpression "json"
1416+-- | binary JSON data, decomposed
1417+jsonb :: TypeExpression schema 'PGjsonb
1418+jsonb = UnsafeTypeExpression "jsonb"
1419+-- | variable length array
1420+vararray
1421+ :: TypeExpression schema pg
1422+ -> TypeExpression schema ('PGvararray pg)
1423+vararray ty = UnsafeTypeExpression $ renderTypeExpression ty <> "[]"
1424+-- | fixed length array
1425+--
1426+-- >>> renderTypeExpression (fixarray (Proxy @2) json)
1427+-- "json[2]"
1428+fixarray
1429+ :: KnownNat n
1430+ => proxy n
1431+ -> TypeExpression schema pg
1432+ -> TypeExpression schema ('PGfixarray n pg)
1433+fixarray p ty = UnsafeTypeExpression $
1434+ renderTypeExpression ty <> "[" <> renderNat p <> "]"
1435+
1436+-- | `pgtype` is a demoted version of a `PGType`
1437+class PGTyped schema (ty :: PGType) where pgtype :: TypeExpression schema ty
1438+instance PGTyped schema 'PGbool where pgtype = bool
1439+instance PGTyped schema 'PGint2 where pgtype = int2
1440+instance PGTyped schema 'PGint4 where pgtype = int4
1441+instance PGTyped schema 'PGint8 where pgtype = int8
1442+instance PGTyped schema 'PGnumeric where pgtype = numeric
1443+instance PGTyped schema 'PGfloat4 where pgtype = float4
1444+instance PGTyped schema 'PGfloat8 where pgtype = float8
1445+instance PGTyped schema 'PGtext where pgtype = text
1446+instance (KnownNat n, 1 <= n)
1447+ => PGTyped schema ('PGchar n) where pgtype = char (Proxy @n)
1448+instance (KnownNat n, 1 <= n)
1449+ => PGTyped schema ('PGvarchar n) where pgtype = varchar (Proxy @n)
1450+instance PGTyped schema 'PGbytea where pgtype = bytea
1451+instance PGTyped schema 'PGtimestamp where pgtype = timestamp
1452+instance PGTyped schema 'PGtimestamptz where pgtype = timestampWithTimeZone
1453+instance PGTyped schema 'PGdate where pgtype = date
1454+instance PGTyped schema 'PGtime where pgtype = time
1455+instance PGTyped schema 'PGtimetz where pgtype = timeWithTimeZone
1456+instance PGTyped schema 'PGinterval where pgtype = interval
1457+instance PGTyped schema 'PGuuid where pgtype = uuid
1458+instance PGTyped schema 'PGjson where pgtype = json
1459+instance PGTyped schema 'PGjsonb where pgtype = jsonb
1460+instance PGTyped schema ty => PGTyped schema ('PGvararray ty) where
1461+ pgtype = vararray (pgtype @schema @ty)
1462+instance (KnownNat n, PGTyped schema ty) => PGTyped schema ('PGfixarray n ty) where
1463+ pgtype = fixarray (Proxy @n) (pgtype @schema @ty)