Given that I do not want to use exceptions in this code, how can I remove the duplicated:
if (rc != SQLITE_OK) {
return rc;
}
checks after most statements in this function below?
int sqlite::update(const std::string& table_name, const std::vector<column_values>& fields, const std::vector<column_values>& where) {
if (db_ == nullptr) {
return SQLITE_ERROR;
}
const std::string sql = update_helper(table_name, fields, where);
sqlite3_stmt* stmt = NULL;
int rc = sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, NULL);
if(rc != SQLITE_OK) {
return rc;
}
// loop thru each parameter, calling bind for each one
rc = bind_fields(stmt, fields);
if(rc != SQLITE_OK) {
return rc;
}
// loop thru each where parameter, calling bind for each one
rc = bind_where(stmt, where);
if (rc != SQLITE_OK) {
return rc;
}
return step_and_finalise(stmt);
}
First off, you are leaking the sqlite3_stmt
if something goes wrong. You need to call sqlite3_finalize()
before return
'ing anything.
As for your question, you can wrap the duplicate if..return
s in a preprocessor macro, eg:
#define CHECK_RC(rc) \
if (rc != SQLITE_OK) \
{ \
sqlite3_finalize(stmt); \
return rc; \
}
int sqlite::update(const std::string& table_name, const std::vector<column_values>& fields, const std::vector<column_values>& where) {
if (db_ == nullptr) {
return SQLITE_ERROR;
}
const std::string sql = update_helper(table_name, fields, where);
sqlite3_stmt* stmt = NULL;
int rc = sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, NULL);
CHECK_RC(rc);
// loop thru each parameter, calling bind for each one
rc = bind_fields(stmt, fields);
CHECK_RC(rc);
// loop thru each where parameter, calling bind for each one
rc = bind_where(stmt, where);
CHECK_RC(rc);
return step_and_finalise(stmt);
}
Alternatively:
#define CHECK_RC(op) \
{ \
int rc = op; \
if (rc != SQLITE_OK) \
{ \
sqlite3_finalize(stmt); \
return rc; \
} \
}
int sqlite::update(const std::string& table_name, const std::vector<column_values>& fields, const std::vector<column_values>& where) {
if (db_ == nullptr) {
return SQLITE_ERROR;
}
const std::string sql = update_helper(table_name, fields, where);
sqlite3_stmt* stmt = NULL;
CHECK_RC(sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, NULL));
// loop thru each parameter, calling bind for each one
CHECK_RC(bind_fields(stmt, fields));
// loop thru each where parameter, calling bind for each one
CHECK_RC(bind_where(stmt, where));
return step_and_finalise(stmt);
}
Or, you can re-write the code to not use multiple return
s, eg:
int sqlite::update(const std::string& table_name, const std::vector<column_values>& fields, const std::vector<column_values>& where) {
int rc;
if (db_ == nullptr) {
rc = SQLITE_ERROR;
}
else {
const std::string sql = update_helper(table_name, fields, where);
sqlite3_stmt* stmt = NULL;
rc = sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, NULL);
if (rc == SQLITE_OK) {
// loop thru each parameter, calling bind for each one
rc = bind_fields(stmt, fields);
if (rc == SQLITE_OK) {
// loop thru each where parameter, calling bind for each one
rc = bind_where(stmt, where);
if (rc == SQLITE_OK) {
rc = step_and_finalise(stmt);
stmt = NULL;
}
}
sqlite3_finalize(stmt);
}
}
return rc;
}
Or, you can throw an exception, eg:
void check_rc(int rc)
{
if (rc != SQLITE_OK)
throw rc;
}
void check_ptr(void* ptr)
{
if (ptr == nullptr)
check_rc(SQLITE_ERROR);
}
int sqlite::update(const std::string& table_name, const std::vector<column_values>& fields, const std::vector<column_values>& where) {
try {
check_ptr(db_);
const std::string sql = update_helper(table_name, fields, where);
sqlite3_stmt* stmt = NULL;
check_rc(sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, NULL));
try {
// loop thru each parameter, calling bind for each one
check_rc(bind_fields(stmt, fields));
// loop thru each where parameter, calling bind for each one
check_rc(bind_where(stmt, where));
return step_and_finalise(stmt);
}
catch (const int) {
sqlite3_finalize(stmt);
throw;
}
}
catch (const int errCode) {
return errCode;
}
}