From ee6d44f3dfb3a6a36de77033f2f7147bf77d1018 Mon Sep 17 00:00:00 2001 From: Clearlove <52417396+Eurekaaw@users.noreply.github.com> Date: Mon, 10 Apr 2023 13:11:02 -0400 Subject: [PATCH] feat(optimizer): push `LogicalLimit` down `LogicalProject`, enabled in batch (#8971) Signed-off-by: Clearlove --- .../planner_test/tests/testdata/limit.yaml | 19 ++++++--- .../src/optimizer/logical_optimization.rs | 8 ++++ .../optimizer/rule/limit_push_down_rule.rs | 40 +++++++++++++++++++ src/frontend/src/optimizer/rule/mod.rs | 3 ++ 4 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 src/frontend/src/optimizer/rule/limit_push_down_rule.rs diff --git a/src/frontend/planner_test/tests/testdata/limit.yaml b/src/frontend/planner_test/tests/testdata/limit.yaml index 2f6ca9a0530e4..8f53f39002ce1 100644 --- a/src/frontend/planner_test/tests/testdata/limit.yaml +++ b/src/frontend/planner_test/tests/testdata/limit.yaml @@ -1,11 +1,15 @@ # This file is automatically generated. See `src/frontend/planner_test/README.md` for more information. - sql: | create table t (v int); - select * from t limit 4; + select v+v from t limit 4; logical_plan: | LogicalLimit { limit: 4, offset: 0 } - └─LogicalProject { exprs: [t.v] } + └─LogicalProject { exprs: [(t.v + t.v) as $expr1] } └─LogicalScan { table: t, columns: [t.v, t._row_id] } + optimized_logical_plan_for_batch: | + LogicalProject { exprs: [(t.v + t.v) as $expr1] } + └─LogicalLimit { limit: 4, offset: 0 } + └─LogicalScan { table: t, columns: [t.v] } - sql: | create table t (v int); select * from t offset 4; @@ -15,13 +19,18 @@ └─LogicalScan { table: t, columns: [t.v, t._row_id] } - sql: | create table t (v int); - select * from ( select * from t limit 5 ) limit 4; + select * from ( select v+v from t limit 5 ) limit 4; logical_plan: | LogicalLimit { limit: 4, offset: 0 } - └─LogicalProject { exprs: [t.v] } + └─LogicalProject { exprs: [$expr1] } └─LogicalLimit { limit: 5, offset: 0 } - └─LogicalProject { exprs: [t.v] } + └─LogicalProject { exprs: [(t.v + t.v) as $expr1] } └─LogicalScan { table: t, columns: [t.v, t._row_id] } + optimized_logical_plan_for_batch: | + LogicalLimit { limit: 4, offset: 0 } + └─LogicalProject { exprs: [(t.v + t.v) as $expr1] } + └─LogicalLimit { limit: 5, offset: 0 } + └─LogicalScan { table: t, columns: [t.v] } - sql: | create table t (v int); select * from t fetch first 4 rows only; diff --git a/src/frontend/src/optimizer/logical_optimization.rs b/src/frontend/src/optimizer/logical_optimization.rs index a907f87e6048c..3b5ccc5d2ea9b 100644 --- a/src/frontend/src/optimizer/logical_optimization.rs +++ b/src/frontend/src/optimizer/logical_optimization.rs @@ -256,6 +256,12 @@ lazy_static! { ApplyOrder::TopDown, ); + static ref LIMIT_PUSH_DOWN: OptimizationStage = OptimizationStage::new( + "Push Down Limit", + vec![LimitPushDownRule::create()], + ApplyOrder::TopDown, + ); + static ref PULL_UP_HOP: OptimizationStage = OptimizationStage::new( "Pull up hop", vec![PullUpHopRule::create()], @@ -503,6 +509,8 @@ impl LogicalOptimizer { plan = plan.optimize_by_rules(&TOP_N_AGG_ON_INDEX); + plan = plan.optimize_by_rules(&LIMIT_PUSH_DOWN); + #[cfg(debug_assertions)] InputRefValidator.validate(plan.clone()); diff --git a/src/frontend/src/optimizer/rule/limit_push_down_rule.rs b/src/frontend/src/optimizer/rule/limit_push_down_rule.rs new file mode 100644 index 0000000000000..461451bad229b --- /dev/null +++ b/src/frontend/src/optimizer/rule/limit_push_down_rule.rs @@ -0,0 +1,40 @@ +// Copyright 2023 RisingWave Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Copyright (c) 2011-present, Facebook, Inc. All rights reserved. +// This source code is licensed under both the GPLv2 (found in the +// COPYING file in the root directory) and Apache 2.0 License +// (found in the LICENSE.Apache file in the root directory). + +use super::{BoxedRule, Rule}; +use crate::optimizer::plan_node::{LogicalLimit, LogicalProject, PlanTreeNodeUnary}; +use crate::optimizer::PlanRef; + +pub struct LimitPushDownRule {} + +impl Rule for LimitPushDownRule { + fn apply(&self, plan: PlanRef) -> Option { + let limit: &LogicalLimit = plan.as_logical_limit()?; + let project: LogicalProject = limit.input().as_logical_project()?.to_owned(); + let input = project.input(); + let logical_limit = limit.clone_with_input(input); + Some(project.clone_with_input(logical_limit.into()).into()) + } +} + +impl LimitPushDownRule { + pub fn create() -> BoxedRule { + Box::new(LimitPushDownRule {}) + } +} diff --git a/src/frontend/src/optimizer/rule/mod.rs b/src/frontend/src/optimizer/rule/mod.rs index a1796bd0a8146..4ca57df9c3b17 100644 --- a/src/frontend/src/optimizer/rule/mod.rs +++ b/src/frontend/src/optimizer/rule/mod.rs @@ -101,6 +101,8 @@ mod always_false_filter_rule; pub use always_false_filter_rule::*; mod join_project_transpose_rule; pub use join_project_transpose_rule::*; +mod limit_push_down_rule; +pub use limit_push_down_rule::*; mod pull_up_hop_rule; pub use pull_up_hop_rule::*; @@ -148,6 +150,7 @@ macro_rules! for_all_rules { , { BushyTreeJoinOrderingRule } , { StreamProjectMergeRule } , { JoinProjectTransposeRule } + , { LimitPushDownRule } , { PullUpHopRule } } };