Support for Snowflake ASOF joins (#1288)

This commit is contained in:
Joey Hain 2024-05-31 02:45:07 -07:00 committed by GitHub
parent 375742d1fa
commit 80c03f5c6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 123 additions and 3 deletions

View file

@ -1562,6 +1562,15 @@ impl fmt::Display for Join {
),
JoinOperator::CrossApply => write!(f, " CROSS APPLY {}", self.relation),
JoinOperator::OuterApply => write!(f, " OUTER APPLY {}", self.relation),
JoinOperator::AsOf {
match_condition,
constraint,
} => write!(
f,
" ASOF JOIN {} MATCH_CONDITION ({match_condition}){}",
self.relation,
suffix(constraint)
),
}
}
}
@ -1587,6 +1596,14 @@ pub enum JoinOperator {
CrossApply,
/// OUTER APPLY (non-standard)
OuterApply,
/// `ASOF` joins are used for joining tables containing time-series data
/// whose timestamp columns do not match exactly.
///
/// See <https://docs.snowflake.com/en/sql-reference/constructs/asof-join>.
AsOf {
match_condition: Expr,
constraint: JoinConstraint,
},
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]

View file

@ -91,6 +91,7 @@ define_keywords!(
AS,
ASC,
ASENSITIVE,
ASOF,
ASSERT,
ASYMMETRIC,
AT,
@ -418,6 +419,7 @@ define_keywords!(
MATCH,
MATCHED,
MATCHES,
MATCH_CONDITION,
MATCH_RECOGNIZE,
MATERIALIZED,
MAX,

View file

@ -3196,6 +3196,16 @@ impl<'a> Parser<'a> {
Ok(values)
}
pub fn parse_parenthesized<T, F>(&mut self, mut f: F) -> Result<T, ParserError>
where
F: FnMut(&mut Parser<'a>) -> Result<T, ParserError>,
{
self.expect_token(&Token::LParen)?;
let res = f(self)?;
self.expect_token(&Token::RParen)?;
Ok(res)
}
/// Parse a comma-separated list of 0+ items accepted by `F`
pub fn parse_comma_separated0<T, F>(&mut self, f: F) -> Result<Vec<T>, ParserError>
where
@ -8505,6 +8515,18 @@ impl<'a> Parser<'a> {
relation: self.parse_table_factor()?,
join_operator: JoinOperator::OuterApply,
}
} else if self.parse_keyword(Keyword::ASOF) {
self.expect_keyword(Keyword::JOIN)?;
let relation = self.parse_table_factor()?;
self.expect_keyword(Keyword::MATCH_CONDITION)?;
let match_condition = self.parse_parenthesized(Self::parse_expr)?;
Join {
relation,
join_operator: JoinOperator::AsOf {
match_condition,
constraint: self.parse_join_constraint(false)?,
},
}
} else {
let natural = self.parse_keyword(Keyword::NATURAL);
let peek_keyword = if let Token::Word(w) = self.peek_token().token {
@ -8951,9 +8973,7 @@ impl<'a> Parser<'a> {
};
self.expect_keyword(Keyword::PATTERN)?;
self.expect_token(&Token::LParen)?;
let pattern = self.parse_pattern()?;
self.expect_token(&Token::RParen)?;
let pattern = self.parse_parenthesized(Self::parse_pattern)?;
self.expect_keyword(Keyword::DEFINE)?;

View file

@ -312,6 +312,20 @@ pub fn table(name: impl Into<String>) -> TableFactor {
}
}
pub fn table_with_alias(name: impl Into<String>, alias: impl Into<String>) -> TableFactor {
TableFactor::Table {
name: ObjectName(vec![Ident::new(name)]),
alias: Some(TableAlias {
name: Ident::new(alias),
columns: vec![],
}),
args: None,
with_hints: vec![],
version: None,
partitions: vec![],
}
}
pub fn join(relation: TableFactor) -> Join {
Join {
relation,