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