persistable-record 0.5.0.1 → 0.5.0.2
raw patch · 5 files changed
+175/−84 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- ChangeLog.md +4/−0
- persistable-record.cabal +3/−2
- src/Database/Record/FromSql.hs +47/−32
- src/Database/Record/Persistable.hs +30/−15
- src/Database/Record/ToSql.hs +91/−35
ChangeLog.md view
@@ -1,5 +1,9 @@ <!-- -*- Markdown -*- --> +## 0.5.0.2++- add tested-with 8.2.1.+ ## 0.5.0.1 - Use Haskell implementation test instead of flag test in .cabal
persistable-record.cabal view
@@ -1,5 +1,5 @@ name: persistable-record-version: 0.5.0.1+version: 0.5.0.2 synopsis: Binding between SQL database values and haskell records. description: This package contiains types to represent table constraints and interfaces to bind between SQL database values and Haskell records.@@ -12,7 +12,8 @@ category: Database build-type: Simple cabal-version: >=1.10-tested-with: GHC == 8.0.1, GHC == 8.0.2+tested-with: GHC == 8.2.1+ , GHC == 8.0.1, GHC == 8.0.2 , GHC == 7.10.1, GHC == 7.10.2, GHC == 7.10.3 , GHC == 7.8.1, GHC == 7.8.2, GHC == 7.8.3, GHC == 7.8.4 , GHC == 7.6.1, GHC == 7.6.2, GHC == 7.6.3
src/Database/Record/FromSql.hs view
@@ -14,17 +14,17 @@ -- Portability : unknown -- -- This module defines interfaces--- from list of SQL type into Haskell type.+-- from list of database value type into Haskell type.+ module Database.Record.FromSql (- -- * Conversion from list of SQL type into record type- -- $recordFromSql+ -- * Conversion from list of database value type into record type RecordFromSql, runTakeRecord, runToRecord, createRecordFromSql, (<&>), maybeRecord, - -- * Inference rules of 'RecordFromSql' conversion+ -- * Derivation rules of 'RecordFromSql' conversion FromSql (recordFromSql), takeRecord, toRecord, @@ -40,11 +40,13 @@ import Database.Record.KeyConstraint (HasColumnConstraint(columnConstraint), ColumnConstraint, NotNull, index) +{- |+'RecordFromSql' 'q' 'a' is data-type wrapping function+to convert from list of database value type (to receive from database) ['q'] into Haskell type 'a' -{- $recordFromSql-Structure of 'RecordFromSql' 'q' 'a' is similar to parser.-While running 'RecordFromSql' behavior is the same as parser-which parse list of SQL type ['q'] stream.+This structure is similar to parser.+While running 'RecordFromSql' behavior is the same as non-fail-able parser+which parse list of database value type ['q'] stream. So, 'RecordFromSql' 'q' is 'Monad' and 'Applicative' instance like parser monad. When, you have data constructor and objects like below.@@ -69,25 +71,23 @@ myRecord = MyRecord \<$\> foo \<*\> bar \<*\> baz @ -}---- | Proof object type to convert from sql value type 'q' list into Haskell type 'a'. newtype RecordFromSql q a = RecordFromSql ([q] -> (a, [q])) --- | Run 'RecordFromSql' proof object.--- Convert from list of SQL type ['q'] into Haskell type 'a' and rest of list ['q'].-runTakeRecord :: RecordFromSql q a -- ^ Proof object which has capability to convert- -> [q] -- ^ list of SQL type+-- | Run 'RecordFromSql' parser function object.+-- Convert from list of database value type ['q'] into Haskell type 'a' and rest of list ['q'].+runTakeRecord :: RecordFromSql q a -- ^ parser function object which has capability to convert+ -> [q] -- ^ list of database value type -> (a, [q]) -- ^ Haskell type and rest of list runTakeRecord (RecordFromSql f) = f --- | Axiom of 'RecordFromSql' for SQL type 'q' and Haskell type 'a'+-- | Axiom of 'RecordFromSql' for database value type 'q' and Haskell type 'a' createRecordFromSql :: ([q] -> (a, [q])) -- ^ Convert function body- -> RecordFromSql q a -- ^ Result proof object+ -> RecordFromSql q a -- ^ Result parser function object createRecordFromSql = RecordFromSql --- | Run 'RecordFromSql' proof object. Convert from list of SQL type ['q'] into Haskell type 'a'.-runToRecord :: RecordFromSql q a -- ^ Proof object which has capability to convert- -> [q] -- ^ list of SQL type+-- | Run 'RecordFromSql' parser function object. Convert from list of database value type ['q'] into Haskell type 'a'.+runToRecord :: RecordFromSql q a -- ^ parser function object which has capability to convert+ -> [q] -- ^ list of database value type -> a -- ^ Haskell type runToRecord r = fst . runTakeRecord r @@ -108,14 +108,14 @@ pure = return (<*>) = ap --- | Derivation rule of 'RecordFromSql' proof object for Haskell tuple (,) type.+-- | Derivation rule of 'RecordFromSql' parser function object for Haskell tuple (,) type. (<&>) :: RecordFromSql q a -> RecordFromSql q b -> RecordFromSql q (a, b) a <&> b = (,) <$> a <*> b infixl 4 <&> --- | Derivation rule of 'RecordFromSql' proof object for Haskell 'Maybe' type.+-- | Derivation rule of 'RecordFromSql' parser function object for Haskell 'Maybe' type. maybeRecord :: PersistableType q => RecordFromSql q a -> ColumnConstraint NotNull a@@ -126,10 +126,25 @@ | otherwise = (Nothing, vals') where (a, vals') = runTakeRecord rec vals +{- |+'FromSql' 'q' 'a' is implicit rule to derive 'RecordFromSql' 'q' 'a' record parser function against type 'a'. --- | Inference rule interface for 'RecordFromSql' proof object.+Generic programming (<https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#generic-programming>)+with default signature is available for 'FromSql' class,+so you can make instance like below:++@+ \{\-\# LANGUAGE DeriveGeneric \#\-\}+ import GHC.Generics (Generic)+ import Database.HDBC (SqlValue)+ --+ data Foo = Foo { ... } deriving Generic+ instance FromSql SqlValue Foo+@++-} class FromSql q a where- -- | 'RecordFromSql' proof object.+ -- | 'RecordFromSql' 'q' 'a' record parser function. recordFromSql :: RecordFromSql q a default recordFromSql :: (Generic a, GFromSql q (Rep a)) => RecordFromSql q a@@ -152,26 +167,26 @@ gFromSql = K1 <$> recordFromSql --- | Inference rule of 'RecordFromSql' proof object which can convert--- from list of SQL type ['q'] into Haskell 'Maybe' type.+-- | Implicit derivation rule of 'RecordFromSql' parser function object which can convert+-- from list of database value type ['q'] into Haskell 'Maybe' type. instance (HasColumnConstraint NotNull a, FromSql q a, PersistableType q) => FromSql q (Maybe a) where recordFromSql = maybeRecord recordFromSql columnConstraint --- | Inference rule of 'RecordFromSql' proof object which can convert--- from /empty/ list of SQL type ['q'] into Haskell unit () type.+-- | Implicit derivation rule of 'RecordFromSql' parser function object which can convert+-- from /empty/ list of database value type ['q'] into Haskell unit () type. instance FromSql q () -- default generic instance --- | Run inferred 'RecordFromSql' proof object.--- Convert from list of SQL type ['q'] into haskell type 'a' and rest of list ['q'].+-- | Run implicit 'RecordFromSql' parser function object.+-- Convert from list of database value type ['q'] into haskell type 'a' and rest of list ['q']. takeRecord :: FromSql q a => [q] -> (a, [q]) takeRecord = runTakeRecord recordFromSql --- | Run inferred 'RecordFromSql' proof object.--- Convert from list of SQL type ['q'] into haskell type 'a'.+-- | Run implicit 'RecordFromSql' parser function object.+-- Convert from list of database value type ['q'] into haskell type 'a'. toRecord :: FromSql q a => [q] -> a toRecord = runToRecord recordFromSql --- | Derivation rule of 'RecordFromSql' proof object for value convert function.+-- | Derivation rule of 'RecordFromSql' parser function object for value convert function. valueRecordFromSql :: (q -> a) -> RecordFromSql q a valueRecordFromSql d = createRecordFromSql $ \qs -> (d $ head qs, tail qs)
src/Database/Record/Persistable.hs view
@@ -13,17 +13,17 @@ -- Stability : experimental -- Portability : unknown ----- This module defines interfaces--- between Haskell type and list of SQL type.+-- This module defines proposition interfaces+-- for database value type and record type width. module Database.Record.Persistable (- -- * Specify SQL type+ -- * Specify database value type PersistableSqlType, runPersistableNullValue, unsafePersistableSqlTypeFromNull, -- * Specify record width PersistableRecordWidth, runPersistableRecordWidth, unsafePersistableRecordWidth, unsafeValueWidth, (<&>), maybeWidth, - -- * Inference rules for proof objects+ -- * Implicit derivation rules, database value type and record type width PersistableType(..), sqlNullValue, PersistableWidth (..), derivedWidth, @@ -41,15 +41,15 @@ import qualified Data.DList as DList --- | Proof object to specify type 'q' is SQL type+-- | Proposition to specify type 'q' is database value type, contains null value newtype PersistableSqlType q = PersistableSqlType q --- | Null value of SQL type 'q'.+-- | Null value of database value type 'q'. runPersistableNullValue :: PersistableSqlType q -> q runPersistableNullValue (PersistableSqlType q) = q --- | Unsafely generate 'PersistableSqlType' proof object from specified SQL null value which type is 'q'.-unsafePersistableSqlTypeFromNull :: q -- ^ SQL null value of SQL type 'q'+-- | Unsafely specify 'PersistableSqlType' axiom from specified database null value which type is 'q'.+unsafePersistableSqlTypeFromNull :: q -- ^ null value of database value type 'q' -> PersistableSqlType q -- ^ Result proof object unsafePersistableSqlTypeFromNull = PersistableSqlType @@ -63,8 +63,8 @@ getProductConst = getConst . unPC {-# INLINE getProductConst #-} --- | Proof object to specify width of Haskell type 'a'--- when converting to SQL type list.+-- | Proposition to specify width of Haskell type 'a'.+-- The width is length of database value list which is converted from Haskell type 'a'. type PersistableRecordWidth a = ProductConst (Sum Int) a -- unsafely map PersistableRecordWidth@@ -84,13 +84,13 @@ instance Show a => Show (ProductConst a b) where show = ("PC " ++) . show . getConst . unPC --- | Unsafely generate 'PersistableRecordWidth' proof object from specified width of Haskell type 'a'.+-- | Unsafely specify 'PersistableRecordWidth' axiom from specified width of Haskell type 'a'. unsafePersistableRecordWidth :: Int -- ^ Specify width of Haskell type 'a' -> PersistableRecordWidth a -- ^ Result proof object unsafePersistableRecordWidth = ProductConst . Const . Sum {-# INLINE unsafePersistableRecordWidth #-} --- | Unsafely generate 'PersistableRecordWidth' proof object for Haskell type 'a' which is single column type.+-- | Unsafely specify 'PersistableRecordWidth' axiom for Haskell type 'a' which is single column type. unsafeValueWidth :: PersistableRecordWidth a unsafeValueWidth = unsafePersistableRecordWidth 1 {-# INLINE unsafeValueWidth #-}@@ -104,16 +104,31 @@ maybeWidth = pmap Just --- | Interface of inference rule for 'PersistableSqlType' proof object+-- | Interface of derivation rule for 'PersistableSqlType'. class Eq q => PersistableType q where persistableType :: PersistableSqlType q --- | Inferred Null value of SQL type.+-- | Implicitly derived null value of database value type. sqlNullValue :: PersistableType q => q sqlNullValue = runPersistableNullValue persistableType --- | Interface of inference rule for 'PersistableRecordWidth' proof object+{- |+'PersistableWidth' 'a' is implicit rule to derive 'PersistableRecordWidth' 'a' width proposition for type 'a'.++Generic programming (<https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#generic-programming>)+with default signature is available for 'PersistableWidth' class,+so you can make instance like below:++@+ \{\-\# LANGUAGE DeriveGeneric \#\-\}+ import GHC.Generics (Generic)+ --+ data Foo = Foo { ... } deriving Generic+ instance PersistableWidth Foo+@++-} class PersistableWidth a where persistableWidth :: PersistableRecordWidth a
src/Database/Record/ToSql.hs view
@@ -14,24 +14,24 @@ -- Portability : unknown -- -- This module defines interfaces--- from Haskell type into list of SQL type.+-- from Haskell type into list of database value type. module Database.Record.ToSql (- -- * Conversion from record type into list of SQL type+ -- * Conversion from record type into list of database value type ToSqlM, RecordToSql, runFromRecord, createRecordToSql, (<&>), - -- * Inference rules of 'RecordToSql' conversion+ -- * Derivation rules of 'RecordToSql' conversion ToSql (recordToSql), putRecord, putEmpty, fromRecord, wrapToSql, valueRecordToSql, -- * Make parameter list for updating with key- updateValuesByUnique', updateValuesByUnique, updateValuesByPrimary,+ updateValuesByUnique', untypedUpdateValuesIndex, unsafeUpdateValuesWithIndexes@@ -51,13 +51,20 @@ (Primary, Unique, KeyConstraint, HasKeyConstraint(keyConstraint), unique, indexes) --- | Context type to convert SQL type list.+-- | Context type to convert into database value list. type ToSqlM q a = Writer (DList q) a runToSqlM :: ToSqlM q a -> [q] runToSqlM = DList.toList . execWriter --- | Proof object type to convert from Haskell type 'a' into list of SQL type ['q'].+{- |+'RecordToSql' 'q' 'a' is data-type wrapping function+to convert from Haskell type 'a' into list of database value type (to send to database) ['q'].++This structure is similar to printer.+While running 'RecordToSql' behavior is the same as list printer.+which appends list of database value type ['q'] stream.+-} newtype RecordToSql q a = RecordToSql (a -> ToSqlM q ()) runRecordToSql :: RecordToSql q a -> a -> ToSqlM q ()@@ -67,15 +74,15 @@ wrapToSql :: (a -> ToSqlM q ()) -> RecordToSql q a wrapToSql = RecordToSql --- | Run 'RecordToSql' proof object. Convert from Haskell type 'a' into list of SQL type ['q'].-runFromRecord :: RecordToSql q a -- ^ Proof object which has capability to convert+-- | Run 'RecordToSql' printer function object. Convert from Haskell type 'a' into list of database value type ['q'].+runFromRecord :: RecordToSql q a -- ^ printer function object which has capability to convert -> a -- ^ Haskell type- -> [q] -- ^ list of SQL type+ -> [q] -- ^ list of database value runFromRecord r = runToSqlM . runRecordToSql r --- | Axiom of 'RecordToSql' for SQL type 'q' and Haksell type 'a'.+-- | Axiom of 'RecordToSql' for database value type 'q' and Haksell type 'a'. createRecordToSql :: (a -> [q]) -- ^ Convert function body- -> RecordToSql q a -- ^ Result proof object+ -> RecordToSql q a -- ^ Result printer function object createRecordToSql f = wrapToSql $ tell . DList.fromList . f -- unsafely map record@@ -89,11 +96,11 @@ runRecordToSql ra a runRecordToSql rb b --- | Derivation rule of 'RecordToSql' proof object for Haskell tuple (,) type.+-- | Derivation rule of 'RecordToSql' printer function object for Haskell tuple (,) type. (<&>) :: RecordToSql q a -> RecordToSql q b -> RecordToSql q (a, b) (<&>) = productToSql $ flip uncurry --- | Derivation rule of 'RecordToSql' proof object for Haskell 'Maybe' type.+-- | Derivation rule of 'RecordToSql' printer function object for Haskell 'Maybe' type. maybeRecord :: PersistableSqlType q -> PersistableRecordWidth a -> RecordToSql q a -> RecordToSql q (Maybe a) maybeRecord qt w ra = wrapToSql d where d (Just r) = runRecordToSql ra r@@ -101,10 +108,53 @@ infixl 4 <&> +{- |+'ToSql' 'q' 'a' is implicit rule to derive 'RecordToSql' 'q' 'a' record printer function for type 'a'. --- | Inference rule interface for 'RecordToSql' proof object.+Generic programming (<https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#generic-programming>)+with default signature is available for 'ToSql' class,+so you can make instance like below:++@+ \{\-\# LANGUAGE DeriveGeneric \#\-\}+ import GHC.Generics (Generic)+ import Database.HDBC (SqlValue)+ --+ data Foo = Foo { ... } deriving Generic+ instance ToSql SqlValue Foo+@++To make instances of 'ToSql' manually,+'ToSql' 'q' 'a' and 'RecordToSql' 'q 'a' are composable with monadic context.+When, you have data constructor and objects like below.++@+ data MyRecord = MyRecord Foo Bar Baz+@++@+ instance ToSql SqlValue Foo where+ ...+ instance ToSql SqlValue Bar where+ ...+ instance ToSql SqlValue Baz where+ ...+@++You can get composed 'ToSql' implicit rule like below.++@+ instance ToSql SqlValue MyRecord where+ recordToSql =+ recordToSql = wrapToSql $ \\ (MyRecord x y z) -> do+ putRecord x+ putRecord y+ putRecord z+@++-} class ToSql q a where- -- | Infer 'RecordToSql' proof object.+ -- | Derived 'RecordToSql' printer function object. recordToSql :: RecordToSql q a default recordToSql :: (Generic a, GToSql q (Rep a)) => RecordToSql q a@@ -126,17 +176,17 @@ gToSql = (\(K1 a) -> a) `mapToSql` recordToSql --- | Inference rule of 'RecordToSql' proof object which can convert--- from Haskell 'Maybe' type into list of SQL type ['q'].+-- | Implicit derivation rule of 'RecordToSql' printer function object which can convert+-- from Haskell 'Maybe' type into list of database value type ['q']. instance (PersistableType q, PersistableWidth a, ToSql q a) => ToSql q (Maybe a) where recordToSql = maybeRecord persistableType persistableWidth recordToSql --- | Inference rule of 'RecordToSql' proof object which can convert--- from Haskell unit () type into /empty/ list of SQL type ['q'].+-- | Implicit derivation rule of 'RecordToSql' printer function object which can convert+-- from Haskell unit () type into /empty/ list of database value type ['q']. instance ToSql q () -- default generic instance --- | Run inferred 'RecordToSql' proof object.--- Context to convert haskell record type 'a' into SQL type 'q' list.+-- | Run implicit 'RecordToSql' printer function object.+-- Context to convert haskell record type 'a' into lib of database value type ['q']. putRecord :: ToSql q a => a -> ToSqlM q () putRecord = runRecordToSql recordToSql @@ -144,19 +194,21 @@ putEmpty :: () -> ToSqlM q () putEmpty = putRecord --- | Run inferred 'RecordToSql' proof object.--- Convert from haskell type 'a' into list of SQL type ['q'].+-- | Run implicit 'RecordToSql' printer function object.+-- Convert from haskell type 'a' into list of database value type ['q']. fromRecord :: ToSql q a => a -> [q] fromRecord = runToSqlM . putRecord --- | Derivation rule of 'RecordToSql' proof object for value convert function.+-- | Derivation rule of 'RecordToSql' printer function object for value convert function. valueRecordToSql :: (a -> q) -> RecordToSql q a valueRecordToSql = createRecordToSql . ((:[]) .) -- | Make untyped indexes to update column from key indexes and record width. -- Expected by update form like ----- /UPDATE <table> SET c0 = ?, c1 = ?, ..., cn = ? WHERE key0 = ? AND key1 = ? AND key2 = ? ... /+-- @+-- UPDATE /table/ SET /c0/ = ?, /c1/ = ?, /c2/ = ? ... WHERE /key0/ = ? AND /key1/ = ? AND key2 = ? ...+-- @ untypedUpdateValuesIndex :: [Int] -- ^ Key indexes -> Int -- ^ Record width -> [Int] -- ^ Indexes to update other than key@@ -165,11 +217,13 @@ otherThanKey = toList $ fromList [0 .. maxIx] \\ fromList key -- | Unsafely specify key indexes to convert from Haskell type `ra`--- into SQL value `q` list expected by update form like+-- into database value `q` list expected by update form like ----- /UPDATE <table> SET c0 = ?, c1 = ?, ..., cn = ? WHERE key0 = ? AND key1 = ? AND key2 = ? ... /+-- @+-- UPDATE /table/ SET /c0/ = ?, /c1/ = ?, /c2/ = ? ... WHERE /key0/ = ? AND /key1/ = ? AND /key2/ = ? ...+-- @ ----- using 'RecordToSql' proof object.+-- using 'RecordToSql' printer function object. unsafeUpdateValuesWithIndexes :: RecordToSql q ra -> [Int] -> ra@@ -181,25 +235,27 @@ valsA = listArray (0, width - 1) vals otherThanKey = untypedUpdateValuesIndex key width --- | Convert from Haskell type `ra` into SQL value `q` list expected by update form like+-- | Convert from Haskell type `ra` into database value `q` list expected by update form like ----- /UPDATE <table> SET c0 = ?, c1 = ?, ..., cn = ? WHERE key0 = ? AND key1 = ? AND key2 = ? ... /+-- @+-- UPDATE /table/ SET /c0/ = ?, /c1/ = ?, /c2/ = ? ... WHERE /key0/ = ? AND /key1/ = ? AND /key2/ = ? ...+-- @ ----- using 'RecordToSql' proof object.+-- using 'RecordToSql' printer function object. updateValuesByUnique' :: RecordToSql q ra- -> KeyConstraint Unique ra -- ^ Unique key table constraint proof object.+ -> KeyConstraint Unique ra -- ^ Unique key table constraint printer function object. -> ra -> [q] updateValuesByUnique' pr uk = unsafeUpdateValuesWithIndexes pr (indexes uk) --- | Convert like 'updateValuesByUnique'' using inferred 'RecordToSql' proof object.+-- | Convert like 'updateValuesByUnique'' using implicit 'RecordToSql' printer function object. updateValuesByUnique :: ToSql q ra- => KeyConstraint Unique ra -- ^ Unique key table constraint proof object.+ => KeyConstraint Unique ra -- ^ Unique key table constraint printer function object. -> ra -> [q] updateValuesByUnique = updateValuesByUnique' recordToSql --- | Convert like 'updateValuesByUnique'' using inferred 'RecordToSql' and 'ColumnConstraint' proof objects.+-- | Convert like 'updateValuesByUnique'' using implicit 'RecordToSql' and 'ColumnConstraint'. updateValuesByPrimary :: (HasKeyConstraint Primary ra, ToSql q ra) => ra -> [q] updateValuesByPrimary = updateValuesByUnique (unique keyConstraint)