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.
use super::ObjectName;
use std::fmt;
/// SQL data types
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -65,47 +66,53 @@ pub enum DataType {
Array(Box<DataType>),
}
impl ToString for DataType {
fn to_string(&self) -> String {
impl fmt::Display for DataType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DataType::Char(size) => format_type_with_optional_length("char", size),
DataType::Varchar(size) => format_type_with_optional_length("character varying", size),
DataType::Uuid => "uuid".to_string(),
DataType::Clob(size) => format!("clob({})", size),
DataType::Binary(size) => format!("binary({})", size),
DataType::Varbinary(size) => format!("varbinary({})", size),
DataType::Blob(size) => format!("blob({})", size),
DataType::Char(size) => format_type_with_optional_length(f, "char", size),
DataType::Varchar(size) => {
format_type_with_optional_length(f, "character varying", size)
}
DataType::Uuid => write!(f, "uuid"),
DataType::Clob(size) => write!(f, "clob({})", 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) => {
if let Some(scale) = scale {
format!("numeric({},{})", precision.unwrap(), scale)
write!(f, "numeric({},{})", precision.unwrap(), scale)
} 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::SmallInt => "smallint".to_string(),
DataType::Int => "int".to_string(),
DataType::BigInt => "bigint".to_string(),
DataType::Real => "real".to_string(),
DataType::Double => "double".to_string(),
DataType::Boolean => "boolean".to_string(),
DataType::Date => "date".to_string(),
DataType::Time => "time".to_string(),
DataType::Timestamp => "timestamp".to_string(),
DataType::Interval => "interval".to_string(),
DataType::Regclass => "regclass".to_string(),
DataType::Text => "text".to_string(),
DataType::Bytea => "bytea".to_string(),
DataType::Array(ty) => format!("{}[]", ty.to_string()),
DataType::Custom(ty) => ty.to_string(),
DataType::Float(size) => format_type_with_optional_length(f, "float", size),
DataType::SmallInt => write!(f, "smallint"),
DataType::Int => write!(f, "int"),
DataType::BigInt => write!(f, "bigint"),
DataType::Real => write!(f, "real"),
DataType::Double => write!(f, "double"),
DataType::Boolean => write!(f, "boolean"),
DataType::Date => write!(f, "date"),
DataType::Time => write!(f, "time"),
DataType::Timestamp => write!(f, "timestamp"),
DataType::Interval => write!(f, "interval"),
DataType::Regclass => write!(f, "regclass"),
DataType::Text => write!(f, "text"),
DataType::Bytea => write!(f, "bytea"),
DataType::Array(ty) => write!(f, "{}[]", ty),
DataType::Custom(ty) => write!(f, "{}", ty),
}
}
}
fn format_type_with_optional_length(sql_type: &str, len: &Option<u64>) -> String {
let mut s = sql_type.to_string();
fn format_type_with_optional_length(
f: &mut fmt::Formatter,
sql_type: &'static str,
len: &Option<u64>,
) -> fmt::Result {
write!(f, "{}", sql_type)?;
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`
//! (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
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -11,11 +12,11 @@ pub enum AlterTableOperation {
DropConstraint { name: Ident },
}
impl ToString for AlterTableOperation {
fn to_string(&self) -> String {
impl fmt::Display for AlterTableOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AlterTableOperation::AddConstraint(c) => format!("ADD {}", c.to_string()),
AlterTableOperation::DropConstraint { name } => format!("DROP CONSTRAINT {}", name),
AlterTableOperation::AddConstraint(c) => write!(f, "ADD {}", c),
AlterTableOperation::DropConstraint { name } => write!(f, "DROP CONSTRAINT {}", name),
}
}
}
@ -46,36 +47,36 @@ pub enum TableConstraint {
},
}
impl ToString for TableConstraint {
fn to_string(&self) -> String {
impl fmt::Display for TableConstraint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TableConstraint::Unique {
name,
columns,
is_primary,
} => format!(
} => write!(
f,
"{}{} ({})",
format_constraint_name(name),
display_constraint_name(name),
if *is_primary { "PRIMARY KEY" } else { "UNIQUE" },
columns.join(", ")
display_comma_separated(columns)
),
TableConstraint::ForeignKey {
name,
columns,
foreign_table,
referred_columns,
} => format!(
} => write!(
f,
"{}FOREIGN KEY ({}) REFERENCES {}({})",
format_constraint_name(name),
columns.join(", "),
foreign_table.to_string(),
referred_columns.join(", ")
),
TableConstraint::Check { name, expr } => format!(
"{}CHECK ({})",
format_constraint_name(name),
expr.to_string()
display_constraint_name(name),
display_comma_separated(columns),
foreign_table,
display_comma_separated(referred_columns)
),
TableConstraint::Check { name, expr } => {
write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
}
}
}
}
@ -89,18 +90,13 @@ pub struct ColumnDef {
pub options: Vec<ColumnOptionDef>,
}
impl ToString for ColumnDef {
fn to_string(&self) -> String {
format!(
"{} {}{}",
self.name,
self.data_type.to_string(),
self.options
.iter()
.map(|c| format!(" {}", c.to_string()))
.collect::<Vec<_>>()
.join("")
)
impl fmt::Display for ColumnDef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.name, self.data_type)?;
for option in &self.options {
write!(f, " {}", option)?;
}
Ok(())
}
}
@ -126,13 +122,9 @@ pub struct ColumnOptionDef {
pub option: ColumnOption,
}
impl ToString for ColumnOptionDef {
fn to_string(&self) -> String {
format!(
"{}{}",
format_constraint_name(&self.name),
self.option.to_string()
)
impl fmt::Display for ColumnOptionDef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", display_constraint_name(&self.name), self.option)
}
}
@ -160,35 +152,39 @@ pub enum ColumnOption {
Check(Expr),
}
impl ToString for ColumnOption {
fn to_string(&self) -> String {
impl fmt::Display for ColumnOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use ColumnOption::*;
match self {
Null => "NULL".to_string(),
NotNull => "NOT NULL".to_string(),
Default(expr) => format!("DEFAULT {}", expr.to_string()),
Null => write!(f, "NULL"),
NotNull => write!(f, "NOT NULL"),
Default(expr) => write!(f, "DEFAULT {}", expr),
Unique { is_primary } => {
if *is_primary {
"PRIMARY KEY".to_string()
} else {
"UNIQUE".to_string()
}
write!(f, "{}", if *is_primary { "PRIMARY KEY" } else { "UNIQUE" })
}
ForeignKey {
foreign_table,
referred_columns,
} => format!(
} => write!(
f,
"REFERENCES {} ({})",
foreign_table.to_string(),
referred_columns.join(", ")
foreign_table,
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 {
name.as_ref()
.map(|name| format!("CONSTRAINT {} ", name))
.unwrap_or_default()
fn display_constraint_name<'a>(name: &'a Option<Ident>) -> impl fmt::Display + 'a {
struct ConstraintName<'a>(&'a Option<Ident>);
impl<'a> fmt::Display for ConstraintName<'a> {
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 value;
use std::ops::Deref;
use std::fmt;
pub use self::data_type::DataType;
pub use self::ddl::{
@ -31,17 +31,41 @@ pub use self::query::{
};
pub use self::value::{DateTimeField, Value};
/// Like `vec.join(", ")`, but for any types implementing ToString.
fn comma_separated_string<I>(iter: I) -> String
struct DisplaySeparated<'a, T>
where
I: IntoIterator,
I::Item: Deref,
<I::Item as Deref>::Target: ToString,
T: fmt::Display,
{
iter.into_iter()
.map(|t| t.deref().to_string())
.collect::<Vec<String>>()
.join(", ")
slice: &'a [T],
sep: &'static str,
}
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"`)
@ -138,95 +162,82 @@ pub enum Expr {
Subquery(Box<Query>),
}
impl ToString for Expr {
fn to_string(&self) -> String {
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expr::Identifier(s) => s.to_string(),
Expr::Wildcard => "*".to_string(),
Expr::QualifiedWildcard(q) => q.join(".") + ".*",
Expr::CompoundIdentifier(s) => s.join("."),
Expr::IsNull(ast) => format!("{} IS NULL", ast.as_ref().to_string()),
Expr::IsNotNull(ast) => format!("{} IS NOT NULL", ast.as_ref().to_string()),
Expr::Identifier(s) => write!(f, "{}", s),
Expr::Wildcard => f.write_str("*"),
Expr::QualifiedWildcard(q) => {
write!(f, "{}", display_separated(q, "."))?;
f.write_str(".*")
}
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,
list,
negated,
} => format!(
} => write!(
f,
"{} {}IN ({})",
expr.as_ref().to_string(),
expr,
if *negated { "NOT " } else { "" },
comma_separated_string(list)
display_comma_separated(list)
),
Expr::InSubquery {
expr,
subquery,
negated,
} => format!(
} => write!(
f,
"{} {}IN ({})",
expr.as_ref().to_string(),
expr,
if *negated { "NOT " } else { "" },
subquery.to_string()
subquery
),
Expr::Between {
expr,
negated,
low,
high,
} => format!(
} => write!(
f,
"{} {}BETWEEN {} AND {}",
expr.to_string(),
expr,
if *negated { "NOT " } else { "" },
low.to_string(),
high.to_string()
low,
high
),
Expr::BinaryOp { left, op, right } => format!(
"{} {} {}",
left.as_ref().to_string(),
op.to_string(),
right.as_ref().to_string()
),
Expr::UnaryOp { op, expr } => {
format!("{} {}", op.to_string(), expr.as_ref().to_string())
}
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::BinaryOp { left, op, right } => write!(f, "{} {} {}", left, op, right),
Expr::UnaryOp { op, expr } => write!(f, "{} {}", op, expr),
Expr::Cast { expr, data_type } => write!(f, "CAST({} AS {})", expr, data_type),
Expr::Extract { field, expr } => write!(f, "EXTRACT({} FROM {})", field, expr),
Expr::Collate { expr, collation } => write!(f, "{} COLLATE {}", expr, collation),
Expr::Nested(ast) => write!(f, "({})", ast),
Expr::Value(v) => write!(f, "{}", v),
Expr::Function(fun) => write!(f, "{}", fun),
Expr::Case {
operand,
conditions,
results,
else_result,
} => {
let mut s = "CASE".to_string();
f.write_str("CASE")?;
if let Some(operand) = operand {
s += &format!(" {}", operand.to_string());
write!(f, " {}", operand)?;
}
s += &conditions
.iter()
.zip(results)
.map(|(c, r)| format!(" WHEN {} THEN {}", c.to_string(), r.to_string()))
.collect::<Vec<String>>()
.join("");
for (c, r) in conditions.iter().zip(results) {
write!(f, " WHEN {} THEN {}", c, r)?;
}
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::Subquery(s) => format!("({})", s.to_string()),
Expr::Exists(s) => write!(f, "EXISTS ({})", s),
Expr::Subquery(s) => write!(f, "({})", s),
}
}
}
@ -239,38 +250,36 @@ pub struct WindowSpec {
pub window_frame: Option<WindowFrame>,
}
impl ToString for WindowSpec {
fn to_string(&self) -> String {
let mut clauses = vec![];
impl fmt::Display for WindowSpec {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut delim = "";
if !self.partition_by.is_empty() {
clauses.push(format!(
delim = " ";
write!(
f,
"PARTITION BY {}",
comma_separated_string(&self.partition_by)
))
};
display_comma_separated(&self.partition_by)
)?;
}
if !self.order_by.is_empty() {
clauses.push(format!(
"ORDER BY {}",
comma_separated_string(&self.order_by)
))
};
f.write_str(delim)?;
delim = " ";
write!(f, "ORDER BY {}", display_comma_separated(&self.order_by))?;
}
if let Some(window_frame) = &self.window_frame {
if let Some(end_bound) = &window_frame.end_bound {
clauses.push(format!(
f.write_str(delim)?;
write!(
f,
"{} BETWEEN {} AND {}",
window_frame.units.to_string(),
window_frame.start_bound.to_string(),
end_bound.to_string()
));
window_frame.units, window_frame.start_bound, end_bound
)?;
} else {
clauses.push(format!(
"{} {}",
window_frame.units.to_string(),
window_frame.start_bound.to_string()
));
f.write_str(delim)?;
write!(f, "{} {}", window_frame.units, window_frame.start_bound)?;
}
}
clauses.join(" ")
Ok(())
}
}
@ -292,13 +301,13 @@ pub enum WindowFrameUnits {
Groups,
}
impl ToString for WindowFrameUnits {
fn to_string(&self) -> String {
match self {
WindowFrameUnits::Rows => "ROWS".to_string(),
WindowFrameUnits::Range => "RANGE".to_string(),
WindowFrameUnits::Groups => "GROUPS".to_string(),
}
impl fmt::Display for WindowFrameUnits {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
WindowFrameUnits::Rows => "ROWS",
WindowFrameUnits::Range => "RANGE",
WindowFrameUnits::Groups => "GROUPS",
})
}
}
@ -329,14 +338,14 @@ pub enum WindowFrameBound {
Following(Option<u64>),
}
impl ToString for WindowFrameBound {
fn to_string(&self) -> String {
impl fmt::Display for WindowFrameBound {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
WindowFrameBound::CurrentRow => "CURRENT ROW".to_string(),
WindowFrameBound::Preceding(None) => "UNBOUNDED PRECEDING".to_string(),
WindowFrameBound::Following(None) => "UNBOUNDED FOLLOWING".to_string(),
WindowFrameBound::Preceding(Some(n)) => format!("{} PRECEDING", n),
WindowFrameBound::Following(Some(n)) => format!("{} FOLLOWING", n),
WindowFrameBound::CurrentRow => f.write_str("CURRENT ROW"),
WindowFrameBound::Preceding(None) => f.write_str("UNBOUNDED PRECEDING"),
WindowFrameBound::Following(None) => f.write_str("UNBOUNDED FOLLOWING"),
WindowFrameBound::Preceding(Some(n)) => write!(f, "{} PRECEDING", n),
WindowFrameBound::Following(Some(n)) => write!(f, "{} FOLLOWING", n),
}
}
}
@ -424,69 +433,70 @@ pub enum Statement {
Rollback { chain: bool },
}
impl ToString for Statement {
fn to_string(&self) -> String {
impl fmt::Display for Statement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Statement::Query(s) => s.to_string(),
Statement::Query(s) => write!(f, "{}", s),
Statement::Insert {
table_name,
columns,
source,
} => {
let mut s = format!("INSERT INTO {} ", table_name.to_string());
write!(f, "INSERT INTO {} ", table_name)?;
if !columns.is_empty() {
s += &format!("({}) ", columns.join(", "));
write!(f, "({}) ", display_comma_separated(columns))?;
}
s += &source.to_string();
s
write!(f, "{}", source)
}
Statement::Copy {
table_name,
columns,
values,
} => {
let mut s = format!("COPY {}", table_name.to_string());
write!(f, "COPY {}", table_name)?;
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() {
s += &format!(
"\n{}",
values
.iter()
.map(|v| v.clone().unwrap_or_else(|| "\\N".to_string()))
.collect::<Vec<String>>()
.join("\t")
);
writeln!(f)?;
let mut delim = "";
for v in values {
write!(f, "{}", delim)?;
delim = "\t";
if let Some(v) = v {
write!(f, "{}", v)?;
} else {
write!(f, "\\N")?;
}
s += "\n\\.";
s
}
}
write!(f, "\n\\.")
}
Statement::Update {
table_name,
assignments,
selection,
} => {
let mut s = format!("UPDATE {}", table_name.to_string());
write!(f, "UPDATE {}", table_name)?;
if !assignments.is_empty() {
s += " SET ";
s += &comma_separated_string(assignments);
write!(f, " SET ")?;
write!(f, "{}", display_comma_separated(assignments))?;
}
if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.to_string());
write!(f, " WHERE {}", selection)?;
}
s
Ok(())
}
Statement::Delete {
table_name,
selection,
} => {
let mut s = format!("DELETE FROM {}", table_name.to_string());
write!(f, "DELETE FROM {}", table_name)?;
if let Some(selection) = selection {
s += &format!(" WHERE {}", selection.to_string());
write!(f, " WHERE {}", selection)?;
}
s
Ok(())
}
Statement::CreateView {
name,
@ -495,25 +505,22 @@ impl ToString for Statement {
materialized,
with_options,
} => {
let modifier = if *materialized { " MATERIALIZED" } else { "" };
let with_options = if !with_options.is_empty() {
format!(" WITH ({})", comma_separated_string(with_options))
} else {
"".into()
};
let columns = if !columns.is_empty() {
format!(" ({})", comma_separated_string(columns))
} else {
"".into()
};
format!(
"CREATE{} VIEW {}{}{} AS {}",
modifier,
name.to_string(),
with_options,
columns,
query.to_string(),
)
write!(f, "CREATE")?;
if *materialized {
write!(f, " MATERIALIZED")?;
}
write!(f, " VIEW {}", name)?;
if !with_options.is_empty() {
write!(f, " WITH ({})", display_comma_separated(with_options))?;
}
if !columns.is_empty() {
write!(f, " ({})", display_comma_separated(columns))?;
}
write!(f, " AS {}", query)
}
Statement::CreateTable {
name,
@ -524,64 +531,66 @@ impl ToString for Statement {
file_format,
location,
} => {
let mut s = format!(
write!(
f,
"CREATE {}TABLE {} ({}",
if *external { "EXTERNAL " } else { "" },
name.to_string(),
comma_separated_string(columns)
);
name,
display_comma_separated(columns)
)?;
if !constraints.is_empty() {
s += &format!(", {}", comma_separated_string(constraints));
write!(f, ", {}", display_comma_separated(constraints))?;
}
s += ")";
write!(f, ")")?;
if *external {
s += &format!(
write!(
f,
" STORED AS {} LOCATION '{}'",
file_format.as_ref().unwrap().to_string(),
file_format.as_ref().unwrap(),
location.as_ref().unwrap()
);
)?;
}
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 } => {
format!("ALTER TABLE {} {}", name.to_string(), operation.to_string())
write!(f, "ALTER TABLE {} {}", name, operation)
}
Statement::Drop {
object_type,
if_exists,
names,
cascade,
} => format!(
} => write!(
f,
"DROP {}{} {}{}",
object_type.to_string(),
object_type,
if *if_exists { " IF EXISTS" } else { "" },
comma_separated_string(names),
display_comma_separated(names),
if *cascade { " CASCADE" } else { "" },
),
Statement::StartTransaction { modes } => format!(
"START TRANSACTION{}",
if modes.is_empty() {
"".into()
} else {
format!(" {}", comma_separated_string(modes))
Statement::StartTransaction { modes } => {
write!(f, "START TRANSACTION")?;
if !modes.is_empty() {
write!(f, " {}", display_comma_separated(modes))?;
}
),
Statement::SetTransaction { modes } => format!(
"SET TRANSACTION{}",
if modes.is_empty() {
"".into()
} else {
format!(" {}", comma_separated_string(modes))
Ok(())
}
Statement::SetTransaction { modes } => {
write!(f, "SET TRANSACTION")?;
if !modes.is_empty() {
write!(f, " {}", display_comma_separated(modes))?;
}
Ok(())
}
),
Statement::Commit { chain } => {
format!("COMMIT{}", if *chain { " AND CHAIN" } else { "" },)
write!(f, "COMMIT{}", if *chain { " AND CHAIN" } else { "" },)
}
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)]
pub struct ObjectName(pub Vec<Ident>);
impl ToString for ObjectName {
fn to_string(&self) -> String {
self.0.join(".")
impl fmt::Display for ObjectName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", display_separated(&self.0, "."))
}
}
@ -604,9 +613,9 @@ pub struct Assignment {
pub value: Expr,
}
impl ToString for Assignment {
fn to_string(&self) -> String {
format!("{} = {}", self.id, self.value.to_string())
impl fmt::Display for Assignment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} = {}", self.id, self.value)
}
}
@ -620,18 +629,19 @@ pub struct Function {
pub distinct: bool,
}
impl ToString for Function {
fn to_string(&self) -> String {
let mut s = format!(
impl fmt::Display for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}({}{})",
self.name.to_string(),
self.name,
if self.distinct { "DISTINCT " } else { "" },
comma_separated_string(&self.args),
);
display_comma_separated(&self.args),
)?;
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,
}
impl ToString for FileFormat {
fn to_string(&self) -> String {
impl fmt::Display for FileFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::FileFormat::*;
write!(
f,
"{}",
match self {
TEXTFILE => "TEXTFILE".to_string(),
SEQUENCEFILE => "SEQUENCEFILE".to_string(),
ORC => "ORC".to_string(),
PARQUET => "PARQUET".to_string(),
AVRO => "AVRO".to_string(),
RCFILE => "RCFILE".to_string(),
JSONFILE => "TEXTFILE".to_string(),
TEXTFILE => "TEXTFILE",
SEQUENCEFILE => "SEQUENCEFILE",
ORC => "ORC",
PARQUET => "PARQUET",
AVRO => "AVRO",
RCFILE => "RCFILE",
JSONFILE => "TEXTFILE",
}
)
}
}
@ -691,12 +705,16 @@ pub enum ObjectType {
View,
}
impl ObjectType {
fn to_string(&self) -> String {
impl fmt::Display for ObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
ObjectType::Table => "TABLE".into(),
ObjectType::View => "VIEW".into(),
ObjectType::Table => "TABLE",
ObjectType::View => "VIEW",
}
)
}
}
@ -706,9 +724,9 @@ pub struct SqlOption {
pub value: Value,
}
impl ToString for SqlOption {
fn to_string(&self) -> String {
format!("{} = {}", self.name.to_string(), self.value.to_string())
impl fmt::Display for SqlOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} = {}", self.name, self.value)
}
}
@ -718,12 +736,12 @@ pub enum TransactionMode {
IsolationLevel(TransactionIsolationLevel),
}
impl ToString for TransactionMode {
fn to_string(&self) -> String {
impl fmt::Display for TransactionMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionMode::*;
match self {
AccessMode(access_mode) => access_mode.to_string(),
IsolationLevel(iso_level) => format!("ISOLATION LEVEL {}", iso_level.to_string()),
AccessMode(access_mode) => write!(f, "{}", access_mode.to_string()),
IsolationLevel(iso_level) => write!(f, "ISOLATION LEVEL {}", iso_level),
}
}
}
@ -734,13 +752,17 @@ pub enum TransactionAccessMode {
ReadWrite,
}
impl ToString for TransactionAccessMode {
fn to_string(&self) -> String {
impl fmt::Display for TransactionAccessMode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionAccessMode::*;
write!(
f,
"{}",
match self {
ReadOnly => "READ ONLY".into(),
ReadWrite => "READ WRITE".into(),
ReadOnly => "READ ONLY",
ReadWrite => "READ WRITE",
}
)
}
}
@ -752,14 +774,18 @@ pub enum TransactionIsolationLevel {
Serializable,
}
impl ToString for TransactionIsolationLevel {
fn to_string(&self) -> String {
impl fmt::Display for TransactionIsolationLevel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TransactionIsolationLevel::*;
write!(
f,
"{}",
match self {
ReadUncommitted => "READ UNCOMMITTED".into(),
ReadCommitted => "READ COMMITTED".into(),
RepeatableRead => "REPEATABLE READ".into(),
Serializable => "SERIALIZABLE".into(),
ReadUncommitted => "READ UNCOMMITTED",
ReadCommitted => "READ COMMITTED",
RepeatableRead => "REPEATABLE READ",
Serializable => "SERIALIZABLE",
}
)
}
}

