diff --git a/main.cpp b/main.cpp index 7de09a1..76faab4 100644 --- a/main.cpp +++ b/main.cpp @@ -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 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() diff --git a/tableview.cpp b/tableview.cpp index 6e94203..3fbcf88 100644 --- a/tableview.cpp +++ b/tableview.cpp @@ -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, diff --git a/views.cpp b/views.cpp index d0fb5a7..beda223 100644 --- a/views.cpp +++ b/views.cpp @@ -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 diff --git a/views.h b/views.h index 62d4024..af42af3 100644 --- a/views.h +++ b/views.h @@ -161,6 +161,7 @@ struct AppState { std::vector 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 table_windows;