Skip to content

Commit d37e42d

Browse files
committed
feat: parse select replace items
1 parent a50487e commit d37e42d

File tree

4 files changed

+57
-48
lines changed

4 files changed

+57
-48
lines changed

src/ast/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ pub use self::operator::{BinaryOperator, UnaryOperator};
3636
pub use self::query::{
3737
Cte, ExceptSelectItem, ExcludeSelectItem, Fetch, IdentWithAlias, Join, JoinConstraint,
3838
JoinOperator, LateralView, LockClause, LockType, NonBlock, Offset, OffsetRows, OrderByExpr,
39-
Query, RenameSelectItem, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr,
40-
SetOperator, SetQuantifier, Table, TableAlias, TableFactor, TableWithJoins, Top, Values,
41-
WildcardAdditionalOptions, With,
39+
Query, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto,
40+
SelectItem, SetExpr, SetOperator, SetQuantifier, Table, TableAlias, TableFactor,
41+
TableWithJoins, Top, Values, WildcardAdditionalOptions, With,
4242
};
4343
pub use self::value::{
4444
escape_quoted_string, DateTimeField, DollarQuotedString, TrimWhereField, Value,

src/ast/query.rs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -540,33 +540,21 @@ impl fmt::Display for ExceptSelectItem {
540540
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
541541
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
542542
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
543-
pub enum ReplaceSelectItem {
544-
/// Single column name with alias without parenthesis.
545-
///
543+
pub struct ReplaceSelectItem {
546544
/// # Syntax
547545
/// ```plaintext
548-
/// <col_name> [AS] <col_alias>
546+
/// (<col_name> [AS] <col_alias>)
549547
/// ```
550-
Single(Box<ReplaceSelectElement>),
551-
/// Multiple column names with aliases inside parenthesis.
552-
/// # Syntax
553548
/// ```plaintext
554549
/// (<col_name> [AS] <col_alias>, <col_name> [AS] <col_alias>, ...)
555550
/// ```
556-
Multiple(Vec<Box<ReplaceSelectElement>>),
551+
pub items: Vec<Box<ReplaceSelectElement>>,
557552
}
558553

559554
impl fmt::Display for ReplaceSelectItem {
560555
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
561556
write!(f, "REPLACE")?;
562-
match self {
563-
Self::Single(column) => {
564-
write!(f, " {column}")?;
565-
}
566-
Self::Multiple(columns) => {
567-
write!(f, " ({})", display_comma_separated(columns))?;
568-
}
569-
}
557+
write!(f, " ({})", display_comma_separated(&self.items))?;
570558
Ok(())
571559
}
572560
}

src/parser.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6254,20 +6254,31 @@ impl<'a> Parser<'a> {
62546254
) -> Result<Option<ReplaceSelectItem>, ParserError> {
62556255
let opt_replace = if self.parse_keyword(Keyword::REPLACE) {
62566256
if self.consume_token(&Token::LParen) {
6257-
let idents =
6258-
self.parse_comma_separated(|parser| Ok(Box::new(parser.parse_select_item()?)))?;
6257+
let items = self.parse_comma_separated(|parser| {
6258+
Ok(Box::new(parser.parse_replace_elements()?))
6259+
})?;
62596260
self.expect_token(&Token::RParen)?;
6260-
Some(ReplaceSelectItem::Multiple(idents))
6261+
Some(ReplaceSelectItem { items })
62616262
} else {
6262-
let ident = self.parse_select_item()?;
6263-
Some(ReplaceSelectItem::Single(Box::new(ident)))
6263+
let tok = self.next_token();
6264+
return self.expected("( after REPLACE but", tok);
62646265
}
62656266
} else {
62666267
None
62676268
};
62686269

62696270
Ok(opt_replace)
62706271
}
6272+
pub fn parse_replace_elements(&mut self) -> Result<ReplaceSelectElement, ParserError> {
6273+
let expr = self.parse_expr()?;
6274+
let as_keyword = self.parse_keyword(Keyword::AS);
6275+
let ident = self.parse_identifier()?;
6276+
Ok(ReplaceSelectElement {
6277+
expr,
6278+
colum_name: ident,
6279+
as_keyword,
6280+
})
6281+
}
62716282

62726283
/// Parse an expression, optionally followed by ASC or DESC (used in ORDER BY)
62736284
pub fn parse_order_by_expr(&mut self) -> Result<OrderByExpr, ParserError> {

tests/sqlparser_bigquery.rs

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -321,37 +321,47 @@ fn test_select_wildcard_with_except() {
321321
#[test]
322322
fn test_select_wildcard_with_replace() {
323323
let select = bigquery_and_generic()
324-
.verified_only_select(r#"SELECT * REPLACE 'widget' AS item_name FROM orders"#);
324+
.verified_only_select(r#"SELECT * REPLACE ('widget' AS item_name) FROM orders"#);
325325
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
326-
opt_replace: Some(ReplaceSelectItem::Single(Box::new(
327-
SelectItem::ExprWithAlias {
326+
opt_replace: Some(ReplaceSelectItem {
327+
items: vec![Box::new(ReplaceSelectElement {
328328
expr: Expr::Value(Value::SingleQuotedString("widget".to_owned())),
329-
alias: Ident::new("item_name"),
330-
},
331-
))),
329+
colum_name: Ident::new("item_name"),
330+
as_keyword: true,
331+
})],
332+
}),
332333
..Default::default()
333334
});
334335
assert_eq!(expected, select.projection[0]);
335336

336-
let select = bigquery_and_generic()
337-
.verified_only_select(r#"SELECT * REPLACE (quantity / 2 AS quantity) FROM orders"#);
337+
let select = bigquery_and_generic().verified_only_select(
338+
r#"SELECT * REPLACE (quantity / 2 AS quantity, 3 AS order_id) FROM orders"#,
339+
);
338340
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
339-
opt_replace: Some(ReplaceSelectItem::Multiple(vec![Box::new(
340-
SelectItem::ExprWithAlias {
341-
expr: Expr::BinaryOp {
342-
left: Box::new(Expr::Identifier(Ident::new("quantity"))),
343-
op: BinaryOperator::Divide,
344-
#[cfg(not(feature = "bigdecimal"))]
345-
right: Box::new(Expr::Value(Value::Number("2".to_string(), false))),
346-
#[cfg(feature = "bigdecimal")]
347-
right: Box::new(Expr::Value(Value::Number(
348-
BigDecimal::from_str("2").unwrap(),
349-
false,
350-
))),
351-
},
352-
alias: Ident::new("quantity"),
353-
},
354-
)])),
341+
opt_replace: Some(ReplaceSelectItem {
342+
items: vec![
343+
Box::new(ReplaceSelectElement {
344+
expr: Expr::BinaryOp {
345+
left: Box::new(Expr::Identifier(Ident::new("quantity"))),
346+
op: BinaryOperator::Divide,
347+
#[cfg(not(feature = "bigdecimal"))]
348+
right: Box::new(Expr::Value(Value::Number("2".to_string(), false))),
349+
#[cfg(feature = "bigdecimal")]
350+
right: Box::new(Expr::Value(Value::Number(
351+
BigDecimal::from_str("2").unwrap(),
352+
false,
353+
))),
354+
},
355+
colum_name: Ident::new("quantity"),
356+
as_keyword: true,
357+
}),
358+
Box::new(ReplaceSelectElement {
359+
expr: Expr::Value(Value::Number("3".to_string(), false)),
360+
colum_name: Ident::new("order_id"),
361+
as_keyword: true,
362+
}),
363+
],
364+
}),
355365
..Default::default()
356366
});
357367
assert_eq!(expected, select.projection[0]);

0 commit comments

Comments
 (0)