{-# LANGUAGE TemplateHaskell #-} module Database.Relational.Schema.Oracle ( module Database.Relational.Schema.Oracle.Config , normalizeColumn, notNull, getType , columnsQuerySQL, primaryKeyQuerySQL ) where import Control.Applicative ((<|>)) import Data.ByteString (ByteString) import Data.Char (toLower) import Data.Map (Map) import qualified Data.Map as Map import Data.Time (LocalTime) import Language.Haskell.TH (TypeQ) import Database.Relational import Database.Relational.Schema.Oracle.Config import Database.Relational.Schema.Oracle.ConsColumns (dbaConsColumns) import qualified Database.Relational.Schema.Oracle.ConsColumns as ConsCols import Database.Relational.Schema.Oracle.Constraints (dbaConstraints) import qualified Database.Relational.Schema.Oracle.Constraints as Cons import Database.Relational.Schema.Oracle.TabColumns (DbaTabColumns, dbaTabColumns) import qualified Database.Relational.Schema.Oracle.TabColumns as Cols -- NOT COMPLETED -- (ref: http://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm) -- | Mapping between type in Oracle DB and Haskell type. mapFromSqlDefault :: Map String TypeQ mapFromSqlDefault = Map.fromList [ ("CHAR", [t|String|]) , ("VARCHAR", [t|String|]) -- deprecated , ("VARCHAR2", [t|String|]) , ("NCHAR", [t|String|]) , ("NVARCHAR2", [t|String|]) -- , ("NUMBER", [t|Integer or Double|]) see 'getType' , ("BINARY_FLOAT", [t|Double|]) -- Float don't work , ("BINARY_DOUBLE", [t|Double|]) , ("DATE", [t|LocalTime|]) , ("BLOB", [t|ByteString|]) , ("CLOB", [t|String|]) , ("NCLOB", [t|String|]) , ("LONG RAW", [t|ByteString|]) -- deprecated , ("RAW", [t|ByteString|]) , ("ROWID", [t|String|]) , ("UROWID", [t|String|]) ] -- | Normalize column name string to query Oracle DB data dictionary. normalizeColumn :: String -> String normalizeColumn = map toLower -- | Not-null attribute information of column. notNull :: DbaTabColumns -> Bool notNull = (== Just "N") . Cols.nullable -- | Get column normalized name and column Haskell type. getType :: Map String TypeQ -- ^ Type mapping specified by user -> DbaTabColumns -- ^ Column info in data dictionary -> Maybe (String, TypeQ) -- ^ Result normalized name and mapped Haskell type getType mapFromSql cols = do ky <- Cols.dataType cols typ <- if ky == "NUMBER" then return $ numberType $ Cols.dataScale cols else Map.lookup ky mapFromSql <|> Map.lookup ky mapFromSqlDefault return (normalizeColumn $ Cols.columnName cols, mayNull typ) where mayNull typ | notNull cols = typ | otherwise = [t|Maybe $(typ)|] numberType Nothing = [t|Integer|] numberType (Just n) | n <= 0 = [t|Integer|] | otherwise = [t|Double|] -- | 'Relation' to query 'DbaTabColumns' from owner name and table name. columnsRelationFromTable :: Relation (String, String) DbaTabColumns columnsRelationFromTable = relation' $ do cols <- query dbaTabColumns (owner, ()) <- placeholder $ \owner -> wheres $ cols ! Cols.owner' .=. owner (name, ()) <- placeholder $ \name -> wheres $ cols ! Cols.tableName' .=. name asc $ cols ! Cols.columnId' return (owner >< name, cols) -- | Phantom typed 'Query' to get 'DbaTabColumns' from owner name and table name. columnsQuerySQL :: Query (String, String) DbaTabColumns columnsQuerySQL = relationalQuery columnsRelationFromTable -- | 'Relation' to query primary key name from owner name and table name. primaryKeyRelation :: Relation (String, String) (Maybe String) primaryKeyRelation = relation' $ do cons <- query dbaConstraints cols <- query dbaTabColumns consCols <- query dbaConsColumns wheres $ cons ! Cons.owner' .=. just (cols ! Cols.owner') wheres $ cons ! Cons.tableName' .=. cols ! Cols.tableName' wheres $ consCols ! ConsCols.columnName' .=. just (cols ! Cols.columnName') wheres $ cons ! Cons.constraintName' .=. consCols ! ConsCols.constraintName' wheres $ cols ! Cols.nullable' .=. just (value "N") wheres $ cons ! Cons.constraintType' .=. just (value "P") (owner, ()) <- placeholder $ \owner -> wheres $ cons ! Cons.owner' .=. just owner (name, ()) <- placeholder $ \name -> wheres $ cons ! Cons.tableName' .=. name asc $ consCols ! ConsCols.position' return (owner >< name, consCols ! ConsCols.columnName') -- | Phantom typed 'Query' to get primary key name from owner name and table name. primaryKeyQuerySQL :: Query (String, String) (Maybe String) primaryKeyQuerySQL = relationalQuery primaryKeyRelation