Wed, 31 Dec 2014 13:27:57 +0100
Ignore runtime configuration files generated during quality assurance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
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/. */
7 #include <limits.h>
8 #include <stdio.h>
10 #include "nsError.h"
11 #include "nsMemory.h"
12 #include "nsProxyRelease.h"
13 #include "nsThreadUtils.h"
14 #include "nsIClassInfoImpl.h"
15 #include "nsIProgrammingLanguage.h"
16 #include "Variant.h"
18 #include "mozIStorageError.h"
20 #include "mozStorageBindingParams.h"
21 #include "mozStorageConnection.h"
22 #include "mozStorageAsyncStatementJSHelper.h"
23 #include "mozStorageAsyncStatementParams.h"
24 #include "mozStoragePrivateHelpers.h"
25 #include "mozStorageStatementRow.h"
26 #include "mozStorageStatement.h"
27 #include "nsDOMClassInfo.h"
29 #include "prlog.h"
31 #ifdef PR_LOGGING
32 extern PRLogModuleInfo *gStorageLog;
33 #endif
35 namespace mozilla {
36 namespace storage {
38 ////////////////////////////////////////////////////////////////////////////////
39 //// nsIClassInfo
41 NS_IMPL_CI_INTERFACE_GETTER(AsyncStatement,
42 mozIStorageAsyncStatement,
43 mozIStorageBaseStatement,
44 mozIStorageBindingParams,
45 mozilla::storage::StorageBaseStatementInternal)
47 class AsyncStatementClassInfo : public nsIClassInfo
48 {
49 public:
50 MOZ_CONSTEXPR AsyncStatementClassInfo() {}
52 NS_DECL_ISUPPORTS_INHERITED
54 NS_IMETHODIMP
55 GetInterfaces(uint32_t *_count, nsIID ***_array)
56 {
57 return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_count, _array);
58 }
60 NS_IMETHODIMP
61 GetHelperForLanguage(uint32_t aLanguage, nsISupports **_helper)
62 {
63 if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
64 static AsyncStatementJSHelper sJSHelper;
65 *_helper = &sJSHelper;
66 return NS_OK;
67 }
69 *_helper = nullptr;
70 return NS_OK;
71 }
73 NS_IMETHODIMP
74 GetContractID(char **_contractID)
75 {
76 *_contractID = nullptr;
77 return NS_OK;
78 }
80 NS_IMETHODIMP
81 GetClassDescription(char **_desc)
82 {
83 *_desc = nullptr;
84 return NS_OK;
85 }
87 NS_IMETHODIMP
88 GetClassID(nsCID **_id)
89 {
90 *_id = nullptr;
91 return NS_OK;
92 }
94 NS_IMETHODIMP
95 GetImplementationLanguage(uint32_t *_language)
96 {
97 *_language = nsIProgrammingLanguage::CPLUSPLUS;
98 return NS_OK;
99 }
101 NS_IMETHODIMP
102 GetFlags(uint32_t *_flags)
103 {
104 *_flags = 0;
105 return NS_OK;
106 }
108 NS_IMETHODIMP
109 GetClassIDNoAlloc(nsCID *_cid)
110 {
111 return NS_ERROR_NOT_AVAILABLE;
112 }
113 };
115 NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::AddRef() { return 2; }
116 NS_IMETHODIMP_(MozExternalRefCountType) AsyncStatementClassInfo::Release() { return 1; }
117 NS_IMPL_QUERY_INTERFACE(AsyncStatementClassInfo, nsIClassInfo)
119 static AsyncStatementClassInfo sAsyncStatementClassInfo;
121 ////////////////////////////////////////////////////////////////////////////////
122 //// AsyncStatement
124 AsyncStatement::AsyncStatement()
125 : StorageBaseStatementInternal()
126 , mFinalized(false)
127 {
128 }
130 nsresult
131 AsyncStatement::initialize(Connection *aDBConnection,
132 sqlite3 *aNativeConnection,
133 const nsACString &aSQLStatement)
134 {
135 MOZ_ASSERT(aDBConnection, "No database connection given!");
136 MOZ_ASSERT(!aDBConnection->isClosed(), "Database connection should be valid");
137 MOZ_ASSERT(aNativeConnection, "No native connection given!");
139 mDBConnection = aDBConnection;
140 mNativeConnection = aNativeConnection;
141 mSQLString = aSQLStatement;
143 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Inited async statement '%s' (0x%p)",
144 mSQLString.get()));
146 #ifdef DEBUG
147 // We want to try and test for LIKE and that consumers are using
148 // escapeStringForLIKE instead of just trusting user input. The idea to
149 // check to see if they are binding a parameter after like instead of just
150 // using a string. We only do this in debug builds because it's expensive!
151 const nsCaseInsensitiveCStringComparator c;
152 nsACString::const_iterator start, end, e;
153 aSQLStatement.BeginReading(start);
154 aSQLStatement.EndReading(end);
155 e = end;
156 while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) {
157 // We have a LIKE in here, so we perform our tests
158 // FindInReadable moves the iterator, so we have to get a new one for
159 // each test we perform.
160 nsACString::const_iterator s1, s2, s3;
161 s1 = s2 = s3 = start;
163 if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) ||
164 ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) ||
165 ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) {
166 // At this point, we didn't find a LIKE statement followed by ?, :,
167 // or @, all of which are valid characters for binding a parameter.
168 // We will warn the consumer that they may not be safely using LIKE.
169 NS_WARNING("Unsafe use of LIKE detected! Please ensure that you "
170 "are using mozIStorageAsyncStatement::escapeStringForLIKE "
171 "and that you are binding that result to the statement "
172 "to prevent SQL injection attacks.");
173 }
175 // resetting start and e
176 start = e;
177 e = end;
178 }
179 #endif
181 return NS_OK;
182 }
184 mozIStorageBindingParams *
185 AsyncStatement::getParams()
186 {
187 nsresult rv;
189 // If we do not have an array object yet, make it.
190 if (!mParamsArray) {
191 nsCOMPtr<mozIStorageBindingParamsArray> array;
192 rv = NewBindingParamsArray(getter_AddRefs(array));
193 NS_ENSURE_SUCCESS(rv, nullptr);
195 mParamsArray = static_cast<BindingParamsArray *>(array.get());
196 }
198 // If there isn't already any rows added, we'll have to add one to use.
199 if (mParamsArray->length() == 0) {
200 nsRefPtr<AsyncBindingParams> params(new AsyncBindingParams(mParamsArray));
201 NS_ENSURE_TRUE(params, nullptr);
203 rv = mParamsArray->AddParams(params);
204 NS_ENSURE_SUCCESS(rv, nullptr);
206 // We have to unlock our params because AddParams locks them. This is safe
207 // because no reference to the params object was, or ever will be given out.
208 params->unlock(nullptr);
210 // We also want to lock our array at this point - we don't want anything to
211 // be added to it.
212 mParamsArray->lock();
213 }
215 return *mParamsArray->begin();
216 }
218 /**
219 * If we are here then we know there are no pending async executions relying on
220 * us (StatementData holds a reference to us; this also goes for our own
221 * AsyncStatementFinalizer which proxies its release to the calling thread) and
222 * so it is always safe to destroy our sqlite3_stmt if one exists. We can be
223 * destroyed on the caller thread by garbage-collection/reference counting or on
224 * the async thread by the last execution of a statement that already lost its
225 * main-thread refs.
226 */
227 AsyncStatement::~AsyncStatement()
228 {
229 destructorAsyncFinalize();
230 cleanupJSHelpers();
232 // If we are getting destroyed on the wrong thread, proxy the connection
233 // release to the right thread. I'm not sure why we do this.
234 bool onCallingThread = false;
235 (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onCallingThread);
236 if (!onCallingThread) {
237 // NS_ProxyRelase only magic forgets for us if mDBConnection is an
238 // nsCOMPtr. Which it is not; it's an nsRefPtr.
239 Connection *forgottenConn = nullptr;
240 mDBConnection.swap(forgottenConn);
241 (void)::NS_ProxyRelease(forgottenConn->threadOpenedOn,
242 static_cast<mozIStorageConnection *>(forgottenConn));
243 }
244 }
246 void
247 AsyncStatement::cleanupJSHelpers()
248 {
249 // We are considered dead at this point, so any wrappers for row or params
250 // need to lose their reference to us.
251 if (mStatementParamsHolder) {
252 nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
253 do_QueryInterface(mStatementParamsHolder);
254 nsCOMPtr<mozIStorageStatementParams> iParams =
255 do_QueryWrappedNative(wrapper);
256 AsyncStatementParams *params =
257 static_cast<AsyncStatementParams *>(iParams.get());
258 params->mStatement = nullptr;
259 mStatementParamsHolder = nullptr;
260 }
261 }
263 ////////////////////////////////////////////////////////////////////////////////
264 //// nsISupports
266 NS_IMPL_ADDREF(AsyncStatement)
267 NS_IMPL_RELEASE(AsyncStatement)
269 NS_INTERFACE_MAP_BEGIN(AsyncStatement)
270 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncStatement)
271 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
272 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
273 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
274 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
275 foundInterface = static_cast<nsIClassInfo *>(&sAsyncStatementClassInfo);
276 }
277 else
278 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageAsyncStatement)
279 NS_INTERFACE_MAP_END
282 ////////////////////////////////////////////////////////////////////////////////
283 //// StorageBaseStatementInternal
285 Connection *
286 AsyncStatement::getOwner()
287 {
288 return mDBConnection;
289 }
291 int
292 AsyncStatement::getAsyncStatement(sqlite3_stmt **_stmt)
293 {
294 #ifdef DEBUG
295 // Make sure we are never called on the connection's owning thread.
296 bool onOpenedThread = false;
297 (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
298 NS_ASSERTION(!onOpenedThread,
299 "We should only be called on the async thread!");
300 #endif
302 if (!mAsyncStatement) {
303 int rc = mDBConnection->prepareStatement(mNativeConnection, mSQLString,
304 &mAsyncStatement);
305 if (rc != SQLITE_OK) {
306 PR_LOG(gStorageLog, PR_LOG_ERROR,
307 ("Sqlite statement prepare error: %d '%s'", rc,
308 ::sqlite3_errmsg(mNativeConnection)));
309 PR_LOG(gStorageLog, PR_LOG_ERROR,
310 ("Statement was: '%s'", mSQLString.get()));
311 *_stmt = nullptr;
312 return rc;
313 }
314 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Initialized statement '%s' (0x%p)",
315 mSQLString.get(),
316 mAsyncStatement));
317 }
319 *_stmt = mAsyncStatement;
320 return SQLITE_OK;
321 }
323 nsresult
324 AsyncStatement::getAsynchronousStatementData(StatementData &_data)
325 {
326 if (mFinalized)
327 return NS_ERROR_UNEXPECTED;
329 // Pass null for the sqlite3_stmt; it will be requested on demand from the
330 // async thread.
331 _data = StatementData(nullptr, bindingParamsArray(), this);
333 return NS_OK;
334 }
336 already_AddRefed<mozIStorageBindingParams>
337 AsyncStatement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
338 {
339 if (mFinalized)
340 return nullptr;
342 nsCOMPtr<mozIStorageBindingParams> params(new AsyncBindingParams(aOwner));
343 return params.forget();
344 }
347 ////////////////////////////////////////////////////////////////////////////////
348 //// mozIStorageAsyncStatement
350 // (nothing is specific to mozIStorageAsyncStatement)
352 ////////////////////////////////////////////////////////////////////////////////
353 //// StorageBaseStatementInternal
355 // proxy to StorageBaseStatementInternal using its define helper.
356 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
357 AsyncStatement,
358 if (mFinalized) return NS_ERROR_UNEXPECTED;)
360 NS_IMETHODIMP
361 AsyncStatement::Finalize()
362 {
363 if (mFinalized)
364 return NS_OK;
366 mFinalized = true;
368 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s'",
369 mSQLString.get()));
371 asyncFinalize();
372 cleanupJSHelpers();
374 return NS_OK;
375 }
377 NS_IMETHODIMP
378 AsyncStatement::BindParameters(mozIStorageBindingParamsArray *aParameters)
379 {
380 if (mFinalized)
381 return NS_ERROR_UNEXPECTED;
383 BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
384 if (array->getOwner() != this)
385 return NS_ERROR_UNEXPECTED;
387 if (array->length() == 0)
388 return NS_ERROR_UNEXPECTED;
390 mParamsArray = array;
391 mParamsArray->lock();
393 return NS_OK;
394 }
396 NS_IMETHODIMP
397 AsyncStatement::GetState(int32_t *_state)
398 {
399 if (mFinalized)
400 *_state = MOZ_STORAGE_STATEMENT_INVALID;
401 else
402 *_state = MOZ_STORAGE_STATEMENT_READY;
404 return NS_OK;
405 }
407 ////////////////////////////////////////////////////////////////////////////////
408 //// mozIStorageBindingParams
410 BOILERPLATE_BIND_PROXIES(
411 AsyncStatement,
412 if (mFinalized) return NS_ERROR_UNEXPECTED;
413 )
415 } // namespace storage
416 } // namespace mozilla