组织sqlite3 C/ c++预处理语句(避免全局代码混乱)



我正在编写一个需要使用准备好的语句来获得性能优势的应用程序,但是我想知道当您拥有数十个(如果不是数百个)准备好的语句的应用程序时,您如何管理它而不会将其变成混乱的全局代码?你只需要在构造函数/函数中准备所有的语句,而不是在使用它们的地方吗?

使用sqlite_exec是很好的,因为查询就在那里,你实际上使用它,但与准备好的语句,我最终有他们在完全不同的代码区域,它模糊/混淆什么变量需要绑定在函数中,实际使用它们。

具体来说,现在我只有一个数据库单例它有私有变量,

sqlite3_stmt *insertPacketCount;
sqlite3_stmt *insertPortContacted;
sqlite3_stmt *insertPacketSize;
sqlite3_stmt *computePacketSizeVariance;
 ...

构造函数有几十个调用sqlite3_prepare_v2,

// Set up all our prepared queries
res = sqlite3_prepare_v2(db,
    "INSERT OR IGNORE INTO packet_counts VALUES(?1, ?2, ?3, 1);",
    -1, &insertPacketCount,  NULL);
expectReturnValueAndFail(SQLITE_OK);
 ...

和其他地方使用它们的实际函数,

void Database::IncrementPacketCount(std::string ip, std::string interface, std::string type, uint64_t increment)
{
    int res;
    res = sqlite3_bind_text(incrementPacketCount, 1, ip.c_str(), -1, SQLITE_STATIC);
    expectReturnValue(SQLITE_OK);
    res = sqlite3_bind_text(incrementPacketCount, 2, interface.c_str(), -1, SQLITE_STATIC);
    expectReturnValue(SQLITE_OK);
    res = sqlite3_bind_text(incrementPacketCount, 3, type.c_str(), -1, SQLITE_STATIC);
    expectReturnValue(SQLITE_OK);
    res = sqlite3_bind_int(incrementPacketCount, 4, increment);
    expectReturnValue(SQLITE_OK);
    res = sqlite3_step(incrementPacketCount);
    expectReturnValue(SQLITE_DONE);
    res = sqlite3_reset(incrementPacketCount);
    expectReturnValue(SQLITE_OK);
}

在使用准备好的查询的函数内部,我不得不不断地返回到prepare语句来弄清楚绑定索引是什么。

对不起,如果这是模糊的,但是有任何库或方法来组织这种事情不牺牲任何性能/安全(像只是追加字符串和调用sqlite_exec)?

您不应该将查询字符串从查询执行中移开。如果您编写的辅助函数只在第一次需要时准备语句,那么您还可以避免在开始时准备所有语句:

void Database::PrepareStatementIfNeeded(sqlite3_stmt **stmt,
                                        const std::string& sql)
{
    if (*stmt == NULL) {
        res = sqlite3_prepare_v2(db, sql.c_str(), -1, stmt, NULL);
        ...
    }
}

(但你仍然必须确保完成所有的任务)

此外,如果您提供参数名称而不是数字,您可以使用sqlite3_bind_parameter_index来获得适当的索引:

void BindParam(sqlite3_stmt *stmt,
               const char *name,
               const std::string& value) // overload for other types
{
    int index = sqlite3_bind_parameter_index(stmt, name);
    if (index == 0)
        error...;
    int res = sqlite3_bind_text(stmt, index, value.c_str(), -1, SQLITE_TRANSIENT);
    ...
}
void Database::IncrementPacketCount(...)
{
    PrepareStatementIfNeeded(&incrementPacketCount,
                             "INSERT OR IGNORE INTO packet_counts"
                             " VALUES(:ip, :intf, :type, 1)");
    BindParameter(incrementPacketCount, "ip",   ip);
    BindParameter(incrementPacketCount, "intf", interface);
    BindParameter(incrementPacketCount, "type", type);
    ...
}

(这些sqlite3_step/sqlite3_reset呼叫看起来好像你重复了无数次;也为它们创建一个帮助函数。)

最新更新