Skip to content

Commit

Permalink
feat(planner): support explain syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
b41sh committed Aug 17, 2022
1 parent f31c0ac commit 85a2bde
Show file tree
Hide file tree
Showing 20 changed files with 1,465 additions and 39 deletions.
25 changes: 25 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/query/ast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ logos = "0.12.1"
nom = "7.1.1"
nom-rule = "0.3.0"
pratt = "0.3.0"
pretty = "0.11.3"
thiserror = "1.0.31"
url = "2.2.2"

Expand Down
5 changes: 4 additions & 1 deletion src/query/ast/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,10 @@ impl<'a> Display for Expr<'a> {
}
write!(f, " END")?;
}
Expr::Exists { subquery, .. } => {
Expr::Exists { not, subquery, .. } => {
if *not {
write!(f, "NOT ")?;
}
write!(f, "EXISTS ({subquery})")?;
}
Expr::Subquery {
Expand Down
45 changes: 45 additions & 0 deletions src/query/ast/src/ast/format/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2022 Datafuse Labs.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod syntax;

use pretty::RcDoc;
pub use syntax::pretty_statement;

pub(crate) const NEST_FACTOR: isize = 4;

pub(crate) fn interweave_comma<'a, D>(docs: D) -> RcDoc<'a>
where D: Iterator<Item = RcDoc<'a>> {
RcDoc::intersperse(docs, RcDoc::text(",").append(RcDoc::line()))
}

pub(crate) fn inline_comma<'a, D>(docs: D) -> RcDoc<'a>
where D: Iterator<Item = RcDoc<'a>> {
RcDoc::intersperse(docs, RcDoc::text(",").append(RcDoc::space()))
}

pub(crate) fn inline_dot<'a, D>(docs: D) -> RcDoc<'a>
where D: Iterator<Item = RcDoc<'a>> {
RcDoc::intersperse(docs, RcDoc::text("."))
}

