@@ -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,1326 @@ 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+ -- >>> :{
1403+ -- let
1404+ -- expression :: Expression (Grouped bys) commons schemas params from ('NotNull 'PGint8)
1405+ -- expression = countStar
1406+ -- in printSQL expression
1407+ -- count(*)
1408+ countStar :: aggr commons schemas params from ('NotNull 'PGint8)
1409+
1410+ -- | >>> :{
1411+ -- let
1412+ -- expression :: Expression (Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity ty]] ('NotNull 'PGint8)
1413+ -- expression = count (All #col)
1414+ -- in printSQL expression
1415+ -- :}
1416+ -- count(ALL "col")
1417+ count
1418+ :: expr commons schemas params from ty
1419+ -- ^ what to count
1420+ -> aggr commons schemas params from ('NotNull 'PGint8)
1421+
1422+ -- | >>> :{
1423+ -- let
1424+ -- expression :: Expression ('Grouped bys) commons schemas params '[tab ::: '["col" ::: 'Null 'PGnumeric]] ('Null 'PGnumeric)
1425+ -- expression = sum_ (Distinct #col)
1426+ -- in printSQL expression
1427+ -- :}
1428+ -- sum(DISTINCT "col")
1429+ sum_
1430+ :: ty `In` PGNum
1431+ => expr commons schemas params from (nullity ty)
1432+ -> aggr commons schemas params from (nullity ty)
1433+
1434+ -- | >>> :{
1435+ -- let
1436+ -- expression :: Expression ('Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity 'PGint4]] (nullity 'PGint4)
1437+ -- expression = bitAnd (Distinct #col)
1438+ -- in printSQL expression
1439+ -- :}
1440+ -- bit_and(DISTINCT "col")
1441+ bitAnd
1442+ :: int `In` PGIntegral
1443+ => expr commons schemas params from (nullity int)
1444+ -- ^ what to aggregate
1445+ -> aggr commons schemas params from (nullity int)
1446+
1447+ -- | >>> :{
1448+ -- let
1449+ -- expression :: Expression ('Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity 'PGint4]] (nullity 'PGint4)
1450+ -- expression = bitOr (All #col)
1451+ -- in printSQL expression
1452+ -- :}
1453+ -- bit_or(ALL "col")
1454+ bitOr
1455+ :: int `In` PGIntegral
1456+ => expr commons schemas params from (nullity int)
1457+ -- ^ what to aggregate
1458+ -> aggr commons schemas params from (nullity int)
1459+
1460+ -- | >>> :{
1461+ -- let
1462+ -- expression :: Expression (Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity 'PGbool]] (nullity 'PGbool)
1463+ -- expression = boolAnd (All #col)
1464+ -- in printSQL expression
1465+ -- :}
1466+ -- bool_and(ALL "col")
1467+ boolAnd
1468+ :: expr commons schemas params from (nullity 'PGbool)
1469+ -- ^ what to aggregate
1470+ -> aggr commons schemas params from (nullity 'PGbool)
1471+
1472+ -- | >>> :{
1473+ -- let
1474+ -- expression :: Expression (Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity 'PGbool]] (nullity 'PGbool)
1475+ -- expression = boolOr (All #col)
1476+ -- in printSQL expression
1477+ -- :}
1478+ -- bool_or(ALL "col")
1479+ boolOr
1480+ :: expr commons schemas params from (nullity 'PGbool)
1481+ -- ^ what to aggregate
1482+ -> aggr commons schemas params from (nullity 'PGbool)
1483+
1484+ -- | synonym for `boolAnd`
1485+ --
1486+ -- >>> :{
1487+ -- let
1488+ -- expression :: Expression (Grouped bys) commons schemas params '[tab ::: '["col" ::: nullity 'PGbool]] (nullity 'PGbool)
1489+ -- expression = every (Distinct #col)
1490+ -- in printSQL expression
1491+ -- :}
1492+ -- every(DISTINCT "col")
1493+ every
1494+ :: expr commons schemas params from (nullity 'PGbool)
1495+ -- ^ what to aggregate
1496+ -> aggr commons schemas params from (nullity 'PGbool)
1497+
1498+ -- | maximum aggregation
1499+ max_
1500+ :: expr commons schemas params from (nullity ty)
1501+ -- ^ what to maximize
1502+ -> aggr commons schemas params from (nullity ty)
1503+
1504+ -- | minimum aggregation
1505+ min_
1506+ :: expr commons schemas params from (nullity ty)
1507+ -- ^ what to minimize
1508+ -> aggr commons schemas params from (nullity ty)
1509+
1510+ -- | average aggregation
1511+ avg
1512+ :: expr commons schemas params from (nullity ty)
1513+ -- ^ what to average
1514+ -> aggr commons schemas params from (nullity (PGAvg ty))
1515+
1516+data Distinction commons schemas params from ty
1517+ = All (Expression 'Ungrouped commons schemas params from ty)
1518+ | Distinct (Expression 'Ungrouped commons schemas params from ty)
1519+ deriving (GHC.Generic,Show,Eq,Ord)
1520+instance NFData (Distinction commons schemas params from ty)
1521+instance RenderSQL (Distinction commons schemas params from ty) where
1522+ renderSQL = \case
1523+ All x -> "ALL" <+> renderSQL x
1524+ Distinct x -> "DISTINCT" <+> renderSQL x
1525+
1526+instance Aggregate Distinction (Expression ('Grouped bys)) where
1527+ countStar = UnsafeExpression "count(*)"
1528+ count = unsafeAggregate "count"
1529+ sum_ = unsafeAggregate "sum"
1530+ bitAnd = unsafeAggregate "bit_and"
1531+ bitOr = unsafeAggregate "bit_or"
1532+ boolAnd = unsafeAggregate "bool_and"
1533+ boolOr = unsafeAggregate "bool_or"
1534+ every = unsafeAggregate "every"
1535+ max_ = unsafeAggregate "max"
1536+ min_ = unsafeAggregate "min"
1537+ avg = unsafeAggregate "avg"
1538+instance Aggregate (Expression grp) (WindowFunction grp) where
1539+ countStar = UnsafeWindowFunction "count(*)"
1540+ count = unsafeWindowFunction1 "count"
1541+ sum_ = unsafeWindowFunction1 "sum"
1542+ bitAnd = unsafeWindowFunction1 "bit_and"
1543+ bitOr = unsafeWindowFunction1 "bit_or"
1544+ boolAnd = unsafeWindowFunction1 "bool_and"
1545+ boolOr = unsafeWindowFunction1 "bool_or"
1546+ every = unsafeWindowFunction1 "every"
1547+ max_ = unsafeWindowFunction1 "max"
1548+ min_ = unsafeWindowFunction1 "min"
1549+ avg = unsafeWindowFunction1 "avg"
1550+
1551+-- | escape hatch to define aggregate functions
1552+unsafeAggregate
1553+ :: RenderSQL (expr commons schemas params from xty)
1554+ => ByteString -- ^ aggregate function
1555+ -> expr commons schemas params from xty
1556+ -> Expression ('Grouped bys) commons schemas params from yty
1557+unsafeAggregate fun x = UnsafeExpression $ mconcat
1558+ [fun, "(", renderSQL x, ")"]
1559+
1560+-- | A type family that calculates `PGAvg` type of a `PGType`.
1561+type family PGAvg ty where
1562+ PGAvg 'PGint2 = 'PGnumeric
1563+ PGAvg 'PGint4 = 'PGnumeric
1564+ PGAvg 'PGint8 = 'PGnumeric
1565+ PGAvg 'PGnumeric = 'PGnumeric
1566+ PGAvg 'PGfloat4 = 'PGfloat8
1567+ PGAvg 'PGfloat8 = 'PGfloat8
1568+ PGAvg 'PGinterval = 'PGinterval
1569+ -- PGAvg pg = TypeError
1570+
1571+{-----------------------------------------
1572+type expressions
1573+-----------------------------------------}
1574+
1575+-- | `TypeExpression`s are used in `cast`s and `createTable` commands.
1576+newtype TypeExpression (schemas :: SchemasType) (ty :: NullityType)
1577+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
1578+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
1579+instance RenderSQL (TypeExpression schemas ty) where
1580+ renderSQL = renderTypeExpression
1581+
1582+-- | The enum or composite type in a `Typedef` can be expressed by its alias.
1583+typedef
1584+ :: (Has sch schemas schema, Has td schema ('Typedef ty))
1585+ => QualifiedAlias sch td
1586+ -> TypeExpression schemas (nullity ty)
1587+typedef = UnsafeTypeExpression . renderSQL
1588+
1589+-- | The composite type corresponding to a `Table` definition can be expressed
1590+-- by its alias.
1591+typetable
1592+ :: (Has sch schemas schema, Has tab schema ('Table table))
1593+ => QualifiedAlias sch tab
1594+ -> TypeExpression schemas (nullity ('PGcomposite (TableToRow table)))
1595+typetable = UnsafeTypeExpression . renderSQL
1596+
1597+-- | The composite type corresponding to a `View` definition can be expressed
1598+-- by its alias.
1599+typeview
1600+ :: (Has sch schemas schema, Has vw schema ('View view))
1601+ => QualifiedAlias sch vw
1602+ -> TypeExpression schemas (nullity ('PGcomposite view))
1603+typeview = UnsafeTypeExpression . renderSQL
1604+
1605+-- | logical Boolean (true/false)
1606+bool :: TypeExpression schemas (nullity 'PGbool)
1607+bool = UnsafeTypeExpression "bool"
1608+-- | signed two-byte integer
1609+int2, smallint :: TypeExpression schemas (nullity 'PGint2)
1610+int2 = UnsafeTypeExpression "int2"
1611+smallint = UnsafeTypeExpression "smallint"
1612+-- | signed four-byte integer
1613+int4, int, integer :: TypeExpression schemas (nullity 'PGint4)
1614+int4 = UnsafeTypeExpression "int4"
1615+int = UnsafeTypeExpression "int"
1616+integer = UnsafeTypeExpression "integer"
1617+-- | signed eight-byte integer
1618+int8, bigint :: TypeExpression schemas (nullity 'PGint8)
1619+int8 = UnsafeTypeExpression "int8"
1620+bigint = UnsafeTypeExpression "bigint"
1621+-- | arbitrary precision numeric type
1622+numeric :: TypeExpression schemas (nullity 'PGnumeric)
1623+numeric = UnsafeTypeExpression "numeric"
1624+-- | single precision floating-point number (4 bytes)
1625+float4, real :: TypeExpression schemas (nullity 'PGfloat4)
1626+float4 = UnsafeTypeExpression "float4"
1627+real = UnsafeTypeExpression "real"
1628+-- | double precision floating-point number (8 bytes)
1629+float8, doublePrecision :: TypeExpression schemas (nullity 'PGfloat8)
1630+float8 = UnsafeTypeExpression "float8"
1631+doublePrecision = UnsafeTypeExpression "double precision"
1632+-- | variable-length character string
1633+text :: TypeExpression schemas (nullity 'PGtext)
1634+text = UnsafeTypeExpression "text"
1635+-- | fixed-length character string
1636+char, character
1637+ :: forall n schemas nullity. (KnownNat n, 1 <= n)
1638+ => TypeExpression schemas (nullity ('PGchar n))
1639+char = UnsafeTypeExpression $ "char(" <> renderNat @n <> ")"
1640+character = UnsafeTypeExpression $ "character(" <> renderNat @n <> ")"
1641+-- | variable-length character string
1642+varchar, characterVarying
1643+ :: forall n schemas nullity. (KnownNat n, 1 <= n)
1644+ => TypeExpression schemas (nullity ('PGvarchar n))
1645+varchar = UnsafeTypeExpression $ "varchar(" <> renderNat @n <> ")"
1646+characterVarying = UnsafeTypeExpression $
1647+ "character varying(" <> renderNat @n <> ")"
1648+-- | binary data ("byte array")
1649+bytea :: TypeExpression schemas (nullity 'PGbytea)
1650+bytea = UnsafeTypeExpression "bytea"
1651+-- | date and time (no time zone)
1652+timestamp :: TypeExpression schemas (nullity 'PGtimestamp)
1653+timestamp = UnsafeTypeExpression "timestamp"
1654+-- | date and time, including time zone
1655+timestampWithTimeZone :: TypeExpression schemas (nullity 'PGtimestamptz)
1656+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
1657+-- | calendar date (year, month, day)
1658+date :: TypeExpression schemas (nullity 'PGdate)
1659+date = UnsafeTypeExpression "date"
1660+-- | time of day (no time zone)
1661+time :: TypeExpression schemas (nullity 'PGtime)
1662+time = UnsafeTypeExpression "time"
1663+-- | time of day, including time zone
1664+timeWithTimeZone :: TypeExpression schemas (nullity 'PGtimetz)
1665+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
1666+-- | time span
1667+interval :: TypeExpression schemas (nullity 'PGinterval)
1668+interval = UnsafeTypeExpression "interval"
1669+-- | universally unique identifier
1670+uuid :: TypeExpression schemas (nullity 'PGuuid)
1671+uuid = UnsafeTypeExpression "uuid"
1672+-- | IPv4 or IPv6 host address
1673+inet :: TypeExpression schemas (nullity 'PGinet)
1674+inet = UnsafeTypeExpression "inet"
1675+-- | textual JSON data
1676+json :: TypeExpression schemas (nullity 'PGjson)
1677+json = UnsafeTypeExpression "json"
1678+-- | binary JSON data, decomposed
1679+jsonb :: TypeExpression schemas (nullity 'PGjsonb)
1680+jsonb = UnsafeTypeExpression "jsonb"
1681+-- | variable length array
1682+vararray
1683+ :: TypeExpression schemas pg
1684+ -> TypeExpression schemas (nullity ('PGvararray pg))
1685+vararray ty = UnsafeTypeExpression $ renderSQL ty <> "[]"
1686+-- | fixed length array
1687+--
1688+-- >>> renderSQL (fixarray @'[2] json)
1689+-- "json[2]"
1690+fixarray
1691+ :: forall dims schemas nullity pg. All KnownNat dims
1692+ => TypeExpression schemas pg
1693+ -> TypeExpression schemas (nullity ('PGfixarray dims pg))
1694+fixarray ty = UnsafeTypeExpression $
1695+ renderSQL ty <> renderDims @dims
1696+ where
1697+ renderDims :: forall ns. All KnownNat ns => ByteString
1698+ renderDims =
1699+ ("[" <>)
1700+ . (<> "]")
1701+ . ByteString.intercalate "]["
1702+ . hcollapse
1703+ $ hcmap (Proxy @KnownNat)
1704+ (K . fromString . show . natVal)
1705+ (hpure Proxy :: NP Proxy ns)
1706+
1707+-- | `pgtype` is a demoted version of a `PGType`
1708+class PGTyped schemas (ty :: NullityType) where
1709+ pgtype :: TypeExpression schemas ty
1710+instance PGTyped schemas (nullity 'PGbool) where pgtype = bool
1711+instance PGTyped schemas (nullity 'PGint2) where pgtype = int2
1712+instance PGTyped schemas (nullity 'PGint4) where pgtype = int4
1713+instance PGTyped schemas (nullity 'PGint8) where pgtype = int8
1714+instance PGTyped schemas (nullity 'PGnumeric) where pgtype = numeric
1715+instance PGTyped schemas (nullity 'PGfloat4) where pgtype = float4
1716+instance PGTyped schemas (nullity 'PGfloat8) where pgtype = float8
1717+instance PGTyped schemas (nullity 'PGtext) where pgtype = text
1718+instance (KnownNat n, 1 <= n)
1719+ => PGTyped schemas (nullity ('PGchar n)) where pgtype = char @n
1720+instance (KnownNat n, 1 <= n)
1721+ => PGTyped schemas (nullity ('PGvarchar n)) where pgtype = varchar @n
1722+instance PGTyped schemas (nullity 'PGbytea) where pgtype = bytea
1723+instance PGTyped schemas (nullity 'PGtimestamp) where pgtype = timestamp
1724+instance PGTyped schemas (nullity 'PGtimestamptz) where pgtype = timestampWithTimeZone
1725+instance PGTyped schemas (nullity 'PGdate) where pgtype = date
1726+instance PGTyped schemas (nullity 'PGtime) where pgtype = time
1727+instance PGTyped schemas (nullity 'PGtimetz) where pgtype = timeWithTimeZone
1728+instance PGTyped schemas (nullity 'PGinterval) where pgtype = interval
1729+instance PGTyped schemas (nullity 'PGuuid) where pgtype = uuid
1730+instance PGTyped schemas (nullity 'PGjson) where pgtype = json
1731+instance PGTyped schemas (nullity 'PGjsonb) where pgtype = jsonb
1732+instance PGTyped schemas ty
1733+ => PGTyped schemas (nullity ('PGvararray ty)) where
1734+ pgtype = vararray (pgtype @schemas @ty)
1735+instance (All KnownNat dims, PGTyped schemas ty)
1736+ => PGTyped schemas (nullity ('PGfixarray dims ty)) where
1737+ pgtype = fixarray @dims (pgtype @schemas @ty)
1738+
1739+{-----------------------------------------
1740+Sorting
1741+-----------------------------------------}
1742+
1743+-- | `SortExpression`s are used by `sortBy` to optionally sort the results
1744+-- of a `Query`. `Asc` or `Desc` set the sort direction of a `NotNull` result
1745+-- column to ascending or descending. Ascending order puts smaller values
1746+-- first, where "smaller" is defined in terms of the `.<` operator. Similarly,
1747+-- descending order is determined with the `.>` operator. `AscNullsFirst`,
1748+-- `AscNullsLast`, `DescNullsFirst` and `DescNullsLast` options are used to
1749+-- determine whether nulls appear before or after non-null values in the sort
1750+-- ordering of a `Null` result column.
1751+data SortExpression grp commons schemas params from where
1752+ Asc
1753+ :: Expression grp commons schemas params from ('NotNull ty)
1754+ -> SortExpression grp commons schemas params from
1755+ Desc
1756+ :: Expression grp commons schemas params from ('NotNull ty)
1757+ -> SortExpression grp commons schemas params from
1758+ AscNullsFirst
1759+ :: Expression grp commons schemas params from ('Null ty)
1760+ -> SortExpression grp commons schemas params from
1761+ AscNullsLast
1762+ :: Expression grp commons schemas params from ('Null ty)
1763+ -> SortExpression grp commons schemas params from
1764+ DescNullsFirst
1765+ :: Expression grp commons schemas params from ('Null ty)
1766+ -> SortExpression grp commons schemas params from
1767+ DescNullsLast
1768+ :: Expression grp commons schemas params from ('Null ty)
1769+ -> SortExpression grp commons schemas params from
1770+deriving instance Show (SortExpression grp commons schemas params from)
1771+
1772+class OrderBy expr where
1773+ orderBy
1774+ :: [SortExpression grp commons schemas params from]
1775+ -> expr grp commons schemas params from
1776+ -> expr grp commons schemas params from
1777+
1778+-- | Render a `SortExpression`.
1779+instance RenderSQL (SortExpression grp commons schemas params from) where
1780+ renderSQL = \case
1781+ Asc expression -> renderSQL expression <+> "ASC"
1782+ Desc expression -> renderSQL expression <+> "DESC"
1783+ AscNullsFirst expression -> renderSQL expression
1784+ <+> "ASC NULLS FIRST"
1785+ DescNullsFirst expression -> renderSQL expression
1786+ <+> "DESC NULLS FIRST"
1787+ AscNullsLast expression -> renderSQL expression <+> "ASC NULLS LAST"
1788+ DescNullsLast expression -> renderSQL expression <+> "DESC NULLS LAST"
1789+
1790+data WindowDefinition grp commons schemas params from where
1791+ WindowDefinition
1792+ :: SListI bys
1793+ => NP (Expression grp commons schemas params from) bys
1794+ -> [SortExpression grp commons schemas params from]
1795+ -> WindowDefinition grp commons schemas params from
1796+
1797+instance OrderBy WindowDefinition where
1798+ orderBy sortsR (WindowDefinition parts sortsL)
1799+ = WindowDefinition parts (sortsL ++ sortsR)
1800+
1801+instance RenderSQL (WindowDefinition commons schemas from grp params) where
1802+ renderSQL (WindowDefinition part ord) =
1803+ renderPartitionByClause part <> renderOrderByClause ord
1804+ where
1805+ renderPartitionByClause = \case
1806+ Nil -> ""
1807+ parts -> "PARTITION" <+> "BY" <+> renderCommaSeparated renderExpression parts
1808+ renderOrderByClause = \case
1809+ [] -> ""
1810+ srts -> " ORDER" <+> "BY"
1811+ <+> commaSeparated (renderSQL <$> srts)
1812+
1813+partitionBy
1814+ :: SListI bys
1815+ => NP (Expression grp commons schemas params from) bys
1816+ -> WindowDefinition grp commons schemas params from
1817+partitionBy bys = WindowDefinition bys []
1818+
1819+newtype WindowFunction
1820+ (grp :: Grouping)
1821+ (commons :: FromType)
1822+ (schemas :: SchemasType)
1823+ (params :: [NullityType])
1824+ (from :: FromType)
1825+ (ty :: NullityType)
1826+ = UnsafeWindowFunction { renderWindowFunction :: ByteString }
1827+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
1828+
1829+instance RenderSQL (WindowFunction grp commons schemas params from ty) where
1830+ renderSQL = renderWindowFunction
1831+
1832+unsafeWindowFunction1
1833+ :: ByteString
1834+ -> Expression grp commons schemas params from ty0
1835+ -> WindowFunction grp commons schemas params from ty1
1836+unsafeWindowFunction1 fun x
1837+ = UnsafeWindowFunction $ fun <> parenthesized (renderSQL x)
1838+
1839+{- | rank of the current row with gaps; same as `rowNumber` of its first peer
1840+>>> printSQL rank
1841+rank()
1842+-}
1843+rank :: WindowFunction grp commons schemas params from ('NotNull 'PGint8)
1844+rank = UnsafeWindowFunction "rank()"
1845+
1846+{- | number of the current row within its partition, counting from 1
1847+>>> printSQL rowNumber
1848+row_number()
1849+-}
1850+rowNumber :: WindowFunction grp commons schemas params from ('NotNull 'PGint8)
1851+rowNumber = UnsafeWindowFunction "row_number()"
1852+
1853+{- | rank of the current row without gaps; this function counts peer groups
1854+>>> printSQL denseRank
1855+dense_rank()
1856+-}
1857+denseRank :: WindowFunction grp commons schemas params from ('NotNull 'PGint8)
1858+denseRank = UnsafeWindowFunction "dense_rank()"
1859+
1860+{- | relative rank of the current row: (rank - 1) / (total partition rows - 1)
1861+>>> printSQL percentRank
1862+percent_rank()
1863+-}
1864+percentRank :: WindowFunction grp commons schemas params from ('NotNull 'PGfloat8)
1865+percentRank = UnsafeWindowFunction "percent_rank()"
1866+
1867+{- | cumulative distribution: (number of partition rows
1868+preceding or peer with current row) / total partition rows
1869+>>> printSQL cumeDist
1870+cume_dist()
1871+-}
1872+cumeDist :: WindowFunction grp commons schemas params from ('NotNull 'PGfloat8)
1873+cumeDist = UnsafeWindowFunction "cume_dist()"
1874+
1875+{- | integer ranging from 1 to the argument value,
1876+dividing the partition as equally as possible
1877+-}
1878+ntile
1879+ :: Expression grp commons schemas params from ('NotNull 'PGint4)
1880+ -- ^ num buckets
1881+ -> WindowFunction grp commons schemas params from ('NotNull 'PGint4)
1882+ntile numBuckets = UnsafeWindowFunction $ "ntile"
1883+ <> parenthesized (renderSQL numBuckets)
1884+
1885+{- | returns value evaluated at the row that is offset rows before the current
1886+row within the partition; if there is no such row, instead return default
1887+(which must be of the same type as value). Both offset and default are
1888+evaluated with respect to the current row.
1889+-}
1890+lag
1891+ :: Expression grp commons schemas params from ty
1892+ -- ^ value
1893+ -> Expression grp commons schemas params from ('NotNull 'PGint4)
1894+ -- ^ offset
1895+ -> Expression grp commons schemas params from ty
1896+ -- ^ default
1897+ -> WindowFunction grp commons schemas params from ty
1898+lag value offset def = UnsafeWindowFunction $ "lag"
1899+ <> parenthesized
1900+ (commaSeparated ([renderSQL value, renderSQL offset, renderSQL def]))
1901+
1902+{- | returns value evaluated at the row that is offset rows after the current
1903+row within the partition; if there is no such row, instead return default
1904+(which must be of the same type as value). Both offset and default are
1905+evaluated with respect to the current row.
1906+-}
1907+lead
1908+ :: Expression grp commons schemas params from ty
1909+ -- ^ value
1910+ -> Expression grp commons schemas params from ('NotNull 'PGint4)
1911+ -- ^ offset
1912+ -> Expression grp commons schemas params from ty
1913+ -- ^ default
1914+ -> WindowFunction grp commons schemas params from ty
1915+lead value offset def = UnsafeWindowFunction $ "lag"
1916+ <> parenthesized
1917+ (commaSeparated ([renderSQL value, renderSQL offset, renderSQL def]))
1918+
1919+{- | returns value evaluated at the row that is the
1920+first row of the window frame
1921+-}
1922+firstValue
1923+ :: Expression grp commons schemas params from ty
1924+ -- ^ value
1925+ -> WindowFunction grp commons schemas params from ty
1926+firstValue value = UnsafeWindowFunction $ "first_value"
1927+ <> parenthesized (renderSQL value)
1928+
1929+{- | returns value evaluated at the row that is the
1930+last row of the window frame
1931+-}
1932+lastValue
1933+ :: Expression grp commons schemas params from ty
1934+ -- ^ value
1935+ -> WindowFunction grp commons schemas params from ty
1936+lastValue value = UnsafeWindowFunction $ "last_value"
1937+ <> parenthesized (renderSQL value)
1938+
1939+{- | returns value evaluated at the row that is the nth
1940+row of the window frame (counting from 1); null if no such row
1941+-}
1942+nthValue
1943+ :: Expression grp commons schemas params from (nullity ty)
1944+ -- ^ value
1945+ -> Expression grp commons schemas params from ('NotNull 'PGint4)
1946+ -- ^ nth
1947+ -> WindowFunction grp commons schemas params from ('Null ty)
1948+nthValue value nth = UnsafeWindowFunction $ "nth_value"
1949+ <> parenthesized (commaSeparated [renderSQL value, renderSQL nth])