Files
fn_registry/python/functions/datascience/detect_declared_keys_duckdb.md
T
egutierrez 68f4ddabce feat(eda): capítulo RELACIONES para AutomaticEDA
Añade el capítulo `relaciones` al motor AutomaticEDA: analiza las
relaciones de clave de la tabla/base y se coloca tras `correlacion`,
antes de `modelos`, en CHAPTER_ORDER.

Capas que renderiza (solo las que aplican; None si no hay nada que decir):
- Claves declaradas: PK/FK/UNIQUE reales del esquema DuckDB, vía la nueva
  función `detect_declared_keys_duckdb` (lee `duckdb_constraints()`).
- Candidatos a clave primaria: los `key_candidates` del TableProfile.
- FK candidatas inter-tabla: reusa `infer_fk_containment_duckdb`
  (containment + señal de nombre) y `build_join_graph` (roles de nodos +
  diagrama Mermaid pegable). Solo si la fuente DuckDB tiene varias tablas.
- FK candidatas intra-tabla: heurística nombre + cardinalidad, vía la nueva
  función pura `suggest_intratable_fk_candidates`, marcada como sugerencia.

Engancha al glosario clicable los términos PK, FK, containment/inclusión y
cardinalidad (contrato §11.1) y usa Group (keep-together) para el grafo.

Funciones nuevas del registry (grupo `eda`):
- detect_declared_keys_duckdb (impure, datascience) + test.
- suggest_intratable_fk_candidates (pure, datascience) + test.

Tests: relaciones_test.py (golden intra + inter, edges, no-cut render) +
los tests de ambas funciones. Suite automatic_eda + render_automatic_eda
verde (89 passed). Golden end-to-end con el pipeline render_automatic_eda
verificado sobre titanic (intra) y una BD customers/orders (inter).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 18:15:15 +02:00

7.1 KiB

name, kind, lang, domain, version, purity, signature, description, tags, params, output, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path
name kind lang domain version purity signature description tags params output uses_functions uses_types returns returns_optional error_type imports tested tests test_file_path file_path
detect_declared_keys_duckdb function py datascience 1.0.0 impure def detect_declared_keys_duckdb(db_path: str, table: str = None) -> dict Detecta las claves DECLARADAS (constraints reales) de un schema DuckDB leyendo la table function duckdb_constraints(): extrae PRIMARY KEY, FOREIGN KEY y UNIQUE (ignora NOT NULL y CHECK) y las devuelve normalizadas con sus columnas, y para las FK con su tabla y columnas referenciadas. Con table=None procesa todas las tablas; con table='X' filtra a PK/UNIQUE de X y a FK cuyo origen es X (case-sensitive). A diferencia de infer_fk_containment_duckdb (que INFIERE FKs candidatas por containment de valores cuando el schema no las declara), esta funcion devuelve las relaciones de clave REALES del schema. Estilo dict-no-throw: nunca lanza. Parte del grupo eda (relaciones de clave).
eda
duckdb
datascience
relations
primary-key
foreign-key
schema
exploratory-data-analysis
name desc
db_path Ruta al archivo DuckDB. Debe existir (lectura read-only via duckdb_query_readonly; no se crea). Un path inexistente devuelve {status:'error', ...}.
name desc
table Si se pasa, filtra los resultados a esa tabla: incluye PRIMARY KEY y UNIQUE cuya tabla sea `table`, y FOREIGN KEY cuya tabla ORIGEN sea `table` (no la referenciada). None (default) devuelve los constraints de todas las tablas. La comparacion es case-sensitive (nombres tal cual los devuelve DuckDB).
dict dict-no-throw. En exito {status:'ok', primary_keys:[{table:str, columns:[str,...]}, ...], foreign_keys:[{table:str, columns:[str,...], referenced_table:str, referenced_columns:[str,...]}, ...], unique:[{table:str, columns:[str,...]}, ...], tables:[str,...]} donde tables es la lista ordenada de tablas (origen) que poseen al menos un constraint PK/FK/UNIQUE emitido. Solo se emiten constraints de clave: NOT NULL y CHECK se ignoran. En error {status:'error', error:str}.
duckdb_query_readonly_py_infra
false error_go_core
true
test_golden_detecta_pks_y_fk
test_golden_ignora_not_null_y_check
test_edge_filtra_por_tabla_orders
test_edge_filtra_por_tabla_customers
test_edge_unique_declarado
test_edge_sin_constraints_listas_vacias
test_error_db_inexistente_no_lanza
test_shape_resultado
python/functions/datascience/detect_declared_keys_duckdb_test.py python/functions/datascience/detect_declared_keys_duckdb.py

Ejemplo

import sys, os, duckdb
sys.path.insert(0, os.path.join("python", "functions"))
from datascience import detect_declared_keys_duckdb

