зеркало из https://github.com/mozilla/gecko-dev.git
Bug 615269 - 'IndexedDB: Cursors should not copy all results before iterating'. r=sicking, a=blocking+.
This commit is contained in:
Родитель
97349c8f41
Коммит
50483cd601
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче