|
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 nsRunnable |
|
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 : mStatement(aStatement) |
|
41 , mConnection(aConnection) |
|
42 { |
|
43 } |
|
44 |
|
45 NS_IMETHOD Run() |
|
46 { |
|
47 if (mStatement->mAsyncStatement) { |
|
48 (void)::sqlite3_finalize(mStatement->mAsyncStatement); |
|
49 mStatement->mAsyncStatement = nullptr; |
|
50 } |
|
51 (void)::NS_ProxyRelease(mConnection->threadOpenedOn, mStatement); |
|
52 return NS_OK; |
|
53 } |
|
54 private: |
|
55 nsRefPtr<StorageBaseStatementInternal> mStatement; |
|
56 nsRefPtr<Connection> mConnection; |
|
57 }; |
|
58 |
|
59 /** |
|
60 * Finalize a sqlite3_stmt on the background thread for a statement whose |
|
61 * destructor was invoked and the statement was non-null. |
|
62 */ |
|
63 class LastDitchSqliteStatementFinalizer : public nsRunnable |
|
64 { |
|
65 public: |
|
66 /** |
|
67 * Event constructor. |
|
68 * |
|
69 * @param aConnection |
|
70 * Used to keep the connection alive. If we failed to do this, it |
|
71 * is possible that the statement going out of scope invoking us |
|
72 * might have the last reference to the connection and so trigger |
|
73 * an attempt to close the connection which is doomed to fail |
|
74 * (because the asynchronous execution thread must exist which will |
|
75 * trigger the failure case). |
|
76 * @param aStatement |
|
77 * The sqlite3_stmt to finalize. This object takes ownership / |
|
78 * responsibility for the instance and all other references to it |
|
79 * should be forgotten. |
|
80 */ |
|
81 LastDitchSqliteStatementFinalizer(nsRefPtr<Connection> &aConnection, |
|
82 sqlite3_stmt *aStatement) |
|
83 : mConnection(aConnection) |
|
84 , mAsyncStatement(aStatement) |
|
85 { |
|
86 NS_PRECONDITION(aConnection, "You must provide a Connection"); |
|
87 } |
|
88 |
|
89 NS_IMETHOD Run() |
|
90 { |
|
91 (void)::sqlite3_finalize(mAsyncStatement); |
|
92 mAsyncStatement = nullptr; |
|
93 |
|
94 // Because of our ambiguous nsISupports we cannot use the NS_ProxyRelease |
|
95 // template helpers. |
|
96 Connection *rawConnection = nullptr; |
|
97 mConnection.swap(rawConnection); |
|
98 (void)::NS_ProxyRelease( |
|
99 rawConnection->threadOpenedOn, |
|
100 NS_ISUPPORTS_CAST(mozIStorageConnection *, rawConnection)); |
|
101 return NS_OK; |
|
102 } |
|
103 private: |
|
104 nsRefPtr<Connection> mConnection; |
|
105 sqlite3_stmt *mAsyncStatement; |
|
106 }; |
|
107 |
|
108 //////////////////////////////////////////////////////////////////////////////// |
|
109 //// StorageBaseStatementInternal |
|
110 |
|
111 StorageBaseStatementInternal::StorageBaseStatementInternal() |
|
112 : mAsyncStatement(nullptr) |
|
113 { |
|
114 } |
|
115 |
|
116 void |
|
117 StorageBaseStatementInternal::asyncFinalize() |
|
118 { |
|
119 nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget(); |
|
120 if (target) { |
|
121 // Attempt to finalize asynchronously |
|
122 nsCOMPtr<nsIRunnable> event = |
|
123 new AsyncStatementFinalizer(this, mDBConnection); |
|
124 |
|
125 // Dispatch. Note that dispatching can fail, typically if |
|
126 // we have a race condition with asyncClose(). It's ok, |
|
127 // let asyncClose() win. |
|
128 (void)target->Dispatch(event, NS_DISPATCH_NORMAL); |
|
129 } |
|
130 // If we cannot get the background thread, |
|
131 // mozStorageConnection::AsyncClose() has already been called and |
|
132 // the statement either has been or will be cleaned up by |
|
133 // internalClose(). |
|
134 } |
|
135 |
|
136 void |
|
137 StorageBaseStatementInternal::destructorAsyncFinalize() |
|
138 { |
|
139 if (!mAsyncStatement) |
|
140 return; |
|
141 |
|
142 // If we reach this point, our owner has not finalized this |
|
143 // statement, yet we are being destructed. If possible, we want to |
|
144 // auto-finalize it early, to release the resources early. |
|
145 nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget(); |
|
146 if (target) { |
|
147 // If we can get the async execution target, we can indeed finalize |
|
148 // the statement, as the connection is still open. |
|
149 bool isAsyncThread = false; |
|
150 (void)target->IsOnCurrentThread(&isAsyncThread); |
|
151 |
|
152 nsCOMPtr<nsIRunnable> event = |
|
153 new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement); |
|
154 if (isAsyncThread) { |
|
155 (void)event->Run(); |
|
156 } else { |
|
157 (void)target->Dispatch(event, NS_DISPATCH_NORMAL); |
|
158 } |
|
159 } |
|
160 |
|
161 // We might not be able to dispatch to the background thread, |
|
162 // presumably because it is being shutdown. Since said shutdown will |
|
163 // finalize the statement, we just need to clean-up around here. |
|
164 mAsyncStatement = nullptr; |
|
165 } |
|
166 |
|
167 NS_IMETHODIMP |
|
168 StorageBaseStatementInternal::NewBindingParamsArray( |
|
169 mozIStorageBindingParamsArray **_array |
|
170 ) |
|
171 { |
|
172 nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this); |
|
173 NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY); |
|
174 |
|
175 array.forget(_array); |
|
176 return NS_OK; |
|
177 } |
|
178 |
|
179 NS_IMETHODIMP |
|
180 StorageBaseStatementInternal::ExecuteAsync( |
|
181 mozIStorageStatementCallback *aCallback, |
|
182 mozIStoragePendingStatement **_stmt |
|
183 ) |
|
184 { |
|
185 // We used to call Connection::ExecuteAsync but it takes a |
|
186 // mozIStorageBaseStatement signature because it is also a public API. Since |
|
187 // our 'this' has no static concept of mozIStorageBaseStatement and Connection |
|
188 // would just QI it back across to a StorageBaseStatementInternal and the |
|
189 // actual logic is very simple, we now roll our own. |
|
190 nsTArray<StatementData> stmts(1); |
|
191 StatementData data; |
|
192 nsresult rv = getAsynchronousStatementData(data); |
|
193 NS_ENSURE_SUCCESS(rv, rv); |
|
194 NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY); |
|
195 |
|
196 // Dispatch to the background |
|
197 return AsyncExecuteStatements::execute(stmts, mDBConnection, |
|
198 mNativeConnection, aCallback, _stmt); |
|
199 } |
|
200 |
|
201 NS_IMETHODIMP |
|
202 StorageBaseStatementInternal::EscapeStringForLIKE( |
|
203 const nsAString &aValue, |
|
204 const char16_t aEscapeChar, |
|
205 nsAString &_escapedString |
|
206 ) |
|
207 { |
|
208 const char16_t MATCH_ALL('%'); |
|
209 const char16_t MATCH_ONE('_'); |
|
210 |
|
211 _escapedString.Truncate(0); |
|
212 |
|
213 for (uint32_t i = 0; i < aValue.Length(); i++) { |
|
214 if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL || |
|
215 aValue[i] == MATCH_ONE) { |
|
216 _escapedString += aEscapeChar; |
|
217 } |
|
218 _escapedString += aValue[i]; |
|
219 } |
|
220 return NS_OK; |
|
221 } |
|
222 |
|
223 } // namespace storage |
|
224 } // namespace mozilla |