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  }