mirror of
https://code.sup39.dev/repos/Wqawg
synced 2025-03-20 04:46:22 +09:00
169 lines
5.8 KiB
Haskell
169 lines
5.8 KiB
Haskell
{- This file is part of Vervis.
|
|
-
|
|
- Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
|
|
-
|
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
|
-
|
|
- The author(s) have dedicated all copyright and related and neighboring
|
|
- rights to this software to the public domain worldwide. This software is
|
|
- distributed without any warranty.
|
|
-
|
|
- You should have received a copy of the CC0 Public Domain Dedication along
|
|
- with this software. If not, see
|
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
|
-}
|
|
|
|
-- | SQL schema backend specifying SQL statements for manipulating a SQL
|
|
-- database's table schema.
|
|
module Database.Persist.Schema.Sql
|
|
( TableName (..)
|
|
, ColumnName (..)
|
|
, ConstraintName (..)
|
|
, Column (..)
|
|
, SchemaBackend (..)
|
|
)
|
|
where
|
|
|
|
import Prelude
|
|
|
|
import Control.Monad (when)
|
|
import Control.Monad.IO.Class (MonadIO)
|
|
import Control.Monad.Trans.Class (lift)
|
|
import Control.Monad.Trans.Reader (ask)
|
|
import Data.Char (isUpper, toLower)
|
|
import Data.Foldable (traverse_)
|
|
import Data.Maybe (isJust)
|
|
import Data.Text (Text)
|
|
import Database.Persist.Sql (Sql, SqlBackend, rawExecute)
|
|
import Database.Persist.Types (SqlType)
|
|
|
|
import qualified Data.Text as T
|
|
|
|
import Database.Persist.Schema
|
|
|
|
newtype TableName = TableName { unTableName :: Text }
|
|
|
|
newtype ColumnName = ColumnName { unColumnName :: Text }
|
|
|
|
newtype ConstraintName = ConstraintName { unConstraintName :: Text }
|
|
|
|
data Column = Column
|
|
{ colName :: ColumnName
|
|
, colType :: SqlType
|
|
, colNull :: MaybeNull
|
|
}
|
|
|
|
exec :: MonadIO m => Sql -> SchemaT SqlBackend m ()
|
|
exec t = lift $ rawExecute t []
|
|
|
|
camelWords :: Text -> [Text]
|
|
camelWords ident =
|
|
let low = toLower
|
|
slow = T.singleton . toLower
|
|
go c t l =
|
|
let (x, y) = T.break isUpper t
|
|
in case (T.null x, T.uncons y) of
|
|
(True, Nothing) -> slow c : l
|
|
(True, Just (d, r)) -> go d r $ slow c : l
|
|
(False, Nothing) -> (low c `T.cons` x) : l
|
|
(False, Just (d, r)) -> go d r $ (low c `T.cons` x) : l
|
|
(a, b) = T.break isUpper ident
|
|
in reverse $ case (T.null a, T.uncons b) of
|
|
(True, Nothing) -> []
|
|
(True, Just (c, r)) -> go c r []
|
|
(False, Nothing) -> [a]
|
|
(False, Just (c, r)) -> go c r [a]
|
|
|
|
dbname :: Text -> Text
|
|
dbname = T.intercalate (T.singleton '_') . camelWords
|
|
|
|
entity2table :: EntityName -> TableName
|
|
entity2table (EntityName t) = TableName $ dbname t
|
|
|
|
field2column :: FieldName -> ColumnName
|
|
field2column (FieldName t) = ColumnName $ dbname t
|
|
|
|
unique2constraint :: UniqueName -> ConstraintName
|
|
unique2constraint (UniqueName t) = ConstraintName $ dbname t
|
|
|
|
type2sql :: SchemaBackend SqlBackend -> FieldType -> SqlType
|
|
type2sql _ (FTPrim t) = t
|
|
type2sql ssb FTRef = ssbRefType ssb
|
|
|
|
mkcolumn :: SchemaBackend SqlBackend -> Field -> Column
|
|
mkcolumn ssb (Field name typ mnull) = Column
|
|
{ colName = field2column name
|
|
, colType = type2sql ssb typ
|
|
, colNull = mnull
|
|
}
|
|
|
|
instance PersistSchema SqlBackend where
|
|
data SchemaBackend SqlBackend = SqlSchemaBackend
|
|
{ ssbRefType :: SqlType
|
|
, ssbCreateTable :: TableName -> [Column] -> Sql
|
|
, ssbRenameTable :: TableName -> TableName -> Sql
|
|
, ssbDropTable :: TableName -> Sql
|
|
, ssbAddColumn :: TableName -> Column -> Maybe Text -> Sql
|
|
, ssbRenameColumn :: TableName -> ColumnName -> ColumnName -> Sql
|
|
, ssbRetypeColumn :: TableName -> ColumnName -> SqlType -> Sql
|
|
, ssbRenullColumn :: TableName -> ColumnName -> MaybeNull -> Sql
|
|
, ssbUnnullColumn :: TableName -> ColumnName -> Text -> Sql
|
|
, ssbDefColumn :: TableName -> ColumnName -> Text -> Sql
|
|
, ssbUndefColumn :: TableName -> ColumnName -> Sql
|
|
, ssbDropColumn :: TableName -> ColumnName -> Sql
|
|
, ssbAddUnique
|
|
:: TableName -> ConstraintName -> [ColumnName] -> Sql
|
|
, ssbAddForeignKey
|
|
:: TableName -> ConstraintName -> ColumnName -> TableName -> Sql
|
|
, ssbRenameConstraint
|
|
:: TableName -> ConstraintName -> ConstraintName -> Sql
|
|
, ssbDropConstraint :: TableName -> ConstraintName -> Sql
|
|
}
|
|
addEntity (Entity name fields uniques) = do
|
|
ssb <- ask
|
|
exec $
|
|
ssbCreateTable ssb (entity2table name) (map (mkcolumn ssb) fields)
|
|
traverse_ (addUnique name) uniques
|
|
removeEntity name = do
|
|
ssb <- ask
|
|
exec $ ssbDropTable ssb $ entity2table name
|
|
addField ent (Field name typ mnull) mdef = do
|
|
ssb <- ask
|
|
exec $
|
|
ssbAddColumn ssb
|
|
(entity2table ent)
|
|
(Column (field2column name) (type2sql ssb typ) mnull)
|
|
mdef
|
|
when (isJust mdef) $
|
|
exec $
|
|
ssbUndefColumn ssb (entity2table ent) (field2column name)
|
|
renameField entity old new = do
|
|
ssb <- ask
|
|
exec $
|
|
ssbRenameColumn ssb
|
|
(entity2table entity)
|
|
(field2column old)
|
|
(field2column new)
|
|
removeField entity field = do
|
|
ssb <- ask
|
|
exec $ ssbDropColumn ssb (entity2table entity) (field2column field)
|
|
addUnique entity (Unique name fields) = do
|
|
ssb <- ask
|
|
exec $
|
|
ssbAddUnique ssb
|
|
(entity2table entity)
|
|
(unique2constraint name)
|
|
(map field2column fields)
|
|
renameUnique entity old new = do
|
|
ssb <- ask
|
|
exec $
|
|
ssbRenameConstraint ssb
|
|
(entity2table entity)
|
|
(unique2constraint old)
|
|
(unique2constraint new)
|
|
removeUnique entity name = do
|
|
ssb <- ask
|
|
exec $
|
|
ssbDropConstraint ssb
|
|
(entity2table entity)
|
|
(unique2constraint name)
|