implement fmt::Display instead of ToString

This commit is contained in:
Bernardo 2019-06-30 14:04:45 +02:00
parent cdba43682f
commit b2b159fed1
8 changed files with 605 additions and 550 deletions

View file

@ -11,6 +11,7 @@
// limitations under the License. // limitations under the License.
use super::ObjectName; use super::ObjectName;
use std::fmt;
/// SQL data types /// SQL data types
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -65,47 +66,53 @@ pub enum DataType {
Array(Box<DataType>), Array(Box<DataType>),
} }
impl ToString for DataType { impl fmt::Display for DataType {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
DataType::Char(size) => format_type_with_optional_length("char", size), DataType::Char(size) => format_type_with_optional_length(f, "char", size),
DataType::Varchar(size) => format_type_with_optional_length("character varying", size), DataType::Varchar(size) => {
DataType::Uuid => "uuid".to_string(), format_type_with_optional_length(f, "character varying", size)
DataType::Clob(size) => format!("clob({})", size), }
DataType::Binary(size) => format!("binary({})", size), DataType::Uuid => write!(f, "uuid"),
DataType::Varbinary(size) => format!("varbinary({})", size), DataType::Clob(size) => write!(f, "clob({})", size),
DataType::Blob(size) => format!("blob({})", size), DataType::Binary(size) => write!(f, "binary({})", size),
DataType::Varbinary(size) => write!(f, "varbinary({})", size),
DataType::Blob(size) => write!(f, "blob({})", size),
DataType::Decimal(precision, scale) => { DataType::Decimal(precision, scale) => {
if let Some(scale) = scale { if let Some(scale) = scale {
format!("numeric({},{})", precision.unwrap(), scale) write!(f, "numeric({},{})", precision.unwrap(), scale)
} else { } else {
format_type_with_optional_length("numeric", precision) format_type_with_optional_length(f, "numeric", precision)
} }
} }
DataType::Float(size) => format_type_with_optional_length("float", size), DataType::Float(size) => format_type_with_optional_length(f, "float", size),
DataType::SmallInt => "smallint".to_string(), DataType::SmallInt => write!(f, "smallint"),
DataType::Int => "int".to_string(), DataType::Int => write!(f, "int"),
DataType::BigInt => "bigint".to_string(), DataType::BigInt => write!(f, "bigint"),
DataType::Real => "real".to_string(), DataType::Real => write!(f, "real"),
DataType::Double => "double".to_string(), DataType::Double => write!(f, "double"),
DataType::Boolean => "boolean".to_string(), DataType::Boolean => write!(f, "boolean"),
DataType::Date => "date".to_string(), DataType::Date => write!(f, "date"),
DataType::Time => "time".to_string(), DataType::Time => write!(f, "time"),
DataType::Timestamp => "timestamp".to_string(), DataType::Timestamp => write!(f, "timestamp"),
DataType::Interval => "interval".to_string(), DataType::Interval => write!(f, "interval"),
DataType::Regclass => "regclass".to_string(), DataType::Regclass => write!(f, "regclass"),
DataType::Text => "text".to_string(), DataType::Text => write!(f, "text"),
DataType::Bytea => "bytea".to_string(), DataType::Bytea => write!(f, "bytea"),
DataType::Array(ty) => format!("{}[]", ty.to_string()), DataType::Array(ty) => write!(f, "{}[]", ty),
DataType::Custom(ty) => ty.to_string(), DataType::Custom(ty) => write!(f, "{}", ty),
} }
} }
} }
fn format_type_with_optional_length(sql_type: &str, len: &Option<u64>) -> String { fn format_type_with_optional_length(
let mut s = sql_type.to_string(); f: &mut fmt::Formatter,
sql_type: &'static str,
len: &Option<u64>,
) -> fmt::Result {
write!(f, "{}", sql_type)?;
if let Some(len) = len { if let Some(len) = len {
s += &format!("({})", len); write!(f, "({})", len)?;
} }
s Ok(())
} }

View file

@ -1,6 +1,7 @@
//! AST types specific to CREATE/ALTER variants of `SQLStatement` //! AST types specific to CREATE/ALTER variants of `SQLStatement`
//! (commonly referred to as Data Definition Language, or DDL) //! (commonly referred to as Data Definition Language, or DDL)
use super::{DataType, Expr, Ident, ObjectName}; use super::{display_comma_separated, DataType, Expr, Ident, ObjectName};
use std::fmt;
/// An `ALTER TABLE` (`SQLStatement::SQLAlterTable`) operation /// An `ALTER TABLE` (`SQLStatement::SQLAlterTable`) operation
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -11,11 +12,11 @@ pub enum AlterTableOperation {
DropConstraint { name: Ident }, DropConstraint { name: Ident },
} }
impl ToString for AlterTableOperation { impl fmt::Display for AlterTableOperation {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
AlterTableOperation::AddConstraint(c) => format!("ADD {}", c.to_string()), AlterTableOperation::AddConstraint(c) => write!(f, "ADD {}", c),
AlterTableOperation::DropConstraint { name } => format!("DROP CONSTRAINT {}", name), AlterTableOperation::DropConstraint { name } => write!(f, "DROP CONSTRAINT {}", name),
} }
} }
} }
@ -46,36 +47,36 @@ pub enum TableConstraint {
}, },
} }
impl ToString for TableConstraint { impl fmt::Display for TableConstraint {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
TableConstraint::Unique { TableConstraint::Unique {
name, name,
columns, columns,
is_primary, is_primary,
} => format!( } => write!(
f,
"{}{} ({})", "{}{} ({})",
format_constraint_name(name), display_constraint_name(name),
if *is_primary { "PRIMARY KEY" } else { "UNIQUE" }, if *is_primary { "PRIMARY KEY" } else { "UNIQUE" },
columns.join(", ") display_comma_separated(columns)
), ),
TableConstraint::ForeignKey { TableConstraint::ForeignKey {
name, name,
columns, columns,
foreign_table, foreign_table,
referred_columns, referred_columns,
} => format!( } => write!(
f,
"{}FOREIGN KEY ({}) REFERENCES {}({})", "{}FOREIGN KEY ({}) REFERENCES {}({})",
format_constraint_name(name), display_constraint_name(name),
columns.join(", "), display_comma_separated(columns),
foreign_table.to_string(), foreign_table,
referred_columns.join(", ") display_comma_separated(referred_columns)
),
TableConstraint::Check { name, expr } => format!(
"{}CHECK ({})",
format_constraint_name(name),
expr.to_string()
), ),
TableConstraint::Check { name, expr } => {
write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
}
} }
} }
} }
@ -89,18 +90,13 @@ pub struct ColumnDef {
pub options: Vec<ColumnOptionDef>, pub options: Vec<ColumnOptionDef>,
} }
impl ToString for ColumnDef { impl fmt::Display for ColumnDef {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format!( write!(f, "{} {}", self.name, self.data_type)?;
"{} {}{}", for option in &self.options {
self.name, write!(f, " {}", option)?;
self.data_type.to_string(), }
self.options Ok(())
.iter()
.map(|c| format!(" {}", c.to_string()))
.collect::<Vec<_>>()
.join("")
)
} }
} }
@ -126,13 +122,9 @@ pub struct ColumnOptionDef {
pub option: ColumnOption, pub option: ColumnOption,
} }
impl ToString for ColumnOptionDef { impl fmt::Display for ColumnOptionDef {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format!( write!(f, "{}{}", display_constraint_name(&self.name), self.option)
"{}{}",
format_constraint_name(&self.name),
self.option.to_string()
)
} }
} }
@ -160,35 +152,39 @@ pub enum ColumnOption {
Check(Expr), Check(Expr),
} }
impl ToString for ColumnOption { impl fmt::Display for ColumnOption {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ColumnOption::*; use ColumnOption::*;
match self { match self {
Null => "NULL".to_string(), Null => write!(f, "NULL"),
NotNull => "NOT NULL".to_string(), NotNull => write!(f, "NOT NULL"),
Default(expr) => format!("DEFAULT {}", expr.to_string()), Default(expr) => write!(f, "DEFAULT {}", expr),
Unique { is_primary } => { Unique { is_primary } => {
if *is_primary { write!(f, "{}", if *is_primary { "PRIMARY KEY" } else { "UNIQUE" })
"PRIMARY KEY".to_string()
} else {
"UNIQUE".to_string()
}
} }
ForeignKey { ForeignKey {
foreign_table, foreign_table,
referred_columns, referred_columns,
} => format!( } => write!(
f,
"REFERENCES {} ({})", "REFERENCES {} ({})",
foreign_table.to_string(), foreign_table,
referred_columns.join(", ") display_comma_separated(referred_columns)
), ),
Check(expr) => format!("CHECK ({})", expr.to_string(),), Check(expr) => write!(f, "CHECK ({})", expr),
} }
} }
} }
fn format_constraint_name(name: &Option<Ident>) -> String { fn display_constraint_name<'a>(name: &'a Option<Ident>) -> impl fmt::Display + 'a {
name.as_ref() struct ConstraintName<'a>(&'a Option<Ident>);
.map(|name| format!("CONSTRAINT {} ", name)) impl<'a> fmt::Display for ConstraintName<'a> {
.unwrap_or_default() fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(name) = self.0 {
write!(f, "CONSTRAINT {} ", name)?;
}
Ok(())
}
}
ConstraintName(name)
} }

