Skip to content

Commit 64f752c

Browse files
committed
bq: typeless structs
1 parent dd733bf commit 64f752c

File tree

3 files changed

+42
-0
lines changed

3 files changed

+42
-0
lines changed

src/ast/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,9 @@ pub enum Expr {
283283
Subquery(Box<Query>),
284284
/// The `LISTAGG` function `SELECT LISTAGG(...) WITHIN GROUP (ORDER BY ...)`
285285
ListAgg(ListAgg),
286+
/// bigquery structs https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type
287+
/// `STRUCT( expr1 [AS field_name] [, ... ])`
288+
Struct(Struct),
286289
}
287290

288291
impl fmt::Display for Expr {
@@ -458,6 +461,7 @@ impl fmt::Display for Expr {
458461
Expr::Exists(s) => write!(f, "EXISTS ({})", s),
459462
Expr::Subquery(s) => write!(f, "({})", s),
460463
Expr::ListAgg(listagg) => write!(f, "{}", listagg),
464+
Expr::Struct(strct) => write!(f, "{}", strct),
461465
}
462466
}
463467
}
@@ -1161,6 +1165,28 @@ impl fmt::Display for ListAgg {
11611165
}
11621166
}
11631167

1168+
/// A `STRUCT` invocation `STRUCT( expr1 [AS field_name] [, ... ])`
1169+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1170+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1171+
pub struct Struct {
1172+
pub fields: Vec<(Expr, Option<Ident>)>,
1173+
}
1174+
1175+
impl fmt::Display for Struct {
1176+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1177+
write!(f, "STRUCT(")?;
1178+
let mut delim = "";
1179+
for (expr, maybe_alias) in self.fields.iter() {
1180+
write!(f, "{}{}", delim, expr)?;
1181+
if let Some(alias) = maybe_alias {
1182+
write!(f, " AS {}", alias)?;
1183+
}
1184+
delim = ", ";
1185+
}
1186+
write!(f, ")")
1187+
}
1188+
}
1189+
11641190
/// The `ON OVERFLOW` clause of a LISTAGG invocation
11651191
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11661192
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ define_keywords!(
430430
STDDEV_SAMP,
431431
STDIN,
432432
STORED,
433+
STRUCT,
433434
SUBMULTISET,
434435
SUBSTRING,
435436
SUBSTRING_REGEX,

src/parser.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ impl<'a> Parser<'a> {
264264
Keyword::EXTRACT => self.parse_extract_expr(),
265265
Keyword::INTERVAL => self.parse_literal_interval(),
266266
Keyword::LISTAGG => self.parse_listagg_expr(),
267+
Keyword::STRUCT if dialect_of!(self is BigQueryDialect) => self.parse_struct(),
267268
Keyword::NOT => Ok(Expr::UnaryOp {
268269
op: UnaryOperator::Not,
269270
expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?),
@@ -639,6 +640,20 @@ impl<'a> Parser<'a> {
639640
}))
640641
}
641642

643+
/// bigquery structs https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#struct_type
644+
pub fn parse_struct(&mut self) -> Result<Expr, ParserError> {
645+
self.expect_token(&Token::LParen)?;
646+
let fields = self.parse_comma_separated(Parser::parse_struct_field)?;
647+
self.expect_token(&Token::RParen)?;
648+
Ok(Expr::Struct(Struct { fields }))
649+
}
650+
651+
pub fn parse_struct_field(&mut self) -> Result<(Expr, Option<Ident>), ParserError> {
652+
let expr = self.parse_expr()?;
653+
let maybe_alias = self.parse_optional_alias(keywords::RESERVED_FOR_COLUMN_ALIAS)?;
654+
Ok((expr, maybe_alias))
655+
}
656+
642657
// This function parses date/time fields for both the EXTRACT function-like
643658
// operator and interval qualifiers. EXTRACT supports a wider set of
644659
// date/time fields than interval qualifiers, so this function may need to

0 commit comments

Comments
 (0)