Skip to content
This repository was archived by the owner on Jan 7, 2025. It is now read-only.

Commit 026a01e

Browse files
authored
feat: add data field in define_plan_node macro (#129)
Add a `data_name` field to `define_plan_node` macro. Plan nodes with data field must implement a method `explain_data`. **An example** ```rust #[derive(Clone, Debug, Serialize, Deserialize)] struct ComplexData { a: i32, b: String, } #[derive(Clone, Debug)] struct PhysicalComplexDummy(PlanNode); impl PhysicalComplexDummy { pub fn explain_data(data: &Value) -> Vec<(&'static str, Pretty<'static>)> { if let Value::Serialized(serialized_data) = data { let data: ComplexData = bincode::deserialize(serialized_data).unwrap(); vec![ ("a", data.a.to_string().into()), ("b", data.b.to_string().into()), ] } else { unreachable!() } } } define_plan_node!( PhysicalComplexDummy: PlanNode, PhysicalScan, [ { 0, child: PlanNode } ], [ ], complex_data ); ``` I could just print data with `format!("{:?}, data)`, but the downside of this is that for a struct ```rust struct Foo { a: i32, b: i32, } ``` The debug output would be `Foo { a: 1, b: 2}`. Here, the struct name is implementation detail that should be hidden from the user, and the bracket surrounding the fields makes these fields seem related in some way, when they are actually not. So just letting user implement `explain_data` gives maximum flexibility.
1 parent 76b98c6 commit 026a01e

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

optd-datafusion-repr/src/plan_nodes.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::sync::Arc;
1818
use arrow_schema::DataType;
1919
use optd_core::{
2020
cascades::{CascadesOptimizer, GroupId},
21-
rel_node::{RelNode, RelNodeMeta, RelNodeMetaMap, RelNodeRef, RelNodeTyp},
21+
rel_node::{RelNode, RelNodeMeta, RelNodeMetaMap, RelNodeRef, RelNodeTyp, Value},
2222
};
2323

2424
pub use agg::{LogicalAgg, PhysicalAgg};
@@ -208,6 +208,10 @@ pub trait OptRelNode: 'static + Clone {
208208
}
209209
}
210210

211+
pub trait ExplainData: OptRelNode {
212+
fn explain_data(data: &Value) -> Vec<(&'static str, Pretty<'static>)>;
213+
}
214+
211215
#[derive(Clone, Debug)]
212216
pub struct PlanNode(pub(crate) OptRelNodeRef);
213217

optd-datafusion-repr/src/plan_nodes/macros.rs