View file

@ -18,7 +18,7 @@ mod operator;
mod query; mod query;
mod value; mod value;
use std::ops::Deref; use std::fmt;
pub use self::data_type::DataType; pub use self::data_type::DataType;
pub use self::ddl::{ pub use self::ddl::{
@ -31,17 +31,41 @@ pub use self::query::{
}; };
pub use self::value::{DateTimeField, Value}; pub use self::value::{DateTimeField, Value};
/// Like `vec.join(", ")`, but for any types implementing ToString. struct DisplaySeparated<'a, T>
fn comma_separated_string<I>(iter: I) -> String
where where
I: IntoIterator, T: fmt::Display,
I::Item: Deref,
<I::Item as Deref>::Target: ToString,
{ {
iter.into_iter() slice: &'a [T],
.map(|t| t.deref().to_string()) sep: &'static str,
.collect::<Vec<String>>() }
.join(", ")
impl<'a, T> fmt::Display for DisplaySeparated<'a, T>
where
T: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut delim = "";
for t in self.slice {
write!(f, "{}", delim)?;
delim = self.sep;
write!(f, "{}", t)?;
}
Ok(())
}
}
fn display_separated<'a, T>(slice: &'a [T], sep: &'static str) -> DisplaySeparated<'a, T>
where
T: fmt::Display,
{
DisplaySeparated { slice, sep }
}
fn display_comma_separated<T>(slice: &[T]) -> DisplaySeparated<'_, T>
where
T: fmt::Display,
{
DisplaySeparated { slice, sep: ", " }
} }
/// Identifier name, in the originally quoted form (e.g. `"id"`) /// Identifier name, in the originally quoted form (e.g. `"id"`)
@ -138,95 +162,82 @@ pub enum Expr {
Subquery(Box<Query>), Subquery(Box<Query>),
} }
impl ToString for Expr { impl fmt::Display for Expr {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Expr::Identifier(s) => s.to_string(), Expr::Identifier(s) => write!(f, "{}", s),
Expr::Wildcard => "*".to_string(), Expr::Wildcard => f.write_str("*"),
Expr::QualifiedWildcard(q) => q.join(".") + ".*", Expr::QualifiedWildcard(q) => {
Expr::CompoundIdentifier(s) => s.join("."), write!(f, "{}", display_separated(q, "."))?;
Expr::IsNull(ast) => format!("{} IS NULL", ast.as_ref().to_string()), f.write_str(".*")
Expr::IsNotNull(ast) => format!("{} IS NOT NULL", ast.as_ref().to_string()), }
Expr::CompoundIdentifier(s) => write!(f, "{}", display_separated(s, ".")),
Expr::IsNull(ast) => write!(f, "{} IS NULL", ast),
Expr::IsNotNull(ast) => write!(f, "{} IS NOT NULL", ast),
Expr::InList { Expr::InList {
expr, expr,
list, list,
negated, negated,
} => format!( } => write!(
f,
"{} {}IN ({})", "{} {}IN ({})",
expr.as_ref().to_string(), expr,
if *negated { "NOT " } else { "" }, if *negated { "NOT " } else { "" },
comma_separated_string(list) display_comma_separated(list)
), ),
Expr::InSubquery { Expr::InSubquery {
expr, expr,
subquery, subquery,
negated, negated,
} => format!( } => write!(
f,
"{} {}IN ({})", "{} {}IN ({})",
expr.as_ref().to_string(), expr,
if *negated { "NOT " } else { "" }, if *negated { "NOT " } else { "" },
subquery.to_string() subquery
), ),
Expr::Between { Expr::Between {
expr, expr,
negated, negated,
low, low,
high, high,
} => format!( } => write!(
f,
"{} {}BETWEEN {} AND {}", "{} {}BETWEEN {} AND {}",
expr.to_string(), expr,
if *negated { "NOT " } else { "" }, if *negated { "NOT " } else { "" },
low.to_string(), low,
high.to_string() high
), ),
Expr::BinaryOp { left, op, right } => format!( Expr::BinaryOp { left, op, right } => write!(f, "{} {} {}", left, op, right),
"{} {} {}", Expr::UnaryOp { op, expr } => write!(f, "{} {}", op, expr),
left.as_ref().to_string(), Expr::Cast { expr, data_type } => write!(f, "CAST({} AS {})", expr, data_type),
op.to_string(), Expr::Extract { field, expr } => write!(f, "EXTRACT({} FROM {})", field, expr),
right.as_ref().to_string() Expr::Collate { expr, collation } => write!(f, "{} COLLATE {}", expr, collation),
), Expr::Nested(ast) => write!(f, "({})", ast),
Expr::UnaryOp { op, expr } => { Expr::Value(v) => write!(f, "{}", v),
format!("{} {}", op.to_string(), expr.as_ref().to_string()) Expr::Function(fun) => write!(f, "{}", fun),
}
Expr::Cast { expr, data_type } => format!(
"CAST({} AS {})",
expr.as_ref().to_string(),
data_type.to_string()
),
Expr::Extract { field, expr } => {
format!("EXTRACT({} FROM {})", field.to_string(), expr.to_string())
}
Expr::Collate { expr, collation } => format!(
"{} COLLATE {}",
expr.as_ref().to_string(),
collation.to_string()
),
Expr::Nested(ast) => format!("({})", ast.as_ref().to_string()),
Expr::Value(v) => v.to_string(),
Expr::Function(f) => f.to_string(),
Expr::Case { Expr::Case {
operand, operand,
conditions, conditions,
results, results,
else_result, else_result,
} => { } => {
let mut s = "CASE".to_string(); f.write_str("CASE")?;
if let Some(operand) = operand { if let Some(operand) = operand {
s += &format!(" {}", operand.to_string()); write!(f, " {}", operand)?;
} }
s += &conditions for (c, r) in conditions.iter().zip(results) {
.iter() write!(f, " WHEN {} THEN {}", c, r)?;
.zip(results) }
.map(|(c, r)| format!(" WHEN {} THEN {}", c.to_string(), r.to_string()))
.collect::<Vec<String>>()
.join("");
if let Some(else_result) = else_result { if let Some(else_result) = else_result {
s += &format!(" ELSE {}", else_result.to_string()) write!(f, " ELSE {}", else_result)?;
} }
s + " END" f.write_str(" END")
} }
Expr::Exists(s) => format!("EXISTS ({})", s.to_string()), Expr::Exists(s) => write!(f, "EXISTS ({})", s),
Expr::Subquery(s) => format!("({})", s.to_string()), Expr::Subquery(s) => write!(f, "({})", s),
} }
} }
} }
@ -239,38 +250,36 @@ pub struct WindowSpec {
pub window_frame: Option<WindowFrame>, pub window_frame: Option<WindowFrame>,
} }
impl ToString for WindowSpec { impl fmt::Display for WindowSpec {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut clauses = vec![]; let mut delim = "";
if !self.partition_by.is_empty() { if !self.partition_by.is_empty() {
clauses.push(format!( delim = " ";
write!(
f,
"PARTITION BY {}", "PARTITION BY {}",
comma_separated_string(&self.partition_by) display_comma_separated(&self.partition_by)
)) )?;
}; }
if !self.order_by.is_empty() { if !self.order_by.is_empty() {
clauses.push(format!( f.write_str(delim)?;
"ORDER BY {}", delim = " ";
comma_separated_string(&self.order_by) write!(f, "ORDER BY {}", display_comma_separated(&self.order_by))?;
)) }
};
if let Some(window_frame) = &self.window_frame { if let Some(window_frame) = &self.window_frame {
if let Some(end_bound) = &window_frame.end_bound { if let Some(end_bound) = &window_frame.end_bound {
clauses.push(format!( f.write_str(delim)?;
write!(
f,
"{} BETWEEN {} AND {}", "{} BETWEEN {} AND {}",
window_frame.units.to_string(), window_frame.units, window_frame.start_bound, end_bound
window_frame.start_bound.to_string(), )?;
end_bound.to_string()
));
} else { } else {
clauses.push(format!( f.write_str(delim)?;
"{} {}", write!(f, "{} {}", window_frame.units, window_frame.start_bound)?;
window_frame.units.to_string(),
window_frame.start_bound.to_string()
));
} }
} }
clauses.join(" ") Ok(())
} }
} }
@ -292,13 +301,13 @@ pub enum WindowFrameUnits {
Groups, Groups,
} }
impl ToString for WindowFrameUnits { impl fmt::Display for WindowFrameUnits {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { f.write_str(match self {
WindowFrameUnits::Rows => "ROWS".to_string(), WindowFrameUnits::Rows => "ROWS",
WindowFrameUnits::Range => "RANGE".to_string(), WindowFrameUnits::Range => "RANGE",
WindowFrameUnits::Groups => "GROUPS".to_string(), WindowFrameUnits::Groups => "GROUPS",
} })
} }
} }
@ -329,14 +338,14 @@ pub enum WindowFrameBound {
Following(Option<u64>), Following(Option<u64>),
} }
impl ToString for WindowFrameBound { impl fmt::Display for WindowFrameBound {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
WindowFrameBound::CurrentRow => "CURRENT ROW".to_string(), WindowFrameBound::CurrentRow => f.write_str("CURRENT ROW"),
WindowFrameBound::Preceding(None) => "UNBOUNDED PRECEDING".to_string(), WindowFrameBound::Preceding(None) => f.write_str("UNBOUNDED PRECEDING"),
WindowFrameBound::Following(None) => "UNBOUNDED FOLLOWING".to_string(), WindowFrameBound::Following(None) => f.write_str("UNBOUNDED FOLLOWING"),
WindowFrameBound::Preceding(Some(n)) => format!("{} PRECEDING", n), WindowFrameBound::Preceding(Some(n)) => write!(f, "{} PRECEDING", n),
WindowFrameBound::Following(Some(n)) => format!("{} FOLLOWING", n), WindowFrameBound::Following(Some(n)) => write!(f, "{} FOLLOWING", n),
} }
} }
} }
@ -424,69 +433,70 @@ pub enum Statement {
Rollback { chain: bool }, Rollback { chain: bool },
} }
impl ToString for Statement { impl fmt::Display for Statement {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Statement::Query(s) => s.to_string(), Statement::Query(s) => write!(f, "{}", s),
Statement::Insert { Statement::Insert {
table_name, table_name,
columns, columns,
source, source,
} => { } => {
let mut s = format!("INSERT INTO {} ", table_name.to_string()); write!(f, "INSERT INTO {} ", table_name)?;
if !columns.is_empty() { if !columns.is_empty() {
s += &format!("({}) ", columns.join(", ")); write!(f, "({}) ", display_comma_separated(columns))?;
} }
s += &source.to_string(); write!(f, "{}", source)
s
} }
Statement::Copy { Statement::Copy {
table_name, table_name,
columns, columns,
values, values,
} => { } => {
let mut s = format!("COPY {}", table_name.to_string()); write!(f, "COPY {}", table_name)?;
if !columns.is_empty() { if !columns.is_empty() {
s += &format!(" ({})", comma_separated_string(columns)); write!(f, " ({})", display_comma_separated(columns))?;
} }
s += " FROM stdin; "; write!(f, " FROM stdin; ")?;
if !values.is_empty() { if !values.is_empty() {
s += &format!( writeln!(f)?;
"\n{}", let mut delim = "";
values for v in values {
.iter() write!(f, "{}", delim)?;
.map(|v| v.clone().unwrap_or_else(|| "\\N".to_string())) delim = "\t";
.collect::<Vec<String>>() if let Some(v) = v {
.join("\t") write!(f, "{}", v)?;
); } else {
write!(f, "\\N")?;
} }
s += "\n\\."; }
s }
write!(f, "\n\\.")
} }
Statement::Update { Statement::Update {
table_name, table_name,
assignments, assignments,
selection, selection,
} => { } => {
let mut s = format!("UPDATE {}", table_name.to_string()); write!(f, "UPDATE {}", table_name)?;
if !assignments.is_empty() { if !assignments.is_empty() {
s += " SET "; write!(f, " SET ")?;
s += &comma_separated_string(assignments); write!(f, "{}", display_comma_separated(assignments))?;
} }
if let Some(selection) = selection { if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.to_string()); write!(f, " WHERE {}", selection)?;
} }
s Ok(())
} }
Statement::Delete { Statement::Delete {
table_name, table_name,
selection, selection,
} => { } => {
let mut s = format!("DELETE FROM {}", table_name.to_string()); write!(f, "DELETE FROM {}", table_name)?;
if let Some(selection) = selection { if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.to_string()); write!(f, " WHERE {}", selection)?;
} }
s Ok(())
} }
Statement::CreateView { Statement::CreateView {
name, name,
@ -495,25 +505,22 @@ impl ToString for Statement {
materialized, materialized,
with_options, with_options,
} => { } => {
let modifier = if *materialized { " MATERIALIZED" } else { "" }; write!(f, "CREATE")?;
let with_options = if !with_options.is_empty() { if *materialized {
format!(" WITH ({})", comma_separated_string(with_options)) write!(f, " MATERIALIZED")?;
} else { }
"".into()
}; write!(f, " VIEW {}", name)?;
let columns = if !columns.is_empty() {
format!(" ({})", comma_separated_string(columns)) if !with_options.is_empty() {
} else { write!(f, " WITH ({})", display_comma_separated(with_options))?;
"".into() }
};
format!( if !columns.is_empty() {
"CREATE{} VIEW {}{}{} AS {}", write!(f, " ({})", display_comma_separated(columns))?;
modifier, }
name.to_string(),
with_options, write!(f, " AS {}", query)
columns,
query.to_string(),
)
} }
Statement::CreateTable { Statement::CreateTable {
name, name,
@ -524,64 +531,66 @@ impl ToString for Statement {
file_format, file_format,
location, location,
} => { } => {
let mut s = format!( write!(
f,
"CREATE {}TABLE {} ({}", "CREATE {}TABLE {} ({}",
if *external { "EXTERNAL " } else { "" }, if *external { "EXTERNAL " } else { "" },
name.to_string(), name,
comma_separated_string(columns) display_comma_separated(columns)
); )?;
if !constraints.is_empty() { if !constraints.is_empty() {
s += &format!(", {}", comma_separated_string(constraints)); write!(f, ", {}", display_comma_separated(constraints))?;
} }
s += ")"; write!(f, ")")?;
if *external { if *external {
s += &format!( write!(
f,
" STORED AS {} LOCATION '{}'", " STORED AS {} LOCATION '{}'",
file_format.as_ref().unwrap().to_string(), file_format.as_ref().unwrap(),
location.as_ref().unwrap() location.as_ref().unwrap()
); )?;
} }
if !with_options.is_empty() { if !with_options.is_empty() {
s += &format!(" WITH ({})", comma_separated_string(with_options)); write!(f, " WITH ({})", display_comma_separated(with_options))?;
} }
s Ok(())
} }
Statement::AlterTable { name, operation } => { Statement::AlterTable { name, operation } => {
format!("ALTER TABLE {} {}", name.to_string(), operation.to_string()) write!(f, "ALTER TABLE {} {}", name, operation)
} }
Statement::Drop { Statement::Drop {
object_type, object_type,
if_exists, if_exists,
names, names,
cascade, cascade,
} => format!( } => write!(
f,
"DROP {}{} {}{}", "DROP {}{} {}{}",
object_type.to_string(), object_type,
if *if_exists { " IF EXISTS" } else { "" }, if *if_exists { " IF EXISTS" } else { "" },
comma_separated_string(names), display_comma_separated(names),
if *cascade { " CASCADE" } else { "" }, if *cascade { " CASCADE" } else { "" },
), ),
Statement::StartTransaction { modes } => format!( Statement::StartTransaction { modes } => {
"START TRANSACTION{}", write!(f, "START TRANSACTION")?;
if modes.is_empty() { if !modes.is_empty() {
"".into() write!(f, " {}", display_comma_separated(modes))?;
} else {
format!(" {}", comma_separated_string(modes))
} }
), Ok(())
Statement::SetTransaction { modes } => format!( }
"SET TRANSACTION{}", Statement::SetTransaction { modes } => {
if modes.is_empty() { write!(f, "SET TRANSACTION")?;
"".into() if !modes.is_empty() {
} else { write!(f, " {}", display_comma_separated(modes))?;
format!(" {}", comma_separated_string(modes)) }
Ok(())
} }
),
Statement::Commit { chain } => { Statement::Commit { chain } => {
format!("COMMIT{}", if *chain { " AND CHAIN" } else { "" },) write!(f, "COMMIT{}", if *chain { " AND CHAIN" } else { "" },)
} }
Statement::Rollback { chain } => { Statement::Rollback { chain } => {
format!("ROLLBACK{}", if *chain { " AND CHAIN" } else { "" },) write!(f, "ROLLBACK{}", if *chain { " AND CHAIN" } else { "" },)
} }
} }
} }
@ -591,9 +600,9 @@ impl ToString for Statement {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ObjectName(pub Vec<Ident>); pub struct ObjectName(pub Vec<Ident>);
impl ToString for ObjectName { impl fmt::Display for ObjectName {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.join(".") write!(f, "{}", display_separated(&self.0, "."))
} }
} }
@ -604,9 +613,9 @@ pub struct Assignment {
pub value: Expr, pub value: Expr,
} }
impl ToString for Assignment { impl fmt::Display for Assignment {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format!("{} = {}", self.id, self.value.to_string()) write!(f, "{} = {}", self.id, self.value)
} }
} }
@ -620,18 +629,19 @@ pub struct Function {
pub distinct: bool, pub distinct: bool,
} }
impl ToString for Function { impl fmt::Display for Function {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = format!( write!(
f,
"{}({}{})", "{}({}{})",
self.name.to_string(), self.name,
if self.distinct { "DISTINCT " } else { "" }, if self.distinct { "DISTINCT " } else { "" },
comma_separated_string(&self.args), display_comma_separated(&self.args),
); )?;
if let Some(o) = &self.over { if let Some(o) = &self.over {
s += &format!(" OVER ({})", o.to_string()) write!(f, " OVER ({})", o)?;
} }
s Ok(())
} }
} }
@ -647,18 +657,22 @@ pub enum FileFormat {
JSONFILE, JSONFILE,
} }
impl ToString for FileFormat { impl fmt::Display for FileFormat {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::FileFormat::*; use self::FileFormat::*;
write!(
f,
"{}",
match self { match self {
TEXTFILE => "TEXTFILE".to_string(), TEXTFILE => "TEXTFILE",
SEQUENCEFILE => "SEQUENCEFILE".to_string(), SEQUENCEFILE => "SEQUENCEFILE",
ORC => "ORC".to_string(), ORC => "ORC",
PARQUET => "PARQUET".to_string(), PARQUET => "PARQUET",
AVRO => "AVRO".to_string(), AVRO => "AVRO",
RCFILE => "RCFILE".to_string(), RCFILE => "RCFILE",
JSONFILE => "TEXTFILE".to_string(), JSONFILE => "TEXTFILE",
} }
)
} }
} }
@ -691,12 +705,16 @@ pub enum ObjectType {
View, View,
} }
impl ObjectType { impl fmt::Display for ObjectType {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self { match self {
ObjectType::Table => "TABLE".into(), ObjectType::Table => "TABLE",
ObjectType::View => "VIEW".into(), ObjectType::View => "VIEW",
} }
)
} }
} }
@ -706,9 +724,9 @@ pub struct SqlOption {
pub value: Value, pub value: Value,
} }
impl ToString for SqlOption { impl fmt::Display for SqlOption {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format!("{} = {}", self.name.to_string(), self.value.to_string()) write!(f, "{} = {}", self.name, self.value)
} }
} }
@ -718,12 +736,12 @@ pub enum TransactionMode {
IsolationLevel(TransactionIsolationLevel), IsolationLevel(TransactionIsolationLevel),
} }
impl ToString for TransactionMode { impl fmt::Display for TransactionMode {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionMode::*; use TransactionMode::*;
match self { match self {
AccessMode(access_mode) => access_mode.to_string(), AccessMode(access_mode) => write!(f, "{}", access_mode.to_string()),
IsolationLevel(iso_level) => format!("ISOLATION LEVEL {}", iso_level.to_string()), IsolationLevel(iso_level) => write!(f, "ISOLATION LEVEL {}", iso_level),
} }
} }
} }
@ -734,13 +752,17 @@ pub enum TransactionAccessMode {
ReadWrite, ReadWrite,
} }
impl ToString for TransactionAccessMode { impl fmt::Display for TransactionAccessMode {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionAccessMode::*; use TransactionAccessMode::*;
write!(
f,
"{}",
match self { match self {
ReadOnly => "READ ONLY".into(), ReadOnly => "READ ONLY",
ReadWrite => "READ WRITE".into(), ReadWrite => "READ WRITE",
} }
)
} }
} }
@ -752,14 +774,18 @@ pub enum TransactionIsolationLevel {
Serializable, Serializable,
} }
impl ToString for TransactionIsolationLevel { impl fmt::Display for TransactionIsolationLevel {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionIsolationLevel::*; use TransactionIsolationLevel::*;
write!(
f,
"{}",
match self { match self {
ReadUncommitted => "READ UNCOMMITTED".into(), ReadUncommitted => "READ UNCOMMITTED",
ReadCommitted => "READ COMMITTED".into(), ReadCommitted => "READ COMMITTED",
RepeatableRead => "REPEATABLE READ".into(), RepeatableRead => "REPEATABLE READ",
Serializable => "SERIALIZABLE".into(), Serializable => "SERIALIZABLE",
} }
)
} }
} }

