Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ $ cargo run --features json_example --example cli FILENAME.sql [--dialectname]

## Users

This parser is currently being used by the [DataFusion] query engine, [LocustDB],
[Ballista], [GlueSQL], [Opteryx], [Polars], [PRQL], [Qrlew], [JumpWire], [ParadeDB], [CipherStash Proxy],
and [GreptimeDB].
This parser is currently being used by the [DataFusion] query engine,
[LocustDB], [Ballista], [GlueSQL], [Opteryx], [Polars], [PRQL], [Qrlew],
[JumpWire], [ParadeDB], [CipherStash Proxy], [Readyset] and [GreptimeDB].

If your project is using sqlparser-rs feel free to make a PR to add it
to this list.
Expand Down Expand Up @@ -282,3 +282,4 @@ licensed as above, without any additional terms or conditions.
[`GenericDialect`]: https://docs.rs/sqlparser/latest/sqlparser/dialect/struct.GenericDialect.html
[CipherStash Proxy]: https://github.com/cipherstash/proxy
[GreptimeDB]: https://github.com/GreptimeTeam/greptimedb
[Readyset]: https://github.com/readysettech/readyset
2 changes: 1 addition & 1 deletion src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2040,7 +2040,7 @@ impl fmt::Display for ColumnOption {
Ok(())
}
Unique(constraint) => {
write!(f, "UNIQUE")?;
write!(f, "UNIQUE{:>}", constraint.index_type_display)?;
if let Some(characteristics) = &constraint.characteristics {
write!(f, " {characteristics}")?;
}
Expand Down
7 changes: 6 additions & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1131,7 +1131,7 @@ pub enum Expr {
/// ```sql
/// TRIM([BOTH | LEADING | TRAILING] [<expr> FROM] <expr>)
/// TRIM(<expr>)
/// TRIM(<expr>, [, characters]) -- only Snowflake or Bigquery
/// TRIM(<expr>, [, characters]) -- PostgreSQL, DuckDB, Snowflake, BigQuery, Generic
/// ```
Trim {
/// The expression to trim from.
Expand Down Expand Up @@ -7625,6 +7625,10 @@ pub enum FunctionArgExpr {
QualifiedWildcard(ObjectName),
/// An unqualified `*` wildcard.
Wildcard,
/// An unqualified `*` wildcard with additional options, e.g. `* EXCLUDE(col)`.
///
/// Used in Snowflake to support expressions like `HASH(* EXCLUDE(col))`.
WildcardWithOptions(WildcardAdditionalOptions),
}

impl From<Expr> for FunctionArgExpr {
Expand All @@ -7643,6 +7647,7 @@ impl fmt::Display for FunctionArgExpr {
FunctionArgExpr::Expr(expr) => write!(f, "{expr}"),
FunctionArgExpr::QualifiedWildcard(prefix) => write!(f, "{prefix}.*"),
FunctionArgExpr::Wildcard => f.write_str("*"),
FunctionArgExpr::WildcardWithOptions(opts) => write!(f, "*{opts}"),
}
}
}
Expand Down
11 changes: 9 additions & 2 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,9 @@ pub struct WildcardAdditionalOptions {
pub opt_replace: Option<ReplaceSelectItem>,
/// `[RENAME ...]`.
pub opt_rename: Option<RenameSelectItem>,
/// `[AS <alias>]`.
/// Redshift syntax: <https://docs.aws.amazon.com/redshift/latest/dg/r_SELECT_list.html>
pub opt_alias: Option<Ident>,
}

impl Default for WildcardAdditionalOptions {
Expand All @@ -945,6 +948,7 @@ impl Default for WildcardAdditionalOptions {
opt_except: None,
opt_replace: None,
opt_rename: None,
opt_alias: None,
}
}
}
Expand All @@ -966,6 +970,9 @@ impl fmt::Display for WildcardAdditionalOptions {
if let Some(rename) = &self.opt_rename {
write!(f, " {rename}")?;
}
if let Some(alias) = &self.opt_alias {
write!(f, " AS {alias}")?;
}
Ok(())
}
}
Expand Down Expand Up @@ -1011,13 +1018,13 @@ pub enum ExcludeSelectItem {
/// ```plaintext
/// <col_name>
/// ```
Single(Ident),
Single(ObjectName),
/// Multiple column names inside parenthesis.
/// # Syntax
/// ```plaintext
/// (<col_name>, <col_name>, ...)
/// ```
Multiple(Vec<Ident>),
Multiple(Vec<ObjectName>),
}

