github.com/dolthub/go-mysql-server@v0.18.0/memory/index.go (about) 1 // Copyright 2020-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 memory 16 17 import ( 18 "fmt" 19 "strings" 20 21 "github.com/dolthub/go-mysql-server/sql" 22 "github.com/dolthub/go-mysql-server/sql/expression" 23 "github.com/dolthub/go-mysql-server/sql/fulltext" 24 "github.com/dolthub/go-mysql-server/sql/types" 25 ) 26 27 const CommentPreventingIndexBuilding = "__FOR TESTING: I cannot be built__" 28 29 type Index struct { 30 DB string // required for engine tests with driver 31 DriverName string // required for engine tests with driver 32 Tbl *Table // required for engine tests with driver 33 TableName string 34 Exprs []sql.Expression 35 Name string 36 Unique bool 37 Spatial bool 38 Fulltext bool 39 CommentStr string 40 PrefixLens []uint16 41 fulltextInfo 42 } 43 44 type fulltextInfo struct { 45 PositionTableName string 46 DocCountTableName string 47 GlobalCountTableName string 48 RowCountTableName string 49 fulltext.KeyColumns 50 } 51 52 var _ sql.Index = (*Index)(nil) 53 var _ sql.FilteredIndex = (*Index)(nil) 54 var _ sql.OrderedIndex = (*Index)(nil) 55 var _ sql.ExtendedIndex = (*Index)(nil) 56 var _ fulltext.Index = (*Index)(nil) 57 58 func (idx *Index) Database() string { return idx.DB } 59 func (idx *Index) Driver() string { return idx.DriverName } 60 func (idx *Index) MemTable() *Table { return idx.Tbl } 61 func (idx *Index) ColumnExpressions() []sql.Expression { return idx.Exprs } 62 func (idx *Index) IsGenerated() bool { return false } 63 64 func (idx *Index) Expressions() []string { 65 var exprs []string 66 for _, e := range idx.Exprs { 67 exprs = append(exprs, e.String()) 68 } 69 return exprs 70 } 71 72 func (idx *Index) ExtendedExpressions() []string { 73 var exprs []string 74 foundCols := make(map[string]struct{}) 75 for _, e := range idx.Exprs { 76 foundCols[strings.ToLower(e.(*expression.GetField).Name())] = struct{}{} 77 exprs = append(exprs, e.String()) 78 } 79 for _, ord := range idx.Tbl.data.schema.PkOrdinals { 80 col := idx.Tbl.data.schema.Schema[ord] 81 if _, ok := foundCols[strings.ToLower(col.Name)]; !ok { 82 exprs = append(exprs, fmt.Sprintf("%s.%s", idx.Tbl.name, col.Name)) 83 } 84 } 85 return exprs 86 } 87 88 // ExtendedExprs returns the same information as ExtendedExpressions, but in sql.Expression form. 89 func (idx *Index) ExtendedExprs() []sql.Expression { 90 var exprs []sql.Expression 91 foundCols := make(map[string]struct{}) 92 for _, e := range idx.Exprs { 93 foundCols[strings.ToLower(e.(*expression.GetField).Name())] = struct{}{} 94 exprs = append(exprs, e) 95 } 96 for _, ord := range idx.Tbl.data.schema.PkOrdinals { 97 col := idx.Tbl.data.schema.Schema[ord] 98 if _, ok := foundCols[strings.ToLower(col.Name)]; !ok { 99 exprs = append(exprs, expression.NewGetFieldWithTable(ord, 0, col.Type, idx.DB, idx.Tbl.name, col.Name, col.Nullable)) 100 } 101 } 102 return exprs 103 } 104 105 func (idx *Index) CanSupport(...sql.Range) bool { 106 return true 107 } 108 109 func (idx *Index) IsUnique() bool { 110 return idx.Unique 111 } 112 113 func (idx *Index) IsSpatial() bool { 114 return idx.Spatial 115 } 116 117 func (idx *Index) IsFullText() bool { 118 return idx.Fulltext 119 } 120 121 func (idx *Index) Comment() string { 122 return idx.CommentStr 123 } 124 125 func (idx *Index) PrefixLengths() []uint16 { 126 return idx.PrefixLens 127 } 128 129 func (idx *Index) IndexType() string { 130 if len(idx.DriverName) > 0 { 131 return idx.DriverName 132 } 133 return "BTREE" // fake but so are you 134 } 135 136 func (idx *Index) rowToIndexStorage(row sql.Row, partitionName string, rowIdx int) (sql.Row, error) { 137 if idx.Name == "PRIMARY" { 138 return row, nil 139 } 140 141 exprs := idx.ExtendedExprs() 142 newRow := make(sql.Row, len(exprs)+1) 143 for i, expr := range exprs { 144 var err error 145 newRow[i], err = expr.Eval(nil, row) 146 if err != nil { 147 return nil, err 148 } 149 } 150 // The final element of the row is the location of the row in the primary table storage slice. 151 newRow[len(exprs)] = primaryRowLocation{ 152 partition: partitionName, 153 idx: rowIdx, 154 } 155 156 return newRow, nil 157 } 158 159 func (idx *Index) rangeFilterExpr(ctx *sql.Context, ranges ...sql.Range) (sql.Expression, error) { 160 if idx.CommentStr == CommentPreventingIndexBuilding { 161 return nil, nil 162 } 163 164 return expression.NewRangeFilterExpr(idx.ExtendedExprs(), ranges) 165 } 166 167 // ColumnExpressionTypes implements the interface sql.Index. 168 func (idx *Index) ColumnExpressionTypes() []sql.ColumnExpressionType { 169 cets := make([]sql.ColumnExpressionType, len(idx.Exprs)) 170 for i, expr := range idx.Exprs { 171 cets[i] = sql.ColumnExpressionType{ 172 Expression: expr.String(), 173 Type: expr.Type(), 174 } 175 } 176 return cets 177 } 178 179 func (idx *Index) ExtendedColumnExpressionTypes() []sql.ColumnExpressionType { 180 cets := make([]sql.ColumnExpressionType, 0, len(idx.Tbl.data.schema.Schema)) 181 cetsInExprs := make(map[string]struct{}) 182 for _, expr := range idx.Exprs { 183 cetsInExprs[strings.ToLower(expr.(*expression.GetField).Name())] = struct{}{} 184 cets = append(cets, sql.ColumnExpressionType{ 185 Expression: expr.String(), 186 Type: expr.Type(), 187 }) 188 } 189 for _, ord := range idx.Tbl.data.schema.PkOrdinals { 190 col := idx.Tbl.data.schema.Schema[ord] 191 if _, ok := cetsInExprs[strings.ToLower(col.Name)]; !ok { 192 cets = append(cets, sql.ColumnExpressionType{ 193 Expression: fmt.Sprintf("%s.%s", idx.Tbl.name, col.Name), 194 Type: col.Type, 195 }) 196 } 197 } 198 return cets 199 } 200 201 func (idx *Index) FullTextTableNames(ctx *sql.Context) (fulltext.IndexTableNames, error) { 202 return fulltext.IndexTableNames{ 203 Config: idx.Tbl.data.fullTextConfigTableName, 204 Position: idx.fulltextInfo.PositionTableName, 205 DocCount: idx.fulltextInfo.DocCountTableName, 206 GlobalCount: idx.fulltextInfo.GlobalCountTableName, 207 RowCount: idx.fulltextInfo.RowCountTableName, 208 }, nil 209 } 210 211 func (idx *Index) FullTextKeyColumns(ctx *sql.Context) (fulltext.KeyColumns, error) { 212 return idx.fulltextInfo.KeyColumns, nil 213 } 214 215 func (idx *Index) ID() string { 216 if len(idx.Name) > 0 { 217 return idx.Name 218 } 219 220 if len(idx.Exprs) == 1 { 221 return idx.Exprs[0].String() 222 } 223 var parts = make([]string, len(idx.Exprs)) 224 for i, e := range idx.Exprs { 225 parts[i] = e.String() 226 } 227 228 return "(" + strings.Join(parts, ", ") + ")" 229 } 230 231 func (idx *Index) Table() string { return idx.TableName } 232 233 func (idx *Index) HandledFilters(filters []sql.Expression) []sql.Expression { 234 var handled []sql.Expression 235 if idx.Spatial { 236 return handled 237 } 238 for _, expr := range filters { 239 if !expression.PreciseComparison(expr) { 240 continue 241 } 242 handled = append(handled, expr) 243 } 244 return handled 245 } 246 247 // validateIndexType returns the best comparison type between the two given types, as it takes into consideration 248 // whether the types contain collations. 249 func (idx *Index) validateIndexType(valType sql.Type, rangeType sql.Type) sql.Type { 250 if _, ok := rangeType.(sql.TypeWithCollation); ok { 251 return rangeType.Promote() 252 } 253 return valType 254 } 255 256 // ExpressionsIndex is an index made out of one or more expressions (usually field expressions), linked to a Table. 257 type ExpressionsIndex interface { 258 sql.Index 259 MemTable() *Table 260 ColumnExpressions() []sql.Expression 261 } 262 263 func (idx *Index) Order() sql.IndexOrder { 264 // If there are any hash-encoded fields, then we will not have a deterministic order 265 // Even though we don't actually hash hash-encoded fields in the in-memory implementation, we 266 // still honor this here so that we can test this behavior. 267 if len(idx.contentHashedFields()) > 0 { 268 return sql.IndexOrderNone 269 } 270 271 return sql.IndexOrderAsc 272 } 273 274 func (idx *Index) Reversible() bool { 275 // If there are any hash-encoded fields, then we will not have a deterministic order 276 // Even though we don't actually hash hash-encoded fields in the in-memory implementation, we 277 // still honor this here so that we can test this behavior. 278 if len(idx.contentHashedFields()) > 0 { 279 return false 280 } 281 282 return true 283 } 284 285 func (idx Index) copy() *Index { 286 return &idx 287 } 288 289 // columnIndexes returns the indexes in the given schema for the fields in this index 290 func (idx *Index) columnIndexes(schema sql.Schema) []int { 291 indexes := make([]int, len(idx.Exprs)) 292 for i, expr := range idx.Exprs { 293 gf, ok := expr.(*expression.GetField) 294 if !ok { 295 panic(fmt.Sprintf("expected GetField expression, got %T", expr)) 296 } 297 indexes[i] = schema.IndexOfColName(gf.Name()) 298 } 299 return indexes 300 } 301 302 // contentHashedFields returns a slice of field indexes in this secondary index that should be hashed, instead 303 // of directly storing their content. This is only applicable to unique secondary indexes. 304 func (idx *Index) contentHashedFields() (contentHashedFields []uint) { 305 if !idx.Unique { 306 return nil 307 } 308 309 for i, expr := range idx.Exprs { 310 if !types.IsTextBlob(expr.Type()) { 311 continue 312 } 313 314 prefixLength := uint16(0) 315 if len(idx.PrefixLens) > i { 316 prefixLength = idx.PrefixLens[i] 317 } 318 if prefixLength == 0 { 319 contentHashedFields = append(contentHashedFields, uint(i)) 320 } 321 } 322 323 return contentHashedFields 324 }