【问题标题】:Organizing sqlite3 C/C++ prepared statements (avoiding mess of global code)组织 sqlite3 C/C++ 准备好的语句(避免全局代码混乱)
【发布时间】:2013-04-23 22:23:32
【问题描述】:

我正在编写一个需要使用准备好的语句来获得性能优势的应用程序,但我想知道当您的应用程序包含几十个甚至数百个准备好的语句时,您如何管理它而不会使它变得一团糟全球代码?您是否只需要在与使用它们的位置完全不同的地方准备构造函数/函数中的所有语句?

使用 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);
}

在使用准备好的查询的函数内部,我一直不得不繁琐地返回准备语句来确定绑定索引是什么。

抱歉,如果这含糊不清,但是否有任何库或方法可以在不牺牲任何性能/安全性的情况下组织此类事情(例如仅附加字符串并调用 sqlite_exec)?

【问题讨论】:

    标签: c++ c sqlite prepared-statement code-organization


    【解决方案1】:

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

    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 调用看起来好像您重复了无数次;也为它们创建一个辅助函数。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-05
      • 1970-01-01
      • 1970-01-01
      • 2020-02-14
      • 1970-01-01
      • 2013-07-29
      相关资源
      最近更新 更多