@@ -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,545 @@ 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+ , index
49+ , row
50+ , field
51+ -- ** Functions
4752 , unsafeBinaryOp
48− , binaryOp
49− , FunctionVar
50− , unsafeFunctionVar
51− , type (--->)
52− , FunctionNDB
53− , unsafeFunctionN
54− , functionN
55− , PGSubset (..)
56− , PGIntersect (..)
53+ , unsafeUnaryOp
54+ , unsafeFunction
55+ , unsafeVariadicFunction
56+ , atan2_
57+ , cast
58+ , quot_
59+ , rem_
60+ , trunc
61+ , round_
62+ , ceiling_
63+ , greatest
64+ , least
65+ -- ** Conditions
66+ , true
67+ , false
68+ , not_
69+ , (.&&)
70+ , (.||)
71+ , Condition
72+ , caseWhenThenElse
73+ , ifThenElse
74+ , (.==)
75+ , (./=)
76+ , (.>=)
77+ , (.<)
78+ , (.<=)
79+ , (.>)
80+ -- ** Time
81+ , currentDate
82+ , currentTime
83+ , currentTimestamp
84+ , localTime
85+ , localTimestamp
86+ -- ** Text
87+ , lower
88+ , upper
89+ , charLength
90+ , like
91+ -- ** Json
92+ -- *** Json and Jsonb operators
93+ , (.->)
94+ , (.->>)
95+ , (.#>)
96+ , (.#>>)
97+ -- *** Jsonb operators
98+ , (.@>)
99+ , (.<@)
100+ , (.?)
101+ , (.?|)
102+ , (.?&)
103+ , (.-.)
104+ , (#-.)
105+ -- *** functions
106+ , jsonLit
107+ , jsonbLit
108+ , toJson
109+ , toJsonb
110+ , arrayToJson
111+ , rowToJson
112+ , jsonBuildArray
113+ , jsonbBuildArray
114+ , jsonBuildObject
115+ , jsonbBuildObject
116+ , jsonObject
117+ , jsonbObject
118+ , jsonZipObject
119+ , jsonbZipObject
120+ , jsonArrayLength
121+ , jsonbArrayLength
122+ , jsonExtractPath
123+ , jsonbExtractPath
124+ , jsonExtractPathAsText
125+ , jsonbExtractPathAsText
126+ , jsonObjectKeys
127+ , jsonbObjectKeys
128+ , jsonTypeof
129+ , jsonbTypeof
130+ , jsonStripNulls
131+ , jsonbStripNulls
132+ , jsonbSet
133+ , jsonbInsert
134+ , jsonbPretty
135+ -- ** Aggregation
136+ , unsafeAggregate, unsafeAggregateDistinct
137+ , sum_, sumDistinct
138+ , PGAvg (avg, avgDistinct)
139+ , bitAnd, bitOr, boolAnd, boolOr
140+ , bitAndDistinct, bitOrDistinct, boolAndDistinct, boolOrDistinct
141+ , countStar
142+ , count, countDistinct
143+ , every, everyDistinct
144+ , max_, maxDistinct, min_, minDistinct
145+ -- * Types
146+ , TypeExpression (UnsafeTypeExpression, renderTypeExpression)
147+ , PGTyped (pgtype)
148+ , typedef
149+ , typetable
150+ , typeview
151+ , bool
152+ , int2
153+ , smallint
154+ , int4
155+ , int
156+ , integer
157+ , int8
158+ , bigint
159+ , numeric
160+ , float4
161+ , real
162+ , float8
163+ , doublePrecision
164+ , text
165+ , char
166+ , character
167+ , varchar
168+ , characterVarying
169+ , bytea
170+ , timestamp
171+ , timestampWithTimeZone
172+ , date
173+ , time
174+ , timeWithTimeZone
175+ , interval
176+ , uuid
177+ , inet
178+ , json
179+ , jsonb
180+ , vararray
181+ , fixarray
57182 -- * Re-export
58183 , (&)
59− , K (..)
60− , unK
184+ , NP ((:*), Nil)
61185 ) where
62186
63187import Control.Category
64188import Control.DeepSeq
65189import Data.ByteString (ByteString)
190+import Data.ByteString.Lazy (toStrict)
66191import Data.Function ((&))
67192import Data.Semigroup hiding (All)
193+import qualified Data.Aeson as JSON
194+import Data.Ratio
68195import Data.String
69−import Generics.SOP hiding (All, from)
196+import Data.Word
197+import Generics.SOP hiding (from)
70198import GHC.OverloadedLabels
71199import GHC.TypeLits
72−import Numeric
73200import Prelude hiding (id, (.))
74201
75202import qualified GHC.Generics as GHC
76203
77−import Squeal.PostgreSQL.Alias
78−import Squeal.PostgreSQL.List
79204import Squeal.PostgreSQL.Render
80205import Squeal.PostgreSQL.Schema
81206
82−-- $setup
83−-- >>> import Squeal.PostgreSQL
84−
85207{-----------------------------------------
86208column expressions
87209-----------------------------------------}
88210
89211{- | `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,
212+such as in the target list of the `Squeal.PostgreSQL.Query.select` command,
92213as new column values in `Squeal.PostgreSQL.Manipulation.insertRow` or
93214`Squeal.PostgreSQL.Manipulation.update`,
94−or in search `Squeal.PostgreSQL.Logic.Condition`s in a number of commands.
215+or in search `Condition`s in a number of commands.
95216
96217The expression syntax allows the calculation of
97218values from primitive expression using arithmetic, logical,
98219and 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`.
113220-}
114221newtype Expression
115− (outer :: FromType)
116− (commons :: FromType)
117− (grp :: Grouping)
118− (schemas :: SchemasType)
119− (params :: [NullityType])
222+ (schema :: SchemaType)
120223 (from :: FromType)
224+ (grouping :: Grouping)
225+ (params :: [NullityType])
121226 (ty :: NullityType)
122227 = UnsafeExpression { renderExpression :: ByteString }
123228 deriving (GHC.Generic,Show,Eq,Ord,NFData)
124−instance RenderSQL (Expression outer commons grp schemas params from ty) where
229+
230+instance RenderSQL (Expression schema from grouping params ty) where
125231 renderSQL = renderExpression
126232
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)
181−-}
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)
233+{- | A `HasParameter` constraint is used to indicate a value that is
234+supplied externally to a SQL statement.
235+`Squeal.PostgreSQL.PQ.manipulateParams`,
236+`Squeal.PostgreSQL.PQ.queryParams` and
237+`Squeal.PostgreSQL.PQ.traversePrepared` support specifying data values
238+separately from the SQL command string, in which case `param`s are used to
239+refer to the out-of-line data values.
210240-}
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
241+class KnownNat n => HasParameter
242+ (n :: Nat)
243+ (schema :: SchemaType)
244+ (params :: [NullityType])
245+ (ty :: NullityType)
246+ | n params -> ty where
247+ -- | `parameter` takes a `Nat` using type application and a `TypeExpression`.
248+ --
249+ -- >>> let expr = parameter @1 int4 :: Expression sch rels grp '[ 'Null 'PGint4] ('Null 'PGint4)
250+ -- >>> printSQL expr
251+ -- ($1 :: int4)
252+ parameter
253+ :: TypeExpression schema ty
254+ -> Expression schema from grouping params ty
255+ parameter ty = UnsafeExpression $ parenthesized $
256+ "$" <> renderNat @n <+> "::"
257+ <+> renderTypeExpression ty
258+instance {-# OVERLAPPING #-} HasParameter 1 schema (ty1:tys) ty1
259+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) schema params ty)
260+ => HasParameter n schema (ty' : params) ty
261+
262+-- | `param` takes a `Nat` using type application and for basic types,
263+-- infers a `TypeExpression`.
264+--
265+-- >>> let expr = param @1 :: Expression sch rels grp '[ 'Null 'PGint4] ('Null 'PGint4)
266+-- >>> printSQL expr
267+-- ($1 :: int4)
268+param
269+ :: forall n schema params from grouping ty
270+ . (PGTyped schema ty, HasParameter n schema params ty)
271+ => Expression schema from grouping params ty -- ^ param
272+param = parameter @n pgtype
273+
274+instance (HasUnique table from columns, Has column columns ty)
275+ => IsLabel column (Expression schema from 'Ungrouped params ty) where
276+ fromLabel = UnsafeExpression $ renderAlias (Alias @column)
277+instance (HasUnique table from columns, Has column columns ty)
278+ => IsLabel column
279+ (Aliased (Expression schema from 'Ungrouped params) (column ::: ty)) where
280+ fromLabel = fromLabel @column `As` Alias @column
281+instance (HasUnique table from columns, Has column columns ty)
282+ => IsLabel column
283+ (NP (Aliased (Expression schema from 'Ungrouped params)) '[column ::: ty]) where
284+ fromLabel = fromLabel @column :* Nil
285+
286+instance (Has table from columns, Has column columns ty)
287+ => IsQualified table column (Expression schema from 'Ungrouped params ty) where
288+ table ! column = UnsafeExpression $
289+ renderAlias table <> "." <> renderAlias column
290+instance (Has table from columns, Has column columns ty)
291+ => IsQualified table column
292+ (Aliased (Expression schema from 'Ungrouped params) (column ::: ty)) where
293+ table ! column = table ! column `As` column
294+instance (Has table from columns, Has column columns ty)
295+ => IsQualified table column
296+ (NP (Aliased (Expression schema from 'Ungrouped params)) '[column ::: ty]) where
297+ table ! column = table ! column :* Nil
245298
246299instance
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
300+ ( HasUnique table from columns
301+ , Has column columns ty
302+ , GroupedBy table column bys
303+ ) => IsLabel column
304+ (Expression schema from ('Grouped bys) params ty) where
305+ fromLabel = UnsafeExpression $ renderAlias (Alias @column)
261306instance
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
307+ ( HasUnique table from columns
308+ , Has column columns ty
309+ , GroupedBy table column bys
310+ ) => IsLabel column
311+ ( Aliased (Expression schema from ('Grouped bys) params)
312+ (column ::: ty) ) where
313+ fromLabel = fromLabel @column `As` Alias @column
269314instance
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
315+ ( HasUnique table from columns
316+ , Has column columns ty
317+ , GroupedBy table column bys
318+ ) => IsLabel column
319+ ( NP (Aliased (Expression schema from ('Grouped bys) params))
320+ '[column ::: ty] ) where
321+ fromLabel = fromLabel @column :* Nil
277322
278323instance
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
324+ ( Has table from columns
325+ , Has column columns ty
326+ , GroupedBy table column bys
327+ ) => IsQualified table column
328+ (Expression schema from ('Grouped bys) params ty) where
329+ table ! column = UnsafeExpression $
330+ renderAlias table <> "." <> renderAlias column
294331instance
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
332+ ( Has table from columns
333+ , Has column columns ty
334+ , GroupedBy table column bys
335+ ) => IsQualified table column
336+ (Aliased (Expression schema from ('Grouped bys) params)
337+ (column ::: ty)) where
338+ table ! column = table ! column `As` column
302339instance
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
340+ ( Has table from columns
341+ , Has column columns ty
342+ , GroupedBy table column bys
343+ ) => IsQualified table column
344+ ( NP (Aliased (Expression schema from ('Grouped bys) params))
345+ '[column ::: ty]) where
346+ table ! column = table ! column :* Nil
347+
348+-- | analagous to `Nothing`
349+--
350+-- >>> printSQL null_
351+-- NULL
352+null_ :: Expression schema rels grouping params ('Null ty)
353+null_ = UnsafeExpression "NULL"
354+
355+-- | analagous to `Just`
356+--
357+-- >>> printSQL $ notNull true
358+-- TRUE
359+notNull
360+ :: Expression schema rels grouping params ('NotNull ty)
361+ -> Expression schema rels grouping params ('Null ty)
362+notNull = UnsafeExpression . renderExpression
363+
364+-- | return the leftmost value which is not NULL
365+--
366+-- >>> printSQL $ coalesce [null_, true] false
367+-- COALESCE(NULL, TRUE, FALSE)
368+coalesce
369+ :: [Expression schema from grouping params ('Null ty)]
370+ -- ^ @NULL@s may be present
371+ -> Expression schema from grouping params ('NotNull ty)
372+ -- ^ @NULL@ is absent
373+ -> Expression schema from grouping params ('NotNull ty)
374+coalesce nullxs notNullx = UnsafeExpression $
375+ "COALESCE" <> parenthesized (commaSeparated
376+ ((renderExpression <$> nullxs) <> [renderExpression notNullx]))
377+
378+-- | analagous to `Data.Maybe.fromMaybe` using @COALESCE@
379+--
380+-- >>> printSQL $ fromNull true null_
381+-- COALESCE(NULL, TRUE)
382+fromNull
383+ :: Expression schema from grouping params ('NotNull ty)
384+ -- ^ what to convert @NULL@ to
385+ -> Expression schema from grouping params ('Null ty)
386+ -> Expression schema from grouping params ('NotNull ty)
387+fromNull notNullx nullx = coalesce [nullx] notNullx
388+
389+-- | >>> printSQL $ null_ & isNull
390+-- NULL IS NULL
391+isNull
392+ :: Expression schema from grouping params ('Null ty)
393+ -- ^ possibly @NULL@
394+ -> Condition schema from grouping params
395+isNull x = UnsafeExpression $ renderExpression x <+> "IS NULL"
396+
397+-- | >>> printSQL $ null_ & isNotNull
398+-- NULL IS NOT NULL
399+isNotNull
400+ :: Expression schema from grouping params ('Null ty)
401+ -- ^ possibly @NULL@
402+ -> Condition schema from grouping params
403+isNotNull x = UnsafeExpression $ renderExpression x <+> "IS NOT NULL"
404+
405+-- | analagous to `maybe` using @IS NULL@
406+--
407+-- >>> printSQL $ matchNull true not_ null_
408+-- CASE WHEN NULL IS NULL THEN TRUE ELSE (NOT NULL) END
409+matchNull
410+ :: Expression schema from grouping params (nullty)
411+ -- ^ what to convert @NULL@ to
412+ -> ( Expression schema from grouping params ('NotNull ty)
413+ -> Expression schema from grouping params (nullty) )
414+ -- ^ function to perform when @NULL@ is absent
415+ -> Expression schema from grouping params ('Null ty)
416+ -> Expression schema from grouping params (nullty)
417+matchNull y f x = ifThenElse (isNull x) y
418+ (f (UnsafeExpression (renderExpression x)))
419+
420+{-| right inverse to `fromNull`, if its arguments are equal then
421+`nullIf` gives @NULL@.
422+
423+>>> :set -XTypeApplications -XDataKinds
424+>>> let expr = nullIf false (param @1) :: Expression schema rels grp '[ 'NotNull 'PGbool] ('Null 'PGbool)
425+>>> printSQL expr
426+NULL IF (FALSE, ($1 :: bool))
427+-}
428+nullIf
429+ :: Expression schema from grouping params ('NotNull ty)
430+ -- ^ @NULL@ is absent
431+ -> Expression schema from grouping params ('NotNull ty)
432+ -- ^ @NULL@ is absent
433+ -> Expression schema from grouping params ('Null ty)
434+nullIf x y = UnsafeExpression $ "NULL IF" <+> parenthesized
435+ (renderExpression x <> ", " <> renderExpression y)
436+
437+-- | >>> printSQL $ array [null_, false, true]
438+-- ARRAY[NULL, FALSE, TRUE]
439+array
440+ :: [Expression schema from grouping params ty]
441+ -- ^ array elements
442+ -> Expression schema from grouping params (nullity ('PGvararray ty))
443+array xs = UnsafeExpression $
444+ "ARRAY[" <> commaSeparated (renderExpression <$> xs) <> "]"
445+
446+-- | >>> printSQL $ array [null_, false, true] & index 2
447+-- (ARRAY[NULL, FALSE, TRUE])[2]
448+index
449+ :: Word64 -- ^ index
450+ -> Expression schema from grouping params (nullity ('PGvararray ty)) -- ^ array
451+ -> Expression schema from grouping params (NullifyType ty)
452+index n expr = UnsafeExpression $
453+ parenthesized (renderExpression expr) <> "[" <> fromString (show n) <> "]"
310454
311455instance (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)
456+ (Expression schema from grouping params (nullity ('PGenum labels))) where
457+ label = UnsafeExpression $ renderLabel (PGlabel @label)
458+
459+-- | A row constructor is an expression that builds a row value
460+-- (also called a composite value) using values for its member fields.
461+--
462+-- >>> :{
463+-- type Complex = 'PGcomposite
464+-- '[ "real" ::: 'NotNull 'PGfloat8
465+-- , "imaginary" ::: 'NotNull 'PGfloat8 ]
466+-- :}
467+--
468+-- >>> let i = row (0 `as` #real :* 1 `as` #imaginary) :: Expression '[] '[] 'Ungrouped '[] ('NotNull Complex)
469+-- >>> printSQL i
470+-- ROW(0, 1)
471+row
472+ :: SListI row
473+ => NP (Aliased (Expression schema from grouping params)) row
474+ -- ^ zero or more expressions for the row field values
475+ -> Expression schema from grouping params (nullity ('PGcomposite row))
476+row exprs = UnsafeExpression $ "ROW" <> parenthesized
477+ (renderCommaSeparated (\ (expr `As` _) -> renderExpression expr) exprs)
478+
479+-- | >>> :{
480+-- type Complex = 'PGcomposite
481+-- '[ "real" ::: 'NotNull 'PGfloat8
482+-- , "imaginary" ::: 'NotNull 'PGfloat8 ]
483+-- :}
484+--
485+-- >>> let i = row (0 `as` #real :* 1 `as` #imaginary) :: Expression '["complex" ::: 'Typedef Complex] '[] 'Ungrouped '[] ('NotNull Complex)
486+-- >>> printSQL $ i & field #complex #imaginary
487+-- (ROW(0, 1)::"complex")."imaginary"
488+field
489+ :: (Has tydef schema ('Typedef ('PGcomposite row)), Has field row ty)
490+ => Alias tydef -- ^ row type
491+ -> Alias field -- ^ field name
492+ -> Expression schema from grouping params ('NotNull ('PGcomposite row))
493+ -> Expression schema from grouping params ty
494+field td fld expr = UnsafeExpression $
495+ parenthesized (renderExpression expr <> "::" <> renderAlias td)
496+ <> "." <> renderAlias fld
497+
498+instance Semigroup
499+ (Expression schema from grouping params (nullity ('PGvararray ty))) where
500+ (<>) = unsafeBinaryOp "||"
501+
502+instance Monoid
503+ (Expression schema from grouping params (nullity ('PGvararray ty))) where
504+ mempty = array []
505+ mappend = (<>)
506+
507+-- | >>> let expr = greatest currentTimestamp [param @1] :: Expression sch rels grp '[ 'NotNull 'PGtimestamptz] ('NotNull 'PGtimestamptz)
508+-- >>> printSQL expr
509+-- GREATEST(CURRENT_TIMESTAMP, ($1 :: timestamp with time zone))
510+greatest
511+ :: Expression schema from grouping params (nullty)
512+ -- ^ needs at least 1 argument
513+ -> [Expression schema from grouping params (nullty)]
514+ -- ^ or more
515+ -> Expression schema from grouping params (nullty)
516+greatest x xs = UnsafeExpression $ "GREATEST("
517+ <> commaSeparated (renderExpression <$> (x:xs)) <> ")"
518+
519+-- | >>> printSQL $ least currentTimestamp [null_]
520+-- LEAST(CURRENT_TIMESTAMP, NULL)
521+least
522+ :: Expression schema from grouping params (nullty)
523+ -- ^ needs at least 1 argument
524+ -> [Expression schema from grouping params (nullty)]
525+ -- ^ or more
526+ -> Expression schema from grouping params (nullty)
527+least x xs = UnsafeExpression $ "LEAST("
528+ <> commaSeparated (renderExpression <$> (x:xs)) <> ")"
314529
315530-- | >>> printSQL $ unsafeBinaryOp "OR" true false
316531-- (TRUE OR FALSE)
317−unsafeBinaryOp :: ByteString -> Operator ty0 ty1 ty2
532+unsafeBinaryOp
533+ :: ByteString
534+ -- ^ operator
535+ -> Expression schema from grouping params (ty0)
536+ -> Expression schema from grouping params (ty1)
537+ -> Expression schema from grouping params (ty2)
318538unsafeBinaryOp 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
539+ renderExpression x <+> op <+> renderExpression y
327540
328−-- | >>> printSQL $ unsafeLeftOp "NOT" true
541+-- | >>> printSQL $ unsafeUnaryOp "NOT" true
329542-- (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
543+unsafeUnaryOp
544+ :: ByteString
545+ -- ^ operator
546+ -> Expression schema from grouping params (ty0)
547+ -> Expression schema from grouping params (ty1)
548+unsafeUnaryOp op x = UnsafeExpression $ parenthesized $
549+ op <+> renderExpression x
351550
352551-- | >>> printSQL $ unsafeFunction "f" true
353552-- f(TRUE)
354−unsafeFunction :: ByteString -> x --> y
553+unsafeFunction
554+ :: ByteString
555+ -- ^ function
556+ -> Expression schema from grouping params (xty)
557+ -> Expression schema from grouping params (yty)
355558unsafeFunction 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
559+ fun <> parenthesized (renderExpression x)
560+
561+-- | Helper for defining variadic functions.
562+unsafeVariadicFunction
563+ :: SListI elems
564+ => ByteString
565+ -- ^ function
566+ -> NP (Expression schema from grouping params) elems
567+ -> Expression schema from grouping params ret
568+unsafeVariadicFunction fun x = UnsafeExpression $
569+ fun <> parenthesized (commaSeparated (hcollapse (hmap (K . renderExpression) x)))
377570
378571instance ty `In` PGNum
379− => Num (Expression outer commons grp schemas params from (null ty)) where
572+ => Num (Expression schema from grouping params (nullity ty)) where
380573 (+) = unsafeBinaryOp "+"
381574 (-) = unsafeBinaryOp "-"
382575 (*) = unsafeBinaryOp "*"
@@ -388,23 +581,18 @@ instance ty `In` PGNum
388581 . show
389582
390583instance (ty `In` PGNum, ty `In` PGFloating) => Fractional
391− (Expression outer commons grp schemas params from (null ty)) where
584+ (Expression schema from grouping params (nullity ty)) where
392585 (/) = unsafeBinaryOp "/"
393− fromRational
394− = UnsafeExpression
395− . fromString
396− . ($ "")
397− . showFFloat Nothing
398− . fromRat @Double
586+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
399587
400588instance (ty `In` PGNum, ty `In` PGFloating) => Floating
401− (Expression outer commons grp schemas params from (null ty)) where
589+ (Expression schema from grouping params (nullity ty)) where
402590 pi = UnsafeExpression "pi()"
403591 exp = unsafeFunction "exp"
404592 log = unsafeFunction "ln"
405593 sqrt = unsafeFunction "sqrt"
406594 b ** x = UnsafeExpression $
407− "power(" <> renderSQL b <> ", " <> renderSQL x <> ")"
595+ "power(" <> renderExpression b <> ", " <> renderExpression x <> ")"
408596 logBase b y = log y / log b
409597 sin = unsafeFunction "sin"
410598 cos = unsafeFunction "cos"
@@ -419,54 +607,1166 @@ instance (ty `In` PGNum, ty `In` PGFloating) => Floating
419607 acosh x = log (x + sqrt (x*x - 1))
420608 atanh x = log ((1 + x) / (1 - x)) / 2
421609
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)
610+-- | >>> :{
611+-- let
612+-- expression :: Expression schema from grouping params (nullity 'PGfloat4)
613+-- expression = atan2_ pi 2
614+-- in printSQL expression
615+-- :}
616+-- atan2(pi(), 2)
617+atan2_
618+ :: float `In` PGFloating
619+ => Expression schema from grouping params (nullity float)
620+ -- ^ numerator
621+ -> Expression schema from grouping params (nullity float)
622+ -- ^ denominator
623+ -> Expression schema from grouping params (nullity float)
624+atan2_ y x = UnsafeExpression $
625+ "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
626+
627+-- When a `cast` is applied to an `Expression` of a known type, it
628+-- represents a run-time type conversion. The cast will succeed only if a
629+-- suitable type conversion operation has been defined.
630+--
631+-- | >>> printSQL $ true & cast int4
632+-- (TRUE :: int4)
633+cast
634+ :: TypeExpression schema ty1
635+ -- ^ type to cast as
636+ -> Expression schema from grouping params ty0
637+ -- ^ value to convert
638+ -> Expression schema from grouping params ty1
639+cast ty x = UnsafeExpression $ parenthesized $
640+ renderExpression x <+> "::" <+> renderTypeExpression ty
641+
642+-- | integer division, truncates the result
643+--
644+-- >>> :{
645+-- let
646+-- expression :: Expression schema from grouping params (nullity 'PGint2)
647+-- expression = 5 `quot_` 2
648+-- in printSQL expression
649+-- :}
650+-- (5 / 2)
651+quot_
652+ :: int `In` PGIntegral
653+ => Expression schema from grouping params (nullity int)
654+ -- ^ numerator
655+ -> Expression schema from grouping params (nullity int)
656+ -- ^ denominator
657+ -> Expression schema from grouping params (nullity int)
658+quot_ = unsafeBinaryOp "/"
659+
660+-- | remainder upon integer division
661+--
662+-- >>> :{
663+-- let
664+-- expression :: Expression schema from grouping params (nullity 'PGint2)
665+-- expression = 5 `rem_` 2
666+-- in printSQL expression
667+-- :}
668+-- (5 % 2)
669+rem_
670+ :: int `In` PGIntegral
671+ => Expression schema from grouping params (nullity int)
672+ -- ^ numerator
673+ -> Expression schema from grouping params (nullity int)
674+ -- ^ denominator
675+ -> Expression schema from grouping params (nullity int)
676+rem_ = unsafeBinaryOp "%"
677+
678+-- | >>> :{
679+-- let
680+-- expression :: Expression schema from grouping params (nullity 'PGfloat4)
681+-- expression = trunc pi
682+-- in printSQL expression
683+-- :}
684+-- trunc(pi())
685+trunc
686+ :: frac `In` PGFloating
687+ => Expression schema from grouping params (nullity frac)
688+ -- ^ fractional number
689+ -> Expression schema from grouping params (nullity frac)
690+trunc = unsafeFunction "trunc"
691+
692+-- | >>> :{
693+-- let
694+-- expression :: Expression schema from grouping params (nullity 'PGfloat4)
695+-- expression = round_ pi
696+-- in printSQL expression
697+-- :}
698+-- round(pi())
699+round_
700+ :: frac `In` PGFloating
701+ => Expression schema from grouping params (nullity frac)
702+ -- ^ fractional number
703+ -> Expression schema from grouping params (nullity frac)
704+round_ = unsafeFunction "round"
705+
706+-- | >>> :{
707+-- let
708+-- expression :: Expression schema from grouping params (nullity 'PGfloat4)
709+-- expression = ceiling_ pi
710+-- in printSQL expression
711+-- :}
712+-- ceiling(pi())
713+ceiling_
714+ :: frac `In` PGFloating
715+ => Expression schema from grouping params (nullity frac)
716+ -- ^ fractional number
717+ -> Expression schema from grouping params (nullity frac)
718+ceiling_ = unsafeFunction "ceiling"
719+
720+-- | A `Condition` is an `Expression`, which can evaluate
721+-- to `true`, `false` or `null_`. This is because SQL uses
722+-- a three valued logic.
723+type Condition schema from grouping params =
724+ Expression schema from grouping params ('Null 'PGbool)
725+
726+-- | >>> printSQL true
727+-- TRUE
728+true :: Expression schema from grouping params (nullity 'PGbool)
729+true = UnsafeExpression "TRUE"
730+
731+-- | >>> printSQL false
732+-- FALSE
733+false :: Expression schema from grouping params (nullity 'PGbool)
734+false = UnsafeExpression "FALSE"
735+
736+-- | >>> printSQL $ not_ true
737+-- (NOT TRUE)
738+not_
739+ :: Expression schema from grouping params (nullity 'PGbool)
740+ -> Expression schema from grouping params (nullity 'PGbool)
741+not_ = unsafeUnaryOp "NOT"
742+
743+-- | >>> printSQL $ true .&& false
744+-- (TRUE AND FALSE)
745+(.&&)
746+ :: Expression schema from grouping params (nullity 'PGbool)
747+ -> Expression schema from grouping params (nullity 'PGbool)
748+ -> Expression schema from grouping params (nullity 'PGbool)
749+infixr 3 .&&
750+(.&&) = unsafeBinaryOp "AND"
751+
752+-- | >>> printSQL $ true .|| false
753+-- (TRUE OR FALSE)
754+(.||)
755+ :: Expression schema from grouping params (nullity 'PGbool)
756+ -> Expression schema from grouping params (nullity 'PGbool)
757+ -> Expression schema from grouping params (nullity 'PGbool)
758+infixr 2 .||
759+(.||) = unsafeBinaryOp "OR"
760+
761+-- | >>> :{
762+-- let
763+-- expression :: Expression schema from grouping params (nullity 'PGint2)
764+-- expression = caseWhenThenElse [(true, 1), (false, 2)] 3
765+-- in printSQL expression
766+-- :}
767+-- CASE WHEN TRUE THEN 1 WHEN FALSE THEN 2 ELSE 3 END
768+caseWhenThenElse
769+ :: [ ( Condition schema from grouping params
770+ , Expression schema from grouping params ty
771+ ) ]
772+ -- ^ whens and thens
773+ -> Expression schema from grouping params ty
774+ -- ^ else
775+ -> Expression schema from grouping params ty
776+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
777+ [ "CASE"
778+ , mconcat
779+ [ mconcat
780+ [ " WHEN ", renderExpression when_
781+ , " THEN ", renderExpression then_
782+ ]
783+ | (when_,then_) <- whenThens
784+ ]
785+ , " ELSE ", renderExpression else_
786+ , " END"
787+ ]
788+
789+-- | >>> :{
790+-- let
791+-- expression :: Expression schema from grouping params (nullity 'PGint2)
792+-- expression = ifThenElse true 1 0
793+-- in printSQL expression
794+-- :}
795+-- CASE WHEN TRUE THEN 1 ELSE 0 END
796+ifThenElse
797+ :: Condition schema from grouping params
798+ -> Expression schema from grouping params ty -- ^ then
799+ -> Expression schema from grouping params ty -- ^ else
800+ -> Expression schema from grouping params ty
801+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
802+
803+-- | Comparison operations like `.==`, `./=`, `.>`, `.>=`, `.<` and `.<=`
804+-- will produce @NULL@s if one of their arguments is @NULL@.
805+--
806+-- >>> printSQL $ true .== null_
807+-- (TRUE = NULL)
808+(.==)
809+ :: Expression schema from grouping params (nullity0 ty) -- ^ lhs
810+ -> Expression schema from grouping params (nullity1 ty) -- ^ rhs
811+ -> Condition schema from grouping params
812+(.==) = unsafeBinaryOp "="
813+infix 4 .==
814+
815+-- | >>> printSQL $ true ./= null_
816+-- (TRUE <> NULL)
817+(./=)
818+ :: Expression schema from grouping params (nullity0 ty) -- ^ lhs
819+ -> Expression schema from grouping params (nullity1 ty) -- ^ rhs
820+ -> Condition schema from grouping params
821+(./=) = unsafeBinaryOp "<>"
822+infix 4 ./=
823+
824+-- | >>> printSQL $ true .>= null_
825+-- (TRUE >= NULL)
826+(.>=)
827+ :: Expression schema from grouping params (nullity0 ty) -- ^ lhs
828+ -> Expression schema from grouping params (nullity1 ty) -- ^ rhs
829+ -> Condition schema from grouping params
830+(.>=) = unsafeBinaryOp ">="
831+infix 4 .>=
832+
833+-- | >>> printSQL $ true .< null_
834+-- (TRUE < NULL)
835+(.<)
836+ :: Expression schema from grouping params (nullity0 ty) -- ^ lhs
837+ -> Expression schema from grouping params (nullity1 ty) -- ^ rhs
838+ -> Condition schema from grouping params
839+(.<) = unsafeBinaryOp "<"
840+infix 4 .<
841+
842+-- | >>> printSQL $ true .<= null_
843+-- (TRUE <= NULL)
844+(.<=)
845+ :: Expression schema from grouping params (nullity0 ty) -- ^ lhs
846+ -> Expression schema from grouping params (nullity1 ty) -- ^ rhs
847+ -> Condition schema from grouping params
848+(.<=) = unsafeBinaryOp "<="
849+infix 4 .<=
850+
851+-- | >>> printSQL $ true .> null_
852+-- (TRUE > NULL)
853+(.>)
854+ :: Expression schema from grouping params (nullity0 ty) -- ^ lhs
855+ -> Expression schema from grouping params (nullity1 ty) -- ^ rhs
856+ -> Condition schema from grouping params
857+(.>) = unsafeBinaryOp ">"
858+infix 4 .>
859+
860+-- | >>> printSQL currentDate
861+-- CURRENT_DATE
862+currentDate
863+ :: Expression schema from grouping params (nullity 'PGdate)
864+currentDate = UnsafeExpression "CURRENT_DATE"
865+
866+-- | >>> printSQL currentTime
867+-- CURRENT_TIME
868+currentTime
869+ :: Expression schema from grouping params (nullity 'PGtimetz)
870+currentTime = UnsafeExpression "CURRENT_TIME"
871+
872+-- | >>> printSQL currentTimestamp
873+-- CURRENT_TIMESTAMP
874+currentTimestamp
875+ :: Expression schema from grouping params (nullity 'PGtimestamptz)
876+currentTimestamp = UnsafeExpression "CURRENT_TIMESTAMP"
877+
878+-- | >>> printSQL localTime
879+-- LOCALTIME
880+localTime
881+ :: Expression schema from grouping params (nullity 'PGtime)
882+localTime = UnsafeExpression "LOCALTIME"
883+
884+-- | >>> printSQL localTimestamp
885+-- LOCALTIMESTAMP
886+localTimestamp
887+ :: Expression schema from grouping params (nullity 'PGtimestamp)
888+localTimestamp = UnsafeExpression "LOCALTIMESTAMP"
889+
890+{-----------------------------------------
891+text
892+-----------------------------------------}
438893
439894instance IsString
440− (Expression outer commons grp schemas params from (null 'PGtext)) where
895+ (Expression schema from grouping params (nullity 'PGtext)) where
441896 fromString str = UnsafeExpression $
442897 "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) <> "\'"
898+ where
899+ escape = \case
900+ '\NUL' -> "\\0"
901+ '\'' -> "''"
902+ '"' -> "\\\""
903+ '\b' -> "\\b"
904+ '\n' -> "\\n"
905+ '\r' -> "\\r"
906+ '\t' -> "\\t"
907+ '\\' -> "\\\\"
908+ c -> [c]
451909
452910instance 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
911+ (Expression schema from grouping params (nullity 'PGtext)) where
463912 (<>) = unsafeBinaryOp "||"
464913
465914instance 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
915+ (Expression schema from grouping params (nullity 'PGtext)) where
471916 mempty = fromString ""
472917 mappend = (<>)
918+
919+-- | >>> printSQL $ lower "ARRRGGG"
920+-- lower(E'ARRRGGG')
921+lower
922+ :: Expression schema from grouping params (nullity 'PGtext)
923+ -- ^ string to lower case
924+ -> Expression schema from grouping params (nullity 'PGtext)
925+lower = unsafeFunction "lower"
926+
927+-- | >>> printSQL $ upper "eeee"
928+-- upper(E'eeee')
929+upper
930+ :: Expression schema from grouping params (nullity 'PGtext)
931+ -- ^ string to upper case
932+ -> Expression schema from grouping params (nullity 'PGtext)
933+upper = unsafeFunction "upper"
934+
935+-- | >>> printSQL $ charLength "four"
936+-- char_length(E'four')
937+charLength
938+ :: Expression schema from grouping params (nullity 'PGtext)
939+ -- ^ string to measure
940+ -> Expression schema from grouping params (nullity 'PGint4)
941+charLength = unsafeFunction "char_length"
942+
943+-- | The `like` expression returns true if the @string@ matches
944+-- the supplied @pattern@. If @pattern@ does not contain percent signs
945+-- or underscores, then the pattern only represents the string itself;
946+-- in that case `like` acts like the equals operator. An underscore (_)
947+-- in pattern stands for (matches) any single character; a percent sign (%)
948+-- matches any sequence of zero or more characters.
949+--
950+-- >>> printSQL $ "abc" `like` "a%"
951+-- (E'abc' LIKE E'a%')
952+like
953+ :: Expression schema from grouping params (nullity 'PGtext)
954+ -- ^ string
955+ -> Expression schema from grouping params (nullity 'PGtext)
956+ -- ^ pattern
957+ -> Expression schema from grouping params (nullity 'PGbool)
958+like = unsafeBinaryOp "LIKE"
959+
960+{-----------------------------------------
961+ -- json and jsonb support
962+
963+See https://www.postgresql.org/docs/10/static/functions-json.html -- most
964+comments lifted directly from this page.
965+
966+Table 9.44: json and jsonb operators
967+-----------------------------------------}
968+
969+-- | Get JSON value (object field or array element) at a key.
970+(.->)
971+ :: (json `In` PGJsonType, key `In` PGJsonKey)
972+ => Expression schema from grouping params (nullity json)
973+ -> Expression schema from grouping params (nullity key)
974+ -> Expression schema from grouping params ('Null json)
975+infixl 8 .->
976+(.->) = unsafeBinaryOp "->"
977+
978+-- | Get JSON value (object field or array element) at a key, as text.
979+(.->>)
980+ :: (json `In` PGJsonType, key `In` PGJsonKey)
981+ => Expression schema from grouping params (nullity json)
982+ -> Expression schema from grouping params (nullity key)
983+ -> Expression schema from grouping params ('Null 'PGtext)
984+infixl 8 .->>
985+(.->>) = unsafeBinaryOp "->>"
986+
987+-- | Get JSON value at a specified path.
988+(.#>)
989+ :: (json `In` PGJsonType, PGTextArray "(.#>)" path)
990+ => Expression schema from grouping params (nullity json)
991+ -> Expression schema from grouping params (nullity path)
992+ -> Expression schema from grouping params ('Null json)
993+infixl 8 .#>
994+(.#>) = unsafeBinaryOp "#>"
995+
996+-- | Get JSON value at a specified path as text.
997+(.#>>)
998+ :: (json `In` PGJsonType, PGTextArray "(.#>>)" path)
999+ => Expression schema from grouping params (nullity json)
1000+ -> Expression schema from grouping params (nullity path)
1001+ -> Expression schema from grouping params ('Null 'PGtext)
1002+infixl 8 .#>>
1003+(.#>>) = unsafeBinaryOp "#>>"
1004+
1005+-- Additional jsonb operators
1006+
1007+-- | Does the left JSON value contain the right JSON path/value entries at the
1008+-- top level?
1009+(.@>)
1010+ :: Expression schema from grouping params (nullity 'PGjsonb)
1011+ -> Expression schema from grouping params (nullity 'PGjsonb)
1012+ -> Condition schema from grouping params
1013+infixl 9 .@>
1014+(.@>) = unsafeBinaryOp "@>"
1015+
1016+-- | Are the left JSON path/value entries contained at the top level within the
1017+-- right JSON value?
1018+(.<@)
1019+ :: Expression schema from grouping params (nullity 'PGjsonb)
1020+ -> Expression schema from grouping params (nullity 'PGjsonb)
1021+ -> Condition schema from grouping params
1022+infixl 9 .<@
1023+(.<@) = unsafeBinaryOp "<@"
1024+
1025+-- | Does the string exist as a top-level key within the JSON value?
1026+(.?)
1027+ :: Expression schema from grouping params (nullity 'PGjsonb)
1028+ -> Expression schema from grouping params (nullity 'PGtext)
1029+ -> Condition schema from grouping params
1030+infixl 9 .?
1031+(.?) = unsafeBinaryOp "?"
1032+
1033+-- | Do any of these array strings exist as top-level keys?
1034+(.?|)
1035+ :: Expression schema from grouping params (nullity 'PGjsonb)
1036+ -> Expression schema from grouping params (nullity ('PGvararray ('NotNull 'PGtext)))
1037+ -> Condition schema from grouping params
1038+infixl 9 .?|
1039+(.?|) = unsafeBinaryOp "?|"
1040+
1041+-- | Do all of these array strings exist as top-level keys?
1042+(.?&)
1043+ :: Expression schema from grouping params (nullity 'PGjsonb)
1044+ -> Expression schema from grouping params (nullity ('PGvararray ('NotNull 'PGtext)))
1045+ -> Condition schema from grouping params
1046+infixl 9 .?&
1047+(.?&) = unsafeBinaryOp "?&"
1048+
1049+-- | Concatenate two jsonb values into a new jsonb value.
1050+instance
1051+ Semigroup (Expression schema from grouping param (nullity 'PGjsonb)) where
1052+ (<>) = unsafeBinaryOp "||"
1053+
1054+-- | Delete a key or keys from a JSON object, or remove an array element.
1055+--
1056+-- If the right operand is..
1057+--
1058+-- @ text @: Delete key/value pair or string element from left operand. Key/value pairs
1059+-- are matched based on their key value.
1060+--
1061+-- @ text[] @: Delete multiple key/value pairs or string elements
1062+-- from left operand. Key/value pairs are matched based on their key value.
1063+--
1064+-- @ integer @: Delete the array element with specified index (Negative integers
1065+-- count from the end). Throws an error if top level container is not an array.
1066+(.-.)
1067+ :: (key `In` '[ 'PGtext, 'PGvararray ('NotNull 'PGtext), 'PGint4, 'PGint2 ]) -- hlint error without parens here
1068+ => Expression schema from grouping params (nullity 'PGjsonb)
1069+ -> Expression schema from grouping params (nullity key)
1070+ -> Expression schema from grouping params (nullity 'PGjsonb)
1071+infixl 6 .-.
1072+(.-.) = unsafeBinaryOp "-"
1073+
1074+-- | Delete the field or element with specified path (for JSON arrays, negative
1075+-- integers count from the end)
1076+(#-.)
1077+ :: PGTextArray "(#-.)" arrayty
1078+ => Expression schema from grouping params (nullity 'PGjsonb)
1079+ -> Expression schema from grouping params (nullity arrayty)
1080+ -> Expression schema from grouping params (nullity 'PGjsonb)
1081+infixl 6 #-.
1082+(#-.) = unsafeBinaryOp "#-"
1083+
1084+{-----------------------------------------
1085+Table 9.45: JSON creation functions
1086+-----------------------------------------}
1087+
1088+-- | Literal binary JSON
1089+jsonbLit
1090+ :: JSON.ToJSON x
1091+ => x -> Expression schema from grouping params (nullity 'PGjsonb)
1092+jsonbLit = cast jsonb . UnsafeExpression
1093+ . singleQuotedUtf8 . toStrict . JSON.encode
1094+
1095+-- | Literal JSON
1096+jsonLit
1097+ :: JSON.ToJSON x
1098+ => x -> Expression schema from grouping params (nullity 'PGjson)
1099+jsonLit = cast json . UnsafeExpression
1100+ . singleQuotedUtf8 . toStrict . JSON.encode
1101+
1102+-- | Returns the value as json. Arrays and composites are converted
1103+-- (recursively) to arrays and objects; otherwise, if there is a cast from the
1104+-- type to json, the cast function will be used to perform the conversion;
1105+-- otherwise, a scalar value is produced. For any scalar type other than a
1106+-- number, a Boolean, or a null value, the text representation will be used, in
1107+-- such a fashion that it is a valid json value.
1108+toJson
1109+ :: Expression schema from grouping params (nullity ty)
1110+ -> Expression schema from grouping params (nullity 'PGjson)
1111+toJson = unsafeFunction "to_json"
1112+
1113+-- | Returns the value as jsonb. Arrays and composites are converted
1114+-- (recursively) to arrays and objects; otherwise, if there is a cast from the
1115+-- type to json, the cast function will be used to perform the conversion;
1116+-- otherwise, a scalar value is produced. For any scalar type other than a
1117+-- number, a Boolean, or a null value, the text representation will be used, in
1118+-- such a fashion that it is a valid jsonb value.
1119+toJsonb
1120+ :: Expression schema from grouping params (nullity ty)
1121+ -> Expression schema from grouping params (nullity 'PGjsonb)
1122+toJsonb = unsafeFunction "to_jsonb"
1123+
1124+-- | Returns the array as a JSON array. A PostgreSQL multidimensional array
1125+-- becomes a JSON array of arrays.
1126+arrayToJson
1127+ :: PGArray "arrayToJson" arr
1128+ => Expression schema from grouping params (nullity arr)
1129+ -> Expression schema from grouping params (nullity 'PGjson)
1130+arrayToJson = unsafeFunction "array_to_json"
1131+
1132+-- | Returns the row as a JSON object.
1133+rowToJson
1134+ :: Expression schema from grouping params (nullity ('PGcomposite ty))
1135+ -> Expression schema from grouping params (nullity 'PGjson)
1136+rowToJson = unsafeFunction "row_to_json"
1137+
1138+-- | Builds a possibly-heterogeneously-typed JSON array out of a variadic
1139+-- argument list.
1140+jsonBuildArray
1141+ :: SListI elems
1142+ => NP (Expression schema from grouping params) elems
1143+ -> Expression schema from grouping params (nullity 'PGjson)
1144+jsonBuildArray = unsafeVariadicFunction "json_build_array"
1145+
1146+-- | Builds a possibly-heterogeneously-typed (binary) JSON array out of a
1147+-- variadic argument list.
1148+jsonbBuildArray
1149+ :: SListI elems
1150+ => NP (Expression schema from grouping params) elems
1151+ -> Expression schema from grouping params (nullity 'PGjsonb)
1152+jsonbBuildArray = unsafeVariadicFunction "jsonb_build_array"
1153+
1154+unsafeRowFunction
1155+ :: All Top elems
1156+ => NP (Aliased (Expression schema from grouping params)) elems
1157+ -> [ByteString]
1158+unsafeRowFunction =
1159+ (`appEndo` []) . hcfoldMap (Proxy :: Proxy Top)
1160+ (\(col `As` name) -> Endo $ \xs ->
1161+ renderAliasString name : renderExpression col : xs)
1162+
1163+-- | Builds a possibly-heterogeneously-typed JSON object out of a variadic
1164+-- argument list. The elements of the argument list must alternate between text
1165+-- and values.
1166+jsonBuildObject
1167+ :: All Top elems
1168+ => NP (Aliased (Expression schema from grouping params)) elems
1169+ -> Expression schema from grouping params (nullity 'PGjson)
1170+jsonBuildObject
1171+ = unsafeFunction "json_build_object"
1172+ . UnsafeExpression
1173+ . commaSeparated
1174+ . unsafeRowFunction
1175+
1176+-- | Builds a possibly-heterogeneously-typed (binary) JSON object out of a
1177+-- variadic argument list. The elements of the argument list must alternate
1178+-- between text and values.
1179+jsonbBuildObject
1180+ :: All Top elems
1181+ => NP (Aliased (Expression schema from grouping params)) elems
1182+ -> Expression schema from grouping params (nullity 'PGjsonb)
1183+jsonbBuildObject
1184+ = unsafeFunction "jsonb_build_object"
1185+ . UnsafeExpression
1186+ . commaSeparated
1187+ . unsafeRowFunction
1188+
1189+-- | Builds a JSON object out of a text array. The array must have either
1190+-- exactly one dimension with an even number of members, in which case they are
1191+-- taken as alternating key/value pairs, or two dimensions such that each inner
1192+-- array has exactly two elements, which are taken as a key/value pair.
1193+jsonObject
1194+ :: PGArrayOf "jsonObject" arr ('NotNull 'PGtext)
1195+ => Expression schema from grouping params (nullity arr)
1196+ -> Expression schema from grouping params (nullity 'PGjson)
1197+jsonObject = unsafeFunction "json_object"
1198+
1199+-- | Builds a binary JSON object out of a text array. The array must have either
1200+-- exactly one dimension with an even number of members, in which case they are
1201+-- taken as alternating key/value pairs, or two dimensions such that each inner
1202+-- array has exactly two elements, which are taken as a key/value pair.
1203+jsonbObject
1204+ :: PGArrayOf "jsonbObject" arr ('NotNull 'PGtext)
1205+ => Expression schema from grouping params (nullity arr)
1206+ -> Expression schema from grouping params (nullity 'PGjsonb)
1207+jsonbObject = unsafeFunction "jsonb_object"
1208+
1209+-- | This is an alternate form of 'jsonObject' that takes two arrays; one for
1210+-- keys and one for values, that are zipped pairwise to create a JSON object.
1211+jsonZipObject
1212+ :: ( PGArrayOf "jsonZipObject" keysArray ('NotNull 'PGtext)
1213+ , PGArrayOf "jsonZipObject" valuesArray ('NotNull 'PGtext))
1214+ => Expression schema from grouping params (nullity keysArray)
1215+ -> Expression schema from grouping params (nullity valuesArray)
1216+ -> Expression schema from grouping params (nullity 'PGjson)
1217+jsonZipObject ks vs =
1218+ unsafeVariadicFunction "json_object" (ks :* vs :* Nil)
1219+
1220+-- | This is an alternate form of 'jsonObject' that takes two arrays; one for
1221+-- keys and one for values, that are zipped pairwise to create a binary JSON
1222+-- object.
1223+jsonbZipObject
1224+ :: ( PGArrayOf "jsonbZipObject" keysArray ('NotNull 'PGtext)
1225+ , PGArrayOf "jsonbZipObject" valuesArray ('NotNull 'PGtext))
1226+ => Expression schema from grouping params (nullity keysArray)
1227+ -> Expression schema from grouping params (nullity valuesArray)
1228+ -> Expression schema from grouping params (nullity 'PGjsonb)
1229+jsonbZipObject ks vs =
1230+ unsafeVariadicFunction "jsonb_object" (ks :* vs :* Nil)
1231+
1232+{-----------------------------------------
1233+Table 9.46: JSON processing functions
1234+-----------------------------------------}
1235+
1236+-- | Returns the number of elements in the outermost JSON array.
1237+jsonArrayLength
1238+ :: Expression schema from grouping params (nullity 'PGjson)
1239+ -> Expression schema from grouping params (nullity 'PGint4)
1240+jsonArrayLength = unsafeFunction "json_array_length"
1241+
1242+-- | Returns the number of elements in the outermost binary JSON array.
1243+jsonbArrayLength
1244+ :: Expression schema from grouping params (nullity 'PGjsonb)
1245+ -> Expression schema from grouping params (nullity 'PGint4)
1246+jsonbArrayLength = unsafeFunction "jsonb_array_length"
1247+
1248+-- | Returns JSON value pointed to by the given path (equivalent to #>
1249+-- operator).
1250+jsonExtractPath
1251+ :: SListI elems
1252+ => Expression schema from grouping params (nullity 'PGjson)
1253+ -> NP (Expression schema from grouping params) elems
1254+ -> Expression schema from grouping params (nullity 'PGjsonb)
1255+jsonExtractPath x xs =
1256+ unsafeVariadicFunction "json_extract_path" (x :* xs)
1257+
1258+-- | Returns JSON value pointed to by the given path (equivalent to #>
1259+-- operator).
1260+jsonbExtractPath
1261+ :: SListI elems
1262+ => Expression schema from grouping params (nullity 'PGjsonb)
1263+ -> NP (Expression schema from grouping params) elems
1264+ -> Expression schema from grouping params (nullity 'PGjsonb)
1265+jsonbExtractPath x xs =
1266+ unsafeVariadicFunction "jsonb_extract_path" (x :* xs)
1267+
1268+-- | Returns JSON value pointed to by the given path (equivalent to #>
1269+-- operator), as text.
1270+jsonExtractPathAsText
1271+ :: SListI elems
1272+ => Expression schema from grouping params (nullity 'PGjson)
1273+ -> NP (Expression schema from grouping params) elems
1274+ -> Expression schema from grouping params (nullity 'PGjson)
1275+jsonExtractPathAsText x xs =
1276+ unsafeVariadicFunction "json_extract_path_text" (x :* xs)
1277+
1278+-- | Returns JSON value pointed to by the given path (equivalent to #>
1279+-- operator), as text.
1280+jsonbExtractPathAsText
1281+ :: SListI elems
1282+ => Expression schema from grouping params (nullity 'PGjsonb)
1283+ -> NP (Expression schema from grouping params) elems
1284+ -> Expression schema from grouping params (nullity 'PGjsonb)
1285+jsonbExtractPathAsText x xs =
1286+ unsafeVariadicFunction "jsonb_extract_path_text" (x :* xs)
1287+
1288+-- | Returns set of keys in the outermost JSON object.
1289+jsonObjectKeys
1290+ :: Expression schema from grouping params (nullity 'PGjson)
1291+ -> Expression schema from grouping params (nullity 'PGtext)
1292+jsonObjectKeys = unsafeFunction "json_object_keys"
1293+
1294+-- | Returns set of keys in the outermost JSON object.
1295+jsonbObjectKeys
1296+ :: Expression schema from grouping params (nullity 'PGjsonb)
1297+ -> Expression schema from grouping params (nullity 'PGtext)
1298+jsonbObjectKeys = unsafeFunction "jsonb_object_keys"
1299+
1300+-- | Returns the type of the outermost JSON value as a text string. Possible
1301+-- types are object, array, string, number, boolean, and null.
1302+jsonTypeof
1303+ :: Expression schema from grouping params (nullity 'PGjson)
1304+ -> Expression schema from grouping params (nullity 'PGtext)
1305+jsonTypeof = unsafeFunction "json_typeof"
1306+
1307+-- | Returns the type of the outermost binary JSON value as a text string.
1308+-- Possible types are object, array, string, number, boolean, and null.
1309+jsonbTypeof
1310+ :: Expression schema from grouping params (nullity 'PGjsonb)
1311+ -> Expression schema from grouping params (nullity 'PGtext)
1312+jsonbTypeof = unsafeFunction "jsonb_typeof"
1313+
1314+-- | Returns its argument with all object fields that have null values omitted.
1315+-- Other null values are untouched.
1316+jsonStripNulls
1317+ :: Expression schema from grouping params (nullity 'PGjson)
1318+ -> Expression schema from grouping params (nullity 'PGjson)
1319+jsonStripNulls = unsafeFunction "json_strip_nulls"
1320+
1321+-- | Returns its argument with all object fields that have null values omitted.
1322+-- Other null values are untouched.
1323+jsonbStripNulls
1324+ :: Expression schema from grouping params (nullity 'PGjsonb)
1325+ -> Expression schema from grouping params (nullity 'PGjsonb)
1326+jsonbStripNulls = unsafeFunction "jsonb_strip_nulls"
1327+
1328+-- | @ jsonbSet target path new_value create_missing @
1329+--
1330+-- Returns target with the section designated by path replaced by new_value,
1331+-- or with new_value added if create_missing is true ( default is true) and the
1332+-- item designated by path does not exist. As with the path orientated
1333+-- operators, negative integers that appear in path count from the end of JSON
1334+-- arrays.
1335+jsonbSet
1336+ :: PGTextArray "jsonbSet" arr
1337+ => Expression schema from grouping params (nullity 'PGjsonb)
1338+ -> Expression schema from grouping params (nullity arr)
1339+ -> Expression schema from grouping params (nullity 'PGjsonb)
1340+ -> Maybe (Expression schema from grouping params (nullity 'PGbool))
1341+ -> Expression schema from grouping params (nullity 'PGjsonb)
1342+jsonbSet tgt path val createMissing = case createMissing of
1343+ Just m -> unsafeVariadicFunction "jsonb_set" (tgt :* path :* val :* m :* Nil)
1344+ Nothing -> unsafeVariadicFunction "jsonb_set" (tgt :* path :* val :* Nil)
1345+
1346+-- | @ jsonbInsert target path new_value insert_after @
1347+--
1348+-- Returns target with new_value inserted. If target section designated by
1349+-- path is in a JSONB array, new_value will be inserted before target or after
1350+-- if insert_after is true (default is false). If target section designated by
1351+-- path is in JSONB object, new_value will be inserted only if target does not
1352+-- exist. As with the path orientated operators, negative integers that appear
1353+-- in path count from the end of JSON arrays.
1354+jsonbInsert
1355+ :: PGTextArray "jsonbInsert" arr
1356+ => Expression schema from grouping params (nullity 'PGjsonb)
1357+ -> Expression schema from grouping params (nullity arr)
1358+ -> Expression schema from grouping params (nullity 'PGjsonb)
1359+ -> Maybe (Expression schema from grouping params (nullity 'PGbool))
1360+ -> Expression schema from grouping params (nullity 'PGjsonb)
1361+jsonbInsert tgt path val insertAfter = case insertAfter of
1362+ Just i -> unsafeVariadicFunction "jsonb_insert" (tgt :* path :* val :* i :* Nil)
1363+ Nothing -> unsafeVariadicFunction "jsonb_insert" (tgt :* path :* val :* Nil)
1364+
1365+-- | Returns its argument as indented JSON text.
1366+jsonbPretty
1367+ :: Expression schema from grouping params (nullity 'PGjsonb)
1368+ -> Expression schema from grouping params (nullity 'PGtext)
1369+jsonbPretty = unsafeFunction "jsonb_pretty"
1370+
1371+{-----------------------------------------
1372+aggregation
1373+-----------------------------------------}
1374+
1375+-- | escape hatch to define aggregate functions
1376+unsafeAggregate
1377+ :: ByteString -- ^ aggregate function
1378+ -> Expression schema from 'Ungrouped params (xty)
1379+ -> Expression schema from ('Grouped bys) params (yty)
1380+unsafeAggregate fun x = UnsafeExpression $ mconcat
1381+ [fun, "(", renderExpression x, ")"]
1382+
1383+-- | escape hatch to define aggregate functions over distinct values
1384+unsafeAggregateDistinct
1385+ :: ByteString -- ^ aggregate function
1386+ -> Expression schema from 'Ungrouped params (xty)
1387+ -> Expression schema from ('Grouped bys) params (yty)
1388+unsafeAggregateDistinct fun x = UnsafeExpression $ mconcat
1389+ [fun, "(DISTINCT ", renderExpression x, ")"]
1390+
1391+-- | >>> :{
1392+-- let
1393+-- expression :: Expression schema '[tab ::: '["col" ::: 'Null 'PGnumeric]] ('Grouped bys) params ('Null 'PGnumeric)
1394+-- expression = sum_ #col
1395+-- in printSQL expression
1396+-- :}
1397+-- sum("col")
1398+sum_
1399+ :: ty `In` PGNum
1400+ => Expression schema from 'Ungrouped params (nullity ty)
1401+ -- ^ what to sum
1402+ -> Expression schema from ('Grouped bys) params (nullity ty)
1403+sum_ = unsafeAggregate "sum"
1404+
1405+-- | >>> :{
1406+-- let
1407+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGnumeric]] ('Grouped bys) params (nullity 'PGnumeric)
1408+-- expression = sumDistinct #col
1409+-- in printSQL expression
1410+-- :}
1411+-- sum(DISTINCT "col")
1412+sumDistinct
1413+ :: ty `In` PGNum
1414+ => Expression schema from 'Ungrouped params (nullity ty)
1415+ -- ^ what to sum
1416+ -> Expression schema from ('Grouped bys) params (nullity ty)
1417+sumDistinct = unsafeAggregateDistinct "sum"
1418+
1419+-- | A constraint for `PGType`s that you can take averages of and the resulting
1420+-- `PGType`.
1421+class PGAvg ty avg | ty -> avg where
1422+ avg, avgDistinct
1423+ :: Expression schema from 'Ungrouped params (nullity ty)
1424+ -- ^ what to average
1425+ -> Expression schema from ('Grouped bys) params (nullity avg)
1426+ avg = unsafeAggregate "avg"
1427+ avgDistinct = unsafeAggregateDistinct "avg"
1428+instance PGAvg 'PGint2 'PGnumeric
1429+instance PGAvg 'PGint4 'PGnumeric
1430+instance PGAvg 'PGint8 'PGnumeric
1431+instance PGAvg 'PGnumeric 'PGnumeric
1432+instance PGAvg 'PGfloat4 'PGfloat8
1433+instance PGAvg 'PGfloat8 'PGfloat8
1434+instance PGAvg 'PGinterval 'PGinterval
1435+
1436+-- | >>> :{
1437+-- let
1438+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1439+-- expression = bitAnd #col
1440+-- in printSQL expression
1441+-- :}
1442+-- bit_and("col")
1443+bitAnd
1444+ :: int `In` PGIntegral
1445+ => Expression schema from 'Ungrouped params (nullity int)
1446+ -- ^ what to aggregate
1447+ -> Expression schema from ('Grouped bys) params (nullity int)
1448+bitAnd = unsafeAggregate "bit_and"
1449+
1450+-- | >>> :{
1451+-- let
1452+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1453+-- expression = bitOr #col
1454+-- in printSQL expression
1455+-- :}
1456+-- bit_or("col")
1457+bitOr
1458+ :: int `In` PGIntegral
1459+ => Expression schema from 'Ungrouped params (nullity int)
1460+ -- ^ what to aggregate
1461+ -> Expression schema from ('Grouped bys) params (nullity int)
1462+bitOr = unsafeAggregate "bit_or"
1463+
1464+-- | >>> :{
1465+-- let
1466+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1467+-- expression = bitAndDistinct #col
1468+-- in printSQL expression
1469+-- :}
1470+-- bit_and(DISTINCT "col")
1471+bitAndDistinct
1472+ :: int `In` PGIntegral
1473+ => Expression schema from 'Ungrouped params (nullity int)
1474+ -- ^ what to aggregate
1475+ -> Expression schema from ('Grouped bys) params (nullity int)
1476+bitAndDistinct = unsafeAggregateDistinct "bit_and"
1477+
1478+-- | >>> :{
1479+-- let
1480+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGint4]] (Grouped bys) params (nullity 'PGint4)
1481+-- expression = bitOrDistinct #col
1482+-- in printSQL expression
1483+-- :}
1484+-- bit_or(DISTINCT "col")
1485+bitOrDistinct
1486+ :: int `In` PGIntegral
1487+ => Expression schema from 'Ungrouped params (nullity int)
1488+ -- ^ what to aggregate
1489+ -> Expression schema from ('Grouped bys) params (nullity int)
1490+bitOrDistinct = unsafeAggregateDistinct "bit_or"
1491+
1492+-- | >>> :{
1493+-- let
1494+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1495+-- expression = boolAnd #col
1496+-- in printSQL expression
1497+-- :}
1498+-- bool_and("col")
1499+boolAnd
1500+ :: Expression schema from 'Ungrouped params (nullity 'PGbool)
1501+ -- ^ what to aggregate
1502+ -> Expression schema from ('Grouped bys) params (nullity 'PGbool)
1503+boolAnd = unsafeAggregate "bool_and"
1504+
1505+-- | >>> :{
1506+-- let
1507+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1508+-- expression = boolOr #col
1509+-- in printSQL expression
1510+-- :}
1511+-- bool_or("col")
1512+boolOr
1513+ :: Expression schema from 'Ungrouped params (nullity 'PGbool)
1514+ -- ^ what to aggregate
1515+ -> Expression schema from ('Grouped bys) params (nullity 'PGbool)
1516+boolOr = unsafeAggregate "bool_or"
1517+
1518+-- | >>> :{
1519+-- let
1520+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1521+-- expression = boolAndDistinct #col
1522+-- in printSQL expression
1523+-- :}
1524+-- bool_and(DISTINCT "col")
1525+boolAndDistinct
1526+ :: Expression schema from 'Ungrouped params (nullity 'PGbool)
1527+ -- ^ what to aggregate
1528+ -> Expression schema from ('Grouped bys) params (nullity 'PGbool)
1529+boolAndDistinct = unsafeAggregateDistinct "bool_and"
1530+
1531+-- | >>> :{
1532+-- let
1533+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1534+-- expression = boolOrDistinct #col
1535+-- in printSQL expression
1536+-- :}
1537+-- bool_or(DISTINCT "col")
1538+boolOrDistinct
1539+ :: Expression schema from 'Ungrouped params (nullity 'PGbool)
1540+ -- ^ what to aggregate
1541+ -> Expression schema from ('Grouped bys) params (nullity 'PGbool)
1542+boolOrDistinct = unsafeAggregateDistinct "bool_or"
1543+
1544+-- | A special aggregation that does not require an input
1545+--
1546+-- >>> printSQL countStar
1547+-- count(*)
1548+countStar
1549+ :: Expression schema from ('Grouped bys) params ('NotNull 'PGint8)
1550+countStar = UnsafeExpression $ "count(*)"
1551+
1552+-- | >>> :{
1553+-- let
1554+-- expression :: Expression schema '[tab ::: '["col" ::: nullity ty]] (Grouped bys) params ('NotNull 'PGint8)
1555+-- expression = count #col
1556+-- in printSQL expression
1557+-- :}
1558+-- count("col")
1559+count
1560+ :: Expression schema from 'Ungrouped params ty
1561+ -- ^ what to count
1562+ -> Expression schema from ('Grouped bys) params ('NotNull 'PGint8)
1563+count = unsafeAggregate "count"
1564+
1565+-- | >>> :{
1566+-- let
1567+-- expression :: Expression schema '[tab ::: '["col" ::: nullity ty]] (Grouped bys) params ('NotNull 'PGint8)
1568+-- expression = countDistinct #col
1569+-- in printSQL expression
1570+-- :}
1571+-- count(DISTINCT "col")
1572+countDistinct
1573+ :: Expression schema from 'Ungrouped params ty
1574+ -- ^ what to count
1575+ -> Expression schema from ('Grouped bys) params ('NotNull 'PGint8)
1576+countDistinct = unsafeAggregateDistinct "count"
1577+
1578+-- | synonym for `boolAnd`
1579+--
1580+-- >>> :{
1581+-- let
1582+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1583+-- expression = every #col
1584+-- in printSQL expression
1585+-- :}
1586+-- every("col")
1587+every
1588+ :: Expression schema from 'Ungrouped params (nullity 'PGbool)
1589+ -- ^ what to aggregate
1590+ -> Expression schema from ('Grouped bys) params (nullity 'PGbool)
1591+every = unsafeAggregate "every"
1592+
1593+-- | synonym for `boolAndDistinct`
1594+--
1595+-- >>> :{
1596+-- let
1597+-- expression :: Expression schema '[tab ::: '["col" ::: nullity 'PGbool]] (Grouped bys) params (nullity 'PGbool)
1598+-- expression = everyDistinct #col
1599+-- in printSQL expression
1600+-- :}
1601+-- every(DISTINCT "col")
1602+everyDistinct
1603+ :: Expression schema from 'Ungrouped params (nullity 'PGbool)
1604+ -- ^ what to aggregate
1605+ -> Expression schema from ('Grouped bys) params (nullity 'PGbool)
1606+everyDistinct = unsafeAggregateDistinct "every"
1607+
1608+-- | minimum and maximum aggregation
1609+max_, min_, maxDistinct, minDistinct
1610+ :: Expression schema from 'Ungrouped params (nullity ty)
1611+ -- ^ what to aggregate
1612+ -> Expression schema from ('Grouped bys) params (nullity ty)
1613+max_ = unsafeAggregate "max"
1614+min_ = unsafeAggregate "min"
1615+maxDistinct = unsafeAggregateDistinct "max"
1616+minDistinct = unsafeAggregateDistinct "min"
1617+
1618+{-----------------------------------------
1619+type expressions
1620+-----------------------------------------}
1621+
1622+-- | `TypeExpression`s are used in `cast`s and `createTable` commands.
1623+newtype TypeExpression (schema :: SchemaType) (ty :: NullityType)
1624+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
1625+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
1626+
1627+-- | The enum or composite type in a `Typedef` can be expressed by its alias.
1628+typedef
1629+ :: Has alias schema ('Typedef ty)
1630+ => Alias alias
1631+ -> TypeExpression schema (nullity ty)
1632+typedef = UnsafeTypeExpression . renderAlias
1633+
1634+-- | The composite type corresponding to a `Table` definition can be expressed
1635+-- by its alias.
1636+typetable
1637+ :: Has alias schema ('Table tab)
1638+ => Alias alias
1639+ -> TypeExpression schema (nullity ('PGcomposite (TableToRow tab)))
1640+typetable = UnsafeTypeExpression . renderAlias
1641+
1642+-- | The composite type corresponding to a `View` definition can be expressed
1643+-- by its alias.
1644+typeview
1645+ :: Has alias schema ('View view)
1646+ => Alias alias
1647+ -> TypeExpression schema (nullity ('PGcomposite view))
1648+typeview = UnsafeTypeExpression . renderAlias
1649+
1650+-- | logical Boolean (true/false)
1651+bool :: TypeExpression schema (nullity 'PGbool)
1652+bool = UnsafeTypeExpression "bool"
1653+-- | signed two-byte integer
1654+int2, smallint :: TypeExpression schema (nullity 'PGint2)
1655+int2 = UnsafeTypeExpression "int2"
1656+smallint = UnsafeTypeExpression "smallint"
1657+-- | signed four-byte integer
1658+int4, int, integer :: TypeExpression schema (nullity 'PGint4)
1659+int4 = UnsafeTypeExpression "int4"
1660+int = UnsafeTypeExpression "int"
1661+integer = UnsafeTypeExpression "integer"
1662+-- | signed eight-byte integer
1663+int8, bigint :: TypeExpression schema (nullity 'PGint8)
1664+int8 = UnsafeTypeExpression "int8"
1665+bigint = UnsafeTypeExpression "bigint"
1666+-- | arbitrary precision numeric type
1667+numeric :: TypeExpression schema (nullity 'PGnumeric)
1668+numeric = UnsafeTypeExpression "numeric"
1669+-- | single precision floating-point number (4 bytes)
1670+float4, real :: TypeExpression schema (nullity 'PGfloat4)
1671+float4 = UnsafeTypeExpression "float4"
1672+real = UnsafeTypeExpression "real"
1673+-- | double precision floating-point number (8 bytes)
1674+float8, doublePrecision :: TypeExpression schema (nullity 'PGfloat8)
1675+float8 = UnsafeTypeExpression "float8"
1676+doublePrecision = UnsafeTypeExpression "double precision"
1677+-- | variable-length character string
1678+text :: TypeExpression schema (nullity 'PGtext)
1679+text = UnsafeTypeExpression "text"
1680+-- | fixed-length character string
1681+char, character
1682+ :: forall n schema nullity. (KnownNat n, 1 <= n)
1683+ => TypeExpression schema (nullity ('PGchar n))
1684+char = UnsafeTypeExpression $ "char(" <> renderNat @n <> ")"
1685+character = UnsafeTypeExpression $ "character(" <> renderNat @n <> ")"
1686+-- | variable-length character string
1687+varchar, characterVarying
1688+ :: forall n schema nullity. (KnownNat n, 1 <= n)
1689+ => TypeExpression schema (nullity ('PGvarchar n))
1690+varchar = UnsafeTypeExpression $ "varchar(" <> renderNat @n <> ")"
1691+characterVarying = UnsafeTypeExpression $
1692+ "character varying(" <> renderNat @n <> ")"
1693+-- | binary data ("byte array")
1694+bytea :: TypeExpression schema (nullity 'PGbytea)
1695+bytea = UnsafeTypeExpression "bytea"
1696+-- | date and time (no time zone)
1697+timestamp :: TypeExpression schema (nullity 'PGtimestamp)
1698+timestamp = UnsafeTypeExpression "timestamp"
1699+-- | date and time, including time zone
1700+timestampWithTimeZone :: TypeExpression schema (nullity 'PGtimestamptz)
1701+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
1702+-- | calendar date (year, month, day)
1703+date :: TypeExpression schema (nullity 'PGdate)
1704+date = UnsafeTypeExpression "date"
1705+-- | time of day (no time zone)
1706+time :: TypeExpression schema (nullity 'PGtime)
1707+time = UnsafeTypeExpression "time"
1708+-- | time of day, including time zone
1709+timeWithTimeZone :: TypeExpression schema (nullity 'PGtimetz)
1710+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
1711+-- | time span
1712+interval :: TypeExpression schema (nullity 'PGinterval)
1713+interval = UnsafeTypeExpression "interval"
1714+-- | universally unique identifier
1715+uuid :: TypeExpression schema (nullity 'PGuuid)
1716+uuid = UnsafeTypeExpression "uuid"
1717+-- | IPv4 or IPv6 host address
1718+inet :: TypeExpression schema (nullity 'PGinet)
1719+inet = UnsafeTypeExpression "inet"
1720+-- | textual JSON data
1721+json :: TypeExpression schema (nullity 'PGjson)
1722+json = UnsafeTypeExpression "json"
1723+-- | binary JSON data, decomposed
1724+jsonb :: TypeExpression schema (nullity 'PGjsonb)
1725+jsonb = UnsafeTypeExpression "jsonb"
1726+-- | variable length array
1727+vararray
1728+ :: TypeExpression schema pg
1729+ -> TypeExpression schema (nullity ('PGvararray pg))
1730+vararray ty = UnsafeTypeExpression $ renderTypeExpression ty <> "[]"
1731+-- | fixed length array
1732+--
1733+-- >>> renderTypeExpression (fixarray @2 json)
1734+-- "json[2]"
1735+fixarray
1736+ :: forall n schema nullity pg. KnownNat n
1737+ => TypeExpression schema pg
1738+ -> TypeExpression schema (nullity ('PGfixarray n pg))
1739+fixarray ty = UnsafeTypeExpression $
1740+ renderTypeExpression ty <> "[" <> renderNat @n <> "]"
1741+
1742+-- | `pgtype` is a demoted version of a `PGType`
1743+class PGTyped schema (ty :: NullityType) where
1744+ pgtype :: TypeExpression schema ty
1745+instance PGTyped schema (nullity 'PGbool) where pgtype = bool
1746+instance PGTyped schema (nullity 'PGint2) where pgtype = int2
1747+instance PGTyped schema (nullity 'PGint4) where pgtype = int4
1748+instance PGTyped schema (nullity 'PGint8) where pgtype = int8
1749+instance PGTyped schema (nullity 'PGnumeric) where pgtype = numeric
1750+instance PGTyped schema (nullity 'PGfloat4) where pgtype = float4
1751+instance PGTyped schema (nullity 'PGfloat8) where pgtype = float8
1752+instance PGTyped schema (nullity 'PGtext) where pgtype = text
1753+instance (KnownNat n, 1 <= n)
1754+ => PGTyped schema (nullity ('PGchar n)) where pgtype = char @n
1755+instance (KnownNat n, 1 <= n)
1756+ => PGTyped schema (nullity ('PGvarchar n)) where pgtype = varchar @n
1757+instance PGTyped schema (nullity 'PGbytea) where pgtype = bytea
1758+instance PGTyped schema (nullity 'PGtimestamp) where pgtype = timestamp
1759+instance PGTyped schema (nullity 'PGtimestamptz) where pgtype = timestampWithTimeZone
1760+instance PGTyped schema (nullity 'PGdate) where pgtype = date
1761+instance PGTyped schema (nullity 'PGtime) where pgtype = time
1762+instance PGTyped schema (nullity 'PGtimetz) where pgtype = timeWithTimeZone
1763+instance PGTyped schema (nullity 'PGinterval) where pgtype = interval
1764+instance PGTyped schema (nullity 'PGuuid) where pgtype = uuid
1765+instance PGTyped schema (nullity 'PGjson) where pgtype = json
1766+instance PGTyped schema (nullity 'PGjsonb) where pgtype = jsonb
1767+instance PGTyped schema ty
1768+ => PGTyped schema (nullity ('PGvararray ty)) where
1769+ pgtype = vararray (pgtype @schema @ty)
1770+instance (KnownNat n, PGTyped schema ty)
1771+ => PGTyped schema (nullity ('PGfixarray n ty)) where
1772+ pgtype = fixarray @n (pgtype @schema @ty)