Skip to content

Commit

Permalink
sql: add DROP TABLE IF EXISTS support
Browse files Browse the repository at this point in the history
  • Loading branch information
erikgrinaker committed Apr 6, 2024
1 parent b7322de commit fe0ed72
Show file tree
Hide file tree
Showing 15 changed files with 90 additions and 20 deletions.
17 changes: 9 additions & 8 deletions docs/sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Numeric types are not interchangable; a float value (even without a fractional p

Keywords are reserved words with special meaning in SQL statements. They are case-insensitive, and must be quoted with `"` to be used as identifiers. The complete list is:

`AS`, `ASC`, `AND`, `BEGIN`, `BOOL`, `BOOLEAN`, `BY`, `CHAR`, `COMMIT`, `CREATE`, `CROSS`, `DEFAULT`,`DELETE`, `DESC`, `DOUBLE`, `DROP`, `EXPLAIN`, `FALSE`, `FLOAT`, `FROM`, `GROUP`, `HAVING`, `INDEX`, `INFINITY`, `INNER`, `INSERT`, `INT`, `INTEGER`, `INTO`, `IS`, `JOIN`, `KEY`, `LEFT`, `LIKE`, `LIMIT`, `NAN`, `NOT`, `NULL`, `OF`, `OFFSET`, `ON`, `ONLY`, `OR`, `ORDER`, `OUTER`, `PRIMARY`, `READ`, `REFERENCES`, `RIGHT`, `ROLLBACK`, `SELECT`, `SET`, `STRING`, `SYSTEM`, `TABLE`, `TEXT`, `TIME`, `TRANSACTION`, `TRUE`, `UNIQUE`, `UPDATE`, `VALUES`, `VARCHAR`, `WHERE`, `WRITE`
`AS`, `ASC`, `AND`, `BEGIN`, `BOOL`, `BOOLEAN`, `BY`, `CHAR`, `COMMIT`, `CREATE`, `CROSS`, `DEFAULT`,`DELETE`, `DESC`, `DOUBLE`, `DROP`, `EXISTS`, `EXPLAIN`, `FALSE`, `FLOAT`, `FROM`, `GROUP`, `HAVING`, `IF`, `INDEX`, `INFINITY`, `INNER`, `INSERT`, `INT`, `INTEGER`, `INTO`, `IS`, `JOIN`, `KEY`, `LEFT`, `LIKE`, `LIMIT`, `NAN`, `NOT`, `NULL`, `OF`, `OFFSET`, `ON`, `ONLY`, `OR`, `ORDER`, `OUTER`, `PRIMARY`, `READ`, `REFERENCES`, `RIGHT`, `ROLLBACK`, `SELECT`, `SET`, `STRING`, `SYSTEM`, `TABLE`, `TEXT`, `TIME`, `TRANSACTION`, `TRUE`, `UNIQUE`, `UPDATE`, `VALUES`, `VARCHAR`, `WHERE`, `WRITE`

### Identifiers

Expand Down Expand Up @@ -74,19 +74,19 @@ Logical operators apply standard logic operations on boolean operands.
The complete truth tables are:

| `AND` | `TRUE` | `FALSE` | `NULL` |
| ----------- | ------- | ------- | ------- |
|-------------|---------|---------|---------|
| **`TRUE`** | `TRUE` | `FALSE` | `NULL` |
| **`FALSE`** | `FALSE` | `FALSE` | `FALSE` |
| **`NULL`** | `NULL` | `FALSE` | `NULL` |

| `OR` | `TRUE` | `FALSE` | `NULL` |
| ----------- | ------ | ------- | ------ |
|-------------|--------|---------|--------|
| **`TRUE`** | `TRUE` | `TRUE` | `TRUE` |
| **`FALSE`** | `TRUE` | `FALSE` | `NULL` |
| **`NULL`** | `TRUE` | `NULL` | `NULL` |

| `NOT` | |
| ----------- | ------- |
|-------------|---------|
| **`TRUE`** | `FALSE` |
| **`FALSE`** | `TRUE` |
| **`NULL`** | `NULL` |
Expand Down Expand Up @@ -141,7 +141,7 @@ String operators operate on string operands.
The operator precedence (order of operations) is as follows:

| Precedence | Operator | Associativity |
| ---------- | ------------------------ | ------------- |
|------------|--------------------------|---------------|
| 9 | `+`, `-`, `NOT` (prefix) | Right |
| 8 | `!`, `IS` (postfix) | Left |
| 7 | `^` | Right |
Expand Down Expand Up @@ -254,13 +254,14 @@ WHERE release_year < 2000 AND bluray = FALSE

### `DROP TABLE`

Deletes a table and all contained data.
Deletes a table and all contained data. Errors if the table does not
exist, unless `IF EXISTS` is given.

<pre>
DROP TABLE <b><i>table_name</i></b>
DROP TABLE [ IF EXISTS ] <b><i>table_name</i></b>
</pre>

* ***`table_name`***: the table to delete. Errors if it does not exist.
* ***`table_name`***: the table to delete.

### `EXPLAIN`

Expand Down
5 changes: 4 additions & 1 deletion src/bin/toysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,10 @@ Storage: {keys} keys, {logical_size} MB logical, {nodes}x {disk_size} MB disk,
ResultSet::Delete { count } => println!("Deleted {} rows", count),
ResultSet::Update { count } => println!("Updated {} rows", count),
ResultSet::CreateTable { name } => println!("Created table {}", name),
ResultSet::DropTable { name } => println!("Dropped table {}", name),
ResultSet::DropTable { name, existed } => match existed {
true => println!("Dropped table {}", name),
false => println!("Table {} did not exit", name),
},
ResultSet::Explain(plan) => println!("{}", plan),
ResultSet::Query { columns, mut rows } => {
if self.show_headers {
Expand Down
3 changes: 2 additions & 1 deletion src/sql/execution/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl<T: Transaction + 'static> dyn Executor<T> {
}
Node::CreateTable { schema } => CreateTable::new(schema),
Node::Delete { table, source } => Delete::new(table, Self::build(*source)),
Node::DropTable { table } => DropTable::new(table),
Node::DropTable { table, if_exists } => DropTable::new(table, if_exists),
Node::Filter { source, predicate } => Filter::new(Self::build(*source), predicate),
Node::HashJoin { left, left_field, right, right_field, outer } => HashJoin::new(
Self::build(*left),
Expand Down Expand Up @@ -107,6 +107,7 @@ pub enum ResultSet {
// Table dropped
DropTable {
name: String,
existed: bool,
},
// Query result
Query {
Expand Down
10 changes: 7 additions & 3 deletions src/sql/execution/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,21 @@ impl<T: Transaction> Executor<T> for CreateTable {
/// A DROP TABLE executor
pub struct DropTable {
table: String,
if_exists: bool,
}

impl DropTable {
pub fn new(table: String) -> Box<Self> {
Box::new(Self { table })
pub fn new(table: String, if_exists: bool) -> Box<Self> {
Box::new(Self { table, if_exists })
}
}

impl<T: Transaction> Executor<T> for DropTable {
fn execute(self: Box<Self>, txn: &mut T) -> Result<ResultSet> {
if self.if_exists && txn.read_table(&self.table)?.is_none() {
return Ok(ResultSet::DropTable { name: self.table, existed: false });
}
txn.delete_table(&self.table)?;
Ok(ResultSet::DropTable { name: self.table })
Ok(ResultSet::DropTable { name: self.table, existed: true })
}
}
5 changes: 4 additions & 1 deletion src/sql/parser/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ pub enum Statement {
name: String,
columns: Vec<Column>,
},
DropTable(String),
DropTable {
name: String,
if_exists: bool,
},

Delete {
table: String,
Expand Down
6 changes: 6 additions & 0 deletions src/sql/parser/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,14 @@ pub enum Keyword {
Desc,
Double,
Drop,
Exists,
Explain,
False,
Float,
From,
Group,
Having,
If,
Index,
Infinity,
Inner,
Expand Down Expand Up @@ -159,12 +161,14 @@ impl Keyword {
"DESC" => Self::Desc,
"DOUBLE" => Self::Double,
"DROP" => Self::Drop,
"EXISTS" => Self::Exists,
"EXPLAIN" => Self::Explain,
"FALSE" => Self::False,
"FLOAT" => Self::Float,
"FROM" => Self::From,
"GROUP" => Self::Group,
"HAVING" => Self::Having,
"IF" => Self::If,
"INDEX" => Self::Index,
"INFINITY" => Self::Infinity,
"INNER" => Self::Inner,
Expand Down Expand Up @@ -230,12 +234,14 @@ impl Keyword {
Self::Desc => "DESC",
Self::Double => "DOUBLE",
Self::Drop => "DROP",
Self::Exists => "EXISTS",
Self::Explain => "EXPLAIN",
Self::False => "FALSE",
Self::Float => "FLOAT",
Self::From => "FROM",
Self::Group => "GROUP",
Self::Having => "HAVING",
Self::If => "IF",
Self::Index => "INDEX",
Self::Infinity => "INFINITY",
Self::Inner => "INNER",
Expand Down
8 changes: 7 additions & 1 deletion src/sql/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,13 @@ impl<'a> Parser<'a> {
/// Parses a DROP TABLE DDL statement. The DROP TABLE prefix has
/// already been consumed.
fn parse_ddl_drop_table(&mut self) -> Result<ast::Statement> {
Ok(ast::Statement::DropTable(self.next_ident()?))
let mut if_exists = false;
if let Some(Token::Keyword(Keyword::If)) = self.next_if_keyword() {
self.next_expect(Some(Token::Keyword(Keyword::Exists)))?;
if_exists = true;
}
let name = self.next_ident()?;
Ok(ast::Statement::DropTable { name, if_exists })
}

/// Parses a column specification
Expand Down
3 changes: 2 additions & 1 deletion src/sql/plan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub enum Node {
},
DropTable {
table: String,
if_exists: bool,
},
Filter {
source: Box<Node>,
Expand Down Expand Up @@ -280,7 +281,7 @@ impl Node {
s += &format!("Delete: {}\n", table);
s += &source.format(indent, false, true);
}
Self::DropTable { table } => {
Self::DropTable { table, if_exists: _ } => {
s += &format!("DropTable: {}\n", table);
}
Self::Filter { source, predicate } => {
Expand Down
4 changes: 3 additions & 1 deletion src/sql/plan/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ impl<'a, C: Catalog> Planner<'a, C> {
)?,
},

ast::Statement::DropTable(table) => Node::DropTable { table },
ast::Statement::DropTable { name, if_exists } => {
Node::DropTable { table: name, if_exists }
}

// DML statements (mutations).
ast::Statement::Delete { table, r#where } => {
Expand Down
2 changes: 2 additions & 0 deletions tests/sql/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ test_schema! { with [
drop_table_bare: "DROP TABLE",
drop_table_missing: "DROP TABLE name",
drop_table_multiple: "DROP TABLE a, c",
drop_table_if_exists: "DROP TABLE IF EXISTS a",
drop_table_if_exists_missing: "DROP TABLE IF EXISTS name",
}
test_schema! { with [
"CREATE TABLE target (id INTEGER PRIMARY KEY)",
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/drop_table
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: DROP TABLE a
Result: DropTable { name: "a" }
Result: DropTable { name: "a", existed: true }

Storage:
CREATE TABLE b (
Expand Down
17 changes: 17 additions & 0 deletions tests/sql/schema/drop_table_if_exists
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Query: DROP TABLE IF EXISTS a
Result: DropTable { name: "a", existed: true }

Storage:
CREATE TABLE b (
id INTEGER PRIMARY KEY
)
[Integer(21)]
[Integer(22)]
[Integer(23)]

CREATE TABLE c (
id INTEGER PRIMARY KEY
)
[Integer(31)]
[Integer(32)]
[Integer(33)]
24 changes: 24 additions & 0 deletions tests/sql/schema/drop_table_if_exists_missing
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Query: DROP TABLE IF EXISTS name
Result: DropTable { name: "name", existed: false }

Storage:
CREATE TABLE a (
id INTEGER PRIMARY KEY
)
[Integer(11)]
[Integer(12)]
[Integer(13)]

CREATE TABLE b (
id INTEGER PRIMARY KEY
)
[Integer(21)]
[Integer(22)]
[Integer(23)]

CREATE TABLE c (
id INTEGER PRIMARY KEY
)
[Integer(31)]
[Integer(32)]
[Integer(33)]
2 changes: 1 addition & 1 deletion tests/sql/schema/drop_table_ref_self
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: DROP TABLE self
Result: DropTable { name: "self" }
Result: DropTable { name: "self", existed: true }

Storage:
CREATE TABLE source (
Expand Down
2 changes: 1 addition & 1 deletion tests/sql/schema/drop_table_ref_source
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Query: DROP TABLE source
Result: DropTable { name: "source" }
Result: DropTable { name: "source", existed: true }

Storage:
CREATE TABLE self (
Expand Down

0 comments on commit fe0ed72

Please sign in to comment.