2012-01-11 24 views
6

Tengo un sistema que se basa en la base de datos SQLite. Cada cliente tiene una base de datos local, y de vez en cuando la actualización llega desde el servidor principal, solo un pequeño archivo .db delta. La tarea es fusionarse a la base de datos local con el archivo delta, el esquema es idéntico en ambos.Error al separar la base de datos SQLite - la base de datos está bloqueada

Para la gestión de mi base de datos utilizo fmdb wrapper que se puede encontrar here. En el hilo principal, mantengo abierta la conexión a la base de datos local. El archivo delta llega en segundo plano, y quiero hacer la fusión en segundo plano para evitar que se congele cualquier interfaz de usuario que esto pueda causar.

En cuanto a la fusión, la única opción que encontré es adjuntar la base de datos delta a la base de datos local, luego insertar/actualizar las filas y finalmente separar el delta. Esto no funciona tan bien como esperaba.

Descripción Código:

  • El onDeltaGenerated método se invoca en un subproceso en segundo plano cada vez que la base de datos de delta está listo para ser procesado (llega desde el servidor y se guarda en la ubicación legible).
  • deltaDBPath es la ubicación absoluta de la base de datos delta en el sistema de archivos.
  • db referencias variables abren la conexión FMDataBase.

Código:

- (void)onDeltaGenerated:(NSNotification*)n { 
NSString* deltaDBPath = [[n userInfo] objectForKey:@"deltaPath"]; 
@synchronized(db) { 
    [db executeUpdate:@"ATTACH DATABASE ? AS delta", deltaDBPath]; 
    if ([db hadError]) { 
     NSLog(@" ****ERROR*** %d: %@", [db lastErrorCode], [db lastErrorMessage]); 
    } else { 
     NSLog(@"Delta attached from %@", deltaDBPath); 
    } 
    [db beginTransaction]; 
    BOOL update1 = NO; 
    BOOL update2 = NO; 
    BOOL transaction = NO; 
    update1 = [db executeUpdate:@"INSERT OR REPLACE INTO equipment SELECT * FROM delta.equipment"]; 
    if (!update1) { 
     NSLog(@" *** ERROR *** update 1 failed!"); 
     NSLog(@" ****ERROR*** %d: %@", [db lastErrorCode], [db lastErrorMessage]); 
    } 
    update2 = [db executeUpdate:@"INSERT OR REPLACE INTO equipmentExt SELECT * FROM delta.equipmentExt"]; 
    if (!update2) { 
     NSLog(@" *** ERROR *** update 2 failed!"); 
     NSLog(@" ****ERROR*** %d: %@", [db lastErrorCode], [db lastErrorMessage]); 
    } 
    transaction = [db commit]; 
    if (!transaction) { 
     NSLog(@" *** ERROR *** transaction failed!"); 
     NSLog(@" ****ERROR*** %d: %@", [db lastErrorCode], [db lastErrorMessage]); 
    } 
    [db executeUpdate:@"DETACH DATABASE delta"]; 
    if ([db hadError]) { 
     NSLog(@" ****ERROR*** %d: %@", [db lastErrorCode], [db lastErrorMessage]); 
    } else { 
     NSLog(@"Delta detached"); 
    } 
} 

}

Después de este método se invoca por primera vez, todo parece estar bien hasta que intento de separar la base de datos. Cuando trato de hacerlo, me sale el siguiente error:

2012-01-11 12:08:52.106 DBApp[1415:11507] Error calling sqlite3_step (1: SQL logic error or missing database) SQLITE_ERROR 
2012-01-11 12:08:52.107 DBApp[1415:11507] DB Query: DETACH delta 
2012-01-11 12:08:52.107 DBApp[1415:11507] ****ERROR*** 1: database delta is locked 

También probé a la misma, pero sin poner inserta en la transacción, el resultado es idéntico. Otra cosa fue eliminar la cláusula @synchronized, pero tampoco suerte. Mi suposición es que si falla al intentar acceder a la conexión de la base de datos local desde el hilo de fondo, ¿pero cómo se las arregla para adjuntar e insertar? Cualquier ayuda apreciada.

