--- a/storage/public/Makefile.in
+++ b/storage/public/Makefile.in
@@ -57,16 +57,18 @@ XPIDLSRCS = \
mozIStorageStatement.idl \
mozIStorageStatementWrapper.idl \
mozIStorageValueArray.idl \
mozIStorageResultSet.idl \
mozIStorageRow.idl \
mozIStorageError.idl \
mozIStorageStatementCallback.idl \
mozIStoragePendingStatement.idl \
+ mozIStorageBindingParamsArray.idl \
+ mozIStorageBindingParams.idl \
$(NULL)
EXPORTS_NAMESPACES = mozilla
EXPORTS = \
mozStorageHelper.h \
mozStorage.h \
$(NULL)
new file mode 100644
--- /dev/null
+++ b/storage/public/mozIStorageBindingParams.idl
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 et
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface nsIVariant;
+
+[scriptable, uuid(a8d4827c-641c-45e3-a9ea-493570b4106b)]
+interface mozIStorageBindingParams : nsISupports {
+ /**
+ * Binds aValue to the parameter with the name aName.
+ *
+ * @param aName
+ * The name of the parameter to bind aValue to.
+ * @param aValue
+ * The value to bind.
+ */
+ void bindByName(in AUTF8String aName,
+ in nsIVariant aValue);
+ [noscript] void bindUTF8StringByName(in AUTF8String aName,
+ in AUTF8String aValue);
+ [noscript] void bindStringByName(in AUTF8String aName,
+ in AString aValue);
+ [noscript] void bindDoubleByName(in AUTF8String aName,
+ in double aValue);
+ [noscript] void bindInt32ByName(in AUTF8String aName,
+ in long aValue);
+ [noscript] void bindInt64ByName(in AUTF8String aName,
+ in long long aValue);
+ [noscript] void bindNullByName(in AUTF8String aName);
+ void bindBlobByName(in AUTF8String aName,
+ [array, const, size_is(aValueSize)] in octet aValue,
+ in unsigned long aValueSize);
+
+ /**
+ * Binds aValue to the parameter with the index aIndex.
+ *
+ * @param aIndex
+ * The zero-based index of the parameter to bind aValue to.
+ * @param aValue
+ * The value to bind.
+ */
+ void bindByIndex(in unsigned long aIndex,
+ in nsIVariant aValue);
+ [noscript] void bindUTF8StringByIndex(in unsigned long aIndex,
+ in AUTF8String aValue);
+ [noscript] void bindStringByIndex(in unsigned long aIndex,
+ in AString aValue);
+ [noscript] void bindDoubleByIndex(in unsigned long aIndex,
+ in double aValue);
+ [noscript] void bindInt32ByIndex(in unsigned long aIndex,
+ in long aValue);
+ [noscript] void bindInt64ByIndex(in unsigned long aIndex,
+ in long long aValue);
+ [noscript] void bindNullByIndex(in unsigned long aIndex);
+ void bindBlobByIndex(in unsigned long aIndex,
+ [array, const, size_is(aValueSize)] in octet aValue,
+ in unsigned long aValueSize);
+};
new file mode 100644
--- /dev/null
+++ b/storage/public/mozIStorageBindingParamsArray.idl
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 et
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface mozIStorageBindingParams;
+
+[scriptable, uuid(e676e1a3-1dc6-4802-ac03-291fa9de7f93)]
+interface mozIStorageBindingParamsArray : nsISupports {
+ /**
+ * Creates a new mozIStorageBindingParams object that can be added to this
+ * array.
+ *
+ * @returns a mozIStorageBindingParams object that can be used to specify
+ * parameters that need to be bound.
+ */
+ mozIStorageBindingParams newBindingParams();
+
+ /**
+ * Adds the parameters to the end of this array.
+ *
+ * @param aParameters
+ * The parameters to add to this array.
+ */
+ void addParams(in mozIStorageBindingParams aParameters);
+};
--- a/storage/public/mozIStorageStatement.idl
+++ b/storage/public/mozIStorageStatement.idl
@@ -38,18 +38,19 @@
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "mozIStorageValueArray.idl"
interface mozIStorageConnection;
interface mozIStorageStatementCallback;
interface mozIStoragePendingStatement;
+interface mozIStorageBindingParamsArray;
-[scriptable, uuid(52d740cd-1c25-471f-a848-98d1a00a2963)]
+[scriptable, uuid(20c45bdd-51d4-4f07-b70e-5feaa6302197)]
interface mozIStorageStatement : mozIStorageValueArray {
/**
* Finalizes a statement so you can successfully close a database connection.
* This method does not need to be used from native callers since you can just
* set the statement to null, but is extremely useful to JS callers.
*/
void finalize();
@@ -125,16 +126,34 @@ interface mozIStorageStatement : mozISto
void bindInt32Parameter(in unsigned long aParamIndex, in long aValue);
void bindInt64Parameter(in unsigned long aParamIndex, in long long aValue);
void bindNullParameter(in unsigned long aParamIndex);
void bindBlobParameter(in unsigned long aParamIndex,
[array,const,size_is(aValueSize)] in octet aValue,
in unsigned long aValueSize);
/**
+ * Binds the array of parameters to the statement. When executeAsync is
+ * called, all the parameters in aParameters are bound and then executed.
+ *
+ * @param aParameters
+ * The array of parameters to bind to the statement upon execution.
+ */
+ void bindParameters(in mozIStorageBindingParamsArray aParameters);
+
+ /**
+ * Creates a new mozIStorageBindingParamsArray that can be used to bind
+ * multiple sets of data to a statement.
+ *
+ * @returns a mozIStorageBindingParamsArray that multiple sets of parameters
+ * can be bound to.
+ */
+ mozIStorageBindingParamsArray newBindingParamsArray();
+
+ /**
* Execute the query, ignoring any results. This is accomplished by
* calling step() once, and then calling reset().
*
* Error and last insert info, etc. are available from
* the mozStorageConnection.
*/
void execute();
--- a/storage/src/Makefile.in
+++ b/storage/src/Makefile.in
@@ -78,16 +78,18 @@ CPPSRCS = \
mozStorageArgValueArray.cpp \
mozStorageSQLFunctions.cpp \
mozStorageRow.cpp \
mozStorageResultSet.cpp \
mozStorageError.cpp \
mozStorageAsyncStatementExecution.cpp \
mozStorageStatementJSHelper.cpp \
mozStoragePrivateHelpers.cpp \
+ mozStorageBindingParamsArray.cpp \
+ mozStorageBindingParams.cpp \
$(NULL)
LOCAL_INCLUDES = \
$(SQLITE_CFLAGS)
# This is the default value. If we ever change it when compiling sqlite, we
# will need to change it here as well.
DEFINES += -DSQLITE_MAX_LIKE_PATTERN_LENGTH=50000
--- a/storage/src/mozStorageAsyncStatementExecution.cpp
+++ b/storage/src/mozStorageAsyncStatementExecution.cpp
@@ -38,22 +38,24 @@
* ***** END LICENSE BLOCK ***** */
#include "nsAutoPtr.h"
#include "prtime.h"
#include "sqlite3.h"
#include "mozIStorageStatementCallback.h"
+#include "mozStorageBindingParams.h"
#include "mozStorageHelper.h"
#include "mozStorageResultSet.h"
#include "mozStorageRow.h"
#include "mozStorageConnection.h"
#include "mozStorageError.h"
#include "mozStoragePrivateHelpers.h"
+#include "mozStorageStatementData.h"
#include "mozStorageAsyncStatementExecution.h"
namespace mozilla {
namespace storage {
/**
* The following constants help batch rows into result sets.
* MAX_MILLISECONDS_BETWEEN_RESULTS was chosen because any user-based task that
@@ -167,17 +169,17 @@ private:
} // anonymous namespace
////////////////////////////////////////////////////////////////////////////////
//// AsyncExecuteStatements
/* static */
nsresult
-AsyncExecuteStatements::execute(sqlite3_stmt_array &aStatements,
+AsyncExecuteStatements::execute(StatementDataArray &aStatements,
Connection *aConnection,
mozIStorageStatementCallback *aCallback,
mozIStoragePendingStatement **_stmt)
{
// Create our event to run in the background
nsRefPtr<AsyncExecuteStatements> event =
new AsyncExecuteStatements(aStatements, aConnection, aCallback);
NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
@@ -188,17 +190,17 @@ AsyncExecuteStatements::execute(sqlite3_
nsresult rv = target->Dispatch(event, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
// Return it as the pending statement object
NS_ADDREF(*_stmt = event);
return NS_OK;
}
-AsyncExecuteStatements::AsyncExecuteStatements(sqlite3_stmt_array &aStatements,
+AsyncExecuteStatements::AsyncExecuteStatements(StatementDataArray &aStatements,
Connection *aConnection,
mozIStorageStatementCallback *aCallback)
: mConnection(aConnection)
, mTransactionManager(nsnull)
, mCallback(aCallback)
, mCallingThread(::do_GetCurrentThread())
, mMaxIntervalWait(::PR_MicrosecondsToInterval(MAX_MILLISECONDS_BETWEEN_RESULTS))
, mIntervalStart(::PR_IntervalNow())
@@ -222,16 +224,57 @@ AsyncExecuteStatements::shouldNotify()
// We do not need to acquire mMutex here because it can only ever be written
// to on the calling thread, and the only thread that can call us is the
// calling thread, so we know that our access is serialized.
return !mCancelRequested;
}
bool
+AsyncExecuteStatements::bindExecuteAndProcessStatement(StatementData &aData,
+ bool aLastStatement)
+{
+ mMutex.AssertNotCurrentThreadOwns();
+
+ sqlite3_stmt *stmt(aData);
+ BindingParamsArray *paramsArray(aData);
+
+ // Iterate through all of our parameters, bind them, and execute.
+ bool continueProcessing = true;
+ BindingParamsArray::iterator itr = paramsArray->begin();
+ BindingParamsArray::iterator end = paramsArray->end();
+ while (itr != end && continueProcessing) {
+ // Bind the data to our statement.
+ nsCOMPtr<mozIStorageError> error;
+ error = (*itr)->bind(stmt);
+ if (error) {
+ // Set our error state.
+ {
+ MutexAutoLock mutex(mMutex);
+ mState = ERROR;
+ }
+
+ // And notify.
+ (void)notifyError(error);
+ return false;
+ }
+
+ // Advance our iterator, execute, and then process the statement.
+ itr++;
+ bool lastStatement = aLastStatement && itr == end;
+ continueProcessing = executeAndProcessStatement(stmt, lastStatement);
+
+ // Always reset our statement.
+ (void)::sqlite3_reset(stmt);
+ }
+
+ return continueProcessing;
+}
+
+bool
AsyncExecuteStatements::executeAndProcessStatement(sqlite3_stmt *aStatement,
bool aLastStatement)
{
mMutex.AssertNotCurrentThreadOwns();
// We need to hold the mutex for statement execution so we can properly
// reflect state in case we are canceled. We release the mutex in a few areas
// in order to allow for cancelation to occur.
@@ -376,20 +419,18 @@ AsyncExecuteStatements::notifyComplete()
{
mMutex.AssertNotCurrentThreadOwns();
NS_ASSERTION(mState != PENDING,
"Still in a pending state when calling Complete!");
// Finalize our statements before we try to commit or rollback. If we are
// canceling and have statements that think they have pending work, the
// rollback will fail.
- for (PRUint32 i = 0; i < mStatements.Length(); i++) {
- (void)::sqlite3_finalize(mStatements[i]);
- mStatements[i] = NULL;
- }
+ for (PRUint32 i = 0; i < mStatements.Length(); i++)
+ mStatements[i].finalize();
// Handle our transaction, if we have one
if (mTransactionManager) {
if (mState == COMPLETED) {
nsresult rv = mTransactionManager->Commit();
if (NS_FAILED(rv)) {
mState = ERROR;
(void)notifyError(mozIStorageError::ERROR,
@@ -425,18 +466,29 @@ AsyncExecuteStatements::notifyError(PRIn
mMutex.AssertNotCurrentThreadOwns();
if (!mCallback)
return NS_OK;
nsCOMPtr<mozIStorageError> errorObj(new Error(aErrorCode, aMessage));
NS_ENSURE_TRUE(errorObj, NS_ERROR_OUT_OF_MEMORY);
+ return notifyError(errorObj);
+}
+
+nsresult
+AsyncExecuteStatements::notifyError(mozIStorageError *aError)
+{
+ mMutex.AssertNotCurrentThreadOwns();
+
+ if (!mCallback)
+ return NS_OK;
+
nsRefPtr<ErrorNotifier> notifier =
- new ErrorNotifier(mCallback, errorObj, this);
+ new ErrorNotifier(mCallback, aError, this);
NS_ENSURE_TRUE(notifier, NS_ERROR_OUT_OF_MEMORY);
return mCallingThread->Dispatch(notifier, NS_DISPATCH_NORMAL);
}
nsresult
AsyncExecuteStatements::notifyResults()
{
@@ -509,27 +561,37 @@ AsyncExecuteStatements::Run()
mState = CANCELED;
}
if (cancelRequested)
return notifyComplete();
// If there is more than one statement, run it in a transaction. We assume
// that we have been given write statements since getting a batch of read
// statements doesn't make a whole lot of sense.
- if (mStatements.Length() > 1) {
+ // Additionally, if we have only one statement and it has parameters to be
+ // bound, we assume that the consumer would want a transaction as well.
+ if (mStatements.Length() > 1 || mStatements[0].hasParametersToBeBound()) {
// We don't error if this failed because it's not terrible if it does.
mTransactionManager = new mozStorageTransaction(mConnection, PR_FALSE,
mozIStorageConnection::TRANSACTION_IMMEDIATE);
}
// Execute each statement, giving the callback results if it returns any.
for (PRUint32 i = 0; i < mStatements.Length(); i++) {
- PRBool finished = (i == (mStatements.Length() - 1));
- if (!executeAndProcessStatement(mStatements[i], finished))
+ bool finished = (i == (mStatements.Length() - 1));
+
+ // If we have parameters to bind, bind them, execute, and process.
+ if (mStatements[i].hasParametersToBeBound()) {
+ if (!bindExecuteAndProcessStatement(mStatements[i], finished))
+ break;
+ }
+ // Otherwise, just execute and process the statement.
+ else if (!executeAndProcessStatement(mStatements[i], finished)) {
break;
+ }
}
// If we still have results that we haven't notified about, take care of
// them now.
if (mResultSet)
(void)notifyResults();
// Notify about completion
--- a/storage/src/mozStorageAsyncStatementExecution.h
+++ b/storage/src/mozStorageAsyncStatementExecution.h
@@ -52,16 +52,17 @@
struct sqlite3_stmt;
class mozStorageTransaction;
namespace mozilla {
namespace storage {
class Connection;
class ResultSet;
+class StatementData;
class AsyncExecuteStatements : public nsIRunnable
, public mozIStoragePendingStatement
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_MOZISTORAGEPENDINGSTATEMENT
@@ -71,52 +72,69 @@ public:
*/
enum ExecutionState {
PENDING = -1,
COMPLETED = mozIStorageStatementCallback::REASON_FINISHED,
CANCELED = mozIStorageStatementCallback::REASON_CANCELED,
ERROR = mozIStorageStatementCallback::REASON_ERROR
};
- typedef nsTArray<sqlite3_stmt *> sqlite3_stmt_array;
+ typedef nsTArray<StatementData> StatementDataArray;
/**
* Executes a statement in the background, and passes results back to the
* caller.
*
* @param aStatements
- * The SQLite statements to execute in the background. Ownership is
- * transfered from the caller.
+ * The statements to execute and possibly bind in the background.
+ * Ownership is transfered from the caller.
* @param aConnection
* The connection that created the statements to execute.
* @param aCallback
* The callback that is notified of results, completion, and errors.
* @param _stmt
* The handle to control the execution of the statements.
*/
- static nsresult execute(sqlite3_stmt_array &aStatements,
+ static nsresult execute(StatementDataArray &aStatements,
Connection *aConnection,
mozIStorageStatementCallback *aCallback,
mozIStoragePendingStatement **_stmt);
/**
* Indicates when events on the calling thread should run or not. Certain
* events posted back to the calling thread should call this see if they
* should run or not.
*
* @returns true if the event should notify still, false otherwise.
*/
bool shouldNotify();
private:
- AsyncExecuteStatements(sqlite3_stmt_array &aStatements,
+ AsyncExecuteStatements(StatementDataArray &aStatements,
Connection *aConnection,
mozIStorageStatementCallback *aCallback);
/**
+ * Binds and then executes a given statement until completion, an error
+ * occurs, or we are canceled. If aLastStatement is true, we should set
+ * mState accordingly.
+ *
+ * @pre mMutex is not held
+ *
+ * @param aData
+ * The StatementData to bind, execute, and then process.
+ * @param aLastStatement
+ * Indicates if this is the last statement or not. If it is, we have
+ * to set the proper state.
+ * @returns true if we should continue to process statements, false otherwise.
+ */
+ bool bindExecuteAndProcessStatement(StatementData &aData,
+ bool aLastStatement);
+
+ /**
* Executes a given statement until completion, an error occurs, or we are
* canceled. If aLastStatement is true, we should set mState accordingly.
*
* @pre mMutex is not held
*
* @param aStatement
* The statement to execute and then process.
* @param aLastStatement
@@ -160,27 +178,30 @@ private:
* Notifies callback about an error.
*
* @pre mMutex is not held
*
* @param aErrorCode
* The error code defined in mozIStorageError for the error.
* @param aMessage
* The error string, if any.
+ * @param aError
+ * The error object to notify the caller with.
*/
nsresult notifyError(PRInt32 aErrorCode, const char *aMessage);
+ nsresult notifyError(mozIStorageError *aError);
/**
* Notifies the callback about a result set.
*
* @pre mMutex is not held
*/
nsresult notifyResults();
- sqlite3_stmt_array mStatements;
+ StatementDataArray mStatements;
nsRefPtr<Connection> mConnection;
mozStorageTransaction *mTransactionManager;
mozIStorageStatementCallback *mCallback;
nsCOMPtr<nsIThread> mCallingThread;
nsRefPtr<ResultSet> mResultSet;
/**
* The maximum amount of time we want to wait between results. Defined by
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageBindingParams.cpp
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <limits.h>
+
+#include "nsString.h"
+
+#include "mozStorageError.h"
+#include "mozStoragePrivateHelpers.h"
+#include "mozStorageBindingParams.h"
+#include "mozStorageBindingParamsArray.h"
+#include "Variant.h"
+
+namespace mozilla {
+namespace storage {
+
+////////////////////////////////////////////////////////////////////////////////
+//// Local Helper Objects
+
+namespace {
+
+struct BindingColumnData
+{
+ BindingColumnData(sqlite3_stmt *aStmt,
+ int aColumn)
+ : stmt(aStmt)
+ , column(aColumn)
+ {
+ }
+ sqlite3_stmt *stmt;
+ int column;
+};
+
+} // anonymous namespace
+
+////////////////////////////////////////////////////////////////////////////////
+//// sqlite3_stmt Specialization Functions (varaintToSQLite3T)
+
+template < >
+int
+sqlite3_T_int(BindingColumnData aData,
+ int aValue)
+{
+ return ::sqlite3_bind_int(aData.stmt, aData.column + 1, aValue);
+}
+
+template < >
+int
+sqlite3_T_int64(BindingColumnData aData,
+ sqlite3_int64 aValue)
+{
+ return ::sqlite3_bind_int64(aData.stmt, aData.column + 1, aValue);
+}
+
+template < >
+int
+sqlite3_T_double(BindingColumnData aData,
+ double aValue)
+{
+ return ::sqlite3_bind_double(aData.stmt, aData.column + 1, aValue);
+}
+
+template < >
+int
+sqlite3_T_text16(BindingColumnData aData,
+ nsString aValue)
+{
+ return ::sqlite3_bind_text16(aData.stmt,
+ aData.column + 1,
+ PromiseFlatString(aValue).get(),
+ aValue.Length() * 2, // Length in bytes!
+ SQLITE_TRANSIENT);
+}
+
+template < >
+int
+sqlite3_T_null(BindingColumnData aData)
+{
+ return ::sqlite3_bind_null(aData.stmt, aData.column + 1);
+}
+
+template < >
+int
+sqlite3_T_blob(BindingColumnData aData,
+ const void *aBlob,
+ int aSize)
+{
+ return ::sqlite3_bind_blob(aData.stmt, aData.column + 1, aBlob, aSize,
+ NS_Free);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// BindingParams
+
+BindingParams::BindingParams(BindingParamsArray *aOwningArray,
+ Statement *aOwningStatement)
+: mOwningArray(aOwningArray)
+, mOwningStatement(aOwningStatement)
+, mLocked(false)
+{
+ (void)mOwningStatement->GetParameterCount(&mParamCount);
+}
+
+void
+BindingParams::lock()
+{
+ NS_ASSERTION(mLocked == false, "Parameters have already been locked!");
+ mLocked = true;
+
+ // We no longer need to hold a reference to our statement or our owning array.
+ // The array owns us at this point, and it will own a reference to the
+ // statement.
+ mOwningStatement = nsnull;
+ mOwningArray = nsnull;
+}
+
+const BindingParamsArray *
+BindingParams::getOwner() const
+{
+ return mOwningArray;
+}
+
+already_AddRefed<mozIStorageError>
+BindingParams::bind(sqlite3_stmt *aStatement)
+{
+ // Iterate through all of our stored data, and bind it.
+ for (PRInt32 i = 0; i < mParameters.Count(); i++) {
+ int rc = variantToSQLiteT(BindingColumnData(aStatement, i), mParameters[i]);
+ if (rc != SQLITE_OK) {
+ // We had an error while trying to bind. Now we need to create an error
+ // object with the right message. Note that we special case
+ // SQLITE_MISMATCH, but otherwise get the message from SQLite.
+ const char *msg = "Could not covert nsIVariant to SQLite type.";
+ if (rc != SQLITE_MISMATCH)
+ msg = ::sqlite3_errmsg(::sqlite3_db_handle(aStatement));
+
+ nsCOMPtr<mozIStorageError> err(new Error(rc, msg));
+ return err.forget();
+ }
+ }
+
+ // No error occurred, so return null!
+ return nsnull;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(
+ BindingParams,
+ mozIStorageBindingParams
+)
+
+///////////////////////////////////////////////////////////////////////////////
+//// mozIStorageBindingParams
+
+NS_IMETHODIMP
+BindingParams::BindByName(const nsACString &aName,
+ nsIVariant *aValue)
+{
+ NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
+
+ // Get the column index that we need to store this at.
+ PRUint32 index;
+ nsresult rv = mOwningStatement->GetParameterIndex(aName, &index);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return BindByIndex(index, aValue);
+}
+
+NS_IMETHODIMP
+BindingParams::BindUTF8StringByName(const nsACString &aName,
+ const nsACString &aValue)
+{
+ nsCOMPtr<nsIVariant> value(new UTF8TextVariant(aValue));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByName(aName, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindStringByName(const nsACString &aName,
+ const nsAString &aValue)
+{
+ nsCOMPtr<nsIVariant> value(new TextVariant(aValue));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByName(aName, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindDoubleByName(const nsACString &aName,
+ double aValue)
+{
+ nsCOMPtr<nsIVariant> value(new FloatVariant(aValue));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByName(aName, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindInt32ByName(const nsACString &aName,
+ PRInt32 aValue)
+{
+ nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByName(aName, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindInt64ByName(const nsACString &aName,
+ PRInt64 aValue)
+{
+ nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByName(aName, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindNullByName(const nsACString &aName)
+{
+ nsCOMPtr<nsIVariant> value(new NullVariant());
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByName(aName, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindBlobByName(const nsACString &aName,
+ const PRUint8 *aValue,
+ PRUint32 aValueSize)
+{
+ NS_ENSURE_ARG_MAX(aValueSize, INT_MAX);
+ std::pair<const void *, int> data(
+ static_cast<const void *>(aValue),
+ int(aValueSize)
+ );
+ nsCOMPtr<nsIVariant> value(new BlobVariant(data));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByName(aName, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindByIndex(PRUint32 aIndex,
+ nsIVariant *aValue)
+{
+ NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
+ ENSURE_INDEX_VALUE(aIndex, mParamCount);
+
+ // Store the variant for later use.
+ NS_ENSURE_TRUE(mParameters.InsertObjectAt(aValue, aIndex),
+ NS_ERROR_OUT_OF_MEMORY);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BindingParams::BindUTF8StringByIndex(PRUint32 aIndex,
+ const nsACString &aValue)
+{
+ nsCOMPtr<nsIVariant> value(new UTF8TextVariant(aValue));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByIndex(aIndex, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindStringByIndex(PRUint32 aIndex,
+ const nsAString &aValue)
+{
+ nsCOMPtr<nsIVariant> value(new TextVariant(aValue));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByIndex(aIndex, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindDoubleByIndex(PRUint32 aIndex,
+ double aValue)
+{
+ nsCOMPtr<nsIVariant> value(new FloatVariant(aValue));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByIndex(aIndex, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindInt32ByIndex(PRUint32 aIndex,
+ PRInt32 aValue)
+{
+ nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByIndex(aIndex, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindInt64ByIndex(PRUint32 aIndex,
+ PRInt64 aValue)
+{
+ nsCOMPtr<nsIVariant> value(new IntegerVariant(aValue));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByIndex(aIndex, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindNullByIndex(PRUint32 aIndex)
+{
+ nsCOMPtr<nsIVariant> value(new NullVariant());
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByIndex(aIndex, value);
+}
+
+NS_IMETHODIMP
+BindingParams::BindBlobByIndex(PRUint32 aIndex,
+ const PRUint8 *aValue,
+ PRUint32 aValueSize)
+{
+ NS_ENSURE_ARG_MAX(aValueSize, INT_MAX);
+ std::pair<const void *, int> data(
+ static_cast<const void *>(aValue),
+ int(aValueSize)
+ );
+ nsCOMPtr<nsIVariant> value(new BlobVariant(data));
+ NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
+
+ return BindByIndex(aIndex, value);
+}
+
+} // namespace storage
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageBindingParams.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _mozStorageBindingParams_h_
+#define _mozStorageBindingParams_h_
+
+#include "nsAutoPtr.h"
+#include "nsCOMArray.h"
+#include "nsIVariant.h"
+
+#include "mozStorageBindingParamsArray.h"
+#include "mozStorageStatement.h"
+#include "mozIStorageBindingParams.h"
+
+class mozIStorageError;
+struct sqlite3_stmt;
+
+namespace mozilla {
+namespace storage {
+
+class BindingParams : public mozIStorageBindingParams
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZISTORAGEBINDINGPARAMS
+
+ /**
+ * Locks the parameters and prevents further modification to it (such as
+ * binding more elements to it).
+ */
+ void lock();
+
+ /**
+ * @returns the pointer to the owning BindingParamsArray.
+ */
+ const BindingParamsArray *getOwner() const;
+
+ /**
+ * Binds our stored data to the statement.
+ *
+ * @param aStatement
+ * The statement to bind our data to.
+ * @returns nsnull on success, or a mozIStorageError object if an error
+ * occurred.
+ */
+ already_AddRefed<mozIStorageError> bind(sqlite3_stmt *aStatement);
+
+ BindingParams(BindingParamsArray *aOwningArray,
+ Statement *aOwningStatement);
+
+private:
+ nsRefPtr<BindingParamsArray> mOwningArray;
+ Statement *mOwningStatement;
+ nsCOMArray<nsIVariant> mParameters;
+ PRUint32 mParamCount;
+ bool mLocked;
+};
+
+} // namespace storage
+} // namespace mozilla
+
+#endif // _mozStorageBindingParams_h_
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageBindingParamsArray.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "mozStorageBindingParamsArray.h"
+#include "mozStorageBindingParams.h"
+
+namespace mozilla {
+namespace storage {
+
+////////////////////////////////////////////////////////////////////////////////
+//// BindingParamsArray
+
+BindingParamsArray::BindingParamsArray(Statement *aOwningStatement)
+: mOwningStatement(aOwningStatement)
+, mLocked(false)
+{
+}
+
+void
+BindingParamsArray::lock()
+{
+ NS_ASSERTION(mLocked == false, "Array has already been locked!");
+ mLocked = true;
+
+ // We also no longer need to hold a reference to our statement since it owns
+ // us.
+ mOwningStatement = nsnull;
+}
+
+const Statement *
+BindingParamsArray::getOwner() const
+{
+ return mOwningStatement;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(
+ BindingParamsArray,
+ mozIStorageBindingParamsArray
+)
+
+///////////////////////////////////////////////////////////////////////////////
+//// mozIStorageBindingParamsArray
+
+NS_IMETHODIMP
+BindingParamsArray::NewBindingParams(mozIStorageBindingParams **_params)
+{
+ NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<mozIStorageBindingParams> params =
+ new BindingParams(this, mOwningStatement);
+ NS_ENSURE_TRUE(params, NS_ERROR_OUT_OF_MEMORY);
+
+ params.forget(_params);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BindingParamsArray::AddParams(mozIStorageBindingParams *aParameters)
+{
+ NS_ENSURE_FALSE(mLocked, NS_ERROR_UNEXPECTED);
+
+ BindingParams *params = static_cast<BindingParams *>(aParameters);
+
+ // Check to make sure that this set of parameters was created with us.
+ if (params->getOwner() != this)
+ return NS_ERROR_UNEXPECTED;
+
+ NS_ENSURE_TRUE(mArray.AppendElement(params), NS_ERROR_OUT_OF_MEMORY);
+
+ // Lock the parameters only after we've successfully added them.
+ params->lock();
+
+ return NS_OK;
+}
+
+} // namespace storage
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageBindingParamsArray.h
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _mozStorageBindingParamsArray_h_
+#define _mozStorageBindingParamsArray_h_
+
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+
+#include "mozIStorageBindingParamsArray.h"
+
+namespace mozilla {
+namespace storage {
+
+class BindingParams;
+class Statement;
+
+class BindingParamsArray : public mozIStorageBindingParamsArray
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_MOZISTORAGEBINDINGPARAMSARRAY
+
+ BindingParamsArray(Statement *aOwningStatement);
+
+ /**
+ * Locks the array and prevents further modification to it (such as adding
+ * more elements to it).
+ */
+ void lock();
+
+ /**
+ * @returns the pointer to the owning BindingParamsArray.
+ */
+ const Statement *getOwner() const;
+
+ class iterator {
+ public:
+ iterator(BindingParamsArray *aArray,
+ PRUint32 aIndex)
+ : mArray(aArray)
+ , mIndex(aIndex)
+ {
+ }
+
+ iterator &operator++(int)
+ {
+ mIndex++;
+ return *this;
+ }
+
+ bool operator==(const iterator &aOther) const
+ {
+ return mIndex == aOther.mIndex;
+ }
+ bool operator!=(const iterator &aOther) const
+ {
+ return !(*this == aOther);
+ }
+ BindingParams *operator*()
+ {
+ NS_ASSERTION(mIndex < mArray->mArray.Length(),
+ "Dereferenceing an invalid value!");
+ return mArray->mArray[mIndex].get();
+ }
+ private:
+ void operator--() { }
+ BindingParamsArray *mArray;
+ PRUint32 mIndex;
+ };
+
+ /**
+ * Obtains an iterator pointing to the beginning of the array.
+ */
+ inline iterator begin()
+ {
+ NS_ASSERTION(mLocked, "Obtaining an iterator when we are not locked!");
+ return iterator(this, 0);
+ }
+
+ /**
+ * Obtains an iterator pointing to the end of the array.
+ */
+ inline iterator end()
+ {
+ NS_ASSERTION(mLocked, "Obtaining an iterator when we are not locked!");
+ return iterator(this, mArray.Length());
+ }
+private:
+ nsRefPtr<Statement> mOwningStatement;
+ nsTArray< nsRefPtr<BindingParams> > mArray;
+ bool mLocked;
+
+ friend class iterator;
+};
+
+} // namespace storage
+} // namespace mozilla
+
+#endif // _mozStorageBindingParamsArray_h_
--- a/storage/src/mozStorageConnection.cpp
+++ b/storage/src/mozStorageConnection.cpp
@@ -57,16 +57,17 @@
#include "mozStorageAsyncStatementExecution.h"
#include "mozStorageSQLFunctions.h"
#include "mozStorageConnection.h"
#include "mozStorageService.h"
#include "mozStorageStatement.h"
#include "mozStorageArgValueArray.h"
#include "mozStoragePrivateHelpers.h"
+#include "mozStorageStatementData.h"
#include "prlog.h"
#include "prprf.h"
#ifdef PR_LOGGING
PRLogModuleInfo* gStorageLog = nsnull;
#endif
@@ -622,17 +623,17 @@ Connection::ExecuteSimpleSQL(const nsACS
nsresult
Connection::ExecuteAsync(mozIStorageStatement **aStatements,
PRUint32 aNumStatements,
mozIStorageStatementCallback *aCallback,
mozIStoragePendingStatement **_handle)
{
int rc = SQLITE_OK;
- nsTArray<sqlite3_stmt *> stmts(aNumStatements);
+ nsTArray<StatementData> stmts(aNumStatements);
for (PRUint32 i = 0; i < aNumStatements && rc == SQLITE_OK; i++) {
sqlite3_stmt *old_stmt =
static_cast<Statement *>(aStatements[i])->nativeStatement();
if (!old_stmt) {
rc = SQLITE_MISUSE;
break;
}
NS_ASSERTION(::sqlite3_db_handle(old_stmt) == mDBConn,
@@ -652,17 +653,19 @@ Connection::ExecuteAsync(mozIStorageStat
("Cloned statement 0x%p to 0x%p", old_stmt, new_stmt));
#endif
// Transfer the bindings
rc = sqlite3_transfer_bindings(old_stmt, new_stmt);
if (rc != SQLITE_OK)
break;
- if (!stmts.AppendElement(new_stmt)) {
+ Statement *storageStmt = static_cast<Statement *>(aStatements[i]);
+ StatementData data(new_stmt, storageStmt->bindingParamsArray());
+ if (!stmts.AppendElement(data)) {
rc = SQLITE_NOMEM;
break;
}
}
// Dispatch to the background
nsresult rv = NS_OK;
if (rc == SQLITE_OK)
--- a/storage/src/mozStorageStatement.cpp
+++ b/storage/src/mozStorageStatement.cpp
@@ -501,16 +501,43 @@ Statement::BindBlobParameter(PRUint32 aP
return NS_ERROR_NOT_INITIALIZED;
int srv = ::sqlite3_bind_blob(mDBStatement, aParamIndex + 1, aValue,
aValueSize, SQLITE_TRANSIENT);
return convertResultCode(srv);
}
NS_IMETHODIMP
+Statement::BindParameters(mozIStorageBindingParamsArray *aParameters)
+{
+ if (!mDBStatement)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ BindingParamsArray *array = static_cast<BindingParamsArray *>(aParameters);
+ if (array->getOwner() != this)
+ return NS_ERROR_UNEXPECTED;
+
+ mParamsArray = array;
+ mParamsArray->lock();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Statement::NewBindingParamsArray(mozIStorageBindingParamsArray **_array)
+{
+ nsCOMPtr<mozIStorageBindingParamsArray> array =
+ new BindingParamsArray(this);
+ NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
+
+ array.forget(_array);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
Statement::Execute()
{
if (!mDBStatement)
return NS_ERROR_NOT_INITIALIZED;
PRBool ret;
nsresult rv = ExecuteStep(&ret);
NS_ENSURE_SUCCESS(rv, rv);
--- a/storage/src/mozStorageStatement.h
+++ b/storage/src/mozStorageStatement.h
@@ -40,16 +40,17 @@
#ifndef _mozStorageStatement_h_
#define _mozStorageStatement_h_
#include "nsAutoPtr.h"
#include "nsString.h"
#include "nsTArray.h"
+#include "mozStorageBindingParamsArray.h"
#include "mozIStorageStatement.h"
class nsIXPConnectJSObjectHolder;
struct sqlite3_stmt;
namespace mozilla {
namespace storage {
class StatementJSHelper;
@@ -77,27 +78,42 @@ public:
const nsACString &aSQLStatement);
/**
* Obtains the native statement pointer.
*/
inline sqlite3_stmt *nativeStatement() { return mDBStatement; }
+ /**
+ * Obtains and transfers ownership of the array of parameters that are bound
+ * to this statment. This can be null.
+ */
+ inline already_AddRefed<BindingParamsArray> bindingParamsArray()
+ {
+ return mParamsArray.forget();
+ }
+
private:
~Statement();
nsRefPtr<Connection> mDBConnection;
sqlite3_stmt *mDBStatement;
PRUint32 mParamCount;
PRUint32 mResultColumnCount;
nsTArray<nsCString> mColumnNames;
bool mExecuting;
/**
+ * Holds the array of parameters to bind to this statement when we execute
+ * it asynchronously.
+ */
+ nsRefPtr<BindingParamsArray> mParamsArray;
+
+ /**
* The following two members are only used with the JS helper. They cache
* the row and params objects.
*/
nsCOMPtr<nsIXPConnectJSObjectHolder> mStatementParamsHolder;
nsCOMPtr<nsIXPConnectJSObjectHolder> mStatementRowHolder;
friend class StatementJSHelper;
};
new file mode 100644
--- /dev/null
+++ b/storage/src/mozStorageStatementData.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 et
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _mozStorageStatementData_h_
+#define _mozStorageStatementData_h_
+
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+
+#include "mozStorageBindingParamsArray.h"
+
+struct sqlite3_stmt;
+
+namespace mozilla {
+namespace storage {
+
+class StatementData
+{
+public:
+ StatementData(sqlite3_stmt *aStatement,
+ already_AddRefed<BindingParamsArray> aParamsArray)
+ : mStatement(aStatement)
+ , mParamsArray(aParamsArray)
+ {
+ }
+ StatementData(const StatementData &aSource)
+ : mStatement(aSource.mStatement)
+ , mParamsArray(aSource.mParamsArray)
+ {
+ }
+ StatementData()
+ {
+ }
+
+ operator sqlite3_stmt *() const
+ {
+ NS_ASSERTION(mStatement, "NULL sqlite3_stmt being handed off!");
+ return mStatement;
+ }
+ operator BindingParamsArray *() const { return mParamsArray; }
+
+ /**
+ * Finalizes and NULLs out our sqlite3_stmt. Also releases our parameter
+ * array since we'll no longer need it.
+ */
+ inline void finalize()
+ {
+ (void)::sqlite3_finalize(mStatement);
+ mStatement = NULL;
+ mParamsArray = nsnull;
+ }
+
+ /**
+ * Indicates if this statement has parameters to be bound before it is
+ * executed.
+ *
+ * @returns true if the statement has parameters to bind against, false
+ * otherwise.
+ */
+ inline bool hasParametersToBeBound() const { return mParamsArray != nsnull; }
+
+private:
+ sqlite3_stmt *mStatement;
+ nsRefPtr<BindingParamsArray> mParamsArray;
+};
+
+} // namespace storage
+} // namespace mozilla
+
+#endif // _mozStorageStatementData_h_
--- a/storage/test/unit/test_connection_executeAsync.js
+++ b/storage/test/unit/test_connection_executeAsync.js
@@ -39,21 +39,19 @@
const INTEGER = 1;
const TEXT = "this is test text";
const REAL = 3.23;
const BLOB = [1, 2];
function test_create_and_add()
{
- dump("test_create_and_add()\n");
-
getOpenedDatabase().executeSimpleSQL(
"CREATE TABLE test (" +
- "id INTEGER PRIMARY KEY, " +
+ "id INTEGER, " +
"string TEXT, " +
"number REAL, " +
"nuller NULL, " +
"blober BLOB" +
")"
);
let stmts = [];
@@ -68,17 +66,16 @@ function test_create_and_add()
stmts[1] = getOpenedDatabase().createStatement(
"INSERT INTO test (string, number, nuller, blober) VALUES (?, ?, ?, ?)"
);
stmts[1].bindStringParameter(0, TEXT);
stmts[1].bindDoubleParameter(1, REAL);
stmts[1].bindNullParameter(2);
stmts[1].bindBlobParameter(3, BLOB, BLOB.length);
- do_test_pending();
getOpenedDatabase().executeAsync(stmts, stmts.length, {
handleResult: function(aResultSet)
{
dump("handleResult("+aResultSet+")\n");
do_throw("unexpected results obtained!");
},
handleError: function(aError)
{
@@ -118,63 +115,153 @@ function test_create_and_add()
try {
do_check_true(stmt.executeStep());
do_check_eq(2, stmt.getInt32(0));
}
finally {
stmt.finalize();
}
- do_test_finished();
+ // Run the next test.
+ run_next_test();
}
});
stmts[0].finalize();
stmts[1].finalize();
}
function test_transaction_created()
{
- dump("test_transaction_created()\n");
-
let stmts = [];
stmts[0] = getOpenedDatabase().createStatement(
"BEGIN"
);
stmts[1] = getOpenedDatabase().createStatement(
"SELECT * FROM test"
);
- do_test_pending()
getOpenedDatabase().executeAsync(stmts, stmts.length, {
handleResult: function(aResultSet)
{
dump("handleResults("+aResultSet+")\n");
do_throw("unexpected results obtained!");
},
handleError: function(aError)
{
dump("handleError("+aError.result+")\n");
},
handleCompletion: function(aReason)
{
dump("handleCompletion("+aReason+")\n");
do_check_eq(Ci.mozIStorageStatementCallback.REASON_ERROR, aReason);
- do_test_finished();
+
+ // Run the next test.
+ run_next_test();
}
});
stmts[0].finalize();
stmts[1].finalize();
}
+function test_multiple_bindings_on_statements()
+{
+ // This tests to make sure that we pass all the statements multiply bound
+ // parameters when we call executeAsync.
+ const AMOUNT_TO_ADD = 5;
+ const ITERATIONS = 5;
+
+ let stmts = [];
+ // We run the same statement twice, and should insert 2 * AMOUNT_TO_ADD.
+ for (let i = 0; i < ITERATIONS; i++) {
+ stmts[i] = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id, string, number, nuller, blober) " +
+ "VALUES (:int, :text, :real, :null, :blob)"
+ );
+ let params = stmts[i].newBindingParamsArray()
+ for (let j = 0; j < AMOUNT_TO_ADD; j++) {
+ let bp = params.newBindingParams();
+ bp.bindByName("int", INTEGER);
+ bp.bindByName("text", TEXT);
+ bp.bindByName("real", REAL);
+ bp.bindByName("null", null);
+ bp.bindBlobByName("blob", BLOB, BLOB.length);
+ params.addParams(bp);
+ }
+ stmts[i].bindParameters(params);
+ }
+
+ // Get our current number of rows in the table.
+ let currentRows = 0;
+ let countStmt = getOpenedDatabase().createStatement(
+ "SELECT COUNT(1) AS count FROM test"
+ );
+ try {
+ do_check_true(countStmt.executeStep());
+ currentRows = countStmt.row.count;
+ }
+ finally {
+ countStmt.reset();
+ }
+
+ // Execute asynchronously.
+ getOpenedDatabase().executeAsync(stmts, stmts.length, {
+ handleResult: function(aResultSet)
+ {
+ do_throw("Unexpected call to handleResult!");
+ },
+ handleError: function(aError)
+ {
+ print("Error code " + aError.result + " with message '" +
+ aError.message + "' returned.");
+ do_throw("Unexpected error!");
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason +
+ ") for test_multiple_bindings_on_statements");
+ do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
+
+ // Check to make sure we added all of our rows.
+ try {
+ do_check_true(countStmt.executeStep());
+ do_check_eq(currentRows + (ITERATIONS * AMOUNT_TO_ADD),
+ countStmt.row.count);
+ }
+ finally {
+ countStmt.finalize();
+ }
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+ stmts.forEach(function(stmt) stmt.finalize());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// Test Runner
let tests =
[
test_create_and_add,
test_transaction_created,
+ test_multiple_bindings_on_statements,
];
+let index = 0;
+
+function run_next_test()
+{
+ if (index < tests.length) {
+ do_test_pending();
+ print("Running the next test: " + tests[index].name);
+ tests[index++]();
+ }
+
+ do_test_finished();
+}
function run_test()
{
cleanup();
- for (let i = 0; i < tests.length; i++)
- tests[i]();
+ do_test_pending();
+ run_next_test();
}
new file mode 100644
--- /dev/null
+++ b/storage/test/unit/test_statement_executeAsync.js
@@ -0,0 +1,1006 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Storage Test Code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+// This file tests the functionality of mozIStorageStatement::executeAsync
+
+const INTEGER = 1;
+const TEXT = "this is test text";
+const REAL = 3.23;
+const BLOB = [1, 2];
+
+function test_create_table()
+{
+ // Ensure our table doesn't exists
+ do_check_false(getOpenedDatabase().tableExists("test"));
+
+ var stmt = getOpenedDatabase().createStatement(
+ "CREATE TABLE test (" +
+ "id INTEGER, " +
+ "string TEXT, " +
+ "number REAL, " +
+ "nuller NULL, " +
+ "blober BLOB" +
+ ")"
+ );
+
+ stmt.executeAsync({
+ handleResult: function(aResultSet)
+ {
+ dump("handleResult("+aResultSet+");\n");
+ do_throw("unexpected results obtained!");
+ },
+ handleError: function(aError)
+ {
+ print("error code " + aerror.result + " with message '" +
+ aerror.message + "' returned.");
+ do_throw("unexpected error!");
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason + ") for test_create_table");
+ do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
+
+ // Check that the table has been created
+ do_check_true(getOpenedDatabase().tableExists("test"));
+
+ // Verify that it's created correctly (this will throw if it wasn't)
+ var stmt = getOpenedDatabase().createStatement(
+ "SELECT id, string, number, nuller, blober FROM test"
+ );
+ stmt.finalize();
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+ stmt.finalize();
+}
+
+function test_add_data()
+{
+ var stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id, string, number, nuller, blober) " +
+ "VALUES (?, ?, ?, ?, ?)"
+ );
+ stmt.bindInt32Parameter(0, INTEGER);
+ stmt.bindStringParameter(1, TEXT);
+ stmt.bindDoubleParameter(2, REAL);
+ stmt.bindNullParameter(3);
+ stmt.bindBlobParameter(4, BLOB, BLOB.length);
+
+ stmt.executeAsync({
+ handleResult: function(aResultSet)
+ {
+ do_throw("unexpected results obtained!");
+ },
+ handleError: function(aError)
+ {
+ print("error code " + aerror.result + " with message '" +
+ aerror.message + "' returned.");
+ do_throw("unexpected error!");
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason + ") for test_add_data");
+ do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
+
+ // Check that the result is in the table
+ var stmt = getOpenedDatabase().createStatement(
+ "SELECT string, number, nuller, blober FROM test WHERE id = ?"
+ );
+ stmt.bindInt32Parameter(0, INTEGER);
+ try {
+ do_check_true(stmt.executeStep());
+ do_check_eq(TEXT, stmt.getString(0));
+ do_check_eq(REAL, stmt.getDouble(1));
+ do_check_true(stmt.getIsNull(2));
+ var count = { value: 0 };
+ var blob = { value: null };
+ stmt.getBlob(3, count, blob);
+ do_check_eq(BLOB.length, count.value);
+ for (var i = 0; i < BLOB.length; i++)
+ do_check_eq(BLOB[i], blob.value[i]);
+ }
+ finally {
+ stmt.reset();
+ stmt.finalize();
+ }
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+ stmt.finalize();
+}
+
+function test_get_data()
+{
+ var stmt = getOpenedDatabase().createStatement(
+ "SELECT string, number, nuller, blober, id FROM test WHERE id = ?"
+ );
+ stmt.bindInt32Parameter(0, 1);
+
+ stmt.executeAsync({
+ resultObtained: false,
+ handleResult: function(aResultSet)
+ {
+ dump("handleResult("+aResultSet+");\n");
+ do_check_false(this.resultObtained);
+ this.resultObtained = true;
+
+ // Check that we have a result
+ var tuple = aResultSet.getNextRow();
+ do_check_neq(null, tuple);
+
+ // Check that it's what we expect
+ do_check_eq(tuple.getResultByName("string"), tuple.getResultByIndex(0));
+ do_check_eq(TEXT, tuple.getResultByName("string"));
+ do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_TEXT,
+ tuple.getTypeOfIndex(0));
+
+ do_check_eq(tuple.getResultByName("number"), tuple.getResultByIndex(1));
+ do_check_eq(REAL, tuple.getResultByName("number"));
+ do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_FLOAT,
+ tuple.getTypeOfIndex(1));
+
+ do_check_eq(tuple.getResultByName("nuller"), tuple.getResultByIndex(2));
+ do_check_eq(null, tuple.getResultByName("nuller"));
+ do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_NULL,
+ tuple.getTypeOfIndex(2));
+
+ var blobByName = tuple.getResultByName("blober");
+ do_check_eq(BLOB.length, blobByName.length);
+ var blobByIndex = tuple.getResultByIndex(3);
+ do_check_eq(BLOB.length, blobByIndex.length);
+ for (var i = 0; i < BLOB.length; i++) {
+ do_check_eq(BLOB[i], blobByName[i]);
+ do_check_eq(BLOB[i], blobByIndex[i]);
+ }
+ var count = { value: 0 };
+ var blob = { value: null };
+ tuple.getBlob(3, count, blob);
+ do_check_eq(BLOB.length, count.value);
+ for (var i = 0; i < BLOB.length; i++)
+ do_check_eq(BLOB[i], blob.value[i]);
+ do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_BLOB,
+ tuple.getTypeOfIndex(3));
+
+ do_check_eq(tuple.getResultByName("id"), tuple.getResultByIndex(4));
+ do_check_eq(INTEGER, tuple.getResultByName("id"));
+ do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER,
+ tuple.getTypeOfIndex(4));
+
+ // check that we have no more results
+ tuple = aResultSet.getNextRow();
+ do_check_eq(null, tuple);
+ },
+ handleError: function(aError)
+ {
+ print("error code " + aerror.result + " with message '" +
+ aerror.message + "' returned.");
+ do_throw("unexpected error!");
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason + ") for test_get_data");
+ do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
+ do_check_true(this.resultObtained);
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+ stmt.finalize();
+}
+
+function test_tuple_out_of_bounds()
+{
+ var stmt = getOpenedDatabase().createStatement(
+ "SELECT string FROM test"
+ );
+
+ stmt.executeAsync({
+ resultObtained: false,
+ handleResult: function(aResultSet)
+ {
+ dump("handleResult("+aResultSet+");\n");
+ do_check_false(this.resultObtained);
+ this.resultObtained = true;
+
+ // Check that we have a result
+ var tuple = aResultSet.getNextRow();
+ do_check_neq(null, tuple);
+
+ // Check all out of bounds - should throw
+ var methods = [
+ "getTypeOfIndex",
+ "getInt32",
+ "getInt64",
+ "getDouble",
+ "getUTF8String",
+ "getString",
+ "getIsNull",
+ ];
+ for (var i in methods) {
+ try {
+ tuple[methods[i]](tuple.numEntries);
+ do_throw("did not throw :(");
+ }
+ catch (e) {
+ do_check_eq(Cr.NS_ERROR_ILLEGAL_VALUE, e.result);
+ }
+ }
+
+ // getBlob requires more args...
+ try {
+ var blob = { value: null };
+ var size = { value: 0 };
+ tuple.getBlob(tuple.numEntries, blob, size);
+ do_throw("did not throw :(");
+ }
+ catch (e) {
+ do_check_eq(Cr.NS_ERROR_ILLEGAL_VALUE, e.result);
+ }
+ },
+ handleError: function(aError)
+ {
+ print("Error code " + aError.result + " with message '" +
+ aError.message + "' returned.");
+ do_throw("unexpected error!");
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason +
+ ") for test_tuple_out_of_bounds");
+ do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
+ do_check_true(this.resultObtained);
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+ stmt.finalize();
+}
+
+function test_no_listener_works_on_success()
+{
+ var stmt = getOpenedDatabase().createStatement(
+ "DELETE FROM test WHERE id = ?"
+ );
+ stmt.bindInt32Parameter(0, 0);
+ stmt.executeAsync();
+ stmt.finalize();
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_no_listener_works_on_results()
+{
+ var stmt = getOpenedDatabase().createStatement(
+ "SELECT ?"
+ );
+ stmt.bindInt32Parameter(0, 1);
+ stmt.executeAsync();
+ stmt.finalize();
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_no_listener_works_on_error()
+{
+ // commit without a transaction will trigger an error
+ var stmt = getOpenedDatabase().createStatement(
+ "COMMIT"
+ );
+ stmt.executeAsync();
+ stmt.finalize();
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_partial_listener_works()
+{
+ var stmt = getOpenedDatabase().createStatement(
+ "DELETE FROM test WHERE id = ?"
+ );
+ stmt.bindInt32Parameter(0, 0);
+ stmt.executeAsync({
+ handleResult: function(aResultSet)
+ {
+ }
+ });
+ stmt.executeAsync({
+ handleError: function(aError)
+ {
+ }
+ });
+ stmt.executeAsync({
+ handleCompletion: function(aReason)
+ {
+ }
+ });
+ stmt.finalize();
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_immediate_cancellation()
+{
+ var stmt = getOpenedDatabase().createStatement(
+ "DELETE FROM test WHERE id = ?"
+ );
+ stmt.bindInt32Parameter(0, 0);
+ let reason = Ci.mozIStorageStatementCallback.REASON_CANCELED;
+ var pendingStatement = stmt.executeAsync({
+ handleResult: function(aResultSet)
+ {
+ do_throw("unexpected result!");
+ },
+ handleError: function(aError)
+ {
+ print("Error code " + aError.result + " with message '" +
+ aError.message + "' returned.");
+ do_throw("unexpected error!");
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason +
+ ") for test_immediate_cancellation");
+ do_check_eq(reason, aReason);
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+
+ // Cancel immediately
+ if (!pendingStatement.cancel()) {
+ // It is possible that we finished before we canceled
+ reason = Ci.mozIStorageStatementCallback.REASON_FINISHED;
+ }
+
+ stmt.finalize();
+}
+
+function test_double_cancellation()
+{
+ var stmt = getOpenedDatabase().createStatement(
+ "DELETE FROM test WHERE id = ?"
+ );
+ stmt.bindInt32Parameter(0, 0);
+ let reason = Ci.mozIStorageStatementCallback.REASON_CANCELED;
+ var pendingStatement = stmt.executeAsync({
+ handleResult: function(aResultSet)
+ {
+ do_throw("unexpected result!");
+ },
+ handleError: function(aError)
+ {
+ print("Error code " + aError.result + " with message '" +
+ aError.message + "' returned.");
+ do_throw("unexpected error!");
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason +
+ ") for test_double_cancellation");
+ do_check_eq(reason, aReason);
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+
+ // Cancel immediately
+ if (!pendingStatement.cancel()) {
+ // It is possible that we finished before we canceled
+ reason = Ci.mozIStorageStatementCallback.REASON_FINISHED;
+ }
+
+ // And cancel again - expect an exception
+ try {
+ pendingStatement.cancel();
+ do_throw("function call should have thrown!");
+ }
+ catch (e) {
+ do_check_eq(Cr.NS_ERROR_UNEXPECTED, e.result);
+ }
+
+ stmt.finalize();
+}
+
+function test_double_execute()
+{
+ var stmt = getOpenedDatabase().createStatement(
+ "SELECT * FROM test"
+ );
+
+ var listener = {
+ _timesCompleted: 0,
+ _hasResults: false,
+ handleResult: function(aResultSet)
+ {
+ do_check_false(this._hasResults);
+ this._hasResults = true;
+ },
+ handleError: function(aError)
+ {
+ print("Error code " + aError.result + " with message '" +
+ aError.message + "' returned.");
+ do_throw("unexpected error!");
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason +
+ ") for test_double_execute (iteration " +
+ (this._timesCompleted + 1) + ")");
+ do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
+ do_check_true(this._hasResults);
+ this._hasResults = false;
+ this._timesCompleted++;
+
+ // Run the next test.
+ if (this._timesCompleted == 2)
+ run_next_test();
+ }
+ }
+ stmt.executeAsync(listener);
+ stmt.executeAsync(listener);
+ stmt.finalize();
+}
+
+function test_finalized_statement_does_not_crash()
+{
+ var stmt = getOpenedDatabase().createStatement(
+ "SELECT * FROM TEST"
+ );
+ stmt.finalize();
+ // we are concerned about a crash here; an error is fine.
+ try {
+ stmt.executeAsync();
+ }
+ catch (ex) {}
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_bind_multiple_rows_by_index()
+{
+ const AMOUNT_TO_ADD = 5;
+ var stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id, string, number, nuller, blober) " +
+ "VALUES (?, ?, ?, ?, ?)"
+ );
+ var array = stmt.newBindingParamsArray();
+ for (let i = 0; i < AMOUNT_TO_ADD; i++) {
+ let bp = array.newBindingParams();
+ bp.bindByIndex(0, INTEGER);
+ bp.bindByIndex(1, TEXT);
+ bp.bindByIndex(2, REAL);
+ bp.bindByIndex(3, null);
+ bp.bindBlobByIndex(4, BLOB, BLOB.length);
+ array.addParams(bp);
+ }
+ stmt.bindParameters(array);
+
+ // Get our current number of rows in the table.
+ var currentRows = 0;
+ var countStmt = getOpenedDatabase().createStatement(
+ "SELECT COUNT(1) AS count FROM test"
+ );
+ try {
+ do_check_true(countStmt.executeStep());
+ currentRows = countStmt.row.count;
+ print("We have " + currentRows + " rows in test_bind_multiple_rows_by_index");
+ }
+ finally {
+ countStmt.reset();
+ }
+
+ // Execute asynchronously.
+ stmt.executeAsync({
+ handleResult: function(aResultSet)
+ {
+ do_throw("Unexpected call to handleResult!");
+ },
+ handleError: function(aError)
+ {
+ print("Error code " + aError.result + " with message '" +
+ aError.message + "' returned.");
+ do_throw("unexpected error!");
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason +
+ ") for test_bind_multiple_rows_by_index");
+ do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
+
+ // Check to make sure we added all of our rows.
+ try {
+ do_check_true(countStmt.executeStep());
+ print("We now have " + currentRows +
+ " rows in test_bind_multiple_rows_by_index");
+ do_check_eq(currentRows + AMOUNT_TO_ADD, countStmt.row.count);
+ }
+ finally {
+ countStmt.finalize();
+ }
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+ stmt.finalize();
+}
+
+function test_bind_multiple_rows_by_name()
+{
+ const AMOUNT_TO_ADD = 5;
+ var stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id, string, number, nuller, blober) " +
+ "VALUES (:int, :text, :real, :null, :blob)"
+ );
+ var array = stmt.newBindingParamsArray();
+ for (let i = 0; i < AMOUNT_TO_ADD; i++) {
+ let bp = array.newBindingParams();
+ bp.bindByName("int", INTEGER);
+ bp.bindByName("text", TEXT);
+ bp.bindByName("real", REAL);
+ bp.bindByName("null", null);
+ bp.bindBlobByName("blob", BLOB, BLOB.length);
+ array.addParams(bp);
+ }
+ stmt.bindParameters(array);
+
+ // Get our current number of rows in the table.
+ var currentRows = 0;
+ var countStmt = getOpenedDatabase().createStatement(
+ "SELECT COUNT(1) AS count FROM test"
+ );
+ try {
+ do_check_true(countStmt.executeStep());
+ currentRows = countStmt.row.count;
+ print("We have " + currentRows + " rows in test_bind_multiple_rows_by_name");
+ }
+ finally {
+ countStmt.reset();
+ }
+
+ // Execute asynchronously.
+ stmt.executeAsync({
+ handleResult: function(aResultSet)
+ {
+ do_throw("Unexpected call to handleResult!");
+ },
+ handleError: function(aError)
+ {
+ print("Error code " + aError.result + " with message '" +
+ aError.message + "' returned.");
+ do_throw("unexpected error!");
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason +
+ ") for test_bind_multiple_rows_by_name");
+ do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
+
+ // Check to make sure we added all of our rows.
+ try {
+ do_check_true(countStmt.executeStep());
+ print("We now have " + currentRows +
+ " rows in test_bind_multiple_rows_by_name");
+ do_check_eq(currentRows + AMOUNT_TO_ADD, countStmt.row.count);
+ }
+ finally {
+ countStmt.finalize();
+ }
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+ stmt.finalize();
+}
+
+function test_bind_out_of_bounds()
+{
+ let stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id) " +
+ "VALUES (?)"
+ );
+
+ let array = stmt.newBindingParamsArray();
+ let bp = array.newBindingParams();
+
+ // Check variant binding.
+ let exceptionCaught = false;
+ try {
+ bp.bindByIndex(1, INTEGER);
+ do_throw("we should have an exception!");
+ }
+ catch(e) {
+ do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
+ exceptionCaught = true;
+ }
+ do_check_true(exceptionCaught);
+
+ // Check blob binding.
+ exceptionCaught = false;
+ try {
+ bp.bindBlobByIndex(1, BLOB, BLOB.length);
+ do_throw("we should have an exception!");
+ }
+ catch(e) {
+ do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
+ exceptionCaught = true;
+ }
+ do_check_true(exceptionCaught);
+
+ stmt.finalize();
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_bind_no_such_name()
+{
+ let stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id) " +
+ "VALUES (:foo)"
+ );
+
+ let array = stmt.newBindingParamsArray();
+ let bp = array.newBindingParams();
+
+ // Check variant binding.
+ let exceptionCaught = false;
+ try {
+ bp.bindByName("doesnotexist", INTEGER);
+ do_throw("we should have an exception!");
+ }
+ catch(e) {
+ do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
+ exceptionCaught = true;
+ }
+ do_check_true(exceptionCaught);
+
+ // Check blob binding.
+ exceptionCaught = false;
+ try {
+ bp.bindBlobByName("doesnotexist", BLOB, BLOB.length);
+ do_throw("we should have an exception!");
+ }
+ catch(e) {
+ do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
+ exceptionCaught = true;
+ }
+ do_check_true(exceptionCaught);
+
+ stmt.finalize();
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_bind_bogus_type_by_index()
+{
+ // We try to bind a JS Object here that should fail to bind.
+ let stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (blober) " +
+ "VALUES (?)"
+ );
+
+ // We get an error after calling executeAsync, not when we bind.
+ let array = stmt.newBindingParamsArray();
+ let bp = array.newBindingParams();
+ bp.bindByIndex(0, run_test);
+ array.addParams(bp);
+ stmt.bindParameters(array);
+
+ stmt.executeAsync({
+ _errorObtained: false,
+ handleResult: function(aResultSet)
+ {
+ do_throw("Unexpected call to handleResult!");
+ },
+ handleError: function(aError)
+ {
+ print("Error code " + aError.result + " with message '" +
+ aError.message + "' returned.");
+ this._errorObtained = true;
+ do_check_eq(aError.result, Ci.mozIStorageError.MISMATCH);
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason +
+ ") for test_bind_bogus_type_by_index");
+ do_check_eq(Ci.mozIStorageStatementCallback.REASON_ERROR, aReason);
+ do_check_true(this._errorObtained);
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+ stmt.finalize();
+}
+
+function test_bind_bogus_type_by_name()
+{
+ // We try to bind a JS Object here that should fail to bind.
+ let stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (blober) " +
+ "VALUES (:blob)"
+ );
+
+ // We get an error after calling executeAsync, not when we bind.
+ let array = stmt.newBindingParamsArray();
+ let bp = array.newBindingParams();
+ bp.bindByName("blob", run_test);
+ array.addParams(bp);
+ stmt.bindParameters(array);
+
+ stmt.executeAsync({
+ _errorObtained: false,
+ handleResult: function(aResultSet)
+ {
+ do_throw("Unexpected call to handleResult!");
+ },
+ handleError: function(aError)
+ {
+ print("Error code " + aError.result + " with message '" +
+ aError.message + "' returned.");
+ this._errorObtained = true;
+ do_check_eq(aError.result, Ci.mozIStorageError.MISMATCH);
+ },
+ handleCompletion: function(aReason)
+ {
+ print("handleCompletion(" + aReason +
+ ") for test_bind_bogus_type_by_name");
+ do_check_eq(Ci.mozIStorageStatementCallback.REASON_ERROR, aReason);
+ do_check_true(this._errorObtained);
+
+ // Run the next test.
+ run_next_test();
+ }
+ });
+ stmt.finalize();
+}
+
+function test_bind_params_already_locked()
+{
+ let stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id) " +
+ "VALUES (:int)"
+ );
+
+ let array = stmt.newBindingParamsArray();
+ let bp = array.newBindingParams();
+ bp.bindByName("int", INTEGER);
+ array.addParams(bp);
+
+ // We should get an error after we call addParams and try to bind again.
+ let exceptionCaught = false;
+ try {
+ bp.bindByName("int", INTEGER);
+ do_throw("we should have an exception!");
+ }
+ catch(e) {
+ do_check_eq(e.result, Cr.NS_ERROR_UNEXPECTED);
+ exceptionCaught = true;
+ }
+ do_check_true(exceptionCaught);
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_bind_params_array_already_locked()
+{
+ let stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id) " +
+ "VALUES (:int)"
+ );
+
+ let array = stmt.newBindingParamsArray();
+ let bp1 = array.newBindingParams();
+ bp1.bindByName("int", INTEGER);
+ array.addParams(bp1);
+ let bp2 = array.newBindingParams();
+ stmt.bindParameters(array);
+ bp2.bindByName("int", INTEGER);
+
+ // We should get an error after we have bound the array to the statement.
+ let exceptionCaught = false;
+ try {
+ array.addParams(bp2);
+ do_throw("we should have an exception!");
+ }
+ catch(e) {
+ do_check_eq(e.result, Cr.NS_ERROR_UNEXPECTED);
+ exceptionCaught = true;
+ }
+ do_check_true(exceptionCaught);
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_no_binding_params_from_locked_array()
+{
+ let stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id) " +
+ "VALUES (:int)"
+ );
+
+ let array = stmt.newBindingParamsArray();
+ let bp = array.newBindingParams();
+ bp.bindByName("int", INTEGER);
+ array.addParams(bp);
+ stmt.bindParameters(array);
+
+ // We should not be able to get a new BindingParams object after we have bound
+ // to the statement.
+ let exceptionCaught = false;
+ try {
+ bp = array.newBindingParams();
+ do_throw("we should have an exception!");
+ }
+ catch(e) {
+ do_check_eq(e.result, Cr.NS_ERROR_UNEXPECTED);
+ exceptionCaught = true;
+ }
+ do_check_true(exceptionCaught);
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_not_right_owning_array()
+{
+ let stmt = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id) " +
+ "VALUES (:int)"
+ );
+
+ let array1 = stmt.newBindingParamsArray();
+ let array2 = stmt.newBindingParamsArray();
+ let bp = array1.newBindingParams();
+ bp.bindByName("int", INTEGER);
+
+ // We should not be able to add bp to array2 since it was created from array1.
+ let exceptionCaught = false;
+ try {
+ array2.addParams(bp);
+ do_throw("we should have an exception!");
+ }
+ catch(e) {
+ do_check_eq(e.result, Cr.NS_ERROR_UNEXPECTED);
+ exceptionCaught = true;
+ }
+ do_check_true(exceptionCaught);
+
+ // Run the next test.
+ run_next_test();
+}
+
+function test_not_right_owning_statement()
+{
+ let stmt1 = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id) " +
+ "VALUES (:int)"
+ );
+ let stmt2 = getOpenedDatabase().createStatement(
+ "INSERT INTO test (id) " +
+ "VALUES (:int)"
+ );
+
+ let array1 = stmt1.newBindingParamsArray();
+ let array2 = stmt2.newBindingParamsArray();
+ let bp = array1.newBindingParams();
+ bp.bindByName("int", INTEGER);
+ array1.addParams(bp);
+
+ // We should not be able to bind array1 since it was created from stmt1.
+ let exceptionCaught = false;
+ try {
+ stmt2.bindParameters(array1);
+ do_throw("we should have an exception!");
+ }
+ catch(e) {
+ do_check_eq(e.result, Cr.NS_ERROR_UNEXPECTED);
+ exceptionCaught = true;
+ }
+ do_check_true(exceptionCaught);
+
+ // Run the next test.
+ run_next_test();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// Test Runner
+
+var tests =
+[
+ test_create_table,
+ test_add_data,
+ test_get_data,
+ test_tuple_out_of_bounds,
+ test_no_listener_works_on_success,
+ test_no_listener_works_on_results,
+ test_no_listener_works_on_error,
+ test_partial_listener_works,
+ test_immediate_cancellation,
+ test_double_cancellation,
+ test_double_execute,
+ test_finalized_statement_does_not_crash,
+ test_bind_multiple_rows_by_index,
+ test_bind_multiple_rows_by_name,
+ test_bind_out_of_bounds,
+ test_bind_no_such_name,
+ test_bind_bogus_type_by_index,
+ test_bind_bogus_type_by_name,
+ test_bind_params_already_locked,
+ test_bind_params_array_already_locked,
+ test_no_binding_params_from_locked_array,
+ test_not_right_owning_array,
+ test_not_right_owning_statement,
+];
+let index = 0;
+
+function run_next_test()
+{
+ if (index < tests.length) {
+ do_test_pending();
+ print("Running the next test: " + tests[index].name);
+ tests[index++]();
+ }
+
+ do_test_finished();
+}
+
+function run_test()
+{
+ cleanup();
+
+ do_test_pending();
+ run_next_test();
+}
deleted file mode 100644
--- a/storage/test/unit/test_storage_statement_executeAsync.js
+++ /dev/null
@@ -1,521 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Storage Test Code.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2008
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Shawn Wilsher <me@shawnwilsher.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-// This file tests the functionality of mozIStorageStatement::executeAsync
-
-const INTEGER = 1;
-const TEXT = "this is test text";
-const REAL = 3.23;
-const BLOB = [1, 2];
-
-function test_create_table()
-{
- dump("test_create_table()\n");
-
- // Ensure our table doesn't exists
- do_check_false(getOpenedDatabase().tableExists("test"));
-
- var stmt = getOpenedDatabase().createStatement(
- "CREATE TABLE test (" +
- "id INTEGER PRIMARY KEY, " +
- "string TEXT, " +
- "number REAL, " +
- "nuller NULL, " +
- "blober BLOB" +
- ")"
- );
-
- do_test_pending();
- stmt.executeAsync({
- handleResult: function(aResultSet)
- {
- dump("handleResult("+aResultSet+");\n");
- do_throw("unexpected results obtained!");
- },
- handleError: function(aError)
- {
- dump("handleError("+aError+");\n");
- do_throw("unexpected error!");
- },
- handleCompletion: function(aReason)
- {
- dump("handleCompletion("+aReason+");\n");
- do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
-
- // Check that the table has been created
- do_check_true(getOpenedDatabase().tableExists("test"));
-
- // Verify that it's created correctly (this will throw if it wasn't)
- var stmt = getOpenedDatabase().createStatement(
- "SELECT id, string, number, nuller, blober FROM test"
- );
- stmt.finalize();
-
- // Now we run the rest of the tests
- for (var i = 0; i < tests.length; i++)
- tests[i]();
-
- do_test_finished();
- }
- });
- stmt.finalize();
-}
-
-function test_add_data()
-{
- dump("test_add_data()\n");
-
- var stmt = getOpenedDatabase().createStatement(
- "INSERT INTO test (id, string, number, nuller, blober) VALUES (?, ?, ?, ?, ?)"
- );
- stmt.bindInt32Parameter(0, INTEGER);
- stmt.bindStringParameter(1, TEXT);
- stmt.bindDoubleParameter(2, REAL);
- stmt.bindNullParameter(3);
- stmt.bindBlobParameter(4, BLOB, BLOB.length);
-
- do_test_pending();
- stmt.executeAsync({
- handleResult: function(aResultSet)
- {
- dump("handleResult("+aResultSet+");\n");
- do_throw("unexpected results obtained!");
- },
- handleError: function(aError)
- {
- dump("handleError("+aError+");\n");
- do_throw("unexpected error!");
- },
- handleCompletion: function(aReason)
- {
- dump("handleCompletion("+aReason+");\n");
- do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
-
- // Check that the result is in the table
- var stmt = getOpenedDatabase().createStatement(
- "SELECT string, number, nuller, blober FROM test WHERE id = ?"
- );
- stmt.bindInt32Parameter(0, INTEGER);
- try {
- do_check_true(stmt.executeStep());
- do_check_eq(TEXT, stmt.getString(0));
- do_check_eq(REAL, stmt.getDouble(1));
- do_check_true(stmt.getIsNull(2));
- var count = { value: 0 };
- var blob = { value: null };
- stmt.getBlob(3, count, blob);
- do_check_eq(BLOB.length, count.value);
- for (var i = 0; i < BLOB.length; i++)
- do_check_eq(BLOB[i], blob.value[i]);
- }
- finally {
- stmt.reset();
- stmt.finalize();
- }
-
- do_test_finished();
- }
- });
- stmt.finalize();
-}
-
-function test_get_data()
-{
- dump("test_get_data()\n");
-
- var stmt = getOpenedDatabase().createStatement(
- "SELECT string, number, nuller, blober, id FROM test WHERE id = ?"
- );
- stmt.bindInt32Parameter(0, 1);
-
- do_test_pending();
- stmt.executeAsync({
- resultObtained: false,
- handleResult: function(aResultSet)
- {
- dump("handleResult("+aResultSet+");\n");
- do_check_false(this.resultObtained);
- this.resultObtained = true;
-
- // Check that we have a result
- var tuple = aResultSet.getNextRow();
- do_check_neq(null, tuple);
-
- // Check that it's what we expect
- do_check_eq(tuple.getResultByName("string"), tuple.getResultByIndex(0));
- do_check_eq(TEXT, tuple.getResultByName("string"));
- do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_TEXT,
- tuple.getTypeOfIndex(0));
-
- do_check_eq(tuple.getResultByName("number"), tuple.getResultByIndex(1));
- do_check_eq(REAL, tuple.getResultByName("number"));
- do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_FLOAT,
- tuple.getTypeOfIndex(1));
-
- do_check_eq(tuple.getResultByName("nuller"), tuple.getResultByIndex(2));
- do_check_eq(null, tuple.getResultByName("nuller"));
- do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_NULL,
- tuple.getTypeOfIndex(2));
-
- var blobByName = tuple.getResultByName("blober");
- do_check_eq(BLOB.length, blobByName.length);
- var blobByIndex = tuple.getResultByIndex(3);
- do_check_eq(BLOB.length, blobByIndex.length);
- for (var i = 0; i < BLOB.length; i++) {
- do_check_eq(BLOB[i], blobByName[i]);
- do_check_eq(BLOB[i], blobByIndex[i]);
- }
- var count = { value: 0 };
- var blob = { value: null };
- tuple.getBlob(3, count, blob);
- do_check_eq(BLOB.length, count.value);
- for (var i = 0; i < BLOB.length; i++)
- do_check_eq(BLOB[i], blob.value[i]);
- do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_BLOB,
- tuple.getTypeOfIndex(3));
-
- do_check_eq(tuple.getResultByName("id"), tuple.getResultByIndex(4));
- do_check_eq(INTEGER, tuple.getResultByName("id"));
- do_check_eq(Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER,
- tuple.getTypeOfIndex(4));
-
- // check that we have no more results
- tuple = aResultSet.getNextRow();
- do_check_eq(null, tuple);
- },
- handleError: function(aError)
- {
- dump("handleError("+aError+");\n");
- do_throw("unexpected error!");
- },
- handleCompletion: function(aReason)
- {
- dump("handleCompletion("+aReason+");\n");
- do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
- do_check_true(this.resultObtained);
- do_test_finished();
- }
- });
- stmt.finalize();
-}
-
-function test_tuple_out_of_bounds()
-{
- dump("test_tuple_out_of_bounds()\n");
-
- var stmt = getOpenedDatabase().createStatement(
- "SELECT string FROM test"
- );
-
- do_test_pending();
- stmt.executeAsync({
- resultObtained: false,
- handleResult: function(aResultSet)
- {
- dump("handleResult("+aResultSet+");\n");
- do_check_false(this.resultObtained);
- this.resultObtained = true;
-
- // Check that we have a result
- var tuple = aResultSet.getNextRow();
- do_check_neq(null, tuple);
-
- // Check all out of bounds - should throw
- var methods = [
- "getTypeOfIndex",
- "getInt32",
- "getInt64",
- "getDouble",
- "getUTF8String",
- "getString",
- "getIsNull",
- ];
- for (var i in methods) {
- try {
- tuple[methods[i]](tuple.numEntries);
- do_throw("did not throw :(");
- }
- catch (e) {
- do_check_eq(Cr.NS_ERROR_ILLEGAL_VALUE, e.result);
- }
- }
-
- // getBlob requires more args...
- try {
- var blob = { value: null };
- var size = { value: 0 };
- tuple.getBlob(tuple.numEntries, blob, size);
- do_throw("did not throw :(");
- }
- catch (e) {
- do_check_eq(Cr.NS_ERROR_ILLEGAL_VALUE, e.result);
- }
- },
- handleError: function(aError)
- {
- dump("handleError("+aError+");\n");
- do_throw("unexpected error!");
- },
- handleCompletion: function(aReason)
- {
- dump("handleCompletion("+aReason+");\n");
- do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
- do_check_true(this.resultObtained);
- do_test_finished();
- }
- });
- stmt.finalize();
-}
-
-function test_no_listener_works_on_success()
-{
- dump("test_no_listener_works_on_success()\n");
-
- var stmt = getOpenedDatabase().createStatement(
- "DELETE FROM test WHERE id = ?"
- );
- stmt.bindInt32Parameter(0, 0);
- stmt.executeAsync();
- stmt.finalize();
-}
-
-function test_no_listener_works_on_results()
-{
- dump("test_no_listener_works_on_results()\n");
-
- var stmt = getOpenedDatabase().createStatement(
- "SELECT ?"
- );
- stmt.bindInt32Parameter(0, 1);
- stmt.executeAsync();
- stmt.finalize();
-}
-
-function test_no_listener_works_on_error()
-{
- return;
- dump("test_no_listener_works_on_error()\n");
-
- // commit without a transaction will trigger an error
- var stmt = getOpenedDatabase().createStatement(
- "COMMIT"
- );
- stmt.executeAsync();
- stmt.finalize();
-}
-
-function test_partial_listener_works()
-{
- dump("test_partial_listener_works()\n");
-
- var stmt = getOpenedDatabase().createStatement(
- "DELETE FROM test WHERE id = ?"
- );
- stmt.bindInt32Parameter(0, 0);
- stmt.executeAsync({
- handleResult: function(aResultSet)
- {
- }
- });
- stmt.executeAsync({
- handleError: function(aError)
- {
- }
- });
- stmt.executeAsync({
- handleCompletion: function(aReason)
- {
- }
- });
- stmt.finalize();
-}
-
-function test_immediate_cancellation()
-{
- dump("test_immediate_cancelation()\n");
-
- var stmt = getOpenedDatabase().createStatement(
- "DELETE FROM test WHERE id = ?"
- );
- stmt.bindInt32Parameter(0, 0);
- let reason = Ci.mozIStorageStatementCallback.REASON_CANCELED;
- var pendingStatement = stmt.executeAsync({
- handleResult: function(aResultSet)
- {
- dump("handleResult("+aResultSet+");\n");
- do_throw("unexpected result!");
- },
- handleError: function(aError)
- {
- dump("handleError("+aError+");\n");
- do_throw("unexpected error!");
- },
- handleCompletion: function(aReason)
- {
- dump("handleCompletion("+aReason+");\n");
- do_check_eq(reason, aReason);
- do_test_finished();
- }
- });
- do_test_pending();
-
- // Cancel immediately
- if (!pendingStatement.cancel()) {
- // It is possible that we finished before we canceled
- reason = Ci.mozIStorageStatementCallback.REASON_FINISHED;
- }
-
- stmt.finalize();
-}
-
-function test_double_cancellation()
-{
- dump("test_double_cancelation()\n");
-
- var stmt = getOpenedDatabase().createStatement(
- "DELETE FROM test WHERE id = ?"
- );
- stmt.bindInt32Parameter(0, 0);
- let reason = Ci.mozIStorageStatementCallback.REASON_CANCELED;
- var pendingStatement = stmt.executeAsync({
- handleResult: function(aResultSet)
- {
- dump("handleResult("+aResultSet+");\n");
- do_throw("unexpected result!");
- },
- handleError: function(aError)
- {
- dump("handleError("+aError+");\n");
- do_throw("unexpected error!");
- },
- handleCompletion: function(aReason)
- {
- dump("handleCompletion("+aReason+");\n");
- do_check_eq(reason, aReason);
- do_test_finished();
- }
- });
- do_test_pending();
-
- // Cancel immediately
- if (!pendingStatement.cancel()) {
- // It is possible that we finished before we canceled
- reason = Ci.mozIStorageStatementCallback.REASON_FINISHED;
- }
-
- // And cancel again - expect an exception
- try {
- pendingStatement.cancel();
- do_throw("function call should have thrown!");
- }
- catch (e) {
- do_check_eq(Cr.NS_ERROR_UNEXPECTED, e.result);
- }
-
- stmt.finalize();
-}
-
-function test_double_execute()
-{
- dump("test_double_execute()\n");
-
- var stmt = getOpenedDatabase().createStatement(
- "SELECT * FROM test"
- );
-
- var listener = {
- handleResult: function(aResultSet)
- {
- dump("handleResult("+aResultSet+");\n");
- },
- handleError: function(aError)
- {
- dump("handleError("+aError+");\n");
- do_throw("unexpected error!");
- },
- handleCompletion: function(aReason)
- {
- dump("handleCompletion("+aReason+");\n");
- do_check_eq(Ci.mozIStorageStatementCallback.REASON_FINISHED, aReason);
- do_test_finished();
- }
- }
- do_test_pending();
- stmt.executeAsync(listener);
- do_test_pending();
- stmt.executeAsync(listener);
- stmt.finalize();
-}
-
-function test_finalized_statement_does_not_crash()
-{
- dump("test_finalized_statement_does_not_crash()\n");
-
- var stmt = getOpenedDatabase().createStatement(
- "SELECT * FROM TEST"
- );
- stmt.finalize();
- // we are concerned about a crash here; an error is fine.
- try {
- stmt.executeAsync();
- }
- catch (ex) {}
-}
-
-var tests =
-[
- test_add_data,
- test_get_data,
- test_tuple_out_of_bounds,
- test_no_listener_works_on_success,
- test_no_listener_works_on_results,
- test_no_listener_works_on_error,
- test_partial_listener_works,
- test_immediate_cancellation,
- test_double_cancellation,
- test_double_execute,
- test_finalized_statement_does_not_crash,
-];
-
-function run_test()
-{
- cleanup();
-
- // This test has to run first and run to completion. When it is done, it will
- // run the rest of the tests.
- test_create_table();
-}