【问题标题】:Application crashes with Assertion Failure message while updating SQLite database更新 SQLite 数据库时应用程序崩溃并显示断言失败消息
【发布时间】:2012-12-31 19:36:19
【问题描述】:

在我的应用程序中,我将一些数据保存在本地 Sqlite 数据库中。 Sqlite 打开,插入一切正常。但是当我尝试更新特定行/记录的数据时,如果失败并显示 ASSERTION FAILURE 消息..

* -[gss_databaseHandler updateRecord::::] 中的断言失败,/Users/gss/Desktop/SalesPro copy/SalesPro/../gss_databaseHandler.m:210

NSString *databasePath;
// Method to open a database, the database will be created if it doesn't exist
-(void)initDatabase
{
// Create a string containing the full path to the sqlite.db inside the documents folder
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
databasePath = [documentsDirectory stringByAppendingPathComponent:@"contact1"];

// Check to see if the database file already exists
bool databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:databasePath];

// Open the database and store the handle as a data member
if (sqlite3_open([databasePath UTF8String], &databaseHandle) == SQLITE_OK)
{
    // Create the database if it doesn't yet exists in the file system
    if (!databaseAlreadyExists)
    {
        // Create the contactList table
        const char *sqlStatement = "CREATE TABLE IF NOT EXISTS contactList (sapCustId TEXT, sapContactId TEXT, record_ID NUMERIC PRIMARY KEY, timestamp TEXT)";
        char *error;
        if (sqlite3_exec(databaseHandle, sqlStatement, NULL, NULL, &error) == SQLITE_OK)
        {
                NSLog(@"Database and tables created.");
        }
        else
        {
                NSLog(@"Error: in creating/opening database");
        }

    }
  }
}


