diff --git a/src/storage/backend/sqlite/SQLite.cc b/src/storage/backend/sqlite/SQLite.cc index d74ff48905..7602b55328 100644 --- a/src/storage/backend/sqlite/SQLite.cc +++ b/src/storage/backend/sqlite/SQLite.cc @@ -19,7 +19,8 @@ using namespace std::chrono_literals; namespace zeek::storage::backend::sqlite { -OperationResult SQLite::RunPragma(std::string_view name, std::optional value) { +OperationResult SQLite::RunPragma(std::string_view name, std::optional value, + StepResultParser value_parser) { char* errorMsg = nullptr; std::chrono::milliseconds time_spent = 0ms; @@ -29,12 +30,24 @@ OperationResult SQLite::RunPragma(std::string_view name, std::optional(cmd.size()), &stmt, nullptr)); + check_res.code != ReturnCode::SUCCESS ) + return check_res; + + OperationResult success_res; + + // Make a unique_ptr out of the statement so we don't have to manually call sqlite3_finalize + // one it when we return in various places. + unique_stmt_ptr stmt_ptr{stmt, sqlite3_finalize}; + while ( pragma_timeout == 0ms || time_spent < pragma_timeout ) { - int res = sqlite3_exec(db, cmd.c_str(), nullptr, nullptr, &errorMsg); - if ( res == SQLITE_OK ) { + auto res = Step(stmt, value_parser, true); + if ( res.code == ReturnCode::SUCCESS ) { + success_res = res; break; } - else if ( res == SQLITE_BUSY ) { + else if ( res.code == ReturnCode::TIMEOUT ) { // If we got back that the database is busy, it likely means that another process is trying to // do their pragmas at startup too. Exponentially back off and try again after a sleep. sqlite3_free(errorMsg); @@ -42,8 +55,8 @@ OperationResult SQLite::RunPragma(std::string_view name, std::optional(); } @@ -530,15 +543,19 @@ OperationResult SQLite::CheckError(int code) { return {ReturnCode::SUCCESS}; } -OperationResult SQLite::Step(sqlite3_stmt* stmt, StepResultParser parser) { +OperationResult SQLite::Step(sqlite3_stmt* stmt, StepResultParser parser, bool is_pragma) { OperationResult ret; int step_status = sqlite3_step(stmt); if ( step_status == SQLITE_ROW ) { if ( parser ) ret = parser(stmt); - else + else if ( ! is_pragma ) ret = {ReturnCode::OPERATION_FAILED, "sqlite3_step should not have returned a value"}; + else + // For most pragmas we don't care about the output, so we don't pass a parser. We still + // may get a row as a response, but we can discard it and just return SUCCESS. + ret = {ReturnCode::SUCCESS}; } else if ( step_status == SQLITE_DONE ) { if ( parser ) diff --git a/src/storage/backend/sqlite/SQLite.h b/src/storage/backend/sqlite/SQLite.h index 65fb5f1bfd..cd07dc8ad5 100644 --- a/src/storage/backend/sqlite/SQLite.h +++ b/src/storage/backend/sqlite/SQLite.h @@ -47,12 +47,13 @@ private: * Abstracts calls to sqlite3_step to properly create an OperationResult * structure based on the result. */ - OperationResult Step(sqlite3_stmt* stmt, StepResultParser parser); + OperationResult Step(sqlite3_stmt* stmt, StepResultParser parser, bool is_pragma = false); /** * Helper utility for running pragmas on the database. */ - OperationResult RunPragma(std::string_view name, std::optional value = std::nullopt); + OperationResult RunPragma(std::string_view name, std::optional value = std::nullopt, + StepResultParser value_parser = nullptr); sqlite3* db = nullptr; sqlite3* expire_db = nullptr;