Bug 628949 - Update visible region / glass regions after we paint. r=roc a=2.0.
[mozilla-central.git] / storage / src / mozStorageAsyncStatement.cpp
blob3652753988abf154e55ab2c4c1e9ff19adc00362
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 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozStorage.
18 * The Initial Developer of the Original Code is the Mozilla Foundation.
19 * Portions created by the Initial Developer are Copyright (C) 2010
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
24 * Shawn Wilsher <me@shawnwilsher.com>
25 * John Zhang <jzhang@aptana.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include <limits.h>
42 #include <stdio.h>
44 #include "nsError.h"
45 #include "nsMemory.h"
46 #include "nsProxyRelease.h"
47 #include "nsThreadUtils.h"
48 #include "nsIClassInfoImpl.h"
49 #include "nsIProgrammingLanguage.h"
50 #include "Variant.h"
52 #include "mozIStorageError.h"
54 #include "mozStorageBindingParams.h"
55 #include "mozStorageConnection.h"
56 #include "mozStorageAsyncStatementJSHelper.h"
57 #include "mozStorageAsyncStatementParams.h"
58 #include "mozStoragePrivateHelpers.h"
59 #include "mozStorageStatementRow.h"
60 #include "mozStorageStatement.h"
62 #include "prlog.h"
64 #ifdef PR_LOGGING
65 extern PRLogModuleInfo *gStorageLog;
66 #endif
68 namespace mozilla {
69 namespace storage {
71 ////////////////////////////////////////////////////////////////////////////////
72 //// nsIClassInfo
74 NS_IMPL_CI_INTERFACE_GETTER4(
75 AsyncStatement
76 , mozIStorageAsyncStatement
77 , mozIStorageBaseStatement
78 , mozIStorageBindingParams
79 , mozilla::storage::StorageBaseStatementInternal
82 class AsyncStatementClassInfo : public nsIClassInfo
84 public:
85 NS_DECL_ISUPPORTS
87 NS_IMETHODIMP
88 GetInterfaces(PRUint32 *_count, nsIID ***_array)
90 return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement)(_count, _array);
93 NS_IMETHODIMP
94 GetHelperForLanguage(PRUint32 aLanguage, nsISupports **_helper)
96 if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
97 static AsyncStatementJSHelper sJSHelper;
98 *_helper = &sJSHelper;
99 return NS_OK;
102 *_helper = nsnull;
103 return NS_OK;
106 NS_IMETHODIMP
107 GetContractID(char **_contractID)
109 *_contractID = nsnull;
110 return NS_OK;
113 NS_IMETHODIMP
114 GetClassDescription(char **_desc)
116 *_desc = nsnull;
117 return NS_OK;
120 NS_IMETHODIMP
121 GetClassID(nsCID **_id)
123 *_id = nsnull;
124 return NS_OK;
127 NS_IMETHODIMP
128 GetImplementationLanguage(PRUint32 *_language)
130 *_language = nsIProgrammingLanguage::CPLUSPLUS;
131 return NS_OK;
134 NS_IMETHODIMP
135 GetFlags(PRUint32 *_flags)
137 *_flags = nsnull;
138 return NS_OK;
141 NS_IMETHODIMP
142 GetClassIDNoAlloc(nsCID *_cid)
144 return NS_ERROR_NOT_AVAILABLE;
148 NS_IMETHODIMP_(nsrefcnt) AsyncStatementClassInfo::AddRef() { return 2; }
149 NS_IMETHODIMP_(nsrefcnt) AsyncStatementClassInfo::Release() { return 1; }
150 NS_IMPL_QUERY_INTERFACE1(AsyncStatementClassInfo, nsIClassInfo)
152 static AsyncStatementClassInfo sAsyncStatementClassInfo;
154 ////////////////////////////////////////////////////////////////////////////////
155 //// AsyncStatement
157 AsyncStatement::AsyncStatement()
158 : StorageBaseStatementInternal()
159 , mFinalized(false)
163 nsresult
164 AsyncStatement::initialize(Connection *aDBConnection,
165 const nsACString &aSQLStatement)
167 NS_ASSERTION(aDBConnection, "No database connection given!");
168 NS_ASSERTION(aDBConnection->GetNativeConnection(),
169 "We should never be called with a null sqlite3 database!");
171 mDBConnection = aDBConnection;
172 mSQLString = aSQLStatement;
174 #ifdef PR_LOGGING
175 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Inited async statement '%s' (0x%p)",
176 mSQLString.get()));
177 #endif
179 #ifdef DEBUG
180 // We want to try and test for LIKE and that consumers are using
181 // escapeStringForLIKE instead of just trusting user input. The idea to
182 // check to see if they are binding a parameter after like instead of just
183 // using a string. We only do this in debug builds because it's expensive!
184 const nsCaseInsensitiveCStringComparator c;
185 nsACString::const_iterator start, end, e;
186 aSQLStatement.BeginReading(start);
187 aSQLStatement.EndReading(end);
188 e = end;
189 while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start, e, c)) {
190 // We have a LIKE in here, so we perform our tests
191 // FindInReadable moves the iterator, so we have to get a new one for
192 // each test we perform.
193 nsACString::const_iterator s1, s2, s3;
194 s1 = s2 = s3 = start;
196 if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1, end, c) ||
197 ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2, end, c) ||
198 ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3, end, c))) {
199 // At this point, we didn't find a LIKE statement followed by ?, :,
200 // or @, all of which are valid characters for binding a parameter.
201 // We will warn the consumer that they may not be safely using LIKE.
202 NS_WARNING("Unsafe use of LIKE detected! Please ensure that you "
203 "are using mozIStorageAsyncStatement::escapeStringForLIKE "
204 "and that you are binding that result to the statement "
205 "to prevent SQL injection attacks.");
208 // resetting start and e
209 start = e;
210 e = end;
212 #endif
214 return NS_OK;
217 mozIStorageBindingParams *
218 AsyncStatement::getParams()
220 nsresult rv;
222 // If we do not have an array object yet, make it.
223 if (!mParamsArray) {
224 nsCOMPtr<mozIStorageBindingParamsArray> array;
225 rv = NewBindingParamsArray(getter_AddRefs(array));
226 NS_ENSURE_SUCCESS(rv, nsnull);
228 mParamsArray = static_cast<BindingParamsArray *>(array.get());
231 // If there isn't already any rows added, we'll have to add one to use.
232 if (mParamsArray->length() == 0) {
233 nsRefPtr<AsyncBindingParams> params(new AsyncBindingParams(mParamsArray));
234 NS_ENSURE_TRUE(params, nsnull);
236 rv = mParamsArray->AddParams(params);
237 NS_ENSURE_SUCCESS(rv, nsnull);
239 // We have to unlock our params because AddParams locks them. This is safe
240 // because no reference to the params object was, or ever will be given out.
241 params->unlock(nsnull);
243 // We also want to lock our array at this point - we don't want anything to
244 // be added to it.
245 mParamsArray->lock();
248 return *mParamsArray->begin();
252 * If we are here then we know there are no pending async executions relying on
253 * us (StatementData holds a reference to us; this also goes for our own
254 * AsyncStatementFinalizer which proxies its release to the calling thread) and
255 * so it is always safe to destroy our sqlite3_stmt if one exists. We can be
256 * destroyed on the caller thread by garbage-collection/reference counting or on
257 * the async thread by the last execution of a statement that already lost its
258 * main-thread refs.
260 AsyncStatement::~AsyncStatement()
262 destructorAsyncFinalize();
263 cleanupJSHelpers();
265 // If we are getting destroyed on the wrong thread, proxy the connection
266 // release to the right thread. I'm not sure why we do this.
267 PRBool onCallingThread = PR_FALSE;
268 (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onCallingThread);
269 if (!onCallingThread) {
270 // NS_ProxyRelase only magic forgets for us if mDBConnection is an
271 // nsCOMPtr. Which it is not; it's an nsRefPtr.
272 Connection *forgottenConn = nsnull;
273 mDBConnection.swap(forgottenConn);
274 (void)::NS_ProxyRelease(forgottenConn->threadOpenedOn,
275 static_cast<mozIStorageConnection *>(forgottenConn));
279 void
280 AsyncStatement::cleanupJSHelpers()
282 // We are considered dead at this point, so any wrappers for row or params
283 // need to lose their reference to us.
284 if (mStatementParamsHolder) {
285 nsCOMPtr<nsIXPConnectWrappedNative> wrapper =
286 do_QueryInterface(mStatementParamsHolder);
287 nsCOMPtr<mozIStorageStatementParams> iParams =
288 do_QueryWrappedNative(wrapper);
289 AsyncStatementParams *params =
290 static_cast<AsyncStatementParams *>(iParams.get());
291 params->mStatement = nsnull;
292 mStatementParamsHolder = nsnull;
296 ////////////////////////////////////////////////////////////////////////////////
297 //// nsISupports
299 NS_IMPL_THREADSAFE_ADDREF(AsyncStatement)
300 NS_IMPL_THREADSAFE_RELEASE(AsyncStatement)
302 NS_INTERFACE_MAP_BEGIN(AsyncStatement)
303 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncStatement)
304 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement)
305 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams)
306 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal)
307 if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
308 foundInterface = static_cast<nsIClassInfo *>(&sAsyncStatementClassInfo);
310 else
311 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageAsyncStatement)
312 NS_INTERFACE_MAP_END
315 ////////////////////////////////////////////////////////////////////////////////
316 //// StorageBaseStatementInternal
318 Connection *
319 AsyncStatement::getOwner()
321 return mDBConnection;
325 AsyncStatement::getAsyncStatement(sqlite3_stmt **_stmt)
327 #ifdef DEBUG
328 // Make sure we are never called on the connection's owning thread.
329 PRBool onOpenedThread = PR_FALSE;
330 (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
331 NS_ASSERTION(!onOpenedThread,
332 "We should only be called on the async thread!");
333 #endif
335 if (!mAsyncStatement) {
336 int rc = prepareStmt(mDBConnection->GetNativeConnection(), mSQLString,
337 &mAsyncStatement);
338 if (rc != SQLITE_OK) {
339 #ifdef PR_LOGGING
340 PR_LOG(gStorageLog, PR_LOG_ERROR,
341 ("Sqlite statement prepare error: %d '%s'", rc,
342 ::sqlite3_errmsg(mDBConnection->GetNativeConnection())));
343 PR_LOG(gStorageLog, PR_LOG_ERROR,
344 ("Statement was: '%s'", mSQLString.get()));
345 #endif
346 *_stmt = nsnull;
347 return rc;
350 #ifdef PR_LOGGING
351 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Initialized statement '%s' (0x%p)",
352 mSQLString.get(),
353 mAsyncStatement));
354 #endif
357 *_stmt = mAsyncStatement;
358 return SQLITE_OK;
361 nsresult
362 AsyncStatement::getAsynchronousStatementData(StatementData &_data)
364 if (mFinalized)
365 return NS_ERROR_UNEXPECTED;
367 // Pass null for the sqlite3_stmt; it will be requested on demand from the
368 // async thread.
369 _data = StatementData(nsnull, bindingParamsArray(), this);
371 return NS_OK;
374 already_AddRefed<mozIStorageBindingParams>
375 AsyncStatement::newBindingParams(mozIStorageBindingParamsArray *aOwner)
377 if (mFinalized)
378 return nsnull;
380 nsCOMPtr<mozIStorageBindingParams> params(new AsyncBindingParams(aOwner));
381 return params.forget();
385 ////////////////////////////////////////////////////////////////////////////////
386 //// mozIStorageAsyncStatement
388 // (nothing is specific to mozIStorageAsyncStatement)
390 ////////////////////////////////////////////////////////////////////////////////
391 //// StorageBaseStatementInternal
393 // proxy to StorageBaseStatementInternal using its define helper.
394 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
395 AsyncStatement,
396 if (mFinalized) return NS_ERROR_UNEXPECTED;)
398 NS_IMETHODIMP
399 AsyncStatement::Finalize()
401 if (mFinalized)
402 return NS_OK;
404 mFinalized = true;
406 #ifdef PR_LOGGING
407 PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Finalizing statement '%s'",
408 mSQLString.get()));
409 #endif
411 asyncFinalize();
412 cleanupJSHelpers();
414 return NS_OK;
417 NS_IMETHODIMP
418 AsyncStatement::BindParameters(mozIStorageBindingParamsArray *aParameters)
420 if (mFinalized)
421 return NS_ERROR_UNEXPECTED;
423 BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
424 if (array->getOwner() != this)
425 return NS_ERROR_UNEXPECTED;
427 if (array->length() == 0)
428 return NS_ERROR_UNEXPECTED;
430 mParamsArray = array;
431 mParamsArray->lock();
433 return NS_OK;
436 NS_IMETHODIMP
437 AsyncStatement::GetState(PRInt32 *_state)
439 if (mFinalized)
440 *_state = MOZ_STORAGE_STATEMENT_INVALID;
441 else
442 *_state = MOZ_STORAGE_STATEMENT_READY;
444 return NS_OK;
447 ////////////////////////////////////////////////////////////////////////////////
448 //// mozIStorageBindingParams
450 BOILERPLATE_BIND_PROXIES(
451 AsyncStatement,
452 if (mFinalized) return NS_ERROR_UNEXPECTED;
455 } // namespace storage
456 } // namespace mozilla