You should not move the query string away from the query execution. If you write a helper function that prepares a statement only when it is first needed, you also avoid having to prepare all of them at the beginning:
void Database::PrepareStatementIfNeeded(sqlite3_stmt **stmt,
const std::string& sql)
{
if (*stmt == NULL) {
res = sqlite3_prepare_v2(db, sql.c_str(), -1, stmt, NULL);
...
}
}
(But you still have to ensure to finalize all of them.)
Furthermore, if you give the parameters names instead of numbers, you can use sqlite3_bind_parameter_index to get the proper 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);
...
}
(And those sqlite3_step
/sqlite3_reset
calls look as if you are repeating them umpteen times; create a helper function for them, too.)