feat(table-node): DuckDB foundation + render colapsado (issue 0010)
- tableview.{h,cpp}: capa C sobre DuckDB v1.1.3.
* tableview_smoke_test (SELECT 42).
* tableview_count (con sql_filter opcional).
* tableview_page (LEFT JOIN sobre ops.entities via ATTACH para flag promoted).
* tableview_create (inserta entidad type_ref='Table' con metadata pointer).
* tableview_refresh_counts (lee Table entities, count cada DuckDB y cachea
por user_data hash).
* tableview_resolve_path (rel a dirname(ops_db) o absoluto).
- AppState::table_node_counts cache, refrescado tras load_input y mutaciones.
- views_table_overlay: rectangulo redondeado overlay ("Table N") encima
de cada nodo type_ref='Table'. Sigue camara via cam_x/cam_y/zoom.
- main.cpp:
* --test-duckdb <path> smoke (SELECT 42).
* --test-tableview <path> bulk test (1M rows count + page offset).
* Refresh de counts tras load + reload_after_mutation.
* Llamada a views_table_overlay despues de graph_labels_draw.
- CMakeLists.txt: link DuckDB::DuckDB + duckdb_copy_runtime.
Smoke tests:
- 1M rows count + page(offset=500k, limit=10) en 0.65 s end-to-end.
- Operations.db con un nodo Table apuntando a duckdb 1M filas: refresh
reporta correctamente "1 tables, 1000000 total rows".
This commit is contained in:
@@ -28,6 +28,9 @@
|
||||
|
||||
#include "../../../../cpp/vendor/sqlite3/sqlite3.h"
|
||||
|
||||
#include "tableview.h"
|
||||
#include "duckdb.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -253,6 +256,16 @@ static bool load_input() {
|
||||
ge::views_reset_visibility(g_app);
|
||||
ge::views_apply_visibility(g_app);
|
||||
|
||||
// Cache de conteos de Table nodes (issue 0010).
|
||||
if (g_input.uri) {
|
||||
ge::tableview_refresh_counts(g_input.uri, &g_app.table_node_counts);
|
||||
int64_t total_rows = 0;
|
||||
for (auto& kv : g_app.table_node_counts) total_rows += kv.second;
|
||||
std::fprintf(stdout,
|
||||
"[graph_explorer] table counts refreshed: %zu tables, %lld total rows\n",
|
||||
g_app.table_node_counts.size(), (long long)total_rows);
|
||||
}
|
||||
|
||||
// Cache de la vista tabla (issue 0004) — pull bulk + neighbors desde grafo.
|
||||
{
|
||||
std::vector<ge::EntityRowSnapshot> snap;
|
||||
@@ -689,6 +702,9 @@ static void render() {
|
||||
ge::views_reset_visibility(g_app);
|
||||
ge::views_apply_visibility(g_app);
|
||||
|
||||
// Refresh Table node counts (issue 0010).
|
||||
ge::tableview_refresh_counts(g_input.uri, &g_app.table_node_counts);
|
||||
|
||||
// Refresh table cache (issue 0004).
|
||||
std::vector<ge::EntityRowSnapshot> snap;
|
||||
if (ge::entity_list_rows(g_input.uri, &snap)) {
|
||||
@@ -941,6 +957,8 @@ static void render() {
|
||||
graph::graph_labels_draw(g_graph, g_viewport, g_label_policy,
|
||||
&get_label_cb, nullptr);
|
||||
}
|
||||
// Table node overlay (issue 0010) — encima de las labels.
|
||||
ge::views_table_overlay(g_app);
|
||||
}
|
||||
ImGui::End();
|
||||
} else {
|
||||
@@ -997,7 +1015,9 @@ static void usage() {
|
||||
" graph_explorer --types <types.yaml>\n"
|
||||
" graph_explorer --layout force|grid|circular|radial|hierarchical|fixed\n"
|
||||
" graph_explorer --project <slug>\n"
|
||||
" graph_explorer --test-types-yaml <path> (load+save+reload smoke test)\n");
|
||||
" graph_explorer --test-types-yaml <path> (load+save+reload smoke test)\n"
|
||||
" graph_explorer --test-duckdb <path> (open + SELECT 42 smoke test)\n"
|
||||
" graph_explorer --test-tableview <path> (1M rows count + page test)\n");
|
||||
}
|
||||
|
||||
// Smoke test del parser+writer (issue 0005 round-trip): carga `path`,
|
||||
@@ -1091,6 +1111,59 @@ int main(int argc, char** argv) {
|
||||
project_arg = argv[++i];
|
||||
} else if (std::strcmp(a, "--test-types-yaml") == 0 && i + 1 < argc) {
|
||||
return test_types_yaml_roundtrip(argv[++i]);
|
||||
} else if (std::strcmp(a, "--test-duckdb") == 0 && i + 1 < argc) {
|
||||
const char* p = argv[++i];
|
||||
if (!ge::tableview_smoke_test(p)) {
|
||||
std::fprintf(stderr, "[duckdb] smoke test FAILED for %s\n", p);
|
||||
return 2;
|
||||
}
|
||||
std::fprintf(stdout, "[duckdb] smoke test OK (SELECT 42 -> 42) on %s\n", p);
|
||||
return 0;
|
||||
} else if (std::strcmp(a, "--test-tableview") == 0 && i + 1 < argc) {
|
||||
// Crea 1M filas en duckdb_path/people, cuenta y pagina.
|
||||
const char* p = argv[++i];
|
||||
std::remove(p); // empezar desde cero
|
||||
duckdb_database db = nullptr;
|
||||
duckdb_connection cn = nullptr;
|
||||
if (duckdb_open(p, &db) == DuckDBError) { std::fprintf(stderr, "open fail\n"); return 2; }
|
||||
duckdb_connect(db, &cn);
|
||||
duckdb_result r;
|
||||
if (duckdb_query(cn,
|
||||
"CREATE TABLE people AS "
|
||||
"SELECT range AS id, 'name_' || CAST(range AS VARCHAR) AS name, "
|
||||
" (range * 7) % 100 AS age FROM range(1000000)", &r) == DuckDBError) {
|
||||
std::fprintf(stderr, "create fail: %s\n",
|
||||
duckdb_result_error(&r) ? duckdb_result_error(&r) : "?");
|
||||
duckdb_destroy_result(&r);
|
||||
duckdb_disconnect(&cn); duckdb_close(&db); return 2;
|
||||
}
|
||||
duckdb_destroy_result(&r);
|
||||
duckdb_disconnect(&cn); duckdb_close(&db);
|
||||
|
||||
int64_t total = 0;
|
||||
if (!ge::tableview_count(p, "people", nullptr, &total) || total != 1000000) {
|
||||
std::fprintf(stderr, "[tableview_count] expected 1000000, got %lld\n",
|
||||
(long long)total);
|
||||
return 2;
|
||||
}
|
||||
std::vector<std::string> cols = { "name", "age" };
|
||||
std::vector<ge::TablePageRow> page;
|
||||
if (!ge::tableview_page(p, "people", "id", cols, nullptr,
|
||||
nullptr, nullptr, 500000, 10, &page)) {
|
||||
std::fprintf(stderr, "[tableview_page] failed\n");
|
||||
return 2;
|
||||
}
|
||||
if (page.size() != 10) {
|
||||
std::fprintf(stderr, "[tableview_page] expected 10 rows, got %zu\n",
|
||||
page.size());
|
||||
return 2;
|
||||
}
|
||||
std::fprintf(stdout,
|
||||
"[tableview] OK — count=%lld, page[0]={id=%s, name=%s, age=%s}\n",
|
||||
(long long)total, page[0].id.c_str(),
|
||||
page[0].values.size() > 0 ? page[0].values[0].c_str() : "",
|
||||
page[0].values.size() > 1 ? page[0].values[1].c_str() : "");
|
||||
return 0;
|
||||
} else if (std::strcmp(a, "--help") == 0 || std::strcmp(a, "-h") == 0) {
|
||||
usage();
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user