module Database.PostgreSQL.PQTypes.Model.Index (
TableIndex(..)
, IndexMethod(..)
, tblIndex
, indexOnColumn
, indexOnColumns
, indexOnColumnWithMethod
, indexOnColumnsWithMethod
, uniqueIndexOnColumn
, uniqueIndexOnColumnWithCondition
, uniqueIndexOnColumns
, indexName
, sqlCreateIndex
, sqlCreateIndexSequentially
, sqlCreateIndexConcurrently
, sqlDropIndex
) where
import Crypto.Hash.RIPEMD160
import Data.ByteString.Base16
import Data.Char
import Data.Monoid
import Data.Monoid.Utils
import Database.PostgreSQL.PQTypes
import Prelude
import qualified Data.ByteString.Char8 as BS
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
data TableIndex = TableIndex {
idxColumns :: [RawSQL ()]
, idxMethod :: IndexMethod
, idxUnique :: Bool
, idxValid :: Bool
, idxWhere :: Maybe (RawSQL ())
} deriving (Eq, Ord, Show)
data IndexMethod =
BTree
| GIN
deriving (Eq, Ord)
instance Show IndexMethod where
show BTree = "btree"
show GIN = "gin"
instance Read IndexMethod where
readsPrec _ (map toLower -> "btree") = [(BTree,"")]
readsPrec _ (map toLower -> "gin") = [(GIN,"")]
readsPrec _ _ = []
tblIndex :: TableIndex
tblIndex = TableIndex {
idxColumns = []
, idxMethod = BTree
, idxUnique = False
, idxValid = True
, idxWhere = Nothing
}
indexOnColumn :: RawSQL () -> TableIndex
indexOnColumn column = tblIndex { idxColumns = [column] }
indexOnColumnWithMethod :: RawSQL () -> IndexMethod -> TableIndex
indexOnColumnWithMethod column method =
tblIndex { idxColumns = [column]
, idxMethod = method }
indexOnColumns :: [RawSQL ()] -> TableIndex
indexOnColumns columns = tblIndex { idxColumns = columns }
indexOnColumnsWithMethod :: [RawSQL ()] -> IndexMethod -> TableIndex
indexOnColumnsWithMethod columns method =
tblIndex { idxColumns = columns
, idxMethod = method }
uniqueIndexOnColumn :: RawSQL () -> TableIndex
uniqueIndexOnColumn column = TableIndex {
idxColumns = [column]
, idxMethod = BTree
, idxUnique = True
, idxValid = True
, idxWhere = Nothing
}
uniqueIndexOnColumns :: [RawSQL ()] -> TableIndex
uniqueIndexOnColumns columns = TableIndex {
idxColumns = columns
, idxMethod = BTree
, idxUnique = True
, idxValid = True
, idxWhere = Nothing
}
uniqueIndexOnColumnWithCondition :: RawSQL () -> RawSQL () -> TableIndex
uniqueIndexOnColumnWithCondition column whereC = TableIndex {
idxColumns = [column]
, idxMethod = BTree
, idxUnique = True
, idxValid = True
, idxWhere = Just whereC
}
indexName :: RawSQL () -> TableIndex -> RawSQL ()
indexName tname TableIndex{..} = flip rawSQL () $ T.take 63 . unRawSQL $ mconcat [
if idxUnique then "unique_idx__" else "idx__"
, tname
, "__"
, mintercalate "__" $ map (asText sanitize) idxColumns
, maybe "" (("__" <>) . hashWhere) idxWhere
]
where
asText f = flip rawSQL () . f . unRawSQL
sanitize = T.pack . foldr go [] . T.unpack
where
go c acc = if isAlphaNum c || c == '_'
then c : acc
else case acc of
('$':_) -> acc
_ -> '$' : acc
hashWhere = asText $ T.decodeUtf8 . encode . BS.take 10 . hash . T.encodeUtf8
{-# DEPRECATED sqlCreateIndex "Use sqlCreateIndexSequentially instead" #-}
sqlCreateIndex :: RawSQL () -> TableIndex -> RawSQL ()
sqlCreateIndex = sqlCreateIndex_ False
sqlCreateIndexSequentially :: RawSQL () -> TableIndex -> RawSQL ()
sqlCreateIndexSequentially = sqlCreateIndex_ False
sqlCreateIndexConcurrently :: RawSQL () -> TableIndex -> RawSQL ()
sqlCreateIndexConcurrently = sqlCreateIndex_ True
sqlCreateIndex_ :: Bool -> RawSQL () -> TableIndex -> RawSQL ()
sqlCreateIndex_ concurrently tname idx@TableIndex{..} = mconcat [
"CREATE"
, if idxUnique then " UNIQUE" else ""
, " INDEX "
, if concurrently then "CONCURRENTLY " else ""
, indexName tname idx
, " ON" <+> tname
, " USING" <+> (rawSQL (T.pack . show $ idxMethod) ()) <+> "("
, mintercalate ", " idxColumns
, ")"
, maybe "" (" WHERE" <+>) idxWhere
]
sqlDropIndex :: RawSQL () -> TableIndex -> RawSQL ()
sqlDropIndex tname idx = "DROP INDEX" <+> indexName tname idx