Files
egutierrez 082008bc00 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".
2026-05-01 01:24:43 +02:00

3.3 KiB

id, title, status, priority, created, revised, completed, depends_on
id title status priority created revised completed depends_on
0010 Nodo tabla — DuckDB foundation + render colapsado completed high 2026-04-30 2026-05-01 2026-05-01
0004
0005
0008

Objetivo

Tier de almacenamiento tabular para nodos Table que pueden contener millones de filas sin saturar operations.db ni el grafo. Las "filas" viven en un .duckdb por proyecto (o por tabla) y se promueven a entidades reales del grafo solo cuando se necesita interactuar con ellas individualmente (relaciones, edicion, etc.).

Esta issue cubre fase 1 — vendoring de DuckDB, funciones tableview_* core, y render colapsado del nodo Table en el viewport. La fase 2 (UI expandida, paginacion, promote/demote, ingesta CSV/Parquet) va en issue 0011.

Modelo de datos

Dos tiers por proyecto:

projects/<proj>/apps/graph_explorer/
  operations.db                # SQLite — grafo (entities + relations + filas promovidas)
  tables/
    <slug>.duckdb              # DuckDB — bulk tabular (millones de filas)

El nodo Table es una entidad normal con type_ref = 'Table' y metadata que apunta a su dataset DuckDB. No contiene filas internamente — es una vista.

{
  "duckdb_path":  "tables/sospechosos.duckdb",
  "table_name":   "people",
  "row_type":     "Person",
  "id_column":    "id",
  "label_column": "name",
  "columns":      ["name","age","email"],
  "filter_sql":   "",
  "expanded":     false
}

Una fila promovida es una entidad en operations.db con metadata de origen:

{
  "source": {
    "duckdb": "tables/sospechosos.duckdb",
    "table":  "people",
    "row_id": "p_42"
  },
  "name": "Ana Lopez",
  "age":  33
}

Cambios en codigo

  • Vendor DuckDB en cpp/vendor/duckdb/ (amalgamation o precompiled). Add library en cpp/CMakeLists.txt.
  • Nuevo paquete cpp/functions/duck/:
    • duck_open(path) -> duckdb_database (con duckdb_open + duckdb_connect).
    • duck_query(conn, sql, params) -> result wrapper.
  • entity_ops (o tableview.{h,cpp} en la app) — funciones a nivel de app:
    • tableview_create(ops_db, duckdb_path, duck_table, row_type, char* out_id) crea entidad Table con metadata + commit en operations.db.
    • tableview_count(duckdb_path, sql_filter, int64_t* out).
    • tableview_page(duckdb_path, sql_filter, offset, limit, vector<TablePageRow>* out).
    • TablePageRow lleva los campos del columns[] resueltos a string + promoted (LEFT JOIN contra ops.entities).
  • graph_load_from_operations: filtrar relaciones CONTAINS_ROW (heredado del modelo viejo, ya no se emiten pero por si se topa con dbs antiguas).
  • views.cpp:
    • Detectar nodos type_ref == "Table" al renderizar etiquetas/contadores.
    • Overlay con ImGui::GetForegroundDrawList() por cada nodo Table: rectangulo redondeado + label "Table · N filas".

Definicion de hecho

  • DuckDB compila y linka en linux + windows (cmake target).
  • Smoke test: abrir un .duckdb vacio, crear tabla con 1M filas (CTAS desde range), correr SELECT COUNT(*) < 100 ms.
  • tableview_create + tableview_count + tableview_page con tests.
  • Un nodo type_ref='Table' en el grafo se renderiza con un cuadrado overlay encima del circulo GPU, con contador de filas obtenido por tableview_count.
  • El contador refresca al recargar el grafo o tras un INSERT en su DuckDB.