Watch at EaseHistorias. Respuestas. Perspectiva.

Tecnología · GUÍA PRÁCTICA

¿SQLite RETURNING sigue bloqueando commit tras fetchone? Termine cada cursor de escritura

Recibir un ID no siempre significa que la sentencia haya terminado. Compare una fila, varias filas y dos cursores antes de repetir una escritura o descartar sus resultados.

Ilustración de ocho formas de leer RETURNING y sus resultados de commit
Ilustración original de ocho pruebas con archivos temporales en CPython3.12.14/SQLite3.53.1, con control de transacciones heredado explícito. Watch at Ease

Si COMMIT informa «SQL statements in progress» después de INSERT ... RETURNING, examine sus propios resultados de escritura pendientes antes de atribuirlo a otro proceso. Recibir un ID y terminar la sentencia son hechos distintos.

Ocho pruebas con archivos temporales muestran cuándo funciona fetchone, por qué no se puede generalizar y cómo cerrar el cursor puede permitir confirmar la escritura mientras se pierden los ID devueltos.

1. Identifique el controlador y quién gestiona la transacción

Anote Python, sqlite3.sqlite_version, el SQL exacto, cada cursor de resultados activo y la configuración de transacciones. Probamos CPython3.12.14 con SQLite3.53.1 en Linux, con autocommit=sqlite3.LEGACY_TRANSACTION_CONTROL e isolation_level="DEFERRED" explícitos. Son resultados limitados al sqlite3 estándar, no a APSW ni a un ORM.

Conserve la excepción completa y la etapa fallida. SQLite permite un COMMIT explícito con SELECT pendientes, pero las escrituras pendientes pueden impedirlo. No clasifique todo SQLITE_BUSY como contención externa ni aumente el tiempo de espera antes de revisar RETURNING en esta conexión. Tampoco cambie el modo de transacciones como atajo que confirme datos accidentalmente.

2. El éxito con una fila no es la regla general

Creamos un archivo nuevo y la tabla t(x integer) para cada caso. INSERT INTO t VALUES(1) RETURNING x sin leer bloqueó commit. En este controlador, obtener su única fila con fetchone() permitió confirmarlo. Con INSERT INTO t VALUES(1),(2),(3) RETURNING x, obtener solo una fila siguió bloqueando commit.

En otros casos de tres filas, fetchall() o fetchmany(1) repetido hasta un lote vacío permitieron confirmar; al reabrir aparecieron los tres valores. Guarde todos los ID necesarios antes de seguir. No asocie resultados y entradas suponiendo su orden: SQLite no garantiza el orden de RETURNING. Leer por lotes limita el tamaño de la lista de Python, no la salida que el motor ya ha almacenado en memoria.

3. Termine todos los cursores sin perder resultados necesarios

En el sexto caso cerramos el cursor de tres filas sin leerlo. Commit funcionó y al reabrir estaban los tres valores insertados, aunque la aplicación no había recogido ninguna fila de RETURNING. Por tanto, Cursor.close() no sustituye a rollback ni a la entrega de los ID necesarios. Úselo así solo cuando abandonar esa salida sea una decisión deliberada.

Otros dos casos mantuvieron dos cursores INSERT ... RETURNING en una conexión. Agotar el primero dejó pendiente el segundo y commit falló. Agotar ambos permitió confirmar; al reabrir aparecieron 1 y99. Conserve las referencias a los cursores en el componente que controla la transacción. No permita que una función devuelva una fila visible y deje silenciosamente otro resultado de escritura activo.

4. Verifique commit por separado de las filas devueltas

En todas las pruebas fallidas, in_transaction seguía en true inmediatamente después del intento. Después cerramos los cursores pendientes y ejecutamos rollback explícitamente; al reabrir no había filas insertadas. El resultado vacío fue consecuencia de esa limpieza deliberada, no de una cancelación automática causada por la excepción. Repetir INSERT mientras la transacción sigue activa puede añadir otra escritura.

Si necesita la salida, consuma y valide los resultados, cierre sus cursores y confirme mediante el responsable de transacciones establecido. Compruebe los valores persistidos con una conexión nueva y conserve la regresión de cardinalidad exacta. Ante errores siga la política de rollback de la aplicación: un ID recibido no basta para informar éxito. No probamos lectores concurrentes, disparadores, grandes salidas ni otros modos autocommit; diagnostíquelos aparte.

Ilustración de las diferencias entre leer filas, cerrar el cursor y confirmar
Ilustración original: entrega de resultados, fin de sentencia y commit se verifican por separado. Cerrar el cursor no cancela la inserción ejecutada. Watch at Ease