impl fmt::Display for ExcludeSelectItem {
Expand Down
10 changes: 7 additions & 3 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1824,6 +1824,7 @@ impl Spanned for WildcardAdditionalOptions {
opt_except,
opt_replace,
opt_rename,
opt_alias,
} = self;

union_spans(
Expand All @@ -1832,7 +1833,8 @@ impl Spanned for WildcardAdditionalOptions {
.chain(opt_exclude.as_ref().map(|i| i.span()))
.chain(opt_rename.as_ref().map(|i| i.span()))
.chain(opt_replace.as_ref().map(|i| i.span()))
.chain(opt_except.as_ref().map(|i| i.span())),
.chain(opt_except.as_ref().map(|i| i.span()))
.chain(opt_alias.as_ref().map(|i| i.span)),
)
}
}
Expand All @@ -1847,8 +1849,8 @@ impl Spanned for IlikeSelectItem {
impl Spanned for ExcludeSelectItem {
fn span(&self) -> Span {
match self {
ExcludeSelectItem::Single(ident) => ident.span,
ExcludeSelectItem::Multiple(vec) => union_spans(vec.iter().map(|i| i.span)),
ExcludeSelectItem::Single(name) => name.span(),
ExcludeSelectItem::Multiple(vec) => union_spans(vec.iter().map(|i| i.span())),
}
}
}
Expand Down Expand Up @@ -2128,6 +2130,7 @@ impl Spanned for FunctionArg {
///
/// Missing spans:
/// - [FunctionArgExpr::Wildcard]
/// - [FunctionArgExpr::WildcardWithOptions]
impl Spanned for FunctionArgExpr {
fn span(&self) -> Span {
match self {
Expand All @@ -2136,6 +2139,7 @@ impl Spanned for FunctionArgExpr {
union_spans(object_name.0.iter().map(|i| i.span()))
}
FunctionArgExpr::Wildcard => Span::empty(),
FunctionArgExpr::WildcardWithOptions(_) => Span::empty(),
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/dialect/bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,8 @@ impl Dialect for BigQueryDialect {
fn supports_select_wildcard_replace(&self) -> bool {
true
}

fn supports_comma_separated_trim(&self) -> bool {
true
}
}
4 changes: 4 additions & 0 deletions src/dialect/clickhouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,8 @@ impl Dialect for ClickHouseDialect {
fn supports_select_wildcard_replace(&self) -> bool {
true
}

fn supports_comma_separated_trim(&self) -> bool {
true
}
}
4 changes: 4 additions & 0 deletions src/dialect/duckdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,8 @@ impl Dialect for DuckDbDialect {
fn supports_select_wildcard_replace(&self) -> bool {
true
}

fn supports_comma_separated_trim(&self) -> bool {
true
}
}
8 changes: 8 additions & 0 deletions src/dialect/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,12 @@ impl Dialect for GenericDialect {
fn supports_constraint_keyword_without_name(&self) -> bool {
true
}

fn supports_key_column_option(&self) -> bool {
true
}

fn supports_comma_separated_trim(&self) -> bool {
true
}
}
30 changes: 30 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,18 @@ pub trait Dialect: Debug + Any {
false
}

/// Returns true if the dialect supports the `KEY` keyword as part of
/// column-level constraints in a `CREATE TABLE` statement.
///
/// When enabled, the parser accepts these MySQL-specific column options:
/// - `UNIQUE [KEY]` — optional `KEY` after `UNIQUE`
/// - `[PRIMARY] KEY` — standalone `KEY` as shorthand for `PRIMARY KEY`
///
/// <https://dev.mysql.com/doc/refman/8.4/en/create-table.html>
fn supports_key_column_option(&self) -> bool {
false
}

/// Returns true if the specified keyword is reserved and cannot be
/// used as an identifier without special handling like quoting.
fn is_reserved_for_identifier(&self, kw: Keyword) -> bool {
Expand Down Expand Up @@ -1517,6 +1529,18 @@ pub trait Dialect: Debug + Any {
false
}

/// Returns true if this dialect supports aliasing a wildcard select item.
///
/// Example:
/// ```sql
/// SELECT t.* AS alias FROM t
/// ```
///
/// [Redshift](https://docs.aws.amazon.com/redshift/latest/dg/r_SELECT_list.html)
fn supports_select_wildcard_with_alias(&self) -> bool {
false
}

/// Returns true if this dialect supports the `OPTIMIZE TABLE` statement.
///
/// Example:
Expand Down Expand Up @@ -1627,6 +1651,12 @@ pub trait Dialect: Debug + Any {
fn supports_select_format(&self) -> bool {
false
}

/// Returns true if the dialect supports the two-argument comma-separated
/// form of the `TRIM` function: `TRIM(expr, characters)`.
fn supports_comma_separated_trim(&self) -> bool {
false
}
}

/// Operators for which precedence must be defined.
Expand Down
56 changes: 51 additions & 5 deletions src/dialect/mssql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,64 @@ impl Dialect for MsSqlDialect {

fn is_select_item_alias(&self, explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
match kw {
// List of keywords that cannot be used as select item aliases in MSSQL
// regardless of whether the alias is explicit or implicit
Keyword::IF | Keyword::ELSE => false,
// List of keywords that cannot be used as select item (column) aliases in MSSQL
// regardless of whether the alias is explicit or implicit.
//
// These are T-SQL statement-starting keywords; allowing them as implicit aliases
// causes the parser to consume the keyword as an alias for the previous expression,
// then fail on the token that follows (e.g. `TABLE`, `@var`, `sp_name`, …).
Keyword::IF
| Keyword::ELSE
| Keyword::DECLARE
| Keyword::EXEC
| Keyword::EXECUTE
| Keyword::INSERT
| Keyword::UPDATE
| Keyword::DELETE
| Keyword::DROP
| Keyword::CREATE
| Keyword::ALTER
| Keyword::TRUNCATE
| Keyword::PRINT
| Keyword::WHILE
| Keyword::RETURN
| Keyword::THROW
| Keyword::RAISERROR
| Keyword::MERGE => false,
_ => explicit || self.is_column_alias(kw, parser),
}
}

fn is_table_factor_alias(&self, explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
match kw {
// List of keywords that cannot be used as table aliases in MSSQL
// regardless of whether the alias is explicit or implicit
Keyword::IF | Keyword::ELSE => false,
// regardless of whether the alias is explicit or implicit.
//
// These are T-SQL statement-starting keywords. Without blocking them here,
// a bare `SELECT * FROM t` followed by a newline and one of these keywords
// would cause the parser to consume the keyword as a table alias for `t`,
// then fail on the token that follows (e.g. `@var`, `sp_name`, `TABLE`, …).
//
// `SET` is already covered by the global `RESERVED_FOR_TABLE_ALIAS` list;
// the keywords below are MSSQL-specific additions.
Keyword::IF
| Keyword::ELSE
| Keyword::DECLARE
| Keyword::EXEC
| Keyword::EXECUTE
| Keyword::INSERT
| Keyword::UPDATE
| Keyword::DELETE
| Keyword::DROP
| Keyword::CREATE
| Keyword::ALTER
| Keyword::TRUNCATE
| Keyword::PRINT
| Keyword::WHILE
| Keyword::RETURN
| Keyword::THROW
| Keyword::RAISERROR
| Keyword::MERGE => false,
_ => explicit || self.is_table_alias(kw, parser),
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/dialect/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ impl Dialect for MySqlDialect {
fn supports_constraint_keyword_without_name(&self) -> bool {
true
}

/// See: <https://dev.mysql.com/doc/refman/8.4/en/create-table.html>
fn supports_key_column_option(&self) -> bool {
true
}
}

/// `LOCK TABLES`
Expand Down
8 changes: 8 additions & 0 deletions src/dialect/postgresql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,4 +302,12 @@ impl Dialect for PostgreSqlDialect {
fn supports_insert_table_alias(&self) -> bool {
true
}

fn supports_create_table_like_parenthesized(&self) -> bool {
true
}

fn supports_comma_separated_trim(&self) -> bool {
true
}
}
4 changes: 4 additions & 0 deletions src/dialect/redshift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ impl Dialect for RedshiftSqlDialect {
true
}

fn supports_select_wildcard_with_alias(&self) -> bool {
true
}

fn supports_select_exclude(&self) -> bool {
true
}
Expand Down
4 changes: 4 additions & 0 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,10 @@ impl Dialect for SnowflakeDialect {
fn supports_lambda_functions(&self) -> bool {
true
}

fn supports_comma_separated_trim(&self) -> bool {
true
}
}

// Peeks ahead to identify tokens that are expected after
Expand Down
4 changes: 4 additions & 0 deletions src/dialect/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,8 @@ impl Dialect for SQLiteDialect {
fn supports_notnull_operator(&self) -> bool {
true
}

fn supports_comma_separated_trim(&self) -> bool {
true
}
}
Loading