@@ -1,7 +1,7 @@
2Module: Squeal.PostgreSQL.Query
2Module: Squeal.PostgreSQL.Query
3Description: Squeal queries
3Description: Squeal queries
4−Copyright: (c) Eitan Chatav, 2019
4+Copyright: (c) Eitan Chatav, 2017
5Maintainer: eitan@morphism.tech
5Maintainer: eitan@morphism.tech
6Stability: experimental
6Stability: experimental
@@ -32,15 +32,13 @@ Squeal queries.
33module Squeal.PostgreSQL.Query
33module Squeal.PostgreSQL.Query
34 ( -- * Queries
34 ( -- * Queries
36− , Query (..)
36+ , Query_
37 -- ** Select
37 -- ** Select
40 , selectDistinct
40 , selectDistinct
41 , selectDistinct_
41 , selectDistinct_
44 , Selection (..)
42 , Selection (..)
45 -- ** Values
43 -- ** Values
@@ -55,7 +53,23 @@ module Squeal.PostgreSQL.Query
55 -- ** With
53 -- ** With
56 , With (..)
54 , With (..)
57 , CommonTableExpression (..)
55 , CommonTableExpression (..)
58− , withRecursive
56+ -- ** Unnest
66+ , jsonbPopulateRecord
67+ , jsonPopulateRecordSet
68+ , jsonbPopulateRecordSet
59 -- * Table Expressions
73 -- * Table Expressions
60 , TableExpression (..)
74 , TableExpression (..)
@@ -79,6 +93,13 @@ module Squeal.PostgreSQL.Query
80 , GroupByClause (..)
94 , GroupByClause (..)
81 , HavingClause (..)
95 , HavingClause (..)
96+ -- * Subquery Expressions
84import Control.DeepSeq
105import Control.DeepSeq
@@ -103,55 +124,10 @@ import Squeal.PostgreSQL.Schema
105-- >>> import Squeal.PostgreSQL
126-- >>> 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
129+The process of retrieving or the command to retrieve data from a database
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
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`.
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.
130+is called a `Query`. 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)
156simple query:
132simple query:
@@ -159,7 +135,7 @@ simple query:
159>>> type Schema = '["tab" ::: 'Table ('[] :=> Columns)]
135>>> type Schema = '["tab" ::: 'Table ('[] :=> Columns)]
162− query :: Query_ (Public Schema) () (Row Int32 Int32)
138+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
163 query = select Star (from (table #tab))
139 query = select Star (from (table #tab))
164in printSQL query
140in printSQL query
@@ -169,21 +145,21 @@ restricted query:
172− query :: Query_ (Public Schema) () (Row Int32 Int32)
148+ query :: Query '[] '[] (Public Schema) '[] '["sum" ::: 'NotNull 'PGint4, "col1" ::: 'NotNull 'PGint4]
174− select_ ((#col1 + #col2) `as` #col1 :* #col1 `as` #col2)
150+ select_ ((#col1 + #col2) `as` #sum :* #col1)
175 ( from (table #tab)
151 ( from (table #tab)
176 & where_ (#col1 .> #col2)
152 & where_ (#col1 .> #col2)
177 & where_ (#col2 .> 0) )
153 & where_ (#col2 .> 0) )
178in printSQL query
154in printSQL query
180−SELECT ("col1" + "col2") AS "col1", "col1" AS "col2" FROM "tab" AS "tab" WHERE (("col1" > "col2") AND ("col2" > 0))
156+SELECT ("col1" + "col2") AS "sum", "col1" AS "col1" FROM "tab" AS "tab" WHERE (("col1" > "col2") AND ("col2" > 0))
186− query :: Query_ (Public Schema) () (Row Int32 Int32)
162+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
187 query = select Star (from (subquery (select Star (from (table #tab)) `as` #sub)))
163 query = select Star (from (subquery (select Star (from (table #tab)) `as` #sub)))
188in printSQL query
164in printSQL query
@@ -193,7 +169,7 @@ limits and offsets:
196− query :: Query_ (Public Schema) () (Row Int32 Int32)
172+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
197 query = select Star (from (table #tab) & limit 100 & offset 2 & limit 50 & offset 2)
173 query = select Star (from (table #tab) & limit 100 & offset 2 & limit 50 & offset 2)
198in printSQL query
174in printSQL query
@@ -203,7 +179,7 @@ parameterized query:
206− query :: Query_ (Public Schema) (Only Int32) (Row Int32 Int32)
182+ query :: Query '[] '[] (Public Schema) '[ 'NotNull 'PGint4] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
207 query = select Star (from (table #tab) & where_ (#col1 .> param @1))
183 query = select Star (from (table #tab) & where_ (#col1 .> param @1))
208in printSQL query
184in printSQL query
@@ -213,21 +189,21 @@ aggregation query:
216− query :: Query_ (Public Schema) () (Row Int64 Int32)
192+ query :: Query '[] '[] (Public Schema) '[] '["sum" ::: 'NotNull 'PGint4, "col1" ::: 'NotNull 'PGint4 ]
218− select_ ((fromNull 0 (sum_ (All #col2))) `as` #col1 :* #col1 `as` #col2)
194+ select_ (sum_ (All #col2) `as` #sum :* #col1)
219 ( from (table (#tab `as` #table1))
195 ( from (table (#tab `as` #table1))
220 & groupBy #col1
196 & groupBy #col1
221− & having (sum_ (Distinct #col2) .> 1) )
197+ & having (#col1 + sum_ (Distinct #col2) .> 1) )
222in printSQL query
198in printSQL query
224−SELECT COALESCE(sum(ALL "col2"), 0) AS "col1", "col1" AS "col2" FROM "tab" AS "table1" GROUP BY "col1" HAVING (sum(DISTINCT "col2") > 1)
200+SELECT sum(ALL "col2") AS "sum", "col1" AS "col1" FROM "tab" AS "table1" GROUP BY "col1" HAVING (("col1" + sum(DISTINCT "col2")) > 1)
226sorted query:
202sorted query:
230− query :: Query_ (Public Schema) () (Row Int32 Int32)
206+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
231 query = select Star (from (table #tab) & orderBy [#col1 & Asc])
207 query = select Star (from (table #tab) & orderBy [#col1 & Asc])
232in printSQL query
208in printSQL query
@@ -235,12 +211,14 @@ SELECT * FROM "tab" AS "tab" ORDER BY "col1" ASC
214+>>> :set -XFlexibleContexts
239type OrdersColumns =
216type OrdersColumns =
240 '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
217 '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
241− , "price" ::: 'NoDef :=> 'NotNull 'PGfloat4
218+ , "price" ::: 'NoDef :=> 'NotNull 'PGfloat4
242− , "customer_id" ::: 'NoDef :=> 'NotNull 'PGint4
219+ , "customer_id" ::: 'NoDef :=> 'NotNull 'PGint4
243− , "shipper_id" ::: 'NoDef :=> 'NotNull 'PGint4 ]
220+ , "shipper_id" ::: 'NoDef :=> 'NotNull 'PGint4
@@ -256,27 +234,22 @@ type OrdersConstraints =
257type OrdersSchema =
235type OrdersSchema =
258 '[ "orders" ::: 'Table (OrdersConstraints :=> OrdersColumns)
236 '[ "orders" ::: 'Table (OrdersConstraints :=> OrdersColumns)
259− , "customers" ::: 'Table (CustomersConstraints :=> NamesColumns)
237+ , "customers" ::: 'Table (CustomersConstraints :=> NamesColumns)
260− , "shippers" ::: 'Table (ShippersConstraints :=> NamesColumns) ]
238+ , "shippers" ::: 'Table (ShippersConstraints :=> NamesColumns) ]
266− , customerName :: Text
267− , shipperName :: Text
268− } deriving GHC.Generic
269−instance SOP.Generic Order
270−instance SOP.HasDatatypeInfo Order
275− query :: Query_ (Public OrdersSchema) () Order
243+ query :: Query '[] '[] (Public OrdersSchema)
245+ '[ "order_price" ::: 'NotNull 'PGfloat4
246+ , "customer_name" ::: 'NotNull 'PGtext
247+ , "shipper_name" ::: 'NotNull 'PGtext
276 query = select_
249 query = select_
277− ( #o ! #price `as` #price :*
250+ ( #o ! #price `as` #order_price :*
278− #c ! #name `as` #customerName :*
251+ #c ! #name `as` #customer_name :*
279− #s ! #name `as` #shipperName )
252+ #s ! #name `as` #shipper_name )
280 ( from (table (#orders `as` #o)
253 ( from (table (#orders `as` #o)
281 & innerJoin (table (#customers `as` #c))
254 & innerJoin (table (#customers `as` #c))
282 (#o ! #customer_id .== #c ! #id)
255 (#o ! #customer_id .== #c ! #id)
@@ -284,16 +257,14 @@ let
284 (#o ! #shipper_id .== #s ! #id)) )
257 (#o ! #shipper_id .== #s ! #id)) )
285in printSQL query
258in printSQL query
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")
260+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")
289self-join:
262self-join:
293− query :: Query_ (Public Schema) () (Row Int32 Int32)
266+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
296− (from (table (#tab `as` #t1) & crossJoin (table (#tab `as` #t2))))
267+ query = select (#t1 & DotStar) (from (table (#tab `as` #t1) & crossJoin (table (#tab `as` #t2))))
297in printSQL query
268in printSQL query
299SELECT "t1".* FROM "tab" AS "t1" CROSS JOIN "tab" AS "t2"
270SELECT "t1".* FROM "tab" AS "t1" CROSS JOIN "tab" AS "t2"
@@ -302,19 +273,17 @@ value queries:
305− query :: Query_ schemas () (Row String Bool)
276+ query :: Query '[] commons schemas '[] '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
306− query = values
277+ query = values (1 `as` #foo :* true `as` #bar) [2 `as` #foo :* false `as` #bar]
307− ("true" `as` #col1 :* true `as` #col2)
308− ["false" `as` #col1 :* false `as` #col2]
309in printSQL query
278in printSQL query
311−SELECT * FROM (VALUES (E'true', TRUE), (E'false', FALSE)) AS t ("col1", "col2")
280+SELECT * FROM (VALUES (1, TRUE), (2, FALSE)) AS t ("foo", "bar")
313set operations:
282set operations:
317− query :: Query_ (Public Schema) () (Row Int32 Int32)
286+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
318 query = select Star (from (table #tab)) `unionAll` select Star (from (table #tab))
287 query = select Star (from (table #tab)) `unionAll` select Star (from (table #tab))
319in printSQL query
288in printSQL query
@@ -324,7 +293,7 @@ with queries:
327− query :: Query_ (Public Schema) () (Row Int32 Int32)
296+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
328 query = with (
297 query = with (
329 select Star (from (table #tab)) `as` #cte1 :>>
298 select Star (from (table #tab)) `as` #cte1 :>>
330 select Star (from (common #cte1)) `as` #cte2
299 select Star (from (common #cte1)) `as` #cte2
@@ -337,35 +306,40 @@ window function queries
340− query :: Query_ (Public Schema) () (Row Int32 Int64)
309+ query :: Query '[] '[] (Public Schema) '[] ["col1" ::: 'NotNull 'PGint4, "rank" ::: 'NotNull 'PGint8]
341 query = select
310 query = select
342− (#col1 & Also (rank `as` #col2 `Over` (partitionBy #col1 & orderBy [#col2 & Asc])))
311+ (#col1 & Also (rank `as` #rank `Over` (partitionBy #col1 & orderBy [#col2 & Asc])))
343 (from (table #tab))
312 (from (table #tab))
344in printSQL query
313in printSQL query
346−SELECT "col1" AS "col1", rank() OVER (PARTITION BY "col1" ORDER BY "col2" ASC) AS "col2" FROM "tab" AS "tab"
315+SELECT "col1" AS "col1", rank() OVER (PARTITION BY "col1" ORDER BY "col2" ASC) AS "rank" FROM "tab" AS "tab"
348correlated subqueries
317correlated subqueries
352− query :: Query_ (Public Schema) () (Only Int32)
321+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4]
354− select (#col1 `as` #fromOnly) (from (table (#tab `as` #t1))
323+ select #col1 (from (table (#tab `as` #t1))
355 & where_ (exists (
324 & where_ (exists (
356 select Star (from (table (#tab `as` #t2))
325 select Star (from (table (#tab `as` #t2))
357 & where_ (#t2 ! #col2 .== #t1 ! #col1)))))
326 & where_ (#t2 ! #col2 .== #t1 ! #col1)))))
358in printSQL query
327in printSQL query
360−SELECT "col1" AS "fromOnly" FROM "tab" AS "t1" WHERE EXISTS (SELECT * FROM "tab" AS "t2" WHERE ("t2"."col2" = "t1"."col1"))
329+SELECT "col1" AS "col1" FROM "tab" AS "t1" WHERE EXISTS (SELECT * FROM "tab" AS "t2" WHERE ("t2"."col2" = "t1"."col1"))
363−type family Query_
331+newtype Query
333+ (commons :: FromType)
364 (schemas :: SchemasType)
334 (schemas :: SchemasType)
365− (parameters :: Type)
335+ (params :: [NullityType])
366− (row :: Type) where
336+ (row :: RowType)
367− Query_ schemas params row =
337+ = UnsafeQuery { renderQuery :: ByteString }
338+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
339+instance RenderSQL (Query outer commons schemas params row) where renderSQL = renderQuery
341+type family Query_ (schemas :: SchemasType) (params :: Type) (row :: Type) where
368− Query '[] '[] schemas (TuplePG params) (RowPG row)
342+ Query_ schemas params row = Query '[] '[] schemas (TuplePG params) (RowPG row)
370-- | The results of two queries can be combined using the set operation
344-- | The results of two queries can be combined using the set operation
371-- `union`. Duplicate rows are eliminated.
345-- `union`. Duplicate rows are eliminated.
@@ -437,40 +411,27 @@ q1 `exceptAll` q2 = UnsafeQuery $
437SELECT queries
411SELECT queries
438-----------------------------------------}
412-----------------------------------------}
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`.
446data Selection outer commons grp schemas params from row where
414data Selection outer commons grp schemas params from row where
417+ => NP (Aliased (Expression outer commons grp schemas params from)) row
418+ -> Selection outer commons grp schemas params from row
448 :: HasUnique tab from row
420 :: HasUnique tab from row
449 => Selection outer commons 'Ungrouped schemas params from row
421 => Selection outer commons 'Ungrouped schemas params from row
450− -- ^ `HasUnique` table in the `FromClause`
452 :: Has tab from row
423 :: Has tab from row
453 => Alias tab
424 => Alias tab
454− -- ^ `Has` table with `Alias`
455 -> Selection outer commons 'Ungrouped schemas params from row
425 -> Selection outer commons 'Ungrouped schemas params from row
458− => NP (Aliased (Expression outer commons grp schemas params from)) row
427+ :: Selection outer commons grp schemas params from right
459− -- ^ `NP` list of `Aliased` `Expression`s
428+ -> Selection outer commons grp schemas params from left
460− -> Selection outer commons grp schemas params from row
429+ -> Selection outer commons grp schemas params from (Join left right)
462 :: SListI row
431 :: SListI row
463 => NP (Aliased (WindowFunction outer commons grp schemas params from)) row
432 => 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
433 -> WindowDefinition outer commons grp schemas params from
466 -> Selection outer commons grp schemas params from row
434 -> 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
474instance (KnownSymbol col, row ~ '[col ::: ty])
435instance (KnownSymbol col, row ~ '[col ::: ty])
475 => Aliasable col
436 => Aliasable col
476 (Expression outer commons grp schemas params from ty)
437 (Expression outer commons grp schemas params from ty)
@@ -530,7 +491,7 @@ instance IsString
531 :: (SListI row, row ~ (x ': xs))
492 :: (SListI row, row ~ (x ': xs))
532 => Selection outer commons grp schemas params from row
493 => Selection outer commons grp schemas params from row
533− -- ^ selection
494+ -- ^ select list
534 -> TableExpression outer commons grp schemas params from
495 -> TableExpression outer commons grp schemas params from
535 -- ^ intermediate virtual table
496 -- ^ intermediate virtual table
536 -> Query outer commons schemas params row
497 -> Query outer commons schemas params row
@@ -539,24 +500,20 @@ select selection tabexpr = UnsafeQuery $
539 <+> renderSQL selection
500 <+> renderSQL selection
540 <+> renderSQL tabexpr
501 <+> 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))
504 :: (SListI row, row ~ (x ': xs))
546 => NP (Aliased (Expression outer commons grp schemas params from)) row
505 => NP (Aliased (Expression outer commons grp schemas params from)) row
548 -> TableExpression outer commons grp schemas params from
506 -> TableExpression outer commons grp schemas params from
549− -- ^ intermediate virtual table
550 -> Query outer commons schemas params row
507 -> Query outer commons schemas params row
551−select_ = select . List
508+select_ list = select (List list)
553-- | After the select list has been processed, the result table can
510-- | After the select list has been processed, the result table can
554-- be subject to the elimination of duplicate rows using `selectDistinct`.
511-- be subject to the elimination of duplicate rows using `selectDistinct`.
555selectDistinct
512selectDistinct
556 :: (SListI columns, columns ~ (col ': cols))
513 :: (SListI columns, columns ~ (col ': cols))
557− => Selection outer commons grp schemas params from columns
514+ => Selection outer commons 'Ungrouped schemas params from columns
558− -- ^ selection
515+ -- ^ select list
559− -> TableExpression outer commons grp schemas params from
516+ -> TableExpression outer commons 'Ungrouped schemas params from
560 -- ^ intermediate virtual table
517 -- ^ intermediate virtual table
561 -> Query outer commons schemas params columns
518 -> Query outer commons schemas params columns
562selectDistinct selection tabexpr = UnsafeQuery $
519selectDistinct selection tabexpr = UnsafeQuery $
@@ -564,65 +521,14 @@ selectDistinct selection tabexpr = UnsafeQuery $
564 <+> renderSQL selection
521 <+> renderSQL selection
565 <+> renderSQL tabexpr
522 <+> renderSQL tabexpr
567−-- | Like `selectDistinct` but takes an `NP` list of `Expression`s instead
568−-- of a general `Selection`.
569selectDistinct_
524selectDistinct_
570 :: (SListI columns, columns ~ (col ': cols))
525 :: (SListI columns, columns ~ (col ': cols))
571− => NP (Aliased (Expression outer commons grp schemas params from)) columns
526+ => NP (Aliased (Expression outer commons 'Ungrouped schemas params from)) columns
572 -- ^ select list
527 -- ^ select list
573− -> TableExpression outer commons grp schemas params from
528+ -> TableExpression outer commons 'Ungrouped schemas params from
574− -- ^ intermediate virtual table
575− -> Query outer commons schemas params columns
576−selectDistinct_ = selectDistinct . List
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.
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
597− -> TableExpression outer commons grp schemas params from
598− -- ^ 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})
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
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
622− -> TableExpression outer commons grp schemas params from
623 -- ^ intermediate virtual table
529 -- ^ intermediate virtual table
624 -> Query outer commons schemas params columns
530 -> Query outer commons schemas params columns
625−selectDistinctOn_ distincts = selectDistinctOn distincts . List
531+selectDistinct_ list = select (List list)
627-- | `values` computes a row value or set of row values
533-- | `values` computes a row value or set of row values
628-- specified by value expressions. It is most commonly used
534-- specified by value expressions. It is most commonly used
@@ -757,7 +663,7 @@ instance RenderSQL (TableExpression outer commons grp schemas params from) where
758 :: FromClause outer commons schemas params from -- ^ table reference
664 :: FromClause outer commons schemas params from -- ^ table reference
759 -> TableExpression outer commons 'Ungrouped schemas params from
665 -> TableExpression outer commons 'Ungrouped schemas params from
760−from tab = TableExpression tab [] noGroups NoHaving [] [] []
666+from tab = TableExpression tab [] NoGroups NoHaving [] [] []
762-- | A `where_` is an endomorphism of `TableExpression`s which adds a
668-- | A `where_` is an endomorphism of `TableExpression`s which adds a
763-- search condition to the `whereClause`.
669-- search condition to the `whereClause`.
@@ -778,7 +684,7 @@ groupBy
778groupBy bys rels = TableExpression
684groupBy bys rels = TableExpression
779 { fromClause = fromClause rels
685 { fromClause = fromClause rels
780 , whereClause = whereClause rels
686 , whereClause = whereClause rels
781− , groupByClause = group bys
687+ , groupByClause = Group bys
782 , havingClause = Having []
688 , havingClause = Having []
783 , orderByClause = []
689 , orderByClause = []
784 , limitClause = limitClause rels
690 , limitClause = limitClause rels
@@ -813,6 +719,170 @@ offset
813 -> TableExpression outer commons grp schemas params from
719 -> TableExpression outer commons grp schemas params from
814offset off rels = rels {offsetClause = off : offsetClause rels}
720offset off rels = rels {offsetClause = off : offsetClause rels}
722+{-----------------------------------------
724+-----------------------------------------}
728+ -> Expression outer commons 'Ungrouped schemas params '[] ty
729+ -> FromClause outer commons schemas params from
730+unsafeSetOfFunction fun expr = UnsafeFromClause $
731+ fun <> "(" <> renderSQL expr <> ")"
733+-- | Expand an array to a set of rows
735+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity ('PGvararray ty))
736+ -> FromClause outer commons schemas params '["unnest" ::: '["unnest" ::: ty]]
737+unnest = unsafeSetOfFunction "unnest"
739+-- | Expands the outermost JSON object into a set of key/value pairs.
740+-- >>> import Data.Aeson
741+-- >>> printSQL (select Star (from (jsonEach (literal (Json (object ["a" .= "foo", "b" .= "bar"]))))))
742+-- SELECT * FROM json_each(('{"a":"foo","b":"bar"}' :: json))
744+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json object
745+ -> FromClause outer commons schemas params
746+ '["json_each" ::: '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGjson]]
747+jsonEach = unsafeSetOfFunction "json_each"
749+-- | Expands the outermost binary JSON object into a set of key/value pairs.
750+-- >>> import Data.Aeson
751+-- >>> printSQL (select Star (from (jsonbEach (literal (Jsonb (object ["a" .= "foo", "b" .= "bar"]))))))
752+-- SELECT * FROM jsonb_each(('{"a":"foo","b":"bar"}' :: jsonb))
754+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb object
755+ -> FromClause outer commons schemas params
756+ '["jsonb_each" ::: '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGjson]]
757+jsonbEach = unsafeSetOfFunction "jsonb_each"
759+-- | Expands the outermost JSON object into a set of key/value pairs.-- | Expands the outermost binary JSON object into a set of key/value pairs.
760+-- >>> import Data.Aeson
761+-- >>> printSQL (select Star (from (jsonEachText (literal (Json (object ["a" .= "foo", "b" .= "bar"]))))))
762+-- SELECT * FROM json_each_text(('{"a":"foo","b":"bar"}' :: json))
764+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json object
765+ -> FromClause outer commons schemas params
766+ '["json_each" ::: '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGtext]]
767+jsonEachText = unsafeSetOfFunction "json_each_text"
769+-- | Expands the outermost binary JSON object into a set of key/value pairs.
770+-- >>> import Data.Aeson
771+-- >>> printSQL (select Star (from (jsonbEachText (literal (Jsonb (object ["a" .= "foo", "b" .= "bar"]))))))
772+-- SELECT * FROM jsonb_each_text(('{"a":"foo","b":"bar"}' :: jsonb))
774+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb object
775+ -> FromClause outer commons schemas params
776+ '["jsonb_each" ::: '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGtext]]
777+jsonbEachText = unsafeSetOfFunction "jsonb_each_text"
779+-- | Returns set of keys in the outermost JSON object.
780+-- >>> import Data.Aeson
781+-- >>> printSQL (jsonObjectKeys (literal (Json (object ["a" .= "foo", "b" .= "bar"]))))
782+-- json_object_keys(('{"a":"foo","b":"bar"}' :: json))
784+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json object
785+ -> FromClause outer commons schemas params
786+ '["json_object_keys" ::: '["json_object_keys" ::: 'NotNull 'PGtext]]
787+jsonObjectKeys = unsafeSetOfFunction "json_object_keys"
789+-- | Returns set of keys in the outermost JSON object.
790+-- >>> import Data.Aeson
791+-- >>> printSQL (jsonbObjectKeys (literal (Jsonb (object ["a" .= "foo", "b" .= "bar"]))))
792+-- jsonb_object_keys(('{"a":"foo","b":"bar"}' :: jsonb))
794+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb object
795+ -> FromClause outer commons schemas params
796+ '["jsonb_object_keys" ::: '["jsonb_object_keys" ::: 'NotNull 'PGtext]]
797+jsonbObjectKeys = unsafeSetOfFunction "jsonb_object_keys"
799+unsafePopulateFunction
802+ -> TypeExpression schemas (nullity ('PGcomposite row))
803+ -> Expression outer commons 'Ungrouped schemas params '[] ty
804+ -> FromClause outer commons schemas params '[fun ::: row]
805+unsafePopulateFunction fun ty expr = UnsafeFromClause $ renderSQL fun
806+ <> parenthesized ("null::" <> renderSQL ty <> ", " <> renderSQL expr)
808+-- | Expands the JSON expression to a row whose columns match the record
809+-- type defined by the given table.
811+ :: TypeExpression schemas (nullity ('PGcomposite row)) -- ^ row type
812+ -> Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json object
813+ -> FromClause outer commons schemas params '["json_populate_record" ::: row]
814+jsonPopulateRecord = unsafePopulateFunction #json_populate_record
816+-- | Expands the binary JSON expression to a row whose columns match the record
817+-- type defined by the given table.
819+ :: TypeExpression schemas (nullity ('PGcomposite row)) -- ^ row type
820+ -> Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb object
821+ -> FromClause outer commons schemas params '["jsonb_populate_record" ::: row]
822+jsonbPopulateRecord = unsafePopulateFunction #jsonb_populate_record
824+-- | Expands the outermost array of objects in the given JSON expression to a
825+-- set of rows whose columns match the record type defined by the given table.
826+jsonPopulateRecordSet
827+ :: TypeExpression schemas (nullity ('PGcomposite row)) -- ^ row type
828+ -> Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json array
829+ -> FromClause outer commons schemas params '["json_populate_record_set" ::: row]
830+jsonPopulateRecordSet = unsafePopulateFunction #json_populate_record_set
832+-- | Expands the outermost array of objects in the given binary JSON expression
833+-- to a set of rows whose columns match the record type defined by the given
835+jsonbPopulateRecordSet
836+ :: TypeExpression schemas (nullity ('PGcomposite row)) -- ^ row type
837+ -> Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb array
838+ -> FromClause outer commons schemas params '["jsonb_populate_record_set" ::: row]
839+jsonbPopulateRecordSet = unsafePopulateFunction #jsonb_populate_record_set
842+ :: (SListI record, json `In` PGJsonType)
844+ -> Expression outer commons 'Ungrouped schemas params '[] (nullity json)
845+ -> Aliased (NP (Aliased (TypeExpression schemas))) (tab ::: record)
846+ -> FromClause outer commons schemas params '[tab ::: record]
847+unsafeRecordFunction fun expr (types `As` tab) = UnsafeFromClause $
848+ fun <> parenthesized (renderSQL expr) <+> "AS" <+> renderSQL tab
849+ <> parenthesized (renderCommaSeparated renderTy types)
851+ renderTy :: Aliased (TypeExpression schemas) ty -> ByteString
852+ renderTy (ty `As` alias) = renderSQL alias <+> renderSQL ty
854+-- | Builds an arbitrary record from a JSON object.
857+ => Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json object
858+ -> Aliased (NP (Aliased (TypeExpression schemas))) (tab ::: record)
859+ -> FromClause outer commons schemas params '[tab ::: record]
860+jsonToRecord = unsafeRecordFunction "json_to_record"
862+-- | Builds an arbitrary record from a binary JSON object.
865+ => Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb object
866+ -> Aliased (NP (Aliased (TypeExpression schemas))) (tab ::: record)
867+ -> FromClause outer commons schemas params '[tab ::: record]
868+jsonbToRecord = unsafeRecordFunction "jsonb_to_record"
870+-- | Builds an arbitrary set of records from a JSON array of objects.
873+ => Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json array
874+ -> Aliased (NP (Aliased (TypeExpression schemas))) (tab ::: record)
875+ -> FromClause outer commons schemas params '[tab ::: record]
876+jsonToRecordSet = unsafeRecordFunction "json_to_record_set"
878+-- | Builds an arbitrary set of records from a binary JSON array of objects.
881+ => Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb array
882+ -> Aliased (NP (Aliased (TypeExpression schemas))) (tab ::: record)
883+ -> FromClause outer commons schemas params '[tab ::: record]
884+jsonbToRecordSet = unsafeRecordFunction "jsonb_to_record_set"
816{-----------------------------------------
886{-----------------------------------------
817FROM clauses
887FROM clauses
818-----------------------------------------}
888-----------------------------------------}
@@ -839,7 +909,7 @@ table (tab `As` alias) = UnsafeFromClause $
840 :: Aliased (Query outer commons schemas params) query
910 :: Aliased (Query outer commons schemas params) query
841 -> FromClause outer commons schemas params '[query]
911 -> FromClause outer commons schemas params '[query]
842−subquery = UnsafeFromClause . renderAliased (parenthesized . renderSQL)
912+subquery = UnsafeFromClause . renderAliased (parenthesized . renderQuery)
844-- | `view` derives a table from a `View`.
914-- | `view` derives a table from a `View`.
@@ -857,16 +927,11 @@ common
857common (cte `As` alias) = UnsafeFromClause $
927common (cte `As` alias) = UnsafeFromClause $
858 renderSQL cte <+> "AS" <+> renderSQL alias
928 renderSQL cte <+> "AS" <+> renderSQL alias
860−instance Additional (FromClause outer commons schemas params) where
861− also right left = UnsafeFromClause $
862− renderSQL left <> ", " <> renderSQL right
865−@left & crossJoin right@. For every possible combination of rows from
930+{- | @left & crossJoin right@. For every possible combination of rows from
866−@left@ and @right@ (i.e., a Cartesian product), the joined table will contain
931+ @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@.
932+ 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
933+ If the tables have @n@ and @m@ rows respectively, the joined table will
869−have @n * m@ rows.
934+ have @n * m@ rows.
872 :: FromClause outer commons schemas params right
937 :: FromClause outer commons schemas params right
@@ -992,20 +1057,19 @@ instance (Has rel rels cols, Has col cols ty, bys ~ '[ '(rel, col)])
992-- done on @NoGroups@ while all output `Expression`s must be aggregated
1057-- done on @NoGroups@ while all output `Expression`s must be aggregated
993-- in @Group Nil@. In general, all output `Expression`s in the
1058-- in @Group Nil@. In general, all output `Expression`s in the
994-- complement of @bys@ must be aggregated in @Group bys@.
1059-- complement of @bys@ must be aggregated in @Group bys@.
995−newtype GroupByClause grp from = UnsafeGroupByClause
1060+data GroupByClause grp from where
996− { renderGroupByClause :: ByteString }
1061+ NoGroups :: GroupByClause 'Ungrouped from
997− deriving (GHC.Generic,Show,Eq,Ord,NFData)
1062+ Group
1064+ => NP (By from) bys
1065+ -> GroupByClause ('Grouped bys) from
1067+-- | Renders a `GroupByClause`.
998instance RenderSQL (GroupByClause grp from) where
1068instance RenderSQL (GroupByClause grp from) where
999− renderSQL = renderGroupByClause
1069+ renderSQL = \case
1000−noGroups :: GroupByClause 'Ungrouped from
1070+ NoGroups -> ""
1001−noGroups = UnsafeGroupByClause ""
1004− => NP (By from) bys
1005− -> GroupByClause ('Grouped bys) from
1006−group bys = UnsafeGroupByClause $ case bys of
1007− Nil -> ""
1071+ Group Nil -> ""
1008− bys' -> " GROUP BY" <+> renderCommaSeparated renderSQL bys'
1072+ Group bys -> " GROUP BY" <+> renderCommaSeparated renderSQL bys
1010-- | A `HavingClause` is used to eliminate groups that are not of interest.
1074-- | A `HavingClause` is used to eliminate groups that are not of interest.
1011-- An `Ungrouped` `TableExpression` may only use `NoHaving` while a `Grouped`
1075-- An `Ungrouped` `TableExpression` may only use `NoHaving` while a `Grouped`
@@ -1028,6 +1092,90 @@ instance RenderSQL (HavingClause outer commons grp schemas params from) where
1028 Having conditions ->
1092 Having conditions ->
1029 " HAVING" <+> commaSeparated (renderSQL <$> conditions)
1093 " HAVING" <+> commaSeparated (renderSQL <$> conditions)
1096+The argument of `exists` is an arbitrary subquery. The subquery is evaluated
1097+to determine whether it returns any rows. If it returns at least one row,
1098+the result of `exists` is `true`; if the subquery returns no rows,
1099+the result of `exists` is `false`.
1101+The subquery can refer to variables from the surrounding query,
1102+which will act as constants during any one evaluation of the subquery.
1104+The subquery will generally only be executed long enough to determine whether
1105+at least one row is returned, not all the way to completion.
1108+ :: Query (Join outer from) commons schemas params row
1109+ -> Condition outer commons grp schemas params from
1110+exists query = UnsafeExpression $ "EXISTS" <+> parenthesized (renderSQL query)
1113+The right-hand side is a parenthesized subquery, which must return
1114+exactly one column. The left-hand expression is evaluated and compared to each
1115+row of the subquery result using the given `Operator`,
1116+which must yield a Boolean result. The result of `subAll` is `true`
1117+if all rows yield true (including the case where the subquery returns no rows).
1118+The result is `false` if any `false` result is found.
1119+The result is `null_` if no comparison with a subquery row returns `false`,
1120+and at least one comparison returns `null_`.
1122+>>> printSQL $ subAll true (.==) (values_ (true `as` #foo))
1123+(TRUE = ALL (SELECT * FROM (VALUES (TRUE)) AS t ("foo")))
1126+ :: Expression outer commons grp schemas params from ty1 -- ^ expression
1127+ -> Operator ty1 ty2 ('Null 'PGbool) -- ^ operator
1128+ -> Query (Join outer from) commons schemas params '[col ::: ty2] -- ^ subquery
1129+ -> Condition outer commons grp schemas params from
1130+subAll expr (?) qry = expr ?
1131+ (UnsafeExpression $ "ALL" <+> parenthesized (renderSQL qry))
1134+The right-hand side is a parenthesized subquery, which must return exactly one column.
1135+The left-hand expression is evaluated and compared to each row of the subquery result
1136+using the given `Operator`, which must yield a Boolean result. The result of `subAny` is `true`
1137+if any `true` result is obtained. The result is `false` if no true result is found
1138+(including the case where the subquery returns no rows).
1140+>>> printSQL $ subAll "foo" like (values_ ("foobar" `as` #foo))
1141+(E'foo' LIKE ALL (SELECT * FROM (VALUES (E'foobar')) AS t ("foo")))
1144+ :: Expression outer commons grp schemas params from ty1 -- ^ expression
1145+ -> Operator ty1 ty2 ('Null 'PGbool) -- ^ operator
1146+ -> Query (Join outer from) commons schemas params '[col ::: ty2] -- ^ subquery
1147+ -> Condition outer commons grp schemas params from
1148+subAny expr (?) qry = expr ?
1149+ (UnsafeExpression $ "ANY" <+> parenthesized (renderSQL qry))
1152+The result is `true` if the left-hand expression's result is equal
1153+to any of the right-hand expressions.
1155+>>> printSQL $ true `in_` [true, false, null_]
1156+TRUE IN (TRUE, FALSE, NULL)
1159+ :: Expression outer commons grp schemas params from ty -- ^ expression
1160+ -> [Expression outer commons grp schemas params from ty]
1161+ -> Condition outer commons grp schemas params from
1162+expr `in_` exprs = UnsafeExpression $ renderSQL expr <+> "IN"
1163+ <+> parenthesized (commaSeparated (renderSQL <$> exprs))
1166+The result is `true` if the left-hand expression's result is not equal
1167+to any of the right-hand expressions.
1169+>>> printSQL $ true `notIn` [false, null_]
1170+TRUE NOT IN (FALSE, NULL)
1173+ :: Expression outer commons grp schemas params from ty -- ^ expression
1174+ -> [Expression outer commons grp schemas params from ty]
1175+ -> Condition outer commons grp schemas params from
1176+expr `notIn` exprs = UnsafeExpression $ renderSQL expr <+> "NOT IN"
1177+ <+> parenthesized (commaSeparated (renderSQL <$> exprs))
1031-- | A `CommonTableExpression` is an auxiliary statement in a `with` clause.
1179-- | A `CommonTableExpression` is an auxiliary statement in a `with` clause.
1032data CommonTableExpression statement
1180data CommonTableExpression statement
1033 (schemas :: SchemasType)
1181 (schemas :: SchemasType)
@@ -1049,8 +1197,8 @@ instance
1049 , commons1 ~ (cte ::: common ': commons)
1197 , commons1 ~ (cte ::: common ': commons)
1050 ) => Aliasable cte
1198 ) => Aliasable cte
1051 (statement commons schemas params common)
1199 (statement commons schemas params common)
1052− (Path (CommonTableExpression statement schemas params) commons commons1) where
1200+ (AlignedList (CommonTableExpression statement schemas params) commons commons1) where
1053− statement `as` cte = csingleton (statement `as` cte)
1201+ statement `as` cte = single (statement `as` cte)
1055instance (forall c s p r. RenderSQL (statement c s p r)) => RenderSQL
1203instance (forall c s p r. RenderSQL (statement c s p r)) => RenderSQL
1056 (CommonTableExpression statement schemas params commons0 commons1) where
1204 (CommonTableExpression statement schemas params commons0 commons1) where
@@ -1062,7 +1210,7 @@ instance (forall c s p r. RenderSQL (statement c s p r)) => RenderSQL
1062-- defining temporary tables that exist just for one query.
1210-- defining temporary tables that exist just for one query.
1063class With statement where
1211class With statement where
1065− :: Path (CommonTableExpression statement schemas params) commons0 commons1
1213+ :: AlignedList (CommonTableExpression statement schemas params) commons0 commons1
1066 -- ^ common table expressions
1214 -- ^ common table expressions
1067 -> statement commons1 schemas params row
1215 -> statement commons1 schemas params row
1068 -- ^ larger query
1216 -- ^ larger query
@@ -1070,29 +1218,4 @@ class With statement where
1070instance With (Query outer) where
1218instance With (Query outer) where
1071 with Done query = query
1219 with Done query = query
1072 with ctes query = UnsafeQuery $
1220 with ctes query = UnsafeQuery $
1073− "WITH" <+> commaSeparated (ctoList renderSQL ctes) <+> renderSQL query
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
1221+ "WITH" <+> renderSQL ctes <+> renderSQL query