Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented

### Added
- Support SQLite's `CREATE TABLE (...) WITHOUT ROWID` (#208) - thanks @mashuai!
- Support SQLite's `CREATE VIRTUAL TABLE` (#209) - thanks @mashuai!

### Fixed

Expand Down
25 changes: 25 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,13 @@ pub enum Statement {
query: Option<Box<Query>>,
without_rowid: bool,
},
/// SQLite's `CREATE VIRTUAL TABLE .. USING <module_name> (<module_args>)`
CreateVirtualTable {
name: ObjectName,
if_not_exists: bool,
module_name: Ident,
module_args: Vec<Ident>,
},
/// CREATE INDEX
CreateIndex {
/// index name
Expand Down Expand Up @@ -695,6 +702,24 @@ impl fmt::Display for Statement {
}
Ok(())
}
Statement::CreateVirtualTable {
name,
if_not_exists,
module_name,
module_args,
} => {
write!(
f,
"CREATE VIRTUAL TABLE {if_not_exists}{name} USING {module_name}",
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
name = name,
module_name = module_name
)?;
if !module_args.is_empty() {
write!(f, " ({})", display_comma_separated(module_args))?;
}
Ok(())
}
Statement::CreateIndex {
name,
table_name,
Expand Down
1 change: 1 addition & 0 deletions src/dialect/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ define_keywords!(
VAR_SAMP,
VERSIONING,
VIEW,
VIRTUAL,
WHEN,
WHENEVER,
WHERE,
Expand Down
28 changes: 24 additions & 4 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub enum IsLateral {
Lateral,
NotLateral,
}
use crate::ast::Statement::CreateVirtualTable;
use IsLateral::*;

impl From<TokenizerError> for ParserError {
Expand Down Expand Up @@ -986,16 +987,35 @@ impl Parser {
self.parse_create_view()
} else if self.parse_keyword(Keyword::EXTERNAL) {
self.parse_create_external_table()
} else if self.parse_keyword(Keyword::VIRTUAL) {
self.parse_create_virtual_table()
} else if self.parse_keyword(Keyword::SCHEMA) {
self.parse_create_schema()
} else {
self.expected(
"TABLE, VIEW, INDEX or SCHEMA after CREATE",
self.peek_token(),
)
self.expected("an object type after CREATE", self.peek_token())
}
}

/// SQLite-specific `CREATE VIRTUAL TABLE`
pub fn parse_create_virtual_table(&mut self) -> Result<Statement, ParserError> {
self.expect_keyword(Keyword::TABLE)?;
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
let table_name = self.parse_object_name()?;
self.expect_keyword(Keyword::USING)?;
let module_name = self.parse_identifier()?;
// SQLite docs note that module "arguments syntax is sufficiently
// general that the arguments can be made to appear as column
// definitions in a traditional CREATE TABLE statement", but
// we don't implement that.
let module_args = self.parse_parenthesized_column_list(Optional)?;
Ok(CreateVirtualTable {
name: table_name,
if_not_exists,
module_name,
module_args,
})
}

pub fn parse_create_schema(&mut self) -> Result<Statement, ParserError> {
let schema_name = self.parse_object_name()?;
Ok(Statement::CreateSchema { schema_name })
Expand Down
22 changes: 22 additions & 0 deletions tests/sqlparser_sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,28 @@ fn parse_create_table_without_rowid() {
}
}

#[test]
fn parse_create_virtual_table() {
let sql = "CREATE VIRTUAL TABLE IF NOT EXISTS t USING module_name (arg1, arg2)";
match sqlite_and_generic().verified_stmt(sql) {
Statement::CreateVirtualTable {
name,
if_not_exists: true,
module_name,
module_args,
} => {
let args = vec![Ident::new("arg1"), Ident::new("arg2")];
assert_eq!("t", name.to_string());
assert_eq!("module_name", module_name.to_string());
assert_eq!(args, module_args);
}
_ => unreachable!(),
}

let sql = "CREATE VIRTUAL TABLE t USING module_name";
sqlite_and_generic().verified_stmt(sql);
}

fn sqlite_and_generic() -> TestedDialects {
TestedDialects {
// we don't have a separate SQLite dialect, so test only the generic dialect for now
Expand Down