diff --git a/cli/app.rs b/cli/app.rs index d214d139f..37a92174e 100644 --- a/cli/app.rs +++ b/cli/app.rs @@ -641,6 +641,11 @@ impl Limbo { let _ = self.writeln(v); }); } + Command::ListIndexes(args) => { + if let Err(e) = self.display_indexes(args.tbl_name) { + let _ = self.writeln(e.to_string()); + } + } Command::Timer(timer_mode) => { self.opts.timer = match timer_mode.mode { TimerMode::On => true, @@ -916,6 +921,55 @@ impl Limbo { Ok(()) } + fn display_indexes(&mut self, maybe_table: Option) -> anyhow::Result<()> { + let sql = match maybe_table { + Some(ref tbl_name) => format!( + "SELECT name FROM sqlite_schema WHERE type='index' AND tbl_name = '{}' ORDER BY 1", + tbl_name + ), + None => String::from("SELECT name FROM sqlite_schema WHERE type='index' ORDER BY 1"), + }; + + match self.conn.query(&sql) { + Ok(Some(ref mut rows)) => { + let mut indexes = String::new(); + loop { + match rows.step()? { + StepResult::Row => { + let row = rows.row().unwrap(); + if let Ok(OwnedValue::Text(idx)) = row.get::<&OwnedValue>(0) { + indexes.push_str(idx.as_str()); + indexes.push(' '); + } + } + StepResult::IO => { + self.io.run_once()?; + } + StepResult::Interrupt => break, + StepResult::Done => break, + StepResult::Busy => { + let _ = self.writeln("database is busy"); + break; + } + } + } + if !indexes.is_empty() { + let _ = self.writeln(indexes.trim_end()); + } + } + Err(err) => { + if err.to_string().contains("no such table: sqlite_schema") { + return Err(anyhow::anyhow!("Unable to access database schema. The database may be using an older SQLite version or may not be properly initialized.")); + } else { + return Err(anyhow::anyhow!("Error querying schema: {}", err)); + } + } + Ok(None) => {} + } + + Ok(()) + } + fn display_tables(&mut self, pattern: Option<&str>) -> anyhow::Result<()> { let sql = match pattern { Some(pattern) => format!( diff --git a/cli/commands/args.rs b/cli/commands/args.rs index 750895049..4c36e6ef6 100644 --- a/cli/commands/args.rs +++ b/cli/commands/args.rs @@ -3,6 +3,12 @@ use clap_complete::{ArgValueCompleter, CompletionCandidate, PathCompleter}; use crate::{input::OutputMode, opcodes_dictionary::OPCODE_DESCRIPTIONS}; +#[derive(Debug, Clone, Args)] +pub struct IndexesArgs { + /// Name of table + pub tbl_name: Option, +} + #[derive(Debug, Clone, Args)] pub struct ExitArgs { /// Exit code diff --git a/cli/commands/mod.rs b/cli/commands/mod.rs index e01828517..bd94c6051 100644 --- a/cli/commands/mod.rs +++ b/cli/commands/mod.rs @@ -2,8 +2,8 @@ pub mod args; pub mod import; use args::{ - CwdArgs, EchoArgs, ExitArgs, LoadExtensionArgs, NullValueArgs, OpcodesArgs, OpenArgs, - OutputModeArgs, SchemaArgs, SetOutputArgs, TablesArgs, TimerArgs, + CwdArgs, EchoArgs, ExitArgs, IndexesArgs, LoadExtensionArgs, NullValueArgs, OpcodesArgs, + OpenArgs, OutputModeArgs, SchemaArgs, SetOutputArgs, TablesArgs, TimerArgs, }; use clap::Parser; use import::ImportArgs; @@ -72,6 +72,9 @@ pub enum Command { /// List vfs modules available #[command(name = "vfslist", display_name = ".vfslist")] ListVfs, + /// Show names of indexes + #[command(name = "indexes", display_name = ".indexes")] + ListIndexes(IndexesArgs), #[command(name = "timer", display_name = ".timer")] Timer(TimerArgs), } diff --git a/cli/input.rs b/cli/input.rs index eac5312dc..e20d5a71a 100644 --- a/cli/input.rs +++ b/cli/input.rs @@ -218,6 +218,8 @@ pub const AFTER_HELP_MSG: &str = r#"Usage Examples: 13. To list all available VFS: .listvfs +14. To show names of indexes: + .indexes ?TABLE? Note: - All SQL commands must end with a semicolon (;).