on dev help sign in
group by
commit6a62475af8772540815d6374640fbb44a4d9bc06 authorEitan Chatav <eitan.chatav@gmail.com> dateMon 7 Aug 2017, 11:54 (9 years ago) parent993b787
2 files changed+288−165
squeel-postgresql/src/Squeel/PostgreSQL/Schema.hs+5 squeel-postgresql/src/Squeel/PostgreSQL/Statement.hs+283−165
squeel-postgresql/src/Squeel/PostgreSQL/Schema.hs+5
@@ -35,6 +35,7 @@ module Squeel.PostgreSQL.Schema
3535 , Alter
3636 , Rename
3737 , Join
38+ , Grouping (..)
3839 , module GHC.OverloadedLabels
3940 , module GHC.TypeLits
4041 ) where
@@ -167,3 +168,7 @@ type family Rename alias0 alias1 xs where
167168type family Join xs ys where
168169 Join '[] ys = ys
169170 Join (x ': xs) ys = x ': Join xs ys
171+
172+data Grouping
173+ = Ungrouped
174+ | Grouped [(Symbol,(Symbol,ColumnType))]
squeel-postgresql/src/Squeel/PostgreSQL/Statement.hs+283−165
@@ -42,31 +42,34 @@ column expressions
4242newtype Expression
4343 (params :: [ColumnType])
4444 (tables :: [(Symbol,[(Symbol,ColumnType)])])
45+ (grouping :: Grouping)
4546 (ty :: ColumnType)
4647 = UnsafeExpression { renderExpression :: ByteString }
4748 deriving (Show,Eq)
4849
4950class KnownNat n => HasParameter (n :: Nat) params ty | n params -> ty where
50− param :: Proxy# n -> Expression params tables ty
51+ param :: Proxy# n -> Expression params tables grouping ty
5152 param p = UnsafeExpression $ ("$" <>) $ fromString $ show $ natVal' p
5253instance {-# OVERLAPPING #-} HasParameter 1 (ty1:tys) ty1
5354instance {-# OVERLAPPABLE #-} (KnownNat n, HasParameter (n-1) params ty)
5455 => HasParameter n (ty' : params) ty
5556
56−param1 :: Expression (ty1:tys) tables ty1
57+param1 :: Expression (ty1:tys) tables grouping ty1
5758param1 = param (proxy# :: Proxy# 1)
58−param2 :: Expression (ty1:ty2:tys) tables ty2
59+param2 :: Expression (ty1:ty2:tys) tables grouping ty2
5960param2 = param (proxy# :: Proxy# 2)
60−param3 :: Expression (ty1:ty2:ty3:tys) tables ty3
61+param3 :: Expression (ty1:ty2:ty3:tys) tables grouping ty3
6162param3 = param (proxy# :: Proxy# 3)
62−param4 :: Expression (ty1:ty2:ty3:ty4:tys) tables ty4
63+param4 :: Expression (ty1:ty2:ty3:ty4:tys) tables grouping ty4
6364param4 = param (proxy# :: Proxy# 4)
64−param5 :: Expression (ty1:ty2:ty3:ty4:ty5:tys) tables ty5
65+param5 :: Expression (ty1:ty2:ty3:ty4:ty5:tys) tables grouping ty5
6566param5 = param (proxy# :: Proxy# 5)
6667
6768class KnownSymbol column => HasColumn column columns ty
6869 | column columns -> ty where
69− getColumn :: Proxy# column -> Expression params '[table ::: columns] ty
70+ getColumn
71+ :: Proxy# column
72+ -> Expression params '[table ::: columns] 'Ungrouped ty
7073 getColumn column = UnsafeExpression $ fromString $ symbolVal' column
7174instance {-# OVERLAPPING #-} KnownSymbol column
7275 => HasColumn column ((column ::: optionality ty) ': tys) ('Required ty)
@@ -74,30 +77,30 @@ instance {-# OVERLAPPABLE #-} (KnownSymbol column, HasColumn column table ty)
7477 => HasColumn column (ty' ': table) ty
7578
7679instance (HasColumn column columns ty, tables ~ '[table ::: columns])
77− => IsLabel column (Expression params tables ty) where
80+ => IsLabel column (Expression params tables 'Ungrouped ty) where
7881 fromLabel = getColumn
7982
8083(&.)
8184 :: (HasTable table tables columns, HasColumn column columns ty)
82− => Alias table -> Alias column -> Expression params tables ty
85+ => Alias table -> Alias column -> Expression params tables 'Ungrouped ty
8386table &. column = UnsafeExpression $
8487 renderAlias table <> "." <> renderAlias column
8588
86−def :: Expression params '[] ('Optional (nullity ty))
89+def :: Expression params '[] grouping ('Optional (nullity ty))
8790def = UnsafeExpression "DEFAULT"
8891
8992notDef
90− :: Expression params '[] ('Required (nullity ty))
91− -> Expression params '[] ('Optional (nullity ty))
93+ :: Expression params '[] grouping ('Required (nullity ty))
94+ -> Expression params '[] grouping ('Optional (nullity ty))
9295notDef = UnsafeExpression . renderExpression
9396
94−null :: Expression params tables (optionality ('Null ty))
97+null :: Expression params tables grouping (optionality ('Null ty))
9598null = UnsafeExpression "NULL"
9699
97100coalesce
98− :: [Expression params tables ('Required ('Null x))]
99− -> Expression params tables ('Required ('NotNull x))
100− -> Expression params tables ('Required ('NotNull x))
101+ :: [Expression params tables grouping ('Required ('Null x))]
102+ -> Expression params tables grouping ('Required ('NotNull x))
103+ -> Expression params tables grouping ('Required ('NotNull x))
101104coalesce nulls isn'tNull = UnsafeExpression $ mconcat
102105 [ "COALESCE("
103106 , ByteString.intercalate ", " $ map renderExpression nulls
@@ -107,28 +110,28 @@ coalesce nulls isn'tNull = UnsafeExpression $ mconcat
107110
108111unsafeBinaryOp
109112 :: ByteString
110− -> Expression params tables ty0
111− -> Expression params tables ty1
112− -> Expression params tables ty2
113+ -> Expression params tables grouping ty0
114+ -> Expression params tables grouping ty1
115+ -> Expression params tables grouping ty2
113116unsafeBinaryOp op x y = UnsafeExpression $ mconcat
114117 ["(", renderExpression x, " ", op, " ", renderExpression y, ")"]
115118
116119unsafeUnaryOp
117120 :: ByteString
118− -> Expression params tables ty0
119− -> Expression params tables ty1
121+ -> Expression params tables grouping ty0
122+ -> Expression params tables grouping ty1
120123unsafeUnaryOp op x = UnsafeExpression $ mconcat
121124 ["(", op, " ", renderExpression x, ")"]
122125
123126unsafeFunction
124127 :: ByteString
125− -> Expression params tables xty
126− -> Expression params tables yty
128+ -> Expression params tables grouping xty
129+ -> Expression params tables grouping yty
127130unsafeFunction fun x = UnsafeExpression $ mconcat
128131 [fun, "(", renderExpression x, ")"]
129132
130133instance PGNum ty
131− => Num (Expression params tables ('Required (nullity ty))) where
134+ => Num (Expression params tables grouping ('Required (nullity ty))) where
132135 (+) = unsafeBinaryOp "+"
133136 (-) = unsafeBinaryOp "-"
134137 (*) = unsafeBinaryOp "*"
@@ -140,13 +143,13 @@ instance PGNum ty
140143 . fromString
141144 . show
142145
143−instance PGFractional ty
144− => Fractional (Expression params tables ('Required (nullity ty))) where
146+instance PGFractional ty => Fractional
147+ (Expression params tables grouping ('Required (nullity ty))) where
145148 (/) = unsafeBinaryOp "/"
146149 fromRational x = fromInteger (numerator x) / fromInteger (denominator x)
147150
148−instance (PGFloating ty, PGCast 'PGNumeric ty, PGTyped ty)
149− => Floating (Expression params tables ('Required (nullity ty))) where
151+instance (PGFloating ty, PGCast 'PGNumeric ty, PGTyped ty) => Floating
152+ (Expression params tables grouping ('Required (nullity ty))) where
150153 pi = UnsafeExpression "pi()"
151154 exp = unsafeFunction "exp"
152155 log = unsafeFunction "ln"
@@ -156,9 +159,9 @@ instance (PGFloating ty, PGCast 'PGNumeric ty, PGTyped ty)
156159 logBase b y = cast pgtype $ logBaseNumeric b y
157160 where
158161 logBaseNumeric
159− :: Expression params tables ('Required (nullity ty))
160− -> Expression params tables ('Required (nullity ty))
161− -> Expression params tables ('Required (nullity 'PGNumeric))
162+ :: Expression params tables grouping ('Required (nullity ty))
163+ -> Expression params tables grouping ('Required (nullity ty))
164+ -> Expression params tables grouping ('Required (nullity 'PGNumeric))
162165 logBaseNumeric b' y' = UnsafeExpression $ mconcat
163166 [ "log("
164167 , renderExpression b' <> "::numeric"
@@ -181,17 +184,17 @@ instance (PGFloating ty, PGCast 'PGNumeric ty, PGTyped ty)
181184
182185atan2
183186 :: PGFloating float
184− => Expression params tables ('Required (nullity float))
185− -> Expression params tables ('Required (nullity float))
186− -> Expression params tables ('Required (nullity float))
187+ => Expression params tables grouping ('Required (nullity float))
188+ -> Expression params tables grouping ('Required (nullity float))
189+ -> Expression params tables grouping ('Required (nullity float))
187190atan2 y x = UnsafeExpression $
188191 "atan2(" <> renderExpression y <> ", " <> renderExpression x <> ")"
189192
190193class PGCast (ty0 :: PGType) (ty1 :: PGType) where
191194 cast
192195 :: TypeExpression ('Required ('Null ty1))
193− -> Expression params tables ('Required (nullity ty0))
194− -> Expression params tables ('Required (nullity ty1))
196+ -> Expression params tables grouping ('Required (nullity ty0))
197+ -> Expression params tables grouping ('Required (nullity ty1))
195198 cast ty x = UnsafeExpression $
196199 "(" <> renderExpression x <> "::" <> renderTypeExpression ty <> ")"
197200instance PGCast 'PGInt2 'PGInt2
@@ -209,96 +212,97 @@ instance PGCast 'PGInt8 'PGNumeric
209212
210213div
211214 :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
212− => Expression params tables ('Required (nullity ty))
213− -> Expression params tables ('Required (nullity ty))
214− -> Expression params tables ('Required (nullity ty))
215+ => Expression params tables grouping ('Required (nullity ty))
216+ -> Expression params tables grouping ('Required (nullity ty))
217+ -> Expression params tables grouping ('Required (nullity ty))
215218div = unsafeBinaryOp "/"
216219
217220mod
218221 :: (PGNum ty, PGTyped ty, PGCast 'PGInt8 ty, PGCast ty 'PGInt8)
219− => Expression params tables ('Required (nullity ty))
220− -> Expression params tables ('Required (nullity ty))
221− -> Expression params tables ('Required (nullity ty))
222+ => Expression params tables grouping ('Required (nullity ty))
223+ -> Expression params tables grouping ('Required (nullity ty))
224+ -> Expression params tables grouping ('Required (nullity ty))
222225mod = unsafeBinaryOp "%"
223226
224227truncate
225228 :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
226− => Expression params tables ('Required (nullity frac))
227− -> Expression params tables ('Required (nullity int))
229+ => Expression params tables grouping ('Required (nullity frac))
230+ -> Expression params tables grouping ('Required (nullity int))
228231truncate = cast pgtype . truncate'
229232 where
230233 truncate'
231− :: Expression params tables ('Required (nullity frac))
232− -> Expression params tables ('Required (nullity 'PGInt4))
234+ :: Expression params tables grouping ('Required (nullity frac))
235+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
233236 truncate' = unsafeFunction "trunc"
234237
235238round
236239 :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
237− => Expression params tables ('Required (nullity frac))
238− -> Expression params tables ('Required (nullity int))
240+ => Expression params tables grouping ('Required (nullity frac))
241+ -> Expression params tables grouping ('Required (nullity int))
239242round = cast pgtype . round'
240243 where
241244 round'
242− :: Expression params tables ('Required (nullity frac))
243− -> Expression params tables ('Required (nullity 'PGInt4))
245+ :: Expression params tables grouping ('Required (nullity frac))
246+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
244247 round' = unsafeFunction "round"
245248
246249ceiling
247250 :: (PGFractional frac, PGCast 'PGInt4 int, PGTyped int)
248− => Expression params tables ('Required (nullity frac))
249− -> Expression params tables ('Required (nullity int))
251+ => Expression params tables grouping ('Required (nullity frac))
252+ -> Expression params tables grouping ('Required (nullity int))
250253ceiling = cast pgtype . ceiling'
251254 where
252255 ceiling'
253− :: Expression params tables ('Required (nullity frac))
254− -> Expression params tables ('Required (nullity 'PGInt4))
256+ :: Expression params tables grouping ('Required (nullity frac))
257+ -> Expression params tables grouping ('Required (nullity 'PGInt4))
255258 ceiling' = unsafeFunction "ceiling"
256259
257−instance IsString (Expression params tables ('Required (nullity 'PGText))) where
258− fromString str = UnsafeExpression $
259− "E\'" <> fromString (escape =<< str) <> "\'"
260− where
261− escape = \case
262− '\NUL' -> "\\0"
263− '\'' -> "''"
264− '"' -> "\\\""
265− '\b' -> "\\b"
266− '\n' -> "\\n"
267− '\r' -> "\\r"
268− '\t' -> "\\t"
269− '\\' -> "\\\\"
270− c -> [c]
271−
272−type Condition params tables =
273− Expression params tables ('Required ('NotNull 'PGBool))
274−
275−true :: Condition params tables
260+instance IsString
261+ (Expression params tables grouping ('Required (nullity 'PGText))) where
262+ fromString str = UnsafeExpression $
263+ "E\'" <> fromString (escape =<< str) <> "\'"
264+ where
265+ escape = \case
266+ '\NUL' -> "\\0"
267+ '\'' -> "''"
268+ '"' -> "\\\""
269+ '\b' -> "\\b"
270+ '\n' -> "\\n"
271+ '\r' -> "\\r"
272+ '\t' -> "\\t"
273+ '\\' -> "\\\\"
274+ c -> [c]
275+
276+type Condition params tables grouping =
277+ Expression params tables grouping ('Required ('NotNull 'PGBool))
278+
279+true :: Condition params tables grouping
276280true = UnsafeExpression "TRUE"
277281
278−false :: Condition params tables
282+false :: Condition params tables grouping
279283false = UnsafeExpression "FALSE"
280284
281−notB
282− :: Condition params tables
283− -> Condition params tables
284−notB = unsafeUnaryOp "NOT"
285+not_
286+ :: Condition params tables grouping
287+ -> Condition params tables grouping
288+not_ = unsafeUnaryOp "NOT"
285289
286290(&&*)
287− :: Condition params tables
288− -> Condition params tables
289− -> Condition params tables
291+ :: Condition params tables grouping
292+ -> Condition params tables grouping
293+ -> Condition params tables grouping
290294(&&*) = unsafeBinaryOp "AND"
291295
292296(||*)
293− :: Condition params tables
294− -> Condition params tables
295− -> Condition params tables
297+ :: Condition params tables grouping
298+ -> Condition params tables grouping
299+ -> Condition params tables grouping
296300(||*) = unsafeBinaryOp "OR"
297301
298302caseWhenThenElse
299− :: [(Condition params tables, Expression params tables ty)]
300− -> Expression params tables ty
301− -> Expression params tables ty
303+ :: [(Condition params tables grouping, Expression params tables grouping ty)]
304+ -> Expression params tables grouping ty
305+ -> Expression params tables grouping ty
302306caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
303307 [ "CASE"
304308 , mconcat
@@ -313,48 +317,86 @@ caseWhenThenElse whenThens else_ = UnsafeExpression $ mconcat
313317 ]
314318
315319ifThenElse
316− :: Condition params tables
317− -> Expression params tables ty
318− -> Expression params tables ty
319− -> Expression params tables ty
320+ :: Condition params tables grouping
321+ -> Expression params tables grouping ty
322+ -> Expression params tables grouping ty
323+ -> Expression params tables grouping ty
320324ifThenElse if_ then_ else_ = caseWhenThenElse [(if_,then_)] else_
321325
322326(==*)
323− :: Expression params tables ty
324− -> Expression params tables ty
325− -> Condition params tables
327+ :: Expression params tables grouping ('Required (nullity ty))
328+ -> Expression params tables grouping ('Required (nullity ty))
329+ -> Expression params tables grouping ('Required (nullity 'PGBool))
326330(==*) = unsafeBinaryOp "="
327331
328332(/=*)
329− :: Expression params tables ty
330− -> Expression params tables ty
331− -> Condition params tables
333+ :: Expression params tables grouping ('Required (nullity ty))
334+ -> Expression params tables grouping ('Required (nullity ty))
335+ -> Expression params tables grouping ('Required (nullity 'PGBool))
332336(/=*) = unsafeBinaryOp "<>"
333337
334338(>=*)
335− :: Expression params tables ty
336− -> Expression params tables ty
337− -> Condition params tables
339+ :: Expression params tables grouping ('Required (nullity ty))
340+ -> Expression params tables grouping ('Required (nullity ty))
341+ -> Expression params tables grouping ('Required (nullity 'PGBool))
338342(>=*) = unsafeBinaryOp ">="
339343
340344(<*)
341− :: Expression params tables ty
342− -> Expression params tables ty
343− -> Condition params tables
345+ :: Expression params tables grouping ('Required (nullity ty))
346+ -> Expression params tables grouping ('Required (nullity ty))
347+ -> Expression params tables grouping ('Required (nullity 'PGBool))
344348(<*) = unsafeBinaryOp "<"
345349
346350(<=*)
347− :: Expression params tables ty
348− -> Expression params tables ty
349− -> Condition params tables
351+ :: Expression params tables grouping ('Required (nullity ty))
352+ -> Expression params tables grouping ('Required (nullity ty))
353+ -> Expression params tables grouping ('Required (nullity 'PGBool))
350354(<=*) = unsafeBinaryOp "<="
351355
352356(>*)
353− :: Expression params tables ty
354− -> Expression params tables ty
355− -> Condition params tables
357+ :: Expression params tables grouping ('Required (nullity ty))
358+ -> Expression params tables grouping ('Required (nullity ty))
359+ -> Expression params tables grouping ('Required (nullity 'PGBool))
356360(>*) = unsafeBinaryOp ">"
357361
362+data SortExpression params tables grouping where
363+ Asc
364+ :: Expression params tables grouping ty
365+ -> SortExpression params tables grouping
366+ Desc
367+ :: Expression params tables grouping ty
368+ -> SortExpression params tables grouping
369+
370+renderSortExpression :: SortExpression params tables grouping -> ByteString
371+renderSortExpression = \case
372+ Asc expression -> renderExpression expression <> " ASC"
373+ Desc expression -> renderExpression expression <> " DESC"
374+
375+data Aggregate (ty0 :: ColumnType) (ty1 :: ColumnType) =
376+ UnsafeAggregate { renderAggregate :: ByteString }
377+
378+sumAggregate
379+ :: PGNum ty
380+ => Aggregate ('Required (nullity ty)) ('Required (nullity ty))
381+sumAggregate = UnsafeAggregate "sum"
382+
383+class (KnownSymbol table, KnownSymbol column)
384+ => GroupedBy table column distincts ty where
385+ getGroup
386+ :: (HasTable table tables columns, HasColumn column columns ty)
387+ => Alias table
388+ -> Alias column
389+ -> Expression params tables ('Grouped distincts) ty
390+
391+class (KnownSymbol table, KnownSymbol column)
392+ => Aggregated table column distincts ty where
393+ getAggregate
394+ :: (HasTable table tables columns, HasColumn column columns ty0)
395+ => Alias table
396+ -> Alias column
397+ -> Aggregate ty0 ty
398+ -> Expression params tables ('Grouped distincts) ty
399+
358400{-----------------------------------------
359401tables
360402-----------------------------------------}
@@ -392,22 +434,22 @@ data TableReference params schema tables where
392434 -> TableReference params schema (Join left right)
393435 InnerJoin
394436 :: TableReference params schema right
395− -> Condition params (Join left right)
437+ -> Condition params (Join left right) 'Ungrouped
396438 -> TableReference params schema left
397439 -> TableReference params schema (Join left right)
398440 LeftOuterJoin
399441 :: TableReference params schema right
400− -> Condition params (Join left right)
442+ -> Condition params (Join left right) 'Ungrouped
401443 -> TableReference params schema left
402444 -> TableReference params schema (Join left (NullifyTables right))
403445 RightOuterJoin
404446 :: TableReference params schema right
405− -> Condition params (Join left right)
447+ -> Condition params (Join left right) 'Ungrouped
406448 -> TableReference params schema left
407449 -> TableReference params schema (Join (NullifyTables left) right)
408450 FullOuterJoin
409451 :: TableReference params schema right
410− -> Condition params (Join left right)
452+ -> Condition params (Join left right) 'Ungrouped
411453 -> TableReference params schema left
412454 -> TableReference params schema
413455 (Join (NullifyTables left) (NullifyTables right))
@@ -450,61 +492,137 @@ renderTableReference = \case
450492 , renderExpression on
451493 ]
452494
495+data By tables tabcolty where
496+ By
497+ :: (HasTable table tables columns, HasColumn column columns ty)
498+ => Alias table
499+ -> Alias column
500+ -> By tables (table ::: (column ::: ty))
501+
502+renderBy :: By tables tabcolty -> ByteString
503+renderBy (By table column) = renderAlias table <> "." <> renderAlias column
504+
505+data GroupByClause tables grouping where
506+ NoGroups :: GroupByClause tables 'Ungrouped
507+ Distinct
508+ :: SListI distincts
509+ => NP (By tables) distincts
510+ -> GroupByClause tables ('Grouped distincts)
511+
512+renderGroupByClause :: GroupByClause tables grouping -> ByteString
513+renderGroupByClause = \case
514+ NoGroups -> ""
515+ Distinct Nil -> ""
516+ Distinct distincts -> " GROUP BY " <> ByteString.intercalate ", "
517+ (hcollapse (hmap (K . renderBy) distincts))
518+
519+data HavingClause params tables grouping where
520+ NoHaving :: HavingClause params tables 'Ungrouped
521+ Having
522+ :: [Condition params tables ('Grouped distincts)]
523+ -> HavingClause params tables ('Grouped distincts)
524+
525+renderHavingClause :: HavingClause params tables grouping -> ByteString
526+renderHavingClause = \case
527+ NoHaving -> ""
528+ Having [] -> ""
529+ Having conditions -> " HAVING " <> ByteString.intercalate ", "
530+ (renderExpression <$> conditions)
531+
453532data TableExpression
454533 (params :: [ColumnType])
455534 (schema :: [(Symbol,[(Symbol,ColumnType)])])
456535 (tables :: [(Symbol,[(Symbol,ColumnType)])])
536+ (grouping :: Grouping)
457537 = TableExpression
458538 { fromClause :: TableReference params schema tables
459− , whereClause :: [Condition params tables]
460− , limitClause :: Maybe Word64
461− , offsetClause :: Maybe Word64
539+ , whereClause :: [Condition params tables 'Ungrouped]
540+ , groupByClause :: GroupByClause tables grouping
541+ , havingClause :: HavingClause params tables grouping
542+ , orderByClause :: [SortExpression params tables grouping]
543+ , limitClause :: [Word64]
544+ , offsetClause :: [Word64]
462545 }
463546
464547renderTableExpression
465− :: TableExpression params schema tables
548+ :: TableExpression params schema tables grouping
466549 -> ByteString
467−renderTableExpression (TableExpression tables whs' lims' offs') = mconcat
468− [ "FROM ", renderTableReference tables
469− , renderWheres whs'
470− , renderLimits lims'
471− , renderOffsets offs'
472− ] where
473− renderWheres = \case
474− [] -> ""
475− wh:[] -> " WHERE " <> renderExpression wh
476− wh:whs -> " WHERE " <> renderExpression (foldr (&&*) wh whs)
477− renderLimits = \case
478− Nothing -> ""
479− Just lim -> " LIMIT " <> fromString (show lim)
480− renderOffsets = \case
481− Nothing -> ""
482− Just off -> " OFFSET " <> fromString (show off)
550+renderTableExpression
551+ (TableExpression tables whs' grps' hvs' srts' lims' offs') = mconcat
552+ [ "FROM ", renderTableReference tables
553+ , renderWheres whs'
554+ , renderGroupByClause grps'
555+ , renderHavingClause hvs'
556+ , renderSorts srts'
557+ , renderLimits lims'
558+ , renderOffsets offs'
559+ ]
560+ where
561+ renderWheres = \case
562+ [] -> ""
563+ wh:[] -> " WHERE " <> renderExpression wh
564+ wh:whs -> " WHERE " <> renderExpression (foldr (&&*) wh whs)
565+ renderSorts = \case
566+ [] -> ""
567+ srts -> " SORT BY "
568+ <> ByteString.intercalate ", " (renderSortExpression <$> srts)
569+ renderLimits = \case
570+ [] -> ""
571+ lims -> " LIMIT " <> fromString (show (minimum lims))
572+ renderOffsets = \case
573+ [] -> ""
574+ offs -> " OFFSET " <> fromString (show (sum offs))
483575
484576from
485577 :: TableReference params schema tables
486− -> TableExpression params schema tables
487−from tables = TableExpression tables [] Nothing Nothing
578+ -> TableExpression params schema tables 'Ungrouped
579+from tables = TableExpression tables [] NoGroups NoHaving [] [] []
488580
489581where_
490− :: Condition params tables
491− -> TableExpression params schema tables
492− -> TableExpression params schema tables
582+ :: Condition params tables 'Ungrouped
583+ -> TableExpression params schema tables grouping
584+ -> TableExpression params schema tables grouping
493585where_ wh tables = tables {whereClause = wh : whereClause tables}
494586
587+group
588+ :: SListI distincts
589+ => NP (By tables) distincts
590+ -> TableExpression params schema tables 'Ungrouped
591+ -> TableExpression params schema tables ('Grouped distincts)
592+group distincts tables = TableExpression
593+ { fromClause = fromClause tables
594+ , whereClause = whereClause tables
595+ , groupByClause = Distinct distincts
596+ , havingClause = Having []
597+ , orderByClause = []
598+ , limitClause = limitClause tables
599+ , offsetClause = offsetClause tables
600+ }
601+
602+having
603+ :: Condition params tables ('Grouped distincts)
604+ -> TableExpression params schema tables ('Grouped distincts)
605+ -> TableExpression params schema tables ('Grouped distincts)
606+having hv tables = tables
607+ { havingClause = case havingClause tables of Having hvs -> Having (hv:hvs) }
608+
609+orderBy
610+ :: [SortExpression params tables grouping]
611+ -> TableExpression params schema tables grouping
612+ -> TableExpression params schema tables grouping
613+orderBy srts tables = tables {orderByClause = orderByClause tables ++ srts}
614+
495615limit
496616 :: Word64
497− -> TableExpression params schema tables
498− -> TableExpression params schema tables
499−limit lim tables = tables
500− {limitClause = maybe (Just lim) (Just . min lim) (limitClause tables)}
617+ -> TableExpression params schema tables grouping
618+ -> TableExpression params schema tables grouping
619+limit lim tables = tables {limitClause = lim : limitClause tables}
501620
502621offset
503622 :: Word64
504− -> TableExpression params schema tables
505− -> TableExpression params schema tables
506−offset off tables = tables
507− {offsetClause = maybe (Just off) (Just . (off +)) (offsetClause tables)}
623+ -> TableExpression params schema tables grouping
624+ -> TableExpression params schema tables grouping
625+offset off tables = tables {offsetClause = off : offsetClause tables}
508626
509627{-----------------------------------------
510628type expressions
@@ -606,7 +724,7 @@ notNull
606724 -> TypeExpression ('Required ('NotNull ty))
607725notNull ty = UnsafeTypeExpression $ renderTypeExpression ty <> " NOT NULL"
608726default_
609− :: Expression '[] '[] ('Required ty)
727+ :: Expression '[] '[] 'Ungrouped ('Required ty)
610728 -> TypeExpression ('Required ty)
611729 -> TypeExpression ('Optional ty)
612730default_ x ty = UnsafeTypeExpression $
@@ -690,8 +808,8 @@ SELECT statements
690808
691809select
692810 :: SListI columns
693− => NP (Aliased (Expression params tables)) columns
694− -> TableExpression params schema tables
811+ => NP (Aliased (Expression params tables grouping)) columns
812+ -> TableExpression params schema tables grouping
695813 -> Query schema params columns
696814select list tabs = UnsafeQuery $
697815 "SELECT " <> renderList list <> " " <> renderTableExpression tabs
@@ -703,14 +821,14 @@ select list tabs = UnsafeQuery $
703821
704822selectStar
705823 :: (tables ~ '[table ::: columns])
706− => TableExpression params schema tables
824+ => TableExpression params schema tables 'Ungrouped
707825 -> Query schema params columns
708826selectStar tabs = UnsafeQuery $ "SELECT * " <> renderTableExpression tabs
709827
710828selectDotStar
711829 :: HasTable table tables columns
712830 => Alias table
713− -> TableExpression params schema tables
831+ -> TableExpression params schema tables 'Ungrouped
714832 -> Query schema params columns
715833selectDotStar table tables = UnsafeQuery $
716834 "SELECT " <> renderAlias table <> ".* " <> renderTableExpression tables
@@ -831,7 +949,7 @@ data AlterColumn (ty0 :: ColumnType) (ty1 :: ColumnType) =
831949 UnsafeAlterColumn {renderAlterColumn :: ByteString}
832950
833951setDefault
834− :: Expression '[] '[] ('Required ty)
952+ :: Expression '[] '[] 'Ungrouped ('Required ty)
835953 -> AlterColumn ('Required ty) ('Optional ty)
836954setDefault expression = UnsafeAlterColumn $
837955 "SET DEFAULT " <> renderExpression expression
@@ -860,7 +978,7 @@ INSERT statements
860978insertInto
861979 :: (SListI columns, HasTable table schema columns)
862980 => Alias table
863− -> NP (Aliased (Expression params '[])) columns
981+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
864982 -> Manipulation schema params '[]
865983insertInto table expressions = UnsafeManipulation $ "INSERT INTO "
866984 <> renderAlias table
@@ -879,8 +997,8 @@ insertInto table expressions = UnsafeManipulation $ "INSERT INTO "
879997insertIntoReturning
880998 :: (SListI columns, SListI results, HasTable table schema columns)
881999 => Alias table
882− -> NP (Aliased (Expression params '[])) columns
883− -> NP (Aliased (Expression params '[table ::: columns])) results
1000+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
1001+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
8841002 -> Manipulation schema params results
8851003insertIntoReturning table inserts results
8861004 = UnsafeManipulation . mconcat $
@@ -911,7 +1029,7 @@ UPDATE statements
9111029
9121030data UpdateExpression params table ty
9131031 = Same
914− | Set (Expression params table ty)
1032+ | Set (Expression params table 'Ungrouped ty)
9151033
9161034renderUpdateExpression
9171035 :: Aliased (UpdateExpression params '[table ::: columns]) column
@@ -928,7 +1046,7 @@ update
9281046 :: (HasTable table schema columns, SListI columns)
9291047 => Alias table
9301048 -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
931− -> Condition params '[table ::: columns]
1049+ -> Condition params '[table ::: columns] 'Ungrouped
9321050 -> Manipulation schema params '[]
9331051update table columns wh = UnsafeManipulation $ mconcat
9341052 [ "UPDATE "
@@ -943,8 +1061,8 @@ updateReturning
9431061 :: (SListI columns, SListI results, HasTable table schema columns)
9441062 => Alias table
9451063 -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
946− -> Condition params '[table ::: columns]
947− -> NP (Aliased (Expression params '[table ::: columns])) results
1064+ -> Condition params '[table ::: columns] 'Ungrouped
1065+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
9481066 -> Manipulation schema params results
9491067updateReturning table updates wh results = UnsafeManipulation $ mconcat
9501068 [ "UPDATE "
@@ -965,9 +1083,9 @@ updateReturning table updates wh results = UnsafeManipulation $ mconcat
9651083upsertInto
9661084 :: (SListI columns, HasTable table schema columns)
9671085 => Alias table
968− -> NP (Aliased (Expression params '[])) columns
1086+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
9691087 -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
970− -> Condition params '[table ::: columns]
1088+ -> Condition params '[table ::: columns] 'Ungrouped
9711089 -> Manipulation schema params '[]
9721090upsertInto table inserts updates wh = UnsafeManipulation . mconcat $
9731091 [ "INSERT INTO "
@@ -992,10 +1110,10 @@ upsertInto table inserts updates wh = UnsafeManipulation . mconcat $
9921110upsertIntoReturning
9931111 :: (SListI columns, SListI results, HasTable table schema columns)
9941112 => Alias table
995− -> NP (Aliased (Expression params '[])) columns
1113+ -> NP (Aliased (Expression params '[] 'Ungrouped)) columns
9961114 -> NP (Aliased (UpdateExpression params '[table ::: columns])) columns
997− -> Condition params '[table ::: columns]
998− -> NP (Aliased (Expression params '[table ::: columns])) results
1115+ -> Condition params '[table ::: columns] 'Ungrouped
1116+ -> NP (Aliased (Expression params '[table ::: columns] 'Ungrouped)) results
9991117 -> Manipulation schema params results
10001118upsertIntoReturning table inserts updates wh results =
10011119 UnsafeManipulation . mconcat $
@@ -1031,7 +1149,7 @@ DELETE statements
10311149deleteFrom
10321150 :: HasTable table schema columns
10331151 => Alias table
1034− -> Condition params '[table ::: columns]
1152+ -> Condition params '[table ::: columns] 'Ungrouped
10351153 -> Manipulation schema params '[]
10361154deleteFrom table wh = UnsafeManipulation $ mconcat
10371155 [ "DELETE FROM "