mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-25 16:34:04 +00:00
implement fmt::Display instead of ToString
This commit is contained in:
parent
cdba43682f
commit
b2b159fed1
8 changed files with 605 additions and 550 deletions
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
110
src/ast/ddl.rs
110
src/ast/ddl.rs
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
474
src/ast/mod.rs
474
src/ast/mod.rs
|
@ -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",
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
236
src/ast/query.rs
236
src/ast/query.rs
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue