@@ -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
@@ -95,6 +116,7 @@ import Squeal.PostgreSQL.Alias
95import Squeal.PostgreSQL.Expression
116import Squeal.PostgreSQL.Expression
96import Squeal.PostgreSQL.Expression.Logic
117import Squeal.PostgreSQL.Expression.Logic
97import Squeal.PostgreSQL.Expression.Sort
118import Squeal.PostgreSQL.Expression.Sort
119+import Squeal.PostgreSQL.Expression.Type
98import Squeal.PostgreSQL.Expression.Window
120import Squeal.PostgreSQL.Expression.Window
99import Squeal.PostgreSQL.List
121import Squeal.PostgreSQL.List
100import Squeal.PostgreSQL.PG
122import Squeal.PostgreSQL.PG
@@ -103,55 +125,10 @@ import Squeal.PostgreSQL.Schema
105-- >>> import Squeal.PostgreSQL
127-- >>> 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
130+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.
131+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:
133simple query:
@@ -159,7 +136,7 @@ simple query:
159>>> type Schema = '["tab" ::: 'Table ('[] :=> Columns)]
136>>> type Schema = '["tab" ::: 'Table ('[] :=> Columns)]
162− query :: Query_ (Public Schema) () (Row Int32 Int32)
139+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
163 query = select Star (from (table #tab))
140 query = select Star (from (table #tab))
164in printSQL query
141in printSQL query
@@ -169,21 +146,21 @@ restricted query:
172− query :: Query_ (Public Schema) () (Row Int32 Int32)
149+ query :: Query '[] '[] (Public Schema) '[] '["sum" ::: 'NotNull 'PGint4, "col1" ::: 'NotNull 'PGint4]
174− select_ ((#col1 + #col2) `as` #col1 :* #col1 `as` #col2)
151+ select_ ((#col1 + #col2) `as` #sum :* #col1)
175 ( from (table #tab)
152 ( from (table #tab)
176 & where_ (#col1 .> #col2)
153 & where_ (#col1 .> #col2)
177 & where_ (#col2 .> 0) )
154 & where_ (#col2 .> 0) )
178in printSQL query
155in printSQL query
180−SELECT ("col1" + "col2") AS "col1", "col1" AS "col2" FROM "tab" AS "tab" WHERE (("col1" > "col2") AND ("col2" > 0))
157+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)
163+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
187 query = select Star (from (subquery (select Star (from (table #tab)) `as` #sub)))
164 query = select Star (from (subquery (select Star (from (table #tab)) `as` #sub)))
188in printSQL query
165in printSQL query
@@ -193,7 +170,7 @@ limits and offsets:
196− query :: Query_ (Public Schema) () (Row Int32 Int32)
173+ 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)
174 query = select Star (from (table #tab) & limit 100 & offset 2 & limit 50 & offset 2)
198in printSQL query
175in printSQL query
@@ -203,7 +180,7 @@ parameterized query:
206− query :: Query_ (Public Schema) (Only Int32) (Row Int32 Int32)
183+ query :: Query '[] '[] (Public Schema) '[ 'NotNull 'PGint4] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
207 query = select Star (from (table #tab) & where_ (#col1 .> param @1))
184 query = select Star (from (table #tab) & where_ (#col1 .> param @1))
208in printSQL query
185in printSQL query
@@ -213,21 +190,21 @@ aggregation query:
216− query :: Query_ (Public Schema) () (Row Int64 Int32)
193+ query :: Query '[] '[] (Public Schema) '[] '["sum" ::: 'NotNull 'PGint4, "col1" ::: 'NotNull 'PGint4 ]
218− select_ ((fromNull 0 (sum_ (All #col2))) `as` #col1 :* #col1 `as` #col2)
195+ select_ (sum_ (All #col2) `as` #sum :* #col1)
219 ( from (table (#tab `as` #table1))
196 ( from (table (#tab `as` #table1))
220 & groupBy #col1
197 & groupBy #col1
221− & having (sum_ (Distinct #col2) .> 1) )
198+ & having (#col1 + sum_ (Distinct #col2) .> 1) )
222in printSQL query
199in 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)
201+SELECT sum(ALL "col2") AS "sum", "col1" AS "col1" FROM "tab" AS "table1" GROUP BY "col1" HAVING (("col1" + sum(DISTINCT "col2")) > 1)
226sorted query:
203sorted query:
230− query :: Query_ (Public Schema) () (Row Int32 Int32)
207+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
231 query = select Star (from (table #tab) & orderBy [#col1 & Asc])
208 query = select Star (from (table #tab) & orderBy [#col1 & Asc])
232in printSQL query
209in printSQL query
@@ -235,12 +212,14 @@ SELECT * FROM "tab" AS "tab" ORDER BY "col1" ASC
215+>>> :set -XFlexibleContexts
239type OrdersColumns =
217type OrdersColumns =
240 '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
218 '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
241− , "price" ::: 'NoDef :=> 'NotNull 'PGfloat4
219+ , "price" ::: 'NoDef :=> 'NotNull 'PGfloat4
242− , "customer_id" ::: 'NoDef :=> 'NotNull 'PGint4
220+ , "customer_id" ::: 'NoDef :=> 'NotNull 'PGint4
243− , "shipper_id" ::: 'NoDef :=> 'NotNull 'PGint4 ]
221+ , "shipper_id" ::: 'NoDef :=> 'NotNull 'PGint4
@@ -256,27 +235,22 @@ type OrdersConstraints =
257type OrdersSchema =
236type OrdersSchema =
258 '[ "orders" ::: 'Table (OrdersConstraints :=> OrdersColumns)
237 '[ "orders" ::: 'Table (OrdersConstraints :=> OrdersColumns)
259− , "customers" ::: 'Table (CustomersConstraints :=> NamesColumns)
238+ , "customers" ::: 'Table (CustomersConstraints :=> NamesColumns)
260− , "shippers" ::: 'Table (ShippersConstraints :=> NamesColumns) ]
239+ , "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
244+ query :: Query '[] '[] (Public OrdersSchema)
246+ '[ "order_price" ::: 'NotNull 'PGfloat4
247+ , "customer_name" ::: 'NotNull 'PGtext
248+ , "shipper_name" ::: 'NotNull 'PGtext
276 query = select_
250 query = select_
277− ( #o ! #price `as` #price :*
251+ ( #o ! #price `as` #order_price :*
278− #c ! #name `as` #customerName :*
252+ #c ! #name `as` #customer_name :*
279− #s ! #name `as` #shipperName )
253+ #s ! #name `as` #shipper_name )
280 ( from (table (#orders `as` #o)
254 ( from (table (#orders `as` #o)
281 & innerJoin (table (#customers `as` #c))
255 & innerJoin (table (#customers `as` #c))
282 (#o ! #customer_id .== #c ! #id)
256 (#o ! #customer_id .== #c ! #id)
@@ -284,16 +258,14 @@ let
284 (#o ! #shipper_id .== #s ! #id)) )
258 (#o ! #shipper_id .== #s ! #id)) )
285in printSQL query
259in 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")
261+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:
263self-join:
293− query :: Query_ (Public Schema) () (Row Int32 Int32)
267+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
296− (from (table (#tab `as` #t1) & crossJoin (table (#tab `as` #t2))))
268+ query = select (#t1 & DotStar) (from (table (#tab `as` #t1) & crossJoin (table (#tab `as` #t2))))
297in printSQL query
269in printSQL query
299SELECT "t1".* FROM "tab" AS "t1" CROSS JOIN "tab" AS "t2"
271SELECT "t1".* FROM "tab" AS "t1" CROSS JOIN "tab" AS "t2"
@@ -302,19 +274,17 @@ value queries:
305− query :: Query_ schemas () (Row String Bool)
277+ query :: Query '[] commons schemas '[] '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
306− query = values
278+ 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
279in printSQL query
311−SELECT * FROM (VALUES (E'true', TRUE), (E'false', FALSE)) AS t ("col1", "col2")
281+SELECT * FROM (VALUES (1, TRUE), (2, FALSE)) AS t ("foo", "bar")
313set operations:
283set operations:
317− query :: Query_ (Public Schema) () (Row Int32 Int32)
287+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
318 query = select Star (from (table #tab)) `unionAll` select Star (from (table #tab))
288 query = select Star (from (table #tab)) `unionAll` select Star (from (table #tab))
319in printSQL query
289in printSQL query
@@ -324,7 +294,7 @@ with queries:
327− query :: Query_ (Public Schema) () (Row Int32 Int32)
297+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
328 query = with (
298 query = with (
329 select Star (from (table #tab)) `as` #cte1 :>>
299 select Star (from (table #tab)) `as` #cte1 :>>
330 select Star (from (common #cte1)) `as` #cte2
300 select Star (from (common #cte1)) `as` #cte2
@@ -337,35 +307,40 @@ window function queries
340− query :: Query_ (Public Schema) () (Row Int32 Int64)
310+ query :: Query '[] '[] (Public Schema) '[] ["col1" ::: 'NotNull 'PGint4, "rank" ::: 'NotNull 'PGint8]
341 query = select
311 query = select
342− (#col1 & Also (rank `as` #col2 `Over` (partitionBy #col1 & orderBy [#col2 & Asc])))
312+ (#col1 & Also (rank `as` #rank `Over` (partitionBy #col1 & orderBy [#col2 & Asc])))
343 (from (table #tab))
313 (from (table #tab))
344in printSQL query
314in printSQL query
346−SELECT "col1" AS "col1", rank() OVER (PARTITION BY "col1" ORDER BY "col2" ASC) AS "col2" FROM "tab" AS "tab"
316+SELECT "col1" AS "col1", rank() OVER (PARTITION BY "col1" ORDER BY "col2" ASC) AS "rank" FROM "tab" AS "tab"
348correlated subqueries
318correlated subqueries
352− query :: Query_ (Public Schema) () (Only Int32)
322+ query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4]
354− select (#col1 `as` #fromOnly) (from (table (#tab `as` #t1))
324+ select #col1 (from (table (#tab `as` #t1))
355 & where_ (exists (
325 & where_ (exists (
356 select Star (from (table (#tab `as` #t2))
326 select Star (from (table (#tab `as` #t2))
357 & where_ (#t2 ! #col2 .== #t1 ! #col1)))))
327 & where_ (#t2 ! #col2 .== #t1 ! #col1)))))
358in printSQL query
328in printSQL query
360−SELECT "col1" AS "fromOnly" FROM "tab" AS "t1" WHERE EXISTS (SELECT * FROM "tab" AS "t2" WHERE ("t2"."col2" = "t1"."col1"))
330+SELECT "col1" AS "col1" FROM "tab" AS "t1" WHERE EXISTS (SELECT * FROM "tab" AS "t2" WHERE ("t2"."col2" = "t1"."col1"))
363−type family Query_
332+newtype Query
334+ (commons :: FromType)
364 (schemas :: SchemasType)
335 (schemas :: SchemasType)
365− (parameters :: Type)
336+ (params :: [NullityType])
366− (row :: Type) where
337+ (row :: RowType)
367− Query_ schemas params row =
338+ = UnsafeQuery { renderQuery :: ByteString }
339+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
340+instance RenderSQL (Query outer commons schemas params row) where renderSQL = renderQuery
342+type family Query_ (schemas :: SchemasType) (params :: Type) (row :: Type) where
368− Query '[] '[] schemas (TuplePG params) (RowPG row)
343+ Query_ schemas params row = Query '[] '[] schemas (TuplePG params) (RowPG row)
370-- | The results of two queries can be combined using the set operation
345-- | The results of two queries can be combined using the set operation
371-- `union`. Duplicate rows are eliminated.
346-- `union`. Duplicate rows are eliminated.
@@ -437,40 +412,27 @@ q1 `exceptAll` q2 = UnsafeQuery $
437SELECT queries
412SELECT queries
438-----------------------------------------}
413-----------------------------------------}
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
415data Selection outer commons grp schemas params from row where
418+ => NP (Aliased (Expression outer commons grp schemas params from)) row
419+ -> Selection outer commons grp schemas params from row
448 :: HasUnique tab from row
421 :: HasUnique tab from row
449 => Selection outer commons 'Ungrouped schemas params from row
422 => Selection outer commons 'Ungrouped schemas params from row
450− -- ^ `HasUnique` table in the `FromClause`
452 :: Has tab from row
424 :: Has tab from row
453 => Alias tab
425 => Alias tab
454− -- ^ `Has` table with `Alias`
455 -> Selection outer commons 'Ungrouped schemas params from row
426 -> Selection outer commons 'Ungrouped schemas params from row
458− => NP (Aliased (Expression outer commons grp schemas params from)) row
428+ :: Selection outer commons grp schemas params from right
459− -- ^ `NP` list of `Aliased` `Expression`s
429+ -> Selection outer commons grp schemas params from left
460− -> Selection outer commons grp schemas params from row
430+ -> Selection outer commons grp schemas params from (Join left right)
462 :: SListI row
432 :: SListI row
463 => NP (Aliased (WindowFunction outer commons grp schemas params from)) row
433 => 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
434 -> WindowDefinition outer commons grp schemas params from
466 -> Selection outer commons grp schemas params from row
435 -> 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])
436instance (KnownSymbol col, row ~ '[col ::: ty])
475 => Aliasable col
437 => Aliasable col
476 (Expression outer commons grp schemas params from ty)
438 (Expression outer commons grp schemas params from ty)
@@ -530,7 +492,7 @@ instance IsString
531 :: (SListI row, row ~ (x ': xs))
493 :: (SListI row, row ~ (x ': xs))
532 => Selection outer commons grp schemas params from row
494 => Selection outer commons grp schemas params from row
533− -- ^ selection
495+ -- ^ select list
534 -> TableExpression outer commons grp schemas params from
496 -> TableExpression outer commons grp schemas params from
535 -- ^ intermediate virtual table
497 -- ^ intermediate virtual table
536 -> Query outer commons schemas params row
498 -> Query outer commons schemas params row
@@ -539,24 +501,20 @@ select selection tabexpr = UnsafeQuery $
539 <+> renderSQL selection
501 <+> renderSQL selection
540 <+> renderSQL tabexpr
502 <+> 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))
505 :: (SListI row, row ~ (x ': xs))
546 => NP (Aliased (Expression outer commons grp schemas params from)) row
506 => NP (Aliased (Expression outer commons grp schemas params from)) row
548 -> TableExpression outer commons grp schemas params from
507 -> TableExpression outer commons grp schemas params from
549− -- ^ intermediate virtual table
550 -> Query outer commons schemas params row
508 -> Query outer commons schemas params row
551−select_ = select . List
509+select_ list = select (List list)
553-- | After the select list has been processed, the result table can
511-- | After the select list has been processed, the result table can
554-- be subject to the elimination of duplicate rows using `selectDistinct`.
512-- be subject to the elimination of duplicate rows using `selectDistinct`.
555selectDistinct
513selectDistinct
556 :: (SListI columns, columns ~ (col ': cols))
514 :: (SListI columns, columns ~ (col ': cols))
557− => Selection outer commons grp schemas params from columns
515+ => Selection outer commons 'Ungrouped schemas params from columns
558− -- ^ selection
516+ -- ^ select list
559− -> TableExpression outer commons grp schemas params from
517+ -> TableExpression outer commons 'Ungrouped schemas params from
560 -- ^ intermediate virtual table
518 -- ^ intermediate virtual table
561 -> Query outer commons schemas params columns
519 -> Query outer commons schemas params columns
562selectDistinct selection tabexpr = UnsafeQuery $
520selectDistinct selection tabexpr = UnsafeQuery $
@@ -564,65 +522,14 @@ selectDistinct selection tabexpr = UnsafeQuery $
564 <+> renderSQL selection
522 <+> renderSQL selection
565 <+> renderSQL tabexpr
523 <+> renderSQL tabexpr
567−-- | Like `selectDistinct` but takes an `NP` list of `Expression`s instead
568−-- of a general `Selection`.
569selectDistinct_
525selectDistinct_
570 :: (SListI columns, columns ~ (col ': cols))
526 :: (SListI columns, columns ~ (col ': cols))
571− => NP (Aliased (Expression outer commons grp schemas params from)) columns
527+ => NP (Aliased (Expression outer commons 'Ungrouped schemas params from)) columns
572 -- ^ select list
528 -- ^ select list
573− -> TableExpression outer commons grp schemas params from
529+ -> 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
530 -- ^ intermediate virtual table
624 -> Query outer commons schemas params columns
531 -> Query outer commons schemas params columns
625−selectDistinctOn_ distincts = selectDistinctOn distincts . List
532+selectDistinct_ list = select (List list)
627-- | `values` computes a row value or set of row values
534-- | `values` computes a row value or set of row values
628-- specified by value expressions. It is most commonly used
535-- specified by value expressions. It is most commonly used
@@ -757,7 +664,7 @@ instance RenderSQL (TableExpression outer commons grp schemas params from) where
758 :: FromClause outer commons schemas params from -- ^ table reference
665 :: FromClause outer commons schemas params from -- ^ table reference
759 -> TableExpression outer commons 'Ungrouped schemas params from
666 -> TableExpression outer commons 'Ungrouped schemas params from
760−from tab = TableExpression tab [] noGroups NoHaving [] [] []
667+from tab = TableExpression tab [] NoGroups NoHaving [] [] []
762-- | A `where_` is an endomorphism of `TableExpression`s which adds a
669-- | A `where_` is an endomorphism of `TableExpression`s which adds a
763-- search condition to the `whereClause`.
670-- search condition to the `whereClause`.
@@ -778,7 +685,7 @@ groupBy
778groupBy bys rels = TableExpression
685groupBy bys rels = TableExpression
779 { fromClause = fromClause rels
686 { fromClause = fromClause rels
780 , whereClause = whereClause rels
687 , whereClause = whereClause rels
781− , groupByClause = group bys
688+ , groupByClause = Group bys
782 , havingClause = Having []
689 , havingClause = Having []
783 , orderByClause = []
690 , orderByClause = []
784 , limitClause = limitClause rels
691 , limitClause = limitClause rels
@@ -813,6 +720,170 @@ offset
813 -> TableExpression outer commons grp schemas params from
720 -> TableExpression outer commons grp schemas params from
814offset off rels = rels {offsetClause = off : offsetClause rels}
721offset off rels = rels {offsetClause = off : offsetClause rels}
723+{-----------------------------------------
725+-----------------------------------------}
729+ -> Expression outer commons 'Ungrouped schemas params '[] ty
730+ -> FromClause outer commons schemas params from
731+unsafeSetOfFunction fun expr = UnsafeFromClause $
732+ fun <> "(" <> renderSQL expr <> ")"
734+-- | Expand an array to a set of rows
736+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity ('PGvararray ty))
737+ -> FromClause outer commons schemas params '["unnest" ::: '["unnest" ::: ty]]
738+unnest = unsafeSetOfFunction "unnest"
740+-- | Expands the outermost JSON object into a set of key/value pairs.
741+-- >>> import Data.Aeson
742+-- >>> printSQL (select Star (from (jsonEach (literal (Json (object ["a" .= "foo", "b" .= "bar"]))))))
743+-- SELECT * FROM json_each(('{"a":"foo","b":"bar"}' :: json))
745+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json object
746+ -> FromClause outer commons schemas params
747+ '["json_each" ::: '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGjson]]
748+jsonEach = unsafeSetOfFunction "json_each"
750+-- | Expands the outermost binary JSON object into a set of key/value pairs.
751+-- >>> import Data.Aeson
752+-- >>> printSQL (select Star (from (jsonbEach (literal (Jsonb (object ["a" .= "foo", "b" .= "bar"]))))))
753+-- SELECT * FROM jsonb_each(('{"a":"foo","b":"bar"}' :: jsonb))
755+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb object
756+ -> FromClause outer commons schemas params
757+ '["jsonb_each" ::: '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGjson]]
758+jsonbEach = unsafeSetOfFunction "jsonb_each"
760+-- | 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.
761+-- >>> import Data.Aeson
762+-- >>> printSQL (select Star (from (jsonEachText (literal (Json (object ["a" .= "foo", "b" .= "bar"]))))))
763+-- SELECT * FROM json_each_text(('{"a":"foo","b":"bar"}' :: json))
765+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json object
766+ -> FromClause outer commons schemas params
767+ '["json_each" ::: '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGtext]]
768+jsonEachText = unsafeSetOfFunction "json_each_text"
770+-- | Expands the outermost binary JSON object into a set of key/value pairs.
771+-- >>> import Data.Aeson
772+-- >>> printSQL (select Star (from (jsonbEachText (literal (Jsonb (object ["a" .= "foo", "b" .= "bar"]))))))
773+-- SELECT * FROM jsonb_each_text(('{"a":"foo","b":"bar"}' :: jsonb))
775+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb object
776+ -> FromClause outer commons schemas params
777+ '["jsonb_each" ::: '["key" ::: 'NotNull 'PGtext, "value" ::: 'NotNull 'PGtext]]
778+jsonbEachText = unsafeSetOfFunction "jsonb_each_text"
780+-- | Returns set of keys in the outermost JSON object.
781+-- >>> import Data.Aeson
782+-- >>> printSQL (jsonObjectKeys (literal (Json (object ["a" .= "foo", "b" .= "bar"]))))
783+-- json_object_keys(('{"a":"foo","b":"bar"}' :: json))
785+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json object
786+ -> FromClause outer commons schemas params
787+ '["json_object_keys" ::: '["json_object_keys" ::: 'NotNull 'PGtext]]
788+jsonObjectKeys = unsafeSetOfFunction "json_object_keys"
790+-- | Returns set of keys in the outermost JSON object.
791+-- >>> import Data.Aeson
792+-- >>> printSQL (jsonbObjectKeys (literal (Jsonb (object ["a" .= "foo", "b" .= "bar"]))))
793+-- jsonb_object_keys(('{"a":"foo","b":"bar"}' :: jsonb))
795+ :: Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb object
796+ -> FromClause outer commons schemas params
797+ '["jsonb_object_keys" ::: '["jsonb_object_keys" ::: 'NotNull 'PGtext]]
798+jsonbObjectKeys = unsafeSetOfFunction "jsonb_object_keys"
800+unsafePopulateFunction
803+ -> TypeExpression schemas (nullity ('PGcomposite row))
804+ -> Expression outer commons 'Ungrouped schemas params '[] ty
805+ -> FromClause outer commons schemas params '[fun ::: row]
806+unsafePopulateFunction fun ty expr = UnsafeFromClause $ renderSQL fun
807+ <> parenthesized ("null::" <> renderSQL ty <> ", " <> renderSQL expr)
809+-- | Expands the JSON expression to a row whose columns match the record
810+-- type defined by the given table.
812+ :: TypeExpression schemas (nullity ('PGcomposite row)) -- ^ row type
813+ -> Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json object
814+ -> FromClause outer commons schemas params '["json_populate_record" ::: row]
815+jsonPopulateRecord = unsafePopulateFunction #json_populate_record
817+-- | Expands the binary JSON expression to a row whose columns match the record
818+-- type defined by the given table.
820+ :: TypeExpression schemas (nullity ('PGcomposite row)) -- ^ row type
821+ -> Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb object
822+ -> FromClause outer commons schemas params '["jsonb_populate_record" ::: row]
823+jsonbPopulateRecord = unsafePopulateFunction #jsonb_populate_record
825+-- | Expands the outermost array of objects in the given JSON expression to a
826+-- set of rows whose columns match the record type defined by the given table.
827+jsonPopulateRecordSet
828+ :: TypeExpression schemas (nullity ('PGcomposite row)) -- ^ row type
829+ -> Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json array
830+ -> FromClause outer commons schemas params '["json_populate_record_set" ::: row]
831+jsonPopulateRecordSet = unsafePopulateFunction #json_populate_record_set
833+-- | Expands the outermost array of objects in the given binary JSON expression
834+-- to a set of rows whose columns match the record type defined by the given
836+jsonbPopulateRecordSet
837+ :: TypeExpression schemas (nullity ('PGcomposite row)) -- ^ row type
838+ -> Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb array
839+ -> FromClause outer commons schemas params '["jsonb_populate_record_set" ::: row]
840+jsonbPopulateRecordSet = unsafePopulateFunction #jsonb_populate_record_set
843+ :: (SListI record, json `In` PGJsonType)
845+ -> Expression outer commons 'Ungrouped schemas params '[] (nullity json)
846+ -> Aliased (NP (Aliased (TypeExpression schemas))) (tab ::: record)
847+ -> FromClause outer commons schemas params '[tab ::: record]
848+unsafeRecordFunction fun expr (types `As` tab) = UnsafeFromClause $
849+ fun <> parenthesized (renderSQL expr) <+> "AS" <+> renderSQL tab
850+ <> parenthesized (renderCommaSeparated renderTy types)
852+ renderTy :: Aliased (TypeExpression schemas) ty -> ByteString
853+ renderTy (ty `As` alias) = renderSQL alias <+> renderSQL ty
855+-- | Builds an arbitrary record from a JSON object.
858+ => Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json object
859+ -> Aliased (NP (Aliased (TypeExpression schemas))) (tab ::: record)
860+ -> FromClause outer commons schemas params '[tab ::: record]
861+jsonToRecord = unsafeRecordFunction "json_to_record"
863+-- | Builds an arbitrary record from a binary JSON object.
866+ => Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb object
867+ -> Aliased (NP (Aliased (TypeExpression schemas))) (tab ::: record)
868+ -> FromClause outer commons schemas params '[tab ::: record]
869+jsonbToRecord = unsafeRecordFunction "jsonb_to_record"
871+-- | Builds an arbitrary set of records from a JSON array of objects.
874+ => Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjson) -- ^ json array
875+ -> Aliased (NP (Aliased (TypeExpression schemas))) (tab ::: record)
876+ -> FromClause outer commons schemas params '[tab ::: record]
877+jsonToRecordSet = unsafeRecordFunction "json_to_record_set"
879+-- | Builds an arbitrary set of records from a binary JSON array of objects.
882+ => Expression outer commons 'Ungrouped schemas params '[] (nullity 'PGjsonb) -- ^ jsonb array
883+ -> Aliased (NP (Aliased (TypeExpression schemas))) (tab ::: record)
884+ -> FromClause outer commons schemas params '[tab ::: record]
885+jsonbToRecordSet = unsafeRecordFunction "jsonb_to_record_set"
816{-----------------------------------------
887{-----------------------------------------
817FROM clauses
888FROM clauses
818-----------------------------------------}
889-----------------------------------------}
@@ -839,7 +910,7 @@ table (tab `As` alias) = UnsafeFromClause $
840 :: Aliased (Query outer commons schemas params) query
911 :: Aliased (Query outer commons schemas params) query
841 -> FromClause outer commons schemas params '[query]
912 -> FromClause outer commons schemas params '[query]
842−subquery = UnsafeFromClause . renderAliased (parenthesized . renderSQL)
913+subquery = UnsafeFromClause . renderAliased (parenthesized . renderQuery)
844-- | `view` derives a table from a `View`.
915-- | `view` derives a table from a `View`.
@@ -857,16 +928,11 @@ common
857common (cte `As` alias) = UnsafeFromClause $
928common (cte `As` alias) = UnsafeFromClause $
858 renderSQL cte <+> "AS" <+> renderSQL alias
929 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
931+{- | @left & crossJoin right@. For every possible combination of rows from
866−@left@ and @right@ (i.e., a Cartesian product), the joined table will contain
932+ @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@.
933+ 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
934+ If the tables have @n@ and @m@ rows respectively, the joined table will
869−have @n * m@ rows.
935+ have @n * m@ rows.
872 :: FromClause outer commons schemas params right
938 :: FromClause outer commons schemas params right
@@ -992,20 +1058,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
1058-- done on @NoGroups@ while all output `Expression`s must be aggregated
993-- in @Group Nil@. In general, all output `Expression`s in the
1059-- in @Group Nil@. In general, all output `Expression`s in the
994-- complement of @bys@ must be aggregated in @Group bys@.
1060-- complement of @bys@ must be aggregated in @Group bys@.
995−newtype GroupByClause grp from = UnsafeGroupByClause
1061+data GroupByClause grp from where
996− { renderGroupByClause :: ByteString }
1062+ NoGroups :: GroupByClause 'Ungrouped from
997− deriving (GHC.Generic,Show,Eq,Ord,NFData)
1063+ Group
1065+ => NP (By from) bys
1066+ -> GroupByClause ('Grouped bys) from
1068+-- | Renders a `GroupByClause`.
998instance RenderSQL (GroupByClause grp from) where
1069instance RenderSQL (GroupByClause grp from) where
999− renderSQL = renderGroupByClause
1070+ renderSQL = \case
1000−noGroups :: GroupByClause 'Ungrouped from
1071+ NoGroups -> ""
1001−noGroups = UnsafeGroupByClause ""
1004− => NP (By from) bys
1005− -> GroupByClause ('Grouped bys) from
1006−group bys = UnsafeGroupByClause $ case bys of
1007− Nil -> ""
1072+ Group Nil -> ""
1008− bys' -> " GROUP BY" <+> renderCommaSeparated renderSQL bys'
1073+ Group bys -> " GROUP BY" <+> renderCommaSeparated renderSQL bys
1010-- | A `HavingClause` is used to eliminate groups that are not of interest.
1075-- | A `HavingClause` is used to eliminate groups that are not of interest.
1011-- An `Ungrouped` `TableExpression` may only use `NoHaving` while a `Grouped`
1076-- An `Ungrouped` `TableExpression` may only use `NoHaving` while a `Grouped`
@@ -1028,6 +1093,90 @@ instance RenderSQL (HavingClause outer commons grp schemas params from) where
1028 Having conditions ->
1093 Having conditions ->
1029 " HAVING" <+> commaSeparated (renderSQL <$> conditions)
1094 " HAVING" <+> commaSeparated (renderSQL <$> conditions)
1097+The argument of `exists` is an arbitrary subquery. The subquery is evaluated
1098+to determine whether it returns any rows. If it returns at least one row,
1099+the result of `exists` is `true`; if the subquery returns no rows,
1100+the result of `exists` is `false`.
1102+The subquery can refer to variables from the surrounding query,
1103+which will act as constants during any one evaluation of the subquery.
1105+The subquery will generally only be executed long enough to determine whether
1106+at least one row is returned, not all the way to completion.
1109+ :: Query (Join outer from) commons schemas params row
1110+ -> Condition outer commons grp schemas params from
1111+exists query = UnsafeExpression $ "EXISTS" <+> parenthesized (renderSQL query)
1114+The right-hand side is a parenthesized subquery, which must return
1115+exactly one column. The left-hand expression is evaluated and compared to each
1116+row of the subquery result using the given `Operator`,
1117+which must yield a Boolean result. The result of `subAll` is `true`
1118+if all rows yield true (including the case where the subquery returns no rows).
1119+The result is `false` if any `false` result is found.
1120+The result is `null_` if no comparison with a subquery row returns `false`,
1121+and at least one comparison returns `null_`.
1123+>>> printSQL $ subAll true (.==) (values_ (true `as` #foo))
1124+(TRUE = ALL (SELECT * FROM (VALUES (TRUE)) AS t ("foo")))
1127+ :: Expression outer commons grp schemas params from ty1 -- ^ expression
1128+ -> Operator ty1 ty2 ('Null 'PGbool) -- ^ operator
1129+ -> Query (Join outer from) commons schemas params '[col ::: ty2] -- ^ subquery
1130+ -> Condition outer commons grp schemas params from
1131+subAll expr (?) qry = expr ?
1132+ (UnsafeExpression $ "ALL" <+> parenthesized (renderSQL qry))
1135+The right-hand side is a parenthesized subquery, which must return exactly one column.
1136+The left-hand expression is evaluated and compared to each row of the subquery result
1137+using the given `Operator`, which must yield a Boolean result. The result of `subAny` is `true`
1138+if any `true` result is obtained. The result is `false` if no true result is found
1139+(including the case where the subquery returns no rows).
1141+>>> printSQL $ subAll "foo" like (values_ ("foobar" `as` #foo))
1142+(E'foo' LIKE ALL (SELECT * FROM (VALUES (E'foobar')) AS t ("foo")))
1145+ :: Expression outer commons grp schemas params from ty1 -- ^ expression
1146+ -> Operator ty1 ty2 ('Null 'PGbool) -- ^ operator
1147+ -> Query (Join outer from) commons schemas params '[col ::: ty2] -- ^ subquery
1148+ -> Condition outer commons grp schemas params from
1149+subAny expr (?) qry = expr ?
1150+ (UnsafeExpression $ "ANY" <+> parenthesized (renderSQL qry))
1153+The result is `true` if the left-hand expression's result is equal
1154+to any of the right-hand expressions.
1156+>>> printSQL $ true `in_` [true, false, null_]
1157+TRUE IN (TRUE, FALSE, NULL)
1160+ :: Expression outer commons grp schemas params from ty -- ^ expression
1161+ -> [Expression outer commons grp schemas params from ty]
1162+ -> Condition outer commons grp schemas params from
1163+expr `in_` exprs = UnsafeExpression $ renderSQL expr <+> "IN"
1164+ <+> parenthesized (commaSeparated (renderSQL <$> exprs))
1167+The result is `true` if the left-hand expression's result is not equal
1168+to any of the right-hand expressions.
1170+>>> printSQL $ true `notIn` [false, null_]
1171+TRUE NOT IN (FALSE, NULL)
1174+ :: Expression outer commons grp schemas params from ty -- ^ expression
1175+ -> [Expression outer commons grp schemas params from ty]
1176+ -> Condition outer commons grp schemas params from
1177+expr `notIn` exprs = UnsafeExpression $ renderSQL expr <+> "NOT IN"
1178+ <+> parenthesized (commaSeparated (renderSQL <$> exprs))
1031-- | A `CommonTableExpression` is an auxiliary statement in a `with` clause.
1180-- | A `CommonTableExpression` is an auxiliary statement in a `with` clause.
1032data CommonTableExpression statement
1181data CommonTableExpression statement
1033 (schemas :: SchemasType)
1182 (schemas :: SchemasType)
@@ -1049,8 +1198,8 @@ instance
1049 , commons1 ~ (cte ::: common ': commons)
1198 , commons1 ~ (cte ::: common ': commons)
1050 ) => Aliasable cte
1199 ) => Aliasable cte
1051 (statement commons schemas params common)
1200 (statement commons schemas params common)
1052− (Path (CommonTableExpression statement schemas params) commons commons1) where
1201+ (AlignedList (CommonTableExpression statement schemas params) commons commons1) where
1053− statement `as` cte = csingleton (statement `as` cte)
1202+ statement `as` cte = single (statement `as` cte)
1055instance (forall c s p r. RenderSQL (statement c s p r)) => RenderSQL
1204instance (forall c s p r. RenderSQL (statement c s p r)) => RenderSQL
1056 (CommonTableExpression statement schemas params commons0 commons1) where
1205 (CommonTableExpression statement schemas params commons0 commons1) where
@@ -1062,7 +1211,7 @@ instance (forall c s p r. RenderSQL (statement c s p r)) => RenderSQL
1062-- defining temporary tables that exist just for one query.
1211-- defining temporary tables that exist just for one query.
1063class With statement where
1212class With statement where
1065− :: Path (CommonTableExpression statement schemas params) commons0 commons1
1214+ :: AlignedList (CommonTableExpression statement schemas params) commons0 commons1
1066 -- ^ common table expressions
1215 -- ^ common table expressions
1067 -> statement commons1 schemas params row
1216 -> statement commons1 schemas params row
1068 -- ^ larger query
1217 -- ^ larger query
@@ -1070,29 +1219,4 @@ class With statement where
1070instance With (Query outer) where
1219instance With (Query outer) where
1071 with Done query = query
1220 with Done query = query
1072 with ctes query = UnsafeQuery $
1221 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
1222+ "WITH" <+> renderSQL ctes <+> renderSQL query