diff --git a/pkg/planner/core/operator/logicalop/logical_aggregation.go b/pkg/planner/core/operator/logicalop/logical_aggregation.go index 5f2e3f6fa650a..5cbfebfe6cbe6 100644 --- a/pkg/planner/core/operator/logicalop/logical_aggregation.go +++ b/pkg/planner/core/operator/logicalop/logical_aggregation.go @@ -447,7 +447,7 @@ func (la *LogicalAggregation) ExtractFD() *fd.FDSet { determinants.Insert(int(one.UniqueID)) groupByColsOutputCols.Insert(int(one.UniqueID)) } - notnull := util.IsNullRejected(la.SCtx(), la.Schema(), x, true) + notnull := util.IsNullRejected(la.SCtx(), la.Schema(), x) if notnull || determinants.SubsetOf(fds.NotNullCols) { notnullColsUniqueIDs.Insert(scalarUniqueID) } diff --git a/pkg/planner/core/operator/logicalop/logical_join.go b/pkg/planner/core/operator/logicalop/logical_join.go index abf074219eb36..872c32e1ffdf5 100644 --- a/pkg/planner/core/operator/logicalop/logical_join.go +++ b/pkg/planner/core/operator/logicalop/logical_join.go @@ -326,7 +326,7 @@ func simplifyOuterJoin(p *LogicalJoin, predicates []expression.Expression) { if expression.ExprFromSchema(expr, outerTable.Schema()) { continue } - isOk := util.IsNullRejected(p.SCtx(), innerTable.Schema(), expr, true) + isOk := util.IsNullRejected(p.SCtx(), innerTable.Schema(), expr) if isOk { canBeSimplified = true break @@ -720,7 +720,7 @@ func (p *LogicalJoin) ConvertOuterToInnerJoin(predicates []expression.Expression if p.JoinType == LeftOuterJoin || p.JoinType == RightOuterJoin { canBeSimplified := false for _, expr := range predicates { - isOk := util.IsNullRejected(p.SCtx(), innerTable.Schema(), expr, true) + isOk := util.IsNullRejected(p.SCtx(), innerTable.Schema(), expr) if isOk { canBeSimplified = true break @@ -1295,13 +1295,13 @@ func (p *LogicalJoin) ExtractOnCondition( } if leftCol != nil && rightCol != nil { if deriveLeft { - if util.IsNullRejected(ctx, leftSchema, expr, true) && !mysql.HasNotNullFlag(leftCol.RetType.GetFlag()) { + if util.IsNullRejected(ctx, leftSchema, expr) && !mysql.HasNotNullFlag(leftCol.RetType.GetFlag()) { notNullExpr := expression.BuildNotNullExpr(ctx.GetExprCtx(), leftCol) leftCond = append(leftCond, notNullExpr) } } if deriveRight { - if util.IsNullRejected(ctx, rightSchema, expr, true) && !mysql.HasNotNullFlag(rightCol.RetType.GetFlag()) { + if util.IsNullRejected(ctx, rightSchema, expr) && !mysql.HasNotNullFlag(rightCol.RetType.GetFlag()) { notNullExpr := expression.BuildNotNullExpr(ctx.GetExprCtx(), rightCol) rightCond = append(rightCond, notNullExpr) } @@ -1998,7 +1998,7 @@ func deriveNotNullExpr(ctx base.PlanContext, expr expression.Expression, schema if childCol == nil { childCol = schema.RetrieveColumn(arg1) } - if util.IsNullRejected(ctx, schema, expr, true) && !mysql.HasNotNullFlag(childCol.RetType.GetFlag()) { + if util.IsNullRejected(ctx, schema, expr) && !mysql.HasNotNullFlag(childCol.RetType.GetFlag()) { return expression.BuildNotNullExpr(ctx.GetExprCtx(), childCol) } return nil diff --git a/pkg/planner/core/operator/logicalop/logical_projection.go b/pkg/planner/core/operator/logicalop/logical_projection.go index f2f930f2df7fe..830f7e248b060 100644 --- a/pkg/planner/core/operator/logicalop/logical_projection.go +++ b/pkg/planner/core/operator/logicalop/logical_projection.go @@ -503,7 +503,7 @@ func (p *LogicalProjection) ExtractFD() *fd.FDSet { // the dependent columns in scalar function should be also considered as output columns as well. outputColsUniqueIDs.Insert(int(one.UniqueID)) } - notnull := util.IsNullRejected(p.SCtx(), p.Schema(), x, true) + notnull := util.IsNullRejected(p.SCtx(), p.Schema(), x) if notnull || determinants.SubsetOf(fds.NotNullCols) { notnullColsUniqueIDs.Insert(scalarUniqueID) } diff --git a/pkg/planner/util/funcdep_misc.go b/pkg/planner/util/funcdep_misc.go index 6db8e65db7b0d..d98dec853ac4f 100644 --- a/pkg/planner/util/funcdep_misc.go +++ b/pkg/planner/util/funcdep_misc.go @@ -42,7 +42,7 @@ func ExtractNotNullFromConds(conditions []expression.Expression, p base.LogicalP if len(cols) == 0 { continue } - if IsNullRejected(p.SCtx(), p.Schema(), condition, true) { + if IsNullRejected(p.SCtx(), p.Schema(), condition) { for _, col := range cols { notnullColsUniqueIDs.Insert(int(col.UniqueID)) } diff --git a/pkg/planner/util/null_misc.go b/pkg/planner/util/null_misc.go index ecb9d0bceb3b9..5028166c9c433 100644 --- a/pkg/planner/util/null_misc.go +++ b/pkg/planner/util/null_misc.go @@ -62,7 +62,9 @@ import ( // classify that exact value. This recovers cases such as COALESCE/IF/IFNULL // that may hide NULL but still collapse after nullification. The bridge stays // conservative for plan-cache-sensitive expressions by refusing to treat -// ParamMarker/DeferredExpr values as static fold results. +// ParamMarker/DeferredExpr values as static fold results. DeferredExpr can +// still be inspected symbolically, but its runtime value must not be folded or +// classified as a compile-time constant. // nullRejectProof holds the two proof results for a sub-expression. // See the file-level comment above for the full model. @@ -71,7 +73,8 @@ type nullRejectProof struct { mustNull bool } -// allConstants checks whether the expression tree consists entirely of constants. +// allConstants checks whether the expression tree can be attempted as a static +// constant tree without lazy constants. func allConstants(ctx expression.BuildContext, expr expression.Expression) bool { if expression.MaybeOverOptimized4PlanCache(ctx, []expression.Expression{expr}) { return false // expression contains non-deterministic parameter @@ -92,9 +95,7 @@ func allConstants(ctx expression.BuildContext, expr expression.Expression) bool // IsNullRejected proves whether `predicate` can be TRUE after every column in // `innerSchema` is replaced with SQL NULL. -func IsNullRejected(ctx base.PlanContext, innerSchema *expression.Schema, predicate expression.Expression, - skipPlanCacheCheck bool) bool { - _ = skipPlanCacheCheck // kept for API compatibility; the new proof does not use EvaluateExprWithNull +func IsNullRejected(ctx base.PlanContext, innerSchema *expression.Schema, predicate expression.Expression) bool { predicate = expression.PushDownNot(ctx.GetNullRejectCheckExprCtx(), predicate) return proveNullRejected(ctx, innerSchema, predicate, true).nonTrue } diff --git a/pkg/planner/util/null_misc_test.go b/pkg/planner/util/null_misc_test.go index 1e8da35666fde..fe4d051605456 100644 --- a/pkg/planner/util/null_misc_test.go +++ b/pkg/planner/util/null_misc_test.go @@ -451,7 +451,7 @@ func TestIsNullRejectedProofModes(t *testing.T) { for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.expected, IsNullRejected(sctx, innerSchema, tt.expr, true)) + require.Equal(t, tt.expected, IsNullRejected(sctx, innerSchema, tt.expr)) }) } }