Giter Club home page Giter Club logo

migrate's Issues

Expose getCurrentVersion function

func (m *Migrator) getCurrentVersion(ctx context.Context) (string, error) {

In some scenario a public usage of this function can be interesting, for example for a migration tool based on your library.

I can propose a PR about this, if the behavior is considered useful.

version comparison of string values

๐Ÿ‘‹ hey there I came across this package (which looks really cool) and I noticed this line:

if thisVersion > currentVersion {
	continue
}

I was wondering how the version compare would handle this scenario:

9.up.sql
10.up.sql

given the following result when comparing the two version strings:

fmt.Println("9" > "10")
// true

have you considered parsing the version as a number?

or maybe you would consider allowing the user to customize the regular expression to ensure extra safety? For example I could customize the up and down expressions to ensure the version is expressed as a date:

// 20210510.up.sql
regexp.MustCompile(`^([\d]{7}).up.sql$`)

or I could customize the expression to ignore the trailing description and only use the digits for comparison:

// 20210510-create-user-table.up.sql
regexp.MustCompile(`^([\d]{7})[\w-]+.up.sql$`)

Allow setting a custom name for migrations table

I think it would be useful and more flexible. Maybe with some functional options or just another New* function.

  • Just an exported field:
type Migrator struct {
	DB *sql.DB
	FS fs.FS
	Table string
}
m := New(db, migrations)
m.Table = "migrate_inator"
  • With another New* function:
type Migrator struct {
	DB *sql.DB
	FS fs.FS
	table string
}

// New Migrator with default options.
func New(db *sql.DB, fs fs.FS) *Migrator {
	return &Migrator{
		DB: db,
		FS: fs,
		table: "migrate",
	}
}

func NewWithTable(db *sql.DB, fs fs.FS, table string) *Migrator {
	return &Migrator{
		DB: db,
		FS: fs,
		table: table,
	}
}
m := NewWithTable(db, migrations, "migrate_inator")
  • With a Config struct:
type Config struct {
	DB *sql.DB
	FS fs.FS
	Table string
}

type Migrator struct {
	DB *sql.DB
	FS fs.FS
	table string
}

// New Migrator with default options.
func New(db *sql.DB, fs fs.FS) *Migrator {
	return NewWithConfig(Config{
		DB: db,
		FS: fs,
		Table: "migrate",
	})
}

func NewWithConfig(cfg Config) *Migrator {
	return &Migrator{
		DB: cfg.DB,
		FS: cfg.FS,
		table: cfg.Table,
	}
}
m := NewWithConfig(Config{
	DB: db,
	FS: migrations,
	Table: "migrate_inator"
})
  • With functional options:
type Migrator struct {
	DB *sql.DB
	FS fs.FS
	table string
}

type Option func(*Migrator)

// New Migrator with default options.
func New(db *sql.DB, fs fs.FS, options ...Option) *Migrator {
	m := &Migrator{
		DB: db,
		FS: fs,
		table: "migrate",
	}

	for _, opt := range options {
		opt(m)
	}

	return m
}

func WithTable(table string) Option {
	return func(m *Migrator) {
		m.table = table
	}
}
m := New(db, migrations, WithTable("migrate_inator"))

callback for sql pre-processing

๐Ÿ‘‹ hey there, I have an interesting use case that is not solved by most migrations tools, and I was wondering if you would be open to the suggestion. First please allow me to describe the problem.

I have a database that supports postgres and sqlite, however, the ddl syntax can be slightly different which means I need two different (but very similar) sets of migrations. In the interest of reducing a significant amount of duplicate code, it would be great if I could leverage templates to have a single set of migrations.

For example:

CREATE TABLE IF NOT EXISTS person (
 id      {{ if eq .driver "sqlite3" }}INTEGER PRIMARY KEY AUTOINCREMENT{{ else }}SERIAL PRIMARY KEY{{ end }}
,name    TEXT
,last    TEXT
,first   TEXT
,address TEXT
,dob     DATE
);

I suspect that templating would be outside the scope of this library, for good reason. Instead, I would propose that this library provides a hook for pre-processing. This would allow me to provide a callback function that executes a template and returns the final sql statement. For example:

		// Normally we wouldn't just string interpolate the version like this,
		// but because we know the version has been matched against the regexes, we know it's safe.
		if _, err := tx.ExecContext(ctx, `update `+m.table+` set version = '`+version+`'`); err != nil {
			return err
		}

+		stmt := string(content)
+		if m.process != nil {
+			stmt = m.process(ctx, stmt)
+		}
+		if _, err := tx.ExecContext(ctx, stmt); err != nil {
-		if _, err := tx.ExecContext(ctx, string(content)); err != nil {
			return err
		}

This would allow me to do something like this:

driver := "postgres"
opts.Process = func(before string) (string, error) {
	t, err := template.New("_").Parse(before)
	if err != nil {
		return before, err
	}
	var buf bytes.Buffer
	err := t.Execute(buf, map[string]string{"driver": driver})
	return buf.String(), err
}

Alternatively, instead of a pre-processing hook, you could allow the user to override the execution:

+		if m.exec != nil {
+			if err := m.exec(ctx, tx, string(content)); err != nil {
+				return err
+			}
+		} else {
			if _, err := tx.ExecContext(ctx, string(content)); err != nil {
				return err
			}
+		}

I probably prefer the first option but the second option might provide some additional flexibility (for example, maybe I want to log the sql statement on error, or skip execution if the template generates an empty string). I think this would be a pretty small change that would solve a very real world problem for authors that are support multiple database vendors. If you are open to supporting this feature, I would be happy to submit a pull request.

Permit custom migration table name to contain '.'

Currently it is impossible to create a migration table with name master.migrations for example.
The reason is the regexp that check the name

tableMatcher = regexp.MustCompile(`^[\w]+$`)

This can be useful when a Postgresql DB contains multiple schemas and you want to create migration table in a specific one.

I suggest to add '.' as a valid character
tableMatcher = regexp.MustCompile(^[\w\.]+$)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.