|
| 1 | +// Copyright 2017 PingCAP, 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 | +// See the License for the specific language governing permissions and |
| 12 | +// limitations under the License. |
| 13 | + |
| 14 | +package ranger |
| 15 | + |
| 16 | +import ( |
| 17 | + "math" |
| 18 | + |
| 19 | + "github.com/juju/errors" |
| 20 | + "github.com/pingcap/tidb/ast" |
| 21 | + "github.com/pingcap/tidb/expression" |
| 22 | + "github.com/pingcap/tidb/sessionctx/variable" |
| 23 | + "github.com/pingcap/tidb/types" |
| 24 | +) |
| 25 | + |
| 26 | +// points2IndexRanges build index ranges from range points. |
| 27 | +// Only the first column in the index is built, extra column ranges will be appended by |
| 28 | +// appendPoints2IndexRanges. |
| 29 | +func points2IndexRanges(sc *variable.StatementContext, rangePoints []point, tp *types.FieldType) ([]*types.IndexRange, error) { |
| 30 | + indexRanges := make([]*types.IndexRange, 0, len(rangePoints)/2) |
| 31 | + for i := 0; i < len(rangePoints); i += 2 { |
| 32 | + startPoint, err := convertPoint(sc, rangePoints[i], tp) |
| 33 | + if err != nil { |
| 34 | + return nil, errors.Trace(err) |
| 35 | + } |
| 36 | + endPoint, err := convertPoint(sc, rangePoints[i+1], tp) |
| 37 | + if err != nil { |
| 38 | + return nil, errors.Trace(err) |
| 39 | + } |
| 40 | + less, err := rangePointLess(sc, startPoint, endPoint) |
| 41 | + if err != nil { |
| 42 | + return nil, errors.Trace(err) |
| 43 | + } |
| 44 | + if !less { |
| 45 | + continue |
| 46 | + } |
| 47 | + ir := &types.IndexRange{ |
| 48 | + LowVal: []types.Datum{startPoint.value}, |
| 49 | + LowExclude: startPoint.excl, |
| 50 | + HighVal: []types.Datum{endPoint.value}, |
| 51 | + HighExclude: endPoint.excl, |
| 52 | + } |
| 53 | + indexRanges = append(indexRanges, ir) |
| 54 | + } |
| 55 | + return indexRanges, nil |
| 56 | +} |
| 57 | + |
| 58 | +func convertPoint(sc *variable.StatementContext, point point, tp *types.FieldType) (point, error) { |
| 59 | + switch point.value.Kind() { |
| 60 | + case types.KindMaxValue, types.KindMinNotNull: |
| 61 | + return point, nil |
| 62 | + } |
| 63 | + casted, err := point.value.ConvertTo(sc, tp) |
| 64 | + if err != nil { |
| 65 | + return point, errors.Trace(err) |
| 66 | + } |
| 67 | + valCmpCasted, err := point.value.CompareDatum(sc, &casted) |
| 68 | + if err != nil { |
| 69 | + return point, errors.Trace(err) |
| 70 | + } |
| 71 | + point.value = casted |
| 72 | + if valCmpCasted == 0 { |
| 73 | + return point, nil |
| 74 | + } |
| 75 | + if point.start { |
| 76 | + if point.excl { |
| 77 | + if valCmpCasted < 0 { |
| 78 | + // e.g. "a > 1.9" convert to "a >= 2". |
| 79 | + point.excl = false |
| 80 | + } |
| 81 | + } else { |
| 82 | + if valCmpCasted > 0 { |
| 83 | + // e.g. "a >= 1.1 convert to "a > 1" |
| 84 | + point.excl = true |
| 85 | + } |
| 86 | + } |
| 87 | + } else { |
| 88 | + if point.excl { |
| 89 | + if valCmpCasted > 0 { |
| 90 | + // e.g. "a < 1.1" convert to "a <= 1" |
| 91 | + point.excl = false |
| 92 | + } |
| 93 | + } else { |
| 94 | + if valCmpCasted < 0 { |
| 95 | + // e.g. "a <= 1.9" convert to "a < 2" |
| 96 | + point.excl = true |
| 97 | + } |
| 98 | + } |
| 99 | + } |
| 100 | + return point, nil |
| 101 | +} |
| 102 | + |
| 103 | +// appendPoints2IndexRanges appends additional column ranges for multi-column index. |
| 104 | +// The additional column ranges can only be appended to point ranges. |
| 105 | +// for example we have an index (a, b), if the condition is (a > 1 and b = 2) |
| 106 | +// then we can not build a conjunctive ranges for this index. |
| 107 | +func appendPoints2IndexRanges(sc *variable.StatementContext, origin []*types.IndexRange, rangePoints []point, |
| 108 | + ft *types.FieldType) ([]*types.IndexRange, error) { |
| 109 | + var newIndexRanges []*types.IndexRange |
| 110 | + for i := 0; i < len(origin); i++ { |
| 111 | + oRange := origin[i] |
| 112 | + if !oRange.IsPoint(sc) { |
| 113 | + newIndexRanges = append(newIndexRanges, oRange) |
| 114 | + } else { |
| 115 | + newRanges, err := appendPoints2IndexRange(sc, oRange, rangePoints, ft) |
| 116 | + if err != nil { |
| 117 | + return nil, errors.Trace(err) |
| 118 | + } |
| 119 | + newIndexRanges = append(newIndexRanges, newRanges...) |
| 120 | + } |
| 121 | + } |
| 122 | + return newIndexRanges, nil |
| 123 | +} |
| 124 | + |
| 125 | +func appendPoints2IndexRange(sc *variable.StatementContext, origin *types.IndexRange, rangePoints []point, |
| 126 | + ft *types.FieldType) ([]*types.IndexRange, error) { |
| 127 | + newRanges := make([]*types.IndexRange, 0, len(rangePoints)/2) |
| 128 | + for i := 0; i < len(rangePoints); i += 2 { |
| 129 | + startPoint, err := convertPoint(sc, rangePoints[i], ft) |
| 130 | + if err != nil { |
| 131 | + return nil, errors.Trace(err) |
| 132 | + } |
| 133 | + endPoint, err := convertPoint(sc, rangePoints[i+1], ft) |
| 134 | + if err != nil { |
| 135 | + return nil, errors.Trace(err) |
| 136 | + } |
| 137 | + less, err := rangePointLess(sc, startPoint, endPoint) |
| 138 | + if err != nil { |
| 139 | + return nil, errors.Trace(err) |
| 140 | + } |
| 141 | + if !less { |
| 142 | + continue |
| 143 | + } |
| 144 | + |
| 145 | + lowVal := make([]types.Datum, len(origin.LowVal)+1) |
| 146 | + copy(lowVal, origin.LowVal) |
| 147 | + lowVal[len(origin.LowVal)] = startPoint.value |
| 148 | + |
| 149 | + highVal := make([]types.Datum, len(origin.HighVal)+1) |
| 150 | + copy(highVal, origin.HighVal) |
| 151 | + highVal[len(origin.HighVal)] = endPoint.value |
| 152 | + |
| 153 | + ir := &types.IndexRange{ |
| 154 | + LowVal: lowVal, |
| 155 | + LowExclude: startPoint.excl, |
| 156 | + HighVal: highVal, |
| 157 | + HighExclude: endPoint.excl, |
| 158 | + } |
| 159 | + newRanges = append(newRanges, ir) |
| 160 | + } |
| 161 | + return newRanges, nil |
| 162 | +} |
| 163 | + |
| 164 | +// points2TableRanges will construct the range slice with the given range points |
| 165 | +func points2TableRanges(sc *variable.StatementContext, rangePoints []point) ([]types.IntColumnRange, error) { |
| 166 | + tableRanges := make([]types.IntColumnRange, 0, len(rangePoints)/2) |
| 167 | + for i := 0; i < len(rangePoints); i += 2 { |
| 168 | + startPoint := rangePoints[i] |
| 169 | + if startPoint.value.IsNull() || startPoint.value.Kind() == types.KindMinNotNull { |
| 170 | + startPoint.value.SetInt64(math.MinInt64) |
| 171 | + } |
| 172 | + startInt, err := startPoint.value.ToInt64(sc) |
| 173 | + if err != nil { |
| 174 | + return nil, errors.Trace(err) |
| 175 | + } |
| 176 | + startDatum := types.NewDatum(startInt) |
| 177 | + cmp, err := startDatum.CompareDatum(sc, &startPoint.value) |
| 178 | + if err != nil { |
| 179 | + return nil, errors.Trace(err) |
| 180 | + } |
| 181 | + if cmp < 0 || (cmp == 0 && startPoint.excl) { |
| 182 | + startInt++ |
| 183 | + } |
| 184 | + endPoint := rangePoints[i+1] |
| 185 | + if endPoint.value.IsNull() { |
| 186 | + endPoint.value.SetInt64(math.MinInt64) |
| 187 | + } else if endPoint.value.Kind() == types.KindMaxValue { |
| 188 | + endPoint.value.SetInt64(math.MaxInt64) |
| 189 | + } |
| 190 | + endInt, err := endPoint.value.ToInt64(sc) |
| 191 | + if err != nil { |
| 192 | + return nil, errors.Trace(err) |
| 193 | + } |
| 194 | + endDatum := types.NewDatum(endInt) |
| 195 | + cmp, err = endDatum.CompareDatum(sc, &endPoint.value) |
| 196 | + if err != nil { |
| 197 | + return nil, errors.Trace(err) |
| 198 | + } |
| 199 | + if cmp > 0 || (cmp == 0 && endPoint.excl) { |
| 200 | + endInt-- |
| 201 | + } |
| 202 | + if startInt > endInt { |
| 203 | + continue |
| 204 | + } |
| 205 | + tableRanges = append(tableRanges, types.IntColumnRange{LowVal: startInt, HighVal: endInt}) |
| 206 | + } |
| 207 | + return tableRanges, nil |
| 208 | +} |
| 209 | + |
| 210 | +func points2ColumnRanges(sc *variable.StatementContext, points []point, tp *types.FieldType) ([]*types.ColumnRange, error) { |
| 211 | + columnRanges := make([]*types.ColumnRange, 0, len(points)/2) |
| 212 | + for i := 0; i < len(points); i += 2 { |
| 213 | + startPoint, err := convertPoint(sc, points[i], tp) |
| 214 | + if err != nil { |
| 215 | + return nil, errors.Trace(err) |
| 216 | + } |
| 217 | + endPoint, err := convertPoint(sc, points[i+1], tp) |
| 218 | + if err != nil { |
| 219 | + return nil, errors.Trace(err) |
| 220 | + } |
| 221 | + less, err := rangePointLess(sc, startPoint, endPoint) |
| 222 | + if err != nil { |
| 223 | + return nil, errors.Trace(err) |
| 224 | + } |
| 225 | + if !less { |
| 226 | + continue |
| 227 | + } |
| 228 | + cr := &types.ColumnRange{ |
| 229 | + Low: startPoint.value, |
| 230 | + LowExcl: startPoint.excl, |
| 231 | + High: endPoint.value, |
| 232 | + HighExcl: endPoint.excl, |
| 233 | + } |
| 234 | + columnRanges = append(columnRanges, cr) |
| 235 | + } |
| 236 | + return columnRanges, nil |
| 237 | +} |
| 238 | + |
| 239 | +// buildTableRange will build range of pk for PhysicalTableScan |
| 240 | +func buildTableRange(accessConditions []expression.Expression, sc *variable.StatementContext) ([]types.IntColumnRange, error) { |
| 241 | + if len(accessConditions) == 0 { |
| 242 | + return FullIntRange(), nil |
| 243 | + } |
| 244 | + |
| 245 | + rb := builder{sc: sc} |
| 246 | + rangePoints := fullRange |
| 247 | + for _, cond := range accessConditions { |
| 248 | + rangePoints = rb.intersection(rangePoints, rb.build(cond)) |
| 249 | + if rb.err != nil { |
| 250 | + return nil, errors.Trace(rb.err) |
| 251 | + } |
| 252 | + } |
| 253 | + ranges, err := points2TableRanges(sc, rangePoints) |
| 254 | + if err != nil { |
| 255 | + return nil, errors.Trace(err) |
| 256 | + } |
| 257 | + return ranges, nil |
| 258 | +} |
| 259 | + |
| 260 | +// buildColumnRange builds the range for sampling histogram to calculate the row count. |
| 261 | +func buildColumnRange(conds []expression.Expression, sc *variable.StatementContext, tp *types.FieldType) ([]*types.ColumnRange, error) { |
| 262 | + if len(conds) == 0 { |
| 263 | + return []*types.ColumnRange{{Low: types.Datum{}, High: types.MaxValueDatum()}}, nil |
| 264 | + } |
| 265 | + |
| 266 | + rb := builder{sc: sc} |
| 267 | + rangePoints := fullRange |
| 268 | + for _, cond := range conds { |
| 269 | + rangePoints = rb.intersection(rangePoints, rb.build(cond)) |
| 270 | + if rb.err != nil { |
| 271 | + return nil, errors.Trace(rb.err) |
| 272 | + } |
| 273 | + } |
| 274 | + ranges, err := points2ColumnRanges(sc, rangePoints, tp) |
| 275 | + if err != nil { |
| 276 | + return nil, errors.Trace(err) |
| 277 | + } |
| 278 | + return ranges, nil |
| 279 | +} |
| 280 | + |
| 281 | +func buildIndexRange(sc *variable.StatementContext, cols []*expression.Column, lengths []int, |
| 282 | + accessCondition []expression.Expression) ([]*types.IndexRange, error) { |
| 283 | + rb := builder{sc: sc} |
| 284 | + var ( |
| 285 | + ranges []*types.IndexRange |
| 286 | + eqAndInCount int |
| 287 | + err error |
| 288 | + ) |
| 289 | + for eqAndInCount = 0; eqAndInCount < len(accessCondition) && eqAndInCount < len(cols); eqAndInCount++ { |
| 290 | + if sf, ok := accessCondition[eqAndInCount].(*expression.ScalarFunction); !ok || (sf.FuncName.L != ast.EQ && sf.FuncName.L != ast.In) { |
| 291 | + break |
| 292 | + } |
| 293 | + // Build ranges for equal or in access conditions. |
| 294 | + point := rb.build(accessCondition[eqAndInCount]) |
| 295 | + if rb.err != nil { |
| 296 | + return nil, errors.Trace(rb.err) |
| 297 | + } |
| 298 | + if eqAndInCount == 0 { |
| 299 | + ranges, err = points2IndexRanges(sc, point, cols[eqAndInCount].RetType) |
| 300 | + } else { |
| 301 | + ranges, err = appendPoints2IndexRanges(sc, ranges, point, cols[eqAndInCount].RetType) |
| 302 | + } |
| 303 | + if err != nil { |
| 304 | + return nil, errors.Trace(err) |
| 305 | + } |
| 306 | + } |
| 307 | + rangePoints := fullRange |
| 308 | + // Build rangePoints for non-equal access conditions. |
| 309 | + for i := eqAndInCount; i < len(accessCondition); i++ { |
| 310 | + rangePoints = rb.intersection(rangePoints, rb.build(accessCondition[i])) |
| 311 | + if rb.err != nil { |
| 312 | + return nil, errors.Trace(rb.err) |
| 313 | + } |
| 314 | + } |
| 315 | + if eqAndInCount == 0 { |
| 316 | + ranges, err = points2IndexRanges(sc, rangePoints, cols[0].RetType) |
| 317 | + } else if eqAndInCount < len(accessCondition) { |
| 318 | + ranges, err = appendPoints2IndexRanges(sc, ranges, rangePoints, cols[eqAndInCount].RetType) |
| 319 | + } |
| 320 | + if err != nil { |
| 321 | + return nil, errors.Trace(err) |
| 322 | + } |
| 323 | + |
| 324 | + // Take prefix index into consideration. |
| 325 | + if hasPrefix(lengths) { |
| 326 | + fixPrefixColRange(ranges, lengths) |
| 327 | + } |
| 328 | + |
| 329 | + if len(ranges) > 0 && len(ranges[0].LowVal) < len(cols) { |
| 330 | + for _, ran := range ranges { |
| 331 | + if ran.HighExclude || ran.LowExclude { |
| 332 | + if ran.HighExclude { |
| 333 | + ran.HighVal = append(ran.HighVal, types.NewDatum(nil)) |
| 334 | + } else { |
| 335 | + ran.HighVal = append(ran.HighVal, types.MaxValueDatum()) |
| 336 | + } |
| 337 | + if ran.LowExclude { |
| 338 | + ran.LowVal = append(ran.LowVal, types.MaxValueDatum()) |
| 339 | + } else { |
| 340 | + ran.LowVal = append(ran.LowVal, types.NewDatum(nil)) |
| 341 | + } |
| 342 | + } |
| 343 | + } |
| 344 | + } |
| 345 | + return ranges, nil |
| 346 | +} |
| 347 | + |
| 348 | +func hasPrefix(lengths []int) bool { |
| 349 | + for _, l := range lengths { |
| 350 | + if l != types.UnspecifiedLength { |
| 351 | + return true |
| 352 | + } |
| 353 | + } |
| 354 | + return false |
| 355 | +} |
| 356 | + |
| 357 | +func fixPrefixColRange(ranges []*types.IndexRange, lengths []int) { |
| 358 | + for _, ran := range ranges { |
| 359 | + for i := 0; i < len(ran.LowVal); i++ { |
| 360 | + fixRangeDatum(&ran.LowVal[i], lengths[i]) |
| 361 | + } |
| 362 | + ran.LowExclude = false |
| 363 | + for i := 0; i < len(ran.HighVal); i++ { |
| 364 | + fixRangeDatum(&ran.HighVal[i], lengths[i]) |
| 365 | + } |
| 366 | + ran.HighExclude = false |
| 367 | + } |
| 368 | +} |
| 369 | + |
| 370 | +func fixRangeDatum(v *types.Datum, length int) { |
| 371 | + // If this column is prefix and the prefix length is smaller than the range, cut it. |
| 372 | + if length != types.UnspecifiedLength && length < len(v.GetBytes()) { |
| 373 | + v.SetBytes(v.GetBytes()[:length]) |
| 374 | + } |
| 375 | +} |
| 376 | + |
| 377 | +// getEQColOffset judge if the expression is a eq function that one side is constant and another is column. |
| 378 | +// If so, it will return the offset of this column in the slice. |
| 379 | +func getEQColOffset(expr expression.Expression, cols []*expression.Column) int { |
| 380 | + f, ok := expr.(*expression.ScalarFunction) |
| 381 | + if !ok || f.FuncName.L != ast.EQ { |
| 382 | + return -1 |
| 383 | + } |
| 384 | + if c, ok := f.GetArgs()[0].(*expression.Column); ok { |
| 385 | + if _, ok := f.GetArgs()[1].(*expression.Constant); ok { |
| 386 | + for i, col := range cols { |
| 387 | + if col.Equal(c, nil) { |
| 388 | + return i |
| 389 | + } |
| 390 | + } |
| 391 | + } |
| 392 | + } |
| 393 | + if c, ok := f.GetArgs()[1].(*expression.Column); ok { |
| 394 | + if _, ok := f.GetArgs()[0].(*expression.Constant); ok { |
| 395 | + for i, col := range cols { |
| 396 | + if col.Equal(c, nil) { |
| 397 | + return i |
| 398 | + } |
| 399 | + } |
| 400 | + } |
| 401 | + } |
| 402 | + return -1 |
| 403 | +} |
| 404 | + |
| 405 | +// BuildRange is a method which can calculate IntColumnRange, ColumnRange, IndexRange. |
| 406 | +func BuildRange(sc *variable.StatementContext, conds []expression.Expression, rangeType int, cols []*expression.Column, |
| 407 | + lengths []int) (retRanges []types.Range, _ error) { |
| 408 | + if rangeType == IntRangeType { |
| 409 | + ranges, err := buildTableRange(conds, sc) |
| 410 | + if err != nil { |
| 411 | + return nil, errors.Trace(err) |
| 412 | + } |
| 413 | + retRanges = make([]types.Range, 0, len(ranges)) |
| 414 | + for _, ran := range ranges { |
| 415 | + retRanges = append(retRanges, ran) |
| 416 | + } |
| 417 | + } else if rangeType == ColumnRangeType { |
| 418 | + ranges, err := buildColumnRange(conds, sc, cols[0].RetType) |
| 419 | + if err != nil { |
| 420 | + return nil, errors.Trace(err) |
| 421 | + } |
| 422 | + retRanges = make([]types.Range, 0, len(ranges)) |
| 423 | + for _, ran := range ranges { |
| 424 | + retRanges = append(retRanges, ran) |
| 425 | + } |
| 426 | + } else if rangeType == IndexRangeType { |
| 427 | + ranges, err := buildIndexRange(sc, cols, lengths, conds) |
| 428 | + if err != nil { |
| 429 | + return nil, errors.Trace(err) |
| 430 | + } |
| 431 | + retRanges = make([]types.Range, 0, len(ranges)) |
| 432 | + for _, ran := range ranges { |
| 433 | + retRanges = append(retRanges, ran) |
| 434 | + } |
| 435 | + } |
| 436 | + return |
| 437 | +} |
| 438 | + |
| 439 | +// Ranges2IntRanges changes []types.Range to []types.IntColumnRange |
| 440 | +func Ranges2IntRanges(ranges []types.Range) []types.IntColumnRange { |
| 441 | + retRanges := make([]types.IntColumnRange, 0, len(ranges)) |
| 442 | + for _, ran := range ranges { |
| 443 | + retRanges = append(retRanges, ran.Convert2IntRange()) |
| 444 | + } |
| 445 | + return retRanges |
| 446 | +} |
| 447 | + |
| 448 | +// Ranges2ColumnRanges changes []types.Range to []*types.ColumnRange |
| 449 | +func Ranges2ColumnRanges(ranges []types.Range) []*types.ColumnRange { |
| 450 | + retRanges := make([]*types.ColumnRange, 0, len(ranges)) |
| 451 | + for _, ran := range ranges { |
| 452 | + retRanges = append(retRanges, ran.Convert2ColumnRange()) |
| 453 | + } |
| 454 | + return retRanges |
| 455 | +} |
| 456 | + |
| 457 | +// Ranges2IndexRanges changes []types.Range to []*types.IndexRange |
| 458 | +func Ranges2IndexRanges(ranges []types.Range) []*types.IndexRange { |
| 459 | + retRanges := make([]*types.IndexRange, 0, len(ranges)) |
| 460 | + for _, ran := range ranges { |
| 461 | + retRanges = append(retRanges, ran.Convert2IndexRange()) |
| 462 | + } |
| 463 | + return retRanges |
| 464 | +} |
0 commit comments