2022-04-26 21:20:59 +02:00
|
|
|
use super::super::SQLiteDatabase;
|
2022-04-27 12:52:31 +02:00
|
|
|
use crate::database::values::definitions::db_row::DbRow;
|
2022-04-26 21:20:59 +02:00
|
|
|
use nu_protocol::{
|
|
|
|
ast::Call,
|
|
|
|
engine::{Command, EngineState, Stack},
|
2022-04-27 01:16:46 +02:00
|
|
|
Category, Example, PipelineData, ShellError, Signature, Value,
|
2022-04-26 21:20:59 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2022-04-27 01:16:46 +02:00
|
|
|
pub struct SchemaDb;
|
2022-04-26 21:20:59 +02:00
|
|
|
|
2022-04-27 01:16:46 +02:00
|
|
|
impl Command for SchemaDb {
|
2022-04-26 21:20:59 +02:00
|
|
|
fn name(&self) -> &str {
|
2022-04-27 01:16:46 +02:00
|
|
|
"db schema"
|
2022-04-26 21:20:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn signature(&self) -> Signature {
|
2022-04-27 01:16:46 +02:00
|
|
|
Signature::build(self.name()).category(Category::Custom("database".into()))
|
2022-04-26 21:20:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn usage(&self) -> &str {
|
2022-04-27 01:16:46 +02:00
|
|
|
"Show database information, including its schema."
|
2022-04-26 21:20:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn examples(&self) -> Vec<Example> {
|
|
|
|
vec![Example {
|
2022-04-27 01:16:46 +02:00
|
|
|
description: "Show the schema of a SQLite database",
|
|
|
|
example: r#"open foo.db | db schema"#,
|
2022-04-26 21:20:59 +02:00
|
|
|
result: None,
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn search_terms(&self) -> Vec<&str> {
|
2022-04-27 01:16:46 +02:00
|
|
|
vec!["database", "info", "SQLite", "schema"]
|
2022-04-26 21:20:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn run(
|
|
|
|
&self,
|
2022-04-27 01:16:46 +02:00
|
|
|
_engine_state: &EngineState,
|
|
|
|
_stack: &mut Stack,
|
2022-04-26 21:20:59 +02:00
|
|
|
call: &Call,
|
2022-04-27 01:16:46 +02:00
|
|
|
input: PipelineData,
|
2022-04-26 21:20:59 +02:00
|
|
|
) -> Result<PipelineData, ShellError> {
|
|
|
|
let mut cols = vec![];
|
|
|
|
let mut vals = vec![];
|
2022-04-27 01:16:46 +02:00
|
|
|
let span = call.head;
|
2022-04-26 21:20:59 +02:00
|
|
|
|
2022-04-27 01:16:46 +02:00
|
|
|
let sqlite_db = SQLiteDatabase::try_from_pipeline(input, span)?;
|
2022-04-26 21:20:59 +02:00
|
|
|
let conn = sqlite_db.open_connection().map_err(|e| {
|
|
|
|
ShellError::GenericError(
|
|
|
|
"Error opening file".into(),
|
|
|
|
e.to_string(),
|
|
|
|
Some(span),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let dbs = sqlite_db.get_databases_and_tables(&conn).map_err(|e| {
|
|
|
|
ShellError::GenericError(
|
|
|
|
"Error getting databases and tables".into(),
|
|
|
|
e.to_string(),
|
|
|
|
Some(span),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
cols.push("db_filename".into());
|
|
|
|
vals.push(Value::String {
|
2022-04-27 01:16:46 +02:00
|
|
|
val: sqlite_db.path.to_string_lossy().to_string(),
|
2022-04-26 21:20:59 +02:00
|
|
|
span,
|
|
|
|
});
|
|
|
|
|
|
|
|
for db in dbs {
|
|
|
|
let tables = db.tables();
|
|
|
|
let mut table_list: Vec<Value> = vec![];
|
|
|
|
let mut table_names = vec![];
|
|
|
|
let mut table_values = vec![];
|
|
|
|
for table in tables {
|
|
|
|
let columns = sqlite_db.get_columns(&conn, &table).map_err(|e| {
|
|
|
|
ShellError::GenericError(
|
|
|
|
"Error getting database columns".into(),
|
|
|
|
e.to_string(),
|
|
|
|
Some(span),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
// a record of column name = column value
|
|
|
|
let mut column_info = vec![];
|
|
|
|
for t in columns {
|
|
|
|
let mut col_names = vec![];
|
|
|
|
let mut col_values = vec![];
|
|
|
|
let fields = t.fields();
|
|
|
|
let columns = t.columns();
|
|
|
|
for (k, v) in fields.iter().zip(columns.iter()) {
|
|
|
|
col_names.push(k.clone());
|
|
|
|
col_values.push(Value::string(v.clone(), span));
|
|
|
|
}
|
|
|
|
column_info.push(Value::Record {
|
|
|
|
cols: col_names.clone(),
|
|
|
|
vals: col_values.clone(),
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let constraints = sqlite_db.get_constraints(&conn, &table).map_err(|e| {
|
|
|
|
ShellError::GenericError(
|
|
|
|
"Error getting DB constraints".into(),
|
|
|
|
e.to_string(),
|
|
|
|
Some(span),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
let mut constraint_info = vec![];
|
|
|
|
for constraint in constraints {
|
|
|
|
let mut con_cols = vec![];
|
|
|
|
let mut con_vals = vec![];
|
|
|
|
let fields = constraint.fields();
|
|
|
|
let columns = constraint.columns();
|
|
|
|
for (k, v) in fields.iter().zip(columns.iter()) {
|
|
|
|
con_cols.push(k.clone());
|
|
|
|
con_vals.push(Value::string(v.clone(), span));
|
|
|
|
}
|
|
|
|
constraint_info.push(Value::Record {
|
|
|
|
cols: con_cols.clone(),
|
|
|
|
vals: con_vals.clone(),
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let foreign_keys = sqlite_db.get_foreign_keys(&conn, &table).map_err(|e| {
|
|
|
|
ShellError::GenericError(
|
|
|
|
"Error getting DB Foreign Keys".into(),
|
|
|
|
e.to_string(),
|
|
|
|
Some(span),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
let mut foreign_key_info = vec![];
|
|
|
|
for fk in foreign_keys {
|
|
|
|
let mut fk_cols = vec![];
|
|
|
|
let mut fk_vals = vec![];
|
|
|
|
let fields = fk.fields();
|
|
|
|
let columns = fk.columns();
|
|
|
|
for (k, v) in fields.iter().zip(columns.iter()) {
|
|
|
|
fk_cols.push(k.clone());
|
|
|
|
fk_vals.push(Value::string(v.clone(), span));
|
|
|
|
}
|
|
|
|
foreign_key_info.push(Value::Record {
|
|
|
|
cols: fk_cols.clone(),
|
|
|
|
vals: fk_vals.clone(),
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let indexes = sqlite_db.get_indexes(&conn, &table).map_err(|e| {
|
|
|
|
ShellError::GenericError(
|
|
|
|
"Error getting DB Indexes".into(),
|
|
|
|
e.to_string(),
|
|
|
|
Some(span),
|
|
|
|
None,
|
|
|
|
Vec::new(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
let mut index_info = vec![];
|
|
|
|
for index in indexes {
|
|
|
|
let mut idx_cols = vec![];
|
|
|
|
let mut idx_vals = vec![];
|
|
|
|
let fields = index.fields();
|
|
|
|
let columns = index.columns();
|
|
|
|
for (k, v) in fields.iter().zip(columns.iter()) {
|
|
|
|
idx_cols.push(k.clone());
|
|
|
|
idx_vals.push(Value::string(v.clone(), span));
|
|
|
|
}
|
|
|
|
index_info.push(Value::Record {
|
|
|
|
cols: idx_cols.clone(),
|
|
|
|
vals: idx_vals.clone(),
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
table_names.push(table.name);
|
|
|
|
table_values.push(Value::Record {
|
|
|
|
cols: vec![
|
|
|
|
"columns".into(),
|
|
|
|
"constraints".into(),
|
|
|
|
"foreign_keys".into(),
|
|
|
|
"indexes".into(),
|
|
|
|
],
|
|
|
|
vals: vec![
|
|
|
|
Value::List {
|
|
|
|
vals: column_info,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Value::List {
|
|
|
|
vals: constraint_info,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Value::List {
|
|
|
|
vals: foreign_key_info,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
Value::List {
|
|
|
|
vals: index_info,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
table_list.push(Value::Record {
|
|
|
|
cols: table_names,
|
|
|
|
vals: table_values,
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
|
|
|
|
cols.push("databases".into());
|
|
|
|
let mut rcols = vec![];
|
|
|
|
let mut rvals = vec![];
|
|
|
|
rcols.push("name".into());
|
|
|
|
rvals.push(Value::string(db.name().to_string(), span));
|
|
|
|
rcols.push("tables".into());
|
|
|
|
rvals.append(&mut table_list);
|
|
|
|
vals.push(Value::Record {
|
|
|
|
cols: rcols,
|
|
|
|
vals: rvals,
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(PipelineData::Value(
|
|
|
|
Value::Record { cols, vals, span },
|
|
|
|
None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|