Implement index.openCursor

This commit is contained in:
Ben Turner 2010-06-04 18:48:12 -07:00
parent 3f04112c9c
commit d5f3eada6f
10 changed files with 645 additions and 74 deletions

View File

@ -54,6 +54,7 @@
#include "AsyncConnectionHelper.h"
#include "IDBEvents.h"
#include "IDBIndexRequest.h"
#include "IDBObjectStoreRequest.h"
#include "IDBTransactionRequest.h"
#include "Savepoint.h"
@ -139,24 +140,95 @@ IDBCursorRequest::Create(IDBRequest* aRequest,
PRUint16 aDirection,
nsTArray<KeyValuePair>& aData)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aRequest, "Null pointer!");
NS_ASSERTION(aTransaction, "Null pointer!");
NS_ASSERTION(aObjectStore, "Null pointer!");
nsRefPtr<IDBCursorRequest> cursor(new IDBCursorRequest());
cursor->mRequest = aRequest;
cursor->mTransaction = aTransaction;
nsRefPtr<IDBCursorRequest> cursor =
IDBCursorRequest::CreateCommon(aRequest, aTransaction, aDirection);
cursor->mObjectStore = aObjectStore;
cursor->mDirection = aDirection;
if (!cursor->mData.SwapElements(aData)) {
NS_ERROR("Out of memory?!");
return nsnull;
}
NS_ASSERTION(!cursor->mData.IsEmpty(), "Should ever have an empty set!");
NS_ASSERTION(!cursor->mData.IsEmpty(), "Should never have an empty set!");
cursor->mDataIndex = cursor->mData.Length() - 1;
cursor->mType = OBJECTSTORE;
return cursor.forget();
}
// static
already_AddRefed<IDBCursorRequest>
IDBCursorRequest::Create(IDBRequest* aRequest,
IDBTransactionRequest* aTransaction,
IDBIndexRequest* aIndex,
PRUint16 aDirection,
nsTArray<KeyKeyPair>& aData)
{
NS_ASSERTION(aIndex, "Null pointer!");
nsRefPtr<IDBCursorRequest> cursor =
IDBCursorRequest::CreateCommon(aRequest, aTransaction, aDirection);
cursor->mObjectStore = aIndex->ObjectStore();
cursor->mIndex = aIndex;
if (!cursor->mKeyData.SwapElements(aData)) {
NS_ERROR("Out of memory?!");
return nsnull;
}
NS_ASSERTION(!cursor->mKeyData.IsEmpty(), "Should never have an empty set!");
cursor->mDataIndex = cursor->mKeyData.Length() - 1;
cursor->mType = INDEX;
return cursor.forget();
}
// static
already_AddRefed<IDBCursorRequest>
IDBCursorRequest::Create(IDBRequest* aRequest,
IDBTransactionRequest* aTransaction,
IDBIndexRequest* aIndex,
PRUint16 aDirection,
nsTArray<KeyValuePair>& aData)
{
NS_ASSERTION(aIndex, "Null pointer!");
nsRefPtr<IDBCursorRequest> cursor =
IDBCursorRequest::CreateCommon(aRequest, aTransaction, aDirection);
cursor->mObjectStore = aIndex->ObjectStore();
cursor->mIndex = aIndex;
if (!cursor->mData.SwapElements(aData)) {
NS_ERROR("Out of memory?!");
return nsnull;
}
NS_ASSERTION(!cursor->mData.IsEmpty(), "Should never have an empty set!");
cursor->mDataIndex = cursor->mData.Length() - 1;
cursor->mType = INDEXOBJECT;
return cursor.forget();
}
// static
already_AddRefed<IDBCursorRequest>
IDBCursorRequest::CreateCommon(IDBRequest* aRequest,
IDBTransactionRequest* aTransaction,
PRUint16 aDirection)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aRequest, "Null pointer!");
NS_ASSERTION(aTransaction, "Null pointer!");
nsRefPtr<IDBCursorRequest> cursor(new IDBCursorRequest());
cursor->mRequest = aRequest;
cursor->mTransaction = aTransaction;
cursor->mDirection = aDirection;
return cursor.forget();
}
@ -166,7 +238,8 @@ IDBCursorRequest::IDBCursorRequest()
mCachedValue(JSVAL_VOID),
mHaveCachedValue(false),
mJSRuntime(nsnull),
mDataIndex(0)
mDataIndex(0),
mType(OBJECTSTORE)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
@ -212,7 +285,9 @@ IDBCursorRequest::GetKey(nsIVariant** aKey)
do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
const Key& key = mData[mDataIndex].key;
const Key& key = mType == INDEX ?
mKeyData[mDataIndex].key :
mData[mDataIndex].key;
NS_ASSERTION(!key.IsUnset() && !key.IsNull(), "Bad key!");
if (key.IsString()) {
@ -242,17 +317,49 @@ IDBCursorRequest::GetKey(nsIVariant** aKey)
}
NS_IMETHODIMP
IDBCursorRequest::GetValue(nsIVariant** /* aValue */)
IDBCursorRequest::GetValue(nsIVariant** aValue)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsresult rv;
if (mType == INDEX) {
nsCOMPtr<nsIWritableVariant> variant =
do_CreateInstance(NS_VARIANT_CONTRACTID);
if (!variant) {
NS_ERROR("Couldn't create variant!");
return NS_ERROR_FAILURE;
}
const Key& value = mKeyData[mDataIndex].value;
NS_ASSERTION(!value.IsUnset() && !value.IsNull(), "Bad key!");
if (value.IsInt()) {
rv = variant->SetAsInt64(value.IntValue());
}
else if (value.IsString()) {
rv = variant->SetAsAString(value.StringValue());
}
else {
NS_NOTREACHED("Bad key type!");
}
NS_ENSURE_SUCCESS(rv, rv);
rv = variant->SetWritable(PR_FALSE);;
NS_ENSURE_SUCCESS(rv, rv);
nsIWritableVariant* result;
variant.forget(&result);
*aValue = result;
return NS_OK;
}
NS_WARNING("Using a slow path for GetValue! Fix this now!");
nsIXPConnect* xpc = nsContentUtils::XPConnect();
NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
nsAXPCNativeCallContext* cc;
nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
rv = xpc->GetCurrentNativeCallContext(&cc);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
@ -271,7 +378,7 @@ IDBCursorRequest::GetValue(nsIVariant** /* aValue */)
JSRuntime* rt = JS_GetRuntime(cx);
JSBool ok = JS_AddNamedRootRT(rt, &mCachedValue,
"IDBCursorRequest::mCachedValue");
"IDBCursorRequest::mCachedValue");
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
mJSRuntime = rt;
@ -313,6 +420,11 @@ IDBCursorRequest::Continue(nsIVariant* aKey,
}
}
if (mType != OBJECTSTORE && !key.IsUnset()) {
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
NS_ENSURE_TRUE(pool, NS_ERROR_FAILURE);
@ -333,6 +445,11 @@ IDBCursorRequest::Update(nsIVariant* aValue,
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mType != OBJECTSTORE) {
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (!mObjectStore->TransactionIsOpen()) {
return NS_ERROR_UNEXPECTED;
}
@ -475,6 +592,11 @@ IDBCursorRequest::Remove(nsIIDBRequest** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (mType != OBJECTSTORE) {
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (!mObjectStore->TransactionIsOpen()) {
return NS_ERROR_UNEXPECTED;
}
@ -630,7 +752,12 @@ ContinueRunnable::Run()
mCursor->mCachedValue = JSVAL_VOID;
mCursor->mHaveCachedValue = false;
mCursor->mData.RemoveElementAt(mCursor->mDataIndex);
if (mCursor->mType == IDBCursorRequest::INDEX) {
mCursor->mKeyData.RemoveElementAt(mCursor->mDataIndex);
}
else {
mCursor->mData.RemoveElementAt(mCursor->mDataIndex);
}
if (mCursor->mDataIndex) {
mCursor->mDataIndex--;
}
@ -642,7 +769,11 @@ ContinueRunnable::Run()
return NS_ERROR_FAILURE;
}
if (mCursor->mData.IsEmpty()) {
PRBool empty = mCursor->mType == IDBCursorRequest::INDEX ?
mCursor->mKeyData.IsEmpty() :
mCursor->mData.IsEmpty();
if (empty) {
rv = variant->SetAsEmpty();
NS_ENSURE_SUCCESS(rv, rv);
}
@ -650,6 +781,11 @@ ContinueRunnable::Run()
if (!mKey.IsUnset()) {
NS_ASSERTION(!mKey.IsNull(), "Huh?!");
if (mCursor->mType != IDBCursorRequest::OBJECTSTORE) {
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_WARNING("Using a slow O(n) search for continue(key), do something "
"smarter!");

View File

@ -49,6 +49,7 @@ class nsIRunnable;
BEGIN_INDEXEDDB_NAMESPACE
class IDBIndexRequest;
class IDBRequest;
class IDBObjectStoreRequest;
class IDBTransactionRequest;
@ -59,6 +60,12 @@ struct KeyValuePair
nsString value;
};
struct KeyKeyPair
{
Key key;
Key value;
};
class ContinueRunnable;
class IDBCursorRequest : public IDBRequest::Generator,
@ -79,14 +86,43 @@ public:
PRUint16 aDirection,
nsTArray<KeyValuePair>& aData);
static
already_AddRefed<IDBCursorRequest>
Create(IDBRequest* aRequest,
IDBTransactionRequest* aTransaction,
IDBIndexRequest* aIndex,
PRUint16 aDirection,
nsTArray<KeyKeyPair>& aData);
static
already_AddRefed<IDBCursorRequest>
Create(IDBRequest* aRequest,
IDBTransactionRequest* aTransaction,
IDBIndexRequest* aIndex,
PRUint16 aDirection,
nsTArray<KeyValuePair>& aData);
enum Type
{
OBJECTSTORE = 0,
INDEX,
INDEXOBJECT
};
protected:
IDBCursorRequest();
~IDBCursorRequest();
static
already_AddRefed<IDBCursorRequest>
CreateCommon(IDBRequest* aRequest,
IDBTransactionRequest* aTransaction,
PRUint16 aDirection);
nsRefPtr<IDBRequest> mRequest;
nsRefPtr<IDBTransactionRequest> mTransaction;
nsRefPtr<IDBObjectStoreRequest> mObjectStore;
nsRefPtr<IDBIndexRequest> mIndex;
PRUint16 mDirection;
@ -96,7 +132,10 @@ protected:
JSRuntime* mJSRuntime;
PRUint32 mDataIndex;
Type mType;
nsTArray<KeyValuePair> mData;
nsTArray<KeyKeyPair> mKeyData;
};
END_INDEXEDDB_NAMESPACE

View File

@ -41,12 +41,14 @@
#include "IDBIndexRequest.h"
#include "nsIIDBDatabaseException.h"
#include "nsIIDBKeyRange.h"
#include "nsDOMClassInfo.h"
#include "nsThreadUtils.h"
#include "mozilla/Storage.h"
#include "AsyncConnectionHelper.h"
#include "IDBCursorRequest.h"
#include "IDBEvents.h"
#include "IDBObjectStoreRequest.h"
#include "IDBTransactionRequest.h"
@ -61,11 +63,11 @@ class GetHelper : public AsyncConnectionHelper
public:
GetHelper(IDBTransactionRequest* aTransaction,
IDBRequest* aRequest,
const nsAString& aValue,
const Key& aKey,
PRInt64 aId,
bool aUnique,
bool aAutoIncrement)
: AsyncConnectionHelper(aTransaction, aRequest), mValue(aValue), mId(aId),
: AsyncConnectionHelper(aTransaction, aRequest), mKey(aKey), mId(aId),
mUnique(aUnique), mAutoIncrement(aAutoIncrement)
{ }
@ -74,14 +76,10 @@ public:
protected:
// In-params.
nsString mValue;
Key mKey;
const PRInt64 mId;
const bool mUnique;
const bool mAutoIncrement;
private:
// Out-params.
Key mKey;
};
class GetObjectHelper : public GetHelper
@ -89,19 +87,58 @@ class GetObjectHelper : public GetHelper
public:
GetObjectHelper(IDBTransactionRequest* aTransaction,
IDBRequest* aRequest,
const nsAString& aValue,
const Key& aKey,
PRInt64 aId,
bool aUnique,
bool aAutoIncrement)
: GetHelper(aTransaction, aRequest, aValue, aId, aUnique, aAutoIncrement)
: GetHelper(aTransaction, aRequest, aKey, aId, aUnique, aAutoIncrement)
{ }
PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
PRUint16 OnSuccess(nsIDOMEventTarget* aTarget);
private:
nsString mValue;
};
// Remove once XPIDL can handle jsvals
class OpenCursorHelper : public AsyncConnectionHelper
{
public:
OpenCursorHelper(IDBTransactionRequest* aTransaction,
IDBRequest* aRequest,
IDBIndexRequest* aIndex,
PRInt64 aId,
bool aUnique,
bool aAutoIncrement,
const Key& aLeftKey,
const Key& aRightKey,
PRUint16 aKeyRangeFlags,
PRUint16 aDirection,
PRBool aPreload)
: AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex), mId(aId),
mUnique(aUnique), mAutoIncrement(aAutoIncrement), mLeftKey(aLeftKey),
mRightKey(aRightKey), mKeyRangeFlags(aKeyRangeFlags),
mDirection(aDirection), mPreload(aPreload)
{ }
PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
PRUint16 GetSuccessResult(nsIWritableVariant* aResult);
private:
// In-params.
nsRefPtr<IDBIndexRequest> mIndex;
const PRInt64 mId;
const bool mUnique;
const bool mAutoIncrement;
const Key mLeftKey;
const Key mRightKey;
const PRUint16 mKeyRangeFlags;
const PRUint16 mDirection;
const PRBool mPreload;
// Out-params.
nsTArray<KeyKeyPair> mData;
};
} // anonymous namespace
@ -191,10 +228,66 @@ IDBIndexRequest::GetUnique(PRBool* aUnique)
////////////////////////////////////////////////////////////////////////////////
//// nsIIDBIndexRequest
NS_IMETHODIMP
IDBIndexRequest::OpenCursor(nsIIDBKeyRange* aKeyRange,
PRUint16 aDirection,
PRBool aPreload,
PRUint8 aOptionalArgCount,
nsIIDBRequest** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!mObjectStore->TransactionIsOpen()) {
return NS_ERROR_UNEXPECTED;
}
if (aKeyRange) {
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (aOptionalArgCount >= 2) {
if (aDirection != nsIIDBCursor::NEXT &&
aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
aDirection != nsIIDBCursor::PREV &&
aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
return NS_ERROR_INVALID_ARG;
}
}
else {
aDirection = nsIIDBCursor::NEXT;
}
if (aDirection != nsIIDBCursor::NEXT) {
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (aPreload) {
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
nsRefPtr<IDBRequest> request = GenerateRequest();
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsRefPtr<OpenCursorHelper> helper =
new OpenCursorHelper(mObjectStore->Transaction(), request, this, mId,
mUnique, mAutoIncrement, Key(), Key(), 0, aDirection,
aPreload);
nsresult rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, rv);
request.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
IDBIndexRequest::OpenObjectCursor(nsIIDBKeyRange* aRange,
PRUint16 aDirection,
PRBool aPreload,
PRUint8 aOptionalArgCount,
nsIIDBRequest** _retval)
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
@ -202,35 +295,29 @@ IDBIndexRequest::OpenObjectCursor(nsIIDBKeyRange* aRange,
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
IDBIndexRequest::OpenCursor(nsIIDBKeyRange* aRange,
PRUint16 aDirection,
PRBool aPreload,
nsIIDBRequest** _retval)
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
IDBIndexRequest::GetObject(nsIVariant* /* aKey */,
IDBIndexRequest::GetObject(nsIVariant* aKey,
nsIIDBRequest** _retval)
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
NS_WARNING("Using a slow path for Get! Fix this now!");
nsString jsonValue;
nsresult rv = IDBObjectStoreRequest::GetJSONFromArg0(jsonValue);
Key key;
nsresult rv = IDBObjectStoreRequest::GetKeyFromVariant(aKey, key);
NS_ENSURE_SUCCESS(rv, rv);
if (key.IsUnset() || key.IsNull()) {
return NS_ERROR_INVALID_ARG;
}
nsRefPtr<IDBRequest> request = GenerateRequest();
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsRefPtr<GetObjectHelper> helper =
new GetObjectHelper(mObjectStore->Transaction(), request, jsonValue, mId,
mUnique, mAutoIncrement);
new GetObjectHelper(mObjectStore->Transaction(), request, key, mId, mUnique,
mAutoIncrement);
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, rv);
@ -239,22 +326,26 @@ IDBIndexRequest::GetObject(nsIVariant* /* aKey */,
}
NS_IMETHODIMP
IDBIndexRequest::Get(nsIVariant* /* aKey */,
IDBIndexRequest::Get(nsIVariant* aKey,
nsIIDBRequest** _retval)
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
NS_WARNING("Using a slow path for Get! Fix this now!");
nsString jsonValue;
nsresult rv = IDBObjectStoreRequest::GetJSONFromArg0(jsonValue);
Key key;
nsresult rv = IDBObjectStoreRequest::GetKeyFromVariant(aKey, key);
NS_ENSURE_SUCCESS(rv, rv);
if (key.IsUnset() || key.IsNull()) {
return NS_ERROR_INVALID_ARG;
}
nsRefPtr<IDBRequest> request = GenerateRequest();
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsRefPtr<GetHelper> helper =
new GetHelper(mObjectStore->Transaction(), request, jsonValue, mId, mUnique,
new GetHelper(mObjectStore->Transaction(), request, key, mId, mUnique,
mAutoIncrement);
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, rv);
@ -277,9 +368,21 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), mId);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("value"), mValue);
NS_NAMED_LITERAL_CSTRING(value, "value");
if (mKey.IsInt()) {
rv = stmt->BindInt64ByName(value, mKey.IntValue());
}
else if (mKey.IsString()) {
rv = stmt->BindStringByName(value, mKey.StringValue());
}
else {
NS_NOTREACHED("Bad key type!");
}
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
mKey = Key::UNSETKEY;
PRBool hasResult;
rv = stmt->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
@ -300,6 +403,9 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
rv = stmt->GetString(0, mKey.ToString());
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
}
return OK;
@ -339,9 +445,21 @@ GetObjectHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"), mId);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("value"), mValue);
NS_NAMED_LITERAL_CSTRING(value, "value");
if (mKey.IsInt()) {
rv = stmt->BindInt64ByName(value, mKey.IntValue());
}
else if (mKey.IsString()) {
rv = stmt->BindStringByName(value, mKey.StringValue());
}
else {
NS_NOTREACHED("Bad key type!");
}
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
mKey = Key::UNSETKEY;
PRBool hasResult;
rv = stmt->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
@ -368,3 +486,124 @@ GetObjectHelper::OnSuccess(nsIDOMEventTarget* aTarget)
aTarget->DispatchEvent(static_cast<nsDOMEvent*>(event), &dummy);
return OK;
}
PRUint16
OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
NS_ASSERTION(aConnection, "Passed a null connection!");
nsCString table;
nsCString keyColumn;
if (mAutoIncrement) {
keyColumn.AssignLiteral("ai_object_data_id");
if (mUnique) {
table.AssignLiteral("unique_ai_index_data");
}
else {
table.AssignLiteral("ai_index_data");
}
}
else {
keyColumn.AssignLiteral("object_data_key");
if (mUnique) {
table.AssignLiteral("unique_index_data");
}
else {
table.AssignLiteral("index_data");
}
}
NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
nsCAutoString query("SELECT value, ");
query.Append(keyColumn);
query.AppendLiteral(" FROM ");
query.Append(table);
query.AppendLiteral(" WHERE index_id = :");
query.Append(indexId);
query.AppendLiteral(" ORDER BY value DESC");
if (!mData.SetCapacity(50)) {
NS_ERROR("Out of memory!");
return nsIIDBDatabaseException::UNKNOWN_ERR;
}
nsCOMPtr<mozIStorageStatement> stmt;
nsresult rv = aConnection->CreateStatement(query, getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
rv = stmt->BindInt64ByName(indexId, mId);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
PRBool hasResult;
while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
if (mData.Capacity() == mData.Length()) {
if (!mData.SetCapacity(mData.Capacity() * 2)) {
NS_ERROR("Out of memory!");
return nsIIDBDatabaseException::UNKNOWN_ERR;
}
}
KeyKeyPair* pair = mData.AppendElement();
NS_ASSERTION(pair, "Shouldn't fail if SetCapacity succeeded!");
PRInt32 keyType;
rv = stmt->GetTypeOfIndex(0, &keyType);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
pair->key = stmt->AsInt64(0);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = stmt->GetString(0, pair->key.ToString());
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
rv = stmt->GetTypeOfIndex(1, &keyType);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
pair->value = stmt->AsInt64(1);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = stmt->GetString(1, pair->value.ToString());
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
}
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
return OK;
}
PRUint16
OpenCursorHelper::GetSuccessResult(nsIWritableVariant* aResult)
{
if (mData.IsEmpty()) {
aResult->SetAsEmpty();
return NS_OK;
}
nsRefPtr<IDBCursorRequest> cursor =
IDBCursorRequest::Create(mRequest, mTransaction, mIndex, mDirection,
mData);
NS_ENSURE_TRUE(cursor, nsIIDBDatabaseException::UNKNOWN_ERR);
aResult->SetAsISupports(static_cast<IDBRequest::Generator*>(cursor));
return OK;
}

