Merge 'Fix .schema command for empty databases' from Diego Reis

Fixes #3274

Closes #3342
This commit is contained in:
Pekka Enberg
2025-09-25 17:25:46 +03:00
committed by GitHub
4 changed files with 30 additions and 21 deletions

View File

@@ -348,13 +348,13 @@ fn py_to_db_value(obj: &Bound<PyAny>) -> Result<turso_core::Value> {
if obj.is_none() {
Ok(Value::Null)
} else if let Ok(integer) = obj.extract::<i64>() {
return Ok(Value::Integer(integer));
Ok(Value::Integer(integer))
} else if let Ok(float) = obj.extract::<f64>() {
return Ok(Value::Float(float));
Ok(Value::Float(float))
} else if let Ok(string) = obj.extract::<String>() {
return Ok(Value::Text(string.into()));
Ok(Value::Text(string.into()))
} else if let Ok(bytes) = obj.downcast::<PyBytes>() {
return Ok(Value::Blob(bytes.as_bytes().to_vec()));
Ok(Value::Blob(bytes.as_bytes().to_vec()))
} else {
return Err(PyErr::new::<ProgrammingError, _>(format!(
"Unsupported Python type: {}",

View File

@@ -1117,6 +1117,17 @@ impl Limbo {
db_display_name: &str,
table_name: &str,
) -> anyhow::Result<bool> {
// Yeah, sqlite also has this hardcoded: https://github.com/sqlite/sqlite/blob/31efe5a0f2f80a263457a1fc6524783c0c45769b/src/shell.c.in#L10765
match table_name {
"sqlite_master" | "sqlite_schema" | "sqlite_temp_master" | "sqlite_temp_schema" => {
let schema = format!(
"CREATE TABLE {table_name} (\n type text,\n name text,\n tbl_name text,\n rootpage integer,\n sql text\n);",
);
let _ = self.writeln(&schema);
return Ok(true);
}
_ => {}
}
let sql = format!(
"SELECT sql, type, name FROM {db_prefix}.sqlite_schema WHERE type IN ('table', 'index', 'view') AND (tbl_name = '{table_name}' OR name = '{table_name}') AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__turso_internal_%' ORDER BY CASE type WHEN 'table' THEN 1 WHEN 'view' THEN 2 WHEN 'index' THEN 3 END, rowid"
);

View File

@@ -1551,7 +1551,7 @@ impl<'a> Parser<'a> {
b"false" => {
Ok(Box::new(Expr::Literal(Literal::Numeric("0".into()))))
}
_ => return Ok(Box::new(Expr::Id(Name::Ident(s)))),
_ => Ok(Box::new(Expr::Id(Name::Ident(s)))),
})
}
_ => Ok(Box::new(Expr::Id(name))),

View File

@@ -410,15 +410,14 @@ fn perform_work(
drop(stmt_borrow);
context.fibers[fiber_idx].statement.replace(None);
// Rollback the transaction if we're in one
if matches!(context.fibers[fiber_idx].state, FiberState::InTx) {
if let Ok(rollback_stmt) =
if matches!(context.fibers[fiber_idx].state, FiberState::InTx)
&& let Ok(rollback_stmt) =
context.fibers[fiber_idx].connection.prepare("ROLLBACK")
{
context.fibers[fiber_idx]
.statement
.replace(Some(rollback_stmt));
context.fibers[fiber_idx].state = FiberState::Idle;
}
{
context.fibers[fiber_idx]
.statement
.replace(Some(rollback_stmt));
context.fibers[fiber_idx].state = FiberState::Idle;
}
return Ok(());
}
@@ -427,15 +426,14 @@ fn perform_work(
drop(stmt_borrow);
context.fibers[fiber_idx].statement.replace(None);
// Rollback the transaction if we're in one
if matches!(context.fibers[fiber_idx].state, FiberState::InTx) {
if let Ok(rollback_stmt) =
if matches!(context.fibers[fiber_idx].state, FiberState::InTx)
&& let Ok(rollback_stmt) =
context.fibers[fiber_idx].connection.prepare("ROLLBACK")
{
context.fibers[fiber_idx]
.statement
.replace(Some(rollback_stmt));
context.fibers[fiber_idx].state = FiberState::Idle;
}
{
context.fibers[fiber_idx]
.statement
.replace(Some(rollback_stmt));
context.fibers[fiber_idx].state = FiberState::Idle;
}
return Ok(());
}