Skip to content

Commit 5e1a628

Browse files
committed
bq: parse wildcard modifiers except/replace
1 parent 227eb17 commit 5e1a628

2 files changed

Lines changed: 85 additions & 8 deletions

File tree

src/ast/query.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,19 +207,52 @@ pub enum SelectItem {
207207
UnnamedExpr(Expr),
208208
/// An expression, followed by `[ AS ] alias`
209209
ExprWithAlias { expr: Expr, alias: Ident },
210-
/// `alias.*` or even `schema.table.*`
211-
QualifiedWildcard(ObjectName),
212-
/// An unqualified `*`
213-
Wildcard,
210+
/// if obj.is_some(), `alias.*` or even `schema.table.*`
211+
/// else an unqualified `*`
212+
/// except and replace are currently expected in bigquery
213+
/// https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#modifiers_for_operator
214+
Wildcard {
215+
prefix: Option<ObjectName>,
216+
except: Vec<Ident>,
217+
replace: Vec<(Expr, Ident)>,
218+
},
214219
}
215220

216221
impl fmt::Display for SelectItem {
217222
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218223
match &self {
219224
SelectItem::UnnamedExpr(expr) => write!(f, "{}", expr),
220225
SelectItem::ExprWithAlias { expr, alias } => write!(f, "{} AS {}", expr, alias),
221-
SelectItem::QualifiedWildcard(prefix) => write!(f, "{}.*", prefix),
222-
SelectItem::Wildcard => write!(f, "*"),
226+
SelectItem::Wildcard {
227+
prefix,
228+
except,
229+
replace,
230+
} => {
231+
if let Some(pre) = prefix {
232+
write!(f, "{}.*", pre)?;
233+
} else {
234+
write!(f, "*")?;
235+
}
236+
let mut delim = "";
237+
if !except.is_empty() {
238+
write!(f, " EXCEPT (")?;
239+
for col in except {
240+
write!(f, "{}{}", delim, col)?;
241+
delim = ", ";
242+
}
243+
write!(f, ")")?;
244+
}
245+
delim = "";
246+
if !replace.is_empty() {
247+
write!(f, " REPLACE (")?;
248+
for &(ref expr, ref alias) in replace.iter() {
249+
write!(f, "{}{} AS {}", delim, expr, alias)?;
250+
delim = ", ";
251+
}
252+
write!(f, ")")?;
253+
}
254+
Ok(())
255+
}
223256
}
224257
}
225258
}

src/parser.rs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2814,9 +2814,19 @@ impl<'a> Parser<'a> {
28142814
pub fn parse_select_item(&mut self) -> Result<SelectItem, ParserError> {
28152815
let expr = self.parse_expr()?;
28162816
if let Expr::Wildcard = expr {
2817-
Ok(SelectItem::Wildcard)
2817+
let (except, replace) = self.parse_wildcard_modifiers()?;
2818+
Ok(SelectItem::Wildcard {
2819+
prefix: None,
2820+
except,
2821+
replace,
2822+
})
28182823
} else if let Expr::QualifiedWildcard(prefix) = expr {
2819-
Ok(SelectItem::QualifiedWildcard(ObjectName(prefix)))
2824+
let (except, replace) = self.parse_wildcard_modifiers()?;
2825+
Ok(SelectItem::Wildcard {
2826+
prefix: Some(ObjectName(prefix)),
2827+
except,
2828+
replace,
2829+
})
28202830
} else {
28212831
// `expr` is a regular SQL expression and can be followed by an alias
28222832
if let Some(alias) = self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)? {
@@ -2827,6 +2837,40 @@ impl<'a> Parser<'a> {
28272837
}
28282838
}
28292839

2840+
/// Parse a comma-delimited list of projections after REPLACE
2841+
/// https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select_replace
2842+
pub fn parse_replace_item(&mut self) -> Result<(Expr, Ident), ParserError> {
2843+
let expr = self.parse_expr()?;
2844+
// `expr` is a regular SQL expression and can be followed by an alias
2845+
if let Some(alias) = self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)? {
2846+
Ok((expr, alias))
2847+
} else {
2848+
parser_err!(self, "REPLACE expression must have alias")
2849+
}
2850+
}
2851+
2852+
pub fn parse_wildcard_modifiers(
2853+
&mut self,
2854+
) -> Result<(Vec<Ident>, Vec<(Expr, Ident)>), ParserError> {
2855+
let except = if self.parse_keyword(Keyword::EXCEPT) {
2856+
self.expect_token(&Token::LParen)?;
2857+
let aliases = self.parse_comma_separated(Parser::parse_identifier)?;
2858+
self.expect_token(&Token::RParen)?;
2859+
aliases
2860+
} else {
2861+
vec![]
2862+
};
2863+
let replace = if self.parse_keyword(Keyword::EXCEPT) {
2864+
self.expect_token(&Token::LParen)?;
2865+
let replace = self.parse_comma_separated(Parser::parse_replace_item)?;
2866+
self.expect_token(&Token::RParen)?;
2867+
replace
2868+
} else {
2869+
vec![]
2870+
};
2871+
Ok((except, replace))
2872+
}
2873+
28302874
/// Parse an expression, optionally followed by ASC or DESC (used in ORDER BY)
28312875
pub fn parse_order_by_expr(&mut self) -> Result<OrderByExpr, ParserError> {
28322876
let expr = self.parse_expr()?;

0 commit comments

Comments
 (0)