diff --git a/src/query/ast/src/ast/format/mod.rs b/src/query/ast/src/ast/format/mod.rs index 6e5bfb746bfc5..12b5fc815a74c 100644 --- a/src/query/ast/src/ast/format/mod.rs +++ b/src/query/ast/src/ast/format/mod.rs @@ -26,8 +26,8 @@ pub use syntax::pretty_statement; #[derive(Clone)] pub struct FormatTreeNode { - payload: T, - children: Vec, + pub payload: T, + pub children: Vec, } impl FormatTreeNode diff --git a/src/query/service/src/interpreters/fragments/v2/plan_fragment.rs b/src/query/service/src/interpreters/fragments/v2/plan_fragment.rs index f806fc83b5509..fc494e0470d28 100644 --- a/src/query/service/src/interpreters/fragments/v2/plan_fragment.rs +++ b/src/query/service/src/interpreters/fragments/v2/plan_fragment.rs @@ -205,6 +205,7 @@ impl PhysicalPlanReplacer for ReplaceReadSource { Ok(PhysicalPlan::TableScan(TableScan { source: Box::new(self.source.clone()), name_mapping: plan.name_mapping.clone(), + table_index: plan.table_index, })) } } diff --git a/src/query/service/src/interpreters/interpreter_explain_v2.rs b/src/query/service/src/interpreters/interpreter_explain_v2.rs index 447dd722e1cf8..bd8677ee6c7bf 100644 --- a/src/query/service/src/interpreters/interpreter_explain_v2.rs +++ b/src/query/service/src/interpreters/interpreter_explain_v2.rs @@ -25,6 +25,7 @@ use super::fragments::Fragmenter; use super::QueryFragmentsActions; use crate::interpreters::Interpreter; use crate::sessions::QueryContext; +use crate::sql::executor::PhysicalPlan; use crate::sql::executor::PhysicalPlanBuilder; use crate::sql::executor::PipelineBuilder; use crate::sql::optimizer::SExpr; @@ -53,7 +54,17 @@ impl Interpreter for ExplainInterpreterV2 { ExplainKind::Ast(stmt) | ExplainKind::Syntax(stmt) => { self.explain_ast_or_syntax(stmt.clone())? } - ExplainKind::Raw | ExplainKind::Plan => self.explain_raw_or_plan(&self.plan)?, + ExplainKind::Raw => self.explain_plan(&self.plan)?, + ExplainKind::Plan => match &self.plan { + Plan::Query { + s_expr, metadata, .. + } => { + let builder = PhysicalPlanBuilder::new(metadata.clone(), self.ctx.clone()); + let plan = builder.build(s_expr).await?; + self.explain_physical_plan(&plan, metadata)? + } + _ => self.explain_plan(&self.plan)?, + }, ExplainKind::Pipeline => match &self.plan { Plan::Query { s_expr, metadata, .. @@ -116,7 +127,7 @@ impl ExplainInterpreterV2 { ])]) } - pub fn explain_raw_or_plan(&self, plan: &Plan) -> Result> { + pub fn explain_plan(&self, plan: &Plan) -> Result> { let result = plan.format_indent()?; let line_splitted_result: Vec<&str> = result.lines().collect(); let formatted_plan = Series::from_data(line_splitted_result); @@ -125,6 +136,19 @@ impl ExplainInterpreterV2 { ])]) } + pub fn explain_physical_plan( + &self, + plan: &PhysicalPlan, + metadata: &MetadataRef, + ) -> Result> { + let result = plan.format(metadata.clone())?; + let line_splitted_result: Vec<&str> = result.lines().collect(); + let formatted_plan = Series::from_data(line_splitted_result); + Ok(vec![DataBlock::create(self.schema.clone(), vec![ + formatted_plan, + ])]) + } + pub async fn explain_pipeline( &self, s_expr: SExpr, diff --git a/src/query/service/src/sql/executor/format.rs b/src/query/service/src/sql/executor/format.rs new file mode 100644 index 0000000000000..0796c1af4a70b --- /dev/null +++ b/src/query/service/src/sql/executor/format.rs @@ -0,0 +1,328 @@ +// Copyright 2022 Datafuse 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. + +use common_ast::ast::FormatTreeNode; +use common_exception::ErrorCode; +use common_exception::Result; +use common_planners::StageKind; +use itertools::Itertools; + +use super::AggregateFinal; +use super::AggregatePartial; +use super::EvalScalar; +use super::Exchange; +use super::Filter; +use super::HashJoin; +use super::Limit; +use super::PhysicalPlan; +use super::Project; +use super::Sort; +use super::TableScan; +use super::UnionAll; +use crate::sql::IndexType; +use crate::sql::MetadataRef; + +impl PhysicalPlan { + pub fn format(&self, metadata: MetadataRef) -> Result { + to_format_tree(self, &metadata)?.format_pretty() + } +} + +fn to_format_tree(plan: &PhysicalPlan, metadata: &MetadataRef) -> Result> { + match plan { + PhysicalPlan::TableScan(plan) => table_scan_to_format_tree(plan, metadata), + PhysicalPlan::Filter(plan) => filter_to_format_tree(plan, metadata), + PhysicalPlan::Project(plan) => project_to_format_tree(plan, metadata), + PhysicalPlan::EvalScalar(plan) => eval_scalar_to_format_tree(plan, metadata), + PhysicalPlan::AggregatePartial(plan) => aggregate_partial_to_format_tree(plan, metadata), + PhysicalPlan::AggregateFinal(plan) => aggregate_final_to_format_tree(plan, metadata), + PhysicalPlan::Sort(plan) => sort_to_format_tree(plan, metadata), + PhysicalPlan::Limit(plan) => limit_to_format_tree(plan, metadata), + PhysicalPlan::HashJoin(plan) => hash_join_to_format_tree(plan, metadata), + PhysicalPlan::Exchange(plan) => exchange_to_format_tree(plan, metadata), + PhysicalPlan::UnionAll(plan) => union_all_to_format_tree(plan, metadata), + PhysicalPlan::ExchangeSource(_) | PhysicalPlan::ExchangeSink(_) => { + Err(ErrorCode::LogicalError("Invalid physical plan")) + } + } +} + +fn table_scan_to_format_tree( + plan: &TableScan, + metadata: &MetadataRef, +) -> Result> { + let table = metadata.read().table(plan.table_index).clone(); + let table_name = format!("{}.{}.{}", table.catalog, table.database, table.name); + let filters = plan + .source + .push_downs + .as_ref() + .map_or("".to_string(), |extras| { + extras + .filters + .iter() + .map(|f| f.column_name()) + .collect::>() + .join(", ") + }); + + let limit = plan + .source + .push_downs + .as_ref() + .map_or("NONE".to_string(), |extras| { + extras + .limit + .map_or("NONE".to_string(), |limit| limit.to_string()) + }); + + Ok(FormatTreeNode::with_children( + "TableScan".to_string(), + vec![ + FormatTreeNode::new(format!("table: {table_name}")), + FormatTreeNode::new(format!("read rows: {}", plan.source.statistics.read_rows)), + FormatTreeNode::new(format!("read bytes: {}", plan.source.statistics.read_bytes)), + FormatTreeNode::new(format!( + "partitions total: {}", + plan.source.statistics.partitions_total + )), + FormatTreeNode::new(format!( + "partitions scanned: {}", + plan.source.statistics.partitions_scanned + )), + FormatTreeNode::new(format!( + "push downs: [filters: [{filters}], limit: {limit}]" + )), + ], + )) +} + +fn filter_to_format_tree(plan: &Filter, metadata: &MetadataRef) -> Result> { + let filter = plan + .predicates + .iter() + .map(|scalar| scalar.pretty_display(metadata)) + .collect::>>()? + .join(", "); + Ok(FormatTreeNode::with_children("Filter".to_string(), vec![ + FormatTreeNode::new(format!("filters: [{filter}]")), + to_format_tree(&plan.input, metadata)?, + ])) +} + +fn project_to_format_tree( + plan: &Project, + metadata: &MetadataRef, +) -> Result> { + let columns = plan + .columns + .iter() + .sorted() + .map(|column| format!("{} (#{})", metadata.read().column(*column).name, column)) + .collect::>() + .join(", "); + Ok(FormatTreeNode::with_children("Project".to_string(), vec![ + FormatTreeNode::new(format!("columns: [{columns}]")), + to_format_tree(&plan.input, metadata)?, + ])) +} + +fn eval_scalar_to_format_tree( + plan: &EvalScalar, + metadata: &MetadataRef, +) -> Result> { + let scalars = plan + .scalars + .iter() + .map(|(scalar, _)| scalar.pretty_display(metadata)) + .collect::>>()? + .join(", "); + Ok(FormatTreeNode::with_children( + "EvalScalar".to_string(), + vec![ + FormatTreeNode::new(format!("expressions: [{scalars}]")), + to_format_tree(&plan.input, metadata)?, + ], + )) +} + +fn aggregate_partial_to_format_tree( + plan: &AggregatePartial, + metadata: &MetadataRef, +) -> Result> { + let group_by = plan + .group_by + .iter() + .map(|column| { + let index = column.parse::()?; + let column = metadata.read().column(index).clone(); + Ok(column.name) + }) + .collect::>>()? + .join(", "); + + let agg_funcs = plan + .agg_funcs + .iter() + .map(|agg| agg.pretty_display(metadata)) + .collect::>>()? + .join(", "); + Ok(FormatTreeNode::with_children( + "AggregatePartial".to_string(), + vec![ + FormatTreeNode::new(format!("group by: [{group_by}]")), + FormatTreeNode::new(format!("aggregate functions: [{agg_funcs}]")), + to_format_tree(&plan.input, metadata)?, + ], + )) +} + +fn aggregate_final_to_format_tree( + plan: &AggregateFinal, + metadata: &MetadataRef, +) -> Result> { + let group_by = plan + .group_by + .iter() + .map(|column| { + let index = column.parse::()?; + let column = metadata.read().column(index).clone(); + Ok(column.name) + }) + .collect::>>()? + .join(", "); + + let agg_funcs = plan + .agg_funcs + .iter() + .map(|agg| agg.pretty_display(metadata)) + .collect::>>()? + .join(", "); + Ok(FormatTreeNode::with_children( + "AggregateFinal".to_string(), + vec![ + FormatTreeNode::new(format!("group by: [{group_by}]")), + FormatTreeNode::new(format!("aggregate functions: [{agg_funcs}]")), + to_format_tree(&plan.input, metadata)?, + ], + )) +} + +fn sort_to_format_tree(plan: &Sort, metadata: &MetadataRef) -> Result> { + let sort_keys = plan + .order_by + .iter() + .map(|sort_key| { + let index = sort_key.order_by.parse::()?; + let column = metadata.read().column(index).clone(); + Ok(format!( + "{} {} {}", + column.name, + if sort_key.asc { "ASC" } else { "DESC" }, + if sort_key.nulls_first { + "NULLS FIRST" + } else { + "NULLS LAST" + } + )) + }) + .collect::>>()? + .join(", "); + Ok(FormatTreeNode::with_children("Sort".to_string(), vec![ + FormatTreeNode::new(format!("sort keys: [{sort_keys}]")), + to_format_tree(&plan.input, metadata)?, + ])) +} + +fn limit_to_format_tree(plan: &Limit, metadata: &MetadataRef) -> Result> { + Ok(FormatTreeNode::with_children("Limit".to_string(), vec![ + FormatTreeNode::new(format!( + "limit: {}", + plan.limit + .map_or("NONE".to_string(), |limit| limit.to_string()) + )), + FormatTreeNode::new(format!("offset: {}", plan.offset)), + to_format_tree(&plan.input, metadata)?, + ])) +} + +fn hash_join_to_format_tree( + plan: &HashJoin, + metadata: &MetadataRef, +) -> Result> { + let build_keys = plan + .build_keys + .iter() + .map(|scalar| scalar.pretty_display(metadata)) + .collect::>>()? + .join(", "); + let probe_keys = plan + .probe_keys + .iter() + .map(|scalar| scalar.pretty_display(metadata)) + .collect::>>()? + .join(", "); + let filters = plan + .other_conditions + .iter() + .map(|filter| filter.pretty_display(metadata)) + .collect::>>()? + .join(", "); + + let mut build_child = to_format_tree(&plan.build, metadata)?; + let mut probe_child = to_format_tree(&plan.probe, metadata)?; + + build_child.payload = format!("{}(Build)", build_child.payload); + probe_child.payload = format!("{}(Probe)", probe_child.payload); + + Ok(FormatTreeNode::with_children("HashJoin".to_string(), vec![ + FormatTreeNode::new(format!("join type: {}", plan.join_type)), + FormatTreeNode::new(format!("build keys: [{build_keys}]")), + FormatTreeNode::new(format!("probe keys: [{probe_keys}]")), + FormatTreeNode::new(format!("filters: [{filters}]")), + build_child, + probe_child, + ])) +} + +fn exchange_to_format_tree( + plan: &Exchange, + metadata: &MetadataRef, +) -> Result> { + Ok(FormatTreeNode::with_children("Exchange".to_string(), vec![ + FormatTreeNode::new(format!("exchange type: {}", match plan.kind { + StageKind::Normal => format!( + "Hash({})", + plan.keys + .iter() + .map(|scalar| { scalar.pretty_display(metadata) }) + .collect::>>()? + .join(", ") + ), + StageKind::Expansive => "Broadcast".to_string(), + StageKind::Merge => "Merge".to_string(), + })), + to_format_tree(&plan.input, metadata)?, + ])) +} + +fn union_all_to_format_tree( + plan: &UnionAll, + metadata: &MetadataRef, +) -> Result> { + Ok(FormatTreeNode::with_children("UnionAll".to_string(), vec![ + to_format_tree(&plan.left, metadata)?, + to_format_tree(&plan.right, metadata)?, + ])) +} diff --git a/src/query/service/src/sql/executor/mod.rs b/src/query/service/src/sql/executor/mod.rs index 6618df446ec26..bd5dd6ecd5201 100644 --- a/src/query/service/src/sql/executor/mod.rs +++ b/src/query/service/src/sql/executor/mod.rs @@ -13,6 +13,7 @@ // limitations under the License. mod expression_builder; +mod format; mod physical_plan; mod physical_plan_builder; mod physical_plan_display; diff --git a/src/query/service/src/sql/executor/physical_plan.rs b/src/query/service/src/sql/executor/physical_plan.rs index bae1bfe111a6d..dda12a63a8bbe 100644 --- a/src/query/service/src/sql/executor/physical_plan.rs +++ b/src/query/service/src/sql/executor/physical_plan.rs @@ -30,6 +30,7 @@ use common_planners::StageKind; use super::physical_scalar::PhysicalScalar; use super::AggregateFunctionDesc; use super::SortDesc; +use crate::sql::optimizer::ColumnSet; use crate::sql::plans::JoinType; use crate::sql::IndexType; @@ -39,6 +40,9 @@ pub type ColumnID = String; pub struct TableScan { pub name_mapping: BTreeMap, pub source: Box, + + /// Only used for display + pub table_index: IndexType, } impl TableScan { @@ -69,6 +73,9 @@ impl Filter { pub struct Project { pub input: Box, pub projections: Vec, + + /// Only used for display + pub columns: ColumnSet, } impl Project { diff --git a/src/query/service/src/sql/executor/physical_plan_builder.rs b/src/query/service/src/sql/executor/physical_plan_builder.rs index f6aa8b9477315..5bcc15e91d2d2 100644 --- a/src/query/service/src/sql/executor/physical_plan_builder.rs +++ b/src/query/service/src/sql/executor/physical_plan_builder.rs @@ -229,6 +229,7 @@ impl PhysicalPlanBuilder { Ok(PhysicalPlan::TableScan(TableScan { name_mapping, source: Box::new(source), + table_index: scan.table_index, })) } RelOperator::PhysicalHashJoin(join) => { @@ -277,6 +278,8 @@ impl PhysicalPlanBuilder { .sorted() .map(|index| input_schema.index_of(index.to_string().as_str())) .collect::>()?, + + columns: project.columns.clone(), })) } RelOperator::EvalScalar(eval_scalar) => Ok(PhysicalPlan::EvalScalar(EvalScalar { diff --git a/src/query/service/src/sql/executor/physical_plan_visitor.rs b/src/query/service/src/sql/executor/physical_plan_visitor.rs index 66d4e1adb465b..241c18f74b709 100644 --- a/src/query/service/src/sql/executor/physical_plan_visitor.rs +++ b/src/query/service/src/sql/executor/physical_plan_visitor.rs @@ -67,6 +67,7 @@ pub trait PhysicalPlanReplacer { Ok(PhysicalPlan::Project(Project { input: Box::new(input), projections: plan.projections.clone(), + columns: plan.columns.clone(), })) } diff --git a/src/query/service/src/sql/executor/physical_scalar.rs b/src/query/service/src/sql/executor/physical_scalar.rs index 0c967b1303ffe..27df646b36f2f 100644 --- a/src/query/service/src/sql/executor/physical_scalar.rs +++ b/src/query/service/src/sql/executor/physical_scalar.rs @@ -12,10 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +use common_datavalues::format_data_type_sql; use common_datavalues::DataTypeImpl; use common_datavalues::DataValue; +use common_exception::Result; use super::ColumnID; +use crate::sql::IndexType; +use crate::sql::MetadataRef; /// Serializable and desugared representation of `Scalar`. #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] @@ -48,6 +52,37 @@ impl PhysicalScalar { PhysicalScalar::Cast { target, .. } => target.clone(), } } + + /// Display with readable variable name. + pub fn pretty_display(&self, metadata: &MetadataRef) -> Result { + match self { + PhysicalScalar::Variable { column_id, .. } => { + let index = column_id.parse::()?; + let column = metadata.read().column(index).clone(); + let table_name = match column.table_index { + Some(table_index) => { + format!("{}.", metadata.read().table(table_index).name.clone()) + } + None => "".to_string(), + }; + Ok(format!("{}{} (#{})", table_name, column.name, index)) + } + PhysicalScalar::Constant { value, .. } => Ok(value.to_string()), + PhysicalScalar::Function { name, args, .. } => { + let args = args + .iter() + .map(|(arg, _)| arg.pretty_display(metadata)) + .collect::>>()? + .join(", "); + Ok(format!("{}({})", name, args)) + } + PhysicalScalar::Cast { input, target } => Ok(format!( + "CAST({} AS {})", + input.pretty_display(metadata)?, + format_data_type_sql(target) + )), + } + } } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] @@ -57,6 +92,24 @@ pub struct AggregateFunctionDesc { pub args: Vec, } +impl AggregateFunctionDesc { + pub fn pretty_display(&self, metadata: &MetadataRef) -> Result { + Ok(format!( + "{}({})", + self.sig.name, + self.args + .iter() + .map(|arg| { + let index = arg.parse::()?; + let column = metadata.read().column(index).clone(); + Ok(column.name) + }) + .collect::>>()? + .join(", ") + )) + } +} + #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct AggregateFunctionSignature { pub name: String, diff --git a/tests/logictest/suites/mode/cluster/04_0002_explain_v2 b/tests/logictest/suites/mode/cluster/04_0002_explain_v2 index 95b93b365e040..8762f6f8ab7a5 100644 --- a/tests/logictest/suites/mode/cluster/04_0002_explain_v2 +++ b/tests/logictest/suites/mode/cluster/04_0002_explain_v2 @@ -14,66 +14,81 @@ statement query T explain select t1.a from t1 where a > 0; ---- -Exchange(Merge) +Exchange +├── exchange type: Merge └── Project - ├── projections: [a (#0)] + ├── columns: [a (#0)] └── Filter - ├── filters: [t1.a (#0) > 0] - └── PhysicalScan + ├── filters: [>(t1.a (#0), 0)] + └── TableScan ├── table: default.default.t1 - ├── filters: [t1.a (#0) > 0] - ├── order by: [] - └── limit: NONE + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [(a > 0)], limit: NONE] statement query T explain select * from t1, t2 where (t1.a = t2.a and t1.a > 3) or (t1.a = t2.a and t2.a > 5 and t1.a > 1); ---- -Exchange(Merge) +Exchange +├── exchange type: Merge └── Filter - ├── filters: [(t1.a (#0) > 3) OR ((t2.a (#2) > 5) AND (t1.a (#0) > 1))] - └── HashJoin: INNER + ├── filters: [or(>(t1.a (#0), 3), and(>(t2.a (#2), 5), >(t1.a (#0), 1)))] + └── HashJoin + ├── join type: INNER ├── build keys: [t2.a (#2)] ├── probe keys: [t1.a (#0)] - ├── other filters: [] - ├── Exchange(Hash) - │ ├── Exchange(Hash): keys: [t1.a (#0)] - │ └── PhysicalScan - │ ├── table: default.default.t1 - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Exchange(Hash) - ├── Exchange(Hash): keys: [t2.a (#2)] - └── PhysicalScan - ├── table: default.default.t2 - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── filters: [] + ├── Exchange(Build) + │ ├── exchange type: Hash(t2.a (#2)) + │ └── TableScan + │ ├── table: default.default.t2 + │ ├── read rows: 0 + │ ├── read bytes: 0 + │ ├── partitions total: 0 + │ ├── partitions scanned: 0 + │ └── push downs: [filters: [], limit: NONE] + └── Exchange(Probe) + ├── exchange type: Hash(t1.a (#0)) + └── TableScan + ├── table: default.default.t1 + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [], limit: NONE] statement query T explain select * from t1, t2 where (t1.a = t2.a and t1.a > 3) or (t1.a = t2.a); ---- -Exchange(Merge) -└── HashJoin: INNER +Exchange +├── exchange type: Merge +└── HashJoin + ├── join type: INNER ├── build keys: [t2.a (#2)] ├── probe keys: [t1.a (#0)] - ├── other filters: [] - ├── Exchange(Hash) - │ ├── Exchange(Hash): keys: [t1.a (#0)] - │ └── PhysicalScan - │ ├── table: default.default.t1 - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Exchange(Hash) - ├── Exchange(Hash): keys: [t2.a (#2)] - └── PhysicalScan - ├── table: default.default.t2 - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── filters: [] + ├── Exchange(Build) + │ ├── exchange type: Hash(t2.a (#2)) + │ └── TableScan + │ ├── table: default.default.t2 + │ ├── read rows: 0 + │ ├── read bytes: 0 + │ ├── partitions total: 0 + │ ├── partitions scanned: 0 + │ └── push downs: [filters: [], limit: NONE] + └── Exchange(Probe) + ├── exchange type: Hash(t1.a (#0)) + └── TableScan + ├── table: default.default.t1 + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [], limit: NONE] statement query T explain raw select * from t1, t2 where (t1.a = t2.a and t1.a > 3) or (t1.a = t2.a); diff --git a/tests/logictest/suites/mode/cluster/exchange.test b/tests/logictest/suites/mode/cluster/exchange.test index 8e79f7ceb8c58..bf9e609d9cd5a 100644 --- a/tests/logictest/suites/mode/cluster/exchange.test +++ b/tests/logictest/suites/mode/cluster/exchange.test @@ -2,133 +2,163 @@ statement query T explain select * from numbers(1) t, numbers(2) t1 where t.number = t1.number; ---- -Exchange(Merge) -└── HashJoin: INNER - ├── build keys: [t1.number (#1)] - ├── probe keys: [t.number (#0)] - ├── other filters: [] - ├── Exchange(Hash) - │ ├── Exchange(Hash): keys: [t.number (#0)] - │ └── PhysicalScan +Exchange +├── exchange type: Merge +└── HashJoin + ├── join type: INNER + ├── build keys: [numbers.number (#1)] + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── Exchange(Build) + │ ├── exchange type: Hash(numbers.number (#1)) + │ └── TableScan │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Exchange(Hash) - ├── Exchange(Hash): keys: [t1.number (#1)] - └── PhysicalScan + │ ├── read rows: 2 + │ ├── read bytes: 16 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Exchange(Probe) + ├── exchange type: Hash(numbers.number (#0)) + └── TableScan ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select * from numbers(1) t, numbers(2) t1, numbers(3) t2 where t.number = t1.number and t.number = t2.number; ---- -Exchange(Merge) -└── HashJoin: INNER - ├── build keys: [t2.number (#2)] - ├── probe keys: [t.number (#0)] - ├── other filters: [] - ├── HashJoin: INNER - │ ├── build keys: [t1.number (#1)] - │ ├── probe keys: [t.number (#0)] - │ ├── other filters: [] - │ ├── Exchange(Hash) - │ │ ├── Exchange(Hash): keys: [t.number (#0)] - │ │ └── PhysicalScan - │ │ ├── table: default.system.numbers - │ │ ├── filters: [] - │ │ ├── order by: [] - │ │ └── limit: NONE - │ └── Exchange(Hash) - │ ├── Exchange(Hash): keys: [t1.number (#1)] - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Exchange(Hash) - ├── Exchange(Hash): keys: [t2.number (#2)] - └── PhysicalScan - ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE +Exchange +├── exchange type: Merge +└── HashJoin + ├── join type: INNER + ├── build keys: [numbers.number (#2)] + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── Exchange(Build) + │ ├── exchange type: Hash(numbers.number (#2)) + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── read rows: 3 + │ ├── read bytes: 24 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── HashJoin(Probe) + ├── join type: INNER + ├── build keys: [numbers.number (#1)] + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── Exchange(Build) + │ ├── exchange type: Hash(numbers.number (#1)) + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── read rows: 2 + │ ├── read bytes: 16 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Exchange(Probe) + ├── exchange type: Hash(numbers.number (#0)) + └── TableScan + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select * from (select number as a, number+1 as b from numbers(1)) t, numbers(2) t1, numbers(3) t2 where a = t1.number and b = t2.number; ---- -Exchange(Merge) -└── HashJoin: INNER - ├── build keys: [t2.number (#4)] - ├── probe keys: [t.b (#1)] - ├── other filters: [] - ├── Exchange(Hash) - │ ├── Exchange(Hash): keys: [t.b (#1)] - │ └── HashJoin: INNER - │ ├── build keys: [t1.number (#3)] - │ ├── probe keys: [t.a (#0)] - │ ├── other filters: [] - │ ├── Exchange(Hash) - │ │ ├── Exchange(Hash): keys: [t.a (#0)] - │ │ └── EvalScalar - │ │ ├── scalars: [+(numbers.number (#0), 1)] - │ │ └── PhysicalScan - │ │ ├── table: default.system.numbers - │ │ ├── filters: [] - │ │ ├── order by: [] - │ │ └── limit: NONE - │ └── Exchange(Hash) - │ ├── Exchange(Hash): keys: [t1.number (#3)] - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Exchange(Hash) - ├── Exchange(Hash): keys: [t2.number (#4)] - └── PhysicalScan - ├── table: default.system.numbers +Exchange +├── exchange type: Merge +└── HashJoin + ├── join type: INNER + ├── build keys: [numbers.number (#4)] + ├── probe keys: [b (#1)] + ├── filters: [] + ├── Exchange(Build) + │ ├── exchange type: Hash(numbers.number (#4)) + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── read rows: 3 + │ ├── read bytes: 24 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Exchange(Probe) + ├── exchange type: Hash(b (#1)) + └── HashJoin + ├── join type: INNER + ├── build keys: [numbers.number (#3)] + ├── probe keys: [numbers.number (#0)] ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── Exchange(Build) + │ ├── exchange type: Hash(numbers.number (#3)) + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── read rows: 2 + │ ├── read bytes: 16 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Exchange(Probe) + ├── exchange type: Hash(numbers.number (#0)) + └── EvalScalar + ├── expressions: [+(numbers.number (#0), 1)] + └── TableScan + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select * from (select sum(number) as number from numbers(1) group by number) t, numbers(2) t1 where t.number = t1.number; ---- -Exchange(Merge) -└── HashJoin: INNER - ├── build keys: [t1.number (#4)] - ├── probe keys: [t.number (#1)] - ├── other filters: [] - ├── Exchange(Hash) - │ ├── Exchange(Hash): keys: [t.number (#1)] - │ └── Project - │ ├── projections: [number (#1)] - │ └── EvalScalar - │ ├── scalars: [sum(number) (#3)] - │ └── Aggregate(Final) - │ ├── group items: [numbers.number (#0)] - │ ├── aggregate functions: [sum(number)] - │ └── Aggregate(Partial) - │ ├── group items: [numbers.number (#0)] - │ ├── aggregate functions: [sum(number)] - │ └── Exchange(Hash) - │ ├── Exchange(Hash): keys: [numbers.number (#0)] - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Exchange(Hash) - ├── Exchange(Hash): keys: [t1.number (#4)] - └── PhysicalScan - ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE +Exchange +├── exchange type: Merge +└── HashJoin + ├── join type: INNER + ├── build keys: [numbers.number (#4)] + ├── probe keys: [number (#1)] + ├── filters: [] + ├── Exchange(Build) + │ ├── exchange type: Hash(numbers.number (#4)) + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── read rows: 2 + │ ├── read bytes: 16 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Exchange(Probe) + ├── exchange type: Hash(number (#1)) + └── Project + ├── columns: [number (#1)] + └── EvalScalar + ├── expressions: [sum(number) (#3)] + └── AggregateFinal + ├── group by: [number] + ├── aggregate functions: [sum(number)] + └── AggregatePartial + ├── group by: [number] + ├── aggregate functions: [sum(number)] + └── Exchange + ├── exchange type: Hash(numbers.number (#0)) + └── TableScan + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] diff --git a/tests/logictest/suites/mode/standalone/explain/explain.test b/tests/logictest/suites/mode/standalone/explain/explain.test index d0124673ebcdf..356ed9e862daa 100644 --- a/tests/logictest/suites/mode/standalone/explain/explain.test +++ b/tests/logictest/suites/mode/standalone/explain/explain.test @@ -15,54 +15,66 @@ explain select t1.a from t1 where a > 0; ---- Project -├── projections: [a (#0)] +├── columns: [a (#0)] └── Filter - ├── filters: [t1.a (#0) > 0] - └── PhysicalScan + ├── filters: [>(t1.a (#0), 0)] + └── TableScan ├── table: default.default.t1 - ├── filters: [t1.a (#0) > 0] - ├── order by: [] - └── limit: NONE + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [(a > 0)], limit: NONE] statement query T explain select * from t1, t2 where (t1.a = t2.a and t1.a > 3) or (t1.a = t2.a and t2.a > 5 and t1.a > 1); ---- Filter -├── filters: [(t1.a (#0) > 3) OR ((t2.a (#2) > 5) AND (t1.a (#0) > 1))] -└── HashJoin: INNER +├── filters: [or(>(t1.a (#0), 3), and(>(t2.a (#2), 5), >(t1.a (#0), 1)))] +└── HashJoin + ├── join type: INNER ├── build keys: [t2.a (#2)] ├── probe keys: [t1.a (#0)] - ├── other filters: [] - ├── PhysicalScan - │ ├── table: default.default.t1 - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan - ├── table: default.default.t2 - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── filters: [] + ├── TableScan(Build) + │ ├── table: default.default.t2 + │ ├── read rows: 0 + │ ├── read bytes: 0 + │ ├── partitions total: 0 + │ ├── partitions scanned: 0 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) + ├── table: default.default.t1 + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [], limit: NONE] statement query T explain select * from t1, t2 where (t1.a = t2.a and t1.a > 3) or (t1.a = t2.a); ---- -HashJoin: INNER +HashJoin +├── join type: INNER ├── build keys: [t2.a (#2)] ├── probe keys: [t1.a (#0)] -├── other filters: [] -├── PhysicalScan -│ ├── table: default.default.t1 -│ ├── filters: [] -│ ├── order by: [] -│ └── limit: NONE -└── PhysicalScan - ├── table: default.default.t2 - ├── filters: [] - ├── order by: [] - └── limit: NONE +├── filters: [] +├── TableScan(Build) +│ ├── table: default.default.t2 +│ ├── read rows: 0 +│ ├── read bytes: 0 +│ ├── partitions total: 0 +│ ├── partitions scanned: 0 +│ └── push downs: [filters: [], limit: NONE] +└── TableScan(Probe) + ├── table: default.default.t1 + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [], limit: NONE] statement query T explain raw select * from t1, t2 where (t1.a = t2.a and t1.a > 3) or (t1.a = t2.a); @@ -220,7 +232,6 @@ AS WHERE number > 10 -onlyif mysql statement query T explain ast select 1, 'ab', [1,2,3] as a, (1, 'a') as t; @@ -243,9 +254,8 @@ Query (children 1) ├── Literal Integer(1) └── Literal String("a") -onlyif mysql statement query T -explain ast select case when a > 1 then 'x' when a < 10 then 'y' else 'z' end from t1 +explain ast select case when a > 1 then 'x' when a < 10 then 'y' else 'z' end from t1; ---- Query (children 1) @@ -269,7 +279,6 @@ Query (children 1) └── TableList (children 1) └── TableIdentifier t1 -onlyif mysql statement query T explain ast select a, sum(b) as sum from t1 where a in (1, 2) and b > 0 and b < 100 group by a order by a limit 3; @@ -307,8 +316,6 @@ Query (children 3) └── LimitList (children 1) └── Literal Integer(3) - -onlyif mysql statement query T explain ast select * from t1 inner join t2 on t1.a = t2.a and t1.b = t2.b and t1.a > 2; @@ -336,7 +343,6 @@ Query (children 1) ├── ColumnIdentifier t1.a └── Literal Integer(2) -onlyif mysql statement query T explain ast with cte (a, b) as (select 1, 2 union all select 3, 4) select a, b from cte; @@ -371,7 +377,6 @@ Query (children 2) └── TableList (children 1) └── TableIdentifier cte -onlyif mysql statement query T explain ast insert into t1 (a, b) values (1, 2),(3, 4); @@ -384,7 +389,6 @@ Insert (children 3) └── Source (children 1) └── ValueSouce -onlyif mysql statement query T explain ast delete from t1 where a > 100 and b > 1 and b < 10; @@ -403,7 +407,6 @@ Delete (children 2) ├── ColumnIdentifier b └── Literal Integer(10) -onlyif mysql statement query T explain ast copy into t1 from 's3://mybucket/data.csv' file_format = ( type = 'CSV' field_delimiter = ',' record_delimiter = '\n' skip_header = 1) size_limit=10; @@ -420,16 +423,14 @@ Copy (children 4) │ └── FileFormat type = "CSV" └── SizeLimit 10 -onlyif mysql statement query T -explain ast create database db1 engine=default +explain ast create database db1 engine=default; ---- CreateDatabase (children 2) ├── DatabaseIdentifier db1 └── DatabaseEngine DEFAULT -onlyif mysql statement query T explain ast create table t3(a int64, b uint64, c float64, d string, e array(int32), f tuple(f1 bool, f2 string)) engine=fuse cluster by (a, b, c) comment='test' compression='LZ4'; @@ -458,7 +459,6 @@ CreateTable (children 5) ├── TableOption comment = "test" └── TableOption compression = "LZ4" -onlyif mysql statement query T explain ast create view v as select number % 3 as a from numbers(100) where number > 10; @@ -481,7 +481,6 @@ CreateView (children 2) ├── ColumnIdentifier number └── Literal Integer(10) -onlyif mysql statement query T explain ast show create table t1; @@ -489,7 +488,6 @@ explain ast show create table t1; ShowCreateTable (children 1) └── TableIdentifier t1 -onlyif mysql statement query T explain ast create user 'test'@'localhost' identified with sha256_password by 'new_password'; diff --git a/tests/logictest/suites/mode/standalone/explain/join.test b/tests/logictest/suites/mode/standalone/explain/join.test index 49d264648d67d..d07ef44e6f5fb 100644 --- a/tests/logictest/suites/mode/standalone/explain/join.test +++ b/tests/logictest/suites/mode/standalone/explain/join.test @@ -3,141 +3,174 @@ explain select t.number from numbers(1) as t, numbers(1) as t1 where t.number = ---- Project -├── projections: [number (#0)] -└── HashJoin: INNER - ├── build keys: [t1.number (#1)] - ├── probe keys: [t.number (#0)] - ├── other filters: [] - ├── PhysicalScan +├── columns: [number (#0)] +└── HashJoin + ├── join type: INNER + ├── build keys: [numbers.number (#1)] + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── TableScan(Build) │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t, numbers(1) as t1 where t.number = t1.number and t.number = t1.number + 1; ---- Project -├── projections: [number (#0)] -└── HashJoin: INNER - ├── build keys: [t1.number (#1), +(t1.number (#1), 1)] - ├── probe keys: [t.number (#0), t.number (#0)] - ├── other filters: [] - ├── PhysicalScan +├── columns: [number (#0)] +└── HashJoin + ├── join type: INNER + ├── build keys: [numbers.number (#1), +(numbers.number (#1), 1)] + ├── probe keys: [numbers.number (#0), numbers.number (#0)] + ├── filters: [] + ├── TableScan(Build) │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t, numbers(1) as t1 where t.number > 1 and 1 < t1.number; ---- Project -├── projections: [number (#0)] -└── CrossJoin +├── columns: [number (#0)] +└── HashJoin + ├── join type: CROSS ├── build keys: [] ├── probe keys: [] - ├── other filters: [] - ├── Filter - │ ├── filters: [t.number (#0) > 1] - │ └── PhysicalScan + ├── filters: [] + ├── Filter(Build) + │ ├── filters: [<(1, numbers.number (#1))] + │ └── TableScan │ ├── table: default.system.numbers - │ ├── filters: [t.number (#0) > 1] - │ ├── order by: [] - │ └── limit: NONE - └── Filter - ├── filters: [1 < t1.number (#1)] - └── PhysicalScan + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [(1 < number)], limit: NONE] + └── Filter(Probe) + ├── filters: [>(numbers.number (#0), 1)] + └── TableScan ├── table: default.system.numbers - ├── filters: [1 < t1.number (#1)] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [(number > 1)], limit: NONE] statement query T explain select t.number from numbers(1) as t, numbers(1) as t1 where t.number + t1.number = 1; ---- Project -├── projections: [number (#0)] +├── columns: [number (#0)] └── Filter - ├── filters: [+(t.number (#0), t1.number (#1)) = 1] - └── CrossJoin + ├── filters: [=(+(numbers.number (#0), numbers.number (#1)), 1)] + └── HashJoin + ├── join type: CROSS ├── build keys: [] ├── probe keys: [] - ├── other filters: [] - ├── PhysicalScan + ├── filters: [] + ├── TableScan(Build) │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t, numbers(1) as t1 where t.number = cast(t1.number as string); ---- Project -├── projections: [number (#0)] -└── HashJoin: INNER - ├── build keys: [CAST(CAST(t1.number (#1) AS VARCHAR) AS DOUBLE)] - ├── probe keys: [CAST(t.number (#0) AS DOUBLE)] - ├── other filters: [] - ├── PhysicalScan +├── columns: [number (#0)] +└── HashJoin + ├── join type: INNER + ├── build keys: [CAST(CAST(numbers.number (#1) AS VARCHAR) AS DOUBLE)] + ├── probe keys: [CAST(numbers.number (#0) AS DOUBLE)] + ├── filters: [] + ├── TableScan(Build) │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t, numbers(1) as t1, numbers(1) as t2 where t1.number = t2.number and t.number = 1; ---- Project -├── projections: [number (#0)] -└── HashJoin: INNER - ├── build keys: [t2.number (#2)] - ├── probe keys: [t1.number (#1)] - ├── other filters: [] - ├── CrossJoin - │ ├── build keys: [] - │ ├── probe keys: [] - │ ├── other filters: [] - │ ├── Filter - │ │ ├── filters: [t.number (#0) = 1] - │ │ └── PhysicalScan - │ │ ├── table: default.system.numbers - │ │ ├── filters: [t.number (#0) = 1] - │ │ ├── order by: [] - │ │ └── limit: NONE - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan - ├── table: default.system.numbers +├── columns: [number (#0)] +└── HashJoin + ├── join type: INNER + ├── build keys: [numbers.number (#2)] + ├── probe keys: [numbers.number (#1)] + ├── filters: [] + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── HashJoin(Probe) + ├── join type: CROSS + ├── build keys: [] + ├── probe keys: [] ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Filter(Probe) + ├── filters: [=(numbers.number (#0), 1)] + └── TableScan + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [(number = 1)], limit: NONE] diff --git a/tests/logictest/suites/mode/standalone/explain/limit.test b/tests/logictest/suites/mode/standalone/explain/limit.test index d13aa7575eb45..22aae38c28192 100644 --- a/tests/logictest/suites/mode/standalone/explain/limit.test +++ b/tests/logictest/suites/mode/standalone/explain/limit.test @@ -3,167 +3,186 @@ explain select * from (select t.number from numbers(10) as t limit 8) limit 9; ---- Limit -├── limit: [9] -├── offset: [0] +├── limit: 9 +├── offset: 0 └── Limit - ├── limit: [8] - ├── offset: [0] - └── PhysicalScan + ├── limit: 8 + ├── offset: 0 + └── TableScan ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: 8 + ├── read rows: 8 + ├── read bytes: 64 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: 8] statement query T explain select * from (select t.number from numbers(10) as t order by number desc) order by number asc; ---- Sort -├── sort keys: [number (#0) ASC] -├── limit: [NONE] +├── sort keys: [number ASC NULLS LAST] └── Sort - ├── sort keys: [number (#0) DESC] - ├── limit: [NONE] - └── PhysicalScan + ├── sort keys: [number DESC NULLS LAST] + └── TableScan ├── table: default.system.numbers - ├── filters: [] - ├── order by: [number (#0) DESC] - └── limit: NONE + ├── read rows: 10 + ├── read bytes: 80 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select number from (select t.number from numbers(10) as t order by number desc limit 8) order by number asc limit 9; ---- Limit -├── limit: [9] -├── offset: [0] +├── limit: 9 +├── offset: 0 └── Sort - ├── sort keys: [number (#0) ASC] - ├── limit: [9] + ├── sort keys: [number ASC NULLS LAST] └── Limit - ├── limit: [8] - ├── offset: [0] + ├── limit: 8 + ├── offset: 0 └── Sort - ├── sort keys: [number (#0) DESC] - ├── limit: [8] - └── PhysicalScan + ├── sort keys: [number DESC NULLS LAST] + └── TableScan ├── table: default.system.numbers - ├── filters: [] - ├── order by: [number (#0) DESC] - └── limit: 8 + ├── read rows: 10 + ├── read bytes: 80 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: 8] statement query T explain select t.number from numbers(1) as t, numbers(1) as t1 where t.number = (select count(*) from numbers(1) as t2, numbers(1) as t3 where t.number = t2.number) group by t.number order by t.number desc limit 3; ---- Limit -├── limit: [3] -├── offset: [0] +├── limit: 3 +├── offset: 0 └── Sort - ├── sort keys: [number (#0) DESC] - ├── limit: [3] - └── Aggregate(Final) - ├── group items: [t.number (#0)] + ├── sort keys: [number DESC NULLS LAST] + └── AggregateFinal + ├── group by: [number] ├── aggregate functions: [] - └── Aggregate(Partial) - ├── group items: [t.number (#0)] + └── AggregatePartial + ├── group by: [number] ├── aggregate functions: [] └── Filter - ├── filters: [t.number (#0) = CAST(if(is_not_null(scalar_subquery_4 (#4)), scalar_subquery_4 (#4), 0) AS BIGINT UNSIGNED)] - └── HashJoin: SINGLE - ├── build keys: [subquery_6 (#6)] - ├── probe keys: [subquery_0 (#0)] - ├── other filters: [] - ├── CrossJoin - │ ├── build keys: [] - │ ├── probe keys: [] - │ ├── other filters: [] - │ ├── PhysicalScan - │ │ ├── table: default.system.numbers - │ │ ├── filters: [] - │ │ ├── order by: [] - │ │ └── limit: NONE - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Project - ├── projections: [COUNT(*) (#4),number (#6)] - └── EvalScalar - ├── scalars: [COUNT(*) (#5)] - └── Aggregate(Final) - ├── group items: [subquery_6 (#6)] - ├── aggregate functions: [COUNT(*)] - └── Aggregate(Partial) - ├── group items: [subquery_6 (#6)] - ├── aggregate functions: [COUNT(*)] - └── HashJoin: INNER - ├── build keys: [t2.number (#2)] - ├── probe keys: [subquery_6 (#6)] - ├── other filters: [] - ├── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── CrossJoin - ├── build keys: [] - ├── probe keys: [] - ├── other filters: [] - ├── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan - ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── filters: [=(numbers.number (#0), CAST(if(is_not_null(COUNT(*) (#4)), COUNT(*) (#4), 0) AS BIGINT UNSIGNED))] + └── HashJoin + ├── join type: SINGLE + ├── build keys: [number (#6)] + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── Project(Build) + │ ├── columns: [COUNT(*) (#4), number (#6)] + │ └── EvalScalar + │ ├── expressions: [COUNT(*) (#5)] + │ └── AggregateFinal + │ ├── group by: [number] + │ ├── aggregate functions: [count()] + │ └── AggregatePartial + │ ├── group by: [number] + │ ├── aggregate functions: [count()] + │ └── HashJoin + │ ├── join type: INNER + │ ├── build keys: [numbers.number (#2)] + │ ├── probe keys: [number (#6)] + │ ├── filters: [] + │ ├── HashJoin(Build) + │ │ ├── join type: CROSS + │ │ ├── build keys: [] + │ │ ├── probe keys: [] + │ │ ├── filters: [] + │ │ ├── TableScan(Build) + │ │ │ ├── table: default.system.numbers + │ │ │ ├── read rows: 1 + │ │ │ ├── read bytes: 8 + │ │ │ ├── partitions total: 1 + │ │ │ ├── partitions scanned: 1 + │ │ │ └── push downs: [filters: [], limit: NONE] + │ │ └── TableScan(Probe) + │ │ ├── table: default.system.numbers + │ │ ├── read rows: 1 + │ │ ├── read bytes: 8 + │ │ ├── partitions total: 1 + │ │ ├── partitions scanned: 1 + │ │ └── push downs: [filters: [], limit: NONE] + │ └── TableScan(Probe) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── HashJoin(Probe) + ├── join type: CROSS + ├── build keys: [] + ├── probe keys: [] + ├── filters: [] + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select * from (select count(t1.number) as c1 from numbers(1) as t1 group by number) as t3 left join (select count(t.number) as c from numbers(2) as t group by number) as t4 on t3.c1=t4.c order by t3.c1 limit 1; ---- Limit -├── limit: [1] -├── offset: [0] +├── limit: 1 +├── offset: 0 └── Sort - ├── sort keys: [c1 (#1) ASC] - ├── limit: [1] - └── HashJoin: LEFT OUTER - ├── build keys: [t4.c (#5)] - ├── probe keys: [CAST(t3.c1 (#1) AS BIGINT UNSIGNED NULL)] - ├── other filters: [] - ├── Project - │ ├── projections: [c1 (#1)] + ├── sort keys: [c1 ASC NULLS LAST] + └── HashJoin + ├── join type: LEFT OUTER + ├── build keys: [c (#5)] + ├── probe keys: [CAST(c1 (#1) AS BIGINT UNSIGNED NULL)] + ├── filters: [] + ├── Project(Build) + │ ├── columns: [c (#5)] │ └── EvalScalar - │ ├── scalars: [count(t1.number) (#3)] - │ └── Aggregate(Final) - │ ├── group items: [t1.number (#0)] - │ ├── aggregate functions: [count(t1.number)] - │ └── Aggregate(Partial) - │ ├── group items: [t1.number (#0)] - │ ├── aggregate functions: [count(t1.number)] - │ └── PhysicalScan + │ ├── expressions: [count(t.number) (#7)] + │ └── AggregateFinal + │ ├── group by: [number] + │ ├── aggregate functions: [count(number)] + │ └── AggregatePartial + │ ├── group by: [number] + │ ├── aggregate functions: [count(number)] + │ └── TableScan │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Project - ├── projections: [c (#5)] + │ ├── read rows: 2 + │ ├── read bytes: 16 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Project(Probe) + ├── columns: [c1 (#1)] └── EvalScalar - ├── scalars: [count(t.number) (#7)] - └── Aggregate(Final) - ├── group items: [t.number (#4)] - ├── aggregate functions: [count(t.number)] - └── Aggregate(Partial) - ├── group items: [t.number (#4)] - ├── aggregate functions: [count(t.number)] - └── PhysicalScan + ├── expressions: [count(t1.number) (#3)] + └── AggregateFinal + ├── group by: [number] + ├── aggregate functions: [count(number)] + └── AggregatePartial + ├── group by: [number] + ├── aggregate functions: [count(number)] + └── TableScan ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] diff --git a/tests/logictest/suites/mode/standalone/explain/prune_column.test b/tests/logictest/suites/mode/standalone/explain/prune_column.test index 195f1f18ddbd0..7296fc31f9061 100644 --- a/tests/logictest/suites/mode/standalone/explain/prune_column.test +++ b/tests/logictest/suites/mode/standalone/explain/prune_column.test @@ -3,196 +3,220 @@ explain select * from (select a from (select number as a, number + 1 as b from n ---- Project -├── projections: [number (#0)] -└── PhysicalScan +├── columns: [number (#0)] +└── TableScan ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select a from (select number as a, count(*) as b from numbers(1) group by a); ---- Project -├── projections: [number (#0)] -└── Aggregate(Final) - ├── group items: [numbers.number (#0)] +├── columns: [number (#0)] +└── AggregateFinal + ├── group by: [number] ├── aggregate functions: [] - └── Aggregate(Partial) - ├── group items: [numbers.number (#0)] + └── AggregatePartial + ├── group by: [number] ├── aggregate functions: [] - └── PhysicalScan + └── TableScan ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select a from (select number as a, number b, sum(number) as c, number as d, number as e from numbers(1) group by a, b, d, e) where b > 1 order by d limit 1; ---- Project -├── projections: [number (#0)] +├── columns: [number (#0)] └── Limit - ├── limit: [1] - ├── offset: [0] + ├── limit: 1 + ├── offset: 0 └── Sort - ├── sort keys: [number (#0) ASC] - ├── limit: [1] + ├── sort keys: [number ASC NULLS LAST] └── Project - ├── projections: [number (#0)] + ├── columns: [number (#0)] └── Filter - ├── filters: [numbers.b (#0) > 1] - └── Aggregate(Final) - ├── group items: [numbers.number (#0), numbers.number (#0), numbers.number (#0), numbers.number (#0)] + ├── filters: [>(numbers.number (#0), 1)] + └── AggregateFinal + ├── group by: [number, number, number, number] ├── aggregate functions: [] - └── Aggregate(Partial) - ├── group items: [numbers.number (#0), numbers.number (#0), numbers.number (#0), numbers.number (#0)] + └── AggregatePartial + ├── group by: [number, number, number, number] ├── aggregate functions: [] - └── PhysicalScan + └── TableScan ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select * from (select t1.a from (select number + 1 as a, number + 1 as b, number + 1 as c, number + 1 as d from numbers(1)) as t1, (select number + 1 as a, number + 1 as b, number + 1 as c from numbers(1)) as t2 where t1.b = t2.b and t1.c = 1); ---- Project -├── projections: [a (#1)] -└── HashJoin: INNER - ├── build keys: [t2.b (#11)] - ├── probe keys: [t1.b (#2)] - ├── other filters: [] - ├── Project - │ ├── projections: [a (#1),b (#2)] - │ └── Filter - │ ├── filters: [t1.c (#3) = 1] - │ └── EvalScalar - │ ├── scalars: [+(numbers.number (#0), 1), +(numbers.number (#0), 1), +(numbers.number (#0), 1)] - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Project - ├── projections: [b (#11)] - └── EvalScalar - ├── scalars: [+(numbers.number (#9), 1)] - └── PhysicalScan - ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE +├── columns: [a (#1)] +└── HashJoin + ├── join type: INNER + ├── build keys: [b (#11)] + ├── probe keys: [b (#2)] + ├── filters: [] + ├── Project(Build) + │ ├── columns: [b (#11)] + │ └── EvalScalar + │ ├── expressions: [+(numbers.number (#9), 1)] + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Project(Probe) + ├── columns: [a (#1), b (#2)] + └── Filter + ├── filters: [=(c (#3), 1)] + └── EvalScalar + ├── expressions: [+(numbers.number (#0), 1), +(numbers.number (#0), 1), +(numbers.number (#0), 1)] + └── TableScan + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t1.a from (select number + 1 as a, number + 1 as b from numbers(1)) as t1 where t1.a = (select count(*) from (select t2.a, t3.a from (select number + 1 as a, number + 1 as b, number + 1 as c, number + 1 as d from numbers(1)) as t2, (select number + 1 as a, number + 1 as b, number + 1 as c from numbers(1)) as t3 where t2.b = t3.b and t2.c = 1)); ---- Project -├── projections: [a (#1)] +├── columns: [a (#1)] └── Filter - ├── filters: [t1.a (#1) = scalar_subquery_21 (#21)] - └── HashJoin: SINGLE + ├── filters: [=(a (#1), COUNT(*) (#21))] + └── HashJoin + ├── join type: SINGLE ├── build keys: [] ├── probe keys: [] - ├── other filters: [] - ├── Project - │ ├── projections: [a (#1)] + ├── filters: [] + ├── Project(Build) + │ ├── columns: [COUNT(*) (#21)] │ └── EvalScalar - │ ├── scalars: [+(numbers.number (#0), 1)] - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Project - ├── projections: [COUNT(*) (#21)] + │ ├── expressions: [COUNT(*) (#22)] + │ └── AggregateFinal + │ ├── group by: [] + │ ├── aggregate functions: [count()] + │ └── AggregatePartial + │ ├── group by: [] + │ ├── aggregate functions: [count()] + │ └── Project + │ ├── columns: [a (#6)] + │ └── HashJoin + │ ├── join type: INNER + │ ├── build keys: [b (#16)] + │ ├── probe keys: [b (#7)] + │ ├── filters: [] + │ ├── Project(Build) + │ │ ├── columns: [b (#16)] + │ │ └── EvalScalar + │ │ ├── expressions: [+(numbers.number (#14), 1)] + │ │ └── TableScan + │ │ ├── table: default.system.numbers + │ │ ├── read rows: 1 + │ │ ├── read bytes: 8 + │ │ ├── partitions total: 1 + │ │ ├── partitions scanned: 1 + │ │ └── push downs: [filters: [], limit: NONE] + │ └── Project(Probe) + │ ├── columns: [a (#6), b (#7)] + │ └── Filter + │ ├── filters: [=(c (#8), 1)] + │ └── EvalScalar + │ ├── expressions: [+(numbers.number (#5), 1), +(numbers.number (#5), 1), +(numbers.number (#5), 1)] + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Project(Probe) + ├── columns: [a (#1)] └── EvalScalar - ├── scalars: [COUNT(*) (#22)] - └── Aggregate(Final) - ├── group items: [] - ├── aggregate functions: [COUNT(*)] - └── Aggregate(Partial) - ├── group items: [] - ├── aggregate functions: [COUNT(*)] - └── Project - ├── projections: [a (#6)] - └── HashJoin: INNER - ├── build keys: [t3.b (#16)] - ├── probe keys: [t2.b (#7)] - ├── other filters: [] - ├── Project - │ ├── projections: [a (#6),b (#7)] - │ └── Filter - │ ├── filters: [t2.c (#8) = 1] - │ └── EvalScalar - │ ├── scalars: [+(numbers.number (#5), 1), +(numbers.number (#5), 1), +(numbers.number (#5), 1)] - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Project - ├── projections: [b (#16)] - └── EvalScalar - ├── scalars: [+(numbers.number (#14), 1)] - └── PhysicalScan - ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── expressions: [+(numbers.number (#0), 1)] + └── TableScan + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select name from system.functions order by example; ---- Project -├── projections: [name (#0)] +├── columns: [name (#0)] └── Sort - ├── sort keys: [example (#7) ASC] - ├── limit: [NONE] - └── PhysicalScan + ├── sort keys: [example ASC NULLS LAST] + └── TableScan ├── table: default.system.functions - ├── filters: [] - ├── order by: [example (#7) ASC] - └── limit: NONE + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(10) t where exists(select * from numbers(10)); ---- Project -├── projections: [number (#0)] -└── CrossJoin +├── columns: [number (#0)] +└── HashJoin + ├── join type: CROSS ├── build keys: [] ├── probe keys: [] - ├── other filters: [] - ├── Project - │ ├── projections: [subquery (#3)] - │ └── Filter - │ ├── filters: [subquery_3 (#3)] - │ └── EvalScalar - │ ├── scalars: [count(*) (#2) = 1] - │ └── Aggregate(Final) - │ ├── group items: [] - │ ├── aggregate functions: [count(*)] - │ └── Aggregate(Partial) - │ ├── group items: [] - │ ├── aggregate functions: [count(*)] - │ └── Limit - │ ├── limit: [1] - │ ├── offset: [0] - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: 1 - └── PhysicalScan - ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── filters: [] + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 10 + │ ├── read bytes: 80 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Project(Probe) + ├── columns: [subquery (#3)] + └── Filter + ├── filters: [subquery (#3)] + └── EvalScalar + ├── expressions: [=(count(*) (#2), 1)] + └── AggregateFinal + ├── group by: [] + ├── aggregate functions: [count()] + └── AggregatePartial + ├── group by: [] + ├── aggregate functions: [count()] + └── Limit + ├── limit: 1 + ├── offset: 0 + └── TableScan + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: 1] diff --git a/tests/logictest/suites/mode/standalone/explain/select.test b/tests/logictest/suites/mode/standalone/explain/select.test index 50fdf55dcde71..b37e717d41b57 100644 --- a/tests/logictest/suites/mode/standalone/explain/select.test +++ b/tests/logictest/suites/mode/standalone/explain/select.test @@ -2,73 +2,85 @@ statement query T explain select * from numbers(1); ---- -PhysicalScan +TableScan ├── table: default.system.numbers -├── filters: [] -├── order by: [] -└── limit: NONE +├── read rows: 1 +├── read bytes: 8 +├── partitions total: 1 +├── partitions scanned: 1 +└── push downs: [filters: [], limit: NONE] statement query T explain select * from (select * from numbers(1)) as t1 where number = 1; ---- Filter -├── filters: [t1.number (#0) = 1] -└── PhysicalScan +├── filters: [=(numbers.number (#0), 1)] +└── TableScan ├── table: default.system.numbers - ├── filters: [t1.number (#0) = 1] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [(number = 1)], limit: NONE] statement query T explain select * from (select number as a, number + 1 as b from numbers(1)) as t1 where a = 1 and b = 1; ---- Filter -├── filters: [t1.a (#0) = 1, t1.b (#1) = 1] +├── filters: [=(numbers.number (#0), 1), =(b (#1), 1)] └── EvalScalar - ├── scalars: [+(numbers.number (#0), 1)] - └── PhysicalScan + ├── expressions: [+(numbers.number (#0), 1)] + └── TableScan ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select * from (select number as a, number + 1 as b from numbers(1)) as t1 where a = 1; ---- EvalScalar -├── scalars: [+(numbers.number (#0), 1)] +├── expressions: [+(numbers.number (#0), 1)] └── Filter - ├── filters: [t1.a (#0) = 1] - └── PhysicalScan + ├── filters: [=(numbers.number (#0), 1)] + └── TableScan ├── table: default.system.numbers - ├── filters: [t1.a (#0) = 1] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [(number = 1)], limit: NONE] statement query T explain select * from numbers(1) where number = pow(1, 1 + 1); ---- Filter -├── filters: [numbers.number (#0) = 1] -└── PhysicalScan +├── filters: [=(numbers.number (#0), 1)] +└── TableScan ├── table: default.system.numbers - ├── filters: [numbers.number (#0) = 1] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [(number = '1.0')], limit: NONE] statement query T explain select * from numbers(1) where TRUE and 1 = 1; ---- -PhysicalScan +TableScan ├── table: default.system.numbers -├── filters: [] -├── order by: [] -└── limit: NONE +├── read rows: 1 +├── read bytes: 8 +├── partitions total: 1 +├── partitions scanned: 1 +└── push downs: [filters: [], limit: NONE] statement query T explain select * from numbers(1) where number = 0 and false; @@ -76,11 +88,13 @@ explain select * from numbers(1) where number = 0 and false; ---- Filter ├── filters: [false] -└── PhysicalScan +└── TableScan ├── table: default.system.numbers - ├── filters: [false] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [false], limit: NONE] statement query T explain select * from numbers(1) where number = 0 and null; @@ -88,11 +102,13 @@ explain select * from numbers(1) where number = 0 and null; ---- Filter ├── filters: [false] -└── PhysicalScan +└── TableScan ├── table: default.system.numbers - ├── filters: [false] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [false], limit: NONE] statement query T explain select * from numbers(1) where null; @@ -100,31 +116,37 @@ explain select * from numbers(1) where null; ---- Filter ├── filters: [NULL] -└── PhysicalScan +└── TableScan ├── table: default.system.numbers - ├── filters: [NULL] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [NULL], limit: NONE] statement query T explain select a from (select number as a, number as b from numbers(1)); ---- -PhysicalScan +TableScan ├── table: default.system.numbers -├── filters: [] -├── order by: [] -└── limit: NONE +├── read rows: 1 +├── read bytes: 8 +├── partitions total: 1 +├── partitions scanned: 1 +└── push downs: [filters: [], limit: NONE] statement query T explain select a from (select number as a, number+1 as b from numbers(1)); ---- Project -├── projections: [number (#0)] -└── PhysicalScan +├── columns: [number (#0)] +└── TableScan ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] diff --git a/tests/logictest/suites/base/03_dml/03_0010_select_limit_offset b/tests/logictest/suites/mode/standalone/explain/select_limit_offset.test similarity index 64% rename from tests/logictest/suites/base/03_dml/03_0010_select_limit_offset rename to tests/logictest/suites/mode/standalone/explain/select_limit_offset.test index e01e0573205c0..aff4f2d66ac9a 100644 --- a/tests/logictest/suites/base/03_dml/03_0010_select_limit_offset +++ b/tests/logictest/suites/mode/standalone/explain/select_limit_offset.test @@ -137,28 +137,33 @@ explain select * from t left join t1 on t.a = t1.a limit 1,2; ---- Limit -├── limit: [2] -├── offset: [1] -└── HashJoin: LEFT OUTER +├── limit: 2 +├── offset: 1 +└── HashJoin + ├── join type: LEFT OUTER ├── build keys: [t1.a (#1)] ├── probe keys: [CAST(t.a (#0) AS INT NULL)] - ├── other filters: [] - ├── Limit - │ ├── limit: [2] - │ ├── offset: [1] - │ └── PhysicalScan - │ ├── table: default.default.t - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: 3 - └── Limit - ├── limit: [2] - ├── offset: [1] - └── PhysicalScan - ├── table: default.default.t1 - ├── filters: [] - ├── order by: [] - └── limit: 3 + ├── filters: [] + ├── Limit(Build) + │ ├── limit: 2 + │ ├── offset: 1 + │ └── TableScan + │ ├── table: default.default.t1 + │ ├── read rows: 2 + │ ├── read bytes: 31 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: 3] + └── Limit(Probe) + ├── limit: 2 + ├── offset: 1 + └── TableScan + ├── table: default.default.t + ├── read rows: 1 + ├── read bytes: 27 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: 3] statement query II select * from t left join t1 on t.a = t1.a limit 1; @@ -171,3 +176,4 @@ drop table if exists t; statement ok drop table if exists t1; + diff --git a/tests/logictest/suites/mode/standalone/explain/subquery.test b/tests/logictest/suites/mode/standalone/explain/subquery.test index 5fe69eacace78..3aa3068a365f5 100644 --- a/tests/logictest/suites/mode/standalone/explain/subquery.test +++ b/tests/logictest/suites/mode/standalone/explain/subquery.test @@ -3,388 +3,475 @@ explain select t.number from numbers(1) as t, numbers(1) as t1 where t.number = ---- Project -├── projections: [number (#0)] +├── columns: [number (#0)] └── Filter - ├── filters: [t.number (#0) = CAST(if(is_not_null(scalar_subquery_4 (#4)), scalar_subquery_4 (#4), 0) AS BIGINT UNSIGNED)] - └── HashJoin: SINGLE - ├── build keys: [subquery_6 (#6)] - ├── probe keys: [subquery_0 (#0)] - ├── other filters: [] - ├── CrossJoin - │ ├── build keys: [] - │ ├── probe keys: [] - │ ├── other filters: [] - │ ├── PhysicalScan - │ │ ├── table: default.system.numbers - │ │ ├── filters: [] - │ │ ├── order by: [] - │ │ └── limit: NONE - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Project - ├── projections: [COUNT(*) (#4),number (#6)] - └── EvalScalar - ├── scalars: [COUNT(*) (#5)] - └── Aggregate(Final) - ├── group items: [subquery_6 (#6)] - ├── aggregate functions: [COUNT(*)] - └── Aggregate(Partial) - ├── group items: [subquery_6 (#6)] - ├── aggregate functions: [COUNT(*)] - └── HashJoin: INNER - ├── build keys: [t2.number (#2)] - ├── probe keys: [subquery_6 (#6)] - ├── other filters: [] - ├── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── CrossJoin - ├── build keys: [] - ├── probe keys: [] - ├── other filters: [] - ├── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan - ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── filters: [=(numbers.number (#0), CAST(if(is_not_null(COUNT(*) (#4)), COUNT(*) (#4), 0) AS BIGINT UNSIGNED))] + └── HashJoin + ├── join type: SINGLE + ├── build keys: [number (#6)] + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── Project(Build) + │ ├── columns: [COUNT(*) (#4), number (#6)] + │ └── EvalScalar + │ ├── expressions: [COUNT(*) (#5)] + │ └── AggregateFinal + │ ├── group by: [number] + │ ├── aggregate functions: [count()] + │ └── AggregatePartial + │ ├── group by: [number] + │ ├── aggregate functions: [count()] + │ └── HashJoin + │ ├── join type: INNER + │ ├── build keys: [numbers.number (#2)] + │ ├── probe keys: [number (#6)] + │ ├── filters: [] + │ ├── HashJoin(Build) + │ │ ├── join type: CROSS + │ │ ├── build keys: [] + │ │ ├── probe keys: [] + │ │ ├── filters: [] + │ │ ├── TableScan(Build) + │ │ │ ├── table: default.system.numbers + │ │ │ ├── read rows: 1 + │ │ │ ├── read bytes: 8 + │ │ │ ├── partitions total: 1 + │ │ │ ├── partitions scanned: 1 + │ │ │ └── push downs: [filters: [], limit: NONE] + │ │ └── TableScan(Probe) + │ │ ├── table: default.system.numbers + │ │ ├── read rows: 1 + │ │ ├── read bytes: 8 + │ │ ├── partitions total: 1 + │ │ ├── partitions scanned: 1 + │ │ └── push downs: [filters: [], limit: NONE] + │ └── TableScan(Probe) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── HashJoin(Probe) + ├── join type: CROSS + ├── build keys: [] + ├── probe keys: [] + ├── filters: [] + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t where exists (select t1.number from numbers(1) as t1 where t.number = t1.number) or t.number > 1; ---- Project -├── projections: [number (#0)] +├── columns: [number (#0)] └── Filter - ├── filters: [(3 (#3)) OR (t.number (#0) > 1)] - └── HashJoin: MARK - ├── build keys: [subquery_0 (#0)] - ├── probe keys: [subquery_2 (#2)] - ├── other filters: [] - ├── HashJoin: INNER - │ ├── build keys: [t1.number (#1)] - │ ├── probe keys: [subquery_2 (#2)] - │ ├── other filters: [] - │ ├── PhysicalScan - │ │ ├── table: default.system.numbers - │ │ ├── filters: [] - │ │ ├── order by: [] - │ │ └── limit: NONE - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan - ├── table: default.system.numbers + ├── filters: [or(marker (#3), >(numbers.number (#0), 1))] + └── HashJoin + ├── join type: MARK + ├── build keys: [numbers.number (#0)] + ├── probe keys: [number (#2)] + ├── filters: [] + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── HashJoin(Probe) + ├── join type: INNER + ├── build keys: [numbers.number (#1)] + ├── probe keys: [number (#2)] ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t where exists (select * from numbers(1) where number = 0); ---- Project -├── projections: [number (#0)] -└── CrossJoin +├── columns: [number (#0)] +└── HashJoin + ├── join type: CROSS ├── build keys: [] ├── probe keys: [] - ├── other filters: [] - ├── Project - │ ├── projections: [subquery (#3)] - │ └── Filter - │ ├── filters: [subquery_3 (#3)] - │ └── EvalScalar - │ ├── scalars: [count(*) (#2) = 1] - │ └── Aggregate(Final) - │ ├── group items: [] - │ ├── aggregate functions: [count(*)] - │ └── Aggregate(Partial) - │ ├── group items: [] - │ ├── aggregate functions: [count(*)] - │ └── Limit - │ ├── limit: [1] - │ ├── offset: [0] - │ └── Filter - │ ├── filters: [numbers.number (#1) = 0] - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [numbers.number (#1) = 0] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan - ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── filters: [] + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Project(Probe) + ├── columns: [subquery (#3)] + └── Filter + ├── filters: [subquery (#3)] + └── EvalScalar + ├── expressions: [=(count(*) (#2), 1)] + └── AggregateFinal + ├── group by: [] + ├── aggregate functions: [count()] + └── AggregatePartial + ├── group by: [] + ├── aggregate functions: [count()] + └── Limit + ├── limit: 1 + ├── offset: 0 + └── Filter + ├── filters: [=(numbers.number (#1), 0)] + └── TableScan + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [(number = 0)], limit: NONE] statement query T explain select t.number from numbers(1) as t where number = (select * from numbers(1) where number = 0); ---- Project -├── projections: [number (#0)] +├── columns: [number (#0)] └── Filter - ├── filters: [t.number (#0) = scalar_subquery_1 (#1)] - └── HashJoin: SINGLE + ├── filters: [=(numbers.number (#0), numbers.number (#1))] + └── HashJoin + ├── join type: SINGLE ├── build keys: [] ├── probe keys: [] - ├── other filters: [] - ├── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Filter - ├── filters: [numbers.number (#1) = 0] - └── PhysicalScan - ├── table: default.system.numbers - ├── filters: [numbers.number (#1) = 0] - ├── order by: [] - └── limit: NONE + ├── filters: [] + ├── Filter(Build) + │ ├── filters: [=(numbers.number (#1), 0)] + │ └── TableScan + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [(number = 0)], limit: NONE] + └── TableScan(Probe) + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t where exists (select * from numbers(1) where number = t.number); ---- Project -├── projections: [number (#0)] -└── HashJoin: SEMI +├── columns: [number (#0)] +└── HashJoin + ├── join type: SEMI ├── build keys: [numbers.number (#1)] - ├── probe keys: [t.number (#0)] - ├── other filters: [] - ├── PhysicalScan + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── TableScan(Build) │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t where not exists (select * from numbers(1) where number = t.number); ---- Project -├── projections: [number (#0)] +├── columns: [number (#0)] └── Filter - ├── filters: [not(3 (#3))] - └── HashJoin: MARK - ├── build keys: [subquery_0 (#0)] - ├── probe keys: [subquery_2 (#2)] - ├── other filters: [] - ├── HashJoin: INNER - │ ├── build keys: [numbers.number (#1)] - │ ├── probe keys: [subquery_2 (#2)] - │ ├── other filters: [] - │ ├── PhysicalScan - │ │ ├── table: default.system.numbers - │ │ ├── filters: [] - │ │ ├── order by: [] - │ │ └── limit: NONE - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan - ├── table: default.system.numbers + ├── filters: [not(marker (#3))] + └── HashJoin + ├── join type: MARK + ├── build keys: [numbers.number (#0)] + ├── probe keys: [number (#2)] + ├── filters: [] + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── HashJoin(Probe) + ├── join type: INNER + ├── build keys: [numbers.number (#1)] + ├── probe keys: [number (#2)] ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select * from numbers(1) as t where exists (select number as a from numbers(1) where number = t.number); ---- Project -├── projections: [number (#0)] -└── HashJoin: SEMI +├── columns: [number (#0)] +└── HashJoin + ├── join type: SEMI ├── build keys: [numbers.number (#1)] - ├── probe keys: [t.number (#0)] - ├── other filters: [] - ├── PhysicalScan + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── TableScan(Build) │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t where exists (select * from numbers(1) where number = t.number and number = 0 and t.number < 10); ---- Project -├── projections: [number (#0)] -└── HashJoin: SEMI +├── columns: [number (#0)] +└── HashJoin + ├── join type: SEMI ├── build keys: [numbers.number (#1)] - ├── probe keys: [t.number (#0)] - ├── other filters: [] - ├── Filter - │ ├── filters: [t.number (#0) < 10] - │ └── PhysicalScan + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── Filter(Build) + │ ├── filters: [=(numbers.number (#1), 0)] + │ └── TableScan │ ├── table: default.system.numbers - │ ├── filters: [t.number (#0) < 10] - │ ├── order by: [] - │ └── limit: NONE - └── Filter - ├── filters: [numbers.number (#1) = 0] - └── PhysicalScan + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [(number = 0)], limit: NONE] + └── Filter(Probe) + ├── filters: [<(numbers.number (#0), 10)] + └── TableScan ├── table: default.system.numbers - ├── filters: [numbers.number (#1) = 0] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [(number < 10)], limit: NONE] statement query T explain select t.number from numbers(1) as t where exists (select * from numbers(1) where number = t.number and t.number < number); ---- Project -├── projections: [number (#0)] -└── HashJoin: SEMI +├── columns: [number (#0)] +└── HashJoin + ├── join type: SEMI ├── build keys: [numbers.number (#1)] - ├── probe keys: [t.number (#0)] - ├── other filters: [t.number (#0) < numbers.number (#1)] - ├── PhysicalScan + ├── probe keys: [numbers.number (#0)] + ├── filters: [<(numbers.number (#0), numbers.number (#1))] + ├── TableScan(Build) │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t where exists (select number as a, number as b, number as c from numbers(1) where number = t.number); ---- Project -├── projections: [number (#0)] -└── HashJoin: SEMI +├── columns: [number (#0)] +└── HashJoin + ├── join type: SEMI ├── build keys: [numbers.number (#1)] - ├── probe keys: [t.number (#0)] - ├── other filters: [] - ├── PhysicalScan + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── TableScan(Build) │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t, numbers(1) as t1 where (select count(*) = 1 from numbers(1) where t.number = number) and t.number = t1.number; ---- Project -├── projections: [number (#0)] +├── columns: [number (#0)] └── Filter - ├── filters: [CAST(if(is_not_null(scalar_subquery_3 (#3)), scalar_subquery_3 (#3), 0) AS BIGINT UNSIGNED)] - └── HashJoin: SINGLE - ├── build keys: [subquery_5 (#5)] - ├── probe keys: [subquery_0 (#0)] - ├── other filters: [] - ├── HashJoin: INNER - │ ├── build keys: [t1.number (#1)] - │ ├── probe keys: [t.number (#0)] - │ ├── other filters: [] - │ ├── PhysicalScan - │ │ ├── table: default.system.numbers - │ │ ├── filters: [] - │ │ ├── order by: [] - │ │ └── limit: NONE - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── Project - ├── projections: [COUNT(*) = 1 (#3),number (#5)] - └── EvalScalar - ├── scalars: [COUNT(*) (#4) = 1] - └── Aggregate(Final) - ├── group items: [subquery_5 (#5)] - ├── aggregate functions: [COUNT(*)] - └── Aggregate(Partial) - ├── group items: [subquery_5 (#5)] - ├── aggregate functions: [COUNT(*)] - └── HashJoin: INNER - ├── build keys: [numbers.number (#2)] - ├── probe keys: [subquery_5 (#5)] - ├── other filters: [] - ├── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan - ├── table: default.system.numbers - ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── filters: [CAST(if(is_not_null(COUNT(*) = 1 (#3)), COUNT(*) = 1 (#3), 0) AS BIGINT UNSIGNED)] + └── HashJoin + ├── join type: SINGLE + ├── build keys: [number (#5)] + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── Project(Build) + │ ├── columns: [COUNT(*) = 1 (#3), number (#5)] + │ └── EvalScalar + │ ├── expressions: [=(COUNT(*) (#4), 1)] + │ └── AggregateFinal + │ ├── group by: [number] + │ ├── aggregate functions: [count()] + │ └── AggregatePartial + │ ├── group by: [number] + │ ├── aggregate functions: [count()] + │ └── HashJoin + │ ├── join type: INNER + │ ├── build keys: [numbers.number (#2)] + │ ├── probe keys: [number (#5)] + │ ├── filters: [] + │ ├── TableScan(Build) + │ │ ├── table: default.system.numbers + │ │ ├── read rows: 1 + │ │ ├── read bytes: 8 + │ │ ├── partitions total: 1 + │ │ ├── partitions scanned: 1 + │ │ └── push downs: [filters: [], limit: NONE] + │ └── TableScan(Probe) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── HashJoin(Probe) + ├── join type: INNER + ├── build keys: [numbers.number (#1)] + ├── probe keys: [numbers.number (#0)] + ├── filters: [] + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] statement query T explain select t.number from numbers(1) as t where exists(select * from numbers(1) as t1 where t.number > t1.number) and not exists(select * from numbers(1) as t1 where t.number < t1.number); ---- Project -├── projections: [number (#0)] +├── columns: [number (#0)] └── Filter - ├── filters: [not(4 (#4))] - └── HashJoin: MARK - ├── build keys: [subquery_0 (#0)] - ├── probe keys: [subquery_3 (#3)] - ├── other filters: [] - ├── Filter - │ ├── filters: [subquery_3 (#3) < t1.number (#2)] - │ └── CrossJoin - │ ├── build keys: [] - │ ├── probe keys: [] - │ ├── other filters: [] - │ ├── PhysicalScan - │ │ ├── table: default.system.numbers - │ │ ├── filters: [] - │ │ ├── order by: [] - │ │ └── limit: NONE - │ └── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── HashJoin: SEMI - ├── build keys: [] - ├── probe keys: [] - ├── other filters: [t.number (#0) > t1.number (#1)] - ├── PhysicalScan - │ ├── table: default.system.numbers - │ ├── filters: [] - │ ├── order by: [] - │ └── limit: NONE - └── PhysicalScan - ├── table: default.system.numbers + ├── filters: [not(marker (#4))] + └── HashJoin + ├── join type: MARK + ├── build keys: [numbers.number (#0)] + ├── probe keys: [number (#3)] + ├── filters: [] + ├── HashJoin(Build) + │ ├── join type: SEMI + │ ├── build keys: [] + │ ├── probe keys: [] + │ ├── filters: [>(numbers.number (#0), numbers.number (#1))] + │ ├── TableScan(Build) + │ │ ├── table: default.system.numbers + │ │ ├── read rows: 1 + │ │ ├── read bytes: 8 + │ │ ├── partitions total: 1 + │ │ ├── partitions scanned: 1 + │ │ └── push downs: [filters: [], limit: NONE] + │ └── TableScan(Probe) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── Filter(Probe) + ├── filters: [<(number (#3), numbers.number (#2))] + └── HashJoin + ├── join type: CROSS + ├── build keys: [] + ├── probe keys: [] ├── filters: [] - ├── order by: [] - └── limit: NONE + ├── TableScan(Build) + │ ├── table: default.system.numbers + │ ├── read rows: 1 + │ ├── read bytes: 8 + │ ├── partitions total: 1 + │ ├── partitions scanned: 1 + │ └── push downs: [filters: [], limit: NONE] + └── TableScan(Probe) + ├── table: default.system.numbers + ├── read rows: 1 + ├── read bytes: 8 + ├── partitions total: 1 + ├── partitions scanned: 1 + └── push downs: [filters: [], limit: NONE] diff --git a/tests/logictest/suites/mode/standalone/explain/where_optimizaiton.test b/tests/logictest/suites/mode/standalone/explain/where_optimizaiton.test index 86e5edcd6a4dd..0df7fd526e0cf 100644 --- a/tests/logictest/suites/mode/standalone/explain/where_optimizaiton.test +++ b/tests/logictest/suites/mode/standalone/explain/where_optimizaiton.test @@ -4,85 +4,91 @@ drop table if exists t_where_optimizer; statement ok create table if not exists t_where_optimizer (a int, b int); --- SQLs that prewhere optimization will not be applied. - statement query T explain select a from t_where_optimizer where a = 1; ---- Project -├── projections: [a (#0)] +├── columns: [a (#0)] └── Filter - ├── filters: [t_where_optimizer.a (#0) = 1] - └── PhysicalScan + ├── filters: [=(t_where_optimizer.a (#0), 1)] + └── TableScan ├── table: default.default.t_where_optimizer - ├── filters: [t_where_optimizer.a (#0) = 1] - ├── order by: [] - └── limit: NONE + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [(a = 1)], limit: NONE] statement query T explain select * from t_where_optimizer where a = b; ---- Filter - ├── filters: [t_where_optimizer.a (#0) = t_where_optimizer.b (#1)] - └── PhysicalScan - ├── table: default.default.t_where_optimizer - ├── filters: [t_where_optimizer.a (#0) = t_where_optimizer.b (#1)] - ├── order by: [] - └── limit: NONE +├── filters: [=(t_where_optimizer.a (#0), t_where_optimizer.b (#1))] +└── TableScan + ├── table: default.default.t_where_optimizer + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [(a = b)], limit: NONE] statement query T explain select * from t_where_optimizer where a = 1 or b > 2; ---- Filter - ├── filters: [(t_where_optimizer.a (#0) = 1) OR (t_where_optimizer.b (#1) > 2)] - └── PhysicalScan - ├── table: default.default.t_where_optimizer - ├── filters: [(t_where_optimizer.a (#0) = 1) OR (t_where_optimizer.b (#1) > 2)] - ├── order by: [] - └── limit: NONE - --- SQLs that prewhere optimization will be applied. +├── filters: [or(=(t_where_optimizer.a (#0), 1), >(t_where_optimizer.b (#1), 2))] +└── TableScan + ├── table: default.default.t_where_optimizer + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [((a = 1) or (b > 2))], limit: NONE] statement query T explain select * from t_where_optimizer where a = 1 and b > 2; ---- -PhysicalScan - ├── table: default.default.t_where_optimizer - ├── filters: [t_where_optimizer.a (#0) = 1, t_where_optimizer.b (#1) > 2] - ├── order by: [] - └── limit: NONE +TableScan +├── table: default.default.t_where_optimizer +├── read rows: 0 +├── read bytes: 0 +├── partitions total: 0 +├── partitions scanned: 0 +└── push downs: [filters: [(a = 1), (b > 2)], limit: NONE] statement query T explain select * from t_where_optimizer where b = 1; ---- -PhysicalScan - ├── table: default.default.t_where_optimizer - ├── filters: [t_where_optimizer.b (#1) = 1] - ├── order by: [] - └── limit: NONE +TableScan +├── table: default.default.t_where_optimizer +├── read rows: 0 +├── read bytes: 0 +├── partitions total: 0 +├── partitions scanned: 0 +└── push downs: [filters: [(b = 1)], limit: NONE] statement query T explain select a from t_where_optimizer where b = 1; ---- Project -├── projections: [a (#0)] -└── PhysicalScan +├── columns: [a (#0)] +└── TableScan ├── table: default.default.t_where_optimizer - ├── filters: [t_where_optimizer.b (#1) = 1] - ├── order by: [] - └── limit: NONE + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [(b = 1)], limit: NONE] statement ok drop table t_where_optimizer; --- Tuple (inner column) type test - statement ok create table t_where_optimizer(id int, s tuple(a int, b int)); @@ -91,12 +97,15 @@ explain select * from t_where_optimizer where s:a > 0; ---- Project -├── projections: [id (#0),s (#1)] -└── PhysicalScan +├── columns: [id (#0), s (#1)] +└── TableScan ├── table: default.default.t_where_optimizer - ├── filters: [t_where_optimizer.s:a (#2) > 0] - ├── order by: [] - └── limit: NONE + ├── read rows: 0 + ├── read bytes: 0 + ├── partitions total: 0 + ├── partitions scanned: 0 + └── push downs: [filters: [(s:a > 0)], limit: NONE] statement ok -drop table t_where_optimizer; \ No newline at end of file +drop table t_where_optimizer; +