@@ -1,7 +1,7 @@
11{-|
22Module: Squeal.PostgreSQL.Query
33Description: Squeal queries
4−Copyright: (c) Eitan Chatav, 2019
4+Copyright: (c) Eitan Chatav, 2017
55Maintainer: eitan@morphism.tech
66Stability: experimental
77
@@ -17,12 +17,8 @@ Squeal queries.
1717 , GeneralizedNewtypeDeriving
1818 , LambdaCase
1919 , MultiParamTypeClasses
20− , OverloadedLabels
2120 , OverloadedStrings
22− , QuantifiedConstraints
23− , ScopedTypeVariables
2421 , StandaloneDeriving
25− , TypeApplications
2622 , TypeFamilies
2723 , TypeInType
2824 , TypeOperators
@@ -32,58 +28,104 @@ Squeal queries.
3228
3329module Squeal.PostgreSQL.Query
3430 ( -- * Queries
35− Query_
36− , Query (..)
37− -- ** Select
38− , select
39− , select_
40− , selectDistinct
41− , selectDistinct_
42− , selectDistinctOn
43− , selectDistinctOn_
44− , Selection (..)
45− -- ** Values
46− , values
47− , values_
48− -- ** Set Operations
31+ Query (UnsafeQuery, renderQuery)
4932 , union
5033 , unionAll
5134 , intersect
5235 , intersectAll
5336 , except
5437 , exceptAll
55− -- ** With
56− , With (..)
57− , CommonTableExpression (..)
58− , withRecursive
38+ -- * Select
39+ , select
40+ , selectDistinct
41+ , selectStar
42+ , selectDistinctStar
43+ , selectDotStar
44+ , selectDistinctDotStar
45+ , values
46+ , values_
5947 -- * Table Expressions
6048 , TableExpression (..)
49+ , renderTableExpression
6150 , from
6251 , where_
6352 , groupBy
6453 , having
54+ , orderBy
6555 , limit
6656 , offset
67− -- * From Clauses
57+ -- ** Json
58+ , jsonEach
59+ , jsonbEach
60+ , jsonEachAsText
61+ , jsonbEachAsText
62+ , jsonObjectKeys
63+ , jsonbObjectKeys
64+ , jsonPopulateRecord
65+ , jsonbPopulateRecord
66+ , jsonPopulateRecordSet
67+ , jsonbPopulateRecordSet
68+ , jsonToRecord
69+ , jsonbToRecord
70+ , jsonToRecordSet
71+ , jsonbToRecordSet
72+ -- * From
6873 , FromClause (..)
6974 , table
7075 , subquery
7176 , view
72− , common
7377 , crossJoin
7478 , innerJoin
7579 , leftOuterJoin
7680 , rightOuterJoin
7781 , fullOuterJoin
7882 -- * Grouping
79− , By (..)
80− , GroupByClause (..)
81− , HavingClause (..)
83+ , By (By1, By2)
84+ , renderBy
85+ , GroupByClause (NoGroups, Group)
86+ , renderGroupByClause
87+ , HavingClause (NoHaving, Having)
88+ , renderHavingClause
89+ -- * Sorting
90+ , SortExpression (..)
91+ , renderSortExpression
92+ -- * Subquery expressions
93+ , in_
94+ , rowIn
95+ , allEq
96+ , rowAllEq
97+ , anyEq
98+ , rowAnyEq
99+ , allNeq
100+ , rowAllNeq
101+ , anyNeq
102+ , rowAnyNeq
103+ , allLt
104+ , rowAllLt
105+ , anyLt
106+ , rowAnyLt
107+ , allLte
108+ , rowAllLte
109+ , anyLte
110+ , rowAnyLte
111+ , allGt
112+ , rowAllGt
113+ , anyGt
114+ , rowAnyGt
115+ , allGte
116+ , rowAllGte
117+ , anyGte
118+ , rowAnyGte
119+ -- * With
120+ , With (with)
121+ , CommonTableExpression (..)
122+ , renderCommonTableExpression
123+ , renderCommonTableExpressions
82124 ) where
83125
84126import Control.DeepSeq
85127import Data.ByteString (ByteString)
86−import Data.Kind (Type)
128+import Data.Monoid hiding (All)
87129import Data.String
88130import Data.Word
89131import Generics.SOP hiding (from)
@@ -91,76 +133,25 @@ import GHC.TypeLits
91133
92134import qualified GHC.Generics as GHC
93135
94−import Squeal.PostgreSQL.Alias
95136import Squeal.PostgreSQL.Expression
96−import Squeal.PostgreSQL.Expression.Logic
97−import Squeal.PostgreSQL.Expression.Sort
98−import Squeal.PostgreSQL.Expression.Window
99−import Squeal.PostgreSQL.List
100−import Squeal.PostgreSQL.PG
101137import Squeal.PostgreSQL.Render
102138import Squeal.PostgreSQL.Schema
103139
104−-- $setup
105−-- >>> import Squeal.PostgreSQL
106−-- >>> import Data.Int (Int32, Int64)
107−-- >>> import Data.Monoid (Sum (..))
108−-- >>> import Data.Text (Text)
109−-- >>> import qualified Generics.SOP as SOP
110−
111−{- |
112−The process of retrieving or the command to retrieve data from
113−a database is called a `Query`.
114−
115−The general `Query` type is parameterized by
116−
117−* @outer :: FromType@ - outer scope for a correlated subquery,
118−* @commons :: FromType@ - scope for all `common` table expressions,
119−* @schemas :: SchemasType@ - scope for all `table`s and `view`s,
120−* @params :: [NullityType]@ - scope for all `Squeal.Expression.Parameter.parameter`s,
121−* @row :: RowType@ - return type of the `Query`.
122−-}
123−newtype Query
124− (outer :: FromType)
125− (commons :: FromType)
126− (schemas :: SchemasType)
127− (params :: [NullityType])
128− (row :: RowType)
129− = UnsafeQuery { renderQuery :: ByteString }
130− deriving (GHC.Generic,Show,Eq,Ord,NFData)
131−instance RenderSQL (Query outer commons schemas params row) where renderSQL = renderQuery
132−
133140{- |
134−The top level `Query_` type is parameterized by a @schemas@ `SchemasType`,
135−against which the query is type-checked, an input @parameters@ Haskell `Type`,
136−and an ouput row Haskell `Type`.
137−
138−A top-level query can be run
139−using `Squeal.PostgreSQL.PQ.runQueryParams`, or if @parameters = ()@
140−using `Squeal.PostgreSQL.PQ.runQuery`.
141−
142−Generally, @parameters@ will be a Haskell tuple or record whose entries
143−may be referenced using positional
144−`Squeal.PostgreSQL.Expression.Parameter.parameter`s and @row@ will be a
145−Haskell record, whose entries will be targeted using overloaded labels.
146−
147−Let's see some examples of queries.
148−
149−>>> :set -XDeriveAnyClass -XDerivingStrategies
150−>>> :{
151−data Row a b = Row { col1 :: a, col2 :: b }
152− deriving stock (GHC.Generic)
153− deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
154−:}
141+The process of retrieving or the command to retrieve data from a database
142+is called a `Query`. The `select`, `selectStar`, `selectDotStar`,
143+`selectDistinct`, `selectDistinctStar` and `selectDistinctDotStar` commands
144+are used to specify queries.
155145
156146simple query:
157147
158−>>> type Columns = '["col1" ::: 'NoDef :=> 'NotNull 'PGint4, "col2" ::: 'NoDef :=> 'NotNull 'PGint4]
159−>>> type Schema = '["tab" ::: 'Table ('[] :=> Columns)]
160148>>> :{
161149let
162− query :: Query_ (Public Schema) () (Row Int32 Int32)
163− query = select Star (from (table #tab))
150+ query :: Query
151+ '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
152+ '[]
153+ '["col" ::: 'Null 'PGint4]
154+ query = selectStar (from (table #tab))
164155in printSQL query
165156:}
166157SELECT * FROM "tab" AS "tab"
@@ -169,22 +160,34 @@ restricted query:
169160
170161>>> :{
171162let
172− query :: Query_ (Public Schema) () (Row Int32 Int32)
163+ query :: Query
164+ '[ "tab" ::: 'Table ('[] :=>
165+ '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
166+ , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ])]
167+ '[]
168+ '[ "sum" ::: 'NotNull 'PGint4
169+ , "col1" ::: 'NotNull 'PGint4 ]
173170 query =
174− select_ ((#col1 + #col2) `as` #col1 :* #col1 `as` #col2)
171+ select
172+ ((#col1 + #col2) `as` #sum :* #col1)
175173 ( from (table #tab)
176174 & where_ (#col1 .> #col2)
177175 & where_ (#col2 .> 0) )
178176in printSQL query
179177:}
180−SELECT ("col1" + "col2") AS "col1", "col1" AS "col2" FROM "tab" AS "tab" WHERE (("col1" > "col2") AND ("col2" > 0))
178+SELECT ("col1" + "col2") AS "sum", "col1" AS "col1" FROM "tab" AS "tab" WHERE (("col1" > "col2") AND ("col2" > 0))
181179
182180subquery:
183181
184182>>> :{
185183let
186− query :: Query_ (Public Schema) () (Row Int32 Int32)
187− query = select Star (from (subquery (select Star (from (table #tab)) `as` #sub)))
184+ query :: Query
185+ '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
186+ '[]
187+ '["col" ::: 'Null 'PGint4]
188+ query =
189+ selectStar
190+ (from (subquery (selectStar (from (table #tab)) `as` #sub)))
188191in printSQL query
189192:}
190193SELECT * FROM (SELECT * FROM "tab" AS "tab") AS "sub"
@@ -193,8 +196,12 @@ limits and offsets:
193196
194197>>> :{
195198let
196− query :: Query_ (Public Schema) () (Row Int32 Int32)
197− query = select Star (from (table #tab) & limit 100 & offset 2 & limit 50 & offset 2)
199+ query :: Query
200+ '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
201+ '[]
202+ '["col" ::: 'Null 'PGint4]
203+ query = selectStar
204+ (from (table #tab) & limit 100 & offset 2 & limit 50 & offset 2)
198205in printSQL query
199206:}
200207SELECT * FROM "tab" AS "tab" LIMIT 50 OFFSET 4
@@ -203,80 +210,85 @@ parameterized query:
203210
204211>>> :{
205212let
206− query :: Query_ (Public Schema) (Only Int32) (Row Int32 Int32)
207− query = select Star (from (table #tab) & where_ (#col1 .> param @1))
213+ query :: Query
214+ '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGfloat8])]
215+ '[ 'NotNull 'PGfloat8]
216+ '["col" ::: 'NotNull 'PGfloat8]
217+ query = selectStar
218+ (from (table #tab) & where_ (#col .> param @1))
208219in printSQL query
209220:}
210−SELECT * FROM "tab" AS "tab" WHERE ("col1" > ($1 :: int4))
221+SELECT * FROM "tab" AS "tab" WHERE ("col" > ($1 :: float8))
211222
212223aggregation query:
213224
214225>>> :{
215226let
216− query :: Query_ (Public Schema) () (Row Int64 Int32)
227+ query :: Query
228+ '[ "tab" ::: 'Table ('[] :=>
229+ '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
230+ , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ])]
231+ '[]
232+ '[ "sum" ::: 'NotNull 'PGint4
233+ , "col1" ::: 'NotNull 'PGint4 ]
217234 query =
218− select_ ((fromNull 0 (sum_ (All #col2))) `as` #col1 :* #col1 `as` #col2)
235+ select (sum_ #col2 `as` #sum :* #col1)
219236 ( from (table (#tab `as` #table1))
220237 & groupBy #col1
221− & having (sum_ (Distinct #col2) .> 1) )
238+ & having (#col1 + sum_ #col2 .> 1) )
222239in printSQL query
223240:}
224−SELECT COALESCE(sum(ALL "col2"), 0) AS "col1", "col1" AS "col2" FROM "tab" AS "table1" GROUP BY "col1" HAVING (sum(DISTINCT "col2") > 1)
241+SELECT sum("col2") AS "sum", "col1" AS "col1" FROM "tab" AS "table1" GROUP BY "col1" HAVING (("col1" + sum("col2")) > 1)
225242
226243sorted query:
227244
228245>>> :{
229246let
230− query :: Query_ (Public Schema) () (Row Int32 Int32)
231− query = select Star (from (table #tab) & orderBy [#col1 & Asc])
247+ query :: Query
248+ '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
249+ '[]
250+ '["col" ::: 'Null 'PGint4]
251+ query = selectStar
252+ (from (table #tab) & orderBy [#col & AscNullsFirst])
232253in printSQL query
233254:}
234−SELECT * FROM "tab" AS "tab" ORDER BY "col1" ASC
255+SELECT * FROM "tab" AS "tab" ORDER BY "col" ASC NULLS FIRST
235256
236257joins:
237258
238−>>> :{
239−type OrdersColumns =
240− '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
241− , "price" ::: 'NoDef :=> 'NotNull 'PGfloat4
242− , "customer_id" ::: 'NoDef :=> 'NotNull 'PGint4
243− , "shipper_id" ::: 'NoDef :=> 'NotNull 'PGint4 ]
244−:}
245−
246−>>> :{
247−type OrdersConstraints =
248− '["pk_orders" ::: PrimaryKey '["id"]
249− ,"fk_customers" ::: ForeignKey '["customer_id"] "customers" '["id"]
250− ,"fk_shippers" ::: ForeignKey '["shipper_id"] "shippers" '["id"] ]
251−:}
252−
253−>>> type NamesColumns = '["id" ::: 'NoDef :=> 'NotNull 'PGint4, "name" ::: 'NoDef :=> 'NotNull 'PGtext]
254−>>> type CustomersConstraints = '["pk_customers" ::: PrimaryKey '["id"]]
255−>>> type ShippersConstraints = '["pk_shippers" ::: PrimaryKey '["id"]]
256−>>> :{
257−type OrdersSchema =
258− '[ "orders" ::: 'Table (OrdersConstraints :=> OrdersColumns)
259− , "customers" ::: 'Table (CustomersConstraints :=> NamesColumns)
260− , "shippers" ::: 'Table (ShippersConstraints :=> NamesColumns) ]
261−:}
262−
263−>>> :{
264−data Order = Order
265− { price :: Float
266− , customerName :: Text
267− , shipperName :: Text
268− } deriving GHC.Generic
269−instance SOP.Generic Order
270−instance SOP.HasDatatypeInfo Order
271−:}
272−
259+>>> :set -XFlexibleContexts
273260>>> :{
274261let
275− query :: Query_ (Public OrdersSchema) () Order
276− query = select_
277− ( #o ! #price `as` #price :*
278− #c ! #name `as` #customerName :*
279− #s ! #name `as` #shipperName )
262+ query :: Query
263+ '[ "orders" ::: 'Table (
264+ '["pk_orders" ::: PrimaryKey '["id"]
265+ ,"fk_customers" ::: ForeignKey '["customer_id"] "customers" '["id"]
266+ ,"fk_shippers" ::: ForeignKey '["shipper_id"] "shippers" '["id"]] :=>
267+ '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
268+ , "price" ::: 'NoDef :=> 'NotNull 'PGfloat4
269+ , "customer_id" ::: 'NoDef :=> 'NotNull 'PGint4
270+ , "shipper_id" ::: 'NoDef :=> 'NotNull 'PGint4
271+ ])
272+ , "customers" ::: 'Table (
273+ '["pk_customers" ::: PrimaryKey '["id"]] :=>
274+ '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
275+ , "name" ::: 'NoDef :=> 'NotNull 'PGtext
276+ ])
277+ , "shippers" ::: 'Table (
278+ '["pk_shippers" ::: PrimaryKey '["id"]] :=>
279+ '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
280+ , "name" ::: 'NoDef :=> 'NotNull 'PGtext
281+ ])
282+ ]
283+ '[]
284+ '[ "order_price" ::: 'NotNull 'PGfloat4
285+ , "customer_name" ::: 'NotNull 'PGtext
286+ , "shipper_name" ::: 'NotNull 'PGtext
287+ ]
288+ query = select
289+ ( #o ! #price `as` #order_price :*
290+ #c ! #name `as` #customer_name :*
291+ #s ! #name `as` #shipper_name )
280292 ( from (table (#orders `as` #o)
281293 & innerJoin (table (#customers `as` #c))
282294 (#o ! #customer_id .== #c ! #id)
@@ -284,38 +296,34 @@ let
284296 (#o ! #shipper_id .== #s ! #id)) )
285297in printSQL query
286298:}
287−SELECT "o"."price" AS "price", "c"."name" AS "customerName", "s"."name" AS "shipperName" FROM "orders" AS "o" INNER JOIN "customers" AS "c" ON ("o"."customer_id" = "c"."id") INNER JOIN "shippers" AS "s" ON ("o"."shipper_id" = "s"."id")
299+SELECT "o"."price" AS "order_price", "c"."name" AS "customer_name", "s"."name" AS "shipper_name" FROM "orders" AS "o" INNER JOIN "customers" AS "c" ON ("o"."customer_id" = "c"."id") INNER JOIN "shippers" AS "s" ON ("o"."shipper_id" = "s"."id")
288300
289301self-join:
290302
291303>>> :{
292304let
293− query :: Query_ (Public Schema) () (Row Int32 Int32)
294− query = select
295− (#t1 & DotStar)
305+ query :: Query
306+ '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
307+ '[]
308+ '["col" ::: 'Null 'PGint4]
309+ query = selectDotStar #t1
296310 (from (table (#tab `as` #t1) & crossJoin (table (#tab `as` #t2))))
297311in printSQL query
298312:}
299313SELECT "t1".* FROM "tab" AS "t1" CROSS JOIN "tab" AS "t2"
300314
301−value queries:
302−
303−>>> :{
304−let
305− query :: Query_ schemas () (Row String Bool)
306− query = values
307− ("true" `as` #col1 :* true `as` #col2)
308− ["false" `as` #col1 :* false `as` #col2]
309−in printSQL query
310−:}
311−SELECT * FROM (VALUES (E'true', TRUE), (E'false', FALSE)) AS t ("col1", "col2")
312−
313315set operations:
314316
315317>>> :{
316318let
317− query :: Query_ (Public Schema) () (Row Int32 Int32)
318− query = select Star (from (table #tab)) `unionAll` select Star (from (table #tab))
319+ query :: Query
320+ '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
321+ '[]
322+ '["col" ::: 'Null 'PGint4]
323+ query =
324+ selectStar (from (table #tab))
325+ `unionAll`
326+ selectStar (from (table #tab))
319327in printSQL query
320328:}
321329(SELECT * FROM "tab" AS "tab") UNION ALL (SELECT * FROM "tab" AS "tab")
@@ -324,305 +332,173 @@ with queries:
324332
325333>>> :{
326334let
327− query :: Query_ (Public Schema) () (Row Int32 Int32)
335+ query :: Query
336+ '[ "t1" ::: 'View
337+ '[ "c1" ::: 'NotNull 'PGtext
338+ , "c2" ::: 'NotNull 'PGtext] ]
339+ '[]
340+ '[ "c1" ::: 'NotNull 'PGtext
341+ , "c2" ::: 'NotNull 'PGtext ]
328342 query = with (
329− select Star (from (table #tab)) `as` #cte1 :>>
330− select Star (from (common #cte1)) `as` #cte2
331− ) (select Star (from (common #cte2)))
332−in printSQL query
333−:}
334−WITH "cte1" AS (SELECT * FROM "tab" AS "tab"), "cte2" AS (SELECT * FROM "cte1" AS "cte1") SELECT * FROM "cte2" AS "cte2"
335−
336−window function queries
337−
338−>>> :{
339−let
340− query :: Query_ (Public Schema) () (Row Int32 Int64)
341− query = select
342− (#col1 & Also (rank `as` #col2 `Over` (partitionBy #col1 & orderBy [#col2 & Asc])))
343− (from (table #tab))
343+ selectStar (from (view #t1)) `as` #t2 :>>
344+ selectStar (from (view #t2)) `as` #t3
345+ ) (selectStar (from (view #t3)))
344346in printSQL query
345347:}
346−SELECT "col1" AS "col1", rank() OVER (PARTITION BY "col1" ORDER BY "col2" ASC) AS "col2" FROM "tab" AS "tab"
347−
348−correlated subqueries
349−
350−>>> :{
351−let
352− query :: Query_ (Public Schema) () (Only Int32)
353− query =
354− select (#col1 `as` #fromOnly) (from (table (#tab `as` #t1))
355− & where_ (exists (
356− select Star (from (table (#tab `as` #t2))
357− & where_ (#t2 ! #col2 .== #t1 ! #col1)))))
358−in printSQL query
359−:}
360−SELECT "col1" AS "fromOnly" FROM "tab" AS "t1" WHERE EXISTS (SELECT * FROM "tab" AS "t2" WHERE ("t2"."col2" = "t1"."col1"))
361−
348+WITH "t2" AS (SELECT * FROM "t1" AS "t1"), "t3" AS (SELECT * FROM "t2" AS "t2") SELECT * FROM "t3" AS "t3"
362349-}
363−type family Query_
364− (schemas :: SchemasType)
365− (parameters :: Type)
366− (row :: Type) where
367− Query_ schemas params row =
368− Query '[] '[] schemas (TuplePG params) (RowPG row)
350+newtype Query
351+ (schema :: SchemaType)
352+ (params :: [NullityType])
353+ (columns :: RowType)
354+ = UnsafeQuery { renderQuery :: ByteString }
355+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
356+instance RenderSQL (Query schema params columns) where renderSQL = renderQuery
369357
370358-- | The results of two queries can be combined using the set operation
371359-- `union`. Duplicate rows are eliminated.
372360union
373− :: Query outer commons schemas params columns
374− -> Query outer commons schemas params columns
375− -> Query outer commons schemas params columns
361+ :: Query schema params columns
362+ -> Query schema params columns
363+ -> Query schema params columns
376364q1 `union` q2 = UnsafeQuery $
377− parenthesized (renderSQL q1)
365+ parenthesized (renderQuery q1)
378366 <+> "UNION"
379− <+> parenthesized (renderSQL q2)
367+ <+> parenthesized (renderQuery q2)
380368
381369-- | The results of two queries can be combined using the set operation
382370-- `unionAll`, the disjoint union. Duplicate rows are retained.
383371unionAll
384− :: Query outer commons schemas params columns
385− -> Query outer commons schemas params columns
386− -> Query outer commons schemas params columns
372+ :: Query schema params columns
373+ -> Query schema params columns
374+ -> Query schema params columns
387375q1 `unionAll` q2 = UnsafeQuery $
388− parenthesized (renderSQL q1)
376+ parenthesized (renderQuery q1)
389377 <+> "UNION" <+> "ALL"
390− <+> parenthesized (renderSQL q2)
378+ <+> parenthesized (renderQuery q2)
391379
392380-- | The results of two queries can be combined using the set operation
393381-- `intersect`, the intersection. Duplicate rows are eliminated.
394382intersect
395− :: Query outer commons schemas params columns
396− -> Query outer commons schemas params columns
397− -> Query outer commons schemas params columns
383+ :: Query schema params columns
384+ -> Query schema params columns
385+ -> Query schema params columns
398386q1 `intersect` q2 = UnsafeQuery $
399− parenthesized (renderSQL q1)
387+ parenthesized (renderQuery q1)
400388 <+> "INTERSECT"
401− <+> parenthesized (renderSQL q2)
389+ <+> parenthesized (renderQuery q2)
402390
403391-- | The results of two queries can be combined using the set operation
404392-- `intersectAll`, the intersection. Duplicate rows are retained.
405393intersectAll
406− :: Query outer commons schemas params columns
407− -> Query outer commons schemas params columns
408− -> Query outer commons schemas params columns
394+ :: Query schema params columns
395+ -> Query schema params columns
396+ -> Query schema params columns
409397q1 `intersectAll` q2 = UnsafeQuery $
410− parenthesized (renderSQL q1)
398+ parenthesized (renderQuery q1)
411399 <+> "INTERSECT" <+> "ALL"
412− <+> parenthesized (renderSQL q2)
400+ <+> parenthesized (renderQuery q2)
413401
414402-- | The results of two queries can be combined using the set operation
415403-- `except`, the set difference. Duplicate rows are eliminated.
416404except
417− :: Query outer commons schemas params columns
418− -> Query outer commons schemas params columns
419− -> Query outer commons schemas params columns
405+ :: Query schema params columns
406+ -> Query schema params columns
407+ -> Query schema params columns
420408q1 `except` q2 = UnsafeQuery $
421− parenthesized (renderSQL q1)
409+ parenthesized (renderQuery q1)
422410 <+> "EXCEPT"
423− <+> parenthesized (renderSQL q2)
411+ <+> parenthesized (renderQuery q2)
424412
425413-- | The results of two queries can be combined using the set operation
426414-- `exceptAll`, the set difference. Duplicate rows are retained.
427415exceptAll
428− :: Query outer commons schemas params columns
429− -> Query outer commons schemas params columns
430− -> Query outer commons schemas params columns
416+ :: Query schema params columns
417+ -> Query schema params columns
418+ -> Query schema params columns
431419q1 `exceptAll` q2 = UnsafeQuery $
432− parenthesized (renderSQL q1)
420+ parenthesized (renderQuery q1)
433421 <+> "EXCEPT" <+> "ALL"
434− <+> parenthesized (renderSQL q2)
422+ <+> parenthesized (renderQuery q2)
435423
436424{-----------------------------------------
437425SELECT queries
438426-----------------------------------------}
439427
440−{- | The simplest kinds of `Selection` are `Star` and `DotStar` which
441−emits all columns that a `TableExpression` produces. A select `List`
442−is a list of `Expression`s. A `Selection` could be a list of
443−`WindowFunction`s `Over` `WindowDefinition`. `Additional` `Selection`s can
444−be selected with `Also`.
445−-}
446−data Selection outer commons grp schemas params from row where
447− Star
448− :: HasUnique tab from row
449− => Selection outer commons 'Ungrouped schemas params from row
450− -- ^ `HasUnique` table in the `FromClause`
451− DotStar
452− :: Has tab from row
453− => Alias tab
454− -- ^ `Has` table with `Alias`
455− -> Selection outer commons 'Ungrouped schemas params from row
456− List
457− :: SListI row
458− => NP (Aliased (Expression outer commons grp schemas params from)) row
459− -- ^ `NP` list of `Aliased` `Expression`s
460− -> Selection outer commons grp schemas params from row
461− Over
462− :: SListI row
463− => NP (Aliased (WindowFunction outer commons grp schemas params from)) row
464− -- ^ `NP` list of `Aliased` `WindowFunction`s
465− -> WindowDefinition outer commons grp schemas params from
466− -> Selection outer commons grp schemas params from row
467− Also
468− :: Selection outer commons grp schemas params from right
469− -- ^ `Additional` `Selection`
470− -> Selection outer commons grp schemas params from left
471− -> Selection outer commons grp schemas params from (Join left right)
472−instance Additional (Selection outer commons grp schemas params from) where
473− also = Also
474−instance (KnownSymbol col, row ~ '[col ::: ty])
475− => Aliasable col
476− (Expression outer commons grp schemas params from ty)
477− (Selection outer commons grp schemas params from row) where
478− expr `as` col = List (expr `as` col)
479−instance (Has tab (Join outer from) row0, Has col row0 ty, row1 ~ '[col ::: ty])
480− => IsQualified tab col
481− (Selection outer commons 'Ungrouped schemas params from row1) where
482− tab ! col = tab ! col `as` col
483−instance
484− ( Has tab (Join outer from) row0
485− , Has col row0 ty
486− , row1 ~ '[col ::: ty]
487− , GroupedBy tab col bys )
488− => IsQualified tab col
489− (Selection outer commons ('Grouped bys) schemas params from row1) where
490− tab ! col = tab ! col `as` col
491−instance (HasUnique tab (Join outer from) row0, Has col row0 ty, row1 ~ '[col ::: ty])
492− => IsLabel col
493− (Selection outer commons 'Ungrouped schemas params from row1) where
494− fromLabel = fromLabel @col `as` Alias
495−instance
496− ( HasUnique tab (Join outer from) row0
497− , Has col row0 ty
498− , row1 ~ '[col ::: ty]
499− , GroupedBy tab col bys )
500− => IsLabel col
501− (Selection outer commons ('Grouped bys) schemas params from row1) where
502− fromLabel = fromLabel @col `as` Alias
503−
504−instance RenderSQL (Selection outer commons grp schemas params from row) where
505− renderSQL = \case
506− List list -> renderCommaSeparated (renderAliased renderSQL) list
507− Star -> "*"
508− DotStar tab -> renderSQL tab <> ".*"
509− Also right left -> renderSQL left <> ", " <> renderSQL right
510− Over winFns winDef ->
511− let
512− renderOver
513− :: Aliased (WindowFunction outer commons grp schemas params from) field
514− -> ByteString
515− renderOver (winFn `As` col) = renderSQL winFn
516− <+> "OVER" <+> parenthesized (renderSQL winDef)
517− <+> "AS" <+> renderSQL col
518− in
519− renderCommaSeparated renderOver winFns
520−
521−instance IsString
522− (Selection outer commons grp schemas params from '["fromOnly" ::: 'NotNull 'PGtext]) where
523− fromString str = fromString str `as` Alias
524−
525428-- | the `TableExpression` in the `select` command constructs an intermediate
526429-- virtual table by possibly combining tables, views, eliminating rows,
527430-- grouping, etc. This table is finally passed on to processing by
528−-- the select list. The `Selection` determines which columns of
431+-- the select list. The select list determines which columns of
529432-- the intermediate table are actually output.
530433select
531− :: (SListI row, row ~ (x ': xs))
532− => Selection outer commons grp schemas params from row
533− -- ^ selection
534− -> TableExpression outer commons grp schemas params from
535− -- ^ intermediate virtual table
536− -> Query outer commons schemas params row
537−select selection tabexpr = UnsafeQuery $
538− "SELECT"
539− <+> renderSQL selection
540− <+> renderSQL tabexpr
541−
542−-- | Like `select` but takes an `NP` list of `Expression`s instead
543−-- of a general `Selection`.
544−select_
545− :: (SListI row, row ~ (x ': xs))
546− => NP (Aliased (Expression outer commons grp schemas params from)) row
434+ :: SListI columns
435+ => NP (Aliased (Expression schema from grouping params)) (column ': columns)
547436 -- ^ select list
548− -> TableExpression outer commons grp schemas params from
437+ -> TableExpression schema params from grouping
549438 -- ^ intermediate virtual table
550− -> Query outer commons schemas params row
551−select_ = select . List
439+ -> Query schema params (column ': columns)
440+select list rels = UnsafeQuery $
441+ "SELECT"
442+ <+> renderCommaSeparated (renderAliasedAs renderExpression) list
443+ <+> renderTableExpression rels
552444
553445-- | After the select list has been processed, the result table can
554446-- be subject to the elimination of duplicate rows using `selectDistinct`.
555447selectDistinct
556− :: (SListI columns, columns ~ (col ': cols))
557− => Selection outer commons grp schemas params from columns
558− -- ^ selection
559− -> TableExpression outer commons grp schemas params from
448+ :: SListI columns
449+ => NP (Aliased (Expression schema from 'Ungrouped params)) (column ': columns)
450+ -- ^ select list
451+ -> TableExpression schema params from 'Ungrouped
560452 -- ^ intermediate virtual table
561− -> Query outer commons schemas params columns
562−selectDistinct selection tabexpr = UnsafeQuery $
453+ -> Query schema params (column ': columns)
454+selectDistinct list rels = UnsafeQuery $
563455 "SELECT DISTINCT"
564− <+> renderSQL selection
565− <+> renderSQL tabexpr
566−
567−-- | Like `selectDistinct` but takes an `NP` list of `Expression`s instead
568−-- of a general `Selection`.
569−selectDistinct_
570− :: (SListI columns, columns ~ (col ': cols))
571− => NP (Aliased (Expression outer commons grp schemas params from)) columns
572− -- ^ select list
573− -> TableExpression outer commons grp schemas params from
456+ <+> renderCommaSeparated (renderAliasedAs renderExpression) list
457+ <+> renderTableExpression rels
458+
459+-- | The simplest kind of query is `selectStar` which emits all columns
460+-- that the table expression produces.
461+selectStar
462+ :: HasUnique table from columns
463+ => TableExpression schema params from 'Ungrouped
574464 -- ^ intermediate virtual table
575− -> Query outer commons schemas params columns
576−selectDistinct_ = selectDistinct . List
577−
578−{-|
579−`selectDistinctOn` keeps only the first row of each set of rows where
580−the given expressions evaluate to equal. The DISTINCT ON expressions are
581−interpreted using the same rules as for ORDER BY. ORDER BY is used to
582−ensure that the desired row appears first.
583−
584−The DISTINCT ON expression(s) must match the leftmost ORDER BY expression(s).
585−The ORDER BY clause will normally contain additional expression(s) that
586−determine the desired precedence of rows within each DISTINCT ON group.
587−
588−In order to guarantee they match and reduce redundancy, this function
589−will prepend the The DISTINCT ON expressions to the ORDER BY clause.
590−-}
591−selectDistinctOn
592− :: (SListI columns, columns ~ (col ': cols))
593− => [SortExpression outer commons grp schemas params from]
594− -- ^ DISTINCT ON expression(s) and prepended to ORDER BY clause
595− -> Selection outer commons grp schemas params from columns
596− -- ^ selection
597− -> TableExpression outer commons grp schemas params from
465+ -> Query schema params columns
466+selectStar rels = UnsafeQuery $ "SELECT" <+> "*" <+> renderTableExpression rels
467+
468+-- | A `selectDistinctStar` emits all columns that the table expression
469+-- produces and eliminates duplicate rows.
470+selectDistinctStar
471+ :: HasUnique table from columns
472+ => TableExpression schema params from 'Ungrouped
598473 -- ^ intermediate virtual table
599− -> Query outer commons schemas params columns
600−selectDistinctOn distincts selection tab = UnsafeQuery $
601− "SELECT DISTINCT ON"
602− <+> parenthesized (commaSeparated (renderDistinctOn <$> distincts))
603− <+> renderSQL selection
604− <+> renderSQL (tab {orderByClause = distincts <> orderByClause tab})
605− where
606− renderDistinctOn = \case
607− Asc expression -> renderSQL expression
608− Desc expression -> renderSQL expression
609− AscNullsFirst expression -> renderSQL expression
610− DescNullsFirst expression -> renderSQL expression
611− AscNullsLast expression -> renderSQL expression
612− DescNullsLast expression -> renderSQL expression
613−
614−-- | Like `selectDistinctOn` but takes an `NP` list of `Expression`s instead
615−-- of a general `Selection`.
616−selectDistinctOn_
617− :: (SListI columns, columns ~ (col ': cols))
618− => [SortExpression outer commons grp schemas params from]
619− -- ^ distinct on and return the first row in ordering
620− -> NP (Aliased (Expression outer commons grp schemas params from)) columns
621− -- ^ selection
622− -> TableExpression outer commons grp schemas params from
474+ -> Query schema params columns
475+selectDistinctStar rels = UnsafeQuery $
476+ "SELECT DISTINCT" <+> "*" <+> renderTableExpression rels
477+
478+-- | When working with multiple tables, it can also be useful to ask
479+-- for all the columns of a particular table, using `selectDotStar`.
480+selectDotStar
481+ :: Has table from columns
482+ => Alias table
483+ -- ^ particular virtual subtable
484+ -> TableExpression schema params from 'Ungrouped
485+ -- ^ intermediate virtual table
486+ -> Query schema params columns
487+selectDotStar rel tab = UnsafeQuery $
488+ "SELECT" <+> renderAlias rel <> ".*" <+> renderTableExpression tab
489+
490+-- | A `selectDistinctDotStar` asks for all the columns of a particular table,
491+-- and eliminates duplicate rows.
492+selectDistinctDotStar
493+ :: Has table from columns
494+ => Alias table
495+ -- ^ particular virtual table
496+ -> TableExpression schema params from 'Ungrouped
623497 -- ^ intermediate virtual table
624− -> Query outer commons schemas params columns
625−selectDistinctOn_ distincts = selectDistinctOn distincts . List
498+ -> Query schema params columns
499+selectDistinctDotStar rel tab = UnsafeQuery $
500+ "SELECT DISTINCT" <+> renderAlias rel <> ".*"
501+ <+> renderTableExpression tab
626502
627503-- | `values` computes a row value or set of row values
628504-- specified by value expressions. It is most commonly used
@@ -630,16 +506,16 @@ selectDistinctOn_ distincts = selectDistinctOn distincts . List
630506-- but it can be used on its own.
631507--
632508-- >>> type Row = '["a" ::: 'NotNull 'PGint4, "b" ::: 'NotNull 'PGtext]
633−-- >>> let query = values (1 `as` #a :* "one" `as` #b) [] :: Query outer commons schemas '[] Row
509+-- >>> let query = values (1 `as` #a :* "one" `as` #b) [] :: Query '[] '[] Row
634510-- >>> printSQL query
635511-- SELECT * FROM (VALUES (1, E'one')) AS t ("a", "b")
636512values
637513 :: SListI cols
638− => NP (Aliased (Expression outer commons 'Ungrouped schemas params '[] )) cols
639− -> [NP (Aliased (Expression outer commons 'Ungrouped schemas params '[] )) cols]
514+ => NP (Aliased (Expression schema '[] 'Ungrouped params)) cols
515+ -> [NP (Aliased (Expression schema '[] 'Ungrouped params)) cols]
640516 -- ^ When more than one row is specified, all the rows must
641517 -- must have the same number of elements
642− -> Query outer commons schemas params cols
518+ -> Query schema params cols
643519values rw rws = UnsafeQuery $ "SELECT * FROM"
644520 <+> parenthesized (
645521 "VALUES"
@@ -650,17 +526,17 @@ values rw rws = UnsafeQuery $ "SELECT * FROM"
650526 <+> parenthesized (renderCommaSeparated renderAliasPart rw)
651527 where
652528 renderAliasPart, renderValuePart
653− :: Aliased (Expression outer commons 'Ungrouped schemas params '[] ) ty -> ByteString
654− renderAliasPart (_ `As` name) = renderSQL name
655− renderValuePart (value `As` _) = renderSQL value
529+ :: Aliased (Expression schema '[] 'Ungrouped params) ty -> ByteString
530+ renderAliasPart (_ `As` name) = renderAlias name
531+ renderValuePart (value `As` _) = renderExpression value
656532
657533-- | `values_` computes a row value or set of row values
658534-- specified by value expressions.
659535values_
660536 :: SListI cols
661− => NP (Aliased (Expression outer commons 'Ungrouped schemas params '[] )) cols
537+ => NP (Aliased (Expression schema '[] 'Ungrouped params)) cols
662538 -- ^ one row of values
663− -> Query outer commons schemas params cols
539+ -> Query schema params cols
664540values_ rw = values rw []
665541
666542{-----------------------------------------
@@ -674,17 +550,15 @@ Table Expressions
674550-- to a table on disk, a so-called base table, but more complex expressions
675551-- can be used to modify or combine base tables in various ways.
676552data TableExpression
677− (outer :: FromType)
678− (commons :: FromType)
679− (grp :: Grouping)
680− (schemas :: SchemasType)
553+ (schema :: SchemaType)
681554 (params :: [NullityType])
682555 (from :: FromType)
556+ (grouping :: Grouping)
683557 = TableExpression
684− { fromClause :: FromClause outer commons schemas params from
558+ { fromClause :: FromClause schema params from
685559 -- ^ A table reference that can be a table name, or a derived table such
686560 -- as a subquery, a @JOIN@ construct, or complex combinations of these.
687− , whereClause :: [Condition outer commons 'Ungrouped schemas params from]
561+ , whereClause :: [Condition schema from 'Ungrouped params]
688562 -- ^ optional search coditions, combined with `.&&`. After the processing
689563 -- of the `fromClause` is done, each row of the derived virtual table
690564 -- is checked against the search condition. If the result of the
@@ -693,20 +567,20 @@ data TableExpression
693567 -- at least one column of the table generated in the `fromClause`;
694568 -- this is not required, but otherwise the WHERE clause will
695569 -- be fairly useless.
696− , groupByClause :: GroupByClause grp from
570+ , groupByClause :: GroupByClause from grouping
697571 -- ^ The `groupByClause` is used to group together those rows in a table
698572 -- that have the same values in all the columns listed. The order in which
699573 -- the columns are listed does not matter. The effect is to combine each
700574 -- set of rows having common values into one group row that represents all
701575 -- rows in the group. This is done to eliminate redundancy in the output
702576 -- and/or compute aggregates that apply to these groups.
703− , havingClause :: HavingClause outer commons grp schemas params from
577+ , havingClause :: HavingClause schema from grouping params
704578 -- ^ If a table has been grouped using `groupBy`, but only certain groups
705579 -- are of interest, the `havingClause` can be used, much like a
706580 -- `whereClause`, to eliminate groups from the result. Expressions in the
707581 -- `havingClause` can refer both to grouped expressions and to ungrouped
708582 -- expressions (which necessarily involve an aggregate function).
709− , orderByClause :: [SortExpression outer commons grp schemas params from]
583+ , orderByClause :: [SortExpression schema from grouping params]
710584 -- ^ The `orderByClause` is for optional sorting. When more than one
711585 -- `SortExpression` is specified, the later (right) values are used to sort
712586 -- rows that are equal according to the earlier (left) values.
@@ -716,69 +590,72 @@ data TableExpression
716590 -- will be returned (but possibly fewer, if the query itself yields
717591 -- fewer rows).
718592 , offsetClause :: [Word64]
719− -- ^ The `offsetClause` is combined with `Prelude.+` to give an offset count
593+ -- ^ The `offsetClause` is combined with `+` to give an offset count
720594 -- if nonempty. The offset count says to skip that many rows before
721595 -- beginning to return rows. The rows are skipped before the limit count
722596 -- is applied.
723− } deriving (GHC.Generic)
597+ }
724598
725599-- | Render a `TableExpression`
726−instance RenderSQL (TableExpression outer commons grp schemas params from) where
727− renderSQL
728− (TableExpression frm' whs' grps' hvs' srts' lims' offs') = mconcat
729− [ "FROM ", renderSQL frm'
730− , renderWheres whs'
731− , renderSQL grps'
732− , renderSQL hvs'
733− , renderOrderByClause srts'
734− , renderLimits lims'
735− , renderOffsets offs' ]
736− where
737− renderWheres = \case
738− [] -> ""
739− wh:[] -> " WHERE" <+> renderSQL wh
740− wh:whs -> " WHERE" <+> renderSQL (foldr (.&&) wh whs)
741− renderOrderByClause = \case
742− [] -> ""
743− srts -> " ORDER BY"
744− <+> commaSeparated (renderSQL <$> srts)
745− renderLimits = \case
746− [] -> ""
747− lims -> " LIMIT" <+> fromString (show (minimum lims))
748− renderOffsets = \case
749− [] -> ""
750− offs -> " OFFSET" <+> fromString (show (sum offs))
600+renderTableExpression
601+ :: TableExpression schema params from grouping
602+ -> ByteString
603+renderTableExpression
604+ (TableExpression frm' whs' grps' hvs' srts' lims' offs') = mconcat
605+ [ "FROM ", renderFromClause frm'
606+ , renderWheres whs'
607+ , renderGroupByClause grps'
608+ , renderHavingClause hvs'
609+ , renderOrderByClause srts'
610+ , renderLimits lims'
611+ , renderOffsets offs'
612+ ]
613+ where
614+ renderWheres = \case
615+ [] -> ""
616+ wh:[] -> " WHERE" <+> renderExpression wh
617+ wh:whs -> " WHERE" <+> renderExpression (foldr (.&&) wh whs)
618+ renderOrderByClause = \case
619+ [] -> ""
620+ srts -> " ORDER BY"
621+ <+> commaSeparated (renderSortExpression <$> srts)
622+ renderLimits = \case
623+ [] -> ""
624+ lims -> " LIMIT" <+> fromString (show (minimum lims))
625+ renderOffsets = \case
626+ [] -> ""
627+ offs -> " OFFSET" <+> fromString (show (sum offs))
751628
752629-- | A `from` generates a `TableExpression` from a table reference that can be
753630-- a table name, or a derived table such as a subquery, a JOIN construct,
754631-- or complex combinations of these. A `from` may be transformed by `where_`,
755−-- `groupBy`, `having`, `orderBy`, `limit` and `offset`, using the `&` operator
632+-- `group`, `having`, `orderBy`, `limit` and `offset`, using the `&` operator
756633-- to match the left-to-right sequencing of their placement in SQL.
757634from
758− :: FromClause outer commons schemas params from -- ^ table reference
759− -> TableExpression outer commons 'Ungrouped schemas params from
760−from tab = TableExpression tab [] noGroups NoHaving [] [] []
635+ :: FromClause schema params from -- ^ table reference
636+ -> TableExpression schema params from 'Ungrouped
637+from rels = TableExpression rels [] NoGroups NoHaving [] [] []
761638
762639-- | A `where_` is an endomorphism of `TableExpression`s which adds a
763640-- search condition to the `whereClause`.
764641where_
765− :: Condition outer commons 'Ungrouped schemas params from -- ^ filtering condition
766− -> TableExpression outer commons grp schemas params from
767− -> TableExpression outer commons grp schemas params from
642+ :: Condition schema from 'Ungrouped params -- ^ filtering condition
643+ -> TableExpression schema params from grouping
644+ -> TableExpression schema params from grouping
768645where_ wh rels = rels {whereClause = wh : whereClause rels}
769646
770647-- | A `groupBy` is a transformation of `TableExpression`s which switches
771−-- its `Grouping` from `Ungrouped` to `Grouped`. Use @groupBy Nil@ to perform
648+-- its `Grouping` from `Ungrouped` to `Grouped`. Use @group Nil@ to perform
772649-- a "grand total" aggregation query.
773650groupBy
774651 :: SListI bys
775652 => NP (By from) bys -- ^ grouped columns
776− -> TableExpression outer commons 'Ungrouped schemas params from
777− -> TableExpression outer commons ('Grouped bys) schemas params from
653+ -> TableExpression schema params from 'Ungrouped
654+ -> TableExpression schema params from ('Grouped bys)
778655groupBy bys rels = TableExpression
779656 { fromClause = fromClause rels
780657 , whereClause = whereClause rels
781− , groupByClause = group bys
658+ , groupByClause = Group bys
782659 , havingClause = Having []
783660 , orderByClause = []
784661 , limitClause = limitClause rels
@@ -788,31 +665,179 @@ groupBy bys rels = TableExpression
788665-- | A `having` is an endomorphism of `TableExpression`s which adds a
789666-- search condition to the `havingClause`.
790667having
791− :: Condition outer commons ('Grouped bys) schemas params from -- ^ having condition
792− -> TableExpression outer commons ('Grouped bys) schemas params from
793− -> TableExpression outer commons ('Grouped bys) schemas params from
668+ :: Condition schema from ('Grouped bys) params -- ^ having condition
669+ -> TableExpression schema params from ('Grouped bys)
670+ -> TableExpression schema params from ('Grouped bys)
794671having hv rels = rels
795672 { havingClause = case havingClause rels of Having hvs -> Having (hv:hvs) }
796673
797−instance OrderBy TableExpression where
798− orderBy srts rels = rels {orderByClause = orderByClause rels ++ srts}
674+-- | An `orderBy` is an endomorphism of `TableExpression`s which appends an
675+-- ordering to the right of the `orderByClause`.
676+orderBy
677+ :: [SortExpression schema from grouping params] -- ^ sort expressions
678+ -> TableExpression schema params from grouping
679+ -> TableExpression schema params from grouping
680+orderBy srts rels = rels {orderByClause = orderByClause rels ++ srts}
799681
800682-- | A `limit` is an endomorphism of `TableExpression`s which adds to the
801683-- `limitClause`.
802684limit
803685 :: Word64 -- ^ limit parameter
804− -> TableExpression outer commons grp schemas params from
805− -> TableExpression outer commons grp schemas params from
686+ -> TableExpression schema params from grouping
687+ -> TableExpression schema params from grouping
806688limit lim rels = rels {limitClause = lim : limitClause rels}
807689
808690-- | An `offset` is an endomorphism of `TableExpression`s which adds to the
809691-- `offsetClause`.
810692offset
811693 :: Word64 -- ^ offset parameter
812− -> TableExpression outer commons grp schemas params from
813− -> TableExpression outer commons grp schemas params from
694+ -> TableExpression schema params from grouping
695+ -> TableExpression schema params from grouping
814696offset off rels = rels {offsetClause = off : offsetClause rels}
815697
698+{-----------------------------------------
699+JSON stuff
700+-----------------------------------------}
701+
702+unsafeSetOfFunction
703+ :: ByteString
704+ -> Expression schema '[] 'Ungrouped params ty
705+ -> Query schema params row
706+unsafeSetOfFunction fun expr = UnsafeQuery $
707+ "SELECT * FROM " <> fun <> "(" <> renderExpression expr <> ")"
708+
709+-- | Expands the outermost JSON object into a set of key/value pairs.
710+jsonEach
711+ :: Expression schema '[] 'Ungrouped params (nullity 'PGjson) -- ^ json
712+ -> Query schema params
713+ '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGjson]
714+jsonEach = unsafeSetOfFunction "json_each"
715+
716+-- | Expands the outermost binary JSON object into a set of key/value pairs.
717+jsonbEach
718+ :: Expression schema '[] 'Ungrouped params (nullity 'PGjsonb) -- ^ jsonb
719+ -> Query schema params
720+ '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGjsonb]
721+jsonbEach = unsafeSetOfFunction "jsonb_each"
722+
723+-- | Expands the outermost JSON object into a set of key/value pairs.
724+jsonEachAsText
725+ :: Expression schema '[] 'Ungrouped params (nullity 'PGjson) -- ^ json
726+ -> Query schema params
727+ '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGtext]
728+jsonEachAsText = unsafeSetOfFunction "json_each_text"
729+
730+-- | Expands the outermost binary JSON object into a set of key/value pairs.
731+jsonbEachAsText
732+ :: Expression schema '[] 'Ungrouped params (nullity 'PGjsonb) -- ^ jsonb
733+ -> Query schema params
734+ '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGtext]
735+jsonbEachAsText = unsafeSetOfFunction "jsonb_each_text"
736+
737+-- | Returns set of keys in the outermost JSON object.
738+jsonObjectKeys
739+ :: Expression schema '[] 'Ungrouped params (nullity 'PGjson)
740+ -> Query schema params '["json_object_keys" ::: 'NotNull 'PGtext]
741+jsonObjectKeys = unsafeSetOfFunction "json_object_keys"
742+
743+-- | Returns set of keys in the outermost JSON object.
744+jsonbObjectKeys
745+ :: Expression schema '[] 'Ungrouped params (nullity 'PGjsonb)
746+ -> Query schema params '["jsonb_object_keys" ::: 'NotNull 'PGtext]
747+jsonbObjectKeys = unsafeSetOfFunction "jsonb_object_keys"
748+
749+unsafePopulateFunction
750+ :: ByteString
751+ -> TypeExpression schema (nullity ('PGcomposite row))
752+ -> Expression schema '[] 'Ungrouped params ty
753+ -> Query schema params row
754+unsafePopulateFunction fun ty expr = UnsafeQuery $
755+ "SELECT * FROM " <> fun <> "("
756+ <> "null::" <> renderTypeExpression ty <> ", "
757+ <> renderExpression expr <> ")"
758+
759+-- | Expands the JSON expression to a row whose columns match the record
760+-- type defined by the given table.
761+jsonPopulateRecord
762+ :: TypeExpression schema (nullity ('PGcomposite row))
763+ -> Expression schema '[] 'Ungrouped params (nullity 'PGjson)
764+ -> Query schema params row
765+jsonPopulateRecord = unsafePopulateFunction "json_populate_record"
766+
767+-- | Expands the binary JSON expression to a row whose columns match the record
768+-- type defined by the given table.
769+jsonbPopulateRecord
770+ :: TypeExpression schema (nullity ('PGcomposite row))
771+ -> Expression schema '[] 'Ungrouped params (nullity 'PGjsonb)
772+ -> Query schema params row
773+jsonbPopulateRecord = unsafePopulateFunction "jsonb_populate_record"
774+
775+-- | Expands the outermost array of objects in the given JSON expression to a
776+-- set of rows whose columns match the record type defined by the given table.
777+jsonPopulateRecordSet
778+ :: TypeExpression schema (nullity ('PGcomposite row))
779+ -> Expression schema '[] 'Ungrouped params (nullity 'PGjson)
780+ -> Query schema params row
781+jsonPopulateRecordSet = unsafePopulateFunction "json_populate_record_set"
782+
783+-- | Expands the outermost array of objects in the given binary JSON expression
784+-- to a set of rows whose columns match the record type defined by the given
785+-- table.
786+jsonbPopulateRecordSet
787+ :: TypeExpression schema (nullity ('PGcomposite row))
788+ -> Expression schema '[] 'Ungrouped params (nullity 'PGjsonb)
789+ -> Query schema params row
790+jsonbPopulateRecordSet = unsafePopulateFunction "jsonb_populate_record_set"
791+
792+unsafeRecordFunction
793+ :: (SListI row, json `In` PGJsonType)
794+ => ByteString
795+ -> Expression schema '[] 'Ungrouped params (nullity json)
796+ -> NP (Aliased (TypeExpression schema)) row
797+ -> Query schema params row
798+unsafeRecordFunction fun expr types = UnsafeQuery $
799+ "SELECT * FROM " <> fun <> "("
800+ <> renderExpression expr <> ")"
801+ <+> "AS" <+> "x" <> parenthesized (renderCommaSeparated renderTy types)
802+ where
803+ renderTy :: Aliased (TypeExpression schema) ty -> ByteString
804+ renderTy (ty `As` alias) =
805+ renderAlias alias <+> renderTypeExpression ty
806+
807+-- | Builds an arbitrary record from a JSON object.
808+jsonToRecord
809+ :: SListI row
810+ => Expression schema '[] 'Ungrouped params (nullity 'PGjson)
811+ -> NP (Aliased (TypeExpression schema)) row
812+ -> Query schema params row
813+jsonToRecord = unsafeRecordFunction "json_to_record"
814+
815+-- | Builds an arbitrary record from a binary JSON object.
816+jsonbToRecord
817+ :: SListI row
818+ => Expression schema '[] 'Ungrouped params (nullity 'PGjsonb)
819+ -> NP (Aliased (TypeExpression schema)) row
820+ -> Query schema params row
821+jsonbToRecord = unsafeRecordFunction "jsonb_to_record"
822+
823+-- | Builds an arbitrary set of records from a JSON array of objects.
824+jsonToRecordSet
825+ :: SListI row
826+ => Expression schema '[] 'Ungrouped params (nullity 'PGjson)
827+ -> NP (Aliased (TypeExpression schema)) row
828+ -> Query schema params row
829+jsonToRecordSet = unsafeRecordFunction "json_to_record_set"
830+
831+-- | Builds an arbitrary set of records from a binary JSON array of objects (see
832+-- note below). As with all functions returning record, the caller must
833+-- explicitly define the structure of the record with an AS clause.
834+jsonbToRecordSet
835+ :: SListI row
836+ => Expression schema '[] 'Ungrouped params (nullity 'PGjsonb)
837+ -> NP (Aliased (TypeExpression schema)) row
838+ -> Query schema params row
839+jsonbToRecordSet = unsafeRecordFunction "jsonb_to_record_set"
840+
816841{-----------------------------------------
817842FROM clauses
818843-----------------------------------------}
@@ -821,76 +846,61 @@ FROM clauses
821846A `FromClause` can be a table name, or a derived table such
822847as a subquery, a @JOIN@ construct, or complex combinations of these.
823848-}
824−newtype FromClause outer commons schemas params from
849+newtype FromClause schema params from
825850 = UnsafeFromClause { renderFromClause :: ByteString }
826851 deriving (GHC.Generic,Show,Eq,Ord,NFData)
827−instance RenderSQL (FromClause outer commons schemas params from) where
828− renderSQL = renderFromClause
829852
830−-- | A real `table` is a table from the database.
853+-- | A real `table` is a table from the schema.
831854table
832− :: (Has sch schemas schema, Has tab schema ('Table table))
833− => Aliased (QualifiedAlias sch) (alias ::: tab)
834− -> FromClause outer commons schemas params '[alias ::: TableToRow table]
855+ :: Has tab schema ('Table table)
856+ => Aliased Alias (alias ::: tab)
857+ -> FromClause schema params '[alias ::: TableToRow table]
835858table (tab `As` alias) = UnsafeFromClause $
836− renderSQL tab <+> "AS" <+> renderSQL alias
859+ renderAlias tab <+> "AS" <+> renderAlias alias
837860
838861-- | `subquery` derives a table from a `Query`.
839862subquery
840− :: Aliased (Query outer commons schemas params) query
841− -> FromClause outer commons schemas params '[query]
842−subquery = UnsafeFromClause . renderAliased (parenthesized . renderSQL)
863+ :: Aliased (Query schema params) rel
864+ -> FromClause schema params '[rel]
865+subquery = UnsafeFromClause . renderAliasedAs (parenthesized . renderQuery)
843866
844867-- | `view` derives a table from a `View`.
845868view
846− :: (Has sch schemas schema, Has vw schema ('View view))
847− => Aliased (QualifiedAlias sch) (alias ::: vw)
848− -> FromClause outer commons schemas params '[alias ::: view]
869+ :: Has view schema ('View row)
870+ => Aliased Alias (alias ::: view)
871+ -> FromClause schema params '[alias ::: row]
849872view (vw `As` alias) = UnsafeFromClause $
850− renderSQL vw <+> "AS" <+> renderSQL alias
851−
852−-- | `common` derives a table from a common table expression.
853−common
854− :: Has cte commons common
855− => Aliased Alias (alias ::: cte)
856− -> FromClause outer commons schemas params '[alias ::: common]
857−common (cte `As` alias) = UnsafeFromClause $
858− renderSQL cte <+> "AS" <+> renderSQL alias
859−
860−instance Additional (FromClause outer commons schemas params) where
861− also right left = UnsafeFromClause $
862− renderSQL left <> ", " <> renderSQL right
873+ renderAlias vw <+> "AS" <+> renderAlias alias
863874
864−{- |
865−@left & crossJoin right@. For every possible combination of rows from
866−@left@ and @right@ (i.e., a Cartesian product), the joined table will contain
867−a row consisting of all columns in @left@ followed by all columns in @right@.
868−If the tables have @n@ and @m@ rows respectively, the joined table will
869−have @n * m@ rows.
875+{- | @left & crossJoin right@. For every possible combination of rows from
876+ @left@ and @right@ (i.e., a Cartesian product), the joined table will contain
877+ a row consisting of all columns in @left@ followed by all columns in @right@.
878+ If the tables have @n@ and @m@ rows respectively, the joined table will
879+ have @n * m@ rows.
870880-}
871881crossJoin
872− :: FromClause outer commons schemas params right
882+ :: FromClause schema params right
873883 -- ^ right
874− -> FromClause outer commons schemas params left
884+ -> FromClause schema params left
875885 -- ^ left
876− -> FromClause outer commons schemas params (Join left right)
886+ -> FromClause schema params (Join left right)
877887crossJoin right left = UnsafeFromClause $
878− renderSQL left <+> "CROSS JOIN" <+> renderSQL right
888+ renderFromClause left <+> "CROSS JOIN" <+> renderFromClause right
879889
880890{- | @left & innerJoin right on@. The joined table is filtered by
881891the @on@ condition.
882892-}
883893innerJoin
884− :: FromClause outer commons schemas params right
894+ :: FromClause schema params right
885895 -- ^ right
886− -> Condition outer commons 'Ungrouped schemas params (Join left right)
896+ -> Condition schema (Join left right) 'Ungrouped params
887897 -- ^ @on@ condition
888− -> FromClause outer commons schemas params left
898+ -> FromClause schema params left
889899 -- ^ left
890− -> FromClause outer commons schemas params (Join left right)
900+ -> FromClause schema params (Join left right)
891901innerJoin right on left = UnsafeFromClause $
892− renderSQL left <+> "INNER JOIN" <+> renderSQL right
893− <+> "ON" <+> renderSQL on
902+ renderFromClause left <+> "INNER JOIN" <+> renderFromClause right
903+ <+> "ON" <+> renderExpression on
894904
895905{- | @left & leftOuterJoin right on@. First, an inner join is performed.
896906 Then, for each row in @left@ that does not satisfy the @on@ condition with
@@ -898,16 +908,16 @@ innerJoin right on left = UnsafeFromClause $
898908 Thus, the joined table always has at least one row for each row in @left@.
899909-}
900910leftOuterJoin
901− :: FromClause outer commons schemas params right
911+ :: FromClause schema params right
902912 -- ^ right
903− -> Condition outer commons 'Ungrouped schemas params (Join left right)
913+ -> Condition schema (Join left right) 'Ungrouped params
904914 -- ^ @on@ condition
905− -> FromClause outer commons schemas params left
915+ -> FromClause schema params left
906916 -- ^ left
907− -> FromClause outer commons schemas params (Join left (NullifyFrom right))
917+ -> FromClause schema params (Join left (NullifyFrom right))
908918leftOuterJoin right on left = UnsafeFromClause $
909− renderSQL left <+> "LEFT OUTER JOIN" <+> renderSQL right
910− <+> "ON" <+> renderSQL on
919+ renderFromClause left <+> "LEFT OUTER JOIN" <+> renderFromClause right
920+ <+> "ON" <+> renderExpression on
911921
912922{- | @left & rightOuterJoin right on@. First, an inner join is performed.
913923 Then, for each row in @right@ that does not satisfy the @on@ condition with
@@ -916,16 +926,16 @@ leftOuterJoin right on left = UnsafeFromClause $
916926 have a row for each row in @right@.
917927-}
918928rightOuterJoin
919− :: FromClause outer commons schemas params right
929+ :: FromClause schema params right
920930 -- ^ right
921− -> Condition outer commons 'Ungrouped schemas params (Join left right)
931+ -> Condition schema (Join left right) 'Ungrouped params
922932 -- ^ @on@ condition
923− -> FromClause outer commons schemas params left
933+ -> FromClause schema params left
924934 -- ^ left
925− -> FromClause outer commons schemas params (Join (NullifyFrom left) right)
935+ -> FromClause schema params (Join (NullifyFrom left) right)
926936rightOuterJoin right on left = UnsafeFromClause $
927− renderSQL left <+> "RIGHT OUTER JOIN" <+> renderSQL right
928− <+> "ON" <+> renderSQL on
937+ renderFromClause left <+> "RIGHT OUTER JOIN" <+> renderFromClause right
938+ <+> "ON" <+> renderExpression on
929939
930940{- | @left & fullOuterJoin right on@. First, an inner join is performed.
931941 Then, for each row in @left@ that does not satisfy the @on@ condition with
@@ -935,23 +945,23 @@ rightOuterJoin right on left = UnsafeFromClause $
935945 is added.
936946-}
937947fullOuterJoin
938− :: FromClause outer commons schemas params right
948+ :: FromClause schema params right
939949 -- ^ right
940− -> Condition outer commons 'Ungrouped schemas params (Join left right)
950+ -> Condition schema (Join left right) 'Ungrouped params
941951 -- ^ @on@ condition
942− -> FromClause outer commons schemas params left
952+ -> FromClause schema params left
943953 -- ^ left
944− -> FromClause outer commons schemas params
954+ -> FromClause schema params
945955 (Join (NullifyFrom left) (NullifyFrom right))
946956fullOuterJoin right on left = UnsafeFromClause $
947− renderSQL left <+> "FULL OUTER JOIN" <+> renderSQL right
948− <+> "ON" <+> renderSQL on
957+ renderFromClause left <+> "FULL OUTER JOIN" <+> renderFromClause right
958+ <+> "ON" <+> renderExpression on
949959
950960{-----------------------------------------
951961Grouping
952962-----------------------------------------}
953963
954−-- | `By`s are used in `groupBy` to reference a list of columns which are then
964+-- | `By`s are used in `group` to reference a list of columns which are then
955965-- used to group together those rows in a table that have the same values
956966-- in all the columns listed. @By \#col@ will reference an unambiguous
957967-- column @col@; otherwise @By2 (\#tab \! \#col)@ will reference a table
@@ -971,10 +981,6 @@ data By
971981deriving instance Show (By from by)
972982deriving instance Eq (By from by)
973983deriving instance Ord (By from by)
974−instance RenderSQL (By from by) where
975− renderSQL = \case
976− By1 column -> renderSQL column
977− By2 rel column -> renderSQL rel <> "." <> renderSQL column
978984
979985instance (HasUnique rel rels cols, Has col cols ty, by ~ '(rel, col))
980986 => IsLabel col (By rels by) where fromLabel = By1 fromLabel
@@ -986,113 +992,415 @@ instance (Has rel rels cols, Has col cols ty, bys ~ '[ '(rel, col)])
986992 => IsQualified rel col (NP (By rels) bys) where
987993 rel ! col = By2 rel col :* Nil
988994
995+-- | Renders a `By`.
996+renderBy :: By from by -> ByteString
997+renderBy = \case
998+ By1 column -> renderAlias column
999+ By2 rel column -> renderAlias rel <> "." <> renderAlias column
1000+
9891001-- | A `GroupByClause` indicates the `Grouping` of a `TableExpression`.
9901002-- A `NoGroups` indicates `Ungrouped` while a `Group` indicates `Grouped`.
9911003-- @NoGroups@ is distinguised from @Group Nil@ since no aggregation can be
9921004-- done on @NoGroups@ while all output `Expression`s must be aggregated
9931005-- in @Group Nil@. In general, all output `Expression`s in the
9941006-- complement of @bys@ must be aggregated in @Group bys@.
995−newtype GroupByClause grp from = UnsafeGroupByClause
996− { renderGroupByClause :: ByteString }
997− deriving (GHC.Generic,Show,Eq,Ord,NFData)
998−instance RenderSQL (GroupByClause grp from) where
999− renderSQL = renderGroupByClause
1000−noGroups :: GroupByClause 'Ungrouped from
1001−noGroups = UnsafeGroupByClause ""
1002−group
1003− :: SListI bys
1004− => NP (By from) bys
1005− -> GroupByClause ('Grouped bys) from
1006−group bys = UnsafeGroupByClause $ case bys of
1007− Nil -> ""
1008− bys' -> " GROUP BY" <+> renderCommaSeparated renderSQL bys'
1007+data GroupByClause from grouping where
1008+ NoGroups :: GroupByClause from 'Ungrouped
1009+ Group
1010+ :: SListI bys
1011+ => NP (By from) bys
1012+ -> GroupByClause from ('Grouped bys)
1013+
1014+-- | Renders a `GroupByClause`.
1015+renderGroupByClause :: GroupByClause from grouping -> ByteString
1016+renderGroupByClause = \case
1017+ NoGroups -> ""
1018+ Group Nil -> ""
1019+ Group bys -> " GROUP BY" <+> renderCommaSeparated renderBy bys
10091020
10101021-- | A `HavingClause` is used to eliminate groups that are not of interest.
10111022-- An `Ungrouped` `TableExpression` may only use `NoHaving` while a `Grouped`
10121023-- `TableExpression` must use `Having` whose conditions are combined with
10131024-- `.&&`.
1014−data HavingClause outer commons grp schemas params from where
1015− NoHaving :: HavingClause outer commons 'Ungrouped schemas params from
1025+data HavingClause schema from grouping params where
1026+ NoHaving :: HavingClause schema from 'Ungrouped params
10161027 Having
1017− :: [Condition outer commons ('Grouped bys) schemas params from]
1018− -> HavingClause outer commons ('Grouped bys) schemas params from
1019−deriving instance Show (HavingClause outer commons grp schemas params from)
1020−deriving instance Eq (HavingClause outer commons grp schemas params from)
1021−deriving instance Ord (HavingClause outer commons grp schemas params from)
1028+ :: [Condition schema from ('Grouped bys) params]
1029+ -> HavingClause schema from ('Grouped bys) params
1030+deriving instance Show (HavingClause schema from grouping params)
1031+deriving instance Eq (HavingClause schema from grouping params)
1032+deriving instance Ord (HavingClause schema from grouping params)
10221033
10231034-- | Render a `HavingClause`.
1024−instance RenderSQL (HavingClause outer commons grp schemas params from) where
1025− renderSQL = \case
1026− NoHaving -> ""
1027− Having [] -> ""
1028− Having conditions ->
1029− " HAVING" <+> commaSeparated (renderSQL <$> conditions)
1035+renderHavingClause :: HavingClause schema from grouping params -> ByteString
1036+renderHavingClause = \case
1037+ NoHaving -> ""
1038+ Having [] -> ""
1039+ Having conditions ->
1040+ " HAVING" <+> commaSeparated (renderExpression <$> conditions)
1041+
1042+{-----------------------------------------
1043+Sorting
1044+-----------------------------------------}
1045+
1046+-- | `SortExpression`s are used by `sortBy` to optionally sort the results
1047+-- of a `Query`. `Asc` or `Desc` set the sort direction of a `NotNull` result
1048+-- column to ascending or descending. Ascending order puts smaller values
1049+-- first, where "smaller" is defined in terms of the `.<` operator. Similarly,
1050+-- descending order is determined with the `.>` operator. `AscNullsFirst`,
1051+-- `AscNullsLast`, `DescNullsFirst` and `DescNullsLast` options are used to
1052+-- determine whether nulls appear before or after non-null values in the sort
1053+-- ordering of a `Null` result column.
1054+data SortExpression schema from grouping params where
1055+ Asc
1056+ :: Expression schema from grouping params ('NotNull ty)
1057+ -> SortExpression schema from grouping params
1058+ Desc
1059+ :: Expression schema from grouping params ('NotNull ty)
1060+ -> SortExpression schema from grouping params
1061+ AscNullsFirst
1062+ :: Expression schema from grouping params ('Null ty)
1063+ -> SortExpression schema from grouping params
1064+ AscNullsLast
1065+ :: Expression schema from grouping params ('Null ty)
1066+ -> SortExpression schema from grouping params
1067+ DescNullsFirst
1068+ :: Expression schema from grouping params ('Null ty)
1069+ -> SortExpression schema from grouping params
1070+ DescNullsLast
1071+ :: Expression schema from grouping params ('Null ty)
1072+ -> SortExpression schema from grouping params
1073+deriving instance Show (SortExpression schema from grouping params)
1074+
1075+-- | Render a `SortExpression`.
1076+renderSortExpression :: SortExpression schema from grouping params -> ByteString
1077+renderSortExpression = \case
1078+ Asc expression -> renderExpression expression <+> "ASC"
1079+ Desc expression -> renderExpression expression <+> "DESC"
1080+ AscNullsFirst expression -> renderExpression expression
1081+ <+> "ASC NULLS FIRST"
1082+ DescNullsFirst expression -> renderExpression expression
1083+ <+> "DESC NULLS FIRST"
1084+ AscNullsLast expression -> renderExpression expression <+> "ASC NULLS LAST"
1085+ DescNullsLast expression -> renderExpression expression <+> "DESC NULLS LAST"
1086+
1087+unsafeSubqueryExpression
1088+ :: ByteString
1089+ -> Expression schema from grp params ty
1090+ -> Query schema params '[alias ::: ty]
1091+ -> Condition schema from grp params
1092+unsafeSubqueryExpression op x q = UnsafeExpression $
1093+ renderExpression x <+> op <+> parenthesized (renderQuery q)
1094+
1095+unsafeRowSubqueryExpression
1096+ :: SListI row
1097+ => ByteString
1098+ -> NP (Aliased (Expression schema from grp params)) row
1099+ -> Query schema params row
1100+ -> Condition schema from grp params
1101+unsafeRowSubqueryExpression op xs q = UnsafeExpression $
1102+ renderExpression (row xs) <+> op <+> parenthesized (renderQuery q)
1103+
1104+-- | The right-hand side is a sub`Query`, which must return exactly one column.
1105+-- The left-hand expression is evaluated and compared to each row of the
1106+-- sub`Query` result. The result of `in_` is `true` if any equal subquery row is found.
1107+-- The result is `false` if no equal row is found
1108+-- (including the case where the subquery returns no rows).
1109+--
1110+-- >>> printSQL $ true `in_` values_ (true `as` #foo)
1111+-- TRUE IN (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1112+in_
1113+ :: Expression schema from grp params ty -- ^ expression
1114+ -> Query schema params '[alias ::: ty] -- ^ subquery
1115+ -> Condition schema from grp params
1116+in_ = unsafeSubqueryExpression "IN"
1117+
1118+{- | The left-hand side of this form of `rowIn` is a row constructor.
1119+The right-hand side is a sub`Query`,
1120+which must return exactly as many columns as
1121+there are expressions in the left-hand row.
1122+The left-hand expressions are evaluated and compared row-wise to each row
1123+of the subquery result. The result of `rowIn`
1124+is `true` if any equal subquery row is found.
1125+The result is `false` if no equal row is found
1126+(including the case where the subquery returns no rows).
1127+
1128+>>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1129+>>> printSQL $ myRow `rowIn` values_ myRow
1130+ROW(1, FALSE) IN (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1131+-}
1132+rowIn
1133+ :: SListI row
1134+ => NP (Aliased (Expression schema from grp params)) row -- ^ row constructor
1135+ -> Query schema params row -- ^ subquery
1136+ -> Condition schema from grp params
1137+rowIn = unsafeRowSubqueryExpression "IN"
1138+
1139+-- | >>> printSQL $ true `allEq` values_ (true `as` #foo)
1140+-- TRUE ALL = (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1141+allEq
1142+ :: Expression schema from grp params ty
1143+ -> Query schema params '[alias ::: ty]
1144+ -> Condition schema from grp params
1145+allEq = unsafeSubqueryExpression "ALL ="
1146+
1147+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1148+-- >>> printSQL $ myRow `rowAllEq` values_ myRow
1149+-- ROW(1, FALSE) ALL = (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1150+rowAllEq
1151+ :: SListI row
1152+ => NP (Aliased (Expression schema from grp params)) row
1153+ -> Query schema params row
1154+ -> Condition schema from grp params
1155+rowAllEq = unsafeRowSubqueryExpression "ALL ="
1156+
1157+-- | >>> printSQL $ true `anyEq` values_ (true `as` #foo)
1158+-- TRUE ANY = (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1159+anyEq
1160+ :: Expression schema from grp params ty
1161+ -> Query schema params '[alias ::: ty]
1162+ -> Condition schema from grp params
1163+anyEq = unsafeSubqueryExpression "ANY ="
1164+
1165+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1166+-- >>> printSQL $ myRow `rowAnyEq` values_ myRow
1167+-- ROW(1, FALSE) ANY = (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1168+rowAnyEq
1169+ :: SListI row
1170+ => NP (Aliased (Expression schema from grp params)) row
1171+ -> Query schema params row
1172+ -> Condition schema from grp params
1173+rowAnyEq = unsafeRowSubqueryExpression "ANY ="
1174+
1175+-- | >>> printSQL $ true `allNeq` values_ (true `as` #foo)
1176+-- TRUE ALL <> (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1177+allNeq
1178+ :: Expression schema from grp params ty
1179+ -> Query schema params '[alias ::: ty]
1180+ -> Condition schema from grp params
1181+allNeq = unsafeSubqueryExpression "ALL <>"
1182+
1183+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1184+-- >>> printSQL $ myRow `rowAllNeq` values_ myRow
1185+-- ROW(1, FALSE) ALL <> (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1186+rowAllNeq
1187+ :: SListI row
1188+ => NP (Aliased (Expression schema from grp params)) row
1189+ -> Query schema params row
1190+ -> Condition schema from grp params
1191+rowAllNeq = unsafeRowSubqueryExpression "ALL <>"
1192+
1193+-- | >>> printSQL $ true `anyNeq` values_ (true `as` #foo)
1194+-- TRUE ANY <> (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1195+anyNeq
1196+ :: Expression schema from grp params ty
1197+ -> Query schema params '[alias ::: ty]
1198+ -> Condition schema from grp params
1199+anyNeq = unsafeSubqueryExpression "ANY <>"
1200+
1201+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1202+-- >>> printSQL $ myRow `rowAnyNeq` values_ myRow
1203+-- ROW(1, FALSE) ANY <> (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1204+rowAnyNeq
1205+ :: SListI row
1206+ => NP (Aliased (Expression schema from grp params)) row
1207+ -> Query schema params row
1208+ -> Condition schema from grp params
1209+rowAnyNeq = unsafeRowSubqueryExpression "ANY <>"
1210+
1211+-- | >>> printSQL $ true `allLt` values_ (true `as` #foo)
1212+-- TRUE ALL < (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1213+allLt
1214+ :: Expression schema from grp params ty
1215+ -> Query schema params '[alias ::: ty]
1216+ -> Condition schema from grp params
1217+allLt = unsafeSubqueryExpression "ALL <"
1218+
1219+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1220+-- >>> printSQL $ myRow `rowAllLt` values_ myRow
1221+-- ROW(1, FALSE) ALL < (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1222+rowAllLt
1223+ :: SListI row
1224+ => NP (Aliased (Expression schema from grp params)) row
1225+ -> Query schema params row
1226+ -> Condition schema from grp params
1227+rowAllLt = unsafeRowSubqueryExpression "ALL <"
1228+
1229+-- | >>> printSQL $ true `anyLt` values_ (true `as` #foo)
1230+-- TRUE ANY < (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1231+anyLt
1232+ :: Expression schema from grp params ty
1233+ -> Query schema params '[alias ::: ty]
1234+ -> Condition schema from grp params
1235+anyLt = unsafeSubqueryExpression "ANY <"
1236+
1237+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1238+-- >>> printSQL $ myRow `rowAllLt` values_ myRow
1239+-- ROW(1, FALSE) ALL < (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1240+rowAnyLt
1241+ :: SListI row
1242+ => NP (Aliased (Expression schema from grp params)) row
1243+ -> Query schema params row
1244+ -> Condition schema from grp params
1245+rowAnyLt = unsafeRowSubqueryExpression "ANY <"
1246+
1247+-- | >>> printSQL $ true `allLte` values_ (true `as` #foo)
1248+-- TRUE ALL <= (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1249+allLte
1250+ :: Expression schema from grp params ty
1251+ -> Query schema params '[alias ::: ty]
1252+ -> Condition schema from grp params
1253+allLte = unsafeSubqueryExpression "ALL <="
1254+
1255+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1256+-- >>> printSQL $ myRow `rowAllLte` values_ myRow
1257+-- ROW(1, FALSE) ALL <= (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1258+rowAllLte
1259+ :: SListI row
1260+ => NP (Aliased (Expression schema from grp params)) row
1261+ -> Query schema params row
1262+ -> Condition schema from grp params
1263+rowAllLte = unsafeRowSubqueryExpression "ALL <="
1264+
1265+-- | >>> printSQL $ true `anyLte` values_ (true `as` #foo)
1266+-- TRUE ANY <= (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1267+anyLte
1268+ :: Expression schema from grp params ty
1269+ -> Query schema params '[alias ::: ty]
1270+ -> Condition schema from grp params
1271+anyLte = unsafeSubqueryExpression "ANY <="
1272+
1273+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1274+-- >>> printSQL $ myRow `rowAnyLte` values_ myRow
1275+-- ROW(1, FALSE) ANY <= (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1276+rowAnyLte
1277+ :: SListI row
1278+ => NP (Aliased (Expression schema from grp params)) row
1279+ -> Query schema params row
1280+ -> Condition schema from grp params
1281+rowAnyLte = unsafeRowSubqueryExpression "ANY <="
1282+
1283+-- | >>> printSQL $ true `allGt` values_ (true `as` #foo)
1284+-- TRUE ALL > (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1285+allGt
1286+ :: Expression schema from grp params ty
1287+ -> Query schema params '[alias ::: ty]
1288+ -> Condition schema from grp params
1289+allGt = unsafeSubqueryExpression "ALL >"
1290+
1291+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1292+-- >>> printSQL $ myRow `rowAllGt` values_ myRow
1293+-- ROW(1, FALSE) ALL > (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1294+rowAllGt
1295+ :: SListI row
1296+ => NP (Aliased (Expression schema from grp params)) row
1297+ -> Query schema params row
1298+ -> Condition schema from grp params
1299+rowAllGt = unsafeRowSubqueryExpression "ALL >"
1300+
1301+-- | >>> printSQL $ true `anyGt` values_ (true `as` #foo)
1302+-- TRUE ANY > (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1303+anyGt
1304+ :: Expression schema from grp params ty
1305+ -> Query schema params '[alias ::: ty]
1306+ -> Condition schema from grp params
1307+anyGt = unsafeSubqueryExpression "ANY >"
1308+
1309+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1310+-- >>> printSQL $ myRow `rowAnyGt` values_ myRow
1311+-- ROW(1, FALSE) ANY > (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1312+rowAnyGt
1313+ :: SListI row
1314+ => NP (Aliased (Expression schema from grp params)) row
1315+ -> Query schema params row
1316+ -> Condition schema from grp params
1317+rowAnyGt = unsafeRowSubqueryExpression "ANY >"
1318+
1319+-- | >>> printSQL $ true `allGte` values_ (true `as` #foo)
1320+-- TRUE ALL >= (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1321+allGte
1322+ :: Expression schema from grp params ty
1323+ -> Query schema params '[alias ::: ty]
1324+ -> Condition schema from grp params
1325+allGte = unsafeSubqueryExpression "ALL >="
1326+
1327+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1328+-- >>> printSQL $ myRow `rowAllGte` values_ myRow
1329+-- ROW(1, FALSE) ALL >= (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1330+rowAllGte
1331+ :: SListI row
1332+ => NP (Aliased (Expression schema from grp params)) row
1333+ -> Query schema params row
1334+ -> Condition schema from grp params
1335+rowAllGte = unsafeRowSubqueryExpression "ALL >="
1336+
1337+-- | >>> printSQL $ true `anyGte` values_ (true `as` #foo)
1338+-- TRUE ANY >= (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1339+anyGte
1340+ :: Expression schema from grp params ty
1341+ -> Query schema params '[alias ::: ty]
1342+ -> Condition schema from grp params
1343+anyGte = unsafeSubqueryExpression "ANY >="
1344+
1345+-- | >>> let myRow = 1 `as` #foo :* false `as` #bar :: NP (Aliased (Expression '[] '[] 'Ungrouped '[])) '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
1346+-- >>> printSQL $ myRow `rowAnyGte` values_ myRow
1347+-- ROW(1, FALSE) ANY >= (SELECT * FROM (VALUES (1, FALSE)) AS t ("foo", "bar"))
1348+rowAnyGte
1349+ :: SListI row
1350+ => NP (Aliased (Expression schema from grp params)) row
1351+ -> Query schema params row
1352+ -> Condition schema from grp params
1353+rowAnyGte = unsafeRowSubqueryExpression "ANY >="
10301354
10311355-- | A `CommonTableExpression` is an auxiliary statement in a `with` clause.
10321356data CommonTableExpression statement
1033− (schemas :: SchemasType)
10341357 (params :: [NullityType])
1035− (commons0 :: FromType)
1036− (commons1 :: FromType) where
1358+ (schema0 :: SchemaType)
1359+ (schema1 :: SchemaType) where
10371360 CommonTableExpression
1038− :: Aliased (statement commons schemas params) (cte ::: common)
1039− -> CommonTableExpression statement schemas params commons (cte ::: common ': commons)
1040−instance
1041− ( KnownSymbol cte
1042− , commons1 ~ (cte ::: common ': commons)
1043− ) => Aliasable cte
1044− (statement commons schemas params common)
1045− (CommonTableExpression statement schemas params commons commons1) where
1046− statement `as` cte = CommonTableExpression (statement `as` cte)
1047−instance
1048− ( KnownSymbol cte
1049− , commons1 ~ (cte ::: common ': commons)
1050− ) => Aliasable cte
1051− (statement commons schemas params common)
1052− (Path (CommonTableExpression statement schemas params) commons commons1) where
1053− statement `as` cte = csingleton (statement `as` cte)
1054−
1055−instance (forall c s p r. RenderSQL (statement c s p r)) => RenderSQL
1056− (CommonTableExpression statement schemas params commons0 commons1) where
1057− renderSQL (CommonTableExpression (statement `As` cte)) =
1058− renderSQL cte <+> "AS" <+> parenthesized (renderSQL statement)
1361+ :: Aliased (statement schema params) (alias ::: cte)
1362+ -> CommonTableExpression
1363+ statement params schema (alias ::: 'View cte ': schema)
1364+instance (KnownSymbol alias, schema1 ~ (alias ::: 'View cte ': schema))
1365+ => Aliasable alias
1366+ (statement schema params cte)
1367+ (CommonTableExpression statement params schema schema1) where
1368+ statement `as` alias = CommonTableExpression (statement `as` alias)
1369+instance (KnownSymbol alias, schema1 ~ (alias ::: 'View cte ': schema))
1370+ => Aliasable alias (statement schema params cte)
1371+ (AlignedList (CommonTableExpression statement params) schema schema1) where
1372+ statement `as` alias = single (statement `as` alias)
1373+
1374+-- | render a `CommonTableExpression`.
1375+renderCommonTableExpression
1376+ :: (forall sch ps row. statement ps sch row -> ByteString)
1377+ -> CommonTableExpression statement params schema0 schema1 -> ByteString
1378+renderCommonTableExpression renderStatement
1379+ (CommonTableExpression (statement `As` alias)) =
1380+ renderAlias alias <+> "AS" <+> parenthesized (renderStatement statement)
1381+
1382+-- | render a non-empty `AlignedList` of `CommonTableExpression`s.
1383+renderCommonTableExpressions
1384+ :: (forall sch ps row. statement ps sch row -> ByteString)
1385+ -> CommonTableExpression statement params schema0 schema1
1386+ -> AlignedList (CommonTableExpression statement params) schema1 schema2
1387+ -> ByteString
1388+renderCommonTableExpressions renderStatement cte ctes =
1389+ renderCommonTableExpression renderStatement cte <> case ctes of
1390+ Done -> ""
1391+ cte' :>> ctes' -> "," <+>
1392+ renderCommonTableExpressions renderStatement cte' ctes'
10591393
10601394-- | `with` provides a way to write auxiliary statements for use in a larger query.
10611395-- These statements, referred to as `CommonTableExpression`s, can be thought of as
10621396-- defining temporary tables that exist just for one query.
10631397class With statement where
10641398 with
1065− :: Path (CommonTableExpression statement schemas params) commons0 commons1
1066− -- ^ common table expressions
1067− -> statement commons1 schemas params row
1068− -- ^ larger query
1069− -> statement commons0 schemas params row
1070−instance With (Query outer) where
1399+ :: AlignedList (CommonTableExpression statement params) schema0 schema1
1400+ -> statement schema1 params row
1401+ -> statement schema0 params row
1402+instance With Query where
10711403 with Done query = query
1072− with ctes query = UnsafeQuery $
1073− "WITH" <+> commaSeparated (ctoList renderSQL ctes) <+> renderSQL query
1074−
1075−{- |
1076−>>> import Data.Monoid (Sum (..))
1077−>>> import Data.Int (Int64)
1078−>>> :{
1079− let
1080− query :: Query_ schema () (Sum Int64)
1081− query = withRecursive
1082− ( values_ ((1 & astype int) `as` #n)
1083− `unionAll`
1084− select_ ((#n + 1) `as` #n)
1085− (from (common #t) & where_ (#n .< 100)) `as` #t )
1086− ( select_ (fromNull 0 (sum_ (All #n)) `as` #getSum) (from (common #t) & groupBy Nil))
1087− in printSQL query
1088−:}
1089−WITH RECURSIVE "t" AS ((SELECT * FROM (VALUES ((1 :: int))) AS t ("n")) UNION ALL (SELECT ("n" + 1) AS "n" FROM "t" AS "t" WHERE ("n" < 100))) SELECT COALESCE(sum(ALL "n"), 0) AS "getSum" FROM "t" AS "t"
1090−-}
1091−withRecursive
1092− :: Aliased (Query outer (recursive ': commons) schemas params) recursive
1093− -> Query outer (recursive ': commons) schemas params row
1094− -> Query outer commons schemas params row
1095−withRecursive (recursive `As` cte) query = UnsafeQuery $
1096− "WITH RECURSIVE" <+> renderSQL cte
1097− <+> "AS" <+> parenthesized (renderSQL recursive)
1098− <+> renderSQL query
1404+ with (cte :>> ctes) query = UnsafeQuery $
1405+ "WITH" <+> renderCommonTableExpressions renderQuery cte ctes
1406+ <+> renderQuery query