# Base de ejemplo en /tmp: orders.customer_id -> customers.id (FK declarada)
path = "/tmp/declared_keys_demo.duckdb"
if os.path.exists(path):
    os.remove(path)
con = duckdb.connect(path)
con.execute("CREATE TABLE customers(id INTEGER PRIMARY KEY, name TEXT)")
con.execute(
    "CREATE TABLE orders("
    "  id INTEGER PRIMARY KEY,"
    "  customer_id INTEGER REFERENCES customers(id),"
    "  amt DOUBLE)"
)
con.close()

res = detect_declared_keys_duckdb(path)
if res["status"] == "ok":
    for pk in res["primary_keys"]:
        print(f"PK  {pk['table']}({', '.join(pk['columns'])})")
    for fk in res["foreign_keys"]:
        print(f"FK  {fk['table']}({', '.join(fk['columns'])}) -> "
              f"{fk['referenced_table']}({', '.join(fk['referenced_columns'])})")
    # PK  customers(id)
    # PK  orders(id)
    # FK  orders(customer_id) -> customers(id)
else:
    print("error:", res["error"])

# Filtrar a una tabla concreta (PK/UNIQUE de orders + FK con origen orders):
solo_orders = detect_declared_keys_duckdb(path, table="orders")
print(solo_orders["tables"])  # ['orders']

Cuando usarla

  • Cuando exploras un esquema DuckDB y quieres mostrar las relaciones de clave REALES (PK/FK/UNIQUE) que el schema ha declarado, sin inferir nada.
  • Como paso del capitulo RELACIONES del grupo eda: primero mira las claves declaradas con esta funcion; si el schema no declara FKs, complementa con infer_fk_containment_duckdb (inferencia por containment).
  • Antes de documentar o migrar un esquema, para listar el contrato de integridad referencial que el motor ya conoce.
  • Para validar que las constraints que esperas (esa FK que creaste con REFERENCES) realmente estan declaradas en la base materializada.

Gotchas

  • Impura: lee de disco via la primitiva read-only duckdb_query_readonly (no crea ni modifica la base). El db_path debe existir; un path inexistente devuelve {status:'error'} (read_only NO crea la base).
  • Requiere duckdb_constraints(): usa la table function duckdb_constraints(), disponible en DuckDB modernos (verificado en 1.5.2). En versiones antiguas sin esa funcion, la query falla y se devuelve {status:'error'}.
  • Solo claves DECLARADAS: devuelve lo que el schema declaro con PRIMARY KEY / FOREIGN KEY (... REFERENCES ...) / UNIQUE. Una tabla materializada con CREATE TABLE AS SELECT NO lleva constraints — para esos casos no habra claves que mostrar y hay que INFERIRLAS (infer_fk_containment_duckdb).
  • NOT NULL y CHECK se ignoran: duckdb_constraints() tambien emite filas NOT NULL (DuckDB genera una por cada columna PK) y CHECK; esta funcion las descarta y solo conserva PK/FK/UNIQUE.
  • Nombres case-sensitive: el filtro table='Orders' no casa con una tabla orders. Se comparan los nombres tal cual los devuelve DuckDB.
  • FK atribuida al origen: una FOREIGN KEY se atribuye a su tabla ORIGEN (el table de la entrada), no a la referenciada. El filtro table='X' trae las FK cuyo origen es X, no las que apuntan a X.
  • tables = tablas dueñas de constraints emitidos: la lista tables contiene solo las tablas que poseen al menos un PK/FK/UNIQUE en el resultado (su campo table), ordenadas. No incluye tablas referenciadas que no tengan constraint propio en la salida.
  • Columnas como listas: constraint_column_names y referenced_column_names son columnas LIST de DuckDB; en 1.5.2 llegan como listas Python. La funcion las normaliza a listas de strings con una red de seguridad por si llegaran como string.

Notas

duckdb_constraints() devuelve una fila por constraint con los campos table_name, constraint_type, constraint_column_names, referenced_table, referenced_column_names. Mapeo a la salida:

PRIMARY KEY -> primary_keys[]: {table, columns}
UNIQUE      -> unique[]:       {table, columns}
FOREIGN KEY -> foreign_keys[]: {table, columns, referenced_table, referenced_columns}
NOT NULL    -> ignorado
CHECK       -> ignorado

Para una FK, referenced_table y referenced_column_names vienen poblados; para PK/UNIQUE, referenced_table es NULL y referenced_column_names una lista vacia.

Complementa a infer_fk_containment_duckdb: esta funcion devuelve las relaciones de clave REALES del schema (declaradas); la otra INFIERE FKs candidatas por containment de valores cuando el schema no las declaro. En el capitulo RELACIONES de AutomaticEDA se usan en orden: primero las declaradas, luego la inferencia como respaldo.