on master help sign in
Use Opaleye adaptors (#190)

* Add htraverseP * Add traverseFieldP * Define and use fromOpaleyespec * Use traverseFieldP * Add htraversePWithField and use it in valuesspec * Use aggregatorApply * Update HTable.hs Co-authored-by: Ollie Charles <ollie@ocharles.org.uk> Co-authored-by: Shane <shane.obrien@circuithub.com>

commit6791677f58737b9b97891b487c1a1b44f1dff208 authortomjaguarpaw <tom-git@jaguarpaw.co.uk> dateTue 16 Aug 2022, 17:34 (4 years ago) parent961e1ba
4 files changed+59−46
rel8.cabal+1−1 src/Rel8/Expr/Opaleye.hs+10−1 src/Rel8/Schema/HTable.hs+23−2 src/Rel8/Table/Opaleye.hs+25−42
rel8.cabal+1−1
@@ -27,7 +27,7 @@ library
2727 , comonad
2828 , contravariant
2929 , hasql ^>= 1.4.5.1 || ^>= 1.5.0.0
30− , opaleye >= 0.9.1.0 && <= 0.9.3.2
30+ , opaleye ^>= 0.9.3.3
3131 , pretty
3232 , profunctors
3333 , product-profunctors
src/Rel8/Expr/Opaleye.hs+10−1
@@ -10,7 +10,7 @@ module Rel8.Expr.Opaleye
1010 , scastExpr, sunsafeCastExpr
1111 , unsafeLiteral
1212 , fromPrimExpr, toPrimExpr, mapPrimExpr, zipPrimExprsWith, traversePrimExpr
13− , toColumn, fromColumn
13+ , toColumn, fromColumn, traverseFieldP
1414 )
1515where
1616
@@ -27,6 +27,9 @@ import Rel8.Schema.Null ( Unnullify, Sql )
2727import Rel8.Type ( DBType, typeInformation )
2828import Rel8.Type.Information ( TypeInformation(..) )
2929
30+-- profunctors
31+import Data.Profunctor ( Profunctor, dimap )
32+
3033
3134castExpr :: Sql DBType a => Expr a -> Expr a
3235castExpr = scastExpr typeInformation
@@ -80,6 +83,12 @@ traversePrimExpr :: Functor f
8083traversePrimExpr f = fmap fromPrimExpr . f . toPrimExpr
8184
8285
86+traverseFieldP :: Profunctor p
87+ => p (Opaleye.Field_ n x) (Opaleye.Field_ m y)
88+ -> p (Expr a) (Expr b)
89+traverseFieldP = dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn)
90+
91+
8392toColumn :: Opaleye.PrimExpr -> Opaleye.Field_ n b
8493toColumn = Opaleye.Column
8594
src/Rel8/Schema/HTable.hs+23−2
@@ -16,7 +16,7 @@
1616module Rel8.Schema.HTable
1717 ( HTable (HField, HConstrainTable)
1818 , hfield, htabulate, htraverse, hdicts, hspecs
19− , hmap, htabulateA
19+ , hmap, htabulateA, htraverseP, htraversePWithField
2020 )
2121where
2222
@@ -32,6 +32,12 @@ import GHC.Generics
3232 )
3333import Prelude
3434
35+-- profunctors
36+import Data.Profunctor ( rmap, Profunctor (lmap) )
37+
38+-- product-profunctors
39+import Data.Profunctor.Product ( ProductProfunctor ((****)) )
40+
3541-- rel8
3642import Rel8.Schema.Dict ( Dict )
3743import Rel8.Schema.Spec ( Spec )
@@ -41,7 +47,6 @@ import qualified Rel8.Schema.Kind as K
4147-- semigroupoids
4248import Data.Functor.Apply ( Apply, (<.>) )
4349
44−
4550-- | A @HTable@ is a functor-indexed/higher-kinded data type that is
4651-- representable ('htabulate'/'hfield'), constrainable ('hdicts'), and
4752-- specified ('hspecs').
@@ -124,6 +129,22 @@ htabulateA :: (HTable t, Apply m)
124129htabulateA f = htraverse getCompose $ htabulate $ Compose . f
125130{-# INLINABLE htabulateA #-}
126131
132+newtype ApplyP p a b = ApplyP { unApplyP :: p a b }
133+
134+instance Profunctor p => Functor (ApplyP p a) where
135+ fmap f = ApplyP . rmap f . unApplyP
136+
137+instance ProductProfunctor p => Apply (ApplyP p a) where
138+ ApplyP f <.> ApplyP x = ApplyP (rmap id f **** x)
139+
140+htraverseP :: (HTable t, ProductProfunctor p)
141+ => (forall a. p (f a) (g a)) -> p (t f) (t g)
142+htraverseP f = htraversePWithField (const f)
143+
144+htraversePWithField :: (HTable t, ProductProfunctor p)
145+ => (forall a. HField t a -> p (f a) (g a)) -> p (t f) (t g)
146+htraversePWithField f = unApplyP $ htabulateA $ \field -> ApplyP $
147+ lmap (flip hfield field) (f field)
127148
128149type GHField :: K.HTable -> Type -> Type
129150newtype GHField t a = GHField (HField (GHColumns (Rep (t Proxy))) a)
src/Rel8/Table/Opaleye.hs+25−42
@@ -28,15 +28,14 @@ where
2828-- base
2929import Data.Functor.Const ( Const( Const ), getConst )
3030import Data.List.NonEmpty ( NonEmpty )
31−import Prelude hiding ( undefined )
31+import Prelude
3232
3333-- opaleye
34+import qualified Opaleye.Adaptors as Opaleye
35+import qualified Opaleye.Field as Opaleye ( Field_ )
3436import qualified Opaleye.Internal.Aggregate as Opaleye
35−import qualified Opaleye.Internal.Binary as Opaleye
36−import qualified Opaleye.Internal.Distinct as Opaleye
3737import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
3838import qualified Opaleye.Internal.PackMap as Opaleye
39−import qualified Opaleye.Internal.Unpackspec as Opaleye
4039import qualified Opaleye.Internal.Values as Opaleye
4140import qualified Opaleye.Table as Opaleye
4241
@@ -48,27 +47,25 @@ import Rel8.Aggregate ( Aggregate( Aggregate ), Aggregates )
4847import Rel8.Expr ( Expr )
4948import Rel8.Expr.Opaleye
5049 ( fromPrimExpr, toPrimExpr
51− , traversePrimExpr
52− , fromColumn, toColumn
53− , scastExpr
50+ , scastExpr, traverseFieldP
5451 )
55−import Rel8.Schema.HTable ( htabulateA, hfield, htraverse, hspecs, htabulate )
52+import Rel8.Schema.HTable ( htabulateA, hfield, hspecs, htabulate,
53+ htraverseP, htraversePWithField )
5654import Rel8.Schema.Name ( Name( Name ), Selects, ppColumn )
5755import Rel8.Schema.Spec ( Spec(..) )
5856import Rel8.Schema.Table ( TableSchema(..), ppTable )
5957import Rel8.Table ( Table, fromColumns, toColumns )
60−import Rel8.Table.Undefined ( undefined )
58+import Rel8.Type.Information ( typeName )
6159
6260-- semigroupoids
6361import Data.Functor.Apply ( WrappedApplicative(..) )
62+import Data.Profunctor.Product ( ProductProfunctor )
6463
6564
6665aggregator :: Aggregates aggregates exprs => Opaleye.Aggregator aggregates exprs
67−aggregator = Opaleye.Aggregator $ Opaleye.PackMap $ \f aggregates ->
68− fmap fromColumns $ unwrapApplicative $ htabulateA $ \field ->
69− WrapApplicative $ case hfield (toColumns aggregates) field of
70− Aggregate (Opaleye.Aggregator (Opaleye.PackMap inner)) ->
71− inner f ()
66+aggregator = dimap toColumns fromColumns $
67+ htraverseP $
68+ lmap (\(Aggregate a) -> (a, ())) Opaleye.aggregatorApply
7269
7370
7471attributes :: Selects names exprs => TableSchema names -> exprs
@@ -79,22 +76,19 @@ attributes schema@TableSchema {columns} = fromColumns $ htabulate $ \field ->
7976 show (ppTable schema) <> "." <> show (ppColumn column)
8077
8178
79+fromOpaleyespec :: (ProductProfunctor p, Table Expr a)
80+ => p (Opaleye.Field_ n x) (Opaleye.Field_ n x)
81+ -> p a a
82+fromOpaleyespec x =
83+ dimap toColumns fromColumns (htraverseP (traverseFieldP x))
84+
85+
8286binaryspec :: Table Expr a => Opaleye.Binaryspec a a
83−binaryspec = Opaleye.Binaryspec $ Opaleye.PackMap $ \f (as, bs) ->
84− fmap fromColumns $ unwrapApplicative $ htabulateA $ \field ->
85− WrapApplicative $
86− case (hfield (toColumns as) field, hfield (toColumns bs) field) of
87− (a, b) -> fromPrimExpr <$> f (toPrimExpr a, toPrimExpr b)
87+binaryspec = fromOpaleyespec Opaleye.binaryspecField
8888
8989
9090distinctspec :: Table Expr a => Opaleye.Distinctspec a a
91−distinctspec =
92− Opaleye.Distinctspec $ Opaleye.Aggregator $ Opaleye.PackMap $ \f ->
93− fmap fromColumns .
94− unwrapApplicative .
95− htraverse
96− (\a -> WrapApplicative $ fromPrimExpr <$> f (Nothing, toPrimExpr a)) .
97− toColumns
91+distinctspec = fromOpaleyespec Opaleye.distinctspecField
9892
9993
10094exprs :: Table Expr a => a -> NonEmpty Opaleye.PrimExpr
@@ -126,21 +120,19 @@ tableFields (toColumns -> names) = dimap toColumns fromColumns $
126120 where
127121 go :: Name a -> Opaleye.TableFields (Expr a) (Expr a)
128122 go (Name name) =
129− dimap (toColumn . toPrimExpr) (fromPrimExpr . fromColumn) $
123+ traverseFieldP $
130124 Opaleye.requiredTableField name
131125
132126
133127unpackspec :: Table Expr a => Opaleye.Unpackspec a a
134−unpackspec = Opaleye.Unpackspec $ Opaleye.PackMap $ \f ->
135− fmap fromColumns .
136− unwrapApplicative .
137− htraverse (WrapApplicative . traversePrimExpr f) .
138− toColumns
128+unpackspec = fromOpaleyespec Opaleye.unpackspecField
139129{-# INLINABLE unpackspec #-}
140130
141131
142132valuesspec :: Table Expr a => Opaleye.Valuesspec a a
143−valuesspec = Opaleye.ValuesspecSafe (toPackMap undefined) unpackspec
133+valuesspec = dimap toColumns fromColumns $
134+ htraversePWithField (traverseFieldP . Opaleye.valuesspecFieldType . typeName)
135+ where typeName = Rel8.Type.Information.typeName . info . hfield hspecs
144136
145137
146138view :: Selects names exprs => names -> exprs
@@ -149,15 +141,6 @@ view columns = fromColumns $ htabulate $ \field ->
149141 Name column -> fromPrimExpr $ Opaleye.BaseTableAttrExpr column
150142
151143
152−toPackMap :: Table Expr a
153− => a -> Opaleye.PackMap Opaleye.PrimExpr Opaleye.PrimExpr () a
154−toPackMap as = Opaleye.PackMap $ \f () ->
155− fmap fromColumns $
156− unwrapApplicative .
157− htraverse (WrapApplicative . traversePrimExpr f) $
158− toColumns as
159−
160−
161144-- | Transform a table by adding 'CAST' to all columns. This is most useful for
162145-- finalising a SELECT or RETURNING statement, guaranteed that the output
163146-- matches what is encoded in each columns TypeInformation.