diff --git a/executor/builder.go b/executor/builder.go index 582a0fd0c75e3..17243f8b690c6 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -4727,6 +4727,8 @@ func (b *executorBuilder) buildWindow(v *plannercore.PhysicalWindow) Executor { exec.orderByCols = orderByCols exec.expectedCmpResult = cmpResult exec.isRangeFrame = true + exec.start.InitCompareCols(b.ctx, exec.orderByCols) + exec.end.InitCompareCols(b.ctx, exec.orderByCols) } } return exec @@ -4749,7 +4751,7 @@ func (b *executorBuilder) buildWindow(v *plannercore.PhysicalWindow) Executor { if len(v.OrderBy) > 0 && v.OrderBy[0].Desc { cmpResult = 1 } - processor = &rangeFrameWindowProcessor{ + tmpProcessor := &rangeFrameWindowProcessor{ windowFuncs: windowFuncs, partialResults: partialResults, start: v.Frame.Start, @@ -4757,6 +4759,11 @@ func (b *executorBuilder) buildWindow(v *plannercore.PhysicalWindow) Executor { orderByCols: orderByCols, expectedCmpResult: cmpResult, } + + tmpProcessor.start.InitCompareCols(b.ctx, orderByCols) + tmpProcessor.end.InitCompareCols(b.ctx, orderByCols) + + processor = tmpProcessor } return &WindowExec{baseExecutor: base, processor: processor, diff --git a/executor/pipelined_window.go b/executor/pipelined_window.go index cda1d9c389fd0..19d94b2093df1 100644 --- a/executor/pipelined_window.go +++ b/executor/pipelined_window.go @@ -262,7 +262,7 @@ func (e *PipelinedWindowExec) getStart(ctx sessionctx.Context) (uint64, error) { var res int64 var err error for i := range e.orderByCols { - res, _, err = e.start.CmpFuncs[i](ctx, e.orderByCols[i], e.start.CalcFuncs[i], e.getRow(start), e.getRow(e.curRowIdx)) + res, _, err = e.start.CmpFuncs[i](ctx, e.start.CompareCols[i], e.start.CalcFuncs[i], e.getRow(start), e.getRow(e.curRowIdx)) if err != nil { return 0, err } @@ -302,7 +302,7 @@ func (e *PipelinedWindowExec) getEnd(ctx sessionctx.Context) (uint64, error) { var res int64 var err error for i := range e.orderByCols { - res, _, err = e.end.CmpFuncs[i](ctx, e.end.CalcFuncs[i], e.orderByCols[i], e.getRow(e.curRowIdx), e.getRow(end)) + res, _, err = e.end.CmpFuncs[i](ctx, e.end.CalcFuncs[i], e.end.CompareCols[i], e.getRow(e.curRowIdx), e.getRow(end)) if err != nil { return 0, err } diff --git a/executor/window.go b/executor/window.go index aaa1e51cacc85..2f261a27787ca 100644 --- a/executor/window.go +++ b/executor/window.go @@ -397,7 +397,7 @@ func (p *rangeFrameWindowProcessor) getStartOffset(ctx sessionctx.Context, rows var res int64 var err error for i := range p.orderByCols { - res, _, err = p.start.CmpFuncs[i](ctx, p.orderByCols[i], p.start.CalcFuncs[i], rows[p.lastStartOffset], rows[p.curRowIdx]) + res, _, err = p.start.CmpFuncs[i](ctx, p.start.CompareCols[i], p.start.CalcFuncs[i], rows[p.lastStartOffset], rows[p.curRowIdx]) if err != nil { return 0, err } @@ -423,7 +423,7 @@ func (p *rangeFrameWindowProcessor) getEndOffset(ctx sessionctx.Context, rows [] var res int64 var err error for i := range p.orderByCols { - res, _, err = p.end.CmpFuncs[i](ctx, p.end.CalcFuncs[i], p.orderByCols[i], rows[p.curRowIdx], rows[p.lastEndOffset]) + res, _, err = p.end.CmpFuncs[i](ctx, p.end.CalcFuncs[i], p.end.CompareCols[i], rows[p.curRowIdx], rows[p.lastEndOffset]) if err != nil { return 0, err } diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go index 83e464f31ec3c..bd2dd30bf0d5a 100644 --- a/planner/core/integration_test.go +++ b/planner/core/integration_test.go @@ -8422,6 +8422,7 @@ func TestIssue45033(t *testing.T) { where alias4.c2 = alias2.alias_col1);`).Check(testkit.Rows("0")) } +<<<<<<< HEAD func TestIssue43116(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -8433,4 +8434,16 @@ func TestIssue43116(t *testing.T) { "└─Selection 8000.00 cop[tikv] in(test.sbtest1.id, 1, 1, 1, 1, 1), in(test.sbtest1.pad, \"1\", \"1\", \"1\", \"1\", \"1\")", " └─TableFullScan 10000.00 cop[tikv] table:a keep order:false, stats:pseudo")) tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Memory capacity of 111 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen")) +======= +func TestIssue46298(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists test.first_range;") + tk.MustExec("create table test.first_range(p int not null, o tinyint not null, v int not null);") + tk.MustExec("insert into test.first_range (p, o, v) values (0, 0, 0), (1, 1, 1), (1, 2, 2), (1, 4, 4), (1, 8, 8), (2, 0, 0), (2, 3, 3), (2, 10, 10), (2, 13, 13), (2, 15, 15), (3, 1, 1), (3, 3, 3), (3, 5, 5), (3, 9, 9), (3, 15, 15), (3, 20, 20), (3, 31, 31);") + tk.MustQuery("select *, first_value(v) over (partition by p order by o range between 3.1 preceding and 2.9 following) as a from test.first_range;") + tk.MustExec(`set @@tidb_enable_pipelined_window_function=0`) + tk.MustQuery("select *, first_value(v) over (partition by p order by o range between 3.1 preceding and 2.9 following) as a from test.first_range;") +>>>>>>> b3ec110e187 (executor: fix the error that is raised when executing the window function with range type frame (#46927)) } diff --git a/planner/core/logical_plan_builder.go b/planner/core/logical_plan_builder.go index defffec481895..899f3078e7dfe 100644 --- a/planner/core/logical_plan_builder.go +++ b/planner/core/logical_plan_builder.go @@ -6436,6 +6436,7 @@ func (b *PlanBuilder) buildWindowFunctionFrameBound(_ context.Context, spec *ast if err != nil { return nil, err } + bound.CmpFuncs[0] = expression.GetCmpFunction(b.ctx, orderByItems[0].Col, bound.CalcFuncs[0]) return bound, nil } diff --git a/planner/core/logical_plans.go b/planner/core/logical_plans.go index eadb48c281aa5..13a5b065b86d6 100644 --- a/planner/core/logical_plans.go +++ b/planner/core/logical_plans.go @@ -1723,6 +1723,8 @@ type FrameBound struct { // We will build the date_add or date_sub functions for frames like `INTERVAL '2:30' MINUTE_SECOND FOLLOWING`, // and plus or minus for frames like `1 preceding`. CalcFuncs []expression.Expression + // Sometimes we need to cast order by column to a specific type when frame type is range + CompareCols []expression.Expression // CmpFuncs is used to decide whether one row is included in the current frame. CmpFuncs []expression.CompareFunc } @@ -1741,6 +1743,23 @@ func (fb *FrameBound) Clone() *FrameBound { return cloned } +// InitCompareCols will init CompareCols +func (fb *FrameBound) InitCompareCols(ctx sessionctx.Context, orderByCols []*expression.Column) { + if len(fb.CalcFuncs) > 0 { + fb.CompareCols = make([]expression.Expression, len(orderByCols)) + if fb.CalcFuncs[0].GetType().EvalType() != orderByCols[0].GetType().EvalType() { + fb.CompareCols[0], _ = expression.NewFunctionBase(ctx, ast.Cast, fb.CalcFuncs[0].GetType(), orderByCols[0]) + + // As compare column has been converted, compare function should also be changed + fb.CmpFuncs[0] = expression.GetCmpFunction(ctx, fb.CompareCols[0], fb.CalcFuncs[0]) + } else { + for i, col := range orderByCols { + fb.CompareCols[i] = col + } + } + } +} + // LogicalWindow represents a logical window function plan. type LogicalWindow struct { logicalSchemaProducer