diff --git a/build/BUILD.bazel b/build/BUILD.bazel index 3e1a9c784f5f7..a3f7b50281558 100644 --- a/build/BUILD.bazel +++ b/build/BUILD.bazel @@ -176,7 +176,6 @@ nogo( }) + select({ "//build:without_rbe": [ - "//build/linter/filepermission", ], "//conditions:default": [], }), diff --git a/pkg/expression/BUILD.bazel b/pkg/expression/BUILD.bazel index bbb5bc6e9efad..79f7c7cdc63cd 100644 --- a/pkg/expression/BUILD.bazel +++ b/pkg/expression/BUILD.bazel @@ -82,7 +82,6 @@ go_library( "//pkg/parser/opcode", "//pkg/parser/terror", "//pkg/parser/types", - "//pkg/planner/funcdep", "//pkg/privilege", "//pkg/sessionctx", "//pkg/sessionctx/stmtctx", @@ -98,6 +97,7 @@ go_library( "//pkg/util/encrypt", "//pkg/util/generatedexpr", "//pkg/util/hack", + "//pkg/util/intset", "//pkg/util/logutil", "//pkg/util/mathutil", "//pkg/util/mock", @@ -119,7 +119,6 @@ go_library( "@com_github_pingcap_tipb//go-tipb", "@com_github_pkg_errors//:errors", "@com_github_tikv_client_go_v2//oracle", - "@org_golang_x_tools//container/intsets", "@org_uber_go_atomic//:atomic", "@org_uber_go_zap//:zap", ], diff --git a/pkg/expression/grouping_sets.go b/pkg/expression/grouping_sets.go index 2aedfb0f7412e..ecd81d38019ce 100644 --- a/pkg/expression/grouping_sets.go +++ b/pkg/expression/grouping_sets.go @@ -19,9 +19,9 @@ import ( "github.com/pingcap/tidb/pkg/kv" "github.com/pingcap/tidb/pkg/parser/mysql" - fd "github.com/pingcap/tidb/pkg/planner/funcdep" "github.com/pingcap/tidb/pkg/sessionctx" "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/intset" "github.com/pingcap/tidb/pkg/util/size" "github.com/pingcap/tipb/go-tipb" ) @@ -178,7 +178,7 @@ func (gss GroupingSets) TargetOne(normalAggArgs []Expression) int { return 0 } // for other normal agg args like: count(a), count(a+b), count(not(a is null)) and so on. - normalAggArgsIDSet := fd.NewFastIntSet() + normalAggArgsIDSet := intset.NewFastIntSet() for _, one := range columnInNormalAggArgs { normalAggArgsIDSet.Insert(int(one.UniqueID)) } @@ -202,7 +202,7 @@ func (gss GroupingSets) TargetOne(normalAggArgs []Expression) int { func (gss GroupingSets) NeedCloneColumn() bool { // for grouping sets like: {},{} / {},{} // the column c should be copied one more time here, otherwise it will be filled with null values and not visible for the other grouping set again. - setIDs := make([]*fd.FastIntSet, 0, len(gss)) + setIDs := make([]*intset.FastIntSet, 0, len(gss)) for _, groupingSet := range gss { setIDs = append(setIDs, groupingSet.AllColIDs()) } @@ -231,8 +231,8 @@ func (gs GroupingSet) IsEmpty() bool { } // AllColIDs collect all the grouping col's uniqueID. (here assuming that all the grouping expressions are single col) -func (gs GroupingSet) AllColIDs() *fd.FastIntSet { - res := fd.NewFastIntSet() +func (gs GroupingSet) AllColIDs() *intset.FastIntSet { + res := intset.NewFastIntSet() for _, groupingExprs := range gs { // on the condition that every grouping expression is single column. // eg: group by a, b, c @@ -313,8 +313,8 @@ func (gss GroupingSets) IsEmpty() bool { } // AllSetsColIDs is used to collect all the column id inside into a fast int set. -func (gss GroupingSets) AllSetsColIDs() *fd.FastIntSet { - res := fd.NewFastIntSet() +func (gss GroupingSets) AllSetsColIDs() *intset.FastIntSet { + res := intset.NewFastIntSet() for _, groupingSet := range gss { res.UnionWith(*groupingSet.AllColIDs()) } @@ -361,8 +361,8 @@ func (g GroupingExprs) IsEmpty() bool { // SubSetOf is used to do the logical computation of subset between two grouping expressions. func (g GroupingExprs) SubSetOf(other GroupingExprs) bool { - oldOne := fd.NewFastIntSet() - newOne := fd.NewFastIntSet() + oldOne := intset.NewFastIntSet() + newOne := intset.NewFastIntSet() for _, one := range g { oldOne.Insert(int(one.(*Column).UniqueID)) } @@ -373,8 +373,8 @@ func (g GroupingExprs) SubSetOf(other GroupingExprs) bool { } // IDSet is used to collect column ids inside grouping expressions into a fast int set. -func (g GroupingExprs) IDSet() *fd.FastIntSet { - res := fd.NewFastIntSet() +func (g GroupingExprs) IDSet() *intset.FastIntSet { + res := intset.NewFastIntSet() for _, one := range g { res.Insert(int(one.(*Column).UniqueID)) } @@ -493,7 +493,7 @@ func AdjustNullabilityFromGroupingSets(gss GroupingSets, schema *Schema) { // set, so it won't be filled with null value at any time, the nullable change is unnecessary. groupingIDs := gss.AllSetsColIDs() // cache the grouping ids set to avoid fetch them multi times below. - groupingIDsSlice := make([]*fd.FastIntSet, 0, len(gss)) + groupingIDsSlice := make([]*intset.FastIntSet, 0, len(gss)) for _, oneGroupingSet := range gss { groupingIDsSlice = append(groupingIDsSlice, oneGroupingSet.AllColIDs()) } @@ -570,7 +570,7 @@ func (gss GroupingSets) DistinctSize() (int, []uint64, map[int]map[uint64]struct func (gss GroupingSets) DistinctSizeWithThreshold(N int) (int, []uint64, map[int]map[uint64]struct{}) { // all the group by item are col, deduplicate from id-set. distinctGroupingIDsPos := make([]int, 0, len(gss)) - originGroupingIDsSlice := make([]*fd.FastIntSet, 0, len(gss)) + originGroupingIDsSlice := make([]*intset.FastIntSet, 0, len(gss)) for _, oneGroupingSet := range gss { curIDs := oneGroupingSet.AllColIDs() diff --git a/pkg/expression/util.go b/pkg/expression/util.go index 041868d53c157..5d0ee104e7c77 100644 --- a/pkg/expression/util.go +++ b/pkg/expression/util.go @@ -35,10 +35,10 @@ import ( driver "github.com/pingcap/tidb/pkg/types/parser_driver" "github.com/pingcap/tidb/pkg/util/chunk" "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/intset" "github.com/pingcap/tidb/pkg/util/logutil" "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" - "golang.org/x/tools/container/intsets" ) // cowExprRef is a copy-on-write slice ref util using in `ColumnSubstitute` @@ -372,15 +372,15 @@ func ExtractColumnsAndCorColumnsFromExpressions(result []*Column, list []Express } // ExtractColumnSet extracts the different values of `UniqueId` for columns in expressions. -func ExtractColumnSet(exprs ...Expression) *intsets.Sparse { - set := &intsets.Sparse{} +func ExtractColumnSet(exprs ...Expression) intset.FastIntSet { + set := intset.NewFastIntSet() for _, expr := range exprs { - extractColumnSet(expr, set) + extractColumnSet(expr, &set) } return set } -func extractColumnSet(expr Expression, set *intsets.Sparse) { +func extractColumnSet(expr Expression, set *intset.FastIntSet) { switch v := expr.(type) { case *Column: set.Insert(int(v.UniqueID)) diff --git a/pkg/planner/core/BUILD.bazel b/pkg/planner/core/BUILD.bazel index 6ef3577b38426..82bf115c8134a 100644 --- a/pkg/planner/core/BUILD.bazel +++ b/pkg/planner/core/BUILD.bazel @@ -144,6 +144,7 @@ go_library( "//pkg/util/hack", "//pkg/util/hint", "//pkg/util/intest", + "//pkg/util/intset", "//pkg/util/kvcache", "//pkg/util/logutil", "//pkg/util/mathutil", diff --git a/pkg/planner/core/collect_column_stats_usage.go b/pkg/planner/core/collect_column_stats_usage.go index 83e0acc09860e..f9c14fc6621a6 100644 --- a/pkg/planner/core/collect_column_stats_usage.go +++ b/pkg/planner/core/collect_column_stats_usage.go @@ -17,7 +17,7 @@ package core import ( "github.com/pingcap/tidb/pkg/expression" "github.com/pingcap/tidb/pkg/parser/model" - "github.com/pingcap/tidb/pkg/planner/funcdep" + "github.com/pingcap/tidb/pkg/util/intset" "golang.org/x/exp/maps" ) @@ -48,7 +48,7 @@ type columnStatsUsageCollector struct { // visitedPhysTblIDs all ds.physicalTableID that have been visited. // It's always collected, even collectHistNeededColumns is not set. - visitedPhysTblIDs *funcdep.FastIntSet + visitedPhysTblIDs *intset.FastIntSet // collectVisitedTable indicates whether to collect visited table collectVisitedTable bool @@ -57,7 +57,7 @@ type columnStatsUsageCollector struct { } func newColumnStatsUsageCollector(collectMode uint64, enabledPlanCapture bool) *columnStatsUsageCollector { - set := funcdep.NewFastIntSet() + set := intset.NewFastIntSet() collector := &columnStatsUsageCollector{ collectMode: collectMode, // Pre-allocate a slice to reduce allocation, 8 doesn't have special meaning. @@ -309,7 +309,7 @@ func (c *columnStatsUsageCollector) collectFromPlan(lp LogicalPlan) { func CollectColumnStatsUsage(lp LogicalPlan, predicate, histNeeded bool) ( []model.TableItemID, []model.TableItemID, - *funcdep.FastIntSet, + *intset.FastIntSet, ) { var mode uint64 if predicate { diff --git a/pkg/planner/core/logical_plan_builder.go b/pkg/planner/core/logical_plan_builder.go index 899b999d6c3c6..69c491215acf6 100644 --- a/pkg/planner/core/logical_plan_builder.go +++ b/pkg/planner/core/logical_plan_builder.go @@ -61,6 +61,7 @@ import ( "github.com/pingcap/tidb/pkg/util/dbterror" "github.com/pingcap/tidb/pkg/util/hack" "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/intset" "github.com/pingcap/tidb/pkg/util/logutil" "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/pingcap/tidb/pkg/util/plancodec" @@ -1869,7 +1870,7 @@ func (b *PlanBuilder) buildProjection(ctx context.Context, p LogicalPlan, fields if fields[offset].AuxiliaryColInAgg { continue } - item := fd.NewFastIntSet() + item := intset.NewFastIntSet() switch x := expr.(type) { case *expression.Column: item.Insert(int(x.UniqueID)) @@ -1911,7 +1912,7 @@ func (b *PlanBuilder) buildProjection(ctx context.Context, p LogicalPlan, fields baseCols := expression.ExtractColumns(expr) errShowCol := baseCols[0] for _, col := range baseCols { - colSet := fd.NewFastIntSet(int(col.UniqueID)) + colSet := intset.NewFastIntSet(int(col.UniqueID)) if !colSet.SubsetOf(strictClosure) { errShowCol = col break @@ -1936,7 +1937,7 @@ func (b *PlanBuilder) buildProjection(ctx context.Context, p LogicalPlan, fields } if fds.GroupByCols.Only1Zero() { // maxOneRow is delayed from agg's ExtractFD logic since some details listed in it. - projectionUniqueIDs := fd.NewFastIntSet() + projectionUniqueIDs := intset.NewFastIntSet() for _, expr := range proj.Exprs { switch x := expr.(type) { case *expression.Column: @@ -5438,7 +5439,7 @@ func (ds *DataSource) ExtractFD() *fd.FDSet { // Once the all conditions are not equal to nil, built it again. if ds.fdSet == nil || ds.allConds != nil { fds := &fd.FDSet{HashCodeToUniqueID: make(map[string]int)} - allCols := fd.NewFastIntSet() + allCols := intset.NewFastIntSet() // should use the column's unique ID avoiding fdSet conflict. for _, col := range ds.TblCols { // todo: change it to int64 @@ -5446,7 +5447,7 @@ func (ds *DataSource) ExtractFD() *fd.FDSet { } // int pk doesn't store its index column in indexInfo. if ds.tableInfo.PKIsHandle { - keyCols := fd.NewFastIntSet() + keyCols := intset.NewFastIntSet() for _, col := range ds.TblCols { if mysql.HasPriKeyFlag(col.RetType.GetFlag()) { keyCols.Insert(int(col.UniqueID)) @@ -5472,7 +5473,7 @@ func (ds *DataSource) ExtractFD() *fd.FDSet { } // other indices including common handle. for _, idx := range ds.tableInfo.Indices { - keyCols := fd.NewFastIntSet() + keyCols := intset.NewFastIntSet() allColIsNotNull := true if ds.isForUpdateRead && changed { latestIndex, ok := latestIndexes[idx.ID] @@ -5531,14 +5532,14 @@ func (ds *DataSource) ExtractFD() *fd.FDSet { // the generated column is sequentially dependent on the forward column. // a int, b int as (a+1), c int as (b+1), here we can build the strict FD down: // {a} -> {b}, {b} -> {c}, put the maintenance of the dependencies between generated columns to the FD graph. - notNullCols := fd.NewFastIntSet() + notNullCols := intset.NewFastIntSet() for _, col := range ds.TblCols { if col.VirtualExpr != nil { - dependencies := fd.NewFastIntSet() + dependencies := intset.NewFastIntSet() dependencies.Insert(int(col.UniqueID)) // dig out just for 1 level. directBaseCol := expression.ExtractColumns(col.VirtualExpr) - determinant := fd.NewFastIntSet() + determinant := intset.NewFastIntSet() for _, col := range directBaseCol { determinant.Insert(int(col.UniqueID)) } @@ -6429,8 +6430,8 @@ func (b *PlanBuilder) buildUpdateLists(ctx context.Context, tableList []*ast.Tab allAssignmentsAreConstant = false } p = np - if col, ok := newExpr.(*expression.Column); ok { - b.ctx.GetSessionVars().StmtCtx.ColRefFromUpdatePlan = append(b.ctx.GetSessionVars().StmtCtx.ColRefFromUpdatePlan, col.UniqueID) + if cols := expression.ExtractColumnSet(newExpr); cols.Len() > 0 { + b.ctx.GetSessionVars().StmtCtx.ColRefFromUpdatePlan.UnionWith(cols) } newList = append(newList, &expression.Assignment{Col: col, ColName: name.ColName, Expr: newExpr}) dbName := name.DBName.L diff --git a/pkg/planner/core/logical_plans.go b/pkg/planner/core/logical_plans.go index 90fa385f9873d..4ead8b5aed0be 100644 --- a/pkg/planner/core/logical_plans.go +++ b/pkg/planner/core/logical_plans.go @@ -36,6 +36,7 @@ import ( "github.com/pingcap/tidb/pkg/statistics" "github.com/pingcap/tidb/pkg/table" "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/intset" "github.com/pingcap/tidb/pkg/util/logutil" "github.com/pingcap/tidb/pkg/util/ranger" "github.com/pingcap/tidb/pkg/util/size" @@ -298,7 +299,7 @@ func (p *LogicalJoin) extractFDForOuterJoin(filtersFromApply []expression.Expres outerFD, innerFD := p.children[0].ExtractFD(), p.children[1].ExtractFD() innerCondition := p.RightConditions outerCondition := p.LeftConditions - outerCols, innerCols := fd.NewFastIntSet(), fd.NewFastIntSet() + outerCols, innerCols := intset.NewFastIntSet(), intset.NewFastIntSet() for _, col := range p.children[0].Schema().Columns { outerCols.Insert(int(col.UniqueID)) } @@ -326,7 +327,7 @@ func (p *LogicalJoin) extractFDForOuterJoin(filtersFromApply []expression.Expres equivUniqueIDs := extractEquivalenceCols(allConds, p.SCtx(), filterFD) filterFD.AddConstants(constUniqueIDs) - equivOuterUniqueIDs := fd.NewFastIntSet() + equivOuterUniqueIDs := intset.NewFastIntSet() equivAcrossNum := 0 for _, equiv := range equivUniqueIDs { filterFD.AddEquivalence(equiv[0], equiv[1]) @@ -354,7 +355,7 @@ func (p *LogicalJoin) extractFDForOuterJoin(filtersFromApply []expression.Expres // other condition may contain right side cols, it doesn't affect the judgement of intersection of non-left-equiv cols. outConditionCols = append(outConditionCols, expression.ExtractColumnsFromExpressions(nil, p.OtherConditions, nil)...) } - outerConditionUniqueIDs := fd.NewFastIntSet() + outerConditionUniqueIDs := intset.NewFastIntSet() for _, col := range outConditionCols { outerConditionUniqueIDs.Insert(int(col.UniqueID)) } @@ -857,8 +858,8 @@ func (p *LogicalProjection) ExtractFD() *fd.FDSet { // basically extract the children's fdSet. fds := p.logicalSchemaProducer.ExtractFD() // collect the output columns' unique ID. - outputColsUniqueIDs := fd.NewFastIntSet() - notnullColsUniqueIDs := fd.NewFastIntSet() + outputColsUniqueIDs := intset.NewFastIntSet() + notnullColsUniqueIDs := intset.NewFastIntSet() outputColsUniqueIDsArray := make([]int, 0, len(p.Schema().Columns)) // here schema extended columns may contain expr, const and column allocated with uniqueID. for _, one := range p.Schema().Columns { @@ -885,7 +886,7 @@ func (p *LogicalProjection) ExtractFD() *fd.FDSet { constantUniqueID = outputColsUniqueIDsArray[idx] fds.RegisterUniqueID(string(x.HashCode(p.SCtx().GetSessionVars().StmtCtx)), constantUniqueID) } - fds.AddConstants(fd.NewFastIntSet(constantUniqueID)) + fds.AddConstants(intset.NewFastIntSet(constantUniqueID)) case *expression.ScalarFunction: // t1(a,b,c), t2(m,n) // select a, (select c+n from t2 where m=b) from t1; @@ -908,9 +909,9 @@ func (p *LogicalProjection) ExtractFD() *fd.FDSet { } else { // since the scalar's hash code has been registered before, the equivalence exists between the unique ID // allocated by phase of building-projection-for-scalar and that of previous registered unique ID. - fds.AddEquivalence(fd.NewFastIntSet(scalarUniqueID), fd.NewFastIntSet(outputColsUniqueIDsArray[idx])) + fds.AddEquivalence(intset.NewFastIntSet(scalarUniqueID), intset.NewFastIntSet(outputColsUniqueIDsArray[idx])) } - determinants := fd.NewFastIntSet() + determinants := intset.NewFastIntSet() extractedColumns := expression.ExtractColumns(x) extractedCorColumns := expression.ExtractCorColumns(x) for _, one := range extractedColumns { @@ -927,7 +928,7 @@ func (p *LogicalProjection) ExtractFD() *fd.FDSet { if notnull || determinants.SubsetOf(fds.NotNullCols) { notnullColsUniqueIDs.Insert(scalarUniqueID) } - fds.AddStrictFunctionalDependency(determinants, fd.NewFastIntSet(scalarUniqueID)) + fds.AddStrictFunctionalDependency(determinants, intset.NewFastIntSet(scalarUniqueID)) } } @@ -1013,10 +1014,10 @@ func (la *LogicalAggregation) ExtractFD() *fd.FDSet { // basically extract the children's fdSet. fds := la.logicalSchemaProducer.ExtractFD() // collect the output columns' unique ID. - outputColsUniqueIDs := fd.NewFastIntSet() - notnullColsUniqueIDs := fd.NewFastIntSet() - groupByColsUniqueIDs := fd.NewFastIntSet() - groupByColsOutputCols := fd.NewFastIntSet() + outputColsUniqueIDs := intset.NewFastIntSet() + notnullColsUniqueIDs := intset.NewFastIntSet() + groupByColsUniqueIDs := intset.NewFastIntSet() + groupByColsOutputCols := intset.NewFastIntSet() // Since the aggregation is build ahead of projection, the latter one will reuse the column with UniqueID allocated in aggregation // via aggMapper, so we don't need unnecessarily maintain the mapping in the FDSet like expr did, just treating // it as normal column. @@ -1051,7 +1052,7 @@ func (la *LogicalAggregation) ExtractFD() *fd.FDSet { fds.RegisterUniqueID(hashCode, scalarUniqueID) groupByColsUniqueIDs.Insert(scalarUniqueID) } - determinants := fd.NewFastIntSet() + determinants := intset.NewFastIntSet() extractedColumns := expression.ExtractColumns(x) extractedCorColumns := expression.ExtractCorColumns(x) for _, one := range extractedColumns { @@ -1066,7 +1067,7 @@ func (la *LogicalAggregation) ExtractFD() *fd.FDSet { if notnull || determinants.SubsetOf(fds.NotNullCols) { notnullColsUniqueIDs.Insert(scalarUniqueID) } - fds.AddStrictFunctionalDependency(determinants, fd.NewFastIntSet(scalarUniqueID)) + fds.AddStrictFunctionalDependency(determinants, intset.NewFastIntSet(scalarUniqueID)) } } @@ -1078,7 +1079,7 @@ func (la *LogicalAggregation) ExtractFD() *fd.FDSet { // // and since any_value will NOT be pushed down to agg schema, which means every firstRow aggDes in the agg logical operator // is meaningless to build the FD with. Let's only store the non-firstRow FD down: {group by items} ~~> {real aggDes} - realAggFuncUniqueID := fd.NewFastIntSet() + realAggFuncUniqueID := intset.NewFastIntSet() for i, aggDes := range la.AggFuncs { if aggDes.Name != "firstrow" { realAggFuncUniqueID.Insert(int(la.schema.Columns[i].UniqueID)) @@ -1095,7 +1096,7 @@ func (la *LogicalAggregation) ExtractFD() *fd.FDSet { // 0 unique id is only used for here. groupByColsUniqueIDs.Insert(0) for i, ok := realAggFuncUniqueID.Next(0); ok; i, ok = realAggFuncUniqueID.Next(i + 1) { - fds.AddStrictFunctionalDependency(groupByColsUniqueIDs, fd.NewFastIntSet(i)) + fds.AddStrictFunctionalDependency(groupByColsUniqueIDs, intset.NewFastIntSet(i)) } } else { // eliminating input columns that are un-projected. @@ -1107,7 +1108,7 @@ func (la *LogicalAggregation) ExtractFD() *fd.FDSet { // 1: it can always distinguish and group the all-null/part-null group column rows. // 2: the rows with all/part null group column are unique row after group operation. // 3: there won't be two same group key with different agg values, so strict FD secured. - fds.AddStrictFunctionalDependency(groupByColsUniqueIDs, fd.NewFastIntSet(i)) + fds.AddStrictFunctionalDependency(groupByColsUniqueIDs, intset.NewFastIntSet(i)) } // agg funcDes has been tag not null flag when building aggregation. @@ -1211,7 +1212,7 @@ type LogicalSelection struct { Conditions []expression.Expression } -func extractNotNullFromConds(conditions []expression.Expression, p LogicalPlan) fd.FastIntSet { +func extractNotNullFromConds(conditions []expression.Expression, p LogicalPlan) intset.FastIntSet { // extract the column NOT NULL rejection characteristic from selection condition. // CNF considered only, DNF doesn't have its meanings (cause that condition's eval may don't take effect) // @@ -1224,7 +1225,7 @@ func extractNotNullFromConds(conditions []expression.Expression, p LogicalPlan) // 2: `b` must be null since only `NULL is NULL` is evaluated as true. // // As a result, `a` will be extracted as not-null column to abound the FDSet. - notnullColsUniqueIDs := fd.NewFastIntSet() + notnullColsUniqueIDs := intset.NewFastIntSet() for _, condition := range conditions { var cols []*expression.Column cols = expression.ExtractColumnsFromExpressions(cols, []expression.Expression{condition}, nil) @@ -1237,13 +1238,13 @@ func extractNotNullFromConds(conditions []expression.Expression, p LogicalPlan) return notnullColsUniqueIDs } -func extractConstantCols(conditions []expression.Expression, sctx sessionctx.Context, fds *fd.FDSet) fd.FastIntSet { +func extractConstantCols(conditions []expression.Expression, sctx sessionctx.Context, fds *fd.FDSet) intset.FastIntSet { // extract constant cols // eg: where a=1 and b is null and (1+c)=5. // TODO: Some columns can only be determined to be constant from multiple constraints (e.g. x <= 1 AND x >= 1) var ( constObjs []expression.Expression - constUniqueIDs = fd.NewFastIntSet() + constUniqueIDs = intset.NewFastIntSet() ) constObjs = expression.ExtractConstantEqColumnsOrScalar(sctx, constObjs, conditions) for _, constObj := range constObjs { @@ -1264,10 +1265,10 @@ func extractConstantCols(conditions []expression.Expression, sctx sessionctx.Con return constUniqueIDs } -func extractEquivalenceCols(conditions []expression.Expression, sctx sessionctx.Context, fds *fd.FDSet) [][]fd.FastIntSet { +func extractEquivalenceCols(conditions []expression.Expression, sctx sessionctx.Context, fds *fd.FDSet) [][]intset.FastIntSet { var equivObjsPair [][]expression.Expression equivObjsPair = expression.ExtractEquivalenceColumns(equivObjsPair, conditions) - equivUniqueIDs := make([][]fd.FastIntSet, 0, len(equivObjsPair)) + equivUniqueIDs := make([][]intset.FastIntSet, 0, len(equivObjsPair)) for _, equivObjPair := range equivObjsPair { // lhs of equivalence. var ( @@ -1301,7 +1302,7 @@ func extractEquivalenceCols(conditions []expression.Expression, sctx sessionctx. rhsUniqueID = scalarUniqueID } } - equivUniqueIDs = append(equivUniqueIDs, []fd.FastIntSet{fd.NewFastIntSet(lhsUniqueID), fd.NewFastIntSet(rhsUniqueID)}) + equivUniqueIDs = append(equivUniqueIDs, []intset.FastIntSet{intset.NewFastIntSet(lhsUniqueID), intset.NewFastIntSet(rhsUniqueID)}) } return equivUniqueIDs } @@ -1311,8 +1312,8 @@ func (p *LogicalSelection) ExtractFD() *fd.FDSet { // basically extract the children's fdSet. fds := p.baseLogicalPlan.ExtractFD() // collect the output columns' unique ID. - outputColsUniqueIDs := fd.NewFastIntSet() - notnullColsUniqueIDs := fd.NewFastIntSet() + outputColsUniqueIDs := intset.NewFastIntSet() + notnullColsUniqueIDs := intset.NewFastIntSet() // eg: select t2.a, count(t2.b) from t1 join t2 using (a) where t1.a = 1 // join's schema will miss t2.a while join.full schema has. since selection // itself doesn't contain schema, extracting schema should tell them apart. diff --git a/pkg/planner/core/plan_stats.go b/pkg/planner/core/plan_stats.go index a2807e2a41047..b798e51dd722f 100644 --- a/pkg/planner/core/plan_stats.go +++ b/pkg/planner/core/plan_stats.go @@ -22,12 +22,12 @@ import ( "github.com/pingcap/tidb/pkg/domain" "github.com/pingcap/tidb/pkg/infoschema" "github.com/pingcap/tidb/pkg/parser/model" - "github.com/pingcap/tidb/pkg/planner/funcdep" "github.com/pingcap/tidb/pkg/sessionctx" "github.com/pingcap/tidb/pkg/sessionctx/variable" "github.com/pingcap/tidb/pkg/sessiontxn" "github.com/pingcap/tidb/pkg/statistics" "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/intset" "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) @@ -52,7 +52,7 @@ func (collectPredicateColumnsPoint) optimize(_ context.Context, plan LogicalPlan is := sessiontxn.GetTxnManager(plan.SCtx()).GetTxnInfoSchema() statsHandle := domain.GetDomain(plan.SCtx()).StatsHandle() tblID2Tbl := make(map[int64]table.Table) - physTblIDsWithNeededCols := funcdep.NewFastIntSet() + physTblIDsWithNeededCols := intset.NewFastIntSet() for _, neededCol := range histNeededColumns { physTblIDsWithNeededCols.Insert(int(neededCol.TableID)) } diff --git a/pkg/planner/core/recheck_cte.go b/pkg/planner/core/recheck_cte.go index bc74077e39d84..87df6cd348f9f 100644 --- a/pkg/planner/core/recheck_cte.go +++ b/pkg/planner/core/recheck_cte.go @@ -14,19 +14,19 @@ package core -import "github.com/pingcap/tidb/pkg/planner/funcdep" +import "github.com/pingcap/tidb/pkg/util/intset" // RecheckCTE fills the IsOuterMostCTE field for CTEs. // It's a temp solution to before we fully use the Sequence to optimize the CTEs. // This func checks whether the CTE is referenced only by the main query or not. func RecheckCTE(p LogicalPlan) { - visited := funcdep.NewFastIntSet() + visited := intset.NewFastIntSet() findCTEs(p, &visited, true) } func findCTEs( p LogicalPlan, - visited *funcdep.FastIntSet, + visited *intset.FastIntSet, isRootTree bool, ) { if cteReader, ok := p.(*LogicalCTE); ok { diff --git a/pkg/planner/core/rule_aggregation_skew_rewrite.go b/pkg/planner/core/rule_aggregation_skew_rewrite.go index d82e0d0dc4d47..5188f0ad49149 100644 --- a/pkg/planner/core/rule_aggregation_skew_rewrite.go +++ b/pkg/planner/core/rule_aggregation_skew_rewrite.go @@ -21,7 +21,7 @@ import ( "github.com/pingcap/tidb/pkg/expression" "github.com/pingcap/tidb/pkg/expression/aggregation" "github.com/pingcap/tidb/pkg/parser/ast" - fd "github.com/pingcap/tidb/pkg/planner/funcdep" + "github.com/pingcap/tidb/pkg/util/intset" ) type skewDistinctAggRewriter struct { @@ -95,7 +95,7 @@ func (a *skewDistinctAggRewriter) rewriteSkewDistinctAgg(agg *LogicalAggregation groupCols := make([]*expression.Column, 0, 3) // columns that should be used by firstrow(), which will be appended to // bottomAgg schema and aggregate functions - firstRowCols := fd.NewFastIntSet() + firstRowCols := intset.NewFastIntSet() for _, groupByItem := range agg.GroupByItems { usedCols := expression.ExtractColumns(groupByItem) groupCols = append(groupCols, usedCols...) diff --git a/pkg/planner/core/rule_eliminate_projection.go b/pkg/planner/core/rule_eliminate_projection.go index 65405cdb87360..d1a7882a22075 100644 --- a/pkg/planner/core/rule_eliminate_projection.go +++ b/pkg/planner/core/rule_eliminate_projection.go @@ -80,13 +80,6 @@ func canProjectionBeEliminatedStrict(p *PhysicalProjection) bool { if p.Schema().Len() != child.Schema().Len() { return false } - for _, ref := range p.SCtx().GetSessionVars().StmtCtx.ColRefFromUpdatePlan { - for _, one := range p.Schema().Columns { - if ref == one.UniqueID { - return false - } - } - } for i, expr := range p.Exprs { col, ok := expr.(*expression.Column) if !ok || !col.Equal(nil, child.Schema().Columns[i]) { @@ -140,6 +133,11 @@ func doPhysicalProjectionElimination(p PhysicalPlan) PhysicalPlan { if childProj, ok := child.(*PhysicalProjection); ok { childProj.SetSchema(p.Schema()) } + for i, col := range p.Schema().Columns { + if p.SCtx().GetSessionVars().StmtCtx.ColRefFromUpdatePlan.Has(int(col.UniqueID)) && !child.Schema().Columns[i].Equal(nil, col) { + return p + } + } return child } diff --git a/pkg/planner/funcdep/BUILD.bazel b/pkg/planner/funcdep/BUILD.bazel index f746f3ca098c3..9c130066930a3 100644 --- a/pkg/planner/funcdep/BUILD.bazel +++ b/pkg/planner/funcdep/BUILD.bazel @@ -4,14 +4,13 @@ go_library( name = "funcdep", srcs = [ "doc.go", - "fast_int_set.go", "fd_graph.go", ], importpath = "github.com/pingcap/tidb/pkg/planner/funcdep", visibility = ["//visibility:public"], deps = [ + "//pkg/util/intset", "//pkg/util/logutil", - "@org_golang_x_tools//container/intsets", ], ) @@ -20,14 +19,12 @@ go_test( timeout = "short", srcs = [ "extract_fd_test.go", - "fast_int_set_bench_test.go", - "fast_int_set_test.go", "fd_graph_test.go", "main_test.go", ], embed = [":funcdep"], flaky = True, - shard_count = 15, + shard_count = 10, deps = [ "//pkg/domain", "//pkg/infoschema", @@ -38,9 +35,8 @@ go_test( "//pkg/testkit", "//pkg/testkit/testsetup", "//pkg/util/hint", + "//pkg/util/intset", "@com_github_stretchr_testify//require", - "@org_golang_x_exp//maps", - "@org_golang_x_tools//container/intsets", "@org_uber_go_goleak//:goleak", ], ) diff --git a/pkg/planner/funcdep/fd_graph.go b/pkg/planner/funcdep/fd_graph.go index 0818a474ddca6..6efc531134b04 100644 --- a/pkg/planner/funcdep/fd_graph.go +++ b/pkg/planner/funcdep/fd_graph.go @@ -18,6 +18,7 @@ import ( "fmt" "strings" + "github.com/pingcap/tidb/pkg/util/intset" "github.com/pingcap/tidb/pkg/util/logutil" ) @@ -25,8 +26,8 @@ type fdEdge struct { // functional dependency = determinants -> dependencies // determinants = from // dependencies = to - from FastIntSet - to FastIntSet + from intset.FastIntSet + to intset.FastIntSet // The value of the strict and eq bool forms the four kind of edges: // functional dependency, lax functional dependency, strict equivalence constraint, lax equivalence constraint. // And if there's a functional dependency `const` -> `column` exists. We would let the from side be empty. @@ -36,7 +37,7 @@ type fdEdge struct { // FD with non-nil conditionNC is hidden in FDSet, it will be visible again when at least one null-reject column in conditionNC. // conditionNC should be satisfied before some FD make vision again, it's quite like lax FD to be strengthened as strict // one. But the constraints should take effect on specified columns from conditionNC rather than just determinant columns. - conditionNC *FastIntSet + conditionNC *intset.FastIntSet } // FDSet is the main portal of functional dependency, it stores the relationship between (extended table / physical table)'s @@ -53,13 +54,13 @@ type FDSet struct { // eg: {1} ~~> {2,3}, when {2,3} not null is applied, it actually does nothing. // but we should record {2,3} as not-null down for the convenience of transferring // Lax FD: {1} ~~> {2,3} to strict FD: {1} --> {2,3} with {1} as not-null next time. - NotNullCols FastIntSet + NotNullCols intset.FastIntSet // HashCodeToUniqueID map the expression's hashcode to a statement allocated unique // ID quite like the unique ID bounded with column. It's mainly used to add the expr // to the fdSet as an extended column. HashCodeToUniqueID map[string]int // GroupByCols is used to record columns / expressions that under the group by phrase. - GroupByCols FastIntSet + GroupByCols intset.FastIntSet HasAggBuilt bool // todo: when multi join and across select block, this may need to be maintained more precisely. @@ -67,7 +68,7 @@ type FDSet struct { } // ClosureOfStrict is exported for outer usage. -func (s *FDSet) ClosureOfStrict(colSet FastIntSet) FastIntSet { +func (s *FDSet) ClosureOfStrict(colSet intset.FastIntSet) intset.FastIntSet { return s.closureOfStrict(colSet) } @@ -76,8 +77,8 @@ func (s *FDSet) ClosureOfStrict(colSet FastIntSet) FastIntSet { // eg: considering closure F: {A-> CD, B -> E}, and input is {AB} // res: AB -> {CDE} (AB is included in trivial FD) // The time complexity is O(n^2). -func (s *FDSet) closureOfStrict(colSet FastIntSet) FastIntSet { - resultSet := NewFastIntSet() +func (s *FDSet) closureOfStrict(colSet intset.FastIntSet) intset.FastIntSet { + resultSet := intset.NewFastIntSet() // self included. resultSet.UnionWith(colSet) for i := 0; i < len(s.fdEdges); i++ { @@ -100,16 +101,16 @@ func (s *FDSet) closureOfStrict(colSet FastIntSet) FastIntSet { } // ClosureOfLax is exported for outer usage. -func (s *FDSet) ClosureOfLax(colSet FastIntSet) FastIntSet { +func (s *FDSet) ClosureOfLax(colSet intset.FastIntSet) intset.FastIntSet { return s.closureOfLax(colSet) } // ClosureOfLax is used to find lax fd closure of X with respect to F. -func (s *FDSet) closureOfLax(colSet FastIntSet) FastIntSet { +func (s *FDSet) closureOfLax(colSet intset.FastIntSet) intset.FastIntSet { // Lax dependencies are not transitive (see figure 2.1 in the paper for // properties that hold for lax dependencies), so only include them if they // are reachable in a single lax dependency step from the input set. - laxOneStepReached := NewFastIntSet() + laxOneStepReached := intset.NewFastIntSet() // self included. laxOneStepReached.UnionWith(colSet) for i := 0; i < len(s.fdEdges); i++ { @@ -135,8 +136,8 @@ func (s *FDSet) closureOfLax(colSet FastIntSet) FastIntSet { } // closureOfEquivalence is to find strict equivalence closure of X with respect to F. -func (s *FDSet) closureOfEquivalence(colSet FastIntSet) FastIntSet { - resultSet := NewFastIntSet() +func (s *FDSet) closureOfEquivalence(colSet intset.FastIntSet) intset.FastIntSet { + resultSet := intset.NewFastIntSet() // self included. resultSet.UnionWith(colSet) for i := 0; i < len(s.fdEdges); i++ { @@ -154,11 +155,11 @@ func (s *FDSet) closureOfEquivalence(colSet FastIntSet) FastIntSet { // InClosure is used to judge whether fd: setA -> setB can be inferred from closure s. // It's a short-circuit version of the `closureOf`. -func (s *FDSet) InClosure(setA, setB FastIntSet) bool { +func (s *FDSet) InClosure(setA, setB intset.FastIntSet) bool { if setB.SubsetOf(setA) { return true } - currentClosure := NewFastIntSet() + currentClosure := intset.NewFastIntSet() // self included. currentClosure.UnionWith(setA) for i := 0; i < len(s.fdEdges); i++ { @@ -191,9 +192,9 @@ func (s *FDSet) InClosure(setA, setB FastIntSet) bool { // ReduceCols is used to minimize the determinants in one fd input. // function dependency = determinants -> dependencies // given: AB -> XY, once B can be inferred from current closure when inserting, take A -> XY instead. -func (s *FDSet) ReduceCols(colSet FastIntSet) FastIntSet { +func (s *FDSet) ReduceCols(colSet intset.FastIntSet) intset.FastIntSet { // Suppose the colSet is A and B, we have A --> B. Then we only need A since B' value is always determined by A. - var removed, result = NewFastIntSet(), NewFastIntSet() + var removed, result = intset.NewFastIntSet(), intset.NewFastIntSet() result.CopyFrom(colSet) for k, ok := colSet.Next(0); ok; k, ok = colSet.Next(k + 1) { removed.Insert(k) @@ -208,17 +209,17 @@ func (s *FDSet) ReduceCols(colSet FastIntSet) FastIntSet { } // AddStrictFunctionalDependency is to add `STRICT` functional dependency to the fdGraph. -func (s *FDSet) AddStrictFunctionalDependency(from, to FastIntSet) { +func (s *FDSet) AddStrictFunctionalDependency(from, to intset.FastIntSet) { s.addFunctionalDependency(from, to, true, false) } // AddLaxFunctionalDependency is to add `LAX` functional dependency to the fdGraph. -func (s *FDSet) AddLaxFunctionalDependency(from, to FastIntSet) { +func (s *FDSet) AddLaxFunctionalDependency(from, to intset.FastIntSet) { s.addFunctionalDependency(from, to, false, false) } // AddNCFunctionalDependency is to add conditional functional dependency to the fdGraph. -func (s *FDSet) AddNCFunctionalDependency(from, to, nc FastIntSet, strict, equiv bool) { +func (s *FDSet) AddNCFunctionalDependency(from, to, nc intset.FastIntSet, strict, equiv bool) { // Since nc edge is invisible by now, just collecting them together simply, once the // null-reject on nc cols is satisfied, let's pick them out and insert into the fdEdge // normally. @@ -239,7 +240,7 @@ func (s *FDSet) AddNCFunctionalDependency(from, to, nc FastIntSet, strict, equiv // // To reduce the edge number, we limit the functional dependency when we insert into the // set. The key code of insert is like the following codes. -func (s *FDSet) addFunctionalDependency(from, to FastIntSet, strict, equiv bool) { +func (s *FDSet) addFunctionalDependency(from, to intset.FastIntSet, strict, equiv bool) { // trivial FD, refused. if to.SubsetOf(from) { return @@ -341,7 +342,7 @@ func (e *fdEdge) implies(otherEdge *fdEdge) bool { // 1: they may be integrated into the origin equivalence superset if this two enclosure have some id in common. // 2: they can be used to extend the existed constant closure, consequently leading some reduce work: see addConstant. // 3: they self can be used to eliminate existed strict/lax FDs, see comments below. -func (s *FDSet) addEquivalence(eqs FastIntSet) { +func (s *FDSet) addEquivalence(eqs intset.FastIntSet) { var addConst bool // get equivalence closure. eqClosure := s.closureOfEquivalence(eqs) @@ -393,7 +394,7 @@ func (s *FDSet) addEquivalence(eqs FastIntSet) { // in our fdSet, the equivalence will be stored like: {a, b, c, e} == {a, b, c,e} // According and characteristic and reflexivity, each element in the equivalence enclosure // can derive whatever in the same enclosure. -func (s *FDSet) AddEquivalence(from, to FastIntSet) { +func (s *FDSet) AddEquivalence(from, to intset.FastIntSet) { // trivial equivalence, refused. if to.SubsetOf(from) { return @@ -414,7 +415,7 @@ func (s *FDSet) AddEquivalence(from, to FastIntSet) { // 1: constant can be propagated in the equivalence/strict closure, turning strict FD as a constant one. // 2: constant can simplify the strict FD both in determinants side and dependencies side. // 3: constant can simplify the lax FD in the dependencies side. -func (s *FDSet) AddConstants(cons FastIntSet) { +func (s *FDSet) AddConstants(cons intset.FastIntSet) { if cons.IsEmpty() { return } @@ -467,7 +468,7 @@ func (s *FDSet) AddConstants(cons FastIntSet) { // would be. So let B be null value, once two row like: <1, null> and <1, null> (false interpreted), // their dependencies may would be <3>, <4>, once we eliminate B here, FDs looks like: <1>,<1> lax // determinate <3>,<4>. -func (e *fdEdge) removeColumnsFromSide(cons FastIntSet) bool { +func (e *fdEdge) removeColumnsFromSide(cons intset.FastIntSet) bool { if e.from.Intersects(cons) { e.from = e.from.Difference(cons) } @@ -490,7 +491,7 @@ func (e *fdEdge) isEquivalence() bool { // // once B is a constant, only the C's value can be determined by A, this kind of irrelevant coefficient // can be removed in the to side both for strict and lax FDs. -func (e *fdEdge) removeColumnsToSide(cons FastIntSet) bool { +func (e *fdEdge) removeColumnsToSide(cons intset.FastIntSet) bool { if e.to.Intersects(cons) { e.to = e.to.Difference(cons) } @@ -498,17 +499,17 @@ func (e *fdEdge) removeColumnsToSide(cons FastIntSet) bool { } // ConstantCols returns the set of columns that will always have the same value for all rows in table. -func (s *FDSet) ConstantCols() FastIntSet { +func (s *FDSet) ConstantCols() intset.FastIntSet { for i := 0; i < len(s.fdEdges); i++ { if s.fdEdges[i].isConstant() { return s.fdEdges[i].to } } - return FastIntSet{} + return intset.FastIntSet{} } // EquivalenceCols returns the set of columns that are constrained to equal to each other. -func (s *FDSet) EquivalenceCols() (eqs []*FastIntSet) { +func (s *FDSet) EquivalenceCols() (eqs []*intset.FastIntSet) { for i := 0; i < len(s.fdEdges); i++ { if s.fdEdges[i].isEquivalence() { // return either side is the same. @@ -521,7 +522,7 @@ func (s *FDSet) EquivalenceCols() (eqs []*FastIntSet) { // MakeNotNull modify the FD set based the listed column with NOT NULL flags. // Most of the case is used in the derived process after predicate evaluation, // which can upgrade lax FDs to strict ones. -func (s *FDSet) MakeNotNull(notNullCols FastIntSet) { +func (s *FDSet) MakeNotNull(notNullCols intset.FastIntSet) { notNullCols.UnionWith(s.NotNullCols) notNullColsSet := s.closureOfEquivalence(notNullCols) // make nc FD visible. @@ -565,7 +566,7 @@ func (s *FDSet) MakeNotNull(notNullCols FastIntSet) { } // MakeNullable make the fd's NotNullCols to be cleaned, after the both side fds are handled it can be nullable. -func (s *FDSet) MakeNullable(nullableCols FastIntSet) { +func (s *FDSet) MakeNullable(nullableCols intset.FastIntSet) { s.NotNullCols.DifferenceWith(nullableCols) } @@ -728,7 +729,7 @@ func (s *FDSet) MakeCartesianProduct(rhs *FDSet) { // - If the right side has no row, we will supply null-extended rows, then the value of any column is NULL, and the equivalence class exists. // - If the right side has rows, no row is filtered out after the filters since no row of the outer side is filtered out. Hence, the equivalence class remains. // -func (s *FDSet) MakeOuterJoin(innerFDs, filterFDs *FDSet, outerCols, innerCols FastIntSet, opt *ArgOpts) { +func (s *FDSet) MakeOuterJoin(innerFDs, filterFDs *FDSet, outerCols, innerCols intset.FastIntSet, opt *ArgOpts) { // copy down the left PK and right PK before the s has changed for later usage. leftPK, ok1 := s.FindPrimaryKey() rightPK, ok2 := innerFDs.FindPrimaryKey() @@ -767,8 +768,8 @@ func (s *FDSet) MakeOuterJoin(innerFDs, filterFDs *FDSet, outerCols, innerCols F } } s.ncEdges = append(s.ncEdges, innerFDs.ncEdges...) - leftCombinedFDFrom := NewFastIntSet() - leftCombinedFDTo := NewFastIntSet() + leftCombinedFDFrom := intset.NewFastIntSet() + leftCombinedFDTo := intset.NewFastIntSet() for _, edge := range filterFDs.fdEdges { // Rule #3.2, constant FD are removed from right side of left join. if edge.isConstant() { @@ -804,7 +805,7 @@ func (s *FDSet) MakeOuterJoin(innerFDs, filterFDs *FDSet, outerCols, innerCols F laxFDTo := equivColsLeft for i, ok := laxFDFrom.Next(0); ok; i, ok = laxFDFrom.Next(i + 1) { for j, ok := laxFDTo.Next(0); ok; j, ok = laxFDTo.Next(j + 1) { - s.addFunctionalDependency(NewFastIntSet(i), NewFastIntSet(j), false, false) + s.addFunctionalDependency(intset.NewFastIntSet(i), intset.NewFastIntSet(j), false, false) } } s.AddNCFunctionalDependency(equivColsLeft, equivColsRight, innerCols, true, true) @@ -867,7 +868,7 @@ type ArgOpts struct { } // FindPrimaryKey checks whether there's a key in the current set which implies key -> all cols. -func (s FDSet) FindPrimaryKey() (*FastIntSet, bool) { +func (s FDSet) FindPrimaryKey() (*intset.FastIntSet, bool) { allCols := s.AllCols() for i := 0; i < len(s.fdEdges); i++ { fd := s.fdEdges[i] @@ -875,7 +876,7 @@ func (s FDSet) FindPrimaryKey() (*FastIntSet, bool) { if fd.strict && !fd.equiv { closure := s.closureOfStrict(fd.from) if allCols.SubsetOf(closure) { - pk := NewFastIntSet() + pk := intset.NewFastIntSet() pk.CopyFrom(fd.from) return &pk, true } @@ -885,8 +886,8 @@ func (s FDSet) FindPrimaryKey() (*FastIntSet, bool) { } // AllCols returns all columns in the current set. -func (s FDSet) AllCols() FastIntSet { - allCols := NewFastIntSet() +func (s FDSet) AllCols() intset.FastIntSet { + allCols := intset.NewFastIntSet() for i := 0; i < len(s.fdEdges); i++ { allCols.UnionWith(s.fdEdges[i].from) if !s.fdEdges[i].equiv { @@ -937,7 +938,7 @@ func (s *FDSet) AddFrom(fds *FDSet) { // f: {a}--> {b,c}, {abc} == {abc} // cols: {a,c} // result: {} --> {a,c}, {a,c} == {a,c} -func (s *FDSet) MaxOneRow(cols FastIntSet) { +func (s *FDSet) MaxOneRow(cols intset.FastIntSet) { cnt := 0 for i := 0; i < len(s.fdEdges); i++ { fd := s.fdEdges[i] @@ -970,7 +971,7 @@ func (s *FDSet) MaxOneRow(cols FastIntSet) { // Formula: // Strict decomposition FD4A: If X −→ Y Z then X −→ Y and X −→ Z. // Lax decomposition FD4B: If X ~→ Y Z and I(R) is Y-definite then X ~→ Z. -func (s *FDSet) ProjectCols(cols FastIntSet) { +func (s *FDSet) ProjectCols(cols intset.FastIntSet) { // **************************************** START LOOP 1 ******************************************** // Ensure the transitive relationship between remaining columns won't be lost. // 1: record all the constant columns @@ -982,7 +983,7 @@ func (s *FDSet) ProjectCols(cols FastIntSet) { // fd1: {a} --> {c} // fd2: {a,b} == {a,b} // if only a is un-projected, the fd1 can actually be kept as {b} --> {c}. - var constCols, detCols, equivCols FastIntSet + var constCols, detCols, equivCols intset.FastIntSet for i := 0; i < len(s.fdEdges); i++ { fd := s.fdEdges[i] @@ -1077,7 +1078,7 @@ func (s *FDSet) ProjectCols(cols FastIntSet) { if !fd.from.SubsetOf(cols) { // equivalence and constant FD couldn't be here. deletedCols := fd.from.Difference(cols) - substitutedCols := NewFastIntSet() + substitutedCols := intset.NewFastIntSet() foundAll := true for c, ok := deletedCols.Next(0); ok; c, ok = deletedCols.Next(c + 1) { // For every un-projected column, try to found their substituted column in projection list. @@ -1150,10 +1151,10 @@ func (s *FDSet) ProjectCols(cols FastIntSet) { } // makeEquivMap try to find the equivalence column of every deleted column in the project list. -func (s *FDSet) makeEquivMap(detCols, projectedCols FastIntSet) map[int]int { +func (s *FDSet) makeEquivMap(detCols, projectedCols intset.FastIntSet) map[int]int { var equivMap map[int]int for i, ok := detCols.Next(0); ok; i, ok = detCols.Next(i + 1) { - var oneCol FastIntSet + var oneCol intset.FastIntSet oneCol.Insert(i) closure := s.closureOfEquivalence(oneCol) closure.IntersectionWith(projectedCols) diff --git a/pkg/planner/funcdep/fd_graph_test.go b/pkg/planner/funcdep/fd_graph_test.go index 27a8b7757340b..f4724358d3991 100644 --- a/pkg/planner/funcdep/fd_graph_test.go +++ b/pkg/planner/funcdep/fd_graph_test.go @@ -17,6 +17,7 @@ package funcdep import ( "testing" + "github.com/pingcap/tidb/pkg/util/intset" "github.com/stretchr/testify/require" ) @@ -25,20 +26,20 @@ func TestAddStrictFunctionalDependency(t *testing.T) { fdEdges: []*fdEdge{}, } fe1 := &fdEdge{ - from: NewFastIntSet(1, 2), // AB -> CDEFG - to: NewFastIntSet(3, 4, 5, 6, 7), + from: intset.NewFastIntSet(1, 2), // AB -> CDEFG + to: intset.NewFastIntSet(3, 4, 5, 6, 7), strict: true, equiv: false, } fe2 := &fdEdge{ - from: NewFastIntSet(1, 2), // AB -> CD - to: NewFastIntSet(3, 4), + from: intset.NewFastIntSet(1, 2), // AB -> CD + to: intset.NewFastIntSet(3, 4), strict: true, equiv: false, } fe3 := &fdEdge{ - from: NewFastIntSet(1, 2), // AB -> EF - to: NewFastIntSet(5, 6), + from: intset.NewFastIntSet(1, 2), // AB -> EF + to: intset.NewFastIntSet(5, 6), strict: true, equiv: false, } @@ -81,32 +82,32 @@ func TestFDSet_ClosureOf(t *testing.T) { fdEdges: []*fdEdge{}, } fe1 := &fdEdge{ - from: NewFastIntSet(1, 2), // AB -> CD - to: NewFastIntSet(3, 4), + from: intset.NewFastIntSet(1, 2), // AB -> CD + to: intset.NewFastIntSet(3, 4), strict: true, equiv: false, } fe2 := &fdEdge{ - from: NewFastIntSet(1, 2), // AB -> EF - to: NewFastIntSet(5, 6), + from: intset.NewFastIntSet(1, 2), // AB -> EF + to: intset.NewFastIntSet(5, 6), strict: true, equiv: false, } fe3 := &fdEdge{ - from: NewFastIntSet(2), // B -> FG - to: NewFastIntSet(6, 7), + from: intset.NewFastIntSet(2), // B -> FG + to: intset.NewFastIntSet(6, 7), strict: true, equiv: false, } fe4 := &fdEdge{ - from: NewFastIntSet(1), // A -> DEH - to: NewFastIntSet(4, 5, 8), + from: intset.NewFastIntSet(1), // A -> DEH + to: intset.NewFastIntSet(4, 5, 8), strict: true, equiv: false, } fd.fdEdges = append(fd.fdEdges, fe1, fe2, fe3, fe4) // A -> ADEH - closure := fd.closureOfStrict(NewFastIntSet(1)).SortedArray() + closure := fd.closureOfStrict(intset.NewFastIntSet(1)).SortedArray() require.Equal(t, len(closure), 4) require.Equal(t, closure[0], 1) require.Equal(t, closure[1], 4) @@ -114,7 +115,7 @@ func TestFDSet_ClosureOf(t *testing.T) { require.Equal(t, closure[3], 8) // AB -> ABCDEFGH fd.fdEdges = append(fd.fdEdges, fe1, fe2, fe3, fe4) - closure = fd.closureOfStrict(NewFastIntSet(1, 2)).SortedArray() + closure = fd.closureOfStrict(intset.NewFastIntSet(1, 2)).SortedArray() require.Equal(t, len(closure), 8) require.Equal(t, closure[0], 1) require.Equal(t, closure[1], 2) @@ -131,25 +132,25 @@ func TestFDSet_ReduceCols(t *testing.T) { fdEdges: []*fdEdge{}, } fe1 := &fdEdge{ - from: NewFastIntSet(1), // A -> CD - to: NewFastIntSet(3, 4), + from: intset.NewFastIntSet(1), // A -> CD + to: intset.NewFastIntSet(3, 4), strict: true, equiv: false, } fe2 := &fdEdge{ - from: NewFastIntSet(3), // C -> DE - to: NewFastIntSet(4, 5), + from: intset.NewFastIntSet(3), // C -> DE + to: intset.NewFastIntSet(4, 5), strict: true, equiv: false, } fe3 := &fdEdge{ - from: NewFastIntSet(3, 5), // CE -> B - to: NewFastIntSet(2), + from: intset.NewFastIntSet(3, 5), // CE -> B + to: intset.NewFastIntSet(2), strict: true, equiv: false, } fd.fdEdges = append(fd.fdEdges, fe1, fe2, fe3) - res := fd.ReduceCols(NewFastIntSet(1, 2)).SortedArray() + res := fd.ReduceCols(intset.NewFastIntSet(1, 2)).SortedArray() require.Equal(t, len(res), 1) require.Equal(t, res[0], 1) } @@ -159,55 +160,55 @@ func TestFDSet_InClosure(t *testing.T) { fdEdges: []*fdEdge{}, } fe1 := &fdEdge{ - from: NewFastIntSet(1, 2), // AB -> CD - to: NewFastIntSet(3, 4), + from: intset.NewFastIntSet(1, 2), // AB -> CD + to: intset.NewFastIntSet(3, 4), strict: true, equiv: false, } fe2 := &fdEdge{ - from: NewFastIntSet(1, 2), // AB -> EF - to: NewFastIntSet(5, 6), + from: intset.NewFastIntSet(1, 2), // AB -> EF + to: intset.NewFastIntSet(5, 6), strict: true, equiv: false, } fe3 := &fdEdge{ - from: NewFastIntSet(2), // B -> FG - to: NewFastIntSet(6, 7), + from: intset.NewFastIntSet(2), // B -> FG + to: intset.NewFastIntSet(6, 7), strict: true, equiv: false, } fd.fdEdges = append(fd.fdEdges, fe1, fe2, fe3) // A -> F : false (determinants should not be torn apart) - require.False(t, fd.InClosure(NewFastIntSet(1), NewFastIntSet(6))) + require.False(t, fd.InClosure(intset.NewFastIntSet(1), intset.NewFastIntSet(6))) // B -> G : true (dependency can be torn apart) - require.True(t, fd.InClosure(NewFastIntSet(2), NewFastIntSet(7))) + require.True(t, fd.InClosure(intset.NewFastIntSet(2), intset.NewFastIntSet(7))) // AB -> E : true (dependency can be torn apart) - require.True(t, fd.InClosure(NewFastIntSet(1, 2), NewFastIntSet(5))) + require.True(t, fd.InClosure(intset.NewFastIntSet(1, 2), intset.NewFastIntSet(5))) // AB -> FG: true (in closure node set) - require.True(t, fd.InClosure(NewFastIntSet(1, 2), NewFastIntSet(6, 7))) + require.True(t, fd.InClosure(intset.NewFastIntSet(1, 2), intset.NewFastIntSet(6, 7))) // AB -> DF: true (in closure node set) - require.True(t, fd.InClosure(NewFastIntSet(1, 2), NewFastIntSet(4, 6))) + require.True(t, fd.InClosure(intset.NewFastIntSet(1, 2), intset.NewFastIntSet(4, 6))) // AB -> EG: true (in closure node set) - require.True(t, fd.InClosure(NewFastIntSet(1, 2), NewFastIntSet(5, 7))) + require.True(t, fd.InClosure(intset.NewFastIntSet(1, 2), intset.NewFastIntSet(5, 7))) // AB -> EGH: false (H is not in closure node set) - require.False(t, fd.InClosure(NewFastIntSet(1, 2), NewFastIntSet(5, 7, 8))) + require.False(t, fd.InClosure(intset.NewFastIntSet(1, 2), intset.NewFastIntSet(5, 7, 8))) fe4 := &fdEdge{ - from: NewFastIntSet(2), // B -> CH - to: NewFastIntSet(3, 8), + from: intset.NewFastIntSet(2), // B -> CH + to: intset.NewFastIntSet(3, 8), strict: true, equiv: false, } fd.fdEdges = append(fd.fdEdges, fe4) // AB -> EGH: true (in closure node set) - require.True(t, fd.InClosure(NewFastIntSet(1, 2), NewFastIntSet(5, 7, 8))) + require.True(t, fd.InClosure(intset.NewFastIntSet(1, 2), intset.NewFastIntSet(5, 7, 8))) } func TestFDSet_AddConstant(t *testing.T) { fd := FDSet{} require.Equal(t, "()", fd.ConstantCols().String()) - fd.AddConstants(NewFastIntSet(1, 2)) // {} --> {a,b} + fd.AddConstants(intset.NewFastIntSet(1, 2)) // {} --> {a,b} require.Equal(t, len(fd.fdEdges), 1) require.True(t, fd.fdEdges[0].strict) require.False(t, fd.fdEdges[0].equiv) @@ -215,7 +216,7 @@ func TestFDSet_AddConstant(t *testing.T) { require.Equal(t, "(1,2)", fd.fdEdges[0].to.String()) require.Equal(t, "(1,2)", fd.ConstantCols().String()) - fd.AddConstants(NewFastIntSet(3)) // c, {} --> {a,b,c} + fd.AddConstants(intset.NewFastIntSet(3)) // c, {} --> {a,b,c} require.Equal(t, len(fd.fdEdges), 1) require.True(t, fd.fdEdges[0].strict) require.False(t, fd.fdEdges[0].equiv) @@ -223,7 +224,7 @@ func TestFDSet_AddConstant(t *testing.T) { require.Equal(t, "(1-3)", fd.fdEdges[0].to.String()) require.Equal(t, "(1-3)", fd.ConstantCols().String()) - fd.AddStrictFunctionalDependency(NewFastIntSet(3, 4), NewFastIntSet(5, 6)) // {c,d} --> {e,f} + fd.AddStrictFunctionalDependency(intset.NewFastIntSet(3, 4), intset.NewFastIntSet(5, 6)) // {c,d} --> {e,f} require.Equal(t, len(fd.fdEdges), 2) require.True(t, fd.fdEdges[0].strict) require.False(t, fd.fdEdges[0].equiv) @@ -235,7 +236,7 @@ func TestFDSet_AddConstant(t *testing.T) { require.Equal(t, "(4)", fd.fdEdges[1].from.String()) // determinant 3 reduced as constant, leaving FD {d} --> {f,g}. require.Equal(t, "(5,6)", fd.fdEdges[1].to.String()) - fd.AddLaxFunctionalDependency(NewFastIntSet(7), NewFastIntSet(5, 6)) // {g} ~~> {e,f} + fd.AddLaxFunctionalDependency(intset.NewFastIntSet(7), intset.NewFastIntSet(5, 6)) // {g} ~~> {e,f} require.Equal(t, len(fd.fdEdges), 3) require.False(t, fd.fdEdges[2].strict) require.False(t, fd.fdEdges[2].equiv) @@ -243,7 +244,7 @@ func TestFDSet_AddConstant(t *testing.T) { require.Equal(t, "(5,6)", fd.fdEdges[2].to.String()) // add d, {} --> {a,b,c,d}, and FD {d} --> {f,g} is transferred to constant closure. - fd.AddConstants(NewFastIntSet(4)) + fd.AddConstants(intset.NewFastIntSet(4)) // => {} --> {a,b,c,d,e,f}, for lax FD {g} ~~> {e,f}, dependencies are constants, removed. require.Equal(t, 1, len(fd.fdEdges)) require.True(t, fd.fdEdges[0].strict) @@ -255,25 +256,25 @@ func TestFDSet_AddConstant(t *testing.T) { func TestFDSet_LaxImplies(t *testing.T) { fd := FDSet{} - fd.AddLaxFunctionalDependency(NewFastIntSet(1), NewFastIntSet(2, 3)) - fd.AddLaxFunctionalDependency(NewFastIntSet(1), NewFastIntSet(2)) + fd.AddLaxFunctionalDependency(intset.NewFastIntSet(1), intset.NewFastIntSet(2, 3)) + fd.AddLaxFunctionalDependency(intset.NewFastIntSet(1), intset.NewFastIntSet(2)) // lax FD won't imply each other once they have the different to side. require.Equal(t, "(1)~~>(2,3), (1)~~>(2)", fd.String()) fd = FDSet{} - fd.AddLaxFunctionalDependency(NewFastIntSet(1), NewFastIntSet(2)) - fd.AddLaxFunctionalDependency(NewFastIntSet(1), NewFastIntSet(2, 3)) + fd.AddLaxFunctionalDependency(intset.NewFastIntSet(1), intset.NewFastIntSet(2)) + fd.AddLaxFunctionalDependency(intset.NewFastIntSet(1), intset.NewFastIntSet(2, 3)) require.Equal(t, "(1)~~>(2), (1)~~>(2,3)", fd.String()) fd = FDSet{} - fd.AddLaxFunctionalDependency(NewFastIntSet(1), NewFastIntSet(3)) - fd.AddLaxFunctionalDependency(NewFastIntSet(1, 2), NewFastIntSet(3)) + fd.AddLaxFunctionalDependency(intset.NewFastIntSet(1), intset.NewFastIntSet(3)) + fd.AddLaxFunctionalDependency(intset.NewFastIntSet(1, 2), intset.NewFastIntSet(3)) // lax FD can imply each other once they have the same to side. {1,2} ~~> {3} implies {1} ~~> {3} require.Equal(t, "(1)~~>(3)", fd.String()) fd = FDSet{} - fd.AddLaxFunctionalDependency(NewFastIntSet(1), NewFastIntSet(3, 4)) - fd.AddLaxFunctionalDependency(NewFastIntSet(1, 2), NewFastIntSet(3)) + fd.AddLaxFunctionalDependency(intset.NewFastIntSet(1), intset.NewFastIntSet(3, 4)) + fd.AddLaxFunctionalDependency(intset.NewFastIntSet(1, 2), intset.NewFastIntSet(3)) // lax FD won't imply each other once they have the different to side. {1,2} ~~> {3} implies {1} ~~> {3} require.Equal(t, "(1)~~>(3,4), (1,2)~~>(3)", fd.String()) } @@ -282,8 +283,8 @@ func TestFDSet_AddEquivalence(t *testing.T) { fd := FDSet{} require.Equal(t, 0, len(fd.EquivalenceCols())) - fd.AddEquivalence(NewFastIntSet(1), NewFastIntSet(2)) // {a} == {b} - require.Equal(t, 1, len(fd.fdEdges)) // res: {a} == {b} + fd.AddEquivalence(intset.NewFastIntSet(1), intset.NewFastIntSet(2)) // {a} == {b} + require.Equal(t, 1, len(fd.fdEdges)) // res: {a} == {b} require.Equal(t, 1, len(fd.EquivalenceCols())) require.True(t, fd.fdEdges[0].strict) require.True(t, fd.fdEdges[0].equiv) @@ -291,8 +292,8 @@ func TestFDSet_AddEquivalence(t *testing.T) { require.Equal(t, "(1,2)", fd.fdEdges[0].to.String()) require.Equal(t, "(1,2)", fd.EquivalenceCols()[0].String()) - fd.AddEquivalence(NewFastIntSet(3), NewFastIntSet(4)) // {c} == {d} - require.Equal(t, 2, len(fd.fdEdges)) // res: {a,b} == {a,b}, {c,d} == {c,d} + fd.AddEquivalence(intset.NewFastIntSet(3), intset.NewFastIntSet(4)) // {c} == {d} + require.Equal(t, 2, len(fd.fdEdges)) // res: {a,b} == {a,b}, {c,d} == {c,d} require.Equal(t, 2, len(fd.EquivalenceCols())) require.True(t, fd.fdEdges[0].strict) require.True(t, fd.fdEdges[0].equiv) @@ -305,15 +306,15 @@ func TestFDSet_AddEquivalence(t *testing.T) { require.Equal(t, "(3,4)", fd.fdEdges[1].to.String()) require.Equal(t, "(3,4)", fd.EquivalenceCols()[1].String()) - fd.AddConstants(NewFastIntSet(4, 5)) // {} --> {d,e} - require.Equal(t, 3, len(fd.fdEdges)) // res: {a,b} == {a,b}, {c,d} == {c,d},{} --> {c,d,e} - require.True(t, fd.fdEdges[2].strict) // explain: constant closure is extended by equivalence {c,d} == {c,d} + fd.AddConstants(intset.NewFastIntSet(4, 5)) // {} --> {d,e} + require.Equal(t, 3, len(fd.fdEdges)) // res: {a,b} == {a,b}, {c,d} == {c,d},{} --> {c,d,e} + require.True(t, fd.fdEdges[2].strict) // explain: constant closure is extended by equivalence {c,d} == {c,d} require.False(t, fd.fdEdges[2].equiv) require.Equal(t, "()", fd.fdEdges[2].from.String()) require.Equal(t, "(3-5)", fd.fdEdges[2].to.String()) require.Equal(t, "(3-5)", fd.ConstantCols().String()) - fd.AddStrictFunctionalDependency(NewFastIntSet(2, 3), NewFastIntSet(5, 6)) // {b,c} --> {e,f} + fd.AddStrictFunctionalDependency(intset.NewFastIntSet(2, 3), intset.NewFastIntSet(5, 6)) // {b,c} --> {e,f} // res: {a,b} == {a,b}, {c,d} == {c,d},{} --> {c,d,e}, {b} --> {e,f} require.Equal(t, 4, len(fd.fdEdges)) // explain: strict FD's from side c is eliminated by constant closure. @@ -322,7 +323,7 @@ func TestFDSet_AddEquivalence(t *testing.T) { require.Equal(t, "(2)", fd.fdEdges[3].from.String()) require.Equal(t, "(5,6)", fd.fdEdges[3].to.String()) - fd.AddEquivalence(NewFastIntSet(2), NewFastIntSet(3)) // {b} == {d} + fd.AddEquivalence(intset.NewFastIntSet(2), intset.NewFastIntSet(3)) // {b} == {d} // res: {a,b,c,d} == {a,b,c,d}, {} --> {a,b,c,d,e,f} // explain: // b = d build the connection between {a,b} == {a,b}, {c,d} == {c,d}, make the superset of equivalence closure. diff --git a/pkg/sessionctx/stmtctx/BUILD.bazel b/pkg/sessionctx/stmtctx/BUILD.bazel index b244a670d0204..cc47403bf21ac 100644 --- a/pkg/sessionctx/stmtctx/BUILD.bazel +++ b/pkg/sessionctx/stmtctx/BUILD.bazel @@ -17,6 +17,7 @@ go_library( "//pkg/util/disk", "//pkg/util/execdetails", "//pkg/util/intest", + "//pkg/util/intset", "//pkg/util/linter/constructor", "//pkg/util/memory", "//pkg/util/nocopy", diff --git a/pkg/sessionctx/stmtctx/stmtctx.go b/pkg/sessionctx/stmtctx/stmtctx.go index f595154e6a5ef..baf318879d39e 100644 --- a/pkg/sessionctx/stmtctx/stmtctx.go +++ b/pkg/sessionctx/stmtctx/stmtctx.go @@ -39,6 +39,7 @@ import ( "github.com/pingcap/tidb/pkg/util/disk" "github.com/pingcap/tidb/pkg/util/execdetails" "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/intset" "github.com/pingcap/tidb/pkg/util/linter/constructor" "github.com/pingcap/tidb/pkg/util/memory" "github.com/pingcap/tidb/pkg/util/nocopy" @@ -390,7 +391,7 @@ type StatementContext struct { // UseDynamicPruneMode indicates whether use UseDynamicPruneMode in query stmt UseDynamicPruneMode bool // ColRefFromPlan mark the column ref used by assignment in update statement. - ColRefFromUpdatePlan []int64 + ColRefFromUpdatePlan intset.FastIntSet // RangeFallback indicates that building complete ranges exceeds the memory limit so it falls back to less accurate ranges such as full range. RangeFallback bool diff --git a/pkg/util/intset/BUILD.bazel b/pkg/util/intset/BUILD.bazel new file mode 100644 index 0000000000000..2838f219d95cf --- /dev/null +++ b/pkg/util/intset/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "intset", + srcs = ["fast_int_set.go"], + importpath = "github.com/pingcap/tidb/pkg/util/intset", + visibility = ["//visibility:public"], + deps = ["@org_golang_x_tools//container/intsets"], +) + +go_test( + name = "intset_test", + timeout = "short", + srcs = [ + "fast_int_set_bench_test.go", + "fast_int_set_test.go", + ], + embed = [":intset"], + flaky = True, + shard_count = 5, + deps = [ + "@com_github_stretchr_testify//require", + "@org_golang_x_exp//maps", + "@org_golang_x_tools//container/intsets", + ], +) diff --git a/pkg/planner/funcdep/fast_int_set.go b/pkg/util/intset/fast_int_set.go similarity index 99% rename from pkg/planner/funcdep/fast_int_set.go rename to pkg/util/intset/fast_int_set.go index 383a526766e5c..67cb5d2b3c94c 100644 --- a/pkg/planner/funcdep/fast_int_set.go +++ b/pkg/util/intset/fast_int_set.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package funcdep +package intset import ( "bytes" diff --git a/pkg/planner/funcdep/fast_int_set_bench_test.go b/pkg/util/intset/fast_int_set_bench_test.go similarity index 99% rename from pkg/planner/funcdep/fast_int_set_bench_test.go rename to pkg/util/intset/fast_int_set_bench_test.go index a81c607a4c9af..3414124d9a1e8 100644 --- a/pkg/planner/funcdep/fast_int_set_bench_test.go +++ b/pkg/util/intset/fast_int_set_bench_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package funcdep +package intset import ( "testing" diff --git a/pkg/planner/funcdep/fast_int_set_test.go b/pkg/util/intset/fast_int_set_test.go similarity index 99% rename from pkg/planner/funcdep/fast_int_set_test.go rename to pkg/util/intset/fast_int_set_test.go index 110484f2849a2..85d8a818086d0 100644 --- a/pkg/planner/funcdep/fast_int_set_test.go +++ b/pkg/util/intset/fast_int_set_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package funcdep +package intset import ( "fmt" diff --git a/tests/integrationtest/r/planner/core/casetest/integration.result b/tests/integrationtest/r/planner/core/casetest/integration.result index f27d6c26820a4..f1bc32f5b8297 100644 --- a/tests/integrationtest/r/planner/core/casetest/integration.result +++ b/tests/integrationtest/r/planner/core/casetest/integration.result @@ -1565,18 +1565,14 @@ explain format = brief update tt, (select 1 as c1 ,2 as c2 ,3 as c3, 4 as c4 uni id estRows task access object operator info Update N/A root N/A └─Projection 0.00 root test.tt.a, test.tt.b, test.tt.c, test.tt.d, test.tt.e, Column#18, Column#19, Column#20, Column#21 - └─Projection 0.00 root test.tt.a, test.tt.b, test.tt.c, test.tt.d, test.tt.e, Column#18, Column#19, Column#20, Column#21 - └─IndexJoin 0.00 root inner join, inner:TableReader, outer key:Column#20, Column#21, inner key:test.tt.c, test.tt.d, equal cond:eq(Column#20, test.tt.c), eq(Column#21, test.tt.d), other cond:or(or(and(eq(Column#20, 11), eq(test.tt.d, 111)), and(eq(Column#20, 22), eq(test.tt.d, 222))), or(and(eq(Column#20, 33), eq(test.tt.d, 333)), and(eq(Column#20, 44), eq(test.tt.d, 444)))), or(or(and(eq(test.tt.c, 11), eq(Column#21, 111)), and(eq(test.tt.c, 22), eq(Column#21, 222))), or(and(eq(test.tt.c, 33), eq(Column#21, 333)), and(eq(test.tt.c, 44), eq(Column#21, 444)))) - ├─Union(Build) 0.00 root - │ ├─Projection 0.00 root Column#6->Column#18, Column#7->Column#19, Column#8->Column#20, Column#9->Column#21 - │ │ └─Projection 0.00 root 1->Column#6, 2->Column#7, 3->Column#8, 4->Column#9 - │ │ └─TableDual 0.00 root rows:0 - │ ├─Projection 0.00 root Column#10->Column#18, Column#11->Column#19, Column#12->Column#20, Column#13->Column#21 - │ │ └─Projection 0.00 root 2->Column#10, 3->Column#11, 4->Column#12, 5->Column#13 - │ │ └─TableDual 0.00 root rows:0 - │ └─Projection 0.00 root Column#14->Column#18, Column#15->Column#19, Column#16->Column#20, Column#17->Column#21 - │ └─Projection 0.00 root 3->Column#14, 4->Column#15, 5->Column#16, 6->Column#17 - │ └─TableDual 0.00 root rows:0 - └─TableReader(Probe) 0.00 root data:Selection - └─Selection 0.00 cop[tikv] or(or(and(eq(test.tt.c, 11), eq(test.tt.d, 111)), and(eq(test.tt.c, 22), eq(test.tt.d, 222))), or(and(eq(test.tt.c, 33), eq(test.tt.d, 333)), and(eq(test.tt.c, 44), eq(test.tt.d, 444)))), or(or(eq(test.tt.c, 11), eq(test.tt.c, 22)), or(eq(test.tt.c, 33), eq(test.tt.c, 44))), or(or(eq(test.tt.d, 111), eq(test.tt.d, 222)), or(eq(test.tt.d, 333), eq(test.tt.d, 444))) - └─TableRangeScan 0.00 cop[tikv] table:tt range: decided by [eq(test.tt.c, Column#20) eq(test.tt.d, Column#21)], keep order:false, stats:pseudo + └─IndexJoin 0.00 root inner join, inner:TableReader, outer key:Column#20, Column#21, inner key:test.tt.c, test.tt.d, equal cond:eq(Column#20, test.tt.c), eq(Column#21, test.tt.d), other cond:or(or(and(eq(Column#20, 11), eq(test.tt.d, 111)), and(eq(Column#20, 22), eq(test.tt.d, 222))), or(and(eq(Column#20, 33), eq(test.tt.d, 333)), and(eq(Column#20, 44), eq(test.tt.d, 444)))), or(or(and(eq(test.tt.c, 11), eq(Column#21, 111)), and(eq(test.tt.c, 22), eq(Column#21, 222))), or(and(eq(test.tt.c, 33), eq(Column#21, 333)), and(eq(test.tt.c, 44), eq(Column#21, 444)))) + ├─Union(Build) 0.00 root + │ ├─Projection 0.00 root 1->Column#18, 2->Column#19, 3->Column#20, 4->Column#21 + │ │ └─TableDual 0.00 root rows:0 + │ ├─Projection 0.00 root 2->Column#18, 3->Column#19, 4->Column#20, 5->Column#21 + │ │ └─TableDual 0.00 root rows:0 + │ └─Projection 0.00 root 3->Column#18, 4->Column#19, 5->Column#20, 6->Column#21 + │ └─TableDual 0.00 root rows:0 + └─TableReader(Probe) 0.00 root data:Selection + └─Selection 0.00 cop[tikv] or(or(and(eq(test.tt.c, 11), eq(test.tt.d, 111)), and(eq(test.tt.c, 22), eq(test.tt.d, 222))), or(and(eq(test.tt.c, 33), eq(test.tt.d, 333)), and(eq(test.tt.c, 44), eq(test.tt.d, 444)))), or(or(eq(test.tt.c, 11), eq(test.tt.c, 22)), or(eq(test.tt.c, 33), eq(test.tt.c, 44))), or(or(eq(test.tt.d, 111), eq(test.tt.d, 222)), or(eq(test.tt.d, 333), eq(test.tt.d, 444))) + └─TableRangeScan 0.00 cop[tikv] table:tt range: decided by [eq(test.tt.c, Column#20) eq(test.tt.d, Column#21)], keep order:false, stats:pseudo diff --git a/tests/integrationtest/r/planner/core/cbo.result b/tests/integrationtest/r/planner/core/cbo.result index 645686c28e8de..6155211411f72 100644 --- a/tests/integrationtest/r/planner/core/cbo.result +++ b/tests/integrationtest/r/planner/core/cbo.result @@ -3,14 +3,13 @@ create table t(a int, b int); explain update t t1, (select distinct b from t) t2 set t1.b = t2.b; id estRows task access object operator info Update_7 N/A root N/A -└─Projection_9 80000000.00 root planner__core__cbo.t.a, planner__core__cbo.t.b, planner__core__cbo.t._tidb_rowid, planner__core__cbo.t.b - └─HashJoin_10 80000000.00 root CARTESIAN inner join - ├─HashAgg_18(Build) 8000.00 root group by:planner__core__cbo.t.b, funcs:firstrow(planner__core__cbo.t.b)->planner__core__cbo.t.b - │ └─TableReader_19 8000.00 root data:HashAgg_14 - │ └─HashAgg_14 8000.00 cop[tikv] group by:planner__core__cbo.t.b, - │ └─TableFullScan_17 10000.00 cop[tikv] table:t keep order:false, stats:pseudo - └─TableReader_13(Probe) 10000.00 root data:TableFullScan_12 - └─TableFullScan_12 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo +└─HashJoin_10 80000000.00 root CARTESIAN inner join + ├─HashAgg_18(Build) 8000.00 root group by:planner__core__cbo.t.b, funcs:firstrow(planner__core__cbo.t.b)->planner__core__cbo.t.b + │ └─TableReader_19 8000.00 root data:HashAgg_14 + │ └─HashAgg_14 8000.00 cop[tikv] group by:planner__core__cbo.t.b, + │ └─TableFullScan_17 10000.00 cop[tikv] table:t keep order:false, stats:pseudo + └─TableReader_13(Probe) 10000.00 root data:TableFullScan_12 + └─TableFullScan_12 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo drop table if exists tb1, tb2; create table tb1(a int, b int, primary key(a)); create table tb2 (a int, b int, c int, d datetime, primary key(c),key idx_u(a)); diff --git a/tests/integrationtest/r/planner/core/issuetest/planner_issue.result b/tests/integrationtest/r/planner/core/issuetest/planner_issue.result index 2b307f9e3ed42..e895723ba2ac2 100644 --- a/tests/integrationtest/r/planner/core/issuetest/planner_issue.result +++ b/tests/integrationtest/r/planner/core/issuetest/planner_issue.result @@ -241,6 +241,29 @@ id value 3 0 4 4 5 5 +drop table if exists t1, t2; +create table t1(id int primary key, a varchar(128)); +create table t2(id int primary key, b varchar(128), c varchar(128)); +UPDATE +t1 +SET +t1.a = IFNULL( +( +SELECT +t2.c +FROM +t2 +WHERE +t2.b = t1.a +ORDER BY +t2.b DESC, +t2.c DESC +LIMIT +1 +), '' +) +WHERE +t1.id = 1; create table A(a int primary key, b int); create table B(b int primary key); create table C(c int primary key, b int); diff --git a/tests/integrationtest/r/planner/core/rule_constant_propagation.result b/tests/integrationtest/r/planner/core/rule_constant_propagation.result index 8caa2d39737ee..1aee0568a457f 100644 --- a/tests/integrationtest/r/planner/core/rule_constant_propagation.result +++ b/tests/integrationtest/r/planner/core/rule_constant_propagation.result @@ -149,15 +149,13 @@ create table s (id int, name varchar(10)); explain Update t, (select * from s where s.id>1) tmp set t.name=tmp.name where t.id=tmp.id; id estRows task access object operator info Update_8 N/A root N/A -└─Projection_11 4166.67 root planner__core__rule_constant_propagation.t.id, planner__core__rule_constant_propagation.t.name, planner__core__rule_constant_propagation.t._tidb_rowid, planner__core__rule_constant_propagation.s.id, planner__core__rule_constant_propagation.s.name - └─HashJoin_12 4166.67 root inner join, equal:[eq(planner__core__rule_constant_propagation.t.id, planner__core__rule_constant_propagation.s.id)] - ├─Projection_17(Build) 3333.33 root planner__core__rule_constant_propagation.s.id, planner__core__rule_constant_propagation.s.name - │ └─TableReader_20 3333.33 root data:Selection_19 - │ └─Selection_19 3333.33 cop[tikv] gt(planner__core__rule_constant_propagation.s.id, 1), not(isnull(planner__core__rule_constant_propagation.s.id)) - │ └─TableFullScan_18 10000.00 cop[tikv] table:s keep order:false, stats:pseudo - └─TableReader_16(Probe) 3333.33 root data:Selection_15 - └─Selection_15 3333.33 cop[tikv] gt(planner__core__rule_constant_propagation.t.id, 1), not(isnull(planner__core__rule_constant_propagation.t.id)) - └─TableFullScan_14 10000.00 cop[tikv] table:t keep order:false, stats:pseudo +└─HashJoin_12 4166.67 root inner join, equal:[eq(planner__core__rule_constant_propagation.t.id, planner__core__rule_constant_propagation.s.id)] + ├─TableReader_20(Build) 3333.33 root data:Selection_19 + │ └─Selection_19 3333.33 cop[tikv] gt(planner__core__rule_constant_propagation.s.id, 1), not(isnull(planner__core__rule_constant_propagation.s.id)) + │ └─TableFullScan_18 10000.00 cop[tikv] table:s keep order:false, stats:pseudo + └─TableReader_16(Probe) 3333.33 root data:Selection_15 + └─Selection_15 3333.33 cop[tikv] gt(planner__core__rule_constant_propagation.t.id, 1), not(isnull(planner__core__rule_constant_propagation.t.id)) + └─TableFullScan_14 10000.00 cop[tikv] table:t keep order:false, stats:pseudo drop table if exists t, s; create table t (id int, name varchar(10)); create table s (id int, name varchar(10)); diff --git a/tests/integrationtest/t/planner/core/issuetest/planner_issue.test b/tests/integrationtest/t/planner/core/issuetest/planner_issue.test index 05bdf185f41c2..6f52509a973aa 100644 --- a/tests/integrationtest/t/planner/core/issuetest/planner_issue.test +++ b/tests/integrationtest/t/planner/core/issuetest/planner_issue.test @@ -183,6 +183,31 @@ where test2.id in ); select * from test2; +# https://github.com/pingcap/tidb/issues/53236 +drop table if exists t1, t2; +create table t1(id int primary key, a varchar(128)); +create table t2(id int primary key, b varchar(128), c varchar(128)); +UPDATE + t1 +SET + t1.a = IFNULL( + ( + SELECT + t2.c + FROM + t2 + WHERE + t2.b = t1.a + ORDER BY + t2.b DESC, + t2.c DESC + LIMIT + 1 + ), '' + ) +WHERE + t1.id = 1; + # https://github.com/pingcap/tidb/issues/51560 create table A(a int primary key, b int); create table B(b int primary key);