Skip to content

Commit

Permalink
Add the option for the parser to try and parse an expression as ident…
Browse files Browse the repository at this point in the history
…ifier if the expression parsing fails
  • Loading branch information
yoavcloud committed Nov 11, 2024
1 parent 334a5bf commit d39af8d
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 185 deletions.
3 changes: 3 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,8 @@ pub enum Expr {
// https://cloud.google.com/bigquery/docs/reference/standard-sql/format-elements#formatting_syntax
format: Option<CastFormat>,
},
/// `DEFAULT` value of a column e.g. INSERT INTO tbl (a, b) VALUES ('foo', DEFAULT)
Default,
/// AT a timestamp to a different timezone e.g. `FROM_UNIXTIME(0) AT TIME ZONE 'UTC-06:00'`
AtTimeZone {
timestamp: Box<Expr>,
Expand Down Expand Up @@ -1432,6 +1434,7 @@ impl fmt::Display for Expr {
write!(f, "{expr}::{data_type}")
}
},
Expr::Default => write!(f, "DEFAULT"),
Expr::Extract {
field,
syntax,
Expand Down
6 changes: 6 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,12 @@ pub trait Dialect: Debug + Any {
fn supports_top_before_distinct(&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 {
keywords::RESERVED_FOR_IDENTIFIER.contains(&kw)
}
}

/// This represents the operators for which precedence must be defined
Expand Down
12 changes: 12 additions & 0 deletions src/dialect/snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use alloc::{format, vec};

use super::keywords::RESERVED_FOR_IDENTIFIER;

/// A [`Dialect`] for [Snowflake](https://www.snowflake.com/)
#[derive(Debug, Default)]
pub struct SnowflakeDialect;
Expand Down Expand Up @@ -203,6 +205,16 @@ impl Dialect for SnowflakeDialect {
fn allow_extract_single_quotes(&self) -> bool {
true
}

fn is_reserved_for_identifier(&self, kw: Keyword) -> bool {
// Unreserve some keywords that Snowflake accepts as identifiers
// See: https://docs.snowflake.com/en/sql-reference/reserved-keywords
if matches!(kw, Keyword::INTERVAL) {
false
} else {
RESERVED_FOR_IDENTIFIER.contains(&kw)
}
}
}

/// Parse snowflake create table statement.
Expand Down
10 changes: 10 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,3 +938,13 @@ pub const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[
Keyword::INTO,
Keyword::END,
];

/// Global list of reserved keywords that cannot be parsed as identifiers
/// without special handling like quoting. Parser should call `Dialect::is_reserved_for_identifier`
/// to allow for each dialect to customize the list.
pub const RESERVED_FOR_IDENTIFIER: &[Keyword] = &[
Keyword::EXISTS,
Keyword::INTERVAL,
Keyword::STRUCT,
Keyword::TRIM,
];
367 changes: 204 additions & 163 deletions src/parser/mod.rs

Large diffs are not rendered by default.

10 changes: 2 additions & 8 deletions tests/sqlparser_bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1744,10 +1744,7 @@ fn parse_merge() {
columns: vec![Ident::new("a"), Ident::new("b"),],
kind: MergeInsertKind::Values(Values {
explicit_row: false,
rows: vec![vec![
Expr::Value(number("1")),
Expr::Identifier(Ident::new("DEFAULT")),
]]
rows: vec![vec![Expr::Value(number("1")), Expr::Default,]]
})
})
},
Expand All @@ -1758,10 +1755,7 @@ fn parse_merge() {
columns: vec![],
kind: MergeInsertKind::Values(Values {
explicit_row: false,
rows: vec![vec![
Expr::Value(number("1")),
Expr::Identifier(Ident::new("DEFAULT")),
]]
rows: vec![vec![Expr::Value(number("1")), Expr::Default,]]
})
})
},
Expand Down
6 changes: 4 additions & 2 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use sqlparser::dialect::{
GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, PostgreSqlDialect, RedshiftSqlDialect,
SQLiteDialect, SnowflakeDialect,
};
use sqlparser::keywords::ALL_KEYWORDS;
use sqlparser::keywords::{Keyword, ALL_KEYWORDS};
use sqlparser::parser::{Parser, ParserError, ParserOptions};
use sqlparser::tokenizer::Tokenizer;
use test_utils::{
Expand Down Expand Up @@ -5109,7 +5109,9 @@ fn parse_interval_dont_require_unit() {

#[test]
fn parse_interval_require_unit() {
let dialects = all_dialects_where(|d| d.require_interval_qualifier());
let dialects = all_dialects_where(|d| {
d.require_interval_qualifier() && d.is_reserved_for_identifier(Keyword::INTERVAL)
});

let sql = "SELECT INTERVAL '1 DAY'";
let err = dialects.parse_sql_statements(sql).unwrap_err();
Expand Down
15 changes: 3 additions & 12 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1352,10 +1352,7 @@ fn parse_set() {
local: false,
hivevar: false,
variables: OneOrManyWithParens::One(ObjectName(vec![Ident::new("a")])),
value: vec![Expr::Identifier(Ident {
value: "DEFAULT".into(),
quote_style: None
})],
value: vec![Expr::Default],
}
);

Expand Down Expand Up @@ -4290,10 +4287,7 @@ fn test_simple_postgres_insert_with_alias() {
body: Box::new(SetExpr::Values(Values {
explicit_row: false,
rows: vec![vec![
Expr::Identifier(Ident {
value: "DEFAULT".to_string(),
quote_style: None
}),
Expr::Default,
Expr::Value(Value::Number("123".to_string(), false))
]]
})),
Expand Down Expand Up @@ -4424,10 +4418,7 @@ fn test_simple_insert_with_quoted_alias() {
body: Box::new(SetExpr::Values(Values {
explicit_row: false,
rows: vec![vec![
Expr::Identifier(Ident {
value: "DEFAULT".to_string(),
quote_style: None
}),
Expr::Default,
Expr::Value(Value::SingleQuotedString("0123".to_string()))
]]
})),
Expand Down

0 comments on commit d39af8d

Please sign in to comment.