d89da1292d
- docs/capabilities/INDEX.md - docs/capabilities/obsidian.md - python/functions/core/render_markdown_table.md - python/functions/core/render_markdown_table.py - python/functions/core/render_markdown_table_test.py - python/functions/core/upsert_sentinel_block.md - python/functions/core/upsert_sentinel_block.py - python/functions/core/upsert_sentinel_block_test.py - python/functions/infra/duckdb_query_readonly.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.6 KiB
4.6 KiB
name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, params, output, tested, tests, test_file_path, file_path
| name | kind | lang | domain | version | purity | signature | description | tags | uses_functions | uses_types | returns | returns_optional | error_type | imports | params | output | tested | tests | test_file_path | file_path | |||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| duckdb_query_readonly | function | py | infra | 1.0.0 | impure | def duckdb_query_readonly(db_path: str, sql: str, params: list = None, max_rows: int = 10000) -> dict | Ejecuta una query SELECT contra una base DuckDB abierta en modo solo lectura (duckdb.connect(db_path, read_only=True)), de modo que nunca crea ni modifica la base. La conexion se cierra siempre en try/finally. Usa parametros posicionales con el marcador '?'. Devuelve un dict sin lanzar (estilo del grupo dav): {status:'ok', columns, rows, row_count, truncated} en exito y {status:'error', error} en fallo. Las filas son list[dict]. Trunca a max_rows para proteger memoria. Convierte valores no serializables: date/datetime/time a isoformat(), Decimal a float, bytes a base64, UUID a str. Depende del paquete duckdb (1.5.2 en python/.venv). |
|
false | error_py_core |
|
|
dict. En exito: {status:'ok', columns:[str,...], rows:[{col:val,...},...], row_count:int, truncated:bool}. En error (sin lanzar): {status:'error', error:str}. Los valores de las filas estan normalizados a tipos JSON-serializables. | true |
|
python/functions/infra/duckdb_query_readonly_test.py | python/functions/infra/duckdb_query_readonly.py |
Ejemplo
import sys
sys.path.insert(0, "python/functions")
import duckdb
from infra.duckdb_query_readonly import duckdb_query_readonly
# Preparamos una base de ejemplo (esto seria un proceso separado en la realidad).
db = "/tmp/ventas.duckdb"
con = duckdb.connect(db)
con.execute("CREATE TABLE ventas (id INTEGER, region VARCHAR, total DECIMAL(10,2))")
con.execute("INSERT INTO ventas VALUES (1, 'norte', 120.50), (2, 'sur', 80.00), (3, 'norte', 45.25)")
con.close()
# Lectura solo-lectura con parametro posicional.
res = duckdb_query_readonly(
db,
"SELECT region, SUM(total) AS total FROM ventas WHERE region = ? GROUP BY region",
params=["norte"],
)
print(res["status"]) # ok
print(res["columns"]) # ['region', 'total']
print(res["rows"]) # [{'region': 'norte', 'total': 165.75}]
print(res["truncated"]) # False
Cuando usarla
Cuando necesitas leer datos de un archivo DuckDB sin riesgo de modificarlo: inspeccionar una base materializada, validar el resultado de un pipeline, alimentar un dashboard o un report, o consultar tablas/Parquet exportados por otra funcion del registry. El modo read_only garantiza que la consulta nunca crea ni altera la base, y el dict de salida es directamente serializable a JSON para pasarlo al siguiente paso de una composicion.
Gotchas
- Lectura real de un archivo en disco (impura). El modo
read_only=Trueexige que el archivo ya exista: a diferencia del modo escritura, no crea la base. Sidb_pathno existe, devuelve{status:'error', error:...}. - Conflicto de lock: si otro proceso tiene la misma base abierta en escritura
con una version de DuckDB distinta, la apertura puede fallar (DuckDB no permite
abrir un archivo bloqueado por otra version del motor). El error se devuelve
como
{status:'error', ...}, no se lanza. max_rowsprotege la memoria: una query que devuelve millones de filas se trunca amax_rowsy marcatruncated=True. Si necesitas todas las filas, pagina con LIMIT/OFFSET en el SQL o subemax_rowsconscientemente.- Los parametros van en
paramscon el marcador?, nunca interpolados en el string del SQL (previene inyeccion). - Valores no JSON-serializables se normalizan en la salida: date/datetime/time a
isoformat(), Decimal a float (puede haber perdida de precision frente al decimal exacto), bytes a base64 y UUID a str.