github.com/gocaveman/caveman@v0.0.0-20191211162744-0ddf99dbdf6e/migrate/migratedbr/migratedbr.go (about)

     1  package migratedbr
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/gocraft/dbr"
     7  )
     8  
     9  func New(driverName, dsn string) (*DbrVersioner, error) {
    10  	return NewTable(driverName, dsn, "migration_state")
    11  }
    12  
    13  func NewTable(driverName, dsn, tableName string) (*DbrVersioner, error) {
    14  
    15  	conn, err := dbr.Open(driverName, dsn, nil)
    16  	if err != nil {
    17  		return nil, err
    18  	}
    19  
    20  	// TODO: might need to do variations on this but for now this should work for mysql, postgres and sqlite3
    21  	// FIXME: category changed to 128 due to some obscure MariaDB encoding issue, needs more thought
    22  	// https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=886756
    23  	_, err = conn.DB.Exec(`
    24  CREATE TABLE IF NOT EXISTS ` + tableName + ` (
    25  	category varchar(128),
    26  	version varchar(255),
    27  	status varchar(255),
    28  	PRIMARY KEY (category)
    29  )
    30  `)
    31  	if err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	return &DbrVersioner{
    36  		Connection: conn,
    37  		TableName:  tableName,
    38  	}, nil
    39  }
    40  
    41  type DbrVersioner struct {
    42  	Connection *dbr.Connection
    43  	TableName  string
    44  }
    45  
    46  func (v *DbrVersioner) Close() error {
    47  	return v.Connection.Close()
    48  }
    49  
    50  func (v *DbrVersioner) Categories() ([]string, error) {
    51  
    52  	sess := v.Connection.NewSession(nil)
    53  	recs := []struct {
    54  		Category string `db:"category"`
    55  	}{}
    56  	n, err := sess.Select("category").From(v.TableName).Load(&recs)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	ret := make([]string, 0, n)
    62  	for _, rec := range recs {
    63  		ret = append(ret, rec.Category)
    64  	}
    65  
    66  	return ret, nil
    67  }
    68  
    69  func (v *DbrVersioner) Version(category string) (string, error) {
    70  
    71  	versionName := ""
    72  
    73  	sess := v.Connection.NewSession(nil)
    74  	err := sess.Select("version").From(v.TableName).Where("category = ?", category).LoadOne(&versionName)
    75  
    76  	// treat missing row as empty version
    77  	if err == dbr.ErrNotFound {
    78  		return "", nil
    79  	}
    80  
    81  	return versionName, err
    82  }
    83  
    84  func (v *DbrVersioner) StartVersionChange(category, currentVersion string) error {
    85  
    86  	sess := v.Connection.NewSession(nil)
    87  
    88  	// tx, err := v.DB.Begin()
    89  	// if err != nil {
    90  	// 	return err
    91  	// }
    92  	// defer tx.Rollback()
    93  
    94  	versionName := ""
    95  
    96  	err := sess.Select("version").From(v.TableName).Where("category = ?", category).LoadOne(&versionName)
    97  	if err == dbr.ErrNotFound {
    98  
    99  		_, err := sess.InsertInto(v.TableName).Columns("category", "version", "status").Values(category, "", "none").Exec()
   100  		if err != nil {
   101  			return err
   102  		}
   103  
   104  	} else if err != nil {
   105  		return err
   106  	}
   107  
   108  	// row := tx.QueryRow(`SELECT version FROM `+v.TableName+` WHERE category = ?`, category)
   109  	// err = row.Scan(&versionName)
   110  	// if err == sql.ErrNoRows {
   111  	// 	_, err := tx.Exec(`INSERT INTO `+v.TableName+`(category, version, status) VALUES(?,?,?)`, category, "", "none")
   112  	// 	if err != nil {
   113  	// 		return err
   114  	// 	}
   115  	// } else if err != nil {
   116  	// 	return err
   117  	// }
   118  
   119  	if versionName != currentVersion {
   120  		return fmt.Errorf("incorrect version, found %q expected %q", versionName, currentVersion)
   121  	}
   122  
   123  	res, err := sess.Update(v.TableName).
   124  		Set("status", "inprogress").
   125  		Where(dbr.And(
   126  			dbr.Eq("category", category),
   127  			dbr.Eq("status", "none"),
   128  			dbr.Eq("version", currentVersion),
   129  		)).
   130  		Exec()
   131  
   132  	if err != nil {
   133  		return err
   134  	}
   135  
   136  	// res, err := tx.Exec(`UPDATE `+v.TableName+` SET status = ? WHERE category = ? AND status = ? AND version = ?`, "inprogress", category, "none", currentVersion)
   137  	// if err != nil {
   138  	// 	return err
   139  	// }
   140  
   141  	n, err := res.RowsAffected()
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	if n != 1 {
   147  		return fmt.Errorf("StartVersionChange UPDATE statement returned num rows affected %d (expected 1)", n)
   148  	}
   149  
   150  	// err = tx.Commit()
   151  	// if err != nil {
   152  	// 	return err
   153  	// }
   154  
   155  	return nil
   156  
   157  }
   158  
   159  func (v *DbrVersioner) EndVersionChange(category, newVersionName string) error {
   160  
   161  	// tx, err := v.DB.Begin()
   162  	// if err != nil {
   163  	// 	return err
   164  	// }
   165  	// defer tx.Rollback()
   166  
   167  	sess := v.Connection.NewSession(nil)
   168  
   169  	res, err := sess.Update(v.TableName).
   170  		Set("version", newVersionName).
   171  		Set("status", "none").
   172  		Where(dbr.And(dbr.Eq("category", category), dbr.Eq("status", "inprogress"))).
   173  		Exec()
   174  
   175  	// res, err := tx.Exec(`UPDATE `+v.TableName+` SET version = ?, status = ? WHERE category = ? AND status = ?`, newVersionName, "none", category, "inprogress")
   176  	// if err != nil {
   177  	// 	return err
   178  	// }
   179  
   180  	n, err := res.RowsAffected()
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	if n != 1 {
   186  		return fmt.Errorf("EndVersionChange UPDATE statement returned num rows affected %d (expected 1)", n)
   187  	}
   188  
   189  	// err = tx.Commit()
   190  	// if err != nil {
   191  	// 	return err
   192  	// }
   193  
   194  	return nil
   195  }