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 }