Editar

I trasladó el código para el hilo principal, por lo que el DB es ahora acceder desde el hilo principal solamente. El problema permanece

Edit2

Ok, así que después de intentar todo, me di por vencido en esto por un momento y luego regresó cuando la primera respuesta apareció aquí. Sorprendentemente, todo parece funcionar bien ahora, por lo que mi código debe ser correcto. Sospecho que este fue el problema con diferentes hilos que bloquean el archivo, ya que utilicé XCode, SQLiteDatabaseBrowser y mi aplicación para abrir la base de datos. Aunque el lsof mostró que el archivo no estaba bloqueado, creo que era incorrecto y que XCode o SQLiteDatabaseBrowser lo estaban bloqueando. Considero que el problema está resuelto, y la lección que se extrajo de esto no es tanto empujarlo y planificar mejor la depuración la próxima vez.

+0

¿Todos los métodos transactionUpdate y execute devuelven SÍ? – Mark

+0

Sí, lo hacen. Además, verifiqué si tal vez algún otro proceso está bloqueando la base de datos (utilicé el fusor), pero este no es el caso. Hasta ahora he estado probando en el simulador, pero ahora veo que tampoco funciona en el iPod Touch, pero falla con el mismo error. – lawicko

+0

Solo marcando: su base de datos no está almacenada en una unidad montada en NFS, ¿o sí? – jogojapan

Respuesta

1

Solo marcando: ¿se imprime correctamente el NSLog(@"Delta attached from %@", deltaDBPath); y los errores que describe ocurren después de eso?


La línea Error calling sqlite3_step (1: SQL logic error or missing database) SQLITE_ERROR es probablemente va a ser el bit más interesante para mirar.

Después de un poco de búsqueda en Google, un problema que surge es que el archivo de la base de datos puede no ser de escritura. http://www.iphonedevsdk.com/forum/iphone-sdk-development/20142-problem-insert-fmdb.html

Si la base de datos principal está actualizando está dentro de paquete de la aplicación, usted no está autorizado a modificarlo - usted debe hacer una copia en el directorio grabable Documents u otro primero.


es el error que ocurre en realidad cuando intenta y separar, o está sucediendo realmente cuando se intenta realizar la transacción INSERT OR REPLACE?

¿Debería poner otro if ([db hadError]) {… justo después de estas declaraciones para asegurarse?

+0

Hola, gracias por tomarte el tiempo de mirar esto. Actualicé mi código para poder detectar el error más fácilmente. El error ocurrió para esta línea: '[db executeUpdate: @" DETACH DATABASE delta "];'. La base de datos es escribible. Volví a ejecutar el código y funciona bien ahora! Voy a probarlo un poco más y a aceptar su respuesta si no aparece nada o si nadie viene con la idea más específica sobre qué podría ser esto. ¡Gracias! – lawicko

1

que tuviera ya

[db open]; 

otra parte?

+0

Claro, lo hice. De lo contrario, fallaría desde el principio. – lawicko

1

¿Está seguro de que restablece todas las consultas a db? Asegúrate de hacer la llamada sqlite3_reset(stmt).

+0

Desafortunadamente, ya no tengo acceso al código original, pero como escribí en las ediciones, parece ser un problema de una sola vez que desapareció mágicamente, y no tuvimos ningún otro problema con él. Pero gracias por la respuesta, estoy seguro de que volveré cuando vuelva a aparecer el error bloqueado de la base de datos. – lawicko

0

Aquí hay uno que me mordió en el (ya sabes dónde): Estaba siendo minucioso (demasiado, al parecer,) la prueba mientras construía una nueva característica que requería una base de datos ATTACHed adicional (en .NET, por camino). Así que

var i = query.ExecuteNonQuery("ATTACH DATABASE @FilePath AS `MergeDestination`;", fullPath); 
FakeCallThatDoesNothing() 
var i = query.ExecuteNonQuery("DETACH DATABASE `MergeDestination`;"); 

cuales, appearently, no permite el tiempo suficiente para que el accesorio se lleve a cabo y los resultados en tanto rascarse la cabeza.

Cuestiones relacionadas