chore: auto-commit (95 archivos)
- 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>
This commit is contained in:
+84
@@ -496,3 +496,87 @@ StageOutput compute_stage(const char* const* in_cells, int in_rows, int in_cols,
|
||||
| Multi-sort drag-reorder | Phase 4 |
|
||||
|
||||
Ver `cpp/apps/primitives_gallery/playground/tables/` para la implementacion del playground.
|
||||
|
||||
---
|
||||
|
||||
## SQL transpile subset (fase 11 — issue 0080)
|
||||
|
||||
TQL emite SQL DuckDB equivalente para que agentes LLM puedan generar TQL o SQL contra los mismos datos. Modulo `tql_to_sql.{h,cpp}` provee `emit_sql(State, tables)`. Mapeo MBQL-style con CTE chain `t0..tN`.
|
||||
|
||||
### Lua subset transpilable
|
||||
|
||||
Lua sigue **potente y sin limites en runtime general** (formula eval en derived cols TQL puro). El subset SOLO aplica al pedir `tql_to_sql::emit_sql()`. Fuera del subset → error compile-time con causa concreta + workaround.
|
||||
|
||||
**Permitido (transpila a SQL DuckDB):**
|
||||
|
||||
| Lua | SQL DuckDB | Ejemplo |
|
||||
|---|---|---|
|
||||
| Literales numero/string/bool/nil | mismas (`'x'`, `TRUE`, `NULL`) | `42`, `"hola"`, `nil` |
|
||||
| Col ref: `[colname]` | `"colname"` (quoted) | `[size_kb]` → `"size_kb"` |
|
||||
| Aritmetica: `+ - * / % - (unary)` | mismas | `[a] + [b] * 2` → `("a" + ("b" * 2))` |
|
||||
| Comparacion: `== ~= < <= > >=` | `= <> < <= > >=` | `[n] >= 10` → `("n" >= 10)` |
|
||||
| Logica: `and or not` | `AND OR NOT` | `[a] and [b]` → `("a" AND "b")` |
|
||||
| String concat: `..` | `\|\|` | `[a] .. "_" .. [b]` → `("a" \|\| '_' \|\| "b")` |
|
||||
| Ternary: `if A then B else C end` | `CASE WHEN A THEN B ELSE C END` | obligatorio `else` |
|
||||
| `math.floor/ceil/abs/sqrt/sin/cos/log/exp` | `floor/ceiling/abs/sqrt/sin/cos/ln/exp` | `math.floor([x])` |
|
||||
| `math.min(a,b)/max(a,b)` | `least(a,b)/greatest(a,b)` | `math.min([a], 100)` |
|
||||
| `string.upper/lower/len(s)` | `upper(s)/lower(s)/length(s)` | `string.upper([name])` |
|
||||
| `string.sub(s, i [, j])` | `substring(s, i [, j-i+1])` | `string.sub([s], 1, 3)` |
|
||||
| `tostring(x)/tonumber(x)` | `CAST(x AS VARCHAR)/CAST(x AS DOUBLE)` | `tonumber([n])` |
|
||||
| Parentesis y precedencia Lua | mismas | `(a + b) * c` |
|
||||
|
||||
**Fuera de subset (error compile-time):**
|
||||
|
||||
- Closures: `function() ... end`
|
||||
- Loops: `for/while/repeat`
|
||||
- Locals: `local x = ...`
|
||||
- Tables: `{...}`, `t[k]`, `t.field`, `table.*`
|
||||
- Multi-return, vararg `...`
|
||||
- `string.gsub/find/match/format/byte/char/rep`
|
||||
- IO/OS/debug: `io.*`, `os.*`, `debug.*`, `package`, `require`, `print`
|
||||
- Coroutines, metatables, `pcall/xpcall`, `rawget/rawset`
|
||||
- Recursion, multi-statement bodies (`;`)
|
||||
- Length operator `#`
|
||||
- Method calls `:`
|
||||
- Ternary sin else: `if A then B end` (subset requiere ambas ramas)
|
||||
|
||||
### Error message ejemplo
|
||||
|
||||
```
|
||||
SQL transpile error en derived col 'fullname':
|
||||
formula = "[first] .. ' ' .. string.gsub([last], 'X', 'Y')"
|
||||
causa: function 'string.gsub' not in SQL transpile whitelist
|
||||
ver docs/TQL.md#sql-transpile-subset
|
||||
workaround: usar TQL puro (sin SQL emit) o reescribir formula
|
||||
```
|
||||
|
||||
### Stage → SQL mapeo
|
||||
|
||||
| TQL element | SQL DuckDB |
|
||||
|---|---|
|
||||
| Stage 0 Raw | CTE `t0 AS (SELECT cols+derived FROM main_t [JOIN ...] [WHERE filters] [ORDER BY sorts])` |
|
||||
| Stage N>=1 | CTE `tN AS (SELECT breakouts+aggs FROM tN-1 [GROUP BY ...] [ORDER BY ...])` |
|
||||
| breakout `"col"` | `"col"` |
|
||||
| breakout `"col:month"` | `date_trunc('month', "col")` |
|
||||
| breakout `"col:year/week/day/hour"` | `date_trunc('year/week/day/hour', "col")` |
|
||||
| Aggregation Count | `COUNT(*)` |
|
||||
| Aggregation Sum/Avg/Min/Max/Stddev | `SUM/AVG/MIN/MAX/STDDEV("col")` |
|
||||
| Aggregation Distinct | `COUNT(DISTINCT "col")` |
|
||||
| Aggregation Median/P25/P75/P90/P99 | `quantile_cont("col", p)` |
|
||||
| Aggregation Percentile p | `quantile_cont("col", p)` |
|
||||
| Filter Op::Eq/Neq/Gt/Gte/Lt/Lte | `"col" = ?` etc (params bound) |
|
||||
| Filter Op::Contains | `"col" LIKE '%v%'` (param `%v%`) |
|
||||
| Filter Op::StartsWith / EndsWith | `LIKE 'v%'` / `LIKE '%v'` |
|
||||
| Sort `{desc, "col"}` | `ORDER BY "col" DESC` |
|
||||
| Join Left/Inner/Right/Full | `LEFT/INNER/RIGHT/FULL OUTER JOIN ... ON ...` |
|
||||
| Join multi-key `on={{l1,r1},{l2,r2}}` | `ON l.l1 = r.r1 AND l.l2 = r.r2` |
|
||||
| Join fields | cols `alias.field AS "alias.field"` |
|
||||
| `main_source` | `FROM "main_source_name"` |
|
||||
|
||||
### Doctrina (Metabase-style)
|
||||
|
||||
- **One-way:** TQL → SQL OK. SQL → TQL no soportado. Razon: traduccion inversa lossy (CTEs, window fns, set ops, lateral, correlated subqueries no caben en TQL).
|
||||
- **Output:** SQL string siempre emitible. Ejecucion requiere DuckDB linkado (build flag `FN_TQL_DUCKDB=1`, opcional).
|
||||
- **Agente flow:** TQL default. SQL solo si app linko DuckDB. UI Ask AI muestra toggle SQL solo cuando disponible.
|
||||
|
||||
Ver issue 0080 + `tql_to_sql.{h,cpp}` para implementacion.
|
||||
|
||||
Reference in New Issue
Block a user