Implement GetAll on objectStores

This commit is contained in:
Ben Turner 2010-06-17 11:57:22 -07:00
parent 556de4c8d3
commit 639f5c6976
6 changed files with 490 additions and 5 deletions

View File

@ -42,6 +42,7 @@
#include "nsIIDBDatabaseException.h"
#include "nsIPrivateDOMEvent.h"
#include "jscntxt.h"
#include "nsContentUtils.h"
#include "nsDOMClassInfo.h"
#include "nsJSON.h"
@ -379,7 +380,7 @@ GetSuccessEvent::GetResult(nsIVariant** /* aResult */)
{
// This is the slow path, need to do this better once XPIDL can pass raw
// jsvals.
NS_WARNING("Using a slow path for GetObject! Fix this now!");
NS_WARNING("Using a slow path for GetResult! Fix this now!");
nsIXPConnect* xpc = nsContentUtils::XPConnect();
NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
@ -399,6 +400,9 @@ GetSuccessEvent::GetResult(nsIVariant** /* aResult */)
}
if (!mJSRuntime) {
nsString jsonValue = mValue;
mValue.Truncate();
JSContext* cx;
rv = cc->GetJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
@ -414,7 +418,7 @@ GetSuccessEvent::GetResult(nsIVariant** /* aResult */)
mJSRuntime = rt;
nsCOMPtr<nsIJSON> json(new nsJSON());
rv = json->DecodeToJSVal(mValue, cx, &mCachedValue);
rv = json->DecodeToJSVal(jsonValue, cx, &mCachedValue);
if (NS_FAILED(rv)) {
mCachedValue = JSVAL_VOID;
@ -427,3 +431,90 @@ GetSuccessEvent::GetResult(nsIVariant** /* aResult */)
cc->SetReturnValueWasSet(PR_TRUE);
return NS_OK;
}
NS_IMETHODIMP
GetAllSuccessEvent::GetResult(nsIVariant** /* aResult */)
{
// This is the slow path, need to do this better once XPIDL can pass raw
// jsvals.
NS_WARNING("Using a slow path for GetResult! Fix this now!");
nsIXPConnect* xpc = nsContentUtils::XPConnect();
NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
nsAXPCNativeCallContext* cc;
nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
jsval* retval;
rv = cc->GetRetValPtr(&retval);
NS_ENSURE_SUCCESS(rv, rv);
if (!mJSRuntime) {
JSContext* cx;
rv = cc->GetJSContext(&cx);
NS_ENSURE_SUCCESS(rv, rv);
JSAutoRequest ar(cx);
JSRuntime* rt = JS_GetRuntime(cx);
JSBool ok = JS_AddNamedRootRT(rt, &mCachedValue,
"GetSuccessEvent::mCachedValue");
NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
mJSRuntime = rt;
// Swap into a stack array so that we don't hang on to the strings if
// something fails.
nsTArray<nsString> values;
if (!mValues.SwapElements(values)) {
NS_ERROR("Failed to swap elements!");
return NS_ERROR_FAILURE;
}
JSObject* array = JS_NewArrayObject(cx, 0, NULL);
if (!array) {
NS_ERROR("Failed to make array!");
return NS_ERROR_FAILURE;
}
mCachedValue = OBJECT_TO_JSVAL(array);
if (!values.IsEmpty()) {
if (!JS_SetArrayLength(cx, array, jsuint(values.Length()))) {
mCachedValue = JSVAL_VOID;
NS_ERROR("Failed to set array length!");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIJSON> json(new nsJSON());
js::AutoValueRooter value(cx);
jsint count = jsint(values.Length());
for (jsint index = 0; index < count; index++) {
nsString jsonValue = values[index];
values[index].Truncate();
rv = json->DecodeToJSVal(jsonValue, cx, value.addr());
if (NS_FAILED(rv)) {
mCachedValue = JSVAL_VOID;
NS_ERROR("Failed to decode!");
return rv;
}
if (!JS_SetElement(cx, array, index, value.addr())) {
mCachedValue = JSVAL_VOID;
NS_ERROR("Failed to set array element!");
return NS_ERROR_FAILURE;
}
}
}
}
*retval = mCachedValue;
cc->SetReturnValueWasSet(PR_TRUE);
return NS_OK;
}

View File

@ -81,6 +81,7 @@ public:
protected:
IDBEvent() : nsDOMEvent(nsnull, nsnull) { }
virtual ~IDBEvent() { }
nsCOMPtr<nsISupports> mSource;
};
@ -159,10 +160,29 @@ public:
private:
nsString mValue;
protected:
jsval mCachedValue;
JSRuntime* mJSRuntime;
};
class GetAllSuccessEvent : public GetSuccessEvent
{
public:
GetAllSuccessEvent(nsTArray<nsString>& aValues)
: GetSuccessEvent(EmptyString())
{
if (!mValues.SwapElements(aValues)) {
NS_ERROR("Failed to swap elements!");
}
}
NS_IMETHOD GetResult(nsIVariant** aResult);
private:
nsTArray<nsString> mValues;
};
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_idbevents_h__