View file

@ -10,6 +10,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::fmt;
/// Unary operators /// Unary operators
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum UnaryOperator { pub enum UnaryOperator {
@ -18,13 +20,17 @@ pub enum UnaryOperator {
Not, Not,
} }
impl ToString for UnaryOperator { impl fmt::Display for UnaryOperator {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self { match self {
UnaryOperator::Plus => "+".to_string(), UnaryOperator::Plus => "+",
UnaryOperator::Minus => "-".to_string(), UnaryOperator::Minus => "-",
UnaryOperator::Not => "NOT".to_string(), UnaryOperator::Not => "NOT",
} }
)
} }
} }
@ -48,24 +54,28 @@ pub enum BinaryOperator {
NotLike, NotLike,
} }
impl ToString for BinaryOperator { impl fmt::Display for BinaryOperator {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self { match self {
BinaryOperator::Plus => "+".to_string(), BinaryOperator::Plus => "+",
BinaryOperator::Minus => "-".to_string(), BinaryOperator::Minus => "-",
BinaryOperator::Multiply => "*".to_string(), BinaryOperator::Multiply => "*",
BinaryOperator::Divide => "/".to_string(), BinaryOperator::Divide => "/",
BinaryOperator::Modulus => "%".to_string(), BinaryOperator::Modulus => "%",
BinaryOperator::Gt => ">".to_string(), BinaryOperator::Gt => ">",
BinaryOperator::Lt => "<".to_string(), BinaryOperator::Lt => "<",
BinaryOperator::GtEq => ">=".to_string(), BinaryOperator::GtEq => ">=",
BinaryOperator::LtEq => "<=".to_string(), BinaryOperator::LtEq => "<=",
BinaryOperator::Eq => "=".to_string(), BinaryOperator::Eq => "=",
BinaryOperator::NotEq => "<>".to_string(), BinaryOperator::NotEq => "<>",
BinaryOperator::And => "AND".to_string(), BinaryOperator::And => "AND",
BinaryOperator::Or => "OR".to_string(), BinaryOperator::Or => "OR",
BinaryOperator::Like => "LIKE".to_string(), BinaryOperator::Like => "LIKE",
BinaryOperator::NotLike => "NOT LIKE".to_string(), BinaryOperator::NotLike => "NOT LIKE",
} }
)
} }
} }

