Los 14 hallazgos H1-H14 del benchmark estan corregidos y verificados con re-corrida.
Commits: caf8c25d (S), c4cff5ed (render H4/H9), e142ef02 (comportamiento H2/H3/H6/H7/H8/H10/H11).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EDA relational: precisión de FK inference (falsos positivos) + filtrar VIEWs + test ATTACH
resuelto
bugfix
registry-quality
registry-only
alta
0173
0174
0176
0177
2026-06-29
2026-06-29
eda
datascience
infer_fk_containment_duckdb
build_join_graph
profile_database
duckdb
benchmark
0175 — EDA relational: precisión de FK inference + filtrar VIEWs
Contexto
El benchmark /eda (29/06/2026, temp/eda_benchmark/EVALUATION.md) confirmó que la inferencia de
claves foráneas a nivel de base es inútil por falsos positivos masivos y que las VISTAS se
perfilan como tablas base. El join graph resultante necesita filtrado manual para ser legible.
Hallazgos cubiertos:
Hallazgo
Severidad
Evidencia del benchmark
H3 — FK inference por contención: 10-20× falsos positivos
crítico
chinook 111 candidatas vs ~11 reales; sakila 565 vs ~30. Casos absurdos: InvoiceLine.Quantity→Album.AlbumId, Genre.GenreId→{Album,Artist,Customer,…}
H10 — coste relacional gastado en computar FK falsas
medio
sakila 31.82s: la mayoría en INTERSECT de los 565 pares candidatos, casi todos falsos
H14 — bug sqlite_master does not exist tras ATTACH (ya parcheado, falta test)
bajo (resuelto)
_run.log: profile_database falló con Catalog Error: src.sqlite_master; re-run posterior ok
Causa raíz (verificada en código, READ-ONLY)
python/functions/datascience/infer_fk_containment_duckdb.py:217-285 emite una FK candidata si
inclusion(A⊆B) ≥ min_inclusiony B "parece clave" (unicidad ≥0.95). No usa el nombre de
la columna, que es la señal más fuerte de FK (AlbumId→Album.AlbumId), ni excluye columnas
no-clave (cantidades, importes) como ORIGEN. Enteros pequeños (GenreId 1..25) están contenidos
en casi todo → ruido.
python/functions/pipelines/profile_database.py:155-159 lista tablas con duckdb_list_tables
sin filtrar table_type → perfila VIEWs y tablas FTS como base (H5), lo que infla el universo de
pares y multiplica las FK falsas (relaciona H10).
H10 es el mismo cambio que H3: filtrar candidatos por nombre antes del INTERSECT reduce
pares (más rápido) y falsos positivos (más preciso) a la vez.
Tareas
H3+H10 — señal de nombre en infer_fk_containment_duckdb.py:217-285: antes de lanzar el
INTERSECT, exigir coincidencia/patrón de nombre entre origen y destino (from_col casa con
to_table/to_col, patrón <X>Id → <X>.<X>Id; case-insensitive). Excluir como ORIGEN columnas
claramente no-clave (cantidades, importes, flags) por heurística de nombre/tipo. Esto poda el
O(tablas²×columnas²) y elimina la mayoría de los falsos positivos. Validar mejor la cardinalidad
(los 1:1 imposibles del benchmark).
H5 — filtrar VIEWs antes de perfilar e inferir FK: filtrar table_type='BASE TABLE' vía
information_schema.tables / duckdb_tables(). Decidir (a confirmar al implementar) si el filtro
va como flag nuevo en duckdb_list_tables (infra, reutilizable) o en profile_database.py tras
listar. Preferir el flag en duckdb_list_tables si no rompe consumidores.
H3 — propagar al join graph: verificar que build_join_graph.py recibe la lista ya filtrada
y que el diagrama Mermaid resultante es legible (sin nodos VIEW ni aristas espurias).
H14 — test de regresión: añadir test (en profile_database_test.py o
infer_fk_containment_duckdb_test.py) que haga ATTACH de una base SQLite pequeña en DuckDB y
perfile, confirmando que se usa information_schema/duckdb_tables() y nunca sqlite_master.
(A confirmar: localizar la función que hace el ATTACH —probablemente summarize_table_duckdb.py
o una primitiva infra duckdb_*— para cubrirla.)
Tests: casos sintéticos con tablas que tengan columnas tipo XId (FK real) y columnas de
cantidad contenidas en claves (falso positivo) → confirmar que solo emite las reales.
Definition of Done
Escenario
Tipo
Comando / evidencia
Resultado esperado
Golden: FK reales sin ruido
e2e
re-correr profile_database sobre chinook
~11 FK candidatas (no 111); incluyen Album.ArtistId→Artist.ArtistId, Invoice.CustomerId→Customer.CustomerId; NO incluyen InvoiceLine.Quantity→Album.AlbumId
Edge: VIEWs excluidas
e2e
re-correr profile_database sobre sakila
n_tables cuenta solo BASE TABLE (sin customer_list/film_list/…); FK candidatas ≪ 565
Edge: cantidad vs clave
unit
infer_fk_containment_duckdb_test.py con columna Quantity contenida en una clave
NO emite FK desde Quantity
Error: ATTACH SQLite
unit
test de regresión ATTACH SQLite→DuckDB
perfila sin sqlite_master does not exist; usa information_schema
Rendimiento (H10)
e2e
medir duración de profile_database sobre sakila
menor que el baseline 31.82s (menos INTERSECT)
Mecánica
—
./fn run infer_fk_containment_duckdb_py_datascience, ./fn run profile_database_py_pipelines; fn index
tests verdes; índice limpio
Re-correr el benchmark sobre chinook y sakila y confirmar que las FK reales son distinguibles del
ruido y que las VIEWs no se cuentan como tablas.
Notas
Issue derivado de temp/eda_benchmark/EDA_ISSUES.md. Tres síntomas (H3/H5/H10) con un núcleo común:
la capa de inferencia de relaciones inter-tabla. Atacarlos juntos en una rama; filtrar VIEWs reduce
el universo de pares y filtrar candidatos por nombre arregla precisión y velocidad a la vez. H14 ya
está parcheado en producción; este issue solo añade el test de regresión que faltaba.
Hermanos: 0173, 0174, 0176, 0177.
Resolucion (2026-06-29, sesion /ausente)
Resuelto y verificado con re-corrida del benchmark EDA. Commit principal: e142ef02. Detalle en reports/ausente-eda-benchmark-2026-06-29.md y temp/eda_benchmark/EVALUATION.md.