github.com/dgollings/migrate@v3.5.4+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, fmt.Errorf("Unable to parse URL. Did you escape all reserved URL characters? "+
    85  			"See: https://github.com/golang-migrate/migrate#database-urls Error: %v", err)
    86  	}
    87  
    88  	if u.Scheme == "" {
    89  		return nil, fmt.Errorf("database driver: invalid URL scheme")
    90  	}
    91  
    92  	driversMu.RLock()
    93  	d, ok := drivers[u.Scheme]
    94  	driversMu.RUnlock()
    95  	if !ok {
    96  		return nil, fmt.Errorf("database driver: unknown driver %v (forgotten import?)", u.Scheme)
    97  	}
    98  
    99  	return d.Open(url)
   100  }
   101  
   102  // Register globally registers a driver.
   103  func Register(name string, driver Driver) {
   104  	driversMu.Lock()
   105  	defer driversMu.Unlock()
   106  	if driver == nil {
   107  		panic("Register driver is nil")
   108  	}
   109  	if _, dup := drivers[name]; dup {
   110  		panic("Register called twice for driver " + name)
   111  	}
   112  	drivers[name] = driver
   113  }
   114  
   115  // List lists the registered drivers
   116  func List() []string {
   117  	driversMu.RLock()
   118  	defer driversMu.RUnlock()
   119  	names := make([]string, 0, len(drivers))
   120  	for n := range drivers {
   121  		names = append(names, n)
   122  	}
   123  	return names
   124  }