Skip to content

Move Statement and Expr so they hold identifiable structs #345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file looks pretty specific to your own environment -- did you mean to propose including it into this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah I think I was a little careless as I initially didn't think I was going to submit a pull request. I also noticed I made the changes on the master branch, would you prefer I did another submission on a separate branch?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also noticed I made the changes on the master branch,

I don't think it matters what branch you work on in your fork (tvallotton:main is fine)

"RA_LOG=lsp_server": "debug",
"RA_LOG_FILE": "/Users/tomas/Documents/sqlparser-rs/output"
}
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Fork
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is probably not accurate

This is a fork of [`sqlparser`](https://github.com/sqlparser-rs/sqlparser-rs). The major difference between `sqlparse` and this fork is that here `Statement` and `Expr` hold structs instead of variables.

# Extensible SQL Lexer and Parser for Rust

[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
Expand Down
18 changes: 15 additions & 3 deletions src/ast/data_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub enum DataType {
/// Variable-length binary type e.g. VARBINARY(10)
Varbinary(u64),
/// Large binary object e.g. BLOB(1000)
Blob(u64),
Blob(Option<u64>),
/// Decimal type with optional precision and scale e.g. DECIMAL(10,2)
Decimal(Option<u64>, Option<u64>),
/// Floating point with optional precision e.g. FLOAT(8)
Expand Down Expand Up @@ -88,7 +88,13 @@ impl fmt::Display for DataType {
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::Blob(size) => {
if let Some(size) = size {
write!(f, "BLOB({})", size)
} else {
write!(f, "BLOB")
}
}
DataType::Decimal(precision, scale) => {
if let Some(scale) = scale {
write!(f, "NUMERIC({},{})", precision.unwrap(), scale)
Expand All @@ -101,7 +107,13 @@ impl fmt::Display for DataType {
DataType::SmallInt(zerofill) => {
format_type_with_optional_length(f, "SMALLINT", zerofill)
}
DataType::Int(zerofill) => format_type_with_optional_length(f, "INT", zerofill),
DataType::Int(zerofill) => {
if let Some(len) = zerofill {
write!(f, "INT({})", len)
} else {
write!(f, "INTEGER")
}
}
DataType::BigInt(zerofill) => format_type_with_optional_length(f, "BIGINT", zerofill),
DataType::Real => write!(f, "REAL"),
DataType::Double => write!(f, "DOUBLE"),
Expand Down
80 changes: 55 additions & 25 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ pub enum AlterTableOperation {
/// `ADD [ COLUMN ] <column_def>`
AddColumn { column_def: ColumnDef },
/// TODO: implement `DROP CONSTRAINT <name>`
DropConstraint { name: Ident },
DropConstraint {
name: Ident,
cascade: bool,
restrict: bool,
},
/// `DROP [ COLUMN ] [ IF EXISTS ] <column_name> [ CASCADE ]`
DropColumn {
column_name: Ident,
Expand Down Expand Up @@ -87,7 +91,19 @@ impl fmt::Display for AlterTableOperation {
display_comma_separated(partitions),
ie = if *if_exists { " IF EXISTS" } else { "" }
),
AlterTableOperation::DropConstraint { name } => write!(f, "DROP CONSTRAINT {}", name),
AlterTableOperation::DropConstraint {
name,
cascade,
restrict,
} => {
if *cascade {
write!(f, "DROP CONSTRAINT {} CASCADE", name)
} else if *restrict {
write!(f, "DROP CONSTRAINT {} RESTRICT", name)
} else {
write!(f, "DROP CONSTRAINT {}", name)
}
}
AlterTableOperation::DropColumn {
column_name,
if_exists,
Expand Down Expand Up @@ -129,54 +145,68 @@ impl fmt::Display for AlterTableOperation {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TableConstraint {
/// `[ CONSTRAINT <name> ] { PRIMARY KEY | UNIQUE } (<columns>)`
Unique {
name: Option<Ident>,
columns: Vec<Ident>,
/// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint
is_primary: bool,
},
Unique(Unique),
/// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
/// REFERENCES <foreign_table> (<referred_columns>)
/// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
/// [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
/// }`).
ForeignKey {
name: Option<Ident>,
columns: Vec<Ident>,
foreign_table: ObjectName,
referred_columns: Vec<Ident>,
on_delete: Option<ReferentialAction>,
on_update: Option<ReferentialAction>,
},
ForeignKey(ForeignKey),
/// `[ CONSTRAINT <name> ] CHECK (<expr>)`
Check {
name: Option<Ident>,
expr: Box<Expr>,
},
Check(Check),
}
/// `[ CONSTRAINT <name> ] { PRIMARY KEY | UNIQUE } (<columns>)`
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Unique {
pub name: Option<Ident>,
pub columns: Vec<Ident>,
/// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint
pub is_primary: bool,
}
/// `[ CONSTRAINT <name> ] CHECK (<expr>)`
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Check {
pub name: Option<Ident>,
pub expr: Box<Expr>,
}

/// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
/// REFERENCES <foreign_table> (<referred_columns>)
/// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
/// [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
/// }`).
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ForeignKey {
pub name: Option<Ident>,
pub columns: Vec<Ident>,
pub foreign_table: ObjectName,
pub referred_columns: Vec<Ident>,
pub on_delete: Option<ReferentialAction>,
pub on_update: Option<ReferentialAction>,
}

impl fmt::Display for TableConstraint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TableConstraint::Unique {
TableConstraint::Unique(Unique {
name,
columns,
is_primary,
} => write!(
}) => write!(
f,
"{}{} ({})",
display_constraint_name(name),
if *is_primary { "PRIMARY KEY" } else { "UNIQUE" },
display_comma_separated(columns)
),
TableConstraint::ForeignKey {
TableConstraint::ForeignKey(ForeignKey {
name,
columns,
foreign_table,
referred_columns,
on_delete,
on_update,
} => {
}) => {
write!(
f,
"{}FOREIGN KEY ({}) REFERENCES {}({})",
Expand All @@ -193,7 +223,7 @@ impl fmt::Display for TableConstraint {
}
Ok(())
}
TableConstraint::Check { name, expr } => {
TableConstraint::Check(Check { name, expr }) => {
write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
}
}
Expand Down
161 changes: 161 additions & 0 deletions src/ast/expression/display.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use super::*;

impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expr::Identifier(s) => write!(f, "{}", s),
Expr::MapAccess(x) => write!(f, "{}", x),
Expr::Wildcard => f.write_str("*"),
Expr::QualifiedWildcard(q) => write!(f, "{}.*", display_separated(q, ".")),
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(x) => write!(f, "{}", x),
Expr::InSubquery(x) => write!(f, "{}", x),
Expr::Between(x) => write!(f, "{}", x),
Expr::BinaryOp(x) => write!(f, "{}", x),
Expr::UnaryOp(x) => write!(f, "{}", x),
Expr::Cast(x) => write!(f, "{}", x),
Expr::TryCast(x) => write!(f, "{}", x),
Expr::Extract(x) => write!(f, "{}", x),
Expr::Collate(x) => write!(f, "{}", x),
Expr::Nested(ast) => write!(f, "({})", ast),
Expr::Value(v) => write!(f, "{}", v),
Expr::TypedString(x) => write!(f, "{}", x),
Expr::Function(fun) => write!(f, "{}", fun),
Expr::Case(x) => write!(f, "{}", x),
Expr::Exists(s) => write!(f, "EXISTS ({})", s),
Expr::Subquery(s) => write!(f, "({})", s),
Expr::ListAgg(listagg) => write!(f, "{}", listagg),
Expr::Substring(x) => write!(f, "{}", x),
Expr::Trim(x) => write!(f, "{}", x),
}
}
}

impl fmt::Display for Trim {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TRIM(")?;
if let Some((ref ident, ref trim_char)) = self.trim_where {
write!(f, "{} {} FROM {}", ident, trim_char, self.expr)?;
} else {
write!(f, "{}", self.expr)?;
}
write!(f, ")")
}
}

impl fmt::Display for Substring {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SUBSTRING({}", self.expr)?;
if let Some(ref from_part) = self.substring_from {
write!(f, " FROM {}", from_part)?;
}
if let Some(ref from_part) = self.substring_for {
write!(f, " FOR {}", from_part)?;
}

write!(f, ")")
}
}

impl fmt::Display for Case {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CASE")?;
if let Some(ref operand) = self.operand {
write!(f, " {}", operand)?;
}
for (c, r) in self.conditions.iter().zip(&self.results) {
write!(f, " WHEN {} THEN {}", c, r)?;
}

if let Some(ref else_result) = self.else_result {
write!(f, " ELSE {}", else_result)?;
}
write!(f, " END")
}
}

impl fmt::Display for TypedString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.data_type)?;
write!(f, " '{}'", &value::escape_single_quote_string(&self.value))
}
}

impl fmt::Display for Collate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} COLLATE {}", self.expr, self.collation)
}
}
impl fmt::Display for UnaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.op == UnaryOperator::PGPostfixFactorial {
write!(f, "{}{}", self.expr, self.op)
} else {
write!(f, "{} {}", self.op, self.expr)
}
}
}
impl fmt::Display for InSubquery {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {}IN ({})",
self.expr,
if self.negated { "NOT " } else { "" },
self.subquery
)
}
}
impl fmt::Display for Between {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {}BETWEEN {} AND {}",
self.expr,
if self.negated { "NOT " } else { "" },
self.low,
self.high
)
}
}
impl fmt::Display for InList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {}IN ({})",
self.expr,
if self.negated { "NOT " } else { "" },
display_comma_separated(&self.list)
)
}
}
impl fmt::Display for BinaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {} {}", self.left, self.op, self.right)
}
}
impl fmt::Display for MapAccess {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}[\"{}\"]", self.column, self.key)
}
}

impl fmt::Display for Cast {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CAST({} AS {})", self.expr, self.data_type)
}
}

impl fmt::Display for TryCast {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TRY_CAST({} AS {})", self.expr, self.data_type)
}
}

impl fmt::Display for Extract {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "EXTRACT({} FROM {})", self.field, self.expr)
}
}
Loading