github.com/dolthub/go-mysql-server@v0.18.0/sql/index_builder.go (about)

     1  // Copyright 2021 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 sql
    16  
    17  import (
    18  	"math"
    19  	"strings"
    20  
    21  	"github.com/shopspring/decimal"
    22  	"gopkg.in/src-d/go-errors.v1"
    23  )
    24  
    25  var (
    26  	ErrInvalidColExpr      = errors.NewKind("the expression `%s` could not be found from the index `%s`")
    27  	ErrRangeSimplification = errors.NewKind("attempting to simplify ranges has removed all ranges")
    28  	ErrInvalidRangeType    = errors.NewKind("encountered the RangeType_Invalid")
    29  )
    30  
    31  // IndexBuilder builds ranges based on the combination of calls made for the given index, and then relies on the Index
    32  // to return an IndexLookup from the created ranges.
    33  type IndexBuilder struct {
    34  	idx          Index
    35  	isInvalid    bool
    36  	err          error
    37  	colExprTypes map[string]Type
    38  	ranges       map[string][]RangeColumnExpr
    39  }
    40  
    41  // NewIndexBuilder returns a new IndexBuilder. Used internally to construct a range that will later be passed to
    42  // integrators through the Index function NewLookup.
    43  func NewIndexBuilder(idx Index) *IndexBuilder {
    44  	colExprTypes := make(map[string]Type)
    45  	ranges := make(map[string][]RangeColumnExpr)
    46  	for _, cet := range idx.ColumnExpressionTypes() {
    47  		colExprTypes[strings.ToLower(cet.Expression)] = cet.Type
    48  		ranges[strings.ToLower(cet.Expression)] = []RangeColumnExpr{AllRangeColumnExpr(cet.Type)}
    49  	}
    50  	return &IndexBuilder{
    51  		idx:          idx,
    52  		isInvalid:    false,
    53  		err:          nil,
    54  		colExprTypes: colExprTypes,
    55  		ranges:       ranges,
    56  	}
    57  }
    58  
    59  func ceil(val interface{}) interface{} {
    60  	switch v := val.(type) {
    61  	case float32:
    62  		return float32(math.Ceil(float64(v)))
    63  	case float64:
    64  		return math.Ceil(v)
    65  	case decimal.Decimal:
    66  		return v.Ceil()
    67  	case string:
    68  		dec, err := decimal.NewFromString(v)
    69  		if err != nil {
    70  			return v
    71  		}
    72  		return ceil(dec)
    73  	case []byte:
    74  		return ceil(string(v))
    75  	default:
    76  		return v
    77  	}
    78  }
    79  
    80  func floor(val interface{}) interface{} {
    81  	switch v := val.(type) {
    82  	case float32:
    83  		return float32(math.Floor(float64(v)))
    84  	case float64:
    85  		return math.Floor(v)
    86  	case decimal.Decimal:
    87  		return v.Floor()
    88  	case string:
    89  		dec, err := decimal.NewFromString(v)
    90  		if err != nil {
    91  			return v
    92  		}
    93  		return floor(dec)
    94  	case []byte:
    95  		return floor(string(v))
    96  	default:
    97  		return v
    98  	}
    99  }
   100  
   101  // Equals represents colExpr = key. For IN expressions, pass all of them in the same Equals call.
   102  func (b *IndexBuilder) Equals(ctx *Context, colExpr string, keys ...interface{}) *IndexBuilder {
   103  	if b.isInvalid {
   104  		return b
   105  	}
   106  	typ, ok := b.colExprTypes[colExpr]
   107  	if !ok {
   108  		b.isInvalid = true
   109  		b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID())
   110  		return b
   111  	}
   112  	potentialRanges := make([]RangeColumnExpr, len(keys))
   113  	for i, k := range keys {
   114  		// if converting from float to int results in rounding, then it's empty range
   115  		if t, ok := typ.(NumberType); ok && !t.IsFloat() {
   116  			f, c := floor(k), ceil(k)
   117  			switch k.(type) {
   118  			case float32, float64:
   119  				if f != c {
   120  					potentialRanges[i] = EmptyRangeColumnExpr(typ)
   121  					continue
   122  				}
   123  			case decimal.Decimal:
   124  				if !f.(decimal.Decimal).Equals(c.(decimal.Decimal)) {
   125  					potentialRanges[i] = EmptyRangeColumnExpr(typ)
   126  					continue
   127  				}
   128  			}
   129  		}
   130  
   131  		res, _, err := typ.Convert(k)
   132  		if err != nil {
   133  			b.isInvalid = true
   134  			b.err = err
   135  			return b
   136  		}
   137  		potentialRanges[i] = ClosedRangeColumnExpr(res, res, typ)
   138  	}
   139  	b.updateCol(ctx, colExpr, potentialRanges...)
   140  	return b
   141  }
   142  
   143  // NotEquals represents colExpr <> key.
   144  func (b *IndexBuilder) NotEquals(ctx *Context, colExpr string, key interface{}) *IndexBuilder {
   145  	if b.isInvalid {
   146  		return b
   147  	}
   148  	typ, ok := b.colExprTypes[colExpr]
   149  	if !ok {
   150  		b.isInvalid = true
   151  		b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID())
   152  		return b
   153  	}
   154  
   155  	// if converting from float to int results in rounding, then it's entire range (excluding nulls)
   156  	f, c := floor(key), ceil(key)
   157  	switch key.(type) {
   158  	case float32, float64:
   159  		if f != c {
   160  			b.updateCol(ctx, colExpr, NotNullRangeColumnExpr(typ))
   161  			return b
   162  		}
   163  	case decimal.Decimal:
   164  		if !f.(decimal.Decimal).Equals(c.(decimal.Decimal)) {
   165  			b.updateCol(ctx, colExpr, NotNullRangeColumnExpr(typ))
   166  			return b
   167  		}
   168  	}
   169  
   170  	key, _, err := typ.Convert(key)
   171  	if err != nil {
   172  		b.isInvalid = true
   173  		b.err = err
   174  		return b
   175  	}
   176  
   177  	b.updateCol(ctx, colExpr, GreaterThanRangeColumnExpr(key, typ), LessThanRangeColumnExpr(key, typ))
   178  	if !b.isInvalid {
   179  		ranges, err := SimplifyRangeColumn(b.ranges[colExpr]...)
   180  		if err != nil {
   181  			b.isInvalid = true
   182  			b.err = err
   183  			return b
   184  		}
   185  		if len(ranges) == 0 {
   186  			b.isInvalid = true
   187  			return b
   188  		}
   189  		b.ranges[colExpr] = ranges
   190  	}
   191  	return b
   192  }
   193  
   194  // GreaterThan represents colExpr > key.
   195  func (b *IndexBuilder) GreaterThan(ctx *Context, colExpr string, key interface{}) *IndexBuilder {
   196  	if b.isInvalid {
   197  		return b
   198  	}
   199  	typ, ok := b.colExprTypes[colExpr]
   200  	if !ok {
   201  		b.isInvalid = true
   202  		b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID())
   203  		return b
   204  	}
   205  
   206  	if t, ok := typ.(NumberType); ok && !t.IsFloat() {
   207  		key = floor(key)
   208  	}
   209  
   210  	key, _, err := typ.Convert(key)
   211  	if err != nil {
   212  		b.isInvalid = true
   213  		b.err = err
   214  		return b
   215  	}
   216  
   217  	b.updateCol(ctx, colExpr, GreaterThanRangeColumnExpr(key, typ))
   218  	return b
   219  }
   220  
   221  // GreaterOrEqual represents colExpr >= key.
   222  func (b *IndexBuilder) GreaterOrEqual(ctx *Context, colExpr string, key interface{}) *IndexBuilder {
   223  	if b.isInvalid {
   224  		return b
   225  	}
   226  	typ, ok := b.colExprTypes[colExpr]
   227  	if !ok {
   228  		b.isInvalid = true
   229  		b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID())
   230  		return b
   231  	}
   232  
   233  	var exclude bool
   234  	if t, ok := typ.(NumberType); ok && !t.IsFloat() {
   235  		newKey := floor(key)
   236  		switch key.(type) {
   237  		case float32, float64:
   238  			exclude = key != newKey
   239  		case decimal.Decimal:
   240  			exclude = !key.(decimal.Decimal).Equals(newKey.(decimal.Decimal))
   241  		}
   242  		key = newKey
   243  	}
   244  
   245  	key, _, err := typ.Convert(key)
   246  	if err != nil {
   247  		b.isInvalid = true
   248  		b.err = err
   249  		return b
   250  	}
   251  
   252  	var rangeColExpr RangeColumnExpr
   253  	if exclude {
   254  		rangeColExpr = GreaterThanRangeColumnExpr(key, typ)
   255  	} else {
   256  		rangeColExpr = GreaterOrEqualRangeColumnExpr(key, typ)
   257  	}
   258  	b.updateCol(ctx, colExpr, rangeColExpr)
   259  
   260  	return b
   261  }
   262  
   263  // LessThan represents colExpr < key.
   264  func (b *IndexBuilder) LessThan(ctx *Context, colExpr string, key interface{}) *IndexBuilder {
   265  	if b.isInvalid {
   266  		return b
   267  	}
   268  	typ, ok := b.colExprTypes[colExpr]
   269  	if !ok {
   270  		b.isInvalid = true
   271  		b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID())
   272  		return b
   273  	}
   274  
   275  	if t, ok := typ.(NumberType); ok && !t.IsFloat() {
   276  		key = ceil(key)
   277  	}
   278  
   279  	key, _, err := typ.Convert(key)
   280  	if err != nil {
   281  		b.isInvalid = true
   282  		b.err = err
   283  		return b
   284  	}
   285  
   286  	b.updateCol(ctx, colExpr, LessThanRangeColumnExpr(key, typ))
   287  	return b
   288  }
   289  
   290  // LessOrEqual represents colExpr <= key.
   291  func (b *IndexBuilder) LessOrEqual(ctx *Context, colExpr string, key interface{}) *IndexBuilder {
   292  	if b.isInvalid {
   293  		return b
   294  	}
   295  	typ, ok := b.colExprTypes[colExpr]
   296  	if !ok {
   297  		b.isInvalid = true
   298  		b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID())
   299  		return b
   300  	}
   301  
   302  	var exclude bool
   303  	if t, ok := typ.(NumberType); ok && !t.IsFloat() {
   304  		newKey := ceil(key)
   305  		switch key.(type) {
   306  		case float32, float64:
   307  			exclude = key != newKey
   308  		case decimal.Decimal:
   309  			exclude = !key.(decimal.Decimal).Equals(newKey.(decimal.Decimal))
   310  		}
   311  		key = newKey
   312  	}
   313  
   314  	key, _, err := typ.Convert(key)
   315  	if err != nil {
   316  		b.isInvalid = true
   317  		b.err = err
   318  		return b
   319  	}
   320  
   321  	var rangeColExpr RangeColumnExpr
   322  	if exclude {
   323  		rangeColExpr = LessThanRangeColumnExpr(key, typ)
   324  	} else {
   325  		rangeColExpr = LessOrEqualRangeColumnExpr(key, typ)
   326  	}
   327  	b.updateCol(ctx, colExpr, rangeColExpr)
   328  
   329  	return b
   330  }
   331  
   332  // IsNull represents colExpr = nil
   333  func (b *IndexBuilder) IsNull(ctx *Context, colExpr string) *IndexBuilder {
   334  	if b.isInvalid {
   335  		return b
   336  	}
   337  	typ, ok := b.colExprTypes[colExpr]
   338  	if !ok {
   339  		b.isInvalid = true
   340  		b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID())
   341  		return b
   342  	}
   343  	b.updateCol(ctx, colExpr, NullRangeColumnExpr(typ))
   344  
   345  	return b
   346  }
   347  
   348  // IsNotNull represents colExpr != nil
   349  func (b *IndexBuilder) IsNotNull(ctx *Context, colExpr string) *IndexBuilder {
   350  	if b.isInvalid {
   351  		return b
   352  	}
   353  	typ, ok := b.colExprTypes[colExpr]
   354  	if !ok {
   355  		b.isInvalid = true
   356  		b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID())
   357  		return b
   358  	}
   359  	b.updateCol(ctx, colExpr, NotNullRangeColumnExpr(typ))
   360  
   361  	return b
   362  }
   363  
   364  // Ranges returns all ranges for this index builder. If the builder is in an error state then this returns nil.
   365  func (b *IndexBuilder) Ranges(ctx *Context) RangeCollection {
   366  	if b.err != nil {
   367  		return nil
   368  	}
   369  	// An invalid builder that did not error got into a state where no columns will ever match, so we return an empty range
   370  	if b.isInvalid {
   371  		cets := b.idx.ColumnExpressionTypes()
   372  		emptyRange := make(Range, len(cets))
   373  		for i, cet := range cets {
   374  			emptyRange[i] = EmptyRangeColumnExpr(cet.Type)
   375  		}
   376  		return RangeCollection{emptyRange}
   377  	}
   378  	var allColumns [][]RangeColumnExpr
   379  	for _, colExpr := range b.idx.Expressions() {
   380  		ranges, ok := b.ranges[strings.ToLower(colExpr)]
   381  		if !ok {
   382  			// An index builder is guaranteed to cover the first n expressions, so if we hit an expression that we do
   383  			// not have an entry for then we've hit all the ranges.
   384  			break
   385  		}
   386  		allColumns = append(allColumns, ranges)
   387  	}
   388  
   389  	// In the builder ranges map we store multiple column expressions per column, however we want all permutations to
   390  	// be their own range, so here we're creating a new range for every permutation.
   391  	colCounts := make([]int, len(allColumns))
   392  	permutation := make([]int, len(allColumns))
   393  	for i, rangeColumn := range allColumns {
   394  		colCounts[i] = len(rangeColumn)
   395  	}
   396  	var ranges []Range
   397  	exit := false
   398  	for !exit {
   399  		exit = true
   400  		currentRange := make(Range, len(allColumns))
   401  		for colIdx, exprCount := range colCounts {
   402  			permutation[colIdx] = (permutation[colIdx] + 1) % exprCount
   403  			if permutation[colIdx] != 0 {
   404  				exit = false
   405  				break
   406  			}
   407  		}
   408  		for colIdx, exprIdx := range permutation {
   409  			currentRange[colIdx] = allColumns[colIdx][exprIdx]
   410  		}
   411  		isempty, err := currentRange.IsEmpty()
   412  		if err != nil {
   413  			b.err = err
   414  			return nil
   415  		}
   416  		if !isempty {
   417  			ranges = append(ranges, currentRange)
   418  		}
   419  	}
   420  	if len(ranges) == 0 {
   421  		cets := b.idx.ColumnExpressionTypes()
   422  		emptyRange := make(Range, len(cets))
   423  		for i, cet := range cets {
   424  			emptyRange[i] = EmptyRangeColumnExpr(cet.Type)
   425  		}
   426  		return RangeCollection{emptyRange}
   427  	}
   428  	return ranges
   429  }
   430  
   431  // Build constructs a new IndexLookup based on the ranges that have been built internally by this builder.
   432  func (b *IndexBuilder) Build(ctx *Context) (IndexLookup, error) {
   433  	if b.err != nil {
   434  		return emptyLookup, b.err
   435  	} else {
   436  		ranges := b.Ranges(ctx)
   437  		if len(ranges) == 0 {
   438  			return emptyLookup, nil
   439  		}
   440  		return IndexLookup{Index: b.idx, Ranges: ranges}, nil
   441  	}
   442  }
   443  
   444  // updateCol updates the internal columns with the given ranges by intersecting each given range with each existing
   445  // range. That means that each given range is treated as an OR with respect to the other given ranges. If multiple
   446  // ranges are to be intersected with respect to one another, multiple calls to updateCol should be made.
   447  func (b *IndexBuilder) updateCol(ctx *Context, colExpr string, potentialRanges ...RangeColumnExpr) {
   448  	if len(potentialRanges) == 0 {
   449  		return
   450  	}
   451  
   452  	currentRanges, ok := b.ranges[colExpr]
   453  	if !ok {
   454  		b.ranges[colExpr] = potentialRanges
   455  		return
   456  	}
   457  
   458  	var newRanges []RangeColumnExpr
   459  	for _, currentRange := range currentRanges {
   460  		for _, potentialRange := range potentialRanges {
   461  
   462  			newRange, ok, err := currentRange.TryIntersect(potentialRange)
   463  			if err != nil {
   464  				b.isInvalid = true
   465  				if !ErrInvalidValue.Is(err) {
   466  					b.err = err
   467  				}
   468  				return
   469  			}
   470  			if ok {
   471  				isempty, err := newRange.IsEmpty()
   472  				if err != nil {
   473  					b.isInvalid = true
   474  					b.err = err
   475  					return
   476  				}
   477  				if !isempty {
   478  					newRanges = append(newRanges, newRange)
   479  				}
   480  			}
   481  		}
   482  	}
   483  
   484  	// If we end up with zero ranges then we had an impossible combination, such as (x < 1 AND x > 1)
   485  	if len(newRanges) == 0 {
   486  		b.isInvalid = true
   487  		return
   488  	}
   489  	b.ranges[colExpr] = newRanges
   490  }
   491  
   492  // SpatialIndexBuilder is like the IndexBuilder, but spatial
   493  type SpatialIndexBuilder struct {
   494  	idx Index
   495  	typ Type
   496  	rng RangeColumnExpr
   497  }
   498  
   499  func NewSpatialIndexBuilder(idx Index) *SpatialIndexBuilder {
   500  	return &SpatialIndexBuilder{idx: idx, typ: idx.ColumnExpressionTypes()[0].Type}
   501  }
   502  
   503  func (b *SpatialIndexBuilder) AddRange(lower, upper interface{}) *SpatialIndexBuilder {
   504  	b.rng = RangeColumnExpr{
   505  		LowerBound: Below{Key: lower},
   506  		UpperBound: Above{Key: upper},
   507  		Typ:        b.typ,
   508  	}
   509  	return b
   510  }
   511  
   512  func (b *SpatialIndexBuilder) Build() (IndexLookup, error) {
   513  	return IndexLookup{
   514  		Index:           b.idx,
   515  		Ranges:          RangeCollection{{b.rng}},
   516  		IsSpatialLookup: true,
   517  	}, nil
   518  }