@@ -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,82 +35,265 @@ 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
4739 , unsafeBinaryOp
48− , binaryOp
49− , FunctionVar
50− , unsafeFunctionVar
51− , type (--->)
52− , FunctionNDB
53− , unsafeFunctionN
54− , functionN
40+ , (:-->)
41+ , unsafeUnaryOpL
42+ , unsafeUnaryOpR
43+ , unsafeFunction
44+ , FunctionHom
45+ , unsafeFunctionHom
46+ , FunctionHet
47+ , unsafeFunctionHet
48+ , HasParameter (parameter)
49+ , param
50+ , Literal (..)
51+ -- * Null
52+ , null_
53+ , notNull
54+ , coalesce
55+ , fromNull
56+ , isNull
57+ , isNotNull
58+ , matchNull
59+ , nullIf
60+ -- * Collections
61+ , array
62+ , array1
63+ , array2
64+ , cardinality
65+ , index
66+ , row
67+ , field
5568 , PGSubset (..)
56− , PGIntersect (..)
69+ -- * Functions
70+ , atan2_
71+ , cast
72+ , quot_
73+ , rem_
74+ , trunc
75+ , round_
76+ , ceiling_
77+ , greatest
78+ , least
79+ -- * Conditions
80+ , true
81+ , false
82+ , not_
83+ , (.&&)
84+ , (.||)
85+ , Condition
86+ , caseWhenThenElse
87+ , ifThenElse
88+ , (.==)
89+ , (./=)
90+ , (.>=)
91+ , (.<)
92+ , (.<=)
93+ , (.>)
94+ , between
95+ , notBetween
96+ , betweenSymmetric
97+ , notBetweenSymmetric
98+ , isDistinctFrom
99+ , isNotDistinctFrom
100+ , isTrue
101+ , isNotTrue
102+ , isFalse
103+ , isNotFalse
104+ , isUnknown
105+ , isNotUnknown
106+ -- * Time
107+ , TimeOp (..)
108+ , currentDate
109+ , currentTime
110+ , currentTimestamp
111+ , localTime
112+ , localTimestamp
113+ , now
114+ , makeDate
115+ , makeTime
116+ , makeTimestamp
117+ , makeTimestamptz
118+ , interval_
119+ , TimeUnit (..)
120+ -- * Text
121+ , lower
122+ , upper
123+ , charLength
124+ , like
125+ , ilike
126+ -- * Json
127+ -- ** Json and Jsonb operators
128+ , (.->)
129+ , (.->>)
130+ , (.#>)
131+ , (.#>>)
132+ -- ** Jsonb operators
133+ , (.?)
134+ , (.?|)
135+ , (.?&)
136+ , (.-.)
137+ , (#-.)
138+ -- ** Functions
139+ , toJson
140+ , toJsonb
141+ , arrayToJson
142+ , rowToJson
143+ , jsonBuildArray
144+ , jsonbBuildArray
145+ , PGBuildObject
146+ , jsonBuildObject
147+ , jsonbBuildObject
148+ , jsonObject
149+ , jsonbObject
150+ , jsonZipObject
151+ , jsonbZipObject
152+ , jsonArrayLength
153+ , jsonbArrayLength
154+ , jsonExtractPath
155+ , jsonbExtractPath
156+ , jsonExtractPathAsText
157+ , jsonbExtractPathAsText
158+ , jsonTypeof
159+ , jsonbTypeof
160+ , jsonStripNulls
161+ , jsonbStripNulls
162+ , jsonbSet
163+ , jsonbInsert
164+ , jsonbPretty
165+ -- * Text Search
166+ , (@@)
167+ , (.&)
168+ , (.|)
169+ , (.!)
170+ , (<->)
171+ , arrayToTSvector
172+ , tsvectorLength
173+ , numnode
174+ , plainToTSquery
175+ , phraseToTSquery
176+ , websearchToTSquery
177+ , queryTree
178+ , toTSquery
179+ , toTSvector
180+ , setWeight
181+ , strip
182+ , jsonToTSvector
183+ , jsonbToTSvector
184+ , tsDelete
185+ , tsFilter
186+ , tsHeadline
187+ -- * Aggregation
188+ , Aggregate (..)
189+ , Distinction (..)
190+ , PGAvg
191+ -- * Window Functions
192+ , WindowDefinition (..)
193+ , partitionBy
194+ , WindowFunction (..)
195+ , rank
196+ , rowNumber
197+ , denseRank
198+ , percentRank
199+ , cumeDist
200+ , ntile
201+ , lag
202+ , lead
203+ , firstValue
204+ , lastValue
205+ , nthValue
206+ -- * Sorting
207+ , SortExpression (..)
208+ , OrderBy (..)
209+ -- * Types
210+ , TypeExpression (..)
211+ , PGTyped (..)
212+ , typedef
213+ , typetable
214+ , typeview
215+ , bool
216+ , int2
217+ , smallint
218+ , int4
219+ , int
220+ , integer
221+ , int8
222+ , bigint
223+ , numeric
224+ , float4
225+ , real
226+ , float8
227+ , doublePrecision
228+ , money
229+ , text
230+ , char
231+ , character
232+ , varchar
233+ , characterVarying
234+ , bytea
235+ , timestamp
236+ , timestampWithTimeZone
237+ , date
238+ , time
239+ , timeWithTimeZone
240+ , interval
241+ , uuid
242+ , inet
243+ , json
244+ , jsonb
245+ , vararray
246+ , fixarray
247+ , tsvector
248+ , tsquery
57249 -- * Re-export
58250 , (&)
251+ , NP (..)
59252 , K (..)
60− , unK
61253 ) where
62254
63255import Control.Category
64256import Control.DeepSeq
65257import Data.ByteString (ByteString)
258+import Data.ByteString.Lazy (toStrict)
66259import Data.Function ((&))
260+import Data.Kind
67261import Data.Semigroup hiding (All)
262+import qualified Data.Aeson as JSON
263+import Data.Int
68264import Data.String
265+import Data.Text (Text)
266+import Data.Word
69267import Generics.SOP hiding (All, from)
70268import GHC.OverloadedLabels
71269import GHC.TypeLits
72270import Numeric
73271import Prelude hiding (id, (.))
74272
273+import qualified Data.ByteString as ByteString
274+import qualified Data.Text as Text
275+import qualified Data.Text.Lazy as Lazy (Text)
276+import qualified Data.Text.Lazy as Lazy.Text
75277import qualified GHC.Generics as GHC
278+import qualified Generics.SOP as SOP
76279
77−import Squeal.PostgreSQL.Alias
78−import Squeal.PostgreSQL.List
280+import Squeal.PostgreSQL.Binary
79281import Squeal.PostgreSQL.Render
80282import Squeal.PostgreSQL.Schema
81283
82−-- $setup
83−-- >>> import Squeal.PostgreSQL
84−
85284{-----------------------------------------
86285column expressions
87286-----------------------------------------}
88287
89288{- | `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,
289+such as in the target list of the `Squeal.PostgreSQL.Query.select` command,
92290as new column values in `Squeal.PostgreSQL.Manipulation.insertRow` or
93291`Squeal.PostgreSQL.Manipulation.update`,
94−or in search `Squeal.PostgreSQL.Logic.Condition`s in a number of commands.
292+or in search `Condition`s in a number of commands.
95293
96294The expression syntax allows the calculation of
97295values from primitive expression using arithmetic, logical,
98296and 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`.
113297-}
114298newtype Expression
115299 (outer :: FromType)
@@ -125,7 +309,7 @@ instance RenderSQL (Expression outer commons grp schemas params from ty) where
125309 renderSQL = renderExpression
126310
127311-- | An `Expr` is a closed `Expression`.
128−-- It is a F@RankNType@ but don't be scared.
312+-- It is a F`RankNType` but don't be scared.
129313-- Think of it as an expression which sees no
130314-- namespaces, so you can't use parameters
131315-- or alias references. It can be used as
@@ -135,7 +319,7 @@ type Expr x
135319 . Expression outer commons grp schemas params from x
136320 -- ^ cannot reference aliases
137321
138−-- | A @RankNType@ for binary operators.
322+-- | A `RankNType` for binary operators.
139323type Operator x1 x2 y
140324 = forall outer commons grp schemas params from
141325 . Expression outer commons grp schemas params from x1
@@ -145,58 +329,35 @@ type Operator x1 x2 y
145329 -> Expression outer commons grp schemas params from y
146330 -- ^ output
147331
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.
332+-- | A `RankNType` for functions with a single argument.
158333-- These could be either function calls or unary operators.
159334-- This is a subtype of the usual Haskell function type `Prelude.->`,
160335-- indeed a subcategory as it is closed under the usual
161336-- `Prelude..` and `Prelude.id`.
162−type (-->) x y
337+type (:-->) x y
163338 = forall outer commons grp schemas params from
164339 . Expression outer commons grp schemas params from x
165340 -- ^ input
166341 -> Expression outer commons grp schemas params from y
167342 -- ^ output
168343
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.
344+{- | A `RankNType` for functions with a fixed-length list of heterogeneous arguments.
177345Use the `*:` operator to end your argument lists, like so.
178346
179−>>> printSQL (unsafeFunctionN "fun" (true :* false :* localTime *: true))
347+>>> printSQL (unsafeFunctionHet "fun" (true :* false :* localTime *: true))
180348fun(TRUE, FALSE, LOCALTIME, TRUE)
181349-}
182−type (--->) xs y
350+type FunctionHet xs y
183351 = forall outer commons grp schemas params from
184352 . NP (Expression outer commons grp schemas params from) xs
185353 -- ^ inputs
186354 -> Expression outer commons grp schemas params from y
187355 -- ^ output
188356
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
357+{- | A `RankNType` for functions with a variable-length list of
197358homogeneous arguments and at least 1 more argument.
198359-}
199−type FunctionVar x0 x1 y
360+type FunctionHom x0 x1 y
200361 = forall outer commons grp schemas params from
201362 . [Expression outer commons grp schemas params from x0]
202363 -- ^ inputs
@@ -205,13 +366,53 @@ type FunctionVar x0 x1 y
205366 -> Expression outer commons grp schemas params from y
206367 -- ^ output
207368
208−{- | >>> printSQL (unsafeFunctionVar "greatest" [true, null_] false)
369+{- | >>> printSQL (unsafeFunctionHom "greatest" [true, null_] false)
209370greatest(TRUE, NULL, FALSE)
210371-}
211−unsafeFunctionVar :: ByteString -> FunctionVar x0 x1 y
212−unsafeFunctionVar fun xs x = UnsafeExpression $ fun <> parenthesized
372+unsafeFunctionHom :: ByteString -> FunctionHom x0 x1 y
373+unsafeFunctionHom fun xs x = UnsafeExpression $ fun <> parenthesized
213374 (commaSeparated (renderSQL <$> xs) <> ", " <> renderSQL x)
214375
376+{- | A `HasParameter` constraint is used to indicate a value that is
377+supplied externally to a SQL statement.
378+`Squeal.PostgreSQL.PQ.manipulateParams`,
379+`Squeal.PostgreSQL.PQ.queryParams` and
380+`Squeal.PostgreSQL.PQ.traversePrepared` support specifying data values
381+separately from the SQL command string, in which case `param`s are used to
382+refer to the out-of-line data values.
383+-}
384+class KnownNat n => HasParameter
385+ (n :: Nat)
386+ (params :: [NullityType])
387+ (ty :: NullityType)
388+ | n params -> ty where
389+ -- | `parameter` takes a `Nat` using type application and a `TypeExpression`.
390+ --
391+ -- >>> let expr = parameter @1 int4 :: Expression outer '[] grp schemas '[ 'Null 'PGint4] from ('Null 'PGint4)
392+ -- >>> printSQL expr
393+ -- ($1 :: int4)
394+ parameter
395+ :: TypeExpression schemas ty
396+ -> Expression outer commons grp schemas params from ty
397+ parameter ty = UnsafeExpression $ parenthesized $
398+ "$" <> renderNat @n <+> "::"
399+ <+> renderSQL ty
400+instance {-# OVERLAPPING #-} HasParameter 1 (ty1:tys) ty1
401+instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) params ty)
402+ => HasParameter n (ty' : params) ty
403+
404+-- | `param` takes a `Nat` using type application and for basic types,
405+-- infers a `TypeExpression`.
406+--
407+-- >>> let expr = param @1 :: Expression outer commons grp schemas '[ 'Null 'PGint4] from ('Null 'PGint4)
408+-- >>> printSQL expr
409+-- ($1 :: int4)
410+param
411+ :: forall n outer commons schemas params from grp ty
412+ . (PGTyped schemas ty, HasParameter n params ty)
413+ => Expression outer commons grp schemas params from ty -- ^ param
414+param = parameter @n (pgtype @schemas)
415+
215416instance (HasUnique tab (Join outer from) row, Has col row ty)
216417 => IsLabel col (Expression outer commons 'Ungrouped schemas params from ty) where
217418 fromLabel = UnsafeExpression $ renderSQL (Alias @col)
@@ -308,73 +509,245 @@ instance
308509 (NP (Aliased (Expression outer commons ('Grouped bys) schemas params from)) columns) where
309510 tab ! col = tab ! col :* Nil
310511
512+-- | analagous to `Nothing`
513+--
514+-- >>> printSQL null_
515+-- NULL
516+null_ :: Expr ('Null ty)
517+null_ = UnsafeExpression "NULL"
518+
519+-- | analagous to `Just`
520+--
521+-- >>> printSQL $ notNull true
522+-- TRUE
523+notNull :: 'NotNull ty :--> 'Null ty
524+notNull = UnsafeExpression . renderSQL
525+
526+-- | return the leftmost value which is not NULL
527+--
528+-- >>> printSQL $ coalesce [null_, true] false
529+-- COALESCE(NULL, TRUE, FALSE)
530+coalesce :: FunctionHom ('Null ty) ('NotNull ty) ('NotNull ty)
531+coalesce nullxs notNullx = UnsafeExpression $
532+ "COALESCE" <> parenthesized (commaSeparated
533+ ((renderSQL <$> nullxs) <> [renderSQL notNullx]))
534+
535+-- | analagous to `Data.Maybe.fromMaybe` using @COALESCE@
536+--
537+-- >>> printSQL $ fromNull true null_
538+-- COALESCE(NULL, TRUE)
539+fromNull
540+ :: Expression outer commons grp schemas params from ('NotNull ty)
541+ -- ^ what to convert @NULL@ to
542+ -> Expression outer commons grp schemas params from ('Null ty)
543+ -> Expression outer commons grp schemas params from ('NotNull ty)
544+fromNull notNullx nullx = coalesce [nullx] notNullx
545+
546+-- | >>> printSQL $ null_ & isNull
547+-- NULL IS NULL
548+isNull :: 'Null ty :--> null 'PGbool
549+isNull x = UnsafeExpression $ renderSQL x <+> "IS NULL"
550+
551+-- | >>> printSQL $ null_ & isNotNull
552+-- NULL IS NOT NULL
553+isNotNull :: 'Null ty :--> null 'PGbool
554+isNotNull x = UnsafeExpression $ renderSQL x <+> "IS NOT NULL"
555+
556+-- | analagous to `maybe` using @IS NULL@
557+--
558+-- >>> printSQL $ matchNull true not_ null_
559+-- CASE WHEN NULL IS NULL THEN TRUE ELSE (NOT NULL) END
560+matchNull
561+ :: Expression outer commons grp schemas params from (nullty)
562+ -- ^ what to convert @NULL@ to
563+ -> ( Expression outer commons grp schemas params from ('NotNull ty)
564+ -> Expression outer commons grp schemas params from (nullty) )
565+ -- ^ function to perform when @NULL@ is absent
566+ -> Expression outer commons grp schemas params from ('Null ty)
567+ -> Expression outer commons grp schemas params from (nullty)
568+matchNull y f x = ifThenElse (isNull x) y
569+ (f (UnsafeExpression (renderSQL x)))
570+
571+{-| right inverse to `fromNull`, if its arguments are equal then
572+`nullIf` gives @NULL@.
573+
574+>>> :set -XTypeApplications -XDataKinds
575+>>> let expr = nullIf (false *: param @1) :: Expression outer commons grp schemas '[ 'NotNull 'PGbool] from ('Null 'PGbool)
576+>>> printSQL expr
577+NULLIF(FALSE, ($1 :: bool))
578+-}
579+nullIf :: FunctionHet '[ 'NotNull ty, 'NotNull ty] ('Null ty)
580+nullIf = unsafeFunctionHet "NULLIF"
581+
582+-- | >>> printSQL $ array [null_, false, true]
583+-- ARRAY[NULL, FALSE, TRUE]
584+array
585+ :: [Expression outer commons grp schemas params from ty]
586+ -- ^ array elements
587+ -> Expression outer commons grp schemas params from (null ('PGvararray ty))
588+array xs = UnsafeExpression $ "ARRAY" <>
589+ bracketed (commaSeparated (renderSQL <$> xs))
590+
591+{- | construct a 1-dimensional fixed length array
592+
593+>>> printSQL $ array1 (null_ :* false *: true)
594+ARRAY[NULL, FALSE, TRUE]
595+
596+>>> :type array1 (null_ :* false *: true)
597+array1 (null_ :* false *: true)
598+ :: Expression
599+ outer
600+ commons
601+ grp
602+ schemas
603+ params
604+ from
605+ (null ('PGfixarray '[3] ('Null 'PGbool)))
606+-}
607+array1
608+ :: (n ~ Length tys, SOP.All ((~) ty) tys)
609+ => NP (Expression outer commons grp schemas params from) tys
610+ -> Expression outer commons grp schemas params from (null ('PGfixarray '[n] ty))
611+array1 xs = UnsafeExpression $ "ARRAY" <>
612+ bracketed (renderCommaSeparated renderSQL xs)
613+
614+{- | construct a 2-dimensional fixed length array
615+
616+>>> printSQL $ array2 ((null_ :* false *: true) *: (false :* null_ *: true))
617+ARRAY[[NULL, FALSE, TRUE], [FALSE, NULL, TRUE]]
618+
619+>>> :type array2 ((null_ :* false *: true) *: (false :* null_ *: true))
620+array2 ((null_ :* false *: true) *: (false :* null_ *: true))
621+ :: Expression
622+ outer
623+ commons
624+ grp
625+ schemas
626+ params
627+ from
628+ (null ('PGfixarray '[2, 3] ('Null 'PGbool)))
629+-}
630+array2
631+ :: ( SOP.All ((~) tys) tyss
632+ , SOP.All SOP.SListI tyss
633+ , Length tyss ~ n1
634+ , SOP.All ((~) ty) tys
635+ , Length tys ~ n2 )
636+ => NP (NP (Expression outer commons grp schemas params from)) tyss
637+ -> Expression outer commons grp schemas params from (null ('PGfixarray '[n1,n2] ty))
638+array2 xss = UnsafeExpression $ "ARRAY" <>
639+ bracketed (renderCommaSeparatedConstraint @SOP.SListI (bracketed . renderCommaSeparated renderSQL) xss)
640+
641+-- | >>> printSQL $ cardinality (array [null_, false, true])
642+-- cardinality(ARRAY[NULL, FALSE, TRUE])
643+cardinality :: null ('PGvararray ty) :--> null 'PGint8
644+cardinality = unsafeFunction "cardinality"
645+
646+-- | >>> printSQL $ array [null_, false, true] & index 2
647+-- (ARRAY[NULL, FALSE, TRUE])[2]
648+index
649+ :: Word64 -- ^ index
650+ -> null ('PGvararray ty) :--> NullifyType ty
651+index n expr = UnsafeExpression $
652+ parenthesized (renderSQL expr) <> "[" <> fromString (show n) <> "]"
653+
311654instance (KnownSymbol label, label `In` labels) => IsPGlabel label
312655 (Expression outer commons grp schemas params from (null ('PGenum labels))) where
313656 label = UnsafeExpression $ renderSQL (PGlabel @label)
314657
658+-- | A row constructor is an expression that builds a row value
659+-- (also called a composite value) using values for its member fields.
660+--
661+-- >>> :{
662+-- type Complex = 'PGcomposite
663+-- '[ "real" ::: 'NotNull 'PGfloat8
664+-- , "imaginary" ::: 'NotNull 'PGfloat8 ]
665+-- :}
666+--
667+-- >>> let i = row (0 `as` #real :* 1 `as` #imaginary) :: Expression outer commons grp schemas params from ('NotNull Complex)
668+-- >>> printSQL i
669+-- ROW(0, 1)
670+row
671+ :: SListI row
672+ => NP (Aliased (Expression outer commons grp schemas params from)) row
673+ -- ^ zero or more expressions for the row field values
674+ -> Expression outer commons grp schemas params from (null ('PGcomposite row))
675+row exprs = UnsafeExpression $ "ROW" <> parenthesized
676+ (renderCommaSeparated (\ (expr `As` _) -> renderSQL expr) exprs)
677+
678+-- | >>> :{
679+-- type Complex = 'PGcomposite
680+-- '[ "real" ::: 'NotNull 'PGfloat8
681+-- , "imaginary" ::: 'NotNull 'PGfloat8 ]
682+-- type Schema = '["complex" ::: 'Typedef Complex]
683+-- :}
684+--
685+-- >>> let i = row (0 `as` #real :* 1 `as` #imaginary) :: Expression outer '[] grp (Public Schema) from params ('NotNull Complex)
686+-- >>> printSQL $ i & field #complex #imaginary
687+-- (ROW(0, 1)::"complex")."imaginary"
688+field
689+ :: ( Has sch schemas schema
690+ , Has tydef schema ('Typedef ('PGcomposite row))
691+ , Has field row ty)
692+ => QualifiedAlias sch tydef -- ^ row type
693+ -> Alias field -- ^ field name
694+ -> Expression outer commons grp schemas params from ('NotNull ('PGcomposite row))
695+ -> Expression outer commons grp schemas params from ty
696+field td fld expr = UnsafeExpression $
697+ parenthesized (renderSQL expr <> "::" <> renderSQL td)
698+ <> "." <> renderSQL fld
699+
700+instance Semigroup
701+ (Expression outer commons grp schemas params from (null ('PGvararray ty))) where
702+ (<>) = unsafeBinaryOp "||"
703+
704+instance Monoid
705+ (Expression outer commons grp schemas params from (null ('PGvararray ty))) where
706+ mempty = array []
707+ mappend = (<>)
708+
709+-- | >>> let expr = greatest [param @1] currentTimestamp :: Expression outer commons grp schemas '[ 'NotNull 'PGtimestamptz] from ('NotNull 'PGtimestamptz)
710+-- >>> printSQL expr
711+-- GREATEST(($1 :: timestamp with time zone), CURRENT_TIMESTAMP)
712+greatest :: FunctionHom ty ty ty
713+greatest = unsafeFunctionHom "GREATEST"
714+
715+-- | >>> printSQL $ least [null_] currentTimestamp
716+-- LEAST(NULL, CURRENT_TIMESTAMP)
717+least :: FunctionHom ty ty ty
718+least = unsafeFunctionHom "LEAST"
719+
315720-- | >>> printSQL $ unsafeBinaryOp "OR" true false
316721-- (TRUE OR FALSE)
317−unsafeBinaryOp :: ByteString -> Operator ty0 ty1 ty2
722+unsafeBinaryOp :: ByteString {- ^ operator -} -> Operator ty0 ty1 ty2
318723unsafeBinaryOp op x y = UnsafeExpression $ parenthesized $
319724 renderSQL x <+> op <+> renderSQL y
320725
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
726+-- | >>> printSQL $ unsafeUnaryOpL "NOT" true
329727-- (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
728+unsafeUnaryOpL :: ByteString {- ^ operator -} -> x :--> y
729+unsafeUnaryOpL op x = UnsafeExpression $ parenthesized $ op <+> renderSQL x
339730
340−-- | >>> printSQL $ true & unsafeRightOp "IS NOT TRUE"
731+-- | >>> printSQL $ true & unsafeUnaryOpR "IS NOT TRUE"
341732-- (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
733+unsafeUnaryOpR :: ByteString {- ^ operator -} -> x :--> y
734+unsafeUnaryOpR op x = UnsafeExpression $ parenthesized $ renderSQL x <+> op
351735
352736-- | >>> printSQL $ unsafeFunction "f" true
353737-- f(TRUE)
354−unsafeFunction :: ByteString -> x --> y
738+unsafeFunction :: ByteString {- ^ function -} -> x :--> y
355739unsafeFunction fun x = UnsafeExpression $
356740 fun <> parenthesized (renderSQL x)
357741
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 $
742+-- | >>> printSQL $ unsafeFunctionHet "f" (currentTime :* localTimestamp :* false *: literal 'a')
743+-- f(CURRENT_TIME, LOCALTIMESTAMP, FALSE, (E'a' :: char(1)))
744+unsafeFunctionHet
745+ :: SListI xs
746+ => ByteString {- ^ function -}
747+ -> FunctionHet xs y
748+unsafeFunctionHet fun xs = UnsafeExpression $
368749 fun <> parenthesized (renderCommaSeparated renderSQL xs)
369750
370−functionN
371− :: ( Has sch schemas schema
372− , Has fun schema ('Function (xs :=> 'Returns y))
373− , SListI xs )
374− => QualifiedAlias sch fun
375− -> FunctionNDB schemas xs y
376−functionN = unsafeFunctionN . renderSQL
377−
378751instance ty `In` PGNum
379752 => Num (Expression outer commons grp schemas params from (null ty)) where
380753 (+) = unsafeBinaryOp "+"
@@ -419,22 +792,364 @@ instance (ty `In` PGNum, ty `In` PGFloating) => Floating
419792 acosh x = log (x + sqrt (x*x - 1))
420793 atanh x = log ((1 + x) / (1 - x)) / 2
421794
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)
795+-- | >>> :{
796+-- let
797+-- expression :: Expr (null 'PGfloat4)
798+-- expression = atan2_ (pi *: 2)
799+-- in printSQL expression
800+-- :}
801+-- atan2(pi(), 2)
802+atan2_
803+ :: float `In` PGFloating
804+ => FunctionHet '[ null float, null float] (null float)
805+atan2_ = unsafeFunctionHet "atan2"
806+
807+-- When a `cast` is applied to an `Expression` of a known type, it
808+-- represents a run-time type conversion. The cast will succeed only if a
809+-- suitable type conversion operation has been defined.
810+--
811+-- | >>> printSQL $ true & cast int4
812+-- (TRUE :: int4)
813+cast
814+ :: TypeExpression schemas ty1
815+ -- ^ type to cast as
816+ -> Expression outer commons grp schemas params from ty0
817+ -- ^ value to convert
818+ -> Expression outer commons grp schemas params from ty1
819+cast ty x = UnsafeExpression $ parenthesized $
820+ renderSQL x <+> "::" <+> renderSQL ty
821+
822+-- | integer division, truncates the result
823+--
824+-- >>> :{
825+-- let
826+-- expression :: Expression outer commons grp schemas params from (null 'PGint2)
827+-- expression = 5 `quot_` 2
828+-- in printSQL expression
829+-- :}
830+-- (5 / 2)
831+quot_
832+ :: int `In` PGIntegral
833+ => Operator (null int) (null int) (null int)
834+quot_ = unsafeBinaryOp "/"
835+
836+-- | remainder upon integer division
837+--
838+-- >>> :{
839+-- let
840+-- expression :: Expression outer commons grp schemas params from (null 'PGint2)
841+-- expression = 5 `rem_` 2
842+-- in printSQL expression
843+-- :}
844+-- (5 % 2)
845+rem_
846+ :: int `In` PGIntegral
847+ => Operator (null int) (null int) (null int)
848+rem_ = unsafeBinaryOp "%"
849+
850+-- | >>> :{
851+-- let
852+-- expression :: Expression outer commons grp schemas params from (null 'PGfloat4)
853+-- expression = trunc pi
854+-- in printSQL expression
855+-- :}
856+-- trunc(pi())
857+trunc :: frac `In` PGFloating => null frac :--> null frac
858+trunc = unsafeFunction "trunc"
859+
860+-- | >>> :{
861+-- let
862+-- expression :: Expression outer commons grp schemas params from (null 'PGfloat4)
863+-- expression = round_ pi
864+-- in printSQL expression
865+-- :}
866+-- round(pi())
867+round_ :: frac `In` PGFloating => null frac :--> null frac
868+round_ = unsafeFunction "round"
869+
870+-- | >>> :{
871+-- let
872+-- expression :: Expression outer commons grp schemas params from (null 'PGfloat4)
873+-- expression = ceiling_ pi
874+-- in printSQL expression
875+-- :}
876+-- ceiling(pi())
877+ceiling_ :: frac `In` PGFloating => null frac :--> null frac
878+ceiling_ = unsafeFunction "ceiling"
879+
880+-- | A `Condition` is an `Expression`, which can evaluate
881+-- to `true`, `false` or `null_`. This is because SQL uses
882+-- a three valued logic.
883+type Condition outer commons grp schemas params from =
884+ Expression outer commons grp schemas params from ('Null 'PGbool)
885+
886+-- | >>> printSQL true
887+-- TRUE
888+true :: Expr (null 'PGbool)
889+true = UnsafeExpression "TRUE"
890+
891+-- | >>> printSQL false
892+-- FALSE
893+false :: Expr (null 'PGbool)
894+false = UnsafeExpression "FALSE"
895+
896+-- | >>> printSQL $ not_ true
897+-- (NOT TRUE)
898+not_ :: null 'PGbool :--> null 'PGbool
899+not_ = unsafeUnaryOpL "NOT"
900+
901+-- | >>> printSQL $ true .&& false
902+-- (TRUE AND FALSE)
903+(.&&) :: Operator (null 'PGbool) (null 'PGbool) (null 'PGbool)
904+infixr 3 .&&
905+(.&&) = unsafeBinaryOp "AND"
906+
907+-- | >>> printSQL $ true .|| false
908+-- (TRUE OR FALSE)
909+(.||) :: Operator (null 'PGbool) (null 'PGbool) (null 'PGbool)
910+infixr 2 .||
911+(.||) = unsafeBinaryOp "OR"
912+
913+-- | >>> :{
914+-- let
915+-- expression :: Expression outer commons grp schemas params from (null 'PGint2)
916+-- expression = caseWhenThenElse [(true, 1), (false, 2)] 3
917+-- in printSQL expression
918+-- :}
919+-- CASE WHEN TRUE THEN 1 WHEN FALSE THEN 2 ELSE 3 END
920+caseWhenThenElse
921+ :: [ ( Condition outer commons grp schemas params from
922+ , Expression outer commons grp schemas params from ty
923+ ) ]
924+ -- ^ whens and thens
925+ -> Expression outer commons grp schemas params from ty
926+ -- ^ else
927+ -> Expression outer commons grp schemas params from ty
928+caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
929+ [ "CASE"
930+ , mconcat
931+ [ mconcat
932+ [ " WHEN ", renderSQL when_
933+ , " THEN ", renderSQL then_
934+ ]
935+ | (when_,then_) <- whenThens
936+ ]
937+ , " ELSE ", renderSQL else_
938+ , " END"
939+ ]
940+
941+-- | >>> :{
942+-- let
943+-- expression :: Expression outer commons grp schemas params from (null 'PGint2)
944+-- expression = ifThenElse true 1 0
945+-- in printSQL expression
946+-- :}
947+-- CASE WHEN TRUE THEN 1 ELSE 0 END
948+ifThenElse
949+ :: Condition outer commons grp schemas params from
950+ -> Expression outer commons grp schemas params from ty -- ^ then
951+ -> Expression outer commons grp schemas params from ty -- ^ else
952+ -> Expression outer commons grp schemas params from ty
953+ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
954+
955+-- | Comparison operations like `.==`, `./=`, `.>`, `.>=`, `.<` and `.<=`
956+-- will produce @NULL@s if one of their arguments is @NULL@.
957+--
958+-- >>> printSQL $ true .== null_
959+-- (TRUE = NULL)
960+(.==) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
961+(.==) = unsafeBinaryOp "="
962+infix 4 .==
963+
964+-- | >>> printSQL $ true ./= null_
965+-- (TRUE <> NULL)
966+(./=) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
967+(./=) = unsafeBinaryOp "<>"
968+infix 4 ./=
969+
970+-- | >>> printSQL $ true .>= null_
971+-- (TRUE >= NULL)
972+(.>=) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
973+(.>=) = unsafeBinaryOp ">="
974+infix 4 .>=
432975
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)
976+-- | >>> printSQL $ true .< null_
977+-- (TRUE < NULL)
978+(.<) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
979+(.<) = unsafeBinaryOp "<"
980+infix 4 .<
981+
982+-- | >>> printSQL $ true .<= null_
983+-- (TRUE <= NULL)
984+(.<=) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
985+(.<=) = unsafeBinaryOp "<="
986+infix 4 .<=
987+
988+-- | >>> printSQL $ true .> null_
989+-- (TRUE > NULL)
990+(.>) :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
991+(.>) = unsafeBinaryOp ">"
992+infix 4 .>
993+
994+{- | >>> printSQL $ true `between` (null_, false)
995+TRUE BETWEEN NULL AND FALSE
996+-}
997+between
998+ :: Expression outer commons grp schemas params from (null ty)
999+ -> ( Expression outer commons grp schemas params from (null ty)
1000+ , Expression outer commons grp schemas params from (null ty) ) -- ^ bounds
1001+ -> Condition outer commons grp schemas params from
1002+between a (x,y) = UnsafeExpression $ renderSQL a <+> "BETWEEN"
1003+ <+> renderSQL x <+> "AND" <+> renderSQL y
1004+
1005+{- | >>> printSQL $ true `notBetween` (null_, false)
1006+TRUE NOT BETWEEN NULL AND FALSE
1007+-}
1008+notBetween
1009+ :: Expression outer commons grp schemas params from (null ty)
1010+ -> ( Expression outer commons grp schemas params from (null ty)
1011+ , Expression outer commons grp schemas params from (null ty) ) -- ^ bounds
1012+ -> Condition outer commons grp schemas params from
1013+notBetween a (x,y) = UnsafeExpression $ renderSQL a <+> "NOT BETWEEN"
1014+ <+> renderSQL x <+> "AND" <+> renderSQL y
1015+
1016+{- | between, after sorting the comparison values
1017+
1018+>>> printSQL $ true `betweenSymmetric` (null_, false)
1019+TRUE BETWEEN SYMMETRIC NULL AND FALSE
1020+-}
1021+betweenSymmetric
1022+ :: Expression outer commons grp schemas params from (null ty)
1023+ -> ( Expression outer commons grp schemas params from (null ty)
1024+ , Expression outer commons grp schemas params from (null ty) ) -- ^ bounds
1025+ -> Condition outer commons grp schemas params from
1026+betweenSymmetric a (x,y) = UnsafeExpression $ renderSQL a
1027+ <+> "BETWEEN SYMMETRIC" <+> renderSQL x <+> "AND" <+> renderSQL y
1028+
1029+{- | not between, after sorting the comparison values
1030+
1031+>>> printSQL $ true `notBetweenSymmetric` (null_, false)
1032+TRUE NOT BETWEEN SYMMETRIC NULL AND FALSE
1033+-}
1034+notBetweenSymmetric
1035+ :: Expression outer commons grp schemas params from (null ty)
1036+ -> ( Expression outer commons grp schemas params from (null ty)
1037+ , Expression outer commons grp schemas params from (null ty) ) -- ^ bounds
1038+ -> Condition outer commons grp schemas params from
1039+notBetweenSymmetric a (x,y) = UnsafeExpression $ renderSQL a
1040+ <+> "NOT BETWEEN SYMMETRIC" <+> renderSQL x <+> "AND" <+> renderSQL y
1041+
1042+{- | not equal, treating null like an ordinary value
1043+
1044+>>> printSQL $ true `isDistinctFrom` null_
1045+(TRUE IS DISTINCT FROM NULL)
1046+-}
1047+isDistinctFrom :: Operator (null0 ty) (null1 ty) ('Null 'PGbool)
1048+isDistinctFrom = unsafeBinaryOp "IS DISTINCT FROM"
1049+
1050+{- | equal, treating null like an ordinary value
1051+
1052+>>> printSQL $ true `isNotDistinctFrom` null_
1053+(TRUE IS NOT DISTINCT FROM NULL)
1054+-}
1055+isNotDistinctFrom :: Operator (null0 ty) (null1 ty) ('NotNull 'PGbool)
1056+isNotDistinctFrom = unsafeBinaryOp "IS NOT DISTINCT FROM"
1057+
1058+{- | is true
1059+
1060+>>> printSQL $ true & isTrue
1061+(TRUE IS TRUE)
1062+-}
1063+isTrue :: null0 'PGbool :--> null1 'PGbool
1064+isTrue = unsafeUnaryOpR "IS TRUE"
1065+
1066+{- | is false or unknown
1067+
1068+>>> printSQL $ true & isNotTrue
1069+(TRUE IS NOT TRUE)
1070+-}
1071+isNotTrue :: null0 'PGbool :--> null1 'PGbool
1072+isNotTrue = unsafeUnaryOpR "IS NOT TRUE"
1073+
1074+{- | is false
1075+
1076+>>> printSQL $ true & isFalse
1077+(TRUE IS FALSE)
1078+-}
1079+isFalse :: null0 'PGbool :--> null1 'PGbool
1080+isFalse = unsafeUnaryOpR "IS FALSE"
1081+
1082+{- | is true or unknown
1083+
1084+>>> printSQL $ true & isNotFalse
1085+(TRUE IS NOT FALSE)
1086+-}
1087+isNotFalse :: null0 'PGbool :--> null1 'PGbool
1088+isNotFalse = unsafeUnaryOpR "IS NOT FALSE"
1089+
1090+{- | is unknown
1091+
1092+>>> printSQL $ true & isUnknown
1093+(TRUE IS UNKNOWN)
1094+-}
1095+isUnknown :: null0 'PGbool :--> null1 'PGbool
1096+isUnknown = unsafeUnaryOpR "IS UNKNOWN"
1097+
1098+{- | is true or false
1099+
1100+>>> printSQL $ true & isNotUnknown
1101+(TRUE IS NOT UNKNOWN)
1102+-}
1103+isNotUnknown :: null0 'PGbool :--> null1 'PGbool
1104+isNotUnknown = unsafeUnaryOpR "IS NOT UNKNOWN"
1105+
1106+-- | >>> printSQL currentDate
1107+-- CURRENT_DATE
1108+currentDate :: Expr (null 'PGdate)
1109+currentDate = UnsafeExpression "CURRENT_DATE"
1110+
1111+-- | >>> printSQL currentTime
1112+-- CURRENT_TIME
1113+currentTime :: Expr (null 'PGtimetz)
1114+currentTime = UnsafeExpression "CURRENT_TIME"
1115+
1116+-- | >>> printSQL currentTimestamp
1117+-- CURRENT_TIMESTAMP
1118+currentTimestamp :: Expr (null 'PGtimestamptz)
1119+currentTimestamp = UnsafeExpression "CURRENT_TIMESTAMP"
1120+
1121+-- | >>> printSQL localTime
1122+-- LOCALTIME
1123+localTime :: Expr (null 'PGtime)
1124+localTime = UnsafeExpression "LOCALTIME"
1125+
1126+-- | >>> printSQL localTimestamp
1127+-- LOCALTIMESTAMP
1128+localTimestamp :: Expr (null 'PGtimestamp)
1129+localTimestamp = UnsafeExpression "LOCALTIMESTAMP"
1130+
1131+-- | Current date and time (equivalent to `currentTimestamp`)
1132+--
1133+-- >>> printSQL now
1134+-- now()
1135+now :: Expr (null 'PGtimestamptz)
1136+now = UnsafeExpression "now()"
1137+
1138+{-----------------------------------------
1139+text
1140+-----------------------------------------}
1141+
1142+escape :: Char -> String
1143+escape = \case
1144+ '\NUL' -> "\\0"
1145+ '\'' -> "''"
1146+ '"' -> "\\\""
1147+ '\b' -> "\\b"
1148+ '\n' -> "\\n"
1149+ '\r' -> "\\r"
1150+ '\t' -> "\\t"
1151+ '\\' -> "\\\\"
1152+ c -> [c]
4381153
4391154instance IsString
4401155 (Expression outer commons grp schemas params from (null 'PGtext)) where
@@ -442,19 +1157,13 @@ instance IsString
4421157 "E\'" <> fromString (escape =<< str) <> "\'"
4431158instance IsString
4441159 (Expression outer commons grp schemas params from (null 'PGtsvector)) where
445− fromString str = UnsafeExpression . parenthesized . (<> " :: tsvector") $
1160+ fromString str = cast tsvector . UnsafeExpression $
4461161 "E\'" <> fromString (escape =<< str) <> "\'"
4471162instance IsString
4481163 (Expression outer commons grp schemas params from (null 'PGtsquery)) where
449− fromString str = UnsafeExpression . parenthesized . (<> " :: tsquery") $
1164+ fromString str = cast tsquery . UnsafeExpression $
4501165 "E\'" <> fromString (escape =<< str) <> "\'"
4511166
452−instance 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 "||"
4581167instance Semigroup
4591168 (Expression outer commons grp schemas params from (null 'PGtext)) where
4601169 (<>) = unsafeBinaryOp "||"
@@ -470,3 +1179,1390 @@ instance Monoid
4701179 (Expression outer commons grp schemas params from (null 'PGtsvector)) where
4711180 mempty = fromString ""
4721181 mappend = (<>)
1182+
1183+-- | >>> printSQL $ lower "ARRRGGG"
1184+-- lower(E'ARRRGGG')
1185+lower :: null 'PGtext :--> null 'PGtext
1186+lower = unsafeFunction "lower"
1187+
1188+-- | >>> printSQL $ upper "eeee"
1189+-- upper(E'eeee')
1190+upper :: null 'PGtext :--> null 'PGtext
1191+upper = unsafeFunction "upper"
1192+
1193+-- | >>> printSQL $ charLength "four"
1194+-- char_length(E'four')
1195+charLength :: null 'PGtext :--> null 'PGint4
1196+charLength = unsafeFunction "char_length"
1197+
1198+-- | The `like` expression returns true if the @string@ matches
1199+-- the supplied @pattern@. If @pattern@ does not contain percent signs
1200+-- or underscores, then the pattern only represents the string itself;
1201+-- in that case `like` acts like the equals operator. An underscore (_)
1202+-- in pattern stands for (matches) any single character; a percent sign (%)
1203+-- matches any sequence of zero or more characters.
1204+--
1205+-- >>> printSQL $ "abc" `like` "a%"
1206+-- (E'abc' LIKE E'a%')
1207+like :: Operator (null 'PGtext) (null 'PGtext) ('Null 'PGbool)
1208+like = unsafeBinaryOp "LIKE"
1209+
1210+-- | The key word ILIKE can be used instead of LIKE to make the
1211+-- match case-insensitive according to the active locale.
1212+--
1213+-- >>> printSQL $ "abc" `ilike` "a%"
1214+-- (E'abc' ILIKE E'a%')
1215+ilike :: Operator (null 'PGtext) (null 'PGtext) ('Null 'PGbool)
1216+ilike = unsafeBinaryOp "ILIKE"
1217+
1218+{-----------------------------------------
1219+ -- json and jsonb support
1220+
1221+See https://www.postgresql.org/docs/10/static/functions-json.html -- most
1222+comments lifted directly from this page.
1223+
1224+Table 9.44: json and jsonb operators
1225+-----------------------------------------}
1226+
1227+-- | Get JSON value (object field or array element) at a key.
1228+(.->)
1229+ :: (json `In` PGJsonType, key `In` PGJsonKey)
1230+ => Operator (null json) (null key) ('Null json)
1231+infixl 8 .->
1232+(.->) = unsafeBinaryOp "->"
1233+
1234+-- | Get JSON value (object field or array element) at a key, as text.
1235+(.->>)
1236+ :: (json `In` PGJsonType, key `In` PGJsonKey)
1237+ => Operator (null json) (null key) ('Null 'PGtext)
1238+infixl 8 .->>
1239+(.->>) = unsafeBinaryOp "->>"
1240+
1241+-- | Get JSON value at a specified path.
1242+(.#>)
1243+ :: json `In` PGJsonType
1244+ => Operator (null json) (null ('PGvararray ('NotNull 'PGtext))) ('Null json)
1245+infixl 8 .#>
1246+(.#>) = unsafeBinaryOp "#>"
1247+
1248+-- | Get JSON value at a specified path as text.
1249+(.#>>)
1250+ :: json `In` PGJsonType
1251+ => Operator (null json) (null ('PGvararray ('NotNull 'PGtext))) ('Null 'PGtext)
1252+infixl 8 .#>>
1253+(.#>>) = unsafeBinaryOp "#>>"
1254+
1255+-- Additional jsonb operators
1256+
1257+class PGSubset container where
1258+ (@>) :: Operator (null0 container) (null1 container) ('Null 'PGbool)
1259+ (@>) = unsafeBinaryOp "@>"
1260+ (<@) :: Operator (null0 container) (null1 container) ('Null 'PGbool)
1261+ (<@) = unsafeBinaryOp "<@"
1262+instance PGSubset 'PGjsonb
1263+instance PGSubset 'PGtsquery
1264+instance PGSubset ('PGvararray ty)
1265+
1266+-- | Does the string exist as a top-level key within the JSON value?
1267+(.?) :: Operator (null 'PGjsonb) (null 'PGtext) ('Null 'PGbool)
1268+infixl 9 .?
1269+(.?) = unsafeBinaryOp "?"
1270+
1271+-- | Do any of these array strings exist as top-level keys?
1272+(.?|) :: Operator
1273+ (null 'PGjsonb)
1274+ (null ('PGvararray ('NotNull 'PGtext)))
1275+ ('Null 'PGbool)
1276+infixl 9 .?|
1277+(.?|) = unsafeBinaryOp "?|"
1278+
1279+-- | Do all of these array strings exist as top-level keys?
1280+(.?&) :: Operator
1281+ (null 'PGjsonb)
1282+ (null ('PGvararray ('NotNull 'PGtext)))
1283+ ('Null 'PGbool)
1284+infixl 9 .?&
1285+(.?&) = unsafeBinaryOp "?&"
1286+
1287+-- | Concatenate two jsonb values into a new jsonb value.
1288+instance Semigroup
1289+ (Expression outer commons grp schemas params from (null 'PGjsonb)) where
1290+ (<>) = unsafeBinaryOp "||"
1291+
1292+-- | Delete a key or keys from a JSON object, or remove an array element.
1293+--
1294+-- If the right operand is..
1295+--
1296+-- @ text @: Delete key/value pair or string element from left operand. Key/value pairs
1297+-- are matched based on their key value.
1298+--
1299+-- @ text[] @: Delete multiple key/value pairs or string elements
1300+-- from left operand. Key/value pairs are matched based on their key value.
1301+--
1302+-- @ integer @: Delete the array element with specified index (Negative integers
1303+-- count from the end). Throws an error if top level container is not an array.
1304+(.-.)
1305+ :: (key `In` '[ 'PGtext, 'PGvararray ('NotNull 'PGtext), 'PGint4, 'PGint2 ]) -- hlint error without parens here
1306+ => Operator (null 'PGjsonb) (null key) (null 'PGjsonb)
1307+infixl 6 .-.
1308+(.-.) = unsafeBinaryOp "-"
1309+
1310+-- | Delete the field or element with specified path (for JSON arrays, negative
1311+-- integers count from the end)
1312+(#-.) :: Operator (null 'PGjsonb) (null ('PGvararray ('NotNull 'PGtext))) (null 'PGjsonb)
1313+infixl 6 #-.
1314+(#-.) = unsafeBinaryOp "#-"
1315+
1316+{-----------------------------------------
1317+Table 9.45: JSON creation functions
1318+-----------------------------------------}
1319+
1320+-- | Returns the value as json. Arrays and composites are converted
1321+-- (recursively) to arrays and objects; otherwise, if there is a cast from the
1322+-- type to json, the cast function will be used to perform the conversion;
1323+-- otherwise, a scalar value is produced. For any scalar type other than a
1324+-- number, a Boolean, or a null value, the text representation will be used, in
1325+-- such a fashion that it is a valid json value.
1326+toJson :: null ty :--> null 'PGjson
1327+toJson = unsafeFunction "to_json"
1328+
1329+-- | Returns the value as jsonb. Arrays and composites are converted
1330+-- (recursively) to arrays and objects; otherwise, if there is a cast from the
1331+-- type to json, the cast function will be used to perform the conversion;
1332+-- otherwise, a scalar value is produced. For any scalar type other than a
1333+-- number, a Boolean, or a null value, the text representation will be used, in
1334+-- such a fashion that it is a valid jsonb value.
1335+toJsonb :: null ty :--> null 'PGjsonb
1336+toJsonb = unsafeFunction "to_jsonb"
1337+
1338+-- | Returns the array as a JSON array. A PostgreSQL multidimensional array
1339+-- becomes a JSON array of arrays.
1340+arrayToJson :: null ('PGvararray ty) :--> null 'PGjson
1341+arrayToJson = unsafeFunction "array_to_json"
1342+
1343+-- | Returns the row as a JSON object.
1344+rowToJson :: null ('PGcomposite ty) :--> null 'PGjson
1345+rowToJson = unsafeFunction "row_to_json"
1346+
1347+-- | Builds a possibly-heterogeneously-typed JSON array out of a variadic
1348+-- argument list.
1349+jsonBuildArray :: SListI tuple => FunctionHet tuple (null 'PGjson)
1350+jsonBuildArray = unsafeFunctionHet "json_build_array"
1351+
1352+-- | Builds a possibly-heterogeneously-typed (binary) JSON array out of a
1353+-- variadic argument list.
1354+jsonbBuildArray :: SListI tuple => FunctionHet tuple (null 'PGjsonb)
1355+jsonbBuildArray = unsafeFunctionHet "jsonb_build_array"
1356+
1357+class PGBuildObject tys where
1358+instance PGBuildObject '[]
1359+instance (PGBuildObject tys, key `In` PGJsonKey)
1360+ => PGBuildObject ('NotNull key ': value ': tys)
1361+
1362+-- | Builds a possibly-heterogeneously-typed JSON object out of a variadic
1363+-- argument list. The elements of the argument list must alternate between text
1364+-- and values.
1365+jsonBuildObject
1366+ :: (SListI elems, PGBuildObject elems)
1367+ => FunctionHet elems (null 'PGjson)
1368+jsonBuildObject = unsafeFunctionHet "json_build_object"
1369+
1370+-- | Builds a possibly-heterogeneously-typed (binary) JSON object out of a
1371+-- variadic argument list. The elements of the argument list must alternate
1372+-- between keys and values.
1373+jsonbBuildObject
1374+ :: (SListI elems, PGBuildObject elems)
1375+ => FunctionHet elems (null 'PGjsonb)
1376+jsonbBuildObject = unsafeFunctionHet "jsonb_build_object"
1377+
1378+-- | Builds a JSON object out of a text array.
1379+-- The array must have two dimensions
1380+-- such that each inner array has exactly two elements,
1381+-- which are taken as a key/value pair.
1382+jsonObject
1383+ :: null ('PGfixarray '[n,2] ('NotNull 'PGtext))
1384+ :--> null 'PGjson
1385+jsonObject = unsafeFunction "json_object"
1386+
1387+-- | Builds a binary JSON object out of a text array.
1388+-- The array must have two dimensions
1389+-- such that each inner array has exactly two elements,
1390+-- which are taken as a key/value pair.
1391+jsonbObject
1392+ :: null ('PGfixarray '[n,2] ('NotNull 'PGtext))
1393+ :--> null 'PGjsonb
1394+jsonbObject = unsafeFunction "jsonb_object"
1395+
1396+-- | This is an alternate form of 'jsonObject' that takes two arrays; one for
1397+-- keys and one for values, that are zipped pairwise to create a JSON object.
1398+jsonZipObject :: FunctionHet
1399+ '[ null ('PGvararray ('NotNull 'PGtext))
1400+ , null ('PGvararray ('NotNull 'PGtext)) ]
1401+ ( null 'PGjson )
1402+jsonZipObject = unsafeFunctionHet "json_object"
1403+
1404+-- | This is an alternate form of 'jsonbObject' that takes two arrays; one for
1405+-- keys and one for values, that are zipped pairwise to create a binary JSON
1406+-- object.
1407+jsonbZipObject :: FunctionHet
1408+ '[ null ('PGvararray ('NotNull 'PGtext))
1409+ , null ('PGvararray ('NotNull 'PGtext)) ]
1410+ ( null 'PGjsonb )
1411+jsonbZipObject = unsafeFunctionHet "jsonb_object"
1412+
1413+{-----------------------------------------
1414+Table 9.46: JSON processing functions
1415+-----------------------------------------}
1416+
1417+-- | Returns the number of elements in the outermost JSON array.
1418+jsonArrayLength :: null 'PGjson :--> null 'PGint4
1419+jsonArrayLength = unsafeFunction "json_array_length"
1420+
1421+-- | Returns the number of elements in the outermost binary JSON array.
1422+jsonbArrayLength :: null 'PGjsonb :--> null 'PGint4
1423+jsonbArrayLength = unsafeFunction "jsonb_array_length"
1424+
1425+-- | Returns JSON value pointed to by the given path (equivalent to #>
1426+-- operator).
1427+jsonExtractPath
1428+ :: SListI elems
1429+ => Expression outer commons grp schemas params from (null 'PGjson)
1430+ -> NP (Expression outer commons grp schemas params from) elems
1431+ -> Expression outer commons grp schemas params from (null 'PGjsonb)
1432+jsonExtractPath x xs =
1433+ unsafeFunctionHet "json_extract_path" (x :* xs)
1434+
1435+-- | Returns JSON value pointed to by the given path (equivalent to #>
1436+-- operator).
1437+jsonbExtractPath
1438+ :: SListI elems
1439+ => Expression outer commons grp schemas params from (null 'PGjsonb)
1440+ -> NP (Expression outer commons grp schemas params from) elems
1441+ -> Expression outer commons grp schemas params from (null 'PGjsonb)
1442+jsonbExtractPath x xs =
1443+ unsafeFunctionHet "jsonb_extract_path" (x :* xs)
1444+
1445+-- | Returns JSON value pointed to by the given path (equivalent to #>
1446+-- operator), as text.
1447+jsonExtractPathAsText
1448+ :: SListI elems
1449+ => Expression outer commons grp schemas params from (null 'PGjson)
1450+ -> NP (Expression outer commons grp schemas params from) elems
1451+ -> Expression outer commons grp schemas params from (null 'PGjson)
1452+jsonExtractPathAsText x xs =
1453+ unsafeFunctionHet "json_extract_path_text" (x :* xs)
1454+
1455+-- | Returns JSON value pointed to by the given path (equivalent to #>
1456+-- operator), as text.
1457+jsonbExtractPathAsText
1458+ :: SListI elems
1459+ => Expression outer commons grp schemas params from (null 'PGjsonb)
1460+ -> NP (Expression outer commons grp schemas params from) elems
1461+ -> Expression outer commons grp schemas params from (null 'PGjsonb)
1462+jsonbExtractPathAsText x xs =
1463+ unsafeFunctionHet "jsonb_extract_path_text" (x :* xs)
1464+
1465+-- | Returns the type of the outermost JSON value as a text string. Possible
1466+-- types are object, array, string, number, boolean, and null.
1467+jsonTypeof :: null 'PGjson :--> null 'PGtext
1468+jsonTypeof = unsafeFunction "json_typeof"
1469+
1470+-- | Returns the type of the outermost binary JSON value as a text string.
1471+-- Possible types are object, array, string, number, boolean, and null.
1472+jsonbTypeof :: null 'PGjsonb :--> null 'PGtext
1473+jsonbTypeof = unsafeFunction "jsonb_typeof"
1474+
1475+-- | Returns its argument with all object fields that have null values omitted.
1476+-- Other null values are untouched.
1477+jsonStripNulls :: null 'PGjson :--> null 'PGjson
1478+jsonStripNulls = unsafeFunction "json_strip_nulls"
1479+
1480+-- | Returns its argument with all object fields that have null values omitted.
1481+-- Other null values are untouched.
1482+jsonbStripNulls :: null 'PGjsonb :--> null 'PGjsonb
1483+jsonbStripNulls = unsafeFunction "jsonb_strip_nulls"
1484+
1485+-- | @ jsonbSet target path new_value create_missing @
1486+--
1487+-- Returns target with the section designated by path replaced by new_value,
1488+-- or with new_value added if create_missing is true ( default is true) and the
1489+-- item designated by path does not exist. As with the path orientated
1490+-- operators, negative integers that appear in path count from the end of JSON
1491+-- arrays.
1492+jsonbSet
1493+ :: Expression outer commons grp schemas params from (null 'PGjsonb)
1494+ -> Expression outer commons grp schemas params from (null ('PGvararray ('NotNull 'PGtext)))
1495+ -> Expression outer commons grp schemas params from (null 'PGjsonb)
1496+ -> Maybe (Expression outer commons grp schemas params from (null 'PGbool))
1497+ -> Expression outer commons grp schemas params from (null 'PGjsonb)
1498+jsonbSet tgt path val createMissing = case createMissing of
1499+ Just m -> unsafeFunctionHet "jsonb_set" (tgt :* path :* val :* m :* Nil)
1500+ Nothing -> unsafeFunctionHet "jsonb_set" (tgt :* path :* val :* Nil)
1501+
1502+-- | @ jsonbInsert target path new_value insert_after @
1503+--
1504+-- Returns target with new_value inserted. If target section designated by
1505+-- path is in a JSONB array, new_value will be inserted before target or after
1506+-- if insert_after is true (default is false). If target section designated by
1507+-- path is in JSONB object, new_value will be inserted only if target does not
1508+-- exist. As with the path orientated operators, negative integers that appear
1509+-- in path count from the end of JSON arrays.
1510+jsonbInsert
1511+ :: Expression outer commons grp schemas params from (null 'PGjsonb)
1512+ -> Expression outer commons grp schemas params from (null ('PGvararray ('NotNull 'PGtext)))
1513+ -> Expression outer commons grp schemas params from (null 'PGjsonb)
1514+ -> Maybe (Expression outer commons grp schemas params from (null 'PGbool))
1515+ -> Expression outer commons grp schemas params from (null 'PGjsonb)
1516+jsonbInsert tgt path val insertAfter = case insertAfter of
1517+ Just i -> unsafeFunctionHet "jsonb_insert" (tgt :* path :* val :* i :* Nil)
1518+ Nothing -> unsafeFunctionHet "jsonb_insert" (tgt :* path :* val :* Nil)
1519+
1520+-- | Returns its argument as indented JSON text.
1521+jsonbPretty :: null 'PGjsonb :--> null 'PGtext
1522+jsonbPretty = unsafeFunction "jsonb_pretty"
1523+
1524+(@@) :: Operator (null 'PGtsvector) (null 'PGtsquery) ('Null 'PGbool)
1525+(@@) = unsafeBinaryOp "@@"
1526+
1527+(.&) :: Operator (null 'PGtsquery) (null 'PGtsquery) (null 'PGtsquery)
1528+(.&) = unsafeBinaryOp "&&"
1529+
1530+(.|) :: Operator (null 'PGtsquery) (null 'PGtsquery) (null 'PGtsquery)
1531+(.|) = unsafeBinaryOp "||"
1532+
1533+(.!) :: null 'PGtsquery :--> null 'PGtsquery
1534+(.!) = unsafeUnaryOpL "!!"
1535+
1536+(<->) :: Operator (null 'PGtsquery) (null 'PGtsquery) (null 'PGtsquery)
1537+(<->) = unsafeBinaryOp "<->"
1538+
1539+arrayToTSvector
1540+ :: null ('PGvararray ('NotNull 'PGtext))
1541+ :--> null 'PGtsvector
1542+arrayToTSvector = unsafeFunction "array_to_tsvector"
1543+
1544+tsvectorLength :: null 'PGtsvector :--> null 'PGint4
1545+tsvectorLength = unsafeFunction "length"
1546+
1547+numnode :: null 'PGtsquery :--> null 'PGint4
1548+numnode = unsafeFunction "numnode"
1549+
1550+plainToTSquery :: null 'PGtext :--> null 'PGtsquery
1551+plainToTSquery = unsafeFunction "plainto_tsquery"
1552+
1553+phraseToTSquery :: null 'PGtext :--> null 'PGtsquery
1554+phraseToTSquery = unsafeFunction "phraseto_tsquery"
1555+
1556+websearchToTSquery :: null 'PGtext :--> null 'PGtsquery
1557+websearchToTSquery = unsafeFunction "websearch_to_tsquery"
1558+
1559+queryTree :: null 'PGtsquery :--> null 'PGtext
1560+queryTree = unsafeFunction "query_tree"
1561+
1562+toTSquery :: null 'PGtext :--> null 'PGtsquery
1563+toTSquery = unsafeFunction "to_tsquery"
1564+
1565+toTSvector
1566+ :: ty `In` '[ 'PGtext, 'PGjson, 'PGjsonb]
1567+ => null ty :--> null 'PGtsvector
1568+toTSvector = unsafeFunction "to_tsvector"
1569+
1570+setWeight
1571+ :: FunctionHet '[null 'PGtsvector, null ('PGchar 1)] (null 'PGtsvector)
1572+setWeight = unsafeFunctionHet "set_weight"
1573+
1574+strip :: null 'PGtsvector :--> null 'PGtsvector
1575+strip = unsafeFunction "strip"
1576+
1577+jsonToTSvector :: FunctionHet '[null 'PGjson, null 'PGjson] (null 'PGtsvector)
1578+jsonToTSvector = unsafeFunctionHet "json_to_tsvector"
1579+
1580+jsonbToTSvector :: FunctionHet '[null 'PGjsonb, null 'PGjsonb] (null 'PGtsvector)
1581+jsonbToTSvector = unsafeFunctionHet "jsonb_to_tsvector"
1582+
1583+tsDelete :: FunctionHet
1584+ '[null 'PGtsvector, null ('PGvararray ('NotNull 'PGtext))]
1585+ (null 'PGtsvector)
1586+tsDelete = unsafeFunctionHet "ts_delete"
1587+
1588+tsFilter :: FunctionHet
1589+ '[null 'PGtsvector, null ('PGvararray ('NotNull ('PGchar 1)))]
1590+ (null 'PGtsvector)
1591+tsFilter = unsafeFunctionHet "ts_filter"
1592+
1593+tsHeadline
1594+ :: document `In` '[ 'PGtext, 'PGjson, 'PGjsonb]
1595+ => FunctionHet '[null document, null 'PGtsquery] (null 'PGtext)
1596+tsHeadline = unsafeFunctionHet "ts_headline"
1597+{-----------------------------------------
1598+aggregation
1599+-----------------------------------------}
1600+
1601+unsafeAggregateHet
1602+ :: SOP.SListI xs
1603+ => ByteString -- ^ function
1604+ -> Distinction (NP (Expression outer commons 'Ungrouped schemas params from)) xs
1605+ -> Expression outer commons ('Grouped bys) schemas params from y
1606+unsafeAggregateHet fun xs = UnsafeExpression $ fun <> parenthesized (renderSQL xs)
1607+
1608+{- |
1609+`Aggregate` functions compute a single result from a set of input values.
1610+`Aggregate` functions can be used as `GroupedBy` `Expression`s as well
1611+as `WindowFunction`s.
1612+-}
1613+class Aggregate expr1 exprHet aggr
1614+ | aggr -> expr1, aggr -> exprHet where
1615+
1616+ -- | A special aggregation that does not require an input
1617+ --
1618+ -- >>> :{
1619+ -- let
1620+ -- expression :: Expression '[] commons ('Grouped bys) schemas params from ('NotNull 'PGint8)
1621+ -- expression = countStar
1622+ -- in printSQL expression
1623+ -- :}
1624+ -- count(*)
1625+ countStar :: aggr ('NotNull 'PGint8)
1626+
1627+ -- | >>> :{
1628+ -- let
1629+ -- expression :: Expression '[] commons ('Grouped bys) schemas params '[tab ::: '["col" ::: null ty]] ('NotNull 'PGint8)
1630+ -- expression = count (All #col)
1631+ -- in printSQL expression
1632+ -- :}
1633+ -- count(ALL "col")
1634+ count
1635+ :: expr1 ty
1636+ -- ^ what to count
1637+ -> aggr ('NotNull 'PGint8)
1638+
1639+ -- | >>> :{
1640+ -- let
1641+ -- expression :: Expression '[] commons ('Grouped bys) schemas params '[tab ::: '["col" ::: 'Null 'PGnumeric]] ('NotNull 'PGnumeric)
1642+ -- expression = sum_ (Distinct #col)
1643+ -- in printSQL expression
1644+ -- :}
1645+ -- sum(DISTINCT "col")
1646+ sum_
1647+ :: ty `In` PGNum
1648+ => expr1 (null ty)
1649+ -> aggr ('NotNull ty)
1650+
1651+ -- | input values, including nulls, concatenated into an array
1652+ arrayAgg
1653+ :: expr1 ty
1654+ -> aggr ('Null ('PGvararray ty))
1655+
1656+ -- | aggregates values as a JSON array
1657+ jsonAgg
1658+ :: expr1 ty
1659+ -> aggr ('Null 'PGjson)
1660+
1661+ -- | aggregates values as a JSON array
1662+ jsonbAgg
1663+ :: expr1 ty
1664+ -> aggr ('Null 'PGjsonb)
1665+
1666+ {- |
1667+ the bitwise AND of all non-null input values, or null if none
1668+ >>> :{
1669+ let
1670+ expression :: Expression '[] commons ('Grouped bys) schemas params '[tab ::: '["col" ::: null 'PGint4]] ('Null 'PGint4)
1671+ expression = bitAnd (Distinct #col)
1672+ in printSQL expression
1673+ :}
1674+ bit_and(DISTINCT "col")
1675+ -}
1676+ bitAnd
1677+ :: int `In` PGIntegral
1678+ => expr1 (null int)
1679+ -- ^ what to aggregate
1680+ -> aggr ('Null int)
1681+
1682+ {- |
1683+ the bitwise OR of all non-null input values, or null if none
1684+
1685+ >>> :{
1686+ let
1687+ expression :: Expression '[] commons ('Grouped bys) schemas params '[tab ::: '["col" ::: null 'PGint4]] ('Null 'PGint4)
1688+ expression = bitOr (All #col)
1689+ in printSQL expression
1690+ :}
1691+ bit_or(ALL "col")
1692+ -}
1693+ bitOr
1694+ :: int `In` PGIntegral
1695+ => expr1 (null int)
1696+ -- ^ what to aggregate
1697+ -> aggr ('Null int)
1698+
1699+ {- |
1700+ true if all input values are true, otherwise false
1701+
1702+ >>> :{
1703+ let
1704+ winFun :: WindowFunction '[] commons 'Ungrouped schemas params '[tab ::: '["col" ::: null 'PGbool]] ('Null 'PGbool)
1705+ winFun = boolAnd #col
1706+ in printSQL winFun
1707+ :}
1708+ bool_and("col")
1709+ -}
1710+ boolAnd
1711+ :: expr1 (null 'PGbool)
1712+ -- ^ what to aggregate
1713+ -> aggr ('Null 'PGbool)
1714+
1715+ {- |
1716+ true if at least one input value is true, otherwise false
1717+
1718+ >>> :{
1719+ let
1720+ expression :: Expression '[] commons ('Grouped bys) schemas params '[tab ::: '["col" ::: null 'PGbool]] ('Null 'PGbool)
1721+ expression = boolOr (All #col)
1722+ in printSQL expression
1723+ :}
1724+ bool_or(ALL "col")
1725+ -}
1726+ boolOr
1727+ :: expr1 (null 'PGbool)
1728+ -- ^ what to aggregate
1729+ -> aggr ('Null 'PGbool)
1730+
1731+ {- |
1732+ equivalent to `boolAnd`
1733+
1734+ >>> :{
1735+ let
1736+ expression :: Expression '[] commons ('Grouped bys) schemas params '[tab ::: '["col" ::: null 'PGbool]] ('Null 'PGbool)
1737+ expression = every (Distinct #col)
1738+ in printSQL expression
1739+ :}
1740+ every(DISTINCT "col")
1741+ -}
1742+ every
1743+ :: expr1 (null 'PGbool)
1744+ -- ^ what to aggregate
1745+ -> aggr ('Null 'PGbool)
1746+
1747+ {- |maximum value of expression across all input values-}
1748+ max_
1749+ :: expr1 (null ty)
1750+ -- ^ what to maximize
1751+ -> aggr ('Null ty)
1752+
1753+ -- | minimum value of expression across all input values
1754+ min_
1755+ :: expr1 (null ty)
1756+ -- ^ what to minimize
1757+ -> aggr ('Null ty)
1758+
1759+ -- | the average (arithmetic mean) of all input values
1760+ avg
1761+ :: expr1 (null ty)
1762+ -- ^ what to average
1763+ -> aggr ('Null (PGAvg ty))
1764+
1765+ {- | correlation coefficient
1766+ >>> :{
1767+ let
1768+ expression :: Expression '[] c ('Grouped g) s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1769+ expression = corr (All (#y *: #x))
1770+ in printSQL expression
1771+ :}
1772+ corr(ALL "y", "x")
1773+ -}
1774+ corr
1775+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1776+ -> aggr ('Null 'PGfloat8)
1777+
1778+ {- | population covariance
1779+ >>> :{
1780+ let
1781+ expression :: Expression '[] c ('Grouped g) s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1782+ expression = covarPop (All (#y *: #x))
1783+ in printSQL expression
1784+ :}
1785+ covar_pop(ALL "y", "x")
1786+ -}
1787+ covarPop
1788+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1789+ -> aggr ('Null 'PGfloat8)
1790+
1791+ {- | sample covariance
1792+ >>> :{
1793+ let
1794+ winFun :: WindowFunction '[] c 'Ungrouped s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1795+ winFun = covarSamp (#y *: #x)
1796+ in printSQL winFun
1797+ :}
1798+ covar_samp("y", "x")
1799+ -}
1800+ covarSamp
1801+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1802+ -> aggr ('Null 'PGfloat8)
1803+
1804+ {- | average of the independent variable (sum(X)/N)
1805+ >>> :{
1806+ let
1807+ expression :: Expression '[] c ('Grouped g) s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1808+ expression = regrAvgX (All (#y *: #x))
1809+ in printSQL expression
1810+ :}
1811+ regr_avgx(ALL "y", "x")
1812+ -}
1813+ regrAvgX
1814+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1815+ -> aggr ('Null 'PGfloat8)
1816+
1817+ {- | average of the dependent variable (sum(Y)/N)
1818+ >>> :{
1819+ let
1820+ winFun :: WindowFunction '[] c 'Ungrouped s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1821+ winFun = regrAvgY (#y *: #x)
1822+ in printSQL winFun
1823+ :}
1824+ regr_avgy("y", "x")
1825+ -}
1826+ regrAvgY
1827+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1828+ -> aggr ('Null 'PGfloat8)
1829+
1830+ {- | number of input rows in which both expressions are nonnull
1831+ >>> :{
1832+ let
1833+ winFun :: WindowFunction '[] c 'Ungrouped s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGint8)
1834+ winFun = regrCount (#y *: #x)
1835+ in printSQL winFun
1836+ :}
1837+ regr_count("y", "x")
1838+ -}
1839+ regrCount
1840+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1841+ -> aggr ('Null 'PGint8)
1842+
1843+ {- | y-intercept of the least-squares-fit linear equation determined by the (X, Y) pairs
1844+ >>> :{
1845+ let
1846+ expression :: Expression '[] c ('Grouped g) s p '[t ::: '["x" ::: 'NotNull 'PGfloat8, "y" ::: 'NotNull 'PGfloat8]] ('Null 'PGfloat8)
1847+ expression = regrIntercept (All (#y *: #x))
1848+ in printSQL expression
1849+ :}
1850+ regr_intercept(ALL "y", "x")
1851+ -}
1852+ regrIntercept
1853+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1854+ -> aggr ('Null 'PGfloat8)
1855+
1856+ -- | @regr_r2(Y, X)@, square of the correlation coefficient
1857+ regrR2
1858+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1859+ -> aggr ('Null 'PGfloat8)
1860+
1861+ -- | @regr_slope(Y, X)@, slope of the least-squares-fit linear equation
1862+ -- determined by the (X, Y) pairs
1863+ regrSlope
1864+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1865+ -> aggr ('Null 'PGfloat8)
1866+
1867+ -- | @regr_sxx(Y, X)@, sum(X^2) - sum(X)^2/N
1868+ -- (“sum of squares” of the independent variable)
1869+ regrSxx
1870+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1871+ -> aggr ('Null 'PGfloat8)
1872+
1873+ -- | @regr_sxy(Y, X)@, sum(X*Y) - sum(X) * sum(Y)/N
1874+ -- (“sum of products” of independent times dependent variable)
1875+ regrSxy
1876+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1877+ -> aggr ('Null 'PGfloat8)
1878+
1879+ -- | @regr_syy(Y, X)@, sum(Y^2) - sum(Y)^2/N
1880+ -- (“sum of squares” of the dependent variable)
1881+ regrSyy
1882+ :: exprHet '[null 'PGfloat8, null 'PGfloat8]
1883+ -> aggr ('Null 'PGfloat8)
1884+
1885+ -- | historical alias for `stddevSamp`
1886+ stddev
1887+ :: expr1 (null ty)
1888+ -> aggr ('Null (PGAvg ty))
1889+
1890+ -- | population standard deviation of the input values
1891+ stddevPop
1892+ :: expr1 (null ty)
1893+ -> aggr ('Null (PGAvg ty))
1894+
1895+ -- | sample standard deviation of the input values
1896+ stddevSamp
1897+ :: expr1 (null ty)
1898+ -> aggr ('Null (PGAvg ty))
1899+
1900+ -- | historical alias for `varSamp`
1901+ variance
1902+ :: expr1 (null ty)
1903+ -> aggr ('Null (PGAvg ty))
1904+
1905+ -- | population variance of the input values
1906+ -- (square of the population standard deviation)
1907+ varPop
1908+ :: expr1 (null ty)
1909+ -> aggr ('Null (PGAvg ty))
1910+
1911+ -- | sample variance of the input values
1912+ -- (square of the sample standard deviation)
1913+ varSamp
1914+ :: expr1 (null ty)
1915+ -> aggr ('Null (PGAvg ty))
1916+
1917+{- |
1918+`Distinction`s are used for the input of `Aggregate` `Expression`s.
1919+`All` invokes the aggregate once for each input row.
1920+`Distinct` invokes the aggregate once for each distinct value of the expression
1921+(or distinct set of values, for multiple expressions) found in the input
1922+-}
1923+data Distinction (expr :: kind -> Type) (ty :: kind)
1924+ = All (expr ty)
1925+ | Distinct (expr ty)
1926+ deriving (GHC.Generic,Show,Eq,Ord)
1927+instance NFData (Distinction (Expression outer commons grp schemas params from) ty)
1928+instance RenderSQL (Distinction (Expression outer commons grp schemas params from) ty) where
1929+ renderSQL = \case
1930+ All x -> "ALL" <+> renderSQL x
1931+ Distinct x -> "DISTINCT" <+> renderSQL x
1932+instance SOP.SListI tys => RenderSQL
1933+ (Distinction (NP (Expression outer commons grp schemas params from)) tys) where
1934+ renderSQL = \case
1935+ All xs -> "ALL" <+> renderCommaSeparated renderSQL xs
1936+ Distinct xs -> "DISTINCT" <+> renderCommaSeparated renderSQL xs
1937+
1938+instance Aggregate
1939+ (Distinction (Expression outer commons 'Ungrouped schemas params from))
1940+ (Distinction (NP (Expression outer commons 'Ungrouped schemas params from)))
1941+ (Expression outer commons ('Grouped bys) schemas params from) where
1942+ countStar = UnsafeExpression "count(*)"
1943+ count = unsafeAggregate1 "count"
1944+ sum_ = unsafeAggregate1 "sum"
1945+ arrayAgg = unsafeAggregate1 "array_agg"
1946+ jsonAgg = unsafeAggregate1 "json_agg"
1947+ jsonbAgg = unsafeAggregate1 "jsonb_agg"
1948+ bitAnd = unsafeAggregate1 "bit_and"
1949+ bitOr = unsafeAggregate1 "bit_or"
1950+ boolAnd = unsafeAggregate1 "bool_and"
1951+ boolOr = unsafeAggregate1 "bool_or"
1952+ every = unsafeAggregate1 "every"
1953+ max_ = unsafeAggregate1 "max"
1954+ min_ = unsafeAggregate1 "min"
1955+ avg = unsafeAggregate1 "avg"
1956+ corr = unsafeAggregateHet "corr"
1957+ covarPop = unsafeAggregateHet "covar_pop"
1958+ covarSamp = unsafeAggregateHet "covar_samp"
1959+ regrAvgX = unsafeAggregateHet "regr_avgx"
1960+ regrAvgY = unsafeAggregateHet "regr_avgy"
1961+ regrCount = unsafeAggregateHet "regr_count"
1962+ regrIntercept = unsafeAggregateHet "regr_intercept"
1963+ regrR2 = unsafeAggregateHet "regr_r2"
1964+ regrSlope = unsafeAggregateHet "regr_slope"
1965+ regrSxx = unsafeAggregateHet "regr_sxx"
1966+ regrSxy = unsafeAggregateHet "regr_sxy"
1967+ regrSyy = unsafeAggregateHet "regr_syy"
1968+ stddev = unsafeAggregate1 "stddev"
1969+ stddevPop = unsafeAggregate1 "stddev_pop"
1970+ stddevSamp = unsafeAggregate1 "stddev_samp"
1971+ variance = unsafeAggregate1 "variance"
1972+ varPop = unsafeAggregate1 "var_pop"
1973+ varSamp = unsafeAggregate1 "var_samp"
1974+instance Aggregate
1975+ (Expression outer commons grp schemas params from)
1976+ (NP (Expression outer commons grp schemas params from))
1977+ (WindowFunction outer commons grp schemas params from) where
1978+ countStar = UnsafeWindowFunction "count(*)"
1979+ count = unsafeWindowFunction1 "count"
1980+ sum_ = unsafeWindowFunction1 "sum"
1981+ arrayAgg = unsafeWindowFunction1 "array_agg"
1982+ jsonAgg = unsafeWindowFunction1 "json_agg"
1983+ jsonbAgg = unsafeWindowFunction1 "jsonb_agg"
1984+ bitAnd = unsafeWindowFunction1 "bit_and"
1985+ bitOr = unsafeWindowFunction1 "bit_or"
1986+ boolAnd = unsafeWindowFunction1 "bool_and"
1987+ boolOr = unsafeWindowFunction1 "bool_or"
1988+ every = unsafeWindowFunction1 "every"
1989+ max_ = unsafeWindowFunction1 "max"
1990+ min_ = unsafeWindowFunction1 "min"
1991+ avg = unsafeWindowFunction1 "avg"
1992+ corr = unsafeWindowFunctionHet "corr"
1993+ covarPop = unsafeWindowFunctionHet "covar_pop"
1994+ covarSamp = unsafeWindowFunctionHet "covar_samp"
1995+ regrAvgX = unsafeWindowFunctionHet "regr_avgx"
1996+ regrAvgY = unsafeWindowFunctionHet "regr_avgy"
1997+ regrCount = unsafeWindowFunctionHet "regr_count"
1998+ regrIntercept = unsafeWindowFunctionHet "regr_intercept"
1999+ regrR2 = unsafeWindowFunctionHet "regr_r2"
2000+ regrSlope = unsafeWindowFunctionHet "regr_slope"
2001+ regrSxx = unsafeWindowFunctionHet "regr_sxx"
2002+ regrSxy = unsafeWindowFunctionHet "regr_sxy"
2003+ regrSyy = unsafeWindowFunctionHet "regr_syy"
2004+ stddev = unsafeWindowFunction1 "stddev"
2005+ stddevPop = unsafeWindowFunction1 "stddev_pop"
2006+ stddevSamp = unsafeWindowFunction1 "stddev_samp"
2007+ variance = unsafeWindowFunction1 "variance"
2008+ varPop = unsafeWindowFunction1 "var_pop"
2009+ varSamp = unsafeWindowFunction1 "var_samp"
2010+
2011+-- | escape hatch to define aggregate functions
2012+unsafeAggregate1
2013+ :: ByteString -- ^ aggregate function
2014+ -> Distinction (Expression outer commons 'Ungrouped schemas params from) x
2015+ -> Expression outer commons ('Grouped bys) schemas params from y
2016+unsafeAggregate1 fun x = UnsafeExpression $ fun <> parenthesized (renderSQL x)
2017+
2018+-- | A type family that calculates `PGAvg` type of a `PGType`.
2019+type family PGAvg ty where
2020+ PGAvg 'PGint2 = 'PGnumeric
2021+ PGAvg 'PGint4 = 'PGnumeric
2022+ PGAvg 'PGint8 = 'PGnumeric
2023+ PGAvg 'PGnumeric = 'PGnumeric
2024+ PGAvg 'PGfloat4 = 'PGfloat8
2025+ PGAvg 'PGfloat8 = 'PGfloat8
2026+ PGAvg 'PGinterval = 'PGinterval
2027+ PGAvg pg = TypeError
2028+ ('Text "Squeal type error: No average for " ':<>: 'ShowType pg)
2029+
2030+{-----------------------------------------
2031+type expressions
2032+-----------------------------------------}
2033+
2034+-- | `TypeExpression`s are used in `cast`s and
2035+-- `Squeal.PostgreSQL.Definition.createTable` commands.
2036+newtype TypeExpression (schemas :: SchemasType) (ty :: NullityType)
2037+ = UnsafeTypeExpression { renderTypeExpression :: ByteString }
2038+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
2039+instance RenderSQL (TypeExpression schemas ty) where
2040+ renderSQL = renderTypeExpression
2041+
2042+-- | The enum or composite type in a `Typedef` can be expressed by its alias.
2043+typedef
2044+ :: (Has sch schemas schema, Has td schema ('Typedef ty))
2045+ => QualifiedAlias sch td
2046+ -> TypeExpression schemas (null ty)
2047+typedef = UnsafeTypeExpression . renderSQL
2048+
2049+-- | The composite type corresponding to a `Table` definition can be expressed
2050+-- by its alias.
2051+typetable
2052+ :: (Has sch schemas schema, Has tab schema ('Table table))
2053+ => QualifiedAlias sch tab
2054+ -> TypeExpression schemas (null ('PGcomposite (TableToRow table)))
2055+typetable = UnsafeTypeExpression . renderSQL
2056+
2057+-- | The composite type corresponding to a `View` definition can be expressed
2058+-- by its alias.
2059+typeview
2060+ :: (Has sch schemas schema, Has vw schema ('View view))
2061+ => QualifiedAlias sch vw
2062+ -> TypeExpression schemas (null ('PGcomposite view))
2063+typeview = UnsafeTypeExpression . renderSQL
2064+
2065+-- | logical Boolean (true/false)
2066+bool :: TypeExpression schemas (null 'PGbool)
2067+bool = UnsafeTypeExpression "bool"
2068+-- | signed two-byte integer
2069+int2, smallint :: TypeExpression schemas (null 'PGint2)
2070+int2 = UnsafeTypeExpression "int2"
2071+smallint = UnsafeTypeExpression "smallint"
2072+-- | signed four-byte integer
2073+int4, int, integer :: TypeExpression schemas (null 'PGint4)
2074+int4 = UnsafeTypeExpression "int4"
2075+int = UnsafeTypeExpression "int"
2076+integer = UnsafeTypeExpression "integer"
2077+-- | signed eight-byte integer
2078+int8, bigint :: TypeExpression schemas (null 'PGint8)
2079+int8 = UnsafeTypeExpression "int8"
2080+bigint = UnsafeTypeExpression "bigint"
2081+-- | arbitrary precision numeric type
2082+numeric :: TypeExpression schemas (null 'PGnumeric)
2083+numeric = UnsafeTypeExpression "numeric"
2084+-- | single precision floating-point number (4 bytes)
2085+float4, real :: TypeExpression schemas (null 'PGfloat4)
2086+float4 = UnsafeTypeExpression "float4"
2087+real = UnsafeTypeExpression "real"
2088+-- | double precision floating-point number (8 bytes)
2089+float8, doublePrecision :: TypeExpression schemas (null 'PGfloat8)
2090+float8 = UnsafeTypeExpression "float8"
2091+doublePrecision = UnsafeTypeExpression "double precision"
2092+-- | currency amount
2093+money :: TypeExpression schema (null 'PGmoney)
2094+money = UnsafeTypeExpression "money"
2095+-- | variable-length character string
2096+text :: TypeExpression schemas (null 'PGtext)
2097+text = UnsafeTypeExpression "text"
2098+-- | fixed-length character string
2099+char, character
2100+ :: forall n schemas null. (KnownNat n, 1 <= n)
2101+ => TypeExpression schemas (null ('PGchar n))
2102+char = UnsafeTypeExpression $ "char(" <> renderNat @n <> ")"
2103+character = UnsafeTypeExpression $ "character(" <> renderNat @n <> ")"
2104+-- | variable-length character string
2105+varchar, characterVarying
2106+ :: forall n schemas null. (KnownNat n, 1 <= n)
2107+ => TypeExpression schemas (null ('PGvarchar n))
2108+varchar = UnsafeTypeExpression $ "varchar(" <> renderNat @n <> ")"
2109+characterVarying = UnsafeTypeExpression $
2110+ "character varying(" <> renderNat @n <> ")"
2111+-- | binary data ("byte array")
2112+bytea :: TypeExpression schemas (null 'PGbytea)
2113+bytea = UnsafeTypeExpression "bytea"
2114+-- | date and time (no time zone)
2115+timestamp :: TypeExpression schemas (null 'PGtimestamp)
2116+timestamp = UnsafeTypeExpression "timestamp"
2117+-- | date and time, including time zone
2118+timestampWithTimeZone :: TypeExpression schemas (null 'PGtimestamptz)
2119+timestampWithTimeZone = UnsafeTypeExpression "timestamp with time zone"
2120+-- | calendar date (year, month, day)
2121+date :: TypeExpression schemas (null 'PGdate)
2122+date = UnsafeTypeExpression "date"
2123+-- | time of day (no time zone)
2124+time :: TypeExpression schemas (null 'PGtime)
2125+time = UnsafeTypeExpression "time"
2126+-- | time of day, including time zone
2127+timeWithTimeZone :: TypeExpression schemas (null 'PGtimetz)
2128+timeWithTimeZone = UnsafeTypeExpression "time with time zone"
2129+-- | time span
2130+interval :: TypeExpression schemas (null 'PGinterval)
2131+interval = UnsafeTypeExpression "interval"
2132+-- | universally unique identifier
2133+uuid :: TypeExpression schemas (null 'PGuuid)
2134+uuid = UnsafeTypeExpression "uuid"
2135+-- | IPv4 or IPv6 host address
2136+inet :: TypeExpression schemas (null 'PGinet)
2137+inet = UnsafeTypeExpression "inet"
2138+-- | textual JSON data
2139+json :: TypeExpression schemas (null 'PGjson)
2140+json = UnsafeTypeExpression "json"
2141+-- | binary JSON data, decomposed
2142+jsonb :: TypeExpression schemas (null 'PGjsonb)
2143+jsonb = UnsafeTypeExpression "jsonb"
2144+-- | variable length array
2145+vararray
2146+ :: TypeExpression schemas pg
2147+ -> TypeExpression schemas (null ('PGvararray pg))
2148+vararray ty = UnsafeTypeExpression $ renderSQL ty <> "[]"
2149+-- | fixed length array
2150+--
2151+-- >>> renderSQL (fixarray @'[2] json)
2152+-- "json[2]"
2153+fixarray
2154+ :: forall dims schemas null pg. SOP.All KnownNat dims
2155+ => TypeExpression schemas pg
2156+ -> TypeExpression schemas (null ('PGfixarray dims pg))
2157+fixarray ty = UnsafeTypeExpression $
2158+ renderSQL ty <> renderDims @dims
2159+ where
2160+ renderDims :: forall ns. SOP.All KnownNat ns => ByteString
2161+ renderDims =
2162+ ("[" <>)
2163+ . (<> "]")
2164+ . ByteString.intercalate "]["
2165+ . hcollapse
2166+ $ hcmap (SOP.Proxy @KnownNat)
2167+ (K . fromString . show . natVal)
2168+ (hpure SOP.Proxy :: NP SOP.Proxy ns)
2169+tsvector :: TypeExpression schemas (null 'PGtsvector)
2170+tsvector = UnsafeTypeExpression "tsvector"
2171+tsquery :: TypeExpression schemas (null 'PGtsquery)
2172+tsquery = UnsafeTypeExpression "tsquery"
2173+
2174+-- | `pgtype` is a demoted version of a `PGType`
2175+class PGTyped schemas (ty :: NullityType) where
2176+ pgtype :: TypeExpression schemas ty
2177+instance PGTyped schemas (null 'PGbool) where pgtype = bool
2178+instance PGTyped schemas (null 'PGint2) where pgtype = int2
2179+instance PGTyped schemas (null 'PGint4) where pgtype = int4
2180+instance PGTyped schemas (null 'PGint8) where pgtype = int8
2181+instance PGTyped schemas (null 'PGnumeric) where pgtype = numeric
2182+instance PGTyped schemas (null 'PGfloat4) where pgtype = float4
2183+instance PGTyped schemas (null 'PGfloat8) where pgtype = float8
2184+instance PGTyped schemas (null 'PGmoney) where pgtype = money
2185+instance PGTyped schemas (null 'PGtext) where pgtype = text
2186+instance (KnownNat n, 1 <= n)
2187+ => PGTyped schemas (null ('PGchar n)) where pgtype = char @n
2188+instance (KnownNat n, 1 <= n)
2189+ => PGTyped schemas (null ('PGvarchar n)) where pgtype = varchar @n
2190+instance PGTyped schemas (null 'PGbytea) where pgtype = bytea
2191+instance PGTyped schemas (null 'PGtimestamp) where pgtype = timestamp
2192+instance PGTyped schemas (null 'PGtimestamptz) where pgtype = timestampWithTimeZone
2193+instance PGTyped schemas (null 'PGdate) where pgtype = date
2194+instance PGTyped schemas (null 'PGtime) where pgtype = time
2195+instance PGTyped schemas (null 'PGtimetz) where pgtype = timeWithTimeZone
2196+instance PGTyped schemas (null 'PGinterval) where pgtype = interval
2197+instance PGTyped schemas (null 'PGuuid) where pgtype = uuid
2198+instance PGTyped schemas (null 'PGjson) where pgtype = json
2199+instance PGTyped schemas (null 'PGjsonb) where pgtype = jsonb
2200+instance PGTyped schemas ty
2201+ => PGTyped schemas (null ('PGvararray ty)) where
2202+ pgtype = vararray (pgtype @schemas @ty)
2203+instance (SOP.All KnownNat dims, PGTyped schemas ty)
2204+ => PGTyped schemas (null ('PGfixarray dims ty)) where
2205+ pgtype = fixarray @dims (pgtype @schemas @ty)
2206+instance PGTyped schemas (null 'PGtsvector) where pgtype = tsvector
2207+instance PGTyped schemas (null 'PGtsquery) where pgtype = tsquery
2208+
2209+{-----------------------------------------
2210+Sorting
2211+-----------------------------------------}
2212+
2213+-- | `SortExpression`s are used by `orderBy` to optionally sort the results
2214+-- of a `Squeal.PostgreSQL.Query.Query`. `Asc` or `Desc`
2215+-- set the sort direction of a `NotNull` result
2216+-- column to ascending or descending. Ascending order puts smaller values
2217+-- first, where "smaller" is defined in terms of the `.<` operator. Similarly,
2218+-- descending order is determined with the `.>` operator. `AscNullsFirst`,
2219+-- `AscNullsLast`, `DescNullsFirst` and `DescNullsLast` options are used to
2220+-- determine whether nulls appear before or after non-null values in the sort
2221+-- ordering of a `Null` result column.
2222+data SortExpression outer commons grp schemas params from where
2223+ Asc
2224+ :: Expression outer commons grp schemas params from ('NotNull ty)
2225+ -> SortExpression outer commons grp schemas params from
2226+ Desc
2227+ :: Expression outer commons grp schemas params from ('NotNull ty)
2228+ -> SortExpression outer commons grp schemas params from
2229+ AscNullsFirst
2230+ :: Expression outer commons grp schemas params from ('Null ty)
2231+ -> SortExpression outer commons grp schemas params from
2232+ AscNullsLast
2233+ :: Expression outer commons grp schemas params from ('Null ty)
2234+ -> SortExpression outer commons grp schemas params from
2235+ DescNullsFirst
2236+ :: Expression outer commons grp schemas params from ('Null ty)
2237+ -> SortExpression outer commons grp schemas params from
2238+ DescNullsLast
2239+ :: Expression outer commons grp schemas params from ('Null ty)
2240+ -> SortExpression outer commons grp schemas params from
2241+deriving instance Show (SortExpression outer commons grp schemas params from)
2242+
2243+{- |
2244+The `orderBy` clause causes the result rows of a `Squeal.PostgreSQL.Query.TableExpression`
2245+to be sorted according to the specified `SortExpression`(s).
2246+If two rows are equal according to the leftmost expression,
2247+they are compared according to the next expression and so on.
2248+If they are equal according to all specified expressions,
2249+they are returned in an implementation-dependent order.
2250+
2251+You can also control the order in which rows are processed by window functions
2252+using `orderBy` within `Squeal.PostgreSQL.Query.Over`.
2253+-}
2254+class OrderBy expr where
2255+ orderBy
2256+ :: [SortExpression outer commons grp schemas params from]
2257+ -> expr outer commons grp schemas params from
2258+ -> expr outer commons grp schemas params from
2259+
2260+-- | Render a `SortExpression`.
2261+instance RenderSQL (SortExpression outer commons grp schemas params from) where
2262+ renderSQL = \case
2263+ Asc expression -> renderSQL expression <+> "ASC"
2264+ Desc expression -> renderSQL expression <+> "DESC"
2265+ AscNullsFirst expression -> renderSQL expression
2266+ <+> "ASC NULLS FIRST"
2267+ DescNullsFirst expression -> renderSQL expression
2268+ <+> "DESC NULLS FIRST"
2269+ AscNullsLast expression -> renderSQL expression <+> "ASC NULLS LAST"
2270+ DescNullsLast expression -> renderSQL expression <+> "DESC NULLS LAST"
2271+
2272+-- | A `WindowDefinition` is a set of table rows that are somehow related
2273+-- to the current row
2274+data WindowDefinition outer commons grp schemas params from where
2275+ WindowDefinition
2276+ :: SListI bys
2277+ => NP (Expression outer commons grp schemas params from) bys
2278+ -> [SortExpression outer commons grp schemas params from]
2279+ -> WindowDefinition outer commons grp schemas params from
2280+
2281+instance OrderBy WindowDefinition where
2282+ orderBy sortsR (WindowDefinition parts sortsL)
2283+ = WindowDefinition parts (sortsL ++ sortsR)
2284+
2285+instance RenderSQL (WindowDefinition outer commons schemas from grp params) where
2286+ renderSQL (WindowDefinition part ord) =
2287+ renderPartitionByClause part <> renderOrderByClause ord
2288+ where
2289+ renderPartitionByClause = \case
2290+ Nil -> ""
2291+ parts -> "PARTITION" <+> "BY" <+> renderCommaSeparated renderExpression parts
2292+ renderOrderByClause = \case
2293+ [] -> ""
2294+ srts -> " ORDER" <+> "BY"
2295+ <+> commaSeparated (renderSQL <$> srts)
2296+
2297+{- |
2298+The `partitionBy` clause within `Squeal.PostgreSQL.Query.Over` divides the rows into groups,
2299+or partitions, that share the same values of the `partitionBy` `Expression`(s).
2300+For each row, the window function is computed across the rows that fall into
2301+the same partition as the current row.
2302+-}
2303+partitionBy
2304+ :: SListI bys
2305+ => NP (Expression outer commons grp schemas params from) bys
2306+ -> WindowDefinition outer commons grp schemas params from
2307+partitionBy bys = WindowDefinition bys []
2308+
2309+{- |
2310+A window function performs a calculation across a set of table rows
2311+that are somehow related to the current row. This is comparable to the type
2312+of calculation that can be done with an aggregate function.
2313+However, window functions do not cause rows to become grouped into a single
2314+output row like non-window aggregate calls would.
2315+Instead, the rows retain their separate identities.
2316+Behind the scenes, the window function is able to access more than
2317+just the current row of the query result.
2318+-}
2319+newtype WindowFunction
2320+ (outer :: FromType)
2321+ (commons :: FromType)
2322+ (grp :: Grouping)
2323+ (schemas :: SchemasType)
2324+ (params :: [NullityType])
2325+ (from :: FromType)
2326+ (ty :: NullityType)
2327+ = UnsafeWindowFunction { renderWindowFunction :: ByteString }
2328+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
2329+
2330+instance RenderSQL (WindowFunction outer commons grp schemas params from ty) where
2331+ renderSQL = renderWindowFunction
2332+
2333+type WinFun0 x
2334+ = forall outer commons grp schemas params from
2335+ . WindowFunction outer commons grp schemas params from x
2336+ -- ^ cannot reference aliases
2337+
2338+type WinFun1 x y
2339+ = forall outer commons grp schemas params from
2340+ . Expression outer commons grp schemas params from x
2341+ -- ^ input
2342+ -> WindowFunction outer commons grp schemas params from y
2343+ -- ^ output
2344+
2345+type WinFunHet xs y
2346+ = forall outer commons grp schemas params from
2347+ . NP (Expression outer commons grp schemas params from) xs
2348+ -- ^ inputs
2349+ -> WindowFunction outer commons grp schemas params from y
2350+ -- ^ output
2351+
2352+unsafeWindowFunction1 :: ByteString -> WinFun1 x y
2353+unsafeWindowFunction1 fun x
2354+ = UnsafeWindowFunction $ fun <> parenthesized (renderSQL x)
2355+
2356+unsafeWindowFunctionHet :: SOP.SListI xs => ByteString -> WinFunHet xs y
2357+unsafeWindowFunctionHet fun xs = UnsafeWindowFunction $ fun <>
2358+ parenthesized (renderCommaSeparated renderSQL xs)
2359+
2360+{- | rank of the current row with gaps; same as `rowNumber` of its first peer
2361+>>> printSQL rank
2362+rank()
2363+-}
2364+rank :: WinFun0 ('NotNull 'PGint8)
2365+rank = UnsafeWindowFunction "rank()"
2366+
2367+{- | number of the current row within its partition, counting from 1
2368+>>> printSQL rowNumber
2369+row_number()
2370+-}
2371+rowNumber :: WinFun0 ('NotNull 'PGint8)
2372+rowNumber = UnsafeWindowFunction "row_number()"
2373+
2374+{- | rank of the current row without gaps; this function counts peer groups
2375+>>> printSQL denseRank
2376+dense_rank()
2377+-}
2378+denseRank :: WinFun0 ('NotNull 'PGint8)
2379+denseRank = UnsafeWindowFunction "dense_rank()"
2380+
2381+{- | relative rank of the current row: (rank - 1) / (total partition rows - 1)
2382+>>> printSQL percentRank
2383+percent_rank()
2384+-}
2385+percentRank :: WinFun0 ('NotNull 'PGfloat8)
2386+percentRank = UnsafeWindowFunction "percent_rank()"
2387+
2388+{- | cumulative distribution: (number of partition rows
2389+preceding or peer with current row) / total partition rows
2390+>>> printSQL cumeDist
2391+cume_dist()
2392+-}
2393+cumeDist :: WinFun0 ('NotNull 'PGfloat8)
2394+cumeDist = UnsafeWindowFunction "cume_dist()"
2395+
2396+{- | integer ranging from 1 to the argument value,
2397+dividing the partition as equally as possible
2398+-}
2399+ntile :: WinFun1 ('NotNull 'PGint4) ('NotNull 'PGint4)
2400+ntile = unsafeWindowFunction1 "ntile"
2401+
2402+{- | returns value evaluated at the row that is offset rows before the current
2403+row within the partition; if there is no such row, instead return default
2404+(which must be of the same type as value). Both offset and default are
2405+evaluated with respect to the current row.
2406+-}
2407+lag :: WinFunHet '[ty, 'NotNull 'PGint4, ty] ty
2408+lag = unsafeWindowFunctionHet "lag"
2409+
2410+{- | returns value evaluated at the row that is offset rows after the current
2411+row within the partition; if there is no such row, instead return default
2412+(which must be of the same type as value). Both offset and default are
2413+evaluated with respect to the current row.
2414+-}
2415+lead :: WinFunHet '[ty, 'NotNull 'PGint4, ty] ty
2416+lead = unsafeWindowFunctionHet "lag"
2417+
2418+{- | returns value evaluated at the row that is the
2419+first row of the window frame
2420+-}
2421+firstValue :: WinFun1 ty ty
2422+firstValue = unsafeWindowFunction1 "first_value"
2423+
2424+{- | returns value evaluated at the row that is the
2425+last row of the window frame
2426+-}
2427+lastValue :: WinFun1 ty ty
2428+lastValue = unsafeWindowFunction1 "last_value"
2429+
2430+{- | returns value evaluated at the row that is the nth
2431+row of the window frame (counting from 1); null if no such row
2432+-}
2433+nthValue :: WinFunHet '[null ty, 'NotNull 'PGint4] ('Null ty)
2434+nthValue = unsafeWindowFunctionHet "nth_value"
2435+
2436+{-|
2437+Create date from year, month and day fields
2438+
2439+>>> printSQL (makeDate (1984 :* 7 *: 3))
2440+make_date(1984, 7, 3)
2441+-}
2442+makeDate :: FunctionHet
2443+ '[ null 'PGint4, null 'PGint4, null 'PGint4 ]
2444+ ( null 'PGdate )
2445+makeDate = unsafeFunctionHet "make_date"
2446+
2447+{-|
2448+Create time from hour, minute and seconds fields
2449+
2450+>>> printSQL (makeTime (8 :* 15 *: 23.5))
2451+make_time(8, 15, 23.5)
2452+-}
2453+makeTime :: FunctionHet
2454+ '[ null 'PGint4, null 'PGint4, null 'PGfloat8 ]
2455+ ( null 'PGtime )
2456+makeTime = unsafeFunctionHet "make_time"
2457+
2458+{-|
2459+Create timestamp from year, month, day, hour, minute and seconds fields
2460+
2461+>>> printSQL (makeTimestamp (2013 :* 7 :* 15 :* 8 :* 15 *: 23.5))
2462+make_timestamp(2013, 7, 15, 8, 15, 23.5)
2463+-}
2464+makeTimestamp :: FunctionHet
2465+ '[ null 'PGint4, null 'PGint4, null 'PGint4
2466+ , null 'PGint4, null 'PGint4, null 'PGfloat8 ]
2467+ ( null 'PGtimestamp )
2468+makeTimestamp = unsafeFunctionHet "make_timestamp"
2469+
2470+{-|
2471+Create timestamp with time zone from
2472+year, month, day, hour, minute and seconds fields;
2473+the current time zone is used
2474+
2475+>>> printSQL (makeTimestamptz (2013 :* 7 :* 15 :* 8 :* 15 *: 23.5))
2476+make_timestamptz(2013, 7, 15, 8, 15, 23.5)
2477+-}
2478+makeTimestamptz :: FunctionHet
2479+ '[ null 'PGint4, null 'PGint4, null 'PGint4
2480+ , null 'PGint4, null 'PGint4, null 'PGfloat8 ]
2481+ ( null 'PGtimestamptz )
2482+makeTimestamptz = unsafeFunctionHet "make_timestamptz"
2483+
2484+{-|
2485+Affine space operations on time types.
2486+-}
2487+class TimeOp time diff | time -> diff where
2488+ {-|
2489+ >>> printSQL (makeDate (1984 :* 7 *: 3) !+ 365)
2490+ (make_date(1984, 7, 3) + 365)
2491+ -}
2492+ (!+) :: Operator (null time) (null diff) (null time)
2493+ (!+) = unsafeBinaryOp "+"
2494+ {-|
2495+ >>> printSQL (365 +! makeDate (1984 :* 7 *: 3))
2496+ (365 + make_date(1984, 7, 3))
2497+ -}
2498+ (+!) :: Operator (null diff) (null time) (null time)
2499+ (+!) = unsafeBinaryOp "+"
2500+ {-|
2501+ >>> printSQL (makeDate (1984 :* 7 *: 3) !- 365)
2502+ (make_date(1984, 7, 3) - 365)
2503+ -}
2504+ (!-) :: Operator (null time) (null diff) (null time)
2505+ (!-) = unsafeBinaryOp "-"
2506+ {-|
2507+ >>> printSQL (makeDate (1984 :* 7 *: 3) !-! currentDate)
2508+ (make_date(1984, 7, 3) - CURRENT_DATE)
2509+ -}
2510+ (!-!) :: Operator (null time) (null time) (null diff)
2511+ (!-!) = unsafeBinaryOp "-"
2512+instance TimeOp 'PGtimestamp 'PGinterval
2513+instance TimeOp 'PGtimestamptz 'PGinterval
2514+instance TimeOp 'PGtime 'PGinterval
2515+instance TimeOp 'PGtimetz 'PGinterval
2516+instance TimeOp 'PGinterval 'PGinterval
2517+instance TimeOp 'PGdate 'PGint4
2518+infixl 6 !+
2519+infixl 6 +!
2520+infixl 6 !-
2521+infixl 6 !-!
2522+
2523+data TimeUnit
2524+ = Years | Months | Weeks | Days
2525+ | Hours | Minutes | Seconds
2526+ | Microseconds | Milliseconds
2527+ | Decades | Centuries | Millennia
2528+ deriving (Eq, Ord, Show, Read, Enum, GHC.Generic)
2529+instance RenderSQL TimeUnit where
2530+ renderSQL = \case
2531+ Years -> "years"
2532+ Months -> "months"
2533+ Weeks -> "weeks"
2534+ Days -> "days"
2535+ Hours -> "hours"
2536+ Minutes -> "minutes"
2537+ Seconds -> "seconds"
2538+ Microseconds -> "microseconds"
2539+ Milliseconds -> "milliseconds"
2540+ Decades -> "decades"
2541+ Centuries -> "centuries"
2542+ Millennia -> "millennia"
2543+
2544+-- | >>> printSQL $ interval_ 7 Days
2545+-- (INTERVAL '7.0 days')
2546+interval_ :: Double -> TimeUnit -> Expr (null 'PGinterval)
2547+interval_ num unit = UnsafeExpression . parenthesized $ "INTERVAL" <+>
2548+ "'" <> fromString (show num) <+> renderSQL unit <> "'"
2549+
2550+class Literal hask where
2551+ literal :: hask -> Expr (null (PG hask))
2552+instance JSON.ToJSON hask => Literal (Json hask) where
2553+ literal = cast json . UnsafeExpression
2554+ . singleQuotedUtf8 . toStrict . JSON.encode . getJson
2555+instance JSON.ToJSON hask => Literal (Jsonb hask) where
2556+ literal = cast jsonb . UnsafeExpression
2557+ . singleQuotedUtf8 . toStrict . JSON.encode . getJsonb
2558+instance Literal Char where
2559+ literal chr = cast (char @1) . UnsafeExpression $
2560+ "E\'" <> fromString (escape chr) <> "\'"
2561+instance Literal String where literal = fromString
2562+instance Literal Int16 where literal = fromIntegral
2563+instance Literal Int32 where literal = fromIntegral
2564+instance Literal Int64 where literal = fromIntegral
2565+instance Literal Float where literal = fromRational . toRational
2566+instance Literal Double where literal = fromRational . toRational
2567+instance Literal Text where literal = fromString . Text.unpack
2568+instance Literal Lazy.Text where literal = fromString . Lazy.Text.unpack