/src/mozilla-central/storage/StorageBaseStatementInternal.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
2 | | * vim: sw=2 ts=2 sts=2 expandtab |
3 | | * This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "StorageBaseStatementInternal.h" |
8 | | |
9 | | #include "nsProxyRelease.h" |
10 | | |
11 | | #include "mozStorageBindingParamsArray.h" |
12 | | #include "mozStorageStatementData.h" |
13 | | #include "mozStorageAsyncStatementExecution.h" |
14 | | |
15 | | namespace mozilla { |
16 | | namespace storage { |
17 | | |
18 | | //////////////////////////////////////////////////////////////////////////////// |
19 | | //// Local Classes |
20 | | |
21 | | /** |
22 | | * Used to finalize an asynchronous statement on the background thread. |
23 | | */ |
24 | | class AsyncStatementFinalizer : public Runnable |
25 | | { |
26 | | public: |
27 | | /** |
28 | | * Constructor for the event. |
29 | | * |
30 | | * @param aStatement |
31 | | * We need the AsyncStatement to be able to get at the sqlite3_stmt; |
32 | | * we only access/create it on the async thread. |
33 | | * @param aConnection |
34 | | * We need the connection to know what thread to release the statement |
35 | | * on. We release the statement on that thread since releasing the |
36 | | * statement might end up releasing the connection too. |
37 | | */ |
38 | | AsyncStatementFinalizer(StorageBaseStatementInternal* aStatement, |
39 | | Connection* aConnection) |
40 | | : Runnable("storage::AsyncStatementFinalizer") |
41 | | , mStatement(aStatement) |
42 | | , mConnection(aConnection) |
43 | 0 | { |
44 | 0 | } |
45 | | |
46 | | NS_IMETHOD Run() override |
47 | 0 | { |
48 | 0 | if (mStatement->mAsyncStatement) { |
49 | 0 | sqlite3_finalize(mStatement->mAsyncStatement); |
50 | 0 | mStatement->mAsyncStatement = nullptr; |
51 | 0 | } |
52 | 0 |
|
53 | 0 | nsCOMPtr<nsIThread> targetThread(mConnection->threadOpenedOn); |
54 | 0 | NS_ProxyRelease( |
55 | 0 | "AsyncStatementFinalizer::mStatement", targetThread, mStatement.forget()); |
56 | 0 | return NS_OK; |
57 | 0 | } |
58 | | private: |
59 | | RefPtr<StorageBaseStatementInternal> mStatement; |
60 | | RefPtr<Connection> mConnection; |
61 | | }; |
62 | | |
63 | | /** |
64 | | * Finalize a sqlite3_stmt on the background thread for a statement whose |
65 | | * destructor was invoked and the statement was non-null. |
66 | | */ |
67 | | class LastDitchSqliteStatementFinalizer : public Runnable |
68 | | { |
69 | | public: |
70 | | /** |
71 | | * Event constructor. |
72 | | * |
73 | | * @param aConnection |
74 | | * Used to keep the connection alive. If we failed to do this, it |
75 | | * is possible that the statement going out of scope invoking us |
76 | | * might have the last reference to the connection and so trigger |
77 | | * an attempt to close the connection which is doomed to fail |
78 | | * (because the asynchronous execution thread must exist which will |
79 | | * trigger the failure case). |
80 | | * @param aStatement |
81 | | * The sqlite3_stmt to finalize. This object takes ownership / |
82 | | * responsibility for the instance and all other references to it |
83 | | * should be forgotten. |
84 | | */ |
85 | | LastDitchSqliteStatementFinalizer(RefPtr<Connection>& aConnection, |
86 | | sqlite3_stmt* aStatement) |
87 | | : Runnable("storage::LastDitchSqliteStatementFinalizer") |
88 | | , mConnection(aConnection) |
89 | | , mAsyncStatement(aStatement) |
90 | 0 | { |
91 | 0 | MOZ_ASSERT(aConnection, "You must provide a Connection"); |
92 | 0 | } |
93 | | |
94 | | NS_IMETHOD Run() override |
95 | 0 | { |
96 | 0 | (void)::sqlite3_finalize(mAsyncStatement); |
97 | 0 | mAsyncStatement = nullptr; |
98 | 0 |
|
99 | 0 | nsCOMPtr<nsIThread> target(mConnection->threadOpenedOn); |
100 | 0 | (void)::NS_ProxyRelease( |
101 | 0 | "LastDitchSqliteStatementFinalizer::mConnection", |
102 | 0 | target, mConnection.forget()); |
103 | 0 | return NS_OK; |
104 | 0 | } |
105 | | private: |
106 | | RefPtr<Connection> mConnection; |
107 | | sqlite3_stmt *mAsyncStatement; |
108 | | }; |
109 | | |
110 | | //////////////////////////////////////////////////////////////////////////////// |
111 | | //// StorageBaseStatementInternal |
112 | | |
113 | | StorageBaseStatementInternal::StorageBaseStatementInternal() |
114 | | : mNativeConnection(nullptr) |
115 | | , mAsyncStatement(nullptr) |
116 | 0 | { |
117 | 0 | } |
118 | | |
119 | | void |
120 | | StorageBaseStatementInternal::asyncFinalize() |
121 | 0 | { |
122 | 0 | nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget(); |
123 | 0 | if (target) { |
124 | 0 | // Attempt to finalize asynchronously |
125 | 0 | nsCOMPtr<nsIRunnable> event = |
126 | 0 | new AsyncStatementFinalizer(this, mDBConnection); |
127 | 0 |
|
128 | 0 | // Dispatch. Note that dispatching can fail, typically if |
129 | 0 | // we have a race condition with asyncClose(). It's ok, |
130 | 0 | // let asyncClose() win. |
131 | 0 | (void)target->Dispatch(event, NS_DISPATCH_NORMAL); |
132 | 0 | } |
133 | 0 | // If we cannot get the background thread, |
134 | 0 | // mozStorageConnection::AsyncClose() has already been called and |
135 | 0 | // the statement either has been or will be cleaned up by |
136 | 0 | // internalClose(). |
137 | 0 | } |
138 | | |
139 | | void |
140 | | StorageBaseStatementInternal::destructorAsyncFinalize() |
141 | 0 | { |
142 | 0 | if (!mAsyncStatement) |
143 | 0 | return; |
144 | 0 | |
145 | 0 | bool isOwningThread = false; |
146 | 0 | (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&isOwningThread); |
147 | 0 | if (isOwningThread) { |
148 | 0 | // If we are the owning thread (currently that means we're also the |
149 | 0 | // main thread), then we can get the async target and just dispatch |
150 | 0 | // to it. |
151 | 0 | nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget(); |
152 | 0 | if (target) { |
153 | 0 | nsCOMPtr<nsIRunnable> event = |
154 | 0 | new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement); |
155 | 0 | (void)target->Dispatch(event, NS_DISPATCH_NORMAL); |
156 | 0 | } |
157 | 0 | } else { |
158 | 0 | // If we're not the owning thread, assume we're the async thread, and |
159 | 0 | // just run the statement. |
160 | 0 | nsCOMPtr<nsIRunnable> event = |
161 | 0 | new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement); |
162 | 0 | (void)event->Run(); |
163 | 0 | } |
164 | 0 |
|
165 | 0 |
|
166 | 0 | // We might not be able to dispatch to the background thread, |
167 | 0 | // presumably because it is being shutdown. Since said shutdown will |
168 | 0 | // finalize the statement, we just need to clean-up around here. |
169 | 0 | mAsyncStatement = nullptr; |
170 | 0 | } |
171 | | |
172 | | NS_IMETHODIMP |
173 | | StorageBaseStatementInternal::NewBindingParamsArray( |
174 | | mozIStorageBindingParamsArray **_array |
175 | | ) |
176 | 0 | { |
177 | 0 | nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this); |
178 | 0 | NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY); |
179 | 0 |
|
180 | 0 | array.forget(_array); |
181 | 0 | return NS_OK; |
182 | 0 | } |
183 | | |
184 | | NS_IMETHODIMP |
185 | | StorageBaseStatementInternal::ExecuteAsync( |
186 | | mozIStorageStatementCallback *aCallback, |
187 | | mozIStoragePendingStatement **_stmt |
188 | | ) |
189 | 0 | { |
190 | 0 | // We used to call Connection::ExecuteAsync but it takes a |
191 | 0 | // mozIStorageBaseStatement signature because it is also a public API. Since |
192 | 0 | // our 'this' has no static concept of mozIStorageBaseStatement and Connection |
193 | 0 | // would just QI it back across to a StorageBaseStatementInternal and the |
194 | 0 | // actual logic is very simple, we now roll our own. |
195 | 0 | nsTArray<StatementData> stmts(1); |
196 | 0 | StatementData data; |
197 | 0 | nsresult rv = getAsynchronousStatementData(data); |
198 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
199 | 0 | NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY); |
200 | 0 |
|
201 | 0 | // Dispatch to the background |
202 | 0 | return AsyncExecuteStatements::execute(stmts, mDBConnection, |
203 | 0 | mNativeConnection, aCallback, _stmt); |
204 | 0 | } |
205 | | |
206 | | NS_IMETHODIMP |
207 | | StorageBaseStatementInternal::EscapeStringForLIKE( |
208 | | const nsAString &aValue, |
209 | | const char16_t aEscapeChar, |
210 | | nsAString &_escapedString |
211 | | ) |
212 | 0 | { |
213 | 0 | const char16_t MATCH_ALL('%'); |
214 | 0 | const char16_t MATCH_ONE('_'); |
215 | 0 |
|
216 | 0 | _escapedString.Truncate(0); |
217 | 0 |
|
218 | 0 | for (uint32_t i = 0; i < aValue.Length(); i++) { |
219 | 0 | if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL || |
220 | 0 | aValue[i] == MATCH_ONE) { |
221 | 0 | _escapedString += aEscapeChar; |
222 | 0 | } |
223 | 0 | _escapedString += aValue[i]; |
224 | 0 | } |
225 | 0 | return NS_OK; |
226 | 0 | } |
227 | | |
228 | | } // namespace storage |
229 | | } // namespace mozilla |