Bug 1462329 [wpt PR 10991] - Server-Timing: test TAO:* for cross-origin resource...
[gecko.git] / storage / mozStorageHelper.h
blob1defbd86f695549225cc4fbbed490025cd781a70
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef MOZSTORAGEHELPER_H
7 #define MOZSTORAGEHELPER_H
9 #include "nsAutoPtr.h"
10 #include "nsString.h"
11 #include "mozilla/DebugOnly.h"
12 #include "nsIConsoleService.h"
13 #include "nsIScriptError.h"
15 #include "mozIStorageAsyncConnection.h"
16 #include "mozIStorageConnection.h"
17 #include "mozIStorageStatement.h"
18 #include "mozIStoragePendingStatement.h"
19 #include "nsError.h"
20 #include "nsIXPConnect.h"
22 /**
23 * This class wraps a transaction inside a given C++ scope, guaranteeing that
24 * the transaction will be completed even if you have an exception or
25 * return early.
27 * A common use is to create an instance with aCommitOnComplete = false (rollback),
28 * then call Commit() on this object manually when your function completes
29 * successfully.
31 * @note nested transactions are not supported by Sqlite, so if a transaction
32 * is already in progress, this object does nothing. Note that in this case,
33 * you may not get the transaction type you asked for, and you won't be able
34 * to rollback.
36 * @param aConnection
37 * The connection to create the transaction on.
38 * @param aCommitOnComplete
39 * Controls whether the transaction is committed or rolled back when
40 * this object goes out of scope.
41 * @param aType [optional]
42 * The transaction type, as defined in mozIStorageConnection. Uses the
43 * default transaction behavior for the connection if unspecified.
44 * @param aAsyncCommit [optional]
45 * Whether commit should be executed asynchronously on the helper thread.
46 * This is a special option introduced as an interim solution to reduce
47 * main-thread fsyncs in Places. Can only be used on main-thread.
49 * WARNING: YOU SHOULD _NOT_ WRITE NEW MAIN-THREAD CODE USING THIS!
51 * Notice that async commit might cause synchronous statements to fail
52 * with SQLITE_BUSY. A possible mitigation strategy is to use
53 * PRAGMA busy_timeout, but notice that might cause main-thread jank.
54 * Finally, if the database is using WAL journaling mode, other
55 * connections won't see the changes done in async committed transactions
56 * until commit is complete.
58 * For all of the above reasons, this should only be used as an interim
59 * solution and avoided completely if possible.
61 class mozStorageTransaction
63 public:
64 mozStorageTransaction(mozIStorageConnection* aConnection,
65 bool aCommitOnComplete,
66 int32_t aType = mozIStorageConnection::TRANSACTION_DEFAULT,
67 bool aAsyncCommit = false)
68 : mConnection(aConnection),
69 mHasTransaction(false),
70 mCommitOnComplete(aCommitOnComplete),
71 mCompleted(false),
72 mAsyncCommit(aAsyncCommit)
74 if (mConnection) {
75 nsAutoCString query("BEGIN");
76 int32_t type = aType;
77 if (type == mozIStorageConnection::TRANSACTION_DEFAULT) {
78 MOZ_ALWAYS_SUCCEEDS(mConnection->GetDefaultTransactionType(&type));
80 switch (type) {
81 case mozIStorageConnection::TRANSACTION_IMMEDIATE:
82 query.AppendLiteral(" IMMEDIATE");
83 break;
84 case mozIStorageConnection::TRANSACTION_EXCLUSIVE:
85 query.AppendLiteral(" EXCLUSIVE");
86 break;
87 case mozIStorageConnection::TRANSACTION_DEFERRED:
88 query.AppendLiteral(" DEFERRED");
89 break;
90 default:
91 MOZ_ASSERT(false, "Unknown transaction type");
93 // If a transaction is already in progress, this will fail, since Sqlite
94 // doesn't support nested transactions.
95 mHasTransaction = NS_SUCCEEDED(mConnection->ExecuteSimpleSQL(query));
99 ~mozStorageTransaction()
101 if (mConnection && mHasTransaction && !mCompleted) {
102 if (mCommitOnComplete) {
103 mozilla::DebugOnly<nsresult> rv = Commit();
104 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
105 "A transaction didn't commit correctly");
107 else {
108 mozilla::DebugOnly<nsresult> rv = Rollback();
109 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
110 "A transaction didn't rollback correctly");
116 * Commits the transaction if one is in progress. If one is not in progress,
117 * this is a NOP since the actual owner of the transaction outside of our
118 * scope is in charge of finally committing or rolling back the transaction.
120 nsresult Commit()
122 if (!mConnection || mCompleted || !mHasTransaction)
123 return NS_OK;
124 mCompleted = true;
126 // TODO (bug 559659): this might fail with SQLITE_BUSY, but we don't handle
127 // it, thus the transaction might stay open until the next COMMIT.
128 nsresult rv;
129 if (mAsyncCommit) {
130 nsCOMPtr<mozIStoragePendingStatement> ps;
131 rv = mConnection->ExecuteSimpleSQLAsync(NS_LITERAL_CSTRING("COMMIT"),
132 nullptr, getter_AddRefs(ps));
134 else {
135 rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT"));
138 if (NS_SUCCEEDED(rv))
139 mHasTransaction = false;
141 return rv;
145 * Rolls back the transaction if one is in progress. If one is not in progress,
146 * this is a NOP since the actual owner of the transaction outside of our
147 * scope is in charge of finally rolling back the transaction.
149 nsresult Rollback()
151 if (!mConnection || mCompleted || !mHasTransaction)
152 return NS_OK;
153 mCompleted = true;
155 // TODO (bug 1062823): from Sqlite 3.7.11 on, rollback won't ever return
156 // a busy error, so this handling can be removed.
157 nsresult rv = NS_OK;
158 do {
159 rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK"));
160 if (rv == NS_ERROR_STORAGE_BUSY)
161 (void)PR_Sleep(PR_INTERVAL_NO_WAIT);
162 } while (rv == NS_ERROR_STORAGE_BUSY);
164 if (NS_SUCCEEDED(rv))
165 mHasTransaction = false;
167 return rv;
170 protected:
171 nsCOMPtr<mozIStorageConnection> mConnection;
172 bool mHasTransaction;
173 bool mCommitOnComplete;
174 bool mCompleted;
175 bool mAsyncCommit;
179 * This class wraps a statement so that it is guaraneed to be reset when
180 * this object goes out of scope.
182 * Note that this always just resets the statement. If the statement doesn't
183 * need resetting, the reset operation is inexpensive.
185 class MOZ_STACK_CLASS mozStorageStatementScoper
187 public:
188 explicit mozStorageStatementScoper(mozIStorageStatement* aStatement)
189 : mStatement(aStatement)
192 ~mozStorageStatementScoper()
194 if (mStatement)
195 mStatement->Reset();
199 * Call this to make the statement not reset. You might do this if you know
200 * that the statement has been reset.
202 void Abandon()
204 mStatement = nullptr;
207 protected:
208 nsCOMPtr<mozIStorageStatement> mStatement;
211 // Use this to make queries uniquely identifiable in telemetry
212 // statistics, especially PRAGMAs. We don't include __LINE__ so that
213 // queries are stable in the face of source code changes.
214 #define MOZ_STORAGE_UNIQUIFY_QUERY_STR "/* " __FILE__ " */ "
216 #endif /* MOZSTORAGEHELPER_H */