Wrap postgres up migrations into a single transaction (#887)

This commit is contained in:
Todd Knight authored and GitHub committed 2021-01-27 15:58:22 -07:00
1 parent c1576dddda
commit d2bb8820ea
9 files changed
+209 -217

No files matched your search

+19 -18
View File
@@ -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
}
+45 -12
View File
@@ -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
+72 -13
View File
@@ -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 {
+43 -7
View File
@@ -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)
})
}
+2 -2
View File
@@ -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)
}
+9 -134
View File
@@ -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;
-22
View File
@@ -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)
}