- (void) updateRecord:(int)recordID:(NSString *)sapCustId:(NSString *)sapContactId:(NSString *)timestamp {

[self initDatabase];

sqlite3_stmt *updateStmt = nil;

    if(updateStmt == nil) {
        const char *sql_stmt = "update contactList Set sapCustId = ?, sapContactId = ?, timestamp = ? Where record_ID = ?";

     if(sqlite3_prepare_v2(databaseHandle, sql_stmt, -1, &updateStmt, NULL) != SQLITE_OK)
         NSAssert1(0, @"Error while creating update statement. '%s'", sqlite3_errmsg(databaseHandle));
    }

    sqlite3_bind_text(updateStmt, 0, [sapCustId UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(updateStmt, 1, [sapContactId UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_int(updateStmt, 2, recordID);
    sqlite3_bind_text(updateStmt, 3, [timestamp UTF8String], -1, SQLITE_TRANSIENT);

    if(SQLITE_DONE != sqlite3_step(updateStmt))
        NSAssert1(0, @"Error while updating. '%s'", sqlite3_errmsg(databaseHandle));

sqlite3_finalize(updateStmt);
sqlite3_close(databaseHandle);

//Reclaim all memory here.
[sapContactId release];
[sapCustId release];

}

请帮忙!..

【问题讨论】:

  • @Lefteris 用错误消息更新它!
  • 从您的 sql 更新语句中,我看到您的 recordID 应该是第 4 个而不是第 3 个传递给 tte sqlite_bind 的变量
  • @Lefteris 我相应地更改了它.. 但收到相同的错误。
  • @ibiren 您已经竭尽全力记录sqlite3_errmsg,这是错误的关键部分,但您没有与我们分享。这就是我们需要的部分。断言发生的事实并不像sqlite3_errmsg 那样有趣,这将告诉我们原因。它隐藏在控制台日志中,因此您只需查看一下即可。就您的错误而言,lefteris 关于参数的顺序是正确的,而 Prateek 关于 sqlite3_reset 是不正确的(尽管他认为它是基于 1 的索引是正确的)。

标签: ios sqlite sql-update xcode4.5


【解决方案1】:

所以,有几个想法:

  1. 正如 Prateek 和 Lefteris 建议的那样,您应该更改 bind 调用的顺序,以便它们与 SQL 中字段的顺序相匹配。它们也以索引一开始,而不是零。我一直觉得奇怪的是 sqlite3_bind 调用的索引从 1 开始,而 sqlite3_column 调用的索引从 0 开始,但这就是它的工作方式。

  2. 虽然这可能没问题,但如果您曾经进行过断言,您通常应该确保首先 (a) 完成所有成功准备的语句; (b) 关闭数据库。您不想冒险让您的数据库处于不一致的状态。

  3. 您可能不应该在这里发布sapContactIdsapCustId。你没有retain 或做任何事情来增加他们的保留数量,所以在这里释放他们可能是不谨慎的。如果您通过静态分析器运行代码(按 shift+command+B 或从产品菜单中选择“分析”),您将无疑会在那里看到建议/警告。

  4. 正如官方 SQLite An Introduction To the SQLite C/C++ Interface 中所述,openprepare_v2stepfinalizeclose 的顺序是完全正确的。您无需致电sqlite3_resetresetpurpose 是为了让你可以重用之前准备好的sqlite3_stmt,但是你在这里没有这样做,所以reset 在这里是不必要的,没有任何效果。

  5. 在您的 cmets 中,您曾说过收到“数据库锁定”错误。如果您在解决上述问题后仍然得到它,请告诉我们您从哪里得到它(在open?在 SQL 语句的prepare 期间?),因为这个问题可能有不同的来源。当然,多线程数据库操作会导致这种情况。您需要通过准确地向我们展示您在哪一行收到此锁定消息来帮助我们诊断此问题,并描述您可能正在执行的其他数据库操作(除了原始问题中的代码),特别是如果您正在执行任何操作后台队列/线程。

    解决“数据库锁定”问题的挑战在于,问题始终不在于您遇到“数据库锁定”错误的代码,而在于您之前未能正确处理的某些数据库交互。从技术上讲,调用sqlite3_finalize 失败可能会导致“数据库锁定”问题,但实际上这种情况很少发生。我看到“数据库锁定”错误通常是由于其他问题而发生的,但我们真的需要知道您是否在打开数据库期间遇到它,或者在尝试preparestep SQL 语句时遇到它。你需要告诉我们哪个让我们在诊断中走得更远。但是,就像我说的,问题无疑在于其他/之前的数据库交互,因此您可能需要分享更多关于您正在执行的其他数据库操作之前“数据库锁定”错误。


如果您对原始问题中有关重写代码的讨论感兴趣,请参阅下文。

一个小建议,但我建议更改您的 initDatabase 方法,以确保在 sqlite3_open 调用失败时记录错误消息,例如:

-(void)initDatabase
{
    // Create a string containing the full path to the sqlite.db inside the documents folder
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *databasePath = [documentsDirectory stringByAppendingPathComponent:@"contact1.sqlite"];
    
    // Check to see if the database file already exists
    bool databaseAlreadyExists = [[NSFileManager defaultManager] fileExistsAtPath:databasePath];
    
    // Open the database and store the handle as a data member
    if (sqlite3_open([databasePath UTF8String], &databaseHandle) == SQLITE_OK)
    {
        // Create the database if it doesn't yet exists in the file system
        if (!databaseAlreadyExists)
        {
            // Create the contactList table
            const char *sqlStatement = "CREATE TABLE IF NOT EXISTS contactList (sapCustId TEXT, sapContactId TEXT, record_ID NUMERIC PRIMARY KEY, timestamp TEXT)";
            char *error;
            if (sqlite3_exec(databaseHandle, sqlStatement, NULL, NULL, &error) == SQLITE_OK)
            {
                NSLog(@"Database and tables created.");
            }
            else
            {
                NSLog(@"%s: error creating table: %s", __FUNCTION__, sqlite3_errmsg(databaseHandle));
            }
        }
    }
    else
    {
        NSLog(@"%s: error opening database: %s", __FUNCTION__, sqlite3_errmsg(databaseHandle));
    }
}

第二,你看insertRecord

  • 您想修正使用四个绑定语句的顺序。

  • 您还可以消除多余的if 语句。

  • 如果您使用断言或从方法中间返回进行数据库交互,您总是希望进行优雅的清理。例如,你会看到我的准备是否失败,我会直接关闭,但如果step 失败,那么我确保finalizeclose

  • 您可能应该删除那些不恰当的release 语句,将它们移到代码中的正确位置。

  • 您应该将方法签名更改为具有命名参数(因为在编码指南的General Rules 部分,Apple 说我们应该命名我们的方法关键字。

因此:

- (void) insertRecord:(int)recordID
            sapCustId:(NSString *)sapCustId
         sapContactId:(NSString *)sapContactId
            timestamp:(NSString *)timestamp
{
    [self initDatabase];
    
    sqlite3_stmt *statement = nil;
    
    const char *sql_stmt = "INSERT INTO contactList (sapCustId, sapContactId, timestamp, record_ID) VALUES (?, ?, ?, ?)";
    
    if(sqlite3_prepare_v2(databaseHandle, sql_stmt, -1, &statement, NULL) != SQLITE_OK)
    {
        NSLog(@"%s: insert prepare error: '%s'", __FUNCTION__, sqlite3_errmsg(databaseHandle));
        sqlite3_close(databaseHandle);
        NSAssert(0, @"insert prepare error");
    }
    
    sqlite3_bind_text(statement, 1, [sapCustId UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(statement, 2, [sapContactId UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(statement, 3, [timestamp UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_int(statement, 4, recordID);
    
    if (sqlite3_step(statement) != SQLITE_DONE)
    {
        NSLog(@"%s: insert step error: '%s'", __FUNCTION__, sqlite3_errmsg(databaseHandle));
        sqlite3_finalize(statement);
        sqlite3_close(databaseHandle);
        NSAssert(0, @"insert step error");
    }
    
    sqlite3_finalize(statement);
    sqlite3_close(databaseHandle);
}

【讨论】:

  • 感谢您的所有建议。我一定会照顾好它。
【解决方案2】:

试试这个

你缺少 sqlite3_reset,编号应该从 1 开始

- (void) updateRecord:(int)recordID:(NSString *)sapCustId:(NSString *)sapContactId:(NSString *)timestamp {

[self initDatabase];

sqlite3_stmt *updateStmt = nil;

    if(updateStmt == nil) {
        const char *sql_stmt = "update contactList Set sapCustId = ?, sapContactId = ?, timestamp = ? Where record_ID = ?";

     if(sqlite3_prepare_v2(databaseHandle, sql_stmt, -1, &updateStmt, NULL) != SQLITE_OK)
         NSAssert1(0, @"Error while creating update statement. '%s'", sqlite3_errmsg(databaseHandle));
    }

    sqlite3_bind_text(updateStmt, 1, [sapCustId UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(updateStmt, 2, [sapContactId UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(updateStmt, 3, [timestamp UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_int(updateStmt, 4, recordID);

    if(SQLITE_DONE != sqlite3_step(updateStmt))
        NSAssert1(0, @"Error while updating. '%s'", sqlite3_errmsg(databaseHandle));

sqlite3_reset(updateStmt);
sqlite3_finalize(updateStmt);
sqlite3_close(databaseHandle);

//Reclaim all memory here.
[sapContactId release];
[sapCustId release];

}

希望对你有所帮助..

编辑:-

您没有在之前的任何函数中添加sqlite3_finalize();,因此您遇到了数据库锁定错误..

【讨论】:

  • 既不会崩溃也不会更新!.. 当 sqlite3_stmt 为 STATIC 时,还应该使用 sqlite3_reset(updatestmt) 吗?..
  • 但是更新时出现错误后你得到了什么。在 NSLog 中
  • 更新时出错。 '数据库被锁定'
  • 是的,现在我们终于找到了您面临的主要问题,检查您是否编写了 sqlite3_finalize(); 的每个函数,您缺少某个地方,因此您的数据库被锁定了..
  • @Prateek 对于sqlite3_finalizesqlite3_reset,这个答案不正确。 ibiren应该致电sqlite3_finalize,不需要致电sqlite3_resetlatter 用于重置准备好的语句,以便您可以再次执行它,这里 ibires 没有这样做。原始代码中的问题是绑定语句的顺序,而不是从一个开始索引,与sqlite3_reset无关。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多