on dev help sign in
query docs
commit9845528e1c1d241ec5a2243ee3b37b7d98fefb7e authorEitan Chatav <eitan@Eitans-MacBook-Pro.local> dateTue 16 Apr 2019, 11:25 (7 years ago) parentd01b51d
1 file changed+93−53
squeal-postgresql/src/Squeal/PostgreSQL/Query.hs+93−53
squeal-postgresql/src/Squeal/PostgreSQL/Query.hs+93−53
@@ -32,8 +32,8 @@ Squeal queries.
3232
3333module Squeal.PostgreSQL.Query
3434 ( -- * Queries
35− Query (..)
36− , Query_
35+ Query_
36+ , Query (..)
3737 -- ** Select
3838 , select
3939 , select_
@@ -101,10 +101,46 @@ import Squeal.PostgreSQL.Schema
101101
102102-- $setup
103103-- >>> import Squeal.PostgreSQL
104+-- >>> import Data.Int (Int32, Int64)
105+-- >>> import Data.Monoid (Sum (..))
106+-- >>> import Data.Text (Text)
107+-- >>> import qualified Generics.SOP as SOP
104108
105109{- |
106−The process of retrieving or the command to retrieve data from a database
107−is called a `Query`. Let's see some examples of queries.
110+The general `Query` type is parameterized by
111+
112+* @outer :: FromType@ - outer scope for a correlated subquery,
113+* @commons :: FromType@ - scope for all `common` table expressions,
114+* @schemas :: SchemasType@ - scope for all `table`s and `view`s,
115+* @params :: [NullityType]@ - scope for all `Squeal.Expression.Parameter.parameter`s,
116+* @row :: RowType@ - return type of the `Query`.
117+-}
118+newtype Query
119+ (outer :: FromType)
120+ (commons :: FromType)
121+ (schemas :: SchemasType)
122+ (params :: [NullityType])
123+ (row :: RowType)
124+ = UnsafeQuery { renderQuery :: ByteString }
125+ deriving (GHC.Generic,Show,Eq,Ord,NFData)
126+instance RenderSQL (Query outer commons schemas params row) where renderSQL = renderQuery
127+
128+{- |
129+The top level `Query_` type is parameterized by a @schemas@ `SchemasType`,
130+against which the query is type-checked, an input @parameters@ Haskell `Type`,
131+and an ouput row Haskell `Type`.
132+
133+A top-level query can be run
134+using `Squeal.PostgreSQL.PQ.runQueryParams`, or if @parameters = ()@
135+using `Squeal.PostgreSQL.PQ.runQuery`.
136+
137+Generally, @parameters@ will be a Haskell tuple or record whose entries
138+may be referenced using positional
139+`Squeal.PostgreSQL.Expression.Parameter.parameter`s and @row@ will be a
140+Haskell record or a generalized record using tuples of `P` types and normal Haskell
141+records, whose entries will be targeted using overloaded labels.
142+
143+Let's see some examples of queries.
108144
109145simple query:
110146
@@ -112,7 +148,7 @@ simple query:
112148>>> type Schema = '["tab" ::: 'Table ('[] :=> Columns)]
113149>>> :{
114150let
115− query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
151+ query :: Query_ (Public Schema) () (P ("col1" ::: Int32), P ("col2" ::: Int32))
116152 query = select Star (from (table #tab))
117153in printSQL query
118154:}
@@ -122,21 +158,21 @@ restricted query:
122158
123159>>> :{
124160let
125− query :: Query '[] '[] (Public Schema) '[] '["sum" ::: 'NotNull 'PGint4, "col1" ::: 'NotNull 'PGint4]
161+ query :: Query_ (Public Schema) () (Sum Int32, P ("col1" ::: Int32))
126162 query =
127− select_ ((#col1 + #col2) `as` #sum :* #col1)
163+ select_ ((#col1 + #col2) `as` #getSum :* #col1)
128164 ( from (table #tab)
129165 & where_ (#col1 .> #col2)
130166 & where_ (#col2 .> 0) )
131167in printSQL query
132168:}
133−SELECT ("col1" + "col2") AS "sum", "col1" AS "col1" FROM "tab" AS "tab" WHERE (("col1" > "col2") AND ("col2" > 0))
169+SELECT ("col1" + "col2") AS "getSum", "col1" AS "col1" FROM "tab" AS "tab" WHERE (("col1" > "col2") AND ("col2" > 0))
134170
135171subquery:
136172
137173>>> :{
138174let
139− query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
175+ query :: Query_ (Public Schema) () (P ("col1" ::: Int32), P ("col2" ::: Int32))
140176 query = select Star (from (subquery (select Star (from (table #tab)) `as` #sub)))
141177in printSQL query
142178:}
@@ -146,7 +182,7 @@ limits and offsets:
146182
147183>>> :{
148184let
149− query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
185+ query :: Query_ (Public Schema) () (P ("col1" ::: Int32), P ("col2" ::: Int32))
150186 query = select Star (from (table #tab) & limit 100 & offset 2 & limit 50 & offset 2)
151187in printSQL query
152188:}
@@ -156,7 +192,7 @@ parameterized query:
156192
157193>>> :{
158194let
159− query :: Query '[] '[] (Public Schema) '[ 'NotNull 'PGint4] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
195+ query :: Query_ (Public Schema) (Only Int32) (P ("col1" ::: Int32), P ("col2" ::: Int32))
160196 query = select Star (from (table #tab) & where_ (#col1 .> param @1))
161197in printSQL query
162198:}
@@ -166,21 +202,21 @@ aggregation query:
166202
167203>>> :{
168204let
169− query :: Query '[] '[] (Public Schema) '[] '["sum" ::: 'NotNull 'PGint4, "col1" ::: 'NotNull 'PGint4 ]
205+ query :: Query_ (Public Schema) () (Sum Int32, P ("col1" ::: Int32))
170206 query =
171− select_ (sum_ (All #col2) `as` #sum :* #col1)
207+ select_ (sum_ (All #col2) `as` #getSum :* #col1)
172208 ( from (table (#tab `as` #table1))
173209 & groupBy #col1
174210 & having (#col1 + sum_ (Distinct #col2) .> 1) )
175211in printSQL query
176212:}
177−SELECT sum(ALL "col2") AS "sum", "col1" AS "col1" FROM "tab" AS "table1" GROUP BY "col1" HAVING (("col1" + sum(DISTINCT "col2")) > 1)
213+SELECT sum(ALL "col2") AS "getSum", "col1" AS "col1" FROM "tab" AS "table1" GROUP BY "col1" HAVING (("col1" + sum(DISTINCT "col2")) > 1)
178214
179215sorted query:
180216
181217>>> :{
182218let
183− query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
219+ query :: Query_ (Public Schema) () (P ("col1" ::: Int32), P ("col2" ::: Int32))
184220 query = select Star (from (table #tab) & orderBy [#col1 & Asc])
185221in printSQL query
186222:}
@@ -188,14 +224,12 @@ SELECT * FROM "tab" AS "tab" ORDER BY "col1" ASC
188224
189225joins:
190226
191−>>> :set -XFlexibleContexts
192227>>> :{
193228type OrdersColumns =
194229 '[ "id" ::: 'NoDef :=> 'NotNull 'PGint4
195− , "price" ::: 'NoDef :=> 'NotNull 'PGfloat4
196− , "customer_id" ::: 'NoDef :=> 'NotNull 'PGint4
197− , "shipper_id" ::: 'NoDef :=> 'NotNull 'PGint4
198− ]
230+ , "price" ::: 'NoDef :=> 'NotNull 'PGfloat4
231+ , "customer_id" ::: 'NoDef :=> 'NotNull 'PGint4
232+ , "shipper_id" ::: 'NoDef :=> 'NotNull 'PGint4 ]
199233:}
200234
201235>>> :{
@@ -211,22 +245,27 @@ type OrdersConstraints =
211245>>> :{
212246type OrdersSchema =
213247 '[ "orders" ::: 'Table (OrdersConstraints :=> OrdersColumns)
214− , "customers" ::: 'Table (CustomersConstraints :=> NamesColumns)
215− , "shippers" ::: 'Table (ShippersConstraints :=> NamesColumns) ]
248+ , "customers" ::: 'Table (CustomersConstraints :=> NamesColumns)
249+ , "shippers" ::: 'Table (ShippersConstraints :=> NamesColumns) ]
250+:}
251+
252+>>> :{
253+data Order = Order
254+ { price :: Float
255+ , customerName :: Text
256+ , shipperName :: Text
257+ } deriving GHC.Generic
258+instance SOP.Generic Order
259+instance SOP.HasDatatypeInfo Order
216260:}
217261
218262>>> :{
219263let
220− query :: Query '[] '[] (Public OrdersSchema)
221− '[]
222− '[ "order_price" ::: 'NotNull 'PGfloat4
223− , "customer_name" ::: 'NotNull 'PGtext
224− , "shipper_name" ::: 'NotNull 'PGtext
225− ]
264+ query :: Query_ (Public OrdersSchema) () Order
226265 query = select_
227− ( #o ! #price `as` #order_price :*
228− #c ! #name `as` #customer_name :*
229− #s ! #name `as` #shipper_name )
266+ ( #o ! #price `as` #price :*
267+ #c ! #name `as` #customerName :*
268+ #s ! #name `as` #shipperName )
230269 ( from (table (#orders `as` #o)
231270 & innerJoin (table (#customers `as` #c))
232271 (#o ! #customer_id .== #c ! #id)
@@ -234,13 +273,13 @@ let
234273 (#o ! #shipper_id .== #s ! #id)) )
235274in printSQL query
236275:}
237−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")
276+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")
238277
239278self-join:
240279
241280>>> :{
242281let
243− query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
282+ query :: Query_ (Public Schema) () (P ("col1" ::: Int32), P ("col2" ::: Int32))
244283 query = select (#t1 & DotStar) (from (table (#tab `as` #t1) & crossJoin (table (#tab `as` #t2))))
245284in printSQL query
246285:}
@@ -250,7 +289,7 @@ value queries:
250289
251290>>> :{
252291let
253− query :: Query '[] commons schemas '[] '["foo" ::: 'NotNull 'PGint2, "bar" ::: 'NotNull 'PGbool]
292+ query :: Query_ schemas () (P ("foo" ::: Int32), P ("bar" ::: Bool))
254293 query = values (1 `as` #foo :* true `as` #bar) [2 `as` #foo :* false `as` #bar]
255294in printSQL query
256295:}
@@ -260,7 +299,7 @@ set operations:
260299
261300>>> :{
262301let
263− query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
302+ query :: Query_ (Public Schema) () (P ("col1" ::: Int32), P ("col2" ::: Int32))
264303 query = select Star (from (table #tab)) `unionAll` select Star (from (table #tab))
265304in printSQL query
266305:}
@@ -270,7 +309,7 @@ with queries:
270309
271310>>> :{
272311let
273− query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4, "col2" ::: 'NotNull 'PGint4]
312+ query :: Query_ (Public Schema) () (P ("col1" ::: Int32), P ("col2" ::: Int32))
274313 query = with (
275314 select Star (from (table #tab)) `as` #cte1 :>>
276315 select Star (from (common #cte1)) `as` #cte2
@@ -283,7 +322,7 @@ window function queries
283322
284323>>> :{
285324let
286− query :: Query '[] '[] (Public Schema) '[] ["col1" ::: 'NotNull 'PGint4, "rank" ::: 'NotNull 'PGint8]
325+ query :: Query_ (Public Schema) () (P ("col1" ::: Int32), P ("rank" ::: Int64))
287326 query = select
288327 (#col1 & Also (rank `as` #rank `Over` (partitionBy #col1 & orderBy [#col2 & Asc])))
289328 (from (table #tab))
@@ -295,7 +334,7 @@ correlated subqueries
295334
296335>>> :{
297336let
298− query :: Query '[] '[] (Public Schema) '[] '["col1" ::: 'NotNull 'PGint4]
337+ query :: Query_ (Public Schema) () (P ("col1" ::: Int32))
299338 query =
300339 select #col1 (from (table (#tab `as` #t1))
301340 & where_ (exists (
@@ -304,19 +343,14 @@ let
304343in printSQL query
305344:}
306345SELECT "col1" AS "col1" FROM "tab" AS "t1" WHERE EXISTS (SELECT * FROM "tab" AS "t2" WHERE ("t2"."col2" = "t1"."col1"))
346+
307347-}
308−newtype Query
309− (outer :: FromType)
310− (commons :: FromType)
348+type family Query_
311349 (schemas :: SchemasType)
312− (params :: [NullityType])
313− (row :: RowType)
314− = UnsafeQuery { renderQuery :: ByteString }
315− deriving (GHC.Generic,Show,Eq,Ord,NFData)
316−instance RenderSQL (Query outer commons schemas params row) where renderSQL = renderQuery
317−
318−type family Query_ (schemas :: SchemasType) (params :: Type) (row :: Type) where
319− Query_ schemas params row = Query '[] '[] schemas (TuplePG params) (RowPG row)
350+ (parameters :: Type)
351+ (row :: Type) where
352+ Query_ schemas params row =
353+ Query '[] '[] schemas (TuplePG params) (RowPG row)
320354
321355-- | The results of two queries can be combined using the set operation
322356-- `union`. Duplicate rows are eliminated.
@@ -481,7 +515,7 @@ instance IsString
481515select
482516 :: (SListI row, row ~ (x ': xs))
483517 => Selection outer commons grp schemas params from row
484− -- ^ select list
518+ -- ^ selection
485519 -> TableExpression outer commons grp schemas params from
486520 -- ^ intermediate virtual table
487521 -> Query outer commons schemas params row
@@ -490,19 +524,23 @@ select selection tabexpr = UnsafeQuery $
490524 <+> renderSQL selection
491525 <+> renderSQL tabexpr
492526
527+-- | Like `select` but takes an `NP` list of `Expression`s instead
528+-- of a general `Selection`.
493529select_
494530 :: (SListI row, row ~ (x ': xs))
495531 => NP (Aliased (Expression outer commons grp schemas params from)) row
532+ -- ^ select list
496533 -> TableExpression outer commons grp schemas params from
534+ -- ^ intermediate virtual table
497535 -> Query outer commons schemas params row
498−select_ list = select (List list)
536+select_ = select . List
499537
500538-- | After the select list has been processed, the result table can
501539-- be subject to the elimination of duplicate rows using `selectDistinct`.
502540selectDistinct
503541 :: (SListI columns, columns ~ (col ': cols))
504542 => Selection outer commons 'Ungrouped schemas params from columns
505− -- ^ select list
543+ -- ^ selection
506544 -> TableExpression outer commons 'Ungrouped schemas params from
507545 -- ^ intermediate virtual table
508546 -> Query outer commons schemas params columns
@@ -511,6 +549,8 @@ selectDistinct selection tabexpr = UnsafeQuery $
511549 <+> renderSQL selection
512550 <+> renderSQL tabexpr
513551
552+-- | Like `selectDistinct` but takes an `NP` list of `Expression`s instead
553+-- of a general `Selection`.
514554selectDistinct_
515555 :: (SListI columns, columns ~ (col ': cols))
516556 => NP (Aliased (Expression outer commons 'Ungrouped schemas params from)) columns
@@ -518,7 +558,7 @@ selectDistinct_
518558 -> TableExpression outer commons 'Ungrouped schemas params from
519559 -- ^ intermediate virtual table
520560 -> Query outer commons schemas params columns
521−selectDistinct_ list = select (List list)
561+selectDistinct_ = selectDistinct . List
522562
523563-- | `values` computes a row value or set of row values
524564-- specified by value expressions. It is most commonly used