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
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.
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 ***** */
46 #include "nsProxyRelease.h"
47 #include "nsThreadUtils.h"
48 #include "nsIClassInfoImpl.h"
49 #include "nsIProgrammingLanguage.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"
65 extern PRLogModuleInfo
*gStorageLog
;
71 ////////////////////////////////////////////////////////////////////////////////
74 NS_IMPL_CI_INTERFACE_GETTER4(
76 , mozIStorageAsyncStatement
77 , mozIStorageBaseStatement
78 , mozIStorageBindingParams
79 , mozilla::storage::StorageBaseStatementInternal
82 class AsyncStatementClassInfo
: public nsIClassInfo
88 GetInterfaces(PRUint32
*_count
, nsIID
***_array
)
90 return NS_CI_INTERFACE_GETTER_NAME(AsyncStatement
)(_count
, _array
);
94 GetHelperForLanguage(PRUint32 aLanguage
, nsISupports
**_helper
)
96 if (aLanguage
== nsIProgrammingLanguage::JAVASCRIPT
) {
97 static AsyncStatementJSHelper sJSHelper
;
98 *_helper
= &sJSHelper
;
107 GetContractID(char **_contractID
)
109 *_contractID
= nsnull
;
114 GetClassDescription(char **_desc
)
121 GetClassID(nsCID
**_id
)
128 GetImplementationLanguage(PRUint32
*_language
)
130 *_language
= nsIProgrammingLanguage::CPLUSPLUS
;
135 GetFlags(PRUint32
*_flags
)
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 ////////////////////////////////////////////////////////////////////////////////
157 AsyncStatement::AsyncStatement()
158 : StorageBaseStatementInternal()
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
;
175 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Inited async statement '%s' (0x%p)",
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
);
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
217 mozIStorageBindingParams
*
218 AsyncStatement::getParams()
222 // If we do not have an array object yet, make it.
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
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
260 AsyncStatement::~AsyncStatement()
262 internalAsyncFinalize();
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(mDBConnection
->threadOpenedOn
, forgottenConn
);
279 AsyncStatement::cleanupJSHelpers()
281 // We are considered dead at this point, so any wrappers for row or params
282 // need to lose their reference to us.
283 if (mStatementParamsHolder
) {
284 nsCOMPtr
<nsIXPConnectWrappedNative
> wrapper
=
285 do_QueryInterface(mStatementParamsHolder
);
286 nsCOMPtr
<mozIStorageStatementParams
> iParams
=
287 do_QueryWrappedNative(wrapper
);
288 AsyncStatementParams
*params
=
289 static_cast<AsyncStatementParams
*>(iParams
.get());
290 params
->mStatement
= nsnull
;
291 mStatementParamsHolder
= nsnull
;
295 ////////////////////////////////////////////////////////////////////////////////
298 NS_IMPL_THREADSAFE_ADDREF(AsyncStatement
)
299 NS_IMPL_THREADSAFE_RELEASE(AsyncStatement
)
301 NS_INTERFACE_MAP_BEGIN(AsyncStatement
)
302 NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncStatement
)
303 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement
)
304 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams
)
305 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal
)
306 if (aIID
.Equals(NS_GET_IID(nsIClassInfo
))) {
307 foundInterface
= static_cast<nsIClassInfo
*>(&sAsyncStatementClassInfo
);
310 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, mozIStorageAsyncStatement
)
314 ////////////////////////////////////////////////////////////////////////////////
315 //// StorageBaseStatementInternal
318 AsyncStatement::getOwner()
320 return mDBConnection
;
324 AsyncStatement::getAsyncStatement(sqlite3_stmt
**_stmt
)
327 // Make sure we are never called on the connection's owning thread.
328 PRBool onOpenedThread
= PR_FALSE
;
329 (void)mDBConnection
->threadOpenedOn
->IsOnCurrentThread(&onOpenedThread
);
330 NS_ASSERTION(!onOpenedThread
,
331 "We should only be called on the async thread!");
334 if (!mAsyncStatement
) {
335 int rc
= prepareStmt(mDBConnection
->GetNativeConnection(), mSQLString
,
337 if (rc
!= SQLITE_OK
) {
339 PR_LOG(gStorageLog
, PR_LOG_ERROR
,
340 ("Sqlite statement prepare error: %d '%s'", rc
,
341 ::sqlite3_errmsg(mDBConnection
->GetNativeConnection())));
342 PR_LOG(gStorageLog
, PR_LOG_ERROR
,
343 ("Statement was: '%s'", mSQLString
.get()));
350 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Initialized statement '%s' (0x%p)",
356 *_stmt
= mAsyncStatement
;
361 AsyncStatement::getAsynchronousStatementData(StatementData
&_data
)
364 return NS_ERROR_UNEXPECTED
;
366 // Pass null for the sqlite3_stmt; it will be requested on demand from the
368 _data
= StatementData(nsnull
, bindingParamsArray(), this);
373 already_AddRefed
<mozIStorageBindingParams
>
374 AsyncStatement::newBindingParams(mozIStorageBindingParamsArray
*aOwner
)
379 nsCOMPtr
<mozIStorageBindingParams
> params(new AsyncBindingParams(aOwner
));
380 return params
.forget();
384 ////////////////////////////////////////////////////////////////////////////////
385 //// mozIStorageAsyncStatement
387 // (nothing is specific to mozIStorageAsyncStatement)
389 ////////////////////////////////////////////////////////////////////////////////
390 //// StorageBaseStatementInternal
392 // proxy to StorageBaseStatementInternal using its define helper.
393 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(
395 if (mFinalized
) return NS_ERROR_UNEXPECTED
;)
398 AsyncStatement::Finalize()
406 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Finalizing statement '%s'",
417 AsyncStatement::BindParameters(mozIStorageBindingParamsArray
*aParameters
)
420 return NS_ERROR_UNEXPECTED
;
422 BindingParamsArray
*array
= static_cast<BindingParamsArray
*>(aParameters
);
423 if (array
->getOwner() != this)
424 return NS_ERROR_UNEXPECTED
;
426 if (array
->length() == 0)
427 return NS_ERROR_UNEXPECTED
;
429 mParamsArray
= array
;
430 mParamsArray
->lock();
436 AsyncStatement::GetState(PRInt32
*_state
)
439 *_state
= MOZ_STORAGE_STATEMENT_INVALID
;
441 *_state
= MOZ_STORAGE_STATEMENT_READY
;
446 ////////////////////////////////////////////////////////////////////////////////
447 //// mozIStorageBindingParams
449 BOILERPLATE_BIND_PROXIES(
451 if (mFinalized
) return NS_ERROR_UNEXPECTED
;
454 } // namespace storage
455 } // namespace mozilla