View File

@ -244,6 +244,39 @@ private:
nsRefPtr<IDBObjectStoreRequest> mObjectStore;
};
class GetAllHelper : public AsyncConnectionHelper
{
public:
GetAllHelper(IDBTransactionRequest* aTransaction,
IDBRequest* aRequest,
PRInt64 aObjectStoreID,
const Key& aLeftKey,
const Key& aRightKey,
const PRUint16 aKeyRangeFlags,
const PRUint32 aLimit,
bool aAutoIncrement)
: AsyncConnectionHelper(aTransaction, aRequest), mOSID(aObjectStoreID),
mLeftKey(aLeftKey), mRightKey(aRightKey), mKeyRangeFlags(aKeyRangeFlags),
mLimit(aLimit), mAutoIncrement(aAutoIncrement)
{ }
PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
PRUint16 OnSuccess(nsIDOMEventTarget* aTarget);
protected:
// In-params.
const PRInt64 mOSID;
const Key mLeftKey;
const Key mRightKey;
const PRUint16 mKeyRangeFlags;
const PRUint32 mLimit;
const bool mAutoIncrement;
private:
// Out-params.
nsTArray<nsString> mValues;
};
inline
nsresult
GetKeyFromObject(JSContext* aCx,
@ -738,8 +771,44 @@ IDBObjectStoreRequest::GetAll(nsIIDBKeyRange* aKeyRange,
return NS_ERROR_UNEXPECTED;
}
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
if (aOptionalArgCount < 2) {
aLimit = PR_UINT32_MAX;
}
Key leftKey, rightKey;
PRUint16 keyRangeFlags = 0;
nsresult rv;
if (aKeyRange) {
rv = aKeyRange->GetFlags(&keyRangeFlags);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIVariant> variant;
rv = aKeyRange->GetLeft(getter_AddRefs(variant));
NS_ENSURE_SUCCESS(rv, rv);
rv = GetKeyFromVariant(variant, leftKey);
NS_ENSURE_SUCCESS(rv, rv);
rv = aKeyRange->GetRight(getter_AddRefs(variant));
NS_ENSURE_SUCCESS(rv, rv);
rv = GetKeyFromVariant(variant, rightKey);
NS_ENSURE_SUCCESS(rv, rv);
}
nsRefPtr<IDBRequest> request = GenerateRequest();
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsRefPtr<GetAllHelper> helper =
new GetAllHelper(mTransaction, request, mId, leftKey, rightKey,
keyRangeFlags, aLimit, mAutoIncrement);
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, rv);
request.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
@ -2000,3 +2069,137 @@ RemoveIndexHelper::GetSuccessResult(nsIWritableVariant* /* aResult */)
return OK;
}
PRUint16
GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
nsCString table;
nsCString keyColumn;
if (mAutoIncrement) {
table.AssignLiteral("ai_object_data");
keyColumn.AssignLiteral("id");
}
else {
table.AssignLiteral("object_data");
keyColumn.AssignLiteral("key_value");
}
NS_NAMED_LITERAL_CSTRING(osid, "osid");
NS_NAMED_LITERAL_CSTRING(leftKeyName, "left_key");
NS_NAMED_LITERAL_CSTRING(rightKeyName, "right_key");
nsCAutoString keyRangeClause;
if (!mLeftKey.IsUnset()) {
keyRangeClause.AppendLiteral(" AND ");
keyRangeClause.Append(keyColumn);
if (mKeyRangeFlags & nsIIDBKeyRange::LEFT_OPEN) {
keyRangeClause.AppendLiteral(" > :");
}
else {
NS_ASSERTION(mKeyRangeFlags & nsIIDBKeyRange::LEFT_BOUND, "Bad flags!");
keyRangeClause.AppendLiteral(" >= :");
}
keyRangeClause.Append(leftKeyName);
}
if (!mRightKey.IsUnset()) {
keyRangeClause.AppendLiteral(" AND ");
keyRangeClause.Append(keyColumn);
if (mKeyRangeFlags & nsIIDBKeyRange::RIGHT_OPEN) {
keyRangeClause.AppendLiteral(" < :");
}
else {
NS_ASSERTION(mKeyRangeFlags & nsIIDBKeyRange::RIGHT_BOUND, "Bad flags!");
keyRangeClause.AppendLiteral(" <= :");
}
keyRangeClause.Append(rightKeyName);
}
nsCAutoString query("SELECT data FROM ");
query.Append(table);
query.AppendLiteral(" WHERE object_store_id = :");
query.Append(osid);
query.Append(keyRangeClause);
query.AppendLiteral(" ORDER BY ");
query.Append(keyColumn);
query.AppendLiteral(" ASC");
if (!mValues.SetCapacity(50)) {
NS_ERROR("Out of memory!");
return nsIIDBDatabaseException::UNKNOWN_ERR;
}
nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
NS_ENSURE_TRUE(stmt, nsIIDBDatabaseException::UNKNOWN_ERR);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt64ByName(osid, mOSID);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
if (!mLeftKey.IsUnset()) {
if (mLeftKey.IsString()) {
rv = stmt->BindStringByName(leftKeyName, mLeftKey.StringValue());
}
else if (mLeftKey.IsInt()) {
rv = stmt->BindInt64ByName(leftKeyName, mLeftKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
}
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
}
if (!mRightKey.IsUnset()) {
if (mRightKey.IsString()) {
rv = stmt->BindStringByName(rightKeyName, mRightKey.StringValue());
}
else if (mRightKey.IsInt()) {
rv = stmt->BindInt64ByName(rightKeyName, mRightKey.IntValue());
}
else {
NS_NOTREACHED("Bad key!");
}
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
}
PRUint32 resultCount = 0;
PRBool hasResult;
while (resultCount++ < mLimit &&
NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
if (mValues.Capacity() == mValues.Length()) {
if (!mValues.SetCapacity(mValues.Capacity() * 2)) {
NS_ERROR("Out of memory!");
return nsIIDBDatabaseException::UNKNOWN_ERR;
}
}
nsString* value = mValues.AppendElement();
NS_ASSERTION(value, "Shouldn't fail if SetCapacity succeeded!");
rv = stmt->GetString(0, *value);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
}
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
return OK;
}
PRUint16
GetAllHelper::OnSuccess(nsIDOMEventTarget* aTarget)
{
NS_ASSERTION(mValues.Length() <= mLimit, "Too many results!");
nsRefPtr<GetAllSuccessEvent> event(new GetAllSuccessEvent(mValues));
NS_ASSERTION(mValues.IsEmpty(), "Should have swapped!");
nsresult rv = event->Init(mRequest, mTransaction);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
PRBool dummy;
aTarget->DispatchEvent(static_cast<nsDOMEvent*>(event), &dummy);
return OK;
}

