diff --git a/README.md b/README.md index 2a0b2e9..35c4dc4 100644 --- a/README.md +++ b/README.md @@ -40,36 +40,36 @@ dynamic correctness. See below for a list of currently implemented features. ### Supported sql statements -| done | `sqlite`-syntax name | sql example | non-standard sql | -| ---- | --------------------------- | ------------------------------- | ---------------- | -| ✅ | `explain-stmt` | `EXPLAIN QUERY PLAN;` | | -| | `alter-table-stmt` | | | -| ✅ | `analyze-stmt` | `ANALYZE my_table;` | | -| | `attach-stmt` | | | -| ✅ | `begin-stmt` | `BEGIN DEFERRED TRANSACTION;` | | -| ✅ | `commit-stmt` | `END TRANSACTION;` | | -| | `create-index-stmt` | | | -| | `create-table-stmt` | | | -| | `create-trigger-stmt` | | | -| | `create-view-stmt` | | | -| | `create-virtual-table-stmt` | | | -| | `delete-stmt` | | | -| | `delete-stmt-limited` | | | -| ✅ | `detach-stmt` | `DETACH DATABASE my_database` | | -| ✅ | `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` | | | -| | `release-stmt` | | | -| ✅ | `rollback-stmt` | `ROLLBACK TO latest_savepoint;` | | -| | `savepoint-stmt` | | | -| | `select-stmt` | | | -| | `update-stmt` | | | -| | `update-stmt-limited` | | | -| ✅ | `vacuum-stmt` | `VACUUM INTO 'repacked.db'` | | +| done | `sqlite`-syntax name | sql example | non-standard sql | +| ---- | --------------------------- | ------------------------------------ | ---------------- | +| ✅ | `explain-stmt` | `EXPLAIN QUERY PLAN;` | | +| | `alter-table-stmt` | | | +| ✅ | `analyze-stmt` | `ANALYZE my_table;` | | +| | `attach-stmt` | | | +| ✅ | `begin-stmt` | `BEGIN DEFERRED TRANSACTION;` | | +| ✅ | `commit-stmt` | `END TRANSACTION;` | | +| | `create-index-stmt` | | | +| | `create-table-stmt` | | | +| | `create-trigger-stmt` | | | +| | `create-view-stmt` | | | +| | `create-virtual-table-stmt` | | | +| | `delete-stmt` | | | +| | `delete-stmt-limited` | | | +| ✅ | `detach-stmt` | `DETACH DATABASE my_database` | | +| ✅ | `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` | | | +| ✅ | `release-stmt` | `RELEASE SAVEPOINT latest_savepoint` | | +| ✅ | `rollback-stmt` | `ROLLBACK TO latest_savepoint;` | | +| ✅ | `savepoint-stmt` | `SAVEPOINT latest_savepoint` | | +| | `select-stmt` | | | +| | `update-stmt` | | | +| | `update-stmt-limited` | | | +| ✅ | `vacuum-stmt` | `VACUUM INTO 'repacked.db'` | | ## Installation @@ -142,6 +142,7 @@ Options: - no-content: Source file is empty - no-statements: Source file is not empty but holds no statements - unimplemented: Source file contains constructs sqleibniz does not yet understand + - unknown-keyword: Source file contains an unknown keyword - bad-sqleibniz-instruction: Source file contains invalid sqleibniz instruction - unterminated-string: Source file contains an unterminated string - unknown-character: The source file contains an unknown character @@ -151,7 +152,7 @@ Options: - semicolon: The source file is missing a semicolon -h, --help - Print help (see a summary with '-h') + Print help (see a summary with '-h' ``` ### Configuration @@ -162,9 +163,10 @@ Consult [src/rules.rs](./src/rules.rs) for configuration documentation and [leibniz.toml](./leibniz.toml) for said example: ```toml -# this is an example file, consult: https://toml.io/en/ and src/rules.rs for -# documentation +# this is an example file, consult: https://toml.io/en/ for syntax help and +# src/rules.rs::Config for all available options [disabled] + # see sqleibniz --help for all available rules rules = [ # by default, sqleibniz specific errors are disabled: "NoContent", # source file is empty @@ -173,6 +175,7 @@ Consult [src/rules.rs](./src/rules.rs) for configuration documentation and "BadSqleibnizInstruction", # source file contains a bad sqleibniz instruction # ignoring sqlite specific diagnostics: + # "UnknownKeyword", # an unknown keyword was encountered # "UnterminatedString", # a not closed string was found # "UnknownCharacter", # an unknown character was found # "InvalidNumericLiteral", # an invalid numeric literal was found @@ -180,6 +183,7 @@ Consult [src/rules.rs](./src/rules.rs) for configuration documentation and # "Syntax", # a structure with incorrect syntax was found # "Semicolon", # a semicolon is missing ] + ``` ### sqleibniz instructions @@ -248,7 +252,7 @@ multiple lines. ## Contribution -Contributions are always welcome <3 +Contributions are always welcome <3, but remember to test all features you contribute. ### Local Dev env diff --git a/example/stmt.sql b/example/stmt.sql index c54ead3..c8dbdd1 100644 --- a/example/stmt.sql +++ b/example/stmt.sql @@ -66,3 +66,7 @@ DROP VIEW IF EXISTS schema_name.view_name; -- https://www.sqlite.org/lang_savepoint.html SAVEPOINT savepoint_name; + +-- https://www.sqlite.org/syntax/release-stmt.html +RELEASE savepoint_name; +RELEASE SAVEPOINT savepoint_name; diff --git a/leibniz.toml b/leibniz.toml index 84e0384..dcf382c 100644 --- a/leibniz.toml +++ b/leibniz.toml @@ -1,6 +1,7 @@ -# this is an example file, consult: https://toml.io/en/ and src/rules.rs for -# documentation +# this is an example file, consult: https://toml.io/en/ for syntax help and +# src/rules.rs::Config for all available options [disabled] + # see sqleibniz --help for all available rules rules = [ # by default, sqleibniz specific errors are disabled: "NoContent", # source file is empty @@ -9,6 +10,7 @@ "BadSqleibnizInstruction", # source file contains a bad sqleibniz instruction # ignoring sqlite specific diagnostics: + # "UnknownKeyword", # an unknown keyword was encountered # "UnterminatedString", # a not closed string was found # "UnknownCharacter", # an unknown character was found # "InvalidNumericLiteral", # an invalid numeric literal was found diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 0781bfa..762d310 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -264,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::RELEASE) => self.release_stmt(), Type::Keyword(Keyword::SAVEPOINT) => self.savepoint_stmt(), Type::Keyword(Keyword::DROP) => self.drop_stmt(), Type::Keyword(Keyword::ANALYZE) => self.analyse_stmt(), @@ -357,6 +358,41 @@ impl<'a> Parser<'a> { // TODO: add new statement function here *_stmt() // fn $1_stmt(&mut self) -> Option> {} + /// https://www.sqlite.org/syntax/release-stmt.html + fn release_stmt(&mut self) -> Option> { + let mut r = nodes::Release { + t: self.cur()?.clone(), + savepoint_name: String::new(), + }; + self.advance(); + + if self.is(Type::Keyword(Keyword::SAVEPOINT)) { + self.advance(); + } + + if let Type::Ident(savepoint) = &self.cur()?.ttype { + r.savepoint_name = savepoint.clone(); + self.advance(); + } else { + let mut err = self.err( + "Unexpected Token", + &format!( + "Expected Ident(), got {:?}", + self.cur()?.ttype + ), + self.cur()?, + Rule::Syntax, + ); + err.doc_url = Some("https://www.sqlite.org/syntax/release-stmt.html"); + self.errors.push(err); + self.advance(); + return None; + } + + self.expect_end("https://www.sqlite.org/syntax/release-stmt.html"); + some_box!(r) + } + /// https://www.sqlite.org/syntax/savepoint-stmt.html fn savepoint_stmt(&mut self) -> Option> { let mut s = nodes::Savepoint { diff --git a/src/parser/nodes.rs b/src/parser/nodes.rs index 3dc7a1d..a99d36a 100644 --- a/src/parser/nodes.rs +++ b/src/parser/nodes.rs @@ -82,8 +82,15 @@ node!( ttype: Keyword, argument: String ); + node!( Savepoint, "Savepoint stmt, see: https://www.sqlite.org/lang_savepoint.html", savepoint_name: String ); + +node!( + Release, + "Release stmt, see: https://www.sqlite.org/lang_savepoint.html", + savepoint_name: String +); diff --git a/src/parser/tests.rs b/src/parser/tests.rs index 2e75462..d36f49e 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -144,6 +144,13 @@ EXPLAIN VACUUM; savepoint_savepoint_name:r"SAVEPOINT savepoint_name;"=vec![Type::Keyword(Keyword::SAVEPOINT)] } + + test_group_pass_assert! { + release_stmt, + + release_savepoint_savepoint_name:r"RELEASE SAVEPOINT savepoint_name;"=vec![Type::Keyword(Keyword::RELEASE)], + release_savepoint_name:r"RELEASE savepoint_name;"=vec![Type::Keyword(Keyword::RELEASE)] + } } #[cfg(test)] @@ -251,4 +258,14 @@ mod should_fail { savepoint_no_savepoint_name:r"SAVEPOINT;" } + + test_group_fail! { + release_stmt, + + release_savepoint_savepoint_name_no_semicolon:r"RELEASE SAVEPOINT savepoint_name", + release_savepoint_name_no_semicolon:r"RELEASE savepoint_name", + + release_savepoint_no_savepoint_name:r"RELEASE SAVEPOINT;", + release_savepoint_no_name:r"RELEASE;" + } }