Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support CREATE TABLE x AS TABLE y #704

Merged
merged 14 commits into from Dec 1, 2022
172 changes: 172 additions & 0 deletions src/parser.rs
Expand Up @@ -4180,6 +4180,23 @@ impl<'a> Parser<'a> {
SetExpr::Query(Box::new(subquery))
} else if self.parse_keyword(Keyword::VALUES) {
SetExpr::Values(self.parse_values()?)
} else if self.parse_keyword(Keyword::TABLE) {
self.next_token();
match &self.tokens[0] {
sarahyurick marked this conversation as resolved.
Show resolved Hide resolved
Token::Word(w) => {
if w.keyword == Keyword::CREATE {
SetExpr::Select(Box::new(self.parse_as_table()?))
} else {
return self.expected(
"CREATE statement",
Token::SingleQuotedString(w.value.clone()),
);
}
}
unexpected => {
return self.expected("CREATE TABLE ... AS TABLE ...", unexpected.clone())
}
}
} else {
return self.expected(
"SELECT, VALUES, or a subquery in the query body",
Expand Down Expand Up @@ -4378,6 +4395,161 @@ impl<'a> Parser<'a> {
})
}

/// Parse `CREATE TABLE x AS TABLE y`
/// the same as `CREATE TABLE x AS SELECT * FROM y`
/// Mostly the same logic as parse_select,
/// except for the "projection" and "from" variables
pub fn parse_as_table(&mut self) -> Result<Select, ParserError> {
sarahyurick marked this conversation as resolved.
Show resolved Hide resolved
let distinct = self.parse_all_or_distinct()?;

let top = if self.parse_keyword(Keyword::TOP) {
Some(self.parse_top()?)
} else {
None
};

let projection = vec![SelectItem::Wildcard];

let into = if self.parse_keyword(Keyword::INTO) {
let temporary = self
.parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY])
.is_some();
let unlogged = self.parse_keyword(Keyword::UNLOGGED);
let table = self.parse_keyword(Keyword::TABLE);
let name = self.parse_object_name()?;
Some(SelectInto {
temporary,
unlogged,
table,
name,
})
} else {
None
};

let mut as_flag = false;
let mut table_flag = false;
let mut table_name = "";
// Grab the table name after `AS TABLE ...`
for token in self.tokens.iter() {
if let Token::Word(w) = token {
if w.keyword == Keyword::AS {
as_flag = true;
} else if w.keyword == Keyword::TABLE && as_flag {
table_flag = true;
} else if as_flag && table_flag {
table_name = &w.value;
as_flag = false;
table_flag = false;
} else if w.keyword != Keyword::TABLE && as_flag {
as_flag = false;
}
}
}

let from = vec![TableWithJoins {
relation: TableFactor::Table {
name: ObjectName(vec![Ident {
value: table_name.to_string(),
quote_style: None,
}]),
alias: None,
args: None,
with_hints: vec![],
},
joins: vec![],
}];

let mut lateral_views = vec![];
loop {
if self.parse_keywords(&[Keyword::LATERAL, Keyword::VIEW]) {
let outer = self.parse_keyword(Keyword::OUTER);
let lateral_view = self.parse_expr()?;
let lateral_view_name = self.parse_object_name()?;
let lateral_col_alias = self
.parse_comma_separated(|parser| {
parser.parse_optional_alias(&[
Keyword::WHERE,
Keyword::GROUP,
Keyword::CLUSTER,
Keyword::HAVING,
Keyword::LATERAL,
])
})?
.into_iter()
.flatten()
.collect();

lateral_views.push(LateralView {
lateral_view,
lateral_view_name,
lateral_col_alias,
outer,
});
} else {
break;
}
}

let selection = if self.parse_keyword(Keyword::WHERE) {
Some(self.parse_expr()?)
} else {
None
};

let group_by = if self.parse_keywords(&[Keyword::GROUP, Keyword::BY]) {
self.parse_comma_separated(Parser::parse_group_by_expr)?
} else {
vec![]
};

let cluster_by = if self.parse_keywords(&[Keyword::CLUSTER, Keyword::BY]) {
self.parse_comma_separated(Parser::parse_expr)?
} else {
vec![]
};

let distribute_by = if self.parse_keywords(&[Keyword::DISTRIBUTE, Keyword::BY]) {
self.parse_comma_separated(Parser::parse_expr)?
} else {
vec![]
};

let sort_by = if self.parse_keywords(&[Keyword::SORT, Keyword::BY]) {
self.parse_comma_separated(Parser::parse_expr)?
} else {
vec![]
};

let having = if self.parse_keyword(Keyword::HAVING) {
Some(self.parse_expr()?)
} else {
None
};

let qualify = if self.parse_keyword(Keyword::QUALIFY) {
Some(self.parse_expr()?)
} else {
None
};

Ok(Select {
distinct,
top,
projection,
into,
from,
lateral_views,
selection,
group_by,
cluster_by,
distribute_by,
sort_by,
having,
qualify,
})
}

pub fn parse_set(&mut self) -> Result<Statement, ParserError> {
let modifier =
self.parse_one_of_keywords(&[Keyword::SESSION, Keyword::LOCAL, Keyword::HIVEVAR]);
Expand Down
11 changes: 11 additions & 0 deletions tests/sqlparser_common.rs
Expand Up @@ -2153,6 +2153,17 @@ fn parse_create_table_as() {
}
}

#[test]
fn parse_create_table_as_table() {
let sql1 = "CREATE TABLE new_table AS TABLE old_table";
sarahyurick marked this conversation as resolved.
Show resolved Hide resolved
let actual_ast = Parser::parse_sql(&GenericDialect {}, sql1).unwrap();

let sql2 = "CREATE TABLE new_table AS SELECT * FROM old_table";
let expected_ast = Parser::parse_sql(&GenericDialect {}, sql2).unwrap();

assert_eq!(actual_ast, expected_ast);
}

#[test]
fn parse_create_table_on_cluster() {
// Using single-quote literal to define current cluster
Expand Down