Support basic CREATE VIEW

This commit is contained in:
Nickolay Ponomarev 2019-02-03 04:01:52 +03:00
parent 346d1ff2e4
commit 0c0cbcaff4
4 changed files with 100 additions and 59 deletions

View file

@ -350,6 +350,7 @@ keyword!(
VARCHAR,
VARYING,
VERSIONING,
VIEW,
WHEN,
WHENEVER,
WHERE,
@ -697,6 +698,7 @@ pub const ALL_KEYWORDS: &'static [&'static str] = &[
VARCHAR,
VARYING,
VERSIONING,
VIEW,
WHEN,
WHENEVER,
WHERE,

View file

@ -173,6 +173,12 @@ pub enum SQLStatement {
/// WHERE
selection: Option<ASTNode>,
},
/// CREATE VIEW
SQLCreateView {
/// View name
name: SQLObjectName,
query: SQLSelect,
},
/// CREATE TABLE
SQLCreateTable {
/// Table name
@ -278,6 +284,9 @@ impl ToString for SQLStatement {
}
s
}
SQLStatement::SQLCreateView { name, query } => {
format!("CREATE VIEW {} AS {}", name.to_string(), query.to_string())
}
SQLStatement::SQLCreateTable { name, columns } => format!(
"CREATE TABLE {} ({})",
name.to_string(),

View file

@ -546,65 +546,10 @@ impl Parser {
/// Parse a SQL CREATE statement
pub fn parse_create(&mut self) -> Result<SQLStatement, ParserError> {
if self.parse_keywords(vec!["TABLE"]) {
let table_name = self.parse_object_name()?;
// parse optional column list (schema)
let mut columns = vec![];
if self.consume_token(&Token::LParen) {
loop {
match self.next_token() {
Some(Token::SQLWord(column_name)) => {
let data_type = self.parse_data_type()?;
let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]);
let is_unique = self.parse_keyword("UNIQUE");
let default = if self.parse_keyword("DEFAULT") {
let expr = self.parse_default_expr(0)?;
Some(expr)
} else {
None
};
let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) {
false
} else if self.parse_keyword("NULL") {
true
} else {
true
};
debug!("default: {:?}", default);
columns.push(SQLColumnDef {
name: column_name.as_sql_ident(),
data_type: data_type,
allow_null,
is_primary,
is_unique,
default,
});
match self.next_token() {
Some(Token::Comma) => {}
Some(Token::RParen) => {
break;
}
other => {
return parser_err!(
format!("Expected ',' or ')' after column definition but found {:?}", other)
);
}
}
}
unexpected => {
return parser_err!(format!(
"Expected column name, got {:?}",
unexpected
));
}
}
}
}
Ok(SQLStatement::SQLCreateTable {
name: table_name,
columns,
})
if self.parse_keyword("TABLE") {
self.parse_create_table()
} else if self.parse_keyword("VIEW") {
self.parse_create_view()
} else {
parser_err!(format!(
"Unexpected token after CREATE: {:?}",
@ -613,6 +558,79 @@ impl Parser {
}
}
pub fn parse_create_view(&mut self) -> Result<SQLStatement, ParserError> {
// Many dialects support `OR REPLACE` | `OR ALTER` right after `CREATE`, but we don't (yet).
// ANSI SQL and Postgres support RECURSIVE here, but we don't support it either.
let name = self.parse_object_name()?;
// Parenthesized "output" columns list could be handled here.
// Some dialects allow WITH here, followed by some keywords (e.g. MS SQL)
// or `(k1=v1, k2=v2, ...)` (Postgres)
self.expect_keyword("AS")?;
self.expect_keyword("SELECT")?;
let query = self.parse_select()?;
// Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here.
Ok(SQLStatement::SQLCreateView { name, query })
}
pub fn parse_create_table(&mut self) -> Result<SQLStatement, ParserError> {
let table_name = self.parse_object_name()?;
// parse optional column list (schema)
let mut columns = vec![];
if self.consume_token(&Token::LParen) {
loop {
match self.next_token() {
Some(Token::SQLWord(column_name)) => {
let data_type = self.parse_data_type()?;
let is_primary = self.parse_keywords(vec!["PRIMARY", "KEY"]);
let is_unique = self.parse_keyword("UNIQUE");
let default = if self.parse_keyword("DEFAULT") {
let expr = self.parse_default_expr(0)?;
Some(expr)
} else {
None
};
let allow_null = if self.parse_keywords(vec!["NOT", "NULL"]) {
false
} else if self.parse_keyword("NULL") {
true
} else {
true
};
debug!("default: {:?}", default);
columns.push(SQLColumnDef {
name: column_name.as_sql_ident(),
data_type: data_type,
allow_null,
is_primary,
is_unique,
default,
});
match self.next_token() {
Some(Token::Comma) => {}
Some(Token::RParen) => {
break;
}
other => {
return parser_err!(format!(
"Expected ',' or ')' after column definition but found {:?}",
other
));
}
}
}
unexpected => {
return parser_err!(format!("Expected column name, got {:?}", unexpected));
}
}
}
}
Ok(SQLStatement::SQLCreateTable {
name: table_name,
columns,
})
}
pub fn parse_table_key(&mut self, constraint_name: SQLIdent) -> Result<TableKey, ParserError> {
let is_primary_key = self.parse_keywords(vec!["PRIMARY", "KEY"]);
let is_unique_key = self.parse_keywords(vec!["UNIQUE", "KEY"]);

View file

@ -700,6 +700,18 @@ fn parse_scalar_subqueries() {
};
}
#[test]
fn parse_create_view() {
let sql = "CREATE VIEW myschema.myview AS SELECT foo FROM bar";
match verified_stmt(sql) {
SQLStatement::SQLCreateView { name, query } => {
assert_eq!("myschema.myview", name.to_string());
assert_eq!("SELECT foo FROM bar", query.to_string());
}
_ => assert!(false),
}
}
#[test]
fn parse_invalid_subquery_without_parens() {
let res = parse_sql_statements("SELECT SELECT 1 FROM bar WHERE 1=1 FROM baz");