@@ -118,6 +118,10 @@ module Squeal.PostgreSQL.Query
118118 , rowAllGte
119119 , anyGte
120120 , rowAnyGte
121+ , With' (with')
122+ , CommonTableExpression (..)
123+ , renderCte
124+ , renderCtes
121125 ) where
122126
123127import Control.DeepSeq
@@ -324,6 +328,43 @@ let
324328in printSQL query
325329:}
326330(SELECT * FROM "tab" AS "tab") UNION ALL (SELECT * FROM "tab" AS "tab")
331+
332+with queries:
333+
334+>>> :{
335+let
336+ query :: Query
337+ '[ "orders" ::: 'View
338+ '[ "region" ::: 'NotNull 'PGtext
339+ , "product" ::: 'NotNull 'PGtext
340+ , "quantity" ::: 'NotNull 'PGint8
341+ , "amount" ::: 'NotNull 'PGnumeric ] ]
342+ '[ 'NotNull 'PGint8 ]
343+ '[ "region" ::: 'NotNull 'PGtext
344+ , "product" ::: 'NotNull 'PGtext
345+ , "product_units" ::: 'NotNull 'PGint8
346+ , "product_sales" ::: 'NotNull 'PGnumeric ]
347+ query = with' (
348+ CommonTableExpression
349+ (((select (#region :* sum_ @'PGnumeric #amount `as` #total_sales)
350+ (from (view #orders) & groupBy #region))
351+ `as` #regional_sales)) :>>
352+ CommonTableExpression
353+ (((select (#region `as` #region)
354+ (from (view #regional_sales) & where_ (#total_sales .> param @1)))
355+ `as` #top_regions)) :>> Done
356+ ) (select (
357+ #region :*
358+ #product :*
359+ sum_ #quantity `as` #product_units :*
360+ sum_ #amount `as` #product_sales
361+ ) (from (view #orders)
362+ & where_ (#region `in_` select #region (from (view #top_regions)))
363+ & groupBy (#region :* #product))
364+ )
365+in printSQL query
366+:}
367+
327368-}
328369newtype Query
329370 (schema :: SchemaType)
@@ -1287,3 +1328,47 @@ rowAnyGte
12871328 -> Query schema params row
12881329 -> Condition schema from grp params
12891330rowAnyGte = unsafeRowSubqueryExpression "ANY >="
1331+
1332+data CommonTableExpression statement
1333+ (params :: [NullityType])
1334+ (schema0 :: SchemaType)
1335+ (schema1 :: SchemaType) where
1336+ CommonTableExpression
1337+ :: Aliased (statement schema params) (alias ::: cte)
1338+ -> CommonTableExpression
1339+ statement params schema (alias ::: 'View cte ': schema)
1340+instance KnownSymbol alias => Aliasable alias (statement schema params cte)
1341+ (CommonTableExpression statement params schema
1342+ (alias ::: 'View cte ': schema)) where
1343+ statement `as` alias = CommonTableExpression (statement `As` alias)
1344+instance KnownSymbol alias => Aliasable alias (statement schema params cte)
1345+ (AlignedList (CommonTableExpression statement params) schema
1346+ (alias ::: 'View cte ': schema)) where
1347+ statement `as` alias = statement `as` alias :>> Done
1348+
1349+renderCte
1350+ :: (forall sch ps row. statement ps sch row -> ByteString)
1351+ -> CommonTableExpression statement params schema0 schema1 -> ByteString
1352+renderCte renderStatement (CommonTableExpression (statement `As` alias)) =
1353+ renderAlias alias <+> "AS" <+> parenthesized (renderStatement statement)
1354+
1355+renderCtes
1356+ :: (forall sch ps row. statement ps sch row -> ByteString)
1357+ -> CommonTableExpression statement params schema0 schema1
1358+ -> AlignedList (CommonTableExpression statement params) schema1 schema2
1359+ -> ByteString
1360+renderCtes renderStatement cte ctes =
1361+ renderCte renderStatement cte <+> case ctes of
1362+ Done -> ""
1363+ cte' :>> ctes' -> "," <+>
1364+ renderCtes renderStatement cte' ctes'
1365+
1366+class With' statement where
1367+ with'
1368+ :: AlignedList (CommonTableExpression statement params) schema0 schema1
1369+ -> statement schema1 params row
1370+ -> statement schema0 params row
1371+instance With' Query where
1372+ with' Done query = query
1373+ with' (cte :>> ctes) query = UnsafeQuery $
1374+ "WITH" <+> renderCtes renderQuery cte ctes <+> renderQuery query