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  }