svn_error_t * svn_sqlite__finish_savepoint(svn_sqlite__db_t *db, svn_error_t *err) { svn_sqlite__stmt_t *stmt; if (err) { svn_error_t *err2; err2 = get_internal_statement(&stmt, db, STMT_INTERNAL_ROLLBACK_TO_SAVEPOINT_SVN); if (!err2) { err2 = svn_error_trace(svn_sqlite__step_done(stmt)); if (err2 && err2->apr_err == SVN_ERR_SQLITE_BUSY) { /* Ok, we have a major problem. Some statement is still open, which makes it impossible to release this savepoint. ### See huge comment in rollback_transaction() for further details */ err2 = svn_error_trace(reset_all_statements(db, err2)); err2 = svn_error_compose_create( svn_error_trace(svn_sqlite__step_done(stmt)), err2); } } err = svn_error_compose_create(err, err2); err2 = get_internal_statement(&stmt, db, STMT_INTERNAL_RELEASE_SAVEPOINT_SVN); if (!err2) err2 = svn_error_trace(svn_sqlite__step_done(stmt)); return svn_error_compose_create(err, err2); } SVN_ERR(get_internal_statement(&stmt, db, STMT_INTERNAL_RELEASE_SAVEPOINT_SVN)); /* ### Releasing a savepoint can fail and leave the db connection unusable; see svn_sqlite__finish_transaction(). */ return svn_error_trace(svn_sqlite__step_done(stmt)); }
/* The body of svn_sqlite__with_transaction() and svn_sqlite__with_immediate_transaction(), which see. */ static svn_error_t * with_transaction(svn_sqlite__db_t *db, svn_sqlite__transaction_callback_t cb_func, void *cb_baton, apr_pool_t *scratch_pool /* NULL allowed */) { svn_error_t *err; err = cb_func(cb_baton, db, scratch_pool); /* Commit or rollback the sqlite transaction. */ if (err) { svn_error_t *err2 = exec_sql(db, "ROLLBACK TRANSACTION;"); if (err2 && err2->apr_err == SVN_ERR_SQLITE_BUSY) { /* ### Houston, we have a problem! We are trying to rollback but we can't because some statements are still busy. This leaves the database unusable for future transactions as the current transaction is still open. As we are returning the actual error as the most relevant error in the chain, our caller might assume that it can retry/compensate on this error (e.g. SVN_WC_LOCKED), while in fact the SQLite database is unusable until the statements started within this transaction are reset and the transaction aborted. We try to compensate by resetting all prepared but unreset statements; but we leave the busy error in the chain anyway to help diagnosing the original error and help in finding where a reset statement is missing. */ err2 = reset_all_statements(db, err2); err2 = svn_error_compose_create( exec_sql(db, "ROLLBACK TRANSACTION;"), err2); } return svn_error_compose_create(err, err2); } return svn_error_trace(exec_sql(db, "COMMIT TRANSACTION;")); }
static svn_error_t * rollback_transaction(svn_sqlite__db_t *db, svn_error_t *error_to_wrap) { svn_sqlite__stmt_t *stmt; svn_error_t *err; err = get_internal_statement(&stmt, db, STMT_INTERNAL_ROLLBACK_TRANSACTION); if (!err) { err = svn_error_trace(svn_sqlite__step_done(stmt)); if (err && err->apr_err == SVN_ERR_SQLITE_BUSY) { /* ### Houston, we have a problem! We are trying to rollback but we can't because some statements are still busy. This leaves the database unusable for future transactions as the current transaction is still open. As we are returning the actual error as the most relevant error in the chain, our caller might assume that it can retry/compensate on this error (e.g. SVN_WC_LOCKED), while in fact the SQLite database is unusable until the statements started within this transaction are reset and the transaction aborted. We try to compensate by resetting all prepared but unreset statements; but we leave the busy error in the chain anyway to help diagnosing the original error and help in finding where a reset statement is missing. */ err = svn_error_trace(reset_all_statements(db, err)); err = svn_error_compose_create( svn_error_trace(svn_sqlite__step_done(stmt)), err); } } if (err) { /* Rollback failed, use a specific error code. */ err = svn_error_create(SVN_SQLITE__ERR_ROLLBACK_FAILED, err, _("SQLite transaction rollback failed")); } return svn_error_compose_create(error_to_wrap, err); }
svn_error_t * svn_sqlite__with_lock(svn_sqlite__db_t *db, svn_sqlite__transaction_callback_t cb_func, void *cb_baton, apr_pool_t *scratch_pool) { svn_error_t *err; int savepoint = db->savepoint_nr++; /* This buffer is plenty big to hold the SAVEPOINT and RELEASE commands. */ char buf[32]; snprintf(buf, sizeof(buf), "SAVEPOINT s%u", savepoint); SVN_ERR(exec_sql(db, buf)); err = cb_func(cb_baton, db, scratch_pool); if (err) { svn_error_t *err2; snprintf(buf, sizeof(buf), "ROLLBACK TO s%u", savepoint); err2 = exec_sql(db, buf); if (err2 && err2->apr_err == SVN_ERR_SQLITE_BUSY) { /* Ok, we have a major problem. Some statement is still open, which makes it impossible to release this savepoint. ### See huge comment in svn_sqlite__with_transaction for further details */ err2 = reset_all_statements(db, err2); err2 = svn_error_compose_create(exec_sql(db, buf), err2); } snprintf(buf, sizeof(buf), "RELEASE s%u", savepoint); err2 = svn_error_compose_create(exec_sql(db, buf), err2); return svn_error_trace(svn_error_compose_create(err, err2)); } snprintf(buf, sizeof(buf), "RELEASE s%u", savepoint); return svn_error_trace(exec_sql(db, buf)); }