SQLite3 на iPhone - insert возвращает код ошибки 8 при попытке записи в базу данных только для чтения - PullRequest
1 голос
/ 07 декабря 2011

Итак, я вот уже неделю как бьюсь головой об стену.

Я пишу приложение для iPhone, в котором есть база данных sqlite. Я могу открыть базу данных и читать из нее (я помещаю туда несколько тестовых данных через командную строку / терминал), выбирать конкретные данные и т. Д. Но я не могу вставить телефон в базу данных. Когда я выполняю sqlite3_exec (...), он возвращает код ошибки 8 «попытка записи базы данных только для чтения».

Я читал здесь другие вопросы о том, что я использую базу данных Main Bundle, а не базу данных пользователей, и что симулятор часто будет просто «позволять вам это делать», в то время как на живом устройстве вы получите ошибку , Ну, это не то, что происходит в моем случае - я получаю эту ошибку при работе на симуляторе. И из того, что я могу сказать, мой код для проверки базы данных точно так же, как многие другие рекомендуют это.

Вот код, который я использую, чтобы убедиться, что база данных существует, а если нет, я копирую ее:

// initialize db (if not already)
+(void) checkDatabase {
    // setup some variables
    Boolean success;
    dbName = @"daarma.sqlite";
    NSArray *documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDirectory, YES);
    // I have tried this with both NSUserDirectory and NSUserDomainMask, desn't seem to make a difference
    NSString *documentsDir = [documentPath objectAtIndex:0];
    dbPath = [documentsDir stringByAppendingPathComponent:dbName];
    //    dbPath = [[NSBundle mainBundle] pathForResource:@"daarma" ofType:@"sqlite"];

    // check to see if the database already exists
    NSFileManager *fm = [NSFileManager defaultManager];
    success = [fm fileExistsAtPath:dbPath];
    if(success) {
        NSLog(@"Database exists, returning.");
        return;
    }

    // if not, we create it
    NSLog(@"Creating database in user profile...");
    NSString *dbPathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:dbName];
    [fm copyItemAtPath:dbPathFromApp toPath:dbPath error:nil];    

    [fm release];
    [documentPath release];
    [documentsDir release];
}

Когда я иду, чтобы вставить данные, используя это:

sqlite3 *db;
int open = sqlite3_open_v2([dbPath UTF8String], &db, -1, NULL);
if(open == 0) {
    NSLog(@"open, inserting");
    NSString *sql = [NSString stringWithFormat:@"insert into affiliates values('1','2','3','4','5','6','7','8','9','10','11','12')"];
    int exec = sqlite3_exec(db, [sql UTF8String], NULL, NULL, NULL);
    NSLog(@"exec = %d",exec);
}
sqlite3_close(db);

exec возвращает с указанным выше кодом ошибки 8: "" попытка записи базы данных только для чтения. "

Я также попробовал обычный перезапуск, очистку проекта, сброс данных симулятора. Я даже зашел в каталог Simulator и удалил все данные приложения вручную. Когда я попытался вернуться, он обнаружил, что базы данных не было, и скопировал ее, но я все еще получаю эту ошибку.

EDIT:

Я только что заметил, что если я делаю это в методе checkDatabase:

NSError *error;
[fm copyItemAtPath:dbPathFromApp toPath:dbPath error:&error]; 
NSLog(@"error = %@",error);

это вызывает сбой симулятора при первом повороте (после сброса содержимого), но каждый раз после этого он возобновляет вышеуказанную ошибку без сбоев. Так что, возможно, я делаю что-то не так с моим методом checkDatabase. ?? :( Он никогда не сообщает мне вывод сообщения об ошибке.

Ответы [ 2 ]

2 голосов
/ 07 декабря 2011

попробуйте изменить функцию открытия на эту

sqlite3_open_v2([dbPath UTF8String], &db, SQLITE_OPEN_READWRITE , NULL);

или в основном использовать тривиальную функцию открытия

Sqlite3_open([dbPath UTF8String],&db);// this should do the job

надеюсь, что поможет:)

1 голос
/ 07 декабря 2011

Я попробовал ваш код, и он потерпел неудачу с open = 21 (SQLITE_MISUSE) в следующей строке:

int open = sqlite3_open_v2([dbPath UTF8String], &db, -1, NULL);

Поскольку вы передаете -1 параметру flags.Это должен быть SQLITE_OPEN_READWRITE.

У меня есть еще несколько комментариев.

[fm release];
[documentPath release];
[documentsDir release];

Эти версии не нужны, потому что вы не выделяете / инициализируете, не сохраняете и не копируете их.

NSString *sql = [NSString stringWithFormat:@"insert into affiliates values('1','2','3','4','5','6','7','8','9','10','11','12')"];
int exec = sqlite3_exec(db, [sql UTF8String], NULL, NULL, NULL);

Вы должны использовать sqlite3_prepare_v2 и параметры связывания вместо stringWithFormat: для SQL.

if(open == 0) { ... }

Когда это возможно, вы должны использовать символическую константу (SQLITE_OK) вместо магического числа (0).

NSError *error;
[fm copyItemAtPath:dbPathFromApp toPath:dbPath error:&error]; 
NSLog(@"error = %@",error);

Вы должны инициализировать error = nil перед вызовом copyItemAtPath: потому что ошибка не изменяется при успешном завершении операции копирования.

...