【问题标题】:C SQLite Inserts BLOB instead of String and Vice Versa When Binding ParametersC SQLite 在绑定参数时插入 BLOB 而不是字符串,反之亦然
【发布时间】:2014-10-12 04:42:49
【问题描述】:

所以我在 C 中与 SQLite 进行参数绑定时遇到了一些问题。我正在使用 sqlite3_bind_* 函数将 BLOB 和字符串插入数据库。然而,在插入之后,我用 SQLiteBrowser 检查了数据库,令我惊讶的是,这些类型都乱七八糟了!这是一些应该重现效果的示例代码。

  1. 这个块创建表。

    const char *TABLE_NAME = "PASSWORD_ENTRY";
    const char *USER_ID_COLUMN_NAME = "USER_ID";
    const char *INDEX_COLUMN_NAME = "INDEX_VALUE";
    const char *SERVICE_COLUMN_NAME = "SERVICE";
    const char *SYM_ENC_KEY_COLUMN_NAME = "SYM_ENC_KEY";
    const char *ASYM_ENC_KEY_COLUMN_NAME = "ASYM_ENC_KEY";
    const char *TIMESTAMP_COLUMN_NAME = "TIMESTAMP";
    
    /* CREATE TABLE IF NOT EXISTS TABLE_NAME (
            USER_ID_COLUMN_NAME INTEGER, 
            INDEX_COLUMN_NAME INTEGER, 
            SERVICE_COLUMN_NAME TEXT, 
            SYM_ENC_KEY_COLUMN_NAME BLOB, 
            ASYM_ENC_KEY_COLUMN_NAME BLOB,
            TIME_STAMP_COLUMN_NAME BLOB, 
            PRIMARY KEY (USER_ID_COLUMN_NAME, INDEX_COLUMN_NAME)); 
    */
    
    char *f = "CREATE TABLE IF NOT EXISTS %s (%s INTEGER, %s INTEGER, %s TEXT, %s BLOB, %s BLOB, %s BLOB, PRIMARY KEY (%s, %s));";
    
    char *s = malloc(snprintf(NULL, 0, f, TABLE_NAME, USER_ID_COLUMN_NAME, INDEX_COLUMN_NAME, SERVICE_COLUMN_NAME, SYM_ENC_KEY_COLUMN_NAME, ASYM_ENC_KEY_COLUMN_NAME, TIMESTAMP_COLUMN_NAME, USER_ID_COLUMN_NAME, INDEX_COLUMN_NAME) + 1);
    
    sprintf(s, f, TABLE_NAME, USER_ID_COLUMN_NAME, INDEX_COLUMN_NAME, SERVICE_COLUMN_NAME, SYM_ENC_KEY_COLUMN_NAME, ASYM_ENC_KEY_COLUMN_NAME, TIMESTAMP_COLUMN_NAME, USER_ID_COLUMN_NAME, INDEX_COLUMN_NAME);
    
    const char *DB_NAME = "passwordmanager.db";
    
    sqlite3* db;
    int r = 0;
    
    // Get the database
    r = sqlite3_open(DB_NAME, &db);
    if (r) {
            printf("Error opening database: %s\n", sqlite3_errmsg(db));
            return NULL;
    }
    printf("Database opened.\n");
    
    r = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
    if (r) {
            printf("Error preparing create table statement: %s\n", sqlite3_errmsg(db));
            return 1;
    }
    
    r = sqlite3_step(stmt);
    if (r != 101 && r) {
            printf("Error executing create table statement: %s\n", sqlite3_errmsg(db));
            return 1;
    }
    printf("Password entry table ready.\n");
    sqlite3_finalize(stmt);
    
  2. 现在已经完成了,我会给你一个示例插入。

    sqlite3_stmt *stmt2;
    long userId = 50l;
    short index = 2;
    long timestamp = 100l;
    char *service = "stackoverflow.com";
    const int SYM_ENC_KEY_LEN = 10;
    const int ASYM_ENC_KEY_LEN = 11;
    char *symEncKey = "symEncKey";
    char *asymEncKey = "asymEncKey";
    
    char *f = "INSERT INTO PASSWORD_ENTRY (USER_ID, INDEX_VALUE, SERVICE, TIMESTAMP, SYM_ENC_KEY, ASYM_ENC_KEY) VALUES (?, ?, ?, ?, ?, ?);";
    
    printf("SQL ready.\n");
    
    r = sqlite3_prepare_v2(db, f, strlen(f), &stmt2, NULL);
    if (r != 0) {
            printf("Error preparing addition statement: %s\n", sqlite3_errmsg(db));
            sqlite3_finalize(stmt2);
            sqlite3_close(db);
            return;
    }
    printf("Prepared the addition statement, binding...\n");
    
    sqlite3_bind_int64(stmt2, 1, (sqlite3_int64) userId);
    sqlite3_bind_int(stmt2, 2, (int) index);
    sqlite3_bind_text(stmt2, 3, service, strlen(service) + 1, 0);
    sqlite3_bind_int64(stmt2, 4, (sqlite_int64) timestamp);
    sqlite3_bind_blob(stmt2, 5, (void *) symEncKey, SYM_ENC_KEY_LEN, 0);
    sqlite3_bind_blob(stmt2, 6, (void *) asymEncKey, ASYM_ENC_KEY_LEN, 0);
    
    // Execute the statement
    r = sqlite3_step(stmt2);
    if (r != 101) {
            printf("Error executing addition statement: %s\n", sqlite3_errmsg(db));
            sqlite3_finalize(stmt2);
            sqlite3_close(db);
            return;
    }
    printf("Executed the addition statement.\n");
    
    sqlite3_finalize(stmt2);
    sqlite3_close(db);
    

现在,如果您想使用 SQLiteBrowser 或您可能拥有的任何类似工具查看数据库,前提是您的运气和我一样,您会看到 SERVICE 列包含一个 BLOB,而 SYM_ENC_KEY 列包含一个字符串,不管我使用了相反的 sqlite3_bind_* 函数。有谁知道这是怎么发生的?如果您需要更多信息,请询问。我是新海报。

【问题讨论】:

  • 这段代码无法编译。

标签: c sqlite text blob


【解决方案1】:
sqlite3_bind_text(stmt2, 3, service, strlen(service) + 1, 0);

零终止符不被视为字符串数据的一部分。 去掉+ 1,或者更好,只给-1

最后一个参数错误;您必须提供析构函数,或SQLITE_TRANSIENTSQLITE_STATIC

_blob 调用也有同样的问题。)


但是,命令行 shell 中 .dump 命令的输出包含以下内容:

INSERT INTO "PASSWORD_ENTRY" VALUES(50,2,'stackoverflow.com',X'73796D456E634B657900',X'6173796D456E634B657900',100);

这是正确的。不存在数据类型问题。

【讨论】:

  • 感谢您这么快回来!我进行了您指定的更改,并且 SERVICE_NAME_COLUMN 似乎工作正常,但是根据 SQLiteBrowser,SYM_ENC_KEY 列仍在存储一个字符串。也许这只是 SQLiteBrowser 中的一个错误,因为您能够使用命令行工具检查语句,并且插入了正确的类型。无论如何,我会接受你的回答,谢谢你的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-11
  • 2012-03-06
  • 1970-01-01
  • 2012-06-13
  • 2015-12-09
  • 2014-11-18
相关资源
最近更新 更多