Update to get composite primary key from PostgreSQL system catalog.

This commit is contained in:
Kei Hibino 2013-06-28 18:03:19 +09:00
parent 508981afb4
commit fccffe406a
2 changed files with 61 additions and 18 deletions

View File

@ -17,7 +17,8 @@ module Database.Relational.Schema.PostgreSQL (
normalizeColumn, notNull, getType, normalizeColumn, notNull, getType,
columnQuerySQL, primaryKeyQuerySQL columnQuerySQL,
primaryKeyLengthQuerySQL, primaryKeyQuerySQL
) where ) where
import Prelude hiding (or) import Prelude hiding (or)
@ -26,6 +27,7 @@ import Language.Haskell.TH (TypeQ)
import Data.Int (Int16, Int32, Int64) import Data.Int (Int16, Int32, Int64)
import Data.Char (toLower) import Data.Char (toLower)
import Data.List (foldl1')
import Data.Map (Map, fromList) import Data.Map (Map, fromList)
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Time import Data.Time
@ -34,15 +36,15 @@ import Data.Time
import Database.Relational.Query.Type (fromRelation) import Database.Relational.Query.Type (fromRelation)
import Database.Relational.Query import Database.Relational.Query
(Query, Relation, query, query', relation', expr, (Query, Relation, query, query', relation', relation, union,
wheres, (.=.), (.>.), in', values, (!), just, wheres, (.=.), (.>.), in', values, (!), expr, fst', snd',
placeholder, asc, value, unsafeProjectSql, (><)) placeholder, asc, value, unsafeProjectSql, (><))
import Database.Relational.Schema.PgCatalog.PgNamespace (pgNamespace) import Database.Relational.Schema.PgCatalog.PgNamespace (pgNamespace)
import qualified Database.Relational.Schema.PgCatalog.PgNamespace as Namespace import qualified Database.Relational.Schema.PgCatalog.PgNamespace as Namespace
import Database.Relational.Schema.PgCatalog.PgClass (pgClass) import Database.Relational.Schema.PgCatalog.PgClass (pgClass)
import qualified Database.Relational.Schema.PgCatalog.PgClass as Class import qualified Database.Relational.Schema.PgCatalog.PgClass as Class
import Database.Relational.Schema.PgCatalog.PgConstraint (pgConstraint) import Database.Relational.Schema.PgCatalog.PgConstraint (PgConstraint, pgConstraint)
import qualified Database.Relational.Schema.PgCatalog.PgConstraint as Constraint import qualified Database.Relational.Schema.PgCatalog.PgConstraint as Constraint
import Database.Relational.Schema.PgCatalog.PgAttribute (PgAttribute, pgAttribute) import Database.Relational.Schema.PgCatalog.PgAttribute (PgAttribute, pgAttribute)
@ -159,20 +161,52 @@ columnRelation = relation' $ do
columnQuerySQL :: Query (String, String) Column columnQuerySQL :: Query (String, String) Column
columnQuerySQL = fromRelation columnRelation columnQuerySQL = fromRelation columnRelation
-- | 'Relation' to query primary key length from schema name and table name.
primaryKeyLengthRelation :: Relation (String, String) Int32
primaryKeyLengthRelation = relation' $ do
(ph, reloid) <- query' relOidRelation
con <- query pgConstraint
wheres $ con ! Constraint.conrelid' .=. expr reloid
wheres $ con ! Constraint.contype' .=. value 'p' -- 'p': primary key constraint type
return (ph, unsafeProjectSql "array_length (conkey, 1)")
-- | Phantom typed 'Query' to get primary key length from schema name and table name.
primaryKeyLengthQuerySQL :: Query (String, String) Int32
primaryKeyLengthQuerySQL = fromRelation primaryKeyLengthRelation
-- | One column which is nth column of composite primary key.
constraintColRelation :: Int32 -> Relation () (PgConstraint, (Int16, Int32))
constraintColRelation i = relation $ do
con <- query pgConstraint
return $ con >< (unsafeProjectSql ("conkey[" ++ show i ++ "]") >< value i)
-- | Make composite primary key relation from primary key length.
constraintColExpandRelation :: Int32 -> Relation () (PgConstraint, (Int16, Int32))
constraintColExpandRelation n =
foldl1' union [constraintColRelation i | i <- [1..n] ]
-- | 'Relation' to query primary key name from schema name and table name. -- | 'Relation' to query primary key name from schema name and table name.
primaryKeyRelation :: Relation (String, String) String primaryKeyRelation :: Int32 -> Relation (String, String) String
primaryKeyRelation = relation' $ do primaryKeyRelation n = relation' $ do
(ph, att) <- query' attributeRelation (ph, att) <- query' attributeRelation
con <- query pgConstraint conEx <- query (constraintColExpandRelation n)
let con = conEx ! fst'
col' = conEx ! snd'
keyIx = col' ! fst'
keyN = col' ! snd'
wheres $ con ! Constraint.conrelid' .=. att ! Attr.attrelid' wheres $ con ! Constraint.conrelid' .=. att ! Attr.attrelid'
wheres $ unsafeProjectSql "conkey[1]" .=. att ! Attr.attnum' wheres $ keyIx .=. att ! Attr.attnum'
wheres $ just (att ! Attr.attnotnull')
wheres $ con ! Constraint.contype' .=. value 'p' -- 'p': primary key constraint type wheres $ con ! Constraint.contype' .=. value 'p' -- 'p': primary key constraint type
wheres $ unsafeProjectSql "array_length (conkey, 1)" .=. value (1 :: Int32)
asc $ keyN
return (ph, att ! Attr.attname') return (ph, att ! Attr.attname')
-- | Phantom typed 'Query' to get primary key name from schema name and table name. -- | Phantom typed 'Query' to get primary key name from schema name and table name.
primaryKeyQuerySQL :: Query (String, String) String primaryKeyQuerySQL :: Int32 -> Query (String, String) String
primaryKeyQuerySQL = fromRelation primaryKeyRelation primaryKeyQuerySQL = fromRelation . primaryKeyRelation

View File

@ -32,7 +32,8 @@ import Database.HDBC.Record.Query (runQuery')
import Database.HDBC.Record.Persistable () import Database.HDBC.Record.Persistable ()
import Database.Relational.Schema.PostgreSQL import Database.Relational.Schema.PostgreSQL
(normalizeColumn, notNull, getType, columnQuerySQL, primaryKeyQuerySQL) (normalizeColumn, notNull, getType, columnQuerySQL,
primaryKeyLengthQuerySQL, primaryKeyQuerySQL)
import Database.Relational.Schema.PgCatalog.PgAttribute (PgAttribute(PgAttribute), tableOfPgAttribute) import Database.Relational.Schema.PgCatalog.PgAttribute (PgAttribute(PgAttribute), tableOfPgAttribute)
import Database.Relational.Schema.PgCatalog.PgType (PgType(..), tableOfPgType) import Database.Relational.Schema.PgCatalog.PgType (PgType(..), tableOfPgType)
import qualified Database.Relational.Schema.PgCatalog.PgType as Type import qualified Database.Relational.Schema.PgCatalog.PgType as Type
@ -64,11 +65,19 @@ getPrimaryKey' :: IConnection conn
getPrimaryKey' conn scm' tbl' = do getPrimaryKey' conn scm' tbl' = do
let scm = map toLower scm' let scm = map toLower scm'
tbl = map toLower tbl' tbl = map toLower tbl'
primCols <- runQuery' conn (scm, tbl) primaryKeyQuerySQL mayKeyLen <- runQuery' conn (scm, tbl) primaryKeyLengthQuerySQL
let primaryKeyCols = normalizeColumn `fmap` primCols case mayKeyLen of
putLog $ "getPrimaryKey: primary key = " ++ show primaryKeyCols [] -> do
putLog $ "getPrimaryKey: Primary key not found."
return primaryKeyCols return []
[keyLen] -> do
primCols <- runQuery' conn (scm, tbl) (primaryKeyQuerySQL keyLen)
let primaryKeyCols = normalizeColumn `fmap` primCols
putLog $ "getPrimaryKey: primary key = " ++ show primaryKeyCols
return primaryKeyCols
_:_:_ -> do
putLog $ "getPrimaryKey: Fail to detect primary key. Something wrong."
return []
getFields' :: IConnection conn getFields' :: IConnection conn
=> TypeMap => TypeMap