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