on dev help sign in
query examples
commite9d269189d7d6e2db585e7a5cd832172829d80da authorEitan Chatav <eitan.chatav@gmail.com> dateWed 23 Aug 2017, 19:34 (9 years ago) parent4c73e25
4 files changed+306−61
squeal-postgresql/src/Squeal/PostgreSQL/Binary.hs+3−3 squeal-postgresql/src/Squeal/PostgreSQL/Definition.hs+6−6 squeal-postgresql/src/Squeal/PostgreSQL/Manipulation.hs+108−45 squeal-postgresql/src/Squeal/PostgreSQL/Query.hs+189−7
squeal-postgresql/src/Squeal/PostgreSQL/Binary.hs+3−3
@@ -249,10 +249,10 @@ instance
249249-- >>> toParams @(Only (Maybe Text)) @'[ 'Required ('Null 'PGtext)] (Only (Just "foo"))
250250-- K (Just "foo") :* Nil
251251--
252−-- >>> type PGShortRow = '["only" ::: 'Required ('Null 'PGtext)]
252+-- >>> type PGShortRow = '["fromOnly" ::: 'Required ('Null 'PGtext)]
253253-- >>> fromRow @PGShortRow @(Only (Maybe Text)) (K (Just "bar") :* Nil)
254−-- Only {only = Just "bar"}
255−newtype Only x = Only { only :: x }
254+-- Only {fromOnly = Just "bar"}
255+newtype Only x = Only { fromOnly :: x }
256256 deriving (Functor,Foldable,Traversable,Eq,Ord,Read,Show,GHC.Generic)
257257instance Generic (Only x)
258258instance HasDatatypeInfo (Only x)
squeal-postgresql/src/Squeal/PostgreSQL/Definition.hs+6−6
@@ -62,10 +62,10 @@ import Control.Category
6262import Control.DeepSeq
6363import Data.ByteString
6464import Data.Monoid
65−import Generics.SOP
6665import GHC.TypeLits
6766import Prelude hiding ((.), id)
6867
68+import qualified Generics.SOP as SOP
6969import qualified GHC.Generics as GHC
7070
7171import Squeal.PostgreSQL.Expression
@@ -104,7 +104,7 @@ CREATE statements
104104-- :}
105105-- "CREATE TABLE tab (a int, b real);"
106106createTable
107− :: (KnownSymbol table, SListI columns)
107+ :: (KnownSymbol table, SOP.SListI columns)
108108 => Alias table -- ^ the name of the table to add
109109 -> NP (Aliased TypeExpression) (column ': columns)
110110 -- ^ the names and datatype of each column
@@ -175,7 +175,7 @@ check condition = UnsafeTableConstraint $
175175-- :}
176176-- "CREATE TABLE tab (a int, b int, UNIQUE (a, b));"
177177unique
178− :: SListI subcolumns
178+ :: SOP.SListI subcolumns
179179 => NP (Column columns) subcolumns
180180 -- ^ unique column or group of columns
181181 -> TableConstraint schema columns
@@ -195,7 +195,7 @@ unique columns = UnsafeTableConstraint $
195195-- :}
196196-- "CREATE TABLE tab (id serial, name text NOT NULL, PRIMARY KEY (id));"
197197primaryKey
198− :: (SListI subcolumns, AllNotNull subcolumns)
198+ :: (SOP.SListI subcolumns, AllNotNull subcolumns)
199199 => NP (Column columns) subcolumns
200200 -- ^ identifying column or group of columns
201201 -> TableConstraint schema columns
@@ -239,8 +239,8 @@ foreignKey
239239 :: ( HasTable reftable schema refcolumns
240240 , SameTypes subcolumns refsubcolumns
241241 , NotAllNull subcolumns
242− , SListI subcolumns
243− , SListI refsubcolumns)
242+ , SOP.SListI subcolumns
243+ , SOP.SListI refsubcolumns)
244244 => NP (Column columns) subcolumns
245245 -- ^ column or columns in the table
246246 -> Alias reftable
squeal-postgresql/src/Squeal/PostgreSQL/Manipulation.hs+108−45
@@ -45,8 +45,8 @@ module Squeal.PostgreSQL.Manipulation
4545import Control.DeepSeq
4646import Data.ByteString
4747import Data.Monoid
48−import Generics.SOP
4948
49+import qualified Generics.SOP as SOP
5050import qualified GHC.Generics as GHC
5151
5252import Squeal.PostgreSQL.Expression
@@ -75,46 +75,109 @@ queryStatement q = UnsafeManipulation $ renderQuery q <> ";"
7575INSERT statements
7676-----------------------------------------}
7777
78−-- | When a table is created, it contains no data. The first thing to do
79−-- before a database can be of much use is to insert data. Data is
80−-- conceptually inserted one row at a time. Of course you can also insert
81−-- more than one row, but there is no way to insert less than one row.
82−-- Even if you know only some column values, a complete row must be created.
83−--
84−-- >>> :{
85−-- let
86−-- manipulation :: Manipulation
87−-- '[ "tab" :::
88−-- '[ "col1" ::: 'Required ('NotNull 'PGint4)
89−-- , "col2" ::: 'Required ('NotNull 'PGint4) ]] '[] '[]
90−-- manipulation =
91−-- insertInto #tab (Values (2 `As` #col1 :* 4 `As` #col2 :* Nil) [])
92−-- OnConflictDoRaise (Returning Nil)
93−-- in renderManipulation manipulation
94−-- :}
95−-- "INSERT INTO tab (col1, col2) VALUES (2, 4);"
96−--
97−-- >>> :{
98−-- let
99−-- manipulation :: Manipulation
100−-- '[ "tab" :::
101−-- '[ "col1" ::: 'Required ('NotNull 'PGint4)
102−-- , "col2" ::: 'Required ('NotNull 'PGint4) ]]
103−-- '[] '[ "sum" ::: 'Required ('NotNull 'PGint4)]
104−-- manipulation =
105−-- insertInto #tab
106−-- (Values
107−-- (2 `As` #col1 :* 4 `As` #col2 :* Nil)
108−-- [6 `As` #col1 :* 8 `As` #col2 :* Nil])
109−-- (OnConflictDoUpdate
110−-- (Set 2 `As` #col1 :* Same `As` #col2 :* Nil)
111−-- (Just (#col1 .== #col2)))
112−-- (Returning $ (#col1 + #col2) `As` #sum :* Nil)
113−-- in renderManipulation manipulation
114−-- :}
115−-- "INSERT INTO tab (col1, col2) VALUES (2, 4), (6, 8) ON CONFLICT DO UPDATE SET col1 = 2 WHERE (col1 = col2) RETURNING (col1 + col2) AS sum;"
78+{- |
79+When a table is created, it contains no data. The first thing to do
80+before a database can be of much use is to insert data. Data is
81+conceptually inserted one row at a time. Of course you can also insert
82+more than one row, but there is no way to insert less than one row.
83+Even if you know only some column values, a complete row must be created.
84+
85+simple insert:
86+
87+>>> :{
88+let
89+ manipulation :: Manipulation
90+ '[ "tab" :::
91+ '[ "col1" ::: 'Required ('NotNull 'PGint4)
92+ , "col2" ::: 'Required ('NotNull 'PGint4) ]] '[] '[]
93+ manipulation =
94+ insertInto #tab (Values (2 `As` #col1 :* 4 `As` #col2 :* Nil) [])
95+ OnConflictDoRaise (Returning Nil)
96+in renderManipulation manipulation
97+:}
98+"INSERT INTO tab (col1, col2) VALUES (2, 4);"
99+
100+parameterized insert:
101+
102+>>> :{
103+let
104+ manipulation :: Manipulation
105+ '[ "tab" :::
106+ '[ "col1" ::: 'Required ('NotNull 'PGint4)
107+ , "col2" ::: 'Required ('NotNull 'PGint4) ]]
108+ '[ 'Required ('NotNull 'PGint4)
109+ , 'Required ('NotNull 'PGint4) ] '[]
110+ manipulation =
111+ insertInto #tab
112+ (Values (param @1 `As` #col1 :* param @2 `As` #col2 :* Nil) [])
113+ OnConflictDoRaise (Returning Nil)
114+in renderManipulation manipulation
115+:}
116+"INSERT INTO tab (col1, col2) VALUES (($1 :: int4), ($2 :: int4));"
117+
118+returning insert:
119+
120+>>> :{
121+let
122+ manipulation :: Manipulation
123+ '[ "tab" :::
124+ '[ "col1" ::: 'Required ('NotNull 'PGint4)
125+ , "col2" ::: 'Required ('NotNull 'PGint4) ]] '[]
126+ '["fromOnly" ::: 'Required ('NotNull 'PGint4)]
127+ manipulation =
128+ insertInto #tab (Values (2 `As` #col1 :* 4 `As` #col2 :* Nil) [])
129+ OnConflictDoRaise (Returning (#col1 `As` #fromOnly :* Nil))
130+in renderManipulation manipulation
131+:}
132+"INSERT INTO tab (col1, col2) VALUES (2, 4) RETURNING col1 AS fromOnly;"
133+
134+query insert:
135+
136+>>> :{
137+let
138+ manipulation :: Manipulation
139+ '[ "tab" :::
140+ '[ "col1" ::: 'Required ('NotNull 'PGint4)
141+ , "col2" ::: 'Required ('NotNull 'PGint4)
142+ ]
143+ , "other_tab" :::
144+ '[ "col1" ::: 'Required ('NotNull 'PGint4)
145+ , "col2" ::: 'Required ('NotNull 'PGint4)
146+ ]
147+ ] '[] '[]
148+ manipulation =
149+ insertInto #tab
150+ ( ValuesQuery $
151+ selectStar (from (Table (#other_tab `As` #t))) )
152+ OnConflictDoRaise (Returning Nil)
153+in renderManipulation manipulation
154+:}
155+"INSERT INTO tab SELECT * FROM other_tab AS t;"
156+
157+upsert:
158+
159+>>> :{
160+let
161+ manipulation :: Manipulation
162+ '[ "tab" :::
163+ '[ "col1" ::: 'Required ('NotNull 'PGint4)
164+ , "col2" ::: 'Required ('NotNull 'PGint4) ]]
165+ '[] '[ "sum" ::: 'Required ('NotNull 'PGint4)]
166+ manipulation =
167+ insertInto #tab
168+ (Values
169+ (2 `As` #col1 :* 4 `As` #col2 :* Nil)
170+ [6 `As` #col1 :* 8 `As` #col2 :* Nil])
171+ (OnConflictDoUpdate
172+ (Set 2 `As` #col1 :* Same `As` #col2 :* Nil)
173+ (Just (#col1 .== #col2)))
174+ (Returning $ (#col1 + #col2) `As` #sum :* Nil)
175+in renderManipulation manipulation
176+:}
177+"INSERT INTO tab (col1, col2) VALUES (2, 4), (6, 8) ON CONFLICT DO UPDATE SET col1 = 2 WHERE (col1 = col2) RETURNING (col1 + col2) AS sum;"
178+-}
116179insertInto
117− :: (SListI columns, SListI results, HasTable table schema columns)
180+ :: (SOP.SListI columns, SOP.SListI results, HasTable table schema columns)
118181 => Alias table -- ^ table to insert into
119182 -> ValuesClause schema params columns -- ^ values to insert
120183 -> ConflictClause columns params
@@ -141,7 +204,7 @@ data ValuesClause
141204
142205-- | Render a `ValuesClause`.
143206renderValuesClause
144− :: SListI columns
207+ :: SOP.SListI columns
145208 => ValuesClause schema params columns
146209 -> ByteString
147210renderValuesClause = \case
@@ -181,7 +244,7 @@ data ReturningClause
181244
182245-- | Render a `ReturningClause`.
183246renderReturningClause
184− :: SListI results
247+ :: SOP.SListI results
185248 => ReturningClause params columns results
186249 -> ByteString
187250renderReturningClause = \case
@@ -205,7 +268,7 @@ data ConflictClause columns params where
205268
206269-- | Render a `ConflictClause`.
207270renderConflictClause
208− :: SListI columns
271+ :: SOP.SListI columns
209272 => ConflictClause columns params
210273 -> ByteString
211274renderConflictClause = \case
@@ -238,7 +301,7 @@ UPDATE statements
238301-- :}
239302-- "UPDATE tab SET col1 = 2 WHERE (col1 <> col2);"
240303update
241− :: (HasTable table schema columns, SListI columns, SListI results)
304+ :: (HasTable table schema columns, SOP.SListI columns, SOP.SListI results)
242305 => Alias table -- ^ table to update
243306 -> NP (Aliased (UpdateExpression columns params)) columns
244307 -- ^ modified values to replace old values
@@ -293,7 +356,7 @@ DELETE statements
293356-- :}
294357-- "DELETE FROM tab WHERE (col1 = col2) RETURNING *;"
295358deleteFrom
296− :: (SListI results, HasTable table schema columns)
359+ :: (SOP.SListI results, HasTable table schema columns)
297360 => Alias table -- ^ table to delete from
298361 -> Condition '[table ::: columns] 'Ungrouped params
299362 -- ^ condition under which to delete a row
squeal-postgresql/src/Squeal/PostgreSQL/Query.hs+189−7
@@ -82,10 +82,192 @@ import Squeal.PostgreSQL.Expression
8282import Squeal.PostgreSQL.Prettyprint
8383import Squeal.PostgreSQL.Schema
8484
85−-- | The process of retrieving or the command to retrieve data from a database
86−-- is called a `Query`. The `select`, `selectStar`, `selectDotStar`,
87−-- `selectDistinct`, `selectDistinctStar` and `selectDistinctDotStar` commands
88−-- are used to specify queries.
85+{- |
86+The process of retrieving or the command to retrieve data from a database
87+is called a `Query`. The `select`, `selectStar`, `selectDotStar`,
88+`selectDistinct`, `selectDistinctStar` and `selectDistinctDotStar` commands
89+are used to specify queries.
90+
91+simple query:
92+
93+>>> :{
94+let
95+ query :: Query '["tab" ::: '["col" ::: 'Required ('Null 'PGint4)]] '[]
96+ '["col" ::: 'Required ('Null 'PGint4)]
97+ query = selectStar (from (Table (#tab `As` #t)))
98+in renderQuery query
99+:}
100+"SELECT * FROM tab AS t"
101+
102+restricted query:
103+
104+>>> :{
105+let
106+ query :: Query
107+ '[ "tab" :::
108+ '[ "col1" ::: 'Required ('NotNull 'PGint4)
109+ , "col2" ::: 'Required ('NotNull 'PGint4) ]]
110+ '[]
111+ '[ "sum" ::: 'Required ('NotNull 'PGint4)
112+ , "col1" ::: 'Required ('NotNull 'PGint4) ]
113+ query =
114+ select
115+ ((#col1 + #col2) `As` #sum :* #col1 `As` #col1 :* Nil)
116+ ( from (Table (#tab `As` #t))
117+ & where_ (#col1 .> #col2)
118+ & where_ (#col2 .> 0) )
119+in renderQuery query
120+:}
121+"SELECT (col1 + col2) AS sum, col1 AS col1 FROM tab AS t WHERE ((col1 > col2) AND (col2 > 0))"
122+
123+subquery:
124+
125+>>> :{
126+let
127+ query :: Query '["tab" ::: '["col" ::: 'Required ('Null 'PGint4)]] '[]
128+ '["col" ::: 'Required ('Null 'PGint4)]
129+ query =
130+ selectStar
131+ (from (Subquery (selectStar (from (Table (#tab `As` #t))) `As` #sub)))
132+in renderQuery query
133+:}
134+"SELECT * FROM (SELECT * FROM tab AS t) AS sub"
135+
136+limits and offsets:
137+
138+>>> :{
139+let
140+ query :: Query '["tab" ::: '["col" ::: 'Required ('Null 'PGint4)]] '[]
141+ '["col" ::: 'Required ('Null 'PGint4)]
142+ query = selectStar
143+ ( from (Table (#tab `As` #t))
144+ & limit 100
145+ & offset 2
146+ & limit 50
147+ & offset 2 )
148+in renderQuery query
149+:}
150+"SELECT * FROM tab AS t LIMIT 50 OFFSET 4"
151+
152+parameterized query:
153+
154+>>> :{
155+let
156+ query :: Query '["tab" ::: '["col" ::: 'Required ('Null 'PGint4)]]
157+ '[ 'Required ('NotNull 'PGbool)]
158+ '["col" ::: 'Required ('Null 'PGint4)]
159+ query = selectStar
160+ (from (Table (#tab `As` #t)) & where_ (param @1))
161+in renderQuery query
162+:}
163+"SELECT * FROM tab AS t WHERE ($1 :: bool)"
164+
165+aggregation query:
166+
167+>>> :{
168+let
169+ query :: Query
170+ '[ "tab" :::
171+ '[ "col1" ::: 'Required ('NotNull 'PGint4)
172+ , "col2" ::: 'Required ('NotNull 'PGint4) ]]
173+ '[]
174+ '[ "sum" ::: 'Required ('NotNull 'PGint4)
175+ , "col1" ::: 'Required ('NotNull 'PGint4) ]
176+ query =
177+ select (sum_ #col2 `As` #sum :* #col1 `As` #col1 :* Nil)
178+ ( from (Table (#tab `As` #table1))
179+ & group (By #col1 :* Nil)
180+ & having (#col1 + sum_ #col2 .> 1) )
181+in renderQuery query
182+:}
183+"SELECT sum(col2) AS sum, col1 AS col1 FROM tab AS table1 GROUP BY col1 HAVING ((col1 + sum(col2)) > 1)"
184+
185+sorted query:
186+
187+>>> :{
188+let
189+ query :: Query '["tab" ::: '["col" ::: 'Required ('Null 'PGint4)]] '[]
190+ '["col" ::: 'Required ('Null 'PGint4)]
191+ query = selectStar
192+ (from (Table (#tab `As` #t)) & orderBy [#col & AscNullsFirst])
193+in renderQuery query
194+:}
195+"SELECT * FROM tab AS t ORDER BY col ASC NULLS FIRST"
196+
197+joins:
198+
199+>>> :set -XFlexibleContexts
200+>>> :{
201+let
202+ query1, query2 :: Query
203+ '[ "orders" :::
204+ '[ "id" ::: 'Required ('NotNull 'PGint4)
205+ , "order_price" ::: 'Required ('NotNull 'PGfloat4)
206+ , "customer_id" ::: 'Required ('NotNull 'PGint4)
207+ , "shipper_id" ::: 'Required ('NotNull 'PGint4)
208+ ]
209+ , "customers" :::
210+ '[ "id" ::: 'Required ('NotNull 'PGint4)
211+ , "name" ::: 'Required ('NotNull 'PGtext)
212+ ]
213+ , "shippers" :::
214+ '[ "id" ::: 'Required ('NotNull 'PGint4)
215+ , "name" ::: 'Required ('NotNull 'PGtext)
216+ ]
217+ ]
218+ '[]
219+ '[ "order_price" ::: 'Required ('NotNull 'PGfloat4)
220+ , "customer_name" ::: 'Required ('NotNull 'PGtext)
221+ , "shipper_name" ::: 'Required ('NotNull 'PGtext)
222+ ]
223+ query1 = select
224+ ( #orders ! #order_price `As` #order_price :*
225+ #customers ! #name `As` #customer_name :*
226+ #shippers ! #name `As` #shipper_name :* Nil )
227+ ( from (Table (#orders `As` #orders)
228+ & CrossJoin (Table (#customers `As` #customers))
229+ & CrossJoin (Table (#shippers `As` #shippers))) )
230+ query2 = select
231+ ( #orders ! #order_price `As` #order_price :*
232+ #customers ! #name `As` #customer_name :*
233+ #shippers ! #name `As` #shipper_name :* Nil )
234+ ( from (Table (#orders `As` #orders)
235+ & InnerJoin (Table (#customers `As` #customers))
236+ (#orders ! #customer_id .== #customers ! #id)
237+ & InnerJoin (Table (#shippers `As` #shippers))
238+ (#orders ! #shipper_id .== #shippers ! #id)) )
239+in do print (renderQuery query1); print (renderQuery query2)
240+:}
241+"SELECT orders.order_price AS order_price, customers.name AS customer_name, shippers.name AS shipper_name FROM orders AS orders CROSS JOIN customers AS customers CROSS JOIN shippers AS shippers"
242+"SELECT orders.order_price AS order_price, customers.name AS customer_name, shippers.name AS shipper_name FROM orders AS orders INNER JOIN customers AS customers ON (orders.customer_id = customers.id) INNER JOIN shippers AS shippers ON (orders.shipper_id = shippers.id)"
243+
244+self-join:
245+
246+>>> :{
247+let
248+ query :: Query '["tab" ::: '["col" ::: 'Required ('Null 'PGint4)]] '[]
249+ '["col" ::: 'Required ('Null 'PGint4)]
250+ query = selectDotStar #t1
251+ (from (Table (#tab `As` #t1) & CrossJoin (Table (#tab `As` #t2))))
252+in renderQuery query
253+:}
254+"SELECT t1.* FROM tab AS t1 CROSS JOIN tab AS t2"
255+
256+set operations:
257+
258+>>> :{
259+let
260+ query :: Query '["tab" ::: '["col" ::: 'Required ('Null 'PGint4)]] '[]
261+ '["col" ::: 'Required ('Null 'PGint4)]
262+ query =
263+ selectStar (from (Table (#tab `As` #t)))
264+ `unionAll`
265+ selectStar (from (Table (#tab `As` #t)))
266+in renderQuery query
267+:}
268+"(SELECT * FROM tab AS t) UNION ALL (SELECT * FROM tab AS t)"
269+-}
270+
89271newtype Query
90272 (schema :: TablesType)
91273 (params :: [ColumnType])
@@ -305,7 +487,7 @@ renderTableExpression
305487 , renderWheres whs'
306488 , renderGroupByClause grps'
307489 , renderHavingClause hvs'
308− , renderSorts srts'
490+ , renderOrderByClause srts'
309491 , renderLimits lims'
310492 , renderOffsets offs'
311493 ]
@@ -314,9 +496,9 @@ renderTableExpression
314496 [] -> ""
315497 wh:[] -> " WHERE" <+> renderExpression wh
316498 wh:whs -> " WHERE" <+> renderExpression (foldr (.&&) wh whs)
317− renderSorts = \case
499+ renderOrderByClause = \case
318500 [] -> ""
319− srts -> " SORT BY"
501+ srts -> " ORDER BY"
320502 <+> commaSeparated (renderSortExpression <$> srts)
321503 renderLimits = \case
322504 [] -> ""