Skip to content

Commit cda026a

Browse files
committed
Fix AS query clause should be after the create table options
According to MySQL[1] and PostgreSQL[2] create table syntax, the AS query should be sit after the table options. But it is parsed before table options include CHARSET and COLLATE, as well as ON COMMIT clause. This PR resolves this issue by moving the position of AS query, and the issue apache#1274 will be aslo resolved after this PR. MySQL: ``` CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] [table_options] [partition_options] [IGNORE | REPLACE] [AS] query_expression ``` PostgreSQL: ``` CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name [ (column_name [, ...] ) ] [ USING method ] [ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ] [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ] [ TABLESPACE tablespace_name ] AS query [ WITH [ NO ] DATA ] ``` [1] https://dev.mysql.com/doc/refman/8.0/en/create-table.html [2] https://www.postgresql.org/docs/current/sql-createtableas.html
1 parent 9108bff commit cda026a

File tree

4 files changed

+61
-10
lines changed

4 files changed

+61
-10
lines changed

src/ast/dml.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,6 @@ impl Display for CreateTable {
418418
write!(f, " WITH TAG ({})", display_comma_separated(tag.as_slice()))?;
419419
}
420420

421-
if let Some(query) = &self.query {
422-
write!(f, " AS {query}")?;
423-
}
424421
if let Some(default_charset) = &self.default_charset {
425422
write!(f, " DEFAULT CHARSET={default_charset}")?;
426423
}
@@ -440,6 +437,9 @@ impl Display for CreateTable {
440437
if self.strict {
441438
write!(f, " STRICT")?;
442439
}
440+
if let Some(query) = &self.query {
441+
write!(f, " AS {query}")?;
442+
}
443443
Ok(())
444444
}
445445
}

src/parser/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5422,13 +5422,6 @@ impl<'a> Parser<'a> {
54225422
Default::default()
54235423
};
54245424

5425-
// Parse optional `AS ( query )`
5426-
let query = if self.parse_keyword(Keyword::AS) {
5427-
Some(self.parse_boxed_query()?)
5428-
} else {
5429-
None
5430-
};
5431-
54325425
let default_charset = if self.parse_keywords(&[Keyword::DEFAULT, Keyword::CHARSET]) {
54335426
self.expect_token(&Token::Eq)?;
54345427
let next_token = self.next_token();
@@ -5470,6 +5463,13 @@ impl<'a> Parser<'a> {
54705463

54715464
let strict = self.parse_keyword(Keyword::STRICT);
54725465

5466+
// Parse optional `AS ( query )`
5467+
let query = if self.parse_keyword(Keyword::AS) {
5468+
Some(self.parse_boxed_query()?)
5469+
} else {
5470+
None
5471+
};
5472+
54735473
let comment = if self.parse_keyword(Keyword::COMMENT) {
54745474
let _ = self.consume_token(&Token::Eq);
54755475
let next_token = self.next_token();

tests/sqlparser_clickhouse.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,30 @@ fn test_query_with_format_clause() {
802802
}
803803
}
804804

805+
#[test]
806+
fn parse_create_table_on_commit_and_as_query() {
807+
let sql = r#"CREATE LOCAL TEMPORARY TABLE test ON COMMIT PRESERVE ROWS AS SELECT 1"#;
808+
match clickhouse_and_generic().verified_stmt(sql) {
809+
Statement::CreateTable(CreateTable {
810+
name,
811+
on_commit,
812+
query,
813+
..
814+
}) => {
815+
assert_eq!(name.to_string(), "test");
816+
assert_eq!(on_commit, Some(OnCommit::PreserveRows));
817+
assert_eq!(
818+
query.unwrap().body.as_select().unwrap().projection,
819+
vec![UnnamedExpr(Expr::Value(Value::Number(
820+
"1".parse().unwrap(),
821+
false
822+
)))]
823+
);
824+
}
825+
_ => unreachable!(),
826+
}
827+
}
828+
805829
fn clickhouse() -> TestedDialects {
806830
TestedDialects {
807831
dialects: vec![Box::new(ClickHouseDialect {})],

tests/sqlparser_mysql.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,33 @@ fn parse_create_table_collate() {
812812
}
813813
}
814814

815+
#[test]
816+
fn parse_create_table_both_options_and_as_query() {
817+
let sql = "CREATE TABLE foo (id INT(11)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb4_0900_ai_ci AS SELECT 1";
818+
match mysql_and_generic().verified_stmt(sql) {
819+
Statement::CreateTable(CreateTable {
820+
name,
821+
collation,
822+
query,
823+
..
824+
}) => {
825+
assert_eq!(name.to_string(), "foo");
826+
assert_eq!(collation, Some("utf8mb4_0900_ai_ci".to_string()));
827+
assert_eq!(
828+
query.unwrap().body.as_select().unwrap().projection,
829+
vec![SelectItem::UnnamedExpr(Expr::Value(number("1")))]
830+
);
831+
}
832+
_ => unreachable!(),
833+
}
834+
835+
let sql = r"CREATE TABLE foo (id INT(11)) ENGINE=InnoDB AS SELECT 1 DEFAULT CHARSET=utf8mb3";
836+
assert!(matches!(
837+
mysql_and_generic().parse_sql_statements(sql),
838+
Err(ParserError::ParserError(_))
839+
));
840+
}
841+
815842
#[test]
816843
fn parse_create_table_comment_character_set() {
817844
let sql = "CREATE TABLE foo (s TEXT CHARACTER SET utf8mb4 COMMENT 'comment')";

0 commit comments

Comments
 (0)