View file

@ -30,27 +30,25 @@ pub struct Query {
pub fetch: Option<Fetch>, pub fetch: Option<Fetch>,
} }
impl ToString for Query { impl fmt::Display for Query {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = String::new();
if !self.ctes.is_empty() { if !self.ctes.is_empty() {
s += &format!("WITH {} ", comma_separated_string(&self.ctes)) write!(f, "WITH {} ", display_comma_separated(&self.ctes))?;
} }
s += &self.body.to_string(); write!(f, "{}", self.body)?;
if !self.order_by.is_empty() { if !self.order_by.is_empty() {
s += &format!(" ORDER BY {}", comma_separated_string(&self.order_by)); write!(f, " ORDER BY {}", display_comma_separated(&self.order_by))?;
} }
if let Some(ref limit) = self.limit { if let Some(ref limit) = self.limit {
s += &format!(" LIMIT {}", limit.to_string()); write!(f, " LIMIT {}", limit)?;
} }
if let Some(ref offset) = self.offset { if let Some(ref offset) = self.offset {
s += &format!(" OFFSET {} ROWS", offset.to_string()); write!(f, " OFFSET {} ROWS", offset)?;
} }
if let Some(ref fetch) = self.fetch { if let Some(ref fetch) = self.fetch {
s.push(' '); write!(f, " {}", fetch)?;
s += &fetch.to_string();
} }
s Ok(())
} }
} }
@ -74,12 +72,12 @@ pub enum SetExpr {
// TODO: ANSI SQL supports `TABLE` here. // TODO: ANSI SQL supports `TABLE` here.
} }
impl ToString for SetExpr { impl fmt::Display for SetExpr {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
SetExpr::Select(s) => s.to_string(), SetExpr::Select(s) => write!(f, "{}", s),
SetExpr::Query(q) => format!("({})", q.to_string()), SetExpr::Query(q) => write!(f, "({})", q),
SetExpr::Values(v) => v.to_string(), SetExpr::Values(v) => write!(f, "{}", v),
SetExpr::SetOperation { SetExpr::SetOperation {
left, left,
right, right,
@ -87,13 +85,7 @@ impl ToString for SetExpr {
all, all,
} => { } => {
let all_str = if *all { " ALL" } else { "" }; let all_str = if *all { " ALL" } else { "" };
format!( write!(f, "{} {}{} {}", left, op, all_str, right)
"{} {}{} {}",
left.to_string(),
op.to_string(),
all_str,
right.to_string()
)
} }
} }
} }
@ -106,13 +98,17 @@ pub enum SetOperator {
Intersect, Intersect,
} }
impl ToString for SetOperator { impl fmt::Display for SetOperator {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self { match self {
SetOperator::Union => "UNION".to_string(), SetOperator::Union => "UNION",
SetOperator::Except => "EXCEPT".to_string(), SetOperator::Except => "EXCEPT",
SetOperator::Intersect => "INTERSECT".to_string(), SetOperator::Intersect => "INTERSECT",
} }
)
} }
} }
@ -134,26 +130,27 @@ pub struct Select {
pub having: Option<Expr>, pub having: Option<Expr>,
} }
impl ToString for Select { impl fmt::Display for Select {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = format!( write!(
f,
"SELECT{} {}", "SELECT{} {}",
if self.distinct { " DISTINCT" } else { "" }, if self.distinct { " DISTINCT" } else { "" },
comma_separated_string(&self.projection) display_comma_separated(&self.projection)
); )?;
if !self.from.is_empty() { if !self.from.is_empty() {
s += &format!(" FROM {}", comma_separated_string(&self.from)); write!(f, " FROM {}", display_comma_separated(&self.from))?;
} }
if let Some(ref selection) = self.selection { if let Some(ref selection) = self.selection {
s += &format!(" WHERE {}", selection.to_string()); write!(f, " WHERE {}", selection)?;
} }
if !self.group_by.is_empty() { if !self.group_by.is_empty() {
s += &format!(" GROUP BY {}", comma_separated_string(&self.group_by)); write!(f, " GROUP BY {}", display_comma_separated(&self.group_by))?;
} }
if let Some(ref having) = self.having { if let Some(ref having) = self.having {
s += &format!(" HAVING {}", having.to_string()); write!(f, " HAVING {}", having)?;
} }
s Ok(())
} }
} }
@ -167,9 +164,9 @@ pub struct Cte {
pub query: Query, pub query: Query,
} }
impl ToString for Cte { impl fmt::Display for Cte {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format!("{} AS ({})", self.alias.to_string(), self.query.to_string()) write!(f, "{} AS ({})", self.alias, self.query)
} }
} }
@ -186,15 +183,13 @@ pub enum SelectItem {
Wildcard, Wildcard,
} }
impl ToString for SelectItem { impl fmt::Display for SelectItem {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self { match &self {
SelectItem::UnnamedExpr(expr) => expr.to_string(), SelectItem::UnnamedExpr(expr) => write!(f, "{}", expr),
SelectItem::ExprWithAlias { expr, alias } => { SelectItem::ExprWithAlias { expr, alias } => write!(f, "{} AS {}", expr, alias),
format!("{} AS {}", expr.to_string(), alias) SelectItem::QualifiedWildcard(prefix) => write!(f, "{}.*", prefix),
} SelectItem::Wildcard => write!(f, "*"),
SelectItem::QualifiedWildcard(prefix) => format!("{}.*", prefix.to_string()),
SelectItem::Wildcard => "*".to_string(),
} }
} }
} }
@ -205,13 +200,13 @@ pub struct TableWithJoins {
pub joins: Vec<Join>, pub joins: Vec<Join>,
} }
impl ToString for TableWithJoins { impl fmt::Display for TableWithJoins {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = self.relation.to_string(); write!(f, "{}", self.relation)?;
for join in &self.joins { for join in &self.joins {
s += &join.to_string(); write!(f, "{}", join)?;
} }
s Ok(())
} }
} }
@ -240,8 +235,8 @@ pub enum TableFactor {
NestedJoin(Box<TableWithJoins>), NestedJoin(Box<TableWithJoins>),
} }
impl ToString for TableFactor { impl fmt::Display for TableFactor {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
TableFactor::Table { TableFactor::Table {
name, name,
@ -249,36 +244,33 @@ impl ToString for TableFactor {
args, args,
with_hints, with_hints,
} => { } => {
let mut s = name.to_string(); write!(f, "{}", name)?;
if !args.is_empty() { if !args.is_empty() {
s += &format!("({})", comma_separated_string(args)) write!(f, "({})", display_comma_separated(args))?;
}; }
if let Some(alias) = alias { if let Some(alias) = alias {
s += &format!(" AS {}", alias.to_string()); write!(f, " AS {}", alias)?;
} }
if !with_hints.is_empty() { if !with_hints.is_empty() {
s += &format!(" WITH ({})", comma_separated_string(with_hints)); write!(f, " WITH ({})", display_comma_separated(with_hints))?;
} }
s Ok(())
} }
TableFactor::Derived { TableFactor::Derived {
lateral, lateral,
subquery, subquery,
alias, alias,
} => { } => {
let mut s = String::new();
if *lateral { if *lateral {
s += "LATERAL "; write!(f, "LATERAL ")?;
} }
s += &format!("({})", subquery.to_string()); write!(f, "({})", subquery)?;
if let Some(alias) = alias { if let Some(alias) = alias {
s += &format!(" AS {}", alias.to_string()); write!(f, " AS {}", alias)?;
} }
s Ok(())
}
TableFactor::NestedJoin(table_reference) => {
format!("({})", table_reference.to_string())
} }
TableFactor::NestedJoin(table_reference) => write!(f, "({})", table_reference),
} }
} }
} }
@ -289,13 +281,13 @@ pub struct TableAlias {
pub columns: Vec<Ident>, pub columns: Vec<Ident>,
} }
impl ToString for TableAlias { impl fmt::Display for TableAlias {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = self.name.clone(); write!(f, "{}", self.name)?;
if !self.columns.is_empty() { if !self.columns.is_empty() {
s += &format!(" ({})", comma_separated_string(&self.columns)); write!(f, " ({})", display_comma_separated(&self.columns))?;
} }
s Ok(())
} }
} }
@ -305,49 +297,61 @@ pub struct Join {
pub join_operator: JoinOperator, pub join_operator: JoinOperator,
} }
impl ToString for Join { impl fmt::Display for Join {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn prefix(constraint: &JoinConstraint) -> String { fn prefix(constraint: &JoinConstraint) -> &'static str {
match constraint { match constraint {
JoinConstraint::Natural => "NATURAL ".to_string(), JoinConstraint::Natural => "NATURAL ",
_ => "".to_string(), _ => "",
} }
} }
fn suffix(constraint: &JoinConstraint) -> String { fn suffix<'a>(constraint: &'a JoinConstraint) -> impl fmt::Display + 'a {
match constraint { struct Suffix<'a>(&'a JoinConstraint);
JoinConstraint::On(expr) => format!(" ON {}", expr.to_string()), impl<'a> fmt::Display for Suffix<'a> {
JoinConstraint::Using(attrs) => format!(" USING({})", attrs.join(", ")), fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
_ => "".to_string(), match self.0 {
JoinConstraint::On(expr) => write!(f, " ON {}", expr),
JoinConstraint::Using(attrs) => {
write!(f, " USING({})", display_comma_separated(attrs))
} }
_ => Ok(()),
}
}
}
Suffix(constraint)
} }
match &self.join_operator { match &self.join_operator {
JoinOperator::Inner(constraint) => format!( JoinOperator::Inner(constraint) => write!(
f,
" {}JOIN {}{}", " {}JOIN {}{}",
prefix(constraint), prefix(constraint),
self.relation.to_string(), self.relation,
suffix(constraint) suffix(constraint)
), ),
JoinOperator::LeftOuter(constraint) => format!( JoinOperator::LeftOuter(constraint) => write!(
f,
" {}LEFT JOIN {}{}", " {}LEFT JOIN {}{}",
prefix(constraint), prefix(constraint),
self.relation.to_string(), self.relation,
suffix(constraint) suffix(constraint)
), ),
JoinOperator::RightOuter(constraint) => format!( JoinOperator::RightOuter(constraint) => write!(
f,
" {}RIGHT JOIN {}{}", " {}RIGHT JOIN {}{}",
prefix(constraint), prefix(constraint),
self.relation.to_string(), self.relation,
suffix(constraint) suffix(constraint)
), ),
JoinOperator::FullOuter(constraint) => format!( JoinOperator::FullOuter(constraint) => write!(
f,
" {}FULL JOIN {}{}", " {}FULL JOIN {}{}",
prefix(constraint), prefix(constraint),
self.relation.to_string(), self.relation,
suffix(constraint) suffix(constraint)
), ),
JoinOperator::CrossJoin => format!(" CROSS JOIN {}", self.relation.to_string()), JoinOperator::CrossJoin => write!(f, " CROSS JOIN {}", self.relation),
JoinOperator::CrossApply => format!(" CROSS APPLY {}", self.relation.to_string()), JoinOperator::CrossApply => write!(f, " CROSS APPLY {}", self.relation),
JoinOperator::OuterApply => format!(" OUTER APPLY {}", self.relation.to_string()), JoinOperator::OuterApply => write!(f, " OUTER APPLY {}", self.relation),
} }
} }
} }
@ -379,12 +383,12 @@ pub struct OrderByExpr {
pub asc: Option<bool>, pub asc: Option<bool>,
} }
impl ToString for OrderByExpr { impl fmt::Display for OrderByExpr {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.asc { match self.asc {
Some(true) => format!("{} ASC", self.expr.to_string()), Some(true) => write!(f, "{} ASC", self.expr),
Some(false) => format!("{} DESC", self.expr.to_string()), Some(false) => write!(f, "{} DESC", self.expr),
None => self.expr.to_string(), None => write!(f, "{}", self.expr),
} }
} }
} }
@ -396,19 +400,14 @@ pub struct Fetch {
pub quantity: Option<Expr>, pub quantity: Option<Expr>,
} }
impl ToString for Fetch { impl fmt::Display for Fetch {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let extension = if self.with_ties { "WITH TIES" } else { "ONLY" }; let extension = if self.with_ties { "WITH TIES" } else { "ONLY" };
if let Some(ref quantity) = self.quantity { if let Some(ref quantity) = self.quantity {
let percent = if self.percent { " PERCENT" } else { "" }; let percent = if self.percent { " PERCENT" } else { "" };
format!( write!(f, "FETCH FIRST {}{} ROWS {}", quantity, percent, extension)
"FETCH FIRST {}{} ROWS {}",
quantity.to_string(),
percent,
extension
)
} else { } else {
format!("FETCH FIRST ROWS {}", extension) write!(f, "FETCH FIRST ROWS {}", extension)
} }
} }
} }
@ -416,12 +415,15 @@ impl ToString for Fetch {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Values(pub Vec<Vec<Expr>>); pub struct Values(pub Vec<Vec<Expr>>);
impl ToString for Values { impl fmt::Display for Values {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let rows = self write!(f, "VALUES ")?;
.0 let mut delim = "";
.iter() for row in &self.0 {
.map(|row| format!("({})", comma_separated_string(row))); write!(f, "{}", delim)?;
format!("VALUES {}", comma_separated_string(rows)) delim = ", ";
write!(f, "({})", display_comma_separated(row))?;
}
Ok(())
} }
} }

