@@ -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
@@ -33,350 +34,559 @@ Squeal expressions are the atoms used to build statements.
3334module Squeal.PostgreSQL.Expression
3435 ( -- * Expression
3536 Expression (..)
36− , Expr
37− , type (-->)
38− , FunctionDB
39− , unsafeFunction
40− , function
41− , unsafeLeftOp
42− , leftOp
43− , unsafeRightOp
44− , rightOp
45− , Operator
46− , OperatorDB
37+ , HasParameter (parameter)
38+ , param
39+ -- ** Null
40+ , null_
41+ , notNull
42+ , coalesce
43+ , fromNull
44+ , isNull
45+ , isNotNull
46+ , matchNull
47+ , nullIf
48+ -- ** Collections
49+ , array
50+ , index
51+ , row
52+ , field
53+ -- ** Functions
4754 , unsafeBinaryOp
48− , binaryOp
49− , FunctionVar
50− , unsafeFunctionVar
51− , type (--->)
52− , FunctionNDB
53− , unsafeFunctionN
54− , functionN
55− , PGSubset (..)
56− , PGIntersect (..)
55+ , unsafeUnaryOp
56+ , unsafeFunction
57+ , unsafeVariadicFunction
58+ , atan2_
59+ , cast
60+ , quot_
61+ , rem_
62+ , trunc
63+ , round_
64+ , ceiling_
65+ , greatest
66+ , least
67+ -- ** Conditions
68+ , true
69+ , false
70+ , not_
71+ , (.&&)
72+ , (.||)
73+ , Condition
74+ , caseWhenThenElse
75+ , ifThenElse
76+ , (.==)
77+ , (./=)
78+ , (.>=)
79+ , (.<)
80+ , (.<=)
81+ , (.>)
82+ -- ** Time
83+ , currentDate
84+ , currentTime
85+ , currentTimestamp
86+ , localTime
87+ , localTimestamp
88+ -- ** Text
89+ , lower
90+ , upper
91+ , charLength
92+ , like
93+ , ilike
94+ -- ** Json
95+ -- *** Json and Jsonb operators
96+ , (.->)
97+ , (.->>)
98+ , (.#>)
99+ , (.#>>)
100+ -- *** Jsonb operators
101+ , (.@>)
102+ , (.<@)
103+ , (.?)
104+ , (.?|)
105+ , (.?&)
106+ , (.-.)
107+ , (#-.)
108+ -- *** Functions
109+ , jsonLit
110+ , jsonbLit
111+ , toJson
112+ , toJsonb
113+ , arrayToJson
114+ , rowToJson
115+ , jsonBuildArray
116+ , jsonbBuildArray
117+ , jsonBuildObject
118+ , jsonbBuildObject
119+ , jsonObject
120+ , jsonbObject
121+ , jsonZipObject
122+ , jsonbZipObject
123+ , jsonArrayLength
124+ , jsonbArrayLength
125+ , jsonExtractPath
126+ , jsonbExtractPath
127+ , jsonExtractPathAsText
128+ , jsonbExtractPathAsText
129+ , jsonTypeof
130+ , jsonbTypeof
131+ , jsonStripNulls
132+ , jsonbStripNulls
133+ , jsonbSet
134+ , jsonbInsert
135+ , jsonbPretty
136+ -- ** Aggregation
137+ , Aggregate (..)
138+ , Distinction (..)
139+ , PGAvg
140+ -- * Window Functions
141+ , WindowDefinition (..)
142+ , partitionBy
143+ , WindowFunction (..)
144+ , rank
145+ , rowNumber
146+ , denseRank
147+ , percentRank
148+ , cumeDist
149+ , ntile
150+ , lag
151+ , lead
152+ , firstValue
153+ , lastValue
154+ , nthValue
155+ -- * Sorting
156+ , SortExpression (..)
157+ , OrderBy (..)
158+ -- * Types
159+ , TypeExpression (..)
160+ , PGTyped (..)
161+ , typedef
162+ , typetable
163+ , typeview
164+ , bool
165+ , int2
166+ , smallint
167+ , int4
168+ , int
169+ , integer
170+ , int8
171+ , bigint
172+ , numeric
173+ , float4
174+ , real
175+ , float8
176+ , doublePrecision
177+ , text
178+ , char
179+ , character
180+ , varchar
181+ , characterVarying
182+ , bytea
183+ , timestamp
184+ , timestampWithTimeZone
185+ , date
186+ , time
187+ , timeWithTimeZone
188+ , interval
189+ , uuid
190+ , inet
191+ , json
192+ , jsonb
193+ , vararray
194+ , fixarray
57195 -- * Re-export
58196 , (&)
59− , K (..)
60− , unK
197+ , NP ((:*), Nil)
61198 ) where
62199
63200import Control.Category
64201import Control.DeepSeq
65202import Data.ByteString (ByteString)
203+import Data.ByteString.Lazy (toStrict)
66204import Data.Function ((&))
67205import Data.Semigroup hiding (All)
206+import qualified Data.Aeson as JSON
207+import Data.Ratio
68208import Data.String
69−import Generics.SOP hiding (All, from)
209+import Data.Word
210+import Generics.SOP hiding (from)
70211import GHC.OverloadedLabels
71212import GHC.TypeLits
72−import Numeric
73213import Prelude hiding (id, (.))
74214
215+import qualified Data.ByteString as ByteString
75216import qualified GHC.Generics as GHC
76217
77−import Squeal.PostgreSQL.Alias
78−import Squeal.PostgreSQL.List
79218import Squeal.PostgreSQL.Render
80219import Squeal.PostgreSQL.Schema
81220
82−-- $setup
83−-- >>> import Squeal.PostgreSQL
84−
85221{-----------------------------------------
86222column expressions
87223-----------------------------------------}
88224
89225{- | `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,
226+such as in the target list of the `Squeal.PostgreSQL.Query.select` command,
92227as new column values in `Squeal.PostgreSQL.Manipulation.insertRow` or
93228`Squeal.PostgreSQL.Manipulation.update`,
94−or in search `Squeal.PostgreSQL.Logic.Condition`s in a number of commands.
229+or in search `Condition`s in a number of commands.
95230
96231The expression syntax allows the calculation of
97232values from primitive expression using arithmetic, logical,
98233and 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`.
113234-}
114235newtype Expression
115− (outer :: FromType)
116− (commons :: FromType)
117236 (grp :: Grouping)
237+ (commons :: FromType)
118238 (schemas :: SchemasType)
119239 (params :: [NullityType])
120240 (from :: FromType)
121241 (ty :: NullityType)
122242 = UnsafeExpression { renderExpression :: ByteString }
123243 deriving (GHC.Generic,Show,Eq,Ord,NFData)
124−instance RenderSQL (Expression outer commons grp schemas params from ty) where
244+
245+instance RenderSQL (Expression grp commons schemas params from ty) where
125246 renderSQL = renderExpression
126247
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.
248+{- | A `HasParameter` constraint is used to indicate a value that is
249+supplied externally to a SQL statement.
250+`Squeal.PostgreSQL.PQ.manipulateParams`,
251+`Squeal.PostgreSQL.PQ.queryParams` and
252+`Squeal.PostgreSQL.PQ.traversePrepared` support specifying data values
253+separately from the SQL command string, in which case `param`s are used to
254+refer to the out-of-line data values.
198255-}
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)
256+class KnownNat n => HasParameter
257+ (n :: Nat)
258+ (params :: [NullityType])
259+ (ty :: NullityType)
260+ | n params -> ty where
261+ -- | `parameter` takes a `Nat` using type application and a `TypeExpression`.
262+ --
263+ -- >>> let expr = parameter @1 int4 :: Expression grp '[] schemas '[ 'Null 'PGint4] from ('Null 'PGint4)
264+ -- >>> printSQL expr
265+ -- ($1 :: int4)
266+ parameter
267+ :: TypeExpression schemas ty
268+ -> Expression grp commons schemas params from ty
269+ parameter ty = UnsafeExpression $ parenthesized $
270+ "$" <> renderNat @n <+> "::"
271+ <+> renderSQL ty
272+instance {-# OVERLAPPING #-} HasParameter 1 (ty1:tys) ty1
273+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) params ty)
274+ => HasParameter n (ty' : params) ty
275+
276+-- | `param` takes a `Nat` using type application and for basic types,
277+-- infers a `TypeExpression`.
278+--
279+-- >>> let expr = param @1 :: Expression grp commons schemas '[ 'Null 'PGint4] from ('Null 'PGint4)
280+-- >>> printSQL expr
281+-- ($1 :: int4)
282+param
283+ :: forall n commons schemas params from grp ty
284+ . (PGTyped schemas ty, HasParameter n params ty)
285+ => Expression grp commons schemas params from ty -- ^ param
286+param = parameter @n (pgtype @schemas)
214287
215−instance (HasUnique tab (Join outer from) row, Has col row ty)
216− => IsLabel col (Expression outer commons 'Ungrouped schemas params from ty) where
288+instance (HasUnique tab from row, Has col row ty)
289+ => IsLabel col (Expression 'Ungrouped commons schemas params from ty) where
217290 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))
291+instance (HasUnique tab from row, Has col row ty, column ~ (col ::: ty))
222292 => IsLabel col
223− (Aliased (Expression outer commons 'Ungrouped schemas params from) column) where
293+ (Aliased (Expression 'Ungrouped commons schemas params from) column) where
224294 fromLabel = fromLabel @col `As` Alias
225−instance (HasUnique tab (Join outer from) row, Has col row ty, columns ~ '[col ::: ty])
295+instance (HasUnique tab from row, Has col row ty, columns ~ '[col ::: ty])
226296 => IsLabel col
227− (NP (Aliased (Expression outer commons 'Ungrouped schemas params from)) columns) where
297+ (NP (Aliased (Expression 'Ungrouped commons schemas params from)) columns) where
228298 fromLabel = fromLabel @col :* Nil
229299
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
300+instance (Has tab from row, Has col row ty)
301+ => IsQualified tab col (Expression 'Ungrouped commons schemas params from ty) where
232302 tab ! col = UnsafeExpression $
233303 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))
304+instance (Has tab from row, Has col row ty, column ~ (col ::: ty))
238305 => IsQualified tab col
239− (Aliased (Expression outer commons 'Ungrouped schemas params from) column) where
306+ (Aliased (Expression 'Ungrouped commons schemas params from) column) where
240307 tab ! col = tab ! col `As` col
241−instance (Has tab (Join outer from) row, Has col row ty, columns ~ '[col ::: ty])
308+instance (Has tab from row, Has col row ty, columns ~ '[col ::: ty])
242309 => IsQualified tab col
243− (NP (Aliased (Expression outer commons 'Ungrouped schemas params from)) columns) where
310+ (NP (Aliased (Expression 'Ungrouped commons schemas params from)) columns) where
244311 tab ! col = tab ! col :* Nil
245312
246313instance
247− ( HasUnique tab (Join outer from) row
314+ ( HasUnique tab from row
248315 , Has col row ty
249316 , GroupedBy tab col bys
250317 ) => IsLabel col
251− (Expression outer commons ('Grouped bys) schemas params from ty) where
318+ (Expression ('Grouped bys) commons schemas params from ty) where
252319 fromLabel = UnsafeExpression $ renderSQL (Alias @col)
253320instance
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
261−instance
262− ( HasUnique tab (Join outer from) row
321+ ( HasUnique tab from row
263322 , Has col row ty
264323 , GroupedBy tab col bys
265324 , column ~ (col ::: ty)
266325 ) => IsLabel col
267− (Aliased (Expression outer commons ('Grouped bys) schemas params from) column) where
326+ (Aliased (Expression ('Grouped bys) commons schemas params from) column) where
268327 fromLabel = fromLabel @col `As` Alias
269328instance
270− ( HasUnique tab (Join outer from) row
329+ ( HasUnique tab from row
271330 , Has col row ty
272331 , GroupedBy tab col bys
273332 , columns ~ '[col ::: ty]
274333 ) => IsLabel col
275− (NP (Aliased (Expression outer commons ('Grouped bys) schemas params from)) columns) where
334+ (NP (Aliased (Expression ('Grouped bys) commons schemas params from)) columns) where
276335 fromLabel = fromLabel @col :* Nil
277336
278337instance
279− ( Has tab (Join outer from) row
338+ ( Has tab from row
280339 , Has col row ty
281340 , GroupedBy tab col bys
282341 ) => IsQualified tab col
283− (Expression outer commons ('Grouped bys) schemas params from ty) where
342+ (Expression ('Grouped bys) commons schemas params from ty) where
284343 tab ! col = UnsafeExpression $
285344 renderSQL tab <> "." <> renderSQL col
286345instance
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
294−instance
295− ( Has tab (Join outer from) row
346+ ( Has tab from row
296347 , Has col row ty
297348 , GroupedBy tab col bys
298349 , column ~ (col ::: ty)
299350 ) => IsQualified tab col
300− (Aliased (Expression outer commons ('Grouped bys) schemas params from) column) where
351+ (Aliased (Expression ('Grouped bys) commons schemas params from) column) where
301352 tab ! col = tab ! col `As` col
302353instance
303− ( Has tab (Join outer from) row
354+ ( Has tab from row
304355 , Has col row ty
305356 , GroupedBy tab col bys
306357 , columns ~ '[col ::: ty]
307358 ) => IsQualified tab col
308− (NP (Aliased (Expression outer commons ('Grouped bys) schemas params from)) columns) where
359+ (NP (Aliased (Expression ('Grouped bys) commons schemas params from)) columns) where
309360 tab ! col = tab ! col :* Nil
310361
362+-- | analagous to `Nothing`
363+--
364+-- >>> printSQL null_
365+-- NULL
366+null_ :: Expression commons schemas rels grouping params ('Null ty)
367+null_ = UnsafeExpression "NULL"
368+
369+-- | analagous to `Just`
370+--
371+-- >>> printSQL $ notNull true
372+-- TRUE
373+notNull
374+ :: Expression commons schemas rels grouping params ('NotNull ty)
375+ -> Expression commons schemas rels grouping params ('Null ty)
376+notNull = UnsafeExpression . renderSQL
377+
378+-- | return the leftmost value which is not NULL
379+--
380+-- >>> printSQL $ coalesce [null_, true] false
381+-- COALESCE(NULL, TRUE, FALSE)
382+coalesce
383+ :: [Expression grp commons schemas params from ('Null ty)]
384+ -- ^ @NULL@s may be present
385+ -> Expression grp commons schemas params from ('NotNull ty)
386+ -- ^ @NULL@ is absent
387+ -> Expression grp commons schemas params from ('NotNull ty)
388+coalesce nullxs notNullx = UnsafeExpression $
389+ "COALESCE" <> parenthesized (commaSeparated
390+ ((renderSQL <$> nullxs) <> [renderSQL notNullx]))
391+
392+-- | analagous to `Data.Maybe.fromMaybe` using @COALESCE@
393+--
394+-- >>> printSQL $ fromNull true null_
395+-- COALESCE(NULL, TRUE)
396+fromNull
397+ :: Expression grp commons schemas params from ('NotNull ty)
398+ -- ^ what to convert @NULL@ to
399+ -> Expression grp commons schemas params from ('Null ty)
400+ -> Expression grp commons schemas params from ('NotNull ty)
401+fromNull notNullx nullx = coalesce [nullx] notNullx
402+
403+-- | >>> printSQL $ null_ & isNull
404+-- NULL IS NULL
405+isNull
406+ :: Expression grp commons schemas params from ('Null ty)
407+ -- ^ possibly @NULL@
408+ -> Condition grp commons schemas params from
409+isNull x = UnsafeExpression $ renderSQL x <+> "IS NULL"
410+
411+-- | >>> printSQL $ null_ & isNotNull
412+-- NULL IS NOT NULL
413+isNotNull
414+ :: Expression grp commons schemas params from ('Null ty)
415+ -- ^ possibly @NULL@
416+ -> Condition grp commons schemas params from
417+isNotNull x = UnsafeExpression $ renderSQL x <+> "IS NOT NULL"
418+
419+-- | analagous to `maybe` using @IS NULL@
420+--
421+-- >>> printSQL $ matchNull true not_ null_
422+-- CASE WHEN NULL IS NULL THEN TRUE ELSE (NOT NULL) END
423+matchNull
424+ :: Expression grp commons schemas params from (nullty)
425+ -- ^ what to convert @NULL@ to
426+ -> ( Expression grp commons schemas params from ('NotNull ty)
427+ -> Expression grp commons schemas params from (nullty) )
428+ -- ^ function to perform when @NULL@ is absent
429+ -> Expression grp commons schemas params from ('Null ty)
430+ -> Expression grp commons schemas params from (nullty)
431+matchNull y f x = ifThenElse (isNull x) y
432+ (f (UnsafeExpression (renderSQL x)))
433+
434+{-| right inverse to `fromNull`, if its arguments are equal then
435+`nullIf` gives @NULL@.
436+
437+>>> :set -XTypeApplications -XDataKinds
438+>>> let expr = nullIf false (param @1) :: Expression grp commons schemas '[ 'NotNull 'PGbool] from ('Null 'PGbool)
439+>>> printSQL expr
440+NULL IF (FALSE, ($1 :: bool))
441+-}
442+nullIf
443+ :: Expression grp commons schemas params from ('NotNull ty)
444+ -- ^ @NULL@ is absent
445+ -> Expression grp commons schemas params from ('NotNull ty)
446+ -- ^ @NULL@ is absent
447+ -> Expression grp commons schemas params from ('Null ty)
448+nullIf x y = UnsafeExpression $ "NULL IF" <+> parenthesized
449+ (renderSQL x <> ", " <> renderSQL y)
450+
451+-- | >>> printSQL $ array [null_, false, true]
452+-- ARRAY[NULL, FALSE, TRUE]
453+array
454+ :: [Expression grp commons schemas params from ty]
455+ -- ^ array elements
456+ -> Expression grp commons schemas params from (nullity ('PGvararray ty))
457+array xs = UnsafeExpression $
458+ "ARRAY[" <> commaSeparated (renderSQL <$> xs) <> "]"
459+
460+-- | >>> printSQL $ array [null_, false, true] & index 2
461+-- (ARRAY[NULL, FALSE, TRUE])[2]
462+index
463+ :: Word64 -- ^ index
464+ -> Expression grp commons schemas params from (nullity ('PGvararray ty)) -- ^ array
465+ -> Expression grp commons schemas params from (NullifyType ty)
466+index n expr = UnsafeExpression $
467+ parenthesized (renderSQL expr) <> "[" <> fromString (show n) <> "]"
468+
311469instance (KnownSymbol label, label `In` labels) => IsPGlabel label
312− (Expression outer commons grp schemas params from (null ('PGenum labels))) where
470+ (Expression grp commons schemas params from (nullity ('PGenum labels))) where
313471 label = UnsafeExpression $ renderSQL (PGlabel @label)
314472
473+-- | A row constructor is an expression that builds a row value
474+-- (also called a composite value) using values for its member fields.
475+--
476+-- >>> :{
477+-- type Complex = 'PGcomposite
478+-- '[ "real" ::: 'NotNull 'PGfloat8
479+-- , "imaginary" ::: 'NotNull 'PGfloat8 ]
480+-- :}
481+--
482+-- >>> let i = row (0 `as` #real :* 1 `as` #imaginary) :: Expression grp commons schemas params from ('NotNull Complex)
483+-- >>> printSQL i
484+-- ROW(0, 1)
485+row
486+ :: SListI row
487+ => NP (Aliased (Expression grp commons schemas params from)) row
488+ -- ^ zero or more expressions for the row field values
489+ -> Expression grp commons schemas params from (nullity ('PGcomposite row))
490+row exprs = UnsafeExpression $ "ROW" <> parenthesized
491+ (renderCommaSeparated (\ (expr `As` _) -> renderSQL expr) exprs)
492+
493+-- | >>> :{
494+-- type Complex = 'PGcomposite
495+-- '[ "real" ::: 'NotNull 'PGfloat8
496+-- , "imaginary" ::: 'NotNull 'PGfloat8 ]
497+-- type Schema = '["complex" ::: 'Typedef Complex]
498+-- :}
499+--
500+-- >>> let i = row (0 `as` #real :* 1 `as` #imaginary) :: Expression grp '[] (Public Schema) from params ('NotNull Complex)
501+-- >>> printSQL $ i & field #complex #imaginary
502+-- (ROW(0, 1)::"complex")."imaginary"
503+field
504+ :: ( Has sch schemas schema
505+ , Has tydef schema ('Typedef ('PGcomposite row))
506+ , Has field row ty)
507+ => QualifiedAlias sch tydef -- ^ row type
508+ -> Alias field -- ^ field name
509+ -> Expression grp commons schemas params from ('NotNull ('PGcomposite row))
510+ -> Expression grp commons schemas params from ty
511+field td fld expr = UnsafeExpression $
512+ parenthesized (renderSQL expr <> "::" <> renderSQL td)
513+ <> "." <> renderSQL fld
514+
515+instance Semigroup
516+ (Expression grp commons schemas params from (nullity ('PGvararray ty))) where
517+ (<>) = unsafeBinaryOp "||"
518+
519+instance Monoid
520+ (Expression grp commons schemas params from (nullity ('PGvararray ty))) where
521+ mempty = array []
522+ mappend = (<>)
523+
524+-- | >>> let expr = greatest currentTimestamp [param @1] :: Expression grp commons schemas '[ 'NotNull 'PGtimestamptz] from ('NotNull 'PGtimestamptz)
525+-- >>> printSQL expr
526+-- GREATEST(CURRENT_TIMESTAMP, ($1 :: timestamp with time zone))
527+greatest
528+ :: Expression grp commons schemas params from (nullty)
529+ -- ^ needs at least 1 argument
530+ -> [Expression grp commons schemas params from (nullty)]
531+ -- ^ or more
532+ -> Expression grp commons schemas params from (nullty)
533+greatest x xs = UnsafeExpression $ "GREATEST("
534+ <> commaSeparated (renderSQL <$> (x:xs)) <> ")"
535+
536+-- | >>> printSQL $ least currentTimestamp [null_]
537+-- LEAST(CURRENT_TIMESTAMP, NULL)
538+least
539+ :: Expression grp commons schemas params from (nullty)
540+ -- ^ needs at least 1 argument
541+ -> [Expression grp commons schemas params from (nullty)]
542+ -- ^ or more
543+ -> Expression grp commons schemas params from (nullty)
544+least x xs = UnsafeExpression $ "LEAST("
545+ <> commaSeparated (renderSQL <$> (x:xs)) <> ")"
546+
315547-- | >>> printSQL $ unsafeBinaryOp "OR" true false
316548-- (TRUE OR FALSE)
317−unsafeBinaryOp :: ByteString -> Operator ty0 ty1 ty2
549+unsafeBinaryOp
550+ :: ByteString
551+ -- ^ operator
552+ -> Expression grp commons schemas params from (ty0)
553+ -> Expression grp commons schemas params from (ty1)
554+ -> Expression grp commons schemas params from (ty2)
318555unsafeBinaryOp op x y = UnsafeExpression $ parenthesized $
319556 renderSQL x <+> op <+> renderSQL y
320557
321−binaryOp
322− :: forall op sch schemas schema x y z.
323− ( Has sch schemas schema
324− , Has op schema ('Operator ('BinaryOp x y z)) )
325− => OperatorDB schemas x y z
326−binaryOp = unsafeBinaryOp $ renderSymbol @op
327−
328−-- | >>> printSQL $ unsafeLeftOp "NOT" true
558+-- | >>> printSQL $ unsafeUnaryOp "NOT" true
329559-- (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
560+unsafeUnaryOp
561+ :: ByteString
562+ -- ^ operator
563+ -> Expression grp commons schemas params from (ty0)
564+ -> Expression grp commons schemas params from (ty1)
565+unsafeUnaryOp op x = UnsafeExpression $ parenthesized $
566+ op <+> renderSQL x
351567
352568-- | >>> printSQL $ unsafeFunction "f" true
353569-- f(TRUE)
354−unsafeFunction :: ByteString -> x --> y
570+unsafeFunction
571+ :: ByteString
572+ -- ^ function
573+ -> Expression grp commons schemas params from (xty)
574+ -> Expression grp commons schemas params from (yty)
355575unsafeFunction fun x = UnsafeExpression $
356576 fun <> parenthesized (renderSQL x)
357577
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
578+-- | Helper for defining variadic functions.
579+unsafeVariadicFunction
580+ :: SListI elems
581+ => ByteString
582+ -- ^ function
583+ -> NP (Expression grp commons schemas params from) elems
584+ -> Expression grp commons schemas params from ret
585+unsafeVariadicFunction fun x = UnsafeExpression $
586+ fun <> parenthesized (commaSeparated (hcollapse (hmap (K . renderSQL) x)))
377587
378588instance ty `In` PGNum
379− => Num (Expression outer commons grp schemas params from (null ty)) where
589+ => Num (Expression grp commons schemas params from (nullity ty)) where
380590 (+) = unsafeBinaryOp "+"
381591 (-) = unsafeBinaryOp "-"
382592 (*) = unsafeBinaryOp "*"
@@ -388,17 +598,12 @@ instance ty `In` PGNum
388598 . show
389599
390600instance (ty `In` PGNum, ty `In` PGFloating) => Fractional
391− (Expression outer commons grp schemas params from (null ty)) where
601+ (Expression grp commons schemas params from (nullity ty)) where
392602 (/) = unsafeBinaryOp "/"
393− fromRational
394− = UnsafeExpression
395− . fromString
396− . ($ "")
397− . showFFloat Nothing
398− . fromRat @Double
603+ fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
399604
400605instance (ty `In` PGNum, ty `In` PGFloating) => Floating
401− (Expression outer commons grp schemas params from (null ty)) where
606+ (Expression grp commons schemas params from (nullity ty)) where
402607 pi = UnsafeExpression "pi()"
403608 exp = unsafeFunction "exp"
404609 log = unsafeFunction "ln"
@@ -419,54 +624,1322 @@ instance (ty `In` PGNum, ty `In` PGFloating) => Floating
419624 acosh x = log (x + sqrt (x*x - 1))
420625 atanh x = log ((1 + x) / (1 - x)) / 2
421626
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)
627+-- | >>> :{
628+-- let
629+-- expression :: Expression grp commons schemas params from (nullity 'PGfloat4)
630+-- expression = atan2_ pi 2
631+-- in printSQL expression
632+-- :}
633+-- atan2(pi(), 2)
634+atan2_
635+ :: float `In` PGFloating
636+ => Expression grp commons schemas params from (nullity float)
637+ -- ^ numerator
638+ -> Expression grp commons schemas params from (nullity float)
639+ -- ^ denominator
640+ -> Expression grp commons schemas params from (nullity float)
641+atan2_ y x = UnsafeExpression $
642+ "atan2(" <> renderSQL y <> ", " <> renderSQL x <> ")"
643+
644+-- When a `cast` is applied to an `Expression` of a known type, it
645+-- represents a run-time type conversion. The cast will succeed only if a
646+-- suitable type conversion operation has been defined.
647+--
648+-- | >>> printSQL $ true & cast int4
649+-- (TRUE :: int4)
650+cast
651+ :: TypeExpression schemas ty1
652+ -- ^ type to cast as
653+ -> Expression grp commons schemas params from ty0
654+ -- ^ value to convert
655+ -> Expression grp commons schemas params from ty1
656+cast ty x = UnsafeExpression $ parenthesized $
657+ renderSQL x <+> "::" <+> renderSQL ty
658+
659+-- | integer division, truncates the result
660+--
661+-- >>> :{
662+-- let
663+-- expression :: Expression grp commons schemas params from (nullity 'PGint2)
664+-- expression = 5 `quot_` 2
665+-- in printSQL expression
666+-- :}
667+-- (5 / 2)
668+quot_
669+ :: int `In` PGIntegral
670+ => Expression grp commons schemas params from (nullity int)
671+ -- ^ numerator
672+ -> Expression grp commons schemas params from (nullity int)
673+ -- ^ denominator
674+ -> Expression grp commons schemas params from (nullity int)
675+quot_ = unsafeBinaryOp "/"
676+
677+-- | remainder upon integer division
678+--
679+-- >>> :{
680+-- let
681+-- expression :: Expression grp commons schemas params from (nullity 'PGint2)
682+-- expression = 5 `rem_` 2
683+-- in printSQL expression
684+-- :}
685+-- (5 % 2)
686+rem_
687+ :: int `In` PGIntegral
688+ => Expression grp commons schemas params from (nullity int)
689+ -- ^ numerator
690+ -> Expression grp commons schemas params from (nullity int)
691+ -- ^ denominator
692+ -> Expression grp commons schemas params from (nullity int)
693+rem_ = unsafeBinaryOp "%"
694+
695+-- | >>> :{
696+-- let
697+-- expression :: Expression grp commons schemas params from (nullity 'PGfloat4)
698+-- expression = trunc pi
699+-- in printSQL expression
700+-- :}
701+-- trunc(pi())
702+trunc
703+ :: frac `In` PGFloating
704+ => Expression grp commons schemas params from (nullity frac)
705+ -- ^ fractional number
706+ -> Expression grp commons schemas params from (nullity frac)
707+trunc = unsafeFunction "trunc"
708+
709+-- | >>> :{
710+-- let
711+-- expression :: Expression grp commons schemas params from (nullity 'PGfloat4)
712+-- expression = round_ pi
713+-- in printSQL expression
714+-- :}
715+-- round(pi())
716+round_
717+ :: frac `In` PGFloating
718+ => Expression grp commons schemas params from (nullity frac)
719+ -- ^ fractional number
720+ -> Expression grp commons schemas params from (nullity frac)
721+round_ = unsafeFunction "round"
722+
723+-- | >>> :{
724+-- let
725+-- expression :: Expression grp commons schemas params from (nullity 'PGfloat4)
726+-- expression = ceiling_ pi
727+-- in printSQL expression
728+-- :}
729+-- ceiling(pi())
730+ceiling_
731+ :: frac `In` PGFloating
732+ => Expression grp commons schemas params from (nullity frac)
733+ -- ^ fractional number
734+ -> Expression grp commons schemas params from (nullity frac)
735+ceiling_ = unsafeFunction "ceiling"
736+
737+-- | A `Condition` is an `Expression`, which can evaluate
738+-- to `true`, `false` or `null_`. This is because SQL uses
739+-- a three valued logic.
740+type Condition grp commons schemas params from =
741+ Expression grp commons schemas params from ('Null 'PGbool)
742+
743+-- | >>> printSQL true
744+-- TRUE
745+true :: Expression grp commons schemas params from (nullity 'PGbool)
746+true = UnsafeExpression "TRUE"
747+
748+-- | >>> printSQL false
749+-- FALSE
750+false :: Expression grp commons schemas params from (nullity 'PGbool)
751+false = UnsafeExpression "FALSE"
752+
753+-- | >>> printSQL $ not_ true
754+-- (NOT TRUE)
755+not_
756+ :: Expression grp commons schemas params from (nullity 'PGbool)
757+ -> Expression grp commons schemas params from (nullity 'PGbool)
758+not_ = unsafeUnaryOp "NOT"
759+
760+-- | >>> printSQL $ true .&& false
761+-- (TRUE AND FALSE)
762+(.&&)
763+ :: Expression grp commons schemas params from (nullity 'PGbool)
764+ -> Expression grp commons schemas params from (nullity 'PGbool)
765+ -> Expression grp commons schemas params from (nullity 'PGbool)
766+infixr 3 .&&
767+(.&&) = unsafeBinaryOp "AND"
768+
769+-- | >>> printSQL $ true .|| false
770+-- (TRUE OR FALSE)
771+(.||)
772+ :: Expression grp commons schemas params from (nullity 'PGbool)
773+ -> Expression grp commons schemas params from (nullity 'PGbool)
774+ -> Expression grp commons schemas params from (nullity 'PGbool)
775+infixr 2 .||
776+(.||) = unsafeBinaryOp "OR"
777+
778+-- | >>> :{
779+-- let
780+-- expression :: Expression grp commons schemas params from (nullity 'PGint2)
781+-- expression = caseWhenThenElse [(true, 1), (false, 2)] 3
782+-- in printSQL expression
783+-- :}
784+-- CASE WHEN TRUE THEN 1 WHEN FALSE THEN 2 ELSE 3 END
785+caseWhenThenElse
786+ :: [ ( Condition grp commons schemas params from
787+ , Expression grp commons schemas params from ty
788+ ) ]
789+ -- ^ whens and thens
790+ -> Expression grp commons schemas params from ty
791+ -- ^ else
792+ -> Expression grp commons schemas params from ty
793+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
794+ [ "CASE"
795+ , mconcat
796+ [ mconcat
797+ [ " WHEN ", renderSQL when_
798+ , " THEN ", renderSQL then_
799+ ]
800+ | (when_,then_) <- whenThens
801+ ]
802+ , " ELSE ", renderSQL else_
803+ , " END"
804+ ]
805+
806+-- | >>> :{
807+-- let
808+-- expression :: Expression grp commons schemas params from (nullity 'PGint2)
809+-- expression = ifThenElse true 1 0
810+-- in printSQL expression
811+-- :}
812+-- CASE WHEN TRUE THEN 1 ELSE 0 END
813+ifThenElse
814+ :: Condition grp commons schemas params from
815+ -> Expression grp commons schemas params from ty -- ^ then
816+ -> Expression grp commons schemas params from ty -- ^ else
817+ -> Expression grp commons schemas params from ty
818+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
819+
820+-- | Comparison operations like `.==`, `./=`, `.>`, `.>=`, `.<` and `.<=`
821+-- will produce @NULL@s if one of their arguments is @NULL@.
822+--
823+-- >>> printSQL $ true .== null_
824+-- (TRUE = NULL)
825+(.==)
826+ :: Expression grp commons schemas params from (nullity0 ty) -- ^ lhs
827+ -> Expression grp commons schemas params from (nullity1 ty) -- ^ rhs
828+ -> Condition grp commons schemas params from
829+(.==) = unsafeBinaryOp "="
830+infix 4 .==
831+
832+-- | >>> printSQL $ true ./= null_
833+-- (TRUE <> NULL)
834+(./=)
835+ :: Expression grp commons schemas params from (nullity0 ty) -- ^ lhs
836+ -> Expression grp commons schemas params from (nullity1 ty) -- ^ rhs
837+ -> Condition grp commons schemas params from
838+(./=) = unsafeBinaryOp "<>"
839+infix 4 ./=
840+
841+-- | >>> printSQL $ true .>= null_
842+-- (TRUE >= NULL)
843+(.>=)
844+ :: Expression grp commons schemas params from (nullity0 ty) -- ^ lhs
845+ -> Expression grp commons schemas params from (nullity1 ty) -- ^ rhs
846+ -> Condition grp commons schemas params from
847+(.>=) = unsafeBinaryOp ">="
848+infix 4 .>=
849+
850+-- | >>> printSQL $ true .< null_
851+-- (TRUE < NULL)
852+(.<)
853+ :: Expression grp commons schemas params from (nullity0 ty) -- ^ lhs
854+ -> Expression grp commons schemas params from (nullity1 ty) -- ^ rhs
855+ -> Condition grp commons schemas params from
856+(.<) = unsafeBinaryOp "<"
857+infix 4 .<
858+
859+-- | >>> printSQL $ true .<= null_
860+-- (TRUE <= NULL)
861+(.<=)
862+ :: Expression grp commons schemas params from (nullity0 ty) -- ^ lhs
863+ -> Expression grp commons schemas params from (nullity1 ty) -- ^ rhs
864+ -> Condition grp commons schemas params from
865+(.<=) = unsafeBinaryOp "<="
866+infix 4 .<=
867+
868+-- | >>> printSQL $ true .> null_
869+-- (TRUE > NULL)
870+(.>)
871+ :: Expression grp commons schemas params from (nullity0 ty) -- ^ lhs
872+ -> Expression grp commons schemas params from (nullity1 ty) -- ^ rhs
873+ -> Condition grp commons schemas params from
874+(.>) = unsafeBinaryOp ">"
875+infix 4 .>
876+
877+-- | >>> printSQL currentDate
878+-- CURRENT_DATE
879+currentDate
880+ :: Expression grp commons schemas params from (nullity 'PGdate)
881+currentDate = UnsafeExpression "CURRENT_DATE"
882+
883+-- | >>> printSQL currentTime
884+-- CURRENT_TIME
885+currentTime
886+ :: Expression grp commons schemas params from (nullity 'PGtimetz)
887+currentTime = UnsafeExpression "CURRENT_TIME"
888+
889+-- | >>> printSQL currentTimestamp
890+-- CURRENT_TIMESTAMP
891+currentTimestamp
892+ :: Expression grp commons schemas params from (nullity 'PGtimestamptz)
893+currentTimestamp = UnsafeExpression "CURRENT_TIMESTAMP"
894+
895+-- | >>> printSQL localTime
896+-- LOCALTIME
897+localTime
898+ :: Expression grp commons schemas params from (nullity 'PGtime)
899+localTime = UnsafeExpression "LOCALTIME"
900+
901+-- | >>> printSQL localTimestamp
902+-- LOCALTIMESTAMP
903+localTimestamp
904+ :: Expression grp commons schemas params from (nullity 'PGtimestamp)
905+localTimestamp = UnsafeExpression "LOCALTIMESTAMP"
906+
907+{-----------------------------------------
908+text
909+-----------------------------------------}
438910
439911instance IsString
440− (Expression outer commons grp schemas params from (null 'PGtext)) where
912+ (Expression grp commons schemas params from (nullity 'PGtext)) where
441913 fromString str = UnsafeExpression $
442914 "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) <> "\'"
915+ where
916+ escape = \case
917+ '\NUL' -> "\\0"
918+ '\'' -> "''"
919+ '"' -> "\\\""
920+ '\b' -> "\\b"
921+ '\n' -> "\\n"
922+ '\r' -> "\\r"
923+ '\t' -> "\\t"
924+ '\\' -> "\\\\"
925+ c -> [c]
451926
452927instance 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
928+ (Expression grp commons schemas params from (nullity 'PGtext)) where
463929 (<>) = unsafeBinaryOp "||"
464930
465931instance 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
932+ (Expression grp commons schemas params from (nullity 'PGtext)) where
471933 mempty = fromString ""
472934 mappend = (<>)
935+
936+-- | >>> printSQL $ lower "ARRRGGG"
937+-- lower(E'ARRRGGG')
938+lower
939+ :: Expression grp commons schemas params from (nullity 'PGtext)
940+ -- ^ string to lower case
941+ -> Expression grp commons schemas params from (nullity 'PGtext)
942+lower = unsafeFunction "lower"
943+
944+-- | >>> printSQL $ upper "eeee"
945+-- upper(E'eeee')
946+upper
947+ :: Expression grp commons schemas params from (nullity 'PGtext)
948+ -- ^ string to upper case
949+ -> Expression grp commons schemas params from (nullity 'PGtext)
950+upper = unsafeFunction "upper"
951+
952+-- | >>> printSQL $ charLength "four"
953+-- char_length(E'four')
954+charLength
955+ :: Expression grp commons schemas params from (nullity 'PGtext)
956+ -- ^ string to measure
957+ -> Expression grp commons schemas params from (nullity 'PGint4)
958+charLength = unsafeFunction "char_length"
959+
960+-- | The `like` expression returns true if the @string@ matches
961+-- the supplied @pattern@. If @pattern@ does not contain percent signs
962+-- or underscores, then the pattern only represents the string itself;
963+-- in that case `like` acts like the equals operator. An underscore (_)
964+-- in pattern stands for (matches) any single character; a percent sign (%)
965+-- matches any sequence of zero or more characters.
966+--
967+-- >>> printSQL $ "abc" `like` "a%"
968+-- (E'abc' LIKE E'a%')
969+like
970+ :: Expression grp commons schemas params from (nullity 'PGtext)
971+ -- ^ string
972+ -> Expression grp commons schemas params from (nullity 'PGtext)
973+ -- ^ pattern
974+ -> Expression grp commons schemas params from (nullity 'PGbool)
975+like = unsafeBinaryOp "LIKE"
976+
977+-- | The key word ILIKE can be used instead of LIKE to make the
978+-- match case-insensitive according to the active locale.
979+--
980+-- >>> printSQL $ "abc" `ilike` "a%"
981+-- (E'abc' ILIKE E'a%')
982+ilike
983+ :: Expression grp commons schemas params from (nullity 'PGtext)
984+ -- ^ string
985+ -> Expression grp commons schemas params from (nullity 'PGtext)
986+ -- ^ pattern
987+ -> Expression grp commons schemas params from (nullity 'PGbool)
988+ilike = unsafeBinaryOp "ILIKE"
989+
990+{-----------------------------------------
991+ -- json and jsonb support
992+
993+See https://www.postgresql.org/docs/10/static/functions-json.html -- most
994+comments lifted directly from this page.
995+
996+Table 9.44: json and jsonb operators
997+-----------------------------------------}
998+
999+-- | Get JSON value (object field or array element) at a key.
1000+(.->)
1001+ :: (json `In` PGJsonType, key `In` PGJsonKey)
1002+ => Expression grp commons schemas params from (nullity json)
1003+ -> Expression grp commons schemas params from (nullity key)
1004+ -> Expression grp commons schemas params from ('Null json)
1005+infixl 8 .->
1006+(.->) = unsafeBinaryOp "->"
1007+
1008+-- | Get JSON value (object field or array element) at a key, as text.
1009+(.->>)
1010+ :: (json `In` PGJsonType, key `In` PGJsonKey)
1011+ => Expression grp commons schemas params from (nullity json)
1012+ -> Expression grp commons schemas params from (nullity key)
1013+ -> Expression grp commons schemas params from ('Null 'PGtext)
1014+infixl 8 .->>
1015+(.->>) = unsafeBinaryOp "->>"
1016+
1017+-- | Get JSON value at a specified path.
1018+(.#>)
1019+ :: (json `In` PGJsonType, PGTextArray "(.#>)" path)
1020+ => Expression grp commons schemas params from (nullity json)
1021+ -> Expression grp commons schemas params from (nullity path)
1022+ -> Expression grp commons schemas params from ('Null json)
1023+infixl 8 .#>
1024+(.#>) = unsafeBinaryOp "#>"
1025+
1026+-- | Get JSON value at a specified path as text.
1027+(.#>>)
1028+ :: (json `In` PGJsonType, PGTextArray "(.#>>)" path)
1029+ => Expression grp commons schemas params from (nullity json)
1030+ -> Expression grp commons schemas params from (nullity path)
1031+ -> Expression grp commons schemas params from ('Null 'PGtext)
1032+infixl 8 .#>>
1033+(.#>>) = unsafeBinaryOp "#>>"
1034+
1035+-- Additional jsonb operators
1036+
1037+-- | Does the left JSON value contain the right JSON path/value entries at the
1038+-- top level?
1039+(.@>)
1040+ :: Expression grp commons schemas params from (nullity 'PGjsonb)
1041+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1042+ -> Condition grp commons schemas params from
1043+infixl 9 .@>
1044+(.@>) = unsafeBinaryOp "@>"
1045+
1046+-- | Are the left JSON path/value entries contained at the top level within the
1047+-- right JSON value?
1048+(.<@)
1049+ :: Expression grp commons schemas params from (nullity 'PGjsonb)
1050+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1051+ -> Condition grp commons schemas params from
1052+infixl 9 .<@
1053+(.<@) = unsafeBinaryOp "<@"
1054+
1055+-- | Does the string exist as a top-level key within the JSON value?
1056+(.?)
1057+ :: Expression grp commons schemas params from (nullity 'PGjsonb)
1058+ -> Expression grp commons schemas params from (nullity 'PGtext)
1059+ -> Condition grp commons schemas params from
1060+infixl 9 .?
1061+(.?) = unsafeBinaryOp "?"
1062+
1063+-- | Do any of these array strings exist as top-level keys?
1064+(.?|)
1065+ :: Expression grp commons schemas params from (nullity 'PGjsonb)
1066+ -> Expression grp commons schemas params from (nullity ('PGvararray ('NotNull 'PGtext)))
1067+ -> Condition grp commons schemas params from
1068+infixl 9 .?|
1069+(.?|) = unsafeBinaryOp "?|"
1070+
1071+-- | Do all of these array strings exist as top-level keys?
1072+(.?&)
1073+ :: Expression grp commons schemas params from (nullity 'PGjsonb)
1074+ -> Expression grp commons schemas params from (nullity ('PGvararray ('NotNull 'PGtext)))
1075+ -> Condition grp commons schemas params from
1076+infixl 9 .?&
1077+(.?&) = unsafeBinaryOp "?&"
1078+
1079+-- | Concatenate two jsonb values into a new jsonb value.
1080+instance
1081+ Semigroup (Expression commons schemas from grouping param (nullity 'PGjsonb)) where
1082+ (<>) = unsafeBinaryOp "||"
1083+
1084+-- | Delete a key or keys from a JSON object, or remove an array element.
1085+--
1086+-- If the right operand is..
1087+--
1088+-- @ text @: Delete key/value pair or string element from left operand. Key/value pairs
1089+-- are matched based on their key value.
1090+--
1091+-- @ text[] @: Delete multiple key/value pairs or string elements
1092+-- from left operand. Key/value pairs are matched based on their key value.
1093+--
1094+-- @ integer @: Delete the array element with specified index (Negative integers
1095+-- count from the end). Throws an error if top level container is not an array.
1096+(.-.)
1097+ :: (key `In` '[ 'PGtext, 'PGvararray ('NotNull 'PGtext), 'PGint4, 'PGint2 ]) -- hlint error without parens here
1098+ => Expression grp commons schemas params from (nullity 'PGjsonb)
1099+ -> Expression grp commons schemas params from (nullity key)
1100+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1101+infixl 6 .-.
1102+(.-.) = unsafeBinaryOp "-"
1103+
1104+-- | Delete the field or element with specified path (for JSON arrays, negative
1105+-- integers count from the end)
1106+(#-.)
1107+ :: PGTextArray "(#-.)" arrayty
1108+ => Expression grp commons schemas params from (nullity 'PGjsonb)
1109+ -> Expression grp commons schemas params from (nullity arrayty)
1110+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1111+infixl 6 #-.
1112+(#-.) = unsafeBinaryOp "#-"
1113+
1114+{-----------------------------------------
1115+Table 9.45: JSON creation functions
1116+-----------------------------------------}
1117+
1118+-- | Literal binary JSON
1119+jsonbLit
1120+ :: JSON.ToJSON x
1121+ => x -> Expression grp commons schemas params from (nullity 'PGjsonb)
1122+jsonbLit = cast jsonb . UnsafeExpression
1123+ . singleQuotedUtf8 . toStrict . JSON.encode
1124+
1125+-- | Literal JSON
1126+jsonLit
1127+ :: JSON.ToJSON x
1128+ => x -> Expression grp commons schemas params from (nullity 'PGjson)
1129+jsonLit = cast json . UnsafeExpression
1130+ . singleQuotedUtf8 . toStrict . JSON.encode
1131+
1132+-- | Returns the value as json. Arrays and composites are converted
1133+-- (recursively) to arrays and objects; otherwise, if there is a cast from the
1134+-- type to json, the cast function will be used to perform the conversion;
1135+-- otherwise, a scalar value is produced. For any scalar type other than a
1136+-- number, a Boolean, or a null value, the text representation will be used, in
1137+-- such a fashion that it is a valid json value.
1138+toJson
1139+ :: Expression grp commons schemas params from (nullity ty)
1140+ -> Expression grp commons schemas params from (nullity 'PGjson)
1141+toJson = unsafeFunction "to_json"
1142+
1143+-- | Returns the value as jsonb. Arrays and composites are converted
1144+-- (recursively) to arrays and objects; otherwise, if there is a cast from the
1145+-- type to json, the cast function will be used to perform the conversion;
1146+-- otherwise, a scalar value is produced. For any scalar type other than a
1147+-- number, a Boolean, or a null value, the text representation will be used, in
1148+-- such a fashion that it is a valid jsonb value.
1149+toJsonb
1150+ :: Expression grp commons schemas params from (nullity ty)
1151+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1152+toJsonb = unsafeFunction "to_jsonb"
1153+
1154+-- | Returns the array as a JSON array. A PostgreSQL multidimensional array
1155+-- becomes a JSON array of arrays.
1156+arrayToJson
1157+ :: PGArray "arrayToJson" arr
1158+ => Expression grp commons schemas params from (nullity arr)
1159+ -> Expression grp commons schemas params from (nullity 'PGjson)
1160+arrayToJson = unsafeFunction "array_to_json"
1161+
1162+-- | Returns the row as a JSON object.
1163+rowToJson
1164+ :: Expression grp commons schemas params from (nullity ('PGcomposite ty))
1165+ -> Expression grp commons schemas params from (nullity 'PGjson)
1166+rowToJson = unsafeFunction "row_to_json"
1167+
1168+-- | Builds a possibly-heterogeneously-typed JSON array out of a variadic
1169+-- argument list.
1170+jsonBuildArray
1171+ :: SListI elems
1172+ => NP (Expression grp commons schemas params from) elems
1173+ -> Expression grp commons schemas params from (nullity 'PGjson)
1174+jsonBuildArray = unsafeVariadicFunction "json_build_array"
1175+
1176+-- | Builds a possibly-heterogeneously-typed (binary) JSON array out of a
1177+-- variadic argument list.
1178+jsonbBuildArray
1179+ :: SListI elems
1180+ => NP (Expression grp commons schemas params from) elems
1181+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1182+jsonbBuildArray = unsafeVariadicFunction "jsonb_build_array"
1183+
1184+unsafeRowFunction
1185+ :: All Top elems
1186+ => NP (Aliased (Expression grp commons schemas params from)) elems
1187+ -> [ByteString]
1188+unsafeRowFunction =
1189+ (`appEndo` []) . hcfoldMap (Proxy :: Proxy Top)
1190+ (\(col `As` name) -> Endo $ \xs ->
1191+ renderAliasString name : renderSQL col : xs)
1192+ where
1193+ renderAliasString :: KnownSymbol alias => Alias alias -> ByteString
1194+ renderAliasString = singleQuotedText . fromString . symbolVal
1195+
1196+-- | Builds a possibly-heterogeneously-typed JSON object out of a variadic
1197+-- argument list. The elements of the argument list must alternate between text
1198+-- and values.
1199+jsonBuildObject
1200+ :: All Top elems
1201+ => NP (Aliased (Expression grp commons schemas params from)) elems
1202+ -> Expression grp commons schemas params from (nullity 'PGjson)
1203+jsonBuildObject
1204+ = unsafeFunction "json_build_object"
1205+ . UnsafeExpression
1206+ . commaSeparated
1207+ . unsafeRowFunction
1208+
1209+-- | Builds a possibly-heterogeneously-typed (binary) JSON object out of a
1210+-- variadic argument list. The elements of the argument list must alternate
1211+-- between text and values.
1212+jsonbBuildObject
1213+ :: All Top elems
1214+ => NP (Aliased (Expression grp commons schemas params from)) elems
1215+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1216+jsonbBuildObject
1217+ = unsafeFunction "jsonb_build_object"
1218+ . UnsafeExpression
1219+ . commaSeparated
1220+ . unsafeRowFunction
1221+
1222+-- | Builds a JSON object out of a text array. The array must have either
1223+-- exactly one dimension with an even number of members, in which case they are
1224+-- taken as alternating key/value pairs, or two dimensions such that each inner
1225+-- array has exactly two elements, which are taken as a key/value pair.
1226+jsonObject
1227+ :: PGArrayOf "jsonObject" arr ('NotNull 'PGtext)
1228+ => Expression grp commons schemas params from (nullity arr)
1229+ -> Expression grp commons schemas params from (nullity 'PGjson)
1230+jsonObject = unsafeFunction "json_object"
1231+
1232+-- | Builds a binary JSON object out of a text array. The array must have either
1233+-- exactly one dimension with an even number of members, in which case they are
1234+-- taken as alternating key/value pairs, or two dimensions such that each inner
1235+-- array has exactly two elements, which are taken as a key/value pair.
1236+jsonbObject
1237+ :: PGArrayOf "jsonbObject" arr ('NotNull 'PGtext)
1238+ => Expression grp commons schemas params from (nullity arr)
1239+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1240+jsonbObject = unsafeFunction "jsonb_object"
1241+
1242+-- | This is an alternate form of 'jsonObject' that takes two arrays; one for
1243+-- keys and one for values, that are zipped pairwise to create a JSON object.
1244+jsonZipObject
1245+ :: ( PGArrayOf "jsonZipObject" keysArray ('NotNull 'PGtext)
1246+ , PGArrayOf "jsonZipObject" valuesArray ('NotNull 'PGtext))
1247+ => Expression grp commons schemas params from (nullity keysArray)
1248+ -> Expression grp commons schemas params from (nullity valuesArray)
1249+ -> Expression grp commons schemas params from (nullity 'PGjson)
1250+jsonZipObject ks vs =
1251+ unsafeVariadicFunction "json_object" (ks :* vs :* Nil)
1252+
1253+-- | This is an alternate form of 'jsonObject' that takes two arrays; one for
1254+-- keys and one for values, that are zipped pairwise to create a binary JSON
1255+-- object.
1256+jsonbZipObject
1257+ :: ( PGArrayOf "jsonbZipObject" keysArray ('NotNull 'PGtext)
1258+ , PGArrayOf "jsonbZipObject" valuesArray ('NotNull 'PGtext))
1259+ => Expression grp commons schemas params from (nullity keysArray)
1260+ -> Expression grp commons schemas params from (nullity valuesArray)
1261+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1262+jsonbZipObject ks vs =
1263+ unsafeVariadicFunction "jsonb_object" (ks :* vs :* Nil)
1264+
1265+{-----------------------------------------
1266+Table 9.46: JSON processing functions
1267+-----------------------------------------}
1268+
1269+-- | Returns the number of elements in the outermost JSON array.
1270+jsonArrayLength
1271+ :: Expression grp commons schemas params from (nullity 'PGjson)
1272+ -> Expression grp commons schemas params from (nullity 'PGint4)
1273+jsonArrayLength = unsafeFunction "json_array_length"
1274+
1275+-- | Returns the number of elements in the outermost binary JSON array.
1276+jsonbArrayLength
1277+ :: Expression grp commons schemas params from (nullity 'PGjsonb)
1278+ -> Expression grp commons schemas params from (nullity 'PGint4)
1279+jsonbArrayLength = unsafeFunction "jsonb_array_length"
1280+
1281+-- | Returns JSON value pointed to by the given path (equivalent to #>
1282+-- operator).
1283+jsonExtractPath
1284+ :: SListI elems
1285+ => Expression grp commons schemas params from (nullity 'PGjson)
1286+ -> NP (Expression grp commons schemas params from) elems
1287+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1288+jsonExtractPath x xs =
1289+ unsafeVariadicFunction "json_extract_path" (x :* xs)
1290+
1291+-- | Returns JSON value pointed to by the given path (equivalent to #>
1292+-- operator).
1293+jsonbExtractPath
1294+ :: SListI elems
1295+ => Expression grp commons schemas params from (nullity 'PGjsonb)
1296+ -> NP (Expression grp commons schemas params from) elems
1297+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1298+jsonbExtractPath x xs =
1299+ unsafeVariadicFunction "jsonb_extract_path" (x :* xs)
1300+
1301+-- | Returns JSON value pointed to by the given path (equivalent to #>
1302+-- operator), as text.
1303+jsonExtractPathAsText
1304+ :: SListI elems
1305+ => Expression grp commons schemas params from (nullity 'PGjson)
1306+ -> NP (Expression grp commons schemas params from) elems
1307+ -> Expression grp commons schemas params from (nullity 'PGjson)
1308+jsonExtractPathAsText x xs =
1309+ unsafeVariadicFunction "json_extract_path_text" (x :* xs)
1310+
1311+-- | Returns JSON value pointed to by the given path (equivalent to #>
1312+-- operator), as text.
1313+jsonbExtractPathAsText
1314+ :: SListI elems
1315+ => Expression grp commons schemas params from (nullity 'PGjsonb)
1316+ -> NP (Expression grp commons schemas params from) elems
1317+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1318+jsonbExtractPathAsText x xs =
1319+ unsafeVariadicFunction "jsonb_extract_path_text" (x :* xs)
1320+
1321+-- | Returns the type of the outermost JSON value as a text string. Possible
1322+-- types are object, array, string, number, boolean, and null.
1323+jsonTypeof
1324+ :: Expression grp commons schemas params from (nullity 'PGjson)
1325+ -> Expression grp commons schemas params from (nullity 'PGtext)
1326+jsonTypeof = unsafeFunction "json_typeof"
1327+
1328+-- | Returns the type of the outermost binary JSON value as a text string.
1329+-- Possible types are object, array, string, number, boolean, and null.
1330+jsonbTypeof
1331+ :: Expression grp commons schemas params from (nullity 'PGjsonb)
1332+ -> Expression grp commons schemas params from (nullity 'PGtext)
1333+jsonbTypeof = unsafeFunction "jsonb_typeof"
1334+
1335+-- | Returns its argument with all object fields that have null values omitted.
1336+-- Other null values are untouched.
1337+jsonStripNulls
1338+ :: Expression grp commons schemas params from (nullity 'PGjson)
1339+ -> Expression grp commons schemas params from (nullity 'PGjson)
1340+jsonStripNulls = unsafeFunction "json_strip_nulls"
1341+
1342+-- | Returns its argument with all object fields that have null values omitted.
1343+-- Other null values are untouched.
1344+jsonbStripNulls
1345+ :: Expression grp commons schemas params from (nullity 'PGjsonb)
1346+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1347+jsonbStripNulls = unsafeFunction "jsonb_strip_nulls"
1348+
1349+-- | @ jsonbSet target path new_value create_missing @
1350+--
1351+-- Returns target with the section designated by path replaced by new_value,
1352+-- or with new_value added if create_missing is true ( default is true) and the
1353+-- item designated by path does not exist. As with the path orientated
1354+-- operators, negative integers that appear in path count from the end of JSON
1355+-- arrays.
1356+jsonbSet
1357+ :: PGTextArray "jsonbSet" arr
1358+ => Expression grp commons schemas params from (nullity 'PGjsonb)
1359+ -> Expression grp commons schemas params from (nullity arr)
1360+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1361+ -> Maybe (Expression grp commons schemas params from (nullity 'PGbool))
1362+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1363+jsonbSet tgt path val createMissing = case createMissing of
1364+ Just m -> unsafeVariadicFunction "jsonb_set" (tgt :* path :* val :* m :* Nil)
1365+ Nothing -> unsafeVariadicFunction "jsonb_set" (tgt :* path :* val :* Nil)
1366+
1367+-- | @ jsonbInsert target path new_value insert_after @
1368+--
1369+-- Returns target with new_value inserted. If target section designated by
1370+-- path is in a JSONB array, new_value will be inserted before target or after
1371+-- if insert_after is true (default is false). If target section designated by
1372+-- path is in JSONB object, new_value will be inserted only if target does not
1373+-- exist. As with the path orientated operators, negative integers that appear
1374+-- in path count from the end of JSON arrays.
1375+jsonbInsert
1376+ :: PGTextArray "jsonbInsert" arr
1377+ => Expression grp commons schemas params from (nullity 'PGjsonb)
1378+ -> Expression grp commons schemas params from (nullity arr)
1379+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1380+ -> Maybe (Expression grp commons schemas params from (nullity 'PGbool))
1381+ -> Expression grp commons schemas params from (nullity 'PGjsonb)
1382+jsonbInsert tgt path val insertAfter = case insertAfter of
1383+ Just i -> unsafeVariadicFunction "jsonb_insert" (tgt :* path :* val :* i :* Nil)
1384+ Nothing -> unsafeVariadicFunction "jsonb_insert" (tgt :* path :* val :* Nil)
1385+
1386+-- | Returns its argument as indented JSON text.
1387+jsonbPretty
1388+ :: Expression grp commons schemas params from (nullity 'PGjsonb)
1389+ -> Expression grp commons schemas params from (nullity 'PGtext)
1390+jsonbPretty = unsafeFunction "jsonb_pretty"
1391+
1392+{-----------------------------------------
1393+aggregation
1394+-----------------------------------------}
1395+
1396+class Aggregate expr aggr | aggr -> expr where
1397+
1398+
1399+
1400+ -- | A special aggregation that does not require an input
1401+ --
1402+ -- >>> printSQL countStar
1403+ -- count(*)
1404+ countStar :: aggr commons schemas params from ('NotNull 'PGint8)
1405+
1406+ -- | >>> :{
1407+ -- let
1408+ -- expression :: Expression (Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity ty]] ('NotNull 'PGint8)
1409+ -- expression = count (All #col)
1410+ -- in printSQL expression
1411+ -- :}
1412+ -- count(ALL "col")
1413+ count
1414+ :: expr commons schemas params from ty
1415+ -- ^ what to count
1416+ -> aggr commons schemas params from ('NotNull 'PGint8)
1417+
1418+ -- | >>> :{
1419+ -- let
1420+ -- expression :: Expression ('Grouped bys) commons schemas params '[tab ::: '["col" ::: 'Null 'PGnumeric]] ('Null 'PGnumeric)
1421+ -- expression = sum_ (Distinct #col)
1422+ -- in printSQL expression
1423+ -- :}
1424+ -- sum(DISTINCT "col")
1425+ sum_
1426+ :: ty `In` PGNum
1427+ => expr commons schemas params from (nullity ty)
1428+ -> aggr commons schemas params from (nullity ty)
1429+
1430+ -- | >>> :{
1431+ -- let
1432+ -- expression :: Expression ('Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity 'PGint4]] (nullity 'PGint4)
1433+ -- expression = bitAnd (Distinct #col)
1434+ -- in printSQL expression
1435+ -- :}
1436+ -- bit_and(DISTINCT "col")
1437+ bitAnd
1438+ :: int `In` PGIntegral
1439+ => expr commons schemas params from (nullity int)
1440+ -- ^ what to aggregate
1441+ -> aggr commons schemas params from (nullity int)
1442+
1443+ -- | >>> :{
1444+ -- let
1445+ -- expression :: Expression ('Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity 'PGint4]] (nullity 'PGint4)
1446+ -- expression = bitOr (All #col)
1447+ -- in printSQL expression
1448+ -- :}
1449+ -- bit_or(ALL "col")
1450+ bitOr
1451+ :: int `In` PGIntegral
1452+ => expr commons schemas params from (nullity int)
1453+ -- ^ what to aggregate
1454+ -> aggr commons schemas params from (nullity int)
1455+
1456+ -- | >>> :{
1457+ -- let
1458+ -- expression :: Expression (Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity 'PGbool]] (nullity 'PGbool)
1459+ -- expression = boolAnd (All #col)
1460+ -- in printSQL expression
1461+ -- :}
1462+ -- bool_and(ALL "col")
1463+ boolAnd
1464+ :: expr commons schemas params from (nullity 'PGbool)
1465+ -- ^ what to aggregate
1466+ -> aggr commons schemas params from (nullity 'PGbool)
1467+
1468+ -- | >>> :{
1469+ -- let
1470+ -- expression :: Expression (Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity 'PGbool]] (nullity 'PGbool)
1471+ -- expression = boolOr (All #col)
1472+ -- in printSQL expression
1473+ -- :}
1474+ -- bool_or(ALL "col")
1475+ boolOr
1476+ :: expr commons schemas params from (nullity 'PGbool)
1477+ -- ^ what to aggregate
1478+ -> aggr commons schemas params from (nullity 'PGbool)
1479+
1480+ -- | synonym for `boolAnd`
1481+ --
1482+ -- >>> :{
1483+ -- let
1484+ -- expression :: Expression (Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity 'PGbool]] (nullity 'PGbool)
1485+ -- expression = every (Distinct #col)
1486+ -- in printSQL expression
1487+ -- :}
1488+ -- every(DISTINCT "col")
1489+ every
1490+ :: expr commons schemas params from (nullity 'PGbool)
1491+ -- ^ what to aggregate
1492+ -> aggr commons schemas params from (nullity 'PGbool)
1493+
1494+ -- | maximum aggregation
1495+ max_
1496+ :: expr commons schemas params from (nullity ty)
1497+ -- ^ what to maximize
1498+ -> aggr commons schemas params from (nullity ty)
1499+
1500+ -- | minimum aggregation
1501+ min_
1502+ :: expr commons schemas params from (nullity ty)
1503+ -- ^ what to minimize
1504+ -> aggr commons schemas params from (nullity ty)
1505+
1506+ -- | average aggregation
1507+ avg
1508+ :: expr commons schemas params from (nullity ty)
1509+ -- ^ what to average
1510+ -> aggr commons schemas params from (nullity (PGAvg ty))
1511+
1512+data Distinction commons schemas params from ty
1513+ = All (Expression 'Ungrouped commons schemas params from ty)
1514+ | Distinct (Expression 'Ungrouped commons schemas params from ty)
1515+ deriving (GHC.Generic,Show,Eq,Ord)
1516+instance NFData (Distinction commons schemas params from ty)
1517+instance RenderSQL (Distinction commons schemas params from ty) where
1518+ renderSQL = \case
1519+ All x -> "ALL" <+> renderSQL x
1520+ Distinct x -> "DISTINCT" <+> renderSQL x
1521+
1522+instance Aggregate Distinction (Expression ('Grouped bys)) where
1523+ countStar = UnsafeExpression "count(*)"
1524+ count = unsafeAggregate "count"
1525+ sum_ = unsafeAggregate "sum"
1526+ bitAnd = unsafeAggregate "bit_and"
1527+ bitOr = unsafeAggregate "bit_or"
1528+ boolAnd = unsafeAggregate "bool_and"
1529+ boolOr = unsafeAggregate "bool_or"
1530+ every = unsafeAggregate "every"
1531+ max_ = unsafeAggregate "max"
1532+ min_ = unsafeAggregate "min"
1533+ avg = unsafeAggregate "avg"
1534+instance Aggregate (Expression grp) (WindowFunction grp) where
1535+ countStar = UnsafeWindowFunction "count(*)"
1536+ count = unsafeWindowFunction1 "count"
1537+ sum_ = unsafeWindowFunction1 "sum"
1538+ bitAnd = unsafeWindowFunction1 "bit_and"
1539+ bitOr = unsafeWindowFunction1 "bit_or"
1540+ boolAnd = unsafeWindowFunction1 "bool_and"
1541+ boolOr = unsafeWindowFunction1 "bool_or"
1542+ every = unsafeWindowFunction1 "every"
1543+ max_ = unsafeWindowFunction1 "max"
1544+ min_ = unsafeWindowFunction1 "min"
1545+ avg = unsafeWindowFunction1 "avg"
1546+
1547+-- | escape hatch to define aggregate functions
1548+unsafeAggregate
1549+ :: RenderSQL (expr commons schemas params from xty)
1550+ => ByteString -- ^ aggregate function
1551+ -> expr commons schemas params from xty
1552+ -> Expression ('Grouped bys) commons schemas params from yty
1553+unsafeAggregate fun x = UnsafeExpression $ mconcat
1554+ [fun, "(", renderSQL x, ")"]
1555+
1556+-- | A type family that calculates `PGAvg` type of a `PGType`.
1557+type family PGAvg ty where
1558+ PGAvg 'PGint2 = 'PGnumeric
1559+ PGAvg 'PGint4 = 'PGnumeric
1560+ PGAvg 'PGint8 = 'PGnumeric
1561+ PGAvg 'PGnumeric = 'PGnumeric
1562+ PGAvg 'PGfloat4 = 'PGfloat8
1563+ PGAvg 'PGfloat8 = 'PGfloat8
1564+ PGAvg 'PGinterval = 'PGinterval
1565+ -- PGAvg pg = TypeError
1566+
1567+{-----------------------------------------
1568+type expressions
1569+-----------------------------------------}
1570+
1571+-- | `TypeExpression`s are used in `cast`s and `createTable` commands.
1572+newtype TypeExpression (schemas :: SchemasType) (ty :: NullityType)
1573+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
1574+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
1575+instance RenderSQL (TypeExpression schemas ty) where
1576+ renderSQL = renderTypeExpression
1577+
1578+-- | The enum or composite type in a `Typedef` can be expressed by its alias.
1579+typedef
1580+ :: (Has sch schemas schema, Has td schema ('Typedef ty))
1581+ => QualifiedAlias sch td
1582+ -> TypeExpression schemas (nullity ty)
1583+typedef = UnsafeTypeExpression . renderSQL
1584+
1585+-- | The composite type corresponding to a `Table` definition can be expressed
1586+-- by its alias.
1587+typetable
1588+ :: (Has sch schemas schema, Has tab schema ('Table table))
1589+ => QualifiedAlias sch tab
1590+ -> TypeExpression schemas (nullity ('PGcomposite (TableToRow table)))
1591+typetable = UnsafeTypeExpression . renderSQL
1592+
1593+-- | The composite type corresponding to a `View` definition can be expressed
1594+-- by its alias.
1595+typeview
1596+ :: (Has sch schemas schema, Has vw schema ('View view))
1597+ => QualifiedAlias sch vw
1598+ -> TypeExpression schemas (nullity ('PGcomposite view))
1599+typeview = UnsafeTypeExpression . renderSQL
1600+
1601+-- | logical Boolean (true/false)
1602+bool :: TypeExpression schemas (nullity 'PGbool)
1603+bool = UnsafeTypeExpression "bool"
1604+-- | signed two-byte integer
1605+int2, smallint :: TypeExpression schemas (nullity 'PGint2)
1606+int2 = UnsafeTypeExpression "int2"
1607+smallint = UnsafeTypeExpression "smallint"
1608+-- | signed four-byte integer
1609+int4, int, integer :: TypeExpression schemas (nullity 'PGint4)
1610+int4 = UnsafeTypeExpression "int4"
1611+int = UnsafeTypeExpression "int"
1612+integer = UnsafeTypeExpression "integer"
1613+-- | signed eight-byte integer
1614+int8, bigint :: TypeExpression schemas (nullity 'PGint8)
1615+int8 = UnsafeTypeExpression "int8"
1616+bigint = UnsafeTypeExpression "bigint"
1617+-- | arbitrary precision numeric type
1618+numeric :: TypeExpression schemas (nullity 'PGnumeric)
1619+numeric = UnsafeTypeExpression "numeric"
1620+-- | single precision floating-point number (4 bytes)
1621+float4, real :: TypeExpression schemas (nullity 'PGfloat4)
1622+float4 = UnsafeTypeExpression "float4"
1623+real = UnsafeTypeExpression "real"
1624+-- | double precision floating-point number (8 bytes)
1625+float8, doublePrecision :: TypeExpression schemas (nullity 'PGfloat8)
1626+float8 = UnsafeTypeExpression "float8"
1627+doublePrecision = UnsafeTypeExpression "double precision"
1628+-- | variable-length character string
1629+text :: TypeExpression schemas (nullity 'PGtext)
1630+text = UnsafeTypeExpression "text"
1631+-- | fixed-length character string
1632+char, character
1633+ :: forall n schemas nullity. (KnownNat n, 1 <= n)
1634+ => TypeExpression schemas (nullity ('PGchar n))
1635+char = UnsafeTypeExpression $ "char(" <> renderNat @n <> ")"
1636+character = UnsafeTypeExpression $ "character(" <> renderNat @n <> ")"
1637+-- | variable-length character string
1638+varchar, characterVarying
1639+ :: forall n schemas nullity. (KnownNat n, 1 <= n)
1640+ => TypeExpression schemas (nullity ('PGvarchar n))
1641+varchar = UnsafeTypeExpression $ "varchar(" <> renderNat @n <> ")"
1642+characterVarying = UnsafeTypeExpression $
1643+ "character varying(" <> renderNat @n <> ")"
1644+-- | binary data ("byte array")
1645+bytea :: TypeExpression schemas (nullity 'PGbytea)
1646+bytea = UnsafeTypeExpression "bytea"
1647+-- | date and time (no time zone)
1648+timestamp :: TypeExpression schemas (nullity 'PGtimestamp)
1649+timestamp = UnsafeTypeExpression "timestamp"
1650+-- | date and time, including time zone
1651+timestampWithTimeZone :: TypeExpression schemas (nullity 'PGtimestamptz)
1652+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
1653+-- | calendar date (year, month, day)
1654+date :: TypeExpression schemas (nullity 'PGdate)
1655+date = UnsafeTypeExpression "date"
1656+-- | time of day (no time zone)
1657+time :: TypeExpression schemas (nullity 'PGtime)
1658+time = UnsafeTypeExpression "time"
1659+-- | time of day, including time zone
1660+timeWithTimeZone :: TypeExpression schemas (nullity 'PGtimetz)
1661+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
1662+-- | time span
1663+interval :: TypeExpression schemas (nullity 'PGinterval)
1664+interval = UnsafeTypeExpression "interval"
1665+-- | universally unique identifier
1666+uuid :: TypeExpression schemas (nullity 'PGuuid)
1667+uuid = UnsafeTypeExpression "uuid"
1668+-- | IPv4 or IPv6 host address
1669+inet :: TypeExpression schemas (nullity 'PGinet)
1670+inet = UnsafeTypeExpression "inet"
1671+-- | textual JSON data
1672+json :: TypeExpression schemas (nullity 'PGjson)
1673+json = UnsafeTypeExpression "json"
1674+-- | binary JSON data, decomposed
1675+jsonb :: TypeExpression schemas (nullity 'PGjsonb)
1676+jsonb = UnsafeTypeExpression "jsonb"
1677+-- | variable length array
1678+vararray
1679+ :: TypeExpression schemas pg
1680+ -> TypeExpression schemas (nullity ('PGvararray pg))
1681+vararray ty = UnsafeTypeExpression $ renderSQL ty <> "[]"
1682+-- | fixed length array
1683+--
1684+-- >>> renderSQL (fixarray @'[2] json)
1685+-- "json[2]"
1686+fixarray
1687+ :: forall dims schemas nullity pg. All KnownNat dims
1688+ => TypeExpression schemas pg
1689+ -> TypeExpression schemas (nullity ('PGfixarray dims pg))
1690+fixarray ty = UnsafeTypeExpression $
1691+ renderSQL ty <> renderDims @dims
1692+ where
1693+ renderDims :: forall ns. All KnownNat ns => ByteString
1694+ renderDims =
1695+ ("[" <>)
1696+ . (<> "]")
1697+ . ByteString.intercalate "]["
1698+ . hcollapse
1699+ $ hcmap (Proxy @KnownNat)
1700+ (K . fromString . show . natVal)
1701+ (hpure Proxy :: NP Proxy ns)
1702+
1703+-- | `pgtype` is a demoted version of a `PGType`
1704+class PGTyped schemas (ty :: NullityType) where
1705+ pgtype :: TypeExpression schemas ty
1706+instance PGTyped schemas (nullity 'PGbool) where pgtype = bool
1707+instance PGTyped schemas (nullity 'PGint2) where pgtype = int2
1708+instance PGTyped schemas (nullity 'PGint4) where pgtype = int4
1709+instance PGTyped schemas (nullity 'PGint8) where pgtype = int8
1710+instance PGTyped schemas (nullity 'PGnumeric) where pgtype = numeric
1711+instance PGTyped schemas (nullity 'PGfloat4) where pgtype = float4
1712+instance PGTyped schemas (nullity 'PGfloat8) where pgtype = float8
1713+instance PGTyped schemas (nullity 'PGtext) where pgtype = text
1714+instance (KnownNat n, 1 <= n)
1715+ => PGTyped schemas (nullity ('PGchar n)) where pgtype = char @n
1716+instance (KnownNat n, 1 <= n)
1717+ => PGTyped schemas (nullity ('PGvarchar n)) where pgtype = varchar @n
1718+instance PGTyped schemas (nullity 'PGbytea) where pgtype = bytea
1719+instance PGTyped schemas (nullity 'PGtimestamp) where pgtype = timestamp
1720+instance PGTyped schemas (nullity 'PGtimestamptz) where pgtype = timestampWithTimeZone
1721+instance PGTyped schemas (nullity 'PGdate) where pgtype = date
1722+instance PGTyped schemas (nullity 'PGtime) where pgtype = time
1723+instance PGTyped schemas (nullity 'PGtimetz) where pgtype = timeWithTimeZone
1724+instance PGTyped schemas (nullity 'PGinterval) where pgtype = interval
1725+instance PGTyped schemas (nullity 'PGuuid) where pgtype = uuid
1726+instance PGTyped schemas (nullity 'PGjson) where pgtype = json
1727+instance PGTyped schemas (nullity 'PGjsonb) where pgtype = jsonb
1728+instance PGTyped schemas ty
1729+ => PGTyped schemas (nullity ('PGvararray ty)) where
1730+ pgtype = vararray (pgtype @schemas @ty)
1731+instance (All KnownNat dims, PGTyped schemas ty)
1732+ => PGTyped schemas (nullity ('PGfixarray dims ty)) where
1733+ pgtype = fixarray @dims (pgtype @schemas @ty)
1734+
1735+{-----------------------------------------
1736+Sorting
1737+-----------------------------------------}
1738+
1739+-- | `SortExpression`s are used by `sortBy` to optionally sort the results
1740+-- of a `Query`. `Asc` or `Desc` set the sort direction of a `NotNull` result
1741+-- column to ascending or descending. Ascending order puts smaller values
1742+-- first, where "smaller" is defined in terms of the `.<` operator. Similarly,
1743+-- descending order is determined with the `.>` operator. `AscNullsFirst`,
1744+-- `AscNullsLast`, `DescNullsFirst` and `DescNullsLast` options are used to
1745+-- determine whether nulls appear before or after non-null values in the sort
1746+-- ordering of a `Null` result column.
1747+data SortExpression grp commons schemas params from where
1748+ Asc
1749+ :: Expression grp commons schemas params from ('NotNull ty)
1750+ -> SortExpression grp commons schemas params from
1751+ Desc
1752+ :: Expression grp commons schemas params from ('NotNull ty)
1753+ -> SortExpression grp commons schemas params from
1754+ AscNullsFirst
1755+ :: Expression grp commons schemas params from ('Null ty)
1756+ -> SortExpression grp commons schemas params from
1757+ AscNullsLast
1758+ :: Expression grp commons schemas params from ('Null ty)
1759+ -> SortExpression grp commons schemas params from
1760+ DescNullsFirst
1761+ :: Expression grp commons schemas params from ('Null ty)
1762+ -> SortExpression grp commons schemas params from
1763+ DescNullsLast
1764+ :: Expression grp commons schemas params from ('Null ty)
1765+ -> SortExpression grp commons schemas params from
1766+deriving instance Show (SortExpression grp commons schemas params from)
1767+
1768+class OrderBy expr where
1769+ orderBy
1770+ :: [SortExpression grp commons schemas params from]
1771+ -> expr grp commons schemas params from
1772+ -> expr grp commons schemas params from
1773+
1774+-- | Render a `SortExpression`.
1775+instance RenderSQL (SortExpression grp commons schemas params from) where
1776+ renderSQL = \case
1777+ Asc expression -> renderSQL expression <+> "ASC"
1778+ Desc expression -> renderSQL expression <+> "DESC"
1779+ AscNullsFirst expression -> renderSQL expression
1780+ <+> "ASC NULLS FIRST"
1781+ DescNullsFirst expression -> renderSQL expression
1782+ <+> "DESC NULLS FIRST"
1783+ AscNullsLast expression -> renderSQL expression <+> "ASC NULLS LAST"
1784+ DescNullsLast expression -> renderSQL expression <+> "DESC NULLS LAST"
1785+
1786+data WindowDefinition grp commons schemas params from where
1787+ WindowDefinition
1788+ :: SListI bys
1789+ => NP (Expression grp commons schemas params from) bys
1790+ -> [SortExpression grp commons schemas params from]
1791+ -> WindowDefinition grp commons schemas params from
1792+
1793+instance OrderBy WindowDefinition where
1794+ orderBy sortsR (WindowDefinition parts sortsL)
1795+ = WindowDefinition parts (sortsL ++ sortsR)
1796+
1797+instance RenderSQL (WindowDefinition commons schemas from grp params) where
1798+ renderSQL (WindowDefinition part ord) =
1799+ renderPartitionByClause part <> renderOrderByClause ord
1800+ where
1801+ renderPartitionByClause = \case
1802+ Nil -> ""
1803+ parts -> "PARTITION" <+> "BY" <+> renderCommaSeparated renderExpression parts
1804+ renderOrderByClause = \case
1805+ [] -> ""
1806+ srts -> " ORDER" <+> "BY"
1807+ <+> commaSeparated (renderSQL <$> srts)
1808+
1809+partitionBy
1810+ :: SListI bys
1811+ => NP (Expression grp commons schemas params from) bys
1812+ -> WindowDefinition grp commons schemas params from
1813+partitionBy bys = WindowDefinition bys []
1814+
1815+newtype WindowFunction
1816+ (grp :: Grouping)
1817+ (commons :: FromType)
1818+ (schemas :: SchemasType)
1819+ (params :: [NullityType])
1820+ (from :: FromType)
1821+ (ty :: NullityType)
1822+ = UnsafeWindowFunction { renderWindowFunction :: ByteString }
1823+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
1824+
1825+instance RenderSQL (WindowFunction grp commons schemas params from ty) where
1826+ renderSQL = renderWindowFunction
1827+
1828+unsafeWindowFunction1
1829+ :: ByteString
1830+ -> Expression grp commons schemas params from ty0
1831+ -> WindowFunction grp commons schemas params from ty1
1832+unsafeWindowFunction1 fun x
1833+ = UnsafeWindowFunction $ fun <> parenthesized (renderSQL x)
1834+
1835+{- | rank of the current row with gaps; same as `rowNumber` of its first peer
1836+>>> printSQL rank
1837+rank()
1838+-}
1839+rank :: WindowFunction grp commons schemas params from ('NotNull 'PGint8)
1840+rank = UnsafeWindowFunction "rank()"
1841+
1842+{- | number of the current row within its partition, counting from 1
1843+>>> printSQL rowNumber
1844+row_number()
1845+-}
1846+rowNumber :: WindowFunction grp commons schemas params from ('NotNull 'PGint8)
1847+rowNumber = UnsafeWindowFunction "row_number()"
1848+
1849+{- | rank of the current row without gaps; this function counts peer groups
1850+>>> printSQL denseRank
1851+dense_rank()
1852+-}
1853+denseRank :: WindowFunction grp commons schemas params from ('NotNull 'PGint8)
1854+denseRank = UnsafeWindowFunction "dense_rank()"
1855+
1856+{- | relative rank of the current row: (rank - 1) / (total partition rows - 1)
1857+>>> printSQL percentRank
1858+percent_rank()
1859+-}
1860+percentRank :: WindowFunction grp commons schemas params from ('NotNull 'PGfloat8)
1861+percentRank = UnsafeWindowFunction "percent_rank()"
1862+
1863+{- | cumulative distribution: (number of partition rows
1864+preceding or peer with current row) / total partition rows
1865+>>> printSQL cumeDist
1866+cume_dist()
1867+-}
1868+cumeDist :: WindowFunction grp commons schemas params from ('NotNull 'PGfloat8)
1869+cumeDist = UnsafeWindowFunction "cume_dist()"
1870+
1871+{- | integer ranging from 1 to the argument value,
1872+dividing the partition as equally as possible
1873+-}
1874+ntile
1875+ :: Expression grp commons schemas params from ('NotNull 'PGint4)
1876+ -- ^ num buckets
1877+ -> WindowFunction grp commons schemas params from ('NotNull 'PGint4)
1878+ntile numBuckets = UnsafeWindowFunction $ "ntile"
1879+ <> parenthesized (renderSQL numBuckets)
1880+
1881+{- | returns value evaluated at the row that is offset rows before the current
1882+row within the partition; if there is no such row, instead return default
1883+(which must be of the same type as value). Both offset and default are
1884+evaluated with respect to the current row.
1885+-}
1886+lag
1887+ :: Expression grp commons schemas params from ty
1888+ -- ^ value
1889+ -> Expression grp commons schemas params from ('NotNull 'PGint4)
1890+ -- ^ offset
1891+ -> Expression grp commons schemas params from ty
1892+ -- ^ default
1893+ -> WindowFunction grp commons schemas params from ty
1894+lag value offset def = UnsafeWindowFunction $ "lag"
1895+ <> parenthesized
1896+ (commaSeparated ([renderSQL value, renderSQL offset, renderSQL def]))
1897+
1898+{- | returns value evaluated at the row that is offset rows after the current
1899+row within the partition; if there is no such row, instead return default
1900+(which must be of the same type as value). Both offset and default are
1901+evaluated with respect to the current row.
1902+-}
1903+lead
1904+ :: Expression grp commons schemas params from ty
1905+ -- ^ value
1906+ -> Expression grp commons schemas params from ('NotNull 'PGint4)
1907+ -- ^ offset
1908+ -> Expression grp commons schemas params from ty
1909+ -- ^ default
1910+ -> WindowFunction grp commons schemas params from ty
1911+lead value offset def = UnsafeWindowFunction $ "lag"
1912+ <> parenthesized
1913+ (commaSeparated ([renderSQL value, renderSQL offset, renderSQL def]))
1914+
1915+{- | returns value evaluated at the row that is the
1916+first row of the window frame
1917+-}
1918+firstValue
1919+ :: Expression grp commons schemas params from ty
1920+ -- ^ value
1921+ -> WindowFunction grp commons schemas params from ty
1922+firstValue value = UnsafeWindowFunction $ "first_value"
1923+ <> parenthesized (renderSQL value)
1924+
1925+{- | returns value evaluated at the row that is the
1926+last row of the window frame
1927+-}
1928+lastValue
1929+ :: Expression grp commons schemas params from ty
1930+ -- ^ value
1931+ -> WindowFunction grp commons schemas params from ty
1932+lastValue value = UnsafeWindowFunction $ "last_value"
1933+ <> parenthesized (renderSQL value)
1934+
1935+{- | returns value evaluated at the row that is the nth
1936+row of the window frame (counting from 1); null if no such row
1937+-}
1938+nthValue
1939+ :: Expression grp commons schemas params from (nullity ty)
1940+ -- ^ value
1941+ -> Expression grp commons schemas params from ('NotNull 'PGint4)
1942+ -- ^ nth
1943+ -> WindowFunction grp commons schemas params from ('Null ty)
1944+nthValue value nth = UnsafeWindowFunction $ "nth_value"
1945+ <> parenthesized (commaSeparated [renderSQL value, renderSQL nth])