У меня есть система, основанная на базе данных SQLite. У каждого клиента есть локальная база данных, и время от времени обновление приходит с главного сервера, только небольшой файл дельты .db. Задача - слить в локальную базу данных дельта-файл, в обоих схема идентична.
Для управления базой данных я использую оболочку fmdb, которую можно найти здесь . В основном потоке я поддерживаю соединение с локальной базой данных. Дельта-файл поступает в фоновом режиме, и я хочу выполнить слияние в фоновом режиме, чтобы избежать зависаний пользовательского интерфейса, которые это может вызвать.
Что касается самого слияния, единственный вариант, который я нашел, - это присоединить базу данных дельты к локальной базе данных, затем вставить / обновить строки и, наконец, отсоединить дельту. Это не работает так гладко, как я ожидал.
Описание кода:
Код:
- (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");
}
}
}
После первого вызова этого метода все в порядке, пока я не попытаюсь отсоединить базу данных. Когда я пытаюсь это сделать, я получаю следующую ошибку:
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
Я тоже пытался сделать то же самое, но без вставки в транзакцию результат идентичный. Другое дело было удалить предложение @synchronized, но тоже не повезло. Я предполагаю, что если не удается получить доступ к подключению к локальной базе данных из фонового потока, но тогда как же ему удается подключиться и вставить? Любая помощь приветствуется.
Edit
Я переместил код в основной поток, поэтому теперь доступ к базе данных осуществляется только из основного потока. Проблема остается.
Edit2
Итак, попробовав все, я на мгновение отказался от этого, а затем вернулся, когда здесь появился первый ответ. Удивительно, но сейчас, похоже, все работает нормально, поэтому мой код должен быть правильным.Я подозреваю, что это была проблема с разными потоками, блокирующими файл, поскольку я использовал XCode, SQLiteDatabaseBrowser и свое приложение для открытия базы данных. Несмотря на то, что lsof показал, что файл не был заблокирован, я думаю, что это было неправильно, и либо XCode, либо SQLiteDatabaseBrowser блокировали его. Я считаю, что проблема решена, и урок, извлеченный из этого, состоит в том, чтобы не зацикливаться на lsof так много, а также лучше спланировать отладку в следующий раз.