merge: quick/page-empty-after-promote — paths normalizados + error visible

This commit is contained in:
2026-05-01 17:02:22 +02:00
4 changed files with 47 additions and 10 deletions
+23 -5
View File
@@ -869,9 +869,19 @@ static void render() {
auto& w = kv.second;
if (!w.page_dirty) continue;
const auto& m = w.meta;
ge::tableview_count(m.duckdb_path_abs.c_str(), m.table_name.c_str(),
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
&w.total_rows);
w.last_error.clear();
bool ok_count = ge::tableview_count(m.duckdb_path_abs.c_str(),
m.table_name.c_str(),
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
&w.total_rows);
if (!ok_count) {
char buf[512];
std::snprintf(buf, sizeof(buf),
"count failed | duckdb=%s table=%s",
m.duckdb_path_abs.c_str(), m.table_name.c_str());
w.last_error = buf;
std::fprintf(stderr, "[graph_explorer] %s\n", buf);
}
if (m.columns.empty()) {
std::vector<std::string> cols;
if (ge::tableview_list_columns(m.duckdb_path_abs.c_str(),
@@ -881,11 +891,19 @@ static void render() {
w.meta.columns = cols;
}
}
ge::tableview_page(m.duckdb_path_abs.c_str(), m.table_name.c_str(),
m.id_column.c_str(), w.meta.columns,
bool ok_page = ge::tableview_page(m.duckdb_path_abs.c_str(),
m.table_name.c_str(), m.id_column.c_str(),
w.meta.columns,
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
g_input_path.c_str(), m.row_type.c_str(),
w.offset, 200, &w.page);
if (!ok_page && w.last_error.empty()) {
char buf[256];
std::snprintf(buf, sizeof(buf),
"page query failed | offset=%lld limit=200", (long long)w.offset);
w.last_error = buf;
std::fprintf(stderr, "[graph_explorer] %s\n", buf);
}
w.page_dirty = false;
}
if (g_app.want_promote_row && !g_app.promote_table_id.empty()
+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,
+4
View File
@@ -1861,6 +1861,10 @@ void views_table_window(AppState& app) {
ImGui::TextDisabled("%s · %s · %lld rows",
m.duckdb_path.c_str(), m.table_name.c_str(),
(long long)w.total_rows);
if (!w.last_error.empty()) {
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
"ERROR: %s", w.last_error.c_str());
}
ImGui::Separator();
// Tabla
+1
View File
@@ -161,6 +161,7 @@ struct AppState {
std::vector<TablePageRow> page;
bool page_dirty = true;
bool open = true; // bound a ImGui::Begin
std::string last_error; // ultimo error de query (vacio = OK)
};
std::unordered_map<std::string, TableWindowState> table_windows;