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 Oracle Corporation code.
18 * The Initial Developer of the Original Code is
20 * Portions created by the Initial Developer are Copyright (C) 2004
21 * the Initial Developer. All Rights Reserved.
24 * Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
25 * Shawn Wilsher <me@shawnwilsher.com>
26 * John Zhang <jzhang@aptana.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
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 "mozStorageStatementJSHelper.h"
57 #include "mozStoragePrivateHelpers.h"
58 #include "mozStorageStatementParams.h"
59 #include "mozStorageStatementRow.h"
60 #include "mozStorageStatement.h"
64 #include "mozilla/FunctionTimer.h"
67 extern PRLogModuleInfo
* gStorageLog
;
73 ////////////////////////////////////////////////////////////////////////////////
76 NS_IMPL_CI_INTERFACE_GETTER5(
79 mozIStorageBaseStatement
,
80 mozIStorageBindingParams
,
81 mozIStorageValueArray
,
82 mozilla::storage::StorageBaseStatementInternal
85 class StatementClassInfo
: public nsIClassInfo
91 GetInterfaces(PRUint32
*_count
, nsIID
***_array
)
93 return NS_CI_INTERFACE_GETTER_NAME(Statement
)(_count
, _array
);
97 GetHelperForLanguage(PRUint32 aLanguage
, nsISupports
**_helper
)
99 if (aLanguage
== nsIProgrammingLanguage::JAVASCRIPT
) {
100 static StatementJSHelper sJSHelper
;
101 *_helper
= &sJSHelper
;
110 GetContractID(char **_contractID
)
112 *_contractID
= nsnull
;
117 GetClassDescription(char **_desc
)
124 GetClassID(nsCID
**_id
)
131 GetImplementationLanguage(PRUint32
*_language
)
133 *_language
= nsIProgrammingLanguage::CPLUSPLUS
;
138 GetFlags(PRUint32
*_flags
)
145 GetClassIDNoAlloc(nsCID
*_cid
)
147 return NS_ERROR_NOT_AVAILABLE
;
151 NS_IMETHODIMP_(nsrefcnt
) StatementClassInfo::AddRef() { return 2; }
152 NS_IMETHODIMP_(nsrefcnt
) StatementClassInfo::Release() { return 1; }
153 NS_IMPL_QUERY_INTERFACE1(StatementClassInfo
, nsIClassInfo
)
155 static StatementClassInfo sStatementClassInfo
;
157 ////////////////////////////////////////////////////////////////////////////////
160 Statement::Statement()
161 : StorageBaseStatementInternal()
169 Statement::initialize(Connection
*aDBConnection
,
170 const nsACString
&aSQLStatement
)
172 NS_ASSERTION(aDBConnection
, "No database connection given!");
173 NS_ASSERTION(!mDBStatement
, "Statement already initialized!");
175 sqlite3
*db
= aDBConnection
->GetNativeConnection();
176 NS_ASSERTION(db
, "We should never be called with a null sqlite3 database!");
178 int srv
= prepareStmt(db
, PromiseFlatCString(aSQLStatement
), &mDBStatement
);
179 if (srv
!= SQLITE_OK
) {
181 PR_LOG(gStorageLog
, PR_LOG_ERROR
,
182 ("Sqlite statement prepare error: %d '%s'", srv
,
183 ::sqlite3_errmsg(db
)));
184 PR_LOG(gStorageLog
, PR_LOG_ERROR
,
185 ("Statement was: '%s'", PromiseFlatCString(aSQLStatement
).get()));
187 return NS_ERROR_FAILURE
;
191 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Initialized statement '%s' (0x%p)",
192 PromiseFlatCString(aSQLStatement
).get(),
196 mDBConnection
= aDBConnection
;
197 mParamCount
= ::sqlite3_bind_parameter_count(mDBStatement
);
198 mResultColumnCount
= ::sqlite3_column_count(mDBStatement
);
199 mColumnNames
.Clear();
201 for (PRUint32 i
= 0; i
< mResultColumnCount
; i
++) {
202 const char *name
= ::sqlite3_column_name(mDBStatement
, i
);
203 (void)mColumnNames
.AppendElement(nsDependentCString(name
));
207 // We want to try and test for LIKE and that consumers are using
208 // escapeStringForLIKE instead of just trusting user input. The idea to
209 // check to see if they are binding a parameter after like instead of just
210 // using a string. We only do this in debug builds because it's expensive!
211 const nsCaseInsensitiveCStringComparator c
;
212 nsACString::const_iterator start
, end
, e
;
213 aSQLStatement
.BeginReading(start
);
214 aSQLStatement
.EndReading(end
);
216 while (::FindInReadable(NS_LITERAL_CSTRING(" LIKE"), start
, e
, c
)) {
217 // We have a LIKE in here, so we perform our tests
218 // FindInReadable moves the iterator, so we have to get a new one for
219 // each test we perform.
220 nsACString::const_iterator s1
, s2
, s3
;
221 s1
= s2
= s3
= start
;
223 if (!(::FindInReadable(NS_LITERAL_CSTRING(" LIKE ?"), s1
, end
, c
) ||
224 ::FindInReadable(NS_LITERAL_CSTRING(" LIKE :"), s2
, end
, c
) ||
225 ::FindInReadable(NS_LITERAL_CSTRING(" LIKE @"), s3
, end
, c
))) {
226 // At this point, we didn't find a LIKE statement followed by ?, :,
227 // or @, all of which are valid characters for binding a parameter.
228 // We will warn the consumer that they may not be safely using LIKE.
229 NS_WARNING("Unsafe use of LIKE detected! Please ensure that you "
230 "are using mozIStorageStatement::escapeStringForLIKE "
231 "and that you are binding that result to the statement "
232 "to prevent SQL injection attacks.");
235 // resetting start and e
244 mozIStorageBindingParams
*
245 Statement::getParams()
249 // If we do not have an array object yet, make it.
251 nsCOMPtr
<mozIStorageBindingParamsArray
> array
;
252 rv
= NewBindingParamsArray(getter_AddRefs(array
));
253 NS_ENSURE_SUCCESS(rv
, nsnull
);
255 mParamsArray
= static_cast<BindingParamsArray
*>(array
.get());
258 // If there isn't already any rows added, we'll have to add one to use.
259 if (mParamsArray
->length() == 0) {
260 nsRefPtr
<BindingParams
> params(new BindingParams(mParamsArray
, this));
261 NS_ENSURE_TRUE(params
, nsnull
);
263 rv
= mParamsArray
->AddParams(params
);
264 NS_ENSURE_SUCCESS(rv
, nsnull
);
266 // We have to unlock our params because AddParams locks them. This is safe
267 // because no reference to the params object was, or ever will be given out.
268 params
->unlock(this);
270 // We also want to lock our array at this point - we don't want anything to
271 // be added to it. Nothing has, or will ever get a reference to it, but we
272 // will get additional safety checks via assertions by doing this.
273 mParamsArray
->lock();
276 return *mParamsArray
->begin();
279 Statement::~Statement()
281 (void)internalFinalize(true);
284 ////////////////////////////////////////////////////////////////////////////////
287 NS_IMPL_THREADSAFE_ADDREF(Statement
)
288 NS_IMPL_THREADSAFE_RELEASE(Statement
)
290 NS_INTERFACE_MAP_BEGIN(Statement
)
291 NS_INTERFACE_MAP_ENTRY(mozIStorageStatement
)
292 NS_INTERFACE_MAP_ENTRY(mozIStorageBaseStatement
)
293 NS_INTERFACE_MAP_ENTRY(mozIStorageBindingParams
)
294 NS_INTERFACE_MAP_ENTRY(mozIStorageValueArray
)
295 NS_INTERFACE_MAP_ENTRY(mozilla::storage::StorageBaseStatementInternal
)
296 if (aIID
.Equals(NS_GET_IID(nsIClassInfo
))) {
297 foundInterface
= static_cast<nsIClassInfo
*>(&sStatementClassInfo
);
300 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, mozIStorageStatement
)
304 ////////////////////////////////////////////////////////////////////////////////
305 //// StorageBaseStatementInternal
308 Statement::getOwner()
310 return mDBConnection
;
314 Statement::getAsyncStatement(sqlite3_stmt
**_stmt
)
316 // If we have no statement, we shouldn't be calling this method!
317 NS_ASSERTION(mDBStatement
!= NULL
, "We have no statement to clone!");
319 // If we do not yet have a cached async statement, clone our statement now.
320 if (!mAsyncStatement
) {
321 nsDependentCString
sql(::sqlite3_sql(mDBStatement
));
322 int rc
= prepareStmt(mDBConnection
->GetNativeConnection(), sql
,
324 if (rc
!= SQLITE_OK
) {
330 PR_LOG(gStorageLog
, PR_LOG_NOTICE
,
331 ("Cloned statement 0x%p to 0x%p", mDBStatement
, mAsyncStatement
));
335 *_stmt
= mAsyncStatement
;
340 Statement::getAsynchronousStatementData(StatementData
&_data
)
343 return NS_ERROR_UNEXPECTED
;
346 int rc
= getAsyncStatement(&stmt
);
348 return convertResultCode(rc
);
350 _data
= StatementData(stmt
, bindingParamsArray(), this);
355 already_AddRefed
<mozIStorageBindingParams
>
356 Statement::newBindingParams(mozIStorageBindingParamsArray
*aOwner
)
358 nsCOMPtr
<mozIStorageBindingParams
> params
= new BindingParams(aOwner
, this);
359 return params
.forget();
363 ////////////////////////////////////////////////////////////////////////////////
364 //// mozIStorageStatement
366 // proxy to StorageBaseStatementInternal using its define helper.
367 MIXIN_IMPL_STORAGEBASESTATEMENTINTERNAL(Statement
, (void)0;)
370 Statement::Clone(mozIStorageStatement
**_statement
)
372 nsRefPtr
<Statement
> statement(new Statement());
373 NS_ENSURE_TRUE(statement
, NS_ERROR_OUT_OF_MEMORY
);
375 nsCAutoString
sql(::sqlite3_sql(mDBStatement
));
376 nsresult rv
= statement
->initialize(mDBConnection
, sql
);
377 NS_ENSURE_SUCCESS(rv
, rv
);
379 statement
.forget(_statement
);
384 Statement::Finalize()
386 return internalFinalize(false);
390 Statement::internalFinalize(bool aDestructing
)
396 PR_LOG(gStorageLog
, PR_LOG_NOTICE
, ("Finalizing statement '%s'",
397 ::sqlite3_sql(mDBStatement
)));
400 int srv
= ::sqlite3_finalize(mDBStatement
);
403 if (mAsyncStatement
) {
404 // If the destructor called us, there are no pending async statements (they
405 // hold a reference to us) and we can/must just kill the statement directly.
407 destructorAsyncFinalize();
412 // We are considered dead at this point, so any wrappers for row or params
413 // need to lose their reference to us.
414 if (mStatementParamsHolder
) {
415 nsCOMPtr
<nsIXPConnectWrappedNative
> wrapper
=
416 do_QueryInterface(mStatementParamsHolder
);
417 nsCOMPtr
<mozIStorageStatementParams
> iParams
=
418 do_QueryWrappedNative(wrapper
);
419 StatementParams
*params
= static_cast<StatementParams
*>(iParams
.get());
420 params
->mStatement
= nsnull
;
421 mStatementParamsHolder
= nsnull
;
424 if (mStatementRowHolder
) {
425 nsCOMPtr
<nsIXPConnectWrappedNative
> wrapper
=
426 do_QueryInterface(mStatementRowHolder
);
427 nsCOMPtr
<mozIStorageStatementRow
> iRow
=
428 do_QueryWrappedNative(wrapper
);
429 StatementRow
*row
= static_cast<StatementRow
*>(iRow
.get());
430 row
->mStatement
= nsnull
;
431 mStatementRowHolder
= nsnull
;
434 return convertResultCode(srv
);
438 Statement::GetParameterCount(PRUint32
*_parameterCount
)
441 return NS_ERROR_NOT_INITIALIZED
;
443 *_parameterCount
= mParamCount
;
448 Statement::GetParameterName(PRUint32 aParamIndex
,
452 return NS_ERROR_NOT_INITIALIZED
;
453 ENSURE_INDEX_VALUE(aParamIndex
, mParamCount
);
455 const char *name
= ::sqlite3_bind_parameter_name(mDBStatement
,
458 // this thing had no name, so fake one
459 nsCAutoString
name(":");
460 name
.AppendInt(aParamIndex
);
464 _name
.Assign(nsDependentCString(name
));
471 Statement::GetParameterIndex(const nsACString
&aName
,
475 return NS_ERROR_NOT_INITIALIZED
;
477 // We do not accept any forms of names other than ":name", but we need to add
478 // the colon for SQLite.
479 nsCAutoString
name(":");
481 int ind
= ::sqlite3_bind_parameter_index(mDBStatement
,
482 PromiseFlatCString(name
).get());
483 if (ind
== 0) // Named parameter not found.
484 return NS_ERROR_INVALID_ARG
;
486 *_index
= ind
- 1; // SQLite indexes are 1-based, we are 0-based.
492 Statement::GetColumnCount(PRUint32
*_columnCount
)
495 return NS_ERROR_NOT_INITIALIZED
;
497 *_columnCount
= mResultColumnCount
;
502 Statement::GetColumnName(PRUint32 aColumnIndex
,
506 return NS_ERROR_NOT_INITIALIZED
;
507 ENSURE_INDEX_VALUE(aColumnIndex
, mResultColumnCount
);
509 const char *cname
= ::sqlite3_column_name(mDBStatement
, aColumnIndex
);
510 _name
.Assign(nsDependentCString(cname
));
516 Statement::GetColumnIndex(const nsACString
&aName
,
520 return NS_ERROR_NOT_INITIALIZED
;
522 // Surprisingly enough, SQLite doesn't provide an API for this. We have to
523 // determine it ourselves sadly.
524 for (PRUint32 i
= 0; i
< mResultColumnCount
; i
++) {
525 if (mColumnNames
[i
].Equals(aName
)) {
531 return NS_ERROR_INVALID_ARG
;
538 return NS_ERROR_NOT_INITIALIZED
;
541 PR_LOG(gStorageLog
, PR_LOG_DEBUG
, ("Resetting statement: '%s'",
542 ::sqlite3_sql(mDBStatement
)));
544 checkAndLogStatementPerformance(mDBStatement
);
547 mParamsArray
= nsnull
;
548 (void)sqlite3_reset(mDBStatement
);
549 (void)sqlite3_clear_bindings(mDBStatement
);
557 Statement::BindParameters(mozIStorageBindingParamsArray
*aParameters
)
560 return NS_ERROR_NOT_INITIALIZED
;
562 BindingParamsArray
*array
= static_cast<BindingParamsArray
*>(aParameters
);
563 if (array
->getOwner() != this)
564 return NS_ERROR_UNEXPECTED
;
566 if (array
->length() == 0)
567 return NS_ERROR_UNEXPECTED
;
569 mParamsArray
= array
;
570 mParamsArray
->lock();
579 return NS_ERROR_NOT_INITIALIZED
;
582 nsresult rv
= ExecuteStep(&ret
);
583 nsresult rv2
= Reset();
585 return NS_FAILED(rv
) ? rv
: rv2
;
589 Statement::ExecuteStep(PRBool
*_moreResults
)
592 return NS_ERROR_NOT_INITIALIZED
;
594 NS_TIME_FUNCTION_MIN_FMT(5, "mozIStorageStatement::ExecuteStep(%s) (0x%p)",
595 mDBConnection
->getFilename().get(), mDBStatement
);
597 // Bind any parameters first before executing.
599 // If we have more than one row of parameters to bind, they shouldn't be
600 // calling this method (and instead use executeAsync).
601 if (mParamsArray
->length() != 1)
602 return NS_ERROR_UNEXPECTED
;
604 BindingParamsArray::iterator row
= mParamsArray
->begin();
605 nsCOMPtr
<IStorageBindingParamsInternal
> bindingInternal
=
606 do_QueryInterface(*row
);
607 nsCOMPtr
<mozIStorageError
> error
= bindingInternal
->bind(mDBStatement
);
610 (void)error
->GetResult(&srv
);
611 return convertResultCode(srv
);
614 // We have bound, so now we can clear our array.
615 mParamsArray
= nsnull
;
617 int srv
= stepStmt(mDBStatement
);
620 if (srv
!= SQLITE_ROW
&& srv
!= SQLITE_DONE
) {
621 nsCAutoString errStr
;
622 (void)mDBConnection
->GetLastErrorString(errStr
);
623 PR_LOG(gStorageLog
, PR_LOG_DEBUG
,
624 ("Statement::ExecuteStep error: %s", errStr
.get()));
628 // SQLITE_ROW and SQLITE_DONE are non-errors
629 if (srv
== SQLITE_ROW
) {
632 *_moreResults
= PR_TRUE
;
635 else if (srv
== SQLITE_DONE
) {
636 // statement is done (no row returned)
638 *_moreResults
= PR_FALSE
;
641 else if (srv
== SQLITE_BUSY
|| srv
== SQLITE_MISUSE
) {
642 mExecuting
= PR_FALSE
;
644 else if (mExecuting
) {
646 PR_LOG(gStorageLog
, PR_LOG_ERROR
,
647 ("SQLite error after mExecuting was true!"));
649 mExecuting
= PR_FALSE
;
652 return convertResultCode(srv
);
656 Statement::GetState(PRInt32
*_state
)
659 *_state
= MOZ_STORAGE_STATEMENT_INVALID
;
661 *_state
= MOZ_STORAGE_STATEMENT_EXECUTING
;
663 *_state
= MOZ_STORAGE_STATEMENT_READY
;
669 Statement::GetColumnDecltype(PRUint32 aParamIndex
,
670 nsACString
&_declType
)
673 return NS_ERROR_NOT_INITIALIZED
;
675 ENSURE_INDEX_VALUE(aParamIndex
, mResultColumnCount
);
677 _declType
.Assign(::sqlite3_column_decltype(mDBStatement
, aParamIndex
));
681 ////////////////////////////////////////////////////////////////////////////////
682 //// mozIStorageValueArray (now part of mozIStorageStatement too)
685 Statement::GetNumEntries(PRUint32
*_length
)
687 *_length
= mResultColumnCount
;
692 Statement::GetTypeOfIndex(PRUint32 aIndex
,
696 return NS_ERROR_NOT_INITIALIZED
;
698 ENSURE_INDEX_VALUE(aIndex
, mResultColumnCount
);
701 return NS_ERROR_UNEXPECTED
;
703 int t
= ::sqlite3_column_type(mDBStatement
, aIndex
);
706 *_type
= mozIStorageStatement::VALUE_TYPE_INTEGER
;
709 *_type
= mozIStorageStatement::VALUE_TYPE_FLOAT
;
712 *_type
= mozIStorageStatement::VALUE_TYPE_TEXT
;
715 *_type
= mozIStorageStatement::VALUE_TYPE_BLOB
;
718 *_type
= mozIStorageStatement::VALUE_TYPE_NULL
;
721 return NS_ERROR_FAILURE
;
728 Statement::GetInt32(PRUint32 aIndex
,
732 return NS_ERROR_NOT_INITIALIZED
;
734 ENSURE_INDEX_VALUE(aIndex
, mResultColumnCount
);
737 return NS_ERROR_UNEXPECTED
;
739 *_value
= ::sqlite3_column_int(mDBStatement
, aIndex
);
744 Statement::GetInt64(PRUint32 aIndex
,
748 return NS_ERROR_NOT_INITIALIZED
;
750 ENSURE_INDEX_VALUE(aIndex
, mResultColumnCount
);
753 return NS_ERROR_UNEXPECTED
;
755 *_value
= ::sqlite3_column_int64(mDBStatement
, aIndex
);
761 Statement::GetDouble(PRUint32 aIndex
,
765 return NS_ERROR_NOT_INITIALIZED
;
767 ENSURE_INDEX_VALUE(aIndex
, mResultColumnCount
);
770 return NS_ERROR_UNEXPECTED
;
772 *_value
= ::sqlite3_column_double(mDBStatement
, aIndex
);
778 Statement::GetUTF8String(PRUint32 aIndex
,
781 // Get type of Index will check aIndex for us, so we don't have to.
783 nsresult rv
= GetTypeOfIndex(aIndex
, &type
);
784 NS_ENSURE_SUCCESS(rv
, rv
);
785 if (type
== mozIStorageStatement::VALUE_TYPE_NULL
) {
786 // NULL columns should have IsVoid set to distinguish them from the empty
789 _value
.SetIsVoid(PR_TRUE
);
793 reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement
,
795 _value
.Assign(value
, ::sqlite3_column_bytes(mDBStatement
, aIndex
));
801 Statement::GetString(PRUint32 aIndex
,
804 // Get type of Index will check aIndex for us, so we don't have to.
806 nsresult rv
= GetTypeOfIndex(aIndex
, &type
);
807 NS_ENSURE_SUCCESS(rv
, rv
);
808 if (type
== mozIStorageStatement::VALUE_TYPE_NULL
) {
809 // NULL columns should have IsVoid set to distinguish them from the empty
812 _value
.SetIsVoid(PR_TRUE
);
814 const PRUnichar
*value
=
815 static_cast<const PRUnichar
*>(::sqlite3_column_text16(mDBStatement
,
817 _value
.Assign(value
, ::sqlite3_column_bytes16(mDBStatement
, aIndex
) / 2);
823 Statement::GetBlob(PRUint32 aIndex
,
828 return NS_ERROR_NOT_INITIALIZED
;
830 ENSURE_INDEX_VALUE(aIndex
, mResultColumnCount
);
833 return NS_ERROR_UNEXPECTED
;
835 int size
= ::sqlite3_column_bytes(mDBStatement
, aIndex
);
838 blob
= nsMemory::Clone(::sqlite3_column_blob(mDBStatement
, aIndex
), size
);
839 NS_ENSURE_TRUE(blob
, NS_ERROR_OUT_OF_MEMORY
);
842 *_blob
= static_cast<PRUint8
*>(blob
);
848 Statement::GetSharedUTF8String(PRUint32 aIndex
,
853 *_length
= ::sqlite3_column_bytes(mDBStatement
, aIndex
);
855 *_value
= reinterpret_cast<const char *>(::sqlite3_column_text(mDBStatement
,
861 Statement::GetSharedString(PRUint32 aIndex
,
863 const PRUnichar
**_value
)
866 *_length
= ::sqlite3_column_bytes16(mDBStatement
, aIndex
);
868 *_value
= static_cast<const PRUnichar
*>(::sqlite3_column_text16(mDBStatement
,
874 Statement::GetSharedBlob(PRUint32 aIndex
,
876 const PRUint8
**_blob
)
878 *_size
= ::sqlite3_column_bytes(mDBStatement
, aIndex
);
879 *_blob
= static_cast<const PRUint8
*>(::sqlite3_column_blob(mDBStatement
,
885 Statement::GetIsNull(PRUint32 aIndex
,
888 // Get type of Index will check aIndex for us, so we don't have to.
890 nsresult rv
= GetTypeOfIndex(aIndex
, &type
);
891 NS_ENSURE_SUCCESS(rv
, rv
);
892 *_isNull
= (type
== mozIStorageStatement::VALUE_TYPE_NULL
);
896 ////////////////////////////////////////////////////////////////////////////////
897 //// mozIStorageBindingParams
899 BOILERPLATE_BIND_PROXIES(
901 if (!mDBStatement
) return NS_ERROR_NOT_INITIALIZED
;
904 } // namespace storage
905 } // namespace mozilla