mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-18 17:14:20 +01:00
Implement pragma database_list
And also the CLI option .databases, which is just manipulating that. This is one step in the road to attach.
This commit is contained in:
@@ -119,7 +119,7 @@ Turso aims to be fully compatible with SQLite, with opt-in features not supporte
|
|||||||
| PRAGMA count_changes | Not Needed | deprecated in SQLite |
|
| PRAGMA count_changes | Not Needed | deprecated in SQLite |
|
||||||
| PRAGMA data_store_directory | Not Needed | deprecated in SQLite |
|
| PRAGMA data_store_directory | Not Needed | deprecated in SQLite |
|
||||||
| PRAGMA data_version | No | |
|
| PRAGMA data_version | No | |
|
||||||
| PRAGMA database_list | No | |
|
| PRAGMA database_list | Yes | |
|
||||||
| PRAGMA default_cache_size | Not Needed | deprecated in SQLite |
|
| PRAGMA default_cache_size | Not Needed | deprecated in SQLite |
|
||||||
| PRAGMA defer_foreign_keys | No | |
|
| PRAGMA defer_foreign_keys | No | |
|
||||||
| PRAGMA empty_result_callbacks | Not Needed | deprecated in SQLite |
|
| PRAGMA empty_result_callbacks | Not Needed | deprecated in SQLite |
|
||||||
|
|||||||
74
cli/app.rs
74
cli/app.rs
@@ -624,6 +624,11 @@ impl Limbo {
|
|||||||
let _ = self.writeln(e.to_string());
|
let _ = self.writeln(e.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Command::Databases => {
|
||||||
|
if let Err(e) = self.display_databases() {
|
||||||
|
let _ = self.writeln(e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
Command::Opcodes(args) => {
|
Command::Opcodes(args) => {
|
||||||
if let Some(opcode) = args.opcode {
|
if let Some(opcode) = args.opcode {
|
||||||
for op in &OPCODE_DESCRIPTIONS {
|
for op in &OPCODE_DESCRIPTIONS {
|
||||||
@@ -1100,6 +1105,75 @@ impl Limbo {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn display_databases(&mut self) -> anyhow::Result<()> {
|
||||||
|
let sql = "PRAGMA database_list";
|
||||||
|
|
||||||
|
match self.conn.query(sql) {
|
||||||
|
Ok(Some(ref mut rows)) => {
|
||||||
|
loop {
|
||||||
|
match rows.step()? {
|
||||||
|
StepResult::Row => {
|
||||||
|
let row = rows.row().unwrap();
|
||||||
|
if let (
|
||||||
|
Ok(Value::Integer(_seq)),
|
||||||
|
Ok(Value::Text(name)),
|
||||||
|
Ok(file_value),
|
||||||
|
) = (
|
||||||
|
row.get::<&Value>(0),
|
||||||
|
row.get::<&Value>(1),
|
||||||
|
row.get::<&Value>(2),
|
||||||
|
) {
|
||||||
|
let file = match file_value {
|
||||||
|
Value::Text(path) => path.as_str(),
|
||||||
|
Value::Null => "",
|
||||||
|
_ => "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Format like SQLite: "main: /path/to/file r/w"
|
||||||
|
let file_display = if file.is_empty() {
|
||||||
|
"\"\"".to_string()
|
||||||
|
} else {
|
||||||
|
file.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Detect readonly mode from connection
|
||||||
|
let mode = if self.conn.is_readonly() {
|
||||||
|
"r/o"
|
||||||
|
} else {
|
||||||
|
"r/w"
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = self.writeln(format!(
|
||||||
|
"{}: {} {}",
|
||||||
|
name.as_str(),
|
||||||
|
file_display,
|
||||||
|
mode
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StepResult::IO => {
|
||||||
|
rows.run_once()?;
|
||||||
|
}
|
||||||
|
StepResult::Interrupt => break,
|
||||||
|
StepResult::Done => break,
|
||||||
|
StepResult::Busy => {
|
||||||
|
let _ = self.writeln("database is busy");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
let _ = self.writeln("No results returned from the query.");
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(anyhow::anyhow!("Error querying database list: {}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_remaining_input(&mut self) {
|
pub fn handle_remaining_input(&mut self) {
|
||||||
if self.input_buff.is_empty() {
|
if self.input_buff.is_empty() {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ pub enum Command {
|
|||||||
Echo(EchoArgs),
|
Echo(EchoArgs),
|
||||||
/// Display tables
|
/// Display tables
|
||||||
Tables(TablesArgs),
|
Tables(TablesArgs),
|
||||||
|
/// Display attached databases
|
||||||
|
Databases,
|
||||||
/// Import data from FILE into TABLE
|
/// Import data from FILE into TABLE
|
||||||
#[command(name = "import", display_name = ".import")]
|
#[command(name = "import", display_name = ".import")]
|
||||||
Import(ImportArgs),
|
Import(ImportArgs),
|
||||||
|
|||||||
28
cli/input.rs
28
cli/input.rs
@@ -200,39 +200,43 @@ pub const AFTER_HELP_MSG: &str = r#"Usage Examples:
|
|||||||
4. To list all tables:
|
4. To list all tables:
|
||||||
.tables
|
.tables
|
||||||
|
|
||||||
5. To list all available SQL opcodes:
|
5. To list all databases:
|
||||||
|
.databases
|
||||||
|
|
||||||
|
6. To list all available SQL opcodes:
|
||||||
.opcodes
|
.opcodes
|
||||||
|
|
||||||
6. To change the current output mode to 'pretty':
|
7. To change the current output mode to 'pretty':
|
||||||
.mode pretty
|
.mode pretty
|
||||||
|
|
||||||
7. Send output to STDOUT if no file is specified:
|
8. Send output to STDOUT if no file is specified:
|
||||||
.output
|
.output
|
||||||
|
|
||||||
8. To change the current working directory to '/tmp':
|
9. To change the current working directory to '/tmp':
|
||||||
.cd /tmp
|
.cd /tmp
|
||||||
|
|
||||||
9. Show the current values of settings:
|
10. Show the current values of settings:
|
||||||
.show
|
.show
|
||||||
|
|
||||||
10. To import csv file 'sample.csv' into 'csv_table' table:
|
11. To import csv file 'sample.csv' into 'csv_table' table:
|
||||||
.import --csv sample.csv csv_table
|
.import --csv sample.csv csv_table
|
||||||
|
|
||||||
11. To display the database contents as SQL:
|
12. To display the database contents as SQL:
|
||||||
.dump
|
.dump
|
||||||
|
|
||||||
12. To load an extension library:
|
13. To load an extension library:
|
||||||
.load /target/debug/liblimbo_regexp
|
.load /target/debug/liblimbo_regexp
|
||||||
|
|
||||||
13. To list all available VFS:
|
14. To list all available VFS:
|
||||||
.listvfs
|
.listvfs
|
||||||
14. To show names of indexes:
|
|
||||||
|
15. To show names of indexes:
|
||||||
.indexes ?TABLE?
|
.indexes ?TABLE?
|
||||||
|
|
||||||
15. To turn on column headers in list mode:
|
16. To turn on column headers in list mode:
|
||||||
.headers on
|
.headers on
|
||||||
|
|
||||||
16. To turn off column headers in list mode:
|
17. To turn off column headers in list mode:
|
||||||
.headers off
|
.headers off
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
|
|||||||
17
core/lib.rs
17
core/lib.rs
@@ -893,6 +893,23 @@ impl Connection {
|
|||||||
self.page_size.get()
|
self.page_size.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_database_canonical_path(&self) -> String {
|
||||||
|
if self._db.path == ":memory:" {
|
||||||
|
// For in-memory databases, SQLite shows empty string
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
// For file databases, try show the full absolute path if that doesn't fail
|
||||||
|
match std::fs::canonicalize(&self._db.path) {
|
||||||
|
Ok(abs_path) => abs_path.to_string_lossy().to_string(),
|
||||||
|
Err(_) => self._db.path.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_readonly(&self) -> bool {
|
||||||
|
self._db.open_flags.contains(OpenFlags::ReadOnly)
|
||||||
|
}
|
||||||
|
|
||||||
/// Reset the page size for the current connection.
|
/// Reset the page size for the current connection.
|
||||||
///
|
///
|
||||||
/// Specifying a new page size does not change the page size immediately.
|
/// Specifying a new page size does not change the page size immediately.
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ pub fn pragma_for(pragma: &PragmaName) -> Pragma {
|
|||||||
| PragmaFlags::NoColumns1,
|
| PragmaFlags::NoColumns1,
|
||||||
&["cache_size"],
|
&["cache_size"],
|
||||||
),
|
),
|
||||||
|
DatabaseList => Pragma::new(PragmaFlags::Result0, &["seq", "name", "file"]),
|
||||||
JournalMode => Pragma::new(
|
JournalMode => Pragma::new(
|
||||||
PragmaFlags::NeedSchema | PragmaFlags::Result0 | PragmaFlags::SchemaReq,
|
PragmaFlags::NeedSchema | PragmaFlags::Result0 | PragmaFlags::SchemaReq,
|
||||||
&["journal_mode"],
|
&["journal_mode"],
|
||||||
|
|||||||
@@ -250,6 +250,7 @@ fn update_pragma(
|
|||||||
connection.set_capture_data_changes(opts);
|
connection.set_capture_data_changes(opts);
|
||||||
Ok((program, TransactionMode::Write))
|
Ok((program, TransactionMode::Write))
|
||||||
}
|
}
|
||||||
|
PragmaName::DatabaseList => unreachable!("database_list cannot be set"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,6 +280,27 @@ fn query_pragma(
|
|||||||
program.add_pragma_result_column(pragma.to_string());
|
program.add_pragma_result_column(pragma.to_string());
|
||||||
Ok((program, TransactionMode::None))
|
Ok((program, TransactionMode::None))
|
||||||
}
|
}
|
||||||
|
PragmaName::DatabaseList => {
|
||||||
|
let base_reg = register;
|
||||||
|
program.alloc_registers(2);
|
||||||
|
|
||||||
|
// For now, we only show the main database (seq=0)
|
||||||
|
// seq (sequence number)
|
||||||
|
program.emit_int(0, base_reg);
|
||||||
|
|
||||||
|
// name
|
||||||
|
program.emit_string8("main".into(), base_reg + 1);
|
||||||
|
|
||||||
|
let file_path = connection.get_database_canonical_path();
|
||||||
|
program.emit_string8(file_path, base_reg + 2);
|
||||||
|
|
||||||
|
program.emit_result_row(base_reg, 3);
|
||||||
|
let pragma = pragma_for(&pragma);
|
||||||
|
for col_name in pragma.columns.iter() {
|
||||||
|
program.add_pragma_result_column(col_name.to_string());
|
||||||
|
}
|
||||||
|
Ok((program, TransactionMode::None))
|
||||||
|
}
|
||||||
PragmaName::JournalMode => {
|
PragmaName::JournalMode => {
|
||||||
program.emit_string8("wal".into(), register);
|
program.emit_string8("wal".into(), register);
|
||||||
program.emit_result_row(register, 1);
|
program.emit_result_row(register, 1);
|
||||||
|
|||||||
@@ -1749,6 +1749,8 @@ pub enum PragmaName {
|
|||||||
AutoVacuum,
|
AutoVacuum,
|
||||||
/// `cache_size` pragma
|
/// `cache_size` pragma
|
||||||
CacheSize,
|
CacheSize,
|
||||||
|
/// List databases
|
||||||
|
DatabaseList,
|
||||||
/// Run integrity check on the database file
|
/// Run integrity check on the database file
|
||||||
IntegrityCheck,
|
IntegrityCheck,
|
||||||
/// `journal_mode` pragma
|
/// `journal_mode` pragma
|
||||||
|
|||||||
Reference in New Issue
Block a user