/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/** * This class wraps a transaction inside a given C++ scope, guaranteeing that * the transaction will be completed even if you have an exception or * return early. * * A common use is to create an instance with aCommitOnComplete = false * (rollback), then call Commit() on this object manually when your function * completes successfully. * * @note nested transactions are not supported by Sqlite, only nested * savepoints, so if a transaction is already in progress, this object creates * a nested savepoint to the existing transaction which is considered as * anonymous savepoint itself. However, aType and aAsyncCommit are ignored * in the case of nested savepoints. * * @param aConnection * The connection to create the transaction on. * @param aCommitOnComplete * Controls whether the transaction is committed or rolled back when * this object goes out of scope. * @param aType [optional] * The transaction type, as defined in mozIStorageConnection. Uses the * default transaction behavior for the connection if unspecified. * @param aAsyncCommit [optional] * Whether commit should be executed asynchronously on the helper thread. * This is a special option introduced as an interim solution to reduce * main-thread fsyncs in Places. Can only be used on main-thread. * * WARNING: YOU SHOULD _NOT_ WRITE NEW MAIN-THREAD CODE USING THIS! * * Notice that async commit might cause synchronous statements to fail * with SQLITE_BUSY. A possible mitigation strategy is to use * PRAGMA busy_timeout, but notice that might cause main-thread jank. * Finally, if the database is using WAL journaling mode, other * connections won't see the changes done in async committed transactions * until commit is complete. * * For all of the above reasons, this should only be used as an interim * solution and avoided completely if possible.
*/ class mozStorageTransaction { using SQLiteMutexAutoLock = mozilla::storage::SQLiteMutexAutoLock;
~mozStorageTransaction() { if (mConnection && mHasTransaction && !mCompleted) { if (mCommitOnComplete) {
mozilla::DebugOnly<nsresult> rv = Commit();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "A transaction didn't commit correctly");
} else {
mozilla::DebugOnly<nsresult> rv = Rollback();
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "A transaction didn't rollback correctly");
}
}
}
/** * Starts the transaction.
*/
nsresult Start() { // XXX We should probably get rid of mHasTransaction and use mConnection // for checking if a transaction has been started. However, we need to // first stop supporting null mConnection and also move aConnection from // the constructor to Start.
MOZ_DIAGNOSTIC_ASSERT(!mHasTransaction);
// XXX We should probably stop supporting null mConnection.
// XXX We should probably get rid of mCompleted and allow to start the // transaction again if it was already committed or rolled back. if (!mConnection || mCompleted) { return NS_OK;
}
// We nee to speculatively set the nesting level to be able to decide // if this is a top level transaction and to be able to generate the // savepoint name.
TransactionStarted(lock);
// If there's a failure we need to revert the speculatively set nesting // level on the connection. auto autoFinishTransaction =
mozilla::MakeScopeExit([&] { TransactionFinished(lock); });
nsAutoCString query;
if (TopLevelTransaction(lock)) {
query.Assign("BEGIN");
int32_t type = mType; if (type == mozIStorageConnection::TRANSACTION_DEFAULT) {
MOZ_ALWAYS_SUCCEEDS(mConnection->GetDefaultTransactionType(&type));
} switch (type) { case mozIStorageConnection::TRANSACTION_IMMEDIATE:
query.AppendLiteral(" IMMEDIATE"); break; case mozIStorageConnection::TRANSACTION_EXCLUSIVE:
query.AppendLiteral(" EXCLUSIVE"); break; case mozIStorageConnection::TRANSACTION_DEFERRED:
query.AppendLiteral(" DEFERRED"); break; default:
MOZ_ASSERT(false, "Unknown transaction type");
}
} else {
query.Assign("SAVEPOINT sp"_ns + IntToCString(mNestingLevel));
}
/** * Commits the transaction if one is in progress. If one is not in progress, * this is a NOP since the actual owner of the transaction outside of our * scope is in charge of finally committing or rolling back the transaction.
*/
nsresult Commit() { // XXX Assert instead of returning NS_OK if the transaction hasn't been // started. if (!mConnection || mCompleted || !mHasTransaction) return NS_OK;
if (TopLevelTransaction(lock)) { // TODO (bug 559659): this might fail with SQLITE_BUSY, but we don't // handle it, thus the transaction might stay open until the next COMMIT. if (mAsyncCommit) {
nsCOMPtr<mozIStoragePendingStatement> ps;
rv = mConnection->ExecuteSimpleSQLAsync("COMMIT"_ns, nullptr,
getter_AddRefs(ps));
} else {
rv = mConnection->ExecuteSimpleSQL("COMMIT"_ns);
}
} else {
rv = mConnection->ExecuteSimpleSQL("RELEASE sp"_ns +
IntToCString(mNestingLevel));
}
NS_ENSURE_SUCCESS(rv, rv);
TransactionFinished(lock);
return NS_OK;
}
/** * Rolls back the transaction if one is in progress. If one is not in * progress, this is a NOP since the actual owner of the transaction outside * of our scope is in charge of finally rolling back the transaction.
*/
nsresult Rollback() { // XXX Assert instead of returning NS_OK if the transaction hasn't been // started. if (!mConnection || mCompleted || !mHasTransaction) return NS_OK;
if (TopLevelTransaction(lock)) { // TODO (bug 1062823): from Sqlite 3.7.11 on, rollback won't ever return // a busy error, so this handling can be removed. do {
rv = mConnection->ExecuteSimpleSQL("ROLLBACK"_ns); if (rv == NS_ERROR_STORAGE_BUSY) (void)PR_Sleep(PR_INTERVAL_NO_WAIT);
} while (rv == NS_ERROR_STORAGE_BUSY);
} else { constauto nestingLevelCString = IntToCString(mNestingLevel);
rv = mConnection->ExecuteSimpleSQL( "ROLLBACK TO sp"_ns + nestingLevelCString + "; RELEASE sp"_ns +
nestingLevelCString);
}
/** * This class wraps a statement so that it is guaraneed to be reset when * this object goes out of scope. * * Note that this always just resets the statement. If the statement doesn't * need resetting, the reset operation is inexpensive.
*/ class MOZ_STACK_CLASS mozStorageStatementScoper { public: explicit mozStorageStatementScoper(mozIStorageStatement* aStatement)
: mStatement(aStatement) {}
~mozStorageStatementScoper() { if (mStatement) mStatement->Reset();
}
/** * Call this to make the statement not reset. You might do this if you know * that the statement has been reset.
*/ void Abandon() { mStatement = nullptr; }
// Use this to make queries uniquely identifiable in telemetry // statistics, especially PRAGMAs. We don't include __LINE__ so that // queries are stable in the face of source code changes. #define MOZ_STORAGE_UNIQUIFY_QUERY_STR "/* " __FILE__ " */ "
#endif/* MOZSTORAGEHELPER_H */
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.