sqlitemigration

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 27, 2021 License: ISC Imports: 7 Imported by: 4

Documentation

Overview

Package sqlitemigration provides a connection pool type that guarantees a series of SQL scripts has been run once successfully before making connections available to the application. This is frequently useful for ensuring tables are created.

Example
package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"zombiezen.com/go/sqlite"
	"zombiezen.com/go/sqlite/sqlitemigration"
	"zombiezen.com/go/sqlite/sqlitex"
)

func main() {
	schema := sqlitemigration.Schema{
		// Replace with a random int32 application ID for your application.
		// Sample command to generate:
		// echo -n '0x' && head -c 4 /dev/urandom | xxd -p
		AppID: 1, // DO NOT USE, SEE ABOVE!

		// Each element of the Migrations slice is applied in sequence. When you
		// want to change the schema, add a new SQL script to this list.
		//
		// Existing databases will pick up at the same position in the Migrations
		// slice as they last left off.
		Migrations: []string{
			"CREATE TABLE foo ( id INTEGER NOT NULL PRIMARY KEY );",

			"ALTER TABLE foo ADD COLUMN name TEXT;",
		},

		// The RepeatableMigration is run after all other Migrations if any
		// migration was run. It is useful for creating triggers and views.
		RepeatableMigration: "DROP VIEW IF EXISTS bar;\n" +
			"CREATE VIEW bar ( id, name ) AS SELECT id, name FROM foo;\n",
	}

	// Set up a temporary directory to store the database.
	dir, err := ioutil.TempDir("", "sqlitemigration")
	if err != nil {
		// handle error
		log.Fatal(err)
	}
	defer os.RemoveAll(dir)

	// Open a pool. This does not block, and will start running any migrations
	// asynchronously.
	pool := sqlitemigration.NewPool(filepath.Join(dir, "foo.db"), schema, sqlitemigration.Options{
		Flags: sqlite.OpenReadWrite | sqlite.OpenCreate | sqlite.OpenNoMutex,
		PrepareConn: func(conn *sqlite.Conn) error {
			// Enable foreign keys. See https://sqlite.org/foreignkeys.html
			return sqlitex.ExecTransient(conn, "PRAGMA foreign_keys = ON;", nil)
		},
		OnError: func(e error) {
			log.Println(e)
		},
	})
	defer pool.Close()

	// Get a connection. This blocks until the migration completes.
	conn, err := pool.Get(context.TODO())
	if err != nil {
		// handle error
	}
	defer pool.Put(conn)

	// Print the list of schema objects created.
	const listSchemaQuery = `SELECT "type", "name" FROM sqlite_master ORDER BY 1, 2;`
	err = sqlitex.ExecTransient(conn, listSchemaQuery, func(stmt *sqlite.Stmt) error {
		fmt.Printf("%-5s %s\n", stmt.ColumnText(0), stmt.ColumnText(1))
		return nil
	})
	if err != nil {
		// handle error
	}

}
Output:

table foo
view  bar

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Migrate

func Migrate(ctx context.Context, conn *sqlite.Conn, schema Schema) error

Migrate performs any unapplied migrations in the schema on the database.

Types

type ConnPrepareFunc

type ConnPrepareFunc func(conn *sqlite.Conn) error

A ConnPrepareFunc is called for each connection in a pool to set up connection-specific state. It must be safe to call from multiple goroutines.

If the ConnPrepareFunc returns an error, then it will be called the next time the connection is about to be used. Once ConnPrepareFunc returns nil for a given connection, it will not be called on that connection again.

type Options

type Options struct {
	// Flags is interpreted the same way as the argument to sqlitex.Open.
	Flags sqlite.OpenFlags

	// PoolSize sets an explicit size to the pool. If less than 1, a reasonable
	// default is used.
	PoolSize int

	// OnStartMigrate is called after the pool has successfully opened a
	// connection to the database but before any migrations have been run.
	OnStartMigrate SignalFunc
	// OnReady is called after the pool has connected to the database and run any
	// necessary migrations.
	OnReady SignalFunc
	// OnError is called when the pool encounters errors while applying the
	// migration. This is typically used for logging errors.
	OnError ReportFunc

	// PrepareConn is called for each connection in the pool to set up functions
	// and other connection-specific state.
	PrepareConn ConnPrepareFunc
}

Options specifies optional behaviors for the pool.

type Pool

type Pool struct {
	// contains filtered or unexported fields
}

Pool is a pool of SQLite connections.

func NewPool

func NewPool(uri string, schema Schema, opts Options) *Pool

NewPool opens a new pool of SQLite connections.

func (*Pool) CheckHealth

func (p *Pool) CheckHealth() error

CheckHealth returns an error if the migration has not completed. Closed pools may report healthy.

func (*Pool) Close

func (p *Pool) Close() error

Close closes all connections in the Pool, potentially interrupting a migration.

func (*Pool) Get

func (p *Pool) Get(ctx context.Context) (*sqlite.Conn, error)

Get gets an SQLite connection from the pool.

func (*Pool) Put

func (p *Pool) Put(conn *sqlite.Conn)

Put puts an SQLite connection back into the pool. See sqlitex.Pool for details.

type ReportFunc

type ReportFunc func(error)

A ReportFunc is called for transient errors the pool encounters while running the migrations. It must be safe to call from multiple goroutines.

type Schema

type Schema struct {
	// AppID is saved to the database file to identify the application.
	// It's used to prevent opening database files for a different
	// application. It should be a positive number, but should not change
	// between runs of the same program. A common way of setting this is with
	// a compile-time constant that was randomly generated.
	AppID int32

	// Migrations is a list of SQL scripts to run. Each script is wrapped in a
	// transaction which is rolled back on any error.
	Migrations []string

	// RepeatableMigration is a SQL script to run if any migrations ran. The
	// script is wrapped in a transaction which is rolled back on any error.
	RepeatableMigration string
}

Schema defines the migrations for the application.

type SignalFunc

type SignalFunc func()

A SignalFunc is called at most once when a particular event in a Pool's lifecycle occurs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL