github.com/fr-nvriep/migrate/v4@v4.3.2/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 // Note that this is a breaking action, a new call to Open() is necessary to 78 // ensure subsequent calls work as expected. 79 Drop() error 80 } 81 82 // Open returns a new driver instance. 83 func Open(url string) (Driver, error) { 84 u, err := nurl.Parse(url) 85 if err != nil { 86 return nil, fmt.Errorf("Unable to parse URL. Did you escape all reserved URL characters? "+ 87 "See: https://github.com/fr-nvriep/migrate#database-urls Error: %v", err) 88 } 89 90 if u.Scheme == "" { 91 return nil, fmt.Errorf("database driver: invalid URL scheme") 92 } 93 94 driversMu.RLock() 95 d, ok := drivers[u.Scheme] 96 driversMu.RUnlock() 97 if !ok { 98 return nil, fmt.Errorf("database driver: unknown driver %v (forgotten import?)", u.Scheme) 99 } 100 101 return d.Open(url) 102 } 103 104 // Register globally registers a driver. 105 func Register(name string, driver Driver) { 106 driversMu.Lock() 107 defer driversMu.Unlock() 108 if driver == nil { 109 panic("Register driver is nil") 110 } 111 if _, dup := drivers[name]; dup { 112 panic("Register called twice for driver " + name) 113 } 114 drivers[name] = driver 115 } 116 117 // List lists the registered drivers 118 func List() []string { 119 driversMu.RLock() 120 defer driversMu.RUnlock() 121 names := make([]string, 0, len(drivers)) 122 for n := range drivers { 123 names = append(names, n) 124 } 125 return names 126 }