From 62d25939e0dbbf251aa43a0197b3a33f6894e4a3 Mon Sep 17 00:00:00 2001 From: ifeanyi Date: Thu, 14 Mar 2024 10:33:58 +0100 Subject: [PATCH] BigQuery: support unquoted hyphen in table/view declaration Adds support for proper object name parsing in bigquery to within a `CREATE TABLE` or `CREATE VIEW` statement. Similar to the parsing logic for when the table/view is later referenced. The lacking part was that bigquery allows unquoted hyphens in the first part of an object name - so that e.g. `CREATE TABLE my-project.mydataset.mytable` wasn't being parsed correctly. --- src/parser/mod.rs | 10 +++++---- tests/sqlparser_bigquery.rs | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a7190563fd..8b2fc0ba9d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -3527,7 +3527,8 @@ impl<'a> Parser<'a> { && self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); // Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet). // ANSI SQL and Postgres support RECURSIVE here, but we don't support it either. - let name = self.parse_object_name(false)?; + let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect); + let name = self.parse_object_name(allow_unquoted_hyphen)?; let columns = self.parse_view_columns()?; let mut options = CreateTableOptions::None; let with_options = self.parse_options(Keyword::WITH)?; @@ -4482,8 +4483,9 @@ impl<'a> Parser<'a> { global: Option, transient: bool, ) -> Result { + let allow_unquoted_hyphen = dialect_of!(self is BigQueryDialect); let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - let table_name = self.parse_object_name(false)?; + let table_name = self.parse_object_name(allow_unquoted_hyphen)?; // Clickhouse has `ON CLUSTER 'cluster'` syntax for DDLs let on_cluster = if self.parse_keywords(&[Keyword::ON, Keyword::CLUSTER]) { @@ -4498,13 +4500,13 @@ impl<'a> Parser<'a> { }; let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) { - self.parse_object_name(false).ok() + self.parse_object_name(allow_unquoted_hyphen).ok() } else { None }; let clone = if self.parse_keyword(Keyword::CLONE) { - self.parse_object_name(false).ok() + self.parse_object_name(allow_unquoted_hyphen).ok() } else { None }; diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 7bc715a0c7..d9081461b1 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -206,6 +206,51 @@ fn parse_create_view_if_not_exists() { } } +#[test] +fn parse_create_view_with_unquoted_hyphen() { + let sql = "CREATE VIEW IF NOT EXISTS my-pro-ject.mydataset.myview AS SELECT 1"; + match bigquery().verified_stmt(sql) { + Statement::CreateView { + name, + query, + if_not_exists, + .. + } => { + assert_eq!("my-pro-ject.mydataset.myview", name.to_string()); + assert_eq!("SELECT 1", query.to_string()); + assert!(if_not_exists); + } + _ => unreachable!(), + } +} + +#[test] +fn parse_create_table_with_unquoted_hyphen() { + let sql = "CREATE TABLE my-pro-ject.mydataset.mytable (x INT64)"; + match bigquery().verified_stmt(sql) { + Statement::CreateTable { name, columns, .. } => { + assert_eq!( + name, + ObjectName(vec![ + "my-pro-ject".into(), + "mydataset".into(), + "mytable".into() + ]) + ); + assert_eq!( + vec![ColumnDef { + name: Ident::new("x"), + data_type: DataType::Int64, + collation: None, + options: vec![] + },], + columns + ); + } + _ => unreachable!(), + } +} + #[test] fn parse_create_table_with_options() { let sql = concat!(