github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/sqle/dolt_index.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  	"errors"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql"
    22  
    23  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    24  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    25  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/lookup"
    26  	"github.com/dolthub/dolt/go/store/types"
    27  )
    28  
    29  type DoltIndex interface {
    30  	sql.Index
    31  	sql.AscendIndex
    32  	sql.DescendIndex
    33  	sql.NegateIndex
    34  	Schema() schema.Schema
    35  	IndexSchema() schema.Schema
    36  	TableData() types.Map
    37  	IndexRowData() types.Map
    38  	Equals(index DoltIndex) bool
    39  }
    40  
    41  type doltIndex struct {
    42  	cols         []schema.Column
    43  	db           sql.Database
    44  	id           string
    45  	indexRowData types.Map
    46  	indexSch     schema.Schema
    47  	table        *doltdb.Table
    48  	tableData    types.Map
    49  	tableName    string
    50  	tableSch     schema.Schema
    51  	unique       bool
    52  	comment      string
    53  	generated    bool
    54  }
    55  
    56  //TODO: have queries using IS NULL make use of indexes
    57  var _ DoltIndex = (*doltIndex)(nil)
    58  
    59  // AscendGreaterOrEqual implements sql.AscendIndex
    60  func (di *doltIndex) AscendGreaterOrEqual(keys ...interface{}) (sql.IndexLookup, error) {
    61  	tpl, err := di.keysToTuple(keys)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	return &doltIndexLookup{
    66  		idx: di,
    67  		ranges: []lookup.Range{
    68  			lookup.GreaterOrEqualRange(tpl),
    69  		},
    70  	}, nil
    71  }
    72  
    73  // AscendLessThan implements sql.AscendIndex
    74  func (di *doltIndex) AscendLessThan(keys ...interface{}) (sql.IndexLookup, error) {
    75  	tpl, err := di.keysToTuple(keys)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	return &doltIndexLookup{
    80  		idx: di,
    81  		ranges: []lookup.Range{
    82  			lookup.LessThanRange(tpl),
    83  		},
    84  	}, nil
    85  }
    86  
    87  // AscendRange implements sql.AscendIndex
    88  // TODO: rename this from AscendRange to BetweenRange or something
    89  func (di *doltIndex) AscendRange(greaterOrEqual, lessThanOrEqual []interface{}) (sql.IndexLookup, error) {
    90  	greaterTpl, err := di.keysToTuple(greaterOrEqual)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	lessTpl, err := di.keysToTuple(lessThanOrEqual)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	r, err := lookup.ClosedRange(greaterTpl, lessTpl)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	return &doltIndexLookup{
   103  		idx: di,
   104  		ranges: []lookup.Range{
   105  			r,
   106  		},
   107  	}, nil
   108  }
   109  
   110  // DescendGreater implements sql.DescendIndex
   111  func (di *doltIndex) DescendGreater(keys ...interface{}) (sql.IndexLookup, error) {
   112  	tpl, err := di.keysToTuple(keys)
   113  	if err != nil {
   114  		return nil, err
   115  	}
   116  	r, err := lookup.GreaterThanRange(tpl)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	return &doltIndexLookup{
   121  		idx: di,
   122  		ranges: []lookup.Range{
   123  			r,
   124  		},
   125  	}, nil
   126  }
   127  
   128  // DescendLessOrEqual implements sql.DescendIndex
   129  func (di *doltIndex) DescendLessOrEqual(keys ...interface{}) (sql.IndexLookup, error) {
   130  	tpl, err := di.keysToTuple(keys)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	r, err := lookup.LessOrEqualRange(tpl)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	return &doltIndexLookup{
   139  		idx: di,
   140  		ranges: []lookup.Range{
   141  			r,
   142  		},
   143  	}, nil
   144  }
   145  
   146  // DescendRange implements sql.DescendIndex
   147  // TODO: fix go-mysql-server to remove this duplicate function
   148  func (di *doltIndex) DescendRange(lessOrEqual, greaterOrEqual []interface{}) (sql.IndexLookup, error) {
   149  	return di.AscendRange(greaterOrEqual, lessOrEqual)
   150  }
   151  
   152  // Database implement sql.Index
   153  func (di *doltIndex) Database() string {
   154  	return di.db.Name()
   155  }
   156  
   157  // Expressions implements sql.Index
   158  func (di *doltIndex) Expressions() []string {
   159  	strs := make([]string, len(di.cols))
   160  	for i, col := range di.cols {
   161  		strs[i] = di.tableName + "." + col.Name
   162  	}
   163  	return strs
   164  }
   165  
   166  // Get implements sql.Index
   167  func (di *doltIndex) Get(keys ...interface{}) (sql.IndexLookup, error) {
   168  	tpl, err := di.keysToTuple(keys)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	r, err := lookup.ClosedRange(tpl, tpl)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	return &doltIndexLookup{
   177  		idx: di,
   178  		ranges: []lookup.Range{
   179  			r,
   180  		},
   181  	}, nil
   182  }
   183  
   184  // Not implements sql.NegateIndex
   185  func (di *doltIndex) Not(keys ...interface{}) (sql.IndexLookup, error) {
   186  	tpl, err := di.keysToTuple(keys)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  	r1 := lookup.LessThanRange(tpl)
   191  	r2, err := lookup.GreaterThanRange(tpl)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	return &doltIndexLookup{
   196  		idx: di,
   197  		ranges: []lookup.Range{
   198  			r1,
   199  			r2,
   200  		},
   201  	}, nil
   202  }
   203  
   204  // Has implements sql.Index
   205  func (*doltIndex) Has(partition sql.Partition, key ...interface{}) (bool, error) {
   206  	return false, errors.New("unimplemented")
   207  }
   208  
   209  // ID implements sql.Index
   210  func (di *doltIndex) ID() string {
   211  	return di.id
   212  }
   213  
   214  // IsUnique implements sql.Index
   215  func (di *doltIndex) IsUnique() bool {
   216  	return di.unique
   217  }
   218  
   219  // Comment implements sql.Index
   220  func (di *doltIndex) Comment() string {
   221  	return di.comment
   222  }
   223  
   224  // IndexType implements sql.Index
   225  func (di *doltIndex) IndexType() string {
   226  	return "BTREE"
   227  }
   228  
   229  // IsGenerated implements sql.Index
   230  func (di *doltIndex) IsGenerated() bool {
   231  	return di.generated
   232  }
   233  
   234  // Schema returns the dolt table schema of this index.
   235  func (di *doltIndex) Schema() schema.Schema {
   236  	return di.tableSch
   237  }
   238  
   239  // Schema returns the dolt index schema.
   240  func (di *doltIndex) IndexSchema() schema.Schema {
   241  	return di.indexSch
   242  }
   243  
   244  // Table implements sql.Index
   245  func (di *doltIndex) Table() string {
   246  	return di.tableName
   247  }
   248  
   249  // TableData returns the map of table data for this index (the map of the target table, not the index storage table)
   250  func (di *doltIndex) TableData() types.Map {
   251  	return di.tableData
   252  }
   253  
   254  // IndexRowData returns the map of index row data.
   255  func (di *doltIndex) IndexRowData() types.Map {
   256  	return di.indexRowData
   257  }
   258  
   259  func (di *doltIndex) keysToTuple(keys []interface{}) (types.Tuple, error) {
   260  	nbf := di.indexRowData.Format()
   261  	if len(di.cols) != len(keys) {
   262  		return types.EmptyTuple(nbf), errors.New("keys must specify all columns for an index")
   263  	}
   264  	var vals []types.Value
   265  	for i, col := range di.cols {
   266  		// As an example, if our TypeInfo is Int8, we should not fail to create a tuple if we are returning all keys
   267  		// that have a value of less than 9001, thus we promote the TypeInfo to the widest type.
   268  		vrw := types.NewMemoryValueStore() // We are creating index keys, therefore we can use an internal store
   269  		val, err := col.TypeInfo.Promote().ConvertValueToNomsValue(context.Background(), vrw, keys[i])
   270  		if err != nil {
   271  			return types.EmptyTuple(nbf), err
   272  		}
   273  		vals = append(vals, types.Uint(col.Tag), val)
   274  	}
   275  	return types.NewTuple(nbf, vals...)
   276  }
   277  
   278  func (di *doltIndex) Equals(oIdx DoltIndex) bool {
   279  	if !expressionsAreEquals(di.Expressions(), oIdx.Expressions()) {
   280  		return false
   281  	}
   282  
   283  	if di.Database() != oIdx.Database() {
   284  		return false
   285  	}
   286  
   287  	if di.Table() != oIdx.Table() {
   288  		return false
   289  	}
   290  
   291  	if di.ID() != oIdx.ID() {
   292  		return false
   293  	}
   294  
   295  	if di.IsUnique() != oIdx.IsUnique() {
   296  		return false
   297  	}
   298  
   299  	if !(schema.SchemasAreEqual(di.IndexSchema(), oIdx.IndexSchema())) {
   300  		return false
   301  	}
   302  
   303  	return true
   304  }
   305  
   306  func expressionsAreEquals(exprs1, exprs2 []string) bool {
   307  	if exprs1 == nil && exprs2 == nil {
   308  		return true
   309  	} else if exprs1 == nil || exprs2 == nil {
   310  		return false
   311  	}
   312  
   313  	if len(exprs1) != len(exprs2) {
   314  		return false
   315  	}
   316  
   317  	for i, expr1 := range exprs1 {
   318  		if expr1 != exprs2[i] {
   319  			return false
   320  		}
   321  	}
   322  
   323  	return true
   324  }