Skip to content

Commit d964dd8

Browse files
committed
map access on a column
1 parent 3f7d421 commit d964dd8

File tree

7 files changed

+54
-13
lines changed

7 files changed

+54
-13
lines changed

src/ast/ddl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ impl fmt::Display for ColumnOption {
295295
}
296296
}
297297

298-
fn display_constraint_name<'a>(name: &'a Option<Ident>) -> impl fmt::Display + 'a {
298+
fn display_constraint_name(name: &Option<Ident>) -> impl fmt::Display + '_ {
299299
struct ConstraintName<'a>(&'a Option<Ident>);
300300
impl<'a> fmt::Display for ConstraintName<'a> {
301301
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

src/ast/mod.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,10 @@ pub enum Expr {
192192
right: Box<Expr>,
193193
},
194194
/// Unary operation e.g. `NOT foo`
195-
UnaryOp { op: UnaryOperator, expr: Box<Expr> },
195+
UnaryOp {
196+
op: UnaryOperator,
197+
expr: Box<Expr>,
198+
},
196199
/// CAST an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))`
197200
Cast {
198201
expr: Box<Expr>,
@@ -214,7 +217,14 @@ pub enum Expr {
214217
/// A constant of form `<data_type> 'value'`.
215218
/// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
216219
/// as well as constants of other types (a non-standard PostgreSQL extension).
217-
TypedString { data_type: DataType, value: String },
220+
TypedString {
221+
data_type: DataType,
222+
value: String,
223+
},
224+
MapAccess {
225+
column: Box<Expr>,
226+
key: String,
227+
},
218228
/// Scalar function call e.g. `LEFT(foo, 5)`
219229
Function(Function),
220230
/// `CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END`
@@ -242,6 +252,7 @@ impl fmt::Display for Expr {
242252
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
243253
match self {
244254
Expr::Identifier(s) => write!(f, "{}", s),
255+
Expr::MapAccess { column, key } => write!(f, "{}[\"{}\"]", column, key),
245256
Expr::Wildcard => f.write_str("*"),
246257
Expr::QualifiedWildcard(q) => write!(f, "{}.*", display_separated(q, ".")),
247258
Expr::CompoundIdentifier(s) => write!(f, "{}", display_separated(s, ".")),

src/ast/query.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ impl fmt::Display for Join {
413413
_ => "",
414414
}
415415
}
416-
fn suffix<'a>(constraint: &'a JoinConstraint) -> impl fmt::Display + 'a {
416+
fn suffix(constraint: &JoinConstraint) -> impl fmt::Display + '_ {
417417
struct Suffix<'a>(&'a JoinConstraint);
418418
impl<'a> fmt::Display for Suffix<'a> {
419419
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

src/dialect/ansi.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ pub struct AnsiDialect {}
1717

1818
impl Dialect for AnsiDialect {
1919
fn is_identifier_start(&self, ch: char) -> bool {
20-
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
20+
('a'..='z').contains(&ch) || ('A'..='Z').contains(&ch)
2121
}
2222

2323
fn is_identifier_part(&self, ch: char) -> bool {
24-
(ch >= 'a' && ch <= 'z')
25-
|| (ch >= 'A' && ch <= 'Z')
26-
|| (ch >= '0' && ch <= '9')
24+
('a'..='z').contains(&ch)
25+
|| ('A'..='Z').contains(&ch)
26+
|| ('0'..='9').contains(&ch)
2727
|| ch == '_'
2828
}
2929
}

src/dialect/generic.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@ pub struct GenericDialect;
1717

1818
impl Dialect for GenericDialect {
1919
fn is_identifier_start(&self, ch: char) -> bool {
20-
(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_' || ch == '#' || ch == '@'
20+
('a'..='z').contains(&ch)
21+
|| ('A'..='Z').contains(&ch)
22+
|| ch == '_'
23+
|| ch == '#'
24+
|| ch == '@'
2125
}
2226

2327
fn is_identifier_part(&self, ch: char) -> bool {
24-
(ch >= 'a' && ch <= 'z')
25-
|| (ch >= 'A' && ch <= 'Z')
26-
|| (ch >= '0' && ch <= '9')
28+
('a'..='z').contains(&ch)
29+
|| ('A'..='Z').contains(&ch)
30+
|| ('0'..='9').contains(&ch)
2731
|| ch == '@'
2832
|| ch == '$'
2933
|| ch == '#'

src/parser.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@ pub enum IsOptional {
4848
Optional,
4949
Mandatory,
5050
}
51+
5152
use IsOptional::*;
5253

5354
pub enum IsLateral {
5455
Lateral,
5556
NotLateral,
5657
}
58+
5759
use crate::ast::Statement::CreateVirtualTable;
5860
use IsLateral::*;
5961

@@ -281,6 +283,7 @@ impl<'a> Parser<'a> {
281283
loop {
282284
let next_precedence = self.get_next_precedence()?;
283285
debug!("next precedence: {:?}", next_precedence);
286+
284287
if precedence >= next_precedence {
285288
break;
286289
}
@@ -422,6 +425,7 @@ impl<'a> Parser<'a> {
422425
self.prev_token();
423426
Ok(Expr::Value(self.parse_value()?))
424427
}
428+
425429
Token::LParen => {
426430
let expr =
427431
if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) {
@@ -845,12 +849,27 @@ impl<'a> Parser<'a> {
845849
op: UnaryOperator::PGPostfixFactorial,
846850
expr: Box::new(expr),
847851
})
852+
} else if Token::LBracket == tok {
853+
self.parse_map_access(expr)
848854
} else {
849855
// Can only happen if `get_next_precedence` got out of sync with this function
850856
panic!("No infix parser for token {:?}", tok)
851857
}
852858
}
853859

860+
pub fn parse_map_access(&mut self, expr: Expr) -> Result<Expr, ParserError> {
861+
let key = self.parse_literal_string()?;
862+
let tok = self.consume_token(&Token::RBracket);
863+
debug!("Tok: {}", tok);
864+
match expr {
865+
e @ Expr::Identifier(_) | e @ Expr::CompoundIdentifier(_) => Ok(Expr::MapAccess {
866+
column: Box::new(e),
867+
key,
868+
}),
869+
_ => Ok(expr),
870+
}
871+
}
872+
854873
/// Parses the parens following the `[ NOT ] IN` operator
855874
pub fn parse_in(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParserError> {
856875
self.expect_token(&Token::LParen)?;
@@ -936,6 +955,7 @@ impl<'a> Parser<'a> {
936955
Token::Mult | Token::Div | Token::Mod | Token::StringConcat => Ok(40),
937956
Token::DoubleColon => Ok(50),
938957
Token::ExclamationMark => Ok(50),
958+
Token::LBracket | Token::RBracket => Ok(10),
939959
_ => Ok(0),
940960
}
941961
}
@@ -2460,7 +2480,7 @@ impl<'a> Parser<'a> {
24602480
}
24612481
}
24622482
Keyword::OUTER => {
2463-
return self.expected("LEFT, RIGHT, or FULL", self.peek_token())
2483+
return self.expected("LEFT, RIGHT, or FULL", self.peek_token());
24642484
}
24652485
_ if natural => {
24662486
return self.expected("a join type after NATURAL", self.peek_token());

tests/sqlparser_hive.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,12 @@ fn rename_table() {
192192
hive().verified_stmt(rename);
193193
}
194194

195+
#[test]
196+
fn map_access() {
197+
let rename = "SELECT a.b[\"asdf\"] FROM db.table WHERE a = 2";
198+
hive().verified_stmt(rename);
199+
}
200+
195201
fn hive() -> TestedDialects {
196202
TestedDialects {
197203
dialects: vec![Box::new(HiveDialect {})],

0 commit comments

Comments
 (0)