Add Aggregation type distinguished from Projection type.

Add projection type (Aggregation) for aggregation
which is distinguished from not aggregated projection type (Projection).
This commit is contained in:
Kei Hibino 2013-05-27 18:07:40 +09:00
parent 5daf8a5f11
commit 2769a6cd3f
3 changed files with 147 additions and 7 deletions

View File

@ -0,0 +1,52 @@
{-# LANGUAGE FlexibleContexts #-}
module Database.Relational.Query.Aggregation (
Aggregation, projection,
liftAggregation,
compose,
just, flattenMaybe,
pi, piMaybe,
unsafeFromProjection
) where
import Prelude hiding (pi)
import Database.Record (PersistableWidth)
import Database.Relational.Query.Projection (Projection)
import qualified Database.Relational.Query.Projection as Projection
import Database.Relational.Query.Pi (Pi)
newtype Aggregation r = Aggregation (Projection r)
projection :: Aggregation r -> Projection r
projection (Aggregation p) = p
liftAggregation :: (Projection a -> Projection b) -> Aggregation a -> Aggregation b
liftAggregation f = Aggregation . f . projection
unsafeFromProjection :: Projection r -> Aggregation r
unsafeFromProjection = Aggregation
compose :: Aggregation a -> Aggregation b -> Aggregation (c a b)
compose (Aggregation a) (Aggregation b) = Aggregation $ a `Projection.compose` b
just :: Aggregation a -> Aggregation (Maybe a)
just = liftAggregation Projection.just
flattenMaybe :: Aggregation (Maybe (Maybe a)) -> Aggregation (Maybe a)
flattenMaybe = liftAggregation Projection.flattenMaybe
definePi :: (Projection a -> Pi a' b' -> Projection b) -> Aggregation a -> Pi a' b' -> Aggregation b
definePi (!!!) p pi' = liftAggregation (!!! pi') p
pi :: PersistableWidth b => Aggregation a -> Pi a b -> Aggregation b
pi = definePi Projection.pi
piMaybe :: PersistableWidth b => Aggregation (Maybe a) -> Pi a b -> Aggregation (Maybe b)
piMaybe = definePi Projection.piMaybe

View File

