--- This module contains basic datatypes and operations to represent --- a relational data model in a type-safe manner. This representation is --- used by the library `Database.CDBI.ER` to provide type safety --- when working with relational databases. --- The tool `erd2cdbi` generates from an entity-relationship model --- a Curry program that represents all entities and relationships --- by the use of this module. --- --- @author Mike Tallarek, changes by Julia Krone --- ---------------------------------------------------------------------------- {-# OPTIONS_CYMAKE -Wno-incomplete-patterns #-} module Database.CDBI.Description where import Time import Database.CDBI.Connection (SQLType, SQLValue(..)) -- ----------------------------------------------------------------------------- -- Datatypes for describing entities -- ----------------------------------------------------------------------------- --- The datatype EntityDescription is a description of a database entity --- type including the name, the types the entity consists of, a function --- transforming an instance of this entity to a list of SQLValues, a --- second function doing the same but converting the key value always to --- SQLNull to ensure that keys are auto incrementing and a --- function transforming a list of SQLValues to an instance of this entity data EntityDescription a = ED String [SQLType] (a -> [SQLValue]) (a -> [SQLValue]) --for insertion ([SQLValue] -> a) --- Entity-types can be combined (For Example Student and Lecture could be --- combined to Data StuLec = StuLec Student Lecture). If a description for --- this new type is written CDBI can look up that type in the database --- The description is a list of Tuples consisting of a String (The name of --- the entity type that will be combined), a "rename-number" n which will --- rename the table to "table as ntable" and a list of SQLTypes (The types --- that make up that entity type). Furthermore there has to be a function --- that transform a list of SQLValues into this combined type, and two --- functions that transform the combined type into a list of SQLValues, the --- first one for updates, the second one for insertion. --- The list of sqlvalues needs to match what is returned by the database. data CombinedDescription a = CD [(Table, Int, [SQLType])] ([SQLValue] -> a) (a -> [[SQLValue]]) (a -> [[SQLValue]]) -- for insertion --- A type representing tablenames type Table = String --- A datatype representing column names. --- The first string is the simple name of the column (for example the --- column Name of the row Student). The second string is the name of the --- column combined with the name of the row (for example Student.Name). --- These names should always be in quotes (for example "Student"."Name") --- so no errors emerge (the name "Group" for --- example would result in errors if not in quotes). --- Has a phantom-type for the value the column represents. data Column _ = Column String String --- Datatype representing columns for selection. --- This datatype has to be distinguished from type Column which is just for --- definition of conditions. --- The type definition consists of the complete name (including tablename), --- the SQLType of the column --- and two functions for the mapping from SQLValue into the resulttype and --- the other way around data ColumnDescription a = ColDesc String SQLType (a -> SQLValue) (SQLValue -> a) --- A constructor for CombinedDescription. --- @param ed1 - Description of the first Entity-Type that is to be combined --- @param rename1 - The "rename-number" for ed1. If it is zero ed1 will --- not be renamed in queries, otherwise is will be renamed as --- follows: "table as ntable" --- @param ed2 - Description of the second Entity-Type that is to be combined --- @param rename2 - Same as rename1 for ed2 --- @param f - A function that describes how the combined entity is built. --- Takes two entities that make up the combined entity as parameters --- and combines those into the combined entity. combineDescriptions :: EntityDescription a -> Int -> EntityDescription b -> Int -> (a -> b -> c) -> (c -> (a, b)) -> CombinedDescription c combineDescriptions ed1 rename1 ed2 rename2 f1 f2 = CD [(getTable ed1, rename1, getTypes ed1), (getTable ed2, rename2, getTypes ed2)] createFunction1 createFunction2 createFunction3 where createFunction1 xs = f1 ((getToEntity ed1) (take lengthEd1 xs)) ((getToEntity ed2) (drop lengthEd1 xs)) where lengthEd1 = length (getTypes ed1) createFunction2 combEnt = let (ent1, ent2) = f2 combEnt in ((getToValues ed1) ent1) : [(getToValues ed2) ent2] createFunction3 combEnt = let (ent1, ent2) = f2 combEnt in ((getToInsertValues ed1) ent1) : [(getToInsertValues ed2) ent2] --- Adds another ED to an already existing CD. --- @param ed1 - The ED to be added --- @param rename - The "rename-number" --- @param f1 - A function that describes how the combined entity is built. --- Takes the entity that should be added and the combined entity as parameter --- and combines those into a new version of the combined entity. --- @param cd - The already existing CD addDescription :: EntityDescription a -> Int -> (a -> b -> b) -> (b -> a) -> CombinedDescription b -> CombinedDescription b addDescription ed1 rename f1 f2 (CD xs f1' f2' f3') = CD ((getTable ed1, rename, getTypes ed1) : xs) createFunction1 createFunction2 createFunction3 where createFunction1 ys = f1 ((getToEntity ed1) (take lengthEd1 ys)) (f1' (drop lengthEd1 ys)) where lengthEd1 = length (getTypes ed1) createFunction2 combEnt = [(getToValues ed1) (f2 combEnt)] ++ (f2' combEnt) createFunction3 combEnt = [(getToInsertValues ed1) (f2 combEnt)] ++ (f3' combEnt) -- ----------------------------------------------------------------------------- -- Auxiliary Functions -- ----------------------------------------------------------------------------- getTable :: EntityDescription a -> String getTable (ED s _ _ _ _) = s getTypes :: EntityDescription a -> [SQLType] getTypes (ED _ t _ _ _) = t getToValues :: EntityDescription a -> (a -> [SQLValue]) getToValues (ED _ _ f _ _) = f getToInsertValues :: EntityDescription a -> (a -> [SQLValue]) getToInsertValues (ED _ _ _ f _) = f getToEntity :: EntityDescription a -> ([SQLValue] -> a) getToEntity (ED _ _ _ _ f) = f getColumnSimple :: Column a -> String getColumnSimple (Column s _ ) = s getColumnFull :: Column a -> String getColumnFull (Column _ s ) = s getColumnName :: ColumnDescription a -> String getColumnName (ColDesc s _ _ _) = s getColumnTableName :: ColumnDescription a -> String getColumnTableName (ColDesc s _ _ _) = s getColumnTyp :: ColumnDescription a -> SQLType getColumnTyp (ColDesc _ t _ _) = t getColumnValueBuilder :: ColumnDescription a -> (a -> SQLValue) getColumnValueBuilder (ColDesc _ _ f _) = f getColumnValueSelector :: ColumnDescription a -> (SQLValue -> a) getColumnValueSelector (ColDesc _ _ _ f) = f -- Conversion functions from Curry values to SQL values. toValueOrNull :: (a -> SQLValue) -> Maybe a -> SQLValue toValueOrNull _ Nothing = SQLNull toValueOrNull f (Just v) = f v sqlKeyOrNull :: (key -> Int) -> Maybe key -> SQLValue sqlKeyOrNull _ Nothing = SQLNull sqlKeyOrNull key2int (Just k) = SQLInt (key2int k) sqlIntOrNull :: (Maybe Int) -> SQLValue sqlIntOrNull Nothing = SQLNull sqlIntOrNull (Just a) = SQLInt a sqlFloatOrNull :: (Maybe Float) -> SQLValue sqlFloatOrNull Nothing = SQLNull sqlFloatOrNull (Just a) = SQLFloat a sqlCharOrNull :: (Maybe Char) -> SQLValue sqlCharOrNull Nothing = SQLNull sqlCharOrNull (Just a) = SQLChar a sqlStringOrNull :: (Maybe String) -> SQLValue sqlStringOrNull Nothing = SQLNull sqlStringOrNull (Just a) = SQLString a sqlString :: String -> SQLValue sqlString a = SQLString a sqlBoolOrNull :: (Maybe Bool) -> SQLValue sqlBoolOrNull Nothing = SQLNull sqlBoolOrNull (Just a) = SQLBool a sqlDateOrNull :: (Maybe ClockTime) -> SQLValue sqlDateOrNull Nothing = SQLNull sqlDateOrNull (Just a) = SQLDate a -- Conversion functions from SQL values to Curry values. keyOrNothing :: (Int -> key) -> SQLValue -> Maybe key keyOrNothing _ SQLNull = Nothing keyOrNothing keycon (SQLInt k) = Just (keycon k) intOrNothing :: SQLValue -> (Maybe Int) intOrNothing SQLNull = Nothing intOrNothing (SQLInt a) = Just a floatOrNothing :: SQLValue -> (Maybe Float) floatOrNothing SQLNull = Nothing floatOrNothing (SQLFloat a) = Just a charOrNothing :: SQLValue -> (Maybe Char) charOrNothing SQLNull = Nothing charOrNothing (SQLChar a) = Just a stringOrNothing :: SQLValue -> (Maybe String) stringOrNothing SQLNull = Nothing stringOrNothing (SQLString a) = Just a fromStringOrNull :: SQLValue -> String fromStringOrNull SQLNull = "" fromStringOrNull (SQLString a) = a boolOrNothing :: SQLValue -> (Maybe Bool) boolOrNothing SQLNull = Nothing boolOrNothing (SQLBool a) = Just a dateOrNothing :: SQLValue -> (Maybe ClockTime) dateOrNothing SQLNull = Nothing dateOrNothing (SQLDate a) = Just a