View File

@ -59,7 +59,7 @@ interface nsIIDBObjectStoreRequest : nsIIDBObjectStore
// Success fires IDBTransactionEvent, result == array of values for given keys
[optional_argc]
nsIIDBRequest
getAll(in nsIIDBKeyRange key,
getAll([optional /* null */] in nsIIDBKeyRange key,
[optional /* unlimited */] in unsigned long limit);
// Success fires IDBTransactionEvent, result == key

View File

@ -52,6 +52,7 @@ _TEST_FILES = \
test_create_objectStore.html \
test_cursors.html \
test_event_source.html \
test_getAll.html \
test_global_data.html \
test_indexes.html \
test_indexes_bad_values.html \

View File

@ -0,0 +1,170 @@
<!--
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 name = window.location.pathname;
const description = "My Test Database";
const values = [ "a", "1", 1, "foo", 300, true, false, 4.5, null ];
let request = moz_indexedDB.open(name, description);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let db = event.result;
request = db.createObjectStore("foo", "", true);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let objectStore = event.result;
request = objectStore.getAll();
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, 0, "No elements");
let addedCount = 0;
for (let i in values) {
request = objectStore.add(values[i]);
request.onerror = errorHandler;
request.onsuccess = function(event) {
if (++addedCount == values.length) {
SimpleTest.executeSoon(function() { testGenerator.next(); });
}
}
}
yield;
request = db.objectStore("foo").getAll();
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, values.length, "Same length");
for (let i in event.result) {
is(event.result[i], values[i], "Same value");
}
request = db.objectStore("foo").getAll(null, 5);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, 5, "Correct length");
for (let i in event.result) {
is(event.result[i], values[i], "Same value");
}
let keyRange = moz_indexedDB.makeBoundKeyRange(1, 9);
request = db.objectStore("foo").getAll(keyRange);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, values.length, "Correct length");
for (let i in event.result) {
is(event.result[i], values[i], "Same value");
}
keyRange = moz_indexedDB.makeBoundKeyRange(4, 7);
request = db.objectStore("foo").getAll(keyRange);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, 4, "Correct length");
for (let i in event.result) {
is(event.result[i], values[parseInt(i) + 3], "Same value");
}
keyRange = moz_indexedDB.makeBoundKeyRange(4, 7);
request = db.objectStore("foo").getAll(keyRange, 2);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, 2, "Correct length");
for (let i in event.result) {
is(event.result[i], values[parseInt(i) + 3], "Same value");
}
keyRange = moz_indexedDB.makeBoundKeyRange(4, 7);
request = db.objectStore("foo").getAll(keyRange, 50);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, 4, "Correct length");
for (let i in event.result) {
is(event.result[i], values[parseInt(i) + 3], "Same value");
}
keyRange = moz_indexedDB.makeBoundKeyRange(4, 7);
request = db.objectStore("foo").getAll(keyRange, 0);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, 0, "Correct length");
keyRange = moz_indexedDB.makeBoundKeyRange(4, 7, true, true);
request = db.objectStore("foo").getAll(keyRange);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, 2, "Correct length");
for (let i in event.result) {
is(event.result[i], values[parseInt(i) + 4], "Same value");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>