github.com/yuukihogo/migrate@v3.0.0+incompatible/database/driver.go (about) 1 // Package database provides the Database interface. 2 // All database drivers must implement this interface, register themselves, 3 // optionally provide a `WithInstance` function and pass the tests 4 // in package database/testing. 5 package database 6 7 import ( 8 "fmt" 9 "io" 10 nurl "net/url" 11 "sync" 12 ) 13 14 var ( 15 ErrLocked = fmt.Errorf("can't acquire lock") 16 ) 17 18 const NilVersion int = -1 19 20 var driversMu sync.RWMutex 21 var drivers = make(map[string]Driver) 22 23 // Driver is the interface every database driver must implement. 24 // 25 // How to implement a database driver? 26 // 1. Implement this interface. 27 // 2. Optionally, add a function named `WithInstance`. 28 // This function should accept an existing DB instance and a Config{} struct 29 // and return a driver instance. 30 // 3. Add a test that calls database/testing.go:Test() 31 // 4. Add own tests for Open(), WithInstance() (when provided) and Close(). 32 // All other functions are tested by tests in database/testing. 33 // Saves you some time and makes sure all database drivers behave the same way. 34 // 5. Call Register in init(). 35 // 36 // Guidelines: 37 // * Don't try to correct user input. Don't assume things. 38 // When in doubt, return an error and explain the situation to the user. 39 // * All configuration input must come from the URL string in func Open() 40 // or the Config{} struct in WithInstance. Don't os.Getenv(). 41 type Driver interface { 42 // Open returns a new driver instance configured with parameters 43 // coming from the URL string. Migrate will call this function 44 // only once per instance. 45 Open(url string) (Driver, error) 46 47 // Close closes the underlying database instance managed by the driver. 48 // Migrate will call this function only once per instance. 49 Close() error 50 51 // Lock should acquire a database lock so that only one migration process 52 // can run at a time. Migrate will call this function before Run is called. 53 // If the implementation can't provide this functionality, return nil. 54 // Return database.ErrLocked if database is already locked. 55 Lock() error 56 57 // Unlock should release the lock. Migrate will call this function after 58 // all migrations have been run. 59 Unlock() error 60 61 // Run applies a migration to the database. migration is garantueed to be not nil. 62 Run(migration io.Reader) error 63 64 // SetVersion saves version and dirty state. 65 // Migrate will call this function before and after each call to Run. 66 // version must be >= -1. -1 means NilVersion. 67 SetVersion(version int, dirty bool) error 68 69 // Version returns the currently active version and if the database is dirty. 70 // When no migration has been applied, it must return version -1. 71 // Dirty means, a previous migration failed and user interaction is required. 72 Version() (version int, dirty bool, err error) 73 74 // Drop deletes everyting in the database. 75 Drop() error 76 } 77 78 // Open returns a new driver instance. 79 func Open(url string) (Driver, error) { 80 u, err := nurl.Parse(url) 81 if err != nil { 82 return nil, err 83 } 84 85 if u.Scheme == "" { 86 return nil, fmt.Errorf("database driver: invalid URL scheme") 87 } 88 89 driversMu.RLock() 90 d, ok := drivers[u.Scheme] 91 driversMu.RUnlock() 92 if !ok { 93 return nil, fmt.Errorf("database driver: unknown driver %v (forgotton import?)", u.Scheme) 94 } 95 96 return d.Open(url) 97 } 98 99 // Register globally registers a driver. 100 func Register(name string, driver Driver) { 101 driversMu.Lock() 102 defer driversMu.Unlock() 103 if driver == nil { 104 panic("Register driver is nil") 105 } 106 if _, dup := drivers[name]; dup { 107 panic("Register called twice for driver " + name) 108 } 109 drivers[name] = driver 110 }