View file

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

View file

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

View file

@ -11,6 +11,7 @@
// limitations under the License.
use ordered_float::OrderedFloat;
use std::fmt;
/// Primitive SQL values such as number and string
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -56,18 +57,18 @@ pub enum Value {
Null,
}
impl ToString for Value {
fn to_string(&self) -> String {
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Value::Long(v) => v.to_string(),
Value::Double(v) => v.to_string(),
Value::SingleQuotedString(v) => format!("'{}'", escape_single_quote_string(v)),
Value::NationalStringLiteral(v) => format!("N'{}'", v),
Value::HexStringLiteral(v) => format!("X'{}'", v),
Value::Boolean(v) => v.to_string(),
Value::Date(v) => format!("DATE '{}'", escape_single_quote_string(v)),
Value::Time(v) => format!("TIME '{}'", escape_single_quote_string(v)),
Value::Timestamp(v) => format!("TIMESTAMP '{}'", escape_single_quote_string(v)),
Value::Long(v) => write!(f, "{}", v),
Value::Double(v) => write!(f, "{}", v),
Value::SingleQuotedString(v) => write!(f, "'{}'", escape_single_quote_string(v)),
Value::NationalStringLiteral(v) => write!(f, "N'{}'", v),
Value::HexStringLiteral(v) => write!(f, "X'{}'", v),
Value::Boolean(v) => write!(f, "{}", v),
Value::Date(v) => write!(f, "DATE '{}'", escape_single_quote_string(v)),
Value::Time(v) => write!(f, "TIME '{}'", escape_single_quote_string(v)),
Value::Timestamp(v) => write!(f, "TIMESTAMP '{}'", escape_single_quote_string(v)),
Value::Interval {
value,
leading_field: DateTimeField::Second,
@ -78,7 +79,8 @@ impl ToString for Value {
// When the leading field is SECOND, the parser guarantees that
// the last field is None.
assert!(last_field.is_none());
format!(
write!(
f,
"INTERVAL '{}' SECOND ({}, {})",
escape_single_quote_string(value),
leading_precision,
@ -92,23 +94,24 @@ impl ToString for Value {
last_field,
fractional_seconds_precision,
} => {
let mut s = format!(
write!(
f,
"INTERVAL '{}' {}",
escape_single_quote_string(value),
leading_field.to_string()
);
leading_field
)?;
if let Some(leading_precision) = leading_precision {
s += &format!(" ({})", leading_precision);
write!(f, " ({})", leading_precision)?;
}
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 {
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,
}
impl ToString for DateTimeField {
fn to_string(&self) -> String {
impl fmt::Display for DateTimeField {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
DateTimeField::Year => "YEAR".to_string(),
DateTimeField::Month => "MONTH".to_string(),
DateTimeField::Day => "DAY".to_string(),
DateTimeField::Hour => "HOUR".to_string(),
DateTimeField::Minute => "MINUTE".to_string(),
DateTimeField::Second => "SECOND".to_string(),
DateTimeField::Year => "YEAR",
DateTimeField::Month => "MONTH",
DateTimeField::Day => "DAY",
DateTimeField::Hour => "HOUR",
DateTimeField::Minute => "MINUTE",
DateTimeField::Second => "SECOND",
}
)
}
}
fn escape_single_quote_string(s: &str) -> String {
let mut escaped = String::new();
for c in s.chars() {
struct EscapeSingleQuoteString<'a>(&'a str);
impl<'a> fmt::Display for EscapeSingleQuoteString<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for c in self.0.chars() {
if c == '\'' {
escaped.push_str("\'\'");
write!(f, "\'\'")?;
} 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::tokenizer::*;
use std::error::Error;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum ParserError {
@ -52,8 +53,8 @@ impl From<TokenizerError> for ParserError {
}
}
impl std::fmt::Display for ParserError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"sql parser error: {}",
@ -728,7 +729,7 @@ impl Parser {
parser_err!(format!(
"Expected {}, found: {}",
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::Dialect;
use std::fmt;
/// SQL Token enumeration
#[derive(Debug, Clone, PartialEq)]
@ -89,40 +90,40 @@ pub enum Token {
RBrace,
}
impl ToString for Token {
fn to_string(&self) -> String {
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Token::Word(ref w) => w.to_string(),
Token::Number(ref n) => n.to_string(),
Token::Char(ref c) => c.to_string(),
Token::SingleQuotedString(ref s) => format!("'{}'", s),
Token::NationalStringLiteral(ref s) => format!("N'{}'", s),
Token::HexStringLiteral(ref s) => format!("X'{}'", s),
Token::Comma => ",".to_string(),
Token::Whitespace(ws) => ws.to_string(),
Token::Eq => "=".to_string(),
Token::Neq => "<>".to_string(),
Token::Lt => "<".to_string(),
Token::Gt => ">".to_string(),
Token::LtEq => "<=".to_string(),
Token::GtEq => ">=".to_string(),
Token::Plus => "+".to_string(),
Token::Minus => "-".to_string(),
Token::Mult => "*".to_string(),
Token::Div => "/".to_string(),
Token::Mod => "%".to_string(),
Token::LParen => "(".to_string(),
Token::RParen => ")".to_string(),
Token::Period => ".".to_string(),
Token::Colon => ":".to_string(),
Token::DoubleColon => "::".to_string(),
Token::SemiColon => ";".to_string(),
Token::Backslash => "\\".to_string(),
Token::LBracket => "[".to_string(),
Token::RBracket => "]".to_string(),
Token::Ampersand => "&".to_string(),
Token::LBrace => "{".to_string(),
Token::RBrace => "}".to_string(),
Token::Word(ref w) => write!(f, "{}", w),
Token::Number(ref n) => f.write_str(n),
Token::Char(ref c) => write!(f, "{}", c),
Token::SingleQuotedString(ref s) => write!(f, "'{}'", s),
Token::NationalStringLiteral(ref s) => write!(f, "N'{}'", s),
Token::HexStringLiteral(ref s) => write!(f, "X'{}'", s),
Token::Comma => f.write_str(","),
Token::Whitespace(ws) => write!(f, "{}", ws),
Token::Eq => f.write_str("="),
Token::Neq => f.write_str("<>"),
Token::Lt => f.write_str("<"),
Token::Gt => f.write_str(">"),
Token::LtEq => f.write_str("<="),
Token::GtEq => f.write_str(">="),
Token::Plus => f.write_str("+"),
Token::Minus => f.write_str("-"),
Token::Mult => f.write_str("*"),
Token::Div => f.write_str("/"),
Token::Mod => f.write_str("%"),
Token::LParen => f.write_str("("),
Token::RParen => f.write_str(")"),
Token::Period => f.write_str("."),
Token::Colon => f.write_str(":"),
Token::DoubleColon => f.write_str("::"),
Token::SemiColon => f.write_str(";"),
Token::Backslash => f.write_str("\\"),
Token::LBracket => f.write_str("["),
Token::RBracket => f.write_str("]"),
Token::Ampersand => f.write_str("&"),
Token::LBrace => f.write_str("{"),
Token::RBrace => f.write_str("}"),
}
}
}
@ -164,13 +165,13 @@ pub struct Word {
pub keyword: String,
}
impl ToString for Word {
fn to_string(&self) -> String {
impl fmt::Display for Word {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.quote_style {
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!"),
}
}
@ -195,14 +196,14 @@ pub enum Whitespace {
MultiLineComment(String),
}
impl ToString for Whitespace {
fn to_string(&self) -> String {
impl fmt::Display for Whitespace {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Whitespace::Space => " ".to_string(),
Whitespace::Newline => "\n".to_string(),
Whitespace::Tab => "\t".to_string(),
Whitespace::SingleLineComment(s) => format!("--{}", s),
Whitespace::MultiLineComment(s) => format!("/*{}*/", s),
Whitespace::Space => f.write_str(" "),
Whitespace::Newline => f.write_str("\n"),
Whitespace::Tab => f.write_str("\t"),
Whitespace::SingleLineComment(s) => write!(f, "--{}", s),
Whitespace::MultiLineComment(s) => write!(f, "/*{}*/", s),
}
}
}