e3c8979e8d
- cmd/fn/doctor.go - cmd/fn/main.go - cpp/apps/primitives_gallery/playground/tables/CMakeLists.txt - cpp/apps/primitives_gallery/playground/tables/data_table.cpp - cpp/apps/primitives_gallery/playground/tables/data_table_logic.cpp - cpp/apps/primitives_gallery/playground/tables/data_table_logic.h - cpp/apps/primitives_gallery/playground/tables/self_test.cpp - cpp/apps/primitives_gallery/playground/tables/tql.cpp - cpp/apps/primitives_gallery/playground/tables/viz.cpp - cpp/apps/primitives_gallery/playground/tables/viz.h - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.2 KiB
4.2 KiB
id, title, status, priority, created, closed, related_components
| id | title | status | priority | created | closed | related_components | |||
|---|---|---|---|---|---|---|---|---|---|
| 0078 | tables playground — joins MBQL-style (fase 9) | done | medium | 2026-05-12 | 2026-05-12 |
|
Contexto
Fase 9 del roadmap del tables playground. Hoy render() acepta un solo input
table. MBQL permite :joins para combinar varias tablas en una sola query.
Queremos lo mismo en TQL.
Roadmap restante tras esta fase:
- 10: drill-through extendido
- 11: LLM API ("Ask AI")
- 12: promote a registry + migrar apps C++
Diseño (referencia MBQL)
MBQL :joins:
:joins [{:source-table 2
:alias "orders"
:condition [:= [:field "user_id"] [:field "user_id" {:join-alias "orders"}]]
:strategy :left-join
:fields :all}]
Adaptacion a nuestro modelo:
- Sin DB ids — usamos nombres de inputs pasados en runtime.
- Soportar multi-key composite via vector de pares.
- Strategies:
left|inner|right|full. - Fields:
all|none| lista.
Cambios
Tipos / API
struct TableInput { string name; vector<string> headers; vector<ColumnType> types; vector<const char*> cells; int rows; int cols; }struct Join { string alias; string source; vector<pair<string,string>> on; JoinStrategy strategy; vector<string> fields; }endata_table_logic.h.State.joins: vector<Join>(antes de stages[0]).render()signature extendida:void render(const char* id, const char* const* headers, int col_count, const char* const* cells, int row_count, State& st, const ColumnType* declared_types, const std::vector<TableInput>* joinables = nullptr);
Logica pura
join_tables_cpp_core(left, right, alias, on, strategy, fields) -> StageOutput.- Tests: left/inner/right/full join + multi-key + NULL handling (left propaga
""). - Pre-pipeline: si
state.joinsno vacio, materializar tabla joined (recorriendo joinables[]) ANTES de aplicar stage 0. - Headers post-join prefijados:
alias.col(preserva originales del main).
UI
- Chip row "Joins:" debajo de "Filters:". + button abre popup con:
- Combo alias (auto-sugerido) + combo source (nombre de input)
- Strategy combo
- on[] list: par left-col / right-col con + para añadir mas pares (multi-key)
- Fields radio: all / none / pick
- Chip muestra
alias <- source on left=right (left-join). Right-click edit, X borrar. - CONDICIONAL: la fila de joins solo se renderiza si
joinables != nullptr && !joinables->empty(). Sin tablas extra → no UI de joins, no se pierde espacio en apps que solo pasan una tabla.
TQL
Nuevo bloque root-level:
return {
version = 1,
display = "table",
joins = {
{alias = "orders", source = "orders_tbl",
on = { {"user_id", "user_id"} },
strategy = "left", fields = "all"},
},
stages = { ... },
columns = { ... },
views = { ... },
}
emit/apply round-trip + tests.
Lua engine
YA aplicado preempt (2026-05-12): ident_cont acepta . para parsear
[alias.col] post-join sin colision.
compute_stage / find_orig_col
No requieren cambios — operan sobre strings de col names. Aceptan alias.col directamente.
extra_panels
No requieren cambios — ven el StageOutput post-join automaticamente.
Pasos de implementacion
join_tables_cpp_core(pure) + tests unit.TableInputstruct +Joinstruct +State.joins.render()signature extendida (defaultnullptr, back-compat).- Pre-pipeline materialize join cuando
state.joinsno vacio. - UI chip row (condicional a joinables disponibles).
- TQL emit/apply joins + tests round-trip.
- Lua
[alias.col]resolver test (la sintaxis ya parsea). - Actualizar tests phase9: 4 join types + multi-key + NULL + TQL round-trip.
No-objetivos (esta fase)
- Subqueries / source-query MBQL — no aplicable, las inputs ya estan materializadas.
- Joins recursivos — flat list, sin chains internos.
- Outer joins con conditions no-igualdad (
>,<, range) — solo=por ahora.
Riesgos
- Performance con tablas grandes — hash join sobre key cols. Para v1, nested loop con hash map sobre right table es suficiente.
- Memoria — joined table puede ser N*M en worst case. Documentar.