packages feed

drifter (empty) → 0.1.0.0

raw patch · 5 files changed

+194/−0 lines, 5 filesdep +basedep +bytestringdep +postgresql-simplesetup-changed

Dependencies added: base, bytestring, postgresql-simple, text, time

Files

+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2015 AndrewRademacher++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ drifter.cabal view
@@ -0,0 +1,31 @@++name:                drifter+version:             0.1.0.0+synopsis:            Simple schema management for arbitrary databases.+description:         Simple support for migrating database schemas, which allows+                     haskell functions to be run as a part of the migration.+license:             MIT+license-file:        LICENSE+author:              AndrewRademacher+maintainer:          andrewrademacher@gmail.com+category:            Database+build-type:          Simple+cabal-version:       >=1.10+homepage:            https://github.com/AndrewRademacher/drifter++source-repository head+    type:     git+    location: git@github.com:AndrewRademacher/drifter.git++library+    hs-source-dirs:     src+    default-language:   Haskell2010++    build-depends:      base                >=4.7   && <4.8+                    ,   postgresql-simple   >=0.4   && <0.5+                    ,   bytestring          >=0.10  && <0.11+                    ,   time                >=1.4   && <1.5+                    ,   text                >=1.2   && <1.3++    exposed-modules:    Drifter.PostgreSQL+                    ,   Drifter.Types
+ src/Drifter/PostgreSQL.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE QuasiQuotes                #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeFamilies               #-}++module Drifter.PostgreSQL+    ( Postgres+    , Method (..)+    , DBConnection (..)+    ) where++import           Control.Applicative+import           Control.Exception+import           Data.ByteString                      (ByteString)+import           Data.Int+import           Data.Time+import           Database.PostgreSQL.Simple+import           Database.PostgreSQL.Simple.FromField+import           Database.PostgreSQL.Simple.FromRow+import           Database.PostgreSQL.Simple.SqlQQ+import           Database.PostgreSQL.Simple.Types++import           Drifter.Types++data Postgres++data instance Method Postgres = Script ByteString+                              | Function (Connection -> IO (Either String ()))++data instance DBConnection Postgres = DBConnection Connection++newtype ChangeId = ChangeId { unChangeId :: Int } deriving (Eq, Ord, Show, FromField)++data ChangeHistory = ChangeHistory+        { histId          :: ChangeId+        , histName        :: Name+        , histDescription :: Maybe Description+        , histTime        :: UTCTime+        } deriving (Show)++instance Eq ChangeHistory where+    a == b = (histName a) == (histName b)++instance Ord ChangeHistory where+    compare a b = compare (histId a) (histId b)++instance FromRow ChangeHistory where+    fromRow = ChangeHistory <$> field <*> field <*> field <*> field++instance Drifter Postgres where+    migrate (DBConnection conn) changes = do+        _ <- bootstrap conn+        hist :: [(ChangeHistory)] <- query_ conn "SELECT id, name, description, time FROM drifter.changelog ORDER BY id"+        start <- findNext hist changes+        migratePostgres conn start++findNext :: [ChangeHistory] -> [Change Postgres] -> IO [Change Postgres]+findNext    []     cs                                   = return cs+findNext (h:hs) (c:cs) | (histName h) == (changeName c) = do putStrLn $ "Skipping: " ++ show (changeName c)+                                                             findNext hs cs+                       | otherwise                      = return (c:cs)+findNext     _      _                                   = do putStrLn "Change Set Exhausted"+                                                             return []++bootstrap :: Connection -> IO Int64+bootstrap conn = execute_ conn [sql|+BEGIN;++CREATE SCHEMA IF NOT EXISTS drifter;++CREATE TABLE IF NOT EXISTS drifter.changelog (+    id              serial                      NOT NULL,+    name            text                        NOT NULL,+    description     text,+    time            timestamp with time zone    NOT NULL,++    PRIMARY KEY (id),+    UNIQUE (name)+);++COMMIT;+|]++migratePostgres :: Connection -> [Change Postgres] -> IO (Either String ())+migratePostgres    _                [] = return $ Right ()+migratePostgres conn (Change n d m:cs) = do+        res <- handleChange conn m+        now <- getCurrentTime++        case res of+            Left  er -> return $ Left er+            Right  _ -> do _ <- logChange conn n d now+                           putStrLn $ "Committed: " ++ show n+                           migratePostgres conn cs++errorHandlers :: [Handler (Either String b)]+errorHandlers = [ Handler (\(ex::SqlError) -> return $ Left $ show ex)+                , Handler (\(ex::FormatError) -> return $ Left $ show ex)+                , Handler (\(ex::ResultError) -> return $ Left $ show ex)+                , Handler (\(ex::QueryError) -> return $ Left $ show ex)+                ]++handleChange :: Connection -> Method Postgres -> IO (Either String ())+handleChange conn (Script q) = (execute_ conn (Query q) >>= \_ -> return $ Right ())+    `catches` errorHandlers+handleChange conn (Function f) = (f conn >>= \_ -> return $ Right ())+    `catches` errorHandlers++logChange :: Connection -> Name -> Maybe Description -> UTCTime -> IO (Either String ())+logChange conn n d now = (execute conn insLog (n, d, now) >>= \_ -> return $ Right ())+    `catches` errorHandlers+    where insLog = [sql|+BEGIN;++INSERT INTO drifter.changelog (name, description, time)+    VALUES (?, ?, ?);++COMMIT;+|]
+ src/Drifter/Types.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE TypeFamilies #-}++module Drifter.Types where++import           Data.Text++type Name        = Text+type Description = Text++data Change a = Change+        { changeName        :: Name+        , changeDescription :: Maybe Description+        , changeMethod      :: Method a+        }++data family Method a++data family DBConnection a++class Drifter a where+    migrate :: DBConnection a -> [Change a] -> IO (Either String ())