// Copyright 2017 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, // See the License for the specific language governing permissions and // limitations under the License. package core import ( "fmt" "sort" "strings" "github.com/pingcap/parser/ast" "github.com/pingcap/parser/model" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/set" ) // AggregateFuncExtractor visits Expr tree. // It converts ColunmNameExpr to AggregateFuncExpr and collects AggregateFuncExpr. type AggregateFuncExtractor struct { inAggregateFuncExpr bool // AggFuncs is the collected AggregateFuncExprs. AggFuncs []*ast.AggregateFuncExpr } // Enter implements Visitor interface. func (a *AggregateFuncExtractor) Enter(n ast.Node) (ast.Node, bool) { switch n.(type) { case *ast.AggregateFuncExpr: a.inAggregateFuncExpr = true case *ast.SelectStmt, *ast.UnionStmt: return n, true } return n, false } // Leave implements Visitor interface. func (a *AggregateFuncExtractor) Leave(n ast.Node) (ast.Node, bool) { switch v := n.(type) { case *ast.AggregateFuncExpr: a.inAggregateFuncExpr = false a.AggFuncs = append(a.AggFuncs, v) } return n, true } // WindowFuncExtractor visits Expr tree. // It converts ColunmNameExpr to WindowFuncExpr and collects WindowFuncExpr. type WindowFuncExtractor struct { // WindowFuncs is the collected WindowFuncExprs. windowFuncs []*ast.WindowFuncExpr } // Enter implements Visitor interface. func (a *WindowFuncExtractor) Enter(n ast.Node) (ast.Node, bool) { switch n.(type) { case *ast.SelectStmt, *ast.UnionStmt: return n, true } return n, false } // Leave implements Visitor interface. func (a *WindowFuncExtractor) Leave(n ast.Node) (ast.Node, bool) { switch v := n.(type) { case *ast.WindowFuncExpr: a.windowFuncs = append(a.windowFuncs, v) } return n, true } // logicalSchemaProducer stores the schema for the logical plans who can produce schema directly. type logicalSchemaProducer struct { schema *expression.Schema names types.NameSlice baseLogicalPlan } // Schema implements the Plan.Schema interface. func (s *logicalSchemaProducer) Schema() *expression.Schema { if s.schema == nil { s.schema = expression.NewSchema() } return s.schema } func (s *logicalSchemaProducer) OutputNames() types.NameSlice { return s.names } func (s *logicalSchemaProducer) SetOutputNames(names types.NameSlice) { s.names = names } // SetSchema implements the Plan.SetSchema interface. func (s *logicalSchemaProducer) SetSchema(schema *expression.Schema) { s.schema = schema } func (s *logicalSchemaProducer) setSchemaAndNames(schema *expression.Schema, names types.NameSlice) { s.schema = schema s.names = names } // inlineProjection prunes unneeded columns inline a executor. func (s *logicalSchemaProducer) inlineProjection(parentUsedCols []*expression.Column) { used := expression.GetUsedList(parentUsedCols, s.schema) for i := len(used) - 1; i >= 0; i-- { if !used[i] { s.schema.Columns = append(s.schema.Columns[:i], s.schema.Columns[i+1:]...) } } } // physicalSchemaProducer stores the schema for the physical plans who can produce schema directly. type physicalSchemaProducer struct { schema *expression.Schema basePhysicalPlan } // Schema implements the Plan.Schema interface. func (s *physicalSchemaProducer) Schema() *expression.Schema { if s.schema == nil { s.schema = expression.NewSchema() } return s.schema } // SetSchema implements the Plan.SetSchema interface. func (s *physicalSchemaProducer) SetSchema(schema *expression.Schema) { s.schema = schema } // baseSchemaProducer stores the schema for the base plans who can produce schema directly. type baseSchemaProducer struct { schema *expression.Schema names types.NameSlice basePlan } // OutputNames returns the outputting names of each column. func (s *baseSchemaProducer) OutputNames() types.NameSlice { return s.names } func (s *baseSchemaProducer) SetOutputNames(names types.NameSlice) { s.names = names } // Schema implements the Plan.Schema interface. func (s *baseSchemaProducer) Schema() *expression.Schema { if s.schema == nil { s.schema = expression.NewSchema() } return s.schema } // SetSchema implements the Plan.SetSchema interface. func (s *baseSchemaProducer) SetSchema(schema *expression.Schema) { s.schema = schema } func (s *baseSchemaProducer) setSchemaAndNames(schema *expression.Schema, names types.NameSlice) { s.schema = schema s.names = names } // Schema implements the Plan.Schema interface. func (p *LogicalMaxOneRow) Schema() *expression.Schema { s := p.Children()[0].Schema().Clone() resetNotNullFlag(s, 0, s.Len()) return s } func buildLogicalJoinSchema(joinType JoinType, join LogicalPlan) *expression.Schema { leftSchema := join.Children()[0].Schema() switch joinType { case SemiJoin, AntiSemiJoin: return leftSchema.Clone() case LeftOuterSemiJoin, AntiLeftOuterSemiJoin: newSchema := leftSchema.Clone() newSchema.Append(join.Schema().Columns[join.Schema().Len()-1]) return newSchema } newSchema := expression.MergeSchema(leftSchema, join.Children()[1].Schema()) if joinType == LeftOuterJoin { resetNotNullFlag(newSchema, leftSchema.Len(), newSchema.Len()) } else if joinType == RightOuterJoin { resetNotNullFlag(newSchema, 0, leftSchema.Len()) } return newSchema } // BuildPhysicalJoinSchema builds the schema of PhysicalJoin from it's children's schema. func BuildPhysicalJoinSchema(joinType JoinType, join PhysicalPlan) *expression.Schema { switch joinType { case SemiJoin, AntiSemiJoin: return join.Children()[0].Schema().Clone() case LeftOuterSemiJoin, AntiLeftOuterSemiJoin: newSchema := join.Children()[0].Schema().Clone() newSchema.Append(join.Schema().Columns[join.Schema().Len()-1]) return newSchema } return expression.MergeSchema(join.Children()[0].Schema(), join.Children()[1].Schema()) } // GetStatsInfo gets the statistics info from a physical plan tree. func GetStatsInfo(i interface{}) map[string]uint64 { if i == nil { // it's a workaround for https://github.com/pingcap/tidb/issues/17419 // To entirely fix this, uncomment the assertion in TestPreparedIssue17419 return nil } p := i.(Plan) var physicalPlan PhysicalPlan switch x := p.(type) { case *Insert: physicalPlan = x.SelectPlan case *Update: physicalPlan = x.SelectPlan case *Delete: physicalPlan = x.SelectPlan case PhysicalPlan: physicalPlan = x } if physicalPlan == nil { return nil } statsInfos := make(map[string]uint64) statsInfos = CollectPlanStatsVersion(physicalPlan, statsInfos) return statsInfos } // extractStringFromStringSet helps extract string info from set.StringSet func extractStringFromStringSet(set set.StringSet) string { if len(set) < 1 { return "" } l := make([]string, 0, len(set)) for k := range set { l = append(l, fmt.Sprintf(`"%s"`, k)) } sort.Strings(l) return fmt.Sprintf("%s", strings.Join(l, ",")) } func tableHasDirtyContent(ctx sessionctx.Context, tableInfo *model.TableInfo) bool { pi := tableInfo.GetPartitionInfo() if pi == nil { return ctx.HasDirtyContent(tableInfo.ID) } // Currently, we add UnionScan on every partition even though only one partition's data is changed. // This is limited by current implementation of Partition Prune. It'll be updated once we modify that part. for _, partition := range pi.Definitions { if ctx.HasDirtyContent(partition.ID) { return true } } return false }