github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/mergeable_indexes_setup_test.go (about)

     1  // Copyright 2020 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  	"fmt"
    20  	"strings"
    21  	"testing"
    22  
    23  	sqle "github.com/dolthub/go-mysql-server"
    24  	"github.com/dolthub/go-mysql-server/sql"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    31  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/lookup"
    32  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/sqlutil"
    33  	"github.com/dolthub/dolt/go/store/types"
    34  )
    35  
    36  func setupMergeableIndexes(t *testing.T, tableName, insertQuery string) (*sqle.Engine, *env.DoltEnv, *testMergeableIndexDb, []*indexTuple, *doltdb.RootValue) {
    37  	dEnv := dtestutils.CreateTestEnv()
    38  	root, err := dEnv.WorkingRoot(context.Background())
    39  	require.NoError(t, err)
    40  	db := NewDatabase("dolt", dEnv.DbData())
    41  	engine, sqlCtx, err := NewTestEngine(context.Background(), db, root)
    42  	require.NoError(t, err)
    43  
    44  	_, iter, err := engine.Query(sqlCtx, fmt.Sprintf(`CREATE TABLE %s (
    45  		pk bigint PRIMARY KEY,
    46  		v1 bigint,
    47  		v2 bigint,
    48  		INDEX idxv1 (v1),
    49  		INDEX idxv2v1 (v2,v1)
    50  	)`, tableName))
    51  	require.NoError(t, err)
    52  	require.NoError(t, drainIter(sqlCtx, iter))
    53  
    54  	_, iter, err = engine.Query(sqlCtx, insertQuery)
    55  	require.NoError(t, err)
    56  	require.NoError(t, drainIter(sqlCtx, iter))
    57  
    58  	sqlTbl, ok, err := db.GetTableInsensitive(sqlCtx, tableName)
    59  	require.NoError(t, err)
    60  	require.True(t, ok)
    61  	tbl, ok := sqlTbl.(*AlterableDoltTable)
    62  	require.True(t, ok)
    63  
    64  	idxv1, ok := tbl.sch.Indexes().GetByNameCaseInsensitive("idxv1")
    65  	require.True(t, ok)
    66  
    67  	table, err := tbl.doltTable(sqlCtx)
    68  	require.NoError(t, err)
    69  
    70  	idxv1RowData, err := table.GetIndexRowData(context.Background(), idxv1.Name())
    71  	require.NoError(t, err)
    72  	idxv1Cols := make([]schema.Column, idxv1.Count())
    73  	for i, tag := range idxv1.IndexedColumnTags() {
    74  		idxv1Cols[i], _ = idxv1.GetColumn(tag)
    75  	}
    76  	idxv1ToTuple := &indexTuple{
    77  		nbf:  idxv1RowData.Format(),
    78  		cols: idxv1Cols,
    79  	}
    80  
    81  	idxv2v1, ok := tbl.sch.Indexes().GetByNameCaseInsensitive("idxv2v1")
    82  	require.True(t, ok)
    83  	idxv2v1RowData, err := table.GetIndexRowData(context.Background(), idxv2v1.Name())
    84  	require.NoError(t, err)
    85  	idxv2v1Cols := make([]schema.Column, idxv2v1.Count())
    86  	for i, tag := range idxv2v1.IndexedColumnTags() {
    87  		idxv2v1Cols[i], _ = idxv2v1.GetColumn(tag)
    88  	}
    89  	idxv2v1ToTuple := &indexTuple{
    90  		nbf:  idxv2v1RowData.Format(),
    91  		cols: idxv2v1Cols,
    92  	}
    93  
    94  	mergeableDb := &testMergeableIndexDb{
    95  		t:   t,
    96  		tbl: tbl,
    97  	}
    98  	engine = sqle.NewDefault()
    99  	engine.AddDatabase(mergeableDb)
   100  
   101  	// Get an updated root to use for the rest of the test
   102  	root, _ = DSessFromSess(sqlCtx.Session).GetRoot(mergeableDb.Name())
   103  
   104  	return engine, dEnv, mergeableDb, []*indexTuple{
   105  		idxv1ToTuple,
   106  		idxv2v1ToTuple,
   107  		{
   108  			nbf:  idxv2v1RowData.Format(),
   109  			cols: idxv2v1Cols[:len(idxv2v1Cols)-1],
   110  		},
   111  	}, root
   112  }
   113  
   114  // Database made to test mergeable indexes while using the full SQL engine.
   115  type testMergeableIndexDb struct {
   116  	t           *testing.T
   117  	tbl         *AlterableDoltTable
   118  	finalRanges func([]lookup.Range) // We return the final range set to compare to the expected ranges
   119  }
   120  
   121  func (db *testMergeableIndexDb) Name() string {
   122  	return "dolt"
   123  }
   124  func (db *testMergeableIndexDb) GetTableInsensitive(_ *sql.Context, tblName string) (sql.Table, bool, error) {
   125  	if strings.ToLower(tblName) == strings.ToLower(db.tbl.tableName) {
   126  		return &testMergeableIndexTable{
   127  			AlterableDoltTable: db.tbl,
   128  			t:                  db.t,
   129  			finalRanges:        db.finalRanges,
   130  		}, true, nil
   131  	}
   132  	return nil, false, nil
   133  }
   134  func (db *testMergeableIndexDb) GetTableNames(_ *sql.Context) ([]string, error) {
   135  	return []string{db.tbl.tableName}, nil
   136  }
   137  
   138  // Table made to test mergeable indexes by intercepting specific index-related functions.
   139  type testMergeableIndexTable struct {
   140  	*AlterableDoltTable
   141  	t           *testing.T
   142  	il          *testMergeableIndexLookup
   143  	finalRanges func([]lookup.Range) // We return the final range set to compare to the expected ranges
   144  }
   145  
   146  var _ sql.IndexedTable = (*testMergeableIndexTable)(nil)
   147  
   148  func (tbl *testMergeableIndexTable) GetIndexes(ctx *sql.Context) ([]sql.Index, error) {
   149  	indexes, err := tbl.AlterableDoltTable.GetIndexes(ctx)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	for i, index := range indexes {
   154  		indexes[i] = &testMergeableDoltIndex{
   155  			doltIndex:   index.(*doltIndex),
   156  			t:           tbl.t,
   157  			finalRanges: tbl.finalRanges,
   158  		}
   159  	}
   160  	return indexes, nil
   161  }
   162  
   163  func (tbl *testMergeableIndexTable) WithIndexLookup(lookup sql.IndexLookup) sql.Table {
   164  	il, ok := lookup.(*testMergeableIndexLookup)
   165  	require.True(tbl.t, ok)
   166  	return &testMergeableIndexTable{
   167  		AlterableDoltTable: tbl.AlterableDoltTable,
   168  		t:                  tbl.t,
   169  		il:                 il,
   170  		finalRanges:        tbl.finalRanges,
   171  	}
   172  }
   173  
   174  type testProjectedMergableIndexTable struct {
   175  	*testMergeableIndexTable
   176  	cols []string
   177  }
   178  
   179  func (tbl *testMergeableIndexTable) WithProjection(colNames []string) sql.Table {
   180  	return &testProjectedMergableIndexTable{tbl, colNames}
   181  }
   182  
   183  func (tbl *testMergeableIndexTable) Partitions(_ *sql.Context) (sql.PartitionIter, error) {
   184  	rowData := tbl.il.IndexRowData()
   185  	return sqlutil.NewSinglePartitionIter(rowData), nil
   186  }
   187  
   188  func (tbl *testMergeableIndexTable) PartitionRows(ctx *sql.Context, part sql.Partition) (sql.RowIter, error) {
   189  	return tbl.il.RowIter(ctx, part.(sqlutil.SinglePartition).RowData)
   190  }
   191  
   192  // Index made to test mergeable indexes by intercepting all calls that return lookups and returning modified lookups.
   193  type testMergeableDoltIndex struct {
   194  	*doltIndex
   195  	t           *testing.T
   196  	finalRanges func([]lookup.Range) // We return the final range set to compare to the expected ranges
   197  }
   198  
   199  func (di *testMergeableDoltIndex) Get(keys ...interface{}) (sql.IndexLookup, error) {
   200  	indexLookup, err := di.doltIndex.Get(keys...)
   201  	return &testMergeableIndexLookup{
   202  		doltIndexLookup: indexLookup.(*doltIndexLookup),
   203  		t:               di.t,
   204  		finalRanges:     di.finalRanges,
   205  	}, err
   206  }
   207  func (di *testMergeableDoltIndex) Not(keys ...interface{}) (sql.IndexLookup, error) {
   208  	indexLookup, err := di.doltIndex.Not(keys...)
   209  	return &testMergeableIndexLookup{
   210  		doltIndexLookup: indexLookup.(*doltIndexLookup),
   211  		t:               di.t,
   212  		finalRanges:     di.finalRanges,
   213  	}, err
   214  }
   215  func (di *testMergeableDoltIndex) AscendGreaterOrEqual(keys ...interface{}) (sql.IndexLookup, error) {
   216  	indexLookup, err := di.doltIndex.AscendGreaterOrEqual(keys...)
   217  	return &testMergeableIndexLookup{
   218  		doltIndexLookup: indexLookup.(*doltIndexLookup),
   219  		t:               di.t,
   220  		finalRanges:     di.finalRanges,
   221  	}, err
   222  }
   223  func (di *testMergeableDoltIndex) AscendLessThan(keys ...interface{}) (sql.IndexLookup, error) {
   224  	indexLookup, err := di.doltIndex.AscendLessThan(keys...)
   225  	return &testMergeableIndexLookup{
   226  		doltIndexLookup: indexLookup.(*doltIndexLookup),
   227  		t:               di.t,
   228  		finalRanges:     di.finalRanges,
   229  	}, err
   230  }
   231  func (di *testMergeableDoltIndex) AscendRange(greaterOrEqual, lessThanOrEqual []interface{}) (sql.IndexLookup, error) {
   232  	indexLookup, err := di.doltIndex.AscendRange(greaterOrEqual, lessThanOrEqual)
   233  	return &testMergeableIndexLookup{
   234  		doltIndexLookup: indexLookup.(*doltIndexLookup),
   235  		t:               di.t,
   236  		finalRanges:     di.finalRanges,
   237  	}, err
   238  }
   239  func (di *testMergeableDoltIndex) DescendGreater(keys ...interface{}) (sql.IndexLookup, error) {
   240  	indexLookup, err := di.doltIndex.DescendGreater(keys...)
   241  	return &testMergeableIndexLookup{
   242  		doltIndexLookup: indexLookup.(*doltIndexLookup),
   243  		t:               di.t,
   244  		finalRanges:     di.finalRanges,
   245  	}, err
   246  }
   247  func (di *testMergeableDoltIndex) DescendLessOrEqual(keys ...interface{}) (sql.IndexLookup, error) {
   248  	indexLookup, err := di.doltIndex.DescendLessOrEqual(keys...)
   249  	return &testMergeableIndexLookup{
   250  		doltIndexLookup: indexLookup.(*doltIndexLookup),
   251  		t:               di.t,
   252  		finalRanges:     di.finalRanges,
   253  	}, err
   254  }
   255  func (di *testMergeableDoltIndex) DescendRange(lessOrEqual, greaterOrEqual []interface{}) (sql.IndexLookup, error) {
   256  	indexLookup, err := di.doltIndex.DescendRange(lessOrEqual, greaterOrEqual)
   257  	return &testMergeableIndexLookup{
   258  		doltIndexLookup: indexLookup.(*doltIndexLookup),
   259  		t:               di.t,
   260  		finalRanges:     di.finalRanges,
   261  	}, err
   262  }
   263  
   264  // Lookup made to test mergeable indexes by intercepting the lookup functions and adding tracking for testing.
   265  type testMergeableIndexLookup struct {
   266  	*doltIndexLookup
   267  	t           *testing.T
   268  	finalRanges func([]lookup.Range) // We return the final range set to compare to the expected ranges
   269  }
   270  
   271  func (il *testMergeableIndexLookup) IsMergeable(indexLookup sql.IndexLookup) bool {
   272  	return il.doltIndexLookup.IsMergeable(indexLookup.(*testMergeableIndexLookup).doltIndexLookup)
   273  }
   274  func (il *testMergeableIndexLookup) Intersection(indexLookups ...sql.IndexLookup) (sql.IndexLookup, error) {
   275  	newLookups := make([]sql.IndexLookup, len(indexLookups))
   276  	for i, otherIl := range indexLookups {
   277  		newLookups[i] = otherIl.(*testMergeableIndexLookup).doltIndexLookup
   278  	}
   279  	intersectedIl, err := il.doltIndexLookup.Intersection(newLookups...)
   280  	if err != nil {
   281  		return nil, err
   282  	}
   283  	return &testMergeableIndexLookup{
   284  		doltIndexLookup: intersectedIl.(*doltIndexLookup),
   285  		t:               il.t,
   286  		finalRanges:     il.finalRanges,
   287  	}, nil
   288  }
   289  func (il *testMergeableIndexLookup) Union(indexLookups ...sql.IndexLookup) (sql.IndexLookup, error) {
   290  	newLookups := make([]sql.IndexLookup, len(indexLookups))
   291  	for i, otherIl := range indexLookups {
   292  		newLookups[i] = otherIl.(*testMergeableIndexLookup).doltIndexLookup
   293  	}
   294  	unionedIl, err := il.doltIndexLookup.Union(newLookups...)
   295  	if err != nil {
   296  		return nil, err
   297  	}
   298  	return &testMergeableIndexLookup{
   299  		doltIndexLookup: unionedIl.(*doltIndexLookup),
   300  		t:               il.t,
   301  		finalRanges:     il.finalRanges,
   302  	}, nil
   303  }
   304  func (il *testMergeableIndexLookup) RowIter(ctx *sql.Context, rowData types.Map) (sql.RowIter, error) {
   305  	il.finalRanges(il.ranges) // this is where the ranges turn into noms.ReadRanges, so we return the final slice here
   306  	return il.doltIndexLookup.RowIter(ctx, rowData, nil)
   307  }
   308  
   309  // indexTuple converts integers into the appropriate tuple for comparison against ranges
   310  type indexTuple struct {
   311  	nbf  *types.NomsBinFormat
   312  	cols []schema.Column
   313  }
   314  
   315  func (it *indexTuple) tuple(vals ...int) types.Tuple {
   316  	if len(it.cols) != len(vals) {
   317  		panic("len of columns in index does not match the given number of values")
   318  	}
   319  	valsWithTags := make([]types.Value, len(vals)*2)
   320  	for i, val := range vals {
   321  		valsWithTags[2*i] = types.Uint(it.cols[i].Tag)
   322  		valsWithTags[2*i+1] = types.Int(val)
   323  	}
   324  	tpl, err := types.NewTuple(it.nbf, valsWithTags...)
   325  	if err != nil {
   326  		panic(err)
   327  	}
   328  	return tpl
   329  }
   330  
   331  func (it *indexTuple) nilTuple() types.Tuple {
   332  	valsWithTags := make([]types.Value, len(it.cols)*2)
   333  	for i := 0; i < len(it.cols); i++ {
   334  		valsWithTags[2*i] = types.Uint(it.cols[i].Tag)
   335  		valsWithTags[2*i+1] = types.NullValue
   336  	}
   337  	tpl, err := types.NewTuple(it.nbf, valsWithTags...)
   338  	if err != nil {
   339  		panic(err)
   340  	}
   341  	return tpl
   342  }