diff options
Diffstat (limited to 'WebCore/storage/IDBObjectStoreBackendImpl.cpp')
-rwxr-xr-x | WebCore/storage/IDBObjectStoreBackendImpl.cpp | 209 |
1 files changed, 187 insertions, 22 deletions
diff --git a/WebCore/storage/IDBObjectStoreBackendImpl.cpp b/WebCore/storage/IDBObjectStoreBackendImpl.cpp index 9732bc1..1b9b76b 100755 --- a/WebCore/storage/IDBObjectStoreBackendImpl.cpp +++ b/WebCore/storage/IDBObjectStoreBackendImpl.cpp @@ -30,10 +30,14 @@ #include "IDBBindingUtilities.h" #include "IDBCallbacks.h" #include "IDBCursorBackendImpl.h" +#include "IDBDatabaseBackendImpl.h" #include "IDBDatabaseException.h" #include "IDBIndexBackendImpl.h" +#include "IDBKeyPath.h" +#include "IDBKeyPathBackendImpl.h" #include "IDBKeyRange.h" -#include "IDBKeyTree.h" +#include "SQLiteDatabase.h" +#include "SQLiteStatement.h" #if ENABLE(INDEXED_DATABASE) @@ -43,12 +47,14 @@ IDBObjectStoreBackendImpl::~IDBObjectStoreBackendImpl() { } -IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(const String& name, const String& keyPath, bool autoIncrement) - : m_name(name) +IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBDatabaseBackendImpl* database, int64_t id, const String& name, const String& keyPath, bool autoIncrement) + : m_database(database) + , m_id(id) + , m_name(name) , m_keyPath(keyPath) , m_autoIncrement(autoIncrement) - , m_tree(Tree::create()) { + loadIndexes(); } PassRefPtr<DOMStringList> IDBObjectStoreBackendImpl::indexNames() const @@ -59,28 +65,86 @@ PassRefPtr<DOMStringList> IDBObjectStoreBackendImpl::indexNames() const return indexNames.release(); } +static String whereClause(IDBKey::Type type) +{ + 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; +} + +static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key) +{ + query.bindInt64(1, id); + bindKey(query, 2, key); +} + void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) { - RefPtr<SerializedScriptValue> value = m_tree->get(key.get()); - if (!value) { + SQLiteStatement query(sqliteDatabase(), "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData " + whereClause(key->type())); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + bindWhereClause(query, m_id, key.get()); + if (query.step() != SQLResultRow) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the object store.")); return; } - callbacks->onSuccess(value.get()); + + ASSERT((key->type() == IDBKey::StringType) != query.isColumnNull(0)); + // FIXME: Implement date. + ASSERT((key->type() == IDBKey::NumberType) != query.isColumnNull(2)); + + callbacks->onSuccess(SerializedScriptValue::createFromWire(query.getColumnText(3))); + ASSERT(query.step() != SQLResultRow); } -void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> prpKey, bool addOnly, PassRefPtr<IDBCallbacks> callbacks) +void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, bool addOnly, PassRefPtr<IDBCallbacks> callbacks) { + RefPtr<SerializedScriptValue> value = prpValue; RefPtr<IDBKey> key = prpKey; if (!m_keyPath.isNull()) { if (key) { - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "A key was supplied for an objectStore that has a keyPath.")); + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "A key was supplied for an objectStore that has a keyPath.")); return; } - ASSERT_NOT_REACHED(); - callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "FIXME: keyPath not yet supported.")); - 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.")); + return; + } + key = idbKeys[0]; } if (!key) { @@ -88,18 +152,66 @@ void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> value, Pas return; } - if (addOnly && m_tree->get(key.get())) { + SQLiteStatement getQuery(sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key->type())); + 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) { callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store.")); return; } - m_tree->put(key.get(), value.get()); + 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); + + ok = putQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + callbacks->onSuccess(key.get()); } void IDBObjectStoreBackendImpl::remove(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) { - m_tree->remove(key.get()); + SQLiteStatement query(sqliteDatabase(), "DELETE FROM ObjectStoreData " + whereClause(key->type())); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + bindWhereClause(query, m_id, key.get()); + if (query.step() != SQLResultDone) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the object store.")); + return; + } + callbacks->onSuccess(); } @@ -110,7 +222,18 @@ void IDBObjectStoreBackendImpl::createIndex(const String& name, const String& ke return; } - RefPtr<IDBIndexBackendInterface> index = IDBIndexBackendImpl::create(name, keyPath, unique); + SQLiteStatement insert(sqliteDatabase(), "INSERT INTO Indexes (objectStoreId, name, keyPath, isUnique) VALUES (?, ?, ?, ?)"); + bool ok = insert.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + insert.bindInt64(1, m_id); + insert.bindText(2, name); + insert.bindText(3, keyPath); + insert.bindInt(4, static_cast<int>(unique)); + ok = insert.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + int64_t id = sqliteDatabase().lastInsertRowID(); + + RefPtr<IDBIndexBackendInterface> index = IDBIndexBackendImpl::create(this, id, name, keyPath, unique); ASSERT(index->name() == name); m_indexes.set(name, index); callbacks->onSuccess(index.release()); @@ -128,19 +251,61 @@ void IDBObjectStoreBackendImpl::removeIndex(const String& name, PassRefPtr<IDBCa return; } + SQLiteStatement deleteQuery(sqliteDatabase(), "DELETE FROM Indexes WHERE name = ? AND objectStoreId = ?"); + bool ok = deleteQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + deleteQuery.bindText(1, name); + deleteQuery.bindInt64(2, m_id); + ok = deleteQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + + // FIXME: Delete index data as well. + m_indexes.remove(name); callbacks->onSuccess(); } void IDBObjectStoreBackendImpl::openCursor(PassRefPtr<IDBKeyRange> range, unsigned short direction, PassRefPtr<IDBCallbacks> callbacks) { + // FIXME: Fully implement. + RefPtr<IDBKey> key = range->left(); - RefPtr<SerializedScriptValue> value = m_tree->get(key.get()); - if (value) { - RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(this, range, static_cast<IDBCursor::Direction>(direction), key, value); - callbacks->onSuccess(cursor.release()); - } else - callbacks->onSuccess(); + SQLiteStatement query(sqliteDatabase(), "SELECT id, value FROM ObjectStoreData " + whereClause(key->type())); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + bindWhereClause(query, m_id, key.get()); + if (query.step() != SQLResultRow) { + callbacks->onSuccess(); + return; + } + + RefPtr<SerializedScriptValue> value = SerializedScriptValue::createFromWire(query.getColumnText(1)); + RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(this, range, static_cast<IDBCursor::Direction>(direction), key.release(), value.release()); + callbacks->onSuccess(cursor.release()); +} + +void IDBObjectStoreBackendImpl::loadIndexes() +{ + SQLiteStatement indexQuery(sqliteDatabase(), "SELECT id, name, keyPath, isUnique FROM Indexes WHERE objectStoreId = ?"); + bool ok = indexQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + indexQuery.bindInt64(1, m_id); + + while (indexQuery.step() == SQLResultRow) { + int64_t id = indexQuery.getColumnInt64(0); + String name = indexQuery.getColumnText(1); + String keyPath = indexQuery.getColumnText(2); + bool unique = !!indexQuery.getColumnInt(3); + + m_indexes.set(name, IDBIndexBackendImpl::create(this, id, name, keyPath, unique)); + } +} + +SQLiteDatabase& IDBObjectStoreBackendImpl::sqliteDatabase() const +{ + return m_database->sqliteDatabase(); } } // namespace WebCore |