packages feed

rel8 1.2.2.0 → 1.3.0.0

raw patch · 23 files changed

+174/−35 lines, 23 filesdep ~hedgehogdep ~opaleyePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: hedgehog, opaleye

API changes (from Hackage documentation)

+ Rel8: avg :: Sql DBSum a => Expr a -> Aggregate a
+ Rel8: class IsMaybe a ~ IsMaybe b => Homonullable a b
+ Rel8: eqTable :: (EqTable a, GTable TEqTable TColumns (Rep (Record a)), Columns a ~ GColumns TColumns (Rep (Record a))) => Columns a (Dict (Sql DBEq))
+ Rel8: ilike :: Expr Text -> Expr Text -> Expr Bool
+ Rel8: like :: Expr Text -> Expr Text -> Expr Bool
+ Rel8: mode :: forall a. EqTable a => Query a -> Query a
+ Rel8: ordTable :: (OrdTable a, GTable TOrdTable TColumns (Rep (Record a)), Columns a ~ GColumns TColumns (Rep (Record a))) => Columns a (Dict (Sql DBOrd))
+ Rel8.Expr.Num: divMod :: Sql DBIntegral a => Expr a -> Expr a -> (Expr a, Expr a)
+ Rel8.Expr.Num: quot :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
+ Rel8.Expr.Num: quotRem :: Sql DBIntegral a => Expr a -> Expr a -> (Expr a, Expr a)
+ Rel8.Expr.Num: rem :: Sql DBIntegral a => Expr a -> Expr a -> Expr a
+ Rel8.Expr.Text: ilike :: Expr Text -> Expr Text -> Expr Bool
+ Rel8.Expr.Text: like :: Expr Text -> Expr Text -> Expr Bool
- Rel8: class DBNum a => DBIntegral a
+ Rel8: class (DBNum a, DBOrd a) => DBIntegral a

Files