@ -1,6 +1,8 @@
module Database.Relational.Query.Projectable ( module Database.Relational.Query.Projectable (
Projectable (project), Projectable (project),
projectAggregation,
value, value,
valueTrue, valueFalse, valueTrue, valueFalse,
@ -10,6 +12,9 @@ module Database.Relational.Query.Projectable (
SqlProjectable (unsafeProjectSql), SqlProjectable (unsafeProjectSql),
valueNull, placeholder, valueNull, placeholder,
unsafeAggregateOp,
count, sum', avg, max', min', every, any', some',
ProjectableShowSql (unsafeShowSql), ProjectableShowSql (unsafeShowSql),
unsafeBinOp, unsafeBinOp,
@ -25,6 +30,7 @@ module Database.Relational.Query.Projectable (
import Prelude hiding (and, or) import Prelude hiding (and, or)
import Data.Int (Int32)
import Data.List (intercalate) import Data.List (intercalate)
import qualified Language.SQL.Keyword as SQL import qualified Language.SQL.Keyword as SQL
@ -37,6 +43,9 @@ import qualified Database.Relational.Query.Expr.Unsafe as UnsafeExpr
import Database.Relational.Query.Projection (Projection, columns, unsafeFromColumns) import Database.Relational.Query.Projection (Projection, columns, unsafeFromColumns)
import qualified Database.Relational.Query.Projection as Projection import qualified Database.Relational.Query.Projection as Projection
import Database.Relational.Query.Aggregation (Aggregation)
import qualified Database.Relational.Query.Aggregation as Aggregation
sqlString :: Projection r -> String sqlString :: Projection r -> String
sqlString = d . columns where sqlString = d . columns where
@ -56,10 +65,13 @@ instance Projectable Projection where
instance Projectable Expr where instance Projectable Expr where
project = toExpr project = toExpr
projectAggregation :: Projectable p => Aggregation a -> p a
projectAggregation = project . Aggregation.projection
unsafeSqlProjection :: String -> Projection t unsafeSqlProjection :: String -> Projection t
unsafeSqlProjection = unsafeFromColumns . (:[]) unsafeSqlProjection = unsafeFromColumns . (:[])
class SqlProjectable p where class SqlProjectable p where
unsafeProjectSql :: String -> p t unsafeProjectSql :: String -> p t
@ -69,6 +81,9 @@ instance SqlProjectable Projection where
instance SqlProjectable Expr where instance SqlProjectable Expr where
unsafeProjectSql = UnsafeExpr.Expr unsafeProjectSql = UnsafeExpr.Expr
instance SqlProjectable Aggregation where
unsafeProjectSql = Aggregation.unsafeFromProjection . unsafeProjectSql
valueNull :: SqlProjectable p => p (Maybe a) valueNull :: SqlProjectable p => p (Maybe a)
valueNull = unsafeProjectSql "NULL" valueNull = unsafeProjectSql "NULL"
@ -97,6 +112,43 @@ instance ProjectableShowSql Projection where
instance ProjectableShowSql Expr where instance ProjectableShowSql Expr where
unsafeShowSql = UnsafeExpr.showExpr unsafeShowSql = UnsafeExpr.showExpr
instance ProjectableShowSql Aggregation where
unsafeShowSql = unsafeShowSql . Aggregation.projection
paren :: String -> String
paren = ('(' :) . (++[')'])
type SqlUniOp = String -> String
sqlUniOp :: SQL.Keyword -> SqlUniOp
sqlUniOp kw = (SQL.wordShow kw ++) . (' ' :) . paren
unsafeAggregateOp :: SQL.Keyword
-> Projection a -> Aggregation b
unsafeAggregateOp op = unsafeProjectSql . sqlUniOp op . unsafeShowSql
count :: Projection a -> Aggregation Int32
count = unsafeAggregateOp SQL.COUNT
sum' :: Num a => Projection a -> Aggregation a
sum' = unsafeAggregateOp SQL.SUM
avg :: (Num a, Fractional b)=> Projection a -> Aggregation b
avg = unsafeAggregateOp SQL.AVG
max' :: Ord a => Projection a -> Aggregation a
max' = unsafeAggregateOp SQL.MAX
min' :: Ord a => Projection a -> Aggregation a
min' = unsafeAggregateOp SQL.MIN
every = unsafeAggregateOp SQL.EVERY
any' = unsafeAggregateOp SQL.ANY
some' = unsafeAggregateOp SQL.SOME
every, any', some' :: Projection Bool -> Aggregation Bool
type SqlBinOp = String -> String -> String type SqlBinOp = String -> String -> String
@ -108,7 +160,6 @@ unsafeBinOp :: (SqlProjectable p, ProjectableShowSql p)
-> p a -> p b -> p c -> p a -> p b -> p c
unsafeBinOp op a b = unsafeProjectSql . paren unsafeBinOp op a b = unsafeProjectSql . paren
$ op (unsafeShowSql a) (unsafeShowSql b) $ op (unsafeShowSql a) (unsafeShowSql b)
where paren = ('(' :) . (++[')'])
compareBinOp :: (SqlProjectable p, ProjectableShowSql p) compareBinOp :: (SqlProjectable p, ProjectableShowSql p)
=> SqlBinOp => SqlBinOp
@ -177,6 +228,9 @@ instance ProjectableZip PlaceHolders where
instance ProjectableZip Projection where instance ProjectableZip Projection where
projectZip = Projection.compose projectZip = Projection.compose
instance ProjectableZip Aggregation where
projectZip = Aggregation.compose
(><) ::ProjectableZip p => p a -> p b -> p (a, b) (><) ::ProjectableZip p => p a -> p b -> p (a, b)
(><) = projectZip (><) = projectZip
@ -196,6 +250,10 @@ instance ProjectableMaybe Expr where
just = Expr.just just = Expr.just
flattenMaybe = Expr.flattenMaybe flattenMaybe = Expr.flattenMaybe
instance ProjectableMaybe Aggregation where
just = Aggregation.just
flattenMaybe = Aggregation.flattenMaybe
infixl 7 .*., ./. infixl 7 .*., ./.
infixl 6 .+., .-. infixl 6 .+., .-.

View File

@ -5,10 +5,13 @@
module Database.Relational.Query.ProjectableExtended ( module Database.Relational.Query.ProjectableExtended (
ProjectableFlattenMaybe (flatten), ProjectableFlattenMaybe (flatten),
piMaybeFlatten, piMaybe',
(!), (!?), (!??), (!), (!?), (!??),
piMaybeAggregation',
(<!>), (<!?>), (<!??>),
ProjectableGeneralizedZip (generalizedZip), (>?<) ProjectableGeneralizedZip (generalizedZip), (>?<)
) where ) where
@ -16,8 +19,11 @@ import Database.Record (PersistableWidth)
import Database.Relational.Query.Projection (Projection) import Database.Relational.Query.Projection (Projection)
import qualified Database.Relational.Query.Projection as Projection import qualified Database.Relational.Query.Projection as Projection
import Database.Relational.Query.Aggregation (Aggregation)
import qualified Database.Relational.Query.Aggregation as Aggregation
import Database.Relational.Query.Projectable import Database.Relational.Query.Projectable
(Projectable(project), ProjectableMaybe (flattenMaybe), ProjectableZip(projectZip)) (Projectable(project), projectAggregation,
ProjectableMaybe (flattenMaybe), ProjectableZip(projectZip))
import Database.Relational.Query.Pi (Pi) import Database.Relational.Query.Pi (Pi)
class ProjectableFlattenMaybe a b where class ProjectableFlattenMaybe a b where
@ -38,14 +44,38 @@ p ! pi' = project $ Projection.pi p pi'
=> Projection (Maybe a) -> Pi a b -> p (Maybe b) => Projection (Maybe a) -> Pi a b -> p (Maybe b)
p !? pi' = project $ Projection.piMaybe p pi' p !? pi' = project $ Projection.piMaybe p pi'
piMaybeFlatten :: (PersistableWidth b, ProjectableFlattenMaybe c (Maybe a)) piMaybe' :: (PersistableWidth b, ProjectableFlattenMaybe c (Maybe a))
=> Projection c -> Pi a b -> Projection (Maybe b) => Projection c -> Pi a b -> Projection (Maybe b)
piMaybeFlatten = Projection.piMaybe . flatten piMaybe' = Projection.piMaybe . flatten
(!??) :: (PersistableWidth b, ProjectableFlattenMaybe c (Maybe a), (!??) :: (PersistableWidth b, ProjectableFlattenMaybe c (Maybe a),
Projectable p, ProjectableMaybe p) Projectable p, ProjectableMaybe p)
=> Projection c -> Pi a b -> p (Maybe b) => Projection c -> Pi a b -> p (Maybe b)
p !?? pi' = project $ piMaybeFlatten p pi' p !?? pi' = project $ piMaybe' p pi'
piMaybeAggregation' :: (PersistableWidth b, ProjectableFlattenMaybe c (Maybe a))
=> Aggregation c -> Pi a b -> Aggregation (Maybe b)
piMaybeAggregation' = Aggregation.piMaybe . flatten
(<!>) :: (PersistableWidth b, Projectable p)
=> Aggregation a
-> Pi a b
-> p b
(<!>) a = projectAggregation . Aggregation.pi a
(<!?>) :: (PersistableWidth b, Projectable p)
=> Aggregation (Maybe a)
-> Pi a b
-> p (Maybe b)
(<!?>) a = projectAggregation . Aggregation.piMaybe a
(<!??>) :: (PersistableWidth b, Projectable p, ProjectableFlattenMaybe c (Maybe a))
=> Aggregation c
-> Pi a b
-> p (Maybe b)
(<!??>) a = projectAggregation . piMaybeAggregation' a
class ProjectableGeneralizedZip a b c where class ProjectableGeneralizedZip a b c where
generalizedZip :: ProjectableZip p => p a -> p b -> p c generalizedZip :: ProjectableZip p => p a -> p b -> p c