fix(tableview): paths normalizados (Windows) + error visible en ventana

Hipotesis del bug 'tras promover, la tabla expandida queda a 0 filas':
en Windows std::filesystem::path::string() devuelve la ruta con
backslashes ('C:\\Users\\...\\operations.db'). Al embebirla en
'ATTACH ''<path>'' AS ops' DuckDB la interpretaba con quirks segun
version, fallaba el ATTACH (silent), pero ademas el siguiente
duckdb_open con paths mixtos podria no abrir el .duckdb correcto.

Cambios:
- tableview_resolve_path normaliza '\\' -> '/' (DuckDB acepta ambos
  para duckdb_open, pero forzamos '/' para evitar ambiguedad en SQL).
- ATTACH normaliza ops_db tambien.
- TableWindowState.last_error: cuando count o page fallan, se setea
  con el path/tabla involucrada y se muestra en rojo en la cabecera
  de la ventana. Asi el bug es visible sin abrir consola.
- tableview_page log incluye la SQL completa cuando falla — facil
  diagnosticar via stderr en linux.
This commit is contained in:
2026-05-01 17:02:22 +02:00
parent 7561c3f4bb
commit e6719a5ae0
4 changed files with 47 additions and 10 deletions
+19 -5
View File
@@ -137,7 +137,12 @@ bool tableview_page(const char* duckdb_path,
// ATTACH del SQLite. Las attaches viven por conexion; idempotente
// detectando si ya existe seria mas robusto pero este path se llama
// por cada page() — abrimos conexion fresca cada vez asi que no.
std::string attach = "ATTACH '" + sql_escape(ops_db) + "' AS ops (TYPE SQLITE)";
// Normaliza backslashes a '/' para que ATTACH lo interprete sin
// ambiguedad en Windows.
std::string ops_norm = ops_db;
for (char& c : ops_norm) if (c == '\\') c = '/';
std::string attach = "ATTACH '" + sql_escape(ops_norm.c_str())
+ "' AS ops (TYPE SQLITE)";
if (!duck_query_silent(h.cn, attach.c_str())) {
// sin fallar — sin promovidas, solo perdemos el flag.
join_ops = false;
@@ -171,8 +176,9 @@ bool tableview_page(const char* duckdb_path,
duckdb_result r;
if (duckdb_query(h.cn, sel.c_str(), &r) == DuckDBError) {
std::fprintf(stderr, "[tableview_page] %s\n",
duckdb_result_error(&r) ? duckdb_result_error(&r) : "?");
const char* e = duckdb_result_error(&r);
std::fprintf(stderr, "[tableview_page] FAIL: %s\n SQL: %s\n",
e ? e : "?", sel.c_str());
duckdb_destroy_result(&r);
return false;
}
@@ -303,12 +309,20 @@ bool is_absolute(const char* p) {
} // namespace
// Normaliza separadores a '/' — DuckDB acepta ambos en duckdb_open pero la
// SQL embedida en ATTACH '...' interpreta backslashes con quirks segun
// version. Forzamos '/' siempre para evitar sorpresas en Windows.
static std::string normalize_path(std::string p) {
for (char& c : p) if (c == '\\') c = '/';
return p;
}
std::string tableview_resolve_path(const char* ops_db, const char* maybe_rel) {
if (!maybe_rel) return "";
if (is_absolute(maybe_rel)) return maybe_rel;
if (is_absolute(maybe_rel)) return normalize_path(maybe_rel);
std::string base = dirname_of(ops_db);
if (base.empty()) base = ".";
return base + "/" + maybe_rel;
return normalize_path(base + "/" + maybe_rel);
}
bool tableview_refresh_counts(const char* ops_db,