github.com/postmates/migrate@v3.0.2-0.20200730201548-1a6ead3e680d+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 // 6. Create a migrate/cli/build_<driver-name>.go file 36 // 7. Add driver name in 'DATABASE' variable in Makefile 37 // 38 // Guidelines: 39 // * Don't try to correct user input. Don't assume things. 40 // When in doubt, return an error and explain the situation to the user. 41 // * All configuration input must come from the URL string in func Open() 42 // or the Config{} struct in WithInstance. Don't os.Getenv(). 43 type Driver interface { 44 // Open returns a new driver instance configured with parameters 45 // coming from the URL string. Migrate will call this function 46 // only once per instance. 47 Open(url string) (Driver, error) 48 49 // Close closes the underlying database instance managed by the driver. 50 // Migrate will call this function only once per instance. 51 Close() error 52 53 // Lock should acquire a database lock so that only one migration process 54 // can run at a time. Migrate will call this function before Run is called. 55 // If the implementation can't provide this functionality, return nil. 56 // Return database.ErrLocked if database is already locked. 57 Lock() error 58 59 // Unlock should release the lock. Migrate will call this function after 60 // all migrations have been run. 61 Unlock() error 62 63 // Run applies a migration to the database. migration is garantueed to be not nil. 64 Run(migration io.Reader) error 65 66 // SetVersion saves version and dirty state. 67 // Migrate will call this function before and after each call to Run. 68 // version must be >= -1. -1 means NilVersion. 69 SetVersion(version int, dirty bool) error 70 71 // Version returns the currently active version and if the database is dirty. 72 // When no migration has been applied, it must return version -1. 73 // Dirty means, a previous migration failed and user interaction is required. 74 Version() (version int, dirty bool, err error) 75 76 // Drop deletes everything in the database. 77 Drop() error 78 } 79 80 // Open returns a new driver instance. 81 func Open(url string) (Driver, error) { 82 u, err := nurl.Parse(url) 83 if err != nil { 84 return nil, err 85 } 86 87 if u.Scheme == "" { 88 return nil, fmt.Errorf("database driver: invalid URL scheme") 89 } 90 91 driversMu.RLock() 92 d, ok := drivers[u.Scheme] 93 driversMu.RUnlock() 94 if !ok { 95 return nil, fmt.Errorf("database driver: unknown driver %v (forgotten import?)", u.Scheme) 96 } 97 98 return d.Open(url) 99 } 100 101 // Register globally registers a driver. 102 func Register(name string, driver Driver) { 103 driversMu.Lock() 104 defer driversMu.Unlock() 105 if driver == nil { 106 panic("Register driver is nil") 107 } 108 if _, dup := drivers[name]; dup { 109 panic("Register called twice for driver " + name) 110 } 111 drivers[name] = driver 112 }