/work/obj-fuzz/dist/include/mozStorageHelper.h
Line | Count | Source (jump to first uncovered line) |
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/. */ |
5 | | |
6 | | #ifndef MOZSTORAGEHELPER_H |
7 | | #define MOZSTORAGEHELPER_H |
8 | | |
9 | | #include "nsAutoPtr.h" |
10 | | #include "nsString.h" |
11 | | #include "mozilla/DebugOnly.h" |
12 | | #include "nsIConsoleService.h" |
13 | | #include "nsIScriptError.h" |
14 | | |
15 | | #include "mozIStorageAsyncConnection.h" |
16 | | #include "mozIStorageConnection.h" |
17 | | #include "mozIStorageStatement.h" |
18 | | #include "mozIStoragePendingStatement.h" |
19 | | #include "nsError.h" |
20 | | #include "nsIXPConnect.h" |
21 | | |
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. |
26 | | * |
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. |
30 | | * |
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. |
35 | | * |
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. |
48 | | * |
49 | | * WARNING: YOU SHOULD _NOT_ WRITE NEW MAIN-THREAD CODE USING THIS! |
50 | | * |
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. |
57 | | * |
58 | | * For all of the above reasons, this should only be used as an interim |
59 | | * solution and avoided completely if possible. |
60 | | */ |
61 | | class mozStorageTransaction |
62 | | { |
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) |
73 | 0 | { |
74 | 0 | if (mConnection) { |
75 | 0 | nsAutoCString query("BEGIN"); |
76 | 0 | int32_t type = aType; |
77 | 0 | if (type == mozIStorageConnection::TRANSACTION_DEFAULT) { |
78 | 0 | MOZ_ALWAYS_SUCCEEDS(mConnection->GetDefaultTransactionType(&type)); |
79 | 0 | } |
80 | 0 | switch (type) { |
81 | 0 | case mozIStorageConnection::TRANSACTION_IMMEDIATE: |
82 | 0 | query.AppendLiteral(" IMMEDIATE"); |
83 | 0 | break; |
84 | 0 | case mozIStorageConnection::TRANSACTION_EXCLUSIVE: |
85 | 0 | query.AppendLiteral(" EXCLUSIVE"); |
86 | 0 | break; |
87 | 0 | case mozIStorageConnection::TRANSACTION_DEFERRED: |
88 | 0 | query.AppendLiteral(" DEFERRED"); |
89 | 0 | break; |
90 | 0 | default: |
91 | 0 | MOZ_ASSERT(false, "Unknown transaction type"); |
92 | 0 | } |
93 | 0 | // If a transaction is already in progress, this will fail, since Sqlite |
94 | 0 | // doesn't support nested transactions. |
95 | 0 | mHasTransaction = NS_SUCCEEDED(mConnection->ExecuteSimpleSQL(query)); |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | | ~mozStorageTransaction() |
100 | 0 | { |
101 | 0 | if (mConnection && mHasTransaction && !mCompleted) { |
102 | 0 | if (mCommitOnComplete) { |
103 | 0 | mozilla::DebugOnly<nsresult> rv = Commit(); |
104 | 0 | NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), |
105 | 0 | "A transaction didn't commit correctly"); |
106 | 0 | } |
107 | 0 | else { |
108 | 0 | mozilla::DebugOnly<nsresult> rv = Rollback(); |
109 | 0 | NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), |
110 | 0 | "A transaction didn't rollback correctly"); |
111 | 0 | } |
112 | 0 | } |
113 | 0 | } |
114 | | |
115 | | /** |
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. |
119 | | */ |
120 | | nsresult Commit() |
121 | 0 | { |
122 | 0 | if (!mConnection || mCompleted || !mHasTransaction) |
123 | 0 | return NS_OK; |
124 | 0 | mCompleted = true; |
125 | 0 |
|
126 | 0 | // TODO (bug 559659): this might fail with SQLITE_BUSY, but we don't handle |
127 | 0 | // it, thus the transaction might stay open until the next COMMIT. |
128 | 0 | nsresult rv; |
129 | 0 | if (mAsyncCommit) { |
130 | 0 | nsCOMPtr<mozIStoragePendingStatement> ps; |
131 | 0 | rv = mConnection->ExecuteSimpleSQLAsync(NS_LITERAL_CSTRING("COMMIT"), |
132 | 0 | nullptr, getter_AddRefs(ps)); |
133 | 0 | } |
134 | 0 | else { |
135 | 0 | rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT")); |
136 | 0 | } |
137 | 0 |
|
138 | 0 | if (NS_SUCCEEDED(rv)) |
139 | 0 | mHasTransaction = false; |
140 | 0 |
|
141 | 0 | return rv; |
142 | 0 | } |
143 | | |
144 | | /** |
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. |
148 | | */ |
149 | | nsresult Rollback() |
150 | 0 | { |
151 | 0 | if (!mConnection || mCompleted || !mHasTransaction) |
152 | 0 | return NS_OK; |
153 | 0 | mCompleted = true; |
154 | 0 |
|
155 | 0 | // TODO (bug 1062823): from Sqlite 3.7.11 on, rollback won't ever return |
156 | 0 | // a busy error, so this handling can be removed. |
157 | 0 | nsresult rv; |
158 | 0 | do { |
159 | 0 | rv = mConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK")); |
160 | 0 | if (rv == NS_ERROR_STORAGE_BUSY) |
161 | 0 | (void)PR_Sleep(PR_INTERVAL_NO_WAIT); |
162 | 0 | } while (rv == NS_ERROR_STORAGE_BUSY); |
163 | 0 |
|
164 | 0 | if (NS_SUCCEEDED(rv)) |
165 | 0 | mHasTransaction = false; |
166 | 0 |
|
167 | 0 | return rv; |
168 | 0 | } |
169 | | |
170 | | protected: |
171 | | nsCOMPtr<mozIStorageConnection> mConnection; |
172 | | bool mHasTransaction; |
173 | | bool mCommitOnComplete; |
174 | | bool mCompleted; |
175 | | bool mAsyncCommit; |
176 | | }; |
177 | | |
178 | | /** |
179 | | * This class wraps a statement so that it is guaraneed to be reset when |
180 | | * this object goes out of scope. |
181 | | * |
182 | | * Note that this always just resets the statement. If the statement doesn't |
183 | | * need resetting, the reset operation is inexpensive. |
184 | | */ |
185 | | class MOZ_STACK_CLASS mozStorageStatementScoper |
186 | | { |
187 | | public: |
188 | | explicit mozStorageStatementScoper(mozIStorageStatement* aStatement) |
189 | | : mStatement(aStatement) |
190 | 0 | { |
191 | 0 | } |
192 | | ~mozStorageStatementScoper() |
193 | 0 | { |
194 | 0 | if (mStatement) |
195 | 0 | mStatement->Reset(); |
196 | 0 | } |
197 | | |
198 | | /** |
199 | | * Call this to make the statement not reset. You might do this if you know |
200 | | * that the statement has been reset. |
201 | | */ |
202 | | void Abandon() |
203 | 0 | { |
204 | 0 | mStatement = nullptr; |
205 | 0 | } |
206 | | |
207 | | protected: |
208 | | nsCOMPtr<mozIStorageStatement> mStatement; |
209 | | }; |
210 | | |
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 | 0 | #define MOZ_STORAGE_UNIQUIFY_QUERY_STR "/* " __FILE__ " */ " |
215 | | |
216 | | #endif /* MOZSTORAGEHELPER_H */ |