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 }