packages feed

selda 0.3.1.0 → 0.3.2.0

raw patch · 8 files changed

+63/−44 lines, 8 files

Files

ChangeLog.md view
@@ -1,6 +1,13 @@ # Revision history for Selda  +## 0.3.2.0 -- 2018-08-07++* Some aggregates are now nullable.+* sum_ on an empty table doesn't crash anymore.+* Aggregating over an empty selectValues doesn't crash anymore.++ ## 0.3.1.0 -- 2018-08-06  * Minor API fix when defining table attributes.
selda.cabal view
@@ -1,5 +1,5 @@ name:                selda-version:             0.3.1.0+version:             0.3.2.0 synopsis:            Multi-backend, high-level EDSL for interacting with SQL databases. description:         This package provides an EDSL for writing portable, type-safe, high-level                      database code. Its feature set includes querying and modifying databases,
src/Database/Selda.hs view
@@ -47,8 +47,9 @@   , (.==), (./=), (.>), (.<), (.>=), (.<=), like   , (.&&), (.||), not_   , literal, int, float, text, true, false, null_-  , roundTo, length_, isNull, ifThenElse, matchNull+  , roundTo, length_, isNull, ifThenElse, ifNull, matchNull   , new, only+  , Mappable (..)     -- * Converting between column types   , round_, just, fromBool, fromInt, toString     -- * Inner queries@@ -278,6 +279,29 @@           -> Col s b matchNull nullvalue f x = ifThenElse (isNull x) nullvalue (f (cast x)) +-- | If the second value is Nothing, return the first value. Otherwise return+--   the second value.+ifNull :: SqlType a => Col s a -> Col s (Maybe a) -> Col s a+ifNull nullvalue x = ifThenElse (isNull x) nullvalue (cast x)++-- | Any container type which can be mapped over.+--   Sort of like 'Functor', if you squint a bit.+class Mappable f where+  type Container f a+  (.<$>) :: (SqlType a, SqlType b)+         => (Col s a -> Col s b)+         -> f s (Container f a)+         -> f s (Container f b)+infixl 4 .<$>++instance Mappable Aggr where+  type Container Aggr a = a+  (.<$>) = liftAggr++instance Mappable Col where+  type Container Col a = Maybe a+  f .<$> mx = cast (f (cast mx))+ -- | Any container type for which we can check object membership. class Set set where   -- | Is the given column contained in the given set?@@ -354,20 +378,20 @@ count = aggr "COUNT"  -- | The average of all values in the given column.-avg :: (SqlType a, Num a) => Col s a -> Aggr s a+avg :: (SqlType a, Num a) => Col s a -> Aggr s (Maybe a) avg = aggr "AVG"  -- | The greatest value in the given column. Texts are compared lexically.-max_ :: SqlOrd a => Col s a -> Aggr s a+max_ :: SqlOrd a => Col s a -> Aggr s (Maybe a) max_ = aggr "MAX"  -- | The smallest value in the given column. Texts are compared lexically.-min_ :: SqlOrd a => Col s a -> Aggr s a+min_ :: SqlOrd a => Col s a -> Aggr s (Maybe a) min_ = aggr "MIN"  -- | Sum all values in the given column. sum_ :: forall a b s. (SqlType a, SqlType b, Num a, Num b) => Col s a -> Aggr s b-sum_ = castAggr . aggr "SUM"+sum_ = liftAggr (ifNull (0::Col s b) . cast) . aggr "SUM"  -- | Round a value to the nearest integer. Equivalent to @roundTo 0@. round_ :: forall s a. (SqlType a, Num a) => Col s Double -> Col s a
src/Database/Selda/Column.hs view
@@ -81,27 +81,10 @@   abs = liftC $ UnOp Abs   signum = liftC $ UnOp Sgn -instance {-# OVERLAPPING #-} (SqlType a, Num a) => Num (Col s (Maybe a)) where-  fromInteger = literal . Just . fromInteger-  (+) = liftC2 $ BinOp Add-  (-) = liftC2 $ BinOp Sub-  (*) = liftC2 $ BinOp Mul-  negate = liftC $ UnOp Neg-  abs = liftC $ UnOp Abs-  signum = liftC $ UnOp Sgn- instance Fractional (Col s Double) where   fromRational = literal . fromRational   (/) = liftC2 $ BinOp Div -instance Fractional (Col s (Maybe Double)) where-  fromRational = literal . Just . fromRational-  (/) = liftC2 $ BinOp Div- instance Fractional (Col s Int) where   fromRational = literal . (truncate :: Double -> Int) . fromRational-  (/) = liftC2 $ BinOp Div--instance Fractional (Col s (Maybe Int)) where-  fromRational = literal . Just . (truncate :: Double -> Int) . fromRational   (/) = liftC2 $ BinOp Div
src/Database/Selda/Inner.hs view
@@ -19,8 +19,10 @@ --   converted into a non-aggregate column. newtype Aggr s a = Aggr {unAggr :: Exp SQL a} -liftAggr :: (Exp SQL a -> Exp SQL b) -> Aggr s a -> Aggr s b-liftAggr f = Aggr . f . unAggr+-- | Lift a function over columns to aggregates.+liftAggr :: (Col s a -> Col s b) -> Aggr s a -> Aggr s b+liftAggr f = Aggr . unOne . f . One . unAggr+  where unOne (One x) = x  -- | Denotes an inner query. --   For aggregation, treating sequencing as the cartesian product of queries
src/Database/Selda/Query.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE FlexibleContexts, OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts, OverloadedStrings, ScopedTypeVariables #-} -- | Query monad and primitive operations. module Database.Selda.Query   ( select, selectValues, Database.Selda.Query.distinct@@ -11,10 +11,13 @@ import Database.Selda.Inner import Database.Selda.Query.Type import Database.Selda.SQL as SQL-import Database.Selda.SqlType (SqlType)+import Database.Selda.SqlType (SqlType, Lit (LNull), SqlTypeRep (..))+import Database.Selda.SqlRow (SqlRow (nestedCols)) import Database.Selda.Table import Database.Selda.Transform import Control.Monad.State.Strict+import Data.Proxy+import GHC.Generics (Rep) import Unsafe.Coerce  -- | Query the given table.@@ -27,11 +30,11 @@  -- | Query an ad hoc table of type @a@. Each element in the given list represents --   one row in the ad hoc table.-selectValues :: Relational a => [a] -> Query s (Row s a)+selectValues :: forall s a. Relational a => [a] -> Query s (Row s a) selectValues [] = Query $ do   st <- get   put $ st {sources = sqlFrom [] EmptyTable : sources st}-  return $ Many (repeat (Untyped $ Col "NULL"))+  return $ Many (gNew (Proxy :: Proxy (Rep a))) selectValues (row:rows) = Query $ do     names <- mapM (const freshName) firstrow     let rns = [Named n (Col n) | n <- names]
src/Database/Selda/Table.hs view
@@ -39,7 +39,7 @@ --   For example: -- -- > data Person = Person--- >   { id   :: Int+-- >   { id   :: ID Person -- >   , name :: Text -- >   , age  :: Int -- >   , pet  :: Maybe Text@@ -47,13 +47,13 @@ -- >   deriving Generic -- > -- > people :: Table Person--- > people = table "people" [name :- autoPrimary]+-- > people = table "people" [pId :- autoPrimary]+-- > pId :*: pName :*: pAge :*: pPet = selectors people -----   This example will create a table with the column types---   @Int :*: Text :*: Int :*: Maybe Text@, where the first field is---   an auto-incrementing primary key.+--   This will result in a table of @Person@s, with an auto-incrementing primary+--   key. -----   If the given type is not a record type, the column names will be+--   If the given type does not have record selectors, the column names will be --   @col_1@, @col_2@, etc. table :: forall a. Relational a          => TableName@@ -112,14 +112,12 @@ -- | A pair of the table with the given name and columns, and all its selectors. --   For example: ----- > tbl :: Table (Int :*: Text)+-- > tbl :: Table (Int, Text) -- > (tbl, tblBar :*: tblBaz)--- >   =  tableWithSelectors "foo"--- >   $  required "bar"--- >   :*: required "baz"+-- >   =  tableWithSelectors "foo" [] -- > -- > q :: Query s Text--- > q = tblBaz <$> select tbl+-- > q = tblBaz `from` select tbl tableWithSelectors :: forall a. Relational a                    => TableName                    -> [Attr a]@@ -133,12 +131,14 @@ --   Selectors can be used to access the fields of a query result tuple, avoiding --   the need to pattern match on the entire tuple. ----- > tbl :: Table (Int :*: Text)--- > tbl = table "foo" $ required "bar" :*: required "baz"+-- > tbl :: Table (Int, Text)+-- > tbl = table "foo" [] -- > (tblBar :*: tblBaz) = selectors tbl -- > -- > q :: Query s Text--- > q = tblBaz <$> select tbl+-- > q = do+-- >   row <- select tbl+-- >   return (row ! tblBaz) selectors :: forall a. Relational a => Table a -> Selectors a selectors _ = selectorsFor (Proxy :: Proxy a) 
src/Database/Selda/Unsafe.hs view
@@ -23,7 +23,7 @@ -- | Cast an aggregate to another type, using whichever coercion semantics --   are used by the underlying SQL implementation. castAggr :: forall s a b. SqlType b => Aggr s a -> Aggr s b-castAggr = liftAggr $ Cast (sqlType (Proxy :: Proxy b))+castAggr = liftAggr cast  -- | A unary operation. Note that the provided function name is spliced --   directly into the resulting SQL query. Thus, this function should ONLY