Wrap postgres up migrations into a single transaction (#887)
This commit is contained in:
1 parent
c1576dddda
commit
d2bb8820ea
9 files changed
+209
-217
No files matched your search
@@ -18,10 +18,17 @@ type driver interface {
|
||||
Lock(context.Context) error
|
||||
Unlock(context.Context) error
|
||||
UnlockShared(context.Context) error
|
||||
Run(context.Context, io.Reader) error
|
||||
// A value of -1 indicates no version is set.
|
||||
SetVersion(context.Context, int, bool) error
|
||||
// A value of -1 indicates no version is set.
|
||||
// Either starts a transactioon internal to the driver or sets a dirty
|
||||
// bit so if the Run fails the CurrentState reflects it.
|
||||
StartRun(context.Context) error
|
||||
// Either commits the transaction or clears the dirty bit.
|
||||
CommitRun() error
|
||||
// Performs the mutation on the driver. This should always be
|
||||
// wrapped by StartRun and CommitRun. The driver must properly
|
||||
// handle the transaction or dirty bit in case of error when
|
||||
// executing Run.
|
||||
Run(context.Context, io.Reader, int) error
|
||||
// A version of -1 indicates no version is set.
|
||||
CurrentState(context.Context) (int, bool, error)
|
||||
}
|
||||
|
||||
@@ -157,6 +164,10 @@ func (b *Manager) RollForward(ctx context.Context) error {
|
||||
// through the passed in context.
|
||||
func (b *Manager) runMigrations(ctx context.Context, qp *statementProvider) error {
|
||||
const op = "schema.(Manager).runMigrations"
|
||||
|
||||
if err := b.driver.StartRun(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
for qp.Next() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -164,22 +175,12 @@ func (b *Manager) runMigrations(ctx context.Context, qp *statementProvider) erro
|
||||
default:
|
||||
// context is not done yet. Continue on to the next query to execute.
|
||||
}
|
||||
|
||||
// set version with dirty state
|
||||
if err := b.driver.SetVersion(ctx, qp.Version(), true); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
|
||||
// TODO: Wrap all the queries provided by the statementProvider into a
|
||||
// single transaction which includes setting the version number.
|
||||
if err := b.driver.Run(ctx, bytes.NewReader(qp.ReadUp())); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
|
||||
// set clean state
|
||||
if err := b.driver.SetVersion(ctx, qp.Version(), false); err != nil {
|
||||
if err := b.driver.Run(ctx, bytes.NewReader(qp.ReadUp()), qp.Version()); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
}
|
||||
if err := b.driver.CommitRun(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package schema
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/db/schema/postgres"
|
||||
@@ -61,12 +62,11 @@ func TestCurrentState(t *testing.T) {
|
||||
|
||||
testDriver, err := postgres.New(ctx, d)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, testDriver.SetVersion(ctx, 2, true))
|
||||
require.NoError(t, testDriver.Run(ctx, strings.NewReader("SELECT 1"), 2))
|
||||
|
||||
want = &State{
|
||||
InitializationStarted: true,
|
||||
BinarySchemaVersion: BinarySchemaVersion(dialect),
|
||||
Dirty: true,
|
||||
DatabaseSchemaVersion: 2,
|
||||
}
|
||||
s, err = m.CurrentState(ctx)
|
||||
@@ -87,10 +87,13 @@ func TestRollForward(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
m, err := NewManager(ctx, dialect, d)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, m.RollForward(ctx))
|
||||
|
||||
// Now set to dirty at an early version
|
||||
testDriver, err := postgres.New(ctx, d)
|
||||
_, err = postgres.New(ctx, d)
|
||||
require.NoError(t, err)
|
||||
testDriver.SetVersion(ctx, 0, true)
|
||||
// TODO: Extract out a way to mock the db to test failing rollforwards.
|
||||
_, err = d.ExecContext(ctx, "TRUNCATE boundary_schema_version; INSERT INTO boundary_schema_version (Version, dirty) VALUES (2, true)")
|
||||
assert.Error(t, m.RollForward(ctx))
|
||||
}
|
||||
|
||||
@@ -115,10 +118,10 @@ func TestRollForward_NotFromFresh(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, m.RollForward(ctx))
|
||||
|
||||
ver, dirty, err := m.driver.CurrentState(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, nState.binarySchemaVersion, ver)
|
||||
assert.False(t, dirty)
|
||||
state, err := m.CurrentState(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, nState.binarySchemaVersion, state.DatabaseSchemaVersion)
|
||||
assert.False(t, state.Dirty)
|
||||
|
||||
// Restore the full set of sql scripts and roll the rest of the way forward.
|
||||
migrationStates[dialect] = oState
|
||||
@@ -126,10 +129,40 @@ func TestRollForward_NotFromFresh(t *testing.T) {
|
||||
newM, err := NewManager(ctx, dialect, d)
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, newM.RollForward(ctx))
|
||||
ver, dirty, err = newM.driver.CurrentState(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, oState.binarySchemaVersion, ver)
|
||||
assert.False(t, dirty)
|
||||
state, err = newM.CurrentState(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, oState.binarySchemaVersion, state.DatabaseSchemaVersion)
|
||||
assert.False(t, state.Dirty)
|
||||
}
|
||||
|
||||
func TestRollForward_BadSQL(t *testing.T) {
|
||||
dialect := "postgres"
|
||||
oState := migrationStates[dialect]
|
||||
defer func() { migrationStates[dialect] = oState }()
|
||||
|
||||
nState := createPartialMigrationState(oState, 8)
|
||||
nState.binarySchemaVersion = 10
|
||||
nState.upMigrations[10] = []byte("SELECT 1 FROM NonExistantTable;")
|
||||
migrationStates[dialect] = nState
|
||||
|
||||
c, u, _, err := docker.StartDbInDocker(dialect)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, c())
|
||||
})
|
||||
d, err := sql.Open(dialect, u)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Initialize the DB with only a portion of the current sql scripts.
|
||||
ctx := context.Background()
|
||||
m, err := NewManager(ctx, dialect, d)
|
||||
require.NoError(t, err)
|
||||
assert.Error(t, m.RollForward(ctx))
|
||||
|
||||
state, err := m.CurrentState(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, nilVersion, state.DatabaseSchemaVersion)
|
||||
assert.False(t, state.Dirty)
|
||||
}
|
||||
|
||||
func TestManager_ExclusiveLock(t *testing.T) {
|
||||
|
||||
@@ -75,12 +75,6 @@ func generate(dialect string) {
|
||||
continue
|
||||
}
|
||||
|
||||
contents, err := ioutil.ReadFile(fmt.Sprintf("%s/%s/%s/%s", srcDir, dialect, ver, name))
|
||||
if err != nil {
|
||||
fmt.Printf("error opening file %s with dialect %s: %v", name, dialect, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
nameParts := strings.SplitN(name, "_", 2)
|
||||
if len(nameParts) != 2 {
|
||||
continue
|
||||
@@ -96,9 +90,25 @@ func generate(dialect string) {
|
||||
if fullV > largestSchemaVersion {
|
||||
largestSchemaVersion = fullV
|
||||
}
|
||||
|
||||
cbts, err := ioutil.ReadFile(fmt.Sprintf("%s/%s/%s/%s", srcDir, dialect, ver, name))
|
||||
if err != nil {
|
||||
fmt.Printf("error opening file %s with dialect %s: %v", name, dialect, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
contents := strings.TrimSpace(string(cbts))
|
||||
if strings.ToLower(contents[:len("begin;")]) == "begin;" {
|
||||
contents = contents[len("begin;"):]
|
||||
}
|
||||
if strings.ToLower(contents[len(contents)-len("commit;"):]) == "commit;" {
|
||||
contents = contents[:len(contents)-len("commit;")]
|
||||
}
|
||||
contents = strings.TrimSpace(contents)
|
||||
|
||||
cv := ContentValues{
|
||||
Name: fmt.Sprint(fullV),
|
||||
Content: string(contents),
|
||||
Content: contents,
|
||||
}
|
||||
switch {
|
||||
case strings.Contains(nameParts[1], ".up."):
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
begin;
|
||||
|
||||
/*
|
||||
┌─────────────────┐
|
||||
┌─────────────────┐ │ target_tcp │
|
||||
@@ -33,8 +35,6 @@
|
||||
|
||||
*/
|
||||
|
||||
begin;
|
||||
|
||||
create table target (
|
||||
public_id wt_public_id primary key,
|
||||
scope_id wt_scope_id not null
|
||||
|
||||
@@ -57,6 +57,8 @@ type Postgres struct {
|
||||
// Locking and unlocking need to use the same connection
|
||||
conn *sql.Conn
|
||||
db *sql.DB
|
||||
|
||||
tx *sql.Tx
|
||||
}
|
||||
|
||||
// New returns a postgres pointer with the provided db verified as
|
||||
@@ -154,10 +156,41 @@ func (p *Postgres) UnlockShared(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartRun starts a transaction that all subsequent calls to Run will use.
|
||||
func (p *Postgres) StartRun(ctx context.Context) error {
|
||||
tx, err := p.conn.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.tx = tx
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommitRun commits the pending transaction if there is one
|
||||
func (p *Postgres) CommitRun() error {
|
||||
const op = "postgres.(Postgres).CommitRun"
|
||||
defer func() {
|
||||
p.tx = nil
|
||||
}()
|
||||
if p.tx == nil {
|
||||
return errors.New(errors.MigrationIntegrity, op, "no pending transaction")
|
||||
}
|
||||
if err := p.tx.Commit(); err != nil {
|
||||
if errRollback := p.tx.Rollback(); errRollback != nil {
|
||||
return multierror.Append(err, errRollback)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type execContexter interface {
|
||||
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
||||
}
|
||||
|
||||
// Executes the sql provided in the passed in io.Reader. The contents of the reader must
|
||||
// fit in memory as the full content is read into a string before being passed to the
|
||||
// backing database.
|
||||
func (p *Postgres) Run(ctx context.Context, migration io.Reader) error {
|
||||
func (p *Postgres) Run(ctx context.Context, migration io.Reader, version int) error {
|
||||
const op = "postgres.(Postgres).Run"
|
||||
migr, err := ioutil.ReadAll(migration)
|
||||
if err != nil {
|
||||
@@ -165,7 +198,21 @@ func (p *Postgres) Run(ctx context.Context, migration io.Reader) error {
|
||||
}
|
||||
// Run migration
|
||||
query := string(migr)
|
||||
if _, err := p.conn.ExecContext(ctx, query); err != nil {
|
||||
|
||||
var extr execContexter = p.conn
|
||||
rollback := func() error { return nil }
|
||||
if p.tx != nil {
|
||||
extr = p.tx
|
||||
rollback = func() error {
|
||||
defer func() { p.tx = nil }()
|
||||
return p.tx.Rollback()
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := extr.ExecContext(ctx, query); err != nil {
|
||||
if rollbackErr := rollback(); rollbackErr != nil {
|
||||
err = multierror.Append(err, rollbackErr)
|
||||
}
|
||||
if pgErr, ok := err.(*pq.Error); ok {
|
||||
var line uint
|
||||
var col uint
|
||||
@@ -187,6 +234,9 @@ func (p *Postgres) Run(ctx context.Context, migration io.Reader) error {
|
||||
}
|
||||
return errors.Wrap(err, op, errors.WithMsg(fmt.Sprintf("migration failed: %s", migr)))
|
||||
}
|
||||
if err := p.setVersion(ctx, version, false); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -226,18 +276,26 @@ func runesLastIndex(input []rune, target rune) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// SetVersion sets the version number, and whether the database is in a dirty state.
|
||||
// setVersion sets the version number, and whether the database is in a dirty state.
|
||||
// A version value of -1 indicates no version is set.
|
||||
func (p *Postgres) SetVersion(ctx context.Context, version int, dirty bool) error {
|
||||
func (p *Postgres) setVersion(ctx context.Context, version int, dirty bool) error {
|
||||
const op = "postgres.(Postgres).SetVersion"
|
||||
tx, err := p.conn.BeginTx(ctx, &sql.TxOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
tx := p.tx
|
||||
var err error
|
||||
if tx == nil {
|
||||
tx, err = p.conn.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
rollback := func() error {
|
||||
defer func() { p.tx = nil }()
|
||||
return tx.Rollback()
|
||||
}
|
||||
|
||||
query := `TRUNCATE ` + pq.QuoteIdentifier(defaultMigrationsTable)
|
||||
if _, err := tx.ExecContext(ctx, query); err != nil {
|
||||
if errRollback := tx.Rollback(); errRollback != nil {
|
||||
if errRollback := rollback(); errRollback != nil {
|
||||
err = multierror.Append(err, errRollback)
|
||||
}
|
||||
return errors.Wrap(err, op)
|
||||
@@ -250,24 +308,25 @@ func (p *Postgres) SetVersion(ctx context.Context, version int, dirty bool) erro
|
||||
query = `INSERT INTO ` + pq.QuoteIdentifier(defaultMigrationsTable) +
|
||||
` (Version, dirty) VALUES ($1, $2)`
|
||||
if _, err := tx.ExecContext(ctx, query, version, dirty); err != nil {
|
||||
if errRollback := tx.Rollback(); errRollback != nil {
|
||||
if errRollback := rollback(); errRollback != nil {
|
||||
err = multierror.Append(err, errRollback)
|
||||
}
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
if p.tx == nil {
|
||||
if err := tx.Commit(); err != nil {
|
||||
return errors.Wrap(err, op)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CurrentState returns the version, if the database is currently in a dirty state, and any error.
|
||||
// A version value of -1 indicates no version is set.
|
||||
func (p *Postgres) CurrentState(ctx context.Context) (version int, dirty bool, err error) {
|
||||
const op = "postgres.(Postgres).Version"
|
||||
const op = "postgres.(Postgres).CurrentState"
|
||||
query := `SELECT Version, dirty FROM ` + pq.QuoteIdentifier(defaultMigrationsTable) + ` LIMIT 1`
|
||||
err = p.conn.QueryRowContext(ctx, query).Scan(&version, &dirty)
|
||||
switch {
|
||||
|
||||
@@ -163,7 +163,7 @@ func TestMultiStatement(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
if err := d.Run(ctx, strings.NewReader("CREATE TABLE foo (foo text); CREATE TABLE bar (bar text);")); err != nil {
|
||||
if err := d.Run(ctx, strings.NewReader("CREATE TABLE foo (foo text); CREATE TABLE bar (bar text);"), 2); err != nil {
|
||||
t.Fatalf("expected err to be nil, got %v", err)
|
||||
}
|
||||
|
||||
@@ -178,6 +178,45 @@ func TestMultiStatement(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestTransaction(t *testing.T) {
|
||||
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
|
||||
ctx := context.Background()
|
||||
ip, port, err := c.FirstPort()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
addr := pgConnectionString(ip, port)
|
||||
d, err := open(t, ctx, addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
if err := d.close(t); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
assert.NoError(t, d.StartRun(ctx))
|
||||
assert.NoError(t, d.Run(ctx, strings.NewReader("CREATE TABLE foo (foo text);"), 2))
|
||||
assert.NoError(t, d.Run(ctx, strings.NewReader("SELECT 1"), 3))
|
||||
assert.NoError(t, d.CommitRun())
|
||||
v, dirty, err := d.CurrentState(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, dirty)
|
||||
assert.Equal(t, 3, v)
|
||||
|
||||
assert.NoError(t, d.StartRun(ctx))
|
||||
assert.NoError(t, d.Run(ctx, strings.NewReader("CREATE TABLE bar (bar text);"), 20))
|
||||
assert.Error(t, d.Run(ctx, strings.NewReader("SELECT 1 FROM NonExistingTable"), 30))
|
||||
assert.Error(t, d.CommitRun())
|
||||
v, dirty, err = d.CurrentState(ctx)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, dirty)
|
||||
assert.Equal(t, 3, v)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWithSchema(t *testing.T) {
|
||||
dktesting.ParallelTest(t, specs, func(t *testing.T, c dktest.ContainerInfo) {
|
||||
ctx := context.Background()
|
||||
@@ -198,10 +237,7 @@ func TestWithSchema(t *testing.T) {
|
||||
}()
|
||||
|
||||
// create foobar schema
|
||||
if err := d.Run(ctx, strings.NewReader("CREATE SCHEMA foobar AUTHORIZATION postgres")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := d.SetVersion(ctx, 1, false); err != nil {
|
||||
if err := d.Run(ctx, strings.NewReader("CREATE SCHEMA foobar AUTHORIZATION postgres"), 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -226,7 +262,7 @@ func TestWithSchema(t *testing.T) {
|
||||
}
|
||||
|
||||
// now update CurrentState and compare
|
||||
if err := d2.SetVersion(ctx, 2, false); err != nil {
|
||||
if err := d2.setVersion(ctx, 2, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
version, _, err = d2.CurrentState(ctx)
|
||||
@@ -303,7 +339,7 @@ func TestRun_Error(t *testing.T) {
|
||||
require.NoError(t, p.close(t))
|
||||
})
|
||||
|
||||
err = p.Run(ctx, bytes.NewReader([]byte("SELECT *\nFROM foo")))
|
||||
err = p.Run(ctx, bytes.NewReader([]byte("SELECT *\nFROM foo")), 2)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ func testRun(t *testing.T, d *Postgres, migration io.Reader) {
|
||||
t.Fatal("migration can't be nil")
|
||||
}
|
||||
|
||||
if err := d.Run(ctx, migration); err != nil {
|
||||
if err := d.Run(ctx, migration, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,7 @@ func testSetVersion(t *testing.T, d *Postgres) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := d.SetVersion(ctx, tc.version, tc.dirty)
|
||||
err := d.setVersion(ctx, tc.version, tc.dirty)
|
||||
if err != tc.expectedErr {
|
||||
t.Fatal("Got unexpected error:", err, "!=", tc.expectedErr)
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ func init() {
|
||||
binarySchemaVersion: 1086,
|
||||
upMigrations: map[int][]byte{
|
||||
1: []byte(`
|
||||
begin;
|
||||
|
||||
create domain wt_public_id as text
|
||||
check(
|
||||
length(trim(value)) > 10
|
||||
@@ -164,13 +162,8 @@ comment on function
|
||||
immutable_columns()
|
||||
is
|
||||
'function used in before update triggers to make columns immutable';
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
2: []byte(`
|
||||
begin;
|
||||
|
||||
-- TODO (jimlambrt 7/2020) remove update_time
|
||||
create table if not exists oplog_entry (
|
||||
id bigint generated always as identity primary key,
|
||||
@@ -278,15 +271,8 @@ values
|
||||
('db_test_scooter', 1),
|
||||
('auth_account', 1),
|
||||
('iam_principal_role', 1);
|
||||
|
||||
|
||||
commit;
|
||||
|
||||
|
||||
`),
|
||||
3: []byte(`
|
||||
begin;
|
||||
|
||||
-- create test tables used in the unit tests for the internal/db package
|
||||
-- these tables (db_test_user, db_test_car, db_test_rental, db_test_scooter) are
|
||||
-- not part of the boundary domain model... they are simply used for testing
|
||||
@@ -414,13 +400,8 @@ create trigger
|
||||
before
|
||||
insert on db_test_scooter
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
6: []byte(`
|
||||
begin;
|
||||
|
||||
create table iam_scope_type_enm (
|
||||
string text not null primary key
|
||||
constraint only_predefined_scope_types_allowed
|
||||
@@ -1123,14 +1104,8 @@ from
|
||||
where
|
||||
gm.member_id = u.public_id and
|
||||
gm.group_id = g.public_id;
|
||||
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
7: []byte(`
|
||||
begin;
|
||||
|
||||
/*
|
||||
|
||||
┌────────────────┐ ┌────────────────┐
|
||||
@@ -1293,13 +1268,8 @@ begin;
|
||||
before update of iam_user_id on auth_account
|
||||
for each row
|
||||
execute procedure update_iam_user_auth_account();
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
8: []byte(`
|
||||
begin;
|
||||
|
||||
-- For now at least the IDs will be the same as the name, because this allows us
|
||||
-- to not have to persist some generated ID to worker and controller nodes.
|
||||
-- Eventually we may want them to diverge, so we have both here for now.
|
||||
@@ -1346,14 +1316,9 @@ create trigger
|
||||
before
|
||||
update on recovery_nonces
|
||||
for each row execute procedure immutable_columns('nonce', 'create_time');
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
11: []byte(`
|
||||
begin;
|
||||
|
||||
-- an auth token belongs to 1 and only 1 auth account
|
||||
-- an auth token belongs to 1 and only 1 auth account
|
||||
-- an auth account can have 0 to many auth tokens
|
||||
create table auth_token (
|
||||
public_id wt_public_id primary key,
|
||||
@@ -1485,13 +1450,8 @@ begin;
|
||||
before
|
||||
update on auth_token
|
||||
for each row execute procedure immutable_columns('public_id', 'auth_account_id', 'create_time');
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
12: []byte(`
|
||||
begin;
|
||||
|
||||
/*
|
||||
|
||||
┌────────────────┐ ┌──────────────────────┐ ┌────────────────────────────┐
|
||||
@@ -1784,14 +1744,9 @@ begin;
|
||||
('auth_password_method', 1),
|
||||
('auth_password_account', 1),
|
||||
('auth_password_credential', 1);
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
13: []byte(`
|
||||
begin;
|
||||
|
||||
create table auth_password_argon2_conf (
|
||||
create table auth_password_argon2_conf (
|
||||
private_id wt_private_id primary key
|
||||
references auth_password_conf (private_id)
|
||||
on delete cascade
|
||||
@@ -1936,14 +1891,9 @@ begin;
|
||||
values
|
||||
('auth_password_argon2_conf', 1),
|
||||
('auth_password_argon2_cred', 1);
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
14: []byte(`
|
||||
begin;
|
||||
|
||||
-- auth_password_conf_union is a union of the configuration settings
|
||||
-- auth_password_conf_union is a union of the configuration settings
|
||||
-- of all supported key derivation functions.
|
||||
-- It will be updated as new key derivation functions are supported.
|
||||
create or replace view auth_password_conf_union as
|
||||
@@ -1964,13 +1914,8 @@ begin;
|
||||
from auth_password_method pm
|
||||
inner join auth_password_conf_union c
|
||||
on pm.password_conf_id = c.password_conf_id;
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
20: []byte(`
|
||||
begin;
|
||||
|
||||
/*
|
||||
|
||||
┌─────────────────┐
|
||||
@@ -2135,13 +2080,8 @@ begin;
|
||||
('host_catalog', 1),
|
||||
('host', 1),
|
||||
('host_set', 1);
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
22: []byte(`
|
||||
begin;
|
||||
|
||||
/*
|
||||
|
||||
┌─────────────────┐ ┌─────────────────────┐
|
||||
@@ -2339,13 +2279,8 @@ begin;
|
||||
('static_host', 1),
|
||||
('static_host_set', 1),
|
||||
('static_host_set_member', 1);
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
30: []byte(`
|
||||
begin;
|
||||
|
||||
-- kms_version_column() will increment the version column whenever row data
|
||||
-- is inserted and should only be used in an before insert trigger. This
|
||||
-- function will overwrite any explicit values to the version column.
|
||||
@@ -2371,12 +2306,8 @@ comment on function
|
||||
kms_version_column()
|
||||
is
|
||||
'function used in before insert triggers to properly set version columns for kms_* tables with a version column';
|
||||
|
||||
commit;
|
||||
`),
|
||||
31: []byte(`
|
||||
begin;
|
||||
|
||||
/*
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
├────────────────────────────────────────────────────────────────────────────────────────────┐ ○
|
||||
@@ -2698,13 +2629,8 @@ before insert on kms_token_key_version
|
||||
values
|
||||
('kms_root_key', 1),
|
||||
('kms_root_key_version', 1);
|
||||
|
||||
commit;
|
||||
`),
|
||||
40: []byte(`
|
||||
begin;
|
||||
|
||||
|
||||
-- insert_target_subtype() is a before insert trigger
|
||||
-- function for subtypes of target
|
||||
create or replace function
|
||||
@@ -2770,8 +2696,6 @@ end if;
|
||||
return new;
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
commit;
|
||||
`),
|
||||
41: []byte(`
|
||||
/*
|
||||
@@ -2809,8 +2733,6 @@ commit;
|
||||
|
||||
*/
|
||||
|
||||
begin;
|
||||
|
||||
create table target (
|
||||
public_id wt_public_id primary key,
|
||||
scope_id wt_scope_id not null
|
||||
@@ -2963,13 +2885,8 @@ insert into oplog_ticket
|
||||
(name, version)
|
||||
values
|
||||
('target_tcp', 1);
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
50: []byte(`
|
||||
begin;
|
||||
|
||||
/*
|
||||
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
@@ -3455,13 +3372,8 @@ begin;
|
||||
session_state ss
|
||||
where
|
||||
s.public_id = ss.session_id;
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
51: []byte(`
|
||||
begin;
|
||||
|
||||
/*
|
||||
|
||||
┌────────────────┐
|
||||
@@ -3835,15 +3747,9 @@ create or replace function
|
||||
);
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
60: []byte(`
|
||||
begin;
|
||||
|
||||
create extension if not exists "pgcrypto";
|
||||
create extension if not exists "pgcrypto";
|
||||
|
||||
create domain wh_inet_port as integer
|
||||
check(
|
||||
@@ -3923,14 +3829,9 @@ begin;
|
||||
as $$
|
||||
select wh_time_id(current_timestamp);
|
||||
$$ language sql;
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
62: []byte(`
|
||||
begin;
|
||||
|
||||
create table wh_date_dimension (
|
||||
create table wh_date_dimension (
|
||||
id integer primary key,
|
||||
date date not null,
|
||||
calendar_quarter wh_dim_text,
|
||||
@@ -4023,14 +3924,9 @@ begin;
|
||||
date_trunc('day', current_timestamp) + interval '24 hours' - interval '1 second',
|
||||
interval '1 second'
|
||||
) as t(second);
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
65: []byte(`
|
||||
begin;
|
||||
|
||||
create table wh_host_dimension (
|
||||
create table wh_host_dimension (
|
||||
-- random id generated using encode(digest(gen_random_bytes(16), 'sha256'), 'base64')
|
||||
-- this is done to prevent conflicts with rows in other clusters
|
||||
-- which enables warehouse data from multiple clusters to be loaded into a
|
||||
@@ -4255,14 +4151,9 @@ begin;
|
||||
from wh_user_dimension
|
||||
where current_row_indicator = 'Current'
|
||||
;
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
66: []byte(`
|
||||
begin;
|
||||
|
||||
-- wh_upsert_host returns the wh_host_dimension id for p_host_id,
|
||||
-- wh_upsert_host returns the wh_host_dimension id for p_host_id,
|
||||
-- p_host_set_id, and p_target_id. wh_upsert_host compares the current values
|
||||
-- in the wh_host_dimension with the current values in the operational tables
|
||||
-- for the provide parameters. If the values between the operational tables
|
||||
@@ -4397,13 +4288,9 @@ begin;
|
||||
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
commit;
|
||||
|
||||
`),
|
||||
68: []byte(`
|
||||
begin;
|
||||
|
||||
-- Column names for numeric fields that are not a measurement end in id or
|
||||
-- Column names for numeric fields that are not a measurement end in id or
|
||||
-- number. This naming convention enables automatic field type detection in
|
||||
-- certain data analysis tools.
|
||||
-- https://help.tableau.com/current/pro/desktop/en-us/data_clean_adm.htm
|
||||
@@ -4585,14 +4472,9 @@ begin;
|
||||
'additive measurement.';
|
||||
|
||||
create index on wh_session_connection_accumulating_fact(session_id);
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
69: []byte(`
|
||||
begin;
|
||||
|
||||
-- wh_rollup_connections calculates the aggregate values from
|
||||
-- wh_rollup_connections calculates the aggregate values from
|
||||
-- wh_session_connection_accumulating_fact for p_session_id and updates
|
||||
-- wh_session_accumulating_fact for p_session_id with those values.
|
||||
create or replace function wh_rollup_connections(p_session_id wt_public_id)
|
||||
@@ -4861,13 +4743,8 @@ begin;
|
||||
after insert on session_connection_state
|
||||
for each row
|
||||
execute function wh_insert_session_connection_state();
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
1001: []byte(`
|
||||
begin;
|
||||
|
||||
-- This series of expressions fixes the primary key on the server table
|
||||
alter table session
|
||||
drop constraint session_server_id_server_type_fkey;
|
||||
@@ -4987,8 +4864,6 @@ create table server_tag (
|
||||
value wt_tagpair,
|
||||
primary key(server_id, key, value)
|
||||
);
|
||||
|
||||
commit;
|
||||
`),
|
||||
1080: []byte(`
|
||||
begin;
|
||||
|
||||
@@ -2,7 +2,6 @@ package schema
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/docker"
|
||||
@@ -42,24 +41,3 @@ func TestMigrateStore(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, ran)
|
||||
}
|
||||
|
||||
func TestMigrateStore_Dirty(t *testing.T) {
|
||||
dialect := "postgres"
|
||||
ctx := context.Background()
|
||||
|
||||
c, u, _, err := docker.StartDbInDocker(dialect)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, c())
|
||||
})
|
||||
|
||||
// Mark the db as dirty indicating a previously run failed migration
|
||||
db, err := sql.Open(dialect, u)
|
||||
require.NoError(t, err)
|
||||
m, err := NewManager(ctx, dialect, db)
|
||||
m.driver.SetVersion(ctx, -1, true)
|
||||
|
||||
b, err := MigrateStore(ctx, dialect, u)
|
||||
assert.Error(t, err)
|
||||
assert.False(t, b)
|
||||
}
|
||||
Reference in new issue
Block a user