diff options
Diffstat (limited to 'WebCore/storage')
-rw-r--r-- | WebCore/storage/DatabaseAuthorizer.cpp | 4 | ||||
-rw-r--r-- | WebCore/storage/IDBAny.cpp | 11 | ||||
-rw-r--r-- | WebCore/storage/IDBAny.h | 14 | ||||
-rw-r--r-- | WebCore/storage/IDBCursor.cpp | 13 | ||||
-rw-r--r-- | WebCore/storage/IDBCursor.h | 24 | ||||
-rw-r--r-- | WebCore/storage/IDBCursor.idl | 2 | ||||
-rw-r--r-- | WebCore/storage/IDBDatabaseBackendImpl.cpp | 2 | ||||
-rw-r--r-- | WebCore/storage/IDBFactoryBackendImpl.cpp | 17 | ||||
-rw-r--r-- | WebCore/storage/IDBIndex.idl | 9 | ||||
-rw-r--r-- | WebCore/storage/IDBIndexBackendImpl.cpp | 27 | ||||
-rw-r--r-- | WebCore/storage/IDBIndexBackendImpl.h | 4 | ||||
-rw-r--r-- | WebCore/storage/IDBKey.cpp | 58 | ||||
-rw-r--r-- | WebCore/storage/IDBKey.h | 5 | ||||
-rw-r--r-- | WebCore/storage/IDBKeyRange.idl | 9 | ||||
-rw-r--r-- | WebCore/storage/IDBObjectStore.idl | 4 | ||||
-rwxr-xr-x | WebCore/storage/IDBObjectStoreBackendImpl.cpp | 202 | ||||
-rw-r--r-- | WebCore/storage/IDBObjectStoreBackendImpl.h | 3 | ||||
-rw-r--r-- | WebCore/storage/IDBRequest.cpp | 78 | ||||
-rw-r--r-- | WebCore/storage/IDBRequest.h | 15 | ||||
-rw-r--r-- | WebCore/storage/IDBRequest.idl | 5 | ||||
-rw-r--r-- | WebCore/storage/IDBTransaction.h | 3 | ||||
-rw-r--r-- | WebCore/storage/IDBTransaction.idl | 2 |
22 files changed, 332 insertions, 179 deletions
diff --git a/WebCore/storage/DatabaseAuthorizer.cpp b/WebCore/storage/DatabaseAuthorizer.cpp index 79e47d4..e287dee 100644 --- a/WebCore/storage/DatabaseAuthorizer.cpp +++ b/WebCore/storage/DatabaseAuthorizer.cpp @@ -29,6 +29,8 @@ #include "config.h" #include "DatabaseAuthorizer.h" +#if ENABLE(DATABASE) + #include "PlatformString.h" #include <wtf/PassRefPtr.h> @@ -419,3 +421,5 @@ int DatabaseAuthorizer::updateDeletesBasedOnTableName(const String& tableName) } } // namespace WebCore + +#endif diff --git a/WebCore/storage/IDBAny.cpp b/WebCore/storage/IDBAny.cpp index 93d2633..7303fd6 100644 --- a/WebCore/storage/IDBAny.cpp +++ b/WebCore/storage/IDBAny.cpp @@ -37,11 +37,18 @@ namespace WebCore { -PassRefPtr<IDBAny> IDBAny::create() +PassRefPtr<IDBAny> IDBAny::createInvalid() { return adoptRef(new IDBAny()); } +PassRefPtr<IDBAny> IDBAny::createNull() +{ + RefPtr<IDBAny> idbAny = adoptRef(new IDBAny()); + idbAny->setNull(); + return idbAny.release(); +} + IDBAny::IDBAny() : m_type(UndefinedType) { @@ -93,7 +100,7 @@ PassRefPtr<SerializedScriptValue> IDBAny::serializedScriptValue() return m_serializedScriptValue; } -void IDBAny::set() +void IDBAny::setNull() { ASSERT(m_type == UndefinedType); m_type = NullType; diff --git a/WebCore/storage/IDBAny.h b/WebCore/storage/IDBAny.h index 950660a..9066e13 100644 --- a/WebCore/storage/IDBAny.h +++ b/WebCore/storage/IDBAny.h @@ -44,11 +44,19 @@ class SerializedScriptValue; class IDBAny : public RefCounted<IDBAny> { public: - static PassRefPtr<IDBAny> create(); + static PassRefPtr<IDBAny> createInvalid(); + static PassRefPtr<IDBAny> createNull(); template<typename T> static PassRefPtr<IDBAny> create(T* idbObject) { - RefPtr<IDBAny> any = IDBAny::create(); + RefPtr<IDBAny> any = IDBAny::createInvalid(); + any->set(idbObject); + return any.release(); + } + template<typename T> + static PassRefPtr<IDBAny> create(PassRefPtr<T> idbObject) + { + RefPtr<IDBAny> any = IDBAny::createInvalid(); any->set(idbObject); return any.release(); } @@ -77,7 +85,7 @@ public: PassRefPtr<SerializedScriptValue> serializedScriptValue(); // Set can only be called once. - void set(); // For "null". + void setNull(); void set(PassRefPtr<IDBCursor>); void set(PassRefPtr<IDBDatabase>); void set(PassRefPtr<IDBFactory>); diff --git a/WebCore/storage/IDBCursor.cpp b/WebCore/storage/IDBCursor.cpp index ae0d127..52945f8 100644 --- a/WebCore/storage/IDBCursor.cpp +++ b/WebCore/storage/IDBCursor.cpp @@ -38,8 +38,9 @@ namespace WebCore { -IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend) +IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request) : m_backend(backend) + , m_request(request) { } @@ -69,11 +70,13 @@ PassRefPtr<IDBRequest> IDBCursor::update(ScriptExecutionContext* context, PassRe return request.release(); } -PassRefPtr<IDBRequest> IDBCursor::continueFunction(ScriptExecutionContext* context, PassRefPtr<IDBKey> key) +void IDBCursor::continueFunction(PassRefPtr<IDBKey> key) { - RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this)); - m_backend->continueFunction(key, request); - return request.release(); + // FIXME: We're not using the context from when continue was called, which means the callback + // will be on the original context openCursor was called on. Is this right? + if (m_request->resetReadyState()) + m_backend->continueFunction(key, m_request); + // FIXME: Else throw? } PassRefPtr<IDBRequest> IDBCursor::remove(ScriptExecutionContext* context) diff --git a/WebCore/storage/IDBCursor.h b/WebCore/storage/IDBCursor.h index ccce001..0c438c4 100644 --- a/WebCore/storage/IDBCursor.h +++ b/WebCore/storage/IDBCursor.h @@ -31,7 +31,6 @@ #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> -#include <wtf/Threading.h> namespace WebCore { @@ -43,7 +42,7 @@ class IDBRequest; class ScriptExecutionContext; class SerializedScriptValue; -class IDBCursor : public ThreadSafeShared<IDBCursor> { +class IDBCursor : public RefCounted<IDBCursor> { public: enum Direction { NEXT = 0, @@ -51,24 +50,25 @@ public: PREV = 2, PREV_NO_DUPLICATE = 3, }; - static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackendInterface> backend) + static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request) { - return adoptRef(new IDBCursor(backend)); + return adoptRef(new IDBCursor(backend, request)); } - virtual ~IDBCursor(); + ~IDBCursor(); // Implement the IDL - virtual unsigned short direction() const; - virtual PassRefPtr<IDBKey> key() const; - virtual PassRefPtr<IDBAny> value() const; - virtual PassRefPtr<IDBRequest> update(ScriptExecutionContext*, PassRefPtr<SerializedScriptValue>); - virtual PassRefPtr<IDBRequest> continueFunction(ScriptExecutionContext*, PassRefPtr<IDBKey> = 0); - virtual PassRefPtr<IDBRequest> remove(ScriptExecutionContext*); + unsigned short direction() const; + PassRefPtr<IDBKey> key() const; + PassRefPtr<IDBAny> value() const; + PassRefPtr<IDBRequest> update(ScriptExecutionContext*, PassRefPtr<SerializedScriptValue>); + void continueFunction(PassRefPtr<IDBKey> = 0); + PassRefPtr<IDBRequest> remove(ScriptExecutionContext*); private: - explicit IDBCursor(PassRefPtr<IDBCursorBackendInterface>); + explicit IDBCursor(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*); RefPtr<IDBCursorBackendInterface> m_backend; + RefPtr<IDBRequest> m_request; }; } // namespace WebCore diff --git a/WebCore/storage/IDBCursor.idl b/WebCore/storage/IDBCursor.idl index 3702ef3..232842b 100644 --- a/WebCore/storage/IDBCursor.idl +++ b/WebCore/storage/IDBCursor.idl @@ -38,7 +38,7 @@ module storage { readonly attribute IDBAny value; [CallWith=ScriptExecutionContext] IDBRequest update(in SerializedScriptValue value); - [CallWith=ScriptExecutionContext, ImplementationFunction=continueFunction] IDBRequest continue(in [Optional] IDBKey key); + [ImplementationFunction=continueFunction] void continue(in [Optional] IDBKey key); [CallWith=ScriptExecutionContext] IDBRequest remove(); }; } diff --git a/WebCore/storage/IDBDatabaseBackendImpl.cpp b/WebCore/storage/IDBDatabaseBackendImpl.cpp index f4f2934..021f70a 100644 --- a/WebCore/storage/IDBDatabaseBackendImpl.cpp +++ b/WebCore/storage/IDBDatabaseBackendImpl.cpp @@ -177,8 +177,8 @@ void IDBDatabaseBackendImpl::removeObjectStore(const String& name, PassRefPtr<ID transaction.begin(); doDelete(sqliteDatabase(), "DELETE FROM ObjectStores WHERE id = ?", objectStore->id()); doDelete(sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStore->id()); + doDelete(sqliteDatabase(), "DELETE FROM IndexData WHERE indexId IN (SELECT id FROM Indexes WHERE objectStoreId = ?)", objectStore->id()); doDelete(sqliteDatabase(), "DELETE FROM Indexes WHERE objectStoreId = ?", objectStore->id()); - // FIXME: Delete index data as well. transaction.commit(); m_objectStores.remove(name); diff --git a/WebCore/storage/IDBFactoryBackendImpl.cpp b/WebCore/storage/IDBFactoryBackendImpl.cpp index d656128..50cf778 100644 --- a/WebCore/storage/IDBFactoryBackendImpl.cpp +++ b/WebCore/storage/IDBFactoryBackendImpl.cpp @@ -83,19 +83,28 @@ static bool createTables(SQLiteDatabase* sqliteDatabase) "CREATE TABLE IF NOT EXISTS MetaData (id INTEGER PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL, version TEXT NOT NULL)", "DROP TABLE IF EXISTS ObjectStores", - "CREATE TABLE IF NOT EXISTS ObjectStores (id INTEGER PRIMARY KEY, name TEXT NOT NULL, keyPath TEXT, doAutoIncrement INTEGER NOT NULL)", + "CREATE TABLE IF NOT EXISTS ObjectStores (id INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE, keyPath TEXT, doAutoIncrement INTEGER NOT NULL)", "DROP INDEX IF EXISTS ObjectStores_name", "CREATE UNIQUE INDEX IF NOT EXISTS ObjectStores_name ON ObjectStores(name)", "DROP TABLE IF EXISTS Indexes", - "CREATE TABLE IF NOT EXISTS Indexes (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), name TEXT NOT NULL, keyPath TEXT, isUnique INTEGER NOT NULL)", + "CREATE TABLE IF NOT EXISTS Indexes (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), name TEXT NOT NULL UNIQUE, keyPath TEXT, isUnique INTEGER NOT NULL)", "DROP INDEX IF EXISTS Indexes_composit", "CREATE UNIQUE INDEX IF NOT EXISTS Indexes_composit ON Indexes(objectStoreId, name)", "DROP TABLE IF EXISTS ObjectStoreData", - "CREATE TABLE IF NOT EXISTS ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, value TEXT NOT NULL)", + "CREATE TABLE IF NOT EXISTS ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT UNIQUE, keyDate INTEGER UNIQUE, keyNumber INTEGER UNIQUE, value TEXT NOT NULL)", "DROP INDEX IF EXISTS ObjectStoreData_composit", - "CREATE UNIQUE INDEX IF NOT EXISTS ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)" + "CREATE UNIQUE INDEX IF NOT EXISTS ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)", + + "DROP TABLE IF EXISTS IndexData", + "CREATE TABLE IF NOT EXISTS IndexData (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexs(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, objectStoreDataId INTEGER NOT NULL UNIQUE REFERENCES ObjectStoreData(id))", + "DROP INDEX IF EXISTS IndexData_composit", + "CREATE UNIQUE INDEX IF NOT EXISTS IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)", + "DROP INDEX IF EXISTS IndexData_objectStoreDataId", + "CREATE UNIQUE INDEX IF NOT EXISTS IndexData_objectStoreDataId ON IndexData(objectStoreDataId)", + "DROP INDEX IF EXISTS IndexData_indexId", + "CREATE UNIQUE INDEX IF NOT EXISTS IndexData_indexId ON IndexData(indexId)" }; for (size_t i = 0; i < arraysize(commands); ++i) { diff --git a/WebCore/storage/IDBIndex.idl b/WebCore/storage/IDBIndex.idl index e796b03..629481e 100644 --- a/WebCore/storage/IDBIndex.idl +++ b/WebCore/storage/IDBIndex.idl @@ -28,11 +28,16 @@ module storage { interface [ Conditional=INDEXED_DATABASE ] IDBIndex { - // FIXME: Complete this file. - readonly attribute DOMString name; + // FIXME: Add "storeName". readonly attribute DOMString keyPath; readonly attribute boolean unique; + + // FIXME: Implement. + //IDBRequest openObjectCursor(in [Optional] IDBKeyRange range, in [Optional] unsigned short direction) raises (DOMException); + //IDBRequest openCursor(in [Optional] IDBKeyRange range, in [Optional] unsigned short direction) raises (DOMException); + //IDBRequest getObject(in IDBKey key) raises (DOMException); + //IDBRequest get(in IDBKey key) raises (DOMException); }; } diff --git a/WebCore/storage/IDBIndexBackendImpl.cpp b/WebCore/storage/IDBIndexBackendImpl.cpp index 180ff1d..42aa62b 100644 --- a/WebCore/storage/IDBIndexBackendImpl.cpp +++ b/WebCore/storage/IDBIndexBackendImpl.cpp @@ -30,7 +30,7 @@ #include "IDBDatabaseBackendImpl.h" #include "IDBObjectStoreBackendImpl.h" -#include "SQLiteDatabase.h" +#include "SQLiteStatement.h" namespace WebCore { @@ -47,6 +47,31 @@ IDBIndexBackendImpl::~IDBIndexBackendImpl() { } +static String whereClause(IDBKey* key) +{ + return "WHERE indexId = ? AND " + key->whereSyntax(); +} + +static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key) +{ + query.bindInt64(1, id); + key->bind(query, 2); +} + +bool IDBIndexBackendImpl::addingKeyAllowed(IDBKey* key) +{ + if (!m_unique) + return true; + + SQLiteStatement query(sqliteDatabase(), "SELECT id FROM IndexData " + whereClause(key)); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + bindWhereClause(query, m_id, key); + bool existingValue = query.step() == SQLResultRow; + + return !existingValue; +} + SQLiteDatabase& IDBIndexBackendImpl::sqliteDatabase() const { return m_objectStore->database()->sqliteDatabase(); diff --git a/WebCore/storage/IDBIndexBackendImpl.h b/WebCore/storage/IDBIndexBackendImpl.h index acf6b1f..97c65f9 100644 --- a/WebCore/storage/IDBIndexBackendImpl.h +++ b/WebCore/storage/IDBIndexBackendImpl.h @@ -32,6 +32,7 @@ namespace WebCore { +class IDBKey; class IDBObjectStoreBackendImpl; class SQLiteDatabase; @@ -43,6 +44,9 @@ public: } virtual ~IDBIndexBackendImpl(); + int64_t id() { return m_id; } + bool addingKeyAllowed(IDBKey*); + // Implements IDBIndexBackendInterface. virtual String name() { return m_name; } virtual String keyPath() { return m_keyPath; } diff --git a/WebCore/storage/IDBKey.cpp b/WebCore/storage/IDBKey.cpp index 50c4c46..6ae79ba 100644 --- a/WebCore/storage/IDBKey.cpp +++ b/WebCore/storage/IDBKey.cpp @@ -28,6 +28,7 @@ #if ENABLE(INDEXED_DATABASE) +#include "SQLiteStatement.h" #include "SerializedScriptValue.h" namespace WebCore { @@ -72,6 +73,63 @@ bool IDBKey::isEqual(IDBKey* other) return false; } +String IDBKey::whereSyntax() const +{ + switch (m_type) { + case IDBKey::StringType: + return "keyString = ?"; + case IDBKey::NumberType: + return "keyNumber = ?"; + // FIXME: Implement date. + case IDBKey::NullType: + return "keyString IS NULL AND keyDate IS NULL AND keyNumber IS NULL"; + } + + ASSERT_NOT_REACHED(); + return ""; +} + +// Returns the number of items bound. +int IDBKey::bind(SQLiteStatement& query, int column) const +{ + switch (m_type) { + case IDBKey::StringType: + query.bindText(column, m_string); + return 1; + case IDBKey::NumberType: + query.bindInt(column, m_number); + return 1; + case IDBKey::NullType: + return 0; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +void IDBKey::bindWithNulls(SQLiteStatement& query, int baseColumn) const +{ + switch (m_type) { + case IDBKey::StringType: + query.bindText(baseColumn + 0, m_string); + query.bindNull(baseColumn + 1); + query.bindNull(baseColumn + 2); + break; + case IDBKey::NumberType: + query.bindNull(baseColumn + 0); + query.bindNull(baseColumn + 1); + query.bindInt(baseColumn + 2, m_number); + break; + case IDBKey::NullType: + query.bindNull(baseColumn + 0); + query.bindNull(baseColumn + 1); + query.bindNull(baseColumn + 2); + break; + default: + ASSERT_NOT_REACHED(); + } +} + } // namespace WebCore #endif diff --git a/WebCore/storage/IDBKey.h b/WebCore/storage/IDBKey.h index 228b4a4..b9a0be7 100644 --- a/WebCore/storage/IDBKey.h +++ b/WebCore/storage/IDBKey.h @@ -33,6 +33,8 @@ namespace WebCore { +class SQLiteStatement; + // FIXME: Add dates. class IDBKey : public RefCounted<IDBKey> { public: @@ -72,6 +74,9 @@ public: } bool isEqual(IDBKey* other); + String whereSyntax() const; + int bind(SQLiteStatement& query, int column) const; + void bindWithNulls(SQLiteStatement& query, int baseColumn) const; private: IDBKey(); diff --git a/WebCore/storage/IDBKeyRange.idl b/WebCore/storage/IDBKeyRange.idl index 6daaec1..8e4d61f 100644 --- a/WebCore/storage/IDBKeyRange.idl +++ b/WebCore/storage/IDBKeyRange.idl @@ -39,10 +39,11 @@ module storage { readonly attribute IDBKey right; readonly attribute unsigned short flags; - IDBKeyRange only(in IDBKey value); - IDBKeyRange leftBound(in IDBKey bound, in [Optional] boolean open); - IDBKeyRange rightBound(in IDBKey bound, in [Optional] boolean open); - IDBKeyRange bound(in IDBKey left, in IDBKey right, in [Optional] boolean openLeft, in [Optional] boolean openRight); + // FIXME: Make ClassMethod work for JSC as well. + [ClassMethod] IDBKeyRange only(in IDBKey value); + [ClassMethod] IDBKeyRange leftBound(in IDBKey bound, in [Optional] boolean open); + [ClassMethod] IDBKeyRange rightBound(in IDBKey bound, in [Optional] boolean open); + [ClassMethod] IDBKeyRange bound(in IDBKey left, in IDBKey right, in [Optional] boolean openLeft, in [Optional] boolean openRight); }; } diff --git a/WebCore/storage/IDBObjectStore.idl b/WebCore/storage/IDBObjectStore.idl index 1649a1d..ccd0311 100644 --- a/WebCore/storage/IDBObjectStore.idl +++ b/WebCore/storage/IDBObjectStore.idl @@ -28,10 +28,8 @@ module storage { interface [ Conditional=INDEXED_DATABASE ] IDBObjectStore { + // FIXME: Many of these should raise on certain errors. [CallWith=ScriptExecutionContext] IDBRequest get(in IDBKey key); - // FIXME: Come to concensus re getAll. - // FIXME: SerializedScriptValue raises an exception if you pass in something that can't be serialized. - // We need to instead "raise" this error via an error callback. [CallWith=ScriptExecutionContext] IDBRequest add(in SerializedScriptValue value, in [Optional] IDBKey key); [CallWith=ScriptExecutionContext] IDBRequest put(in SerializedScriptValue value, in [Optional] IDBKey key); [CallWith=ScriptExecutionContext] IDBRequest remove(in IDBKey key); diff --git a/WebCore/storage/IDBObjectStoreBackendImpl.cpp b/WebCore/storage/IDBObjectStoreBackendImpl.cpp index aeb3ced..86237de 100755 --- a/WebCore/storage/IDBObjectStoreBackendImpl.cpp +++ b/WebCore/storage/IDBObjectStoreBackendImpl.cpp @@ -38,6 +38,7 @@ #include "IDBKeyRange.h" #include "SQLiteDatabase.h" #include "SQLiteStatement.h" +#include "SQLiteTransaction.h" #if ENABLE(INDEXED_DATABASE) @@ -65,50 +66,20 @@ PassRefPtr<DOMStringList> IDBObjectStoreBackendImpl::indexNames() const return indexNames.release(); } -static String whereClause(IDBKey::Type type) +static String whereClause(IDBKey* key) { - switch (type) { - case IDBKey::StringType: - return "WHERE objectStoreId = ? AND keyString = ?"; - case IDBKey::NumberType: - return "WHERE objectStoreId = ? AND keyNumber = ?"; - // FIXME: Implement date. - case IDBKey::NullType: - return "WHERE objectStoreId = ? AND keyString IS NULL AND keyDate IS NULL AND keyNumber IS NULL"; - } - - ASSERT_NOT_REACHED(); - return ""; -} - -// Returns number of items bound. -static int bindKey(SQLiteStatement& query, int column, IDBKey* key) -{ - switch (key->type()) { - case IDBKey::StringType: - query.bindText(column, key->string()); - return 1; - case IDBKey::NumberType: - query.bindInt(column, key->number()); - return 1; - // FIXME: Implement date. - case IDBKey::NullType: - return 0; - } - - ASSERT_NOT_REACHED(); - return 0; + return "WHERE objectStoreId = ? AND " + key->whereSyntax(); } static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key) { query.bindInt64(1, id); - bindKey(query, 2, key); + key->bind(query, 2); } void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) { - SQLiteStatement query(sqliteDatabase(), "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData " + whereClause(key->type())); + SQLiteStatement query(sqliteDatabase(), "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData " + whereClause(key.get())); bool ok = query.prepare() == SQLResultOk; ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? @@ -126,6 +97,59 @@ void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallba ASSERT(query.step() != SQLResultRow); } +static PassRefPtr<IDBKey> fetchKeyFromKeyPath(SerializedScriptValue* value, const String& keyPath) +{ + Vector<RefPtr<SerializedScriptValue> > values; + values.append(value); + Vector<RefPtr<IDBKey> > keys; + IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(values, keyPath, keys); + if (keys.isEmpty()) + return 0; + ASSERT(keys.size() == 1); + return keys[0].release(); +} + +static void putObjectStoreData(SQLiteDatabase& db, IDBKey* key, SerializedScriptValue* value, int64_t objectStoreId, int64_t* dataRowId) +{ + String sql = *dataRowId != -1 ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?" + : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)"; + SQLiteStatement query(db, sql); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + key->bindWithNulls(query, 1); + query.bindText(4, value->toWireString()); + if (*dataRowId != -1) + query.bindInt(5, *dataRowId); + else + query.bindInt64(5, objectStoreId); + + ok = query.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + if (*dataRowId == -1) + *dataRowId = db.lastInsertRowID(); +} + +static void putIndexData(SQLiteDatabase& db, IDBKey* key, int64_t indexId, int64_t objectStoreDataId) +{ + SQLiteStatement deleteQuery(db, "DELETE FROM IndexData WHERE objectStoreDataId = ?"); + bool ok = deleteQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + deleteQuery.bindInt64(1, objectStoreDataId); + ok = deleteQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + + SQLiteStatement putQuery(db, "INSERT INTO IndexData (keyString, keyDate, keyNumber, indexId, objectStoreDataId) VALUES (?, ?, ?, ?, ?)"); + ok = putQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + key->bindWithNulls(putQuery, 1); + putQuery.bindInt64(4, indexId); + putQuery.bindInt64(5, objectStoreDataId); + + ok = putQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? +} + void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, bool addOnly, PassRefPtr<IDBCallbacks> callbacks) { RefPtr<SerializedScriptValue> value = prpValue; @@ -136,73 +160,56 @@ void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "A key was supplied for an objectStore that has a keyPath.")); return; } - Vector<RefPtr<SerializedScriptValue> > values; - values.append(value); - Vector<RefPtr<IDBKey> > idbKeys; - IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(values, m_keyPath, idbKeys); - if (idbKeys.isEmpty()) { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "An invalid keyPath was supplied for an objectStore.")); + key = fetchKeyFromKeyPath(value.get(), m_keyPath); + if (!key) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "The key could not be fetched from the keyPath.")); return; } - key = idbKeys[0]; - } - - if (!key) { + } else if (!key) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "No key supplied.")); return; } - SQLiteStatement getQuery(sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key->type())); + Vector<RefPtr<IDBKey> > indexKeys; + for (IndexMap::iterator it = m_indexes.begin(); it != m_indexes.end(); ++it) { + RefPtr<IDBKey> key = fetchKeyFromKeyPath(value.get(), it->second->keyPath()); + if (!key) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "The key could not be fetched from an index's keyPath.")); + return; + } + if (!it->second->addingKeyAllowed(key.get())) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "One of the derived (from a keyPath) keys for an index does not satisfy its uniqueness requirements.")); + return; + } + indexKeys.append(key.release()); + } + + SQLiteStatement getQuery(sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key.get())); bool ok = getQuery.prepare() == SQLResultOk; ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? bindWhereClause(getQuery, m_id, key.get()); - bool existingValue = getQuery.step() == SQLResultRow; - if (addOnly && existingValue) { + bool isExistingValue = getQuery.step() == SQLResultRow; + if (addOnly && isExistingValue) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store.")); return; } - String sql = existingValue ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?" - : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)"; - SQLiteStatement putQuery(sqliteDatabase(), sql); - ok = putQuery.prepare() == SQLResultOk; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? - switch (key->type()) { - case IDBKey::StringType: - putQuery.bindText(1, key->string()); - putQuery.bindNull(2); - putQuery.bindNull(3); - break; - // FIXME: Implement date. - case IDBKey::NumberType: - putQuery.bindNull(1); - putQuery.bindNull(2); - putQuery.bindInt(3, key->number()); - break; - case IDBKey::NullType: - putQuery.bindNull(1); - putQuery.bindNull(2); - putQuery.bindNull(3); - break; - default: - ASSERT_NOT_REACHED(); - } - putQuery.bindText(4, value->toWireString()); - if (existingValue) - putQuery.bindInt(5, getQuery.getColumnInt(0)); - else - putQuery.bindInt64(5, m_id); + // Before this point, don't do any mutation. After this point, don't error out. - ok = putQuery.step() == SQLResultDone; - ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + int64_t dataRowId = isExistingValue ? getQuery.getColumnInt(0) : -1; + putObjectStoreData(sqliteDatabase(), key.get(), value.get(), m_id, &dataRowId); + + int i = 0; + for (IndexMap::iterator it = m_indexes.begin(); it != m_indexes.end(); ++it, ++i) + putIndexData(sqliteDatabase(), indexKeys[i].get(), it->second->id(), dataRowId); callbacks->onSuccess(key.get()); } void IDBObjectStoreBackendImpl::remove(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) { - SQLiteStatement query(sqliteDatabase(), "DELETE FROM ObjectStoreData " + whereClause(key->type())); + SQLiteStatement query(sqliteDatabase(), "DELETE FROM ObjectStoreData " + whereClause(key.get())); bool ok = query.prepare() == SQLResultOk; ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? @@ -233,10 +240,10 @@ void IDBObjectStoreBackendImpl::createIndex(const String& name, const String& ke ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. int64_t id = sqliteDatabase().lastInsertRowID(); - RefPtr<IDBIndexBackendInterface> index = IDBIndexBackendImpl::create(this, id, name, keyPath, unique); + RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(this, id, name, keyPath, unique); ASSERT(index->name() == name); m_indexes.set(name, index); - callbacks->onSuccess(index.release()); + callbacks->onSuccess(index.get()); } PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::index(const String& name) @@ -244,22 +251,29 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::index(const Stri return m_indexes.get(name); } -void IDBObjectStoreBackendImpl::removeIndex(const String& name, PassRefPtr<IDBCallbacks> callbacks) +static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id) { - if (!m_indexes.contains(name)) { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Index name does not exist.")); - return; - } - - SQLiteStatement deleteQuery(sqliteDatabase(), "DELETE FROM Indexes WHERE name = ? AND objectStoreId = ?"); + SQLiteStatement deleteQuery(db, sql); bool ok = deleteQuery.prepare() == SQLResultOk; ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. - deleteQuery.bindText(1, name); - deleteQuery.bindInt64(2, m_id); + deleteQuery.bindInt64(1, id); ok = deleteQuery.step() == SQLResultDone; ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. +} + +void IDBObjectStoreBackendImpl::removeIndex(const String& name, PassRefPtr<IDBCallbacks> callbacks) +{ + RefPtr<IDBIndexBackendImpl> index = m_indexes.get(name); + if (!index) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Index name does not exist.")); + return; + } - // FIXME: Delete index data as well. + SQLiteTransaction transaction(sqliteDatabase()); + transaction.begin(); + doDelete(sqliteDatabase(), "DELETE FROM Indexes WHERE id = ?", index->id()); + doDelete(sqliteDatabase(), "DELETE FROM IndexData WHERE indexId = ?", index->id()); + transaction.commit(); m_indexes.remove(name); callbacks->onSuccess(); @@ -337,9 +351,9 @@ void IDBObjectStoreBackendImpl::openCursor(PassRefPtr<IDBKeyRange> range, unsign int currentColumn = 1; if (range->flags() & IDBKeyRange::LEFT_BOUND || range->flags() == IDBKeyRange::SINGLE) - currentColumn += bindKey(*query, currentColumn, range->left().get()); + currentColumn += range->left()->bind(*query, currentColumn); if (range->flags() & IDBKeyRange::RIGHT_BOUND || range->flags() == IDBKeyRange::SINGLE) - currentColumn += bindKey(*query, currentColumn, range->right().get()); + currentColumn += range->right()->bind(*query, currentColumn); query->bindInt64(currentColumn, m_id); if (query->step() != SQLResultRow) { diff --git a/WebCore/storage/IDBObjectStoreBackendImpl.h b/WebCore/storage/IDBObjectStoreBackendImpl.h index e1058c8..32ef920 100644 --- a/WebCore/storage/IDBObjectStoreBackendImpl.h +++ b/WebCore/storage/IDBObjectStoreBackendImpl.h @@ -35,6 +35,7 @@ namespace WebCore { class IDBDatabaseBackendImpl; +class IDBIndexBackendImpl; class SQLiteDatabase; class IDBObjectStoreBackendImpl : public IDBObjectStoreBackendInterface { @@ -75,7 +76,7 @@ private: String m_keyPath; bool m_autoIncrement; - typedef HashMap<String, RefPtr<IDBIndexBackendInterface> > IndexMap; + typedef HashMap<String, RefPtr<IDBIndexBackendImpl> > IndexMap; IndexMap m_indexes; }; diff --git a/WebCore/storage/IDBRequest.cpp b/WebCore/storage/IDBRequest.cpp index 94ef7e5..3513c17 100644 --- a/WebCore/storage/IDBRequest.cpp +++ b/WebCore/storage/IDBRequest.cpp @@ -48,71 +48,72 @@ namespace WebCore { IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source) : ActiveDOMObject(context, this) , m_source(source) - , m_result(IDBAny::create()) , m_timer(this, &IDBRequest::timerFired) , m_aborted(false) - , m_readyState(INITIAL) + , m_readyState(LOADING) { } IDBRequest::~IDBRequest() { - if (m_readyState != DONE) - abort(); + abort(); +} + +bool IDBRequest::resetReadyState() +{ + if (m_aborted) + return false; + ASSERT(m_readyState == DONE); + m_readyState = LOADING; + return true; } void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error) { - onEventCommon(); - m_error = error; + scheduleEvent(0, error); } void IDBRequest::onSuccess() { - onEventCommon(); - m_result->set(); + scheduleEvent(IDBAny::createNull(), 0); } void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend) { - onEventCommon(); - m_result->set(IDBCursor::create(backend)); + scheduleEvent(IDBAny::create(IDBCursor::create(backend, this)), 0); } void IDBRequest::onSuccess(PassRefPtr<IDBDatabaseBackendInterface> backend) { - onEventCommon(); - m_result->set(IDBDatabase::create(backend)); + scheduleEvent(IDBAny::create(IDBDatabase::create(backend)), 0); } void IDBRequest::onSuccess(PassRefPtr<IDBIndexBackendInterface> backend) { - onEventCommon(); - m_result->set(IDBIndex::create(backend)); + scheduleEvent(IDBAny::create(IDBIndex::create(backend)), 0); } void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey) { - onEventCommon(); - m_result->set(idbKey); + scheduleEvent(IDBAny::create(idbKey), 0); } void IDBRequest::onSuccess(PassRefPtr<IDBObjectStoreBackendInterface> backend) { - onEventCommon(); - m_result->set(IDBObjectStore::create(backend)); + scheduleEvent(IDBAny::create(IDBObjectStore::create(backend)), 0); } void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptValue) { - onEventCommon(); - m_result->set(serializedScriptValue); + scheduleEvent(IDBAny::create(serializedScriptValue), 0); } void IDBRequest::abort() { m_timer.stop(); m_aborted = true; + m_pendingEvents.clear(); + // FIXME: This should cancel any pending work being done in the backend. } @@ -140,38 +141,47 @@ EventTargetData* IDBRequest::ensureEventTargetData() void IDBRequest::timerFired(Timer<IDBRequest>*) { - ASSERT(m_readyState == DONE); ASSERT(m_selfRef); ASSERT(!m_aborted); + ASSERT(m_pendingEvents.size()); // We need to keep self-referencing ourself, otherwise it's possible we'll be deleted. // But in some cases, suspend() could be called while we're dispatching an event, so we // need to make sure that resume() doesn't re-start the timer based on m_selfRef being set. RefPtr<IDBRequest> selfRef = m_selfRef.release(); - if (m_error) { - ASSERT(m_result->type() == IDBAny::UndefinedType); - dispatchEvent(IDBErrorEvent::create(m_source, *m_error)); - } else { - ASSERT(m_result->type() != IDBAny::UndefinedType); - dispatchEvent(IDBSuccessEvent::create(m_source, m_result)); + Vector<PendingEvent> pendingEvents; + pendingEvents.swap(m_pendingEvents); + for (size_t i = 0; i < pendingEvents.size(); ++i) { + PendingEvent pendingEvent = pendingEvents[i]; + if (pendingEvent.m_error) { + ASSERT(!pendingEvent.m_result); + dispatchEvent(IDBErrorEvent::create(m_source, *pendingEvent.m_error)); + } else { + ASSERT(pendingEvent.m_result->type() != IDBAny::UndefinedType); + dispatchEvent(IDBSuccessEvent::create(m_source, pendingEvent.m_result)); + } } } -void IDBRequest::onEventCommon() +void IDBRequest::scheduleEvent(PassRefPtr<IDBAny> result, PassRefPtr<IDBDatabaseError> error) { ASSERT(m_readyState < DONE); - ASSERT(m_result->type() == IDBAny::UndefinedType); - ASSERT(!m_error); - ASSERT(!m_selfRef); - ASSERT(!m_timer.isActive()); + ASSERT(!!m_selfRef == m_timer.isActive()); if (m_aborted) return; + PendingEvent pendingEvent; + pendingEvent.m_result = result; + pendingEvent.m_error = error; + m_pendingEvents.append(pendingEvent); + m_readyState = DONE; - m_selfRef = this; - m_timer.startOneShot(0); + if (!m_timer.isActive()) { + m_selfRef = this; + m_timer.startOneShot(0); + } } } // namespace WebCore diff --git a/WebCore/storage/IDBRequest.h b/WebCore/storage/IDBRequest.h index ddfdcf3..9b0ea7e 100644 --- a/WebCore/storage/IDBRequest.h +++ b/WebCore/storage/IDBRequest.h @@ -38,6 +38,7 @@ #include "IDBAny.h" #include "IDBCallbacks.h" #include "Timer.h" +#include <wtf/Vector.h> namespace WebCore { @@ -49,16 +50,15 @@ public: // Defined in the IDL void abort(); enum ReadyState { - INITIAL = 0, LOADING = 1, DONE = 2 }; unsigned short readyState() const { return m_readyState; } - PassRefPtr<IDBDatabaseError> error() const { return m_error; } - PassRefPtr<IDBAny> result() { return m_result; } DEFINE_ATTRIBUTE_EVENT_LISTENER(success); DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + bool resetReadyState(); + // IDBCallbacks virtual void onError(PassRefPtr<IDBDatabaseError>); virtual void onSuccess(); // For "null". @@ -83,7 +83,7 @@ private: IDBRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source); void timerFired(Timer<IDBRequest>*); - void onEventCommon(); + void scheduleEvent(PassRefPtr<IDBAny> result, PassRefPtr<IDBDatabaseError>); // EventTarget virtual void refEventTarget() { ref(); } @@ -93,8 +93,11 @@ private: RefPtr<IDBAny> m_source; - RefPtr<IDBAny> m_result; - RefPtr<IDBDatabaseError> m_error; + struct PendingEvent { + RefPtr<IDBAny> m_result; + RefPtr<IDBDatabaseError> m_error; + }; + Vector<PendingEvent> m_pendingEvents; // Used to fire events asynchronously. Timer<IDBRequest> m_timer; diff --git a/WebCore/storage/IDBRequest.idl b/WebCore/storage/IDBRequest.idl index 9d7e0e4..3036b6b 100644 --- a/WebCore/storage/IDBRequest.idl +++ b/WebCore/storage/IDBRequest.idl @@ -35,15 +35,10 @@ module storage { void abort(); // States - const unsigned short INITIAL = 0; const unsigned short LOADING = 1; const unsigned short DONE = 2; readonly attribute unsigned short readyState; - // Possible results - readonly attribute IDBDatabaseError error; - readonly attribute IDBAny result; - // Events attribute EventListener onsuccess; attribute EventListener onerror; diff --git a/WebCore/storage/IDBTransaction.h b/WebCore/storage/IDBTransaction.h index 3c7f9dd..0d6f3ea 100644 --- a/WebCore/storage/IDBTransaction.h +++ b/WebCore/storage/IDBTransaction.h @@ -54,7 +54,8 @@ public: enum Mode { READ_WRITE = 0, READ_ONLY = 1, - SNAPSHOT_READ = 2 + SNAPSHOT_READ = 2, + VERSION_CHANGE = 3 }; unsigned short mode() const; diff --git a/WebCore/storage/IDBTransaction.idl b/WebCore/storage/IDBTransaction.idl index a3907dc..cdf9f63 100644 --- a/WebCore/storage/IDBTransaction.idl +++ b/WebCore/storage/IDBTransaction.idl @@ -33,6 +33,8 @@ module storage { const unsigned short READ_WRITE = 0; const unsigned short READ_ONLY = 1; const unsigned short SNAPSHOT_READ = 2; + const unsigned short VERSION_CHANGE = 3; + // Properties readonly attribute unsigned short mode; readonly attribute IDBDatabase db; |