diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs index c5e3cbf09..95b358a7e 100644 --- a/src/dialect/mysql.rs +++ b/src/dialect/mysql.rs @@ -82,7 +82,7 @@ fn parse_lock_tables(parser: &mut Parser) -> Result { // tbl_name [[AS] alias] lock_type fn parse_lock_table(parser: &mut Parser) -> Result { - let table = parser.parse_identifier()?; + let table = parser.parse_identifier(false)?; let alias = parser.parse_optional_alias(&[Keyword::READ, Keyword::WRITE, Keyword::LOW_PRIORITY])?; let lock_type = parse_lock_tables_type(parser)?; diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs index a0b192c85..cbd150511 100644 --- a/src/dialect/postgresql.rs +++ b/src/dialect/postgresql.rs @@ -57,11 +57,11 @@ pub fn parse_comment(parser: &mut Parser) -> Result { let (object_type, object_name) = match token.token { Token::Word(w) if w.keyword == Keyword::COLUMN => { - let object_name = parser.parse_object_name()?; + let object_name = parser.parse_object_name(false)?; (CommentObject::Column, object_name) } Token::Word(w) if w.keyword == Keyword::TABLE => { - let object_name = parser.parse_object_name()?; + let object_name = parser.parse_object_name(false)?; (CommentObject::Table, object_name) } _ => parser.expected("comment object_type", token)?, diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs index ca318cad4..8ffaf5944 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -91,7 +91,7 @@ pub fn parse_create_stage( ) -> Result { //[ IF NOT EXISTS ] let if_not_exists = parser.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - let name = parser.parse_object_name()?; + let name = parser.parse_object_name(false)?; let mut directory_table_params = Vec::new(); let mut file_format = Vec::new(); let mut copy_options = Vec::new(); @@ -181,7 +181,7 @@ pub fn parse_snowflake_stage_name(parser: &mut Parser) -> Result { parser.prev_token(); - Ok(parser.parse_object_name()?) + Ok(parser.parse_object_name(false)?) } } } @@ -219,7 +219,7 @@ pub fn parse_copy_into(parser: &mut Parser) -> Result { } _ => { parser.prev_token(); - from_stage = parser.parse_object_name()?; + from_stage = parser.parse_object_name(false)?; stage_params = parse_stage_params(parser)?; // as diff --git a/src/parser/alter.rs b/src/parser/alter.rs index 838b64899..7bf99af66 100644 --- a/src/parser/alter.rs +++ b/src/parser/alter.rs @@ -37,17 +37,17 @@ impl<'a> Parser<'a> { } fn parse_mssql_alter_role(&mut self) -> Result { - let role_name = self.parse_identifier()?; + let role_name = self.parse_identifier(false)?; let operation = if self.parse_keywords(&[Keyword::ADD, Keyword::MEMBER]) { - let member_name = self.parse_identifier()?; + let member_name = self.parse_identifier(false)?; AlterRoleOperation::AddMember { member_name } } else if self.parse_keywords(&[Keyword::DROP, Keyword::MEMBER]) { - let member_name = self.parse_identifier()?; + let member_name = self.parse_identifier(false)?; AlterRoleOperation::DropMember { member_name } } else if self.parse_keywords(&[Keyword::WITH, Keyword::NAME]) { if self.consume_token(&Token::Eq) { - let role_name = self.parse_identifier()?; + let role_name = self.parse_identifier(false)?; AlterRoleOperation::RenameRole { role_name } } else { return self.expected("= after WITH NAME ", self.peek_token()); @@ -63,25 +63,25 @@ impl<'a> Parser<'a> { } fn parse_pg_alter_role(&mut self) -> Result { - let role_name = self.parse_identifier()?; + let role_name = self.parse_identifier(false)?; // [ IN DATABASE _`database_name`_ ] let in_database = if self.parse_keywords(&[Keyword::IN, Keyword::DATABASE]) { - self.parse_object_name().ok() + self.parse_object_name(false).ok() } else { None }; let operation = if self.parse_keyword(Keyword::RENAME) { if self.parse_keyword(Keyword::TO) { - let role_name = self.parse_identifier()?; + let role_name = self.parse_identifier(false)?; AlterRoleOperation::RenameRole { role_name } } else { return self.expected("TO after RENAME", self.peek_token()); } // SET } else if self.parse_keyword(Keyword::SET) { - let config_name = self.parse_object_name()?; + let config_name = self.parse_object_name(false)?; // FROM CURRENT if self.parse_keywords(&[Keyword::FROM, Keyword::CURRENT]) { AlterRoleOperation::Set { @@ -117,7 +117,7 @@ impl<'a> Parser<'a> { in_database, } } else { - let config_name = self.parse_object_name()?; + let config_name = self.parse_object_name(false)?; AlterRoleOperation::Reset { config_name: ResetConfig::ConfigName(config_name), in_database, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index cab57a587..c99d1f6e0 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -569,7 +569,7 @@ impl<'a> Parser<'a> { FlushType::OptimizerCosts } else if self.parse_keywords(&[Keyword::RELAY, Keyword::LOGS]) { if self.parse_keywords(&[Keyword::FOR, Keyword::CHANNEL]) { - channel = Some(self.parse_object_name().unwrap().to_string()); + channel = Some(self.parse_object_name(false).unwrap().to_string()); } FlushType::RelayLogs } else if self.parse_keywords(&[Keyword::SLOW, Keyword::LOGS]) { @@ -593,7 +593,7 @@ impl<'a> Parser<'a> { } Keyword::NoKeyword => { self.prev_token(); - tables = self.parse_comma_separated(Parser::parse_object_name)?; + tables = self.parse_comma_separated(|p| p.parse_object_name(false))?; } _ => {} }, @@ -625,7 +625,7 @@ impl<'a> Parser<'a> { pub fn parse_msck(&mut self) -> Result { let repair = self.parse_keyword(Keyword::REPAIR); self.expect_keyword(Keyword::TABLE)?; - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; let partition_action = self .maybe_parse(|parser| { let pa = match parser.parse_one_of_keywords(&[ @@ -651,7 +651,7 @@ impl<'a> Parser<'a> { pub fn parse_truncate(&mut self) -> Result { let table = self.parse_keyword(Keyword::TABLE); - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; let mut partitions = None; if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; @@ -669,7 +669,7 @@ impl<'a> Parser<'a> { let database = self.parse_keyword(Keyword::DATABASE); let database_file_name = self.parse_expr()?; self.expect_keyword(Keyword::AS)?; - let schema_name = self.parse_identifier()?; + let schema_name = self.parse_identifier(false)?; Ok(Statement::AttachDatabase { database, schema_name, @@ -679,7 +679,7 @@ impl<'a> Parser<'a> { pub fn parse_analyze(&mut self) -> Result { self.expect_keyword(Keyword::TABLE)?; - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; let mut for_columns = false; let mut cache_metadata = false; let mut noscan = false; @@ -705,7 +705,7 @@ impl<'a> Parser<'a> { columns = self .maybe_parse(|parser| { - parser.parse_comma_separated(Parser::parse_identifier) + parser.parse_comma_separated(|p| p.parse_identifier(false)) }) .unwrap_or_default(); for_columns = true @@ -842,13 +842,13 @@ impl<'a> Parser<'a> { } pub fn parse_savepoint(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; Ok(Statement::Savepoint { name }) } pub fn parse_release(&mut self) -> Result { let _ = self.parse_keyword(Keyword::SAVEPOINT); - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; Ok(Statement::ReleaseSavepoint { name }) } @@ -1097,7 +1097,7 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::COLLATE) { Ok(Expr::Collate { expr: Box::new(expr), - collation: self.parse_object_name()?, + collation: self.parse_object_name(false)?, }) } else { Ok(expr) @@ -1137,7 +1137,7 @@ impl<'a> Parser<'a> { let window_spec = self.parse_window_spec()?; Some(WindowType::WindowSpec(window_spec)) } else { - Some(WindowType::NamedWindow(self.parse_identifier()?)) + Some(WindowType::NamedWindow(self.parse_identifier(false)?)) } } else { None @@ -1360,7 +1360,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let expr = self.parse_expr()?; if self.parse_keyword(Keyword::USING) { - let charset = self.parse_object_name()?; + let charset = self.parse_object_name(false)?; self.expect_token(&Token::RParen)?; return Ok(Expr::Convert { expr: Box::new(expr), @@ -1372,7 +1372,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::Comma)?; let data_type = self.parse_data_type()?; let charset = if self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) { - Some(self.parse_object_name()?) + Some(self.parse_object_name(false)?) } else { None }; @@ -2027,7 +2027,7 @@ impl<'a> Parser<'a> { self.peek_token().location }); } - let field_name = self.parse_identifier()?; + let field_name = self.parse_identifier(false)?; Ok(Expr::Named { expr: expr.into(), name: field_name, @@ -2107,7 +2107,7 @@ impl<'a> Parser<'a> { let field_name = if is_anonymous_field { None } else { - Some(self.parse_identifier()?) + Some(self.parse_identifier(false)?) }; let (field_type, trailing_bracket) = self.parse_data_type_helper()?; @@ -2684,6 +2684,23 @@ impl<'a> Parser<'a> { } } + /// Return the first token, possibly whitespace, that has not yet been processed + /// (or None if reached end-of-file). + pub fn peek_token_no_skip(&self) -> TokenWithLocation { + self.peek_nth_token_no_skip(0) + } + + /// Return nth token, possibly whitespace, that has not yet been processed. + pub fn peek_nth_token_no_skip(&self, n: usize) -> TokenWithLocation { + self.tokens + .get(self.index + n) + .cloned() + .unwrap_or(TokenWithLocation { + token: Token::EOF, + location: Location { line: 0, column: 0 }, + }) + } + /// Return the first non-whitespace token that has not yet been processed /// (or None if reached end-of-file) and mark it as processed. OK to call /// repeatedly after reaching EOF. @@ -3013,7 +3030,7 @@ impl<'a> Parser<'a> { pub fn parse_cache_table(&mut self) -> Result { let (mut table_flag, mut options, mut has_as, mut query) = (None, vec![], false, None); if self.parse_keyword(Keyword::TABLE) { - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; if self.peek_token().token != Token::EOF { if let Token::Word(word) = self.peek_token().token { if word.keyword == Keyword::OPTIONS { @@ -3044,9 +3061,9 @@ impl<'a> Parser<'a> { }) } } else { - table_flag = Some(self.parse_object_name()?); + table_flag = Some(self.parse_object_name(false)?); if self.parse_keyword(Keyword::TABLE) { - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; if self.peek_token() != Token::EOF { if let Token::Word(word) = self.peek_token().token { if word.keyword == Keyword::OPTIONS { @@ -3104,7 +3121,7 @@ impl<'a> Parser<'a> { let has_table = self.parse_keyword(Keyword::TABLE); if has_table { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; if self.peek_token().token == Token::EOF { Ok(Statement::UNCache { table_name, @@ -3122,9 +3139,9 @@ impl<'a> Parser<'a> { pub fn parse_create_virtual_table(&mut self) -> Result { self.expect_keyword(Keyword::TABLE)?; let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; self.expect_keyword(Keyword::USING)?; - let module_name = self.parse_identifier()?; + let module_name = self.parse_identifier(false)?; // SQLite docs note that module "arguments syntax is sufficiently // general that the arguments can be made to appear as column // definitions in a traditional CREATE TABLE statement", but @@ -3151,14 +3168,16 @@ impl<'a> Parser<'a> { fn parse_schema_name(&mut self) -> Result { if self.parse_keyword(Keyword::AUTHORIZATION) { - Ok(SchemaName::UnnamedAuthorization(self.parse_identifier()?)) + Ok(SchemaName::UnnamedAuthorization( + self.parse_identifier(false)?, + )) } else { - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; if self.parse_keyword(Keyword::AUTHORIZATION) { Ok(SchemaName::NamedAuthorization( name, - self.parse_identifier()?, + self.parse_identifier(false)?, )) } else { Ok(SchemaName::Simple(name)) @@ -3168,7 +3187,7 @@ impl<'a> Parser<'a> { pub fn parse_create_database(&mut self) -> Result { let ine = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - let db_name = self.parse_object_name()?; + let db_name = self.parse_object_name(false)?; let mut location = None; let mut managed_location = None; loop { @@ -3216,7 +3235,7 @@ impl<'a> Parser<'a> { temporary: bool, ) -> Result { if dialect_of!(self is HiveDialect) { - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; self.expect_keyword(Keyword::AS)?; let class_name = self.parse_function_definition()?; let params = CreateFunctionBody { @@ -3234,7 +3253,7 @@ impl<'a> Parser<'a> { params, }) } else if dialect_of!(self is PostgreSqlDialect) { - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; self.expect_token(&Token::LParen)?; let args = if self.consume_token(&Token::RParen) { self.prev_token(); @@ -3319,7 +3338,7 @@ impl<'a> Parser<'a> { body.as_ = Some(self.parse_function_definition()?); } else if self.parse_keyword(Keyword::LANGUAGE) { ensure_not_set(&body.language, "LANGUAGE")?; - body.language = Some(self.parse_identifier()?); + body.language = Some(self.parse_identifier(false)?); } else if self.parse_keyword(Keyword::IMMUTABLE) { ensure_not_set(&body.behavior, "IMMUTABLE | STABLE | VOLATILE")?; body.behavior = Some(FunctionBehavior::Immutable); @@ -3344,7 +3363,7 @@ impl<'a> Parser<'a> { temporary: bool, ) -> Result { if dialect_of!(self is DuckDbDialect | GenericDialect) { - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; self.expect_token(&Token::LParen)?; let args = if self.consume_token(&Token::RParen) { self.prev_token(); @@ -3374,7 +3393,7 @@ impl<'a> Parser<'a> { } fn parse_macro_arg(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let default_expr = if self.consume_token(&Token::DuckAssignment) || self.consume_token(&Token::RArrow) { @@ -3391,7 +3410,7 @@ impl<'a> Parser<'a> { ) -> Result { self.expect_keyword(Keyword::TABLE)?; let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; let (columns, constraints) = self.parse_columns()?; let hive_distribution = self.parse_hive_distribution()?; @@ -3462,7 +3481,7 @@ 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()?; + let name = self.parse_object_name(false)?; let columns = self.parse_view_columns()?; let mut options = CreateTableOptions::None; let with_options = self.parse_options(Keyword::WITH)?; @@ -3516,7 +3535,7 @@ impl<'a> Parser<'a> { pub fn parse_create_role(&mut self) -> Result { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - let names = self.parse_comma_separated(Parser::parse_object_name)?; + let names = self.parse_comma_separated(|p| p.parse_object_name(false))?; let _ = self.parse_keyword(Keyword::WITH); // [ WITH ] @@ -3579,7 +3598,7 @@ impl<'a> Parser<'a> { if authorization_owner.is_some() { parser_err!("Found multiple AUTHORIZATION", loc) } else { - authorization_owner = Some(self.parse_object_name()?); + authorization_owner = Some(self.parse_object_name(false)?); Ok(()) } } @@ -3674,14 +3693,14 @@ impl<'a> Parser<'a> { if !in_role.is_empty() { parser_err!("Found multiple IN ROLE", loc) } else { - in_role = self.parse_comma_separated(Parser::parse_identifier)?; + in_role = self.parse_comma_separated(|p| p.parse_identifier(false))?; Ok(()) } } else if self.parse_keyword(Keyword::GROUP) { if !in_group.is_empty() { parser_err!("Found multiple IN GROUP", loc) } else { - in_group = self.parse_comma_separated(Parser::parse_identifier)?; + in_group = self.parse_comma_separated(|p| p.parse_identifier(false))?; Ok(()) } } else { @@ -3692,7 +3711,7 @@ impl<'a> Parser<'a> { if !role.is_empty() { parser_err!("Found multiple ROLE", loc) } else { - role = self.parse_comma_separated(Parser::parse_identifier)?; + role = self.parse_comma_separated(|p| p.parse_identifier(false))?; Ok(()) } } @@ -3700,7 +3719,7 @@ impl<'a> Parser<'a> { if !user.is_empty() { parser_err!("Found multiple USER", loc) } else { - user = self.parse_comma_separated(Parser::parse_identifier)?; + user = self.parse_comma_separated(|p| p.parse_identifier(false))?; Ok(()) } } @@ -3708,7 +3727,7 @@ impl<'a> Parser<'a> { if !admin.is_empty() { parser_err!("Found multiple ADMIN", loc) } else { - admin = self.parse_comma_separated(Parser::parse_identifier)?; + admin = self.parse_comma_separated(|p| p.parse_identifier(false))?; Ok(()) } } @@ -3768,7 +3787,7 @@ impl<'a> Parser<'a> { // Many dialects support the non standard `IF EXISTS` clause and allow // specifying multiple objects to delete in a single statement let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); - let names = self.parse_comma_separated(Parser::parse_object_name)?; + let names = self.parse_comma_separated(|p| p.parse_object_name(false))?; let loc = self.peek_token().location; let cascade = self.parse_keyword(Keyword::CASCADE); @@ -3814,7 +3833,7 @@ impl<'a> Parser<'a> { } fn parse_drop_function_desc(&mut self) -> Result { - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; let args = if self.consume_token(&Token::LParen) { if self.consume_token(&Token::RParen) { @@ -3836,7 +3855,7 @@ impl<'a> Parser<'a> { /// CURSOR [ { WITH | WITHOUT } HOLD ] FOR query /// ``` pub fn parse_declare(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let binary = self.parse_keyword(Keyword::BINARY); let sensitive = if self.parse_keyword(Keyword::INSENSITIVE) { @@ -3929,10 +3948,10 @@ impl<'a> Parser<'a> { self.expect_one_of_keywords(&[Keyword::FROM, Keyword::IN])?; - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let into = if self.parse_keyword(Keyword::INTO) { - Some(self.parse_object_name()?) + Some(self.parse_object_name(false)?) } else { None }; @@ -3966,15 +3985,15 @@ impl<'a> Parser<'a> { let concurrently = self.parse_keyword(Keyword::CONCURRENTLY); let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let index_name = if if_not_exists || !self.parse_keyword(Keyword::ON) { - let index_name = self.parse_object_name()?; + let index_name = self.parse_object_name(false)?; self.expect_keyword(Keyword::ON)?; Some(index_name) } else { None }; - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; let using = if self.parse_keyword(Keyword::USING) { - Some(self.parse_identifier()?) + Some(self.parse_identifier(false)?) } else { None }; @@ -3984,7 +4003,7 @@ impl<'a> Parser<'a> { let include = if self.parse_keyword(Keyword::INCLUDE) { self.expect_token(&Token::LParen)?; - let columns = self.parse_comma_separated(Parser::parse_identifier)?; + let columns = self.parse_comma_separated(|p| p.parse_identifier(false))?; self.expect_token(&Token::RParen)?; columns } else { @@ -4021,17 +4040,17 @@ impl<'a> Parser<'a> { pub fn parse_create_extension(&mut self) -> Result { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let (schema, version, cascade) = if self.parse_keyword(Keyword::WITH) { let schema = if self.parse_keyword(Keyword::SCHEMA) { - Some(self.parse_identifier()?) + Some(self.parse_identifier(false)?) } else { None }; let version = if self.parse_keyword(Keyword::VERSION) { - Some(self.parse_identifier()?) + Some(self.parse_identifier(false)?) } else { None }; @@ -4116,7 +4135,7 @@ impl<'a> Parser<'a> { transient: bool, ) -> Result { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; // Clickhouse has `ON CLUSTER 'cluster'` syntax for DDLs let on_cluster = if self.parse_keywords(&[Keyword::ON, Keyword::CLUSTER]) { @@ -4131,13 +4150,13 @@ impl<'a> Parser<'a> { }; let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) { - self.parse_object_name().ok() + self.parse_object_name(false).ok() } else { None }; let clone = if self.parse_keyword(Keyword::CLONE) { - self.parse_object_name().ok() + self.parse_object_name(false).ok() } else { None }; @@ -4179,14 +4198,14 @@ impl<'a> Parser<'a> { let order_by = if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) { if self.consume_token(&Token::LParen) { let columns = if self.peek_token() != Token::RParen { - self.parse_comma_separated(Parser::parse_identifier)? + self.parse_comma_separated(|p| p.parse_identifier(false))? } else { vec![] }; self.expect_token(&Token::RParen)?; Some(columns) } else { - Some(vec![self.parse_identifier()?]) + Some(vec![self.parse_identifier(false)?]) } } else { None @@ -4300,7 +4319,7 @@ impl<'a> Parser<'a> { let mut cluster_by = None; if self.parse_keywords(&[Keyword::CLUSTER, Keyword::BY]) { - cluster_by = Some(self.parse_comma_separated(Parser::parse_identifier)?); + cluster_by = Some(self.parse_comma_separated(|p| p.parse_identifier(false))?); }; let mut options = None; @@ -4367,27 +4386,27 @@ impl<'a> Parser<'a> { } pub fn parse_procedure_param(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let data_type = self.parse_data_type()?; Ok(ProcedureParam { name, data_type }) } pub fn parse_column_def(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let data_type = if self.is_column_type_sqlite_unspecified() { DataType::Unspecified } else { self.parse_data_type()? }; let mut collation = if self.parse_keyword(Keyword::COLLATE) { - Some(self.parse_object_name()?) + Some(self.parse_object_name(false)?) } else { None }; let mut options = vec![]; loop { if self.parse_keyword(Keyword::CONSTRAINT) { - let name = Some(self.parse_identifier()?); + let name = Some(self.parse_identifier(false)?); if let Some(option) = self.parse_optional_column_option()? { options.push(ColumnOptionDef { name, option }); } else { @@ -4401,7 +4420,7 @@ impl<'a> Parser<'a> { } else if dialect_of!(self is MySqlDialect | GenericDialect) && self.parse_keyword(Keyword::COLLATE) { - collation = Some(self.parse_object_name()?); + collation = Some(self.parse_object_name(false)?); } else { break; }; @@ -4439,7 +4458,9 @@ impl<'a> Parser<'a> { pub fn parse_optional_column_option(&mut self) -> Result, ParserError> { if self.parse_keywords(&[Keyword::CHARACTER, Keyword::SET]) { - Ok(Some(ColumnOption::CharacterSet(self.parse_object_name()?))) + Ok(Some(ColumnOption::CharacterSet( + self.parse_object_name(false)?, + ))) } else if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) { Ok(Some(ColumnOption::NotNull)) } else if self.parse_keywords(&[Keyword::COMMENT]) { @@ -4457,7 +4478,7 @@ impl<'a> Parser<'a> { } else if self.parse_keyword(Keyword::UNIQUE) { Ok(Some(ColumnOption::Unique { is_primary: false })) } else if self.parse_keyword(Keyword::REFERENCES) { - let foreign_table = self.parse_object_name()?; + let foreign_table = self.parse_object_name(false)?; // PostgreSQL allows omitting the column list and // uses the primary key column of the foreign table by default let referred_columns = self.parse_parenthesized_column_list(Optional, false)?; @@ -4637,7 +4658,7 @@ impl<'a> Parser<'a> { &mut self, ) -> Result, ParserError> { let name = if self.parse_keyword(Keyword::CONSTRAINT) { - Some(self.parse_identifier()?) + Some(self.parse_identifier(false)?) } else { None }; @@ -4652,7 +4673,7 @@ impl<'a> Parser<'a> { // optional constraint name let name = self - .maybe_parse(|parser| parser.parse_identifier()) + .maybe_parse(|parser| parser.parse_identifier(false)) .or(name); let columns = self.parse_parenthesized_column_list(Mandatory, false)?; @@ -4666,7 +4687,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::KEY)?; let columns = self.parse_parenthesized_column_list(Mandatory, false)?; self.expect_keyword(Keyword::REFERENCES)?; - let foreign_table = self.parse_object_name()?; + let foreign_table = self.parse_object_name(false)?; let referred_columns = self.parse_parenthesized_column_list(Mandatory, false)?; let mut on_delete = None; let mut on_update = None; @@ -4704,7 +4725,7 @@ impl<'a> Parser<'a> { let name = match self.peek_token().token { Token::Word(word) if word.keyword == Keyword::USING => None, - _ => self.maybe_parse(|parser| parser.parse_identifier()), + _ => self.maybe_parse(|parser| parser.parse_identifier(false)), }; let index_type = if self.parse_keyword(Keyword::USING) { @@ -4745,7 +4766,7 @@ impl<'a> Parser<'a> { KeyOrIndexDisplay::None }; - let opt_index_name = self.maybe_parse(|parser| parser.parse_identifier()); + let opt_index_name = self.maybe_parse(|parser| parser.parse_identifier(false)); let columns = self.parse_parenthesized_column_list(Mandatory, false)?; @@ -4789,7 +4810,7 @@ impl<'a> Parser<'a> { } pub fn parse_sql_option(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; self.expect_token(&Token::Eq)?; let value = self.parse_expr()?; Ok(SqlOption { name, value }) @@ -4843,18 +4864,18 @@ impl<'a> Parser<'a> { } } else if self.parse_keyword(Keyword::RENAME) { if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::CONSTRAINT) { - let old_name = self.parse_identifier()?; + let old_name = self.parse_identifier(false)?; self.expect_keyword(Keyword::TO)?; - let new_name = self.parse_identifier()?; + let new_name = self.parse_identifier(false)?; AlterTableOperation::RenameConstraint { old_name, new_name } } else if self.parse_keyword(Keyword::TO) { - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; AlterTableOperation::RenameTable { table_name } } else { let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] - let old_column_name = self.parse_identifier()?; + let old_column_name = self.parse_identifier(false)?; self.expect_keyword(Keyword::TO)?; - let new_column_name = self.parse_identifier()?; + let new_column_name = self.parse_identifier(false)?; AlterTableOperation::RenameColumn { old_column_name, new_column_name, @@ -4864,10 +4885,10 @@ impl<'a> Parser<'a> { if self.parse_keywords(&[Keyword::ROW, Keyword::LEVEL, Keyword::SECURITY]) { AlterTableOperation::DisableRowLevelSecurity {} } else if self.parse_keyword(Keyword::RULE) { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; AlterTableOperation::DisableRule { name } } else if self.parse_keyword(Keyword::TRIGGER) { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; AlterTableOperation::DisableTrigger { name } } else { return self.expected( @@ -4877,24 +4898,24 @@ impl<'a> Parser<'a> { } } else if self.parse_keyword(Keyword::ENABLE) { if self.parse_keywords(&[Keyword::ALWAYS, Keyword::RULE]) { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; AlterTableOperation::EnableAlwaysRule { name } } else if self.parse_keywords(&[Keyword::ALWAYS, Keyword::TRIGGER]) { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; AlterTableOperation::EnableAlwaysTrigger { name } } else if self.parse_keywords(&[Keyword::ROW, Keyword::LEVEL, Keyword::SECURITY]) { AlterTableOperation::EnableRowLevelSecurity {} } else if self.parse_keywords(&[Keyword::REPLICA, Keyword::RULE]) { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; AlterTableOperation::EnableReplicaRule { name } } else if self.parse_keywords(&[Keyword::REPLICA, Keyword::TRIGGER]) { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; AlterTableOperation::EnableReplicaTrigger { name } } else if self.parse_keyword(Keyword::RULE) { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; AlterTableOperation::EnableRule { name } } else if self.parse_keyword(Keyword::TRIGGER) { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; AlterTableOperation::EnableTrigger { name } } else { return self.expected( @@ -4921,7 +4942,7 @@ impl<'a> Parser<'a> { } } else if self.parse_keyword(Keyword::CONSTRAINT) { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let cascade = self.parse_keyword(Keyword::CASCADE); AlterTableOperation::DropConstraint { if_exists, @@ -4935,7 +4956,7 @@ impl<'a> Parser<'a> { } else { let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); - let column_name = self.parse_identifier()?; + let column_name = self.parse_identifier(false)?; let cascade = self.parse_keyword(Keyword::CASCADE); AlterTableOperation::DropColumn { column_name, @@ -4958,8 +4979,8 @@ impl<'a> Parser<'a> { } } else if self.parse_keyword(Keyword::CHANGE) { let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] - let old_name = self.parse_identifier()?; - let new_name = self.parse_identifier()?; + let old_name = self.parse_identifier(false)?; + let new_name = self.parse_identifier(false)?; let data_type = self.parse_data_type()?; let mut options = vec![]; while let Some(option) = self.parse_optional_column_option()? { @@ -4974,7 +4995,7 @@ impl<'a> Parser<'a> { } } else if self.parse_keyword(Keyword::ALTER) { let _ = self.parse_keyword(Keyword::COLUMN); // [ COLUMN ] - let column_name = self.parse_identifier()?; + let column_name = self.parse_identifier(false)?; let is_postgresql = dialect_of!(self is PostgreSqlDialect); let op: AlterColumnOperation = if self.parse_keywords(&[ @@ -5036,7 +5057,7 @@ impl<'a> Parser<'a> { AlterTableOperation::AlterColumn { column_name, op } } else if self.parse_keyword(Keyword::SWAP) { self.expect_keyword(Keyword::WITH)?; - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; AlterTableOperation::SwapWith { table_name } } else { return self.expected( @@ -5059,7 +5080,7 @@ impl<'a> Parser<'a> { Keyword::TABLE => { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ] - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; let operations = self.parse_comma_separated(Parser::parse_alter_table_operation)?; Ok(Statement::AlterTable { name: table_name, @@ -5069,10 +5090,10 @@ impl<'a> Parser<'a> { }) } Keyword::INDEX => { - let index_name = self.parse_object_name()?; + let index_name = self.parse_object_name(false)?; let operation = if self.parse_keyword(Keyword::RENAME) { if self.parse_keyword(Keyword::TO) { - let index_name = self.parse_object_name()?; + let index_name = self.parse_object_name(false)?; AlterIndexOperation::RenameIndex { index_name } } else { return self.expected("TO after RENAME", self.peek_token()); @@ -5093,7 +5114,7 @@ impl<'a> Parser<'a> { } pub fn parse_alter_view(&mut self) -> Result { - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; let columns = self.parse_parenthesized_column_list(Optional, false)?; let with_options = self.parse_options(Keyword::WITH)?; @@ -5112,7 +5133,7 @@ impl<'a> Parser<'a> { /// Parse a `CALL procedure_name(arg1, arg2, ...)` /// or `CALL procedure_name` statement pub fn parse_call(&mut self) -> Result { - let object_name = self.parse_object_name()?; + let object_name = self.parse_object_name(false)?; if self.peek_token().token == Token::LParen { match self.parse_function(object_name)? { Expr::Function(f) => Ok(Statement::Call(f)), @@ -5142,7 +5163,7 @@ impl<'a> Parser<'a> { source = CopySource::Query(Box::new(self.parse_query()?)); self.expect_token(&Token::RParen)?; } else { - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; let columns = self.parse_parenthesized_column_list(Optional, false)?; source = CopySource::Table { table_name, @@ -5206,7 +5227,7 @@ impl<'a> Parser<'a> { let cursor = if self.parse_keyword(Keyword::ALL) { CloseCursor::All } else { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; CloseCursor::Specific { name } }; @@ -5228,7 +5249,7 @@ impl<'a> Parser<'a> { Keyword::FORCE_NULL, Keyword::ENCODING, ]) { - Some(Keyword::FORMAT) => CopyOption::Format(self.parse_identifier()?), + Some(Keyword::FORMAT) => CopyOption::Format(self.parse_identifier(false)?), Some(Keyword::FREEZE) => CopyOption::Freeze(!matches!( self.parse_one_of_keywords(&[Keyword::TRUE, Keyword::FALSE]), Some(Keyword::FALSE) @@ -5304,12 +5325,12 @@ impl<'a> Parser<'a> { } Some(Keyword::FORCE) if self.parse_keywords(&[Keyword::NOT, Keyword::NULL]) => { CopyLegacyCsvOption::ForceNotNull( - self.parse_comma_separated(Parser::parse_identifier)?, + self.parse_comma_separated(|p| p.parse_identifier(false))?, ) } Some(Keyword::FORCE) if self.parse_keywords(&[Keyword::QUOTE]) => { CopyLegacyCsvOption::ForceQuote( - self.parse_comma_separated(Parser::parse_identifier)?, + self.parse_comma_separated(|p| p.parse_identifier(false))?, ) } _ => self.expected("csv option", self.peek_token())?, @@ -5420,7 +5441,7 @@ impl<'a> Parser<'a> { Token::HexStringLiteral(ref s) => Ok(Value::HexStringLiteral(s.to_string())), Token::Placeholder(ref s) => Ok(Value::Placeholder(s.to_string())), tok @ Token::Colon | tok @ Token::AtSign => { - // Not calling self.parse_identifier()? because only in placeholder we want to check numbers as idfentifies + // Not calling self.parse_identifier(false)? because only in placeholder we want to check numbers as idfentifies // This because snowflake allows numbers as placeholders let next_token = self.next_token(); let ident = match next_token.token { @@ -5758,7 +5779,7 @@ impl<'a> Parser<'a> { } _ => { self.prev_token(); - let type_name = self.parse_object_name()?; + let type_name = self.parse_object_name(false)?; if let Some(modifiers) = self.parse_optional_type_modifiers()? { Ok(DataType::Custom(type_name, modifiers)) } else { @@ -5799,9 +5820,9 @@ impl<'a> Parser<'a> { /// Strictly parse `identifier AS identifier` pub fn parse_identifier_with_alias(&mut self) -> Result { - let ident = self.parse_identifier()?; + let ident = self.parse_identifier(false)?; self.expect_keyword(Keyword::AS)?; - let alias = self.parse_identifier()?; + let alias = self.parse_identifier(false)?; Ok(IdentWithAlias { ident, alias }) } @@ -5867,10 +5888,14 @@ impl<'a> Parser<'a> { /// Parse a possibly qualified, possibly quoted identifier, e.g. /// `foo` or `myschema."table" - pub fn parse_object_name(&mut self) -> Result { + /// + /// The `in_table_clause` parameter indicates whether the object name is a table in a FROM, JOIN, + /// or similar table clause. Currently, this is used only to support unquoted hyphenated identifiers + /// in this context on BigQuery. + pub fn parse_object_name(&mut self, in_table_clause: bool) -> Result { let mut idents = vec![]; loop { - idents.push(self.parse_identifier()?); + idents.push(self.parse_identifier(in_table_clause)?); if !self.consume_token(&Token::Period) { break; } @@ -6002,10 +6027,65 @@ impl<'a> Parser<'a> { } /// Parse a simple one-word identifier (possibly quoted, possibly a keyword) - pub fn parse_identifier(&mut self) -> Result { + /// + /// The `in_table_clause` parameter indicates whether the identifier is a table in a FROM, JOIN, or + /// similar table clause. Currently, this is used only to support unquoted hyphenated identifiers in + // this context on BigQuery. + pub fn parse_identifier(&mut self, in_table_clause: bool) -> Result { let next_token = self.next_token(); match next_token.token { - Token::Word(w) => Ok(w.to_ident()), + Token::Word(w) => { + let mut ident = w.to_ident(); + + // On BigQuery, hyphens are permitted in unquoted identifiers inside of a FROM or + // TABLE clause [0]. + // + // The first segment must be an ordinary unquoted identifier, e.g. it must not start + // with a digit. Subsequent segments are either must either be valid identifiers or + // integers, e.g. foo-123 is allowed, but foo-123a is not. + // + // [0] https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical + if dialect_of!(self is BigQueryDialect) + && w.quote_style.is_none() + && in_table_clause + { + let mut requires_whitespace = false; + while matches!(self.peek_token_no_skip().token, Token::Minus) { + self.next_token(); + ident.value.push('-'); + + let token = self + .next_token_no_skip() + .cloned() + .unwrap_or(TokenWithLocation::wrap(Token::EOF)); + requires_whitespace = match token.token { + Token::Word(next_word) if next_word.quote_style.is_none() => { + ident.value.push_str(&next_word.value); + false + } + Token::Number(s, false) if s.chars().all(|c| c.is_ascii_digit()) => { + ident.value.push_str(&s); + true + } + _ => { + return self + .expected("continuation of hyphenated identifier", token); + } + } + } + + // If the last segment was a number, we must check that it's followed by whitespace, + // otherwise foo-123a will be parsed as `foo-123` with the alias `a`. + if requires_whitespace { + let token = self.next_token(); + if !matches!(token.token, Token::EOF | Token::Whitespace(_)) { + return self + .expected("whitespace following hyphenated identifier", token); + } + } + } + Ok(ident) + } Token::SingleQuotedString(s) => Ok(Ident::with_quote('\'', s)), Token::DoubleQuotedString(s) => Ok(Ident::with_quote('\"', s)), _ => self.expected("identifier", next_token), @@ -6030,7 +6110,7 @@ impl<'a> Parser<'a> { /// Parses a column definition within a view. fn parse_view_column(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let options = if dialect_of!(self is BigQueryDialect | GenericDialect) && self.parse_keyword(Keyword::OPTIONS) { @@ -6053,7 +6133,7 @@ impl<'a> Parser<'a> { self.next_token(); Ok(vec![]) } else { - let cols = self.parse_comma_separated(Parser::parse_identifier)?; + let cols = self.parse_comma_separated(|p| p.parse_identifier(false))?; self.expect_token(&Token::RParen)?; Ok(cols) } @@ -6175,7 +6255,7 @@ impl<'a> Parser<'a> { pub fn parse_delete(&mut self) -> Result { let tables = if !self.parse_keyword(Keyword::FROM) { - let tables = self.parse_comma_separated(Parser::parse_object_name)?; + let tables = self.parse_comma_separated(|p| p.parse_object_name(false))?; self.expect_keyword(Keyword::FROM)?; tables } else { @@ -6266,7 +6346,7 @@ impl<'a> Parser<'a> { format, }), _ => { - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; Ok(Statement::ExplainTable { describe_alias, table_name, @@ -6496,7 +6576,7 @@ impl<'a> Parser<'a> { /// Parse a CTE (`alias [( col1, col2, ... )] AS (subquery)`) pub fn parse_cte(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let mut cte = if self.parse_keyword(Keyword::AS) { self.expect_token(&Token::LParen)?; @@ -6525,7 +6605,7 @@ impl<'a> Parser<'a> { } }; if self.parse_keyword(Keyword::FROM) { - cte.from = Some(self.parse_identifier()?); + cte.from = Some(self.parse_identifier(false)?); } Ok(cte) } @@ -6647,7 +6727,7 @@ impl<'a> Parser<'a> { .is_some(); let unlogged = self.parse_keyword(Keyword::UNLOGGED); let table = self.parse_keyword(Keyword::TABLE); - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; Some(SelectInto { temporary, unlogged, @@ -6674,7 +6754,7 @@ impl<'a> Parser<'a> { if self.parse_keywords(&[Keyword::LATERAL, Keyword::VIEW]) { let outer = self.parse_keyword(Keyword::OUTER); let lateral_view = self.parse_expr()?; - let lateral_view_name = self.parse_object_name()?; + let lateral_view_name = self.parse_object_name(false)?; let lateral_col_alias = self .parse_comma_separated(|parser| { parser.parse_optional_alias(&[ @@ -6830,7 +6910,7 @@ impl<'a> Parser<'a> { let role_name = if self.parse_keyword(Keyword::NONE) { None } else { - Some(self.parse_identifier()?) + Some(self.parse_identifier(false)?) }; return Ok(Statement::SetRole { context_modifier, @@ -6841,7 +6921,7 @@ impl<'a> Parser<'a> { let variable = if self.parse_keywords(&[Keyword::TIME, Keyword::ZONE]) { ObjectName(vec!["TIMEZONE".into()]) } else { - self.parse_object_name()? + self.parse_object_name(false)? }; if variable.to_string().eq_ignore_ascii_case("NAMES") @@ -6974,7 +7054,7 @@ impl<'a> Parser<'a> { ))), }?; - let obj_name = self.parse_object_name()?; + let obj_name = self.parse_object_name(false)?; Ok(Statement::ShowCreate { obj_type, obj_name }) } @@ -6985,10 +7065,10 @@ impl<'a> Parser<'a> { full: bool, ) -> Result { self.expect_one_of_keywords(&[Keyword::FROM, Keyword::IN])?; - let object_name = self.parse_object_name()?; + let object_name = self.parse_object_name(false)?; let table_name = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) { Some(_) => { - let db_name = vec![self.parse_identifier()?]; + let db_name = vec![self.parse_identifier(false)?]; let ObjectName(table_name) = object_name; let object_name = db_name.into_iter().chain(table_name).collect(); ObjectName(object_name) @@ -7010,7 +7090,7 @@ impl<'a> Parser<'a> { full: bool, ) -> Result { let db_name = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::IN]) { - Some(_) => Some(self.parse_identifier()?), + Some(_) => Some(self.parse_identifier(false)?), None => None, }; let filter = self.parse_show_statement_filter()?; @@ -7051,7 +7131,7 @@ impl<'a> Parser<'a> { } pub fn parse_use(&mut self) -> Result { - let db_name = self.parse_identifier()?; + let db_name = self.parse_identifier(false)?; Ok(Statement::Use { db_name }) } @@ -7177,7 +7257,7 @@ impl<'a> Parser<'a> { if self.consume_token(&Token::LParen) { self.parse_derived_table_factor(Lateral) } else { - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; self.expect_token(&Token::LParen)?; let args = self.parse_optional_args()?; let alias = self.parse_optional_table_alias(keywords::RESERVED_FOR_TABLE_ALIAS)?; @@ -7356,7 +7436,7 @@ impl<'a> Parser<'a> { alias, }) } else { - let name = self.parse_object_name()?; + let name = self.parse_object_name(true)?; let partitions: Vec = if dialect_of!(self is MySqlDialect | GenericDialect) && self.parse_keyword(Keyword::PARTITION) @@ -7428,7 +7508,7 @@ impl<'a> Parser<'a> { /// Parses MySQL's JSON_TABLE column definition. /// For example: `id INT EXISTS PATH '$' DEFAULT '0' ON EMPTY ERROR ON ERROR` pub fn parse_json_table_column_def(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let r#type = self.parse_data_type()?; let exists = self.parse_keyword(Keyword::EXISTS); self.expect_keyword(Keyword::PATH)?; @@ -7497,7 +7577,7 @@ impl<'a> Parser<'a> { }?; let function = self.parse_function(ObjectName(vec![Ident::new(function_name)]))?; self.expect_keyword(Keyword::FOR)?; - let value_column = self.parse_object_name()?.0; + let value_column = self.parse_object_name(false)?.0; self.expect_keyword(Keyword::IN)?; self.expect_token(&Token::LParen)?; let pivot_values = self.parse_comma_separated(Parser::parse_value)?; @@ -7518,9 +7598,9 @@ impl<'a> Parser<'a> { table: TableFactor, ) -> Result { self.expect_token(&Token::LParen)?; - let value = self.parse_identifier()?; + let value = self.parse_identifier(false)?; self.expect_keyword(Keyword::FOR)?; - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; self.expect_keyword(Keyword::IN)?; let columns = self.parse_parenthesized_column_list(Mandatory, false)?; self.expect_token(&Token::RParen)?; @@ -7554,14 +7634,14 @@ impl<'a> Parser<'a> { let (privileges, objects) = self.parse_grant_revoke_privileges_objects()?; self.expect_keyword(Keyword::TO)?; - let grantees = self.parse_comma_separated(Parser::parse_identifier)?; + let grantees = self.parse_comma_separated(|p| p.parse_identifier(false))?; let with_grant_option = self.parse_keywords(&[Keyword::WITH, Keyword::GRANT, Keyword::OPTION]); let granted_by = self .parse_keywords(&[Keyword::GRANTED, Keyword::BY]) - .then(|| self.parse_identifier().unwrap()); + .then(|| self.parse_identifier(false).unwrap()); Ok(Statement::Grant { privileges, @@ -7622,7 +7702,7 @@ impl<'a> Parser<'a> { Keyword::SCHEMA, ]) { GrantObjects::AllTablesInSchema { - schemas: self.parse_comma_separated(Parser::parse_object_name)?, + schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, } } else if self.parse_keywords(&[ Keyword::ALL, @@ -7631,12 +7711,12 @@ impl<'a> Parser<'a> { Keyword::SCHEMA, ]) { GrantObjects::AllSequencesInSchema { - schemas: self.parse_comma_separated(Parser::parse_object_name)?, + schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?, } } else { let object_type = self.parse_one_of_keywords(&[Keyword::SEQUENCE, Keyword::SCHEMA, Keyword::TABLE]); - let objects = self.parse_comma_separated(Parser::parse_object_name); + let objects = self.parse_comma_separated(|p| p.parse_object_name(false)); match object_type { Some(Keyword::SCHEMA) => GrantObjects::Schemas(objects?), Some(Keyword::SEQUENCE) => GrantObjects::Sequences(objects?), @@ -7685,11 +7765,11 @@ impl<'a> Parser<'a> { let (privileges, objects) = self.parse_grant_revoke_privileges_objects()?; self.expect_keyword(Keyword::FROM)?; - let grantees = self.parse_comma_separated(Parser::parse_identifier)?; + let grantees = self.parse_comma_separated(|p| p.parse_identifier(false))?; let granted_by = self .parse_keywords(&[Keyword::GRANTED, Keyword::BY]) - .then(|| self.parse_identifier().unwrap()); + .then(|| self.parse_identifier(false).unwrap()); let loc = self.peek_token().location; let cascade = self.parse_keyword(Keyword::CASCADE); @@ -7782,11 +7862,11 @@ impl<'a> Parser<'a> { } else { // Hive lets you put table here regardless let table = self.parse_keyword(Keyword::TABLE); - let table_name = self.parse_object_name()?; + let table_name = self.parse_object_name(false)?; let table_alias = if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::AS) { - Some(self.parse_identifier()?) + Some(self.parse_identifier(false)?) } else { None }; @@ -7813,7 +7893,7 @@ impl<'a> Parser<'a> { if self.parse_keyword(Keyword::CONFLICT) { let conflict_target = if self.parse_keywords(&[Keyword::ON, Keyword::CONSTRAINT]) { - Some(ConflictTarget::OnConstraint(self.parse_object_name()?)) + Some(ConflictTarget::OnConstraint(self.parse_object_name(false)?)) } else if self.peek_token() == Token::LParen { Some(ConflictTarget::Columns( self.parse_parenthesized_column_list(IsOptional::Mandatory, false)?, @@ -7933,7 +8013,7 @@ impl<'a> Parser<'a> { pub fn parse_function_args(&mut self) -> Result { if self.peek_nth_token(1) == Token::RArrow { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; self.expect_token(&Token::RArrow)?; let arg = self.parse_wildcard_expr()?.into(); @@ -8074,11 +8154,12 @@ impl<'a> Parser<'a> { ) -> Result, ParserError> { let opt_exclude = if self.parse_keyword(Keyword::EXCLUDE) { if self.consume_token(&Token::LParen) { - let columns = self.parse_comma_separated(|parser| parser.parse_identifier())?; + let columns = + self.parse_comma_separated(|parser| parser.parse_identifier(false))?; self.expect_token(&Token::RParen)?; Some(ExcludeSelectItem::Multiple(columns)) } else { - let column = self.parse_identifier()?; + let column = self.parse_identifier(false)?; Some(ExcludeSelectItem::Single(column)) } } else { @@ -8111,7 +8192,7 @@ impl<'a> Parser<'a> { } } else { // Clickhouse allows EXCEPT column_name - let ident = self.parse_identifier()?; + let ident = self.parse_identifier(false)?; Some(ExceptSelectItem { first_element: ident, additional_elements: vec![], @@ -8169,7 +8250,7 @@ impl<'a> Parser<'a> { pub fn parse_replace_elements(&mut self) -> Result { let expr = self.parse_expr()?; let as_keyword = self.parse_keyword(Keyword::AS); - let ident = self.parse_identifier()?; + let ident = self.parse_identifier(false)?; Ok(ReplaceSelectElement { expr, column_name: ident, @@ -8289,7 +8370,7 @@ impl<'a> Parser<'a> { _ => unreachable!(), }; let of = if self.parse_keyword(Keyword::OF) { - Some(self.parse_object_name()?) + Some(self.parse_object_name(false)?) } else { None }; @@ -8426,7 +8507,7 @@ impl<'a> Parser<'a> { pub fn parse_rollback_savepoint(&mut self) -> Result, ParserError> { if self.parse_keyword(Keyword::TO) { let _ = self.parse_keyword(Keyword::SAVEPOINT); - let savepoint = self.parse_identifier()?; + let savepoint = self.parse_identifier(false)?; Ok(Some(savepoint)) } else { @@ -8436,12 +8517,12 @@ impl<'a> Parser<'a> { pub fn parse_deallocate(&mut self) -> Result { let prepare = self.parse_keyword(Keyword::PREPARE); - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; Ok(Statement::Deallocate { name, prepare }) } pub fn parse_execute(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let mut parameters = vec![]; if self.consume_token(&Token::LParen) { @@ -8453,7 +8534,7 @@ impl<'a> Parser<'a> { } pub fn parse_prepare(&mut self) -> Result { - let name = self.parse_identifier()?; + let name = self.parse_identifier(false)?; let mut data_types = vec![]; if self.consume_token(&Token::LParen) { @@ -8583,7 +8664,7 @@ impl<'a> Parser<'a> { // PRAGMA [schema-name '.'] pragma-name [('=' pragma-value) | '(' pragma-value ')'] pub fn parse_pragma(&mut self) -> Result { - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; if self.consume_token(&Token::LParen) { let value = self.parse_pragma_value()?; self.expect_token(&Token::RParen)?; @@ -8616,7 +8697,7 @@ impl<'a> Parser<'a> { //[ IF NOT EXISTS ] let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); //name - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; //[ AS data_type ] let mut data_type: Option = None; if self.parse_keywords(&[Keyword::AS]) { @@ -8628,7 +8709,7 @@ impl<'a> Parser<'a> { if self.parse_keywords(&[Keyword::NONE]) { Some(ObjectName(vec![Ident::new("NONE")])) } else { - Some(self.parse_object_name()?) + Some(self.parse_object_name(false)?) } } else { None @@ -8716,7 +8797,7 @@ impl<'a> Parser<'a> { } pub fn parse_named_window(&mut self) -> Result { - let ident = self.parse_identifier()?; + let ident = self.parse_identifier(false)?; self.expect_keyword(Keyword::AS)?; self.expect_token(&Token::LParen)?; let window_spec = self.parse_window_spec()?; @@ -8724,7 +8805,7 @@ impl<'a> Parser<'a> { } pub fn parse_create_procedure(&mut self, or_alter: bool) -> Result { - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; let params = self.parse_optional_procedure_parameters()?; self.expect_keyword(Keyword::AS)?; self.expect_keyword(Keyword::BEGIN)?; @@ -8764,7 +8845,7 @@ impl<'a> Parser<'a> { } pub fn parse_create_type(&mut self) -> Result { - let name = self.parse_object_name()?; + let name = self.parse_object_name(false)?; self.expect_keyword(Keyword::AS)?; let mut attributes = vec![]; @@ -8776,10 +8857,10 @@ impl<'a> Parser<'a> { } loop { - let attr_name = self.parse_identifier()?; + let attr_name = self.parse_identifier(false)?; let attr_data_type = self.parse_data_type()?; let attr_collation = if self.parse_keyword(Keyword::COLLATE) { - Some(self.parse_object_name()?) + Some(self.parse_object_name(false)?) } else { None }; @@ -8805,7 +8886,7 @@ impl<'a> Parser<'a> { fn parse_partitions(&mut self) -> Result, ParserError> { self.expect_token(&Token::LParen)?; - let partitions = self.parse_comma_separated(Parser::parse_identifier)?; + let partitions = self.parse_comma_separated(|p| p.parse_identifier(false))?; self.expect_token(&Token::RParen)?; Ok(partitions) } diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 90e1293df..a2309a430 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -878,6 +878,47 @@ fn parse_table_identifiers() { Ident::with_quote('`', "da-sh-es"), ], ); + + test_table_ident( + "foo-bar.baz-123", + Some("foo-bar.baz-123"), + vec![Ident::new("foo-bar"), Ident::new("baz-123")], + ); + + test_table_ident_err("foo-`bar`"); + test_table_ident_err("`foo`-bar"); + test_table_ident_err("foo-123a"); + test_table_ident_err("foo - bar"); + test_table_ident_err("123-bar"); + test_table_ident_err("bar-"); +} + +#[test] +fn parse_hyphenated_table_identifiers() { + bigquery().one_statement_parses_to( + "select * from foo-bar f join baz-qux b on f.id = b.id", + "SELECT * FROM foo-bar AS f JOIN baz-qux AS b ON f.id = b.id", + ); + + assert_eq!( + bigquery() + .verified_only_select_with_canonical( + "SELECT foo-bar.x FROM t", + "SELECT foo - bar.x FROM t" + ) + .projection[0], + SelectItem::UnnamedExpr(Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("foo"))), + op: BinaryOperator::Minus, + right: Box::new(Expr::CompoundIdentifier(vec![ + Ident::new("bar"), + Ident::new("x") + ])) + }) + ); + + let error_sql = "select foo-bar.* from foo-bar"; + assert!(bigquery().parse_sql_statements(error_sql).is_err()); } #[test] diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 50ea1d5a7..477e7cf5f 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -507,14 +507,15 @@ fn parse_select_with_table_alias() { #[test] fn parse_invalid_table_name() { - let ast = all_dialects() - .run_parser_method("db.public..customer", |parser| parser.parse_object_name()); + let ast = all_dialects().run_parser_method("db.public..customer", |parser| { + parser.parse_object_name(false) + }); assert!(ast.is_err()); } #[test] fn parse_no_table_name() { - let ast = all_dialects().run_parser_method("", |parser| parser.parse_object_name()); + let ast = all_dialects().run_parser_method("", |parser| parser.parse_object_name(false)); assert!(ast.is_err()); }