【问题标题】:Why do I get a SQLITE_MISUSE : Out of Memory error?为什么我会收到 SQLITE_MISUSE : Out of Memory 错误?
【发布时间】:2013-03-07 22:33:38
【问题描述】:

我正在编写一个直接访问SQLiteiOS 应用程序。我在 Android 上做过很多次这样的事情,所以我很难找出我的错误所在 - 但是我的插入返回 SQLITE_MISUSE 错误(代码 21),并显示消息“内存不足”。以下是我为引导我进入此插页所采取的步骤。

首先,表创建

NSString *sql = @"CREATE TABLE IF NOT EXISTS UsersTable (lastName TEXT,id TEXT PRIMARY KEY NOT NULL,picture BLOB,firstName TEXT,age TEXT,email TEXT,sex TEXT,height TEXT,weight TEXT)";

//create the database if it does not yet exist
NSFileManager *filemgr = [NSFileManager defaultManager];
if ([filemgr fileExistsAtPath: path ] == NO)
{
    const char *dbpath = [path UTF8String];

    //This was if (sqlite3_open(dbpath, &store) == SQLITE_OK) , but it has not made a difference.
    if (sqlite3_open_v2(dbpath, &store, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) == SQLITE_OK)
    {
        char *errMsg = NULL;
        const char *sql_stmt = [sql UTF8String];

        if (sqlite3_exec(store, sql_stmt, NULL, NULL, &errMsg) != SQLITE_OK)
        {
            NSLog(@"Failed to create table: %s", errMsg);
        }
        if (errMsg)
            sqlite3_free(errMsg);
    }
    else
    {
        NSLog(@"Failed to open/create database");
    }
}

接下来,插入(当前使用电子邮件地址作为用户 ID):

INSERT INTO UsersTable (id,lastName,firstName,email) VALUES ("jsmith@foobar.com","Smith","John","jsmith@foobar.com")

我对所有数据库交互都使用一个选择器,所以上面的文字在这里传递:

-(int)execSQL:(NSString *)statement
{
    NSLog(@"%@",statement);

    const char *insert_stmt = [statement UTF8String];
    sqlite3_stmt *stmnt;

    sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL);
    int result = sqlite3_step(stmnt);

    sqlite3_finalize(stmnt);

    if (result != SQLITE_OK)
    {
        NSLog(@"Error: %s", sqlite3_errmsg(store));//This prints "Error: out of memory"
    }
    return result;
}

我做错了什么?

【问题讨论】:

  • 获取完整错误。 NSLog(@"Error: %s", sqlite3_errmsg(store));.
  • sqlite 要求文本值使用单引号,而不是双引号。
  • 最后一件事——以防万一——不要使用字符串格式来创建 SQL 语句。准备语句,然后使用sqlite3_bind_xxx 将值绑定到查询中。 INSERT INTO table (c1, c2, c2) VALUES (?, ?, ?)。对sqlite3_bind_xxx 的调用将用正确的值替换?。这可确保正确引用值并正确转义特殊字符。
  • “内存不足”错误几乎总是意味着您的数据库指针(您的 sqlite3 * 变量)没有通过调用 sqlite3_open_v2 正确设置。
  • @rmaddy 我添加了用于打开数据库的代码,并从使用sqlite3_open 更改为sqlite3_open_v2,但仍然遇到相同的错误。还有其他指针吗? (我目前正在处理您的其他建议)

标签: ios objective-c sqlite


【解决方案1】:

您的打开例程仅在数据库不存在时创建/打开数据库。您的数据库可能已经存在,因此您的例程甚至没有打开它。

底线,如果你尝试在不打开数据库的情况下调用 SQLite 函数,你将得到 SQLITE_MISUSE 返回码(这表明 SQLite 函数没有按正确的顺序调用),sqlite3_errmsg 将返回神秘的“内存不足”错误。