View file

@ -11,6 +11,7 @@
// limitations under the License. // limitations under the License.
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;
use std::fmt;
/// Primitive SQL values such as number and string /// Primitive SQL values such as number and string
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -56,18 +57,18 @@ pub enum Value {
Null, Null,
} }
impl ToString for Value { impl fmt::Display for Value {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Value::Long(v) => v.to_string(), Value::Long(v) => write!(f, "{}", v),
Value::Double(v) => v.to_string(), Value::Double(v) => write!(f, "{}", v),
Value::SingleQuotedString(v) => format!("'{}'", escape_single_quote_string(v)), Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)),
Value::NationalStringLiteral(v) => format!("N'{}'", v), Value::NationalStringLiteral(v) => write!(f, "N'{}'", v),
Value::HexStringLiteral(v) => format!("X'{}'", v), Value::HexStringLiteral(v) => write!(f, "X'{}'", v),
Value::Boolean(v) => v.to_string(), Value::Boolean(v) => write!(f, "{}", v),
Value::Date(v) => format!("DATE '{}'", escape_single_quote_string(v)), Value::Date(v) => write!(f, "DATE '{}'", escape_single_quote_string(v)),
Value::Time(v) => format!("TIME '{}'", escape_single_quote_string(v)), Value::Time(v) => write!(f, "TIME '{}'", escape_single_quote_string(v)),
Value::Timestamp(v) => format!("TIMESTAMP '{}'", escape_single_quote_string(v)), Value::Timestamp(v) => write!(f, "TIMESTAMP '{}'", escape_single_quote_string(v)),
Value::Interval { Value::Interval {
value, value,
leading_field: DateTimeField::Second, leading_field: DateTimeField::Second,
@ -78,7 +79,8 @@ impl ToString for Value {
// When the leading field is SECOND, the parser guarantees that // When the leading field is SECOND, the parser guarantees that
// the last field is None. // the last field is None.
assert!(last_field.is_none()); assert!(last_field.is_none());
format!( write!(
f,
"INTERVAL '{}' SECOND ({}, {})", "INTERVAL '{}' SECOND ({}, {})",
escape_single_quote_string(value), escape_single_quote_string(value),
leading_precision, leading_precision,
@ -92,23 +94,24 @@ impl ToString for Value {
last_field, last_field,
fractional_seconds_precision, fractional_seconds_precision,
} => { } => {
let mut s = format!( write!(
f,
"INTERVAL '{}' {}", "INTERVAL '{}' {}",
escape_single_quote_string(value), escape_single_quote_string(value),
leading_field.to_string() leading_field
); )?;
if let Some(leading_precision) = leading_precision { if let Some(leading_precision) = leading_precision {
s += &format!(" ({})", leading_precision); write!(f, " ({})", leading_precision)?;
} }
if let Some(last_field) = last_field { if let Some(last_field) = last_field {
s += &format!(" TO {}", last_field.to_string()); write!(f, " TO {}", last_field)?;
} }
if let Some(fractional_seconds_precision) = fractional_seconds_precision { if let Some(fractional_seconds_precision) = fractional_seconds_precision {
s += &format!(" ({})", fractional_seconds_precision); write!(f, " ({})", fractional_seconds_precision)?;
} }
s Ok(())
} }
Value::Null => "NULL".to_string(), Value::Null => write!(f, "NULL"),
} }
} }
} }
@ -123,27 +126,36 @@ pub enum DateTimeField {
Second, Second,
} }
impl ToString for DateTimeField { impl fmt::Display for DateTimeField {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self { match self {
DateTimeField::Year => "YEAR".to_string(), DateTimeField::Year => "YEAR",
DateTimeField::Month => "MONTH".to_string(), DateTimeField::Month => "MONTH",
DateTimeField::Day => "DAY".to_string(), DateTimeField::Day => "DAY",
DateTimeField::Hour => "HOUR".to_string(), DateTimeField::Hour => "HOUR",
DateTimeField::Minute => "MINUTE".to_string(), DateTimeField::Minute => "MINUTE",
DateTimeField::Second => "SECOND".to_string(), DateTimeField::Second => "SECOND",
} }
)
} }
} }
fn escape_single_quote_string(s: &str) -> String { struct EscapeSingleQuoteString<'a>(&'a str);
let mut escaped = String::new(); impl<'a> fmt::Display for EscapeSingleQuoteString<'a> {
for c in s.chars() { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for c in self.0.chars() {
if c == '\'' { if c == '\'' {
escaped.push_str("\'\'"); write!(f, "\'\'")?;
} else { } else {
escaped.push(c); write!(f, "{}", c)?;
} }
} }
escaped Ok(())
}
}
fn escape_single_quote_string(s: &str) -> EscapeSingleQuoteString<'_> {
EscapeSingleQuoteString(s)
} }

