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 }