Skip to content

Commit 2bb8144

Browse files
authored
Add support for MYSQL's CREATE TABLE SELECT expr (#1515)
1 parent 6d907d3 commit 2bb8144

File tree

4 files changed

+47
-1
lines changed

4 files changed

+47
-1
lines changed

src/dialect/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,11 @@ pub trait Dialect: Debug + Any {
633633
fn supports_comment_on(&self) -> bool {
634634
false
635635
}
636+
637+
/// Returns true if the dialect supports the `CREATE TABLE SELECT` statement
638+
fn supports_create_table_select(&self) -> bool {
639+
false
640+
}
636641
}
637642

638643
/// This represents the operators for which precedence must be defined

src/dialect/mysql.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ impl Dialect for MySqlDialect {
9797
fn supports_limit_comma(&self) -> bool {
9898
true
9999
}
100+
101+
/// see <https://dev.mysql.com/doc/refman/8.4/en/create-table-select.html>
102+
fn supports_create_table_select(&self) -> bool {
103+
true
104+
}
100105
}
101106

102107
/// `LOCK TABLES`

src/parser/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5990,6 +5990,11 @@ impl<'a> Parser<'a> {
59905990
// Parse optional `AS ( query )`
59915991
let query = if self.parse_keyword(Keyword::AS) {
59925992
Some(self.parse_query()?)
5993+
} else if self.dialect.supports_create_table_select() && self.parse_keyword(Keyword::SELECT)
5994+
{
5995+
// rewind the SELECT keyword
5996+
self.prev_token();
5997+
Some(self.parse_query()?)
59935998
} else {
59945999
None
59956000
};

tests/sqlparser_common.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6501,7 +6501,17 @@ fn parse_multiple_statements() {
65016501
);
65026502
test_with("DELETE FROM foo", "SELECT", " bar");
65036503
test_with("INSERT INTO foo VALUES (1)", "SELECT", " bar");
6504-
test_with("CREATE TABLE foo (baz INT)", "SELECT", " bar");
6504+
// Since MySQL supports the `CREATE TABLE SELECT` syntax, this needs to be handled separately
6505+
let res = parse_sql_statements("CREATE TABLE foo (baz INT); SELECT bar");
6506+
assert_eq!(
6507+
vec![
6508+
one_statement_parses_to("CREATE TABLE foo (baz INT)", ""),
6509+
one_statement_parses_to("SELECT bar", ""),
6510+
],
6511+
res.unwrap()
6512+
);
6513+
// Check that extra semicolon at the end is stripped by normalization:
6514+
one_statement_parses_to("CREATE TABLE foo (baz INT);", "CREATE TABLE foo (baz INT)");
65056515
// Make sure that empty statements do not cause an error:
65066516
let res = parse_sql_statements(";;");
65076517
assert_eq!(0, res.unwrap().len());
@@ -11717,3 +11727,24 @@ fn parse_comments() {
1171711727
ParserError::ParserError("Expected: comment object_type, found: UNKNOWN".to_string())
1171811728
);
1171911729
}
11730+
11731+
#[test]
11732+
fn parse_create_table_select() {
11733+
let dialects = all_dialects_where(|d| d.supports_create_table_select());
11734+
let sql_1 = r#"CREATE TABLE foo (baz INT) SELECT bar"#;
11735+
let expected = r#"CREATE TABLE foo (baz INT) AS SELECT bar"#;
11736+
let _ = dialects.one_statement_parses_to(sql_1, expected);
11737+
11738+
let sql_2 = r#"CREATE TABLE foo (baz INT, name STRING) SELECT bar, oth_name FROM test.table_a"#;
11739+
let expected =
11740+
r#"CREATE TABLE foo (baz INT, name STRING) AS SELECT bar, oth_name FROM test.table_a"#;
11741+
let _ = dialects.one_statement_parses_to(sql_2, expected);
11742+
11743+
let dialects = all_dialects_where(|d| !d.supports_create_table_select());
11744+
for sql in [sql_1, sql_2] {
11745+
assert_eq!(
11746+
dialects.parse_sql_statements(sql).unwrap_err(),
11747+
ParserError::ParserError("Expected: end of statement, found: SELECT".to_string())
11748+
);
11749+
}
11750+
}

0 commit comments

Comments
 (0)