Add GetAll to indexes

This commit is contained in:
Ben Turner 2010-06-17 14:49:54 -07:00
parent 48a9f10a19
commit de6d1fa868
7 changed files with 554 additions and 72 deletions

View File

@ -535,7 +535,7 @@ IDBCursorRequest::Update(nsIVariant* aValue,
keyString.Length());
NS_ENSURE_TRUE(str, NS_ERROR_FAILURE);
*prop.addr() = STRING_TO_JSVAL(str);
prop.set(STRING_TO_JSVAL(str));
}
else {
NS_NOTREACHED("Bad key!");

View File

@ -518,3 +518,106 @@ GetAllSuccessEvent::GetResult(nsIVariant** /* aResult */)
cc->SetReturnValueWasSet(PR_TRUE);
return NS_OK;
}
NS_IMETHODIMP
GetAllKeySuccessEvent::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<Key> keys;
if (!mKeys.SwapElements(keys)) {
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 (!keys.IsEmpty()) {
if (!JS_SetArrayLength(cx, array, jsuint(keys.Length()))) {
mCachedValue = JSVAL_VOID;
NS_ERROR("Failed to set array length!");
return NS_ERROR_FAILURE;
}
js::AutoValueRooter value(cx);
jsint count = jsint(keys.Length());
for (jsint index = 0; index < count; index++) {
const Key& key = keys[index];
NS_ASSERTION(!key.IsUnset() && !key.IsNull(), "Bad key!");
if (key.IsInt()) {
if (!JS_NewNumberValue(cx, key.IntValue(), value.addr())) {
mCachedValue = JSVAL_VOID;
NS_ERROR("Failed to make number value!");
return NS_ERROR_FAILURE;
}
}
else if (key.IsString()) {
const nsString& keyString = key.StringValue();
JSString* str = JS_NewUCStringCopyN(cx,
reinterpret_cast<const jschar*>(keyString.get()),
keyString.Length());
if (!str) {
mCachedValue = JSVAL_VOID;
NS_ERROR("Failed to make new string value!");
return NS_ERROR_FAILURE;
}
value.set(STRING_TO_JSVAL(str));
}
else {
NS_NOTREACHED("Bad key!");
}
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

@ -53,6 +53,8 @@
#include "jsapi.h"
#include "nsDOMEvent.h"
#include "mozilla/dom/indexedDB/IDBObjectStoreRequest.h"
#define SUCCESS_EVT_STR "success"
#define ERROR_EVT_STR "error"
#define COMPLETE_EVT_STR "complete"
@ -183,6 +185,23 @@ private:
nsTArray<nsString> mValues;
};
class GetAllKeySuccessEvent : public GetSuccessEvent
{
public:
GetAllKeySuccessEvent(nsTArray<Key>& aKeys)
: GetSuccessEvent(EmptyString())
{
if (!mKeys.SwapElements(aKeys)) {
NS_ERROR("Failed to swap elements!");
}
}
NS_IMETHOD GetResult(nsIVariant** aResult);
private:
nsTArray<Key> mKeys;
};
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_idbevents_h__

View File

@ -97,10 +97,32 @@ public:
PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
PRUint16 OnSuccess(nsIDOMEventTarget* aTarget);
private:
protected:
nsString mValue;
};
class GetAllHelper : public GetHelper
{
public:
GetAllHelper(IDBTransactionRequest* aTransaction,
IDBRequest* aRequest,
const Key& aKey,
PRInt64 aId,
bool aUnique,
bool aAutoIncrement,
const PRUint32 aLimit)
: GetHelper(aTransaction, aRequest, aKey, aId, aUnique, aAutoIncrement),
mLimit(aLimit)
{ }
PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
PRUint16 OnSuccess(nsIDOMEventTarget* aTarget);
protected:
const PRUint32 mLimit;
nsTArray<Key> mKeys;
};
class OpenCursorHelper : public AsyncConnectionHelper
{
public:
@ -226,9 +248,6 @@ NS_INTERFACE_MAP_END
DOMCI_DATA(IDBIndexRequest, IDBIndexRequest)
////////////////////////////////////////////////////////////////////////////////
//// nsIIDBIndex
NS_IMETHODIMP
IDBIndexRequest::GetName(nsAString& aName)
{
@ -264,8 +283,107 @@ IDBIndexRequest::GetUnique(PRBool* aUnique)
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
//// nsIIDBIndexRequest
NS_IMETHODIMP
IDBIndexRequest::Get(nsIVariant* aKey,
nsIIDBRequest** _retval)
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
NS_WARNING("Using a slow path for Get! Fix this now!");
Key key;
nsresult rv = IDBObjectStoreRequest::GetKeyFromVariant(aKey, key);
NS_ENSURE_SUCCESS(rv, rv);
if (key.IsUnset() || key.IsNull()) {
return NS_ERROR_INVALID_ARG;
}
nsRefPtr<IDBRequest> request = GenerateRequest();
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsRefPtr<GetHelper> helper =
new GetHelper(mObjectStore->Transaction(), request, key, mId, mUnique,
mAutoIncrement);
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, rv);
request.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
IDBIndexRequest::GetObject(nsIVariant* aKey,
nsIIDBRequest** _retval)
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
NS_WARNING("Using a slow path for Get! Fix this now!");
Key key;
nsresult rv = IDBObjectStoreRequest::GetKeyFromVariant(aKey, key);
NS_ENSURE_SUCCESS(rv, rv);
if (key.IsUnset() || key.IsNull()) {
return NS_ERROR_INVALID_ARG;
}
nsRefPtr<IDBRequest> request = GenerateRequest();
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsRefPtr<GetObjectHelper> helper =
new GetObjectHelper(mObjectStore->Transaction(), request, key, mId, mUnique,
mAutoIncrement);
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, rv);
request.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
IDBIndexRequest::GetAll(nsIVariant* aKey,
PRUint32 aLimit,
PRUint8 aOptionalArgCount,
nsIIDBRequest** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
Key key;
nsresult rv = IDBObjectStoreRequest::GetKeyFromVariant(aKey, key);
NS_ENSURE_SUCCESS(rv, rv);
if (key.IsNull()) {
key = Key::UNSETKEY;
}
if (aOptionalArgCount < 2) {
aLimit = PR_UINT32_MAX;
}
nsRefPtr<IDBRequest> request = GenerateRequest();
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsRefPtr<GetAllHelper> helper =
new GetAllHelper(mObjectStore->Transaction(), request, key, mId, mUnique,
mAutoIncrement, aLimit);
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, rv);
request.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
IDBIndexRequest::GetAllObjects(nsIVariant* aKey,
PRUint32 aLimit,
PRUint8 aOptionalArgCount,
nsIIDBRequest** _retval)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_NOTYETIMPLEMENTED("Implement me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
IDBIndexRequest::OpenCursor(nsIIDBKeyRange* aKeyRange,
@ -413,65 +531,6 @@ IDBIndexRequest::OpenObjectCursor(nsIIDBKeyRange* aKeyRange,
return NS_OK;
}
NS_IMETHODIMP
IDBIndexRequest::GetObject(nsIVariant* aKey,
nsIIDBRequest** _retval)
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
NS_WARNING("Using a slow path for Get! Fix this now!");
Key key;
nsresult rv = IDBObjectStoreRequest::GetKeyFromVariant(aKey, key);
NS_ENSURE_SUCCESS(rv, rv);
if (key.IsUnset() || key.IsNull()) {
return NS_ERROR_INVALID_ARG;
}
nsRefPtr<IDBRequest> request = GenerateRequest();
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsRefPtr<GetObjectHelper> helper =
new GetObjectHelper(mObjectStore->Transaction(), request, key, mId, mUnique,
mAutoIncrement);
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, rv);
request.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
IDBIndexRequest::Get(nsIVariant* aKey,
nsIIDBRequest** _retval)
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
NS_WARNING("Using a slow path for Get! Fix this now!");
Key key;
nsresult rv = IDBObjectStoreRequest::GetKeyFromVariant(aKey, key);
NS_ENSURE_SUCCESS(rv, rv);
if (key.IsUnset() || key.IsNull()) {
return NS_ERROR_INVALID_ARG;
}
nsRefPtr<IDBRequest> request = GenerateRequest();
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
nsRefPtr<GetHelper> helper =
new GetHelper(mObjectStore->Transaction(), request, key, mId, mUnique,
mAutoIncrement);
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, rv);
request.forget(_retval);
return NS_OK;
}
PRUint16
GetHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
@ -605,6 +664,128 @@ GetObjectHelper::OnSuccess(nsIDOMEventTarget* aTarget)
return OK;
}
PRUint16
GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
NS_ASSERTION(aConnection, "Passed a null connection!");
if (!mKeys.SetCapacity(50)) {
NS_ERROR("Out of memory!");
return nsIIDBDatabaseException::UNKNOWN_ERR;
}
nsCString keyColumn;
nsCString tableName;
if (mAutoIncrement) {
keyColumn.AssignLiteral("ai_object_data_id");
if (mUnique) {
tableName.AssignLiteral("unique_ai_index_data");
}
else {
tableName.AssignLiteral("ai_index_data");
}
}
else {
keyColumn.AssignLiteral("object_data_key");
if (mUnique) {
tableName.AssignLiteral("unique_index_data");
}
else {
tableName.AssignLiteral("index_data");
}
}
NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
NS_NAMED_LITERAL_CSTRING(value, "value");
nsCString keyClause;
if (!mKey.IsUnset()) {
keyClause = NS_LITERAL_CSTRING(" AND ") + value +
NS_LITERAL_CSTRING(" = :") + value;
}
nsCString query = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
NS_LITERAL_CSTRING(" FROM ") + tableName +
NS_LITERAL_CSTRING(" WHERE ") + indexId +
NS_LITERAL_CSTRING(" = :") + indexId +
keyClause;
nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
NS_ENSURE_TRUE(stmt, nsIIDBDatabaseException::UNKNOWN_ERR);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt64ByName(indexId, mId);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
if (!mKey.IsUnset()) {
if (mKey.IsInt()) {
rv = stmt->BindInt64ByName(value, mKey.IntValue());
}
else if (mKey.IsString()) {
rv = stmt->BindStringByName(value, mKey.StringValue());
}
else {
NS_NOTREACHED("Bad key type!");
}
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
}
PRUint32 resultCount = 0;
PRBool hasResult;
while(resultCount++ < mLimit &&
NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
if (mKeys.Capacity() == mKeys.Length()) {
if (!mKeys.SetCapacity(mKeys.Capacity() * 2)) {
NS_ERROR("Out of memory!");
return nsIIDBDatabaseException::UNKNOWN_ERR;
}
}
Key* key = mKeys.AppendElement();
NS_ASSERTION(key, "This shouldn't fail!");
PRInt32 keyType;
rv = stmt->GetTypeOfIndex(0, &keyType);
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Bad key type!");
if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
*key = stmt->AsInt64(0);
}
else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
rv = stmt->GetString(0, key->ToString());
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
}
else {
NS_NOTREACHED("Bad SQLite type!");
}
}
NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
return OK;
}
PRUint16
GetAllHelper::OnSuccess(nsIDOMEventTarget* aTarget)
{
nsRefPtr<GetAllKeySuccessEvent> event(new GetAllKeySuccessEvent(mKeys));
NS_ASSERTION(mKeys.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;
}
PRUint16
OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{

View File

@ -51,6 +51,22 @@ interface nsIVariant;
[scriptable, uuid(8c21dfc3-62fa-470e-bc92-7d115b0fac97)]
interface nsIIDBIndexRequest : nsIIDBIndex
{
nsIIDBRequest
get(in nsIVariant key);
nsIIDBRequest
getObject(in nsIVariant key);
[optional_argc]
nsIIDBRequest
getAll([optional /* null */] in nsIVariant key,
[optional /* unlimited */] in unsigned long limit);
[optional_argc]
nsIIDBRequest
getAllObjects([optional /* null */] in nsIVariant key,
[optional /* unlimited */] in unsigned long limit);
[optional_argc]
nsIIDBRequest
openCursor([optional /* null */] in nsIIDBKeyRange range,
@ -63,10 +79,4 @@ interface nsIIDBIndexRequest : nsIIDBIndex
[optional /* nsIIDBCursor::NEXT */]
in unsigned short direction,
[optional /* false */] in boolean preload);
nsIIDBRequest
getObject(in nsIVariant key);
nsIIDBRequest
get(in nsIVariant key);
};

View File

@ -54,6 +54,7 @@ _TEST_FILES = \
test_event_source.html \
test_getAll.html \
test_global_data.html \
test_index_getAll.html \
test_indexes.html \
test_indexes_bad_values.html \
test_key_requirements.html \

View File

@ -0,0 +1,168 @@
<!--
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 objectStoreName = "People";
const objectStoreData = [
{ key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
{ key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
{ key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
{ key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
{ key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
{ key: "237-23-7737", value: { name: "Pat", height: 65 } }
];
const indexData = [
{ name: "name", keyPath: "name", unique: true },
{ name: "height", keyPath: "height", unique: false },
{ name: "weight", keyPath: "weight", unique: false }
];
const objectStoreDataNameSort = [
{ key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
{ key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
{ key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
{ key: "237-23-7737", value: { name: "Pat", height: 65 } },
{ key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } },
{ key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } }
];
const objectStoreDataWeightSort = [
{ key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
{ key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
{ key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
{ key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
{ key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }
];
const objectStoreDataHeightSort = [
{ key: "237-23-7733", value: { name: "Ann", height: 52, weight: 110 } },
{ key: "237-23-7735", value: { name: "Sue", height: 58, weight: 130 } },
{ key: "237-23-7732", value: { name: "Bob", height: 60, weight: 120 } },
{ key: "237-23-7736", value: { name: "Joe", height: 65, weight: 150 } },
{ key: "237-23-7737", value: { name: "Pat", height: 65 } },
{ key: "237-23-7734", value: { name: "Ron", height: 73, weight: 180 } }
];
let request = moz_indexedDB.open(name, description);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
let event = yield;
let db = event.result;
request = db.createObjectStore(objectStoreName, "");
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
let objectStore = event.result;
// First, add all our data to the object store.
let addedData = 0;
for (let i in objectStoreData) {
request = objectStore.add(objectStoreData[i].value,
objectStoreData[i].key);
request.onerror = errorHandler;
request.onsuccess = function(event) {
if (++addedData == objectStoreData.length) {
testGenerator.send(event);
}
}
}
event = yield;
// Now create the index.
addedData = 0;
for (let i in indexData) {
request = objectStore.createIndex(indexData[i].name,
indexData[i].keyPath,
indexData[i].unique);
request.onerror = errorHandler;
request.onsuccess = function(event) {
if (++addedData == indexData.length) {
is(objectStore.indexNames.length, addedData, "Good index count");
SimpleTest.executeSoon(function() { testGenerator.next() });
}
}
}
yield;
objectStore = db.objectStore(objectStoreName);
request = objectStore.index("height").getAll(65);
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], objectStoreDataHeightSort[parseInt(i) + 3].key,
"Correct key");
}
request = objectStore.index("height").getAll();
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, objectStoreDataHeightSort.length,
"Correct length");
for (let i in event.result) {
is(event.result[i], objectStoreDataHeightSort[i].key, "Correct key");
}
request = objectStore.index("height").getAll(null, 4);
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], objectStoreDataHeightSort[i].key, "Correct key");
}
request = objectStore.index("height").getAll(65, 1);
request.onerror = errorHandler;
request.onsuccess = grabEventAndContinueHandler;
event = yield;
is(event.result instanceof Array, true, "Got an array object");
is(event.result.length, 1, "Correct length");
for (let i in event.result) {
is(event.result[i], objectStoreDataHeightSort[parseInt(i) + 3].key,
"Correct key");
}
finishTest();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();"></body>
</html>