#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NodeCostEstimate {
pub node_cost: f64,
pub total_cost: f64,
pub output_rows: u64,
pub memory_usage: usize,
pub expression_cost: f64,
}
impl NodeCostEstimate {
pub fn new(node_cost: f64, total_cost: f64, output_rows: u64) -> Self {
Self {
node_cost,
total_cost,
output_rows,
memory_usage: 0,
expression_cost: 0.0,
}
}
pub fn with_memory_and_expression(
node_cost: f64,
total_cost: f64,
output_rows: u64,
memory_usage: usize,
expression_cost: f64,
) -> Self {
Self {
node_cost,
total_cost,
output_rows,
memory_usage,
expression_cost,
}
}
pub fn leaf(node_cost: f64, output_rows: u64) -> Self {
Self {
node_cost,
total_cost: node_cost,
output_rows,
memory_usage: 0,
expression_cost: 0.0,
}
}
pub fn zero() -> Self {
Self {
node_cost: 0.0,
total_cost: 0.0,
output_rows: 0,
memory_usage: 0,
expression_cost: 0.0,
}
}
pub fn combine_children(children: &[Self], node_cost: f64, output_rows: u64) -> Self {
let child_total_cost: f64 = children.iter().map(|e| e.total_cost).sum();
let total_memory: usize = children.iter().map(|e| e.memory_usage).sum();
let total_expression_cost: f64 = children.iter().map(|e| e.expression_cost).sum();
Self {
node_cost,
total_cost: node_cost + child_total_cost,
output_rows,
memory_usage: total_memory,
expression_cost: total_expression_cost,
}
}
pub fn cost_ratio(&self) -> f64 {
if self.total_cost == 0.0 {
0.0
} else {
self.node_cost / self.total_cost
}
}
pub fn is_valid(&self) -> bool {
self.node_cost >= 0.0 && self.total_cost >= 0.0
}
}
impl Default for NodeCostEstimate {
fn default() -> Self {
Self::zero()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_cost_estimate_new() {
let estimate = NodeCostEstimate::new(10.0, 100.0, 50);
assert_eq!(estimate.node_cost, 10.0);
assert_eq!(estimate.total_cost, 100.0);
assert_eq!(estimate.output_rows, 50);
assert_eq!(estimate.memory_usage, 0);
assert_eq!(estimate.expression_cost, 0.0);
}
#[test]
fn test_node_cost_estimate_with_memory_and_expression() {
let estimate = NodeCostEstimate::with_memory_and_expression(10.0, 100.0, 50, 1024, 5.0);
assert_eq!(estimate.node_cost, 10.0);
assert_eq!(estimate.total_cost, 100.0);
assert_eq!(estimate.output_rows, 50);
assert_eq!(estimate.memory_usage, 1024);
assert_eq!(estimate.expression_cost, 5.0);
}
#[test]
fn test_node_cost_estimate_leaf() {
let estimate = NodeCostEstimate::leaf(10.0, 50);
assert_eq!(estimate.node_cost, 10.0);
assert_eq!(estimate.total_cost, 10.0);
assert_eq!(estimate.output_rows, 50);
assert_eq!(estimate.memory_usage, 0);
assert_eq!(estimate.expression_cost, 0.0);
}
#[test]
fn test_node_cost_estimate_zero() {
let estimate = NodeCostEstimate::zero();
assert_eq!(estimate.node_cost, 0.0);
assert_eq!(estimate.total_cost, 0.0);
assert_eq!(estimate.output_rows, 0);
assert_eq!(estimate.memory_usage, 0);
assert_eq!(estimate.expression_cost, 0.0);
}
#[test]
fn test_combine_children() {
let child1 = NodeCostEstimate::with_memory_and_expression(10.0, 10.0, 100, 512, 2.0);
let child2 = NodeCostEstimate::with_memory_and_expression(20.0, 20.0, 200, 1024, 3.0);
let combined = NodeCostEstimate::combine_children(&[child1, child2], 5.0, 50);
assert_eq!(combined.node_cost, 5.0);
assert_eq!(combined.total_cost, 35.0); assert_eq!(combined.output_rows, 50);
assert_eq!(combined.memory_usage, 1536); assert_eq!(combined.expression_cost, 5.0); }
#[test]
fn test_cost_ratio() {
let estimate = NodeCostEstimate::new(10.0, 100.0, 50);
assert_eq!(estimate.cost_ratio(), 0.1);
let zero = NodeCostEstimate::zero();
assert_eq!(zero.cost_ratio(), 0.0);
}
#[test]
fn test_is_valid() {
let valid = NodeCostEstimate::new(10.0, 100.0, 50);
assert!(valid.is_valid());
let invalid = NodeCostEstimate::new(-1.0, 100.0, 50);
assert!(!invalid.is_valid());
}
}