Skip to content

Commit

Permalink
parser: support for DROP INDEX,TABLE,TRIGGER,VIEW stmt
Browse files Browse the repository at this point in the history
  • Loading branch information
xNaCly committed Nov 19, 2024
1 parent d49541d commit 47c8d3c
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 8 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ dynamic correctness. See below for a list of currently implemented features.
| | `delete-stmt` | | |
| | `delete-stmt-limited` | | |
|| `detach-stmt` | `DETACH DATABASE my_database` | |
| | `drop-index-stmt` | | |
| | `drop-table-stmt` | | |
| | `drop-view-stmt` | | |
|| `drop-index-stmt` | `DROP INDEX my_index;` | |
|| `drop-table-stmt` | `DROP TABLE my_table;` | |
|| `drop-trigger-stmt` | `DROP TRIGGER my_trigger;` | |
|| `drop-view-stmt` | `DROP VIEW my_view;` | |
| | `insert-stmt` | | |
| | `pragma-stmt` | | sqlite specific |
| | `reindex-stmt` | | |
Expand Down
15 changes: 15 additions & 0 deletions example/stmt.sql
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,18 @@ ANALYZE schema_name;
ANALYZE index_or_table_name.index_or_table_name;
ANALYZE schema_name.index_or_table_name;

-- https://www.sqlite.org/lang_dropindex.html
DROP INDEX index_name;
DROP INDEX IF EXISTS schema_name.index_name;

-- https://www.sqlite.org/lang_table.html
DROP TABLE table_name;
DROP TABLE IF EXISTS schema_name.table_name;

-- https://www.sqlite.org/lang_trigger.html
DROP TRIGGER trigger_name;
DROP TRIGGER IF EXISTS schema_name.trigger_name;

-- https://www.sqlite.org/lang_view.html
DROP VIEW view_name;
DROP VIEW IF EXISTS schema_name.view_name;
96 changes: 93 additions & 3 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ mod nodes;
mod tests;
mod tracer;

/// prints a parser function call trace if #[cfg(feature = "trace_parser")]
macro_rules! trace {
($tracer:expr, $fn:literal, $tok:expr) => {
#[cfg(feature = "trace_parser")]
$tracer.call($fn, $tok.map(|t| t.ttype.clone()));
};
}

