@@ -1,13 +1,14 @@
11{-|
22Module: Squeal.PostgreSQL.Expression
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
@@ -34,213 +35,365 @@ module Squeal.PostgreSQL.Expression
3435 ( -- * Expression
3536 Expression (..)
3637 , Expr
37− , type (-->)
38− , FunctionDB
39− , unsafeFunction
40− , function
41− , unsafeLeftOp
42− , leftOp
43− , unsafeRightOp
44− , rightOp
4538 , Operator
46− , OperatorDB
47− , unsafeBinaryOp
48− , binaryOp
49− , FunctionVar
50− , unsafeFunctionVar
51− , type (--->)
52− , FunctionNDB
53− , unsafeFunctionN
54− , functionN
39+ , (:-->)
40+ , HasParameter (parameter)
41+ , param
42+ -- ** Null
43+ , null_
44+ , notNull
45+ , coalesce
46+ , fromNull
47+ , isNull
48+ , isNotNull
49+ , matchNull
50+ , nullIf
51+ -- ** Collections
52+ , array
53+ , cardinality
54+ , index
55+ , row
56+ , field
5557 , PGSubset (..)
56− , PGIntersect (..)
58+ -- ** Functions
59+ , unsafeBinaryOp
60+ , unsafeUnaryOpL
61+ , unsafeFunction
62+ , unsafeVariadicFunction
63+ , atan2_
64+ , cast
65+ , quot_
66+ , rem_
67+ , trunc
68+ , round_
69+ , ceiling_
70+ , greatest
71+ , least
72+ , litChar
73+ -- ** Conditions
74+ , true
75+ , false
76+ , not_
77+ , (.&&)
78+ , (.||)
79+ , Condition
80+ , caseWhenThenElse
81+ , ifThenElse
82+ , (.==)
83+ , (./=)
84+ , (.>=)
85+ , (.<)
86+ , (.<=)
87+ , (.>)
88+ , between
89+ , notBetween
90+ , betweenSymmetric
91+ , notBetweenSymmetric
92+ , isDistinctFrom
93+ , isNotDistinctFrom
94+ , isTrue
95+ , isNotTrue
96+ , isFalse
97+ , isNotFalse
98+ , isUnknown
99+ , isNotUnknown
100+ -- ** Time
101+ , currentDate
102+ , currentTime
103+ , currentTimestamp
104+ , localTime
105+ , localTimestamp
106+ , TimeOp (..)
107+ , makeDate
108+ , makeTime
109+ , makeTimestamp
110+ , makeTimestamptz
111+ -- ** Text
112+ , lower
113+ , upper
114+ , charLength
115+ , like
116+ , ilike
117+ -- ** Json
118+ -- *** Json and Jsonb operators
119+ , (.->)
120+ , (.->>)
121+ , (.#>)
122+ , (.#>>)
123+ -- *** Jsonb operators
124+ , (.?)
125+ , (.?|)
126+ , (.?&)
127+ , (.-.)
128+ , (#-.)
129+ -- *** Functions
130+ , jsonLit
131+ , jsonbLit
132+ , toJson
133+ , toJsonb
134+ , arrayToJson
135+ , rowToJson
136+ , jsonBuildArray
137+ , jsonbBuildArray
138+ , jsonBuildObject
139+ , jsonbBuildObject
140+ , jsonObject
141+ , jsonbObject
142+ , jsonZipObject
143+ , jsonbZipObject
144+ , jsonArrayLength
145+ , jsonbArrayLength
146+ , jsonExtractPath
147+ , jsonbExtractPath
148+ , jsonExtractPathAsText
149+ , jsonbExtractPathAsText
150+ , jsonTypeof
151+ , jsonbTypeof
152+ , jsonStripNulls
153+ , jsonbStripNulls
154+ , jsonbSet
155+ , jsonbInsert
156+ , jsonbPretty
157+ -- ** Text Search
158+ , (@@)
159+ , (.&)
160+ , (.|)
161+ , (.!)
162+ , (<->)
163+ , arrayToTSvector
164+ , tsvectorLength
165+ , numnode
166+ , plainToTSquery
167+ , phraseToTSquery
168+ , websearchToTSquery
169+ , queryTree
170+ , toTSquery
171+ , toTSvector
172+ , setWeight
173+ , strip
174+ , jsonToTSvector
175+ , jsonbToTSvector
176+ , tsDelete
177+ , tsFilter
178+ , tsHeadline
179+ -- ** Aggregation
180+ , Aggregate (..)
181+ , Distinction (..)
182+ , PGAvg
183+ -- * Window Functions
184+ , WindowDefinition (..)
185+ , partitionBy
186+ , WindowFunction (..)
187+ , rank
188+ , rowNumber
189+ , denseRank
190+ , percentRank
191+ , cumeDist
192+ , ntile
193+ , lag
194+ , lead
195+ , firstValue
196+ , lastValue
197+ , nthValue
198+ -- * Sorting
199+ , SortExpression (..)
200+ , OrderBy (..)
201+ -- * Types
202+ , TypeExpression (..)
203+ , PGTyped (..)
204+ , typedef
205+ , typetable
206+ , typeview
207+ , bool
208+ , int2
209+ , smallint
210+ , int4
211+ , int
212+ , integer
213+ , int8
214+ , bigint
215+ , numeric
216+ , float4
217+ , real
218+ , float8
219+ , doublePrecision
220+ , money
221+ , text
222+ , char
223+ , character
224+ , varchar
225+ , characterVarying
226+ , bytea
227+ , timestamp
228+ , timestampWithTimeZone
229+ , date
230+ , time
231+ , timeWithTimeZone
232+ , interval
233+ , uuid
234+ , inet
235+ , json
236+ , jsonb
237+ , vararray
238+ , fixarray
239+ , tsvector
240+ , tsquery
57241 -- * Re-export
58242 , (&)
243+ , NP (..)
244+ , (:*:) (..)
59245 , K (..)
60− , unK
61246 ) where
62247
63248import Control.Category
64249import Control.DeepSeq
65250import Data.ByteString (ByteString)
251+import Data.ByteString.Lazy (toStrict)
66252import Data.Function ((&))
67253import Data.Semigroup hiding (All)
254+import qualified Data.Aeson as JSON
68255import Data.String
256+import Data.Word
69257import Generics.SOP hiding (All, from)
258+import GHC.Generics ((:*:) (..))
70259import GHC.OverloadedLabels
71260import GHC.TypeLits
72261import Numeric
73262import Prelude hiding (id, (.))
74263
264+import qualified Data.ByteString as ByteString
75265import qualified GHC.Generics as GHC
266+import qualified Generics.SOP as SOP
76267
77−import Squeal.PostgreSQL.Alias
78−import Squeal.PostgreSQL.List
79268import Squeal.PostgreSQL.Render
80269import Squeal.PostgreSQL.Schema
81270
82−-- $setup
83−-- >>> import Squeal.PostgreSQL
84−
85271{-----------------------------------------
86272column expressions
87273-----------------------------------------}
88274
89275{- | `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,
276+such as in the target list of the `Squeal.PostgreSQL.Query.select` command,
92277as new column values in `Squeal.PostgreSQL.Manipulation.insertRow` or
93278`Squeal.PostgreSQL.Manipulation.update`,
94−or in search `Squeal.PostgreSQL.Logic.Condition`s in a number of commands.
279+or in search `Condition`s in a number of commands.
95280
96281The expression syntax allows the calculation of
97282values from primitive expression using arithmetic, logical,
98283and 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`.
113284-}
114285newtype Expression
115286 (outer :: FromType)
116− (commons :: FromType)
117287 (grp :: Grouping)
288+ (commons :: FromType)
118289 (schemas :: SchemasType)
119290 (params :: [NullityType])
120291 (from :: FromType)
121292 (ty :: NullityType)
122293 = UnsafeExpression { renderExpression :: ByteString }
123294 deriving (GHC.Generic,Show,Eq,Ord,NFData)
124−instance RenderSQL (Expression outer commons grp schemas params from ty) where
295+instance RenderSQL (Expression outer grp commons schemas params from ty) where
125296 renderSQL = renderExpression
126297
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.
133298type Expr x
134− = forall outer commons grp schemas params from
135− . Expression outer commons grp schemas params from x
136− -- ^ cannot reference aliases
299+ = forall outer grp commons schemas params from
300+ . Expression outer grp commons schemas params from x
137301
138−-- | A @RankNType@ for binary operators.
139302type Operator x1 x2 y
140− = forall outer commons grp schemas params from
141− . Expression outer commons grp schemas params from x1
142− -- ^ left input
143− -> Expression outer commons grp schemas params from x2
144− -- ^ right input
145− -> Expression outer commons grp schemas params from y
146− -- ^ output
147−
148−type OperatorDB schemas x1 x2 y
149− = forall outer commons grp params from
150− . Expression outer commons grp schemas params from x1
151− -- ^ left input
152− -> Expression outer commons grp schemas params from x2
153− -- ^ right input
154− -> Expression outer commons grp schemas params from y
155− -- ^ output
156−
157−-- | A @RankNType@ for functions with a single argument.
158−-- These could be either function calls or unary operators.
159−-- This is a subtype of the usual Haskell function type `Prelude.->`,
160−-- indeed a subcategory as it is closed under the usual
161−-- `Prelude..` and `Prelude.id`.
162−type (-->) x y
163− = forall outer commons grp schemas params from
164− . Expression outer commons grp schemas params from x
165− -- ^ input
166− -> Expression outer commons grp schemas params from y
167− -- ^ output
168−
169−type FunctionDB schemas x y
170− = forall outer commons grp params from
171− . Expression outer commons grp schemas params from x
172− -- ^ input
173− -> Expression outer commons grp schemas params from y
174− -- ^ output
175−
176−{- | A @RankNType@ for functions with a fixed-length list of heterogeneous arguments.
177−Use the `*:` operator to end your argument lists, like so.
178−
179−>>> printSQL (unsafeFunctionN "fun" (true :* false :* localTime *: true))
180−fun(TRUE, FALSE, LOCALTIME, TRUE)
181−-}
182−type (--->) xs y
183− = forall outer commons grp schemas params from
184− . NP (Expression outer commons grp schemas params from) xs
185− -- ^ inputs
186− -> Expression outer commons grp schemas params from y
187− -- ^ output
188−
189−type FunctionNDB schemas xs y
190− = forall outer commons grp params from
191− . NP (Expression outer commons grp schemas params from) xs
192− -- ^ inputs
193− -> Expression outer commons grp schemas params from y
194− -- ^ output
195−
196−{- | A @RankNType@ for functions with a variable-length list of
197−homogeneous arguments and at least 1 more argument.
198−-}
199−type FunctionVar x0 x1 y
200− = forall outer commons grp schemas params from
201− . [Expression outer commons grp schemas params from x0]
202− -- ^ inputs
203− -> Expression outer commons grp schemas params from x1
204− -- ^ must have at least 1 input
205− -> Expression outer commons grp schemas params from y
206− -- ^ output
207−
208−{- | >>> printSQL (unsafeFunctionVar "greatest" [true, null_] false)
209−greatest(TRUE, NULL, FALSE)
210−-}
211−unsafeFunctionVar :: ByteString -> FunctionVar x0 x1 y
212−unsafeFunctionVar fun xs x = UnsafeExpression $ fun <> parenthesized
303+ = forall outer grp commons schemas params from
304+ . Expression outer grp commons schemas params from x1
305+ -> Expression outer grp commons schemas params from x2
306+ -> Expression outer grp commons schemas params from y
307+
308+type (:-->) x y
309+ = forall outer grp commons schemas params from
310+ . Expression outer grp commons schemas params from x
311+ -> Expression outer grp commons schemas params from y
312+
313+type FunctionHet xs y
314+ = forall outer grp commons schemas params from
315+ . NP (Expression outer grp commons schemas params from) xs
316+ -> Expression outer grp commons schemas params from y
317+
318+type FunctionHom x y
319+ = forall outer grp commons schemas params from
320+ . [Expression outer grp commons schemas params from x]
321+ -> Expression outer grp commons schemas params from x
322+ -> Expression outer grp commons schemas params from y
323+
324+unsafeFunctionHom :: ByteString -> FunctionHom x y
325+unsafeFunctionHom fun xs x = UnsafeExpression $ fun <> parenthesized
213326 (commaSeparated (renderSQL <$> xs) <> ", " <> renderSQL x)
214327
328+{- | A `HasParameter` constraint is used to indicate a value that is
329+supplied externally to a SQL statement.
330+`Squeal.PostgreSQL.PQ.manipulateParams`,
331+`Squeal.PostgreSQL.PQ.queryParams` and
332+`Squeal.PostgreSQL.PQ.traversePrepared` support specifying data values
333+separately from the SQL command string, in which case `param`s are used to
334+refer to the out-of-line data values.
335+-}
336+class KnownNat n => HasParameter
337+ (n :: Nat)
338+ (params :: [NullityType])
339+ (ty :: NullityType)
340+ | n params -> ty where
341+ -- | `parameter` takes a `Nat` using type application and a `TypeExpression`.
342+ --
343+ -- >>> let expr = parameter @1 int4 :: Expression outer grp '[] schemas '[ 'Null 'PGint4] from ('Null 'PGint4)
344+ -- >>> printSQL expr
345+ -- ($1 :: int4)
346+ parameter
347+ :: TypeExpression schemas ty
348+ -> Expression outer grp commons schemas params from ty
349+ parameter ty = UnsafeExpression $ parenthesized $
350+ "$" <> renderNat @n <+> "::"
351+ <+> renderSQL ty
352+instance {-# OVERLAPPING #-} HasParameter 1 (ty1:tys) ty1
353+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) params ty)
354+ => HasParameter n (ty' : params) ty
355+
356+-- | `param` takes a `Nat` using type application and for basic types,
357+-- infers a `TypeExpression`.
358+--
359+-- >>> let expr = param @1 :: Expression outer grp commons schemas '[ 'Null 'PGint4] from ('Null 'PGint4)
360+-- >>> printSQL expr
361+-- ($1 :: int4)
362+param
363+ :: forall n outer commons schemas params from grp ty
364+ . (PGTyped schemas ty, HasParameter n params ty)
365+ => Expression outer grp commons schemas params from ty -- ^ param
366+param = parameter @n (pgtype @schemas)
367+
215368instance (HasUnique tab (Join outer from) row, Has col row ty)
216− => IsLabel col (Expression outer commons 'Ungrouped schemas params from ty) where
369+ => IsLabel col (Expression outer 'Ungrouped commons schemas params from ty) where
217370 fromLabel = UnsafeExpression $ renderSQL (Alias @col)
218371instance (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
372+ => IsLabel col (NP (Expression outer 'Ungrouped commons schemas params from) tys) where
220373 fromLabel = fromLabel @col :* Nil
221374instance (HasUnique tab (Join outer from) row, Has col row ty, column ~ (col ::: ty))
222375 => IsLabel col
223− (Aliased (Expression outer commons 'Ungrouped schemas params from) column) where
376+ (Aliased (Expression outer 'Ungrouped commons schemas params from) column) where
224377 fromLabel = fromLabel @col `As` Alias
225378instance (HasUnique tab (Join outer from) row, Has col row ty, columns ~ '[col ::: ty])
226379 => IsLabel col
227− (NP (Aliased (Expression outer commons 'Ungrouped schemas params from)) columns) where
380+ (NP (Aliased (Expression outer 'Ungrouped commons schemas params from)) columns) where
228381 fromLabel = fromLabel @col :* Nil
229382
230383instance (Has tab (Join outer from) row, Has col row ty)
231− => IsQualified tab col (Expression outer commons 'Ungrouped schemas params from ty) where
384+ => IsQualified tab col (Expression outer 'Ungrouped commons schemas params from ty) where
232385 tab ! col = UnsafeExpression $
233386 renderSQL tab <> "." <> renderSQL col
234387instance (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
388+ => IsQualified tab col (NP (Expression outer 'Ungrouped commons schemas params from) tys) where
236389 tab ! col = tab ! col :* Nil
237390instance (Has tab (Join outer from) row, Has col row ty, column ~ (col ::: ty))
238391 => IsQualified tab col
239− (Aliased (Expression outer commons 'Ungrouped schemas params from) column) where
392+ (Aliased (Expression outer 'Ungrouped commons schemas params from) column) where
240393 tab ! col = tab ! col `As` col
241394instance (Has tab (Join outer from) row, Has col row ty, columns ~ '[col ::: ty])
242395 => IsQualified tab col
243− (NP (Aliased (Expression outer commons 'Ungrouped schemas params from)) columns) where
396+ (NP (Aliased (Expression outer 'Ungrouped commons schemas params from)) columns) where
244397 tab ! col = tab ! col :* Nil
245398
246399instance
@@ -248,7 +401,7 @@ instance
248401 , Has col row ty
249402 , GroupedBy tab col bys
250403 ) => IsLabel col
251− (Expression outer commons ('Grouped bys) schemas params from ty) where
404+ (Expression outer ('Grouped bys) commons schemas params from ty) where
252405 fromLabel = UnsafeExpression $ renderSQL (Alias @col)
253406instance
254407 ( HasUnique tab (Join outer from) row
@@ -256,7 +409,7 @@ instance
256409 , GroupedBy tab col bys
257410 , tys ~ '[ty]
258411 ) => IsLabel col
259− (NP (Expression outer commons ('Grouped bys) schemas params from) tys) where
412+ (NP (Expression outer ('Grouped bys) commons schemas params from) tys) where
260413 fromLabel = fromLabel @col :* Nil
261414instance
262415 ( HasUnique tab (Join outer from) row
@@ -264,7 +417,7 @@ instance
264417 , GroupedBy tab col bys
265418 , column ~ (col ::: ty)
266419 ) => IsLabel col
267− (Aliased (Expression outer commons ('Grouped bys) schemas params from) column) where
420+ (Aliased (Expression outer ('Grouped bys) commons schemas params from) column) where
268421 fromLabel = fromLabel @col `As` Alias
269422instance
270423 ( HasUnique tab (Join outer from) row
@@ -272,7 +425,7 @@ instance
272425 , GroupedBy tab col bys
273426 , columns ~ '[col ::: ty]
274427 ) => IsLabel col
275− (NP (Aliased (Expression outer commons ('Grouped bys) schemas params from)) columns) where
428+ (NP (Aliased (Expression outer ('Grouped bys) commons schemas params from)) columns) where
276429 fromLabel = fromLabel @col :* Nil
277430
278431instance
@@ -280,7 +433,7 @@ instance
280433 , Has col row ty
281434 , GroupedBy tab col bys
282435 ) => IsQualified tab col
283− (Expression outer commons ('Grouped bys) schemas params from ty) where
436+ (Expression outer ('Grouped bys) commons schemas params from ty) where
284437 tab ! col = UnsafeExpression $
285438 renderSQL tab <> "." <> renderSQL col
286439instance
@@ -289,7 +442,7 @@ instance
289442 , GroupedBy tab col bys
290443 , tys ~ '[ty]
291444 ) => IsQualified tab col
292− (NP (Expression outer commons ('Grouped bys) schemas params from) tys) where
445+ (NP (Expression outer ('Grouped bys) commons schemas params from) tys) where
293446 tab ! col = tab ! col :* Nil
294447instance
295448 ( Has tab (Join outer from) row
@@ -297,7 +450,7 @@ instance
297450 , GroupedBy tab col bys
298451 , column ~ (col ::: ty)
299452 ) => IsQualified tab col
300− (Aliased (Expression outer commons ('Grouped bys) schemas params from) column) where
453+ (Aliased (Expression outer ('Grouped bys) commons schemas params from) column) where
301454 tab ! col = tab ! col `As` col
302455instance
303456 ( Has tab (Join outer from) row
@@ -305,78 +458,223 @@ instance
305458 , GroupedBy tab col bys
306459 , columns ~ '[col ::: ty]
307460 ) => IsQualified tab col
308− (NP (Aliased (Expression outer commons ('Grouped bys) schemas params from)) columns) where
461+ (NP (Aliased (Expression outer ('Grouped bys) commons schemas params from)) columns) where
309462 tab ! col = tab ! col :* Nil
310463
464+-- | analagous to `Nothing`
465+--
466+-- >>> printSQL null_
467+-- NULL
468+null_ :: Expr ('Null ty)
469+null_ = UnsafeExpression "NULL"
470+
471+-- | analagous to `Just`
472+--
473+-- >>> printSQL $ notNull true
474+-- TRUE
475+notNull :: 'NotNull ty :--> 'Null ty
476+notNull = UnsafeExpression . renderSQL
477+
478+-- | return the leftmost value which is not NULL
479+--
480+-- >>> printSQL $ coalesce [null_, true] false
481+-- COALESCE(NULL, TRUE, FALSE)
482+coalesce
483+ :: [Expression outer grp commons schemas params from ('Null ty)]
484+ -- ^ @NULL@s may be present
485+ -> Expression outer grp commons schemas params from ('NotNull ty)
486+ -- ^ @NULL@ is absent
487+ -> Expression outer grp commons schemas params from ('NotNull ty)
488+coalesce nullxs notNullx = UnsafeExpression $
489+ "COALESCE" <> parenthesized (commaSeparated
490+ ((renderSQL <$> nullxs) <> [renderSQL notNullx]))
491+
492+-- | analagous to `Data.Maybe.fromMaybe` using @COALESCE@
493+--
494+-- >>> printSQL $ fromNull true null_
495+-- COALESCE(NULL, TRUE)
496+fromNull
497+ :: Expression outer grp commons schemas params from ('NotNull ty)
498+ -- ^ what to convert @NULL@ to
499+ -> Expression outer grp commons schemas params from ('Null ty)
500+ -> Expression outer grp commons schemas params from ('NotNull ty)
501+fromNull notNullx nullx = coalesce [nullx] notNullx
502+
503+-- | >>> printSQL $ null_ & isNull
504+-- NULL IS NULL
505+isNull
506+ :: Expression outer grp commons schemas params from ('Null ty)
507+ -- ^ possibly @NULL@
508+ -> Condition outer grp commons schemas params from
509+isNull x = UnsafeExpression $ renderSQL x <+> "IS NULL"
510+
511+-- | >>> printSQL $ null_ & isNotNull
512+-- NULL IS NOT NULL
513+isNotNull
514+ :: Expression outer grp commons schemas params from ('Null ty)
515+ -- ^ possibly @NULL@
516+ -> Condition outer grp commons schemas params from
517+isNotNull x = UnsafeExpression $ renderSQL x <+> "IS NOT NULL"
518+
519+-- | analagous to `maybe` using @IS NULL@
520+--
521+-- >>> printSQL $ matchNull true not_ null_
522+-- CASE WHEN NULL IS NULL THEN TRUE ELSE (NOT NULL) END
523+matchNull
524+ :: Expression outer grp commons schemas params from (nullty)
525+ -- ^ what to convert @NULL@ to
526+ -> ( Expression outer grp commons schemas params from ('NotNull ty)
527+ -> Expression outer grp commons schemas params from (nullty) )
528+ -- ^ function to perform when @NULL@ is absent
529+ -> Expression outer grp commons schemas params from ('Null ty)
530+ -> Expression outer grp commons schemas params from (nullty)
531+matchNull y f x = ifThenElse (isNull x) y
532+ (f (UnsafeExpression (renderSQL x)))
533+
534+{-| right inverse to `fromNull`, if its arguments are equal then
535+`nullIf` gives @NULL@.
536+
537+>>> :set -XTypeApplications -XDataKinds
538+>>> let expr = nullIf false (param @1) :: Expression outer grp commons schemas '[ 'NotNull 'PGbool] from ('Null 'PGbool)
539+>>> printSQL expr
540+NULL IF (FALSE, ($1 :: bool))
541+-}
542+nullIf
543+ :: Expression outer grp commons schemas params from ('NotNull ty)
544+ -- ^ @NULL@ is absent
545+ -> Expression outer grp commons schemas params from ('NotNull ty)
546+ -- ^ @NULL@ is absent
547+ -> Expression outer grp commons schemas params from ('Null ty)
548+nullIf x y = UnsafeExpression $ "NULL IF" <+> parenthesized
549+ (renderSQL x <> ", " <> renderSQL y)
550+
551+-- | >>> printSQL $ array [null_, false, true]
552+-- ARRAY[NULL, FALSE, TRUE]
553+array
554+ :: [Expression outer grp commons schemas params from ty]
555+ -- ^ array elements
556+ -> Expression outer grp commons schemas params from (null ('PGvararray ty))
557+array xs = UnsafeExpression $
558+ "ARRAY[" <> commaSeparated (renderSQL <$> xs) <> "]"
559+
560+-- | >>> printSQL $ cardinality (array [null_, false, true])
561+-- cardinality(ARRAY[NULL, FALSE, TRUE])
562+cardinality :: null ('PGvararray ty) :--> null 'PGint8
563+cardinality = unsafeFunction "cardinality"
564+
565+-- | >>> printSQL $ array [null_, false, true] & index 2
566+-- (ARRAY[NULL, FALSE, TRUE])[2]
567+index
568+ :: Word64 -- ^ index
569+ -> null ('PGvararray ty) :--> NullifyType ty
570+index n expr = UnsafeExpression $
571+ parenthesized (renderSQL expr) <> "[" <> fromString (show n) <> "]"
572+
311573instance (KnownSymbol label, label `In` labels) => IsPGlabel label
312− (Expression outer commons grp schemas params from (null ('PGenum labels))) where
574+ (Expression outer grp commons schemas params from (null ('PGenum labels))) where
313575 label = UnsafeExpression $ renderSQL (PGlabel @label)
314576
577+-- | A row constructor is an expression that builds a row value
578+-- (also called a composite value) using values for its member fields.
579+--
580+-- >>> :{
581+-- type Complex = 'PGcomposite
582+-- '[ "real" ::: 'NotNull 'PGfloat8
583+-- , "imaginary" ::: 'NotNull 'PGfloat8 ]
584+-- :}
585+--
586+-- >>> let i = row (0 `as` #real :* 1 `as` #imaginary) :: Expression outer grp commons schemas params from ('NotNull Complex)
587+-- >>> printSQL i
588+-- ROW(0, 1)
589+row
590+ :: SListI row
591+ => NP (Aliased (Expression outer grp commons schemas params from)) row
592+ -- ^ zero or more expressions for the row field values
593+ -> Expression outer grp commons schemas params from (null ('PGcomposite row))
594+row exprs = UnsafeExpression $ "ROW" <> parenthesized
595+ (renderCommaSeparated (\ (expr `As` _) -> renderSQL expr) exprs)
596+
597+-- | >>> :{
598+-- type Complex = 'PGcomposite
599+-- '[ "real" ::: 'NotNull 'PGfloat8
600+-- , "imaginary" ::: 'NotNull 'PGfloat8 ]
601+-- type Schema = '["complex" ::: 'Typedef Complex]
602+-- :}
603+--
604+-- >>> let i = row (0 `as` #real :* 1 `as` #imaginary) :: Expression outer grp '[] (Public Schema) from params ('NotNull Complex)
605+-- >>> printSQL $ i & field #complex #imaginary
606+-- (ROW(0, 1)::"complex")."imaginary"
607+field
608+ :: ( Has sch schemas schema
609+ , Has tydef schema ('Typedef ('PGcomposite row))
610+ , Has field row ty)
611+ => QualifiedAlias sch tydef -- ^ row type
612+ -> Alias field -- ^ field name
613+ -> Expression outer grp commons schemas params from ('NotNull ('PGcomposite row))
614+ -> Expression outer grp commons schemas params from ty
615+field td fld expr = UnsafeExpression $
616+ parenthesized (renderSQL expr <> "::" <> renderSQL td)
617+ <> "." <> renderSQL fld
618+
619+instance Semigroup
620+ (Expression outer grp commons schemas params from (null ('PGvararray ty))) where
621+ (<>) = unsafeBinaryOp "||"
622+
623+instance Monoid
624+ (Expression outer grp commons schemas params from (null ('PGvararray ty))) where
625+ mempty = array []
626+ mappend = (<>)
627+
628+-- | >>> let expr = greatest [param @1] currentTimestamp :: Expression outer grp commons schemas '[ 'NotNull 'PGtimestamptz] from ('NotNull 'PGtimestamptz)
629+-- >>> printSQL expr
630+-- GREATEST(($1 :: timestamp with time zone), CURRENT_TIMESTAMP)
631+greatest :: FunctionHom ty ty
632+greatest = unsafeFunctionHom "GREATEST"
633+
634+-- | >>> printSQL $ least [null_] currentTimestamp
635+-- LEAST(NULL, CURRENT_TIMESTAMP)
636+least :: FunctionHom ty ty
637+least = unsafeFunctionHom "LEAST"
638+
315639-- | >>> printSQL $ unsafeBinaryOp "OR" true false
316640-- (TRUE OR FALSE)
317−unsafeBinaryOp :: ByteString -> Operator ty0 ty1 ty2
641+unsafeBinaryOp :: ByteString {- ^ operator -} -> Operator ty0 ty1 ty2
318642unsafeBinaryOp op x y = UnsafeExpression $ parenthesized $
319643 renderSQL x <+> op <+> renderSQL y
320644
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
645+-- | >>> printSQL $ unsafeUnaryOpL "NOT" true
329646-- (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
647+unsafeUnaryOpL :: ByteString {- ^ operator -} -> x :--> y
648+unsafeUnaryOpL op x = UnsafeExpression $ parenthesized $ op <+> renderSQL x
649+
650+unsafeUnaryOpR :: ByteString {- ^ operator -} -> x :--> y
651+unsafeUnaryOpR op x = UnsafeExpression $ parenthesized $ renderSQL x <+> op
351652
352653-- | >>> printSQL $ unsafeFunction "f" true
353654-- f(TRUE)
354−unsafeFunction :: ByteString -> x --> y
655+unsafeFunction :: ByteString {- ^ function -} -> x :--> y
355656unsafeFunction fun x = UnsafeExpression $
356657 fun <> parenthesized (renderSQL x)
357658
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 $
659+unsafeFunctionHet
660+ :: SListI xs
661+ => ByteString {- ^ function -}
662+ -> FunctionHet xs y
663+unsafeFunctionHet fun xs = UnsafeExpression $
368664 fun <> parenthesized (renderCommaSeparated renderSQL xs)
369665
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
666+-- | Helper for defining variadic functions.
667+unsafeVariadicFunction
668+ :: SListI elems
669+ => ByteString
670+ -- ^ function
671+ -> NP (Expression outer grp commons schemas params from) elems
672+ -> Expression outer grp commons schemas params from ret
673+unsafeVariadicFunction fun x = UnsafeExpression $
674+ fun <> parenthesized (commaSeparated (hcollapse (hmap (K . renderSQL) x)))
377675
378676instance ty `In` PGNum
379− => Num (Expression outer commons grp schemas params from (null ty)) where
677+ => Num (Expression outer grp commons schemas params from (null ty)) where
380678 (+) = unsafeBinaryOp "+"
381679 (-) = unsafeBinaryOp "-"
382680 (*) = unsafeBinaryOp "*"
@@ -388,7 +686,7 @@ instance ty `In` PGNum
388686 . show
389687
390688instance (ty `In` PGNum, ty `In` PGFloating) => Fractional
391− (Expression outer commons grp schemas params from (null ty)) where
689+ (Expression outer grp commons schemas params from (null ty)) where
392690 (/) = unsafeBinaryOp "/"
393691 fromRational
394692 = UnsafeExpression
@@ -398,7 +696,7 @@ instance (ty `In` PGNum, ty `In` PGFloating) => Fractional
398696 . fromRat @Double
399697
400698instance (ty `In` PGNum, ty `In` PGFloating) => Floating
401− (Expression outer commons grp schemas params from (null ty)) where
699+ (Expression outer grp commons schemas params from (null ty)) where
402700 pi = UnsafeExpression "pi()"
403701 exp = unsafeFunction "exp"
404702 log = unsafeFunction "ln"
@@ -419,54 +717,1791 @@ instance (ty `In` PGNum, ty `In` PGFloating) => Floating
419717 acosh x = log (x + sqrt (x*x - 1))
420718 atanh x = log ((1 + x) / (1 - x)) / 2
421719
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)
720+-- | >>> :{
721+-- let
722+-- expression :: Expr (null 'PGfloat4)
723+-- expression = atan2_ (pi *: 2)
724+-- in printSQL expression
725+-- :}
726+-- atan2(pi(), 2)
727+atan2_
728+ :: float `In` PGFloating
729+ => FunctionHet '[ null float, null float] (null float)
730+atan2_ = unsafeFunctionHet "atan2"
731+
732+-- When a `cast` is applied to an `Expression` of a known type, it
733+-- represents a run-time type conversion. The cast will succeed only if a
734+-- suitable type conversion operation has been defined.
735+--
736+-- | >>> printSQL $ true & cast int4
737+-- (TRUE :: int4)
738+cast
739+ :: TypeExpression schemas ty1
740+ -- ^ type to cast as
741+ -> Expression outer grp commons schemas params from ty0
742+ -- ^ value to convert
743+ -> Expression outer grp commons schemas params from ty1
744+cast ty x = UnsafeExpression $ parenthesized $
745+ renderSQL x <+> "::" <+> renderSQL ty
746+
747+-- | integer division, truncates the result
748+--
749+-- >>> :{
750+-- let
751+-- expression :: Expression outer grp commons schemas params from (null 'PGint2)
752+-- expression = 5 `quot_` 2
753+-- in printSQL expression
754+-- :}
755+-- (5 / 2)
756+quot_
757+ :: int `In` PGIntegral
758+ => Operator (null int) (null int) (null int)
759+quot_ = unsafeBinaryOp "/"
760+
761+-- | remainder upon integer division
762+--
763+-- >>> :{
764+-- let
765+-- expression :: Expression outer grp commons schemas params from (null 'PGint2)
766+-- expression = 5 `rem_` 2
767+-- in printSQL expression
768+-- :}
769+-- (5 % 2)
770+rem_
771+ :: int `In` PGIntegral
772+ => Operator (null int) (null int) (null int)
773+rem_ = unsafeBinaryOp "%"
774+
775+-- | >>> :{
776+-- let
777+-- expression :: Expression outer grp commons schemas params from (null 'PGfloat4)
778+-- expression = trunc pi
779+-- in printSQL expression
780+-- :}
781+-- trunc(pi())
782+trunc :: frac `In` PGFloating => null frac :--> null frac
783+trunc = unsafeFunction "trunc"
784+
785+-- | >>> :{
786+-- let
787+-- expression :: Expression outer grp commons schemas params from (null 'PGfloat4)
788+-- expression = round_ pi
789+-- in printSQL expression
790+-- :}
791+-- round(pi())
792+round_ :: frac `In` PGFloating => null frac :--> null frac
793+round_ = unsafeFunction "round"
794+
795+-- | >>> :{
796+-- let
797+-- expression :: Expression outer grp commons schemas params from (null 'PGfloat4)
798+-- expression = ceiling_ pi
799+-- in printSQL expression
800+-- :}
801+-- ceiling(pi())
802+ceiling_ :: frac `In` PGFloating => null frac :--> null frac
803+ceiling_ = unsafeFunction "ceiling"
804+
805+-- | A `Condition` is an `Expression`, which can evaluate
806+-- to `true`, `false` or `null_`. This is because SQL uses
807+-- a three valued logic.
808+type Condition outer grp commons schemas params from =
809+ Expression outer grp commons schemas params from ('Null 'PGbool)
810+
811+-- | >>> printSQL true
812+-- TRUE
813+true :: Expr (null 'PGbool)
814+true = UnsafeExpression "TRUE"
815+
816+-- | >>> printSQL false
817+-- FALSE
818+false :: Expr (null 'PGbool)
819+false = UnsafeExpression "FALSE"
820+
821+-- | >>> printSQL $ not_ true
822+-- (NOT TRUE)
823+not_ :: null 'PGbool :--> null 'PGbool
824+not_ = unsafeUnaryOpL "NOT"
825+
826+-- | >>> printSQL $ true .&& false
827+-- (TRUE AND FALSE)
828+(.&&) :: Operator (null 'PGbool) (null 'PGbool) (null 'PGbool)
829+infixr 3 .&&
830+(.&&) = unsafeBinaryOp "AND"
831+
832+-- | >>> printSQL $ true .|| false
833+-- (TRUE OR FALSE)
834+(.||) :: Operator (null 'PGbool) (null 'PGbool) (null 'PGbool)
835+infixr 2 .||
836+(.||) = unsafeBinaryOp "OR"
837+
838+-- | >>> :{
839+-- let
840+-- expression :: Expression outer grp commons schemas params from (null 'PGint2)
841+-- expression = caseWhenThenElse [(true, 1), (false, 2)] 3
842+-- in printSQL expression
843+-- :}
844+-- CASE WHEN TRUE THEN 1 WHEN FALSE THEN 2 ELSE 3 END
845+caseWhenThenElse
846+ :: [ ( Condition outer grp commons schemas params from
847+ , Expression outer grp commons schemas params from ty
848+ ) ]
849+ -- ^ whens and thens
850+ -> Expression outer grp commons schemas params from ty
851+ -- ^ else
852+ -> Expression outer grp commons schemas params from ty
853+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
854+ [ "CASE"
855+ , mconcat
856+ [ mconcat
857+ [ " WHEN ", renderSQL when_
858+ , " THEN ", renderSQL then_
859+ ]
860+ | (when_,then_) <- whenThens
861+ ]
862+ , " ELSE ", renderSQL else_
863+ , " END"
864+ ]
865+
866+-- | >>> :{
867+-- let
868+-- expression :: Expression outer grp commons schemas params from (null 'PGint2)
869+-- expression = ifThenElse true 1 0
870+-- in printSQL expression
871+-- :}
872+-- CASE WHEN TRUE THEN 1 ELSE 0 END
873+ifThenElse
874+ :: Condition outer grp commons schemas params from
875+ -> Expression outer grp commons schemas params from ty -- ^ then
876+ -> Expression outer grp commons schemas params from ty -- ^ else
877+ -> Expression outer grp commons schemas params from ty
878+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
879+
880+-- | Comparison operations like `.==`, `./=`, `.>`, `.>=`, `.<` and `.<=`
881+-- will produce @NULL@s if one of their arguments is @NULL@.
882+--
883+-- >>> printSQL $ true .== null_
884+-- (TRUE = NULL)
885+(.==) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
886+(.==) = unsafeBinaryOp "="
887+infix 4 .==
888+
889+-- | >>> printSQL $ true ./= null_
890+-- (TRUE <> NULL)
891+(./=) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
892+(./=) = unsafeBinaryOp "<>"
893+infix 4 ./=
894+
895+-- | >>> printSQL $ true .>= null_
896+-- (TRUE >= NULL)
897+(.>=) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
898+(.>=) = unsafeBinaryOp ">="
899+infix 4 .>=
900+
901+-- | >>> printSQL $ true .< null_
902+-- (TRUE < NULL)
903+(.<) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
904+(.<) = unsafeBinaryOp "<"
905+infix 4 .<
906+
907+-- | >>> printSQL $ true .<= null_
908+-- (TRUE <= NULL)
909+(.<=) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
910+(.<=) = unsafeBinaryOp "<="
911+infix 4 .<=
912+
913+-- | >>> printSQL $ true .> null_
914+-- (TRUE > NULL)
915+(.>) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
916+(.>) = unsafeBinaryOp ">"
917+infix 4 .>
918+
919+{- | between
920+>>> printSQL $ true `between` (null_, false)
921+TRUE BETWEEN NULL AND FALSE
922+-}
923+between
924+ :: Expression outer grp commons schemas params from (null ty)
925+ -> ( Expression outer grp commons schemas params from (null ty)
926+ , Expression outer grp commons schemas params from (null ty) ) -- ^ bounds
927+ -> Condition outer grp commons schemas params from
928+between a (x,y) = UnsafeExpression $ renderSQL a <+> "BETWEEN"
929+ <+> renderSQL x <+> "AND" <+> renderSQL y
930+
931+{- | not between
932+>>> printSQL $ true `notBetween` (null_, false)
933+TRUE NOT BETWEEN NULL AND FALSE
934+-}
935+notBetween
936+ :: Expression outer grp commons schemas params from (null ty)
937+ -> ( Expression outer grp commons schemas params from (null ty)
938+ , Expression outer grp commons schemas params from (null ty) ) -- ^ bounds
939+ -> Condition outer grp commons schemas params from
940+notBetween a (x,y) = UnsafeExpression $ renderSQL a <+> "NOT BETWEEN"
941+ <+> renderSQL x <+> "AND" <+> renderSQL y
942+
943+{- | between, after sorting the comparison values
944+>>> printSQL $ true `betweenSymmetric` (null_, false)
945+TRUE BETWEEN SYMMETRIC NULL AND FALSE
946+-}
947+betweenSymmetric
948+ :: Expression outer grp commons schemas params from (null ty)
949+ -> ( Expression outer grp commons schemas params from (null ty)
950+ , Expression outer grp commons schemas params from (null ty) ) -- ^ bounds
951+ -> Condition outer grp commons schemas params from
952+betweenSymmetric a (x,y) = UnsafeExpression $ renderSQL a
953+ <+> "BETWEEN SYMMETRIC" <+> renderSQL x <+> "AND" <+> renderSQL y
432954
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)
955+{- | not between, after sorting the comparison values
956+>>> printSQL $ true `notBetweenSymmetric` (null_, false)
957+TRUE NOT BETWEEN SYMMETRIC NULL AND FALSE
958+-}
959+notBetweenSymmetric
960+ :: Expression outer grp commons schemas params from (null ty)
961+ -> ( Expression outer grp commons schemas params from (null ty)
962+ , Expression outer grp commons schemas params from (null ty) ) -- ^ bounds
963+ -> Condition outer grp commons schemas params from
964+notBetweenSymmetric a (x,y) = UnsafeExpression $ renderSQL a
965+ <+> "NOT BETWEEN SYMMETRIC" <+> renderSQL x <+> "AND" <+> renderSQL y
966+
967+{- | not equal, treating null like an ordinary value
968+>>> printSQL $ true `isDistinctFrom` null_
969+(TRUE IS DISTINCT FROM NULL)
970+-}
971+isDistinctFrom :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
972+isDistinctFrom = unsafeBinaryOp "IS DISTINCT FROM"
973+
974+{- | equal, treating null like an ordinary value
975+>>> printSQL $ true `isNotDistinctFrom` null_
976+(TRUE IS NOT DISTINCT FROM NULL)
977+-}
978+isNotDistinctFrom :: Operator (null0 ty) (null1 ty) ('NotNull 'PGbool)
979+isNotDistinctFrom = unsafeBinaryOp "IS NOT DISTINCT FROM"
980+
981+{- | is true
982+>>> printSQL $ true & isTrue
983+(TRUE IS TRUE)
984+-}
985+isTrue :: null 'PGbool :--> 'NotNull 'PGbool
986+isTrue = unsafeUnaryOpR "IS TRUE"
987+
988+{- | is false or unknown
989+>>> printSQL $ true & isNotTrue
990+(TRUE IS NOT TRUE)
991+-}
992+isNotTrue :: null 'PGbool :--> 'NotNull 'PGbool
993+isNotTrue = unsafeUnaryOpR "IS NOT TRUE"
994+
995+{- | is false
996+>>> printSQL $ true & isFalse
997+(TRUE IS FALSE)
998+-}
999+isFalse :: null 'PGbool :--> 'NotNull 'PGbool
1000+isFalse = unsafeUnaryOpR "IS FALSE"
1001+
1002+{- | is true or unknown
1003+>>> printSQL $ true & isNotFalse
1004+(TRUE IS NOT FALSE)
1005+-}
1006+isNotFalse :: null 'PGbool :--> 'NotNull 'PGbool
1007+isNotFalse = unsafeUnaryOpR "IS NOT FALSE"
1008+
1009+{- | is unknown
1010+>>> printSQL $ true & isUnknown
1011+(TRUE IS UNKNOWN)
1012+-}
1013+isUnknown :: null 'PGbool :--> 'NotNull 'PGbool
1014+isUnknown = unsafeUnaryOpR "IS UNKNOWN"
1015+
1016+{- | is true or false
1017+>>> printSQL $ true & isNotUnknown
1018+(TRUE IS NOT UNKNOWN)
1019+-}
1020+isNotUnknown :: null 'PGbool :--> 'NotNull 'PGbool
1021+isNotUnknown = unsafeUnaryOpR "IS NOT UNKNOWN"
1022+
1023+-- | >>> printSQL currentDate
1024+-- CURRENT_DATE
1025+currentDate :: Expr (null 'PGdate)
1026+currentDate = UnsafeExpression "CURRENT_DATE"
1027+
1028+-- | >>> printSQL currentTime
1029+-- CURRENT_TIME
1030+currentTime :: Expr (null 'PGtimetz)
1031+currentTime = UnsafeExpression "CURRENT_TIME"
1032+
1033+-- | >>> printSQL currentTimestamp
1034+-- CURRENT_TIMESTAMP
1035+currentTimestamp :: Expr (null 'PGtimestamptz)
1036+currentTimestamp = UnsafeExpression "CURRENT_TIMESTAMP"
1037+
1038+-- | >>> printSQL localTime
1039+-- LOCALTIME
1040+localTime :: Expr (null 'PGtime)
1041+localTime = UnsafeExpression "LOCALTIME"
1042+
1043+-- | >>> printSQL localTimestamp
1044+-- LOCALTIMESTAMP
1045+localTimestamp :: Expr (null 'PGtimestamp)
1046+localTimestamp = UnsafeExpression "LOCALTIMESTAMP"
1047+
1048+{-----------------------------------------
1049+text
1050+-----------------------------------------}
1051+
1052+escape :: Char -> String
1053+escape = \case
1054+ '\NUL' -> "\\0"
1055+ '\'' -> "''"
1056+ '"' -> "\\\""
1057+ '\b' -> "\\b"
1058+ '\n' -> "\\n"
1059+ '\r' -> "\\r"
1060+ '\t' -> "\\t"
1061+ '\\' -> "\\\\"
1062+ c -> [c]
4381063
4391064instance IsString
440− (Expression outer commons grp schemas params from (null 'PGtext)) where
1065+ (Expression outer grp commons schemas params from (null 'PGtext)) where
4411066 fromString str = UnsafeExpression $
4421067 "E\'" <> fromString (escape =<< str) <> "\'"
4431068instance IsString
444− (Expression outer commons grp schemas params from (null 'PGtsvector)) where
445− fromString str = UnsafeExpression . parenthesized . (<> " :: tsvector") $
1069+ (Expression outer grp commons schemas params from (null 'PGtsvector)) where
1070+ fromString str = cast tsvector . UnsafeExpression $
4461071 "E\'" <> fromString (escape =<< str) <> "\'"
4471072instance IsString
448− (Expression outer commons grp schemas params from (null 'PGtsquery)) where
449− fromString str = UnsafeExpression . parenthesized . (<> " :: tsquery") $
1073+ (Expression outer grp commons schemas params from (null 'PGtsquery)) where
1074+ fromString str = cast tsquery . UnsafeExpression $
4501075 "E\'" <> fromString (escape =<< str) <> "\'"
4511076
4521077instance 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
1078+ (Expression outer grp commons schemas params from (null 'PGtext)) where
4601079 (<>) = unsafeBinaryOp "||"
4611080instance Semigroup
462− (Expression outer commons grp schemas params from (null 'PGtsvector)) where
1081+ (Expression outer grp commons schemas params from (null 'PGtsvector)) where
4631082 (<>) = unsafeBinaryOp "||"
4641083
4651084instance Monoid
466− (Expression outer commons grp schemas params from (null 'PGtext)) where
1085+ (Expression outer grp commons schemas params from (null 'PGtext)) where
4671086 mempty = fromString ""
4681087 mappend = (<>)
4691088instance Monoid
470− (Expression outer commons grp schemas params from (null 'PGtsvector)) where
1089+ (Expression outer grp commons schemas params from (null 'PGtsvector)) where
4711090 mempty = fromString ""
4721091 mappend = (<>)
1092+
1093+-- | >>> printSQL $ lower "ARRRGGG"
1094+-- lower(E'ARRRGGG')
1095+lower :: null 'PGtext :--> null 'PGtext
1096+lower = unsafeFunction "lower"
1097+
1098+-- | >>> printSQL $ upper "eeee"
1099+-- upper(E'eeee')
1100+upper :: null 'PGtext :--> null 'PGtext
1101+upper = unsafeFunction "upper"
1102+
1103+-- | >>> printSQL $ charLength "four"
1104+-- char_length(E'four')
1105+charLength :: null 'PGtext :--> null 'PGint4
1106+charLength = unsafeFunction "char_length"
1107+
1108+-- | The `like` expression returns true if the @string@ matches
1109+-- the supplied @pattern@. If @pattern@ does not contain percent signs
1110+-- or underscores, then the pattern only represents the string itself;
1111+-- in that case `like` acts like the equals operator. An underscore (_)
1112+-- in pattern stands for (matches) any single character; a percent sign (%)
1113+-- matches any sequence of zero or more characters.
1114+--
1115+-- >>> printSQL $ "abc" `like` "a%"
1116+-- (E'abc' LIKE E'a%')
1117+like :: Operator (null 'PGtext) (null 'PGtext) ('Null 'PGbool)
1118+like = unsafeBinaryOp "LIKE"
1119+
1120+-- | The key word ILIKE can be used instead of LIKE to make the
1121+-- match case-insensitive according to the active locale.
1122+--
1123+-- >>> printSQL $ "abc" `ilike` "a%"
1124+-- (E'abc' ILIKE E'a%')
1125+ilike :: Operator (null 'PGtext) (null 'PGtext) ('Null 'PGbool)
1126+ilike = unsafeBinaryOp "ILIKE"
1127+
1128+{-----------------------------------------
1129+ -- json and jsonb support
1130+
1131+See https://www.postgresql.org/docs/10/static/functions-json.html -- most
1132+comments lifted directly from this page.
1133+
1134+Table 9.44: json and jsonb operators
1135+-----------------------------------------}
1136+
1137+-- | Get JSON value (object field or array element) at a key.
1138+(.->)
1139+ :: (json `In` PGJsonType, key `In` PGJsonKey)
1140+ => Operator (null json) (null key) ('Null json)
1141+infixl 8 .->
1142+(.->) = unsafeBinaryOp "->"
1143+
1144+-- | Get JSON value (object field or array element) at a key, as text.
1145+(.->>)
1146+ :: (json `In` PGJsonType, key `In` PGJsonKey)
1147+ => Operator (null json) (null key) ('Null 'PGtext)
1148+infixl 8 .->>
1149+(.->>) = unsafeBinaryOp "->>"
1150+
1151+-- | Get JSON value at a specified path.
1152+(.#>)
1153+ :: (json `In` PGJsonType, PGTextArray "(.#>)" path)
1154+ => Operator (null json) (null path) ('Null json)
1155+infixl 8 .#>
1156+(.#>) = unsafeBinaryOp "#>"
1157+
1158+-- | Get JSON value at a specified path as text.
1159+(.#>>)
1160+ :: (json `In` PGJsonType, PGTextArray "(.#>>)" path)
1161+ => Operator (null json) (null path) ('Null 'PGtext)
1162+infixl 8 .#>>
1163+(.#>>) = unsafeBinaryOp "#>>"
1164+
1165+-- Additional jsonb operators
1166+
1167+class PGSubset container where
1168+ (@>) :: Operator (null0 container) (null1 container) ('Null 'PGbool)
1169+ (@>) = unsafeBinaryOp "@>"
1170+ (<@) :: Operator (null0 container) (null1 container) ('Null 'PGbool)
1171+ (<@) = unsafeBinaryOp "<@"
1172+instance PGSubset 'PGjsonb
1173+instance PGSubset 'PGtsquery
1174+instance PGSubset ('PGvararray ty)
1175+
1176+-- | Does the string exist as a top-level key within the JSON value?
1177+(.?) :: Operator (null 'PGjsonb) (null 'PGtext) ('Null 'PGbool)
1178+infixl 9 .?
1179+(.?) = unsafeBinaryOp "?"
1180+
1181+-- | Do any of these array strings exist as top-level keys?
1182+(.?|) :: Operator
1183+ (null 'PGjsonb)
1184+ (null ('PGvararray ('NotNull 'PGtext)))
1185+ ('Null 'PGbool)
1186+infixl 9 .?|
1187+(.?|) = unsafeBinaryOp "?|"
1188+
1189+-- | Do all of these array strings exist as top-level keys?
1190+(.?&) :: Operator
1191+ (null 'PGjsonb)
1192+ (null ('PGvararray ('NotNull 'PGtext)))
1193+ ('Null 'PGbool)
1194+infixl 9 .?&
1195+(.?&) = unsafeBinaryOp "?&"
1196+
1197+-- | Concatenate two jsonb values into a new jsonb value.
1198+instance Semigroup
1199+ (Expression outer grp commons schemas params from (null 'PGjsonb)) where
1200+ (<>) = unsafeBinaryOp "||"
1201+
1202+-- | Delete a key or keys from a JSON object, or remove an array element.
1203+--
1204+-- If the right operand is..
1205+--
1206+-- @ text @: Delete key/value pair or string element from left operand. Key/value pairs
1207+-- are matched based on their key value.
1208+--
1209+-- @ text[] @: Delete multiple key/value pairs or string elements
1210+-- from left operand. Key/value pairs are matched based on their key value.
1211+--
1212+-- @ integer @: Delete the array element with specified index (Negative integers
1213+-- count from the end). Throws an error if top level container is not an array.
1214+(.-.)
1215+ :: (key `In` '[ 'PGtext, 'PGvararray ('NotNull 'PGtext), 'PGint4, 'PGint2 ]) -- hlint error without parens here
1216+ => Operator (null 'PGjsonb) (null key) (null 'PGjsonb)
1217+infixl 6 .-.
1218+(.-.) = unsafeBinaryOp "-"
1219+
1220+-- | Delete the field or element with specified path (for JSON arrays, negative
1221+-- integers count from the end)
1222+(#-.)
1223+ :: PGTextArray "(#-.)" arrayty
1224+ => Operator (null 'PGjsonb) (null arrayty)(null 'PGjsonb)
1225+infixl 6 #-.
1226+(#-.) = unsafeBinaryOp "#-"
1227+
1228+{-----------------------------------------
1229+Table 9.45: JSON creation functions
1230+-----------------------------------------}
1231+
1232+-- | Literal binary JSON
1233+jsonbLit :: JSON.ToJSON x => x -> Expr (null 'PGjsonb)
1234+jsonbLit = cast jsonb . UnsafeExpression
1235+ . singleQuotedUtf8 . toStrict . JSON.encode
1236+
1237+-- | Literal JSON
1238+jsonLit :: JSON.ToJSON x => x -> Expr (null 'PGjson)
1239+jsonLit = cast json . UnsafeExpression
1240+ . singleQuotedUtf8 . toStrict . JSON.encode
1241+
1242+-- | Returns the value as json. Arrays and composites are converted
1243+-- (recursively) to arrays and objects; otherwise, if there is a cast from the
1244+-- type to json, the cast function will be used to perform the conversion;
1245+-- otherwise, a scalar value is produced. For any scalar type other than a
1246+-- number, a Boolean, or a null value, the text representation will be used, in
1247+-- such a fashion that it is a valid json value.
1248+toJson :: null ty :--> null 'PGjson
1249+toJson = unsafeFunction "to_json"
1250+
1251+-- | Returns the value as jsonb. Arrays and composites are converted
1252+-- (recursively) to arrays and objects; otherwise, if there is a cast from the
1253+-- type to json, the cast function will be used to perform the conversion;
1254+-- otherwise, a scalar value is produced. For any scalar type other than a
1255+-- number, a Boolean, or a null value, the text representation will be used, in
1256+-- such a fashion that it is a valid jsonb value.
1257+toJsonb :: null ty :--> null 'PGjsonb
1258+toJsonb = unsafeFunction "to_jsonb"
1259+
1260+-- | Returns the array as a JSON array. A PostgreSQL multidimensional array
1261+-- becomes a JSON array of arrays.
1262+arrayToJson
1263+ :: PGArray "arrayToJson" arr
1264+ => null arr :--> null 'PGjson
1265+arrayToJson = unsafeFunction "array_to_json"
1266+
1267+-- | Returns the row as a JSON object.
1268+rowToJson :: null ('PGcomposite ty) :--> null 'PGjson
1269+rowToJson = unsafeFunction "row_to_json"
1270+
1271+-- | Builds a possibly-heterogeneously-typed JSON array out of a variadic
1272+-- argument list.
1273+jsonBuildArray
1274+ :: SListI elems
1275+ => NP (Expression outer grp commons schemas params from) elems
1276+ -> Expression outer grp commons schemas params from (null 'PGjson)
1277+jsonBuildArray = unsafeVariadicFunction "json_build_array"
1278+
1279+-- | Builds a possibly-heterogeneously-typed (binary) JSON array out of a
1280+-- variadic argument list.
1281+jsonbBuildArray
1282+ :: SListI elems
1283+ => NP (Expression outer grp commons schemas params from) elems
1284+ -> Expression outer grp commons schemas params from (null 'PGjsonb)
1285+jsonbBuildArray = unsafeVariadicFunction "jsonb_build_array"
1286+
1287+unsafeRowFunction
1288+ :: SOP.SListI elems
1289+ => NP (Aliased (Expression outer grp commons schemas params from)) elems
1290+ -> [ByteString]
1291+unsafeRowFunction =
1292+ (`appEndo` []) . hcfoldMap (Proxy :: SOP.Proxy SOP.Top)
1293+ (\(col `As` name) -> Endo $ \xs ->
1294+ renderAliasString name : renderSQL col : xs)
1295+ where
1296+ renderAliasString :: KnownSymbol alias => Alias alias -> ByteString
1297+ renderAliasString = singleQuotedText . fromString . symbolVal
1298+
1299+-- | Builds a possibly-heterogeneously-typed JSON object out of a variadic
1300+-- argument list. The elements of the argument list must alternate between text
1301+-- and values.
1302+jsonBuildObject
1303+ :: SOP.SListI elems
1304+ => NP (Aliased (Expression outer grp commons schemas params from)) elems
1305+ -> Expression outer grp commons schemas params from (null 'PGjson)
1306+jsonBuildObject
1307+ = unsafeFunction "json_build_object"
1308+ . UnsafeExpression
1309+ . commaSeparated
1310+ . unsafeRowFunction
1311+
1312+-- | Builds a possibly-heterogeneously-typed (binary) JSON object out of a
1313+-- variadic argument list. The elements of the argument list must alternate
1314+-- between text and values.
1315+jsonbBuildObject
1316+ :: SOP.SListI elems
1317+ => NP (Aliased (Expression outer grp commons schemas params from)) elems
1318+ -> Expression outer grp commons schemas params from (null 'PGjsonb)
1319+jsonbBuildObject
1320+ = unsafeFunction "jsonb_build_object"
1321+ . UnsafeExpression
1322+ . commaSeparated
1323+ . unsafeRowFunction
1324+
1325+-- | Builds a JSON object out of a text array. The array must have either
1326+-- exactly one dimension with an even number of members, in which case they are
1327+-- taken as alternating key/value pairs, or two dimensions such that each inner
1328+-- array has exactly two elements, which are taken as a key/value pair.
1329+jsonObject
1330+ :: PGArrayOf "jsonObject" arr ('NotNull 'PGtext)
1331+ => null arr :--> null 'PGjson
1332+jsonObject = unsafeFunction "json_object"
1333+
1334+-- | Builds a binary JSON object out of a text array. The array must have either
1335+-- exactly one dimension with an even number of members, in which case they are
1336+-- taken as alternating key/value pairs, or two dimensions such that each inner
1337+-- array has exactly two elements, which are taken as a key/value pair.
1338+jsonbObject
1339+ :: PGArrayOf "jsonbObject" arr ('NotNull 'PGtext)
1340+ => null arr :--> null 'PGjsonb
1341+jsonbObject = unsafeFunction "jsonb_object"
1342+
1343+-- | This is an alternate form of 'jsonObject' that takes two arrays; one for
1344+-- keys and one for values, that are zipped pairwise to create a JSON object.
1345+jsonZipObject
1346+ :: ( PGArrayOf "jsonZipObject" keysArray ('NotNull 'PGtext)
1347+ , PGArrayOf "jsonZipObject" valuesArray ('NotNull 'PGtext))
1348+ => Expression outer grp commons schemas params from (null keysArray)
1349+ -> Expression outer grp commons schemas params from (null valuesArray)
1350+ -> Expression outer grp commons schemas params from (null 'PGjson)
1351+jsonZipObject ks vs =
1352+ unsafeVariadicFunction "json_object" (ks :* vs :* Nil)
1353+
1354+-- | This is an alternate form of 'jsonObject' that takes two arrays; one for
1355+-- keys and one for values, that are zipped pairwise to create a binary JSON
1356+-- object.
1357+jsonbZipObject
1358+ :: ( PGArrayOf "jsonbZipObject" keysArray ('NotNull 'PGtext)
1359+ , PGArrayOf "jsonbZipObject" valuesArray ('NotNull 'PGtext))
1360+ => Expression outer grp commons schemas params from (null keysArray)
1361+ -> Expression outer grp commons schemas params from (null valuesArray)
1362+ -> Expression outer grp commons schemas params from (null 'PGjsonb)
1363+jsonbZipObject ks vs =
1364+ unsafeVariadicFunction "jsonb_object" (ks :* vs :* Nil)
1365+
1366+{-----------------------------------------
1367+Table 9.46: JSON processing functions
1368+-----------------------------------------}
1369+
1370+-- | Returns the number of elements in the outermost JSON array.
1371+jsonArrayLength :: null 'PGjson :--> null 'PGint4
1372+jsonArrayLength = unsafeFunction "json_array_length"
1373+
1374+-- | Returns the number of elements in the outermost binary JSON array.
1375+jsonbArrayLength :: null 'PGjsonb :--> null 'PGint4
1376+jsonbArrayLength = unsafeFunction "jsonb_array_length"
1377+
1378+-- | Returns JSON value pointed to by the given path (equivalent to #>
1379+-- operator).
1380+jsonExtractPath
1381+ :: SListI elems
1382+ => Expression outer grp commons schemas params from (null 'PGjson)
1383+ -> NP (Expression outer grp commons schemas params from) elems
1384+ -> Expression outer grp commons schemas params from (null 'PGjsonb)
1385+jsonExtractPath x xs =
1386+ unsafeVariadicFunction "json_extract_path" (x :* xs)
1387+
1388+-- | Returns JSON value pointed to by the given path (equivalent to #>
1389+-- operator).
1390+jsonbExtractPath
1391+ :: SListI elems
1392+ => Expression outer grp commons schemas params from (null 'PGjsonb)
1393+ -> NP (Expression outer grp commons schemas params from) elems
1394+ -> Expression outer grp commons schemas params from (null 'PGjsonb)
1395+jsonbExtractPath x xs =
1396+ unsafeVariadicFunction "jsonb_extract_path" (x :* xs)
1397+
1398+-- | Returns JSON value pointed to by the given path (equivalent to #>
1399+-- operator), as text.
1400+jsonExtractPathAsText
1401+ :: SListI elems
1402+ => Expression outer grp commons schemas params from (null 'PGjson)
1403+ -> NP (Expression outer grp commons schemas params from) elems
1404+ -> Expression outer grp commons schemas params from (null 'PGjson)
1405+jsonExtractPathAsText x xs =
1406+ unsafeVariadicFunction "json_extract_path_text" (x :* xs)
1407+
1408+-- | Returns JSON value pointed to by the given path (equivalent to #>
1409+-- operator), as text.
1410+jsonbExtractPathAsText
1411+ :: SListI elems
1412+ => Expression outer grp commons schemas params from (null 'PGjsonb)
1413+ -> NP (Expression outer grp commons schemas params from) elems
1414+ -> Expression outer grp commons schemas params from (null 'PGjsonb)
1415+jsonbExtractPathAsText x xs =
1416+ unsafeVariadicFunction "jsonb_extract_path_text" (x :* xs)
1417+
1418+-- | Returns the type of the outermost JSON value as a text string. Possible
1419+-- types are object, array, string, number, boolean, and null.
1420+jsonTypeof :: null 'PGjson :--> null 'PGtext
1421+jsonTypeof = unsafeFunction "json_typeof"
1422+
1423+-- | Returns the type of the outermost binary JSON value as a text string.
1424+-- Possible types are object, array, string, number, boolean, and null.
1425+jsonbTypeof :: null 'PGjsonb :--> null 'PGtext
1426+jsonbTypeof = unsafeFunction "jsonb_typeof"
1427+
1428+-- | Returns its argument with all object fields that have null values omitted.
1429+-- Other null values are untouched.
1430+jsonStripNulls :: null 'PGjson :--> null 'PGjson
1431+jsonStripNulls = unsafeFunction "json_strip_nulls"
1432+
1433+-- | Returns its argument with all object fields that have null values omitted.
1434+-- Other null values are untouched.
1435+jsonbStripNulls :: null 'PGjsonb :--> null 'PGjsonb
1436+jsonbStripNulls = unsafeFunction "jsonb_strip_nulls"
1437+
1438+-- | @ jsonbSet target path new_value create_missing @
1439+--
1440+-- Returns target with the section designated by path replaced by new_value,
1441+-- or with new_value added if create_missing is true ( default is true) and the
1442+-- item designated by path does not exist. As with the path orientated
1443+-- operators, negative integers that appear in path count from the end of JSON
1444+-- arrays.
1445+jsonbSet
1446+ :: PGTextArray "jsonbSet" arr
1447+ => Expression outer grp commons schemas params from (null 'PGjsonb)
1448+ -> Expression outer grp commons schemas params from (null arr)
1449+ -> Expression outer grp commons schemas params from (null 'PGjsonb)
1450+ -> Maybe (Expression outer grp commons schemas params from (null 'PGbool))
1451+ -> Expression outer grp commons schemas params from (null 'PGjsonb)
1452+jsonbSet tgt path val createMissing = case createMissing of
1453+ Just m -> unsafeVariadicFunction "jsonb_set" (tgt :* path :* val :* m :* Nil)
1454+ Nothing -> unsafeVariadicFunction "jsonb_set" (tgt :* path :* val :* Nil)
1455+
1456+-- | @ jsonbInsert target path new_value insert_after @
1457+--
1458+-- Returns target with new_value inserted. If target section designated by
1459+-- path is in a JSONB array, new_value will be inserted before target or after
1460+-- if insert_after is true (default is false). If target section designated by
1461+-- path is in JSONB object, new_value will be inserted only if target does not
1462+-- exist. As with the path orientated operators, negative integers that appear
1463+-- in path count from the end of JSON arrays.
1464+jsonbInsert
1465+ :: PGTextArray "jsonbInsert" arr
1466+ => Expression outer grp commons schemas params from (null 'PGjsonb)
1467+ -> Expression outer grp commons schemas params from (null arr)
1468+ -> Expression outer grp commons schemas params from (null 'PGjsonb)
1469+ -> Maybe (Expression outer grp commons schemas params from (null 'PGbool))
1470+ -> Expression outer grp commons schemas params from (null 'PGjsonb)
1471+jsonbInsert tgt path val insertAfter = case insertAfter of
1472+ Just i -> unsafeVariadicFunction "jsonb_insert" (tgt :* path :* val :* i :* Nil)
1473+ Nothing -> unsafeVariadicFunction "jsonb_insert" (tgt :* path :* val :* Nil)
1474+
1475+-- | Returns its argument as indented JSON text.
1476+jsonbPretty :: null 'PGjsonb :--> null 'PGtext
1477+jsonbPretty = unsafeFunction "jsonb_pretty"
1478+
1479+(@@) :: Operator (null 'PGtsvector) (null 'PGtsquery) ('Null 'PGbool)
1480+(@@) = unsafeBinaryOp "@@"
1481+
1482+(.&) :: Operator (null 'PGtsquery) (null 'PGtsquery) (null 'PGtsquery)
1483+(.&) = unsafeBinaryOp "&&"
1484+
1485+(.|) :: Operator (null 'PGtsquery) (null 'PGtsquery) (null 'PGtsquery)
1486+(.|) = unsafeBinaryOp "||"
1487+
1488+(.!) :: null 'PGtsquery :--> null 'PGtsquery
1489+(.!) = unsafeUnaryOpL "!!"
1490+
1491+(<->) :: Operator (null 'PGtsquery) (null 'PGtsquery) (null 'PGtsquery)
1492+(<->) = unsafeBinaryOp "<->"
1493+
1494+arrayToTSvector
1495+ :: null ('PGvararray ('NotNull 'PGtext))
1496+ :--> null 'PGtsvector
1497+arrayToTSvector = unsafeFunction "array_to_tsvector"
1498+
1499+tsvectorLength :: null 'PGtsvector :--> null 'PGint4
1500+tsvectorLength = unsafeFunction "length"
1501+
1502+numnode :: null 'PGtsquery :--> null 'PGint4
1503+numnode = unsafeFunction "numnode"
1504+
1505+plainToTSquery :: null 'PGtext :--> null 'PGtsquery
1506+plainToTSquery = unsafeFunction "plainto_tsquery"
1507+
1508+phraseToTSquery :: null 'PGtext :--> null 'PGtsquery
1509+phraseToTSquery = unsafeFunction "phraseto_tsquery"
1510+
1511+websearchToTSquery :: null 'PGtext :--> null 'PGtsquery
1512+websearchToTSquery = unsafeFunction "websearch_to_tsquery"
1513+
1514+queryTree :: null 'PGtsquery :--> null 'PGtext
1515+queryTree = unsafeFunction "query_tree"
1516+
1517+toTSquery :: null 'PGtext :--> null 'PGtsquery
1518+toTSquery = unsafeFunction "to_tsquery"
1519+
1520+toTSvector
1521+ :: ty `In` '[ 'PGtext, 'PGjson, 'PGjsonb]
1522+ => null ty :--> null 'PGtsquery
1523+toTSvector = unsafeFunction "to_tsvector"
1524+
1525+setWeight
1526+ :: FunctionHet '[null 'PGtsvector, null ('PGchar 1)] (null 'PGtsvector)
1527+setWeight = unsafeFunctionHet "set_weight"
1528+
1529+litChar :: Char -> Expr (null ('PGchar 1))
1530+litChar chr = UnsafeExpression $
1531+ "E\'" <> fromString (escape =<< [chr]) <> "\'"
1532+
1533+strip :: null 'PGtsvector :--> null 'PGtsvector
1534+strip = unsafeFunction "strip"
1535+
1536+jsonToTSvector :: FunctionHet '[null 'PGjson, null 'PGjson] (null 'PGtsvector)
1537+jsonToTSvector = unsafeFunctionHet "json_to_tsvector"
1538+
1539+jsonbToTSvector :: FunctionHet '[null 'PGjsonb, null 'PGjsonb] (null 'PGtsvector)
1540+jsonbToTSvector = unsafeFunctionHet "jsonb_to_tsvector"
1541+
1542+tsDelete :: FunctionHet
1543+ '[null 'PGtsvector, null ('PGvararray ('NotNull 'PGtext))]
1544+ (null 'PGtsvector)
1545+tsDelete = unsafeFunctionHet "ts_delete"
1546+
1547+tsFilter :: FunctionHet
1548+ '[null 'PGtsvector, null ('PGvararray ('NotNull ('PGchar 1)))]
1549+ (null 'PGtsvector)
1550+tsFilter = unsafeFunctionHet "ts_filter"
1551+
1552+tsHeadline
1553+ :: document `In` '[ 'PGtext, 'PGjson, 'PGjsonb]
1554+ => FunctionHet '[null document, null 'PGtsquery] (null 'PGtext)
1555+tsHeadline = unsafeFunctionHet "ts_headline"
1556+{-----------------------------------------
1557+aggregation
1558+-----------------------------------------}
1559+
1560+{- |
1561+`Aggregate` functions compute a single result from a set of input values.
1562+`Aggregate` functions can be used as `GroupedBy` `Expression`s as well
1563+as `WindowFunction`s.
1564+-}
1565+class Aggregate expr1 expr2 aggr
1566+ | aggr -> expr1, aggr -> expr2 where
1567+
1568+ -- | A special aggregation that does not require an input
1569+ --
1570+ -- >>> :{
1571+ -- let
1572+ -- expression :: Expression '[] ('Grouped bys) commons schemas params from ('NotNull 'PGint8)
1573+ -- expression = countStar
1574+ -- in printSQL expression
1575+ -- :}
1576+ -- count(*)
1577+ countStar :: aggr ('NotNull 'PGint8)
1578+
1579+ -- | >>> :{
1580+ -- let
1581+ -- expression :: Expression '[] ('Grouped bys) commons schemas params '[tab ::: '["col" ::: null ty]] ('NotNull 'PGint8)
1582+ -- expression = count (All #col)
1583+ -- in printSQL expression
1584+ -- :}
1585+ -- count(ALL "col")
1586+ count
1587+ :: expr1 ty
1588+ -- ^ what to count
1589+ -> aggr ('NotNull 'PGint8)
1590+
1591+ -- | >>> :{
1592+ -- let
1593+ -- expression :: Expression '[] ('Grouped bys) commons schemas params '[tab ::: '["col" ::: 'Null 'PGnumeric]] ('NotNull 'PGnumeric)
1594+ -- expression = sum_ (Distinct #col)
1595+ -- in printSQL expression
1596+ -- :}
1597+ -- sum(DISTINCT "col")
1598+ sum_
1599+ :: ty `In` PGNum
1600+ => expr1 (null ty)
1601+ -> aggr ('NotNull ty)
1602+
1603+ -- | input values, including nulls, concatenated into an array
1604+ arrayAgg
1605+ :: expr1 ty
1606+ -> aggr ('Null ('PGvararray ty))
1607+
1608+ -- | aggregates values as a JSON array
1609+ jsonAgg
1610+ :: expr1 ty
1611+ -> aggr ('Null 'PGjson)
1612+
1613+ -- | aggregates values as a JSON array
1614+ jsonbAgg
1615+ :: expr1 ty
1616+ -> aggr ('Null 'PGjsonb)
1617+
1618+ {- |
1619+ the bitwise AND of all non-null input values, or null if none
1620+ >>> :{
1621+ let
1622+ expression :: Expression '[] ('Grouped bys) commons schemas params '[tab ::: '["col" ::: null 'PGint4]] ('Null 'PGint4)
1623+ expression = bitAnd (Distinct #col)
1624+ in printSQL expression
1625+ :}
1626+ bit_and(DISTINCT "col")
1627+ -}
1628+ bitAnd
1629+ :: int `In` PGIntegral
1630+ => expr1 (null int)
1631+ -- ^ what to aggregate
1632+ -> aggr ('Null int)
1633+
1634+ {- |
1635+ the bitwise OR of all non-null input values, or null if none
1636+
1637+ >>> :{
1638+ let
1639+ expression :: Expression '[] ('Grouped bys) commons schemas params '[tab ::: '["col" ::: null 'PGint4]] ('Null 'PGint4)
1640+ expression = bitOr (All #col)
1641+ in printSQL expression
1642+ :}
1643+ bit_or(ALL "col")
1644+ -}
1645+ bitOr
1646+ :: int `In` PGIntegral
1647+ => expr1 (null int)
1648+ -- ^ what to aggregate
1649+ -> aggr ('Null int)
1650+
1651+ {- |
1652+ true if all input values are true, otherwise false
1653+
1654+ >>> :{
1655+ let
1656+ winFun :: WindowFunction '[] 'Ungrouped commons schemas params '[tab ::: '["col" ::: null 'PGbool]] ('Null 'PGbool)
1657+ winFun = boolAnd #col
1658+ in printSQL winFun
1659+ :}
1660+ bool_and("col")
1661+ -}
1662+ boolAnd
1663+ :: expr1 (null 'PGbool)
1664+ -- ^ what to aggregate
1665+ -> aggr ('Null 'PGbool)
1666+
1667+ {- |
1668+ true if at least one input value is true, otherwise false
1669+
1670+ >>> :{
1671+ let
1672+ expression :: Expression '[] ('Grouped bys) commons schemas params '[tab ::: '["col" ::: null 'PGbool]] ('Null 'PGbool)
1673+ expression = boolOr (All #col)
1674+ in printSQL expression
1675+ :}
1676+ bool_or(ALL "col")
1677+ -}
1678+ boolOr
1679+ :: expr1(null 'PGbool)
1680+ -- ^ what to aggregate
1681+ -> aggr ('Null 'PGbool)
1682+
1683+ {- |
1684+ equivalent to `boolAnd`
1685+
1686+ >>> :{
1687+ let
1688+ expression :: Expression '[] ('Grouped bys) commons schemas params '[tab ::: '["col" ::: null 'PGbool]] ('Null 'PGbool)
1689+ expression = every (Distinct #col)
1690+ in printSQL expression
1691+ :}
1692+ every(DISTINCT "col")
1693+ -}
1694+ every
1695+ :: expr1 (null 'PGbool)
1696+ -- ^ what to aggregate
1697+ -> aggr ('Null 'PGbool)
1698+
1699+ {- |maximum value of expression across all input values-}
1700+ max_
1701+ :: expr1 (null ty)
1702+ -- ^ what to maximize
1703+ -> aggr ('Null ty)
1704+
1705+ -- | minimum value of expression across all input values
1706+ min_
1707+ :: expr1 (null ty)
1708+ -- ^ what to minimize
1709+ -> aggr ('Null ty)
1710+
1711+ -- | the average (arithmetic mean) of all input values
1712+ avg
1713+ :: expr1 (null ty)
1714+ -- ^ what to average
1715+ -> aggr ('Null (PGAvg ty))
1716+
1717+ {- | correlation coefficient
1718+ >>> :{
1719+ let
1720+ expression :: Expression '[] ('Grouped g) c s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1721+ expression = corr (All (#y :*: #x))
1722+ in printSQL expression
1723+ :}
1724+ corr(ALL "y", "x")
1725+ -}
1726+ corr
1727+ :: expr2 (null 'PGfloat8)
1728+ -> aggr ('Null 'PGfloat8)
1729+
1730+ {- | population covariance
1731+ >>> :{
1732+ let
1733+ expression :: Expression '[] ('Grouped g) c s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1734+ expression = covarPop (All (#y :*: #x))
1735+ in printSQL expression
1736+ :}
1737+ covar_pop(ALL "y", "x")
1738+ -}
1739+ covarPop
1740+ :: expr2 (null 'PGfloat8)
1741+ -> aggr ('Null 'PGfloat8)
1742+
1743+ {- | sample covariance
1744+ >>> :{
1745+ let
1746+ winFun :: WindowFunction '[] 'Ungrouped c s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1747+ winFun = covarSamp (#y :*: #x)
1748+ in printSQL winFun
1749+ :}
1750+ covar_samp("y", "x")
1751+ -}
1752+ covarSamp
1753+ :: expr2 (null 'PGfloat8)
1754+ -> aggr ('Null 'PGfloat8)
1755+
1756+ {- | average of the independent variable (sum(X)/N)
1757+ >>> :{
1758+ let
1759+ expression :: Expression '[] ('Grouped g) c s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1760+ expression = regrAvgX (All (#y :*: #x))
1761+ in printSQL expression
1762+ :}
1763+ regr_avgx(ALL "y", "x")
1764+ -}
1765+ regrAvgX
1766+ :: expr2 (null 'PGfloat8)
1767+ -> aggr ('Null 'PGfloat8)
1768+
1769+ {- | average of the dependent variable (sum(Y)/N)
1770+ >>> :{
1771+ let
1772+ winFun :: WindowFunction '[] 'Ungrouped c s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1773+ winFun = regrAvgY (#y :*: #x)
1774+ in printSQL winFun
1775+ :}
1776+ regr_avgy("y", "x")
1777+ -}
1778+ regrAvgY
1779+ :: expr2 (null 'PGfloat8)
1780+ -> aggr ('Null 'PGfloat8)
1781+
1782+ {- | number of input rows in which both expressions are nonnull
1783+ >>> :{
1784+ let
1785+ winFun :: WindowFunction '[] 'Ungrouped c s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGint8)
1786+ winFun = regrCount (#y :*: #x)
1787+ in printSQL winFun
1788+ :}
1789+ regr_count("y", "x")
1790+ -}
1791+ regrCount
1792+ :: expr2 (null 'PGfloat8)
1793+ -> aggr ('Null 'PGint8)
1794+
1795+ {- | y-intercept of the least-squares-fit linear equation determined by the (X, Y) pairs
1796+ >>> :{
1797+ let
1798+ expression :: Expression '[] ('Grouped g) c s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1799+ expression = regrIntercept (All (#y :*: #x))
1800+ in printSQL expression
1801+ :}
1802+ regr_intercept(ALL "y", "x")
1803+ -}
1804+ regrIntercept
1805+ :: expr2 (null 'PGfloat8)
1806+ -> aggr ('Null 'PGfloat8)
1807+
1808+ -- | @regr_r2(Y, X)@, square of the correlation coefficient
1809+ regrR2
1810+ :: expr2 (null 'PGfloat8)
1811+ -> aggr ('Null 'PGfloat8)
1812+
1813+ -- | @regr_slope(Y, X)@, slope of the least-squares-fit linear equation
1814+ -- determined by the (X, Y) pairs
1815+ regrSlope
1816+ :: expr2 (null 'PGfloat8)
1817+ -> aggr ('Null 'PGfloat8)
1818+
1819+ -- | @regr_sxx(Y, X)@, sum(X^2) - sum(X)^2/N
1820+ -- (“sum of squares” of the independent variable)
1821+ regrSxx
1822+ :: expr2 (null 'PGfloat8)
1823+ -> aggr ('Null 'PGfloat8)
1824+
1825+ -- | @regr_sxy(Y, X)@, sum(X*Y) - sum(X) * sum(Y)/N
1826+ -- (“sum of products” of independent times dependent variable)
1827+ regrSxy
1828+ :: expr2 (null 'PGfloat8)
1829+ -> aggr ('Null 'PGfloat8)
1830+
1831+ -- | @regr_syy(Y, X)@, sum(Y^2) - sum(Y)^2/N
1832+ -- (“sum of squares” of the dependent variable)
1833+ regrSyy
1834+ :: expr2 (null 'PGfloat8)
1835+ -> aggr ('Null 'PGfloat8)
1836+
1837+ -- | historical alias for `stddevSamp`
1838+ stddev
1839+ :: expr1 (null ty)
1840+ -> aggr ('Null (PGAvg ty))
1841+
1842+ -- | population standard deviation of the input values
1843+ stddevPop
1844+ :: expr1 (null ty)
1845+ -> aggr ('Null (PGAvg ty))
1846+
1847+ -- | sample standard deviation of the input values
1848+ stddevSamp
1849+ :: expr1 (null ty)
1850+ -> aggr ('Null (PGAvg ty))
1851+
1852+ -- | historical alias for `varSamp`
1853+ variance
1854+ :: expr1 (null ty)
1855+ -> aggr ('Null (PGAvg ty))
1856+
1857+ -- | population variance of the input values
1858+ -- (square of the population standard deviation)
1859+ varPop
1860+ :: expr1 (null ty)
1861+ -> aggr ('Null (PGAvg ty))
1862+
1863+ -- | sample variance of the input values
1864+ -- (square of the sample standard deviation)
1865+ varSamp
1866+ :: expr1 (null ty)
1867+ -> aggr ('Null (PGAvg ty))
1868+
1869+{- |
1870+`Distinction`s are used for the input of `Aggregate` `Expression`s.
1871+`All` invokes the aggregate once for each input row.
1872+`Distinct` invokes the aggregate once for each distinct value of the expression
1873+(or distinct set of values, for multiple expressions) found in the input
1874+-}
1875+data Distinction expr ty
1876+ = All (expr ty)
1877+ | Distinct (expr ty)
1878+ deriving (GHC.Generic,Show,Eq,Ord)
1879+instance NFData (Distinction (Expression outer grp commons schemas params from) ty)
1880+instance RenderSQL (Distinction (Expression outer grp commons schemas params from) ty) where
1881+ renderSQL = \case
1882+ All x -> "ALL" <+> renderSQL x
1883+ Distinct x -> "DISTINCT" <+> renderSQL x
1884+instance RenderSQL
1885+ (Distinction
1886+ ( Expression outer grp commons schemas params from
1887+ :*:
1888+ Expression outer grp commons schemas params from) xty ) where
1889+ renderSQL = \case
1890+ All (x :*: x') ->
1891+ "ALL" <+> renderSQL x <> ", " <> renderSQL x'
1892+ Distinct (x :*: x') ->
1893+ "DISTINCT" <+> renderSQL x <> ", " <> renderSQL x'
1894+
1895+instance Aggregate
1896+ (Distinction (Expression outer 'Ungrouped commons schemas params from))
1897+ (Distinction (Expression outer 'Ungrouped commons schemas params from :*: Expression outer 'Ungrouped commons schemas params from))
1898+ (Expression outer ('Grouped bys) commons schemas params from) where
1899+ countStar = UnsafeExpression "count(*)"
1900+ count = unsafeAggregate1 "count"
1901+ sum_ = unsafeAggregate1 "sum"
1902+ arrayAgg = unsafeAggregate1 "array_agg"
1903+ jsonAgg = unsafeAggregate1 "json_agg"
1904+ jsonbAgg = unsafeAggregate1 "jsonb_agg"
1905+ bitAnd = unsafeAggregate1 "bit_and"
1906+ bitOr = unsafeAggregate1 "bit_or"
1907+ boolAnd = unsafeAggregate1 "bool_and"
1908+ boolOr = unsafeAggregate1 "bool_or"
1909+ every = unsafeAggregate1 "every"
1910+ max_ = unsafeAggregate1 "max"
1911+ min_ = unsafeAggregate1 "min"
1912+ avg = unsafeAggregate1 "avg"
1913+ corr = unsafeAggregate2 "corr"
1914+ covarPop = unsafeAggregate2 "covar_pop"
1915+ covarSamp = unsafeAggregate2 "covar_samp"
1916+ regrAvgX = unsafeAggregate2 "regr_avgx"
1917+ regrAvgY = unsafeAggregate2 "regr_avgy"
1918+ regrCount = unsafeAggregate2 "regr_count"
1919+ regrIntercept = unsafeAggregate2 "regr_intercept"
1920+ regrR2 = unsafeAggregate2 "regr_r2"
1921+ regrSlope = unsafeAggregate2 "regr_slope"
1922+ regrSxx = unsafeAggregate2 "regr_sxx"
1923+ regrSxy = unsafeAggregate2 "regr_sxy"
1924+ regrSyy = unsafeAggregate2 "regr_syy"
1925+ stddev = unsafeAggregate1 "stddev"
1926+ stddevPop = unsafeAggregate1 "stddev_pop"
1927+ stddevSamp = unsafeAggregate1 "stddev_samp"
1928+ variance = unsafeAggregate1 "variance"
1929+ varPop = unsafeAggregate1 "var_pop"
1930+ varSamp = unsafeAggregate1 "var_samp"
1931+instance Aggregate
1932+ (Expression outer grp commons schemas params from)
1933+ (Expression outer grp commons schemas params from :*: Expression outer grp commons schemas params from)
1934+ (WindowFunction outer grp commons schemas params from) where
1935+ countStar = UnsafeWindowFunction "count(*)"
1936+ count = unsafeWindowFunction1 "count"
1937+ sum_ = unsafeWindowFunction1 "sum"
1938+ arrayAgg = unsafeWindowFunction1 "array_agg"
1939+ jsonAgg = unsafeWindowFunction1 "json_agg"
1940+ jsonbAgg = unsafeWindowFunction1 "jsonb_agg"
1941+ bitAnd = unsafeWindowFunction1 "bit_and"
1942+ bitOr = unsafeWindowFunction1 "bit_or"
1943+ boolAnd = unsafeWindowFunction1 "bool_and"
1944+ boolOr = unsafeWindowFunction1 "bool_or"
1945+ every = unsafeWindowFunction1 "every"
1946+ max_ = unsafeWindowFunction1 "max"
1947+ min_ = unsafeWindowFunction1 "min"
1948+ avg = unsafeWindowFunction1 "avg"
1949+ corr = unsafeWindowFunction2 "corr"
1950+ covarPop = unsafeWindowFunction2 "covar_pop"
1951+ covarSamp = unsafeWindowFunction2 "covar_samp"
1952+ regrAvgX = unsafeWindowFunction2 "regr_avgx"
1953+ regrAvgY = unsafeWindowFunction2 "regr_avgy"
1954+ regrCount = unsafeWindowFunction2 "regr_count"
1955+ regrIntercept = unsafeWindowFunction2 "regr_intercept"
1956+ regrR2 = unsafeWindowFunction2 "regr_r2"
1957+ regrSlope = unsafeWindowFunction2 "regr_slope"
1958+ regrSxx = unsafeWindowFunction2 "regr_sxx"
1959+ regrSxy = unsafeWindowFunction2 "regr_sxy"
1960+ regrSyy = unsafeWindowFunction2 "regr_syy"
1961+ stddev = unsafeWindowFunction1 "stddev"
1962+ stddevPop = unsafeWindowFunction1 "stddev_pop"
1963+ stddevSamp = unsafeWindowFunction1 "stddev_samp"
1964+ variance = unsafeWindowFunction1 "variance"
1965+ varPop = unsafeWindowFunction1 "var_pop"
1966+ varSamp = unsafeWindowFunction1 "var_samp"
1967+
1968+-- | escape hatch to define aggregate functions
1969+unsafeAggregate1
1970+ :: ByteString -- ^ aggregate function
1971+ -> Distinction (Expression outer 'Ungrouped commons schemas params from) xty
1972+ -> Expression outer ('Grouped bys) commons schemas params from yty
1973+unsafeAggregate1 fun x = UnsafeExpression $ mconcat
1974+ [fun, "(", renderSQL x, ")"]
1975+
1976+-- | escape hatch to define aggregate binary functions
1977+unsafeAggregate2
1978+ :: ByteString -- ^ aggregate function
1979+ -> Distinction (Expression outer 'Ungrouped commons schemas params from :*: Expression outer 'Ungrouped commons schemas params from) xty
1980+ -> Expression outer ('Grouped bys) commons schemas params from yty
1981+unsafeAggregate2 fun xx = UnsafeExpression $ mconcat
1982+ [fun, "(", renderSQL xx, ")"]
1983+
1984+-- | A type family that calculates `PGAvg` type of a `PGType`.
1985+type family PGAvg ty where
1986+ PGAvg 'PGint2 = 'PGnumeric
1987+ PGAvg 'PGint4 = 'PGnumeric
1988+ PGAvg 'PGint8 = 'PGnumeric
1989+ PGAvg 'PGnumeric = 'PGnumeric
1990+ PGAvg 'PGfloat4 = 'PGfloat8
1991+ PGAvg 'PGfloat8 = 'PGfloat8
1992+ PGAvg 'PGinterval = 'PGinterval
1993+ PGAvg pg = TypeError
1994+ ('Text "Squeal type error: No average for " ':<>: 'ShowType pg)
1995+
1996+{-----------------------------------------
1997+type expressions
1998+-----------------------------------------}
1999+
2000+-- | `TypeExpression`s are used in `cast`s and
2001+-- `Squeal.PostgreSQL.Definition.createTable` commands.
2002+newtype TypeExpression (schemas :: SchemasType) (ty :: NullityType)
2003+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
2004+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
2005+instance RenderSQL (TypeExpression schemas ty) where
2006+ renderSQL = renderTypeExpression
2007+
2008+-- | The enum or composite type in a `Typedef` can be expressed by its alias.
2009+typedef
2010+ :: (Has sch schemas schema, Has td schema ('Typedef ty))
2011+ => QualifiedAlias sch td
2012+ -> TypeExpression schemas (null ty)
2013+typedef = UnsafeTypeExpression . renderSQL
2014+
2015+-- | The composite type corresponding to a `Table` definition can be expressed
2016+-- by its alias.
2017+typetable
2018+ :: (Has sch schemas schema, Has tab schema ('Table table))
2019+ => QualifiedAlias sch tab
2020+ -> TypeExpression schemas (null ('PGcomposite (TableToRow table)))
2021+typetable = UnsafeTypeExpression . renderSQL
2022+
2023+-- | The composite type corresponding to a `View` definition can be expressed
2024+-- by its alias.
2025+typeview
2026+ :: (Has sch schemas schema, Has vw schema ('View view))
2027+ => QualifiedAlias sch vw
2028+ -> TypeExpression schemas (null ('PGcomposite view))
2029+typeview = UnsafeTypeExpression . renderSQL
2030+
2031+-- | logical Boolean (true/false)
2032+bool :: TypeExpression schemas (null 'PGbool)
2033+bool = UnsafeTypeExpression "bool"
2034+-- | signed two-byte integer
2035+int2, smallint :: TypeExpression schemas (null 'PGint2)
2036+int2 = UnsafeTypeExpression "int2"
2037+smallint = UnsafeTypeExpression "smallint"
2038+-- | signed four-byte integer
2039+int4, int, integer :: TypeExpression schemas (null 'PGint4)
2040+int4 = UnsafeTypeExpression "int4"
2041+int = UnsafeTypeExpression "int"
2042+integer = UnsafeTypeExpression "integer"
2043+-- | signed eight-byte integer
2044+int8, bigint :: TypeExpression schemas (null 'PGint8)
2045+int8 = UnsafeTypeExpression "int8"
2046+bigint = UnsafeTypeExpression "bigint"
2047+-- | arbitrary precision numeric type
2048+numeric :: TypeExpression schemas (null 'PGnumeric)
2049+numeric = UnsafeTypeExpression "numeric"
2050+-- | single precision floating-point number (4 bytes)
2051+float4, real :: TypeExpression schemas (null 'PGfloat4)
2052+float4 = UnsafeTypeExpression "float4"
2053+real = UnsafeTypeExpression "real"
2054+-- | double precision floating-point number (8 bytes)
2055+float8, doublePrecision :: TypeExpression schemas (null 'PGfloat8)
2056+float8 = UnsafeTypeExpression "float8"
2057+doublePrecision = UnsafeTypeExpression "double precision"
2058+-- | currency amount
2059+money :: TypeExpression schema (null 'PGmoney)
2060+money = UnsafeTypeExpression "money"
2061+-- | variable-length character string
2062+text :: TypeExpression schemas (null 'PGtext)
2063+text = UnsafeTypeExpression "text"
2064+-- | fixed-length character string
2065+char, character
2066+ :: forall n schemas null. (KnownNat n, 1 <= n)
2067+ => TypeExpression schemas (null ('PGchar n))
2068+char = UnsafeTypeExpression $ "char(" <> renderNat @n <> ")"
2069+character = UnsafeTypeExpression $ "character(" <> renderNat @n <> ")"
2070+-- | variable-length character string
2071+varchar, characterVarying
2072+ :: forall n schemas null. (KnownNat n, 1 <= n)
2073+ => TypeExpression schemas (null ('PGvarchar n))
2074+varchar = UnsafeTypeExpression $ "varchar(" <> renderNat @n <> ")"
2075+characterVarying = UnsafeTypeExpression $
2076+ "character varying(" <> renderNat @n <> ")"
2077+-- | binary data ("byte array")
2078+bytea :: TypeExpression schemas (null 'PGbytea)
2079+bytea = UnsafeTypeExpression "bytea"
2080+-- | date and time (no time zone)
2081+timestamp :: TypeExpression schemas (null 'PGtimestamp)
2082+timestamp = UnsafeTypeExpression "timestamp"
2083+-- | date and time, including time zone
2084+timestampWithTimeZone :: TypeExpression schemas (null 'PGtimestamptz)
2085+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
2086+-- | calendar date (year, month, day)
2087+date :: TypeExpression schemas (null 'PGdate)
2088+date = UnsafeTypeExpression "date"
2089+-- | time of day (no time zone)
2090+time :: TypeExpression schemas (null 'PGtime)
2091+time = UnsafeTypeExpression "time"
2092+-- | time of day, including time zone
2093+timeWithTimeZone :: TypeExpression schemas (null 'PGtimetz)
2094+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
2095+-- | time span
2096+interval :: TypeExpression schemas (null 'PGinterval)
2097+interval = UnsafeTypeExpression "interval"
2098+-- | universally unique identifier
2099+uuid :: TypeExpression schemas (null 'PGuuid)
2100+uuid = UnsafeTypeExpression "uuid"
2101+-- | IPv4 or IPv6 host address
2102+inet :: TypeExpression schemas (null 'PGinet)
2103+inet = UnsafeTypeExpression "inet"
2104+-- | textual JSON data
2105+json :: TypeExpression schemas (null 'PGjson)
2106+json = UnsafeTypeExpression "json"
2107+-- | binary JSON data, decomposed
2108+jsonb :: TypeExpression schemas (null 'PGjsonb)
2109+jsonb = UnsafeTypeExpression "jsonb"
2110+-- | variable length array
2111+vararray
2112+ :: TypeExpression schemas pg
2113+ -> TypeExpression schemas (null ('PGvararray pg))
2114+vararray ty = UnsafeTypeExpression $ renderSQL ty <> "[]"
2115+-- | fixed length array
2116+--
2117+-- >>> renderSQL (fixarray @'[2] json)
2118+-- "json[2]"
2119+fixarray
2120+ :: forall dims schemas null pg. SOP.All KnownNat dims
2121+ => TypeExpression schemas pg
2122+ -> TypeExpression schemas (null ('PGfixarray dims pg))
2123+fixarray ty = UnsafeTypeExpression $
2124+ renderSQL ty <> renderDims @dims
2125+ where
2126+ renderDims :: forall ns. SOP.All KnownNat ns => ByteString
2127+ renderDims =
2128+ ("[" <>)
2129+ . (<> "]")
2130+ . ByteString.intercalate "]["
2131+ . hcollapse
2132+ $ hcmap (SOP.Proxy @KnownNat)
2133+ (K . fromString . show . natVal)
2134+ (hpure SOP.Proxy :: NP SOP.Proxy ns)
2135+tsvector :: TypeExpression schemas (null 'PGtsvector)
2136+tsvector = UnsafeTypeExpression "tsvector"
2137+tsquery :: TypeExpression schemas (null 'PGtsquery)
2138+tsquery = UnsafeTypeExpression "tsquery"
2139+
2140+-- | `pgtype` is a demoted version of a `PGType`
2141+class PGTyped schemas (ty :: NullityType) where
2142+ pgtype :: TypeExpression schemas ty
2143+instance PGTyped schemas (null 'PGbool) where pgtype = bool
2144+instance PGTyped schemas (null 'PGint2) where pgtype = int2
2145+instance PGTyped schemas (null 'PGint4) where pgtype = int4
2146+instance PGTyped schemas (null 'PGint8) where pgtype = int8
2147+instance PGTyped schemas (null 'PGnumeric) where pgtype = numeric
2148+instance PGTyped schemas (null 'PGfloat4) where pgtype = float4
2149+instance PGTyped schemas (null 'PGfloat8) where pgtype = float8
2150+instance PGTyped schemas (null 'PGmoney) where pgtype = money
2151+instance PGTyped schemas (null 'PGtext) where pgtype = text
2152+instance (KnownNat n, 1 <= n)
2153+ => PGTyped schemas (null ('PGchar n)) where pgtype = char @n
2154+instance (KnownNat n, 1 <= n)
2155+ => PGTyped schemas (null ('PGvarchar n)) where pgtype = varchar @n
2156+instance PGTyped schemas (null 'PGbytea) where pgtype = bytea
2157+instance PGTyped schemas (null 'PGtimestamp) where pgtype = timestamp
2158+instance PGTyped schemas (null 'PGtimestamptz) where pgtype = timestampWithTimeZone
2159+instance PGTyped schemas (null 'PGdate) where pgtype = date
2160+instance PGTyped schemas (null 'PGtime) where pgtype = time
2161+instance PGTyped schemas (null 'PGtimetz) where pgtype = timeWithTimeZone
2162+instance PGTyped schemas (null 'PGinterval) where pgtype = interval
2163+instance PGTyped schemas (null 'PGuuid) where pgtype = uuid
2164+instance PGTyped schemas (null 'PGjson) where pgtype = json
2165+instance PGTyped schemas (null 'PGjsonb) where pgtype = jsonb
2166+instance PGTyped schemas ty
2167+ => PGTyped schemas (null ('PGvararray ty)) where
2168+ pgtype = vararray (pgtype @schemas @ty)
2169+instance (SOP.All KnownNat dims, PGTyped schemas ty)
2170+ => PGTyped schemas (null ('PGfixarray dims ty)) where
2171+ pgtype = fixarray @dims (pgtype @schemas @ty)
2172+instance PGTyped schemas (null 'PGtsvector) where pgtype = tsvector
2173+instance PGTyped schemas (null 'PGtsquery) where pgtype = tsquery
2174+
2175+{-----------------------------------------
2176+Sorting
2177+-----------------------------------------}
2178+
2179+-- | `SortExpression`s are used by `orderBy` to optionally sort the results
2180+-- of a `Squeal.PostgreSQL.Query.Query`. `Asc` or `Desc`
2181+-- set the sort direction of a `NotNull` result
2182+-- column to ascending or descending. Ascending order puts smaller values
2183+-- first, where "smaller" is defined in terms of the `.<` operator. Similarly,
2184+-- descending order is determined with the `.>` operator. `AscNullsFirst`,
2185+-- `AscNullsLast`, `DescNullsFirst` and `DescNullsLast` options are used to
2186+-- determine whether nulls appear before or after non-null values in the sort
2187+-- ordering of a `Null` result column.
2188+data SortExpression outer grp commons schemas params from where
2189+ Asc
2190+ :: Expression outer grp commons schemas params from ('NotNull ty)
2191+ -> SortExpression outer grp commons schemas params from
2192+ Desc
2193+ :: Expression outer grp commons schemas params from ('NotNull ty)
2194+ -> SortExpression outer grp commons schemas params from
2195+ AscNullsFirst
2196+ :: Expression outer grp commons schemas params from ('Null ty)
2197+ -> SortExpression outer grp commons schemas params from
2198+ AscNullsLast
2199+ :: Expression outer grp commons schemas params from ('Null ty)
2200+ -> SortExpression outer grp commons schemas params from
2201+ DescNullsFirst
2202+ :: Expression outer grp commons schemas params from ('Null ty)
2203+ -> SortExpression outer grp commons schemas params from
2204+ DescNullsLast
2205+ :: Expression outer grp commons schemas params from ('Null ty)
2206+ -> SortExpression outer grp commons schemas params from
2207+deriving instance Show (SortExpression outer grp commons schemas params from)
2208+
2209+{- |
2210+The `orderBy` clause causes the result rows of a `Squeal.PostgreSQL.Query.TableExpression`
2211+to be sorted according to the specified `SortExpression`(s).
2212+If two rows are equal according to the leftmost expression,
2213+they are compared according to the next expression and so on.
2214+If they are equal according to all specified expressions,
2215+they are returned in an implementation-dependent order.
2216+
2217+You can also control the order in which rows are processed by window functions
2218+using `orderBy` within `Squeal.PostgreSQL.Query.Over`.
2219+-}
2220+class OrderBy expr where
2221+ orderBy
2222+ :: [SortExpression outer grp commons schemas params from]
2223+ -> expr outer grp commons schemas params from
2224+ -> expr outer grp commons schemas params from
2225+
2226+-- | Render a `SortExpression`.
2227+instance RenderSQL (SortExpression outer grp commons schemas params from) where
2228+ renderSQL = \case
2229+ Asc expression -> renderSQL expression <+> "ASC"
2230+ Desc expression -> renderSQL expression <+> "DESC"
2231+ AscNullsFirst expression -> renderSQL expression
2232+ <+> "ASC NULLS FIRST"
2233+ DescNullsFirst expression -> renderSQL expression
2234+ <+> "DESC NULLS FIRST"
2235+ AscNullsLast expression -> renderSQL expression <+> "ASC NULLS LAST"
2236+ DescNullsLast expression -> renderSQL expression <+> "DESC NULLS LAST"
2237+
2238+-- | A `WindowDefinition` is a set of table rows that are somehow related
2239+-- to the current row
2240+data WindowDefinition outer grp commons schemas params from where
2241+ WindowDefinition
2242+ :: SListI bys
2243+ => NP (Expression outer grp commons schemas params from) bys
2244+ -> [SortExpression outer grp commons schemas params from]
2245+ -> WindowDefinition outer grp commons schemas params from
2246+
2247+instance OrderBy WindowDefinition where
2248+ orderBy sortsR (WindowDefinition parts sortsL)
2249+ = WindowDefinition parts (sortsL ++ sortsR)
2250+
2251+instance RenderSQL (WindowDefinition outer commons schemas from grp params) where
2252+ renderSQL (WindowDefinition part ord) =
2253+ renderPartitionByClause part <> renderOrderByClause ord
2254+ where
2255+ renderPartitionByClause = \case
2256+ Nil -> ""
2257+ parts -> "PARTITION" <+> "BY" <+> renderCommaSeparated renderExpression parts
2258+ renderOrderByClause = \case
2259+ [] -> ""
2260+ srts -> " ORDER" <+> "BY"
2261+ <+> commaSeparated (renderSQL <$> srts)
2262+
2263+{- |
2264+The `partitionBy` clause within `Squeal.PostgreSQL.Query.Over` divides the rows into groups,
2265+or partitions, that share the same values of the `partitionBy` `Expression`(s).
2266+For each row, the window function is computed across the rows that fall into
2267+the same partition as the current row.
2268+-}
2269+partitionBy
2270+ :: SListI bys
2271+ => NP (Expression outer grp commons schemas params from) bys
2272+ -> WindowDefinition outer grp commons schemas params from
2273+partitionBy bys = WindowDefinition bys []
2274+
2275+{- |
2276+A window function performs a calculation across a set of table rows
2277+that are somehow related to the current row. This is comparable to the type
2278+of calculation that can be done with an aggregate function.
2279+However, window functions do not cause rows to become grouped into a single
2280+output row like non-window aggregate calls would.
2281+Instead, the rows retain their separate identities.
2282+Behind the scenes, the window function is able to access more than
2283+just the current row of the query result.
2284+-}
2285+newtype WindowFunction
2286+ (outer :: FromType)
2287+ (grp :: Grouping)
2288+ (commons :: FromType)
2289+ (schemas :: SchemasType)
2290+ (params :: [NullityType])
2291+ (from :: FromType)
2292+ (ty :: NullityType)
2293+ = UnsafeWindowFunction { renderWindowFunction :: ByteString }
2294+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
2295+
2296+instance RenderSQL (WindowFunction outer grp commons schemas params from ty) where
2297+ renderSQL = renderWindowFunction
2298+
2299+unsafeWindowFunction1
2300+ :: ByteString
2301+ -> Expression outer grp commons schemas params from ty0
2302+ -> WindowFunction outer grp commons schemas params from ty1
2303+unsafeWindowFunction1 fun x
2304+ = UnsafeWindowFunction $ fun <> parenthesized (renderSQL x)
2305+
2306+unsafeWindowFunction2
2307+ :: ByteString
2308+ -> (Expression outer grp commons schemas params from :*: Expression outer grp commons schemas params from) ty0
2309+ -> WindowFunction outer grp commons schemas params from ty1
2310+unsafeWindowFunction2 fun (x :*: x')
2311+ = UnsafeWindowFunction $ fun <> parenthesized (renderSQL x <> ", " <> renderSQL x')
2312+
2313+{- | rank of the current row with gaps; same as `rowNumber` of its first peer
2314+>>> printSQL rank
2315+rank()
2316+-}
2317+rank :: WindowFunction outer grp commons schemas params from ('NotNull 'PGint8)
2318+rank = UnsafeWindowFunction "rank()"
2319+
2320+{- | number of the current row within its partition, counting from 1
2321+>>> printSQL rowNumber
2322+row_number()
2323+-}
2324+rowNumber :: WindowFunction outer grp commons schemas params from ('NotNull 'PGint8)
2325+rowNumber = UnsafeWindowFunction "row_number()"
2326+
2327+{- | rank of the current row without gaps; this function counts peer groups
2328+>>> printSQL denseRank
2329+dense_rank()
2330+-}
2331+denseRank :: WindowFunction outer grp commons schemas params from ('NotNull 'PGint8)
2332+denseRank = UnsafeWindowFunction "dense_rank()"
2333+
2334+{- | relative rank of the current row: (rank - 1) / (total partition rows - 1)
2335+>>> printSQL percentRank
2336+percent_rank()
2337+-}
2338+percentRank :: WindowFunction outer grp commons schemas params from ('NotNull 'PGfloat8)
2339+percentRank = UnsafeWindowFunction "percent_rank()"
2340+
2341+{- | cumulative distribution: (number of partition rows
2342+preceding or peer with current row) / total partition rows
2343+>>> printSQL cumeDist
2344+cume_dist()
2345+-}
2346+cumeDist :: WindowFunction outer grp commons schemas params from ('NotNull 'PGfloat8)
2347+cumeDist = UnsafeWindowFunction "cume_dist()"
2348+
2349+{- | integer ranging from 1 to the argument value,
2350+dividing the partition as equally as possible
2351+-}
2352+ntile
2353+ :: Expression outer grp commons schemas params from ('NotNull 'PGint4)
2354+ -- ^ num buckets
2355+ -> WindowFunction outer grp commons schemas params from ('NotNull 'PGint4)
2356+ntile = unsafeWindowFunction1 "ntile"
2357+
2358+{- | returns value evaluated at the row that is offset rows before the current
2359+row within the partition; if there is no such row, instead return default
2360+(which must be of the same type as value). Both offset and default are
2361+evaluated with respect to the current row.
2362+-}
2363+lag
2364+ :: Expression outer grp commons schemas params from ty
2365+ -- ^ value
2366+ -> Expression outer grp commons schemas params from ('NotNull 'PGint4)
2367+ -- ^ offset
2368+ -> Expression outer grp commons schemas params from ty
2369+ -- ^ default
2370+ -> WindowFunction outer grp commons schemas params from ty
2371+lag value offset def = UnsafeWindowFunction $ "lag"
2372+ <> parenthesized
2373+ (commaSeparated ([renderSQL value, renderSQL offset, renderSQL def]))
2374+
2375+{- | returns value evaluated at the row that is offset rows after the current
2376+row within the partition; if there is no such row, instead return default
2377+(which must be of the same type as value). Both offset and default are
2378+evaluated with respect to the current row.
2379+-}
2380+lead
2381+ :: Expression outer grp commons schemas params from ty
2382+ -- ^ value
2383+ -> Expression outer grp commons schemas params from ('NotNull 'PGint4)
2384+ -- ^ offset
2385+ -> Expression outer grp commons schemas params from ty
2386+ -- ^ default
2387+ -> WindowFunction outer grp commons schemas params from ty
2388+lead value offset def = UnsafeWindowFunction $ "lag"
2389+ <> parenthesized
2390+ (commaSeparated ([renderSQL value, renderSQL offset, renderSQL def]))
2391+
2392+{- | returns value evaluated at the row that is the
2393+first row of the window frame
2394+-}
2395+firstValue
2396+ :: Expression outer grp commons schemas params from ty
2397+ -- ^ value
2398+ -> WindowFunction outer grp commons schemas params from ty
2399+firstValue = unsafeWindowFunction1 "first_value"
2400+
2401+{- | returns value evaluated at the row that is the
2402+last row of the window frame
2403+-}
2404+lastValue
2405+ :: Expression outer grp commons schemas params from ty
2406+ -- ^ value
2407+ -> WindowFunction outer grp commons schemas params from ty
2408+lastValue = unsafeWindowFunction1 "last_value"
2409+
2410+{- | returns value evaluated at the row that is the nth
2411+row of the window frame (counting from 1); null if no such row
2412+-}
2413+nthValue
2414+ :: Expression outer grp commons schemas params from (null ty)
2415+ -- ^ value
2416+ -> Expression outer grp commons schemas params from ('NotNull 'PGint4)
2417+ -- ^ nth
2418+ -> WindowFunction outer grp commons schemas params from ('Null ty)
2419+nthValue value nth = UnsafeWindowFunction $ "nth_value"
2420+ <> parenthesized (commaSeparated [renderSQL value, renderSQL nth])
2421+
2422+{-|
2423+Create date from year, month and day fields
2424+
2425+>>> printSQL (makeDate (1984 :* 7 *: 3))
2426+make_date(1984, 7, 3)
2427+-}
2428+makeDate :: FunctionHet
2429+ '[ null 'PGint4, null 'PGint4, null 'PGint4 ]
2430+ ( null 'PGdate )
2431+makeDate = unsafeFunctionHet "make_date"
2432+
2433+{-|
2434+Create time from hour, minute and seconds fields
2435+
2436+>>> printSQL (makeTime (8 :* 15 *: 23.5))
2437+make_time(8, 15, 23.5)
2438+-}
2439+makeTime :: FunctionHet
2440+ '[ null 'PGint4, null 'PGint4, null 'PGfloat8 ]
2441+ ( null 'PGtime )
2442+makeTime = unsafeFunctionHet "make_time"
2443+
2444+{-|
2445+Create timestamp from year, month, day, hour, minute and seconds fields
2446+
2447+>>> printSQL (makeTimestamp (2013 :* 7 :* 15 :* 8 :* 15 *: 23.5))
2448+make_timestamp(2013, 7, 15, 8, 15, 23.5)
2449+-}
2450+makeTimestamp :: FunctionHet
2451+ '[ null 'PGint4, null 'PGint4, null 'PGint4
2452+ , null 'PGint4, null 'PGint4, null 'PGfloat8 ]
2453+ ( null 'PGtimestamp )
2454+makeTimestamp = unsafeFunctionHet "make_timestamp"
2455+
2456+{-|
2457+Create timestamp with time zone from
2458+year, month, day, hour, minute and seconds fields;
2459+the current time zone is used
2460+
2461+>>> printSQL (makeTimestamptz (2013 :* 7 :* 15 :* 8 :* 15 *: 23.5))
2462+make_timestamptz(2013, 7, 15, 8, 15, 23.5)
2463+-}
2464+makeTimestamptz :: FunctionHet
2465+ '[ null 'PGint4, null 'PGint4, null 'PGint4
2466+ , null 'PGint4, null 'PGint4, null 'PGfloat8 ]
2467+ ( null 'PGtimestamptz )
2468+makeTimestamptz = unsafeFunctionHet "make_timestamptz"
2469+
2470+{-|
2471+Affine space operations on time types.
2472+-}
2473+class TimeOp time diff | time -> diff where
2474+ {-|
2475+ >>> printSQL (makeDate (1984 :* 7 *: 3) !+ 365)
2476+ (make_date(1984, 7, 3) + 365)
2477+ -}
2478+ (!+) :: Operator (null time) (null diff) (null time)
2479+ (!+) = unsafeBinaryOp "+"
2480+ {-|
2481+ >>> printSQL (365 +! makeDate (1984 :* 7 *: 3))
2482+ (365 + make_date(1984, 7, 3))
2483+ -}
2484+ (+!) :: Operator (null diff) (null time) (null time)
2485+ (+!) = unsafeBinaryOp "+"
2486+ {-|
2487+ >>> printSQL (makeDate (1984 :* 7 *: 3) !- 365)
2488+ (make_date(1984, 7, 3) - 365)
2489+ -}
2490+ (!-) :: Operator (null time) (null diff) (null time)
2491+ (!-) = unsafeBinaryOp "-"
2492+ {-|
2493+ >>> printSQL (makeDate (1984 :* 7 *: 3) !-! currentDate)
2494+ (make_date(1984, 7, 3) - CURRENT_DATE)
2495+ -}
2496+ (!-!) :: Operator (null time) (null time) (null diff)
2497+ (!-!) = unsafeBinaryOp "-"
2498+instance TimeOp 'PGtimestamp 'PGinterval
2499+instance TimeOp 'PGtimestamptz 'PGinterval
2500+instance TimeOp 'PGtime 'PGinterval
2501+instance TimeOp 'PGtimetz 'PGinterval
2502+instance TimeOp 'PGinterval 'PGinterval
2503+instance TimeOp 'PGdate 'PGint4
2504+infixl 6 !+
2505+infixl 6 +!
2506+infixl 6 !-
2507+infixl 6 !-!