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 }