relational-schemas-0.1.5.0: src/Database/Relational/Schema/Oracle.hs
{-# LANGUAGE TemplateHaskell #-}
module Database.Relational.Schema.Oracle
( 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.OracleDataDictionary.ConsColumns (dbaConsColumns)
import qualified Database.Relational.Schema.OracleDataDictionary.ConsColumns as ConsCols
import Database.Relational.Schema.OracleDataDictionary.Constraints (dbaConstraints)
import qualified Database.Relational.Schema.OracleDataDictionary.Constraints as Cons
import Database.Relational.Schema.OracleDataDictionary.TabColumns (DbaTabColumns, dbaTabColumns)
import qualified Database.Relational.Schema.OracleDataDictionary.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