Merge 'core: pretty-print EXPLAIN QUERY PLAN' from Piotr Sarna

Mimics SQLite's EXPLAIN QUERY PLAN pretty printer.

Example run on a preexisting db:
```
$ cargo run /tmp/srn "explain query plan select * from t natural join t join t2 on t.id = 2*t2.id where t.id in (select * from t);"
QUERY PLAN
`--JOIN ON t.id = 2 * t2.id
   |--JOIN
   |  |--SCAN t FILTER t.id IN (SELECT * FROM t)
   |  `--SCAN t
   `--SCAN t2

```

Fixes https://github.com/penberg/limbo/issues/295

Closes #296
This commit is contained in:
Pekka Enberg
2024-08-18 21:34:16 +03:00

View File

@@ -254,8 +254,22 @@ impl Display for Aggregate {
// For EXPLAIN QUERY PLAN
impl Display for Operator {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fn fmt_operator(operator: &Operator, f: &mut Formatter, level: usize) -> fmt::Result {
let indent = " ".repeat(level);
fn fmt_operator(
operator: &Operator,
f: &mut Formatter,
level: usize,
last: bool,
) -> fmt::Result {
let indent = if level == 0 {
if last { "`--" } else { "|--" }.to_string()
} else {
format!(
" {}{}",
"| ".repeat(level - 1),
if last { "`--" } else { "|--" }
)
};
match operator {
Operator::Aggregate {
source, aggregates, ..
@@ -267,7 +281,7 @@ impl Display for Operator {
.collect::<Vec<String>>()
.join(", ");
writeln!(f, "{}AGGREGATE {}", indent, aggregates_display_string)?;
fmt_operator(source, f, level + 1)
fmt_operator(source, f, level + 1, true)
}
Operator::Filter {
source, predicates, ..
@@ -278,7 +292,7 @@ impl Display for Operator {
.collect::<Vec<String>>()
.join(" AND ");
writeln!(f, "{}FILTER {}", indent, predicates_string)?;
fmt_operator(source, f, level + 1)
fmt_operator(source, f, level + 1, true)
}
Operator::SeekRowid {
table,
@@ -305,12 +319,11 @@ impl Display for Operator {
indent, &table.name, rowid_predicate
)?,
}
Ok(())
}
Operator::Limit { source, limit, .. } => {
writeln!(f, "{}TAKE {}", indent, limit)?;
fmt_operator(source, f, level + 1)
fmt_operator(source, f, level + 1, true)
}
Operator::Join {
left,
@@ -334,8 +347,8 @@ impl Display for Operator {
}
None => writeln!(f, "{}{}", indent, join_name)?,
}
fmt_operator(left, f, level + 1)?;
fmt_operator(right, f, level + 1)
fmt_operator(left, f, level + 1, false)?;
fmt_operator(right, f, level + 1, true)
}
Operator::Order { source, key, .. } => {
let sort_keys_string = key
@@ -344,7 +357,7 @@ impl Display for Operator {
.collect::<Vec<String>>()
.join(", ");
writeln!(f, "{}SORT {}", indent, sort_keys_string)?;
fmt_operator(source, f, level + 1)
fmt_operator(source, f, level + 1, true)
}
Operator::Projection {
source,
@@ -361,7 +374,7 @@ impl Display for Operator {
.collect::<Vec<String>>()
.join(", ");
writeln!(f, "{}PROJECT {}", indent, expressions)?;
fmt_operator(source, f, level + 1)
fmt_operator(source, f, level + 1, true)
}
Operator::Scan {
table,
@@ -369,7 +382,11 @@ impl Display for Operator {
table_identifier,
..
} => {
let table_name = format!("{} AS {}", &table.name, &table_identifier);
let table_name = if table.name == *table_identifier {
table.name.clone()
} else {
format!("{} AS {}", &table.name, &table_identifier)
};
let filter_string = filter.as_ref().map(|f| {
let filters_string = f
.iter()
@@ -387,7 +404,8 @@ impl Display for Operator {
Operator::Nothing => Ok(()),
}
}
fmt_operator(self, f, 0)
writeln!(f, "QUERY PLAN")?;
fmt_operator(self, f, 0, true)
}
}