github.com/scraniel/migrate@v0.0.0-20230320185700-339088f36cee/database/driver.go (about) 1 // Package database provides the Driver 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 "sync" 11 12 iurl "github.com/golang-migrate/migrate/v4/internal/url" 13 ) 14 15 var ( 16 ErrLocked = fmt.Errorf("can't acquire lock") 17 ErrNotLocked = fmt.Errorf("can't unlock, as not currently locked") 18 ) 19 20 const NilVersion int = -1 21 22 var driversMu sync.RWMutex 23 var drivers = make(map[string]Driver) 24 25 // Driver is the interface every database driver must implement. 26 // 27 // How to implement a database driver? 28 // 1. Implement this interface. 29 // 2. Optionally, add a function named `WithInstance`. 30 // This function should accept an existing DB instance and a Config{} struct 31 // and return a driver instance. 32 // 3. Add a test that calls database/testing.go:Test() 33 // 4. Add own tests for Open(), WithInstance() (when provided) and Close(). 34 // All other functions are tested by tests in database/testing. 35 // Saves you some time and makes sure all database drivers behave the same way. 36 // 5. Call Register in init(). 37 // 6. Create a internal/cli/build_<driver-name>.go file 38 // 7. Add driver name in 'DATABASE' variable in Makefile 39 // 40 // Guidelines: 41 // - Don't try to correct user input. Don't assume things. 42 // When in doubt, return an error and explain the situation to the user. 43 // - All configuration input must come from the URL string in func Open() 44 // or the Config{} struct in WithInstance. Don't os.Getenv(). 45 type Driver interface { 46 // Open returns a new driver instance configured with parameters 47 // coming from the URL string. Migrate will call this function 48 // only once per instance. 49 Open(url string) (Driver, error) 50 51 // Close closes the underlying database instance managed by the driver. 52 // Migrate will call this function only once per instance. 53 Close() error 54 55 // Lock should acquire a database lock so that only one migration process 56 // can run at a time. Migrate will call this function before Run is called. 57 // If the implementation can't provide this functionality, return nil. 58 // Return database.ErrLocked if database is already locked. 59 Lock() error 60 61 // Unlock should release the lock. Migrate will call this function after 62 // all migrations have been run. 63 Unlock() error 64 65 // Run applies a migration to the database. migration is guaranteed to be not nil. 66 Run(migration io.Reader) error 67 68 // SetVersion saves version and dirty state. 69 // Migrate will call this function before and after each call to Run. 70 // version must be >= -1. -1 means NilVersion. 71 SetVersion(version int, dirty bool) error 72 73 // Version returns the currently active version and if the database is dirty. 74 // When no migration has been applied, it must return version -1. 75 // Dirty means, a previous migration failed and user interaction is required. 76 Version() (version int, dirty bool, err error) 77 78 // Drop deletes everything in the database. 79 // Note that this is a breaking action, a new call to Open() is necessary to 80 // ensure subsequent calls work as expected. 81 Drop() error 82 } 83 84 // Open returns a new driver instance. 85 func Open(url string) (Driver, error) { 86 scheme, err := iurl.SchemeFromURL(url) 87 if err != nil { 88 return nil, err 89 } 90 91 driversMu.RLock() 92 d, ok := drivers[scheme] 93 driversMu.RUnlock() 94 if !ok { 95 return nil, fmt.Errorf("database driver: unknown driver %v (forgotten import?)", 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 } 113 114 // List lists the registered drivers 115 func List() []string { 116 driversMu.RLock() 117 defer driversMu.RUnlock() 118 names := make([]string, 0, len(drivers)) 119 for n := range drivers { 120 names = append(names, n) 121 } 122 return names 123 }