forked from extern/nushell
# Description When implementing a `Command`, one must also import all the types present in the function signatures for `Command`. This makes it so that we often import the same set of types in each command implementation file. E.g., something like this: ```rust use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Span, Type, Value, }; ``` This PR adds the `nu_engine::command_prelude` module which contains the necessary and commonly used types to implement a `Command`: ```rust // command_prelude.rs pub use crate::CallExt; pub use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, record, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, IntoSpanned, PipelineData, Record, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value, }; ``` This should reduce the boilerplate needed to implement a command and also gives us a place to track the breadth of the `Command` API. I tried to be conservative with what went into the prelude modules, since it might be hard/annoying to remove items from the prelude in the future. Let me know if something should be included or excluded.
98 lines
3.1 KiB
Rust
98 lines
3.1 KiB
Rust
use crate::database::{values::sqlite::nu_value_to_params, SQLiteDatabase};
|
|
use nu_engine::command_prelude::*;
|
|
|
|
#[derive(Clone)]
|
|
pub struct QueryDb;
|
|
|
|
impl Command for QueryDb {
|
|
fn name(&self) -> &str {
|
|
"query db"
|
|
}
|
|
|
|
fn signature(&self) -> Signature {
|
|
Signature::build(self.name())
|
|
.input_output_types(vec![(Type::Any, Type::Any)])
|
|
.required(
|
|
"SQL",
|
|
SyntaxShape::String,
|
|
"SQL to execute against the database.",
|
|
)
|
|
.named(
|
|
"params",
|
|
// TODO: Use SyntaxShape::OneOf with Records and Lists, when Lists no longer break inside OneOf
|
|
SyntaxShape::Any,
|
|
"List of parameters for the SQL statement",
|
|
Some('p'),
|
|
)
|
|
.category(Category::Database)
|
|
}
|
|
|
|
fn usage(&self) -> &str {
|
|
"Query a database using SQL."
|
|
}
|
|
|
|
fn examples(&self) -> Vec<Example> {
|
|
vec![
|
|
Example {
|
|
description: "Execute SQL against a SQLite database",
|
|
example: r#"open foo.db | query db "SELECT * FROM Bar""#,
|
|
result: None,
|
|
},
|
|
Example {
|
|
description: "Execute a SQL statement with parameters",
|
|
example: r#"stor create -t my_table -c { first: str, second: int }
|
|
stor open | query db "INSERT INTO my_table VALUES (?, ?)" -p [hello 123]"#,
|
|
result: None,
|
|
},
|
|
Example {
|
|
description: "Execute a SQL statement with named parameters",
|
|
example: r#"stor create -t my_table -c { first: str, second: int }
|
|
stor insert -t my_table -d { first: 'hello', second: '123' }
|
|
stor open | query db "SELECT * FROM my_table WHERE second = :search_second" -p { search_second: 123 }"#,
|
|
result: Some(Value::test_list(vec![Value::test_record(record! {
|
|
"first" => Value::test_string("hello"),
|
|
"second" => Value::test_int(123)
|
|
})])),
|
|
},
|
|
]
|
|
}
|
|
|
|
fn search_terms(&self) -> Vec<&str> {
|
|
vec!["database", "SQLite"]
|
|
}
|
|
|
|
fn run(
|
|
&self,
|
|
engine_state: &EngineState,
|
|
stack: &mut Stack,
|
|
call: &Call,
|
|
input: PipelineData,
|
|
) -> Result<PipelineData, ShellError> {
|
|
let sql: Spanned<String> = call.req(engine_state, stack, 0)?;
|
|
let params_value: Value = call
|
|
.get_flag(engine_state, stack, "params")?
|
|
.unwrap_or_else(|| Value::nothing(Span::unknown()));
|
|
|
|
let params = nu_value_to_params(params_value)?;
|
|
|
|
let db = SQLiteDatabase::try_from_pipeline(input, call.head)?;
|
|
db.query(&sql, params, call.head)
|
|
.map(IntoPipelineData::into_pipeline_data)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use crate::{StorCreate, StorInsert, StorOpen};
|
|
|
|
use super::*;
|
|
|
|
#[ignore = "stor db does not persist changes between pipelines"]
|
|
#[test]
|
|
fn test_examples() {
|
|
use crate::test_examples_with_commands;
|
|
|
|
test_examples_with_commands(QueryDb {}, &[&StorOpen, &StorCreate, &StorInsert])
|
|
}
|
|
}
|