Files
tidb/pkg/planner/util/misc.go

382 lines
11 KiB
Go

// Copyright 2022 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package util
import (
"encoding/binary"
"fmt"
"iter"
"math"
"reflect"
"time"
"unsafe"
"github.com/pingcap/tidb/pkg/expression"
"github.com/pingcap/tidb/pkg/kv"
"github.com/pingcap/tidb/pkg/meta/model"
"github.com/pingcap/tidb/pkg/parser/ast"
"github.com/pingcap/tidb/pkg/planner/core/base"
"github.com/pingcap/tidb/pkg/planner/property"
"github.com/pingcap/tidb/pkg/types"
h "github.com/pingcap/tidb/pkg/util/hint"
"github.com/pingcap/tidb/pkg/util/intest"
"github.com/pingcap/tidb/pkg/util/ranger"
)
// SliceDeepClone uses Clone() to clone a slice.
// The elements in the slice must implement func (T) Clone() T.
func SliceDeepClone[T interface{ Clone() T }](s []T) []T {
if s == nil {
return nil
}
cloned := make([]T, 0, len(s))
for _, item := range s {
cloned = append(cloned, item.Clone())
}
return cloned
}
// SliceRecursiveFlattenIter returns an iterator (iter.Seq2) that recursively iterates over all elements of an
// any-dimensional slice of any type.
// Performance note:
// For each slice, this function need to check the dynamic type before iterating over it. For each non-leaf slice, this
// function uses reflect to iterate over it. Be careful when trying to use this function in performance-critical code.
/*
Example:
paths := [][][]*AccessPath{...}
for idx, path := range SliceRecursiveFlattenIter[*AccessPath](paths) {
// path is a *AccessPath here
}
*/
func SliceRecursiveFlattenIter[E any, T any, Slice ~[]T](s Slice) iter.Seq2[int, E] {
return func(yield func(int, E) bool) {
sliceRecursiveFlattenIterHelper(s, yield, 0)
}
}
func sliceRecursiveFlattenIterHelper[E any, Slice any](
s Slice,
yield func(int, E) bool,
startIdx int,
) (nextIdx int, stop bool) {
intest.AssertFunc(func() bool {
return reflect.TypeOf(s).Kind() == reflect.Slice
})
// Case 1: Input slice is []E, which means it's already the lowest level.
if leafSlice, isLeafSlice := any(s).([]E); isLeafSlice {
idx := startIdx
for _, v := range leafSlice {
if !yield(idx, v) {
return idx + 1, true
}
idx++
}
return idx, false
}
// Case 2: Otherwise, element of Slice is still a slice, we need to flatten it recursively.
idx := startIdx
// We have to use reflect to iterate over the slice here.
v := reflect.ValueOf(s)
for i := range v.Len() {
val := v.Index(i).Interface()
intest.AssertFunc(func() bool {
return reflect.TypeOf(val).Kind() == reflect.Slice
})
idx, stop = sliceRecursiveFlattenIterHelper[E](val, yield, idx)
if stop {
return idx, true
}
}
return idx, false
}
// CloneFieldNames uses types.FieldName.Clone to clone a slice of types.FieldName.
func CloneFieldNames(names []*types.FieldName) []*types.FieldName {
if names == nil {
return nil
}
cloned := make([]*types.FieldName, len(names))
for i, name := range names {
cloned[i] = new(types.FieldName)
*cloned[i] = *name
}
return cloned
}
// CloneCIStrs uses ast.CIStr.Clone to clone a slice of ast.CIStr.
func CloneCIStrs(strs []ast.CIStr) []ast.CIStr {
if strs == nil {
return nil
}
cloned := make([]ast.CIStr, 0, len(strs))
cloned = append(cloned, strs...)
return cloned
}
// CloneExprs uses Expression.Clone to clone a slice of Expression.
func CloneExprs(exprs []expression.Expression) []expression.Expression {
if exprs == nil {
return nil
}
cloned := make([]expression.Expression, 0, len(exprs))
for _, e := range exprs {
cloned = append(cloned, e.Clone())
}
return cloned
}
// CloneExpressions uses CloneExprs to clone a slice of expression.Expression.
func CloneExpressions(exprs []expression.Expression) []expression.Expression {
return CloneExprs(exprs)
}
// CloneAssignments uses (*Assignment).Clone to clone a slice of *Assignment.
func CloneAssignments(assignments []*expression.Assignment) []*expression.Assignment {
if assignments == nil {
return nil
}
cloned := make([]*expression.Assignment, 0, len(assignments))
for _, a := range assignments {
cloned = append(cloned, a.Clone())
}
return cloned
}
// CloneHandleCols uses HandleCols.Clone to clone a slice of HandleCols.
func CloneHandleCols(handles []HandleCols) []HandleCols {
if handles == nil {
return nil
}
cloned := make([]HandleCols, 0, len(handles))
for _, h := range handles {
cloned = append(cloned, h.Clone())
}
return cloned
}
// CloneCols uses (*Column).Clone to clone a slice of *Column.
func CloneCols(cols []*expression.Column) []*expression.Column {
if cols == nil {
return nil
}
cloned := make([]*expression.Column, 0, len(cols))
for _, c := range cols {
if c == nil {
cloned = append(cloned, nil)
continue
}
cloned = append(cloned, c.Clone().(*expression.Column))
}
return cloned
}
// CloneConstants uses (*Constant).Clone to clone a slice of *Constant.
func CloneConstants(constants []*expression.Constant) []*expression.Constant {
if constants == nil {
return nil
}
cloned := make([]*expression.Constant, 0, len(constants))
for _, c := range constants {
cloned = append(cloned, c.Clone().(*expression.Constant))
}
return cloned
}
// CloneDatums uses Datum.Clone to clone a slice of Datum.
func CloneDatums(datums []types.Datum) []types.Datum {
if datums == nil {
return nil
}
cloned := make([]types.Datum, 0, len(datums))
for _, d := range datums {
cloned = append(cloned, *d.Clone())
}
return cloned
}
// CloneDatum2D uses CloneDatums to clone a 2D slice of Datum.
func CloneDatum2D(datums [][]types.Datum) [][]types.Datum {
if datums == nil {
return nil
}
cloned := make([][]types.Datum, 0, len(datums))
for _, d := range datums {
cloned = append(cloned, CloneDatums(d))
}
return cloned
}
// CloneColInfos uses (*ColumnInfo).Clone to clone a slice of *ColumnInfo.
func CloneColInfos(cols []*model.ColumnInfo) []*model.ColumnInfo {
if cols == nil {
return nil
}
cloned := make([]*model.ColumnInfo, 0, len(cols))
for _, c := range cols {
cloned = append(cloned, c.Clone())
}
return cloned
}
// CloneRanges uses (*Range).Clone to clone a slice of *Range.
func CloneRanges(ranges []*ranger.Range) []*ranger.Range {
if ranges == nil {
return nil
}
cloned := make([]*ranger.Range, 0, len(ranges))
for _, r := range ranges {
cloned = append(cloned, r.Clone())
}
return cloned
}
// CloneByItemss uses (*ByItems).Clone to clone a slice of *ByItems.
func CloneByItemss(byItems []*ByItems) []*ByItems {
if byItems == nil {
return nil
}
cloned := make([]*ByItems, 0, len(byItems))
for _, item := range byItems {
cloned = append(cloned, item.Clone())
}
return cloned
}
// CloneSortItems uses SortItem.Clone to clone a slice of SortItem.
func CloneSortItems(items []property.SortItem) []property.SortItem {
if items == nil {
return nil
}
cloned := make([]property.SortItem, 0, len(items))
for _, item := range items {
cloned = append(cloned, item.Clone())
}
return cloned
}
// CloneHandles uses Handle.Copy to clone a slice of Handle.
func CloneHandles(handles []kv.Handle) []kv.Handle {
if handles == nil {
return nil
}
cloned := make([]kv.Handle, 0, len(handles))
for _, h := range handles {
cloned = append(cloned, h.Copy())
}
return cloned
}
// QueryTimeRange represents a time range specified by TIME_RANGE hint
type QueryTimeRange struct {
From time.Time
To time.Time
}
// Condition returns a WHERE clause base on it's value
func (tr *QueryTimeRange) Condition() string {
return fmt.Sprintf("where time>='%s' and time<='%s'",
tr.From.Format(MetricTableTimeFormat), tr.To.Format(MetricTableTimeFormat))
}
// MetricTableTimeFormat is the time format for metric table explain and format.
const MetricTableTimeFormat = "2006-01-02 15:04:05.999"
const emptyQueryTimeRangeSize = int64(unsafe.Sizeof(QueryTimeRange{}))
// MemoryUsage return the memory usage of QueryTimeRange
func (tr *QueryTimeRange) MemoryUsage() (sum int64) {
if tr == nil {
return
}
return emptyQueryTimeRangeSize
}
// EncodeIntAsUint32 is used for LogicalPlan Interface
func EncodeIntAsUint32(result []byte, value int) []byte {
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], uint32(value))
return append(result, buf[:]...)
}
// GetMaxSortPrefix returns the prefix offset of sortCols in allCols.
func GetMaxSortPrefix(sortCols, allCols []*expression.Column) []int {
tmpSchema := expression.NewSchema(allCols...)
sortColOffsets := make([]int, 0, len(sortCols))
for _, sortCol := range sortCols {
offset := tmpSchema.ColumnIndex(sortCol)
if offset == -1 {
return sortColOffsets
}
sortColOffsets = append(sortColOffsets, offset)
}
return sortColOffsets
}
// DeriveLimitStats derives the stats of the top-n plan.
func DeriveLimitStats(childProfile *property.StatsInfo, limitCount float64) *property.StatsInfo {
stats := &property.StatsInfo{
RowCount: math.Min(limitCount, childProfile.RowCount),
ColNDVs: make(map[int64]float64, len(childProfile.ColNDVs)),
// limit operation does not change the histogram (kind of sample).
HistColl: childProfile.HistColl,
}
for id, c := range childProfile.ColNDVs {
stats.ColNDVs[id] = math.Min(c, stats.RowCount)
}
return stats
}
// ExtractTableAlias returns table alias of the base.LogicalPlan's columns.
// It will return nil when there are multiple table alias, because the alias is only used to check if
// the base.LogicalPlan Match some optimizer hints, and hints are not expected to take effect in this case.
func ExtractTableAlias(p base.Plan, parentOffset int) *h.HintedTable {
if len(p.OutputNames()) > 0 && p.OutputNames()[0].TblName.L != "" {
firstName := p.OutputNames()[0]
for _, name := range p.OutputNames() {
if name.TblName.L != firstName.TblName.L ||
(name.DBName.L != "" && firstName.DBName.L != "" &&
name.DBName.L != firstName.DBName.L) { // DBName can be nil, see #46160
return nil
}
}
qbOffset := p.QueryBlockOffset()
var blockAsNames []ast.HintTable
if p := p.SCtx().GetSessionVars().PlannerSelectBlockAsName.Load(); p != nil {
blockAsNames = *p
}
// For sub-queries like `(select * from t) t1`, t1 should belong to its surrounding select block.
if qbOffset != parentOffset && blockAsNames != nil && blockAsNames[qbOffset].TableName.L != "" {
qbOffset = parentOffset
}
dbName := firstName.DBName
if dbName.L == "" {
dbName = ast.NewCIStr(p.SCtx().GetSessionVars().CurrentDB)
}
return &h.HintedTable{DBName: dbName, TblName: firstName.TblName, SelectOffset: qbOffset}
}
return nil
}
// GetPushDownCtx creates a PushDownContext from PlanContext
func GetPushDownCtx(pctx base.PlanContext) expression.PushDownContext {
return GetPushDownCtxFromBuildPBContext(pctx.GetBuildPBCtx())
}
// GetPushDownCtxFromBuildPBContext creates a PushDownContext from BuildPBContext
func GetPushDownCtxFromBuildPBContext(bctx *base.BuildPBContext) expression.PushDownContext {
return expression.NewPushDownContext(bctx.GetExprCtx().GetEvalCtx(), bctx.GetClient(),
bctx.InExplainStmt, bctx.WarnHandler, bctx.ExtraWarnghandler, bctx.GroupConcatMaxLen)
}