github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/procedures_table_test.go (about) 1 // Copyright 2023 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 sqle 16 17 import ( 18 "context" 19 "io" 20 "testing" 21 "time" 22 23 "github.com/dolthub/go-mysql-server/sql" 24 gmstypes "github.com/dolthub/go-mysql-server/sql/types" 25 "github.com/stretchr/testify/assert" 26 "github.com/stretchr/testify/require" 27 28 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 29 "github.com/dolthub/dolt/go/libraries/doltcore/dtestutils" 30 "github.com/dolthub/dolt/go/libraries/doltcore/env" 31 "github.com/dolthub/dolt/go/libraries/doltcore/table/editor" 32 ) 33 34 // Tests the codepath for migrating the dolt_procedures system table from an older schema 35 // to the latest schema 36 func TestProceduresMigration(t *testing.T) { 37 dEnv := dtestutils.CreateTestEnv() 38 tmpDir, err := dEnv.TempTableFilesDir() 39 require.NoError(t, err) 40 opts := editor.Options{Deaf: dEnv.DbEaFactory(), Tempdir: tmpDir} 41 42 timestamp := time.Now().Truncate(time.Minute).UTC() 43 44 ctx, db := newDatabaseWithProcedures(t, dEnv, opts, timestamp) 45 46 t.Run("test migration logic", func(t *testing.T) { 47 // Call the logic to migrate it to the latest schema 48 tbl, err := DoltProceduresGetTable(ctx, *db) 49 require.NoError(t, err) 50 51 // Assert that the data was migrated correctly 52 rows := readAllRows(ctx, t, tbl) 53 expectedRows := []sql.Row{ 54 {"proc1", "create procedure proc1() SELECT 42 as pk from dual;", timestamp, timestamp, nil}, 55 {"proc2", "create procedure proc2() SELECT 'HELLO' as greeting from dual;", timestamp, timestamp, nil}, 56 } 57 assert.Equal(t, expectedRows, rows) 58 }) 59 60 t.Run("test that fetching stored procedure triggers the migration logic", func(t *testing.T) { 61 // Call the logic to migrate it to the latest schema 62 _, found, err := db.GetStoredProcedure(ctx, "proc1") 63 require.NoError(t, err) 64 require.True(t, found) 65 66 // Assert that the data was migrated correctly 67 tbl, found, err := db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) 68 require.NoError(t, err) 69 require.True(t, found) 70 rows := readAllRows(ctx, t, tbl.(*WritableDoltTable)) 71 expectedRows := []sql.Row{ 72 {"proc1", "create procedure proc1() SELECT 42 as pk from dual;", timestamp, timestamp, nil}, 73 {"proc2", "create procedure proc2() SELECT 'HELLO' as greeting from dual;", timestamp, timestamp, nil}, 74 } 75 assert.Equal(t, expectedRows, rows) 76 }) 77 78 t.Run("test that adding a new stored procedure triggers the migration logic", func(t *testing.T) { 79 // Call the logic to migrate it to the latest schema 80 proc3 := sql.StoredProcedureDetails{ 81 Name: "proc3", 82 CreateStatement: "create procedure proc3() SELECT 47 as pk from dual;", 83 CreatedAt: timestamp, 84 ModifiedAt: timestamp, 85 SqlMode: "NO_ENGINE_SUBSTITUTION", 86 } 87 err := db.SaveStoredProcedure(ctx, proc3) 88 require.NoError(t, err) 89 90 // Assert that the data was migrated correctly 91 tbl, found, err := db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) 92 require.NoError(t, err) 93 require.True(t, found) 94 rows := readAllRows(ctx, t, tbl.(*WritableDoltTable)) 95 expectedRows := []sql.Row{ 96 {"proc1", "create procedure proc1() SELECT 42 as pk from dual;", timestamp, timestamp, nil}, 97 {"proc2", "create procedure proc2() SELECT 'HELLO' as greeting from dual;", timestamp, timestamp, nil}, 98 {"proc3", "create procedure proc3() SELECT 47 as pk from dual;", timestamp, timestamp, "NO_ENGINE_SUBSTITUTION"}, 99 } 100 assert.Equal(t, expectedRows, rows) 101 }) 102 103 } 104 105 func newDatabaseWithProcedures(t *testing.T, dEnv *env.DoltEnv, opts editor.Options, timestamp time.Time) (*sql.Context, *Database) { 106 db, err := NewDatabase(context.Background(), "dolt", dEnv.DbData(), opts) 107 require.NoError(t, err) 108 109 _, ctx, err := NewTestEngine(dEnv, context.Background(), db) 110 require.NoError(t, err) 111 112 // Create the dolt_procedures table with its original schema 113 err = db.createSqlTable(ctx, doltdb.ProceduresTableName, "", sql.NewPrimaryKeySchema(sql.Schema{ 114 {Name: doltdb.ProceduresTableNameCol, Type: gmstypes.Text, Source: doltdb.ProceduresTableName, PrimaryKey: true}, 115 {Name: doltdb.ProceduresTableCreateStmtCol, Type: gmstypes.Text, Source: doltdb.ProceduresTableName, PrimaryKey: false}, 116 {Name: doltdb.ProceduresTableCreatedAtCol, Type: gmstypes.Timestamp, Source: doltdb.ProceduresTableName, PrimaryKey: false}, 117 {Name: doltdb.ProceduresTableModifiedAtCol, Type: gmstypes.Timestamp, Source: doltdb.ProceduresTableName, PrimaryKey: false}, 118 }), sql.Collation_Default, "") 119 require.NoError(t, err) 120 121 sqlTbl, found, err := db.GetTableInsensitive(ctx, doltdb.ProceduresTableName) 122 require.NoError(t, err) 123 require.True(t, found) 124 125 // Insert some test data for procedures 126 inserter := sqlTbl.(*WritableDoltTable).Inserter(ctx) 127 require.NoError(t, inserter.Insert(ctx, sql.Row{"proc1", "create procedure proc1() SELECT 42 as pk from dual;", timestamp, timestamp})) 128 require.NoError(t, inserter.Insert(ctx, sql.Row{"proc2", "create procedure proc2() SELECT 'HELLO' as greeting from dual;", timestamp, timestamp})) 129 require.NoError(t, inserter.Close(ctx)) 130 131 return ctx, &db 132 } 133 134 func readAllRows(ctx *sql.Context, t *testing.T, tbl *WritableDoltTable) []sql.Row { 135 iter, err := SqlTableToRowIter(ctx, tbl.DoltTable, nil) 136 require.NoError(t, err) 137 138 var rows []sql.Row 139 for { 140 row, err := iter.Next(ctx) 141 if err == io.EOF { 142 break 143 } 144 145 require.NoError(t, err) 146 rows = append(rows, row) 147 } 148 require.NoError(t, iter.Close(ctx)) 149 150 return rows 151 }