diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 1f6806e78..c13fa95d6 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4802,7 +4802,14 @@ 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 => { - let ident = self.parse_identifier()?; + // Not calling self.parse_identifier()? 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 { + Token::Word(w) => Ok(w.to_ident()), + Token::Number(w, false) => Ok(Ident::new(w)), + _ => self.expected("placeholder", next_token), + }?; let placeholder = tok.to_string() + &ident.value; Ok(Value::Placeholder(placeholder)) } diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 54d6b5542..a959a4a4e 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -1079,6 +1079,20 @@ fn test_snowflake_trim() { ); } +#[test] +fn test_number_placeholder() { + let sql_only_select = "SELECT :1"; + let select = snowflake().verified_only_select(sql_only_select); + assert_eq!( + &Expr::Value(Value::Placeholder(":1".into())), + expr_from_projection(only(&select.projection)) + ); + + snowflake() + .parse_sql_statements("alter role 1 with name = 'foo'") + .expect_err("should have failed"); +} + #[test] fn parse_position_not_function_columns() { snowflake_and_generic()