This is an automated email from the ASF dual-hosted git repository. LucaCappelletti94 pushed a commit to branch fix-table-command-parsing in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
commit 459589ca7ab0117ae38525f390e2ba0e31dfa461 Author: LucaCappelletti94 <[email protected]> AuthorDate: Tue Sep 22 08:50:46 2026 +0200 Fix TABLE command identifier quoting and token consumption --- src/ast/query.rs | 7 +++---- src/parser/mod.rs | 40 ++++++---------------------------------- tests/sqlparser_common.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/ast/query.rs b/src/ast/query.rs index 296e4e8c..98a9c84a 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -295,14 +295,13 @@ impl fmt::Display for SetQuantifier { #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -/// A [`TABLE` command]( https://www.postgresql.org/docs/current/sql-select.html#SQL-TABLE) +/// A [`TABLE` command](https://www.postgresql.org/docs/current/sql-select.html#SQL-TABLE) #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] -/// A (possibly schema-qualified) table reference used in `FROM` clauses. pub struct Table { /// Optional table name (absent for e.g. `TABLE` command without argument). - pub table_name: Option<String>, + pub table_name: Option<Ident>, /// Optional schema/catalog name qualifying the table. - pub schema_name: Option<String>, + pub schema_name: Option<Ident>, } impl fmt::Display for Table { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 15f135ff..f273469e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -15625,44 +15625,16 @@ impl<'a> Parser<'a> { /// Parse `CREATE TABLE x AS TABLE y` pub fn parse_as_table(&mut self) -> Result<Table, ParserError> { - let token1 = self.next_token(); - let token2 = self.next_token(); - let token3 = self.next_token(); - - let table_name; - let schema_name; - if token2 == Token::Period { - match token1.token { - Token::Word(w) => { - schema_name = w.value; - } - _ => { - return self.expected("Schema name", token1); - } - } - match token3.token { - Token::Word(w) => { - table_name = w.value; - } - _ => { - return self.expected("Table name", token3); - } - } + let first_name = self.parse_identifier()?; + if self.consume_token(&Token::Period) { + let second_name = self.parse_identifier()?; Ok(Table { - table_name: Some(table_name), - schema_name: Some(schema_name), + table_name: Some(second_name), + schema_name: Some(first_name), }) } else { - match token1.token { - Token::Word(w) => { - table_name = w.value; - } - _ => { - return self.expected("Table name", token1); - } - } Ok(Table { - table_name: Some(table_name), + table_name: Some(first_name), schema_name: None, }) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index c4aa607d..4343e652 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4848,7 +4848,7 @@ fn parse_create_table_as_table() { let expected_query1 = Box::new(Query { with: None, body: Box::new(SetExpr::Table(Box::new(Table { - table_name: Some("old_table".to_string()), + table_name: Some(Ident::new("old_table")), schema_name: None, }))), order_by: None, @@ -4874,8 +4874,8 @@ fn parse_create_table_as_table() { let expected_query2 = Box::new(Query { with: None, body: Box::new(SetExpr::Table(Box::new(Table { - table_name: Some("old_table".to_string()), - schema_name: Some("schema_name".to_string()), + table_name: Some(Ident::new("old_table")), + schema_name: Some(Ident::new("schema_name")), }))), order_by: None, limit_clause: None, @@ -20080,3 +20080,44 @@ fn parse_unary_minus_never_renders_line_comment() { all_dialects().verified_stmt("SELECT -1"); all_dialects().verified_stmt("SELECT -x"); } + +#[test] +fn parse_table_preserves_quotes_and_trailing_tokens() { + let dialects = TestedDialects::new(vec![ + Box::new(AnsiDialect {}), + Box::new(GenericDialect {}), + Box::new(PostgreSqlDialect {}), + Box::new(DuckDbDialect {}), + Box::new(SnowflakeDialect {}), + ]); + dialects.verified_stmt(r#"CREATE TABLE new_table AS TABLE "old_table""#); + dialects.verified_stmt(r#"CREATE TABLE new_table AS TABLE "schema_name"."old_table""#); + dialects.verified_stmt("CREATE TABLE new_table AS TABLE old_table ORDER BY x"); + dialects.verified_stmt("CREATE TABLE new_table AS TABLE old_table LIMIT 10"); + dialects.verified_stmt("SELECT * FROM (TABLE old_table ORDER BY x)"); + + let backtick_dialects = TestedDialects::new(vec![ + Box::new(AnsiDialect {}), + Box::new(GenericDialect {}), + Box::new(MySqlDialect {}), + ]); + backtick_dialects.verified_stmt("CREATE TABLE new_table AS TABLE `old_table`"); + backtick_dialects.verified_stmt("CREATE TABLE new_table AS TABLE `%mpty`"); + backtick_dialects.verified_stmt("INSERT INTO t TABLE `%mpty`"); + + let err = dialects + .parse_sql_statements("CREATE TABLE new_table AS TABLE %mpty") + .unwrap_err(); + assert_eq!( + ParserError::ParserError("Expected: identifier, found: %".to_string()), + err + ); + + let err = backtick_dialects + .parse_sql_statements("CREATE TABLE new_table AS TABLE `x` ORE") + .unwrap_err(); + assert_eq!( + ParserError::ParserError("Expected: end of statement, found: ORE".to_string()), + err + ); +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