pub(crate) fn parenthenized(doc: RcDoc<'_>) -> RcDoc<'_> {
RcDoc::text("(")
.append(RcDoc::line_())
.append(doc)
.nest(NEST_FACTOR)
.append(RcDoc::line_())
.append(RcDoc::text(")"))
.group()
}
228 changes: 228 additions & 0 deletions src/query/ast/src/ast/format/syntax/ddl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// Copyright 2022 Datafuse Labs.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use pretty::RcDoc;

use super::expr::pretty_expr;
use super::query::pretty_query;
use crate::ast::interweave_comma;
use crate::ast::parenthenized;
use crate::ast::AlterTableAction;
use crate::ast::AlterTableStmt;
use crate::ast::AlterViewStmt;
use crate::ast::CreateTableSource;
use crate::ast::CreateTableStmt;
use crate::ast::CreateViewStmt;
use crate::ast::NEST_FACTOR;

pub(crate) fn pretty_create_table(stmt: CreateTableStmt) -> RcDoc {
RcDoc::text("CREATE")
.append(if stmt.transient {
RcDoc::space().append(RcDoc::text("TRANSIENT"))
} else {
RcDoc::nil()
})
.append(RcDoc::space().append(RcDoc::text("TABLE")))
.append(if stmt.if_not_exists {
RcDoc::space().append(RcDoc::text("IF NOT EXISTS"))
} else {
RcDoc::nil()
})
.append(
RcDoc::space()
.append(if let Some(catalog) = stmt.catalog {
RcDoc::text(catalog.to_string()).append(RcDoc::text("."))
} else {
RcDoc::nil()
})
.append(if let Some(database) = stmt.database {
RcDoc::text(database.to_string()).append(RcDoc::text("."))
} else {
RcDoc::nil()
})
.append(RcDoc::text(stmt.table.to_string())),
)
.append(if let Some(source) = stmt.source {
pretty_table_source(source)
} else {
RcDoc::nil()
})
.append(if let Some(engine) = stmt.engine {
RcDoc::space()
.append(RcDoc::text("ENGINE ="))
.append(RcDoc::space())
.append(engine.to_string())
} else {
RcDoc::nil()
})
.append(if !stmt.cluster_by.is_empty() {
RcDoc::line()
.append(RcDoc::text("CLUSTER BY "))
.append(parenthenized(
interweave_comma(stmt.cluster_by.into_iter().map(pretty_expr)).group(),
))
} else {
RcDoc::nil()
})
.append(if !stmt.table_options.is_empty() {
RcDoc::line()
.append(interweave_comma(stmt.table_options.iter().map(|(k, v)| {
RcDoc::text(k.clone())
.append(RcDoc::space())
.append(RcDoc::text("="))
.append(RcDoc::space())
.append(RcDoc::text("'"))
.append(RcDoc::text(v.clone()))
.append(RcDoc::text("'"))
})))
.group()
} else {
RcDoc::nil()
})
.append(if let Some(as_query) = stmt.as_query {
RcDoc::line().append(RcDoc::text("AS")).append(
RcDoc::line()
.nest(NEST_FACTOR)
.append(pretty_query(*as_query).nest(NEST_FACTOR).group()),
)
} else {
RcDoc::nil()
})
}

fn pretty_table_source(source: CreateTableSource) -> RcDoc {
match source {
CreateTableSource::Columns(columns) => RcDoc::space().append(parenthenized(
interweave_comma(
columns
.into_iter()
.map(|column| RcDoc::text(column.to_string())),
)
.group(),
)),
CreateTableSource::Like {
catalog,
database,
table,
} => RcDoc::space()
.append(RcDoc::text("LIKE"))
.append(RcDoc::space())
.append(if let Some(catalog) = catalog {
RcDoc::text(catalog.to_string()).append(RcDoc::text("."))
} else {
RcDoc::nil()
})
.append(if let Some(database) = database {
RcDoc::text(database.to_string()).append(RcDoc::text("."))
} else {
RcDoc::nil()
})
.append(RcDoc::text(table.to_string())),
}
}

pub(crate) fn pretty_alter_table(stmt: AlterTableStmt) -> RcDoc {
RcDoc::text("ALTER TABLE")
.append(if stmt.if_exists {
RcDoc::space().append(RcDoc::text("IF EXISTS"))
} else {
RcDoc::nil()
})
.append(
RcDoc::space()
.append(if let Some(catalog) = stmt.catalog {
RcDoc::text(catalog.to_string()).append(RcDoc::text("."))
} else {
RcDoc::nil()
})
.append(if let Some(database) = stmt.database {
RcDoc::text(database.to_string()).append(RcDoc::text("."))
} else {
RcDoc::nil()
})
.append(RcDoc::text(stmt.table.to_string())),
)
.append(pretty_alter_table_action(stmt.action))
}

pub(crate) fn pretty_alter_table_action(action: AlterTableAction) -> RcDoc {
match action {
AlterTableAction::RenameTable { new_table } => RcDoc::line()
.append(RcDoc::text("RENAME TO "))
.append(RcDoc::text(new_table.to_string())),
AlterTableAction::AlterTableClusterKey { cluster_by } => RcDoc::line()
.append(RcDoc::text("CLUSTER BY "))
.append(parenthenized(
interweave_comma(cluster_by.into_iter().map(pretty_expr)).group(),
)),
AlterTableAction::DropTableClusterKey => {
RcDoc::line().append(RcDoc::text("DROP CLUSTER KEY"))
}
}
}

pub(crate) fn pretty_create_view(stmt: CreateViewStmt) -> RcDoc {
RcDoc::text("CREATE VIEW")
.append(if stmt.if_not_exists {
RcDoc::space().append(RcDoc::text("IF NOT EXISTS"))
} else {
RcDoc::nil()
})
.append(
RcDoc::space()
.append(if let Some(catalog) = stmt.catalog {
RcDoc::text(catalog.to_string()).append(RcDoc::text("."))
} else {
RcDoc::nil()
})
.append(if let Some(database) = stmt.database {
RcDoc::text(database.to_string()).append(RcDoc::text("."))
} else {
RcDoc::nil()
})
.append(RcDoc::text(stmt.view.to_string())),
)
.append(
RcDoc::line().append(RcDoc::text("AS")).append(
RcDoc::line()
.nest(NEST_FACTOR)
.append(pretty_query(*stmt.query).nest(NEST_FACTOR).group()),
),
)
}

pub(crate) fn pretty_alter_view(stmt: AlterViewStmt) -> RcDoc {
RcDoc::text("ALTER VIEW")
.append(
RcDoc::space()
.append(if let Some(catalog) = stmt.catalog {
RcDoc::text(catalog.to_string()).append(RcDoc::text("."))
} else {
RcDoc::nil()
})
.append(if let Some(database) = stmt.database {
RcDoc::text(database.to_string()).append(RcDoc::text("."))
} else {
RcDoc::nil()
})
.append(RcDoc::text(stmt.view.to_string())),
)
.append(
RcDoc::line().append(RcDoc::text("AS")).append(
RcDoc::line()
.nest(NEST_FACTOR)
.append(pretty_query(*stmt.query).nest(NEST_FACTOR).group()),
),
)
}
Loading

0 comments on commit 85a2bde

Please sign in to comment.