--- id: 0010 title: Nodo tabla — DuckDB foundation + render colapsado status: pending priority: high created: 2026-04-30 revised: 2026-05-01 depends_on: [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//apps/graph_explorer/ operations.db # SQLite — grafo (entities + relations + filas promovidas) tables/ .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. ```json { "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: ```json { "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* 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.