其他一些不相关的观察:

  1. 你真的应该检查sqlite3_prepare的返回码:

    - (int)execSQL:(NSString *)statement
    {
        int result;
    
        NSLog(@"%@",statement);
    
        const char *insert_stmt = [statement UTF8String];
        sqlite3_stmt *stmnt;
    
        if ((result = sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL)) != SQLITE_OK)
        {
            NSLog(@"%s: prepare failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
            return result;
        }
    
        if ((result = sqlite3_step(stmnt)) != SQLITE_DONE)
        {
            NSLog(@"%s: step failure: '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
        }
    
        sqlite3_finalize(stmnt);
    
        return result;
    }
    

    根据我的经验,许多常见的开发问题都与 SQL 本身有关,这可以通过检查 sqlite3_prepare_v2 语句的返回码来识别。

  2. 您真的不应该在NSString 中构建您的SQL 语句。您打开自己的 SQL 注入攻击,或者考虑到更良性的情况,如果某人的名字中有引号,则只是一个 SQL 错误,例如。 The "Destroyer"。您应该使用 ? 占位符,然后使用 sqlite3_bind_xxx 函数绑定值。比如:

    - (int)insertIdentifier:(NSString *)identifier
                   lastName:(NSString *)lastName
                  firstName:(NSString *)firstName
                      email:(NSString *)email
    {
        int result;
    
        const char *insert_stmt = "INSERT INTO UsersTable (id, lastName, firstName, email) VALUES (?, ?, ?, ?);";
        sqlite3_stmt *stmnt;
    
        if ((result = sqlite3_prepare_v2(store, insert_stmt, -1, &stmnt, NULL)) != SQLITE_OK)
        {
            NSLog(@"%s: prepare failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
            return result;
        }
    
        if ((result = sqlite3_bind_text(stmnt, 1, [identifier UTF8String], -1, NULL)) != SQLITE_OK)
        {
            NSLog(@"%s: bind #1 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
            sqlite3_finalize(stmnt);
            return result;
        }
    
        if ((result = sqlite3_bind_text(stmnt, 2, [lastName UTF8String], -1, NULL)) != SQLITE_OK)
        {
            NSLog(@"%s: bind #2 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
            sqlite3_finalize(stmnt);
            return result;
        }
    
        if ((result = sqlite3_bind_text(stmnt, 3, [firstName UTF8String], -1, NULL)) != SQLITE_OK)
        {
            NSLog(@"%s: bind #3 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
            sqlite3_finalize(stmnt);
            return result;
        }
    
        if ((result = sqlite3_bind_text(stmnt, 4, [email UTF8String], -1, NULL)) != SQLITE_OK)
        {
            NSLog(@"%s: bind #4 failure '%s' (%d)", __FUNCTION__, sqlite3_errmsg(store), result);
            sqlite3_finalize(stmnt);
            return result;
        }
    
        if ((result = sqlite3_step(stmnt)) != SQLITE_DONE)
        {
            NSLog(@"%s: step failure: '%s'", __FUNCTION__, sqlite3_errmsg(store));
        }
    
        sqlite3_finalize(stmnt);
    
        return result;
    }
    

    你可以这样称呼它:

    [self insertIdentifier:@"jsmith@foobar.com"
                  lastName:@"Smith"
                 firstName:@"John"
                     email:@"jsmith@foobar.com"];
    
  3. 1234563我建议您考虑查看FMDB。它是 SQLite 函数的一个很好的薄包装器,极大地简化了在 Objective-C 中编写 SQLite 代码的练习。

【讨论】:

    【解决方案2】:

    您没有检查sqlite3_prepare_v2 语句的值。如果不是SQLITE_OK,那就有问题了。

    另外,数据库文件是否已经存在?如果没有,您需要创建它或从包中加载它。

    【讨论】:

      猜你喜欢
      • 2019-03-20
      • 2019-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-06
      • 2019-04-15
      • 2018-08-01
      • 1970-01-01
      相关资源
      最近更新 更多