+105-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
1+
/// Plan nodes with data fields must implement `ExplainData` trait. An example:
2+
///
3+
/// ```ignore
4+
/// #[derive(Clone, Debug)]
5+
/// struct PhysicalDummy(PlanNode);
6+
///
7+
/// // Implement `OptRelNode` using `define_plan_node!`...
8+
///
9+
/// impl ExplainData for PhysicalDummy {
10+
/// fn explain_data(data: &Value) -> Vec<(&'static str, Pretty<'static>)> {
11+
/// if let Value::Int32(i) = data {
12+
/// vec![("primitive_data", i.to_string().into())]
13+
/// } else {
14+
/// unreachable!()
15+
/// }
16+
/// }
17+
/// }
18+
/// ```
119
macro_rules! define_plan_node {
220
(
321
$struct_name:ident : $meta_typ:tt,
422
$variant:ident,
523
[ $({ $child_id:literal, $child_name:ident : $child_meta_typ:ty }),* ] ,
624
[ $({ $attr_id:literal, $attr_name:ident : $attr_meta_typ:ty }),* ]
725
$(, { $inner_name:ident : $inner_typ:ty })?
26+
$(, $data_name:ident)?
827
) => {
928
impl OptRelNode for $struct_name {
1029
fn into_rel_node(self) -> OptRelNodeRef {
@@ -30,6 +49,8 @@ macro_rules! define_plan_node {
3049
if let Some(meta_map) = meta_map {
3150
fields = fields.with_meta(self.0.get_meta(meta_map));
3251
};
52+
define_plan_node!(@expand_fields self, $struct_name, fields $(, $data_name)?);
53+
3354
pretty_xmlish::Pretty::simple_record(
3455
stringify!($struct_name),
3556
fields,
@@ -44,16 +65,22 @@ macro_rules! define_plan_node {
4465
pub fn new(
4566
$($child_name : $child_meta_typ,)*
4667
$($attr_name : $attr_meta_typ),*
68+
$($data_name: Value)?
4769
$(, $inner_name : $inner_typ)?
4870
) -> $struct_name {
71+
#[allow(unused_mut, unused)]
72+
let mut data = None;
73+
$(
74+
data = Some($data_name);
75+
)*
4976
$struct_name($meta_typ(
5077
optd_core::rel_node::RelNode {
5178
typ: OptRelNodeTyp::$variant $( ($inner_name) )?,
5279
children: vec![
5380
$($child_name.into_rel_node(),)*
5481
$($attr_name.into_rel_node()),*
5582
],
56-
data: None,
83+
data,
5784
}
5885
.into(),
5986
))
@@ -83,6 +110,83 @@ macro_rules! define_plan_node {
83110
)?
84111
}
85112
};
113+
// Dummy branch that does nothing when data is `None`.
114+
(@expand_fields $self:ident, $struct_name:ident, $fields:ident) => {};
115+
// Expand explain fields with data.
116+
(@expand_fields $self:ident, $struct_name:ident, $fields:ident, $data_name:ident) => {
117+
let data = $self.0 .0.data.as_ref().unwrap();
118+
$fields.extend($struct_name::explain_data(data));
119+
};
86120
}
87121

88122
pub(crate) use define_plan_node;
123+
124+
#[cfg(test)]
125+
mod test {
126+
use crate::plan_nodes::*;
127+
use optd_core::rel_node::Value;
128+
use serde::{Deserialize, Serialize};
129+
130+
fn get_explain_str(pretty: &Pretty) -> String {
131+
let mut config = PrettyConfig {
132+
need_boundaries: false,
133+
reduced_spaces: false,
134+
width: 300,
135+
..Default::default()
136+
};
137+
let mut out = String::new();
138+
config.unicode(&mut out, pretty);
139+
out
140+
}
141+
142+
/// Ensure `define_plan_node` works with data field.
143+
#[test]
144+
fn test_explain_complex_data() {
145+
#[derive(Clone, Debug, Serialize, Deserialize)]
146+
struct ComplexData {
147+
a: i32,
148+
b: String,
149+
}
150+
151+
#[derive(Clone, Debug)]
152+
struct PhysicalComplexDummy(PlanNode);
153+
154+
impl ExplainData for PhysicalComplexDummy {
155+
fn explain_data(data: &Value) -> Vec<(&'static str, Pretty<'static>)> {
156+
if let Value::Serialized(serialized_data) = data {
157+
let data: ComplexData = bincode::deserialize(serialized_data).unwrap();
158+
vec![
159+
("a", data.a.to_string().into()),
160+
("b", data.b.to_string().into()),
161+
]
162+
} else {
163+
unreachable!()
164+
}
165+
}
166+
}
167+
168+
define_plan_node!(
169+
PhysicalComplexDummy: PlanNode,
170+
PhysicalScan, [
171+
{ 0, child: PlanNode }
172+
], [
173+
],
174+
complex_data
175+
);
176+
177+
let node = PhysicalComplexDummy::new(
178+
LogicalScan::new("a".to_string()).0,
179+
Value::Serialized(
180+
bincode::serialize(&ComplexData {
181+
a: 1,
182+
b: "a".to_string(),
183+
})
184+
.unwrap()
185+
.into_iter()
186+
.collect(),
187+
),
188+
);
189+
let pretty = node.dispatch_explain(None);
190+
println!("{}", get_explain_str(&pretty));
191+
}
192+
}

0 commit comments

Comments
 (0)