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