View file

@ -19,6 +19,7 @@ use super::dialect::keywords;
use super::dialect::Dialect; use super::dialect::Dialect;
use super::tokenizer::*; use super::tokenizer::*;
use std::error::Error; use std::error::Error;
use std::fmt;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum ParserError { pub enum ParserError {
@ -52,8 +53,8 @@ impl From<TokenizerError> for ParserError {
} }
} }
impl std::fmt::Display for ParserError { impl fmt::Display for ParserError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
"sql parser error: {}", "sql parser error: {}",
@ -728,7 +729,7 @@ impl Parser {
parser_err!(format!( parser_err!(format!(
"Expected {}, found: {}", "Expected {}, found: {}",
expected, expected,
found.map_or("EOF".to_string(), |t| t.to_string()) found.map_or_else(|| "EOF".to_string(), |t| format!("{}", t))
)) ))
} }

View file

@ -21,6 +21,7 @@ use std::str::Chars;
use super::dialect::keywords::ALL_KEYWORDS; use super::dialect::keywords::ALL_KEYWORDS;
use super::dialect::Dialect; use super::dialect::Dialect;
use std::fmt;
/// SQL Token enumeration /// SQL Token enumeration
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -89,40 +90,40 @@ pub enum Token {
RBrace, RBrace,
} }
impl ToString for Token { impl fmt::Display for Token {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Token::Word(ref w) => w.to_string(), Token::Word(ref w) => write!(f, "{}", w),
Token::Number(ref n) => n.to_string(), Token::Number(ref n) => f.write_str(n),
Token::Char(ref c) => c.to_string(), Token::Char(ref c) => write!(f, "{}", c),
Token::SingleQuotedString(ref s) => format!("'{}'", s), Token::SingleQuotedString(ref s) => write!(f, "'{}'", s),
Token::NationalStringLiteral(ref s) => format!("N'{}'", s), Token::NationalStringLiteral(ref s) => write!(f, "N'{}'", s),
Token::HexStringLiteral(ref s) => format!("X'{}'", s), Token::HexStringLiteral(ref s) => write!(f, "X'{}'", s),
Token::Comma => ",".to_string(), Token::Comma => f.write_str(","),
Token::Whitespace(ws) => ws.to_string(), Token::Whitespace(ws) => write!(f, "{}", ws),
Token::Eq => "=".to_string(), Token::Eq => f.write_str("="),
Token::Neq => "<>".to_string(), Token::Neq => f.write_str("<>"),
Token::Lt => "<".to_string(), Token::Lt => f.write_str("<"),
Token::Gt => ">".to_string(), Token::Gt => f.write_str(">"),
Token::LtEq => "<=".to_string(), Token::LtEq => f.write_str("<="),
Token::GtEq => ">=".to_string(), Token::GtEq => f.write_str(">="),
Token::Plus => "+".to_string(), Token::Plus => f.write_str("+"),
Token::Minus => "-".to_string(), Token::Minus => f.write_str("-"),
Token::Mult => "*".to_string(), Token::Mult => f.write_str("*"),
Token::Div => "/".to_string(), Token::Div => f.write_str("/"),
Token::Mod => "%".to_string(), Token::Mod => f.write_str("%"),
Token::LParen => "(".to_string(), Token::LParen => f.write_str("("),
Token::RParen => ")".to_string(), Token::RParen => f.write_str(")"),
Token::Period => ".".to_string(), Token::Period => f.write_str("."),
Token::Colon => ":".to_string(), Token::Colon => f.write_str(":"),
Token::DoubleColon => "::".to_string(), Token::DoubleColon => f.write_str("::"),
Token::SemiColon => ";".to_string(), Token::SemiColon => f.write_str(";"),
Token::Backslash => "\\".to_string(), Token::Backslash => f.write_str("\\"),
Token::LBracket => "[".to_string(), Token::LBracket => f.write_str("["),
Token::RBracket => "]".to_string(), Token::RBracket => f.write_str("]"),
Token::Ampersand => "&".to_string(), Token::Ampersand => f.write_str("&"),
Token::LBrace => "{".to_string(), Token::LBrace => f.write_str("{"),
Token::RBrace => "}".to_string(), Token::RBrace => f.write_str("}"),
} }
} }
} }
@ -164,13 +165,13 @@ pub struct Word {
pub keyword: String, pub keyword: String,
} }
impl ToString for Word { impl fmt::Display for Word {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.quote_style { match self.quote_style {
Some(s) if s == '"' || s == '[' || s == '`' => { Some(s) if s == '"' || s == '[' || s == '`' => {
format!("{}{}{}", s, self.value, Word::matching_end_quote(s)) write!(f, "{}{}{}", s, self.value, Word::matching_end_quote(s))
} }
None => self.value.clone(), None => f.write_str(&self.value),
_ => panic!("Unexpected quote_style!"), _ => panic!("Unexpected quote_style!"),
} }
} }
@ -195,14 +196,14 @@ pub enum Whitespace {
MultiLineComment(String), MultiLineComment(String),
} }
impl ToString for Whitespace { impl fmt::Display for Whitespace {
fn to_string(&self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Whitespace::Space => " ".to_string(), Whitespace::Space => f.write_str(" "),
Whitespace::Newline => "\n".to_string(), Whitespace::Newline => f.write_str("\n"),
Whitespace::Tab => "\t".to_string(), Whitespace::Tab => f.write_str("\t"),
Whitespace::SingleLineComment(s) => format!("--{}", s), Whitespace::SingleLineComment(s) => write!(f, "--{}", s),
Whitespace::MultiLineComment(s) => format!("/*{}*/", s), Whitespace::MultiLineComment(s) => write!(f, "/*{}*/", s),
} }
} }
} }