Skip to content

Commit a53f1d2

Browse files
authored
Support SQLite CREATE VIRTUAL TABLE (apache#209)
`CREATE VIRTUAL TABLE .. USING <module_name> (<module_args>)` https://www.sqlite.org/lang_createvtab.html
1 parent 0c83e5d commit a53f1d2

5 files changed

Lines changed: 73 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Check https://github.com/andygrove/sqlparser-rs/commits/master for undocumented
1212

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

1617
### Fixed
1718

src/ast/mod.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,13 @@ pub enum Statement {
484484
query: Option<Box<Query>>,
485485
without_rowid: bool,
486486
},
487+
/// SQLite's `CREATE VIRTUAL TABLE .. USING <module_name> (<module_args>)`
488+
CreateVirtualTable {
489+
name: ObjectName,
490+
if_not_exists: bool,
491+
module_name: Ident,
492+
module_args: Vec<Ident>,
493+
},
487494
/// CREATE INDEX
488495
CreateIndex {
489496
/// index name
@@ -695,6 +702,24 @@ impl fmt::Display for Statement {
695702
}
696703
Ok(())
697704
}
705+
Statement::CreateVirtualTable {
706+
name,
707+
if_not_exists,
708+
module_name,
709+
module_args,
710+
} => {
711+
write!(
712+
f,
713+
"CREATE VIRTUAL TABLE {if_not_exists}{name} USING {module_name}",
714+
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
715+
name = name,
716+
module_name = module_name
717+
)?;
718+
if !module_args.is_empty() {
719+
write!(f, " ({})", display_comma_separated(module_args))?;
720+
}
721+
Ok(())
722+
}
698723
Statement::CreateIndex {
699724
name,
700725
table_name,

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ define_keywords!(
438438
VAR_SAMP,
439439
VERSIONING,
440440
VIEW,
441+
VIRTUAL,
441442
WHEN,
442443
WHENEVER,
443444
WHERE,

src/parser.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub enum IsLateral {
5555
Lateral,
5656
NotLateral,
5757
}
58+
use crate::ast::Statement::CreateVirtualTable;
5859
use IsLateral::*;
5960

6061
impl From<TokenizerError> for ParserError {
@@ -986,16 +987,35 @@ impl Parser {
986987
self.parse_create_view()
987988
} else if self.parse_keyword(Keyword::EXTERNAL) {
988989
self.parse_create_external_table()
990+
} else if self.parse_keyword(Keyword::VIRTUAL) {
991+
self.parse_create_virtual_table()
989992
} else if self.parse_keyword(Keyword::SCHEMA) {
990993
self.parse_create_schema()
991994
} else {
992-
self.expected(
993-
"TABLE, VIEW, INDEX or SCHEMA after CREATE",
994-
self.peek_token(),
995-
)
995+
self.expected("an object type after CREATE", self.peek_token())
996996
}
997997
}
998998

999+
/// SQLite-specific `CREATE VIRTUAL TABLE`
1000+
pub fn parse_create_virtual_table(&mut self) -> Result<Statement, ParserError> {
1001+
self.expect_keyword(Keyword::TABLE)?;
1002+
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
1003+
let table_name = self.parse_object_name()?;
1004+
self.expect_keyword(Keyword::USING)?;
1005+
let module_name = self.parse_identifier()?;
1006+
// SQLite docs note that module "arguments syntax is sufficiently
1007+
// general that the arguments can be made to appear as column
1008+
// definitions in a traditional CREATE TABLE statement", but
1009+
// we don't implement that.
1010+
let module_args = self.parse_parenthesized_column_list(Optional)?;
1011+
Ok(CreateVirtualTable {
1012+
name: table_name,
1013+
if_not_exists,
1014+
module_name,
1015+
module_args,
1016+
})
1017+
}
1018+
9991019
pub fn parse_create_schema(&mut self) -> Result<Statement, ParserError> {
10001020
let schema_name = self.parse_object_name()?;
10011021
Ok(Statement::CreateSchema { schema_name })

tests/sqlparser_sqlite.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,28 @@ fn parse_create_table_without_rowid() {
3333
}
3434
}
3535

36+
#[test]
37+
fn parse_create_virtual_table() {
38+
let sql = "CREATE VIRTUAL TABLE IF NOT EXISTS t USING module_name (arg1, arg2)";
39+
match sqlite_and_generic().verified_stmt(sql) {
40+
Statement::CreateVirtualTable {
41+
name,
42+
if_not_exists: true,
43+
module_name,
44+
module_args,
45+
} => {
46+
let args = vec![Ident::new("arg1"), Ident::new("arg2")];
47+
assert_eq!("t", name.to_string());
48+
assert_eq!("module_name", module_name.to_string());
49+
assert_eq!(args, module_args);
50+
}
51+
_ => unreachable!(),
52+
}
53+
54+
let sql = "CREATE VIRTUAL TABLE t USING module_name";
55+
sqlite_and_generic().verified_stmt(sql);
56+
}
57+
3658
fn sqlite_and_generic() -> TestedDialects {
3759
TestedDialects {
3860
// we don't have a separate SQLite dialect, so test only the generic dialect for now

0 commit comments

Comments
 (0)