@@ -1,7 +1,7 @@
2Module: Squeal.PostgreSQL.Query
2Module: Squeal.PostgreSQL.Query
3Description: Squeal queries
3Description: Squeal queries
4−Copyright: (c) Eitan Chatav, 2017
4+Copyright: (c) Eitan Chatav, 2019
5Maintainer: eitan@morphism.tech
5Maintainer: eitan@morphism.tech
6Stability: experimental
6Stability: experimental
@@ -17,8 +17,12 @@ Squeal queries.
17 , GeneralizedNewtypeDeriving
17 , GeneralizedNewtypeDeriving
18 , LambdaCase
18 , LambdaCase
19 , MultiParamTypeClasses
19 , MultiParamTypeClasses
20 , OverloadedStrings
21 , OverloadedStrings
22+ , QuantifiedConstraints
23+ , ScopedTypeVariables
21 , StandaloneDeriving
24 , StandaloneDeriving
22 , TypeFamilies
26 , TypeFamilies
23 , TypeInType
27 , TypeInType
24 , TypeOperators
28 , TypeOperators
@@ -28,104 +32,58 @@ Squeal queries.
29module Squeal.PostgreSQL.Query
33module Squeal.PostgreSQL.Query
30 ( -- * Queries
34 ( -- * Queries
31− Query (UnsafeQuery, renderQuery)
35+ Query_
33 , unionAll
50 , unionAll
34 , intersect
51 , intersect
35 , intersectAll
52 , intersectAll
37 , exceptAll
54 , exceptAll
38− -- * Select
55+ -- ** With
39− , select
56+ , With (..)
40− , selectDistinct
57+ , CommonTableExpression (..)
41− , selectStar
58+ , withRecursive
44− , selectDistinctDotStar
47 -- * Table Expressions
59 -- * Table Expressions
48 , TableExpression (..)
60 , TableExpression (..)
49− , renderTableExpression
65− , jsonbPopulateRecord
66− , jsonPopulateRecordSet
67− , jsonbPopulateRecordSet
72− -- * From
67+ -- * From Clauses
73 , FromClause (..)
68 , FromClause (..)
75 , subquery
70 , subquery
77 , crossJoin
73 , crossJoin
78 , innerJoin
74 , innerJoin
79 , leftOuterJoin
75 , leftOuterJoin
80 , rightOuterJoin
76 , rightOuterJoin
81 , fullOuterJoin
77 , fullOuterJoin
82 -- * Grouping
78 -- * Grouping
83− , By (By1, By2)
79+ , By (..)
85− , GroupByClause (NoGroups, Group)
80+ , GroupByClause (..)
86− , renderGroupByClause
87− , HavingClause (NoHaving, Having)
81+ , HavingClause (..)
90− , SortExpression (..)
91− , renderSortExpression
92− -- * Subquery expressions
121− , CommonTableExpression (..)
122− , renderCommonTableExpression
123− , renderCommonTableExpressions
126import Control.DeepSeq
84import Control.DeepSeq
127import Data.ByteString (ByteString)
85import Data.ByteString (ByteString)
128−import Data.Monoid hiding (All)
86+import Data.Kind (Type)
129import Data.String
87import Data.String
130import Data.Word
88import Data.Word
131import Generics.SOP hiding (from)
89import Generics.SOP hiding (from)
@@ -133,25 +91,76 @@ import GHC.TypeLits
134import qualified GHC.Generics as GHC
92import qualified GHC.Generics as GHC
94+import Squeal.PostgreSQL.Alias
136import Squeal.PostgreSQL.Expression
95import 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
137import Squeal.PostgreSQL.Render
101import Squeal.PostgreSQL.Render
138import Squeal.PostgreSQL.Schema
102import Squeal.PostgreSQL.Schema
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
112+The process of retrieving or the command to retrieve data from
113+a database is called a `Query`.
115+The general `Query` type is parameterized by
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`.
125+ (commons :: FromType)
126+ (schemas :: SchemasType)
127+ (params :: [NullityType])
129+ = UnsafeQuery { renderQuery :: ByteString }
130+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
131+instance RenderSQL (Query outer commons schemas params row) where renderSQL = renderQuery
141−The process of retrieving or the command to retrieve data from a database
134+The top level `Query_` type is parameterized by a @schemas@ `SchemasType`,
142−is called a `Query`. The `select`, `selectStar`, `selectDotStar`,
135+against which the query is type-checked, an input @parameters@ Haskell `Type`,
143−`selectDistinct`, `selectDistinctStar` and `selectDistinctDotStar` commands
136+and an ouput row Haskell `Type`.
144−are used to specify queries.
137+
138+A top-level query can be run
139+using `Squeal.PostgreSQL.PQ.runQueryParams`, or if @parameters = ()@
140+using `Squeal.PostgreSQL.PQ.runQuery`.
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.
147+Let's see some examples of queries.
149+>>> :set -XDeriveAnyClass -XDerivingStrategies
151+data Row a b = Row { col1 :: a, col2 :: b }
152+ deriving stock (GHC.Generic)
153+ deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
146simple query:
156simple query:
158+>>> type Columns = '["col1" ::: 'NoDef :=> 'NotNull 'PGint4, "col2" ::: 'NoDef :=> 'NotNull 'PGint4]
159+>>> type Schema = '["tab" ::: 'Table ('[] :=> Columns)]
150− query :: Query
162+ query :: Query_ (Public Schema) () (Row Int32 Int32)
151− '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
153− '["col" ::: 'Null 'PGint4]
154− query = selectStar (from (table #tab))
163+ query = select Star (from (table #tab))
155in printSQL query
164in printSQL query
157SELECT * FROM "tab" AS "tab"
166SELECT * FROM "tab" AS "tab"
@@ -160,34 +169,22 @@ restricted query:
163− query :: Query
172+ query :: Query_ (Public Schema) () (Row Int32 Int32)
164− '[ "tab" ::: 'Table ('[] :=>
165− '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
166− , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ])]
168− '[ "sum" ::: 'NotNull 'PGint4
169− , "col1" ::: 'NotNull 'PGint4 ]
172− ((#col1 + #col2) `as` #sum :* #col1)
174+ select_ ((#col1 + #col2) `as` #col1 :* #col1 `as` #col2)
173 ( from (table #tab)
175 ( from (table #tab)
174 & where_ (#col1 .> #col2)
176 & where_ (#col1 .> #col2)
175 & where_ (#col2 .> 0) )
177 & where_ (#col2 .> 0) )
176in printSQL query
178in printSQL query
178−SELECT ("col1" + "col2") AS "sum", "col1" AS "col1" FROM "tab" AS "tab" WHERE (("col1" > "col2") AND ("col2" > 0))
180+SELECT ("col1" + "col2") AS "col1", "col1" AS "col2" FROM "tab" AS "tab" WHERE (("col1" > "col2") AND ("col2" > 0))
184− query :: Query
186+ query :: Query_ (Public Schema) () (Row Int32 Int32)
185− '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
187− '["col" ::: 'Null 'PGint4]
190− (from (subquery (selectStar (from (table #tab)) `as` #sub)))
187+ query = select Star (from (subquery (select Star (from (table #tab)) `as` #sub)))
191in printSQL query
188in printSQL query
193SELECT * FROM (SELECT * FROM "tab" AS "tab") AS "sub"
190SELECT * FROM (SELECT * FROM "tab" AS "tab") AS "sub"
@@ -196,12 +193,8 @@ limits and offsets:
199− query :: Query
196+ query :: Query_ (Public Schema) () (Row Int32 Int32)
200− '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
202− '["col" ::: 'Null 'PGint4]
204− (from (table #tab) & limit 100 & offset 2 & limit 50 & offset 2)
197+ query = select Star (from (table #tab) & limit 100 & offset 2 & limit 50 & offset 2)
205in printSQL query
198in printSQL query
207SELECT * FROM "tab" AS "tab" LIMIT 50 OFFSET 4
200SELECT * FROM "tab" AS "tab" LIMIT 50 OFFSET 4
@@ -210,85 +203,80 @@ parameterized query:
213− query :: Query
206+ query :: Query_ (Public Schema) (Only Int32) (Row Int32 Int32)
214− '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'NotNull 'PGfloat8])]
215− '[ 'NotNull 'PGfloat8]
216− '["col" ::: 'NotNull 'PGfloat8]
218− (from (table #tab) & where_ (#col .> param @1))
207+ query = select Star (from (table #tab) & where_ (#col1 .> param @1))
219in printSQL query
208in printSQL query
221−SELECT * FROM "tab" AS "tab" WHERE ("col" > ($1 :: float8))
210+SELECT * FROM "tab" AS "tab" WHERE ("col1" > ($1 :: int4))
223aggregation query:
212aggregation query:
227− query :: Query
216+ query :: Query_ (Public Schema) () (Row Int64 Int32)
228− '[ "tab" ::: 'Table ('[] :=>
229− '[ "col1" ::: 'NoDef :=> 'NotNull 'PGint4
230− , "col2" ::: 'NoDef :=> 'NotNull 'PGint4 ])]
232− '[ "sum" ::: 'NotNull 'PGint4
233− , "col1" ::: 'NotNull 'PGint4 ]
235− select (sum_ #col2 `as` #sum :* #col1)
218+ select_ ((fromNull 0 (sum_ (All #col2))) `as` #col1 :* #col1 `as` #col2)
236 ( from (table (#tab `as` #table1))
219 ( from (table (#tab `as` #table1))
237 & groupBy #col1
220 & groupBy #col1
238− & having (#col1 + sum_ #col2 .> 1) )
221+ & having (sum_ (Distinct #col2) .> 1) )
239in printSQL query
222in printSQL query
241−SELECT sum("col2") AS "sum", "col1" AS "col1" FROM "tab" AS "table1" GROUP BY "col1" HAVING (("col1" + sum("col2")) > 1)
224+SELECT COALESCE(sum(ALL "col2"), 0) AS "col1", "col1" AS "col2" FROM "tab" AS "table1" GROUP BY "col1" HAVING (sum(DISTINCT "col2") > 1)
243sorted query:
226sorted query:
247− query :: Query
230+ query :: Query_ (Public Schema) () (Row Int32 Int32)
248− '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
250− '["col" ::: 'Null 'PGint4]
252− (from (table #tab) & orderBy [#col & AscNullsFirst])
231+ query = select Star (from (table #tab) & orderBy [#col1 & Asc])
253in printSQL query
232in printSQL query
255−SELECT * FROM "tab" AS "tab" ORDER BY "col" ASC NULLS FIRST
234+SELECT * FROM "tab" AS "tab" ORDER BY "col1" ASC
259−>>> :set -XFlexibleContexts
238+>>> :{
240+ '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
241+ , "price" ::: 'NoDef :=> 'NotNull 'PGfloat4
242+ , "customer_id" ::: 'NoDef :=> 'NotNull 'PGint4
243+ , "shipper_id" ::: 'NoDef :=> 'NotNull 'PGint4 ]
247+type OrdersConstraints =
248+ '["pk_orders" ::: PrimaryKey '["id"]
249+ ,"fk_customers" ::: ForeignKey '["customer_id"] "customers" '["id"]
250+ ,"fk_shippers" ::: ForeignKey '["shipper_id"] "shippers" '["id"] ]
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"]]
258+ '[ "orders" ::: 'Table (OrdersConstraints :=> OrdersColumns)
259+ , "customers" ::: 'Table (CustomersConstraints :=> NamesColumns)
260+ , "shippers" ::: 'Table (ShippersConstraints :=> NamesColumns) ]
266+ , customerName :: Text
267+ , shipperName :: Text
268+ } deriving GHC.Generic
269+instance SOP.Generic Order
270+instance SOP.HasDatatypeInfo Order
262− query :: Query
275+ query :: Query_ (Public OrdersSchema) () Order
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
272− , "customers" ::: 'Table (
273− '["pk_customers" ::: PrimaryKey '["id"]] :=>
274− '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
275− , "name" ::: 'NoDef :=> 'NotNull 'PGtext
277− , "shippers" ::: 'Table (
278− '["pk_shippers" ::: PrimaryKey '["id"]] :=>
279− '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
280− , "name" ::: 'NoDef :=> 'NotNull 'PGtext
284− '[ "order_price" ::: 'NotNull 'PGfloat4
285− , "customer_name" ::: 'NotNull 'PGtext
286− , "shipper_name" ::: 'NotNull 'PGtext
288− query = select
276+ query = select_
289− ( #o ! #price `as` #order_price :*
277+ ( #o ! #price `as` #price :*
290− #c ! #name `as` #customer_name :*
278+ #c ! #name `as` #customerName :*
291− #s ! #name `as` #shipper_name )
279+ #s ! #name `as` #shipperName )
292 ( from (table (#orders `as` #o)
280 ( from (table (#orders `as` #o)
293 & innerJoin (table (#customers `as` #c))
281 & innerJoin (table (#customers `as` #c))
294 (#o ! #customer_id .== #c ! #id)
282 (#o ! #customer_id .== #c ! #id)
@@ -296,34 +284,38 @@ let
296 (#o ! #shipper_id .== #s ! #id)) )
284 (#o ! #shipper_id .== #s ! #id)) )
297in printSQL query
285in printSQL query
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")
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")
301self-join:
289self-join:
305− query :: Query
293+ query :: Query_ (Public Schema) () (Row Int32 Int32)
306− '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
294+ query = select
307− '[]
295+ (#t1 & DotStar)
308− '["col" ::: 'Null 'PGint4]
309− query = selectDotStar #t1
310 (from (table (#tab `as` #t1) & crossJoin (table (#tab `as` #t2))))
296 (from (table (#tab `as` #t1) & crossJoin (table (#tab `as` #t2))))
311in printSQL query
297in printSQL query
313SELECT "t1".* FROM "tab" AS "t1" CROSS JOIN "tab" AS "t2"
299SELECT "t1".* FROM "tab" AS "t1" CROSS JOIN "tab" AS "t2"
305+ query :: Query_ schemas () (Row String Bool)
307+ ("true" `as` #col1 :* true `as` #col2)
308+ ["false" `as` #col1 :* false `as` #col2]
311+SELECT * FROM (VALUES (E'true', TRUE), (E'false', FALSE)) AS t ("col1", "col2")
315set operations:
313set operations:
319− query :: Query
317+ query :: Query_ (Public Schema) () (Row Int32 Int32)
320− '["tab" ::: 'Table ('[] :=> '["col" ::: 'NoDef :=> 'Null 'PGint4])]
318+ query = select Star (from (table #tab)) `unionAll` select Star (from (table #tab))
322− '["col" ::: 'Null 'PGint4]
324− selectStar (from (table #tab))
326− selectStar (from (table #tab))
327in printSQL query
319in printSQL query
329(SELECT * FROM "tab" AS "tab") UNION ALL (SELECT * FROM "tab" AS "tab")
321(SELECT * FROM "tab" AS "tab") UNION ALL (SELECT * FROM "tab" AS "tab")
@@ -332,173 +324,305 @@ with queries:
335− query :: Query
327+ query :: Query_ (Public Schema) () (Row Int32 Int32)
337− '[ "c1" ::: 'NotNull 'PGtext
338− , "c2" ::: 'NotNull 'PGtext] ]
340− '[ "c1" ::: 'NotNull 'PGtext
341− , "c2" ::: 'NotNull 'PGtext ]
342 query = with (
328 query = with (
343− selectStar (from (view #t1)) `as` #t2 :>>
329+ select Star (from (table #tab)) `as` #cte1 :>>
344− selectStar (from (view #t2)) `as` #t3
330+ select Star (from (common #cte1)) `as` #cte2
345− ) (selectStar (from (view #t3)))
331+ ) (select Star (from (common #cte2)))
334+WITH "cte1" AS (SELECT * FROM "tab" AS "tab"), "cte2" AS (SELECT * FROM "cte1" AS "cte1") SELECT * FROM "cte2" AS "cte2"
336+window function queries
340+ query :: Query_ (Public Schema) () (Row Int32 Int64)
342+ (#col1 & Also (rank `as` #col2 `Over` (partitionBy #col1 & orderBy [#col2 & Asc])))
346in printSQL query
344in printSQL query
348−WITH "t2" AS (SELECT * FROM "t1" AS "t1"), "t3" AS (SELECT * FROM "t2" AS "t2") SELECT * FROM "t3" AS "t3"
346+SELECT "col1" AS "col1", rank() OVER (PARTITION BY "col1" ORDER BY "col2" ASC) AS "col2" FROM "tab" AS "tab"
348+correlated subqueries
352+ query :: Query_ (Public Schema) () (Only Int32)
354+ select (#col1 `as` #fromOnly) (from (table (#tab `as` #t1))
356+ select Star (from (table (#tab `as` #t2))
357+ & where_ (#t2 ! #col2 .== #t1 ! #col1)))))
360+SELECT "col1" AS "fromOnly" FROM "tab" AS "t1" WHERE EXISTS (SELECT * FROM "tab" AS "t2" WHERE ("t2"."col2" = "t1"."col1"))
350−newtype Query
363+type family Query_
351− (schema :: SchemaType)
364+ (schemas :: SchemasType)
352− (params :: [NullityType])
365+ (parameters :: Type)
353− (columns :: RowType)
366+ (row :: Type) where
354− = UnsafeQuery { renderQuery :: ByteString }
367+ Query_ schemas params row =
355− deriving (GHC.Generic,Show,Eq,Ord,NFData)
368+ Query '[] '[] schemas (TuplePG params) (RowPG row)
356−instance RenderSQL (Query schema params columns) where renderSQL = renderQuery
358-- | The results of two queries can be combined using the set operation
370-- | The results of two queries can be combined using the set operation
359-- `union`. Duplicate rows are eliminated.
371-- `union`. Duplicate rows are eliminated.
361− :: Query schema params columns
373+ :: Query outer commons schemas params columns
362− -> Query schema params columns
374+ -> Query outer commons schemas params columns
363− -> Query schema params columns
375+ -> Query outer commons schemas params columns
364q1 `union` q2 = UnsafeQuery $
376q1 `union` q2 = UnsafeQuery $
365− parenthesized (renderQuery q1)
377+ parenthesized (renderSQL q1)
366 <+> "UNION"
378 <+> "UNION"
367− <+> parenthesized (renderQuery q2)
379+ <+> parenthesized (renderSQL q2)
369-- | The results of two queries can be combined using the set operation
381-- | The results of two queries can be combined using the set operation
370-- `unionAll`, the disjoint union. Duplicate rows are retained.
382-- `unionAll`, the disjoint union. Duplicate rows are retained.
372− :: Query schema params columns
384+ :: Query outer commons schemas params columns
373− -> Query schema params columns
385+ -> Query outer commons schemas params columns
374− -> Query schema params columns
386+ -> Query outer commons schemas params columns
375q1 `unionAll` q2 = UnsafeQuery $
387q1 `unionAll` q2 = UnsafeQuery $
376− parenthesized (renderQuery q1)
388+ parenthesized (renderSQL q1)
377 <+> "UNION" <+> "ALL"
389 <+> "UNION" <+> "ALL"
378− <+> parenthesized (renderQuery q2)
390+ <+> parenthesized (renderSQL q2)
380-- | The results of two queries can be combined using the set operation
392-- | The results of two queries can be combined using the set operation
381-- `intersect`, the intersection. Duplicate rows are eliminated.
393-- `intersect`, the intersection. Duplicate rows are eliminated.
383− :: Query schema params columns
395+ :: Query outer commons schemas params columns
384− -> Query schema params columns
396+ -> Query outer commons schemas params columns
385− -> Query schema params columns
397+ -> Query outer commons schemas params columns
386q1 `intersect` q2 = UnsafeQuery $
398q1 `intersect` q2 = UnsafeQuery $
387− parenthesized (renderQuery q1)
399+ parenthesized (renderSQL q1)
388 <+> "INTERSECT"
400 <+> "INTERSECT"
389− <+> parenthesized (renderQuery q2)
401+ <+> parenthesized (renderSQL q2)
391-- | The results of two queries can be combined using the set operation
403-- | The results of two queries can be combined using the set operation
392-- `intersectAll`, the intersection. Duplicate rows are retained.
404-- `intersectAll`, the intersection. Duplicate rows are retained.
393intersectAll
405intersectAll
394− :: Query schema params columns
406+ :: Query outer commons schemas params columns
395− -> Query schema params columns
407+ -> Query outer commons schemas params columns
396− -> Query schema params columns
408+ -> Query outer commons schemas params columns
397q1 `intersectAll` q2 = UnsafeQuery $
409q1 `intersectAll` q2 = UnsafeQuery $
398− parenthesized (renderQuery q1)
410+ parenthesized (renderSQL q1)
399 <+> "INTERSECT" <+> "ALL"
411 <+> "INTERSECT" <+> "ALL"
400− <+> parenthesized (renderQuery q2)
412+ <+> parenthesized (renderSQL q2)
402-- | The results of two queries can be combined using the set operation
414-- | The results of two queries can be combined using the set operation
403-- `except`, the set difference. Duplicate rows are eliminated.
415-- `except`, the set difference. Duplicate rows are eliminated.
405− :: Query schema params columns
417+ :: Query outer commons schemas params columns
406− -> Query schema params columns
418+ -> Query outer commons schemas params columns
407− -> Query schema params columns
419+ -> Query outer commons schemas params columns
408q1 `except` q2 = UnsafeQuery $
420q1 `except` q2 = UnsafeQuery $
409− parenthesized (renderQuery q1)
421+ parenthesized (renderSQL q1)
410 <+> "EXCEPT"
422 <+> "EXCEPT"
411− <+> parenthesized (renderQuery q2)
423+ <+> parenthesized (renderSQL q2)
413-- | The results of two queries can be combined using the set operation
425-- | The results of two queries can be combined using the set operation
414-- `exceptAll`, the set difference. Duplicate rows are retained.
426-- `exceptAll`, the set difference. Duplicate rows are retained.
416− :: Query schema params columns
428+ :: Query outer commons schemas params columns
417− -> Query schema params columns
429+ -> Query outer commons schemas params columns
418− -> Query schema params columns
430+ -> Query outer commons schemas params columns
419q1 `exceptAll` q2 = UnsafeQuery $
431q1 `exceptAll` q2 = UnsafeQuery $
420− parenthesized (renderQuery q1)
432+ parenthesized (renderSQL q1)
421 <+> "EXCEPT" <+> "ALL"
433 <+> "EXCEPT" <+> "ALL"
422− <+> parenthesized (renderQuery q2)
434+ <+> parenthesized (renderSQL q2)
424{-----------------------------------------
436{-----------------------------------------
425SELECT queries
437SELECT queries
426-----------------------------------------}
438-----------------------------------------}
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`.
446+data Selection outer commons grp schemas params from row where
448+ :: HasUnique tab from row
449+ => Selection outer commons 'Ungrouped schemas params from row
450+ -- ^ `HasUnique` table in the `FromClause`
454+ -- ^ `Has` table with `Alias`
455+ -> Selection outer commons 'Ungrouped schemas params from 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
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
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
474+instance (KnownSymbol col, row ~ '[col ::: ty])
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
484+ ( Has tab (Join outer from) row0
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])
493+ (Selection outer commons 'Ungrouped schemas params from row1) where
494+ fromLabel = fromLabel @col `as` Alias
496+ ( HasUnique tab (Join outer from) row0
498+ , row1 ~ '[col ::: ty]
499+ , GroupedBy tab col bys )
501+ (Selection outer commons ('Grouped bys) schemas params from row1) where
502+ fromLabel = fromLabel @col `as` Alias
504+instance RenderSQL (Selection outer commons grp schemas params from row) where
506+ List list -> renderCommaSeparated (renderAliased renderSQL) list
508+ DotStar tab -> renderSQL tab <> ".*"
509+ Also right left -> renderSQL left <> ", " <> renderSQL right
510+ Over winFns winDef ->
513+ :: Aliased (WindowFunction outer commons grp schemas params from) field
515+ renderOver (winFn `As` col) = renderSQL winFn
516+ <+> "OVER" <+> parenthesized (renderSQL winDef)
517+ <+> "AS" <+> renderSQL col
519+ renderCommaSeparated renderOver winFns
522+ (Selection outer commons grp schemas params from '["fromOnly" ::: 'NotNull 'PGtext]) where
523+ fromString str = fromString str `as` Alias
428-- | the `TableExpression` in the `select` command constructs an intermediate
525-- | the `TableExpression` in the `select` command constructs an intermediate
429-- virtual table by possibly combining tables, views, eliminating rows,
526-- virtual table by possibly combining tables, views, eliminating rows,
430-- grouping, etc. This table is finally passed on to processing by
527-- grouping, etc. This table is finally passed on to processing by
431−-- the select list. The select list determines which columns of
528+-- the select list. The `Selection` determines which columns of
432-- the intermediate table are actually output.
529-- the intermediate table are actually output.
434− :: SListI columns
531+ :: (SListI row, row ~ (x ': xs))
435− => NP (Aliased (Expression schema from grouping params)) (column ': columns)
532+ => Selection outer commons grp schemas params from row
436− -- ^ select list
533+ -- ^ selection
437− -> TableExpression schema params from grouping
534+ -> TableExpression outer commons grp schemas params from
438 -- ^ intermediate virtual table
535 -- ^ intermediate virtual table
439− -> Query schema params (column ': columns)
536+ -> Query outer commons schemas params row
440−select list rels = UnsafeQuery $
537+select selection tabexpr = UnsafeQuery $
442− <+> renderCommaSeparated (renderAliasedAs renderExpression) list
539+ <+> renderSQL selection
443− <+> renderTableExpression rels
540+ <+> renderSQL tabexpr
542+-- | Like `select` but takes an `NP` list of `Expression`s instead
543+-- of a general `Selection`.
545+ :: (SListI row, row ~ (x ': xs))
546+ => NP (Aliased (Expression outer commons grp schemas params from)) row
548+ -> TableExpression outer commons grp schemas params from
549+ -- ^ intermediate virtual table
550+ -> Query outer commons schemas params row
551+select_ = select . List
445-- | After the select list has been processed, the result table can
553-- | After the select list has been processed, the result table can
446-- be subject to the elimination of duplicate rows using `selectDistinct`.
554-- be subject to the elimination of duplicate rows using `selectDistinct`.
447selectDistinct
555selectDistinct
448− :: SListI columns
556+ :: (SListI columns, columns ~ (col ': cols))
449− => NP (Aliased (Expression schema from 'Ungrouped params)) (column ': columns)
557+ => Selection outer commons grp schemas params from columns
450− -- ^ select list
558+ -- ^ selection
451− -> TableExpression schema params from 'Ungrouped
559+ -> TableExpression outer commons grp schemas params from
452 -- ^ intermediate virtual table
560 -- ^ intermediate virtual table
453− -> Query schema params (column ': columns)
561+ -> Query outer commons schemas params columns
454−selectDistinct list rels = UnsafeQuery $
562+selectDistinct selection tabexpr = UnsafeQuery $
455 "SELECT DISTINCT"
563 "SELECT DISTINCT"
456− <+> renderCommaSeparated (renderAliasedAs renderExpression) list
564+ <+> renderSQL selection
457− <+> renderTableExpression rels
565+ <+> renderSQL tabexpr
459−-- | The simplest kind of query is `selectStar` which emits all columns
567+-- | Like `selectDistinct` but takes an `NP` list of `Expression`s instead
460−-- that the table expression produces.
568+-- of a general `Selection`.
461−selectStar
569+selectDistinct_
462− :: HasUnique table from columns
570+ :: (SListI columns, columns ~ (col ': cols))
463− => TableExpression schema params from 'Ungrouped
571+ => NP (Aliased (Expression outer commons grp schemas params from)) columns
464− -- ^ intermediate virtual table
572+ -- ^ select list
465− -> Query schema params columns
573+ -> TableExpression outer commons grp schemas params from
466−selectStar rels = UnsafeQuery $ "SELECT" <+> "*" <+> renderTableExpression rels
468−-- | A `selectDistinctStar` emits all columns that the table expression
469−-- produces and eliminates duplicate rows.
471− :: HasUnique table from columns
472− => TableExpression schema params from 'Ungrouped
473 -- ^ intermediate virtual table
574 -- ^ intermediate virtual table
474− -> Query schema params columns
575+ -> Query outer commons schemas params columns
475−selectDistinctStar rels = UnsafeQuery $
576+selectDistinct_ = selectDistinct . List
476− "SELECT DISTINCT" <+> "*" <+> renderTableExpression rels
577+
478−-- | When working with multiple tables, it can also be useful to ask
579+`selectDistinctOn` keeps only the first row of each set of rows where
479−-- for all the columns of a particular table, using `selectDotStar`.
580+the given expressions evaluate to equal. The DISTINCT ON expressions are
480−selectDotStar
581+interpreted using the same rules as for ORDER BY. ORDER BY is used to
481− :: Has table from columns
582+ensure that the desired row appears first.
483− -- ^ particular virtual subtable
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.
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.
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
484− -> TableExpression schema params from 'Ungrouped
597+ -> TableExpression outer commons grp schemas params from
485 -- ^ intermediate virtual table
598 -- ^ intermediate virtual table
486− -> Query schema params columns
599+ -> Query outer commons schemas params columns
487−selectDotStar rel tab = UnsafeQuery $
600+selectDistinctOn distincts selection tab = UnsafeQuery $
488− "SELECT" <+> renderAlias rel <> ".*" <+> renderTableExpression tab
601+ "SELECT DISTINCT ON"
489−
602+ <+> parenthesized (commaSeparated (renderDistinctOn <$> distincts))
490−-- | A `selectDistinctDotStar` asks for all the columns of a particular table,
603+ <+> renderSQL selection
491−-- and eliminates duplicate rows.
604+ <+> renderSQL (tab {orderByClause = distincts <> orderByClause tab})
492−selectDistinctDotStar
605+ where
493− :: Has table from columns
606+ renderDistinctOn = \case
494− => Alias table
607+ Asc expression -> renderSQL expression
495− -- ^ particular virtual table
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
614+-- | Like `selectDistinctOn` but takes an `NP` list of `Expression`s instead
615+-- of a general `Selection`.
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
496− -> TableExpression schema params from 'Ungrouped
622+ -> TableExpression outer commons grp schemas params from
497 -- ^ intermediate virtual table
623 -- ^ intermediate virtual table
498− -> Query schema params columns
624+ -> Query outer commons schemas params columns
499−selectDistinctDotStar rel tab = UnsafeQuery $
625+selectDistinctOn_ distincts = selectDistinctOn distincts . List
500− "SELECT DISTINCT" <+> renderAlias rel <> ".*"
501− <+> renderTableExpression tab
503-- | `values` computes a row value or set of row values
627-- | `values` computes a row value or set of row values
504-- specified by value expressions. It is most commonly used
628-- specified by value expressions. It is most commonly used
@@ -506,16 +630,16 @@ selectDistinctDotStar rel tab = UnsafeQuery $
506-- but it can be used on its own.
630-- but it can be used on its own.
508-- >>> type Row = '["a" ::: 'NotNull 'PGint4, "b" ::: 'NotNull 'PGtext]
632-- >>> type Row = '["a" ::: 'NotNull 'PGint4, "b" ::: 'NotNull 'PGtext]
509−-- >>> let query = values (1 `as` #a :* "one" `as` #b) [] :: Query '[] '[] Row
633+-- >>> let query = values (1 `as` #a :* "one" `as` #b) [] :: Query outer commons schemas '[] Row
510-- >>> printSQL query
634-- >>> printSQL query
511-- SELECT * FROM (VALUES (1, E'one')) AS t ("a", "b")
635-- SELECT * FROM (VALUES (1, E'one')) AS t ("a", "b")
513 :: SListI cols
637 :: SListI cols
514− => NP (Aliased (Expression schema '[] 'Ungrouped params)) cols
638+ => NP (Aliased (Expression outer commons 'Ungrouped schemas params '[] )) cols
515− -> [NP (Aliased (Expression schema '[] 'Ungrouped params)) cols]
639+ -> [NP (Aliased (Expression outer commons 'Ungrouped schemas params '[] )) cols]
516 -- ^ When more than one row is specified, all the rows must
640 -- ^ When more than one row is specified, all the rows must
517 -- must have the same number of elements
641 -- must have the same number of elements
518− -> Query schema params cols
642+ -> Query outer commons schemas params cols
519values rw rws = UnsafeQuery $ "SELECT * FROM"
643values rw rws = UnsafeQuery $ "SELECT * FROM"
520 <+> parenthesized (
644 <+> parenthesized (
@@ -526,17 +650,17 @@ values rw rws = UnsafeQuery $ "SELECT * FROM"
526 <+> parenthesized (renderCommaSeparated renderAliasPart rw)
650 <+> parenthesized (renderCommaSeparated renderAliasPart rw)
528 renderAliasPart, renderValuePart
652 renderAliasPart, renderValuePart
529− :: Aliased (Expression schema '[] 'Ungrouped params) ty -> ByteString
653+ :: Aliased (Expression outer commons 'Ungrouped schemas params '[] ) ty -> ByteString
530− renderAliasPart (_ `As` name) = renderAlias name
654+ renderAliasPart (_ `As` name) = renderSQL name
531− renderValuePart (value `As` _) = renderExpression value
655+ renderValuePart (value `As` _) = renderSQL value
533-- | `values_` computes a row value or set of row values
657-- | `values_` computes a row value or set of row values
534-- specified by value expressions.
658-- specified by value expressions.
536 :: SListI cols
660 :: SListI cols
537− => NP (Aliased (Expression schema '[] 'Ungrouped params)) cols
661+ => NP (Aliased (Expression outer commons 'Ungrouped schemas params '[] )) cols
538 -- ^ one row of values
662 -- ^ one row of values
539− -> Query schema params cols
663+ -> Query outer commons schemas params cols
540values_ rw = values rw []
664values_ rw = values rw []
542{-----------------------------------------
666{-----------------------------------------
@@ -550,15 +674,17 @@ Table Expressions
550-- to a table on disk, a so-called base table, but more complex expressions
674-- to a table on disk, a so-called base table, but more complex expressions
551-- can be used to modify or combine base tables in various ways.
675-- can be used to modify or combine base tables in various ways.
552data TableExpression
676data TableExpression
553− (schema :: SchemaType)
677+ (outer :: FromType)
678+ (commons :: FromType)
680+ (schemas :: SchemasType)
554 (params :: [NullityType])
681 (params :: [NullityType])
555 (from :: FromType)
682 (from :: FromType)
556− (grouping :: Grouping)
557 = TableExpression
683 = TableExpression
558− { fromClause :: FromClause schema params from
684+ { fromClause :: FromClause outer commons schemas params from
559 -- ^ A table reference that can be a table name, or a derived table such
685 -- ^ A table reference that can be a table name, or a derived table such
560 -- as a subquery, a @JOIN@ construct, or complex combinations of these.
686 -- as a subquery, a @JOIN@ construct, or complex combinations of these.
561− , whereClause :: [Condition schema from 'Ungrouped params]
687+ , whereClause :: [Condition outer commons 'Ungrouped schemas params from]
562 -- ^ optional search coditions, combined with `.&&`. After the processing
688 -- ^ optional search coditions, combined with `.&&`. After the processing
563 -- of the `fromClause` is done, each row of the derived virtual table
689 -- of the `fromClause` is done, each row of the derived virtual table
564 -- is checked against the search condition. If the result of the
690 -- is checked against the search condition. If the result of the
@@ -567,20 +693,20 @@ data TableExpression
567 -- at least one column of the table generated in the `fromClause`;
693 -- at least one column of the table generated in the `fromClause`;
568 -- this is not required, but otherwise the WHERE clause will
694 -- this is not required, but otherwise the WHERE clause will
569 -- be fairly useless.
695 -- be fairly useless.
570− , groupByClause :: GroupByClause from grouping
696+ , groupByClause :: GroupByClause grp from
571 -- ^ The `groupByClause` is used to group together those rows in a table
697 -- ^ The `groupByClause` is used to group together those rows in a table
572 -- that have the same values in all the columns listed. The order in which
698 -- that have the same values in all the columns listed. The order in which
573 -- the columns are listed does not matter. The effect is to combine each
699 -- the columns are listed does not matter. The effect is to combine each
574 -- set of rows having common values into one group row that represents all
700 -- set of rows having common values into one group row that represents all
575 -- rows in the group. This is done to eliminate redundancy in the output
701 -- rows in the group. This is done to eliminate redundancy in the output
576 -- and/or compute aggregates that apply to these groups.
702 -- and/or compute aggregates that apply to these groups.
577− , havingClause :: HavingClause schema from grouping params
703+ , havingClause :: HavingClause outer commons grp schemas params from
578 -- ^ If a table has been grouped using `groupBy`, but only certain groups
704 -- ^ If a table has been grouped using `groupBy`, but only certain groups
579 -- are of interest, the `havingClause` can be used, much like a
705 -- are of interest, the `havingClause` can be used, much like a
580 -- `whereClause`, to eliminate groups from the result. Expressions in the
706 -- `whereClause`, to eliminate groups from the result. Expressions in the
581 -- `havingClause` can refer both to grouped expressions and to ungrouped
707 -- `havingClause` can refer both to grouped expressions and to ungrouped
582 -- expressions (which necessarily involve an aggregate function).
708 -- expressions (which necessarily involve an aggregate function).
583− , orderByClause :: [SortExpression schema from grouping params]
709+ , orderByClause :: [SortExpression outer commons grp schemas params from]
584 -- ^ The `orderByClause` is for optional sorting. When more than one
710 -- ^ The `orderByClause` is for optional sorting. When more than one
585 -- `SortExpression` is specified, the later (right) values are used to sort
711 -- `SortExpression` is specified, the later (right) values are used to sort
586 -- rows that are equal according to the earlier (left) values.
712 -- rows that are equal according to the earlier (left) values.
@@ -590,72 +716,69 @@ data TableExpression
590 -- will be returned (but possibly fewer, if the query itself yields
716 -- will be returned (but possibly fewer, if the query itself yields
591 -- fewer rows).
717 -- fewer rows).
592 , offsetClause :: [Word64]
718 , offsetClause :: [Word64]
593− -- ^ The `offsetClause` is combined with `+` to give an offset count
719+ -- ^ The `offsetClause` is combined with `Prelude.+` to give an offset count
594 -- if nonempty. The offset count says to skip that many rows before
720 -- if nonempty. The offset count says to skip that many rows before
595 -- beginning to return rows. The rows are skipped before the limit count
721 -- beginning to return rows. The rows are skipped before the limit count
596 -- is applied.
722 -- is applied.
597− }
723+ } deriving (GHC.Generic)
599-- | Render a `TableExpression`
725-- | Render a `TableExpression`
600−renderTableExpression
601− :: TableExpression schema params from grouping
726+instance RenderSQL (TableExpression outer commons grp schemas params from) where
602− -> ByteString
727+ renderSQL
603−renderTableExpression
604− (TableExpression frm' whs' grps' hvs' srts' lims' offs') = mconcat
728+ (TableExpression frm' whs' grps' hvs' srts' lims' offs') = mconcat
605− [ "FROM ", renderFromClause frm'
729+ [ "FROM ", renderSQL frm'
606− , renderWheres whs'
730+ , renderWheres whs'
607− , renderGroupByClause grps'
731+ , renderSQL grps'
608− , renderHavingClause hvs'
732+ , renderSQL hvs'
609− , renderOrderByClause srts'
733+ , renderOrderByClause srts'
610− , renderLimits lims'
734+ , renderLimits lims'
611− , renderOffsets offs'
735+ , renderOffsets offs' ]
614− renderWheres = \case
737+ renderWheres = \case
615− [] -> ""
738+ [] -> ""
616− wh:[] -> " WHERE" <+> renderExpression wh
739+ wh:[] -> " WHERE" <+> renderSQL wh
617− wh:whs -> " WHERE" <+> renderExpression (foldr (.&&) wh whs)
740+ wh:whs -> " WHERE" <+> renderSQL (foldr (.&&) wh whs)
618− renderOrderByClause = \case
741+ renderOrderByClause = \case
619− [] -> ""
742+ [] -> ""
620− srts -> " ORDER BY"
743+ srts -> " ORDER BY"
621− <+> commaSeparated (renderSortExpression <$> srts)
744+ <+> commaSeparated (renderSQL <$> srts)
622− renderLimits = \case
745+ renderLimits = \case
623− [] -> ""
746+ [] -> ""
624− lims -> " LIMIT" <+> fromString (show (minimum lims))
747+ lims -> " LIMIT" <+> fromString (show (minimum lims))
625− renderOffsets = \case
748+ renderOffsets = \case
626− [] -> ""
749+ [] -> ""
627− offs -> " OFFSET" <+> fromString (show (sum offs))
750+ offs -> " OFFSET" <+> fromString (show (sum offs))
629-- | A `from` generates a `TableExpression` from a table reference that can be
752-- | A `from` generates a `TableExpression` from a table reference that can be
630-- a table name, or a derived table such as a subquery, a JOIN construct,
753-- a table name, or a derived table such as a subquery, a JOIN construct,
631-- or complex combinations of these. A `from` may be transformed by `where_`,
754-- or complex combinations of these. A `from` may be transformed by `where_`,
632−-- `group`, `having`, `orderBy`, `limit` and `offset`, using the `&` operator
755+-- `groupBy`, `having`, `orderBy`, `limit` and `offset`, using the `&` operator
633-- to match the left-to-right sequencing of their placement in SQL.
756-- to match the left-to-right sequencing of their placement in SQL.
635− :: FromClause schema params from -- ^ table reference
758+ :: FromClause outer commons schemas params from -- ^ table reference
636− -> TableExpression schema params from 'Ungrouped
759+ -> TableExpression outer commons 'Ungrouped schemas params from
637−from rels = TableExpression rels [] NoGroups NoHaving [] [] []
760+from tab = TableExpression tab [] noGroups NoHaving [] [] []
639-- | A `where_` is an endomorphism of `TableExpression`s which adds a
762-- | A `where_` is an endomorphism of `TableExpression`s which adds a
640-- search condition to the `whereClause`.
763-- search condition to the `whereClause`.
642− :: Condition schema from 'Ungrouped params -- ^ filtering condition
765+ :: Condition outer commons 'Ungrouped schemas params from -- ^ filtering condition
643− -> TableExpression schema params from grouping
766+ -> TableExpression outer commons grp schemas params from
644− -> TableExpression schema params from grouping
767+ -> TableExpression outer commons grp schemas params from
645where_ wh rels = rels {whereClause = wh : whereClause rels}
768where_ wh rels = rels {whereClause = wh : whereClause rels}
647-- | A `groupBy` is a transformation of `TableExpression`s which switches
770-- | A `groupBy` is a transformation of `TableExpression`s which switches
648−-- its `Grouping` from `Ungrouped` to `Grouped`. Use @group Nil@ to perform
771+-- its `Grouping` from `Ungrouped` to `Grouped`. Use @groupBy Nil@ to perform
649-- a "grand total" aggregation query.
772-- a "grand total" aggregation query.
651 :: SListI bys
774 :: SListI bys
652 => NP (By from) bys -- ^ grouped columns
775 => NP (By from) bys -- ^ grouped columns
653− -> TableExpression schema params from 'Ungrouped
776+ -> TableExpression outer commons 'Ungrouped schemas params from
654− -> TableExpression schema params from ('Grouped bys)
777+ -> TableExpression outer commons ('Grouped bys) schemas params from
655groupBy bys rels = TableExpression
778groupBy bys rels = TableExpression
656 { fromClause = fromClause rels
779 { fromClause = fromClause rels
657 , whereClause = whereClause rels
780 , whereClause = whereClause rels
658− , groupByClause = Group bys
781+ , groupByClause = group bys
659 , havingClause = Having []
782 , havingClause = Having []
660 , orderByClause = []
783 , orderByClause = []
661 , limitClause = limitClause rels
784 , limitClause = limitClause rels
@@ -665,179 +788,31 @@ groupBy bys rels = TableExpression
665-- | A `having` is an endomorphism of `TableExpression`s which adds a
788-- | A `having` is an endomorphism of `TableExpression`s which adds a
666-- search condition to the `havingClause`.
789-- search condition to the `havingClause`.
668− :: Condition schema from ('Grouped bys) params -- ^ having condition
791+ :: Condition outer commons ('Grouped bys) schemas params from -- ^ having condition
669− -> TableExpression schema params from ('Grouped bys)
792+ -> TableExpression outer commons ('Grouped bys) schemas params from
670− -> TableExpression schema params from ('Grouped bys)
793+ -> TableExpression outer commons ('Grouped bys) schemas params from
671having hv rels = rels
794having hv rels = rels
672 { havingClause = case havingClause rels of Having hvs -> Having (hv:hvs) }
795 { havingClause = case havingClause rels of Having hvs -> Having (hv:hvs) }
674−-- | An `orderBy` is an endomorphism of `TableExpression`s which appends an
797+instance OrderBy TableExpression where
675−-- ordering to the right of the `orderByClause`.
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}
798+ orderBy srts rels = rels {orderByClause = orderByClause rels ++ srts}
682-- | A `limit` is an endomorphism of `TableExpression`s which adds to the
800-- | A `limit` is an endomorphism of `TableExpression`s which adds to the
683-- `limitClause`.
801-- `limitClause`.
685 :: Word64 -- ^ limit parameter
803 :: Word64 -- ^ limit parameter
686− -> TableExpression schema params from grouping
804+ -> TableExpression outer commons grp schemas params from
687− -> TableExpression schema params from grouping
805+ -> TableExpression outer commons grp schemas params from
688limit lim rels = rels {limitClause = lim : limitClause rels}
806limit lim rels = rels {limitClause = lim : limitClause rels}
690-- | An `offset` is an endomorphism of `TableExpression`s which adds to the
808-- | An `offset` is an endomorphism of `TableExpression`s which adds to the
691-- `offsetClause`.
809-- `offsetClause`.
693 :: Word64 -- ^ offset parameter
811 :: Word64 -- ^ offset parameter
694− -> TableExpression schema params from grouping
812+ -> TableExpression outer commons grp schemas params from
695− -> TableExpression schema params from grouping
813+ -> TableExpression outer commons grp schemas params from
696offset off rels = rels {offsetClause = off : offsetClause rels}
814offset off rels = rels {offsetClause = off : offsetClause rels}
698−{-----------------------------------------
700−-----------------------------------------}
704− -> Expression schema '[] 'Ungrouped params ty
705− -> Query schema params row
706−unsafeSetOfFunction fun expr = UnsafeQuery $
707− "SELECT * FROM " <> fun <> "(" <> renderExpression expr <> ")"
709−-- | Expands the outermost JSON object into a set of key/value pairs.
711− :: Expression schema '[] 'Ungrouped params (nullity 'PGjson) -- ^ json
712− -> Query schema params
713− '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGjson]
714−jsonEach = unsafeSetOfFunction "json_each"
716−-- | Expands the outermost binary JSON object into a set of key/value pairs.
718− :: Expression schema '[] 'Ungrouped params (nullity 'PGjsonb) -- ^ jsonb
719− -> Query schema params
720− '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGjsonb]
721−jsonbEach = unsafeSetOfFunction "jsonb_each"
723−-- | Expands the outermost JSON object into a set of key/value pairs.
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"
730−-- | Expands the outermost binary JSON object into a set of key/value pairs.
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"
737−-- | Returns set of keys in the outermost JSON object.
739− :: Expression schema '[] 'Ungrouped params (nullity 'PGjson)
740− -> Query schema params '["json_object_keys" ::: 'NotNull 'PGtext]
741−jsonObjectKeys = unsafeSetOfFunction "json_object_keys"
743−-- | Returns set of keys in the outermost JSON object.
745− :: Expression schema '[] 'Ungrouped params (nullity 'PGjsonb)
746− -> Query schema params '["jsonb_object_keys" ::: 'NotNull 'PGtext]
747−jsonbObjectKeys = unsafeSetOfFunction "jsonb_object_keys"
749−unsafePopulateFunction
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 <> ")"
759−-- | Expands the JSON expression to a row whose columns match the record
760−-- type defined by the given table.
762− :: TypeExpression schema (nullity ('PGcomposite row))
763− -> Expression schema '[] 'Ungrouped params (nullity 'PGjson)
764− -> Query schema params row
765−jsonPopulateRecord = unsafePopulateFunction "json_populate_record"
767−-- | Expands the binary JSON expression to a row whose columns match the record
768−-- type defined by the given table.
770− :: TypeExpression schema (nullity ('PGcomposite row))
771− -> Expression schema '[] 'Ungrouped params (nullity 'PGjsonb)
772− -> Query schema params row
773−jsonbPopulateRecord = unsafePopulateFunction "jsonb_populate_record"
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"
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
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"
793− :: (SListI row, json `In` PGJsonType)
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)
803− renderTy :: Aliased (TypeExpression schema) ty -> ByteString
804− renderTy (ty `As` alias) =
805− renderAlias alias <+> renderTypeExpression ty
807−-- | Builds an arbitrary record from a JSON object.
810− => Expression schema '[] 'Ungrouped params (nullity 'PGjson)
811− -> NP (Aliased (TypeExpression schema)) row
812− -> Query schema params row
813−jsonToRecord = unsafeRecordFunction "json_to_record"
815−-- | Builds an arbitrary record from a binary JSON object.
818− => Expression schema '[] 'Ungrouped params (nullity 'PGjsonb)
819− -> NP (Aliased (TypeExpression schema)) row
820− -> Query schema params row
821−jsonbToRecord = unsafeRecordFunction "jsonb_to_record"
823−-- | Builds an arbitrary set of records from a JSON array of objects.
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"
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.
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"
841{-----------------------------------------
816{-----------------------------------------
842FROM clauses
817FROM clauses
843-----------------------------------------}
818-----------------------------------------}
@@ -846,61 +821,76 @@ FROM clauses
846A `FromClause` can be a table name, or a derived table such
821A `FromClause` can be a table name, or a derived table such
847as a subquery, a @JOIN@ construct, or complex combinations of these.
822as a subquery, a @JOIN@ construct, or complex combinations of these.
849−newtype FromClause schema params from
824+newtype FromClause outer commons schemas params from
850 = UnsafeFromClause { renderFromClause :: ByteString }
825 = UnsafeFromClause { renderFromClause :: ByteString }
851 deriving (GHC.Generic,Show,Eq,Ord,NFData)
826 deriving (GHC.Generic,Show,Eq,Ord,NFData)
827+instance RenderSQL (FromClause outer commons schemas params from) where
828+ renderSQL = renderFromClause
853−-- | A real `table` is a table from the schema.
830+-- | A real `table` is a table from the database.
855− :: Has tab schema ('Table table)
832+ :: (Has sch schemas schema, Has tab schema ('Table table))
856− => Aliased Alias (alias ::: tab)
833+ => Aliased (QualifiedAlias sch) (alias ::: tab)
857− -> FromClause schema params '[alias ::: TableToRow table]
834+ -> FromClause outer commons schemas params '[alias ::: TableToRow table]
858table (tab `As` alias) = UnsafeFromClause $
835table (tab `As` alias) = UnsafeFromClause $
859− renderAlias tab <+> "AS" <+> renderAlias alias
836+ renderSQL tab <+> "AS" <+> renderSQL alias
861-- | `subquery` derives a table from a `Query`.
838-- | `subquery` derives a table from a `Query`.
863− :: Aliased (Query schema params) rel
840+ :: Aliased (Query outer commons schemas params) query
864− -> FromClause schema params '[rel]
841+ -> FromClause outer commons schemas params '[query]
865−subquery = UnsafeFromClause . renderAliasedAs (parenthesized . renderQuery)
842+subquery = UnsafeFromClause . renderAliased (parenthesized . renderSQL)
867-- | `view` derives a table from a `View`.
844-- | `view` derives a table from a `View`.
869− :: Has view schema ('View row)
846+ :: (Has sch schemas schema, Has vw schema ('View view))
870− => Aliased Alias (alias ::: view)
847+ => Aliased (QualifiedAlias sch) (alias ::: vw)
871− -> FromClause schema params '[alias ::: row]
848+ -> FromClause outer commons schemas params '[alias ::: view]
872view (vw `As` alias) = UnsafeFromClause $
849view (vw `As` alias) = UnsafeFromClause $
873− renderAlias vw <+> "AS" <+> renderAlias alias
850+ renderSQL vw <+> "AS" <+> renderSQL alias
852+-- | `common` derives a table from a common table expression.
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
860+instance Additional (FromClause outer commons schemas params) where
861+ also right left = UnsafeFromClause $
862+ renderSQL left <> ", " <> renderSQL right
875−{- | @left & crossJoin right@. For every possible combination of rows from
865+@left & crossJoin right@. For every possible combination of rows from
876− @left@ and @right@ (i.e., a Cartesian product), the joined table will contain
866+@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@.
867+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
868+If the tables have @n@ and @m@ rows respectively, the joined table will
879− have @n * m@ rows.
869+have @n * m@ rows.
882− :: FromClause schema params right
872+ :: FromClause outer commons schemas params right
883 -- ^ right
873 -- ^ right
884− -> FromClause schema params left
874+ -> FromClause outer commons schemas params left
885 -- ^ left
875 -- ^ left
886− -> FromClause schema params (Join left right)
876+ -> FromClause outer commons schemas params (Join left right)
887crossJoin right left = UnsafeFromClause $
877crossJoin right left = UnsafeFromClause $
888− renderFromClause left <+> "CROSS JOIN" <+> renderFromClause right
878+ renderSQL left <+> "CROSS JOIN" <+> renderSQL right
890{- | @left & innerJoin right on@. The joined table is filtered by
880{- | @left & innerJoin right on@. The joined table is filtered by
891the @on@ condition.
881the @on@ condition.
894− :: FromClause schema params right
884+ :: FromClause outer commons schemas params right
895 -- ^ right
885 -- ^ right
896− -> Condition schema (Join left right) 'Ungrouped params
886+ -> Condition outer commons 'Ungrouped schemas params (Join left right)
897 -- ^ @on@ condition
887 -- ^ @on@ condition
898− -> FromClause schema params left
888+ -> FromClause outer commons schemas params left
899 -- ^ left
889 -- ^ left
900− -> FromClause schema params (Join left right)
890+ -> FromClause outer commons schemas params (Join left right)
901innerJoin right on left = UnsafeFromClause $
891innerJoin right on left = UnsafeFromClause $
902− renderFromClause left <+> "INNER JOIN" <+> renderFromClause right
892+ renderSQL left <+> "INNER JOIN" <+> renderSQL right
903− <+> "ON" <+> renderExpression on
893+ <+> "ON" <+> renderSQL on
905{- | @left & leftOuterJoin right on@. First, an inner join is performed.
895{- | @left & leftOuterJoin right on@. First, an inner join is performed.
906 Then, for each row in @left@ that does not satisfy the @on@ condition with
896 Then, for each row in @left@ that does not satisfy the @on@ condition with
@@ -908,16 +898,16 @@ innerJoin right on left = UnsafeFromClause $
908 Thus, the joined table always has at least one row for each row in @left@.
898 Thus, the joined table always has at least one row for each row in @left@.
910leftOuterJoin
900leftOuterJoin
911− :: FromClause schema params right
901+ :: FromClause outer commons schemas params right
912 -- ^ right
902 -- ^ right
913− -> Condition schema (Join left right) 'Ungrouped params
903+ -> Condition outer commons 'Ungrouped schemas params (Join left right)
914 -- ^ @on@ condition
904 -- ^ @on@ condition
915− -> FromClause schema params left
905+ -> FromClause outer commons schemas params left
916 -- ^ left
906 -- ^ left
917− -> FromClause schema params (Join left (NullifyFrom right))
907+ -> FromClause outer commons schemas params (Join left (NullifyFrom right))
918leftOuterJoin right on left = UnsafeFromClause $
908leftOuterJoin right on left = UnsafeFromClause $
919− renderFromClause left <+> "LEFT OUTER JOIN" <+> renderFromClause right
909+ renderSQL left <+> "LEFT OUTER JOIN" <+> renderSQL right
920− <+> "ON" <+> renderExpression on
910+ <+> "ON" <+> renderSQL on
922{- | @left & rightOuterJoin right on@. First, an inner join is performed.
912{- | @left & rightOuterJoin right on@. First, an inner join is performed.
923 Then, for each row in @right@ that does not satisfy the @on@ condition with
913 Then, for each row in @right@ that does not satisfy the @on@ condition with
@@ -926,16 +916,16 @@ leftOuterJoin right on left = UnsafeFromClause $
926 have a row for each row in @right@.
916 have a row for each row in @right@.
928rightOuterJoin
918rightOuterJoin
929− :: FromClause schema params right
919+ :: FromClause outer commons schemas params right
930 -- ^ right
920 -- ^ right
931− -> Condition schema (Join left right) 'Ungrouped params
921+ -> Condition outer commons 'Ungrouped schemas params (Join left right)
932 -- ^ @on@ condition
922 -- ^ @on@ condition
933− -> FromClause schema params left
923+ -> FromClause outer commons schemas params left
934 -- ^ left
924 -- ^ left
935− -> FromClause schema params (Join (NullifyFrom left) right)
925+ -> FromClause outer commons schemas params (Join (NullifyFrom left) right)
936rightOuterJoin right on left = UnsafeFromClause $
926rightOuterJoin right on left = UnsafeFromClause $
937− renderFromClause left <+> "RIGHT OUTER JOIN" <+> renderFromClause right
927+ renderSQL left <+> "RIGHT OUTER JOIN" <+> renderSQL right
938− <+> "ON" <+> renderExpression on
928+ <+> "ON" <+> renderSQL on
940{- | @left & fullOuterJoin right on@. First, an inner join is performed.
930{- | @left & fullOuterJoin right on@. First, an inner join is performed.
941 Then, for each row in @left@ that does not satisfy the @on@ condition with
931 Then, for each row in @left@ that does not satisfy the @on@ condition with
@@ -945,23 +935,23 @@ rightOuterJoin right on left = UnsafeFromClause $
945 is added.
935 is added.
947fullOuterJoin
937fullOuterJoin
948− :: FromClause schema params right
938+ :: FromClause outer commons schemas params right
949 -- ^ right
939 -- ^ right
950− -> Condition schema (Join left right) 'Ungrouped params
940+ -> Condition outer commons 'Ungrouped schemas params (Join left right)
951 -- ^ @on@ condition
941 -- ^ @on@ condition
952− -> FromClause schema params left
942+ -> FromClause outer commons schemas params left
953 -- ^ left
943 -- ^ left
954− -> FromClause schema params
944+ -> FromClause outer commons schemas params
955 (Join (NullifyFrom left) (NullifyFrom right))
945 (Join (NullifyFrom left) (NullifyFrom right))
956fullOuterJoin right on left = UnsafeFromClause $
946fullOuterJoin right on left = UnsafeFromClause $
957− renderFromClause left <+> "FULL OUTER JOIN" <+> renderFromClause right
947+ renderSQL left <+> "FULL OUTER JOIN" <+> renderSQL right
958− <+> "ON" <+> renderExpression on
948+ <+> "ON" <+> renderSQL on
960{-----------------------------------------
950{-----------------------------------------
962-----------------------------------------}
952-----------------------------------------}
964−-- | `By`s are used in `group` to reference a list of columns which are then
954+-- | `By`s are used in `groupBy` to reference a list of columns which are then
965-- used to group together those rows in a table that have the same values
955-- used to group together those rows in a table that have the same values
966-- in all the columns listed. @By \#col@ will reference an unambiguous
956-- in all the columns listed. @By \#col@ will reference an unambiguous
967-- column @col@; otherwise @By2 (\#tab \! \#col)@ will reference a table
957-- column @col@; otherwise @By2 (\#tab \! \#col)@ will reference a table
@@ -981,6 +971,10 @@ data By
981deriving instance Show (By from by)
971deriving instance Show (By from by)
982deriving instance Eq (By from by)
972deriving instance Eq (By from by)
983deriving instance Ord (By from by)
973deriving instance Ord (By from by)
974+instance RenderSQL (By from by) where
976+ By1 column -> renderSQL column
977+ By2 rel column -> renderSQL rel <> "." <> renderSQL column
985instance (HasUnique rel rels cols, Has col cols ty, by ~ '(rel, col))
979instance (HasUnique rel rels cols, Has col cols ty, by ~ '(rel, col))
986 => IsLabel col (By rels by) where fromLabel = By1 fromLabel
980 => IsLabel col (By rels by) where fromLabel = By1 fromLabel
@@ -992,415 +986,113 @@ instance (Has rel rels cols, Has col cols ty, bys ~ '[ '(rel, col)])
992 => IsQualified rel col (NP (By rels) bys) where
986 => IsQualified rel col (NP (By rels) bys) where
993 rel ! col = By2 rel col :* Nil
987 rel ! col = By2 rel col :* Nil
996−renderBy :: By from by -> ByteString
998− By1 column -> renderAlias column
999− By2 rel column -> renderAlias rel <> "." <> renderAlias column
1001-- | A `GroupByClause` indicates the `Grouping` of a `TableExpression`.
989-- | A `GroupByClause` indicates the `Grouping` of a `TableExpression`.
1002-- A `NoGroups` indicates `Ungrouped` while a `Group` indicates `Grouped`.
990-- A `NoGroups` indicates `Ungrouped` while a `Group` indicates `Grouped`.
1003-- @NoGroups@ is distinguised from @Group Nil@ since no aggregation can be
991-- @NoGroups@ is distinguised from @Group Nil@ since no aggregation can be
1004-- done on @NoGroups@ while all output `Expression`s must be aggregated
992-- done on @NoGroups@ while all output `Expression`s must be aggregated
1005-- in @Group Nil@. In general, all output `Expression`s in the
993-- in @Group Nil@. In general, all output `Expression`s in the
1006-- complement of @bys@ must be aggregated in @Group bys@.
994-- complement of @bys@ must be aggregated in @Group bys@.
1007−data GroupByClause from grouping where
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
1008− NoGroups :: GroupByClause from 'Ungrouped
1000+noGroups :: GroupByClause 'Ungrouped from
1009− Group
1001+noGroups = UnsafeGroupByClause ""
1010− :: SListI bys
1003+ :: SListI bys
1011− => NP (By from) bys
1004+ => NP (By from) bys
1012− -> GroupByClause from ('Grouped bys)
1005+ -> GroupByClause ('Grouped bys) from
1013−
1006+group bys = UnsafeGroupByClause $ case bys of
1014−-- | Renders a `GroupByClause`.
1015−renderGroupByClause :: GroupByClause from grouping -> ByteString
1016−renderGroupByClause = \case
1018− Group Nil -> ""
1007+ Nil -> ""
1019− Group bys -> " GROUP BY" <+> renderCommaSeparated renderBy bys
1008+ bys' -> " GROUP BY" <+> renderCommaSeparated renderSQL bys'
1021-- | A `HavingClause` is used to eliminate groups that are not of interest.
1010-- | A `HavingClause` is used to eliminate groups that are not of interest.
1022-- An `Ungrouped` `TableExpression` may only use `NoHaving` while a `Grouped`
1011-- An `Ungrouped` `TableExpression` may only use `NoHaving` while a `Grouped`
1023-- `TableExpression` must use `Having` whose conditions are combined with
1012-- `TableExpression` must use `Having` whose conditions are combined with
1024-- `.&&`.
1013-- `.&&`.
1025−data HavingClause schema from grouping params where
1014+data HavingClause outer commons grp schemas params from where
1026− NoHaving :: HavingClause schema from 'Ungrouped params
1015+ NoHaving :: HavingClause outer commons 'Ungrouped schemas params from
1028− :: [Condition schema from ('Grouped bys) params]
1017+ :: [Condition outer commons ('Grouped bys) schemas params from]
1029− -> HavingClause schema from ('Grouped bys) params
1018+ -> HavingClause outer commons ('Grouped bys) schemas params from
1030−deriving instance Show (HavingClause schema from grouping params)
1019+deriving instance Show (HavingClause outer commons grp schemas params from)
1031−deriving instance Eq (HavingClause schema from grouping params)
1020+deriving instance Eq (HavingClause outer commons grp schemas params from)
1032−deriving instance Ord (HavingClause schema from grouping params)
1021+deriving instance Ord (HavingClause outer commons grp schemas params from)
1034-- | Render a `HavingClause`.
1023-- | Render a `HavingClause`.
1035−renderHavingClause :: HavingClause schema from grouping params -> ByteString
1024+instance RenderSQL (HavingClause outer commons grp schemas params from) where
1036−renderHavingClause = \case
1025+ renderSQL = \case
1037− NoHaving -> ""
1026+ NoHaving -> ""
1038− Having [] -> ""
1027+ Having [] -> ""
1039− Having conditions ->
1028+ Having conditions ->
1040− " HAVING" <+> commaSeparated (renderExpression <$> conditions)
1029+ " HAVING" <+> commaSeparated (renderSQL <$> conditions)
1042−{-----------------------------------------
1044−-----------------------------------------}
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
1056− :: Expression schema from grouping params ('NotNull ty)
1057− -> SortExpression schema from grouping params
1059− :: Expression schema from grouping params ('NotNull ty)
1060− -> SortExpression schema from grouping params
1062− :: Expression schema from grouping params ('Null ty)
1063− -> SortExpression schema from grouping params
1065− :: Expression schema from grouping params ('Null ty)
1066− -> SortExpression schema from grouping params
1068− :: Expression schema from grouping params ('Null ty)
1069− -> SortExpression schema from grouping params
1071− :: Expression schema from grouping params ('Null ty)
1072− -> SortExpression schema from grouping params
1073−deriving instance Show (SortExpression schema from grouping params)
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"
1087−unsafeSubqueryExpression
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)
1095−unsafeRowSubqueryExpression
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)
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).
1110−-- >>> printSQL $ true `in_` values_ (true `as` #foo)
1111−-- TRUE IN (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
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"
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).
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"))
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"
1139−-- | >>> printSQL $ true `allEq` values_ (true `as` #foo)
1140−-- TRUE ALL = (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1142− :: Expression schema from grp params ty
1143− -> Query schema params '[alias ::: ty]
1144− -> Condition schema from grp params
1145−allEq = unsafeSubqueryExpression "ALL ="
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"))
1152− => NP (Aliased (Expression schema from grp params)) row
1153− -> Query schema params row
1154− -> Condition schema from grp params
1155−rowAllEq = unsafeRowSubqueryExpression "ALL ="
1157−-- | >>> printSQL $ true `anyEq` values_ (true `as` #foo)
1158−-- TRUE ANY = (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1160− :: Expression schema from grp params ty
1161− -> Query schema params '[alias ::: ty]
1162− -> Condition schema from grp params
1163−anyEq = unsafeSubqueryExpression "ANY ="
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"))
1170− => NP (Aliased (Expression schema from grp params)) row
1171− -> Query schema params row
1172− -> Condition schema from grp params
1173−rowAnyEq = unsafeRowSubqueryExpression "ANY ="
1175−-- | >>> printSQL $ true `allNeq` values_ (true `as` #foo)
1176−-- TRUE ALL <> (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1178− :: Expression schema from grp params ty
1179− -> Query schema params '[alias ::: ty]
1180− -> Condition schema from grp params
1181−allNeq = unsafeSubqueryExpression "ALL <>"
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"))
1188− => NP (Aliased (Expression schema from grp params)) row
1189− -> Query schema params row
1190− -> Condition schema from grp params
1191−rowAllNeq = unsafeRowSubqueryExpression "ALL <>"
1193−-- | >>> printSQL $ true `anyNeq` values_ (true `as` #foo)
1194−-- TRUE ANY <> (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1196− :: Expression schema from grp params ty
1197− -> Query schema params '[alias ::: ty]
1198− -> Condition schema from grp params
1199−anyNeq = unsafeSubqueryExpression "ANY <>"
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"))
1206− => NP (Aliased (Expression schema from grp params)) row
1207− -> Query schema params row
1208− -> Condition schema from grp params
1209−rowAnyNeq = unsafeRowSubqueryExpression "ANY <>"
1211−-- | >>> printSQL $ true `allLt` values_ (true `as` #foo)
1212−-- TRUE ALL < (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1214− :: Expression schema from grp params ty
1215− -> Query schema params '[alias ::: ty]
1216− -> Condition schema from grp params
1217−allLt = unsafeSubqueryExpression "ALL <"
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"))
1224− => NP (Aliased (Expression schema from grp params)) row
1225− -> Query schema params row
1226− -> Condition schema from grp params
1227−rowAllLt = unsafeRowSubqueryExpression "ALL <"
1229−-- | >>> printSQL $ true `anyLt` values_ (true `as` #foo)
1230−-- TRUE ANY < (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1232− :: Expression schema from grp params ty
1233− -> Query schema params '[alias ::: ty]
1234− -> Condition schema from grp params
1235−anyLt = unsafeSubqueryExpression "ANY <"
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"))
1242− => NP (Aliased (Expression schema from grp params)) row
1243− -> Query schema params row
1244− -> Condition schema from grp params
1245−rowAnyLt = unsafeRowSubqueryExpression "ANY <"
1247−-- | >>> printSQL $ true `allLte` values_ (true `as` #foo)
1248−-- TRUE ALL <= (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1250− :: Expression schema from grp params ty
1251− -> Query schema params '[alias ::: ty]
1252− -> Condition schema from grp params
1253−allLte = unsafeSubqueryExpression "ALL <="
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"))
1260− => NP (Aliased (Expression schema from grp params)) row
1261− -> Query schema params row
1262− -> Condition schema from grp params
1263−rowAllLte = unsafeRowSubqueryExpression "ALL <="
1265−-- | >>> printSQL $ true `anyLte` values_ (true `as` #foo)
1266−-- TRUE ANY <= (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1268− :: Expression schema from grp params ty
1269− -> Query schema params '[alias ::: ty]
1270− -> Condition schema from grp params
1271−anyLte = unsafeSubqueryExpression "ANY <="
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"))
1278− => NP (Aliased (Expression schema from grp params)) row
1279− -> Query schema params row
1280− -> Condition schema from grp params
1281−rowAnyLte = unsafeRowSubqueryExpression "ANY <="
1283−-- | >>> printSQL $ true `allGt` values_ (true `as` #foo)
1284−-- TRUE ALL > (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1286− :: Expression schema from grp params ty
1287− -> Query schema params '[alias ::: ty]
1288− -> Condition schema from grp params
1289−allGt = unsafeSubqueryExpression "ALL >"
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"))
1296− => NP (Aliased (Expression schema from grp params)) row
1297− -> Query schema params row
1298− -> Condition schema from grp params
1299−rowAllGt = unsafeRowSubqueryExpression "ALL >"
1301−-- | >>> printSQL $ true `anyGt` values_ (true `as` #foo)
1302−-- TRUE ANY > (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1304− :: Expression schema from grp params ty
1305− -> Query schema params '[alias ::: ty]
1306− -> Condition schema from grp params
1307−anyGt = unsafeSubqueryExpression "ANY >"
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"))
1314− => NP (Aliased (Expression schema from grp params)) row
1315− -> Query schema params row
1316− -> Condition schema from grp params
1317−rowAnyGt = unsafeRowSubqueryExpression "ANY >"
1319−-- | >>> printSQL $ true `allGte` values_ (true `as` #foo)
1320−-- TRUE ALL >= (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1322− :: Expression schema from grp params ty
1323− -> Query schema params '[alias ::: ty]
1324− -> Condition schema from grp params
1325−allGte = unsafeSubqueryExpression "ALL >="
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"))
1332− => NP (Aliased (Expression schema from grp params)) row
1333− -> Query schema params row
1334− -> Condition schema from grp params
1335−rowAllGte = unsafeRowSubqueryExpression "ALL >="
1337−-- | >>> printSQL $ true `anyGte` values_ (true `as` #foo)
1338−-- TRUE ANY >= (SELECT * FROM (VALUES (TRUE)) AS t ("foo"))
1340− :: Expression schema from grp params ty
1341− -> Query schema params '[alias ::: ty]
1342− -> Condition schema from grp params
1343−anyGte = unsafeSubqueryExpression "ANY >="
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"))
1350− => NP (Aliased (Expression schema from grp params)) row
1351− -> Query schema params row
1352− -> Condition schema from grp params
1353−rowAnyGte = unsafeRowSubqueryExpression "ANY >="
1355-- | A `CommonTableExpression` is an auxiliary statement in a `with` clause.
1031-- | A `CommonTableExpression` is an auxiliary statement in a `with` clause.
1356data CommonTableExpression statement
1032data CommonTableExpression statement
1033+ (schemas :: SchemasType)
1357 (params :: [NullityType])
1034 (params :: [NullityType])
1358− (schema0 :: SchemaType)
1035+ (commons0 :: FromType)
1359− (schema1 :: SchemaType) where
1036+ (commons1 :: FromType) where
1360 CommonTableExpression
1037 CommonTableExpression
1361− :: Aliased (statement schema params) (alias ::: cte)
1038+ :: Aliased (statement commons schemas params) (cte ::: common)
1362− -> CommonTableExpression
1039+ -> CommonTableExpression statement schemas params commons (cte ::: common ': commons)
1363− statement params schema (alias ::: 'View cte ': schema)
1040+instance
1364−instance (KnownSymbol alias, schema1 ~ (alias ::: 'View cte ': schema))
1041+ ( KnownSymbol cte
1042+ , commons1 ~ (cte ::: common ': commons)
1365− => Aliasable alias
1043+ ) => Aliasable cte
1366− (statement schema params cte)
1044+ (statement commons schemas params common)
1367− (CommonTableExpression statement params schema schema1) where
1045+ (CommonTableExpression statement schemas params commons commons1) where
1368− statement `as` alias = CommonTableExpression (statement `as` alias)
1046+ statement `as` cte = CommonTableExpression (statement `as` cte)
1369−instance (KnownSymbol alias, schema1 ~ (alias ::: 'View cte ': schema))
1047+instance
1049+ , commons1 ~ (cte ::: common ': commons)
1370− => Aliasable alias (statement schema params cte)
1051+ (statement commons schemas params common)
1371− (AlignedList (CommonTableExpression statement params) schema schema1) where
1052+ (Path (CommonTableExpression statement schemas params) commons commons1) where
1372− statement `as` alias = single (statement `as` alias)
1053+ statement `as` cte = csingleton (statement `as` cte)
1374−-- | render a `CommonTableExpression`.
1055+instance (forall c s p r. RenderSQL (statement c s p r)) => RenderSQL
1375−renderCommonTableExpression
1376− :: (forall sch ps row. statement ps sch row -> ByteString)
1377− -> CommonTableExpression statement params schema0 schema1 -> ByteString
1056+ (CommonTableExpression statement schemas params commons0 commons1) where
1378−renderCommonTableExpression renderStatement
1379− (CommonTableExpression (statement `As` alias)) =
1057+ renderSQL (CommonTableExpression (statement `As` cte)) =
1380− renderAlias alias <+> "AS" <+> parenthesized (renderStatement statement)
1058+ renderSQL cte <+> "AS" <+> parenthesized (renderSQL statement)
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
1388−renderCommonTableExpressions renderStatement cte ctes =
1389− renderCommonTableExpression renderStatement cte <> case ctes of
1391− cte' :>> ctes' -> "," <+>
1392− renderCommonTableExpressions renderStatement cte' ctes'
1394-- | `with` provides a way to write auxiliary statements for use in a larger query.
1060-- | `with` provides a way to write auxiliary statements for use in a larger query.
1395-- These statements, referred to as `CommonTableExpression`s, can be thought of as
1061-- These statements, referred to as `CommonTableExpression`s, can be thought of as
1396-- defining temporary tables that exist just for one query.
1062-- defining temporary tables that exist just for one query.
1397class With statement where
1063class With statement where
1399− :: AlignedList (CommonTableExpression statement params) schema0 schema1
1065+ :: Path (CommonTableExpression statement schemas params) commons0 commons1
1066+ -- ^ common table expressions
1400− -> statement schema1 params row
1067+ -> statement commons1 schemas params row
1401− -> statement schema0 params row
1069+ -> statement commons0 schemas params row
1402−instance With Query where
1070+instance With (Query outer) where
1403 with Done query = query
1071 with Done query = query
1404− with (cte :>> ctes) query = UnsafeQuery $
1072+ with ctes query = UnsafeQuery $
1405− "WITH" <+> renderCommonTableExpressions renderQuery cte ctes
1073+ "WITH" <+> commaSeparated (ctoList renderSQL ctes) <+> renderSQL query
1406− <+> renderQuery query
1074+
1076+>>> import Data.Monoid (Sum (..))
1077+>>> import Data.Int (Int64)
1080+ query :: Query_ schema () (Sum Int64)
1081+ query = withRecursive
1082+ ( values_ ((1 & astype int) `as` #n)
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))
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"
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