@@ -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,368 +16,478 @@ 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
2827 , TypeOperators
2928 , UndecidableInstances
30− , RankNTypes
3129#-}
3230
3331module Squeal.PostgreSQL.Expression
3432 ( -- * 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
33+ Expression (UnsafeExpression, renderExpression)
34+ , HasParameter (parameter)
35+ , param
36+ -- ** Null
37+ , null_
38+ , notNull
39+ , coalesce
40+ , fromNull
41+ , isNull
42+ , isNotNull
43+ , matchNull
44+ , nullIf
45+ -- ** Collections
46+ , array
47+ , row
48+ -- ** Functions
4749 , unsafeBinaryOp
48− , binaryOp
49− , FunctionVar
50− , unsafeFunctionVar
51− , type (--->)
52− , FunctionNDB
53− , unsafeFunctionN
54− , functionN
55− , PGSubset (..)
56− , PGIntersect (..)
50+ , unsafeUnaryOp
51+ , unsafeFunction
52+ , atan2_
53+ , cast
54+ , quot_
55+ , rem_
56+ , trunc
57+ , round_
58+ , ceiling_
59+ , greatest
60+ , least
61+ -- ** Conditions
62+ , Condition
63+ , true
64+ , false
65+ , not_
66+ , (.&&)
67+ , (.||)
68+ , caseWhenThenElse
69+ , ifThenElse
70+ , (.==)
71+ , (./=)
72+ , (.>=)
73+ , (.<)
74+ , (.<=)
75+ , (.>)
76+ -- ** Time
77+ , currentDate
78+ , currentTime
79+ , currentTimestamp
80+ , localTime
81+ , localTimestamp
82+ -- ** Text
83+ , lower
84+ , upper
85+ , charLength
86+ , like
87+ -- ** Aggregation
88+ , unsafeAggregate, unsafeAggregateDistinct
89+ , sum_, sumDistinct
90+ , PGAvg (avg, avgDistinct)
91+ , bitAnd, bitOr, boolAnd, boolOr
92+ , bitAndDistinct, bitOrDistinct, boolAndDistinct, boolOrDistinct
93+ , countStar
94+ , count, countDistinct
95+ , every, everyDistinct
96+ , max_, maxDistinct, min_, minDistinct
97+ -- * Types
98+ , TypeExpression (UnsafeTypeExpression, renderTypeExpression)
99+ , PGTyped (pgtype)
100+ , bool
101+ , int2
102+ , smallint
103+ , int4
104+ , int
105+ , integer
106+ , int8
107+ , bigint
108+ , numeric
109+ , float4
110+ , real
111+ , float8
112+ , doublePrecision
113+ , text
114+ , char
115+ , character
116+ , varchar
117+ , characterVarying
118+ , bytea
119+ , timestamp
120+ , timestampWithTimeZone
121+ , date
122+ , time
123+ , timeWithTimeZone
124+ , interval
125+ , uuid
126+ , inet
127+ , json
128+ , jsonb
129+ , vararray
130+ , fixarray
57131 -- * Re-export
58132 , (&)
59− , K (..)
60− , unK
133+ , NP ((:*), Nil)
61134 ) where
62135
63136import Control.Category
64137import Control.DeepSeq
65138import Data.ByteString (ByteString)
66139import Data.Function ((&))
67−import Data.Semigroup hiding (All)
140+import Data.Semigroup
141+import Data.Ratio
68142import Data.String
69−import Generics.SOP hiding (All, from)
143+import Generics.SOP hiding (from)
70144import GHC.OverloadedLabels
71145import GHC.TypeLits
72−import Numeric
73146import Prelude hiding (id, (.))
74147
75148import qualified GHC.Generics as GHC
76149
77−import Squeal.PostgreSQL.Alias
78−import Squeal.PostgreSQL.List
79150import Squeal.PostgreSQL.Render
80151import Squeal.PostgreSQL.Schema
81152
82−-- $setup
83−-- >>> import Squeal.PostgreSQL
84−
85153{-----------------------------------------
86154column expressions
87155-----------------------------------------}
88156
89157{- | `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,
158+such as in the target list of the `Squeal.PostgreSQL.Query.select` command,
92159as new column values in `Squeal.PostgreSQL.Manipulation.insertRow` or
93160`Squeal.PostgreSQL.Manipulation.update`,
94−or in search `Squeal.PostgreSQL.Logic.Condition`s in a number of commands.
161+or in search `Condition`s in a number of commands.
95162
96163The expression syntax allows the calculation of
97164values from primitive expression using arithmetic, logical,
98165and 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`.
113166-}
114167newtype Expression
115− (outer :: FromType)
116− (commons :: FromType)
117− (grp :: Grouping)
118− (schemas :: SchemasType)
168+ (schema :: SchemaType)
169+ (relations :: RelationsType)
170+ (grouping :: Grouping)
119171 (params :: [NullityType])
120− (from :: FromType)
121172 (ty :: NullityType)
122173 = UnsafeExpression { renderExpression :: ByteString }
123174 deriving (GHC.Generic,Show,Eq,Ord,NFData)
124−instance RenderSQL (Expression outer commons grp schemas params from ty) where
175+
176+instance RenderSQL (Expression schema relations grouping params ty) where
125177 renderSQL = renderExpression
126178
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)
179+{- | A `HasParameter` constraint is used to indicate a value that is
180+supplied externally to a SQL statement.
181+`Squeal.PostgreSQL.PQ.manipulateParams`,
182+`Squeal.PostgreSQL.PQ.queryParams` and
183+`Squeal.PostgreSQL.PQ.traversePrepared` support specifying data values
184+separately from the SQL command string, in which case `param`s are used to
185+refer to the out-of-line data values.
181186-}
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
187+class KnownNat n => HasParameter
188+ (n :: Nat)
189+ (schema :: SchemaType)
190+ (params :: [NullityType])
191+ (ty :: NullityType)
192+ | n params -> ty where
193+ -- | `parameter` takes a `Nat` using type application and a `TypeExpression`.
194+ --
195+ -- >>> let expr = parameter @1 int4 :: Expression sch rels grp '[ 'Null 'PGint4] ('Null 'PGint4)
196+ -- >>> printSQL expr
197+ -- ($1 :: int4)
198+ parameter
199+ :: TypeExpression schema (PGTypeOf ty)
200+ -> Expression schema relations grouping params ty
201+ parameter ty = UnsafeExpression $ parenthesized $
202+ "$" <> renderNat (Proxy @n) <+> "::"
203+ <+> renderTypeExpression ty
204+instance {-# OVERLAPPING #-} HasParameter 1 schema (ty1:tys) ty1
205+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) schema params ty)
206+ => HasParameter n schema (ty' : params) ty
207+
208+-- | `param` takes a `Nat` using type application and for basic types,
209+-- infers a `TypeExpression`.
210+--
211+-- >>> let expr = param @1 :: Expression sch rels grp '[ 'Null 'PGint4] ('Null 'PGint4)
212+-- >>> printSQL expr
213+-- ($1 :: int4)
214+param
215+ :: forall n schema params relations grouping ty
216+ . (PGTyped schema (PGTypeOf ty), HasParameter n schema params ty)
217+ => Expression schema relations grouping params ty -- ^ param
218+param = parameter @n pgtype
219+
220+instance (HasUnique relation relations columns, Has column columns ty)
221+ => IsLabel column (Expression schema relations 'Ungrouped params ty) where
222+ fromLabel = UnsafeExpression $ renderAlias (Alias @column)
223+instance (HasUnique relation relations columns, Has column columns ty)
224+ => IsLabel column
225+ (Aliased (Expression schema relations 'Ungrouped params) (column ::: ty)) where
226+ fromLabel = fromLabel @column `As` Alias @column
227+instance (HasUnique relation relations columns, Has column columns ty)
228+ => IsLabel column
229+ (NP (Aliased (Expression schema relations 'Ungrouped params)) '[column ::: ty]) where
230+ fromLabel = fromLabel @column :* Nil
231+
232+instance (Has relation relations columns, Has column columns ty)
233+ => IsQualified relation column (Expression schema relations 'Ungrouped params ty) where
234+ relation ! column = UnsafeExpression $
235+ renderAlias relation <> "." <> renderAlias column
236+instance (Has relation relations columns, Has column columns ty)
237+ => IsQualified relation column
238+ (Aliased (Expression schema relations 'Ungrouped params) (column ::: ty)) where
239+ relation ! column = relation ! column `As` column
240+instance (Has relation relations columns, Has column columns ty)
241+ => IsQualified relation column
242+ (NP (Aliased (Expression schema relations 'Ungrouped params)) '[column ::: ty]) where
243+ relation ! column = relation ! column :* Nil
245244
246245instance
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
246+ ( HasUnique relation relations columns
247+ , Has column columns ty
248+ , GroupedBy relation column bys
249+ ) => IsLabel column
250+ (Expression schema relations ('Grouped bys) params ty) where
251+ fromLabel = UnsafeExpression $ renderAlias (Alias @column)
261252instance
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
253+ ( HasUnique relation relations columns
254+ , Has column columns ty
255+ , GroupedBy relation column bys
256+ ) => IsLabel column
257+ ( Aliased (Expression schema relations ('Grouped bys) params)
258+ (column ::: ty) ) where
259+ fromLabel = fromLabel @column `As` Alias @column
269260instance
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
261+ ( HasUnique relation relations columns
262+ , Has column columns ty
263+ , GroupedBy relation column bys
264+ ) => IsLabel column
265+ ( NP (Aliased (Expression schema relations ('Grouped bys) params))
266+ '[column ::: ty] ) where
267+ fromLabel = fromLabel @column :* Nil
277268
278269instance
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
286−instance
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
270+ ( Has relation relations columns
271+ , Has column columns ty
272+ , GroupedBy relation column bys
273+ ) => IsQualified relation column
274+ (Expression schema relations ('Grouped bys) params ty) where
275+ relation ! column = UnsafeExpression $
276+ renderAlias relation <> "." <> renderAlias column
294277instance
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
278+ ( Has relation relations columns
279+ , Has column columns ty
280+ , GroupedBy relation column bys
281+ ) => IsQualified relation column
282+ (Aliased (Expression schema relations ('Grouped bys) params)
283+ (column ::: ty)) where
284+ relation ! column = relation ! column `As` column
302285instance
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
286+ ( Has relation relations columns
287+ , Has column columns ty
288+ , GroupedBy relation column bys
289+ ) => IsQualified relation column
290+ ( NP (Aliased (Expression schema relations ('Grouped bys) params))
291+ '[column ::: ty]) where
292+ relation ! column = relation ! column :* Nil
293+
294+-- | analagous to `Nothing`
295+--
296+-- >>> printSQL null_
297+-- NULL
298+null_ :: Expression schema rels grouping params ('Null ty)
299+null_ = UnsafeExpression "NULL"
300+
301+-- | analagous to `Just`
302+--
303+-- >>> printSQL $ notNull true
304+-- TRUE
305+notNull
306+ :: Expression schema rels grouping params ('NotNull ty)
307+ -> Expression schema rels grouping params ('Null ty)
308+notNull = UnsafeExpression . renderExpression
309+
310+-- | return the leftmost value which is not NULL
311+--
312+-- >>> printSQL $ coalesce [null_, notNull true] false
313+-- COALESCE(NULL, TRUE, FALSE)
314+coalesce
315+ :: [Expression schema relations grouping params ('Null ty)]
316+ -- ^ @NULL@s may be present
317+ -> Expression schema relations grouping params ('NotNull ty)
318+ -- ^ @NULL@ is absent
319+ -> Expression schema relations grouping params ('NotNull ty)
320+coalesce nullxs notNullx = UnsafeExpression $
321+ "COALESCE" <> parenthesized (commaSeparated
322+ ((renderExpression <$> nullxs) <> [renderExpression notNullx]))
323+
324+-- | analagous to `Data.Maybe.fromMaybe` using @COALESCE@
325+--
326+-- >>> printSQL $ fromNull true null_
327+-- COALESCE(NULL, TRUE)
328+fromNull
329+ :: Expression schema relations grouping params ('NotNull ty)
330+ -- ^ what to convert @NULL@ to
331+ -> Expression schema relations grouping params ('Null ty)
332+ -> Expression schema relations grouping params ('NotNull ty)
333+fromNull notNullx nullx = coalesce [nullx] notNullx
334+
335+-- | >>> printSQL $ null_ & isNull
336+-- NULL IS NULL
337+isNull
338+ :: Expression schema relations grouping params ('Null ty)
339+ -- ^ possibly @NULL@
340+ -> Condition schema relations grouping params
341+isNull x = UnsafeExpression $ renderExpression x <+> "IS NULL"
342+
343+-- | >>> printSQL $ null_ & isNotNull
344+-- NULL IS NOT NULL
345+isNotNull
346+ :: Expression schema relations grouping params ('Null ty)
347+ -- ^ possibly @NULL@
348+ -> Condition schema relations grouping params
349+isNotNull x = UnsafeExpression $ renderExpression x <+> "IS NOT NULL"
350+
351+-- | analagous to `maybe` using @IS NULL@
352+--
353+-- >>> printSQL $ matchNull true not_ null_
354+-- CASE WHEN NULL IS NULL THEN TRUE ELSE (NOT NULL) END
355+matchNull
356+ :: Expression schema relations grouping params (nullty)
357+ -- ^ what to convert @NULL@ to
358+ -> ( Expression schema relations grouping params ('NotNull ty)
359+ -> Expression schema relations grouping params (nullty) )
360+ -- ^ function to perform when @NULL@ is absent
361+ -> Expression schema relations grouping params ('Null ty)
362+ -> Expression schema relations grouping params (nullty)
363+matchNull y f x = ifThenElse (isNull x) y
364+ (f (UnsafeExpression (renderExpression x)))
365+
366+{-| right inverse to `fromNull`, if its arguments are equal then
367+`nullIf` gives @NULL@.
368+
369+>>> :set -XTypeApplications -XDataKinds
370+>>> let expr = nullIf false (param @1) :: Expression schema rels grp '[ 'NotNull 'PGbool] ('Null 'PGbool)
371+>>> printSQL expr
372+NULL IF (FALSE, ($1 :: bool))
373+-}
374+nullIf
375+ :: Expression schema relations grouping params ('NotNull ty)
376+ -- ^ @NULL@ is absent
377+ -> Expression schema relations grouping params ('NotNull ty)
378+ -- ^ @NULL@ is absent
379+ -> Expression schema relations grouping params ('Null ty)
380+nullIf x y = UnsafeExpression $ "NULL IF" <+> parenthesized
381+ (renderExpression x <> ", " <> renderExpression y)
382+
383+-- | >>> printSQL $ array [null_, notNull false, notNull true]
384+-- ARRAY[NULL, FALSE, TRUE]
385+array
386+ :: [Expression schema relations grouping params ('Null ty)]
387+ -- ^ array elements
388+ -> Expression schema relations grouping params (nullity ('PGvararray ty))
389+array xs = UnsafeExpression $
390+ "ARRAY[" <> commaSeparated (renderExpression <$> xs) <> "]"
310391
311392instance (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)
393+ (Expression schema relations grouping params (nullity ('PGenum labels))) where
394+ label = UnsafeExpression $ renderLabel (PGlabel @label)
395+
396+-- | A row constructor is an expression that builds a row value
397+-- (also called a composite value) using values for its member fields.
398+--
399+-- >>> type Complex = PGcomposite '["real" ::: 'PGfloat8, "imaginary" ::: 'PGfloat8]
400+-- >>> let i = row (0 `As` #real :* 1 `As` #imaginary :* Nil) :: Expression '[] '[] 'Ungrouped '[] ('NotNull Complex)
401+-- >>> printSQL i
402+-- ROW(0, 1)
403+row
404+ :: SListI (Nulls fields)
405+ => NP (Aliased (Expression schema relations grouping params)) (Nulls fields)
406+ -- ^ zero or more expressions for the row field values
407+ -> Expression schema relations grouping params (nullity ('PGcomposite fields))
408+row exprs = UnsafeExpression $ "ROW" <> parenthesized
409+ (renderCommaSeparated (\ (expr `As` _) -> renderExpression expr) exprs)
410+
411+instance Has field fields ty => IsLabel field
412+ ( Expression schema relation grouping params (nullity ('PGcomposite fields))
413+ -> Expression schema relation grouping params ('Null ty) ) where
414+ fromLabel expr = UnsafeExpression $
415+ parenthesized (renderExpression expr) <> "." <>
416+ fromString (symbolVal (Proxy @field))
417+
418+instance Semigroup
419+ (Expression schema relations grouping params (nullity ('PGvararray ty))) where
420+ (<>) = unsafeBinaryOp "||"
421+
422+instance Monoid
423+ (Expression schema relations grouping params (nullity ('PGvararray ty))) where
424+ mempty = array []
425+ mappend = (<>)
426+
427+-- | >>> let expr = greatest currentTimestamp [param @1] :: Expression sch rels grp '[ 'NotNull 'PGtimestamptz] ('NotNull 'PGtimestamptz)
428+-- >>> printSQL expr
429+-- GREATEST(CURRENT_TIMESTAMP, ($1 :: timestamp with time zone))
430+greatest
431+ :: Expression schema relations grouping params (nullty)
432+ -- ^ needs at least 1 argument
433+ -> [Expression schema relations grouping params (nullty)]
434+ -- ^ or more
435+ -> Expression schema relations grouping params (nullty)
436+greatest x xs = UnsafeExpression $ "GREATEST("
437+ <> commaSeparated (renderExpression <$> (x:xs)) <> ")"
438+
439+-- | >>> printSQL $ least currentTimestamp [null_]
440+-- LEAST(CURRENT_TIMESTAMP, NULL)
441+least
442+ :: Expression schema relations grouping params (nullty)
443+ -- ^ needs at least 1 argument
444+ -> [Expression schema relations grouping params (nullty)]
445+ -- ^ or more
446+ -> Expression schema relations grouping params (nullty)
447+least x xs = UnsafeExpression $ "LEAST("
448+ <> commaSeparated (renderExpression <$> (x:xs)) <> ")"
314449
315450-- | >>> printSQL $ unsafeBinaryOp "OR" true false
316451-- (TRUE OR FALSE)
317−unsafeBinaryOp :: ByteString -> Operator ty0 ty1 ty2
452+unsafeBinaryOp
453+ :: ByteString
454+ -- ^ operator
455+ -> Expression schema relations grouping params (ty0)
456+ -> Expression schema relations grouping params (ty1)
457+ -> Expression schema relations grouping params (ty2)
318458unsafeBinaryOp op x y = UnsafeExpression $ parenthesized $
319− renderSQL x <+> op <+> renderSQL y
320−
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
459+ renderExpression x <+> op <+> renderExpression y
327460
328−-- | >>> printSQL $ unsafeLeftOp "NOT" true
461+-- | >>> printSQL $ unsafeUnaryOp "NOT" true
329462-- (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
463+unsafeUnaryOp
464+ :: ByteString
465+ -- ^ operator
466+ -> Expression schema relations grouping params (ty0)
467+ -> Expression schema relations grouping params (ty1)
468+unsafeUnaryOp op x = UnsafeExpression $ parenthesized $
469+ op <+> renderExpression x
351470
352471-- | >>> printSQL $ unsafeFunction "f" true
353472-- f(TRUE)
354−unsafeFunction :: ByteString -> x --> y
473+unsafeFunction
474+ :: ByteString
475+ -- ^ function
476+ -> Expression schema relations grouping params (xty)
477+ -> Expression schema relations grouping params (yty)
355478unsafeFunction 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
479+ fun <> parenthesized (renderExpression x)
480+
481+unsafeVariadicFunction
482+ :: ByteString
483+ -- ^ function
484+ -> [Expression schema relations grouping params xty]
485+ -> Expression schema relations grouping params (yty)
486+unsafeVariadicFunction fun x = UnsafeExpression $
487+ fun <> parenthesized (commaSeparated (map renderExpression x))
488+
489+instance PGNum ty
490+ => Num (Expression schema relations grouping params (nullity ty)) where
380491 (+) = unsafeBinaryOp "+"
381492 (-) = unsafeBinaryOp "-"
382493 (*) = unsafeBinaryOp "*"
@@ -387,24 +498,19 @@ instance ty `In` PGNum
387498 . fromString
388499 . show
389500
390−instance (ty `In` PGNum, ty `In` PGFloating) => Fractional
391− (Expression outer commons grp schemas params from (null ty)) where
501+instance (PGNum ty, PGFloating ty) => Fractional
502+ (Expression schema relations grouping params (nullity ty)) where
392503 (/) = unsafeBinaryOp "/"
393− fromRational
394− = UnsafeExpression
395− . fromString
396− . ($ "")
397− . showFFloat Nothing
398− . fromRat @Double
504+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
399505
400−instance (ty `In` PGNum, ty `In` PGFloating) => Floating
401− (Expression outer commons grp schemas params from (null ty)) where
506+instance (PGNum ty, PGFloating ty) => Floating
507+ (Expression schema relations grouping params (nullity ty)) where
402508 pi = UnsafeExpression "pi()"
403509 exp = unsafeFunction "exp"
404510 log = unsafeFunction "ln"
405511 sqrt = unsafeFunction "sqrt"
406512 b ** x = UnsafeExpression $
407− "power(" <> renderSQL b <> ", " <> renderSQL x <> ")"
513+ "power(" <> renderExpression b <> ", " <> renderExpression x <> ")"
408514 logBase b y = log y / log b
409515 sin = unsafeFunction "sin"
410516 cos = unsafeFunction "cos"
@@ -419,54 +525,897 @@ instance (ty `In` PGNum, ty `In` PGFloating) => Floating
419525 acosh x = log (x + sqrt (x*x - 1))
420526 atanh x = log ((1 + x) / (1 - x)) / 2
421527
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)
528+-- | >>> :{
529+-- let
530+-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
531+-- expression = atan2_ pi 2
532+-- in printSQL expression
533+-- :}
534+-- atan2(pi(), 2)
535+atan2_
536+ :: PGFloating float
537+ => Expression schema relations grouping params (nullity float)
538+ -- ^ numerator
539+ -> Expression schema relations grouping params (nullity float)
540+ -- ^ denominator
541+ -> Expression schema relations grouping params (nullity float)
542+atan2_ y x = UnsafeExpression $
543+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
544+
545+-- When a `cast` is applied to an `Expression` of a known type, it
546+-- represents a run-time type conversion. The cast will succeed only if a
547+-- suitable type conversion operation has been defined.
548+--
549+-- | >>> printSQL $ true & cast int4
550+-- (TRUE :: int4)
551+cast
552+ :: TypeExpression schema ty1
553+ -- ^ type to cast as
554+ -> Expression schema relations grouping params (nullity ty0)
555+ -- ^ value to convert
556+ -> Expression schema relations grouping params (nullity ty1)
557+cast ty x = UnsafeExpression $ parenthesized $
558+ renderExpression x <+> "::" <+> renderTypeExpression ty
559+
560+-- | integer division, truncates the result
561+--
562+-- >>> :{
563+-- let
564+-- expression :: Expression schema relations grouping params (nullity 'PGint2)
565+-- expression = 5 `quot_` 2
566+-- in printSQL expression
567+-- :}
568+-- (5 / 2)
569+quot_
570+ :: PGIntegral int
571+ => Expression schema relations grouping params (nullity int)
572+ -- ^ numerator
573+ -> Expression schema relations grouping params (nullity int)
574+ -- ^ denominator
575+ -> Expression schema relations grouping params (nullity int)
576+quot_ = unsafeBinaryOp "/"
577+
578+-- | remainder upon integer division
579+--
580+-- >>> :{
581+-- let
582+-- expression :: Expression schema relations grouping params (nullity 'PGint2)
583+-- expression = 5 `rem_` 2
584+-- in printSQL expression
585+-- :}
586+-- (5 % 2)
587+rem_
588+ :: PGIntegral int
589+ => Expression schema relations grouping params (nullity int)
590+ -- ^ numerator
591+ -> Expression schema relations grouping params (nullity int)
592+ -- ^ denominator
593+ -> Expression schema relations grouping params (nullity int)
594+rem_ = unsafeBinaryOp "%"
595+
596+-- | >>> :{
597+-- let
598+-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
599+-- expression = trunc pi
600+-- in printSQL expression
601+-- :}
602+-- trunc(pi())
603+trunc
604+ :: PGFloating frac
605+ => Expression schema relations grouping params (nullity frac)
606+ -- ^ fractional number
607+ -> Expression schema relations grouping params (nullity frac)
608+trunc = unsafeFunction "trunc"
609+
610+-- | >>> :{
611+-- let
612+-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
613+-- expression = round_ pi
614+-- in printSQL expression
615+-- :}
616+-- round(pi())
617+round_
618+ :: PGFloating frac
619+ => Expression schema relations grouping params (nullity frac)
620+ -- ^ fractional number
621+ -> Expression schema relations grouping params (nullity frac)
622+round_ = unsafeFunction "round"
623+
624+-- | >>> :{
625+-- let
626+-- expression :: Expression schema relations grouping params (nullity 'PGfloat4)
627+-- expression = ceiling_ pi
628+-- in printSQL expression
629+-- :}
630+-- ceiling(pi())
631+ceiling_
632+ :: PGFloating frac
633+ => Expression schema relations grouping params (nullity frac)
634+ -- ^ fractional number
635+ -> Expression schema relations grouping params (nullity frac)
636+ceiling_ = unsafeFunction "ceiling"
637+
638+-- | A `Condition` is a boolean valued `Expression`. While SQL allows
639+-- conditions to have @NULL@, Squeal instead chooses to disallow @NULL@,
640+-- forcing one to handle the case of @NULL@ explicitly to produce
641+-- a `Condition`.
642+type Condition schema relations grouping params =
643+ Expression schema relations grouping params ('NotNull 'PGbool)
644+
645+-- | >>> printSQL true
646+-- TRUE
647+true :: Condition schema relations grouping params
648+true = UnsafeExpression "TRUE"
649+
650+-- | >>> printSQL false
651+-- FALSE
652+false :: Condition schema relations grouping params
653+false = UnsafeExpression "FALSE"
654+
655+-- | >>> printSQL $ not_ true
656+-- (NOT TRUE)
657+not_
658+ :: Condition schema relations grouping params
659+ -> Condition schema relations grouping params
660+not_ = unsafeUnaryOp "NOT"
661+
662+-- | >>> printSQL $ true .&& false
663+-- (TRUE AND FALSE)
664+(.&&)
665+ :: Condition schema relations grouping params
666+ -> Condition schema relations grouping params
667+ -> Condition schema relations grouping params
668+(.&&) = unsafeBinaryOp "AND"
669+
670+-- | >>> printSQL $ true .|| false
671+-- (TRUE OR FALSE)
672+(.||)
673+ :: Condition schema relations grouping params
674+ -> Condition schema relations grouping params
675+ -> Condition schema relations grouping params
676+(.||) = unsafeBinaryOp "OR"
677+
678+-- | >>> :{
679+-- let
680+-- expression :: Expression schema relations grouping params (nullity 'PGint2)
681+-- expression = caseWhenThenElse [(true, 1), (false, 2)] 3
682+-- in printSQL expression
683+-- :}
684+-- CASE WHEN TRUE THEN 1 WHEN FALSE THEN 2 ELSE 3 END
685+caseWhenThenElse
686+ :: [ ( Condition schema relations grouping params
687+ , Expression schema relations grouping params (ty)
688+ ) ]
689+ -- ^ whens and thens
690+ -> Expression schema relations grouping params (ty)
691+ -- ^ else
692+ -> Expression schema relations grouping params (ty)
693+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
694+ [ "CASE"
695+ , mconcat
696+ [ mconcat
697+ [ " WHEN ", renderExpression when_
698+ , " THEN ", renderExpression then_
699+ ]
700+ | (when_,then_) <- whenThens
701+ ]
702+ , " ELSE ", renderExpression else_
703+ , " END"
704+ ]
705+
706+-- | >>> :{
707+-- let
708+-- expression :: Expression schema relations grouping params (nullity 'PGint2)
709+-- expression = ifThenElse true 1 0
710+-- in printSQL expression
711+-- :}
712+-- CASE WHEN TRUE THEN 1 ELSE 0 END
713+ifThenElse
714+ :: Condition schema relations grouping params
715+ -> Expression schema relations grouping params (ty) -- ^ then
716+ -> Expression schema relations grouping params (ty) -- ^ else
717+ -> Expression schema relations grouping params (ty)
718+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
719+
720+-- | Comparison operations like `.==`, `./=`, `.>`, `.>=`, `.<` and `.<=`
721+-- will produce @NULL@s if one of their arguments is @NULL@.
722+--
723+-- >>> printSQL $ notNull true .== null_
724+-- (TRUE = NULL)
725+(.==)
726+ :: Expression schema relations grouping params (nullity ty) -- ^ lhs
727+ -> Expression schema relations grouping params (nullity ty) -- ^ rhs
728+ -> Expression schema relations grouping params (nullity 'PGbool)
729+(.==) = unsafeBinaryOp "="
730+infix 4 .==
731+
732+-- | >>> printSQL $ notNull true ./= null_
733+-- (TRUE <> NULL)
734+(./=)
735+ :: Expression schema relations grouping params (nullity ty) -- ^ lhs
736+ -> Expression schema relations grouping params (nullity ty) -- ^ rhs
737+ -> Expression schema relations grouping params (nullity 'PGbool)
738+(./=) = unsafeBinaryOp "<>"
739+infix 4 ./=
740+
741+-- | >>> printSQL $ notNull true .>= null_
742+-- (TRUE >= NULL)
743+(.>=)
744+ :: Expression schema relations grouping params (nullity ty) -- ^ lhs
745+ -> Expression schema relations grouping params (nullity ty) -- ^ rhs
746+ -> Expression schema relations grouping params (nullity 'PGbool)
747+(.>=) = unsafeBinaryOp ">="
748+infix 4 .>=
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 currentDate
778+-- CURRENT_DATE
779+currentDate
780+ :: Expression schema relations grouping params (nullity 'PGdate)
781+currentDate = UnsafeExpression "CURRENT_DATE"
782+
783+-- | >>> printSQL currentTime
784+-- CURRENT_TIME
785+currentTime
786+ :: Expression schema relations grouping params (nullity 'PGtimetz)
787+currentTime = UnsafeExpression "CURRENT_TIME"
788+
789+-- | >>> printSQL currentTimestamp
790+-- CURRENT_TIMESTAMP
791+currentTimestamp
792+ :: Expression schema relations grouping params (nullity 'PGtimestamptz)
793+currentTimestamp = UnsafeExpression "CURRENT_TIMESTAMP"
794+
795+-- | >>> printSQL localTime
796+-- LOCALTIME
797+localTime
798+ :: Expression schema relations grouping params (nullity 'PGtime)
799+localTime = UnsafeExpression "LOCALTIME"
800+
801+-- | >>> printSQL localTimestamp
802+-- LOCALTIMESTAMP
803+localTimestamp
804+ :: Expression schema relations grouping params (nullity 'PGtimestamp)
805+localTimestamp = UnsafeExpression "LOCALTIMESTAMP"
806+
807+{-----------------------------------------
808+text
809+-----------------------------------------}
438810
439811instance IsString
440− (Expression outer commons grp schemas params from (null 'PGtext)) where
812+ (Expression schema relations grouping params (nullity 'PGtext)) where
441813 fromString str = UnsafeExpression $
442814 "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) <> "\'"
815+ where
816+ escape = \case
817+ '\NUL' -> "\\0"
818+ '\'' -> "''"
819+ '"' -> "\\\""
820+ '\b' -> "\\b"
821+ '\n' -> "\\n"
822+ '\r' -> "\\r"
823+ '\t' -> "\\t"
824+ '\\' -> "\\\\"
825+ c -> [c]
451826
452827instance 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
828+ (Expression schema relations grouping params (nullity 'PGtext)) where
463829 (<>) = unsafeBinaryOp "||"
464830
465831instance 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
832+ (Expression schema relations grouping params (nullity 'PGtext)) where
471833 mempty = fromString ""
472834 mappend = (<>)
835+
836+-- | >>> printSQL $ lower "ARRRGGG"
837+-- lower(E'ARRRGGG')
838+lower
839+ :: Expression schema relations grouping params (nullity 'PGtext)
840+ -- ^ string to lower case
841+ -> Expression schema relations grouping params (nullity 'PGtext)
842+lower = unsafeFunction "lower"
843+
844+-- | >>> printSQL $ upper "eeee"
845+-- upper(E'eeee')
846+upper
847+ :: Expression schema relations grouping params (nullity 'PGtext)
848+ -- ^ string to upper case
849+ -> Expression schema relations grouping params (nullity 'PGtext)
850+upper = unsafeFunction "upper"
851+
852+-- | >>> printSQL $ charLength "four"
853+-- char_length(E'four')
854+charLength
855+ :: Expression schema relations grouping params (nullity 'PGtext)
856+ -- ^ string to measure
857+ -> Expression schema relations grouping params (nullity 'PGint4)
858+charLength = unsafeFunction "char_length"
859+
860+-- | The `like` expression returns true if the @string@ matches
861+-- the supplied @pattern@. If @pattern@ does not contain percent signs
862+-- or underscores, then the pattern only represents the string itself;
863+-- in that case `like` acts like the equals operator. An underscore (_)
864+-- in pattern stands for (matches) any single character; a percent sign (%)
865+-- matches any sequence of zero or more characters.
866+--
867+-- >>> printSQL $ "abc" `like` "a%"
868+-- (E'abc' LIKE E'a%')
869+like
870+ :: Expression schema relations grouping params (nullity 'PGtext)
871+ -- ^ string
872+ -> Expression schema relations grouping params (nullity 'PGtext)
873+ -- ^ pattern
874+ -> Expression schema relations grouping params (nullity 'PGbool)
875+like = unsafeBinaryOp "LIKE"
876+
877+{-----------------------------------------
878+json and jsonb operators
879+see https://www.postgresql.org/docs/10/static/functions-json.html
880+-----------------------------------------}
881+
882+type IsJSONKey key = key `In` '[ 'PGint2, 'PGint4, 'PGtext ]
883+type IsJSON json = json `In` '[ 'PGjson, 'PGjsonb ]
884+
885+class IsArray arr => IsArrayOf arr ty
886+instance arrTy ~ expectTy => IsArrayOf ('PGvararray arrTy) expectTy
887+instance arrTy ~ expectTy => IsArrayOf ('PGfixarray n arrTy) expectTy
888+
889+-- IsArray will generate nicer error messages when it is all that is required.
890+class IsArray arr
891+instance IsArray ('PGvararray x)
892+instance IsArray ('PGfixarray n x)
893+
894+type IsPGtextArray arr = IsArrayOf arr 'PGtext
895+
896+-- | Get JSON value (object field or array element) at a key.
897+(.->)
898+ :: (IsJSON json, IsJSONKey key)
899+ => Expression schema relations grouping params (jnull json)
900+ -> Expression schema relations grouping params (knull key)
901+ -> Expression schema relations grouping params ('Null json)
902+(.->) = unsafeBinaryOp "->"
903+
904+-- | Get JSON value (object field or array element) at a key, as text.
905+(.->>)
906+ :: (IsJSON json, IsJSONKey key)
907+ => Expression schema relations grouping params (jnull json)
908+ -> Expression schema relations grouping params (knull key)
909+ -> Expression schema relations grouping params ('Null 'PGtext)
910+(.->>) = unsafeBinaryOp "->>"
911+
912+-- | Get JSON value at a specified path.
913+(.#>)
914+ :: (IsJSON json, IsPGtextArray path)
915+ => Expression schema relations grouping params (jnull json)
916+ -> Expression schema relations grouping params (knull path)
917+ -> Expression schema relations grouping params ('Null json)
918+(.#>) = unsafeBinaryOp "#>"
919+
920+-- | Get JSON value at a specified path as text.
921+(.#>>)
922+ :: (IsJSON json, IsPGtextArray path)
923+ => Expression schema relations grouping params (jnull json)
924+ -> Expression schema relations grouping params (knull path)
925+ -> Expression schema relations grouping params ('Null 'PGtext)
926+(.#>>) = unsafeBinaryOp "#>"
927+
928+-- Additional jsonb operators
929+
930+-- | Or operator for nulls; if either argument is null, then the result is null.
931+type family Null2 a b where
932+ Null2 'Null a = 'Null
933+ Null2 a 'Null = 'Null
934+ Null2 'NotNull 'NotNull = 'NotNull
935+
936+-- | See 'Null2'
937+type family NullOfN xs where
938+ NullOfN ('Null ty:xs) = 'Null
939+ NullOfN (x:xs) = NullOfN xs
940+ NullOfN '[] = 'NotNull
941+
942+-- | Does the left JSON value contain the right JSON path/value entries at the
943+-- top level?
944+(.@>)
945+ :: Expression schema relations grouping params (null0 'PGjsonb)
946+ -> Expression schema relations grouping params (null1 'PGjsonb)
947+ -> Expression schema relations grouping params (Null2 null0 null1 'PGbool)
948+(.@>) = unsafeBinaryOp "@>"
949+
950+-- | Are the left JSON path/value entries contained at the top level within the
951+-- right JSON value?
952+(.<@)
953+ :: Expression schema relations grouping params (null0 'PGjsonb)
954+ -> Expression schema relations grouping params (null1 'PGjsonb)
955+ -> Expression schema relations grouping params (Null2 null0 null1 'PGbool)
956+(.<@) = unsafeBinaryOp "<@"
957+
958+-- | Does the string exist as a top-level key within the JSON value?
959+(.?)
960+ :: Expression schema relations grouping params (null0 'PGjsonb)
961+ -> Expression schema relations grouping params (null1 'PGtext)
962+ -> Expression schema relations grouping params (Null2 null0 null1 'PGbool)
963+(.?) = unsafeBinaryOp "?"
964+
965+-- | Do any of these array strings exist as top-level keys?
966+(.?|)
967+ :: Expression schema relations grouping params (null0 'PGjsonb)
968+ -> Expression schema relations grouping params (null1 ('PGvararray 'PGtext))
969+ -> Expression schema relations grouping params (Null2 null0 null1 'PGbool)
970+(.?|) = unsafeBinaryOp "?|"
971+
972+-- | Do all of these array strings exist as top-level keys?
973+(.?&)
974+ :: Expression schema relations grouping params (null0 'PGjsonb)
975+ -> Expression schema relations grouping params (null1 ('PGvararray 'PGtext))
976+ -> Expression schema relations grouping params (Null2 null0 null1 'PGbool)
977+(.?&) = unsafeBinaryOp "?&"
978+
979+-- | Concatenate two jsonb values into a new jsonb value
980+(.||.)
981+ :: Expression schema relations grouping params (null0 'PGjsonb)
982+ -> Expression schema relations grouping params (null1 'PGjsonb)
983+ -> Expression schema relations grouping params (Null2 null0 null1 'PGjsonb)
984+(.||.) = unsafeBinaryOp "||"
985+
986+-- | Delete a key or keys from a JSON object, or remove an array element.
987+--
988+-- If the right operand is..
989+--
990+-- @ text @: Delete key/value pair or string element from left operand. Key/value pairs
991+-- are matched based on their key value.
992+--
993+-- @ text[] @: Delete multiple key/value pairs or string elements
994+-- from left operand. Key/value pairs are matched based on their key value.
995+--
996+-- @ integer @: Delete the array element with specified index (Negative integers
997+-- count from the end). Throws an error if top level container is not an array.
998+(.-.)
999+ :: (key `In` '[ 'PGtext, 'PGvararray 'PGtext, 'PGint4, 'PGint2 ]) -- hlint error without parens here
1000+ => Expression schema relations grouping params (null0 'PGjsonb)
1001+ -> Expression schema relations grouping params (null1 key)
1002+ -> Expression schema relations grouping params (Null2 null0 null1 'PGjsonb)
1003+(.-.) = unsafeBinaryOp "-"
1004+
1005+-- | Delete the field or element with specified path (for JSON arrays, negative
1006+-- integers count from the end)
1007+(#-.)
1008+ :: IsPGtextArray arrayty
1009+ => Expression schema relations grouping params (null0 'PGjsonb)
1010+ -> Expression schema relations grouping params (null1 arrayty)
1011+ -> Expression schema relations grouping params (Null2 null0 null1 'PGjsonb)
1012+(#-.) = unsafeBinaryOp "#-"
1013+
1014+toJson
1015+ :: Expression schema relations grouping params (nullity ty)
1016+ -> Expression schema relations grouping params (nullity 'PGjson)
1017+toJson = unsafeFunction "to_json"
1018+
1019+toJsonb
1020+ :: Expression schema relations grouping params (nullity ty)
1021+ -> Expression schema relations grouping params (nullity 'PGjsonb)
1022+toJsonb = unsafeUnaryOp "to_jsonb"
1023+
1024+arrayToJson
1025+ :: IsArray arr
1026+ => Expression schema relations grouping params (nullity arr)
1027+ -> Expression schema relations grouping params (nullity 'PGjson)
1028+arrayToJson = unsafeFunction "array_to_json"
1029+
1030+rowToJson
1031+ :: Expression schema relations grouping params (nullity ('PGcomposite ty))
1032+ -> Expression schema relations grouping params (nullity 'PGjson)
1033+rowToJson = unsafeFunction "row_to_json"
1034+
1035+-- TODO: jsonBuildArray, jsonbBuildArray, jsonObject, jsonbObject
1036+-- probably something like
1037+-- :: NP (Expression s r g p) xs -> Expression s r g p ('NotNull 'PGjson[b])
1038+
1039+{-----------------------------------------
1040+aggregation
1041+-----------------------------------------}
1042+
1043+-- | escape hatch to define aggregate functions
1044+unsafeAggregate
1045+ :: ByteString -- ^ aggregate function
1046+ -> Expression schema relations 'Ungrouped params (xty)
1047+ -> Expression schema relations ('Grouped bys) params (yty)
1048+unsafeAggregate fun x = UnsafeExpression $ mconcat
1049+ [fun, "(", renderExpression x, ")"]
1050+
1051+-- | escape hatch to define aggregate functions over distinct values
1052+unsafeAggregateDistinct
1053+ :: ByteString -- ^ aggregate function
1054+ -> Expression schema relations 'Ungrouped params (xty)
1055+ -> Expression schema relations ('Grouped bys) params (yty)
1056+unsafeAggregateDistinct fun x = UnsafeExpression $ mconcat
1057+ [fun, "(DISTINCT ", renderExpression x, ")"]
1058+
1059+-- | >>> :{
1060+-- let
1061+-- expression :: Expression schema '[tab ::: '["col" ::: 'Null 'PGnumeric]] ('Grouped bys) params ('Null 'PGnumeric)
1062+-- expression = sum_ #col
1063+-- in printSQL expression
1064+-- :}
1065+-- sum("col")
1066+sum_
1067+ :: PGNum ty
1068+ => Expression schema relations 'Ungrouped params (nullity ty)
1069+ -- ^ what to sum
1070+ -> Expression schema relations ('Grouped bys) params (nullity ty)
1071+sum_ = unsafeAggregate "sum"
1072+
1073+-- | >>> :{
1074+-- let
1075+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGnumeric]] ('Grouped bys) params (nullity 'PGnumeric)
1076+-- expression = sumDistinct #col
1077+-- in printSQL expression
1078+-- :}
1079+-- sum(DISTINCT "col")
1080+sumDistinct
1081+ :: PGNum ty
1082+ => Expression schema relations 'Ungrouped params (nullity ty)
1083+ -- ^ what to sum
1084+ -> Expression schema relations ('Grouped bys) params (nullity ty)
1085+sumDistinct = unsafeAggregateDistinct "sum"
1086+
1087+-- | A constraint for `PGType`s that you can take averages of and the resulting
1088+-- `PGType`.
1089+class PGAvg ty avg | ty -> avg where
1090+ avg, avgDistinct
1091+ :: Expression schema relations 'Ungrouped params (nullity ty)
1092+ -- ^ what to average
1093+ -> Expression schema relations ('Grouped bys) params (nullity avg)
1094+ avg = unsafeAggregate "avg"
1095+ avgDistinct = unsafeAggregateDistinct "avg"
1096+instance PGAvg 'PGint2 'PGnumeric
1097+instance PGAvg 'PGint4 'PGnumeric
1098+instance PGAvg 'PGint8 'PGnumeric
1099+instance PGAvg 'PGnumeric 'PGnumeric
1100+instance PGAvg 'PGfloat4 'PGfloat8
1101+instance PGAvg 'PGfloat8 'PGfloat8
1102+instance PGAvg 'PGinterval 'PGinterval
1103+
1104+-- | >>> :{
1105+-- let
1106+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1107+-- expression = bitAnd #col
1108+-- in printSQL expression
1109+-- :}
1110+-- bit_and("col")
1111+bitAnd
1112+ :: PGIntegral int
1113+ => Expression schema relations 'Ungrouped params (nullity int)
1114+ -- ^ what to aggregate
1115+ -> Expression schema relations ('Grouped bys) params (nullity int)
1116+bitAnd = unsafeAggregate "bit_and"
1117+
1118+-- | >>> :{
1119+-- let
1120+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1121+-- expression = bitOr #col
1122+-- in printSQL expression
1123+-- :}
1124+-- bit_or("col")
1125+bitOr
1126+ :: PGIntegral int
1127+ => Expression schema relations 'Ungrouped params (nullity int)
1128+ -- ^ what to aggregate
1129+ -> Expression schema relations ('Grouped bys) params (nullity int)
1130+bitOr = unsafeAggregate "bit_or"
1131+
1132+-- | >>> :{
1133+-- let
1134+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1135+-- expression = bitAndDistinct #col
1136+-- in printSQL expression
1137+-- :}
1138+-- bit_and(DISTINCT "col")
1139+bitAndDistinct
1140+ :: PGIntegral int
1141+ => Expression schema relations 'Ungrouped params (nullity int)
1142+ -- ^ what to aggregate
1143+ -> Expression schema relations ('Grouped bys) params (nullity int)
1144+bitAndDistinct = unsafeAggregateDistinct "bit_and"
1145+
1146+-- | >>> :{
1147+-- let
1148+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1149+-- expression = bitOrDistinct #col
1150+-- in printSQL expression
1151+-- :}
1152+-- bit_or(DISTINCT "col")
1153+bitOrDistinct
1154+ :: PGIntegral int
1155+ => Expression schema relations 'Ungrouped params (nullity int)
1156+ -- ^ what to aggregate
1157+ -> Expression schema relations ('Grouped bys) params (nullity int)
1158+bitOrDistinct = unsafeAggregateDistinct "bit_or"
1159+
1160+-- | >>> :{
1161+-- let
1162+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1163+-- expression = boolAnd #col
1164+-- in printSQL expression
1165+-- :}
1166+-- bool_and("col")
1167+boolAnd
1168+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1169+ -- ^ what to aggregate
1170+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1171+boolAnd = unsafeAggregate "bool_and"
1172+
1173+-- | >>> :{
1174+-- let
1175+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1176+-- expression = boolOr #col
1177+-- in printSQL expression
1178+-- :}
1179+-- bool_or("col")
1180+boolOr
1181+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1182+ -- ^ what to aggregate
1183+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1184+boolOr = unsafeAggregate "bool_or"
1185+
1186+-- | >>> :{
1187+-- let
1188+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1189+-- expression = boolAndDistinct #col
1190+-- in printSQL expression
1191+-- :}
1192+-- bool_and(DISTINCT "col")
1193+boolAndDistinct
1194+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1195+ -- ^ what to aggregate
1196+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1197+boolAndDistinct = unsafeAggregateDistinct "bool_and"
1198+
1199+-- | >>> :{
1200+-- let
1201+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1202+-- expression = boolOrDistinct #col
1203+-- in printSQL expression
1204+-- :}
1205+-- bool_or(DISTINCT "col")
1206+boolOrDistinct
1207+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1208+ -- ^ what to aggregate
1209+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1210+boolOrDistinct = unsafeAggregateDistinct "bool_or"
1211+
1212+-- | A special aggregation that does not require an input
1213+--
1214+-- >>> printSQL countStar
1215+-- count(*)
1216+countStar
1217+ :: Expression schema relations ('Grouped bys) params ('NotNull 'PGint8)
1218+countStar = UnsafeExpression $ "count(*)"
1219+
1220+-- | >>> :{
1221+-- let
1222+-- expression :: Expression schema '[tab ::: '["col" ::: nullity ty]] (Grouped bys) params ('NotNull 'PGint8)
1223+-- expression = count #col
1224+-- in printSQL expression
1225+-- :}
1226+-- count("col")
1227+count
1228+ :: Expression schema relations 'Ungrouped params ty
1229+ -- ^ what to count
1230+ -> Expression schema relations ('Grouped bys) params ('NotNull 'PGint8)
1231+count = unsafeAggregate "count"
1232+
1233+-- | >>> :{
1234+-- let
1235+-- expression :: Expression schema '[tab ::: '["col" ::: nullity ty]] (Grouped bys) params ('NotNull 'PGint8)
1236+-- expression = countDistinct #col
1237+-- in printSQL expression
1238+-- :}
1239+-- count(DISTINCT "col")
1240+countDistinct
1241+ :: Expression schema relations 'Ungrouped params ty
1242+ -- ^ what to count
1243+ -> Expression schema relations ('Grouped bys) params ('NotNull 'PGint8)
1244+countDistinct = unsafeAggregateDistinct "count"
1245+
1246+-- | synonym for `boolAnd`
1247+--
1248+-- >>> :{
1249+-- let
1250+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1251+-- expression = every #col
1252+-- in printSQL expression
1253+-- :}
1254+-- every("col")
1255+every
1256+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1257+ -- ^ what to aggregate
1258+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1259+every = unsafeAggregate "every"
1260+
1261+-- | synonym for `boolAndDistinct`
1262+--
1263+-- >>> :{
1264+-- let
1265+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1266+-- expression = everyDistinct #col
1267+-- in printSQL expression
1268+-- :}
1269+-- every(DISTINCT "col")
1270+everyDistinct
1271+ :: Expression schema relations 'Ungrouped params (nullity 'PGbool)
1272+ -- ^ what to aggregate
1273+ -> Expression schema relations ('Grouped bys) params (nullity 'PGbool)
1274+everyDistinct = unsafeAggregateDistinct "every"
1275+
1276+-- | minimum and maximum aggregation
1277+max_, min_, maxDistinct, minDistinct
1278+ :: Expression schema relations 'Ungrouped params (nullity ty)
1279+ -- ^ what to aggregate
1280+ -> Expression schema relations ('Grouped bys) params (nullity ty)
1281+max_ = unsafeAggregate "max"
1282+min_ = unsafeAggregate "min"
1283+maxDistinct = unsafeAggregateDistinct "max"
1284+minDistinct = unsafeAggregateDistinct "min"
1285+
1286+{-----------------------------------------
1287+type expressions
1288+-----------------------------------------}
1289+
1290+-- | `TypeExpression`s are used in `cast`s and `createTable` commands.
1291+newtype TypeExpression (schema :: SchemaType) (ty :: PGType)
1292+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
1293+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
1294+
1295+instance (Has alias schema ('Typedef ty))
1296+ => IsLabel alias (TypeExpression schema ty) where
1297+ fromLabel = UnsafeTypeExpression (renderAlias (fromLabel @alias))
1298+
1299+-- | logical Boolean (true/false)
1300+bool :: TypeExpression schema 'PGbool
1301+bool = UnsafeTypeExpression "bool"
1302+-- | signed two-byte integer
1303+int2, smallint :: TypeExpression schema 'PGint2
1304+int2 = UnsafeTypeExpression "int2"
1305+smallint = UnsafeTypeExpression "smallint"
1306+-- | signed four-byte integer
1307+int4, int, integer :: TypeExpression schema 'PGint4
1308+int4 = UnsafeTypeExpression "int4"
1309+int = UnsafeTypeExpression "int"
1310+integer = UnsafeTypeExpression "integer"
1311+-- | signed eight-byte integer
1312+int8, bigint :: TypeExpression schema 'PGint8
1313+int8 = UnsafeTypeExpression "int8"
1314+bigint = UnsafeTypeExpression "bigint"
1315+-- | arbitrary precision numeric type
1316+numeric :: TypeExpression schema 'PGnumeric
1317+numeric = UnsafeTypeExpression "numeric"
1318+-- | single precision floating-point number (4 bytes)
1319+float4, real :: TypeExpression schema 'PGfloat4
1320+float4 = UnsafeTypeExpression "float4"
1321+real = UnsafeTypeExpression "real"
1322+-- | double precision floating-point number (8 bytes)
1323+float8, doublePrecision :: TypeExpression schema 'PGfloat8
1324+float8 = UnsafeTypeExpression "float8"
1325+doublePrecision = UnsafeTypeExpression "double precision"
1326+-- | variable-length character string
1327+text :: TypeExpression schema 'PGtext
1328+text = UnsafeTypeExpression "text"
1329+-- | fixed-length character string
1330+char, character
1331+ :: (KnownNat n, 1 <= n)
1332+ => proxy n
1333+ -> TypeExpression schema ('PGchar n)
1334+char p = UnsafeTypeExpression $ "char(" <> renderNat p <> ")"
1335+character p = UnsafeTypeExpression $ "character(" <> renderNat p <> ")"
1336+-- | variable-length character string
1337+varchar, characterVarying
1338+ :: (KnownNat n, 1 <= n)
1339+ => proxy n
1340+ -> TypeExpression schema ('PGvarchar n)
1341+varchar p = UnsafeTypeExpression $ "varchar(" <> renderNat p <> ")"
1342+characterVarying p = UnsafeTypeExpression $
1343+ "character varying(" <> renderNat p <> ")"
1344+-- | binary data ("byte array")
1345+bytea :: TypeExpression schema 'PGbytea
1346+bytea = UnsafeTypeExpression "bytea"
1347+-- | date and time (no time zone)
1348+timestamp :: TypeExpression schema 'PGtimestamp
1349+timestamp = UnsafeTypeExpression "timestamp"
1350+-- | date and time, including time zone
1351+timestampWithTimeZone :: TypeExpression schema 'PGtimestamptz
1352+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
1353+-- | calendar date (year, month, day)
1354+date :: TypeExpression schema 'PGdate
1355+date = UnsafeTypeExpression "date"
1356+-- | time of day (no time zone)
1357+time :: TypeExpression schema 'PGtime
1358+time = UnsafeTypeExpression "time"
1359+-- | time of day, including time zone
1360+timeWithTimeZone :: TypeExpression schema 'PGtimetz
1361+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
1362+-- | time span
1363+interval :: TypeExpression schema 'PGinterval
1364+interval = UnsafeTypeExpression "interval"
1365+-- | universally unique identifier
1366+uuid :: TypeExpression schema 'PGuuid
1367+uuid = UnsafeTypeExpression "uuid"
1368+-- | IPv4 or IPv6 host address
1369+inet :: TypeExpression schema 'PGinet
1370+inet = UnsafeTypeExpression "inet"
1371+-- | textual JSON data
1372+json :: TypeExpression schema 'PGjson
1373+json = UnsafeTypeExpression "json"
1374+-- | binary JSON data, decomposed
1375+jsonb :: TypeExpression schema 'PGjsonb
1376+jsonb = UnsafeTypeExpression "jsonb"
1377+-- | variable length array
1378+vararray
1379+ :: TypeExpression schema pg
1380+ -> TypeExpression schema ('PGvararray pg)
1381+vararray ty = UnsafeTypeExpression $ renderTypeExpression ty <> "[]"
1382+-- | fixed length array
1383+--
1384+-- >>> renderTypeExpression (fixarray (Proxy @2) json)
1385+-- "json[2]"
1386+fixarray
1387+ :: KnownNat n
1388+ => proxy n
1389+ -> TypeExpression schema pg
1390+ -> TypeExpression schema ('PGfixarray n pg)
1391+fixarray p ty = UnsafeTypeExpression $
1392+ renderTypeExpression ty <> "[" <> renderNat p <> "]"
1393+
1394+-- | `pgtype` is a demoted version of a `PGType`
1395+class PGTyped schema (ty :: PGType) where pgtype :: TypeExpression schema ty
1396+instance PGTyped schema 'PGbool where pgtype = bool
1397+instance PGTyped schema 'PGint2 where pgtype = int2
1398+instance PGTyped schema 'PGint4 where pgtype = int4
1399+instance PGTyped schema 'PGint8 where pgtype = int8
1400+instance PGTyped schema 'PGnumeric where pgtype = numeric
1401+instance PGTyped schema 'PGfloat4 where pgtype = float4
1402+instance PGTyped schema 'PGfloat8 where pgtype = float8
1403+instance PGTyped schema 'PGtext where pgtype = text
1404+instance (KnownNat n, 1 <= n)
1405+ => PGTyped schema ('PGchar n) where pgtype = char (Proxy @n)
1406+instance (KnownNat n, 1 <= n)
1407+ => PGTyped schema ('PGvarchar n) where pgtype = varchar (Proxy @n)
1408+instance PGTyped schema 'PGbytea where pgtype = bytea
1409+instance PGTyped schema 'PGtimestamp where pgtype = timestamp
1410+instance PGTyped schema 'PGtimestamptz where pgtype = timestampWithTimeZone
1411+instance PGTyped schema 'PGdate where pgtype = date
1412+instance PGTyped schema 'PGtime where pgtype = time
1413+instance PGTyped schema 'PGtimetz where pgtype = timeWithTimeZone
1414+instance PGTyped schema 'PGinterval where pgtype = interval
1415+instance PGTyped schema 'PGuuid where pgtype = uuid
1416+instance PGTyped schema 'PGjson where pgtype = json
1417+instance PGTyped schema 'PGjsonb where pgtype = jsonb
1418+instance PGTyped schema ty => PGTyped schema ('PGvararray ty) where
1419+ pgtype = vararray (pgtype @schema @ty)
1420+instance (KnownNat n, PGTyped schema ty) => PGTyped schema ('PGfixarray n ty) where
1421+ pgtype = fixarray (Proxy @n) (pgtype @schema @ty)