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