/// restores trace indent if #[cfg(feature = "trace_parser")]
macro_rules! detrace {
($tracer:expr) => {
#[cfg(feature = "trace_parser")]
Expand Down Expand Up @@ -262,6 +264,7 @@ impl<'a> Parser<'a> {
trace!(self.tracer, "sql_stmt", self.cur());
let r = match self.cur()?.ttype {
// TODO: add new statement starts here
Type::Keyword(Keyword::DROP) => self.drop_stmt(),
Type::Keyword(Keyword::ANALYZE) => self.analyse_stmt(),
Type::Keyword(Keyword::DETACH) => self.detach_stmt(),
Type::Keyword(Keyword::ROLLBACK) => self.rollback_stmt(),
Expand Down Expand Up @@ -337,6 +340,90 @@ impl<'a> Parser<'a> {
// TODO: add new statement function here *_stmt()
// fn $1_stmt(&mut self) -> Option<Box<dyn nodes::Node>> {}

/// https://www.sqlite.org/lang_dropindex.html
/// https://www.sqlite.org/lang_droptable.html
/// https://www.sqlite.org/lang_droptrigger.html
/// https://www.sqlite.org/lang_dropview.html
fn drop_stmt(&mut self) -> Option<Box<dyn nodes::Node>> {
trace!(self.tracer, "drop_stmt ", self.cur());
let mut drop = nodes::Drop {
t: self.cur()?.clone(),
if_exists: false,
// dummy value
ttype: Keyword::NULL,
argument: String::new(),
};
self.advance();

match self.cur()?.ttype {
Type::Keyword(Keyword::INDEX) => (),
Type::Keyword(Keyword::TABLE) => (),
Type::Keyword(Keyword::TRIGGER) => (),
Type::Keyword(Keyword::VIEW) => (),
_ => {
let mut err = self.err(
"Unexpected Token",
&format!(
"DROP requires either TRIGGER, TABLE, TRIGGER or VIEW at this point, got {:?}",
self.cur()?.ttype
),
self.cur()?,
Rule::Syntax,
);
err.doc_url = Some("https://www.sqlite.org/lang.html");
self.errors.push(err);
self.advance();
return None;
}
}

// we checked if the keyword is valid above
if let Type::Keyword(keyword) = &self.cur()?.ttype {
drop.ttype = keyword.clone();
}

// skip either INDEX;TABLE;TRIGGER or VIEW
self.advance();

if self.is(Type::Keyword(Keyword::IF)) {
self.advance();
self.consume(Type::Keyword(Keyword::EXISTS));
drop.if_exists = true;
}
if let Type::Ident(schema_name) = self.cur()?.ttype.clone() {
// table/index/view/trigger of a schema_name
drop.argument.push_str(&schema_name);
if self.next_is(Type::Dot) {
// skip Type::Ident from above
self.advance();
// skip Type::Dot
self.advance();
if let Type::Ident(index_trigger_table_view) = self.cur()?.ttype.clone() {
drop.argument.push('.');
drop.argument.push_str(&index_trigger_table_view);
} else {
let mut err = self.err(
"Unexpected Token",
&format!(
"DROP requires Ident(<index_or_trigger_or_table_or_view>) after Dot and Ident(<schema_name>), got {:?}",
self.cur()?.ttype
),
self.cur()?,
Rule::Syntax,
);
err.doc_url = Some("https://www.sqlite.org/lang.html");
self.advance();
self.errors.push(err);
}
}
self.advance();
}

detrace!(self.tracer);
some_box!(drop)
}

/// https://www.sqlite.org/syntax/analyze-stmt.html
fn analyse_stmt(&mut self) -> Option<Box<dyn nodes::Node>> {
trace!(self.tracer, "analyse_stmt", self.cur());
let mut a = nodes::Analyze {
Expand All @@ -350,15 +437,16 @@ impl<'a> Parser<'a> {
match &self.cur()?.ttype {
// ANALYZE schema_name.table_or_index_name
Type::Ident(ident) if self.next_is(Type::Dot) => {
let mut tuple: (String, String) = (String::from(ident), "".into());
let mut schema_name = String::from(ident);
// skip ident
self.advance();
// skip dot
self.advance();

if let Type::Ident(ident) = &self.cur()?.ttype {
tuple.1 = ident.to_string();
a.schema_with_table_or_index_name = Some(tuple);
schema_name.push('.');
schema_name += ident.as_str();
a.schema_with_table_or_index_name = Some(schema_name);
self.advance();
} else {
let mut err = self.err(
Expand All @@ -371,6 +459,7 @@ impl<'a> Parser<'a> {
Rule::Syntax,
);
err.doc_url = Some("https://www.sqlite.org/lang_analyze.html");
self.advance();
self.errors.push(err);
}
}
Expand All @@ -388,6 +477,7 @@ impl<'a> Parser<'a> {
some_box!(a)
}

/// https://www.sqlite.org/syntax/detach-stmt.html
fn detach_stmt(&mut self) -> Option<Box<dyn nodes::Node>> {
trace!(self.tracer, "detach_stmt", self.cur());
let t = self.cur()?.clone();
Expand Down
12 changes: 10 additions & 2 deletions src/parser/nodes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::types::Token;
use crate::types::{Keyword, Token};

/// Generates a Node from the given input:
///
Expand Down Expand Up @@ -72,5 +72,13 @@ node!(
Analyze,
"Analyze stmt, see: https://www.sqlite.org/syntax/lang_analyze.html",
schema_index_or_table_name: Option<String>,
schema_with_table_or_index_name: Option<(String,String)>
schema_with_table_or_index_name: Option<String>
);

node!(
Drop,
"Drop stmt, see: https://www.sqlite.org/lang_dropindex.html, https://www.sqlite.org/lang_droptable.html, https://www.sqlite.org/lang_droptrigger.html and https://www.sqlite.org/lang_dropview.html",
if_exists: bool,
ttype: Keyword,
argument: String
);
13 changes: 13 additions & 0 deletions src/parser/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,19 @@ EXPLAIN VACUUM;
analyze_index_or_table_name:r"ANALYZE index_or_table_name;"=vec![Type::Keyword(Keyword::ANALYZE)],
analyze_schema_name_with_subtable:r"ANALYZE schema_name.index_or_table_name;"=vec![Type::Keyword(Keyword::ANALYZE)]
}

test_group_pass_assert! {
drop_stmt,

drop_index_index_name:r"DROP INDEX index_name;"=vec![Type::Keyword(Keyword::DROP)],
drop_index_if_exists_schema_name_index_name:r"DROP INDEX IF EXISTS schema_name.index_name;"=vec![Type::Keyword(Keyword::DROP)],
drop_table_table_name:r"DROP TABLE table_name;"=vec![Type::Keyword(Keyword::DROP)],
drop_table_if_exists_schema_name_table_name:r"DROP TABLE IF EXISTS schema_name.table_name;"=vec![Type::Keyword(Keyword::DROP)],
drop_trigger_trigger_name:r"DROP TRIGGER trigger_name;"=vec![Type::Keyword(Keyword::DROP)],
drop_trigger_if_exists_schema_name_trigger_name:r"DROP TRIGGER IF EXISTS schema_name.trigger_name;"=vec![Type::Keyword(Keyword::DROP)],
drop_view_view_name:r"DROP VIEW view_name;"=vec![Type::Keyword(Keyword::DROP)],
drop_view_if_exists_schema_name_view_name:r"DROP VIEW IF EXISTS schema_name.view_name;"=vec![Type::Keyword(Keyword::DROP)]
}
}

#[cfg(test)]
Expand Down

0 comments on commit 47c8d3c

Please sign in to comment.