mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-11-06 19:06:58 +00:00
feat(promql): draw the fused node's tree in a bracketed block
The tree lines carried their own absolute indentation, so a nested fused node's tree sat in the same column as the node's real children: in the early logical snapshots of EXPLAIN VERBOSE the region fused node's tree root read as a sibling of the node and its TableScan as a child of the window above it. The tree now opens a native_tree=[ ... ] block that starts at column zero, the same shape MergeScan uses for its remote input, so only the lines after the closing bracket are plan children. Signed-off-by: discord9 <discord9@163.com>
This commit is contained in:
@@ -1075,7 +1075,7 @@ impl UserDefinedLogicalNodeCore for FusedSeries {
|
||||
"{separator}(state routing: canonical labels except {excluded:?})"
|
||||
)?;
|
||||
}
|
||||
write!(f, "\n{}", self.tree)
|
||||
write!(f, "\nnative_tree=[\n{}\n]", self.tree)
|
||||
}
|
||||
|
||||
fn with_exprs_and_inputs(
|
||||
@@ -2593,7 +2593,11 @@ impl DisplayAs for FusedSeriesExec {
|
||||
if self.format == FusedOutputFormat::SeriesFrame {
|
||||
write!(f, ": (output: series_frame)")?;
|
||||
}
|
||||
write!(f, "\n{}", self.tree.display_with(&self.node_metrics()))
|
||||
write!(
|
||||
f,
|
||||
"\nnative_tree=[\n{}\n]",
|
||||
self.tree.display_with(&self.node_metrics())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2439,10 +2439,10 @@ impl Display for Explain<'_> {
|
||||
fn fmt_for_explain_prints_the_native_tree() {
|
||||
let text = Explain(&fused(rate_tree())).to_string();
|
||||
assert!(
|
||||
text.starts_with("FusedSeries\n Window: rate [300000ms]"),
|
||||
text.starts_with("FusedSeries\nnative_tree=[\nWindow: rate [300000ms]"),
|
||||
"{text}"
|
||||
);
|
||||
assert!(text.contains(" Input: raw samples source=0"), "{text}");
|
||||
assert!(text.contains("\n Input: raw samples source=0"), "{text}");
|
||||
assert!(!text.contains("metrics="), "{text}");
|
||||
}
|
||||
|
||||
@@ -2456,16 +2456,16 @@ fn fmt_as_prints_the_native_tree() {
|
||||
assert!(!text.contains("metrics="), "{text}");
|
||||
|
||||
assert!(
|
||||
text.starts_with("FusedSeriesExec\n Window: rate"),
|
||||
text.starts_with("FusedSeriesExec\nnative_tree=[\nWindow: rate"),
|
||||
"the node line precedes its tree: {text}"
|
||||
);
|
||||
// A distinctive line of the compiled tree: the window's bound range, offset, anchor and grid.
|
||||
assert!(
|
||||
text.contains(" Window: rate [300000ms] offset 0 anchor - on grid 300000/60000x5"),
|
||||
text.contains("\nWindow: rate [300000ms] offset 0 anchor - on grid 300000/60000x5"),
|
||||
"the node line carries the tree it runs: {text}"
|
||||
);
|
||||
assert!(
|
||||
text.contains(" Input: raw samples"),
|
||||
text.contains("\n Input: raw samples"),
|
||||
"the node line carries the tree's source: {text}"
|
||||
);
|
||||
}
|
||||
@@ -2480,7 +2480,7 @@ async fn fmt_as_reports_node_metrics_after_execution() {
|
||||
let text = displayable(exec.as_ref()).indent(false).to_string();
|
||||
println!("=== physical_plan after execution ===\n{text}");
|
||||
assert!(
|
||||
text.starts_with("FusedSeriesExec\n Window: rate"),
|
||||
text.starts_with("FusedSeriesExec\nnative_tree=[\nWindow: rate"),
|
||||
"{text}"
|
||||
);
|
||||
assert!(text.contains("metrics=["), "{text}");
|
||||
@@ -5832,7 +5832,7 @@ fn the_explain_text_names_the_frame_format_only() {
|
||||
assert!(
|
||||
Explain(&rows)
|
||||
.to_string()
|
||||
.starts_with("FusedSeries\n Window:")
|
||||
.starts_with("FusedSeries\nnative_tree=[\nWindow:")
|
||||
);
|
||||
let framed = FusedSeries::try_new_with_format(
|
||||
rows.tree().clone(),
|
||||
@@ -5845,21 +5845,21 @@ fn the_explain_text_names_the_frame_format_only() {
|
||||
assert!(
|
||||
Explain(&framed)
|
||||
.to_string()
|
||||
.starts_with("FusedSeries: (output: series_frame)\n Window:"),
|
||||
.starts_with("FusedSeries: (output: series_frame)\nnative_tree=[\nWindow:"),
|
||||
"a frame output names its format: {}",
|
||||
Explain(&framed)
|
||||
);
|
||||
let framed_exec = plan_of(framed, memory_exec(Vec::new())).unwrap();
|
||||
let text = displayable(framed_exec.as_ref()).indent(false).to_string();
|
||||
assert!(
|
||||
text.starts_with("FusedSeriesExec: (output: series_frame)\n Window:"),
|
||||
text.starts_with("FusedSeriesExec: (output: series_frame)\nnative_tree=[\nWindow:"),
|
||||
"{text}"
|
||||
);
|
||||
let canonical = canonical_producer_node();
|
||||
let text = Explain(&canonical).to_string();
|
||||
assert!(
|
||||
text.starts_with(
|
||||
"FusedSeries: (output: series_frame, per-partition canonical merge)\n Merge:"
|
||||
"FusedSeries: (output: series_frame, per-partition canonical merge)\nnative_tree=[\nMerge:"
|
||||
),
|
||||
"{text}"
|
||||
);
|
||||
@@ -5867,7 +5867,7 @@ fn the_explain_text_names_the_frame_format_only() {
|
||||
let plain = plan(rate_tree(), memory_exec(Vec::new())).expect("the runtime executes the tree");
|
||||
let text = displayable(plain.as_ref()).indent(false).to_string();
|
||||
assert!(
|
||||
text.starts_with("FusedSeriesExec\n Window:"),
|
||||
text.starts_with("FusedSeriesExec\nnative_tree=[\nWindow:"),
|
||||
"the row-table node line has no parameters: {text}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -519,8 +519,7 @@ impl NativeTree {
|
||||
report_node(self.root(), ROOT_PATH, metrics)
|
||||
}
|
||||
|
||||
/// This tree rendered under its operator line, with each node's own
|
||||
/// figures when `metrics` holds them.
|
||||
/// This tree rendered from column zero, with each node's own figures when `metrics` holds them.
|
||||
pub fn display_with<'a>(&'a self, metrics: &'a NodeMetricsSnapshot) -> NativeTreeDisplay<'a> {
|
||||
NativeTreeDisplay {
|
||||
tree: self,
|
||||
@@ -538,9 +537,10 @@ pub struct NativeTreeDisplay<'a> {
|
||||
|
||||
impl fmt::Display for NativeTreeDisplay<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// The root sits one level in from the operator line it is drawn under, and each child one
|
||||
// level below its consumer.
|
||||
write_report(f, &self.tree.report(self.metrics), 1)
|
||||
// The root starts at column zero: the block the caller brackets the tree in carries the
|
||||
// indentation, so each line here is depth-relative to that block rather than to the
|
||||
// caller's own place in the plan.
|
||||
write_report(f, &self.tree.report(self.metrics), 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,10 +567,11 @@ fn write_report(
|
||||
}
|
||||
|
||||
impl fmt::Display for NativeTree {
|
||||
/// One line per node, outermost first, two spaces per depth, without figures: this is the
|
||||
/// tree's own shape, which is what a display that holds no metrics snapshot can show.
|
||||
/// One line per node, outermost first, from column zero with two spaces per depth and without
|
||||
/// figures: this is the tree's own shape, which is what a display that holds no metrics
|
||||
/// snapshot can show.
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write_report(f, &self.report(&NodeMetricsSnapshot::default()), 1)
|
||||
write_report(f, &self.report(&NodeMetricsSnapshot::default()), 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ fn shown(lines: &[&str]) -> String {
|
||||
fn a_tree_is_plan_lines_only() {
|
||||
let tree = NativeTree::try_new(raw_input()).unwrap();
|
||||
|
||||
assert_eq!(tree.to_string(), shown(&[" Input: raw samples source=0"]));
|
||||
assert_eq!(tree.to_string(), shown(&["Input: raw samples source=0"]));
|
||||
assert_eq!(
|
||||
NativeTree::try_new(window_over(
|
||||
raw_input(),
|
||||
@@ -138,8 +138,8 @@ fn a_tree_is_plan_lines_only() {
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
shown(&[
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
"Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -161,8 +161,8 @@ fn a_window_over_raw_samples_is_shown_once_per_node() {
|
||||
assert_eq!(
|
||||
tree.to_string(),
|
||||
shown(&[
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
"Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -187,9 +187,9 @@ fn a_map_nests_below_the_window_it_maps() {
|
||||
assert_eq!(
|
||||
tree.to_string(),
|
||||
shown(&[
|
||||
" Map: abs, round 3",
|
||||
" Window: rate [300000ms] offset 60000 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
"Map: abs, round 3",
|
||||
" Window: rate [300000ms] offset 60000 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -211,8 +211,8 @@ fn a_window_names_its_constant_argument_and_its_anchor() {
|
||||
assert_eq!(
|
||||
tree.to_string(),
|
||||
shown(&[
|
||||
" Window: quantile_over_time 0.9 [60000ms] offset 0 anchor 42 on grid 1000/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
"Window: quantile_over_time 0.9 [60000ms] offset 0 anchor 42 on grid 1000/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -236,7 +236,7 @@ fn an_input_reports_whether_it_is_raw_and_what_ordering_it_guarantees() {
|
||||
let tree = NativeTree::try_new(evaluated_input).unwrap();
|
||||
assert_eq!(
|
||||
tree.to_string(),
|
||||
shown(&[" Input: evaluated samples source=0, contiguous",])
|
||||
shown(&["Input: evaluated samples source=0, contiguous",])
|
||||
);
|
||||
|
||||
let interleaved = NativeType::Samples(Arc::new(SeriesSchema {
|
||||
@@ -254,7 +254,7 @@ fn an_input_reports_whether_it_is_raw_and_what_ordering_it_guarantees() {
|
||||
let tree = NativeTree::try_new(raw_input).unwrap();
|
||||
assert_eq!(
|
||||
tree.to_string(),
|
||||
shown(&[" Input: raw samples source=0, interleaved"])
|
||||
shown(&["Input: raw samples source=0, interleaved"])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ fn a_scalar_and_an_instant_line_show_what_they_hold() {
|
||||
output: NativeType::Scalar(Arc::clone(&out)),
|
||||
};
|
||||
let tree = NativeTree::try_new(scalar).unwrap();
|
||||
assert_eq!(tree.to_string(), shown(&[" Scalar: 2"]));
|
||||
assert_eq!(tree.to_string(), shown(&["Scalar: 2"]));
|
||||
|
||||
let instant = NativeNode {
|
||||
expr: NativeExpr::Instant {
|
||||
@@ -287,8 +287,8 @@ fn a_scalar_and_an_instant_line_show_what_they_hold() {
|
||||
assert_eq!(
|
||||
tree.to_string(),
|
||||
shown(&[
|
||||
" Instant: lookback 300000ms offset 60000 on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
"Instant: lookback 300000ms offset 60000 on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -320,9 +320,9 @@ fn an_aggregate_and_a_binary_name_their_binding() {
|
||||
assert_eq!(
|
||||
tree.to_string(),
|
||||
shown(&[
|
||||
" Aggregate: sum by [job] complete",
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
"Aggregate: sum by [job] complete",
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
])
|
||||
);
|
||||
|
||||
@@ -356,10 +356,10 @@ fn an_aggregate_and_a_binary_name_their_binding() {
|
||||
assert_eq!(
|
||||
tree.to_string(),
|
||||
shown(&[
|
||||
" Binary: + ignoring [] one-to-one",
|
||||
" Scalar: 2",
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
"Binary: + ignoring [] one-to-one",
|
||||
" Scalar: 2",
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
])
|
||||
);
|
||||
|
||||
@@ -375,7 +375,7 @@ fn an_aggregate_and_a_binary_name_their_binding() {
|
||||
.to_string()
|
||||
.lines()
|
||||
.next(),
|
||||
Some(" Binary: + ignoring [] one-to-one fill_right 0.5")
|
||||
Some("Binary: + ignoring [] one-to-one fill_right 0.5")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -409,15 +409,15 @@ fn a_set_operation_names_its_operator_and_labels() {
|
||||
};
|
||||
assert_eq!(
|
||||
root_line(SetOp::And, MatchingLabels::On(vec!["job".into()])),
|
||||
" Set: and on [job]"
|
||||
"Set: and on [job]"
|
||||
);
|
||||
assert_eq!(
|
||||
root_line(SetOp::Or, MatchingLabels::Ignoring(vec!["job".into()])),
|
||||
" Set: or ignoring [job]"
|
||||
"Set: or ignoring [job]"
|
||||
);
|
||||
assert_eq!(
|
||||
root_line(SetOp::Unless, MatchingLabels::Ignoring(Vec::new())),
|
||||
" Set: unless ignoring []"
|
||||
"Set: unless ignoring []"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -451,9 +451,9 @@ fn a_without_grouping_does_not_read_as_by() {
|
||||
assert_eq!(
|
||||
tree.to_string(),
|
||||
shown(&[
|
||||
" Aggregate: sum without [job] complete",
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
"Aggregate: sum without [job] complete",
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4",
|
||||
" Input: raw samples source=0",
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -501,22 +501,22 @@ fn a_vector_operation_names_its_binding() {
|
||||
top: false,
|
||||
group: Grouping::Without(Arc::new(["job".into()])),
|
||||
},
|
||||
" Vector: bottomk 3 without [job]",
|
||||
"Vector: bottomk 3 without [job]",
|
||||
),
|
||||
(
|
||||
BoundVectorOp::LimitK {
|
||||
k: 2.0,
|
||||
group: Grouping::By(Arc::new([])),
|
||||
},
|
||||
" Vector: limitk 2 by []",
|
||||
"Vector: limitk 2 by []",
|
||||
),
|
||||
(
|
||||
BoundVectorOp::LimitRatio { ratio: -0.5 },
|
||||
" Vector: limit_ratio -0.5",
|
||||
"Vector: limit_ratio -0.5",
|
||||
),
|
||||
(
|
||||
BoundVectorOp::Sort(SortOrder::ByLabelDesc(vec!["job".into(), "le".into()])),
|
||||
" Vector: sort_by_label_desc [job, le]",
|
||||
"Vector: sort_by_label_desc [job, le]",
|
||||
),
|
||||
(
|
||||
BoundVectorOp::Absent {
|
||||
@@ -529,14 +529,14 @@ fn a_vector_operation_names_its_binding() {
|
||||
)
|
||||
.unwrap(),
|
||||
},
|
||||
r#" Vector: absent {job="x"}"#,
|
||||
r#"Vector: absent {job="x"}"#,
|
||||
),
|
||||
(
|
||||
BoundVectorOp::CountValues {
|
||||
label: "value".into(),
|
||||
group: Grouping::Without(Arc::new(["job".into()])),
|
||||
},
|
||||
r#" Vector: count_values "value" without [job]"#,
|
||||
r#"Vector: count_values "value" without [job]"#,
|
||||
),
|
||||
];
|
||||
for (op, line) in lines {
|
||||
@@ -573,7 +573,7 @@ fn a_modified_window_names_its_modifier() {
|
||||
assert_eq!(
|
||||
tree.to_string().lines().next(),
|
||||
Some(
|
||||
" Window: rate [300000ms] smoothed lookback 300000ms offset 0 anchor - on grid \
|
||||
"Window: rate [300000ms] smoothed lookback 300000ms offset 0 anchor - on grid \
|
||||
0/60000x4"
|
||||
)
|
||||
);
|
||||
@@ -624,10 +624,10 @@ fn each_node_shows_only_the_figures_it_recorded() {
|
||||
assert_eq!(
|
||||
tree.display_with(&metrics).to_string(),
|
||||
shown(&[
|
||||
" Aggregate: sum by [job] complete, metrics=[groups: 120]",
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4, \
|
||||
"Aggregate: sum by [job] complete, metrics=[groups: 120]",
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/60000x4, \
|
||||
metrics=[output_batches: 4, output_values: 7200]",
|
||||
" Input: raw samples source=0, metrics=[input_batches: 1, input_samples: 1486080]",
|
||||
" Input: raw samples source=0, metrics=[input_batches: 1, input_samples: 1486080]",
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1039,8 +1039,8 @@ fn the_display_of_a_compiled_tree_is_the_tree() {
|
||||
assert_eq!(
|
||||
tree.to_string(),
|
||||
[
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/5000x21",
|
||||
" Input: raw samples source=0",
|
||||
"Window: rate [300000ms] offset 0 anchor - on grid 0/5000x21",
|
||||
" Input: raw samples source=0",
|
||||
]
|
||||
.join("\n")
|
||||
);
|
||||
@@ -1055,29 +1055,29 @@ fn the_display_of_a_tree_the_batch_added_to_is_the_query() {
|
||||
(
|
||||
"sum by(job)(rate(x[5m]) * 100)",
|
||||
&[
|
||||
" Aggregate: sum by [job] complete",
|
||||
" Binary: * ignoring [] one-to-one",
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/5000x21",
|
||||
" Input: raw samples source=0",
|
||||
" Scalar: 100",
|
||||
"Aggregate: sum by [job] complete",
|
||||
" Binary: * ignoring [] one-to-one",
|
||||
" Window: rate [300000ms] offset 0 anchor - on grid 0/5000x21",
|
||||
" Input: raw samples source=0",
|
||||
" Scalar: 100",
|
||||
],
|
||||
),
|
||||
(
|
||||
"-x",
|
||||
&[
|
||||
" Binary: * ignoring [] one-to-one",
|
||||
" Scalar: -1",
|
||||
" Instant: lookback 5000ms offset 0 on grid 0/5000x21",
|
||||
" Input: raw samples source=0",
|
||||
"Binary: * ignoring [] one-to-one",
|
||||
" Scalar: -1",
|
||||
" Instant: lookback 5000ms offset 0 on grid 0/5000x21",
|
||||
" Input: raw samples source=0",
|
||||
],
|
||||
),
|
||||
(
|
||||
"0 < x",
|
||||
&[
|
||||
" Binary: < ignoring [] one-to-one",
|
||||
" Scalar: 0",
|
||||
" Instant: lookback 5000ms offset 0 on grid 0/5000x21",
|
||||
" Input: raw samples source=0",
|
||||
"Binary: < ignoring [] one-to-one",
|
||||
" Scalar: 0",
|
||||
" Instant: lookback 5000ms offset 0 on grid 0/5000x21",
|
||||
" Input: raw samples source=0",
|
||||
],
|
||||
),
|
||||
];
|
||||
@@ -1289,7 +1289,7 @@ fn the_supported_math_functions_map_to_their_kernels() {
|
||||
let root = display.lines().next().expect("a compiled tree has a root");
|
||||
assert_eq!(
|
||||
root,
|
||||
format!(" Map: {op}"),
|
||||
format!("Map: {op}"),
|
||||
"`{query}` bound the wrong kernel"
|
||||
);
|
||||
}
|
||||
@@ -2853,7 +2853,7 @@ fn the_aggregates_that_are_not_a_fold_bind_a_vector_operation() {
|
||||
.unwrap_or_else(|error| panic!("`{query}` was refused: {error}"));
|
||||
let display = tree.to_string();
|
||||
let root = display.lines().next().expect("a compiled tree has a root");
|
||||
assert_eq!(root, format!(" Vector: {op}"), "`{query}`");
|
||||
assert_eq!(root, format!("Vector: {op}"), "`{query}`");
|
||||
}
|
||||
|
||||
let error = compile(
|
||||
@@ -4287,7 +4287,7 @@ fn whole_vector_functions_bind_a_vector_operation() {
|
||||
.unwrap_or_else(|error| panic!("`{query}` was refused: {error}"));
|
||||
let display = tree.to_string();
|
||||
let root = display.lines().next().expect("a compiled tree has a root");
|
||||
assert_eq!(root, format!(" Vector: {op}"), "`{query}`");
|
||||
assert_eq!(root, format!("Vector: {op}"), "`{query}`");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4830,7 +4830,7 @@ fn label_functions_bind_their_change_into_a_map() {
|
||||
.unwrap_or_else(|error| panic!("`{query}` was refused: {error}"));
|
||||
let display = tree.to_string();
|
||||
let root = display.lines().next().expect("a compiled tree has a root");
|
||||
assert_eq!(root, format!(" Map: {op}"), "`{query}`");
|
||||
assert_eq!(root, format!("Map: {op}"), "`{query}`");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4853,7 +4853,7 @@ fn histogram_functions_bind_their_kernel_into_a_map() {
|
||||
.unwrap_or_else(|error| panic!("`{query}` was refused: {error}"));
|
||||
let display = tree.to_string();
|
||||
let root = display.lines().next().expect("a compiled tree has a root");
|
||||
assert_eq!(root, format!(" Map: {name}"), "`{query}`");
|
||||
assert_eq!(root, format!("Map: {name}"), "`{query}`");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2620,7 +2620,10 @@ async fn native_fused_rate_plans_to_a_fused_series_node() {
|
||||
let plan = build_native_fused_plan("rate(some_metric[5m])", true).await;
|
||||
|
||||
let display = plan.display_indent().to_string();
|
||||
assert!(display.contains(" Window: rate [300000ms]"), "{display}");
|
||||
assert!(
|
||||
display.contains("native_tree=[\nWindow: rate [300000ms]"),
|
||||
"{display}"
|
||||
);
|
||||
// The node reads the selector's own scan; nothing between them packs the frame it reads.
|
||||
assert!(display.contains("TableScan: some_metric"), "{display}");
|
||||
// The node keeps the compiled tree, not the query text.
|
||||
@@ -4225,14 +4228,18 @@ async fn native_split_explain_renders_each_tree_including_remote_input() {
|
||||
let (final_text, remote_text) = text
|
||||
.split_once("MergeScan [is_placeholder=false, remote_input=[")
|
||||
.expect("the partial is inside a merge scan");
|
||||
assert!(final_text.starts_with("FusedSeries\n Binary:"), "{text}");
|
||||
assert!(
|
||||
final_text.starts_with("FusedSeries\nnative_tree=[\nBinary:"),
|
||||
"{text}"
|
||||
);
|
||||
assert!(
|
||||
final_text.contains("Aggregate: avg by [tag_0] final"),
|
||||
"{text}"
|
||||
);
|
||||
assert!(
|
||||
remote_text
|
||||
.contains("FusedSeries: (output: series_frame)\n Aggregate: avg by [tag_0] partial"),
|
||||
remote_text.contains(
|
||||
"FusedSeries: (output: series_frame)\nnative_tree=[\nAggregate: avg by [tag_0] partial"
|
||||
),
|
||||
"{text}"
|
||||
);
|
||||
assert!(remote_text.contains("Window: irate [60000ms]"), "{text}");
|
||||
|
||||
Reference in New Issue
Block a user