1c4a4b9259
Cinco funciones nuevas para soportar DuckDB como fuente de verdad del project osint:
Grupo duckdb (escritura, complementan a duckdb_query_readonly):
- duckdb_execute_py_infra (impure): ejecuta INSERT/UPDATE/DELETE/DDL en read-write, commit, {status,rowcount}. 6 tests.
- duckdb_upsert_py_infra (impure): UPSERT ON CONFLICT actualizando solo update_cols → ownership selectivo (un re-upsert no pisa columnas excluidas). 7 tests.
Grupo dav (libretas de contactos + vCard multi-valor):
- dav_make_addressbook_py_infra (impure): crea una libreta CardDAV nueva via extended MKCOL (RFC 5689). Idempotente. 12 tests.
- dav_list_addressbooks_py_infra (impure): lista las libretas del contacts-home (PROPFIND Depth:1). 7 tests.
- build_vcard_py_core (pure): serializa un contacto a vCard 3.0 multi-valor (N TEL/EMAIL/ADR + X-OSINT-*). 5 tests.
Paginas de capacidad duckdb.md y dav.md actualizadas.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.8 KiB
4.8 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_execute | function | py | infra | 1.0.0 | impure | def duckdb_execute(db_path: str, sql: str, params: list = None) -> dict | Ejecuta UNA sentencia de escritura (INSERT/UPDATE/DELETE/DDL) contra una base DuckDB abierta en conexion read-write (duckdb.connect(db_path)), hace commit y cierra siempre en try/finally. En modo escritura DuckDB crea el archivo si no existe. Es el primitivo de escritura del grupo duckdb; complementa a duckdb_query_readonly_py_infra (solo lectura). Usa parametros posicionales con el marcador '?'. Devuelve un dict sin lanzar (estilo del grupo): {status:'ok', rowcount} en exito y {status:'error', error} en fallo. rowcount es el numero de filas afectadas; DuckDB no expone un rowcount fiable (siempre -1) pero tras INSERT/UPDATE/DELETE el fetchall() del cursor devuelve [(n,)] de donde se extrae; para DDL u operaciones sin filas queda en -1 sin fallar. Depende del paquete duckdb (1.5.2 en python/.venv). |
|
false | error_py_core |
|
|
dict. En exito: {status:'ok', rowcount:int} donde rowcount es el numero de filas afectadas (o -1 cuando la sentencia no reporta filas, p.ej. DDL). En error (sin lanzar): {status:'error', error:str}. | true |
|
python/functions/infra/duckdb_execute_test.py | python/functions/infra/duckdb_execute.py |
Ejemplo
import sys
sys.path.insert(0, "python/functions")
from infra.duckdb_execute import duckdb_execute
db = "/tmp/eventos.duckdb"
# DDL: crear la tabla (la base se crea sola si no existia).
print(duckdb_execute(db, "CREATE TABLE eventos (id INTEGER, tipo VARCHAR)"))
# {'status': 'ok', 'rowcount': -1} (DDL no reporta filas)
# DML: insertar con parametros posicionales.
res = duckdb_execute(
db,
"INSERT INTO eventos VALUES (?, ?), (?, ?)",
params=[1, "login", 2, "logout"],
)
print(res)
# {'status': 'ok', 'rowcount': 2}
# UPDATE.
print(duckdb_execute(db, "UPDATE eventos SET tipo = ? WHERE id = ?", params=["signin", 1]))
# {'status': 'ok', 'rowcount': 1}
Cuando usarla
Cuando un service single-writer necesita escribir DDL/DML en su DuckDB: crear o
migrar tablas, insertar registros nuevos, actualizar estado o borrar filas en un
archivo DuckDB que ese proceso posee. Es la mitad de escritura del grupo duckdb:
usa duckdb_query_readonly_py_infra para leer (sin riesgo de modificar la base) y
duckdb_execute_py_infra para escribir con commit. El dict de salida con
rowcount es directamente serializable a JSON para pasarlo al siguiente paso de
una composicion.
Gotchas
- Escritura real de un archivo en disco (impura). Abre en modo read-write y hace
commit; cualquier fallo se devuelve como
{status:'error', ...}, nunca se lanza. - DuckDB es single-writer: solo un proceso puede tener la base abierta en
escritura a la vez. Si otro proceso ya la tiene abierta en write,
connectfalla con un error de lock (Could not set lock on file ...) que se devuelve como{status:'error', ...}. Diseña el acceso para que un unico proceso sea el escritor; los lectores deben usarduckdb_query_readonly(read_only=True). rowcountno es fiable en todos los casos. DuckDB no expone uncursor.rowcountutil (siempre devuelve -1); esta funcion lee el conteo delfetchall()que DuckDB emite tras INSERT/UPDATE/DELETE ([(n,)]). Para DDL (CREATE/DROP/ALTER) y operaciones que no reportan filas,rowcountqueda en-1a proposito: NO trates-1como error.- Ejecuta UNA sentencia por llamada (
con.execute(sql, params)). No es para scripts multi-statement separados por;; para eso encadena varias llamadas o usa una funcion/pipeline dedicada. - Los parametros van en
paramscon el marcador?, nunca interpolados en el string del SQL (previene inyeccion). - A diferencia del modo read-only, este modo crea el archivo si no existe. Un
db_pathcon un directorio padre inexistente si falla y se reporta como error.