Merge 'Add the .indexes command' from Anton Harniakou

Adds ability to view database indices using the `.indexes ?TABLE?`
command.

Closes #1409
This commit is contained in:
Pekka Enberg 2025-04-27 20:46:02 +03:00
commit ab841c47bc
4 changed files with 67 additions and 2 deletions

View file

@ -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<String>) -> 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!(

View file

@ -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<String>,
}
#[derive(Debug, Clone, Args)]
pub struct ExitArgs {
/// Exit code

View file

@ -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),
}

View file

@ -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 (;).