View File

@ -61,6 +61,11 @@ public:
Create(IDBObjectStoreRequest* aObjectStore,
const IndexInfo* aIndexInfo);
IDBObjectStoreRequest* ObjectStore()
{
return mObjectStore;
}
protected:
IDBIndexRequest();
~IDBIndexRequest();

View File

@ -71,7 +71,7 @@ BEGIN_INDEXEDDB_NAMESPACE
struct IndexUpdateInfo
{
IndexInfo info;
nsString value;
Key value;
};
END_INDEXEDDB_NAMESPACE
@ -411,7 +411,7 @@ nsresult
IDBObjectStoreRequest::GetKeyPathValueFromJSON(const nsAString& aJSON,
const nsAString& aKeyPath,
JSContext** aCx,
nsAString& aValue)
Key& aValue)
{
NS_ASSERTION(!aJSON.IsEmpty(), "Empty JSON!");
NS_ASSERTION(!aKeyPath.IsEmpty(), "Empty keyPath!");
@ -443,8 +443,31 @@ IDBObjectStoreRequest::GetKeyPathValueFromJSON(const nsAString& aJSON,
value.addr());
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
rv = json->EncodeFromJSVal(value.addr(), *aCx, aValue);
NS_ENSURE_SUCCESS(rv, rv);
if (JSVAL_IS_VOID(value.value())) {
// Not sure what to do if the object doesn't have a value for our index...
aValue = Key::UNSETKEY;
}
else if (JSVAL_IS_INT(value.value())) {
aValue = JSVAL_TO_INT(value.value());
}
else if (JSVAL_IS_DOUBLE(value.value())) {
aValue = *JSVAL_TO_DOUBLE(value.value());
}
else if (JSVAL_IS_STRING(value.value())) {
JSString* str = JSVAL_TO_STRING(value.value());
size_t len = JS_GetStringLength(str);
if (len) {
const PRUnichar* chars =
reinterpret_cast<PRUnichar*>(JS_GetStringChars(str));
aValue = nsDependentString(chars, len);
}
else {
aValue = EmptyString();
}
}
else {
return NS_ERROR_INVALID_ARG;
}
return NS_OK;
}
@ -561,8 +584,6 @@ IDBObjectStoreRequest::GetAddInfo(/* jsval aValue, */
cloneObj = JSVAL_TO_OBJECT(clone.value());
}
nsCOMPtr<nsIJSON> json(new nsJSON());
for (PRUint32 indexesIndex = 0; indexesIndex < indexesCount; indexesIndex++) {
const IndexInfo& indexInfo = objectStoreInfo->indexes[indexesIndex];
@ -575,14 +596,32 @@ IDBObjectStoreRequest::GetAddInfo(/* jsval aValue, */
&keyPathValue);
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
nsString value;
Key value;
if (JSVAL_IS_VOID(keyPathValue)) {
// Not sure what to do if the object doesn't have a value for our index...
NS_WARNING("Using an empty string for an index match failure!");
// No value here, continue.
continue;
}
if (JSVAL_IS_INT(keyPathValue)) {
value = JSVAL_TO_INT(keyPathValue);
}
else if (JSVAL_IS_DOUBLE(keyPathValue)) {
value = *JSVAL_TO_DOUBLE(keyPathValue);
}
else if (JSVAL_IS_STRING(keyPathValue)) {
JSString* str = JSVAL_TO_STRING(keyPathValue);
size_t len = JS_GetStringLength(str);
if (len) {
const PRUnichar* chars =
reinterpret_cast<PRUnichar*>(JS_GetStringChars(str));
value = nsDependentString(chars, len);
}
else {
value = EmptyString();
}
}
else {
rv = json->EncodeFromJSVal(&keyPathValue, cx, value);
NS_ENSURE_SUCCESS(rv, rv);
return NS_ERROR_INVALID_ARG;
}
IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
@ -592,6 +631,7 @@ IDBObjectStoreRequest::GetAddInfo(/* jsval aValue, */
updateInfo->value = value;
}
nsCOMPtr<nsIJSON> json(new nsJSON());
rv = json->EncodeFromJSVal(clone.addr(), cx, aJSON);
NS_ENSURE_SUCCESS(rv, rv);
@ -923,6 +963,11 @@ IDBObjectStoreRequest::OpenCursor(nsIIDBKeyRange* aKeyRange,
aDirection = nsIIDBCursor::NEXT;
}
if (aDirection != nsIIDBCursor::NEXT) {
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
if (aPreload) {
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
@ -1383,7 +1428,20 @@ AddHelper::UpdateIndexes(mozIStorageConnection* aConnection,
}
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("value"), updateInfo.value);
NS_NAMED_LITERAL_CSTRING(value, "value");
if (updateInfo.value.IsInt()) {
rv = stmt->BindInt64ByName(value, updateInfo.value.IntValue());
}
else if (updateInfo.value.IsString()) {
rv = stmt->BindStringByName(value, updateInfo.value.StringValue());
}
else if (updateInfo.value.IsUnset()) {
rv = stmt->BindStringByName(value, updateInfo.value.StringValue());
}
else {
NS_NOTREACHED("Unknown key type!");
}
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
@ -1630,6 +1688,9 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
rv = stmt->GetString(0, pair->key.ToString());
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
#ifdef DEBUG
{
@ -1768,14 +1829,28 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
rv = stmt->GetString(1, json);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
nsString value;
Key key;
JSContext* cx = nsnull;
rv = IDBObjectStoreRequest::GetKeyPathValueFromJSON(json, mKeyPath, &cx,
value);
key);
// XXX this should be a constraint error maybe?
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
rv = insertStmt->BindStringByName(NS_LITERAL_CSTRING("value"), value);
NS_NAMED_LITERAL_CSTRING(value, "value");
if (key.IsUnset()) {
continue;
}
if (key.IsInt()) {
rv = insertStmt->BindInt64ByName(value, key.IntValue());
}
else if (key.IsString()) {
rv = insertStmt->BindStringByName(value, key.StringValue());
}
else {
return nsIIDBDatabaseException::CONSTRAINT_ERR;
}
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
rv = insertStmt->Execute();

View File

@ -230,7 +230,7 @@ public:
GetKeyPathValueFromJSON(const nsAString& aJSON,
const nsAString& aKeyPath,
JSContext** aCx,
nsAString& aValue);
Key& aValue);
bool TransactionIsOpen() const
{

View File

@ -203,7 +203,7 @@ CreateTables(mozIStorageConnection* aDBConn)
"index_id INTEGER NOT NULL, "
"object_data_id INTEGER NOT NULL, "
"object_data_key NOT NULL, " // NONE affinity
"value TEXT NOT NULL, "
"value NOT NULL, "
"PRIMARY KEY (id), "
"FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
"CASCADE, "
@ -226,7 +226,7 @@ CreateTables(mozIStorageConnection* aDBConn)
"index_id INTEGER NOT NULL, "
"object_data_id INTEGER NOT NULL, "
"object_data_key NOT NULL, " // NONE affinity
"value TEXT NOT NULL, "
"value NOT NULL, "
"PRIMARY KEY (id), "
"UNIQUE (index_id, value), "
"FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
@ -243,7 +243,7 @@ CreateTables(mozIStorageConnection* aDBConn)
"id INTEGER, "
"index_id INTEGER NOT NULL, "
"ai_object_data_id INTEGER NOT NULL, "
"value TEXT NOT NULL, "
"value NOT NULL, "
"PRIMARY KEY (id), "
"FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
"CASCADE, "
@ -265,7 +265,7 @@ CreateTables(mozIStorageConnection* aDBConn)
"id INTEGER, "
"index_id INTEGER NOT NULL, "
"ai_object_data_id INTEGER NOT NULL, "
"value TEXT NOT NULL, "
"value NOT NULL, "
"PRIMARY KEY (id), "
"UNIQUE (index_id, value), "
"FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "

View File

@ -51,17 +51,19 @@ interface nsIVariant;
[scriptable, uuid(8c21dfc3-62fa-470e-bc92-7d115b0fac97)]
interface nsIIDBIndexRequest : nsIIDBIndex
{
[optional_argc]
nsIIDBRequest
openCursor([optional /* null */] in nsIIDBKeyRange range,
[optional /* nsIIDBCursor::NEXT */] in unsigned short direction,
[optional /* false */] in boolean preload);
[optional_argc]
nsIIDBRequest
openObjectCursor([optional /* null */] in nsIIDBKeyRange range,
[optional /* nsIIDBCursor::NEXT */]
in unsigned short direction,
[optional /* false */] in boolean preload);
nsIIDBRequest
openCursor([optional /* null */] in nsIIDBKeyRange range,
[optional /* nsIIDBCursor::NEXT */] in unsigned short direction,
[optional /* false */] in boolean preload);
nsIIDBRequest
getObject(in nsIVariant key);

View File

@ -37,6 +37,23 @@
{ name: "weight", keyPath: "weight", unique: false }
];
const objectStoreDataNameSort = [
{ key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
{ key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
{ key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
{ key: "237-23-7737", value: { name: "Pat", height: 65 } },
{ key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
{ key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }
];
const objectStoreDataWeightSort = [
{ key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
{ key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
{ key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
{ key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
{ key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }
];
let request = indexedDB.open(name, description);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
@ -117,6 +134,64 @@
is(event.result.height, 60, "Correct height returned!");
is(event.result.weight, 120, "Correct weight returned!");
let keyIndex = 0;
request = objectStore.index("name").openCursor();
request.onerror = errorHandler;
request.onsuccess = function (event) {
let cursor = event.result;
if (cursor) {
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
is(cursor.value, objectStoreDataNameSort[keyIndex].key,
"Correct value");
let ok = cursor.continue();
is(ok, true, "Correct return from continue");
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
is(cursor.value, objectStoreDataNameSort[keyIndex].key,
"Correct value");
keyIndex++;
}
else {
testGenerator.next();
}
}
yield;
keyIndex = 0;
request = objectStore.index("weight").openCursor();
request.onerror = errorHandler;
request.onsuccess = function (event) {
let cursor = event.result;
if (cursor) {
is(cursor.key, objectStoreDataWeightSort[keyIndex].value.weight,
"Correct key");
is(cursor.value, objectStoreDataWeightSort[keyIndex].key,
"Correct value");
let ok = cursor.continue();
is(ok, true, "Correct return from continue");
is(cursor.key, objectStoreDataWeightSort[keyIndex].value.weight,
"Correct key");
is(cursor.value, objectStoreDataWeightSort[keyIndex].key,
"Correct value");
keyIndex++;
}
else {
testGenerator.next();
}
}
yield;
is(keyIndex, objectStoreData.length - 1, "Saw all the expected keys");
objectStore = db.objectStore(objectStoreName, READ_WRITE);
request = objectStore.add({ name: "Bob", height: 62, weight: 170 },
"237-23-7738");

View File

@ -494,8 +494,8 @@ members = [
'-nsIIDBCursorRequest.continue',
'-nsIIDBCursorRequest.value',
'-nsIIDBCursorRequest.update',
'-nsIIDBIndexRequest.get',
'-nsIIDBIndexRequest.getObject',
'-nsIIDBIndexRequest.openCursor',
'-nsIIDBIndexRequest.openObjectCursor'
]
# Most interfaces can be found by searching the includePath; to find