github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/migrate/integration_test.go (about)

     1  // Copyright 2022 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package migrate_test
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql"
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  
    25  	"github.com/dolthub/dolt/go/cmd/dolt/commands"
    26  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/migrate"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    31  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    32  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
    33  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle"
    34  	"github.com/dolthub/dolt/go/store/chunks"
    35  	"github.com/dolthub/dolt/go/store/datas"
    36  	"github.com/dolthub/dolt/go/store/prolly/tree"
    37  	"github.com/dolthub/dolt/go/store/types"
    38  )
    39  
    40  type migrationTest struct {
    41  	name    string
    42  	hook    setupHook
    43  	setup   []string
    44  	asserts []assertion
    45  	err     string
    46  }
    47  
    48  // a setupHook performs special setup operations that cannot
    49  // be performed via SQL statements (eg TEXT type PRIMARY KEY)
    50  type setupHook func(context.Context, *env.DoltEnv) (*env.DoltEnv, error)
    51  
    52  type assertion struct {
    53  	query    string
    54  	expected []sql.Row
    55  }
    56  
    57  func TestMigration(t *testing.T) {
    58  	if types.Format_Default != types.Format_LD_1 {
    59  		t.Skip()
    60  	}
    61  
    62  	tests := []migrationTest{
    63  		{
    64  			name: "smoke test",
    65  			setup: []string{
    66  				"CREATE TABLE test (pk int primary key)",
    67  				"INSERT INTO test VALUES (1),(2),(3)",
    68  				"CALL dolt_add('.')",
    69  				"CALL dolt_commit('-am', 'new table')",
    70  			},
    71  			asserts: []assertion{
    72  				{
    73  					query:    "SELECT * FROM test",
    74  					expected: []sql.Row{{int32(1)}, {int32(2)}, {int32(3)}},
    75  				},
    76  				{
    77  					query:    "SELECT count(*) FROM dolt_log",
    78  					expected: []sql.Row{{int64(2)}},
    79  				},
    80  				{
    81  					query:    "SELECT count(*) FROM `dolt/dolt_migrated_commits`.dolt_commit_mapping",
    82  					expected: []sql.Row{{int64(2)}},
    83  				},
    84  			},
    85  		},
    86  		{
    87  			name: "TEXT primary key, BLOB secondary key",
    88  			hook: SetupHookRefKeys,
    89  			setup: []string{
    90  				// from setup hook:
    91  				//   CREATE TABLE test (
    92  				//     pk TEXT PRIMARY KEY,
    93  				//     c0 int,
    94  				//     c1 BLOB,
    95  				//     INDEX blob_idx(c1),
    96  				//   );
    97  				"INSERT INTO test VALUES ('a', 2, 'a')",
    98  				"CALL dolt_add('.')",
    99  				"CALL dolt_commit('-am', 'new table')",
   100  			},
   101  			asserts: []assertion{
   102  				{
   103  					query:    "SELECT * FROM test",
   104  					expected: []sql.Row{{"a", int32(2), []byte("a")}},
   105  				},
   106  				{
   107  					query: "DESCRIBE test",
   108  					expected: []sql.Row{
   109  						{"pk", "varchar(16383)", "NO", "PRI", "NULL", ""},
   110  						{"c0", "int", "YES", "", "NULL", ""},
   111  						{"c1", "varbinary(16383)", "YES", "MUL", "NULL", ""},
   112  					},
   113  				},
   114  			},
   115  		},
   116  		{
   117  			name: "create more commits",
   118  			setup: []string{
   119  				"CREATE TABLE test (pk int primary key)",
   120  				"INSERT INTO test VALUES (1),(2),(3)",
   121  				"CALL dolt_commit('-Am', 'new table')",
   122  				"INSERT INTO test VALUES (4)",
   123  				"CALL dolt_commit('-am', 'added row 4')",
   124  				"INSERT INTO test VALUES (5)",
   125  				"CALL dolt_commit('-am', 'added row 5')",
   126  			},
   127  			asserts: []assertion{
   128  				{
   129  					query:    "SELECT count(*) FROM dolt_log",
   130  					expected: []sql.Row{{int64(4)}},
   131  				},
   132  				{
   133  					query:    "SELECT count(*) FROM `dolt/dolt_migrated_commits`.dolt_commit_mapping",
   134  					expected: []sql.Row{{int64(4)}},
   135  				},
   136  				{
   137  					query:    "SELECT count(*) FROM `dolt/dolt_migrated_commits`.dolt_commit_mapping WHERE new_commit_hash IN (SELECT commit_hash FROM dolt_log)",
   138  					expected: []sql.Row{{int64(4)}},
   139  				},
   140  				{
   141  					query:    "SELECT count(*) FROM `dolt/dolt_migrated_commits`.dolt_commit_mapping WHERE new_commit_hash NOT IN (SELECT commit_hash FROM dolt_log)",
   142  					expected: []sql.Row{{int64(0)}},
   143  				},
   144  			},
   145  		},
   146  	}
   147  
   148  	for _, test := range tests {
   149  		t.Run(test.name, func(t *testing.T) {
   150  			ctx := context.Background()
   151  			preEnv := setupMigrationTest(t, ctx, test)
   152  			postEnv := runMigration(t, ctx, preEnv)
   153  			root, err := postEnv.WorkingRoot(ctx)
   154  			require.NoError(t, err)
   155  			for _, a := range test.asserts {
   156  				actual, err := sqle.ExecuteSelect(postEnv, root, a.query)
   157  				assert.NoError(t, err)
   158  				assert.Equal(t, a.expected, actual)
   159  			}
   160  		})
   161  	}
   162  }
   163  
   164  func setupMigrationTest(t *testing.T, ctx context.Context, test migrationTest) *env.DoltEnv {
   165  	dEnv := dtestutils.CreateTestEnv()
   166  
   167  	// run setup hook before other setup queries
   168  	if test.hook != nil {
   169  		var err error
   170  		dEnv, err = test.hook(ctx, dEnv)
   171  		require.NoError(t, err)
   172  	}
   173  	cliCtx, err := commands.NewArgFreeCliContext(ctx, dEnv)
   174  	require.NoError(t, err)
   175  
   176  	cmd := commands.SqlCmd{}
   177  	for _, query := range test.setup {
   178  		code := cmd.Exec(ctx, cmd.Name(), []string{"-q", query}, dEnv, cliCtx)
   179  		require.Equal(t, 0, code)
   180  	}
   181  	return dEnv
   182  }
   183  
   184  func SetupHookRefKeys(ctx context.Context, dEnv *env.DoltEnv) (*env.DoltEnv, error) {
   185  	pk, _ := schema.NewColumnWithTypeInfo("pk", 1, typeinfo.TextType, true, "", false, "", schema.NotNullConstraint{})
   186  	c0, _ := schema.NewColumnWithTypeInfo("c0", 2, typeinfo.Int32Type, false, "", false, "")
   187  	c1, _ := schema.NewColumnWithTypeInfo("c1", 3, typeinfo.BlobType, false, "", false, "")
   188  
   189  	sch, err := schema.SchemaFromCols(schema.NewColCollection(pk, c0, c1))
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  	_, err = sch.Indexes().AddIndexByColNames("blob_idx", []string{"c1"}, nil, schema.IndexProperties{IsUserDefined: true})
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	ws, err := dEnv.WorkingSet(ctx)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	root, err := doltdb.CreateEmptyTable(ctx, ws.WorkingRoot(), doltdb.TableName{Name: "test"}, sch)
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	if err = dEnv.UpdateWorkingSet(ctx, ws.WithWorkingRoot(root)); err != nil {
   207  		return nil, err
   208  	}
   209  	return dEnv, nil
   210  }
   211  
   212  func runMigration(t *testing.T, ctx context.Context, preEnv *env.DoltEnv) (postEnv *env.DoltEnv) {
   213  	ddb, err := initTestMigrationDB(ctx)
   214  	require.NoError(t, err)
   215  	postEnv = &env.DoltEnv{
   216  		Version:   preEnv.Version,
   217  		Config:    preEnv.Config,
   218  		RepoState: preEnv.RepoState,
   219  		FS:        preEnv.FS,
   220  		DoltDB:    ddb,
   221  	}
   222  
   223  	err = migrate.TraverseDAG(ctx, migrate.Environment{}, preEnv.DoltDB, postEnv.DoltDB)
   224  	assert.NoError(t, err)
   225  	return
   226  }
   227  
   228  func initTestMigrationDB(ctx context.Context) (*doltdb.DoltDB, error) {
   229  	var db datas.Database
   230  	storage := &chunks.MemoryStorage{}
   231  	cs := storage.NewViewWithFormat("__DOLT__")
   232  	vrw := types.NewValueStore(cs)
   233  	ns := tree.NewNodeStore(cs)
   234  	db = datas.NewTypesDatabase(vrw, ns)
   235  
   236  	name, email := "user", "user@fake.horse"
   237  	meta, err := datas.NewCommitMeta(name, email, "test migration")
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  
   242  	rv, err := doltdb.EmptyRootValue(ctx, vrw, ns)
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  
   247  	ds, err := db.GetDataset(ctx, ref.NewInternalRef("migration").String())
   248  	if err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	_, err = db.Commit(ctx, ds, rv.NomsValue(), datas.CommitOptions{Meta: meta})
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  	return doltdb.DoltDBFromCS(cs), nil
   257  }