mirror of
https://github.com/nushell/nushell.git
synced 2024-12-11 17:51:50 +01:00
304 lines
8.6 KiB
Rust
304 lines
8.6 KiB
Rust
use super::super::SQLiteDatabase;
|
|
use crate::database::values::definitions::{db::Db, db_row::DbRow, db_table::DbTable};
|
|
use nu_protocol::{
|
|
ast::Call,
|
|
engine::{Command, EngineState, Stack},
|
|
Category, Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
|
};
|
|
use rusqlite::Connection;
|
|
#[derive(Clone)]
|
|
pub struct SchemaDb;
|
|
|
|
impl Command for SchemaDb {
|
|
fn name(&self) -> &str {
|
|
"schema"
|
|
}
|
|
|
|
fn signature(&self) -> Signature {
|
|
Signature::build(self.name())
|
|
.input_type(Type::Custom("database".into()))
|
|
.output_type(Type::Any)
|
|
.category(Category::Custom("database".into()))
|
|
}
|
|
|
|
fn usage(&self) -> &str {
|
|
"Show sqlite database information, including its schema."
|
|
}
|
|
|
|
fn examples(&self) -> Vec<Example> {
|
|
vec![Example {
|
|
description: "Show the schema of a SQLite database",
|
|
example: r#"open foo.db | into db | schema"#,
|
|
result: None,
|
|
}]
|
|
}
|
|
|
|
fn search_terms(&self) -> Vec<&str> {
|
|
vec!["database", "info", "SQLite", "schema"]
|
|
}
|
|
|
|
fn run(
|
|
&self,
|
|
_engine_state: &EngineState,
|
|
_stack: &mut Stack,
|
|
call: &Call,
|
|
input: PipelineData,
|
|
) -> Result<PipelineData, ShellError> {
|
|
let mut cols = vec![];
|
|
let mut vals = vec![];
|
|
let span = call.head;
|
|
|
|
let sqlite_db = SQLiteDatabase::try_from_pipeline(input, span)?;
|
|
let conn = open_sqlite_db_connection(&sqlite_db, span)?;
|
|
let dbs = get_databases_and_tables(&sqlite_db, &conn, span)?;
|
|
|
|
cols.push("db_filename".into());
|
|
vals.push(Value::String {
|
|
val: sqlite_db.connection.to_string(),
|
|
span,
|
|
});
|
|
|
|
for db in dbs {
|
|
let tables = get_database_tables(&db);
|
|
let mut table_list: Vec<Value> = vec![];
|
|
let mut table_names = vec![];
|
|
let mut table_values = vec![];
|
|
for table in tables {
|
|
let column_info = get_table_columns(&sqlite_db, &conn, &table, span)?;
|
|
let constraint_info = get_table_constraints(&sqlite_db, &conn, &table, span)?;
|
|
let foreign_key_info = get_table_foreign_keys(&sqlite_db, &conn, &table, span)?;
|
|
let index_info = get_table_indexes(&sqlite_db, &conn, &table, 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,
|
|
))
|
|
}
|
|
}
|
|
|
|
fn open_sqlite_db_connection(db: &SQLiteDatabase, span: Span) -> Result<Connection, ShellError> {
|
|
db.open_connection().map_err(|e| {
|
|
ShellError::GenericError(
|
|
"Error opening file".into(),
|
|
e.to_string(),
|
|
Some(span),
|
|
None,
|
|
Vec::new(),
|
|
)
|
|
})
|
|
}
|
|
|
|
fn get_databases_and_tables(
|
|
db: &SQLiteDatabase,
|
|
conn: &Connection,
|
|
span: Span,
|
|
) -> Result<Vec<Db>, ShellError> {
|
|
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(),
|
|
)
|
|
})
|
|
}
|
|
|
|
fn get_database_tables(db: &Db) -> Vec<DbTable> {
|
|
db.tables()
|
|
}
|
|
|
|
fn get_table_columns(
|
|
db: &SQLiteDatabase,
|
|
conn: &Connection,
|
|
table: &DbTable,
|
|
span: Span,
|
|
) -> Result<Vec<Value>, ShellError> {
|
|
let columns = 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,
|
|
});
|
|
}
|
|
|
|
Ok(column_info)
|
|
}
|
|
|
|
fn get_table_constraints(
|
|
db: &SQLiteDatabase,
|
|
conn: &Connection,
|
|
table: &DbTable,
|
|
span: Span,
|
|
) -> Result<Vec<Value>, ShellError> {
|
|
let constraints = 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,
|
|
});
|
|
}
|
|
|
|
Ok(constraint_info)
|
|
}
|
|
|
|
fn get_table_foreign_keys(
|
|
db: &SQLiteDatabase,
|
|
conn: &Connection,
|
|
table: &DbTable,
|
|
span: Span,
|
|
) -> Result<Vec<Value>, ShellError> {
|
|
let foreign_keys = 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,
|
|
});
|
|
}
|
|
|
|
Ok(foreign_key_info)
|
|
}
|
|
|
|
fn get_table_indexes(
|
|
db: &SQLiteDatabase,
|
|
conn: &Connection,
|
|
table: &DbTable,
|
|
span: Span,
|
|
) -> Result<Vec<Value>, ShellError> {
|
|
let indexes = 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,
|
|
});
|
|
}
|
|
|
|
Ok(index_info)
|
|
}
|