github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/schema/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 schema
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  
    21  	"github.com/dolthub/dolt/go/store/types"
    22  )
    23  
    24  type Index interface {
    25  	// AllTags returns the tags of the columns in the entire index, including the primary keys.
    26  	// If we imagined a dolt index as being a standard dolt table, then the tags would represent the schema columns.
    27  	AllTags() []uint64
    28  	// ColumnNames returns the names of the columns in the index.
    29  	ColumnNames() []string
    30  	// Comment returns the comment that was provided upon index creation.
    31  	Comment() string
    32  	// Count returns the number of indexed columns in this index.
    33  	Count() int
    34  	// Equals returns whether this Index is equivalent to another. This does not check for column names, thus those may
    35  	// be renamed and the index equivalence will be preserved. It also does not depend on the table's primary keys.
    36  	Equals(other Index) bool
    37  	// GetColumn returns the column for the given tag and whether the column was found or not.
    38  	GetColumn(tag uint64) (Column, bool)
    39  	// IndexedColumnTags returns the tags of the columns in the index.
    40  	IndexedColumnTags() []uint64
    41  	// IsUnique returns whether the given index has the UNIQUE constraint.
    42  	IsUnique() bool
    43  	// IsUserDefined returns whether the given index was created by a user or automatically generated.
    44  	IsUserDefined() bool
    45  	// Name returns the name of the index.
    46  	Name() string
    47  	// PrimaryKeyTags returns the primary keys of the indexed table, in the order that they're stored for that table.
    48  	PrimaryKeyTags() []uint64
    49  	// Schema returns the schema for the internal index map. Can be used for table operations.
    50  	Schema() Schema
    51  	// VerifyMap returns whether the given map iterator contains all valid keys and values for this index.
    52  	VerifyMap(ctx context.Context, iter types.MapIterator, nbf *types.NomsBinFormat) error
    53  }
    54  
    55  var _ Index = (*indexImpl)(nil)
    56  
    57  type indexImpl struct {
    58  	name          string
    59  	tags          []uint64
    60  	allTags       []uint64
    61  	indexColl     *indexCollectionImpl
    62  	isUnique      bool
    63  	isUserDefined bool
    64  	comment       string
    65  }
    66  
    67  func NewIndex(name string, tags, allTags []uint64, indexColl *indexCollectionImpl, props IndexProperties) Index {
    68  	return &indexImpl{
    69  		name:          name,
    70  		tags:          tags,
    71  		allTags:       allTags,
    72  		indexColl:     indexColl,
    73  		isUnique:      props.IsUnique,
    74  		isUserDefined: props.IsUserDefined,
    75  		comment:       props.Comment,
    76  	}
    77  }
    78  
    79  // AllTags implements Index.
    80  func (ix *indexImpl) AllTags() []uint64 {
    81  	return ix.allTags
    82  }
    83  
    84  // ColumnNames implements Index.
    85  func (ix *indexImpl) ColumnNames() []string {
    86  	colNames := make([]string, len(ix.tags))
    87  	for i, tag := range ix.tags {
    88  		colNames[i] = ix.indexColl.colColl.TagToCol[tag].Name
    89  	}
    90  	return colNames
    91  }
    92  
    93  // Comment implements Index.
    94  func (ix *indexImpl) Comment() string {
    95  	return ix.comment
    96  }
    97  
    98  // Count implements Index.
    99  func (ix *indexImpl) Count() int {
   100  	return len(ix.tags)
   101  }
   102  
   103  // Equals implements Index.
   104  func (ix *indexImpl) Equals(other Index) bool {
   105  	if ix.Count() != other.Count() {
   106  		return false
   107  	}
   108  
   109  	// we're only interested in columns the index is defined over, not the table's primary keys
   110  	tt := ix.IndexedColumnTags()
   111  	ot := other.IndexedColumnTags()
   112  	for i := range tt {
   113  		if tt[i] != ot[i] {
   114  			return false
   115  		}
   116  	}
   117  
   118  	return ix.IsUnique() == other.IsUnique() &&
   119  		ix.Comment() == other.Comment() &&
   120  		ix.Name() == other.Name()
   121  }
   122  
   123  // GetColumn implements Index.
   124  func (ix *indexImpl) GetColumn(tag uint64) (Column, bool) {
   125  	return ix.indexColl.colColl.GetByTag(tag)
   126  }
   127  
   128  // IndexedColumnTags implements Index.
   129  func (ix *indexImpl) IndexedColumnTags() []uint64 {
   130  	return ix.tags
   131  }
   132  
   133  // IsUnique implements Index.
   134  func (ix *indexImpl) IsUnique() bool {
   135  	return ix.isUnique
   136  }
   137  
   138  // IsUserDefined implements Index.
   139  func (ix *indexImpl) IsUserDefined() bool {
   140  	return ix.isUserDefined
   141  }
   142  
   143  // Name implements Index.
   144  func (ix *indexImpl) Name() string {
   145  	return ix.name
   146  }
   147  
   148  // PrimaryKeyTags implements Index.
   149  func (ix *indexImpl) PrimaryKeyTags() []uint64 {
   150  	return ix.indexColl.pks
   151  }
   152  
   153  // Schema implements Index.
   154  func (ix *indexImpl) Schema() Schema {
   155  	cols := make([]Column, len(ix.allTags))
   156  	for i, tag := range ix.allTags {
   157  		col := ix.indexColl.colColl.TagToCol[tag]
   158  		cols[i] = Column{
   159  			Name:        col.Name,
   160  			Tag:         tag,
   161  			Kind:        col.Kind,
   162  			IsPartOfPK:  true,
   163  			TypeInfo:    col.TypeInfo,
   164  			Constraints: nil,
   165  		}
   166  	}
   167  	allCols := NewColCollection(cols...)
   168  	nonPkCols := NewColCollection()
   169  	return &schemaImpl{
   170  		pkCols:          allCols,
   171  		nonPKCols:       nonPkCols,
   172  		allCols:         allCols,
   173  		indexCollection: NewIndexCollection(nil),
   174  		checkCollection: NewCheckCollection(),
   175  	}
   176  }
   177  
   178  // VerifyMap implements Index.
   179  func (ix *indexImpl) VerifyMap(ctx context.Context, iter types.MapIterator, nbf *types.NomsBinFormat) error {
   180  	lastKey := types.EmptyTuple(nbf)
   181  	var keyVal types.Value
   182  	var valVal types.Value
   183  	expectedVal := types.EmptyTuple(nbf)
   184  	var err error
   185  	cols := make([]Column, len(ix.allTags))
   186  	for i, tag := range ix.allTags {
   187  		var ok bool
   188  		cols[i], ok = ix.indexColl.colColl.TagToCol[tag]
   189  		if !ok {
   190  			return fmt.Errorf("index `%s` has column with tag `%d` which cannot be found", ix.name, tag)
   191  		}
   192  	}
   193  
   194  	for keyVal, valVal, err = iter.Next(ctx); err == nil && keyVal != nil; keyVal, valVal, err = iter.Next(ctx) {
   195  		key := keyVal.(types.Tuple)
   196  		i := 0
   197  		hasNull := false
   198  		if key.Len() != uint64(2*len(cols)) {
   199  			return fmt.Errorf("mismatched value count in key tuple compared to what index `%s` expects", ix.name)
   200  		}
   201  		err = key.WalkValues(ctx, func(v types.Value) error {
   202  			colIndex := i / 2
   203  			isTag := i%2 == 0
   204  			if isTag {
   205  				if !v.Equals(types.Uint(cols[colIndex].Tag)) {
   206  					return fmt.Errorf("column order of map does not match what index `%s` expects", ix.name)
   207  				}
   208  			} else {
   209  				if types.IsNull(v) {
   210  					hasNull = true
   211  				} else if v.Kind() != cols[colIndex].TypeInfo.NomsKind() {
   212  					return fmt.Errorf("column value in map does not match what index `%s` expects", ix.name)
   213  				}
   214  			}
   215  			i++
   216  			return nil
   217  		})
   218  		if err != nil {
   219  			return err
   220  		}
   221  		if ix.isUnique && !hasNull {
   222  			partialKeysEqual, err := key.PrefixEquals(ctx, lastKey, uint64(len(ix.tags)*2))
   223  			if err != nil {
   224  				return err
   225  			}
   226  			if partialKeysEqual {
   227  				return fmt.Errorf("UNIQUE constraint violation while verifying index: %s", ix.name)
   228  			}
   229  		}
   230  		if !expectedVal.Equals(valVal) {
   231  			return fmt.Errorf("index map value should be empty")
   232  		}
   233  		lastKey = key
   234  	}
   235  	return err
   236  }
   237  
   238  // copy returns an exact copy of the calling index.
   239  func (ix *indexImpl) copy() *indexImpl {
   240  	newIx := *ix
   241  	newIx.tags = make([]uint64, len(ix.tags))
   242  	_ = copy(newIx.tags, ix.tags)
   243  	newIx.allTags = make([]uint64, len(ix.allTags))
   244  	_ = copy(newIx.allTags, ix.allTags)
   245  	return &newIx
   246  }