sqlite.h


Registrar una retrollamada para manipular errores SQLITE_BUSY

int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);

Esta función asigna una función de retrollamada que podrá ser invocada cada vez se haga un intento de abrir una tabla de una base de datos que haya sido bloqueada por otro hilo o proceso.

Si la retrollamada es NULL, entonces se devuelve SQLITE_BUSY o SQLITE_IOERR_BLOCKED inmediatamente después de que se encuentre un bloqueo. Si la retrollamada no es NULL, entonces será llamada con dos argumentos.

El primer argumento a la retrollamada es una copia del puntero void* que será el tercer argumento a sqlite3_busy_handler(). El segundo argumento es el número de veces que el manipulador de ocupado ha sido invocado para este evento de bloqueo. Si la retrollamada retorna 0, entonces no se harán intentos adicionales para acceder a la base de datos y se retornará SQLITE_BUSY o SQLITE_IOERR_BLOCKED. Si la retrollamada retorna con un valor distinto de cero, entonces se hacer otro intento de abrir la base de datos para lectura y el ciclo se repite.

La presencia de un manipulador de ocupado no garantiza que sea invocada cuando haya una contención de bloqueo. Si SQLite determina que invocar al manipulador puede provocar un punto muerto, seguirá adelante y retornará SQLITE_BUSY o SQLITE_IOERR_BLOCKED en lugar de invodar al manipupador. Consideremos un escenario donde un proceso mantiene un bloqueo de lectura que se está intentando promocionar a un bloqueo reservado y un segundo proceso está manteniendo un bloqueo reservado que se intenta promocionar a un bloqueo exclusivo. El primer proceso no puede proceder porque está siendo bloqueado por el segundo, y el segundo no puede proceder porque está siendo bloqueado por el primero. Si los dos procesos invocan a los manipuladores, ninguno hará ningún progreso. Por lo tanto, SQLite retornará SQLITE_BUSY al primer proceso, con la esperanza que que eso induzca al primer proceso a liberar su bloqueo de lectura y permita al segundo proceso continuar.

La retrollamada de ocupado por defecto es NULL.

El error SQLITE_BUSY se convierte a SQLITE_IOERR_BLOCKED cuando SQLite está en medio de una transacción larga donde todos los cambios no pueden ser introducidos en la caché de memoria. SQLite podrá seguir manteniendo un bloqueo RESERVED en el fichero de base de datos, pero necesitará promocionar ese bloqueo a EXCLUSIVE para que pueda seguir volcando páginas de caché en la base de datos sin perjudicar a lectores concurrentes. Si no es capaz de promover el bloqueo, entonces la caché de memoria quedará en un estado incoherente y el error se promueve desde el relativamente benigno SQLITE_BUSY al más severo SQLITE_IOERR_BLOCKED. Esta promoción de código de error fuerza un rollback automático de los cambios. Ver CorruptionFollowingBusyError para comprender por qué esto es importante.

Sólo puede haber un manipulador de ocupado definido para cada conexión de base de datos. Activar un nuevo manipulador de ocupado elimina cualquier manipulador activado previamente. Hay que tener en cuenta que llamando a sqlite3_busy_timeout() también se activa o desactiva el manipulador de ocupado.

La retrollamada de ocupado no debe realizar acciones que modifiquen la conexión de la base de datos que ha invocado el manipulador de ocupado. Tales acciones tienen como resultad un comportamiento indefinido.

Un manipulador de ocupado no debe cerrar la conexión de base de datos o la sentencia preparada que invoca al manipulador de ocupado.