Changelog.md view
@@ -1,3 +1,22 @@+# 1.3.0.0 (2022-01-31)++## Breaking changes++* `div` and `mod` have been changed to match Haskell semantics. If you need the PostgreSQL `div()` and `mod()` functions, use `quot` and `rem`. While this is not an API change, we feel this is a breaking change in semantics and have bumped the major version number. ([#155](https://github.com/circuithub/rel8/pull/155))++## New features++* `divMod` and `quotRem` functions have been added, matching Haskell's `Prelude` functions. ([#155](https://github.com/circuithub/rel8/pull/155))+* `avg` and `mode` aggregation functions to find the mean value of an expression, or the most common row in a query, respectively. ([#152](https://github.com/circuithub/rel8/pull/152))+* The full `EqTable` and `OrdTable` classes have been exported, allowing for instances to be manually created. ([#157](https://github.com/circuithub/rel8/pull/157))+* Added `like` and `ilike` (for the `LIKE` and `ILIKE` operators). ([#146](https://github.com/circuithub/rel8/pull/146))++## Other++* Rel8 now requires Opaleye 0.9. ([#158](https://github.com/circuithub/rel8/pull/158))+* Rel8's test suite supports Hedgehog 1.1. ([#160](https://github.com/circuithub/rel8/pull/160))+* The documentation for binary operations has been corrected. ([#162](https://github.com/circuithub/rel8/pull/162))+ # 1.2.2.0 (2021-11-21)  ## Other
rel8.cabal view
@@ -1,6 +1,6 @@ cabal-version:       2.0 name:                rel8-version:             1.2.2.0+version:             1.3.0.0 synopsis:            Hey! Hey! Can u rel8? license:             BSD3 license-file:        LICENSE@@ -27,7 +27,7 @@     , comonad     , contravariant     , hasql ^>= 1.4.5.1 || ^>= 1.5.0.0-    , opaleye ^>= 0.8.0.0+    , opaleye ^>= 0.9.0.0     , pretty     , profunctors     , product-profunctors@@ -46,6 +46,7 @@     -Wno-missing-import-lists -Wno-prepositive-qualified-module     -Wno-monomorphism-restriction     -Wno-missing-local-signatures+    -Wno-missing-kind-signatures   hs-source-dirs:     src   exposed-modules:@@ -203,7 +204,7 @@     , containers     , hasql     , hasql-transaction-    , hedgehog          ^>=1.0.2+    , hedgehog          ^>= 1.0 || ^>= 1.1     , mmorph     , rel8     , scientific
src/Rel8.hs view
@@ -46,8 +46,8 @@   , Transposes   , AltTable((<|>:))   , AlternativeTable( emptyTable )-  , EqTable, (==:), (/=:)-  , OrdTable, (<:), (<=:), (>:), (>=:), ascTable, descTable, greatest, least+  , EqTable(..), (==:), (/=:)+  , OrdTable(..), (<:), (<=:), (>:), (>=:), ascTable, descTable, greatest, least   , lit   , bool   , case_@@ -139,6 +139,7 @@     -- ** @null@   , NotNull   , Nullable+  , Homonullable   , null   , nullify   , nullable@@ -157,6 +158,7 @@   , (==.), (/=.), (==?), (/=?)   , in_   , boolExpr, caseExpr+  , like, ilike      -- ** Ordering   , DBOrd@@ -225,10 +227,11 @@   , countRows   , groupBy   , listAgg, listAggExpr+  , mode   , nonEmptyAgg, nonEmptyAggExpr   , DBMax, max   , DBMin, min-  , DBSum, sum, sumWhere+  , DBSum, sum, sumWhere, avg   , DBString, stringAgg   , count   , countStar@@ -316,6 +319,7 @@ import Rel8.Expr.Order import Rel8.Expr.Serialize import Rel8.Expr.Sequence+import Rel8.Expr.Text ( like, ilike ) import Rel8.Generic.Rel8able ( KRel8able, Rel8able ) import Rel8.Order import Rel8.Query
src/Rel8/Expr/Aggregate.hs view
@@ -10,6 +10,7 @@   , and, or   , min, max   , sum, sumWhere+  , avg   , stringAgg   , groupByExpr   , listAggExpr, nonEmptyAggExpr@@ -120,13 +121,27 @@  -- | Corresponds to @sum@. Note that in SQL, @sum@ is type changing - for -- example the @sum@ of @integer@ returns a @bigint@. Rel8 doesn't support--- this, and will add explicit cast back to the original input type. This can+-- this, and will add explicit casts back to the original input type. This can -- lead to overflows, and if you anticipate very large sums, you should upcast -- your input. sum :: Sql DBSum a => Expr a -> Aggregate a sum = unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $   Just Aggregator     { operation = Opaleye.AggrSum+    , ordering = []+    , distinction = Opaleye.AggrAll+    }+++-- | Corresponds to @avg@. Note that in SQL, @avg@ is type changing - for+-- example, the @avg@ of @integer@ returns a @numeric@. Rel8 doesn't support+-- this, and will add explicit casts back to the original input type. If you+-- need a fractional result on an integral column, you should cast your input+-- to 'Double' or 'Data.Scientific.Scientific' before calling 'avg'.+avg :: Sql DBSum a => Expr a -> Aggregate a+avg = unsafeMakeAggregate toPrimExpr (castExpr . fromPrimExpr) $+  Just Aggregator+    { operation = Opaleye.AggrAvg     , ordering = []     , distinction = Opaleye.AggrAll     }
src/Rel8/Expr/Num.hs view
@@ -4,18 +4,24 @@ {-# options_ghc -fno-warn-redundant-constraints #-}  module Rel8.Expr.Num-  ( fromIntegral, realToFrac, div, mod, ceiling, floor, round, truncate+  ( fromIntegral+  , realToFrac+  , div, mod, divMod+  , quot, rem, quotRem+  , ceiling, floor, round, truncate   ) where  -- base-import Prelude ()+import Prelude ( (+), (-), fst, negate, signum, snd )  -- rel import Rel8.Expr ( Expr( Expr ) )+import Rel8.Expr.Eq ( (==.) ) import Rel8.Expr.Function ( function ) import Rel8.Expr.Opaleye ( castExpr ) import Rel8.Schema.Null ( Homonullable, Sql )+import Rel8.Table.Bool ( bool ) import Rel8.Type.Num ( DBFractional, DBIntegral, DBNum )  @@ -42,14 +48,41 @@ ceiling = function "ceiling"  --- | Perform integral division. Corresponds to the @div()@ function.+-- | Emulates the behaviour of the Haskell function 'Prelude.div' in+-- PostgreSQL. div :: Sql DBIntegral a => Expr a -> Expr a -> Expr a-div = function "div"+div n d = fst (divMod n d)  --- | Corresponds to the @mod()@ function.+-- | Emulates the behaviour of the Haskell function 'Prelude.mod' in+-- PostgreSQL. mod :: Sql DBIntegral a => Expr a -> Expr a -> Expr a-mod = function "mod"+mod n d = snd (divMod n d)+++-- | Simultaneous 'div' and 'mod'.+divMod :: Sql DBIntegral a => Expr a -> Expr a -> (Expr a, Expr a)+divMod n d = bool qr (q - 1, r + d) (signum r ==. negate (signum d))+  where+    qr@(q, r) = quotRem n d+++-- | Perform integral division. Corresponds to the @div()@ function in+-- PostgreSQL, which behaves like Haskell's 'Prelude.quot' rather than+-- 'Prelude.div'.+quot :: Sql DBIntegral a => Expr a -> Expr a -> Expr a+quot = function "div"+++-- | Corresponds to the @mod()@ function in PostgreSQL, which behaves like+-- Haskell's 'Prelude.rem' rather than 'Prelude.mod'.+rem :: Sql DBIntegral a => Expr a -> Expr a -> Expr a+rem = function "mod"+++-- | Simultaneous 'quot' and 'rem'.+quotRem :: Sql DBIntegral a => Expr a -> Expr a -> (Expr a, Expr a)+quotRem n d = (quot n d, rem n d)   -- | Round a 'DFractional' to a 'DBIntegral' by rounding to the nearest smaller
src/Rel8/Expr/Opaleye.hs view
@@ -80,9 +80,9 @@ traversePrimExpr f = fmap fromPrimExpr . f . toPrimExpr  -toColumn :: Opaleye.PrimExpr -> Opaleye.Column b+toColumn :: Opaleye.PrimExpr -> Opaleye.Field_ n b toColumn = Opaleye.Column  -fromColumn :: Opaleye.Column b -> Opaleye.PrimExpr+fromColumn :: Opaleye.Field_ n b -> Opaleye.PrimExpr fromColumn (Opaleye.Column a) = a
src/Rel8/Expr/Text.hs view
@@ -17,6 +17,9 @@   , pgClientEncoding, quoteIdent, quoteLiteral, quoteNullable, regexpReplace   , regexpSplitToArray, repeat, replace, reverse, right, rpad, rtrim   , splitPart, strpos, substr, translate++    -- * @LIKE@ and @ILIKE@+  , like, ilike   ) where @@ -24,7 +27,7 @@ import Data.Bool ( Bool ) import Data.Int ( Int32 ) import Data.Maybe ( Maybe( Nothing, Just ) )-import Prelude ()+import Prelude ( flip )  -- bytestring import Data.ByteString ( ByteString )@@ -49,7 +52,7 @@   -- | Matches regular expression, case sensitive--- +-- -- Corresponds to the @~.@ operator. (~.) :: Expr Text -> Expr Text -> Expr Bool (~.) = binaryOperator "~."@@ -273,3 +276,21 @@ -- | Corresponds to the @translate@ function. translate :: Expr Text -> Expr Text -> Expr Text -> Expr Text translate = function "translate"+++-- | @like x y@ corresponds to the expression @y LIKE x@.+--+-- Note that the arguments to @like@ are swapped. This is to aid currying, so+-- you can write expressions like+-- @filter (like "Rel%" . packageName) =<< each haskellPackages@+like :: Expr Text -> Expr Text -> Expr Bool+like = flip (binaryOperator "LIKE")+++-- | @ilike x y@ corresponds to the expression @y ILIKE x@.+--+-- Note that the arguments to @ilike@ are swapped. This is to aid currying, so+-- you can write expressions like+-- @filter (ilike "Rel%" . packageName) =<< each haskellPackages@+ilike :: Expr Text -> Expr Text -> Expr Bool+ilike = flip (binaryOperator "ILIKE")
src/Rel8/Generic/Record.hs view
@@ -142,6 +142,7 @@   uncount = id  +type Record :: Type -> Type newtype Record a = Record   { unrecord :: a   }
src/Rel8/Query/Aggregate.hs view
@@ -1,13 +1,17 @@ {-# language FlexibleContexts #-} {-# language MonoLocalBinds #-}+{-# language ScopedTypeVariables #-}+{-# language TypeApplications #-}  module Rel8.Query.Aggregate   ( aggregate   , countRows+  , mode   ) where  -- base+import Data.Functor.Contravariant ( (>$<) ) import Data.Int ( Int64 ) import Prelude @@ -18,9 +22,16 @@ import Rel8.Aggregate ( Aggregates ) import Rel8.Expr ( Expr ) import Rel8.Expr.Aggregate ( countStar )+import Rel8.Expr.Order ( desc ) import Rel8.Query ( Query )+import Rel8.Query.Limit ( limit ) import Rel8.Query.Maybe ( optional ) import Rel8.Query.Opaleye ( mapOpaleye )+import Rel8.Query.Order ( orderBy )+import Rel8.Table ( toColumns )+import Rel8.Table.Aggregate ( hgroupBy )+import Rel8.Table.Cols ( Cols( Cols ), fromCols )+import Rel8.Table.Eq ( EqTable, eqTable ) import Rel8.Table.Opaleye ( aggregator ) import Rel8.Table.Maybe ( maybeTable ) @@ -35,3 +46,11 @@ -- will return @0@. countRows :: Query a -> Query (Expr Int64) countRows = fmap (maybeTable 0 id) . optional . aggregate . fmap (const countStar)+++-- | Return the most common row in a query.+mode :: forall a. EqTable a => Query a -> Query a+mode rows = limit 1 $ fmap (fromCols . snd) $ orderBy (fst >$< desc) $ do+  aggregate $ do+    row <- toColumns <$> rows+    pure (countStar, Cols $ hgroupBy (eqTable @a) row)
src/Rel8/Query/Exists.hs view
@@ -12,7 +12,7 @@  -- opaleye import qualified Opaleye.Exists as Opaleye-import qualified Opaleye.Operators as Opaleye hiding ( exists )+import qualified Opaleye.Operators as Opaleye  -- rel8 import Rel8.Expr ( Expr )
src/Rel8/Query/Set.hs view
@@ -23,36 +23,36 @@   -- | Combine the results of two queries of the same type, collapsing--- duplicates.  @union a b@ is the same as the SQL statement @x UNION b@.+-- duplicates.  @union a b@ is the same as the SQL statement @a UNION b@. union :: EqTable a => Query a -> Query a -> Query a union = zipOpaleyeWith (Opaleye.unionExplicit binaryspec)   -- | Combine the results of two queries of the same type, retaining duplicates.--- @unionAll a b@ is the same as the SQL statement @x UNION ALL b@.+-- @unionAll a b@ is the same as the SQL statement @a UNION ALL b@. unionAll :: Table Expr a => Query a -> Query a -> Query a unionAll = zipOpaleyeWith (Opaleye.unionAllExplicit binaryspec)   -- | Find the intersection of two queries, collapsing duplicates.  @intersect a--- b@ is the same as the SQL statement @x INTERSECT b@.+-- b@ is the same as the SQL statement @a INTERSECT b@. intersect :: EqTable a => Query a -> Query a -> Query a intersect = zipOpaleyeWith (Opaleye.intersectExplicit binaryspec)   -- | Find the intersection of two queries, retaining duplicates.  @intersectAll--- a b@ is the same as the SQL statement @x INTERSECT ALL b@.+-- a b@ is the same as the SQL statement @a INTERSECT ALL b@. intersectAll :: EqTable a => Query a -> Query a -> Query a intersectAll = zipOpaleyeWith (Opaleye.intersectAllExplicit binaryspec)   -- | Find the difference of two queries, collapsing duplicates @except a b@ is--- the same as the SQL statement @x EXCEPT b@.+-- the same as the SQL statement @a EXCEPT b@. except :: EqTable a => Query a -> Query a -> Query a except = zipOpaleyeWith (Opaleye.exceptExplicit binaryspec)   -- | Find the difference of two queries, retaining duplicates.  @exceptAll a b@--- is the same as the SQL statement @x EXCEPT ALL b@.+-- is the same as the SQL statement @a EXCEPT ALL b@. exceptAll :: EqTable a => Query a -> Query a -> Query a exceptAll = zipOpaleyeWith (Opaleye.exceptAllExplicit binaryspec)
src/Rel8/Schema/HTable/Vectorize.hs view
@@ -27,7 +27,7 @@ where  -- base-import Data.Kind ( Type )+import Data.Kind ( Constraint, Type ) import Data.List.NonEmpty ( NonEmpty ) import GHC.Generics (Generic) import Prelude@@ -53,6 +53,7 @@ import Data.Zip ( Unzip, Zip, Zippy(..) )  +type Vector :: (Type -> Type) -> Constraint class Vector list where   listNotNull :: proxy a -> Dict NotNull (list a)   vectorTypeInformation :: ()
src/Rel8/Schema/Table.hs view
@@ -2,6 +2,7 @@ {-# language DerivingStrategies #-} {-# language DisambiguateRecordFields #-} {-# language NamedFieldPuns #-}+{-# language StandaloneKindSignatures #-}  module Rel8.Schema.Table   ( TableSchema(..)@@ -10,6 +11,7 @@ where  -- base+import Data.Kind ( Type ) import Prelude  -- opaleye@@ -26,6 +28,7 @@ --  -- For each selectable table in your database, you should provide a -- @TableSchema@ in order to interact with the table via Rel8.+type TableSchema :: Type -> Type data TableSchema names = TableSchema   { name :: String     -- ^ The name of the table.
src/Rel8/Statement/Select.hs view
@@ -3,6 +3,7 @@ {-# language FlexibleContexts #-} {-# language MonoLocalBinds #-} {-# language ScopedTypeVariables #-}+{-# language StandaloneKindSignatures #-} {-# language TypeApplications #-}  module Rel8.Statement.Select@@ -17,6 +18,7 @@  -- base import Data.Foldable ( toList )+import Data.Kind ( Type ) import Data.List.NonEmpty ( NonEmpty( (:|) ) ) import Data.Void ( Void ) import Prelude hiding ( undefined )@@ -116,6 +118,7 @@     (a, primQuery, _) = Opaleye.runSimpleQueryArrStart (toOpaleye query) ()  +type Optimized :: Type -> Type data Optimized a = Empty | Unit | Optimized a   deriving stock (Functor, Foldable, Traversable) 
src/Rel8/Table/HKD.hs view
@@ -157,6 +157,7 @@   => HKDable a  +type Top_ :: Constraint class Top_ instance Top_ 
src/Rel8/Table/Opaleye.hs view
@@ -7,6 +7,8 @@ {-# language TypeFamilies #-} {-# language ViewPatterns #-} +{-# options_ghc -Wno-deprecations #-}+ module Rel8.Table.Opaleye   ( aggregator   , attributes@@ -137,7 +139,7 @@ {-# INLINABLE unpackspec #-}  -valuesspec :: Table Expr a => Opaleye.ValuesspecSafe a a+valuesspec :: Table Expr a => Opaleye.Valuesspec a a valuesspec = Opaleye.ValuesspecSafe (toPackMap undefined) unpackspec  
src/Rel8/Type.hs view
@@ -128,10 +128,10 @@ instance DBType Float where   typeInformation = TypeInformation     { encode = \x -> Opaleye.ConstExpr-        if | x == (1 /0) -> Opaleye.OtherLit "'Infinity'"-           | isNaN x     -> Opaleye.OtherLit "'NaN'"-           | x == (-1/0) -> Opaleye.OtherLit "'-Infinity'"-           | otherwise   -> Opaleye.NumericLit $ realToFrac x+        if | x == (1 / 0)  -> Opaleye.OtherLit "'Infinity'"+           | isNaN x       -> Opaleye.OtherLit "'NaN'"+           | x == (-1 / 0) -> Opaleye.OtherLit "'-Infinity'"+           | otherwise     -> Opaleye.NumericLit $ realToFrac x     , decode = Hasql.float4     , typeName = "float4"     }@@ -141,10 +141,10 @@ instance DBType Double where   typeInformation = TypeInformation     { encode = \x -> Opaleye.ConstExpr-        if | x == (1 /0) -> Opaleye.OtherLit "'Infinity'"-           | isNaN x     -> Opaleye.OtherLit "'NaN'"-           | x == (-1/0) -> Opaleye.OtherLit "'-Infinity'"-           | otherwise   -> Opaleye.NumericLit $ realToFrac x+        if | x == (1 / 0)  -> Opaleye.OtherLit "'Infinity'"+           | isNaN x       -> Opaleye.OtherLit "'NaN'"+           | x == (-1 / 0) -> Opaleye.OtherLit "'-Infinity'"+           | otherwise     -> Opaleye.NumericLit $ realToFrac x     , decode = Hasql.float8     , typeName = "float8"     }
src/Rel8/Type/Enum.hs view
@@ -106,6 +106,7 @@  -- | Types that are sum types, where each constructor is unary (that is, has no -- fields).+type Enumable :: Type -> Constraint class (Generic a, GEnumable (Rep a)) => Enumable a instance (Generic a, GEnumable (Rep a)) => Enumable a 
src/Rel8/Type/JSONBEncoded.hs view
@@ -1,3 +1,5 @@+{-# language StandaloneKindSignatures #-}+ module Rel8.Type.JSONBEncoded ( JSONBEncoded(..) ) where  -- aeson@@ -6,6 +8,7 @@  -- base import Data.Bifunctor ( first )+import Data.Kind ( Type ) import Prelude  -- hasql@@ -20,6 +23,7 @@   -- | Like 'Rel8.JSONEncoded', but works for @jsonb@ columns.+type JSONBEncoded :: Type -> Type newtype JSONBEncoded a = JSONBEncoded { fromJSONBEncoded :: a }  
src/Rel8/Type/JSONEncoded.hs view
@@ -1,3 +1,5 @@+{-# language StandaloneKindSignatures #-}+ module Rel8.Type.JSONEncoded ( JSONEncoded(..) ) where  -- aeson@@ -5,6 +7,7 @@ import Data.Aeson.Types ( parseEither )  -- base+import Data.Kind ( Type ) import Prelude  -- rel8@@ -15,6 +18,7 @@ -- | A deriving-via helper type for column types that store a Haskell value -- using a JSON encoding described by @aeson@'s 'ToJSON' and 'FromJSON' type -- classes.+type JSONEncoded :: Type -> Type newtype JSONEncoded a = JSONEncoded { fromJSONEncoded :: a }  
src/Rel8/Type/Num.hs view
@@ -18,6 +18,7 @@  -- rel8 import Rel8.Type ( DBType )+import Rel8.Type.Ord ( DBOrd )  -- scientific import Data.Scientific ( Scientific )@@ -39,13 +40,14 @@ -- expressions. This is a Rel8 concept, and allows us to provide -- 'fromIntegral'. type DBIntegral :: Type -> Constraint-class DBNum a => DBIntegral a+class (DBNum a, DBOrd a) => DBIntegral a instance DBIntegral Int16 instance DBIntegral Int32 instance DBIntegral Int64   -- | The class of database types that support the @/@ operator.+type DBFractional :: Type -> Constraint class DBNum a => DBFractional a instance DBFractional Float instance DBFractional Double@@ -53,6 +55,7 @@   -- | The class of database types that support the @/@ operator.+type DBFloating :: Type -> Constraint class DBFractional a => DBFloating a instance DBFloating Float instance DBFloating Double
src/Rel8/Type/ReadShow.hs view
@@ -1,10 +1,12 @@ {-# language ScopedTypeVariables #-}+{-# language StandaloneKindSignatures #-} {-# language TypeApplications #-} {-# language ViewPatterns #-}  module Rel8.Type.ReadShow ( ReadShow(..) ) where  -- base+import Data.Kind ( Type ) import Data.Proxy ( Proxy( Proxy ) ) import Data.Typeable ( Typeable, typeRep ) import Prelude @@ -20,6 +22,7 @@  -- | A deriving-via helper type for column types that store a Haskell value -- using a Haskell's 'Read' and 'Show' type classes.+type ReadShow :: Type -> Type newtype ReadShow a = ReadShow { fromReadShow :: a }  
src/Rel8/Type/Tag.hs view
@@ -90,6 +90,7 @@   memptyExpr = litExpr mempty  +type Tag :: Type newtype Tag = Tag Text   deriving newtype     ( Eq, Ord, Read, Show