use crate::{
ast::*,
fmt::Formatter,
parser::{Comment, CommentStyle, Location, Metadata},
};
pub(super) trait Literal<'s, ID>
where
ID: Copy + Ord,
{
fn literal(&self) -> LiteralDisplay<'_, ID, Self>
where
Self: Sized,
{
LiteralDisplay {
lit: self,
_id: Default::default(),
}
}
}
pub(super) trait LiteralWith<'s, ID>
where
ID: Copy + Ord,
{
fn literal_with<'m, M: Metadata<'s, NodeId = ID>>(&self, meta: &'m M)
-> impl std::fmt::Display;
}
impl<'s, ID, L> Literal<'s, ID> for L
where
ID: Copy + Ord,
L: LiteralWith<'s, ID>,
{
}
pub(super) struct LiteralDisplay<'l, ID, L> {
lit: &'l L,
_id: std::marker::PhantomData<ID>,
}
impl<'l, 's, ID, L> LiteralDisplay<'l, ID, L>
where
ID: Copy + Ord,
L: LiteralWith<'s, ID>,
{
pub fn with<'m, M: Metadata<'s, NodeId = ID>>(self, meta: &'m M) -> impl std::fmt::Display {
self.lit.literal_with(meta)
}
}
impl<'l, 's, ID, L> std::fmt::Display for LiteralDisplay<'l, ID, L>
where
ID: Copy + Ord,
L: LiteralWith<'s, ID>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.lit.literal_with(&NoMetadata::default()).fmt(f)
}
}
struct NoMetadata<'s, ID> {
_lifetime: std::marker::PhantomData<&'s ()>,
_id: std::marker::PhantomData<ID>,
}
impl<'s, ID> Default for NoMetadata<'s, ID> {
fn default() -> Self {
Self {
_lifetime: Default::default(),
_id: Default::default(),
}
}
}
impl<'s, ID> Metadata<'s> for NoMetadata<'s, ID>
where
ID: Copy + Ord,
{
type NodeId = ID;
fn location(&self, _node: Self::NodeId) -> Location {
Location { line: 0, col: 0 }
}
fn comments(&self, _node: Self::NodeId) -> (&[Comment<'s>], &[Comment<'s>]) {
(&[][..], &[][..])
}
}
struct ContextFormattedDisplay<'r, 'm, R, M>(&'r R, &'m M);
impl<'r, 'm, 's, R, M> std::fmt::Display for ContextFormattedDisplay<'r, 'm, R, M>
where
M: Metadata<'s>,
R: ContextFormatted<'s, M>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.cfmt(&mut ContextWriter::new(f, self.1))
}
}
macro_rules! literal_with_impl {
($( $type:ident ),* $(,)?) => {
$(
impl<'s, ID> LiteralWith<'s, ID> for $type<'s, ID>
where
ID: Copy + Ord,
{
fn literal_with<'r, M: Metadata<'s, NodeId = ID>>(&self, meta: &'r M) -> impl std::fmt::Display {
ContextFormattedDisplay(self, meta)
}
}
)*
};
}
literal_with_impl!(Expr, Condition, Statement);
#[derive(Copy, Clone)]
enum Whitespace {
None,
Present,
Delayed,
}
#[derive(Copy, Clone)]
enum DebugMode {
None,
Default,
Verbose,
}
struct ContextWriter<'f, 'a, 'm, M> {
fmt: &'f mut std::fmt::Formatter<'a>,
debug_mode: DebugMode,
meta: &'m M,
last_comment: Location,
last_whitespace: Whitespace,
}
impl<'f, 'a, 'm, M> ContextWriter<'f, 'a, 'm, M> {
fn new(fmt: &'f mut std::fmt::Formatter<'a>, meta: &'m M) -> Self {
let debug_mode = match (fmt.alternate(), fmt.sign_plus()) {
(true, true) => DebugMode::Verbose,
(true, false) => DebugMode::Default,
(false, _) => DebugMode::None,
};
Self {
fmt,
meta,
debug_mode,
last_comment: (0, 0).into(),
last_whitespace: Whitespace::Present,
}
}
fn on_first_char_(&mut self, c: char) -> std::fmt::Result {
if matches!(self.last_whitespace, Whitespace::Delayed) {
if c.is_ascii_whitespace() {
self.last_whitespace = Whitespace::Present;
} else {
std::fmt::Write::write_char(self.fmt, ' ')?;
}
}
Ok(())
}
fn on_last_char_(&mut self, c: char) {
self.last_whitespace = if c.is_ascii_whitespace() {
Whitespace::Present
} else {
Whitespace::None
};
}
fn section<F: FnOnce(&mut Self) -> std::fmt::Result>(
&mut self,
name: &str,
f: F,
) -> std::fmt::Result {
match self.debug_mode {
DebugMode::None => f(self),
DebugMode::Default => {
self.write_char('{')?;
f(self)?;
self.write_char('}')?;
Ok(())
}
DebugMode::Verbose => {
self.write_str(name)?;
self.write_char('{')?;
f(self)?;
self.write_char('}')?;
Ok(())
}
}
}
fn with_debug_mode<F: FnOnce(&mut Self) -> std::fmt::Result>(
&mut self,
debug_mode: DebugMode,
f: F,
) -> std::fmt::Result {
let prev = self.debug_mode;
self.debug_mode = debug_mode;
let r = f(self);
self.debug_mode = prev;
r
}
}
impl<'f, 'a, 'm, M> crate::fmt::Formatter for ContextWriter<'f, 'a, 'm, M> {
fn write_char(&mut self, c: char) -> std::fmt::Result {
self.on_first_char_(c)?;
std::fmt::Write::write_char(self.fmt, c)?;
self.on_last_char_(c);
Ok(())
}
fn write_str(&mut self, s: &str) -> std::fmt::Result {
if let Some(c) = s.chars().next() {
self.on_first_char_(c)?;
}
self.fmt.write_str(s)?;
if let Some(c) = s.chars().last() {
self.on_last_char_(c);
}
Ok(())
}
}
impl<'f, 'a, 's, 'm, M> ContextWriter<'f, 'a, 'm, M>
where
M: Metadata<'s>,
{
fn with_comments<T, F>(&mut self, node: &Node<T, M::NodeId>, f: F) -> std::fmt::Result
where
F: FnOnce(&T, &mut Self) -> std::fmt::Result,
{
let (before, after) = self.meta.comments(node.1);
self.emit_comments(before)?;
f(&node.0, self)?;
self.emit_comments(after)
}
fn emit_comments(&mut self, comments: &[Comment<'_>]) -> std::fmt::Result {
fn write_comment<'s, M: Metadata<'s>>(
f: &mut ContextWriter<'_, '_, '_, M>,
c: &Comment<'_>,
) -> std::fmt::Result {
match f.last_whitespace {
Whitespace::None => {
f.write_char(' ')?;
}
Whitespace::Present => {
}
Whitespace::Delayed => {
f.write_char(' ')?;
}
}
let (prefix, suffix, whitespace) = match c.style {
CommentStyle::Line => ("--", "\n", Whitespace::Present),
CommentStyle::Block => ("/*", "*/", Whitespace::Delayed),
};
f.fmt.write_str(prefix)?;
f.fmt.write_str(c.text)?;
f.fmt.write_str(suffix)?;
f.last_whitespace = whitespace;
Ok(())
}
let last_comment = self.last_comment;
match comments {
[] => {}
[x] => {
if last_comment != x.loc {
for c in comments {
write_comment(self, c)?;
}
self.last_comment = x.loc;
}
}
[_, .., y] => {
if last_comment != y.loc {
for c in comments {
write_comment(self, c)?;
}
self.last_comment = y.loc;
}
}
}
Ok(())
}
}
trait ContextFormatted<'s, M: Metadata<'s>> {
fn cfmt(&self, f: &mut ContextWriter<'_, '_, '_, M>) -> std::fmt::Result;
}
macro_rules! context_formatted {
(fn $type:ident::cfmt(&$self:ident, $f:ident) $body:block) => {
impl<'_s, _ID, _M: Metadata<'_s, NodeId = _ID>> ContextFormatted<'_s, _M>
for $type<'_s, _ID>
{
fn cfmt(&$self, $f: &mut ContextWriter<'_, '_, '_, _M>) -> std::fmt::Result {
$body
}
}
};
(fn $type:ident<$source_lifetime:lifetime>::cfmt(&$self:ident, $f:ident) $body:block) => {
impl<$source_lifetime, _ID, _M: Metadata<$source_lifetime, NodeId = _ID>>
ContextFormatted<$source_lifetime, _M> for $type<$source_lifetime>
{
fn cfmt(&$self, $f: &ContextWriter<'_, '_, '_, _M>) -> std::fmt::Result {
$body
}
}
};
(fn $type:ident<$ID:ident>::cfmt(&$self:ident, $f:ident) $body:block) => {
impl<'_s, $ID, _M: Metadata<'_s, NodeId = $ID>> ContextFormatted<'_s, _M>
for $type<$ID>
{
fn cfmt(&$self, $f: &mut ContextWriter<'_, '_, '_, _M>) -> std::fmt::Result {
$body
}
}
};
}
context_formatted! {
fn Identifier::cfmt(&self, f) {
f.section("Identifier", |f| {
match self {
Identifier::Simple(node) => f.with_comments(node, crate::fmt::Display::fmt),
Identifier::Qualified(nodes) => {
if let Some((ident, rest)) = nodes.split_first() {
f.with_comments(ident, crate::fmt::Display::fmt)?;
for ident in rest {
f.write_str(".")?;
f.with_comments(ident, crate::fmt::Display::fmt)?;
}
}
Ok(())
}
}
})
}
}
context_formatted! {
fn Expr::cfmt(&self, f) {
match self {
Expr::Value(value) => f.with_comments(value, crate::fmt::Display::fmt),
Expr::Identifier(ident) => ident.cfmt(f),
Expr::Nested(node) => f.with_comments(node, |expr,f| {
f.write_char('(')?;
expr.cfmt(f)?;
f.write_char(')')
}),
Expr::SubQuery(node) => f.with_comments(node, |query, f| {
f.write_char('(')?;
query.cfmt(f)?;
f.write_char(')')
}),
Expr::Unary(unary) => {
f.section("Expr::Unary", |f| {
f.with_comments(&unary.op, |op, f| {
f.write_str(match op {
UnaryExprOp::Add => "+",
UnaryExprOp::Sub => "-",
})
})?;
unary.expr.cfmt(f)?;
Ok(())
})
}
Expr::Binary(binary) => {
f.section("Expr::Binary", |f| {
binary.left.cfmt(f)?;
f.with_comments(&binary.op, |op, f| {
f.write_str(match op {
BinaryExprOp::Add => " + ",
BinaryExprOp::Sub => " - ",
BinaryExprOp::Mul => " * ",
BinaryExprOp::Div => " / ",
BinaryExprOp::Concat => " || ",
})
})?;
binary.right.cfmt(f)
})
},
Expr::Case(case) => {
f.section("Expr::Case", |f| {
let CaseExpr { case_token, when_branches, else_branch, end_token } = &**case;
f.with_comments(case_token, |_, f| f.write_str("CASE"))?;
match when_branches {
CaseWhens::Simple { case_expr, whens } => {
f.write_char(' ')?;
case_expr.cfmt(f)?;
for CaseWhenSimple { when_token, when_expr, then_token, return_expr } in whens {
f.write_char(' ')?;
f.section("Expr::Case::When", |f| {
f.with_comments(when_token, |_ , f| f.write_str("WHEN "))?;
when_expr.cfmt(f)?;
f.with_comments(then_token, |_, f| f.write_str(" THEN "))?;
return_expr.cfmt(f)
})?;
}
},
CaseWhens::Searched { whens } => {
for CaseWhenSearched { when_token, when_condition, then_token, return_expr } in whens {
f.write_char(' ')?;
f.section("Expr::Case::When", |f| {
f.with_comments(when_token, |_, f| f.write_str("WHEN "))?;
when_condition.cfmt(f)?;
f.with_comments(then_token, |_, f| f.write_str(" THEN "))?;
return_expr.cfmt(f)
})?;
}
},
}
if let Some(CaseElse { else_token, return_expr }) = else_branch {
f.write_char(' ')?;
f.section("Expr::Case::Else", |f| {
f.with_comments(else_token, |_, f| f.write_str("ELSE "))?;
return_expr.cfmt(f)
})?;
}
f.with_comments(end_token, |_, f| f.write_str(" END"))
})
}
Expr::Function(func) => f.section("Expr::Function", |f| {
func.name.cfmt(f)?;
func.params.cfmt(f)?;
Ok(())
}),
}
}
}
context_formatted! {
fn FunctionParams::cfmt(&self, f) {
let Self {args,within_group, from_row, respect_nulls, over } = self;
f.with_comments(args, |args, f| {
f.write_char('(')?;
match args {
FunctionArgs::None(node) => f.with_comments(node, |_, _| Ok(()))?,
FunctionArgs::List(FunctionArgsList { duplicates, args }) => {
let sep = cfmt_duplicates_opt(f, duplicates)?;
if let Some((arg, rest)) = args.split_first() {
f.write_str(sep)?;
arg.cfmt(f)?;
for arg in rest {
f.write_str(", ")?;
arg.cfmt(f)?;
}
}
}
}
f.write_char(')')
})?;
if let Some(within_group) = within_group {
f.write_char(' ')?;
within_group.cfmt(f)?;
}
if let Some(from_row) = from_row {
f.write_char(' ')?;
from_row.cfmt(f)?;
}
if let Some(respect_nulls) = respect_nulls {
f.write_char(' ')?;
respect_nulls.cfmt(f)?;
}
if let Some(over) = over {
f.write_char(' ')?;
over.cfmt(f)?;
}
Ok(())
}
}
context_formatted! {
fn FunctionArg::cfmt(&self, f) {
let Self { arg, clause } = self;
arg.cfmt(f)?;
if let Some(clause) = clause {
f.write_char(' ')?;
clause.cfmt(f)?;
}
Ok(())
}
}
context_formatted! {
fn FunctionArgType::cfmt(&self, f) {
match self {
FunctionArgType::Wildcard(wildcard) => {
f.with_comments(wildcard, |_, f| f.write_char('*'))
}
FunctionArgType::ExtractExpr(expr) => expr.cfmt(f),
FunctionArgType::Named(NamedFunctionArg { name, operator_token, arg }) => {
f.with_comments(name, crate::fmt::Display::fmt)?;
f.write_char(' ')?;
f.with_comments(operator_token, |_, f| f.write_str("=>"))?;
f.write_char(' ')?;
arg.cfmt(f)
},
FunctionArgType::Expr(expr) => expr.cfmt(f),
}
}
}
context_formatted! {
fn FunctionArgClause::cfmt(&self, f) {
match self {
FunctionArgClause::UsingCharset(clause) => clause.cfmt(f),
FunctionArgClause::RespectNulls(clause) => clause.cfmt(f),
FunctionArgClause::OnOverflow(clause) => clause.cfmt(f),
}
}
}
context_formatted! {
fn UsingCharset<ID>::cfmt(&self, f) {
let Self {
using_token,
charset_token,
} = self;
f.with_comments(using_token, |_, f| f.write_str("USING"))?;
f.write_char(' ')?;
f.with_comments(charset_token, |charset, f| {
f.write_str(match charset {
Charset::Database => "CHAR_CS",
Charset::National => "NCHAR_CS",
})
})
}
}
context_formatted! {
fn ExtractExpr::cfmt(&self, f) {
let Self { datetime_token, from_token, expr } = self;
f.with_comments(datetime_token, |token, f| f.write_str(match token {
ExtractDatetime::Year => "YEAR",
ExtractDatetime::Month => "MONTH",
ExtractDatetime::Day => "DAY",
ExtractDatetime::Hour => "HOUR",
ExtractDatetime::Minute => "MINUTE",
ExtractDatetime::Second => "SECOND",
ExtractDatetime::TimezoneHour => "TIMEZONE_HOUR",
ExtractDatetime::TimezoneMinute => "TIMEZONE_MINUTE",
ExtractDatetime::TimezoneRegion => "TIMEZONE_REGION",
ExtractDatetime::TimezoneAbbreviation => "TIMEZONE_ABBR",
}))?;
f.write_char(' ')?;
f.with_comments(from_token, |_, f| f.write_str("FROM"))?;
f.write_char(' ')?;
expr.cfmt(f)
}
}
context_formatted! {
fn OnOverflow::cfmt(&self, f) {
f.section("OnOverflow", |f| {
let OnOverflow { on_token, overflow_token, action } = self;
f.with_comments(on_token, |_, f| f.write_str("ON"))?;
f.write_char(' ')?;
f.with_comments(overflow_token, |_, f| f.write_str("OVERFLOW"))?;
f.write_char(' ')?;
match action {
OnOverflowAction::Error { error_token } => {
f.with_comments(error_token, |_, f| f.write_str("ERROR"))
}
OnOverflowAction::Truncate { truncate_token, indicator, with_count } => {
f.with_comments(truncate_token, |_, f| f.write_str("TRUNCATE"))?;
if let Some(indicator) = indicator {
f.write_char(' ')?;
indicator.cfmt(f)?;
}
if let Some(OnOverflowCount { count_option, count_token }) = with_count {
f.write_char(' ')?;
f.with_comments(count_option, |count_option, f| f.write_str(match count_option {
OnOverflowCountOption::With => "WITH",
OnOverflowCountOption::Without => "WITHOUT",
}))?;
f.write_char(' ')?;
f.with_comments(count_token, |_, f| f.write_str("COUNT"))?;
}
Ok(())
},
}
})
}
}
context_formatted! {
fn FromRow<ID>::cfmt(&self, f) {
let Self {
from_token,
type_token,
} = self;
f.section("FromRow", |f| {
f.with_comments(from_token, |_, f| f.write_str("FROM"))?;
f.write_char(' ')?;
f.with_comments(type_token, |t, f| {
f.write_str(match t {
FromRowType::First => "FIRST",
FromRowType::Last => "LAST",
})
})
})
}
}
context_formatted! {
fn RespectNulls<ID>::cfmt(&self, f) {
let Self {
type_token,
nulls_token,
} = self;
f.section("NullsInclusion", |f| {
f.with_comments(type_token, |t, f| {
f.write_str(match t {
RespectNullsType::Respect => "RESPECT",
RespectNullsType::Ignore => "IGNORE",
})
})?;
f.write_char(' ')?;
f.with_comments(nulls_token, |_, f| f.write_str("NULLS"))
})
}
}
context_formatted! {
fn WithinGroup::cfmt(&self, f) {
let WithinGroup { within_token, group_token, order_by } = self;
f.section("WithinGroup", |f| {
f.with_comments(within_token, |_, f| f.write_str("WITHIN"))?;
f.write_char(' ')?;
f.with_comments(group_token, |_, f| f.write_str("GROUP"))?;
f.write_char(' ')?;
f.with_comments(order_by, |order_by, f| {
f.write_char('(')?;
order_by.cfmt(f)?;
f.write_char(')')
})
})
}
}
context_formatted! {
fn FunctionWindow::cfmt(&self, f) {
let FunctionWindow { over_token, window } = self;
f.section("FunctionWindow", |f| {
f.with_comments(over_token, |_, f| f.write_str("OVER"))?;
match &window {
FunctionWindowType::Named(name) => {
f.write_char(' ')?;
f.with_comments(name, crate::fmt::Display::fmt)
}
FunctionWindowType::Specified(spec) => {
f.write_char(' ')?;
f.with_comments(spec, |spec, f| spec.cfmt(f))
}
}
})
}
}
context_formatted! {
fn WindowFrameBoundType::cfmt(&self, f) {
fn direction_to_str(d: &WindowFrameBoundDirection) -> &'static str {
match d {
WindowFrameBoundDirection::Preceding => "PRECEDING",
WindowFrameBoundDirection::Following => "FOLLOWING",
}
}
match self {
WindowFrameBoundType::Unbounded { unbounded_token, direction_token } => {
f.with_comments(unbounded_token, |_, f| f.write_str("UNBOUNDED"))?;
f.write_char(' ')?;
f.with_comments(direction_token, |direction, f| f.write_str(direction_to_str(direction)))?;
},
WindowFrameBoundType::CurrentRow { current_token, row_token } => {
f.with_comments(current_token, |_, f| f.write_str("CURRENT"))?;
f.write_char(' ')?;
f.with_comments(row_token, |_, f| f.write_str("ROW"))?;
},
WindowFrameBoundType::Value { expr, direction_token } => {
expr.cfmt(f)?;
f.write_char(' ')?;
f.with_comments(direction_token, |direction, f| f.write_str(direction_to_str(direction)))?;
},
}
Ok(())
}
}
context_formatted! {
fn WindowSpec::cfmt(&self, f) {
f.write_char('(')?;
match self {
WindowSpec::Empty(empty) => {
f.with_comments(empty, |_, _| Ok(()))?;
}
WindowSpec::Details(WindowSpecDetails { window_name, partition_by, order_by, frame }) => {
let mut produced = false;
if let Some(window_name) = window_name {
f.with_comments(window_name, crate::fmt::Display::fmt)?;
produced = true;
}
if let Some(partition_by) = partition_by {
if produced {
f.write_char(' ')?;
}
partition_by.cfmt(f)?;
produced = true;
}
if let Some(order_by) = order_by {
if produced {
f.write_char(' ')?;
}
order_by.cfmt(f)?;
}
if let Some(WindowFrame {unit_token, bounds, exclude}) = frame {
f.write_char(' ')?;
f.section("WindowSpecDetails::WindowFrame", |f| {
f.with_comments(unit_token, |unit, f| f.write_str(match unit {
WindowFrameUnit::Rows => "ROWS",
WindowFrameUnit::Range => "RANGE",
WindowFrameUnit::Groups => "GROUPS",
}))?;
f.write_char(' ')?;
match bounds {
WindowFrameBounds::Between { between_token, start, and_token, end } => {
f.with_comments(between_token, |_, f| f.write_str("BETWEEN"))?;
f.write_char(' ')?;
start.cfmt(f)?;
f.write_char(' ')?;
f.with_comments(and_token, |_, f| f.write_str("AND"))?;
f.write_char(' ')?;
end.cfmt(f)?;
}
WindowFrameBounds::Start(bound) => bound.cfmt(f)?,
}
if let Some(WindowFrameExclude { exclude_token, subject }) = exclude {
f.write_char(' ')?;
f.with_comments(exclude_token, |_, f| f.write_str("EXCLUDE"))?;
f.write_char(' ')?;
match subject {
WindowFrameExcludeSubject::CurrentRow { current_token, row_token } => {
f.with_comments(current_token, |_, f| f.write_str("CURRENT"))?;
f.write_char(' ')?;
f.with_comments(row_token, |_, f| f.write_str("ROW"))?;
},
WindowFrameExcludeSubject::Group { group_token } => {
f.with_comments(group_token, |_, f| f.write_str("GROUP"))?;
}
WindowFrameExcludeSubject::Ties { ties_token } => {
f.with_comments(ties_token, |_, f| f.write_str("TIES"))?;
},
WindowFrameExcludeSubject::NoOthers { no_token, others_token } => {
f.with_comments(no_token, |_, f| f.write_str("NO"))?;
f.write_char(' ')?;
f.with_comments(others_token, |_, f| f.write_str("OTHERS"))?;
}
}
}
Ok(())
})?;
}
}
}
f.write_char(')')
}
}
struct BorrowedExprList<'r, 's, ID>(&'r Node<Vec<Expr<'s, ID>>, ID>);
impl<'r, 's, ID, M: Metadata<'s, NodeId = ID>> ContextFormatted<'s, M>
for BorrowedExprList<'r, 's, ID>
{
fn cfmt(&self, f: &mut ContextWriter<'_, '_, '_, M>) -> std::fmt::Result {
f.with_comments(self.0, |exprs, f| {
f.write_char('(')?;
if let Some((expr, more)) = exprs.split_first() {
expr.cfmt(f)?;
for expr in more {
f.write_str(", ")?;
expr.cfmt(f)?;
}
}
f.write_char(')')
})
}
}
context_formatted! {
fn Statement::cfmt(&self, f) {
match &self.statement {
StatementType::Empty => {},
StatementType::Select(select) => select.cfmt(f)?,
};
if let Some(terminator) = self.terminator.as_ref() {
f.with_comments(terminator, |_, f| f.write_char(';'))?;
}
Ok(())
}
}
context_formatted! {
fn Select::cfmt(&self, f) {
let Select { query, for_update } = self;
query.cfmt(f)?;
if let Some(for_update) = for_update {
f.write_char(' ')?;
for_update.cfmt(f)?;
}
Ok(())
}
}
context_formatted! {
fn ForUpdate::cfmt(&self, f) {
f.section("ForUpdate", |f| {
let ForUpdate { for_token, update_token, of_columns, lock_opts } = self;
f.with_comments(for_token, |_, f| f.write_str("FOR"))?;
f.write_char(' ')?;
f.with_comments(update_token, |_, f| f.write_str("UPDATE"))?;
if let Some(of_columns) = of_columns {
f.write_char(' ')?;
of_columns.cfmt(f)?;
}
if let Some(lock_opts) = lock_opts {
f.write_char(' ')?;
lock_opts.cfmt(f)?;
}
Ok(())
})
}
}
context_formatted! {
fn ForUpdateOfColumns::cfmt(&self, f) {
f.section("ForUpdateOfColumns", |f| {
f.with_comments(&self.of_token, |_, f| f.write_str("OF"))?;
f.write_char(' ')?;
if let Some((col, more)) = self.columns.split_first() {
col.cfmt(f)?;
for col in more {
f.write_str(", ")?;
col.cfmt(f)?;
}
}
Ok(())
})
}
}
context_formatted! {
fn ForUpdateLockOptions::cfmt(&self, f) {
f.section("ForUpdateLockOptions", |f| {
match self {
ForUpdateLockOptions::Skip { skip_token, locked_token } => {
f.with_comments(skip_token, |_, f| f.write_str("SKIP"))?;
f.write_char(' ')?;
f.with_comments(locked_token, |_, f| f.write_str("LOCKED"))?;
},
ForUpdateLockOptions::NoWait { nowait_token } => {
f.with_comments(nowait_token, |_, f| f.write_str("NOWAIT"))?;
},
ForUpdateLockOptions::Wait { wait_token, duration } => {
f.with_comments(wait_token, |_, f| f.write_str("WAIT"))?;
f.write_char(' ')?;
f.with_comments(duration, |duration, f| f.write_str(duration))?;
},
}
Ok(())
})
}
}
context_formatted! {
fn Query::cfmt(&self, f) {
let Query { with, body } = self;
if let Some(With { with_keyword, ctes }) = with {
f.section("Query::With", |f| {
f.with_comments(with_keyword, |_, f| f.write_str("WITH "))?;
if let Some((table, more)) = ctes.split_first() {
table.cfmt(f)?;
for table in more {
f.write_str(", ")?;
table.cfmt(f)?;
}
}
Ok(())
})?;
f.write_char(' ')?;
}
body.cfmt(f)
}
}
context_formatted! {
fn QueryBody::cfmt(&self, f) {
let QueryBody { block, order_by, row_limit } = self;
block.cfmt(f)?;
if let Some(order_by) = order_by.as_ref() {
f.write_char(' ')?;
order_by.cfmt(f)?;
}
if let Some(RowLimitOffset { offset_token, offset_value, row_token }) = &row_limit.offset {
f.write_char(' ')?;
f.section("QueryBody::RowLimit::Offset", |f| {
f.with_comments(offset_token, |_, f| f.write_str("OFFSET"))?;
f.write_char(' ')?;
offset_value.cfmt(f)?;
f.write_char(' ')?;
f.with_comments(row_token, |t, f| f.write_str(row_token_as_str(t)))
})?;
}
if let Some(RowLimitFetch{
fetch_token,
first_token,
row_token,
fetch_amount,
fetch_amount_spec,
}) = &row_limit.fetch {
f.write_char(' ')?;
f.section("QueryBody::RowLimit::Fetch", |f| {
f.with_comments(fetch_token, |_, f| f.write_str("FETCH"))?;
f.write_char(' ')?;
f.with_comments(first_token, |t, f| f.write_str(match t {
FetchFirstKeyword::First => "FIRST",
FetchFirstKeyword::Next => "NEXT",
}))?;
f.write_char(' ')?;
match fetch_amount {
FetchAmount::Count(expr) => expr.cfmt(f)?,
FetchAmount::Percent { amount, percent_token } => {
amount.cfmt(f)?;
f.write_char(' ')?;
f.with_comments(percent_token, |_, f| f.write_str("PERCENT"))?;
},
}
f.write_char(' ')?;
f.with_comments(row_token, |t, f| f.write_str(row_token_as_str(t)))?;
f.write_char(' ')?;
match fetch_amount_spec {
FetchAmountSpec::Only { only_token } => f.with_comments(only_token, |_, f| f.write_str("ONLY"))?,
FetchAmountSpec::WithTies { with_token, ties_token } => {
f.with_comments(with_token, |_, f| f.write_str("WITH"))?;
f.write_char(' ')?;
f.with_comments(ties_token, |_, f| f.write_str("TIES"))?;
}
}
Ok(())
})?;
}
Ok(())
}
}
fn row_token_as_str(t: &RowKeyword) -> &'static str {
match t {
RowKeyword::Row => "ROW",
RowKeyword::Rows => "ROWS",
}
}
context_formatted! {
fn QueryBlock::cfmt(&self, f) {
match self {
QueryBlock::Select(block) => block.cfmt(f),
QueryBlock::Composed(left, op, right) =>
f.section("QueryBlock::Composed", |f| {
f.section("QueryBlock::Composed::Left", |f| left.cfmt(f))?;
f.with_comments(&op.operation, |op, f| f.write_str(match op {
ComposeOperation::Union => " UNION ",
ComposeOperation::Intersect => " INTERSECT ",
ComposeOperation::Minus => " MINUS ",
ComposeOperation::Except => " EXCEPT ",
}))?;
if let Some(all_token) = &op.all_token {
f.with_comments(all_token, |_, f| f.write_str("ALL "))?;
}
f.section("QueryBlock::Composed::Right", |f| right.cfmt(f))
}),
QueryBlock::Nested(node) => {
f.with_comments(node, |query, f| {
f.write_char('(')?;
query.cfmt(f)?;
f.write_char(')')
})
},
}
}
}
context_formatted! {
fn QuerySelect::cfmt(&self, f) {
let QuerySelect { select_token, hint, duplicates, projection, from, selection, group_by, windows } = self;
f.with_comments(select_token, |_, f| f.write_str("SELECT "))?;
if let Some(duplicates) = duplicates {
f.with_comments(duplicates, Duplicates::cfmt)?;
f.write_char(' ')?;
}
if let Some(hint) = hint {
f.with_comments(hint, crate::fmt::Display::fmt)?;
f.write_char(' ')?;
}
if let Some((item, more)) = projection.split_first() {
item.cfmt(f)?;
for item in more {
f.write_str(", ")?;
item.cfmt(f)?;
}
}
f.with_comments(&from.from_token, |_, f| f.write_str(" FROM "))?;
if let Some((table, more)) = from.tables.split_first() {
table.cfmt(f)?;
for table in more {
f.write_str(", ")?;
table.cfmt(f)?;
}
}
if let Some(selection) = selection.as_ref() {
f.with_comments(&selection.where_token, |_, f| f.write_str(" WHERE "))?;
selection.condition.cfmt(f)?;
}
if let Some(GroupBy{ group_token, by_token, group_type, having }) = group_by.as_ref() {
f.write_char(' ')?;
f.section("QuerySelect::GroupBy", |f| {
f.with_comments(group_token, |_, f| f.write_str("GROUP "))?;
f.with_comments(by_token, |_, f| f.write_str("BY "))?;
match group_type {
GroupByType::Exprs(exprs) => match exprs {
GroupByExprs::All(parens) => {
f.with_comments(parens, |inner, f| {
f.write_char('(')?;
f.with_comments(inner, |_, _| Ok(()))?;
f.write_char(')')
})?;
}
GroupByExprs::Exprs(exprs) => {
if let Some((expr, more)) = exprs.split_first() {
expr.cfmt(f)?;
for expr in more {
f.write_str(", ")?;
expr.cfmt(f)?;
}
}
}
GroupByExprs::Nested(nested) => BorrowedExprList(nested).cfmt(f)?,
},
}
if let Some(HavingCondition { having_token, condition }) = having {
f.write_char(' ')?;
f.with_comments(having_token, |_, f| f.write_str("HAVING "))?;
condition.cfmt(f)?;
}
Ok(())
})?;
}
if let Some(QueryWindows { window_token, windows }) = windows {
f.write_char(' ')?;
f.section("QuerySelect::Windows", |f| {
f.with_comments(window_token, |_, f| f.write_str("WINDOW"))?;
if let Some((w, more)) = windows.split_first() {
f.write_char(' ')?;
w.cfmt(f)?;
for w in more {
f.write_str(", ")?;
w.cfmt(f)?;
}
}
Ok(())
})?;
}
Ok(())
}
}
impl<'_s, _M: Metadata<'_s>> ContextFormatted<'_s, _M> for Duplicates {
fn cfmt(&self, f: &mut ContextWriter<'_, '_, '_, _M>) -> std::fmt::Result {
f.write_str(match self {
Duplicates::All => "ALL",
Duplicates::Distinct => "DISTINCT",
Duplicates::Unique => "UNIQUE",
})
}
}
fn cfmt_duplicates_opt<'s, M: Metadata<'s>>(
f: &mut ContextWriter<'_, '_, '_, M>,
duplicates: &Option<Node<Duplicates, M::NodeId>>,
) -> std::result::Result<&'static str, std::fmt::Error> {
Ok(if let Some(dup) = duplicates {
f.with_comments(dup, Duplicates::cfmt)?;
" "
} else {
""
})
}
context_formatted! {
fn QueryWindow::cfmt(&self, f) {
let QueryWindow { name, as_token, window } = self;
f.with_comments(name, crate::fmt::Display::fmt)?;
f.write_char(' ')?;
f.with_comments(as_token, |_, f| f.write_str("AS"))?;
f.write_char(' ')?;
f.with_comments(window, |w, f| w.cfmt(f))
}
}
context_formatted! {
fn CteTable::cfmt(&self, f) {
match self {
CteTable::SubQuery(cte_sub_query) => f.section("CteTable::SubQuery", |f| {
f.with_comments(&cte_sub_query.name, crate::fmt::Display::fmt)?;
if let Some(aliases) = cte_sub_query.column_aliases.as_ref() {
f.with_comments(aliases, |_, f| {
f.write_char('(')?;
if let Some((alias, more)) = aliases.split_first() {
f.with_comments(alias, crate::fmt::Display::fmt)?;
for alias in more {
f.write_str(", ")?;
f.with_comments(alias, crate::fmt::Display::fmt)?;
}
}
f.write_char(')')
})?;
}
f.with_comments(&cte_sub_query.as_token, |_, f| f.write_str(" AS "))?;
f.with_comments(&cte_sub_query.query, |query, f| {
f.write_char('(')?;
query.cfmt(f)?;
f.write_char(')')
})
}),
}
}
}
context_formatted! {
fn ProjectionItem::cfmt(&self, f) {
match self {
ProjectionItem::Expr(expr, as_alias) => {
if let Some(AsAlias { as_token, name }) = as_alias {
f.section("ProjectionItem::Expr::Aliased", |f| {
expr.cfmt(f)?;
if let Some(as_token) = as_token {
f.with_comments(as_token, |_, f| f.write_str(" AS"))?;
}
if let Some(name) = name {
f.with_comments(name, |ident, f| {
f.write_char(' ')?;
crate::fmt::Display::fmt(ident, f)
})?
}
Ok(())
})
} else {
expr.cfmt(f)
}
},
ProjectionItem::Wildcard(wildcard) => {
wildcard.cfmt(f)
}
}
}
}
context_formatted! {
fn ProjectionWildcard::cfmt(&self, f) {
match self {
ProjectionWildcard::Simple(node) => {
f.with_comments(node, |_, f| f.write_char('*'))
}
ProjectionWildcard::Qualified(ident, node) => {
f.section("ProjectionWildcard::Qualified", |f| {
f.with_debug_mode(DebugMode::None, |f| ident.cfmt(f))?;
f.write_char('.')?;
f.with_comments(node, |_, f| f.write_char('*'))
})
}
}
}
}
context_formatted! {
fn QueryTable::cfmt(&self, f) {
match self {
QueryTable::Table(QueryTableWithJoins { table, joins }) => {
table.cfmt(f)?;
for join in joins {
f.write_char(' ')?;
join.cfmt(f)?;
}
Ok(())
},
}
}
}
context_formatted! {
fn AliasedQueryTable::cfmt(&self, f) {
let AliasedQueryTable { table, alias } = self;
if let Some(alias) = alias {
f.section("AliasedQueryTable", |f| {
table.cfmt(f)?;
f.write_char(' ')?;
f.with_comments(alias, crate::fmt::Display::fmt)
})
} else {
table.cfmt(f)
}
}
}
context_formatted! {
fn QueryTableType::cfmt(&self, f) {
match self {
QueryTableType::Nested(nested) => {
f.with_comments(nested, |nested, f| {
f.write_char('(')?;
nested.cfmt(f)?;
f.write_char(')')
})
}
QueryTableType::Expr(expr) => expr.cfmt(f),
QueryTableType::Only { only_token, expression } =>
f.section("QueryTableType::Only", |f| {
f.with_comments(only_token, |_, f| f.write_str("ONLY "))?;
f.with_comments(expression, |expr, f| {
f.write_char('(')?;
expr.cfmt(f)?;
f.write_char(')')
})
}),
QueryTableType::Containers(ContainersTable { containers_token: token, table }) =>
f.section("QueryTableType::Containers", |f| {
f.with_comments(token, |_, f| f.write_str("CONTAINERS"))?;
f.with_comments(table, |table, f| {
f.write_char('(')?;
table.cfmt(f)?;
f.write_char(')')
})
}),
QueryTableType::Shards(ShardsTable { shards_token: token, table }) =>
f.section("QueryTableType::Shards", |f| {
f.with_comments(token, |_, f| f.write_str("SHARDS"))?;
f.with_comments(table, |table, f| {
f.write_char('(')?;
table.cfmt(f)?;
f.write_char(')')
})
}),
}
}
}
context_formatted! {
fn QueryTableExpression::cfmt(&self, f) {
match self {
QueryTableExpression::Name(ident) => ident.cfmt(f),
QueryTableExpression::SubQuery(SubQuery { lateral_token, query }) => {
if let Some(lateral_token) = lateral_token {
f.with_comments(lateral_token, |_, f| f.write_str("LATERAL "))?;
}
f.with_comments(query, |query, f| {
f.write_char('(')?;
query.cfmt(f)?;
f.write_char(')')
})
},
QueryTableExpression::Table(TableCollection { table_token, expr, as_outer_join }) =>
f.section("QueryTableExpression::Table", |f| {
f.with_comments(table_token, |_, f| f.write_str("TABLE"))?;
f.with_comments(expr, |expr, f| {
f.write_char('(')?;
expr.cfmt(f)?;
f.write_char(')')
})?;
if let Some(as_outer_join) = as_outer_join {
f.write_char(' ')?;
f.with_comments(as_outer_join, |as_outer_join, f| {
f.write_char('(')?;
f.with_comments(as_outer_join, |_, f| f.write_char('+'))?;
f.write_char(')')
})?;
}
Ok(())
}),
}
}
}
context_formatted! {
fn OrderBy::cfmt(&self, f) {
f.section("OrderBy", |f| {
f.with_comments(&self.order_token, |_, f| f.write_str("ORDER"))?;
if let Some(siblings) = self.siblings_token.as_ref() {
f.with_comments(siblings, |_, f| f.write_str(" SIBLINGS"))?;
}
f.with_comments(&self.by_token, |_, f| f.write_str(" BY "))?;
if let Some((order_expr, more)) = self.expressions.split_first() {
order_expr.cfmt(f)?;
for order_expr in more {
f.write_str(", ")?;
order_expr.cfmt(f)?;
}
}
Ok(())
})
}
}
context_formatted! {
fn OrderExpr::cfmt(&self, f) {
if self.direction.is_none() && self.nulls.is_none() {
self.expr.cfmt(f)
} else {
f.section("OrderExpr", |f| {
self.expr.cfmt(f)?;
if let Some(direction) = self.direction.as_ref() {
f.with_comments(direction, |direction, f| f.write_str(match direction {
OrderDirection::Asc => " ASC",
OrderDirection::Desc => " DESC",
}))?;
}
if let Some(OrderNulls { nulls_token, position }) = self.nulls.as_ref() {
f.with_comments(nulls_token, |_, f| f.write_str(" NULLS"))?;
f.with_comments(position, |position, f| f.write_str(match position {
OrderNullsPosition::First => " FIRST",
OrderNullsPosition::Last => " LAST",
}))?;
}
Ok(())
})
}
}
}
context_formatted! {
fn Condition::cfmt(&self, f) {
match self {
Condition::Nested(node) =>
f.with_comments(node, |nested, f| {
f.write_char('(')?;
nested.cfmt(f)?;
f.write_char(')')
}),
Condition::Exists(ExistsCondition { exists_token, query }) =>
f.section("Condition::Exists", |f| {
f.with_comments(exists_token, |_, f| f.write_str("EXISTS"))?;
f.write_char(' ')?;
f.with_comments(query, |query, f| {
f.write_char('(')?;
query.cfmt(f)?;
f.write_char(')')
})
}),
Condition::Between(between_condition) =>
f.section("Condition::Between", |f| {
let BetweenCondition {
expr,
not_token,
between_token,
range_from,
and_token,
range_upto
} = &**between_condition;
expr.cfmt(f)?;
if let Some(not_token) = not_token {
f.write_char(' ')?;
f.with_comments(not_token, |_, f| f.write_str("NOT"))?;
}
f.write_char(' ')?;
f.with_comments(between_token, |_, f| f.write_str("BETWEEN"))?;
f.write_char(' ')?;
range_from.cfmt(f)?;
f.with_comments(and_token, |_, f| f.write_str(" AND "))?;
range_upto.cfmt(f)
}),
Condition::In(in_condition) =>
f.section("Condition::In", |f| {
let InCondition { expr, not_token, in_token, values } = &**in_condition;
expr.cfmt(f)?;
if let Some(not_token) = not_token {
f.write_char(' ')?;
f.with_comments(not_token, |_, f| f.write_str("NOT"))?;
}
f.write_char(' ')?;
f.with_comments(in_token, |_, f| f.write_str("IN"))?;
f.write_char(' ')?;
values.cfmt(f)
}),
Condition::Compare(CompareCondition{ left, op: CompareOp { kind, quantifier }, right }) =>
f.section("Condition::Compare", |f| {
left.cfmt(f)?;
f.with_comments(kind, |kind, f| f.write_str(match kind {
CompareKind::Eq => " = ",
CompareKind::NotEq(sym) => match sym {
NotEqSymbol::Logical => " != ",
NotEqSymbol::Diamond => " <> ",
NotEqSymbol::Bitwise => " ^= ",
},
CompareKind::Lt => " < ",
CompareKind::LtEq => " <= ",
CompareKind::Gt => " > ",
CompareKind::GtEq => " >= ",
}))?;
if let Some(quantifier) = quantifier {
f.with_comments(quantifier, |quantifier, f|
f.write_str(match quantifier {
CompareQuantifier::All => "ALL ",
CompareQuantifier::Any => "ANY ",
CompareQuantifier::Some => "SOME ",
}))?;
}
right.cfmt(f)
}),
Condition::And(AndCondition { left, and_token, right }) =>
f.section("Condition::And", |f| {
left.cfmt(f)?;
f.with_comments(and_token, |_, f| f.write_str(" AND "))?;
right.cfmt(f)
}),
Condition::Or(OrCondition{ left, or_token, right }) =>
f.section("Condition::Or", |f| {
left.cfmt(f)?;
f.with_comments(or_token, |_, f| f.write_str(" OR "))?;
right.cfmt(f)
}),
Condition::Not(NotCondition { not_token, condition }) =>
f.section("Condition::Not", |f| {
f.with_comments(not_token, |_, f| f.write_str("NOT "))?;
condition.cfmt(f)
}),
Condition::Like(like_condition) =>
f.section("Condition::Like", |f| {
let LikeCondition { source, not_token, like_token, pattern, escape } = &**like_condition;
source.cfmt(f)?;
if let Some(not_token) = not_token {
f.write_char(' ')?;
f.with_comments(not_token, |_, f| f.write_str("NOT"))?;
}
f.write_char(' ')?;
f.with_comments(like_token, |like_variant, f| f.write_str(match like_variant {
LikeVariant::Like => "LIKE ",
LikeVariant::Like2 => "LIKE2 ",
LikeVariant::Like4 => "LIKE4 ",
LikeVariant::LikeC => "LIKEC ",
}))?;
pattern.cfmt(f)?;
if let Some(LikeEscape { escape_token, escape_char }) = escape {
f.with_comments(escape_token, |_, f| f.write_str(" ESCAPE "))?;
escape_char.cfmt(f)?;
}
Ok(())
}),
Condition::RegexpLike(regexp_like_condition) =>
f.section("Condition::RegexpLike", |f| {
let RegexpLikeCondition { regexp_like_token, params } = &**regexp_like_condition;
f.with_comments(regexp_like_token, |_, f| f.write_str("REGEXP_LIKE"))?;
f.with_comments(params, |RegexpLikeParams{ source, pattern, options }, f| {
f.write_char('(')?;
source.cfmt(f)?;
f.write_str(", ")?;
pattern.cfmt(f)?;
if let Some(options) = options {
f.write_str(", ")?;
options.cfmt(f)?;
}
f.write_char(')')
})
}),
Condition::IsNull(IsNullCondition { expr, is_token, not_token, null_token }) =>
f.section("Condition::IsNull", |f| {
expr.cfmt(f)?;
f.write_char(' ')?;
f.with_comments(is_token, |_, f| f.write_str("IS"))?;
if let Some(not_token) = not_token {
f.write_char(' ')?;
f.with_comments(not_token, |_, f| f.write_str("NOT"))?;
}
f.write_char(' ')?;
f.with_comments(null_token, |_, f| f.write_str("NULL"))
}),
Condition::IsFloat(IsFloatCondition { expr, is_token, not_token, float_type }) =>
f.section("Condition::IsFloat", |f| {
expr.cfmt(f)?;
f.write_char(' ')?;
f.with_comments(is_token, |_, f| f.write_str("IS"))?;
if let Some(not_token) = not_token {
f.write_char(' ')?;
f.with_comments(not_token, |_, f| f.write_str("NOT"))?;
}
f.write_char(' ')?;
f.with_comments(float_type, |float_type, f| f.write_str(match float_type {
FloatType::Nan => "NAN",
FloatType::Infinite => "INFINITE",
}))
})
}
}
}
context_formatted! {
fn CompareExpr::cfmt(&self, f) {
match self {
CompareExpr::Expr(expr) => expr.cfmt(f),
CompareExpr::List(exprs) => exprs.cfmt(f),
CompareExpr::Lists(exprss) => {
f.with_comments(exprss, |exprss, f| {
f.write_char('(')?;
if let Some((exprs, more)) = exprss.split_first() {
exprs.cfmt(f)?;
for exprs in more {
f.write_str(", ")?;
exprs.cfmt(f)?;
}
}
f.write_char(')')
})
}
}
}
}
context_formatted! {
fn ExprList::cfmt(&self, f) {
BorrowedExprList(&self.0).cfmt(f)
}
}
context_formatted! {
fn TableJoin::cfmt(&self, f) {
f.section("TableJoin", |f| {
match self {
TableJoin::Inner(InnerTableJoin::Inner(InnerJoinedTable {
inner_token,
join_token,
table,
condition
})) => {
if let Some(inner_token) = inner_token {
f.with_comments(inner_token, |_, f| f.write_str("INNER "))?;
}
f.with_comments(join_token, |_, f| f.write_str("JOIN "))?;
table.cfmt(f)?;
f.write_char(' ')?;
condition.cfmt(f)?;
},
TableJoin::Inner(InnerTableJoin::Cross(CrossInnerJoinedTable {
cross_token,
join_token,
table
})) => {
f.with_comments(cross_token, |_, f| f.write_str("CROSS "))?;
f.with_comments(join_token, |_, f| f.write_str("JOIN "))?;
table.cfmt(f)?;
},
TableJoin::Inner(InnerTableJoin::Natural(NaturalInnerJoinedTable {
natural_token,
inner_token,
join_token,
table
})) => {
f.with_comments(natural_token, |_, f| f.write_str("NATURAL "))?;
if let Some(inner_token) = inner_token {
f.with_comments(inner_token, |_, f| f.write_str("INNER "))?;
}
f.with_comments(join_token, |_, f| f.write_str("JOIN "))?;
table.cfmt(f)?;
},
TableJoin::Outer(OuterTableJoin{
type_token,
outer_token,
join_token,
table,
partition_by,
condition
}) => {
if let Some(OuterTableJoinParitionBy::Left(partition_by)) = partition_by.as_ref() {
partition_by.cfmt(f)?;
f.write_char(' ')?;
}
if let OuterJoinCondition::Natural { natural_token } = condition {
f.with_comments(natural_token, |_, f| f.write_str("NATURAL "))?;
}
f.with_comments(type_token, |type_token, f| f.write_str(match type_token {
OuterJoinType::Full => "FULL ",
OuterJoinType::Left => "LEFT ",
OuterJoinType::Right => "RIGHT ",
}))?;
if let Some(outer_token) = outer_token.as_ref() {
f.with_comments(outer_token, |_, f| f.write_str("OUTER "))?;
}
f.with_comments(join_token, |_, f| f.write_str("JOIN "))?;
table.cfmt(f)?;
if let Some(OuterTableJoinParitionBy::Right(partition_by)) = partition_by.as_ref() {
f.write_char(' ')?;
partition_by.cfmt(f)?;
}
if let OuterJoinCondition::Regular(Some(join_condition)) = condition {
f.write_char(' ')?;
join_condition.cfmt(f)?;
}
},
TableJoin::Apply(ApplyTableJoin{ type_token, apply_token, table }) => {
f.with_comments(type_token, |type_token, f| f.write_str(match type_token {
ApplyJoinType::Cross => "CROSS ",
ApplyJoinType::Outer => "OUTER ",
}))?;
f.with_comments(apply_token, |_, f| f.write_str("APPLY "))?;
match table {
ApplyJoinTable::Table(table) => table.cfmt(f)?,
ApplyJoinTable::Expr(expr) => expr.cfmt(f)?,
}
},
}
Ok(())
})
}
}
context_formatted! {
fn PartitionBy::cfmt(&self, f) {
f.section("PartitionBy", |f| {
let PartitionBy{ partition_token, by_token, expressions } = self;
f.with_comments(partition_token, |_, f| f.write_str("PARTITION "))?;
f.with_comments(by_token, |_, f| f.write_str("BY "))?;
match expressions {
PartitionByExprs::Exprs(exprs) => {
if let Some((expr, more)) = exprs.split_first() {
expr.cfmt(f)?;
for expr in more {
f.write_str(", ")?;
expr.cfmt(f)?;
}
}
Ok(())
},
PartitionByExprs::List(list) => list.cfmt(f),
}
})
}
}
context_formatted! {
fn JoinCondition::cfmt(&self, f) {
match self {
JoinCondition::On(JoinConditionOn { on_token, condition }) => {
f.with_comments(on_token, |_, f| f.write_str("ON "))?;
condition.cfmt(f)?;
},
JoinCondition::Using(JoinConditionUsing { using_token, columns }) => {
f.with_comments(using_token, |_, f| f.write_str("USING "))?;
f.with_comments(columns, |columns, f| {
f.write_char('(')?;
if let Some((col, more)) = columns.split_first() {
f.with_comments(col, crate::fmt::Display::fmt)?;
for col in more {
f.write_str(", ")?;
f.with_comments(col, crate::fmt::Display::fmt)?;
}
}
f.write_char(')')
})?;
},
}
Ok(())
}
}