Bug 694138: Support an array of strings as keyPath on objectStores and indexes. r=janv/bent

This commit is contained in:
Jonas Sicking 2011-12-20 02:59:07 -08:00
Родитель fe44081f28
Коммит 9d8687d0bf
18 изменённых файлов: 477 добавлений и 91 удалений

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

@ -117,6 +117,7 @@ IndexInfo::IndexInfo(const IndexInfo& aOther)
: id(aOther.id),
name(aOther.name),
keyPath(aOther.keyPath),
keyPathArray(aOther.keyPathArray),
unique(aOther.unique),
multiEntry(aOther.multiEntry)
{

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

@ -121,6 +121,7 @@ struct IndexInfo
PRInt64 id;
nsString name;
nsString keyPath;
nsTArray<nsString> keyPathArray;
bool unique;
bool multiEntry;
};
@ -148,6 +149,7 @@ public:
nsString name;
PRInt64 id;
nsString keyPath;
nsTArray<nsString> keyPathArray;
// Main-thread only members. This must *not* be touced on the database thread
nsTArray<IndexInfo> indexes;

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

@ -388,6 +388,7 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
nsString keyPath;
keyPath.SetIsVoid(true);
nsTArray<nsString> keyPathArray;
bool autoIncrement = false;
if (!JSVAL_IS_VOID(aOptions) && !JSVAL_IS_NULL(aOptions)) {
@ -399,6 +400,7 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
NS_ASSERTION(JSVAL_IS_OBJECT(aOptions), "Huh?!");
JSObject* options = JSVAL_TO_OBJECT(aOptions);
// Get keyPath
jsval val;
if (!JS_GetPropertyById(aCx, options, nsDOMClassInfo::sKeyPath_id, &val)) {
NS_WARNING("JS_GetPropertyById failed!");
@ -406,17 +408,55 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
}
if (!JSVAL_IS_VOID(val) && !JSVAL_IS_NULL(val)) {
JSString* str = JS_ValueToString(aCx, val);
if (!str) {
NS_WARNING("JS_ValueToString failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
if (!JSVAL_IS_PRIMITIVE(val) &&
JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(val))) {
JSObject* obj = JSVAL_TO_OBJECT(val);
jsuint length;
if (!JS_GetArrayLength(aCx, obj, &length)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (!length) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
keyPathArray.SetCapacity(length);
for (jsuint index = 0; index < length; index++) {
jsval val;
JSString* jsstr;
nsDependentJSString str;
if (!JS_GetElement(aCx, obj, index, &val) ||
!(jsstr = JS_ValueToString(aCx, val)) ||
!str.init(aCx, jsstr)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (!IDBObjectStore::IsValidKeyPath(aCx, str)) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
keyPathArray.AppendElement(str);
}
NS_ASSERTION(!keyPathArray.IsEmpty(), "This shouldn't have happened!");
}
nsDependentJSString dependentKeyPath;
if (!dependentKeyPath.init(aCx, str)) {
NS_WARNING("Initializing keyPath failed!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
else {
JSString* jsstr;
nsDependentJSString str;
if (!(jsstr = JS_ValueToString(aCx, val)) ||
!str.init(aCx, jsstr)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (!IDBObjectStore::IsValidKeyPath(aCx, str)) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
keyPath = str;
}
keyPath = dependentKeyPath;
}
if (!JS_GetPropertyById(aCx, options, nsDOMClassInfo::sAutoIncrement_id,
@ -437,13 +477,9 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
}
if (!keyPath.IsVoid()) {
if (keyPath.IsEmpty() && autoIncrement) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
if (!IDBObjectStore::IsValidKeyPath(aCx, keyPath)) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
if (autoIncrement &&
((!keyPath.IsVoid() && keyPath.IsEmpty()) || !keyPathArray.IsEmpty())) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
nsRefPtr<ObjectStoreInfo> newInfo(new ObjectStoreInfo());
@ -451,6 +487,7 @@ IDBDatabase::CreateObjectStore(const nsAString& aName,
newInfo->name = aName;
newInfo->id = databaseInfo->nextObjectStoreId++;
newInfo->keyPath = keyPath;
newInfo->keyPathArray = keyPathArray;
newInfo->nextAutoIncrementId = autoIncrement ? 1 : 0;
newInfo->comittedAutoIncrementId = newInfo->nextAutoIncrementId;
@ -746,11 +783,30 @@ CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mObjectStore->Name());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = mObjectStore->HasKeyPath() ?
stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
mObjectStore->KeyPath()) :
stmt->BindNullByName(NS_LITERAL_CSTRING("key_path"));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (mObjectStore->UsesKeyPathArray()) {
// We use a comma in the beginning to indicate that it's an array of
// key paths. This is to be able to tell a string-keypath from an
// array-keypath which contains only one item.
// It also makes serializing easier :-)
nsAutoString keyPath;
const nsTArray<nsString>& keyPaths = mObjectStore->KeyPathArray();
for (PRUint32 i = 0; i < keyPaths.Length(); ++i) {
keyPath.Append(NS_LITERAL_STRING(",") + keyPaths[i]);
}
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
keyPath);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else if (mObjectStore->HasKeyPath()) {
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
mObjectStore->KeyPath());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
rv = stmt->BindNullByName(NS_LITERAL_CSTRING("key_path"));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

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

@ -49,6 +49,7 @@
#include "nsAppDirectoryServiceDefs.h"
#include "nsComponentManagerUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDOMClassInfoID.h"
@ -202,6 +203,13 @@ IDBFactory::GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
return NS_OK;
}
inline
bool
IgnoreWhitespace(PRUnichar c)
{
return false;
}
// static
nsresult
IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
@ -245,8 +253,27 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
else {
NS_ASSERTION(columnType == mozIStorageStatement::VALUE_TYPE_TEXT,
"Should be a string");
rv = stmt->GetString(2, info->keyPath);
nsString keyPath;
rv = stmt->GetString(2, keyPath);
NS_ENSURE_SUCCESS(rv, rv);
if (!keyPath.IsEmpty() && keyPath.First() == ',') {
// We use a comma in the beginning to indicate that it's an array of
// key paths. This is to be able to tell a string-keypath from an
// array-keypath which contains only one item.
nsCharSeparatedTokenizerTemplate<IgnoreWhitespace>
tokenizer(keyPath, ',');
tokenizer.nextToken();
while (tokenizer.hasMoreTokens()) {
info->keyPathArray.AppendElement(tokenizer.nextToken());
}
NS_ASSERTION(!info->keyPathArray.IsEmpty(),
"Should have at least one keypath");
}
else {
info->keyPath = keyPath;
}
}
info->nextAutoIncrementId = stmt->AsInt64(3);
@ -291,8 +318,25 @@ IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
rv = stmt->GetString(2, indexInfo->name);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->GetString(3, indexInfo->keyPath);
nsString keyPath;
rv = stmt->GetString(3, keyPath);
NS_ENSURE_SUCCESS(rv, rv);
if (!keyPath.IsEmpty() && keyPath.First() == ',') {
// We use a comma in the beginning to indicate that it's an array of
// key paths. This is to be able to tell a string-keypath from an
// array-keypath which contains only one item.
nsCharSeparatedTokenizerTemplate<IgnoreWhitespace>
tokenizer(keyPath, ',');
tokenizer.nextToken();
while (tokenizer.hasMoreTokens()) {
indexInfo->keyPathArray.AppendElement(tokenizer.nextToken());
}
NS_ASSERTION(!indexInfo->keyPathArray.IsEmpty(),
"Should have at least one keypath");
}
else {
indexInfo->keyPath = keyPath;
}
indexInfo->unique = !!stmt->AsInt32(4);
indexInfo->multiEntry = !!stmt->AsInt32(5);

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

@ -48,6 +48,8 @@
#include "nsEventDispatcher.h"
#include "nsThreadUtils.h"
#include "mozilla/storage.h"
#include "xpcprivate.h"
#include "XPCQuickStubs.h"
#include "AsyncConnectionHelper.h"
#include "IDBCursor.h"
@ -319,6 +321,7 @@ IDBIndex::Create(IDBObjectStore* aObjectStore,
index->mId = aIndexInfo->id;
index->mName = aIndexInfo->name;
index->mKeyPath = aIndexInfo->keyPath;
index->mKeyPathArray = aIndexInfo->keyPathArray;
index->mUnique = aIndexInfo->unique;
index->mMultiEntry = aIndexInfo->multiEntry;
@ -380,11 +383,38 @@ IDBIndex::GetStoreName(nsAString& aStoreName)
}
NS_IMETHODIMP
IDBIndex::GetKeyPath(nsAString& aKeyPath)
IDBIndex::GetKeyPath(JSContext* aCx,
jsval* aVal)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
aKeyPath.Assign(mKeyPath);
if (UsesKeyPathArray()) {
JSObject* array = JS_NewArrayObject(aCx, mKeyPathArray.Length(), nsnull);
if (!array) {
NS_WARNING("Failed to make array!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
for (PRUint32 i = 0; i < mKeyPathArray.Length(); ++i) {
jsval val;
nsString tmp(mKeyPathArray[i]);
if (!xpc_qsStringToJsval(aCx, tmp, &val)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (!JS_SetElement(aCx, array, i, &val)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
}
*aVal = OBJECT_TO_JSVAL(array);
}
else {
nsString tmp(mKeyPath);
if (!xpc_qsStringToJsval(aCx, tmp, aVal)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
}
return NS_OK;
}

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

@ -97,6 +97,16 @@ public:
return mKeyPath;
}
bool UsesKeyPathArray() const
{
return !mKeyPathArray.IsEmpty();
}
const nsTArray<nsString>& KeyPathArray() const
{
return mKeyPathArray;
}
private:
IDBIndex();
~IDBIndex();
@ -109,6 +119,7 @@ private:
PRInt64 mId;
nsString mName;
nsString mKeyPath;
nsTArray<nsString> mKeyPathArray;
bool mUnique;
bool mMultiEntry;
};

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

@ -475,6 +475,32 @@ GetKeyFromValue(JSContext* aCx,
return NS_OK;
}
inline
nsresult
GetKeyFromValue(JSContext* aCx,
jsval aVal,
const nsTArray<nsString>& aKeyPathArray,
Key& aKey)
{
NS_ASSERTION(!aKeyPathArray.IsEmpty(),
"Should not use empty keyPath array");
for (PRUint32 i = 0; i < aKeyPathArray.Length(); ++i) {
jsval key;
nsresult rv = GetJSValFromKeyPath(aCx, aVal, aKeyPathArray[i], key);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(aKey.AppendArrayItem(aCx, i == 0, key))) {
NS_ASSERTION(aKey.IsUnset(), "Encoding error should unset");
return NS_OK;
}
}
aKey.FinishArray();
return NS_OK;
}
inline
already_AddRefed<IDBRequest>
GenerateRequest(IDBObjectStore* aObjectStore)
@ -513,6 +539,7 @@ IDBObjectStore::Create(IDBTransaction* aTransaction,
objectStore->mName = aStoreInfo->name;
objectStore->mId = aStoreInfo->id;
objectStore->mKeyPath = aStoreInfo->keyPath;
objectStore->mKeyPathArray = aStoreInfo->keyPathArray;
objectStore->mAutoIncrement = !!aStoreInfo->nextAutoIncrementId;
objectStore->mDatabaseId = aDatabaseId;
objectStore->mInfo = aStoreInfo;
@ -564,14 +591,31 @@ IDBObjectStore::IsValidKeyPath(JSContext* aCx,
nsresult
IDBObjectStore::AppendIndexUpdateInfo(PRInt64 aIndexID,
const nsAString& aKeyPath,
const nsTArray<nsString>& aKeyPathArray,
bool aUnique,
bool aMultiEntry,
JSContext* aCx,
jsval aObject,
jsval aVal,
nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
{
nsresult rv;
if (!aKeyPathArray.IsEmpty()) {
Key arrayKey;
rv = GetKeyFromValue(aCx, aVal, aKeyPathArray, arrayKey);
NS_ENSURE_SUCCESS(rv, rv);
if (!arrayKey.IsUnset()) {
IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
updateInfo->indexId = aIndexID;
updateInfo->indexUnique = aUnique;
updateInfo->value = arrayKey;
}
return NS_OK;
}
jsval key;
nsresult rv = GetJSValFromKeyPath(aCx, aObject, aKeyPath, key);
rv = GetJSValFromKeyPath(aCx, aVal, aKeyPath, key);
NS_ENSURE_SUCCESS(rv, rv);
if (aMultiEntry && !JSVAL_IS_PRIMITIVE(key) &&
@ -1128,7 +1172,12 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
else if (!mAutoIncrement) {
// Inline keys live on the object. Make sure that the value passed in is an
// object.
rv = GetKeyFromValue(aCx, aValue, mKeyPath, aKey);
if (UsesKeyPathArray()) {
rv = GetKeyFromValue(aCx, aValue, mKeyPathArray, aKey);
}
else {
rv = GetKeyFromValue(aCx, aValue, mKeyPath, aKey);
}
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1145,8 +1194,9 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
const IndexInfo& indexInfo = mInfo->indexes[indexesIndex];
rv = AppendIndexUpdateInfo(indexInfo.id, indexInfo.keyPath,
indexInfo.unique, indexInfo.multiEntry,
aCx, aValue, aUpdateInfoArray);
indexInfo.keyPathArray, indexInfo.unique,
indexInfo.multiEntry, aCx, aValue,
aUpdateInfoArray);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1364,11 +1414,38 @@ IDBObjectStore::GetName(nsAString& aName)
}
NS_IMETHODIMP
IDBObjectStore::GetKeyPath(nsAString& aKeyPath)
IDBObjectStore::GetKeyPath(JSContext* aCx,
jsval* aVal)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
aKeyPath.Assign(mKeyPath);
if (UsesKeyPathArray()) {
JSObject* array = JS_NewArrayObject(aCx, mKeyPathArray.Length(), nsnull);
if (!array) {
NS_WARNING("Failed to make array!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
for (PRUint32 i = 0; i < mKeyPathArray.Length(); ++i) {
jsval val;
nsString tmp(mKeyPathArray[i]);
if (!xpc_qsStringToJsval(aCx, tmp, &val)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (!JS_SetElement(aCx, array, i, &val)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
}
*aVal = OBJECT_TO_JSVAL(array);
}
else {
nsString tmp(mKeyPath);
if (!xpc_qsStringToJsval(aCx, tmp, aVal)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
}
return NS_OK;
}
@ -1613,17 +1690,69 @@ IDBObjectStore::OpenCursor(const jsval& aKey,
NS_IMETHODIMP
IDBObjectStore::CreateIndex(const nsAString& aName,
const nsAString& aKeyPath,
const jsval& aKeyPath,
const jsval& aOptions,
JSContext* aCx,
nsIIDBIndex** _retval)
{
NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
if (!IsValidKeyPath(aCx, aKeyPath)) {
return NS_ERROR_DOM_SYNTAX_ERR;
// Get KeyPath
nsString keyPath;
nsTArray<nsString> keyPathArray;
// See if this is a JS array.
if (!JSVAL_IS_PRIMITIVE(aKeyPath) &&
JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(aKeyPath))) {
JSObject* obj = JSVAL_TO_OBJECT(aKeyPath);
jsuint length;
if (!JS_GetArrayLength(aCx, obj, &length)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (!length) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
keyPathArray.SetCapacity(length);
for (jsuint index = 0; index < length; index++) {
jsval val;
JSString* jsstr;
nsDependentJSString str;
if (!JS_GetElement(aCx, obj, index, &val) ||
!(jsstr = JS_ValueToString(aCx, val)) ||
!str.init(aCx, jsstr)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (!IsValidKeyPath(aCx, str)) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
keyPathArray.AppendElement(str);
}
NS_ASSERTION(!keyPathArray.IsEmpty(), "This shouldn't have happened!");
}
else {
JSString* jsstr;
nsDependentJSString str;
if (!(jsstr = JS_ValueToString(aCx, aKeyPath)) ||
!str.init(aCx, jsstr)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (!IsValidKeyPath(aCx, str)) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
keyPath = str;
}
// Check name and current mode
IDBTransaction* transaction = AsyncConnectionHelper::GetCurrentTransaction();
if (!transaction ||
@ -1685,12 +1814,17 @@ IDBObjectStore::CreateIndex(const nsAString& aName,
multiEntry = !!boolVal;
}
if (multiEntry && !keyPathArray.IsEmpty()) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
DatabaseInfo* databaseInfo = mTransaction->DBInfo();
IndexInfo* indexInfo = mInfo->indexes.AppendElement();
indexInfo->id = databaseInfo->nextIndexId++;
indexInfo->name = aName;
indexInfo->keyPath = aKeyPath;
indexInfo->keyPath = keyPath;
indexInfo->keyPathArray.SwapElements(keyPathArray);
indexInfo->unique = unique;
indexInfo->multiEntry = multiEntry;
@ -1912,7 +2046,6 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
}
else if (mKey.IsFloat() &&
mKey.ToFloat() >= mObjectStore->Info()->nextAutoIncrementId) {
// XXX Once we support floats, we should use floor(mKey.ToFloat()) here
autoIncrementNum = floor(mKey.ToFloat());
}
@ -2423,9 +2556,25 @@ CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mIndex->Name());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
mIndex->KeyPath());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (mIndex->UsesKeyPathArray()) {
// We use a comma in the beginning to indicate that it's an array of
// key paths. This is to be able to tell a string-keypath from an
// array-keypath which contains only one item.
// It also makes serializing easier :-)
nsAutoString keyPath;
const nsTArray<nsString>& keyPaths = mIndex->KeyPathArray();
for (PRUint32 i = 0; i < keyPaths.Length(); ++i) {
keyPath.Append(NS_LITERAL_STRING(",") + keyPaths[i]);
}
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
keyPath);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
else {
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("key_path"),
mIndex->KeyPath());
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("unique"),
mIndex->IsUnique() ? 1 : 0);
@ -2521,6 +2670,7 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
nsTArray<IndexUpdateInfo> updateInfo;
rv = IDBObjectStore::AppendIndexUpdateInfo(mIndex->Id(),
mIndex->KeyPath(),
mIndex->KeyPathArray(),
mIndex->IsUnique(),
mIndex->IsMultiEntry(),
tlsEntry->Context(),

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

@ -81,6 +81,7 @@ public:
static nsresult
AppendIndexUpdateInfo(PRInt64 aIndexID,
const nsAString& aKeyPath,
const nsTArray<nsString>& aKeyPathArray,
bool aUnique,
bool aMultiEntry,
JSContext* aCx,
@ -159,7 +160,17 @@ public:
const bool HasKeyPath() const
{
return !mKeyPath.IsVoid();
return !mKeyPath.IsVoid() || !mKeyPathArray.IsEmpty();
}
bool UsesKeyPathArray() const
{
return !mKeyPathArray.IsEmpty();
}
const nsTArray<nsString>& KeyPathArray() const
{
return mKeyPathArray;
}
IDBTransaction* Transaction()
@ -199,6 +210,7 @@ private:
PRInt64 mId;
nsString mName;
nsString mKeyPath;
nsTArray<nsString> mKeyPathArray;
bool mAutoIncrement;
nsCOMPtr<nsIAtom> mDatabaseId;
nsRefPtr<ObjectStoreInfo> mInfo;

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

@ -20,7 +20,8 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Turner <bent.mozilla@gmail.com>
* Jan Varga <Jan.Varga@gmail.com>
* Jonas Sicking <jonas@sicking.cc>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or

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

@ -191,6 +191,28 @@ public:
return NS_OK;
}
nsresult AppendArrayItem(JSContext* aCx,
bool aFirst,
const jsval aVal)
{
if (aFirst) {
Unset();
}
nsresult rv = EncodeJSVal(aCx, aVal, aFirst ? eMaxType : 0);
if (NS_FAILED(rv)) {
Unset();
return rv;
}
return NS_OK;
}
void FinishArray()
{
TrimBuffer();
}
const nsCString& GetBuffer() const
{
return mBuffer;

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

@ -216,8 +216,8 @@ CreateTables(mozIStorageConnection* aDBConn)
"id INTEGER PRIMARY KEY, "
"object_store_id INTEGER NOT NULL, "
"key_value BLOB DEFAULT NULL, "
"data BLOB NOT NULL, "
"file_ids TEXT, "
"data BLOB NOT NULL, "
"UNIQUE (object_store_id, key_value), "
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
"CASCADE"
@ -1168,8 +1168,8 @@ UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
"id INTEGER PRIMARY KEY, "
"object_store_id INTEGER NOT NULL, "
"key_value BLOB DEFAULT NULL, "
"data BLOB NOT NULL, "
"file_ids TEXT, "
"data BLOB NOT NULL, "
"UNIQUE (object_store_id, key_value), "
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
"CASCADE"
@ -1179,7 +1179,7 @@ UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"INSERT INTO object_data "
"SELECT id, object_store_id, key_value, data, file_ids "
"SELECT id, object_store_id, key_value, file_ids, data "
"FROM temp_upgrade;"
));
NS_ENSURE_SUCCESS(rv, rv);
@ -1259,7 +1259,7 @@ UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
NS_ENSURE_SUCCESS(rv, rv);
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"INSERT OR IGNORE INTO index_data "
"INSERT INTO index_data "
"SELECT index_id, value, object_data_key, object_data_id "
"FROM temp_upgrade;"
));

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

@ -47,14 +47,15 @@ interface nsIIDBRequest;
* http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBIndex for more
* information.
*/
[scriptable, builtinclass, uuid(fcb9a158-833e-4aa9-ab19-ab90cbb50afc)]
[scriptable, builtinclass, uuid(233ec586-7b34-4263-b27e-a4991b757597)]
interface nsIIDBIndex : nsISupports
{
readonly attribute DOMString name;
readonly attribute DOMString storeName;
readonly attribute DOMString keyPath;
[implicit_jscontext]
readonly attribute jsval keyPath;
readonly attribute boolean unique;

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

@ -50,12 +50,13 @@ interface nsIDOMDOMStringList;
* http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-nsIIDBObjectStore
* for more information.
*/
[scriptable, builtinclass, uuid(e93c5ca4-89da-4eb4-b839-271ba4f65a27)]
[scriptable, builtinclass, uuid(e0d308ea-b804-4962-918a-28ec0aa4e42b)]
interface nsIIDBObjectStore : nsISupports
{
readonly attribute DOMString name;
readonly attribute DOMString keyPath;
[implicit_jscontext]
readonly attribute jsval keyPath;
readonly attribute nsIDOMDOMStringList indexNames;
@ -111,7 +112,7 @@ interface nsIIDBObjectStore : nsISupports
[implicit_jscontext]
nsIIDBIndex
createIndex([Null(Stringify)] in DOMString name,
[Null(Stringify)] in DOMString keyPath,
in jsval keyPath,
[optional /* none */] in jsval options);
// Returns object immediately

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

@ -85,6 +85,36 @@ ExpectError.prototype = {
}
};
function compareKeys(k1, k2) {
let t = typeof k1;
if (t != typeof k2)
return false;
if (t !== "object")
return k1 === k2;
if (k1 instanceof Date) {
return (k2 instanceof Date) &&
k1.getTime() === k2.getTime();
}
if (k1 instanceof Array) {
if (!(k2 instanceof Array) ||
k1.length != k2.length)
return false;
for (let i = 0; i < k1.length; ++i) {
if (!compareKeys(k1[i], k2[i]))
return false;
}
return true;
}
return false;
}
function addPermission(type, allow, url)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");

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

@ -48,6 +48,26 @@
{ keyPath: "foo.2.bar", exception: true },
{ keyPath: "foo. .bar", exception: true },
{ keyPath: ".bar", exception: true },
{ keyPath: ["foo", "bar"], value: { foo: 1, bar: 2 }, key: [1, 2] },
{ keyPath: ["foo"], value: { foo: 1, bar: 2 }, key: [1] },
{ keyPath: ["foo", "bar", "bar"], value: { foo: 1, bar: "x" }, key: [1, "x", "x"] },
{ keyPath: ["x", "y"], value: { x: [], y: "x" }, key: [[], "x"] },
{ keyPath: ["x", "y"], value: { x: [[1]], y: "x" }, key: [[[1]], "x"] },
{ keyPath: ["x", "y"], value: { x: [[1]], y: new Date(1) }, key: [[[1]], new Date(1)] },
{ keyPath: ["x", "y"], value: { x: [[1]], y: [new Date(3)] }, key: [[[1]], [new Date(3)]] },
{ keyPath: ["x", "y.bar"], value: { x: "hi", y: { bar: "x"} }, key: ["hi", "x"] },
{ keyPath: ["x.y", "y.bar"], value: { x: { y: "hello" }, y: { bar: "nurse"} }, key: ["hello", "nurse"] },
{ keyPath: ["", ""], value: 5, key: [5, 5] },
{ keyPath: ["x", "y"], value: { x: 1 } },
{ keyPath: ["x", "y"], value: { y: 1 } },
{ keyPath: ["x", "y"], value: { x: 1, y: undefined } },
{ keyPath: ["x", "y"], value: { x: null, y: 1 } },
{ keyPath: ["x", "y.bar"], value: { x: null, y: { bar: "x"} } },
{ keyPath: ["x", "y"], value: { x: 1, y: false } },
{ keyPath: ["x", "y", "z"], value: { x: 1, y: false, z: "a" } },
{ keyPath: [".x", "y", "z"], exception: true },
{ keyPath: ["x", "y ", "z"], exception: true },
];
let openRequest = mozIndexedDB.open(name, 1);
@ -62,13 +82,16 @@
// Test creating object stores and inserting data
for (let i = 0; i < keyPaths.length; i++) {
let info = keyPaths[i];
let test = " for objectStore test " + JSON.stringify(info);
if (!stores[info.keyPath]) {
let indexName = JSON.stringify(info.keyPath);
if (!stores[indexName]) {
try {
let objectStore = db.createObjectStore(info.keyPath, { keyPath: info.keyPath });
let objectStore = db.createObjectStore(indexName, { keyPath: info.keyPath });
ok(!("exception" in info), "shouldn't throw" + test);
is(objectStore.keyPath, info.keyPath, "correct keyPath property" + test);
stores[info.keyPath] = objectStore;
is(JSON.stringify(objectStore.keyPath), JSON.stringify(info.keyPath),
"correct keyPath property" + test);
stores[indexName] = objectStore;
} catch (e) {
ok("exception" in info, "should throw" + test);
ok(e instanceof DOMException, "Got a DOM Exception" + test);
@ -77,7 +100,7 @@
}
}
let store = stores[info.keyPath];
let store = stores[indexName];
try {
request = store.add(info.value);
@ -95,7 +118,12 @@
let e = yield;
is(e.type, "success", "inserted successfully" + test);
is(e.target, request, "expected target" + test);
is(request.result, info.key, "found correct key" + test);
ok(compareKeys(request.result, info.key), "found correct key" + test);
is(mozIndexedDB.cmp(request.result, info.key), 0, "returned key compares correctly" + test);
store.get(info.key).onsuccess = grabEventAndContinueHandler;
e = yield;
isnot(e.target.result, undefined, "Did find entry");
store.clear().onsuccess = grabEventAndContinueHandler;
yield;
@ -107,12 +135,14 @@
for (let i = 0; i < keyPaths.length; i++) {
let test = " for index test " + JSON.stringify(info);
let info = keyPaths[i];
if (!indexes[info.keyPath]) {
let indexName = JSON.stringify(info.keyPath);
if (!indexes[indexName]) {
try {
let index = store.createIndex(info.keyPath, info.keyPath);
let index = store.createIndex(indexName, info.keyPath);
ok(!("exception" in info), "shouldn't throw" + test);
is(index.keyPath, info.keyPath, "index has correct keyPath property" + test);
indexes[info.keyPath] = index;
is(JSON.stringify(index.keyPath), JSON.stringify(info.keyPath),
"index has correct keyPath property" + test);
indexes[indexName] = index;
} catch (e) {
ok("exception" in info, "should throw" + test);
ok(e instanceof DOMException, "Got a DOM Exception" + test);
@ -121,7 +151,7 @@
}
}
let index = indexes[info.keyPath];
let index = indexes[indexName];
request = store.add(info.value, 1);
if ("key" in info) {

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

@ -50,6 +50,22 @@
ok(true, "createIndex with no keyPath should throw");
}
try {
request = objectStore.createIndex("Hola", ["foo"], { multiEntry: true });
ok(false, "createIndex with array keyPath and multiEntry should throw");
}
catch(e) {
ok(true, "createIndex with array keyPath and multiEntry should throw");
}
try {
request = objectStore.createIndex("Hola", []);
ok(false, "createIndex with empty array keyPath should throw");
}
catch(e) {
ok(true, "createIndex with empty array keyPath should throw");
}
try {
request = objectStore.createIndex("foo", "bar", 10);
ok(false, "createIndex with bad options should throw");

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

@ -52,6 +52,14 @@
ok(true, "createObjectStore with bad options");
}
try {
db.createObjectStore("foo", { keyPath: ["foo"], autoIncrement: true });
ok(false, "createObjectStore with keyPath-array and autoIncrement should throw");
}
catch(e) {
ok(true, "createObjectStore with keyPath-array and autoIncrement should throw");
}
ok(db.createObjectStore("foo", { foo: "" }),
"createObjectStore with unknown options should not throw");
db.deleteObjectStore("foo");

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

@ -11,35 +11,6 @@
<script type="text/javascript;version=1.7">
function compareKeys(k1, k2) {
let t = typeof k1;
if (t != typeof k2)
return false;
if (t !== "object")
return k1 === k2;
if (k1 instanceof Date) {
return (k2 instanceof Date) &&
k1.getTime() === k2.getTime();
}
if (k1 instanceof Array) {
if (!(k2 instanceof Array) ||
k1.length != k2.length)
return false;
for (let i = 0; i < k1.length; ++i) {
if (!compareKeys(k1[i], k2[i]))
return false;
}
return true;
}
return false;
}
function testSteps()
{
const dbname = window.location.pathname;