Skip to content

Commit

Permalink
Add support for MYSQL's CREATE TABLE SELECT expr (#1515)
Browse files Browse the repository at this point in the history
  • Loading branch information
wugeer authored Nov 13, 2024
1 parent 6d907d3 commit 2bb8144
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,11 @@ pub trait Dialect: Debug + Any {
fn supports_comment_on(&self) -> bool {
false
}

/// Returns true if the dialect supports the `CREATE TABLE SELECT` statement
fn supports_create_table_select(&self) -> bool {
false
}
}

/// This represents the operators for which precedence must be defined
Expand Down
5 changes: 5 additions & 0 deletions src/dialect/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ impl Dialect for MySqlDialect {
fn supports_limit_comma(&self) -> bool {
true
}

/// see <https://dev.mysql.com/doc/refman/8.4/en/create-table-select.html>
fn supports_create_table_select(&self) -> bool {
true
}
}

/// `LOCK TABLES`
Expand Down
5 changes: 5 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5990,6 +5990,11 @@ impl<'a> Parser<'a> {
// Parse optional `AS ( query )`
let query = if self.parse_keyword(Keyword::AS) {
Some(self.parse_query()?)
} else if self.dialect.supports_create_table_select() && self.parse_keyword(Keyword::SELECT)
{
// rewind the SELECT keyword
self.prev_token();
Some(self.parse_query()?)
} else {
None
};
Expand Down
33 changes: 32 additions & 1 deletion tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6501,7 +6501,17 @@ fn parse_multiple_statements() {
);
test_with("DELETE FROM foo", "SELECT", " bar");
test_with("INSERT INTO foo VALUES (1)", "SELECT", " bar");
test_with("CREATE TABLE foo (baz INT)", "SELECT", " bar");
// Since MySQL supports the `CREATE TABLE SELECT` syntax, this needs to be handled separately
let res = parse_sql_statements("CREATE TABLE foo (baz INT); SELECT bar");
assert_eq!(
vec![
one_statement_parses_to("CREATE TABLE foo (baz INT)", ""),
one_statement_parses_to("SELECT bar", ""),
],
res.unwrap()
);
// Check that extra semicolon at the end is stripped by normalization:
one_statement_parses_to("CREATE TABLE foo (baz INT);", "CREATE TABLE foo (baz INT)");
// Make sure that empty statements do not cause an error:
let res = parse_sql_statements(";;");
assert_eq!(0, res.unwrap().len());
Expand Down Expand Up @@ -11717,3 +11727,24 @@ fn parse_comments() {
ParserError::ParserError("Expected: comment object_type, found: UNKNOWN".to_string())
);
}

#[test]
fn parse_create_table_select() {
let dialects = all_dialects_where(|d| d.supports_create_table_select());
let sql_1 = r#"CREATE TABLE foo (baz INT) SELECT bar"#;
let expected = r#"CREATE TABLE foo (baz INT) AS SELECT bar"#;
let _ = dialects.one_statement_parses_to(sql_1, expected);

let sql_2 = r#"CREATE TABLE foo (baz INT, name STRING) SELECT bar, oth_name FROM test.table_a"#;
let expected =
r#"CREATE TABLE foo (baz INT, name STRING) AS SELECT bar, oth_name FROM test.table_a"#;
let _ = dialects.one_statement_parses_to(sql_2, expected);

let dialects = all_dialects_where(|d| !d.supports_create_table_select());
for sql in [sql_1, sql_2] {
assert_eq!(
dialects.parse_sql_statements(sql).unwrap_err(),
ParserError::ParserError("Expected: end of statement, found: SELECT".to_string())
);
}
}

0 comments on commit 2bb8144

Please sign in to comment.