Bug 490085 - Add ability to bind multiple sets of parameters and execute asynchronously

Improves the asynchronous API by allowing multiple parameters to be bound before
executing it.  The statement is then executed with each set of parameters
asynchronously.
r=asuth
sr=vlad

--HG--
rename : storage/test/unit/test_storage_statement_executeAsync.js => storage/test/unit/test_statement_executeAsync.js
This commit is contained in:
Shawn Wilsher 2009-06-17 12:12:51 -07:00
parent 2df095f854
commit e25711d0bd
18 changed files with 2254 additions and 551 deletions

View File

@ -62,6 +62,8 @@ XPIDLSRCS = \
mozIStorageError.idl \
mozIStorageStatementCallback.idl \
mozIStoragePendingStatement.idl \
mozIStorageBindingParamsArray.idl \
mozIStorageBindingParams.idl \
$(NULL)
EXPORTS_NAMESPACES = mozilla

View File

@ -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);
};

View File

@ -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);
};

View File

@ -43,8 +43,9 @@
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.
@ -129,6 +130,24 @@ interface mozIStorageStatement : mozIStorageValueArray {
[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().

View File

@ -83,6 +83,8 @@ CPPSRCS = \
mozStorageAsyncStatementExecution.cpp \
mozStorageStatementJSHelper.cpp \
mozStoragePrivateHelpers.cpp \
mozStorageBindingParamsArray.cpp \
mozStorageBindingParams.cpp \
$(NULL)
LOCAL_INCLUDES = \

View File

@ -43,12 +43,14 @@
#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 {
@ -172,7 +174,7 @@ private:
/* static */
nsresult
AsyncExecuteStatements::execute(sqlite3_stmt_array &aStatements,
AsyncExecuteStatements::execute(StatementDataArray &aStatements,
Connection *aConnection,
mozIStorageStatementCallback *aCallback,
mozIStoragePendingStatement **_stmt)
@ -193,7 +195,7 @@ AsyncExecuteStatements::execute(sqlite3_stmt_array &aStatements,
return NS_OK;
}
AsyncExecuteStatements::AsyncExecuteStatements(sqlite3_stmt_array &aStatements,
AsyncExecuteStatements::AsyncExecuteStatements(StatementDataArray &aStatements,
Connection *aConnection,
mozIStorageStatementCallback *aCallback)
: mConnection(aConnection)
@ -226,6 +228,47 @@ AsyncExecuteStatements::shouldNotify()
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)
@ -377,10 +420,8 @@ AsyncExecuteStatements::notifyComplete()
// 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) {
@ -426,8 +467,19 @@ AsyncExecuteStatements::notifyError(PRInt32 aErrorCode,
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);
@ -510,7 +562,9 @@ AsyncExecuteStatements::Run()
// 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);
@ -518,9 +572,17 @@ AsyncExecuteStatements::Run()
// 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

View File

@ -57,6 +57,7 @@ namespace storage {
class Connection;
class ResultSet;
class StatementData;
class AsyncExecuteStatements : public nsIRunnable
, public mozIStoragePendingStatement
@ -76,15 +77,15 @@ public:
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
@ -92,7 +93,7 @@ public:
* @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);
@ -107,10 +108,27 @@ public:
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.
@ -165,8 +183,11 @@ private:
* 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.
@ -175,7 +196,7 @@ private:
*/
nsresult notifyResults();
sqlite3_stmt_array mStatements;
StatementDataArray mStatements;
nsRefPtr<Connection> mConnection;
mozStorageTransaction *mTransactionManager;
mozIStorageStatementCallback *mCallback;

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -62,6 +62,7 @@
#include "mozStorageStatement.h"
#include "mozStorageArgValueArray.h"
#include "mozStoragePrivateHelpers.h"
#include "mozStorageStatementData.h"
#include "prlog.h"
#include "prprf.h"
@ -627,7 +628,7 @@ Connection::ExecuteAsync(mozIStorageStatement **aStatements,
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();
@ -657,7 +658,9 @@ Connection::ExecuteAsync(mozIStorageStatement **aStatements,
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;
}

View File

@ -505,6 +505,33 @@ Statement::BindBlobParameter(PRUint32 aParamIndex,
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()
{

View File

@ -45,6 +45,7 @@
#include "nsTArray.h"
#include "mozStorageBindingParamsArray.h"
#include "mozIStorageStatement.h"
class nsIXPConnectJSObjectHolder;
@ -82,6 +83,15 @@ public:
*/
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();
@ -92,6 +102,12 @@ private:
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.

View File

@ -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_

View File

@ -44,11 +44,9 @@ 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, " +
@ -73,7 +71,6 @@ function test_create_and_add()
stmts[1].bindNullParameter(2);
stmts[1].bindBlobParameter(3, BLOB, BLOB.length);
do_test_pending();
getOpenedDatabase().executeAsync(stmts, stmts.length, {
handleResult: function(aResultSet)
{
@ -123,7 +120,8 @@ function test_create_and_add()
stmt.finalize();
}
do_test_finished();
// Run the next test.
run_next_test();
}
});
stmts[0].finalize();
@ -132,8 +130,6 @@ function test_create_and_add()
function test_transaction_created()
{
dump("test_transaction_created()\n");
let stmts = [];
stmts[0] = getOpenedDatabase().createStatement(
"BEGIN"
@ -142,7 +138,6 @@ function test_transaction_created()
"SELECT * FROM test"
);
do_test_pending()
getOpenedDatabase().executeAsync(stmts, stmts.length, {
handleResult: function(aResultSet)
{
@ -157,24 +152,116 @@ function test_transaction_created()
{
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();
}

File diff suppressed because it is too large Load Diff

View File

@ -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();
}