Skip to content

Commit

Permalink
Merge branch 'feature/1533-dereference-expr-2' into wren/0.12.3-array…
Browse files Browse the repository at this point in the history
…-struct

# Conflicts:
#	src/dialect/mod.rs
#	src/parser/mod.rs
#	tests/sqlparser_common.rs
  • Loading branch information
goldmedal committed Nov 27, 2024
2 parents 55f1eeb + dc5e540 commit bb1833f
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 18 deletions.
4 changes: 4 additions & 0 deletions src/dialect/bigquery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,8 @@ impl Dialect for BigQueryDialect {
fn require_interval_qualifier(&self) -> bool {
true
}

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

/// See DuckDB <https://duckdb.org/docs/sql/data_types/struct.html#retrieving-from-structs>
fn support_period_map_access_key(&self) -> bool {
true
}
}
4 changes: 4 additions & 0 deletions src/dialect/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,8 @@ impl Dialect for GenericDialect {
fn supports_named_fn_args_with_assignment_operator(&self) -> bool {
true
}

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

/// Return true if the dialect supports the period map access key
///
/// Access on BigQuery nested and repeated expressions can
/// mix notations in the same expression.
/// <https://cloud.google.com/bigquery/docs/nested-repeated#query_nested_and_repeated_columns>
fn support_period_map_access_key(&self) -> bool {
false
}

/// Returns true if the dialect supports PartiQL for querying semi-structured data
/// <https://partiql.org/index.html>
fn supports_partiql(&self) -> bool {
Expand Down
64 changes: 46 additions & 18 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2976,12 +2976,23 @@ impl<'a> Parser<'a> {
})
} else if Token::LBracket == tok {
if dialect_of!(self is PostgreSqlDialect | DuckDbDialect | GenericDialect) {
self.parse_subscript(expr)
let expr = self.parse_multi_dim_subscript(expr)?;
if self.dialect.support_period_map_access_key() {
self.parse_map_access(expr, vec![])
} else {
Ok(expr)
}
} else if dialect_of!(self is SnowflakeDialect) || self.dialect.supports_partiql() {
self.prev_token();
self.parse_json_access(expr)
} else {
self.parse_map_access(expr)
let key = self.parse_expr()?;
self.expect_token(&Token::RBracket)?;
let keys = vec![MapAccessKey {
key,
syntax: MapAccessSyntax::Bracket,
}];
self.parse_map_access(expr, keys)
}
} else if dialect_of!(self is SnowflakeDialect | GenericDialect) && Token::Colon == tok {
self.prev_token();
Expand Down Expand Up @@ -3077,6 +3088,19 @@ impl<'a> Parser<'a> {
})
}

/// Parse an multi-dimension array accessing like `[1:3][1][1]`
///
/// Parser is right after the first `[`
pub fn parse_multi_dim_subscript(&mut self, mut expr: Expr) -> Result<Expr, ParserError> {
loop {
expr = self.parse_subscript(expr)?;
if !self.consume_token(&Token::LBracket) {
break;
}
}
Ok(expr)
}

/// Parses an array subscript like `[1:3]`
///
/// Parser is right after `[`
Expand Down Expand Up @@ -3147,14 +3171,15 @@ impl<'a> Parser<'a> {
Ok(JsonPath { path })
}

pub fn parse_map_access(&mut self, expr: Expr) -> Result<Expr, ParserError> {
let key = self.parse_expr()?;
self.expect_token(&Token::RBracket)?;

let mut keys = vec![MapAccessKey {
key,
syntax: MapAccessSyntax::Bracket,
}];
/// Parse the map access like `[key]` or `.key` if [Dialect::support_period_map_access_key] is true
/// It could be an access-chain like `[key1][key2].key3`
///
/// The parameter `keys` is an initialized buffer that could contain some keys parsed from other places.
pub fn parse_map_access(
&mut self,
expr: Expr,
mut keys: Vec<MapAccessKey>,
) -> Result<Expr, ParserError> {
loop {
let key = match self.peek_token().token {
Token::LBracket => {
Expand All @@ -3166,10 +3191,7 @@ impl<'a> Parser<'a> {
syntax: MapAccessSyntax::Bracket,
}
}
// Access on BigQuery nested and repeated expressions can
// mix notations in the same expression.
// https://cloud.google.com/bigquery/docs/nested-repeated#query_nested_and_repeated_columns
Token::Period if dialect_of!(self is BigQueryDialect) => {
Token::Period if self.dialect.support_period_map_access_key() => {
self.next_token(); // consume `.`
MapAccessKey {
key: self.parse_expr()?,
Expand All @@ -3181,10 +3203,16 @@ impl<'a> Parser<'a> {
keys.push(key);
}

Ok(Expr::MapAccess {
column: Box::new(expr),
keys,
})
// If no any key be collected, it means the elements have been parsed to [Subscript]
// e.g. `select abc[1]` or `select abc[1][2]`
if keys.is_empty() {
Ok(expr)
} else {
Ok(Expr::MapAccess {
column: Box::new(expr),
keys,
})
}
}

/// Parses the parens following the `[ NOT ] IN` operator.
Expand Down
22 changes: 22 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12368,6 +12368,28 @@ fn parse_create_table_select() {
}
}

#[test]
fn test_period_map_access() {
let supported_dialects = TestedDialects::new(vec![
Box::new(GenericDialect {}),
Box::new(DuckDbDialect {}),
]);
let sqls = [
"SELECT abc[1] FROM t",
"SELECT abc[1].f1 FROM t",
"SELECT abc[1].f1.f2 FROM t",
"SELECT f1.abc[1] FROM t",
"SELECT f1.f2.abc[1] FROM t",
"SELECT f1.abc[1].f2 FROM t",
"SELECT abc['a'][1].f1 FROM t",
"SELECT abc['a'].f1[1].f2 FROM t",
"SELECT abc['a'].f1[1].f2[2] FROM t",
];
for sql in sqls {
supported_dialects.verified_stmt(sql);
}
}

#[test]
fn test_reserved_keywords_for_identifiers() {
let dialects = all_dialects_where(|d| d.is_reserved_for_identifier(Keyword::INTERVAL));
Expand Down

0 comments on commit bb1833f

Please sign in to comment.