This is an automated email from the ASF dual-hosted git repository.

iffyio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 5da702fc Add support for Snowflake AT/BEFORE (#1667)
5da702fc is described below

commit 5da702fc19f9dc73559d9a6f0408729f1121444a
Author: Yoav Cohen <[email protected]>
AuthorDate: Sun Jan 19 12:08:45 2025 +0100

    Add support for Snowflake AT/BEFORE (#1667)
---
 src/ast/query.rs             |  6 ++++++
 src/dialect/bigquery.rs      |  5 +++++
 src/dialect/mod.rs           |  6 ++++++
 src/dialect/mssql.rs         |  5 +++++
 src/dialect/snowflake.rs     |  5 +++++
 src/parser/mod.rs            | 26 ++++++++++++++------------
 tests/sqlparser_snowflake.rs |  7 +++++++
 7 files changed, 48 insertions(+), 12 deletions(-)

diff --git a/src/ast/query.rs b/src/ast/query.rs
index 66c70b46..4053dd23 100644
--- a/src/ast/query.rs
+++ b/src/ast/query.rs
@@ -1873,13 +1873,19 @@ impl fmt::Display for TableAliasColumnDef {
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub enum TableVersion {
+    /// When the table version is defined using `FOR SYSTEM_TIME AS OF`.
+    /// For example: `SELECT * FROM tbl FOR SYSTEM_TIME AS OF 
TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)`
     ForSystemTimeAsOf(Expr),
+    /// When the table version is defined using a function.
+    /// For example: `SELECT * FROM tbl AT(TIMESTAMP => '2020-08-14 09:30:00')`
+    Function(Expr),
 }
 
 impl Display for TableVersion {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             TableVersion::ForSystemTimeAsOf(e) => write!(f, " FOR SYSTEM_TIME 
AS OF {e}")?,
+            TableVersion::Function(func) => write!(f, " {func}")?,
         }
         Ok(())
     }
diff --git a/src/dialect/bigquery.rs b/src/dialect/bigquery.rs
index 66d7d206..e92169a3 100644
--- a/src/dialect/bigquery.rs
+++ b/src/dialect/bigquery.rs
@@ -77,4 +77,9 @@ impl Dialect for BigQueryDialect {
     fn supports_struct_literal(&self) -> bool {
         true
     }
+
+    // See <https://cloud.google.com/bigquery/docs/access-historical-data>
+    fn supports_timestamp_versioning(&self) -> bool {
+        true
+    }
 }
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index c69253b7..119bb3cf 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -834,6 +834,12 @@ pub trait Dialect: Debug + Any {
     fn is_table_factor_alias(&self, explicit: bool, kw: &Keyword, _parser: 
&mut Parser) -> bool {
         explicit || !keywords::RESERVED_FOR_TABLE_ALIAS.contains(kw)
     }
+
+    /// Returns true if this dialect supports querying historical table data
+    /// by specifying which version of the data to query.
+    fn supports_timestamp_versioning(&self) -> bool {
+        false
+    }
 }
 
 /// This represents the operators for which precedence must be defined
diff --git a/src/dialect/mssql.rs b/src/dialect/mssql.rs
index 67a64894..7d8611cb 100644
--- a/src/dialect/mssql.rs
+++ b/src/dialect/mssql.rs
@@ -90,4 +90,9 @@ impl Dialect for MsSqlDialect {
     fn supports_set_stmt_without_operator(&self) -> bool {
         true
     }
+
+    /// See: 
<https://learn.microsoft.com/en-us/sql/relational-databases/tables/querying-data-in-a-system-versioned-temporal-table>
+    fn supports_timestamp_versioning(&self) -> bool {
+        true
+    }
 }
diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs
index f6e9c9eb..78237acd 100644
--- a/src/dialect/snowflake.rs
+++ b/src/dialect/snowflake.rs
@@ -296,6 +296,11 @@ impl Dialect for SnowflakeDialect {
             _ => true,
         }
     }
+
+    /// See: <https://docs.snowflake.com/en/sql-reference/constructs/at-before>
+    fn supports_timestamp_versioning(&self) -> bool {
+        true
+    }
 }
 
 fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> 
Result<Statement, ParserError> {
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index a3adb023..51bbcfab 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -11208,7 +11208,7 @@ impl<'a> Parser<'a> {
             };
 
             // Parse potential version qualifier
-            let version = self.parse_table_version()?;
+            let version = self.maybe_parse_table_version()?;
 
             // Postgres, MSSQL, ClickHouse: table-valued functions:
             let args = if self.consume_token(&Token::LParen) {
@@ -11639,18 +11639,20 @@ impl<'a> Parser<'a> {
         }
     }
 
-    /// Parse a given table version specifier.
-    ///
-    /// For now it only supports timestamp versioning for BigQuery and MSSQL 
dialects.
-    pub fn parse_table_version(&mut self) -> Result<Option<TableVersion>, 
ParserError> {
-        if dialect_of!(self is BigQueryDialect | MsSqlDialect)
-            && self.parse_keywords(&[Keyword::FOR, Keyword::SYSTEM_TIME, 
Keyword::AS, Keyword::OF])
-        {
-            let expr = self.parse_expr()?;
-            Ok(Some(TableVersion::ForSystemTimeAsOf(expr)))
-        } else {
-            Ok(None)
+    /// Parses a the timestamp version specifier (i.e. query historical data)
+    pub fn maybe_parse_table_version(&mut self) -> 
Result<Option<TableVersion>, ParserError> {
+        if self.dialect.supports_timestamp_versioning() {
+            if self.parse_keywords(&[Keyword::FOR, Keyword::SYSTEM_TIME, 
Keyword::AS, Keyword::OF])
+            {
+                let expr = self.parse_expr()?;
+                return Ok(Some(TableVersion::ForSystemTimeAsOf(expr)));
+            } else if self.peek_keyword(Keyword::AT) || 
self.peek_keyword(Keyword::BEFORE) {
+                let func_name = self.parse_object_name(true)?;
+                let func = self.parse_function(func_name)?;
+                return Ok(Some(TableVersion::Function(func)));
+            }
         }
+        Ok(None)
     }
 
     /// Parses MySQL's JSON_TABLE column definition.
diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs
index fe6439b3..0c4bdf14 100644
--- a/tests/sqlparser_snowflake.rs
+++ b/tests/sqlparser_snowflake.rs
@@ -3051,3 +3051,10 @@ fn test_sql_keywords_as_select_item_aliases() {
             .is_err());
     }
 }
+
+#[test]
+fn test_timetravel_at_before() {
+    snowflake().verified_only_select("SELECT * FROM tbl AT(TIMESTAMP => 
'2024-12-15 00:00:00')");
+    snowflake()
+        .verified_only_select("SELECT * FROM tbl BEFORE(TIMESTAMP => 
'2024-12-15 00:00:00')");
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to