Bug 615269 - 'IndexedDB: Cursors should not copy all results before iterating'. r=sicking, a=blocking+.

This commit is contained in:
Ben Turner 2010-12-09 18:15:00 -08:00
Родитель 97349c8f41
Коммит 50483cd601
12 изменённых файлов: 1299 добавлений и 580 удалений

Просмотреть файл

@ -128,19 +128,70 @@ GenerateRequest(IDBCursor* aCursor)
BEGIN_INDEXEDDB_NAMESPACE
class ContinueRunnable : public nsRunnable
class ContinueHelper : public AsyncConnectionHelper
{
public:
NS_DECL_NSIRUNNABLE
ContinueHelper(IDBCursor* aCursor)
: AsyncConnectionHelper(aCursor->mTransaction, aCursor->mRequest),
mCursor(aCursor)
{ }
ContinueRunnable(IDBCursor* aCursor,
const Key& aKey)
: mCursor(aCursor), mKey(aKey)
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
nsresult GetSuccessResult(nsIWritableVariant* aResult);
void ReleaseMainThreadObjects()
{
mCursor = nsnull;
AsyncConnectionHelper::ReleaseMainThreadObjects();
}
protected:
virtual nsresult
BindArgumentsToStatement(mozIStorageStatement* aStatement) = 0;
virtual nsresult
GatherResultsFromStatement(mozIStorageStatement* aStatement) = 0;
protected:
nsRefPtr<IDBCursor> mCursor;
Key mKey;
Key mObjectKey;
nsString mValue;
};
class ContinueObjectStoreHelper : public ContinueHelper
{
public:
ContinueObjectStoreHelper(IDBCursor* aCursor)
: ContinueHelper(aCursor)
{ }
private:
nsRefPtr<IDBCursor> mCursor;
const Key mKey;
nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement);
nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
};
class ContinueIndexHelper : public ContinueHelper
{
public:
ContinueIndexHelper(IDBCursor* aCursor)
: ContinueHelper(aCursor)
{ }
private:
nsresult BindArgumentsToStatement(mozIStorageStatement* aStatement);
nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
};
class ContinueIndexObjectHelper : public ContinueIndexHelper
{
public:
ContinueIndexObjectHelper(IDBCursor* aCursor)
: ContinueIndexHelper(aCursor)
{ }
private:
nsresult GatherResultsFromStatement(mozIStorageStatement* aStatement);
};
END_INDEXEDDB_NAMESPACE
@ -151,23 +202,24 @@ IDBCursor::Create(IDBRequest* aRequest,
IDBTransaction* aTransaction,
IDBObjectStore* aObjectStore,
PRUint16 aDirection,
nsTArray<KeyValuePair>& aData)
const Key& aRangeKey,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery,
const Key& aKey,
const nsAString& aValue)
{
NS_ASSERTION(aObjectStore, "Null pointer!");
NS_ASSERTION(!aKey.IsUnset() && !aKey.IsNull(), "Bad key!");
nsRefPtr<IDBCursor> cursor =
IDBCursor::CreateCommon(aRequest, aTransaction, aDirection);
IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection,
aRangeKey, aContinueQuery, aContinueToQuery);
NS_ASSERTION(cursor, "This shouldn't fail!");
cursor->mObjectStore = aObjectStore;
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 = OBJECTSTORE;
cursor->mKey = aKey;
cursor->mValue = aValue;
return cursor.forget();
}
@ -178,24 +230,26 @@ IDBCursor::Create(IDBRequest* aRequest,
IDBTransaction* aTransaction,
IDBIndex* aIndex,
PRUint16 aDirection,
nsTArray<KeyKeyPair>& aData)
const Key& aRangeKey,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery,
const Key& aKey,
const Key& aObjectKey)
{
NS_ASSERTION(aIndex, "Null pointer!");
NS_ASSERTION(!aKey.IsUnset() && !aKey.IsNull(), "Bad key!");
NS_ASSERTION(!aObjectKey.IsUnset() && !aObjectKey.IsNull(), "Bad key!");
nsRefPtr<IDBCursor> cursor =
IDBCursor::CreateCommon(aRequest, aTransaction, aDirection);
IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(),
aDirection, aRangeKey, aContinueQuery,
aContinueToQuery);
NS_ASSERTION(cursor, "This shouldn't fail!");
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;
cursor->mKey = aKey,
cursor->mObjectKey = aObjectKey;
return cursor.forget();
}
@ -206,24 +260,28 @@ IDBCursor::Create(IDBRequest* aRequest,
IDBTransaction* aTransaction,
IDBIndex* aIndex,
PRUint16 aDirection,
nsTArray<KeyValuePair>& aData)
const Key& aRangeKey,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery,
const Key& aKey,
const Key& aObjectKey,
const nsAString& aValue)
{
NS_ASSERTION(aIndex, "Null pointer!");
NS_ASSERTION(!aKey.IsUnset() && !aKey.IsNull(), "Bad key!");
nsRefPtr<IDBCursor> cursor =
IDBCursor::CreateCommon(aRequest, aTransaction, aDirection);
IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(),
aDirection, aRangeKey, aContinueQuery,
aContinueToQuery);
NS_ASSERTION(cursor, "This shouldn't fail!");
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;
cursor->mKey = aKey;
cursor->mObjectKey = aObjectKey;
cursor->mValue = aValue;
return cursor.forget();
}
@ -232,32 +290,41 @@ IDBCursor::Create(IDBRequest* aRequest,
already_AddRefed<IDBCursor>
IDBCursor::CreateCommon(IDBRequest* aRequest,
IDBTransaction* aTransaction,
PRUint16 aDirection)
IDBObjectStore* aObjectStore,
PRUint16 aDirection,
const Key& aRangeKey,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aRequest, "Null pointer!");
NS_ASSERTION(aTransaction, "Null pointer!");
NS_ASSERTION(aObjectStore, "Null pointer!");
NS_ASSERTION(!aContinueQuery.IsEmpty(), "Empty query!");
NS_ASSERTION(!aContinueToQuery.IsEmpty(), "Empty query!");
nsRefPtr<IDBCursor> cursor(new IDBCursor());
cursor->mScriptContext = aTransaction->Database()->ScriptContext();
cursor->mOwner = aTransaction->Database()->Owner();
nsRefPtr<IDBCursor> cursor = new IDBCursor();
cursor->mRequest = aRequest;
cursor->mTransaction = aTransaction;
cursor->mObjectStore = aObjectStore;
cursor->mScriptContext = aTransaction->Database()->ScriptContext();
cursor->mOwner = aTransaction->Database()->Owner();
cursor->mDirection = aDirection;
cursor->mContinueQuery = aContinueQuery;
cursor->mContinueToQuery = aContinueToQuery;
cursor->mRangeKey = aRangeKey;
return cursor.forget();
}
IDBCursor::IDBCursor()
: mDirection(nsIIDBCursor::NEXT),
: mType(OBJECTSTORE),
mDirection(nsIIDBCursor::NEXT),
mCachedValue(JSVAL_VOID),
mHaveCachedValue(false),
mValueRooted(false),
mContinueCalled(false),
mDataIndex(0),
mType(OBJECTSTORE)
mContinueCalled(false)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
@ -349,17 +416,14 @@ IDBCursor::GetKey(nsIVariant** aKey)
do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
const Key& key = mType == INDEX ?
mKeyData[mDataIndex].key :
mData[mDataIndex].key;
NS_ASSERTION(!key.IsUnset() && !key.IsNull(), "Bad key!");
NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Bad key!");
if (key.IsString()) {
rv = variant->SetAsAString(key.StringValue());
if (mKey.IsString()) {
rv = variant->SetAsAString(mKey.StringValue());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else if (key.IsInt()) {
rv = variant->SetAsInt64(key.IntValue());
else if (mKey.IsInt()) {
rv = variant->SetAsInt64(mKey.IntValue());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
@ -389,10 +453,9 @@ IDBCursor::GetValue(JSContext* aCx,
nsresult rv;
if (mType == INDEX) {
const Key& value = mKeyData[mDataIndex].value;
NS_ASSERTION(!value.IsUnset() && !value.IsNull(), "Bad key!");
NS_ASSERTION(!mObjectKey.IsUnset() && !mObjectKey.IsNull(), "Bad key!");
rv = IDBObjectStore::GetJSValFromKey(value, aCx, aValue);
rv = IDBObjectStore::GetJSValFromKey(mObjectKey, aCx, aValue);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return NS_OK;
@ -402,7 +465,7 @@ IDBCursor::GetValue(JSContext* aCx,
JSAutoRequest ar(aCx);
nsCOMPtr<nsIJSON> json(new nsJSON());
rv = json->DecodeToJSVal(mData[mDataIndex].value, aCx, &mCachedValue);
rv = json->DecodeToJSVal(mValue, aCx, &mCachedValue);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_SERIAL_ERR);
if (!mValueRooted) {
@ -420,20 +483,14 @@ IDBCursor::GetValue(JSContext* aCx,
NS_IMETHODIMP
IDBCursor::Continue(const jsval &aKey,
JSContext* aCx,
PRUint8 aOptionalArgCount,
PRBool* _retval)
PRUint8 aOptionalArgCount)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
if (!mTransaction->TransactionIsOpen()) {
if (!mTransaction->TransactionIsOpen() || mContinueCalled) {
return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
}
if (mContinueCalled) {
// XXX Update the spec for precise behavior here.
return NS_OK;
}
Key key;
nsresult rv = IDBObjectStore::GetKeyFromJSVal(aKey, key);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR);
@ -447,19 +504,63 @@ IDBCursor::Continue(const jsval &aKey,
}
}
TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
NS_ENSURE_TRUE(pool, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (!key.IsUnset()) {
switch (mDirection) {
case nsIIDBCursor::NEXT:
case nsIIDBCursor::NEXT_NO_DUPLICATE:
if (key <= mKey) {
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
}
break;
nsRefPtr<ContinueRunnable> runnable(new ContinueRunnable(this, key));
case nsIIDBCursor::PREV:
case nsIIDBCursor::PREV_NO_DUPLICATE:
if (key >= mKey) {
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
}
break;
rv = pool->Dispatch(mTransaction, runnable, false, nsnull);
default:
NS_NOTREACHED("Unknown direction type!");
}
}
mContinueToKey = key;
#ifdef DEBUG
{
PRUint16 readyState;
if (NS_FAILED(mRequest->GetReadyState(&readyState))) {
NS_ERROR("This should never fail!");
}
NS_ASSERTION(readyState == nsIIDBRequest::DONE, "Should be DONE!");
}
#endif
mRequest->Reset();
nsRefPtr<ContinueHelper> helper;
switch (mType) {
case OBJECTSTORE:
helper = new ContinueObjectStoreHelper(this);
break;
case INDEX:
helper = new ContinueIndexHelper(this);
break;
case INDEXOBJECT:
helper = new ContinueIndexObjectHelper(this);
break;
default:
NS_NOTREACHED("Unknown cursor type!");
}
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
mTransaction->OnNewRequest();
mContinueCalled = true;
*_retval = PR_TRUE;
return NS_OK;
}
@ -478,10 +579,9 @@ IDBCursor::Update(const jsval& aValue,
NS_WARNING("Update for non-objectStore cursors is not implemented!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
NS_ASSERTION(mObjectStore, "This cannot be null!");
const Key& key = mData[mDataIndex].key;
NS_ASSERTION(!key.IsUnset() && !key.IsNull(), "Bad key!");
NS_ASSERTION(mObjectStore, "This cannot be null!");
NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Bad key!");
JSAutoRequest ar(aCx);
@ -501,7 +601,7 @@ IDBCursor::Update(const jsval& aValue,
NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (JSVAL_IS_VOID(prop.jsval_value())) {
rv = IDBObjectStore::GetJSValFromKey(key, aCx, prop.jsval_addr());
rv = IDBObjectStore::GetJSValFromKey(mKey, aCx, prop.jsval_addr());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
ok = JS_StructuredClone(aCx, clone.jsval_value(), clone.jsval_addr());
@ -517,7 +617,7 @@ IDBCursor::Update(const jsval& aValue,
rv = IDBObjectStore::GetKeyFromJSVal(prop.jsval_value(), newKey);
NS_ENSURE_SUCCESS(rv, rv);
if (newKey.IsUnset() || newKey.IsNull() || newKey != key) {
if (newKey.IsUnset() || newKey.IsNull() || newKey != mKey) {
return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
}
}
@ -544,7 +644,7 @@ IDBCursor::Update(const jsval& aValue,
NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsRefPtr<UpdateHelper> helper =
new UpdateHelper(mTransaction, request, mObjectStore->Id(), jsonValue, key,
new UpdateHelper(mTransaction, request, mObjectStore->Id(), jsonValue, mKey,
mObjectStore->IsAutoIncrement(), indexUpdateInfo);
rv = helper->DispatchToTransactionPool();
@ -567,16 +667,15 @@ IDBCursor::Delete(nsIIDBRequest** _retval)
NS_WARNING("Delete for non-objectStore cursors is not implemented!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
NS_ASSERTION(mObjectStore, "This cannot be null!");
const Key& key = mData[mDataIndex].key;
NS_ASSERTION(!key.IsUnset() && !key.IsNull(), "Bad key!");
NS_ASSERTION(mObjectStore, "This cannot be null!");
NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Bad key!");
nsRefPtr<IDBRequest> request = GenerateRequest(this);
NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsRefPtr<DeleteHelper> helper =
new DeleteHelper(mTransaction, request, mObjectStore->Id(), key,
new DeleteHelper(mTransaction, request, mObjectStore->Id(), mKey,
mObjectStore->IsAutoIncrement());
nsresult rv = helper->DispatchToTransactionPool();
@ -707,129 +806,330 @@ DeleteHelper::GetSuccessResult(nsIWritableVariant* aResult)
return NS_OK;
}
NS_IMETHODIMP
ContinueRunnable::Run()
nsresult
ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
// We need to pick a query based on whether or not the cursor's mContinueToKey
// is set. If it is unset then othing was passed to continue so we'll grab the
// next item in the database that is greater than (less than, if we're running
// a PREV cursor) the current key. If it is set then a key was passed to
// continue so we'll grab the next item in the database that is greater than
// (less than, if we're running a PREV cursor) or equal to the key that was
// specified.
const nsCString& query = mCursor->mContinueToKey.IsUnset() ?
mCursor->mContinueQuery :
mCursor->mContinueToQuery;
NS_ASSERTION(!query.IsEmpty(), "Bad query!");
nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
mozStorageStatementScoper scoper(stmt);
nsresult rv = BindArgumentsToStatement(stmt);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
PRBool hasResult;
rv = stmt->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (hasResult) {
rv = GatherResultsFromStatement(stmt);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
mKey = Key::UNSETKEY;
}
return NS_OK;
}
nsresult
ContinueHelper::GetSuccessResult(nsIWritableVariant* aResult)
{
nsresult rv;
if (!NS_IsMainThread()) {
rv = NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
if (mKey.IsUnset()) {
rv = aResult->SetAsEmpty();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// mCursor must be null after this method finishes. Swap out now.
nsRefPtr<IDBCursor> cursor;
cursor.swap(mCursor);
#ifdef DEBUG
NS_ASSERTION(!mKey.IsNull(), "Huh?!");
if (mCursor->mType != IDBCursor::OBJECTSTORE) {
NS_ASSERTION(!mObjectKey.IsUnset() && !mObjectKey.IsNull(), "Bad key!");
}
#endif
// Remove cached stuff from last time.
cursor->mCachedKey = nsnull;
cursor->mCachedValue = JSVAL_VOID;
cursor->mHaveCachedValue = false;
cursor->mContinueCalled = false;
mCursor->mCachedKey = nsnull;
mCursor->mCachedObjectKey = nsnull;
mCursor->mCachedValue = JSVAL_VOID;
mCursor->mHaveCachedValue = false;
mCursor->mContinueCalled = false;
if (cursor->mType == IDBCursor::INDEX) {
cursor->mKeyData.RemoveElementAt(cursor->mDataIndex);
}
else {
cursor->mData.RemoveElementAt(cursor->mDataIndex);
}
if (cursor->mDataIndex) {
cursor->mDataIndex--;
}
// And set new values.
mCursor->mKey = mKey;
mCursor->mObjectKey = mObjectKey;
mCursor->mValue = mValue;
mCursor->mContinueToKey = Key::UNSETKEY;
nsCOMPtr<nsIWritableVariant> variant =
do_CreateInstance(NS_VARIANT_CONTRACTID);
if (!variant) {
NS_ERROR("Couldn't create variant!");
return NS_ERROR_FAILURE;
}
PRBool empty = cursor->mType == IDBCursor::INDEX ?
cursor->mKeyData.IsEmpty() :
cursor->mData.IsEmpty();
if (empty) {
rv = variant->SetAsEmpty();
NS_ENSURE_SUCCESS(rv, rv);
}
else {
if (!mKey.IsUnset()) {
NS_ASSERTION(!mKey.IsNull(), "Huh?!");
NS_WARNING("Using a slow O(n) search for continue(key), do something "
"smarter!");
// Skip ahead to our next key match.
PRInt32 index = PRInt32(cursor->mDataIndex);
if (cursor->mType == IDBCursor::INDEX) {
while (index >= 0) {
const Key& key = cursor->mKeyData[index].key;
if (mKey == key) {
break;
}
if (key < mKey) {
index--;
continue;
}
index = -1;
break;
}
if (index >= 0) {
cursor->mDataIndex = PRUint32(index);
cursor->mKeyData.RemoveElementsAt(index + 1,
cursor->mKeyData.Length() - index
- 1);
}
}
else {
while (index >= 0) {
const Key& key = cursor->mData[index].key;
if (mKey == key) {
break;
}
if (key < mKey) {
index--;
continue;
}
index = -1;
break;
}
if (index >= 0) {
cursor->mDataIndex = PRUint32(index);
cursor->mData.RemoveElementsAt(index + 1,
cursor->mData.Length() - index - 1);
}
}
}
rv = variant->SetAsISupports(cursor);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = variant->SetWritable(PR_FALSE);
rv = aResult->SetAsISupports(mCursor);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMEvent> event =
IDBSuccessEvent::Create(cursor->mRequest, variant, cursor->mTransaction);
if (!event) {
NS_ERROR("Failed to create event!");
return NS_ERROR_FAILURE;
}
AsyncConnectionHelper::SetCurrentTransaction(cursor->mTransaction);
PRBool dummy;
cursor->mRequest->DispatchEvent(event, &dummy);
NS_ASSERTION(AsyncConnectionHelper::GetCurrentTransaction() ==
cursor->mTransaction, "Should be unchanged!");
AsyncConnectionHelper::SetCurrentTransaction(nsnull);
cursor->mTransaction->OnRequestFinished();
return NS_OK;
}
nsresult
ContinueObjectStoreHelper::BindArgumentsToStatement(
mozIStorageStatement* aStatement)
{
// Bind object store id.
nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"),
mCursor->mObjectStore->Id());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
// Bind current key.
const Key& currentKey = mCursor->mContinueToKey.IsUnset() ?
mCursor->mKey :
mCursor->mContinueToKey;
if (currentKey.IsString()) {
rv = aStatement->BindStringByName(currentKeyName, currentKey.StringValue());
}
else if (currentKey.IsInt()) {
rv = aStatement->BindInt64ByName(currentKeyName, currentKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
}
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
// Bind range key if it is specified.
const Key& rangeKey = mCursor->mRangeKey;
if (!rangeKey.IsUnset()) {
if (rangeKey.IsString()) {
rv = aStatement->BindStringByName(rangeKeyName, rangeKey.StringValue());
}
else if (rangeKey.IsInt()) {
rv = aStatement->BindInt64ByName(rangeKeyName, rangeKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
}
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
return NS_OK;
}
nsresult
ContinueObjectStoreHelper::GatherResultsFromStatement(
mozIStorageStatement* aStatement)
{
// Figure out what kind of key we have next.
PRInt32 keyType;
nsresult rv = aStatement->GetTypeOfIndex(0, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
mKey = aStatement->AsInt64(0);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = aStatement->GetString(0, mKey.ToString());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
#ifdef DEBUG
{
PRInt32 valueType;
NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(1, &valueType)) &&
valueType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad value type!");
}
#endif
rv = aStatement->GetString(1, mValue);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return NS_OK;
}
nsresult
ContinueIndexHelper::BindArgumentsToStatement(mozIStorageStatement* aStatement)
{
// Bind index id.
nsresult rv = aStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"),
mCursor->mIndex->Id());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
// Bind current key.
const Key& currentKey = mCursor->mContinueToKey.IsUnset() ?
mCursor->mKey :
mCursor->mContinueToKey;
if (currentKey.IsString()) {
rv = aStatement->BindStringByName(currentKeyName, currentKey.StringValue());
}
else if (currentKey.IsInt()) {
rv = aStatement->BindInt64ByName(currentKeyName, currentKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
}
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
// Bind range key if it is specified.
if (!mCursor->mRangeKey.IsUnset()) {
NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
if (mCursor->mRangeKey.IsString()) {
rv = aStatement->BindStringByName(rangeKeyName,
mCursor->mRangeKey.StringValue());
}
else if (mCursor->mRangeKey.IsInt()) {
rv = aStatement->BindInt64ByName(rangeKeyName,
mCursor->mRangeKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
}
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
// Bind object key if duplicates are allowed and we're not continuing to a
// specific key.
if ((mCursor->mDirection == nsIIDBCursor::NEXT ||
mCursor->mDirection == nsIIDBCursor::PREV) &&
mCursor->mContinueToKey.IsUnset()) {
NS_ASSERTION(!mCursor->mObjectKey.IsUnset() &&
!mCursor->mObjectKey.IsNull(), "Bad key!");
NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key");
if (mCursor->mObjectKey.IsString()) {
rv = aStatement->BindStringByName(objectKeyName,
mCursor->mObjectKey.StringValue());
}
else if (mCursor->mObjectKey.IsInt()) {
rv = aStatement->BindInt64ByName(objectKeyName,
mCursor->mObjectKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
}
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
return NS_OK;
}
nsresult
ContinueIndexHelper::GatherResultsFromStatement(
mozIStorageStatement* aStatement)
{
PRInt32 keyType;
nsresult rv = aStatement->GetTypeOfIndex(0, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
mKey = aStatement->AsInt64(0);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = aStatement->GetString(0, mKey.ToString());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
rv = aStatement->GetTypeOfIndex(1, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
mObjectKey = aStatement->AsInt64(1);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = aStatement->GetString(1, mObjectKey.ToString());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
return NS_OK;
}
nsresult
ContinueIndexObjectHelper::GatherResultsFromStatement(
mozIStorageStatement* aStatement)
{
PRInt32 keyType;
nsresult rv = aStatement->GetTypeOfIndex(0, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
mKey = aStatement->AsInt64(0);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = aStatement->GetString(0, mKey.ToString());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
rv = aStatement->GetTypeOfIndex(1, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
mObjectKey = aStatement->AsInt64(1);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = aStatement->GetString(1, mObjectKey.ToString());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
#ifdef DEBUG
{
PRInt32 valueType;
NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(2, &valueType)) &&
valueType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad value type!");
}
#endif
rv = aStatement->GetString(2, mValue);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return NS_OK;
}

Просмотреть файл

@ -57,23 +57,17 @@ class IDBIndex;
class IDBRequest;
class IDBTransaction;
struct KeyValuePair
{
Key key;
nsString value;
};
struct KeyKeyPair
{
Key key;
Key value;
};
class ContinueRunnable;
class ContinueHelper;
class ContinueObjectStoreHelper;
class ContinueIndexHelper;
class ContinueIndexObjectHelper;
class IDBCursor : public nsIIDBCursor
{
friend class ContinueRunnable;
friend class ContinueHelper;
friend class ContinueObjectStoreHelper;
friend class ContinueIndexHelper;
friend class ContinueIndexObjectHelper;
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@ -81,29 +75,45 @@ public:
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBCursor)
// For OBJECTSTORE cursors.
static
already_AddRefed<IDBCursor>
Create(IDBRequest* aRequest,
IDBTransaction* aTransaction,
IDBObjectStore* aObjectStore,
PRUint16 aDirection,
nsTArray<KeyValuePair>& aData);
const Key& aRangeKey,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery,
const Key& aKey,
const nsAString& aValue);
// For INDEX cursors.
static
already_AddRefed<IDBCursor>
Create(IDBRequest* aRequest,
IDBTransaction* aTransaction,
IDBIndex* aIndex,
PRUint16 aDirection,
nsTArray<KeyKeyPair>& aData);
const Key& aRangeKey,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery,
const Key& aKey,
const Key& aObjectKey);
// For INDEXOBJECT cursors.
static
already_AddRefed<IDBCursor>
Create(IDBRequest* aRequest,
IDBTransaction* aTransaction,
IDBIndex* aIndex,
PRUint16 aDirection,
nsTArray<KeyValuePair>& aData);
const Key& aRangeKey,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery,
const Key& aKey,
const Key& aObjectKey,
const nsAString& aValue);
enum Type
{
@ -125,7 +135,11 @@ protected:
already_AddRefed<IDBCursor>
CreateCommon(IDBRequest* aRequest,
IDBTransaction* aTransaction,
PRUint16 aDirection);
IDBObjectStore* aObjectStore,
PRUint16 aDirection,
const Key& aRangeKey,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery);
nsRefPtr<IDBRequest> mRequest;
nsRefPtr<IDBTransaction> mTransaction;
@ -135,19 +149,26 @@ protected:
nsCOMPtr<nsIScriptContext> mScriptContext;
nsCOMPtr<nsPIDOMWindow> mOwner;
PRUint16 mDirection;
nsCOMPtr<nsIVariant> mCachedKey;
jsval mCachedValue;
bool mHaveCachedValue;
bool mValueRooted;
bool mContinueCalled;
PRUint32 mDataIndex;
nsCOMPtr<nsIVariant> mCachedObjectKey;
Type mType;
nsTArray<KeyValuePair> mData;
nsTArray<KeyKeyPair> mKeyData;
PRUint16 mDirection;
nsCString mContinueQuery;
nsCString mContinueToQuery;
jsval mCachedValue;
Key mRangeKey;
Key mKey;
Key mObjectKey;
nsString mValue;
Key mContinueToKey;
bool mHaveCachedValue;
bool mValueRooted;
bool mContinueCalled;
};
END_INDEXEDDB_NAMESPACE

Просмотреть файл

@ -173,7 +173,11 @@ private:
const PRUint16 mDirection;
// Out-params.
nsTArray<KeyKeyPair> mData;
Key mKey;
Key mObjectKey;
nsCString mContinueQuery;
nsCString mContinueToQuery;
Key mRangeKey;
};
class OpenObjectCursorHelper : public AsyncConnectionHelper
@ -211,7 +215,12 @@ private:
const PRUint16 mDirection;
// Out-params.
nsTArray<KeyValuePair> mData;
Key mKey;
Key mObjectKey;
nsString mValue;
nsCString mContinueQuery;
nsCString mContinueToQuery;
Key mRangeKey;
};
inline
@ -994,11 +1003,6 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
NS_ASSERTION(aConnection, "Passed a null connection!");
if (!mData.SetCapacity(50)) {
NS_ERROR("Out of memory!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsCString table;
nsCString keyColumn;
@ -1021,84 +1025,61 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
}
}
NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
NS_NAMED_LITERAL_CSTRING(leftKeyName, "left_key");
NS_NAMED_LITERAL_CSTRING(rightKeyName, "right_key");
NS_NAMED_LITERAL_CSTRING(id, "id");
NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
NS_NAMED_LITERAL_CSTRING(value, "value");
nsCAutoString keyRangeClause;
if (!mLowerKey.IsUnset()) {
keyRangeClause = NS_LITERAL_CSTRING(" AND value");
if (mLowerOpen) {
keyRangeClause.AppendLiteral(" > :");
}
else {
keyRangeClause.AppendLiteral(" >= :");
}
keyRangeClause.Append(leftKeyName);
AppendConditionClause(value, lowerKeyName, false, !mLowerOpen,
keyRangeClause);
}
if (!mUpperKey.IsUnset()) {
keyRangeClause.AppendLiteral(" AND value");
if (mUpperOpen) {
keyRangeClause.AppendLiteral(" < :");
}
else {
keyRangeClause.AppendLiteral(" <= :");
}
keyRangeClause.Append(rightKeyName);
AppendConditionClause(value, upperKeyName, true, !mUpperOpen,
keyRangeClause);
}
nsCString groupClause;
nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + value;
switch (mDirection) {
case nsIIDBCursor::NEXT:
case nsIIDBCursor::PREV:
case nsIIDBCursor::NEXT_NO_DUPLICATE:
directionClause.AppendLiteral(" ASC");
break;
case nsIIDBCursor::NEXT_NO_DUPLICATE:
case nsIIDBCursor::PREV:
case nsIIDBCursor::PREV_NO_DUPLICATE:
groupClause = NS_LITERAL_CSTRING(" GROUP BY value");
directionClause.AppendLiteral(" DESC");
break;
default:
NS_NOTREACHED("Unknown direction!");
}
directionClause += NS_LITERAL_CSTRING(", ") + keyColumn +
NS_LITERAL_CSTRING(" ASC");
nsCString directionClause;
switch (mDirection) {
case nsIIDBCursor::NEXT:
case nsIIDBCursor::NEXT_NO_DUPLICATE:
directionClause = NS_LITERAL_CSTRING("DESC");
break;
nsCString firstQuery = NS_LITERAL_CSTRING("SELECT value, ") + keyColumn +
NS_LITERAL_CSTRING(" FROM ") + table +
NS_LITERAL_CSTRING(" WHERE index_id = :") + id +
keyRangeClause + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
case nsIIDBCursor::PREV:
case nsIIDBCursor::PREV_NO_DUPLICATE:
directionClause = NS_LITERAL_CSTRING("ASC");
break;
default:
NS_NOTREACHED("Unknown direction!");
}
nsCString query = NS_LITERAL_CSTRING("SELECT value, ") + keyColumn +
NS_LITERAL_CSTRING(" FROM ") + table +
NS_LITERAL_CSTRING(" WHERE index_id = :") + indexId +
keyRangeClause + groupClause +
NS_LITERAL_CSTRING(" ORDER BY value ") + directionClause;
nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
nsCOMPtr<mozIStorageStatement> stmt =
mTransaction->GetCachedStatement(firstQuery);
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id());
nsresult rv = stmt->BindInt64ByName(id, mIndex->Id());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (!mLowerKey.IsUnset()) {
if (mLowerKey.IsString()) {
rv = stmt->BindStringByName(leftKeyName, mLowerKey.StringValue());
rv = stmt->BindStringByName(lowerKeyName, mLowerKey.StringValue());
}
else if (mLowerKey.IsInt()) {
rv = stmt->BindInt64ByName(leftKeyName, mLowerKey.IntValue());
rv = stmt->BindInt64ByName(lowerKeyName, mLowerKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
@ -1108,10 +1089,10 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
if (!mUpperKey.IsUnset()) {
if (mUpperKey.IsString()) {
rv = stmt->BindStringByName(rightKeyName, mUpperKey.StringValue());
rv = stmt->BindStringByName(upperKeyName, mUpperKey.StringValue());
}
else if (mUpperKey.IsInt()) {
rv = stmt->BindInt64ByName(rightKeyName, mUpperKey.IntValue());
rv = stmt->BindInt64ByName(upperKeyName, mUpperKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
@ -1120,69 +1101,135 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
}
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 NS_ERROR_DOM_INDEXEDDB_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, NS_ERROR_DOM_INDEXEDDB_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, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
rv = stmt->GetTypeOfIndex(1, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_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, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
}
rv = stmt->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (!hasResult) {
mKey = Key::UNSETKEY;
return NS_OK;
}
PRInt32 keyType;
rv = stmt->GetTypeOfIndex(0, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
mKey = stmt->AsInt64(0);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = stmt->GetString(0, mKey.ToString());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
rv = stmt->GetTypeOfIndex(1, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
mObjectKey = stmt->AsInt64(1);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = stmt->GetString(1, mObjectKey.ToString());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
// Now we need to make the query to get the next match.
nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT value, ") + keyColumn +
NS_LITERAL_CSTRING(" FROM ") + table +
NS_LITERAL_CSTRING(" WHERE index_id = :") + id;
NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
NS_NAMED_LITERAL_CSTRING(objectKey, "object_key");
switch (mDirection) {
case nsIIDBCursor::NEXT:
if (!mUpperKey.IsUnset()) {
AppendConditionClause(value, rangeKey, true, !mUpperOpen, queryStart);
mRangeKey = mUpperKey;
}
mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ( ( value = :") +
currentKey + NS_LITERAL_CSTRING(" AND ") + keyColumn +
NS_LITERAL_CSTRING(" > :") + objectKey +
NS_LITERAL_CSTRING(" ) OR ( value > :") + currentKey +
NS_LITERAL_CSTRING(" ) )") + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value >= :") +
currentKey + NS_LITERAL_CSTRING(" LIMIT 1");
break;
case nsIIDBCursor::NEXT_NO_DUPLICATE:
if (!mUpperKey.IsUnset()) {
AppendConditionClause(value, rangeKey, true, !mUpperOpen, queryStart);
mRangeKey = mUpperKey;
}
mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND value > :") +
currentKey + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value >= :") +
currentKey + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
break;
case nsIIDBCursor::PREV:
if (!mLowerKey.IsUnset()) {
AppendConditionClause(value, rangeKey, false, !mLowerOpen, queryStart);
mRangeKey = mLowerKey;
}
mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ( ( value = :") +
currentKey + NS_LITERAL_CSTRING(" AND ") + keyColumn +
NS_LITERAL_CSTRING(" < :") + objectKey +
NS_LITERAL_CSTRING(" ) OR ( value < :") + currentKey +
NS_LITERAL_CSTRING(" ) )") + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value <= :") +
currentKey + NS_LITERAL_CSTRING(" LIMIT 1");
break;
case nsIIDBCursor::PREV_NO_DUPLICATE:
if (!mLowerKey.IsUnset()) {
AppendConditionClause(value, rangeKey, false, !mLowerOpen, queryStart);
mRangeKey = mLowerKey;
}
mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND value < :") +
currentKey + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value <= :") +
currentKey + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
break;
default:
NS_NOTREACHED("Unknown direction type!");
}
return NS_OK;
}
nsresult
OpenCursorHelper::GetSuccessResult(nsIWritableVariant* aResult)
{
if (mData.IsEmpty()) {
if (mKey.IsUnset()) {
aResult->SetAsEmpty();
return NS_OK;
}
nsRefPtr<IDBCursor> cursor =
IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mData);
IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey,
mContinueQuery, mContinueToQuery, mKey, mObjectKey);
NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
aResult->SetAsISupports(cursor);
@ -1194,18 +1241,15 @@ OpenObjectCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
NS_ASSERTION(aConnection, "Passed a null connection!");
if (!mData.SetCapacity(50)) {
NS_ERROR("Out of memory!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsCString indexTable;
nsCString objectTable;
nsCString objectDataId;
nsCString objectDataIdColumn;
nsCString keyValueColumn;
if (mIndex->IsAutoIncrement()) {
objectTable.AssignLiteral("ai_object_data");
objectDataId.AssignLiteral("ai_object_data_id");
objectDataIdColumn.AssignLiteral("ai_object_data_id");
keyValueColumn.AssignLiteral("id");
if (mIndex->IsUnique()) {
indexTable.AssignLiteral("ai_unique_index_data");
}
@ -1215,7 +1259,8 @@ OpenObjectCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
}
else {
objectTable.AssignLiteral("object_data");
objectDataId.AssignLiteral("object_data_id");
objectDataIdColumn.AssignLiteral("object_data_id");
keyValueColumn.AssignLiteral("key_value");
if (mIndex->IsUnique()) {
indexTable.AssignLiteral("unique_index_data");
}
@ -1224,96 +1269,70 @@ OpenObjectCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
}
}
NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
NS_NAMED_LITERAL_CSTRING(leftKeyName, "left_key");
NS_NAMED_LITERAL_CSTRING(rightKeyName, "right_key");
NS_NAMED_LITERAL_CSTRING(value, "value");
NS_NAMED_LITERAL_CSTRING(data, "data");
NS_NAMED_LITERAL_CSTRING(id, "id");
NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
nsCString value = indexTable + NS_LITERAL_CSTRING(".value");
nsCString data = objectTable + NS_LITERAL_CSTRING(".data");
nsCString keyValue = objectTable + NS_LITERAL_CSTRING(".") + keyValueColumn;
nsCAutoString keyRangeClause;
if (!mLowerKey.IsUnset()) {
keyRangeClause = NS_LITERAL_CSTRING(" AND value");
if (mLowerOpen) {
keyRangeClause.AppendLiteral(" > :");
}
else {
keyRangeClause.AppendLiteral(" >= :");
}
keyRangeClause.Append(leftKeyName);
AppendConditionClause(value, lowerKeyName, false, !mLowerOpen,
keyRangeClause);
}
if (!mUpperKey.IsUnset()) {
keyRangeClause += NS_LITERAL_CSTRING(" AND value");
if (mUpperOpen) {
keyRangeClause.AppendLiteral(" < :");
}
else {
keyRangeClause.AppendLiteral(" <= :");
}
keyRangeClause.Append(rightKeyName);
AppendConditionClause(value, upperKeyName, true, !mUpperOpen,
keyRangeClause);
}
nsCString groupClause;
nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + value;
switch (mDirection) {
case nsIIDBCursor::NEXT:
case nsIIDBCursor::PREV:
case nsIIDBCursor::NEXT_NO_DUPLICATE:
directionClause.AppendLiteral(" ASC");
break;
case nsIIDBCursor::NEXT_NO_DUPLICATE:
case nsIIDBCursor::PREV:
case nsIIDBCursor::PREV_NO_DUPLICATE:
groupClause = NS_LITERAL_CSTRING(" GROUP BY ") + indexTable +
NS_LITERAL_CSTRING(".") + value;
directionClause.AppendLiteral(" DESC");
break;
default:
NS_NOTREACHED("Unknown direction!");
}
directionClause += NS_LITERAL_CSTRING(", ") + keyValue +
NS_LITERAL_CSTRING(" ASC");
nsCString directionClause;
switch (mDirection) {
case nsIIDBCursor::NEXT:
case nsIIDBCursor::NEXT_NO_DUPLICATE:
directionClause = NS_LITERAL_CSTRING(" DESC");
break;
nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + value +
NS_LITERAL_CSTRING(", ") + keyValue +
NS_LITERAL_CSTRING(", ") + data +
NS_LITERAL_CSTRING(" FROM ") + objectTable +
NS_LITERAL_CSTRING(" INNER JOIN ") + indexTable +
NS_LITERAL_CSTRING(" ON ") + indexTable +
NS_LITERAL_CSTRING(".") + objectDataIdColumn +
NS_LITERAL_CSTRING(" = ") + objectTable +
NS_LITERAL_CSTRING(".id WHERE ") + indexTable +
NS_LITERAL_CSTRING(".index_id = :id") +
keyRangeClause + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
case nsIIDBCursor::PREV:
case nsIIDBCursor::PREV_NO_DUPLICATE:
directionClause = NS_LITERAL_CSTRING(" ASC");
break;
default:
NS_NOTREACHED("Unknown direction!");
}
nsCString query = NS_LITERAL_CSTRING("SELECT ") + indexTable +
NS_LITERAL_CSTRING(".") + value +
NS_LITERAL_CSTRING(", ") + objectTable +
NS_LITERAL_CSTRING(".") + data +
NS_LITERAL_CSTRING(" FROM ") + objectTable +
NS_LITERAL_CSTRING(" INNER JOIN ") + indexTable +
NS_LITERAL_CSTRING(" ON ") + indexTable +
NS_LITERAL_CSTRING(".") + objectDataId +
NS_LITERAL_CSTRING(" = ") + objectTable +
NS_LITERAL_CSTRING(".id WHERE ") + indexId +
NS_LITERAL_CSTRING(" = :") + indexId + keyRangeClause +
groupClause + NS_LITERAL_CSTRING(" ORDER BY ") +
indexTable + NS_LITERAL_CSTRING(".") + value +
directionClause;
nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
nsCOMPtr<mozIStorageStatement> stmt =
mTransaction->GetCachedStatement(firstQuery);
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id());
nsresult rv = stmt->BindInt64ByName(id, mIndex->Id());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (!mLowerKey.IsUnset()) {
if (mLowerKey.IsString()) {
rv = stmt->BindStringByName(leftKeyName, mLowerKey.StringValue());
rv = stmt->BindStringByName(lowerKeyName, mLowerKey.StringValue());
}
else if (mLowerKey.IsInt()) {
rv = stmt->BindInt64ByName(leftKeyName, mLowerKey.IntValue());
rv = stmt->BindInt64ByName(lowerKeyName, mLowerKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
@ -1323,10 +1342,10 @@ OpenObjectCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
if (!mUpperKey.IsUnset()) {
if (mUpperKey.IsString()) {
rv = stmt->BindStringByName(rightKeyName, mUpperKey.StringValue());
rv = stmt->BindStringByName(upperKeyName, mUpperKey.StringValue());
}
else if (mUpperKey.IsInt()) {
rv = stmt->BindInt64ByName(rightKeyName, mUpperKey.IntValue());
rv = stmt->BindInt64ByName(upperKeyName, mUpperKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
@ -1335,54 +1354,172 @@ OpenObjectCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
}
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 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
}
rv = stmt->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
KeyValuePair* pair = mData.AppendElement();
NS_ASSERTION(pair, "Shouldn't fail if SetCapacity succeeded!");
if (!hasResult) {
mKey = Key::UNSETKEY;
return NS_OK;
}
PRInt32 keyType;
rv = stmt->GetTypeOfIndex(0, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
PRInt32 keyType;
rv = stmt->GetTypeOfIndex(0, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
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, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
rv = stmt->GetString(1, pair->value);
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
mKey = stmt->AsInt64(0);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = stmt->GetString(0, mKey.ToString());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
rv = stmt->GetTypeOfIndex(1, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
mObjectKey = stmt->AsInt64(1);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = stmt->GetString(1, mObjectKey.ToString());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
#ifdef DEBUG
{
PRInt32 valueType;
NS_ASSERTION(NS_SUCCEEDED(stmt->GetTypeOfIndex(2, &valueType)) &&
valueType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad value type!");
}
#endif
rv = stmt->GetString(2, mValue);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
/*
SELECT index_data.value, object_data.key_value, object_data.data
FROM object_data INNER JOIN index_data
ON index_data.object_data_id = object_data.id
WHERE index_data.index_id = 2 AND index_data.value < 73
AND ( ( index_data.value = 65 AND object_data.key_value > "237-23-7736" )
OR ( index_data.value > 65 ) )
ORDER BY index_data.value ASC, object_data.key_value ASC
*/
// Now we need to make the query to get the next match.
nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT ") + value +
NS_LITERAL_CSTRING(", ") + keyValue +
NS_LITERAL_CSTRING(", ") + data +
NS_LITERAL_CSTRING(" FROM ") + objectTable +
NS_LITERAL_CSTRING(" INNER JOIN ") + indexTable +
NS_LITERAL_CSTRING(" ON ") + indexTable +
NS_LITERAL_CSTRING(".") + objectDataIdColumn +
NS_LITERAL_CSTRING(" = ") + objectTable +
NS_LITERAL_CSTRING(".id WHERE ") + indexTable +
NS_LITERAL_CSTRING(".index_id = :id");
NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
NS_NAMED_LITERAL_CSTRING(objectKey, "object_key");
switch (mDirection) {
case nsIIDBCursor::NEXT:
if (!mUpperKey.IsUnset()) {
AppendConditionClause(value, rangeKey, true, !mUpperOpen, queryStart);
mRangeKey = mUpperKey;
}
mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ( ( ") +
value + NS_LITERAL_CSTRING(" = :") + currentKey +
NS_LITERAL_CSTRING(" AND ") + keyValue +
NS_LITERAL_CSTRING(" > :") + objectKey +
NS_LITERAL_CSTRING(" ) OR ( ") + value +
NS_LITERAL_CSTRING(" > :") + currentKey +
NS_LITERAL_CSTRING(" ) )") + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
NS_LITERAL_CSTRING(" >= :") +
currentKey + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
break;
case nsIIDBCursor::NEXT_NO_DUPLICATE:
if (!mUpperKey.IsUnset()) {
AppendConditionClause(value, rangeKey, true, !mUpperOpen, queryStart);
mRangeKey = mUpperKey;
}
mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
NS_LITERAL_CSTRING(" > :") + currentKey +
directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
NS_LITERAL_CSTRING(" >= :") + currentKey +
directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
break;
case nsIIDBCursor::PREV:
if (!mLowerKey.IsUnset()) {
AppendConditionClause(value, rangeKey, false, !mLowerOpen, queryStart);
mRangeKey = mLowerKey;
}
mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ( ( ") +
value + NS_LITERAL_CSTRING(" = :") + currentKey +
NS_LITERAL_CSTRING(" AND ") + keyValue +
NS_LITERAL_CSTRING(" < :") + objectKey +
NS_LITERAL_CSTRING(" ) OR ( ") + value +
NS_LITERAL_CSTRING(" < :") + currentKey +
NS_LITERAL_CSTRING(" ) )") + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
NS_LITERAL_CSTRING(" <= :") +
currentKey + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
break;
case nsIIDBCursor::PREV_NO_DUPLICATE:
if (!mLowerKey.IsUnset()) {
AppendConditionClause(value, rangeKey, false, !mLowerOpen, queryStart);
mRangeKey = mLowerKey;
}
mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
NS_LITERAL_CSTRING(" < :") + currentKey +
directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
NS_LITERAL_CSTRING(" <= :") + currentKey +
directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
break;
default:
NS_NOTREACHED("Unknown direction type!");
}
return NS_OK;
}
nsresult
OpenObjectCursorHelper::GetSuccessResult(nsIWritableVariant* aResult)
{
if (mData.IsEmpty()) {
if (mKey.IsUnset()) {
aResult->SetAsEmpty();
return NS_OK;
}
nsRefPtr<IDBCursor> cursor =
IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mData);
IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey,
mContinueQuery, mContinueToQuery, mKey, mObjectKey,
mValue);
NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
aResult->SetAsISupports(cursor);

Просмотреть файл

@ -207,7 +207,11 @@ private:
const PRUint16 mDirection;
// Out-params.
nsTArray<KeyValuePair> mData;
Key mKey;
nsString mValue;
nsCString mContinueQuery;
nsCString mContinueToQuery;
Key mRangeKey;
};
class CreateIndexHelper : public AsyncConnectionHelper
@ -1828,74 +1832,56 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
keyColumn.AssignLiteral("key_value");
}
NS_NAMED_LITERAL_CSTRING(osid, "osid");
NS_NAMED_LITERAL_CSTRING(leftKeyName, "left_key");
NS_NAMED_LITERAL_CSTRING(rightKeyName, "right_key");
NS_NAMED_LITERAL_CSTRING(id, "id");
NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
nsCAutoString keyRangeClause;
if (!mLowerKey.IsUnset()) {
keyRangeClause = NS_LITERAL_CSTRING(" AND ") + keyColumn;
if (mLowerOpen) {
keyRangeClause.AppendLiteral(" > :");
}
else {
keyRangeClause.AppendLiteral(" >= :");
}
keyRangeClause.Append(leftKeyName);
AppendConditionClause(keyColumn, lowerKeyName, false, !mLowerOpen,
keyRangeClause);
}
if (!mUpperKey.IsUnset()) {
keyRangeClause += NS_LITERAL_CSTRING(" AND ") + keyColumn;
if (mUpperOpen) {
keyRangeClause.AppendLiteral(" < :");
}
else {
keyRangeClause.AppendLiteral(" <= :");
}
keyRangeClause.Append(rightKeyName);
AppendConditionClause(keyColumn, upperKeyName, true, !mUpperOpen,
keyRangeClause);
}
nsCString directionClause;
nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyColumn;
switch (mDirection) {
case nsIIDBCursor::NEXT:
case nsIIDBCursor::NEXT_NO_DUPLICATE:
directionClause = NS_LITERAL_CSTRING(" DESC");
directionClause += NS_LITERAL_CSTRING(" ASC");
break;
case nsIIDBCursor::PREV:
case nsIIDBCursor::PREV_NO_DUPLICATE:
directionClause = NS_LITERAL_CSTRING(" ASC");
directionClause += NS_LITERAL_CSTRING(" DESC");
break;
default:
NS_NOTREACHED("Unknown direction type!");
}
nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
NS_LITERAL_CSTRING(", data FROM ") + table +
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid +
keyRangeClause + NS_LITERAL_CSTRING(" ORDER BY ") +
keyColumn + directionClause;
nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
NS_LITERAL_CSTRING(", data FROM ") + table +
NS_LITERAL_CSTRING(" WHERE object_store_id = :") +
id + keyRangeClause + directionClause;
if (!mData.SetCapacity(50)) {
NS_ERROR("Out of memory!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
nsCOMPtr<mozIStorageStatement> stmt =
mTransaction->GetCachedStatement(firstQuery);
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id());
nsresult rv = stmt->BindInt64ByName(id, mObjectStore->Id());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (!mLowerKey.IsUnset()) {
if (mLowerKey.IsString()) {
rv = stmt->BindStringByName(leftKeyName, mLowerKey.StringValue());
rv = stmt->BindStringByName(lowerKeyName, mLowerKey.StringValue());
}
else if (mLowerKey.IsInt()) {
rv = stmt->BindInt64ByName(leftKeyName, mLowerKey.IntValue());
rv = stmt->BindInt64ByName(lowerKeyName, mLowerKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
@ -1905,10 +1891,10 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
if (!mUpperKey.IsUnset()) {
if (mUpperKey.IsString()) {
rv = stmt->BindStringByName(rightKeyName, mUpperKey.StringValue());
rv = stmt->BindStringByName(upperKeyName, mUpperKey.StringValue());
}
else if (mUpperKey.IsInt()) {
rv = stmt->BindInt64ByName(rightKeyName, mUpperKey.IntValue());
rv = stmt->BindInt64ByName(upperKeyName, mUpperKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
@ -1916,66 +1902,110 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
NS_WARNING("Copying all results for cursor snapshot, do something smarter!");
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 NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
}
rv = stmt->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
KeyValuePair* pair = mData.AppendElement();
NS_ASSERTION(pair, "Shouldn't fail if SetCapacity succeeded!");
if (!hasResult) {
mKey = Key::UNSETKEY;
return NS_OK;
}
PRInt32 keyType;
rv = stmt->GetTypeOfIndex(0, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
PRInt32 keyType;
rv = stmt->GetTypeOfIndex(0, &keyType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_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, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
#ifdef DEBUG
{
PRInt32 valueType;
NS_ASSERTION(NS_SUCCEEDED(stmt->GetTypeOfIndex(1, &valueType)) &&
valueType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad value type!");
}
#endif
rv = stmt->GetString(1, pair->value);
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
mKey = stmt->AsInt64(0);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = stmt->GetString(0, mKey.ToString());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
#ifdef DEBUG
{
PRInt32 valueType;
NS_ASSERTION(NS_SUCCEEDED(stmt->GetTypeOfIndex(1, &valueType)) &&
valueType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad value type!");
}
#endif
rv = stmt->GetString(1, mValue);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
// Now we need to make the query to get the next match.
keyRangeClause.Truncate();
nsCAutoString continueToKeyRangeClause;
NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
switch (mDirection) {
case nsIIDBCursor::NEXT:
case nsIIDBCursor::NEXT_NO_DUPLICATE:
AppendConditionClause(keyColumn, currentKey, false, false,
keyRangeClause);
AppendConditionClause(keyColumn, currentKey, false, true,
continueToKeyRangeClause);
if (!mUpperKey.IsUnset()) {
AppendConditionClause(keyColumn, rangeKey, true, !mUpperOpen,
keyRangeClause);
AppendConditionClause(keyColumn, rangeKey, true, !mUpperOpen,
continueToKeyRangeClause);
mRangeKey = mUpperKey;
}
break;
case nsIIDBCursor::PREV:
case nsIIDBCursor::PREV_NO_DUPLICATE:
AppendConditionClause(keyColumn, currentKey, true, false, keyRangeClause);
AppendConditionClause(keyColumn, currentKey, true, true,
continueToKeyRangeClause);
if (!mLowerKey.IsUnset()) {
AppendConditionClause(keyColumn, rangeKey, false, !mLowerOpen,
keyRangeClause);
AppendConditionClause(keyColumn, rangeKey, false, !mLowerOpen,
continueToKeyRangeClause);
mRangeKey = mLowerKey;
}
break;
default:
NS_NOTREACHED("Unknown direction type!");
}
mContinueQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
NS_LITERAL_CSTRING(", data FROM ") + table +
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + id +
keyRangeClause + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
mContinueToQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
NS_LITERAL_CSTRING(", data FROM ") + table +
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + id +
continueToKeyRangeClause + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
return NS_OK;
}
nsresult
OpenCursorHelper::GetSuccessResult(nsIWritableVariant* aResult)
{
if (mData.IsEmpty()) {
if (mKey.IsUnset()) {
aResult->SetAsEmpty();
return NS_OK;
}
nsRefPtr<IDBCursor> cursor =
IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection, mData);
IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
mRangeKey, mContinueQuery, mContinueToQuery, mKey,
mValue);
NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
aResult->SetAsISupports(cursor);
@ -2193,8 +2223,8 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
}
NS_NAMED_LITERAL_CSTRING(osid, "osid");
NS_NAMED_LITERAL_CSTRING(leftKeyName, "left_key");
NS_NAMED_LITERAL_CSTRING(rightKeyName, "right_key");
NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
nsCAutoString keyRangeClause;
if (!mLowerKey.IsUnset()) {
@ -2205,7 +2235,7 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
else {
keyRangeClause.AppendLiteral(" >= :");
}
keyRangeClause.Append(leftKeyName);
keyRangeClause.Append(lowerKeyName);
}
if (!mUpperKey.IsUnset()) {
@ -2216,7 +2246,7 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
else {
keyRangeClause.AppendLiteral(" <= :");
}
keyRangeClause.Append(rightKeyName);
keyRangeClause.Append(upperKeyName);
}
nsCAutoString limitClause;
@ -2245,10 +2275,10 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
if (!mLowerKey.IsUnset()) {
if (mLowerKey.IsString()) {
rv = stmt->BindStringByName(leftKeyName, mLowerKey.StringValue());
rv = stmt->BindStringByName(lowerKeyName, mLowerKey.StringValue());
}
else if (mLowerKey.IsInt()) {
rv = stmt->BindInt64ByName(leftKeyName, mLowerKey.IntValue());
rv = stmt->BindInt64ByName(lowerKeyName, mLowerKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
@ -2258,10 +2288,10 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
if (!mUpperKey.IsUnset()) {
if (mUpperKey.IsString()) {
rv = stmt->BindStringByName(rightKeyName, mUpperKey.StringValue());
rv = stmt->BindStringByName(upperKeyName, mUpperKey.StringValue());
}
else if (mUpperKey.IsInt()) {
rv = stmt->BindInt64ByName(rightKeyName, mUpperKey.IntValue());
rv = stmt->BindInt64ByName(upperKeyName, mUpperKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");

Просмотреть файл

@ -79,6 +79,11 @@ public:
return mSource;
}
void Reset()
{
mReadyState = nsIIDBRequest::LOADING;
}
void SetDone()
{
NS_ASSERTION(mReadyState != nsIIDBRequest::DONE, "Already set!");

Просмотреть файл

@ -60,4 +60,33 @@
#define USING_INDEXEDDB_NAMESPACE \
using namespace mozilla::dom::indexedDB;
BEGIN_INDEXEDDB_NAMESPACE
inline
void
AppendConditionClause(const nsACString& aColumnName,
const nsACString& aArgName,
bool aLessThan,
bool aEquals,
nsACString& aResult)
{
aResult += NS_LITERAL_CSTRING(" AND ") + aColumnName +
NS_LITERAL_CSTRING(" ");
if (aLessThan) {
aResult.AppendLiteral("<");
}
else {
aResult.AppendLiteral(">");
}
if (aEquals) {
aResult.AppendLiteral("=");
}
aResult += NS_LITERAL_CSTRING(" :") + aArgName;
}
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_indexeddatabase_h__

Просмотреть файл

@ -167,6 +167,16 @@ public:
return !(*this == aOther || *this < aOther);
}
bool operator<=(const Key& aOther) const
{
return (*this == aOther || *this < aOther);
}
bool operator>=(const Key& aOther) const
{
return (*this == aOther || !(*this < aOther));
}
bool IsUnset() const { return mType == UNSETKEY; }
bool IsNull() const { return mType == NULLKEY; }
bool IsString() const { return mType == STRINGKEY; }

Просмотреть файл

@ -67,15 +67,10 @@ interface nsIIDBCursor : nsISupports
[implicit_jscontext]
readonly attribute jsval value;
// Returns true always for non-preloaded cursors. Calling continue means that
// the same onsuccess function will be called again with the new key/value
// (or null if no more matches).
//
// For preloaded cursors returns true if key/value have been set to new
// values. If false then no more matches are available and getting the key,
// value property will throw, as will calling update() and remove().
// Calling continue means that the same onsuccess function will be called
// again with the new key/value (or null if no more matches).
[implicit_jscontext, optional_argc]
boolean continue([optional /* undefined */] in jsval key);
void continue([optional /* undefined */] in jsval key);
// Success fires IDBTransactionEvent, result == key
[implicit_jscontext]

Просмотреть файл

@ -56,6 +56,7 @@ TEST_FILES = \
test_create_index.html \
test_create_objectStore.html \
test_cursors.html \
test_cursor_mutation.html \
test_cursor_update_updates_indexes.html \
test_event_propagation.html \
test_event_source.html \

Просмотреть файл

@ -0,0 +1,133 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Indexed Database Property Test</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function testSteps()
{
const objectStoreData = [
// This one will be removed.
{ ss: "237-23-7732", name: "Bob" },
// These will always be included.
{ ss: "237-23-7733", name: "Ann" },
{ ss: "237-23-7734", name: "Ron" },
{ ss: "237-23-7735", name: "Sue" },
{ ss: "237-23-7736", name: "Joe" },
// This one will be added.
{ ss: "237-23-7737", name: "Pat" }
];
// Post-add and post-remove data ordered by name.
const objectStoreDataNameSort = [ 1, 4, 5, 2, 3 ];
let request = moz_indexedDB.open(window.location.pathname);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let db = event.result;
db.setVersion("1").onsuccess = grabEventAndContinueHandler;
event = yield;
event.transaction.oncomplete = continueToNextStep;
let objectStore = db.createObjectStore("foo", "ss");
objectStore.createIndex("name", "name", true);
for (let i = 0; i < objectStoreData.length - 1; i++) {
objectStore.add(objectStoreData[i]);
}
yield;
let count = 0;
let sawAdded = false;
let sawRemoved = false;
db.transaction("foo").objectStore("foo").openCursor().onsuccess =
function(event) {
event.transaction.oncomplete = continueToNextStep;
let cursor = event.result;
if (cursor) {
if (cursor.value.name == objectStoreData[0].name) {
sawRemoved = true;
}
if (cursor.value.name ==
objectStoreData[objectStoreData.length - 1].name) {
sawAdded = true;
}
cursor.continue();
count++;
}
};
yield;
is(count, objectStoreData.length - 1, "Good initial count");
is(sawAdded, false, "Didn't see item that is about to be added");
is(sawRemoved, true, "Saw item that is about to be removed");
count = 0;
sawAdded = false;
sawRemoved = false;
db.transaction("foo", IDBTransaction.READ_WRITE).objectStore("foo")
.index("name").openObjectCursor().onsuccess = function(event) {
event.transaction.oncomplete = continueToNextStep;
let cursor = event.result;
if (cursor) {
if (cursor.value.name == objectStoreData[0].name) {
sawRemoved = true;
}
if (cursor.value.name ==
objectStoreData[objectStoreData.length - 1].name) {
sawAdded = true;
}
is(cursor.value.name,
objectStoreData[objectStoreDataNameSort[count++]].name,
"Correct name");
if (count == 1) {
let objectStore = event.transaction.objectStore("foo");
objectStore.delete(objectStoreData[0].ss)
.onsuccess = function(event) {
objectStore.add(objectStoreData[objectStoreData.length - 1])
.onsuccess =
function(event) {
cursor.continue();
};
};
}
else {
cursor.continue();
}
}
};
yield;
is(count, objectStoreData.length - 1, "Good final count");
is(sawAdded, true, "Saw item that was added");
is(sawRemoved, false, "Didn't see item that was removed");
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>

Просмотреть файл

@ -100,11 +100,16 @@
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
ok(true, "continue twice should not throw");
try {
cursor.continue();
ok(false, "continue twice should throw");
}
catch (e) {
ok(e instanceof IDBDatabaseException, "got a database exception");
is(e.code, IDBDatabaseException.NOT_ALLOWED_ERR, "correct code");
}
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
@ -130,8 +135,7 @@
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
@ -156,8 +160,12 @@
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
let retval = keyIndex ? cursor.continue() : cursor.continue("b");
is(retval, true, "Correct return from continue");
if (keyIndex) {
cursor.continue();
}
else {
cursor.continue("b");
}
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
@ -182,8 +190,12 @@
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
let retval = keyIndex ? cursor.continue() : cursor.continue(10);
is(retval, true, "Correct return from continue");
if (keyIndex) {
cursor.continue();
}
else {
cursor.continue(10);
}
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
@ -208,13 +220,17 @@
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
let retval = keyIndex ? cursor.continue() : cursor.continue("c");
is(retval, true, "Correct return from continue");
if (keyIndex) {
cursor.continue();
}
else {
cursor.continue("c");
}
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
keyIndex++;
keyIndex += keyIndex ? 1 : 7;
}
else {
testGenerator.next();
@ -322,8 +338,7 @@
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, sortedKeys[keyIndex], "Correct key");
is(cursor.value, "foo", "Correct value");

Просмотреть файл

@ -143,6 +143,8 @@
is(event.result.height, 60, "Correct height returned!");
is(event.result.weight, 120, "Correct weight returned!");
ok(true, "Test group 1");
let keyIndex = 0;
request = objectStore.index("name").openCursor();
@ -155,8 +157,7 @@
is(cursor.value, objectStoreDataNameSort[keyIndex].key,
"Correct value");
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -173,6 +174,8 @@
is(keyIndex, objectStoreData.length, "Saw all the expected keys");
ok(true, "Test group 2");
keyIndex = 0;
request = objectStore.index("weight").openCursor(null, NEXT);
@ -185,8 +188,7 @@
is(cursor.value, objectStoreDataWeightSort[keyIndex].key,
"Correct value");
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, objectStoreDataWeightSort[keyIndex].value.weight,
"Correct key");
@ -212,6 +214,8 @@
request.onsuccess = unexpectedSuccessHandler;
event = yield;
ok(true, "Test group 3");
keyIndex = objectStoreDataNameSort.length - 1;
request = objectStore.index("name").openCursor(null, PREV);
@ -224,8 +228,7 @@
is(cursor.value, objectStoreDataNameSort[keyIndex].key,
"Correct value");
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -242,6 +245,8 @@
is(keyIndex, -1, "Saw all the expected keys");
ok(true, "Test group 4");
keyIndex = 1;
let keyRange = IDBKeyRange.bound("Bob", "Ron");
@ -266,6 +271,8 @@
is(keyIndex, 5, "Saw all the expected keys");
ok(true, "Test group 5");
keyIndex = 2;
let keyRange = IDBKeyRange.bound("Bob", "Ron", true);
@ -290,6 +297,8 @@
is(keyIndex, 5, "Saw all the expected keys");
ok(true, "Test group 6");
keyIndex = 1;
let keyRange = IDBKeyRange.bound("Bob", "Ron", false, true);
@ -314,6 +323,8 @@
is(keyIndex, 4, "Saw all the expected keys");
ok(true, "Test group 7");
keyIndex = 2;
keyRange = IDBKeyRange.bound("Bob", "Ron", true, true);
@ -338,6 +349,8 @@
is(keyIndex, 4, "Saw all the expected keys");
ok(true, "Test group 8");
keyIndex = 1;
keyRange = IDBKeyRange.lowerBound("Bob");
@ -362,6 +375,8 @@
is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys");
ok(true, "Test group 9");
keyIndex = 2;
keyRange = IDBKeyRange.lowerBound("Bob", true);
@ -386,6 +401,8 @@
is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys");
ok(true, "Test group 10");
keyIndex = 0;
keyRange = IDBKeyRange.upperBound("Joe");
@ -410,6 +427,8 @@
is(keyIndex, 3, "Saw all the expected keys");
ok(true, "Test group 11");
keyIndex = 0;
keyRange = IDBKeyRange.upperBound("Joe", true);
@ -434,6 +453,8 @@
is(keyIndex, 2, "Saw all the expected keys");
ok(true, "Test group 12");
keyIndex = 3;
keyRange = IDBKeyRange.only("Pat");
@ -458,6 +479,8 @@
is(keyIndex, 4, "Saw all the expected keys");
ok(true, "Test group 13");
keyIndex = 0;
request = objectStore.index("name").openObjectCursor();
@ -478,8 +501,7 @@
"Correct weight");
}
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -504,6 +526,8 @@
is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys");
ok(true, "Test group 14");
keyIndex = objectStoreDataNameSort.length - 1;
request = objectStore.index("name").openObjectCursor(null, PREV);
@ -524,8 +548,7 @@
"Correct weight");
}
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -550,6 +573,8 @@
is(keyIndex, -1, "Saw all the expected keys");
ok(true, "Test group 15");
keyIndex = 1;
keyRange = IDBKeyRange.bound("Bob", "Ron");
@ -571,8 +596,7 @@
"Correct weight");
}
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -597,6 +621,8 @@
is(keyIndex, 5, "Saw all the expected keys");
ok(true, "Test group 16");
keyIndex = 2;
keyRange = IDBKeyRange.bound("Bob", "Ron", true);
@ -618,8 +644,7 @@
"Correct weight");
}
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -644,6 +669,8 @@
is(keyIndex, 5, "Saw all the expected keys");
ok(true, "Test group 17");
keyIndex = 1;
keyRange = IDBKeyRange.bound("Bob", "Ron", false, true);
@ -665,8 +692,7 @@
"Correct weight");
}
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -691,6 +717,8 @@
is(keyIndex, 4, "Saw all the expected keys");
ok(true, "Test group 18");
keyIndex = 2;
keyRange = IDBKeyRange.bound("Bob", "Ron", true, true);
@ -712,8 +740,7 @@
"Correct weight");
}
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -738,6 +765,8 @@
is(keyIndex, 4, "Saw all the expected keys");
ok(true, "Test group 19");
keyIndex = 4;
keyRange = IDBKeyRange.bound("Bob", "Ron");
@ -759,8 +788,7 @@
"Correct weight");
}
let retval = cursor.continue();
is(retval, true, "Correct return from continue");
cursor.continue();
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -785,6 +813,8 @@
is(keyIndex, 0, "Saw all the expected keys");
ok(true, "Test group 20");
// Test NEXT_NO_DUPLICATE
keyIndex = 3;
keyRange = IDBKeyRange.only(65);
@ -810,7 +840,9 @@
is(keyIndex, 5, "Saw all the expected keys");
keyIndex = 4;
ok(true, "Test group 21");
keyIndex = 3;
keyRange = IDBKeyRange.only(65);
request = objectStore.index("height").openCursor(keyRange,
@ -833,7 +865,9 @@
}
yield;
is(keyIndex, 5, "Saw all the expected keys");
is(keyIndex, 4, "Saw all the expected keys");
ok(true, "Test group 22");
keyIndex = 5;
@ -849,7 +883,7 @@
"Correct value");
cursor.continue();
if (keyIndex == 4) {
if (keyIndex == 5) {
keyIndex--;
}
keyIndex--;
@ -862,7 +896,8 @@
is(keyIndex, -1, "Saw all the expected keys");
// Test NEXT_NO_DUPLICATE
ok(true, "Test group 23");
keyIndex = 3;
keyRange = IDBKeyRange.only(65);
@ -895,7 +930,9 @@
is(keyIndex, 5, "Saw all the expected keys");
keyIndex = 4;
ok(true, "Test group 24");
keyIndex = 3;
keyRange = IDBKeyRange.only(65);
request = objectStore.index("height").openObjectCursor(keyRange,
@ -926,7 +963,9 @@
}
yield;
is(keyIndex, 5, "Saw all the expected keys");
is(keyIndex, 4, "Saw all the expected keys");
ok(true, "Test group 25");
keyIndex = 5;
@ -950,7 +989,7 @@
}
cursor.continue();
if (keyIndex == 4) {
if (keyIndex == 5) {
keyIndex--;
}
keyIndex--;
@ -963,6 +1002,8 @@
is(keyIndex, -1, "Saw all the expected keys");
ok(true, "Test group 26");
keyIndex = 0;
request = objectStore.index("name").openCursor();
@ -977,8 +1018,7 @@
let nextKey = !keyIndex ? "Pat" : undefined;
let retval = cursor.continue(nextKey);
is(retval, true, "Correct return from continue");
cursor.continue(nextKey);
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -1000,6 +1040,8 @@
is(keyIndex, objectStoreData.length, "Saw all the expected keys");
ok(true, "Test group 27");
keyIndex = 0;
request = objectStore.index("name").openCursor();
@ -1014,15 +1056,14 @@
let nextKey = !keyIndex ? "Flo" : undefined;
let retval = cursor.continue(nextKey);
is(retval, true, "Correct return from continue");
cursor.continue(nextKey);
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
is(cursor.value, objectStoreDataNameSort[keyIndex].key,
"Correct value");
keyIndex++;
keyIndex += keyIndex ? 1 : 2;
}
else {
testGenerator.next();
@ -1032,6 +1073,8 @@
is(keyIndex, objectStoreData.length, "Saw all the expected keys");
ok(true, "Test group 28");
keyIndex = 0;
request = objectStore.index("name").openObjectCursor();
@ -1054,8 +1097,7 @@
let nextKey = !keyIndex ? "Pat" : undefined;
let retval = cursor.continue(nextKey);
is(retval, true, "Correct return from continue");
cursor.continue(nextKey);
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -1085,6 +1127,8 @@
is(keyIndex, objectStoreDataNameSort.length, "Saw all the expected keys");
ok(true, "Test group 29");
keyIndex = 0;
request = objectStore.index("name").openObjectCursor();
@ -1107,8 +1151,7 @@
let nextKey = !keyIndex ? "Flo" : undefined;
let retval = cursor.continue(nextKey);
is(retval, true, "Correct return from continue");
cursor.continue(nextKey);
is(cursor.key, objectStoreDataNameSort[keyIndex].value.name,
"Correct key");
@ -1123,7 +1166,7 @@
"Correct weight");
}
keyIndex++;
keyIndex += keyIndex ? 1 : 2;
}
else {
testGenerator.next();