michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef MOZSTORAGEHELPER_H michael@0: #define MOZSTORAGEHELPER_H michael@0: michael@0: #include "nsAutoPtr.h" michael@0: michael@0: #include "mozIStorageAsyncConnection.h" michael@0: #include "mozIStorageConnection.h" michael@0: #include "mozIStorageStatement.h" michael@0: #include "nsError.h" michael@0: michael@0: /** michael@0: * This class wraps a transaction inside a given C++ scope, guaranteeing that michael@0: * the transaction will be completed even if you have an exception or michael@0: * return early. michael@0: * michael@0: * aCommitOnComplete controls whether the transaction is committed or rolled michael@0: * back when it goes out of scope. A common use is to create an instance with michael@0: * commitOnComplete = FALSE (rollback), then call Commit on this object manually michael@0: * when your function completes successfully. michael@0: * michael@0: * Note that nested transactions are not supported by sqlite, so if a transaction michael@0: * is already in progress, this object does nothing. Note that in this case, michael@0: * you may not get the transaction type you ask for, and you won't be able michael@0: * to rollback. michael@0: * michael@0: * Note: This class is templatized to be also usable with internal data michael@0: * structures. External users of this class should generally use michael@0: * |mozStorageTransaction| instead. michael@0: */ michael@0: template michael@0: class mozStorageTransactionBase michael@0: { michael@0: public: michael@0: mozStorageTransactionBase(T* aConnection, michael@0: bool aCommitOnComplete, michael@0: int32_t aType = mozIStorageConnection::TRANSACTION_DEFERRED) michael@0: : mConnection(aConnection), michael@0: mHasTransaction(false), michael@0: mCommitOnComplete(aCommitOnComplete), michael@0: mCompleted(false) michael@0: { michael@0: // We won't try to get a transaction if one is already in progress. michael@0: if (mConnection) michael@0: mHasTransaction = NS_SUCCEEDED(mConnection->BeginTransactionAs(aType)); michael@0: } michael@0: ~mozStorageTransactionBase() michael@0: { michael@0: if (mConnection && mHasTransaction && ! mCompleted) { michael@0: if (mCommitOnComplete) michael@0: mConnection->CommitTransaction(); michael@0: else michael@0: mConnection->RollbackTransaction(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Commits the transaction if one is in progress. If one is not in progress, michael@0: * this is a NOP since the actual owner of the transaction outside of our michael@0: * scope is in charge of finally comitting or rolling back the transaction. michael@0: */ michael@0: nsresult Commit() michael@0: { michael@0: if (!mConnection || mCompleted) michael@0: return NS_OK; // no connection, or already done michael@0: mCompleted = true; michael@0: if (! mHasTransaction) michael@0: return NS_OK; // transaction not ours, ignore michael@0: nsresult rv = mConnection->CommitTransaction(); michael@0: if (NS_SUCCEEDED(rv)) michael@0: mHasTransaction = false; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /** michael@0: * Rolls back the transaction in progress. You should only call this function michael@0: * if this object has a real transaction (HasTransaction() = true) because michael@0: * otherwise, there is no transaction to roll back. michael@0: */ michael@0: nsresult Rollback() michael@0: { michael@0: if (!mConnection || mCompleted) michael@0: return NS_OK; // no connection, or already done michael@0: mCompleted = true; michael@0: if (! mHasTransaction) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // It is possible that a rollback will return busy, so we busy wait... michael@0: nsresult rv = NS_OK; michael@0: do { michael@0: rv = mConnection->RollbackTransaction(); michael@0: if (rv == NS_ERROR_STORAGE_BUSY) michael@0: (void)PR_Sleep(PR_INTERVAL_NO_WAIT); michael@0: } while (rv == NS_ERROR_STORAGE_BUSY); michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: mHasTransaction = false; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /** michael@0: * Returns whether this object wraps a real transaction. False means that michael@0: * this object doesn't do anything because there was already a transaction in michael@0: * progress when it was created. michael@0: */ michael@0: bool HasTransaction() michael@0: { michael@0: return mHasTransaction; michael@0: } michael@0: michael@0: /** michael@0: * This sets the default action (commit or rollback) when this object goes michael@0: * out of scope. michael@0: */ michael@0: void SetDefaultAction(bool aCommitOnComplete) michael@0: { michael@0: mCommitOnComplete = aCommitOnComplete; michael@0: } michael@0: michael@0: protected: michael@0: U mConnection; michael@0: bool mHasTransaction; michael@0: bool mCommitOnComplete; michael@0: bool mCompleted; michael@0: }; michael@0: michael@0: /** michael@0: * An instance of the mozStorageTransaction<> family dedicated michael@0: * to |mozIStorageConnection|. michael@0: */ michael@0: typedef mozStorageTransactionBase > michael@0: mozStorageTransaction; michael@0: michael@0: michael@0: michael@0: /** michael@0: * This class wraps a statement so that it is guaraneed to be reset when michael@0: * this object goes out of scope. michael@0: * michael@0: * Note that this always just resets the statement. If the statement doesn't michael@0: * need resetting, the reset operation is inexpensive. michael@0: */ michael@0: class MOZ_STACK_CLASS mozStorageStatementScoper michael@0: { michael@0: public: michael@0: mozStorageStatementScoper(mozIStorageStatement* aStatement) michael@0: : mStatement(aStatement) michael@0: { michael@0: } michael@0: ~mozStorageStatementScoper() michael@0: { michael@0: if (mStatement) michael@0: mStatement->Reset(); michael@0: } michael@0: michael@0: /** michael@0: * Call this to make the statement not reset. You might do this if you know michael@0: * that the statement has been reset. michael@0: */ michael@0: void Abandon() michael@0: { michael@0: mStatement = nullptr; michael@0: } michael@0: michael@0: protected: michael@0: nsCOMPtr mStatement; michael@0: }; michael@0: michael@0: // Use this to make queries uniquely identifiable in telemetry michael@0: // statistics, especially PRAGMAs. We don't include __LINE__ so that michael@0: // queries are stable in the face of source code changes. michael@0: #define MOZ_STORAGE_UNIQUIFY_QUERY_STR "/* " __FILE__ " */ " michael@0: michael@0: #endif /* MOZSTORAGEHELPER_H */