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:
@@ -0,0 +1,95 @@
|
||||
---
|
||||
id: 0010
|
||||
title: Nodo tabla — DuckDB foundation + render colapsado
|
||||
status: completed
|
||||
priority: high
|
||||
created: 2026-04-30
|
||||
revised: 2026-05-01
|
||||
completed: 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/<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.
|
||||
|
||||
```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<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.
|
||||
Reference in New Issue
Block a user