use crate::ast::ast::{EdgeDirection, Expression, PathType};
use crate::plan::logical::{AggregateFunction, JoinType, LogicalNode, LogicalPlan, PathElement};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeCreation {
pub storage_id: String,
pub labels: Vec<String>,
pub properties: HashMap<String, Expression>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EdgeCreation {
pub storage_id: String,
pub from_node_id: String,
pub to_node_id: String,
pub label: String,
pub properties: HashMap<String, Expression>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GraphIndexOperation {
FindNeighbors {
start_node: String,
direction: EdgeDirection,
max_hops: Option<usize>,
edge_labels: Vec<String>,
},
ShortestPath {
start_node: String,
end_node: String,
max_length: Option<usize>,
},
IsReachable {
start_node: String,
end_node: String,
max_hops: Option<usize>,
},
PatternMatch {
pattern: String,
max_results: Option<usize>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PhysicalPlan {
pub root: PhysicalNode,
pub estimated_cost: f64,
pub estimated_rows: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PhysicalNode {
NodeSeqScan {
variable: String,
labels: Vec<String>,
properties: Option<HashMap<String, Expression>>,
estimated_rows: usize,
estimated_cost: f64,
},
NodeIndexScan {
variable: String,
labels: Vec<String>,
properties: Option<HashMap<String, Expression>>,
estimated_rows: usize,
estimated_cost: f64,
},
EdgeSeqScan {
variable: String,
labels: Vec<String>,
properties: Option<HashMap<String, Expression>>,
estimated_rows: usize,
estimated_cost: f64,
},
IndexedExpand {
from_variable: String,
edge_variable: Option<String>,
to_variable: String,
edge_labels: Vec<String>,
direction: EdgeDirection,
properties: Option<HashMap<String, Expression>>,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
HashExpand {
from_variable: String,
edge_variable: Option<String>,
to_variable: String,
edge_labels: Vec<String>,
direction: EdgeDirection,
properties: Option<HashMap<String, Expression>>,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
PathTraversal {
path_type: PathType,
from_variable: String,
to_variable: String,
path_elements: Vec<PathElement>,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
Filter {
condition: Expression,
input: Box<PhysicalNode>,
selectivity: f64,
estimated_rows: usize,
estimated_cost: f64,
},
Project {
expressions: Vec<ProjectionItem>,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
HashJoin {
join_type: JoinType,
condition: Option<Expression>,
build_keys: Vec<Expression>,
probe_keys: Vec<Expression>,
build: Box<PhysicalNode>,
probe: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
NestedLoopJoin {
join_type: JoinType,
condition: Option<Expression>,
left: Box<PhysicalNode>,
right: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
SortMergeJoin {
join_type: JoinType,
left_keys: Vec<Expression>,
right_keys: Vec<Expression>,
left: Box<PhysicalNode>,
right: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
UnionAll {
inputs: Vec<PhysicalNode>,
all: bool, estimated_rows: usize,
estimated_cost: f64,
},
Intersect {
left: Box<PhysicalNode>,
right: Box<PhysicalNode>,
all: bool,
estimated_rows: usize,
estimated_cost: f64,
},
Except {
left: Box<PhysicalNode>,
right: Box<PhysicalNode>,
all: bool,
estimated_rows: usize,
estimated_cost: f64,
},
HashAggregate {
group_by: Vec<Expression>,
aggregates: Vec<AggregateItem>,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
SortAggregate {
group_by: Vec<Expression>,
aggregates: Vec<AggregateItem>,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
Having {
condition: Expression,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
ExternalSort {
expressions: Vec<SortItem>,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
InMemorySort {
expressions: Vec<SortItem>,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
Distinct {
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
Limit {
count: usize,
offset: Option<usize>,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
GenericFunction {
function_name: String,
arguments: Vec<Expression>,
input: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
ExistsSubquery {
subplan: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
optimized: bool, },
NotExistsSubquery {
subplan: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
optimized: bool,
},
InSubquery {
expression: Expression,
subplan: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
NotInSubquery {
expression: Expression,
subplan: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
ScalarSubquery {
subplan: Box<PhysicalNode>,
estimated_rows: usize,
estimated_cost: f64,
},
WithQuery {
original_query: Box<crate::ast::ast::WithQuery>,
estimated_rows: usize,
estimated_cost: f64,
},
Unwind {
expression: Expression,
variable: String,
input: Option<Box<PhysicalNode>>,
estimated_rows: usize,
estimated_cost: f64,
},
GraphIndexScan {
index_name: String,
operation: GraphIndexOperation,
parameters: HashMap<String, Expression>,
estimated_rows: usize,
estimated_cost: f64,
},
IndexJoin {
left: Box<PhysicalNode>,
right: Box<PhysicalNode>,
join_type: JoinType,
index_name: String,
join_condition: Expression,
estimated_rows: usize,
estimated_cost: f64,
},
Insert {
node_creations: Vec<NodeCreation>,
edge_creations: Vec<EdgeCreation>,
estimated_ops: usize,
estimated_cost: f64,
},
Update {
target_variable: String,
properties: HashMap<String, Expression>,
input: Box<PhysicalNode>,
estimated_ops: usize,
estimated_cost: f64,
},
Delete {
target_variables: Vec<String>,
detach: bool,
input: Box<PhysicalNode>,
estimated_ops: usize,
estimated_cost: f64,
},
SingleRow {
estimated_rows: usize, estimated_cost: f64, },
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PhysicalOperator {
Scan,
Filter,
Project,
Join,
Aggregate,
Sort,
Limit,
Union,
Subquery,
WithQuery,
Unwind,
GraphIndexScan,
IndexJoin,
Insert,
Update,
Delete,
SingleRow,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectionItem {
pub expression: Expression,
pub alias: Option<String>,
pub output_type: OutputType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AggregateItem {
pub function: AggregateFunction,
pub expression: Expression,
pub alias: Option<String>,
pub output_type: OutputType,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortItem {
pub expression: Expression,
pub ascending: bool,
pub nulls_first: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum OutputType {
String,
Integer,
Float,
Boolean,
DateTime,
Array,
Null,
}
impl PhysicalPlan {
pub fn new(root: PhysicalNode) -> Self {
let estimated_cost = root.get_cost();
let estimated_rows = root.get_row_count();
Self {
root,
estimated_cost,
estimated_rows,
}
}
pub fn from_logical(logical: &LogicalPlan) -> Self {
let root = Self::convert_logical_node(&logical.root);
Self::new(root)
}
fn convert_logical_node(logical: &LogicalNode) -> PhysicalNode {
match logical {
LogicalNode::NodeScan {
variable,
labels,
properties,
} => {
let estimated_rows = 1000; let estimated_cost = estimated_rows as f64 * 0.1;
if labels.is_empty() {
PhysicalNode::NodeSeqScan {
variable: variable.clone(),
labels: labels.clone(),
properties: properties.clone(),
estimated_rows,
estimated_cost,
}
} else {
PhysicalNode::NodeIndexScan {
variable: variable.clone(),
labels: labels.clone(),
properties: properties.clone(),
estimated_rows: estimated_rows / 10, estimated_cost: estimated_cost * 0.5, }
}
}
LogicalNode::EdgeScan {
variable,
labels,
properties,
} => {
let estimated_rows = 5000;
let estimated_cost = estimated_rows as f64 * 0.1;
PhysicalNode::EdgeSeqScan {
variable: variable.clone(),
labels: labels.clone(),
properties: properties.clone(),
estimated_rows,
estimated_cost,
}
}
LogicalNode::Expand {
from_variable,
edge_variable,
to_variable,
edge_labels,
direction,
properties,
input,
} => {
let input_physical = Box::new(Self::convert_logical_node(input));
let input_rows = input_physical.get_row_count();
let estimated_rows = input_rows * 5; let estimated_cost = estimated_rows as f64 * 0.2;
if input_rows > 10000 {
PhysicalNode::HashExpand {
from_variable: from_variable.clone(),
edge_variable: edge_variable.clone(),
to_variable: to_variable.clone(),
edge_labels: edge_labels.clone(),
direction: direction.clone(),
properties: properties.clone(),
input: input_physical,
estimated_rows,
estimated_cost,
}
} else {
PhysicalNode::IndexedExpand {
from_variable: from_variable.clone(),
edge_variable: edge_variable.clone(),
to_variable: to_variable.clone(),
edge_labels: edge_labels.clone(),
direction: direction.clone(),
properties: properties.clone(),
input: input_physical,
estimated_rows,
estimated_cost,
}
}
}
LogicalNode::Filter { condition, input } => {
let input_physical = Box::new(Self::convert_logical_node(input));
let input_rows = input_physical.get_row_count();
let selectivity = 0.5; let estimated_rows = (input_rows as f64 * selectivity) as usize;
let estimated_cost = input_physical.get_cost() + (input_rows as f64 * 0.01);
PhysicalNode::Filter {
condition: condition.clone(),
input: input_physical,
selectivity,
estimated_rows,
estimated_cost,
}
}
LogicalNode::Project { expressions, input } => {
let input_physical = Box::new(Self::convert_logical_node(input));
let estimated_rows = input_physical.get_row_count();
let estimated_cost = input_physical.get_cost() + (estimated_rows as f64 * 0.005);
PhysicalNode::Project {
expressions: expressions
.iter()
.map(|expr| ProjectionItem {
expression: expr.expression.clone(),
alias: expr.alias.clone(),
output_type: OutputType::String, })
.collect(),
input: input_physical,
estimated_rows,
estimated_cost,
}
}
LogicalNode::Sort { expressions, input } => {
let input_physical = Box::new(Self::convert_logical_node(input));
let estimated_rows = input_physical.get_row_count();
let estimated_cost = input_physical.get_cost()
+ (estimated_rows as f64 * (estimated_rows as f64).log2() * 0.001);
let sort_items: Vec<SortItem> = expressions
.iter()
.map(|expr| SortItem {
expression: expr.expression.clone(),
ascending: expr.ascending,
nulls_first: false,
})
.collect();
if estimated_rows > 100000 {
PhysicalNode::ExternalSort {
expressions: sort_items,
input: input_physical,
estimated_rows,
estimated_cost,
}
} else {
PhysicalNode::InMemorySort {
expressions: sort_items,
input: input_physical,
estimated_rows,
estimated_cost,
}
}
}
LogicalNode::Distinct { input } => {
let input_physical = Box::new(Self::convert_logical_node(input));
let input_rows = input_physical.get_row_count();
let estimated_rows = input_rows / 2; let estimated_cost = input_physical.get_cost() + (input_rows as f64 * 0.01);
PhysicalNode::Distinct {
input: input_physical,
estimated_rows,
estimated_cost,
}
}
LogicalNode::Limit {
count,
offset,
input,
} => {
let input_physical = Box::new(Self::convert_logical_node(input));
let input_rows = input_physical.get_row_count();
let offset_val = offset.unwrap_or(0);
let estimated_rows = (*count).min(input_rows.saturating_sub(offset_val));
let estimated_cost =
input_physical.get_cost() * (estimated_rows as f64 / input_rows as f64);
PhysicalNode::Limit {
count: *count,
offset: *offset,
input: input_physical,
estimated_rows,
estimated_cost,
}
}
LogicalNode::GenericFunction {
function_name,
arguments,
input,
} => {
let input_physical = Box::new(Self::convert_logical_node(input));
let _input_rows = input_physical.get_row_count();
let estimated_rows = 1; let estimated_cost = input_physical.get_cost() + (arguments.len() as f64 * 2.0);
PhysicalNode::GenericFunction {
function_name: function_name.clone(),
arguments: arguments.clone(),
input: input_physical,
estimated_rows,
estimated_cost,
}
}
LogicalNode::Aggregate {
group_by,
aggregates,
input,
} => {
let input_physical = Box::new(Self::convert_logical_node(input));
let input_rows = input_physical.get_row_count();
let estimated_rows = if group_by.is_empty() {
1 } else {
input_rows / 10 };
let estimated_cost = input_physical.get_cost() + (input_rows as f64 * 0.05);
let physical_aggregates: Vec<AggregateItem> = aggregates
.iter()
.map(|agg| {
AggregateItem {
function: agg.function.clone(),
expression: agg.expression.clone(),
alias: agg.alias.clone(),
output_type: OutputType::Float, }
})
.collect();
if input_rows > 10000 {
PhysicalNode::HashAggregate {
group_by: group_by.clone(),
aggregates: physical_aggregates,
input: input_physical,
estimated_rows,
estimated_cost,
}
} else {
PhysicalNode::SortAggregate {
group_by: group_by.clone(),
aggregates: physical_aggregates,
input: input_physical,
estimated_rows,
estimated_cost,
}
}
}
LogicalNode::Having { condition, input } => {
let input_physical = Box::new(Self::convert_logical_node(input));
let input_rows = input_physical.get_row_count();
let selectivity = 0.3; let estimated_rows = (input_rows as f64 * selectivity) as usize;
let estimated_cost = input_physical.get_cost() + (input_rows as f64 * 0.02);
PhysicalNode::Having {
condition: condition.clone(),
input: input_physical,
estimated_rows,
estimated_cost,
}
}
LogicalNode::PathTraversal {
path_type,
from_variable,
to_variable,
path_elements,
input,
} => {
let input_physical = Box::new(Self::convert_logical_node(input));
let input_rows = input_physical.get_row_count();
let complexity_factor = match path_type {
PathType::Walk => 1.0, PathType::Trail => 2.0, PathType::SimplePath => 3.0, PathType::AcyclicPath => 4.0, };
let estimated_rows = input_rows * 10; let estimated_cost =
input_physical.get_cost() + (estimated_rows as f64 * complexity_factor * 0.1);
PhysicalNode::PathTraversal {
path_type: path_type.clone(),
from_variable: from_variable.clone(),
to_variable: to_variable.clone(),
path_elements: path_elements.clone(),
input: input_physical,
estimated_rows,
estimated_cost,
}
}
LogicalNode::Join {
join_type,
condition,
left,
right,
} => {
let left_physical = Box::new(Self::convert_logical_node(left));
let right_physical = Box::new(Self::convert_logical_node(right));
let left_rows = left_physical.get_row_count();
let right_rows = right_physical.get_row_count();
let estimated_rows = match join_type {
JoinType::Inner => (left_rows * right_rows) / 1000, JoinType::LeftOuter => left_rows.max((left_rows * right_rows) / 1000),
JoinType::RightOuter => right_rows.max((left_rows * right_rows) / 1000),
JoinType::FullOuter => left_rows + right_rows,
JoinType::Cross => left_rows * right_rows,
JoinType::LeftSemi => left_rows / 2, JoinType::LeftAnti => left_rows / 2, };
let estimated_cost = left_physical.get_cost()
+ right_physical.get_cost()
+ (left_rows * right_rows) as f64 * 0.001;
if left_rows > 10000 && right_rows > 10000 {
PhysicalNode::SortMergeJoin {
join_type: join_type.clone(),
left_keys: vec![], right_keys: vec![], left: left_physical,
right: right_physical,
estimated_rows,
estimated_cost,
}
} else if right_rows < left_rows {
PhysicalNode::HashJoin {
join_type: join_type.clone(),
condition: condition.clone(),
build_keys: vec![], probe_keys: vec![], build: right_physical, probe: left_physical,
estimated_rows,
estimated_cost,
}
} else {
PhysicalNode::NestedLoopJoin {
join_type: join_type.clone(),
condition: condition.clone(),
left: left_physical,
right: right_physical,
estimated_rows,
estimated_cost,
}
}
}
LogicalNode::ExistsSubquery { subquery, .. } => {
let subplan = Box::new(Self::convert_logical_node(subquery));
let estimated_rows = subplan.get_row_count();
let estimated_cost = subplan.get_cost() + 10.0;
PhysicalNode::ExistsSubquery {
subplan,
estimated_rows,
estimated_cost,
optimized: true, }
}
LogicalNode::NotExistsSubquery { subquery, .. } => {
let subplan = Box::new(Self::convert_logical_node(subquery));
let estimated_rows = subplan.get_row_count();
let estimated_cost = subplan.get_cost() + 10.0;
PhysicalNode::NotExistsSubquery {
subplan,
estimated_rows,
estimated_cost,
optimized: true,
}
}
LogicalNode::InSubquery {
expression,
subquery,
..
} => {
let subplan = Box::new(Self::convert_logical_node(subquery));
let estimated_rows = subplan.get_row_count();
let estimated_cost = subplan.get_cost() + (estimated_rows as f64 * 0.1);
PhysicalNode::InSubquery {
expression: expression.clone(),
subplan,
estimated_rows,
estimated_cost,
}
}
LogicalNode::NotInSubquery {
expression,
subquery,
..
} => {
let subplan = Box::new(Self::convert_logical_node(subquery));
let estimated_rows = subplan.get_row_count();
let estimated_cost = subplan.get_cost() + (estimated_rows as f64 * 0.1);
PhysicalNode::NotInSubquery {
expression: expression.clone(),
subplan,
estimated_rows,
estimated_cost,
}
}
LogicalNode::ScalarSubquery { subquery, .. } => {
let subplan = Box::new(Self::convert_logical_node(subquery));
let estimated_rows = 1; let estimated_cost = subplan.get_cost() + 5.0;
PhysicalNode::ScalarSubquery {
subplan,
estimated_rows,
estimated_cost,
}
}
LogicalNode::WithQuery { original_query } => {
let estimated_rows = 100; let estimated_cost = 50.0;
PhysicalNode::WithQuery {
original_query: original_query.clone(),
estimated_rows,
estimated_cost,
}
}
LogicalNode::Unwind {
expression,
variable,
input,
} => {
let base_estimated_rows = 10; let estimated_cost = 5.0;
let input_physical = input
.as_ref()
.map(|inp| Box::new(Self::convert_logical_node(inp)));
PhysicalNode::Unwind {
expression: expression.clone(),
variable: variable.clone(),
input: input_physical,
estimated_rows: base_estimated_rows,
estimated_cost,
}
}
LogicalNode::Insert {
patterns,
identifier_mappings: _,
} => {
let mut node_creations = Vec::new();
let mut edge_creations = Vec::new();
for pattern in patterns {
match pattern {
crate::plan::logical::InsertPattern::CreateNode {
storage_id,
labels,
properties,
..
} => {
node_creations.push(NodeCreation {
storage_id: storage_id.clone(),
labels: labels.clone(),
properties: properties.clone(),
});
}
crate::plan::logical::InsertPattern::CreateEdge {
storage_id,
from_node_id,
to_node_id,
label,
properties,
..
} => {
edge_creations.push(EdgeCreation {
storage_id: storage_id.clone(),
from_node_id: from_node_id.clone(),
to_node_id: to_node_id.clone(),
label: label.clone(),
properties: properties.clone(),
});
}
}
}
let estimated_ops = node_creations.len() + edge_creations.len();
let estimated_cost = estimated_ops as f64 * 2.0;
PhysicalNode::Insert {
node_creations,
edge_creations,
estimated_ops,
estimated_cost,
}
}
LogicalNode::Update {
target_variable,
properties,
input,
} => {
let input_physical = Box::new(Self::convert_logical_node(input));
let input_rows = input_physical.get_row_count();
let estimated_ops = input_rows; let estimated_cost = input_physical.get_cost() + (estimated_ops as f64 * 1.5);
PhysicalNode::Update {
target_variable: target_variable.clone(),
properties: properties.clone(),
input: input_physical,
estimated_ops,
estimated_cost,
}
}
LogicalNode::Delete {
target_variables,
detach,
input,
} => {
let input_physical = Box::new(Self::convert_logical_node(input));
let input_rows = input_physical.get_row_count();
let estimated_ops = input_rows; let estimated_cost = input_physical.get_cost()
+ (estimated_ops as f64 * if *detach { 3.0 } else { 2.0 });
PhysicalNode::Delete {
target_variables: target_variables.clone(),
detach: *detach,
input: input_physical,
estimated_ops,
estimated_cost,
}
}
LogicalNode::Union { inputs, all } => {
let physical_inputs: Vec<PhysicalNode> = inputs
.iter()
.map(|input| Self::convert_logical_node(input))
.collect();
let estimated_rows: usize = physical_inputs
.iter()
.map(|input| input.get_row_count())
.sum();
let estimated_cost: f64 = physical_inputs
.iter()
.map(|input| input.get_cost())
.sum::<f64>()
+ (estimated_rows as f64 * 0.1);
PhysicalNode::UnionAll {
inputs: physical_inputs,
all: *all, estimated_rows,
estimated_cost,
}
}
LogicalNode::Intersect { left, right, all } => {
let left_physical = Box::new(Self::convert_logical_node(left));
let right_physical = Box::new(Self::convert_logical_node(right));
let estimated_rows = left_physical
.get_row_count()
.min(right_physical.get_row_count());
let estimated_cost = left_physical.get_cost()
+ right_physical.get_cost()
+ (estimated_rows as f64 * 0.5);
PhysicalNode::Intersect {
left: left_physical,
right: right_physical,
all: *all,
estimated_rows,
estimated_cost,
}
}
LogicalNode::Except { left, right, all } => {
let left_physical = Box::new(Self::convert_logical_node(left));
let right_physical = Box::new(Self::convert_logical_node(right));
let left_rows = left_physical.get_row_count();
let right_rows = right_physical.get_row_count();
let estimated_rows = left_rows.saturating_sub(right_rows.min(left_rows));
let estimated_cost = left_physical.get_cost()
+ right_physical.get_cost()
+ (estimated_rows as f64 * 0.5);
PhysicalNode::Except {
left: left_physical,
right: right_physical,
all: *all,
estimated_rows,
estimated_cost,
}
}
LogicalNode::SingleRow => {
PhysicalNode::SingleRow {
estimated_rows: 1, estimated_cost: 0.001, }
}
}
}
pub fn get_estimated_cost(&self) -> f64 {
self.estimated_cost
}
pub fn get_estimated_rows(&self) -> usize {
self.estimated_rows
}
pub fn get_operators(&self) -> Vec<PhysicalOperator> {
self.root.get_operators()
}
#[allow(dead_code)] fn extract_label_from_logical_node(node: &Box<LogicalNode>) -> Option<String> {
match node.as_ref() {
LogicalNode::NodeScan { labels, .. } => {
labels.first().cloned()
}
LogicalNode::Filter { input, .. }
| LogicalNode::Project { input, .. }
| LogicalNode::Limit { input, .. }
| LogicalNode::Sort { input, .. } => {
Self::extract_label_from_logical_node(input)
}
_ => None,
}
}
}
impl PhysicalNode {
pub fn get_cost(&self) -> f64 {
match self {
PhysicalNode::NodeSeqScan { estimated_cost, .. } => *estimated_cost,
PhysicalNode::NodeIndexScan { estimated_cost, .. } => *estimated_cost,
PhysicalNode::EdgeSeqScan { estimated_cost, .. } => *estimated_cost,
PhysicalNode::IndexedExpand { estimated_cost, .. } => *estimated_cost,
PhysicalNode::HashExpand { estimated_cost, .. } => *estimated_cost,
PhysicalNode::PathTraversal { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Filter { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Project { estimated_cost, .. } => *estimated_cost,
PhysicalNode::HashJoin { estimated_cost, .. } => *estimated_cost,
PhysicalNode::NestedLoopJoin { estimated_cost, .. } => *estimated_cost,
PhysicalNode::SortMergeJoin { estimated_cost, .. } => *estimated_cost,
PhysicalNode::UnionAll { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Intersect { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Except { estimated_cost, .. } => *estimated_cost,
PhysicalNode::HashAggregate { estimated_cost, .. } => *estimated_cost,
PhysicalNode::SortAggregate { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Having { estimated_cost, .. } => *estimated_cost,
PhysicalNode::ExternalSort { estimated_cost, .. } => *estimated_cost,
PhysicalNode::InMemorySort { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Distinct { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Limit { estimated_cost, .. } => *estimated_cost,
PhysicalNode::GenericFunction { estimated_cost, .. } => *estimated_cost,
PhysicalNode::ExistsSubquery { estimated_cost, .. } => *estimated_cost,
PhysicalNode::NotExistsSubquery { estimated_cost, .. } => *estimated_cost,
PhysicalNode::InSubquery { estimated_cost, .. } => *estimated_cost,
PhysicalNode::NotInSubquery { estimated_cost, .. } => *estimated_cost,
PhysicalNode::ScalarSubquery { estimated_cost, .. } => *estimated_cost,
PhysicalNode::WithQuery { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Unwind { estimated_cost, .. } => *estimated_cost,
PhysicalNode::GraphIndexScan { estimated_cost, .. } => *estimated_cost,
PhysicalNode::IndexJoin { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Insert { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Update { estimated_cost, .. } => *estimated_cost,
PhysicalNode::Delete { estimated_cost, .. } => *estimated_cost,
PhysicalNode::SingleRow { estimated_cost, .. } => *estimated_cost,
}
}
pub fn get_row_count(&self) -> usize {
match self {
PhysicalNode::NodeSeqScan { estimated_rows, .. } => *estimated_rows,
PhysicalNode::NodeIndexScan { estimated_rows, .. } => *estimated_rows,
PhysicalNode::EdgeSeqScan { estimated_rows, .. } => *estimated_rows,
PhysicalNode::IndexedExpand { estimated_rows, .. } => *estimated_rows,
PhysicalNode::HashExpand { estimated_rows, .. } => *estimated_rows,
PhysicalNode::PathTraversal { estimated_rows, .. } => *estimated_rows,
PhysicalNode::Filter { estimated_rows, .. } => *estimated_rows,
PhysicalNode::Project { estimated_rows, .. } => *estimated_rows,
PhysicalNode::HashJoin { estimated_rows, .. } => *estimated_rows,
PhysicalNode::NestedLoopJoin { estimated_rows, .. } => *estimated_rows,
PhysicalNode::SortMergeJoin { estimated_rows, .. } => *estimated_rows,
PhysicalNode::UnionAll { estimated_rows, .. } => *estimated_rows,
PhysicalNode::Intersect { estimated_rows, .. } => *estimated_rows,
PhysicalNode::Except { estimated_rows, .. } => *estimated_rows,
PhysicalNode::HashAggregate { estimated_rows, .. } => *estimated_rows,
PhysicalNode::SortAggregate { estimated_rows, .. } => *estimated_rows,
PhysicalNode::ExternalSort { estimated_rows, .. } => *estimated_rows,
PhysicalNode::InMemorySort { estimated_rows, .. } => *estimated_rows,
PhysicalNode::Distinct { estimated_rows, .. } => *estimated_rows,
PhysicalNode::Limit { estimated_rows, .. } => *estimated_rows,
PhysicalNode::GenericFunction { estimated_rows, .. } => *estimated_rows,
PhysicalNode::ExistsSubquery { estimated_rows, .. } => *estimated_rows,
PhysicalNode::NotExistsSubquery { estimated_rows, .. } => *estimated_rows,
PhysicalNode::InSubquery { estimated_rows, .. } => *estimated_rows,
PhysicalNode::NotInSubquery { estimated_rows, .. } => *estimated_rows,
PhysicalNode::ScalarSubquery { estimated_rows, .. } => *estimated_rows,
PhysicalNode::WithQuery { estimated_rows, .. } => *estimated_rows,
PhysicalNode::Having { estimated_rows, .. } => *estimated_rows,
PhysicalNode::Unwind { estimated_rows, .. } => *estimated_rows,
PhysicalNode::GraphIndexScan { estimated_rows, .. } => *estimated_rows,
PhysicalNode::IndexJoin { estimated_rows, .. } => *estimated_rows,
PhysicalNode::Insert { estimated_ops, .. } => *estimated_ops,
PhysicalNode::Update { estimated_ops, .. } => *estimated_ops,
PhysicalNode::Delete { estimated_ops, .. } => *estimated_ops,
PhysicalNode::SingleRow { estimated_rows, .. } => *estimated_rows,
}
}
pub fn get_operators(&self) -> Vec<PhysicalOperator> {
let mut operators = vec![self.get_operator_type()];
match self {
PhysicalNode::IndexedExpand { input, .. }
| PhysicalNode::HashExpand { input, .. }
| PhysicalNode::PathTraversal { input, .. }
| PhysicalNode::Filter { input, .. }
| PhysicalNode::Project { input, .. }
| PhysicalNode::HashAggregate { input, .. }
| PhysicalNode::SortAggregate { input, .. }
| PhysicalNode::ExternalSort { input, .. }
| PhysicalNode::InMemorySort { input, .. }
| PhysicalNode::Distinct { input, .. }
| PhysicalNode::Limit { input, .. }
| PhysicalNode::GenericFunction { input, .. } => {
operators.extend(input.get_operators());
}
PhysicalNode::ExistsSubquery { subplan, .. }
| PhysicalNode::NotExistsSubquery { subplan, .. }
| PhysicalNode::InSubquery { subplan, .. }
| PhysicalNode::NotInSubquery { subplan, .. }
| PhysicalNode::ScalarSubquery { subplan, .. } => {
operators.extend(subplan.get_operators());
}
PhysicalNode::WithQuery { .. } => {
}
PhysicalNode::Unwind { input, .. } => {
if let Some(input_node) = input {
operators.extend(input_node.get_operators());
}
}
PhysicalNode::HashJoin { build, probe, .. }
| PhysicalNode::NestedLoopJoin {
left: build,
right: probe,
..
}
| PhysicalNode::SortMergeJoin {
left: build,
right: probe,
..
} => {
operators.extend(build.get_operators());
operators.extend(probe.get_operators());
}
PhysicalNode::IndexJoin { left, right, .. } => {
operators.extend(left.get_operators());
operators.extend(right.get_operators());
}
PhysicalNode::UnionAll { inputs, .. } => {
for input in inputs {
operators.extend(input.get_operators());
}
}
PhysicalNode::Intersect { left, right, .. } => {
operators.extend(left.get_operators());
operators.extend(right.get_operators());
}
PhysicalNode::Except { left, right, .. } => {
operators.extend(left.get_operators());
operators.extend(right.get_operators());
}
_ => {} }
operators
}
fn get_operator_type(&self) -> PhysicalOperator {
match self {
PhysicalNode::NodeSeqScan { .. }
| PhysicalNode::NodeIndexScan { .. }
| PhysicalNode::EdgeSeqScan { .. } => PhysicalOperator::Scan,
PhysicalNode::IndexedExpand { .. } | PhysicalNode::HashExpand { .. } => {
PhysicalOperator::Join
}
PhysicalNode::Filter { .. } | PhysicalNode::Having { .. } => PhysicalOperator::Filter,
PhysicalNode::Project { .. } => PhysicalOperator::Project,
PhysicalNode::HashJoin { .. }
| PhysicalNode::NestedLoopJoin { .. }
| PhysicalNode::SortMergeJoin { .. } => PhysicalOperator::Join,
PhysicalNode::UnionAll { .. } => PhysicalOperator::Union,
PhysicalNode::Intersect { .. } => PhysicalOperator::Union,
PhysicalNode::Except { .. } => PhysicalOperator::Union,
PhysicalNode::HashAggregate { .. } | PhysicalNode::SortAggregate { .. } => {
PhysicalOperator::Aggregate
}
PhysicalNode::ExternalSort { .. } | PhysicalNode::InMemorySort { .. } => {
PhysicalOperator::Sort
}
PhysicalNode::Distinct { .. } => PhysicalOperator::Sort,
PhysicalNode::Limit { .. } => PhysicalOperator::Limit,
PhysicalNode::GenericFunction { .. } => PhysicalOperator::Aggregate, PhysicalNode::PathTraversal { .. } => PhysicalOperator::Join,
PhysicalNode::ExistsSubquery { .. }
| PhysicalNode::NotExistsSubquery { .. }
| PhysicalNode::InSubquery { .. }
| PhysicalNode::NotInSubquery { .. }
| PhysicalNode::ScalarSubquery { .. } => PhysicalOperator::Subquery,
PhysicalNode::WithQuery { .. } => PhysicalOperator::WithQuery,
PhysicalNode::Unwind { .. } => PhysicalOperator::Unwind,
PhysicalNode::GraphIndexScan { .. } => PhysicalOperator::GraphIndexScan,
PhysicalNode::IndexJoin { .. } => PhysicalOperator::IndexJoin,
PhysicalNode::Insert { .. } => PhysicalOperator::Insert,
PhysicalNode::Update { .. } => PhysicalOperator::Update,
PhysicalNode::Delete { .. } => PhysicalOperator::Delete,
PhysicalNode::SingleRow { .. } => PhysicalOperator::SingleRow,
}
}
}