Skip to main content

nu_command/stor/
delete.rs

1use crate::database::{MEMORY_DB, SQLiteDatabase};
2use nu_engine::command_prelude::*;
3use nu_protocol::Signals;
4use nu_protocol::shell_error::generic::GenericError;
5use std::fmt::Write;
6
7#[derive(Clone)]
8pub struct StorDelete;
9
10impl Command for StorDelete {
11    fn name(&self) -> &str {
12        "stor delete"
13    }
14
15    fn signature(&self) -> Signature {
16        Signature::build("stor delete")
17            .input_output_types(vec![(Type::Nothing, Type::table())])
18            .required_named(
19                "table-name",
20                SyntaxShape::String,
21                "Name of the table you want to delete or delete from.",
22                Some('t'),
23            )
24            .named(
25                "where-clause",
26                SyntaxShape::String,
27                "A sql string to use as a where clause without the WHERE keyword.",
28                Some('w'),
29            )
30            .allow_variants_without_examples(true)
31            .category(Category::Database)
32    }
33
34    fn description(&self) -> &str {
35        "Delete a table or specified rows in the in-memory sqlite database."
36    }
37
38    fn search_terms(&self) -> Vec<&str> {
39        vec!["sqlite", "remove", "table", "saving", "drop"]
40    }
41
42    fn examples(&self) -> Vec<Example<'_>> {
43        vec![
44            Example {
45                description: "Delete a table from the in-memory sqlite database",
46                example: "stor delete --table-name nudb",
47                result: None,
48            },
49            Example {
50                description: "Delete some rows from the in-memory sqlite database with a where clause",
51                example: "stor delete --table-name nudb --where-clause \"int1 == 5\"",
52                result: None,
53            },
54        ]
55    }
56
57    fn run(
58        &self,
59        engine_state: &EngineState,
60        stack: &mut Stack,
61        call: &Call,
62        _input: PipelineData,
63    ) -> Result<PipelineData, ShellError> {
64        let span = call.head;
65        // For dropping/deleting an entire table
66        let table_name_opt: Option<String> = call.get_flag(engine_state, stack, "table-name")?;
67
68        // For deleting rows from a table
69        let where_clause_opt: Option<String> =
70            call.get_flag(engine_state, stack, "where-clause")?;
71
72        if table_name_opt.is_none() && where_clause_opt.is_none() {
73            return Err(ShellError::MissingParameter {
74                param_name: "requires at least one of table-name or where-clause".into(),
75                span,
76            });
77        }
78
79        if table_name_opt.is_none() && where_clause_opt.is_some() {
80            return Err(ShellError::MissingParameter {
81                param_name: "using the where-clause requires the use of a table-name".into(),
82                span,
83            });
84        }
85
86        // Open the in-mem database
87        let db = Box::new(SQLiteDatabase::new(
88            std::path::Path::new(MEMORY_DB),
89            Signals::empty(),
90        ));
91
92        if let Some(new_table_name) = table_name_opt
93            && let Ok(conn) = db.open_connection()
94        {
95            let sql_stmt = match where_clause_opt {
96                None => {
97                    // We're deleting an entire table
98                    format!("DROP TABLE {new_table_name}")
99                }
100                Some(where_clause) => {
101                    // We're just deleting some rows
102                    let mut delete_stmt = format!("DELETE FROM {new_table_name} ");
103
104                    // Yup, this is a bit janky, but I'm not sure a better way to do this without having
105                    // --and and --or flags as well as supporting ==, !=, <>, is null, is not null, etc.
106                    // and other sql syntax. So, for now, just type a sql where clause as a string.
107                    write!(delete_stmt, "WHERE {where_clause}")
108                        .expect("writing to a String is infallible");
109                    delete_stmt
110                }
111            };
112
113            // dbg!(&sql_stmt);
114            conn.execute(&sql_stmt, []).map_err(|err| {
115                ShellError::Generic(GenericError::new_internal(
116                    "Failed to delete using the SQLite connection in memory from delete.rs.",
117                    err.to_string(),
118                ))
119            })?;
120        }
121        // dbg!(db.clone());
122        Ok(Value::custom(db, span).into_pipeline_data())
123    }
124}
125
126#[cfg(test)]
127mod test {
128    use super::*;
129
130    #[test]
131    fn test_examples() -> nu_test_support::Result {
132        nu_test_support::test().examples(StorDelete)
133    }
134}