summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/storage
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/storage')
-rw-r--r--Source/WebCore/storage/Database.cpp8
-rw-r--r--Source/WebCore/storage/IDBAny.cpp16
-rw-r--r--Source/WebCore/storage/IDBAny.h5
-rw-r--r--Source/WebCore/storage/IDBBackingStore.cpp996
-rw-r--r--Source/WebCore/storage/IDBBackingStore.h114
-rw-r--r--Source/WebCore/storage/IDBCallbacks.h2
-rw-r--r--Source/WebCore/storage/IDBCompleteEvent.h57
-rw-r--r--Source/WebCore/storage/IDBCursor.cpp29
-rw-r--r--Source/WebCore/storage/IDBCursor.h20
-rw-r--r--Source/WebCore/storage/IDBCursor.idl3
-rw-r--r--Source/WebCore/storage/IDBCursorBackendImpl.cpp101
-rw-r--r--Source/WebCore/storage/IDBCursorBackendImpl.h39
-rw-r--r--Source/WebCore/storage/IDBCursorBackendInterface.h10
-rw-r--r--Source/WebCore/storage/IDBCursorWithValue.cpp (renamed from Source/WebCore/storage/IDBErrorEvent.cpp)26
-rw-r--r--Source/WebCore/storage/IDBCursorWithValue.h (renamed from Source/WebCore/storage/IDBSQLiteDatabase.h)33
-rw-r--r--Source/WebCore/storage/IDBCursorWithValue.idl (renamed from Source/WebCore/storage/IDBEvent.idl)7
-rw-r--r--Source/WebCore/storage/IDBDatabase.cpp57
-rw-r--r--Source/WebCore/storage/IDBDatabase.h27
-rw-r--r--Source/WebCore/storage/IDBDatabase.idl5
-rw-r--r--Source/WebCore/storage/IDBDatabaseBackendImpl.cpp179
-rw-r--r--Source/WebCore/storage/IDBDatabaseBackendImpl.h26
-rw-r--r--Source/WebCore/storage/IDBDatabaseBackendInterface.h8
-rw-r--r--Source/WebCore/storage/IDBDatabaseCallbacks.h (renamed from Source/WebCore/storage/IDBSQLiteDatabase.cpp)27
-rw-r--r--Source/WebCore/storage/IDBDatabaseException.h4
-rw-r--r--Source/WebCore/storage/IDBDatabaseException.idl2
-rw-r--r--Source/WebCore/storage/IDBErrorEvent.h66
-rw-r--r--Source/WebCore/storage/IDBEvent.h62
-rw-r--r--Source/WebCore/storage/IDBEventDispatcher.cpp (renamed from Source/WebCore/storage/IDBEvent.cpp)54
-rw-r--r--Source/WebCore/storage/IDBEventDispatcher.h (renamed from Source/WebCore/storage/IDBAbortEvent.cpp)31
-rw-r--r--Source/WebCore/storage/IDBFactoryBackendImpl.cpp173
-rw-r--r--Source/WebCore/storage/IDBFactoryBackendImpl.h10
-rw-r--r--Source/WebCore/storage/IDBIndex.cpp23
-rw-r--r--Source/WebCore/storage/IDBIndex.h11
-rw-r--r--Source/WebCore/storage/IDBIndex.idl2
-rw-r--r--Source/WebCore/storage/IDBIndexBackendImpl.cpp127
-rw-r--r--Source/WebCore/storage/IDBIndexBackendImpl.h27
-rw-r--r--Source/WebCore/storage/IDBKey.cpp131
-rw-r--r--Source/WebCore/storage/IDBKey.h12
-rw-r--r--Source/WebCore/storage/IDBKeyPathBackendImpl.cpp6
-rw-r--r--Source/WebCore/storage/IDBKeyPathBackendImpl.h1
-rw-r--r--Source/WebCore/storage/IDBKeyRange.cpp22
-rw-r--r--Source/WebCore/storage/IDBKeyRange.h6
-rw-r--r--Source/WebCore/storage/IDBKeyRange.idl2
-rw-r--r--Source/WebCore/storage/IDBObjectStore.cpp37
-rw-r--r--Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp290
-rw-r--r--Source/WebCore/storage/IDBObjectStoreBackendImpl.h20
-rw-r--r--Source/WebCore/storage/IDBRequest.cpp185
-rw-r--r--Source/WebCore/storage/IDBRequest.h32
-rw-r--r--Source/WebCore/storage/IDBRequest.idl8
-rw-r--r--Source/WebCore/storage/IDBSuccessEvent.h62
-rw-r--r--Source/WebCore/storage/IDBTransaction.cpp29
-rw-r--r--Source/WebCore/storage/IDBTransaction.h5
-rw-r--r--Source/WebCore/storage/IDBTransactionBackendImpl.cpp4
-rw-r--r--Source/WebCore/storage/IDBTransactionBackendImpl.h4
-rw-r--r--Source/WebCore/storage/IDBTransactionBackendInterface.h2
-rw-r--r--Source/WebCore/storage/IDBTransactionCoordinator.cpp1
-rw-r--r--Source/WebCore/storage/IDBVersionChangeEvent.cpp (renamed from Source/WebCore/storage/IDBCompleteEvent.cpp)23
-rw-r--r--Source/WebCore/storage/IDBVersionChangeEvent.h (renamed from Source/WebCore/storage/IDBAbortEvent.h)29
-rw-r--r--Source/WebCore/storage/IDBVersionChangeEvent.idl (renamed from Source/WebCore/storage/IDBSuccessEvent.idl)9
-rw-r--r--Source/WebCore/storage/IDBVersionChangeRequest.cpp (renamed from Source/WebCore/storage/IDBSuccessEvent.cpp)28
-rw-r--r--Source/WebCore/storage/IDBVersionChangeRequest.h54
-rw-r--r--Source/WebCore/storage/IDBVersionChangeRequest.idl (renamed from Source/WebCore/storage/IDBErrorEvent.idl)13
-rw-r--r--Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp7
63 files changed, 2059 insertions, 1350 deletions
diff --git a/Source/WebCore/storage/Database.cpp b/Source/WebCore/storage/Database.cpp
index 75f616a..b072edb 100644
--- a/Source/WebCore/storage/Database.cpp
+++ b/Source/WebCore/storage/Database.cpp
@@ -258,8 +258,10 @@ void Database::changeVersion(const String& oldVersion, const String& newVersion,
PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
PassRefPtr<VoidCallback> successCallback)
{
- m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion)));
+ RefPtr<SQLTransaction> transaction =
+ SQLTransaction::create(this, callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion));
MutexLocker locker(m_transactionInProgressMutex);
+ m_transactionQueue.append(transaction.release());
if (!m_transactionInProgress)
scheduleTransaction();
}
@@ -277,8 +279,10 @@ void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, Pass
void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
PassRefPtr<VoidCallback> successCallback, bool readOnly)
{
- m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, 0, readOnly));
+ RefPtr<SQLTransaction> transaction =
+ SQLTransaction::create(this, callback, errorCallback, successCallback, 0, readOnly);
MutexLocker locker(m_transactionInProgressMutex);
+ m_transactionQueue.append(transaction.release());
if (!m_transactionInProgress)
scheduleTransaction();
}
diff --git a/Source/WebCore/storage/IDBAny.cpp b/Source/WebCore/storage/IDBAny.cpp
index 3a049c0..1e076a9 100644
--- a/Source/WebCore/storage/IDBAny.cpp
+++ b/Source/WebCore/storage/IDBAny.cpp
@@ -28,7 +28,7 @@
#if ENABLE(INDEXED_DATABASE)
-#include "IDBCursor.h"
+#include "IDBCursorWithValue.h"
#include "IDBDatabase.h"
#include "IDBFactory.h"
#include "IDBIndex.h"
@@ -64,6 +64,13 @@ PassRefPtr<IDBCursor> IDBAny::idbCursor()
return m_idbCursor;
}
+
+PassRefPtr<IDBCursorWithValue> IDBAny::idbCursorWithValue()
+{
+ ASSERT(m_type == IDBCursorWithValueType);
+ return m_idbCursorWithValue;
+}
+
PassRefPtr<IDBDatabase> IDBAny::idbDatabase()
{
ASSERT(m_type == IDBDatabaseType);
@@ -112,6 +119,13 @@ void IDBAny::setNull()
m_type = NullType;
}
+void IDBAny::set(PassRefPtr<IDBCursorWithValue> value)
+{
+ ASSERT(m_type == UndefinedType);
+ m_type = IDBCursorWithValueType;
+ m_idbCursorWithValue = value;
+}
+
void IDBAny::set(PassRefPtr<IDBCursor> value)
{
ASSERT(m_type == UndefinedType);
diff --git a/Source/WebCore/storage/IDBAny.h b/Source/WebCore/storage/IDBAny.h
index 8e3f83e..e7e94f9 100644
--- a/Source/WebCore/storage/IDBAny.h
+++ b/Source/WebCore/storage/IDBAny.h
@@ -35,6 +35,7 @@
namespace WebCore {
class IDBCursor;
+class IDBCursorWithValue;
class IDBDatabase;
class IDBFactory;
class IDBIndex;
@@ -67,6 +68,7 @@ public:
UndefinedType = 0,
NullType,
IDBCursorType,
+ IDBCursorWithValueType,
IDBDatabaseType,
IDBFactoryType,
IDBIndexType,
@@ -79,6 +81,7 @@ public:
Type type() const { return m_type; }
// Use type() to figure out which one of these you're allowed to call.
PassRefPtr<IDBCursor> idbCursor();
+ PassRefPtr<IDBCursorWithValue> idbCursorWithValue();
PassRefPtr<IDBDatabase> idbDatabase();
PassRefPtr<IDBFactory> idbFactory();
PassRefPtr<IDBIndex> idbIndex();
@@ -90,6 +93,7 @@ public:
// Set can only be called once.
void setNull();
void set(PassRefPtr<IDBCursor>);
+ void set(PassRefPtr<IDBCursorWithValue>);
void set(PassRefPtr<IDBDatabase>);
void set(PassRefPtr<IDBFactory>);
void set(PassRefPtr<IDBIndex>);
@@ -105,6 +109,7 @@ private:
// Only one of the following should ever be in use at any given time.
RefPtr<IDBCursor> m_idbCursor;
+ RefPtr<IDBCursorWithValue> m_idbCursorWithValue;
RefPtr<IDBDatabase> m_idbDatabase;
RefPtr<IDBFactory> m_idbFactory;
RefPtr<IDBIndex> m_idbIndex;
diff --git a/Source/WebCore/storage/IDBBackingStore.cpp b/Source/WebCore/storage/IDBBackingStore.cpp
new file mode 100644
index 0000000..3859c4c
--- /dev/null
+++ b/Source/WebCore/storage/IDBBackingStore.cpp
@@ -0,0 +1,996 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "IDBBackingStore.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "FileSystem.h"
+#include "IDBFactoryBackendImpl.h"
+#include "IDBKey.h"
+#include "IDBKeyRange.h"
+#include "SQLiteDatabase.h"
+#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
+#include "SecurityOrigin.h"
+
+namespace WebCore {
+
+IDBBackingStore::IDBBackingStore(String identifier, IDBFactoryBackendImpl* factory)
+ : m_identifier(identifier)
+ , m_factory(factory)
+{
+ m_factory->addIDBBackingStore(identifier, this);
+}
+
+IDBBackingStore::~IDBBackingStore()
+{
+ m_factory->removeIDBBackingStore(m_identifier);
+}
+
+static bool runCommands(SQLiteDatabase& sqliteDatabase, const char** commands, size_t numberOfCommands)
+{
+ SQLiteTransaction transaction(sqliteDatabase, false);
+ transaction.begin();
+ for (size_t i = 0; i < numberOfCommands; ++i) {
+ if (!sqliteDatabase.executeCommand(commands[i])) {
+ LOG_ERROR("Failed to run the following command for IndexedDB: %s", commands[i]);
+ return false;
+ }
+ }
+ transaction.commit();
+ return true;
+}
+
+static bool createTables(SQLiteDatabase& sqliteDatabase)
+{
+ if (sqliteDatabase.tableExists("Databases"))
+ return true;
+ static const char* commands[] = {
+ "CREATE TABLE Databases (id INTEGER PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL, version TEXT NOT NULL)",
+ "CREATE UNIQUE INDEX Databases_name ON Databases(name)",
+
+ "CREATE TABLE ObjectStores (id INTEGER PRIMARY KEY, name TEXT NOT NULL, keyPath TEXT, doAutoIncrement INTEGER NOT NULL, databaseId INTEGER NOT NULL REFERENCES Databases(id))",
+ "CREATE UNIQUE INDEX ObjectStores_composit ON ObjectStores(databaseId, name)",
+
+ "CREATE TABLE Indexes (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), name TEXT NOT NULL, keyPath TEXT, isUnique INTEGER NOT NULL)",
+ "CREATE UNIQUE INDEX Indexes_composit ON Indexes(objectStoreId, name)",
+
+ "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, value TEXT NOT NULL)",
+ "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)",
+
+ "CREATE TABLE IndexData (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))",
+ "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)",
+ "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId)",
+ "CREATE INDEX IndexData_indexId ON IndexData(indexId)",
+ };
+
+ return runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0]));
+}
+
+static bool createMetaDataTable(SQLiteDatabase& sqliteDatabase)
+{
+ static const char* commands[] = {
+ "CREATE TABLE MetaData (name TEXT PRIMARY KEY, value NONE)",
+ "INSERT INTO MetaData VALUES ('version', 1)",
+ };
+
+ return runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0]));
+}
+
+static bool getDatabaseSchemaVersion(SQLiteDatabase& sqliteDatabase, int* databaseVersion)
+{
+ SQLiteStatement query(sqliteDatabase, "SELECT value FROM MetaData WHERE name = 'version'");
+ if (query.prepare() != SQLResultOk || query.step() != SQLResultRow)
+ return false;
+
+ *databaseVersion = query.getColumnInt(0);
+ return query.finalize() == SQLResultOk;
+}
+
+static bool migrateDatabase(SQLiteDatabase& sqliteDatabase)
+{
+ if (!sqliteDatabase.tableExists("MetaData")) {
+ if (!createMetaDataTable(sqliteDatabase))
+ return false;
+ }
+
+ int databaseVersion;
+ if (!getDatabaseSchemaVersion(sqliteDatabase, &databaseVersion))
+ return false;
+
+ if (databaseVersion == 1) {
+ static const char* commands[] = {
+ "DROP TABLE IF EXISTS ObjectStoreData2",
+ "CREATE TABLE ObjectStoreData2 (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, keyNumber REAL, value TEXT NOT NULL)",
+ "INSERT INTO ObjectStoreData2 SELECT * FROM ObjectStoreData",
+ "DROP TABLE ObjectStoreData", // This depends on SQLite not enforcing referential consistency.
+ "ALTER TABLE ObjectStoreData2 RENAME TO ObjectStoreData",
+ "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)",
+ "DROP TABLE IF EXISTS IndexData2", // This depends on SQLite not enforcing referential consistency.
+ "CREATE TABLE IndexData2 (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate REAL, keyNumber REAL, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))",
+ "INSERT INTO IndexData2 SELECT * FROM IndexData",
+ "DROP TABLE IndexData",
+ "ALTER TABLE IndexData2 RENAME TO IndexData",
+ "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)",
+ "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId)",
+ "CREATE INDEX IndexData_indexId ON IndexData(indexId)",
+ "UPDATE MetaData SET value = 2 WHERE name = 'version'",
+ };
+
+ if (!runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0])))
+ return false;
+
+ databaseVersion = 2;
+ }
+
+ if (databaseVersion == 2) {
+ // We need to make the ObjectStoreData.value be a BLOB instead of TEXT.
+ static const char* commands[] = {
+ "DROP TABLE IF EXISTS ObjectStoreData", // This drops associated indices.
+ "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, keyNumber REAL, value BLOB NOT NULL)",
+ "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)",
+ "UPDATE MetaData SET value = 3 WHERE name = 'version'",
+ };
+
+ if (!runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0])))
+ return false;
+
+ databaseVersion = 3;
+ }
+
+ return true;
+}
+
+PassRefPtr<IDBBackingStore> IDBBackingStore::open(SecurityOrigin* securityOrigin, const String& pathBase, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl* factory)
+{
+ RefPtr<IDBBackingStore> backingStore(adoptRef(new IDBBackingStore(fileIdentifier, factory)));
+
+ String path = ":memory:";
+ if (!pathBase.isEmpty()) {
+ if (!makeAllDirectories(pathBase)) {
+ // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out.
+ LOG_ERROR("Unabled to create LocalStorage database path %s", pathBase.utf8().data());
+ return 0;
+ }
+ path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb");
+ }
+
+ if (!backingStore->m_db.open(path)) {
+ // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out.
+ LOG_ERROR("Failed to open database file %s for IndexedDB", path.utf8().data());
+ return 0;
+ }
+
+ // FIXME: Error checking?
+ backingStore->m_db.setMaximumSize(maximumSize);
+ backingStore->m_db.turnOnIncrementalAutoVacuum();
+
+ if (!createTables(backingStore->m_db))
+ return 0;
+ if (!migrateDatabase(backingStore->m_db))
+ return 0;
+
+ return backingStore.release();
+}
+
+bool IDBBackingStore::extractIDBDatabaseMetaData(const String& name, String& foundVersion, int64_t& foundId)
+{
+ SQLiteStatement databaseQuery(m_db, "SELECT id, version FROM Databases WHERE name = ?");
+ if (databaseQuery.prepare() != SQLResultOk) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+ databaseQuery.bindText(1, name);
+ if (databaseQuery.step() != SQLResultRow)
+ return false;
+
+ foundId = databaseQuery.getColumnInt64(0);
+ foundVersion = databaseQuery.getColumnText(1);
+
+ if (databaseQuery.step() == SQLResultRow)
+ ASSERT_NOT_REACHED();
+ return true;
+}
+
+bool IDBBackingStore::setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId)
+{
+ ASSERT(!name.isNull());
+ ASSERT(!version.isNull());
+
+ String sql = invalidRowId ? "INSERT INTO Databases (name, description, version) VALUES (?, '', ?)" : "UPDATE Databases SET name = ?, version = ? WHERE id = ?";
+ SQLiteStatement query(m_db, sql);
+ if (query.prepare() != SQLResultOk) {
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+
+ query.bindText(1, name);
+ query.bindText(2, version);
+ if (!invalidRowId)
+ query.bindInt64(3, rowId);
+
+ if (query.step() != SQLResultDone)
+ return false;
+
+ if (invalidRowId)
+ rowId = m_db.lastInsertRowID();
+
+ return true;
+}
+
+void IDBBackingStore::getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags)
+{
+ SQLiteStatement query(m_db, "SELECT id, name, keyPath, doAutoIncrement FROM ObjectStores WHERE databaseId = ?");
+ bool ok = query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ ASSERT(foundIds.isEmpty());
+ ASSERT(foundNames.isEmpty());
+ ASSERT(foundKeyPaths.isEmpty());
+ ASSERT(foundAutoIncrementFlags.isEmpty());
+
+ query.bindInt64(1, databaseId);
+
+ while (query.step() == SQLResultRow) {
+ foundIds.append(query.getColumnInt64(0));
+ foundNames.append(query.getColumnText(1));
+ foundKeyPaths.append(query.getColumnText(2));
+ foundAutoIncrementFlags.append(!!query.getColumnInt(3));
+ }
+}
+
+bool IDBBackingStore::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, int64_t databaseId, int64_t& assignedObjectStoreId)
+{
+ SQLiteStatement query(m_db, "INSERT INTO ObjectStores (name, keyPath, doAutoIncrement, databaseId) VALUES (?, ?, ?, ?)");
+ if (query.prepare() != SQLResultOk)
+ return false;
+
+ query.bindText(1, name);
+ query.bindText(2, keyPath);
+ query.bindInt(3, static_cast<int>(autoIncrement));
+ query.bindInt64(4, databaseId);
+
+ if (query.step() != SQLResultDone)
+ return false;
+
+ assignedObjectStoreId = m_db.lastInsertRowID();
+ return true;
+}
+
+static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id)
+{
+ SQLiteStatement deleteQuery(db, sql);
+ bool ok = deleteQuery.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling.
+ deleteQuery.bindInt64(1, id);
+ ok = deleteQuery.step() == SQLResultDone;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling.
+}
+
+void IDBBackingStore::deleteObjectStore(int64_t objectStoreId)
+{
+ doDelete(m_db, "DELETE FROM ObjectStores WHERE id = ?", objectStoreId);
+ doDelete(m_db, "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStoreId);
+ doDelete(m_db, "DELETE FROM IndexData WHERE indexId IN (SELECT id FROM Indexes WHERE objectStoreId = ?)", objectStoreId);
+ doDelete(m_db, "DELETE FROM Indexes WHERE objectStoreId = ?", objectStoreId);
+}
+
+static String whereSyntaxForKey(const IDBKey& key, String qualifiedTableName = "")
+{
+ switch (key.type()) {
+ case IDBKey::StringType:
+ return qualifiedTableName + "keyString = ? AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL ";
+ case IDBKey::NumberType:
+ return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber = ? ";
+ case IDBKey::DateType:
+ return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate = ? AND " + qualifiedTableName + "keyNumber IS NULL ";
+ case IDBKey::NullType:
+ return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL ";
+ }
+
+ ASSERT_NOT_REACHED();
+ return "";
+}
+
+// Returns the number of items bound.
+static int bindKeyToQuery(SQLiteStatement& query, int column, const IDBKey& key)
+{
+ switch (key.type()) {
+ case IDBKey::StringType:
+ query.bindText(column, key.string());
+ return 1;
+ case IDBKey::DateType:
+ query.bindDouble(column, key.date());
+ return 1;
+ case IDBKey::NumberType:
+ query.bindDouble(column, key.number());
+ return 1;
+ case IDBKey::NullType:
+ return 0;
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+static String lowerCursorWhereFragment(const IDBKey& key, String comparisonOperator, String qualifiedTableName = "")
+{
+ switch (key.type()) {
+ case IDBKey::StringType:
+ return "? " + comparisonOperator + " " + qualifiedTableName + "keyString AND ";
+ case IDBKey::DateType:
+ return "(? " + comparisonOperator + " " + qualifiedTableName + "keyDate OR NOT " + qualifiedTableName + "keyString IS NULL) AND ";
+ case IDBKey::NumberType:
+ return "(? " + comparisonOperator + " " + qualifiedTableName + "keyNumber OR NOT " + qualifiedTableName + "keyString IS NULL OR NOT " + qualifiedTableName + "keyDate IS NULL) AND ";
+ case IDBKey::NullType:
+ if (comparisonOperator == "<")
+ return "NOT(" + qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL) AND ";
+ return ""; // If it's =, the upper bound half will do the constraining. If it's <=, then that's a no-op.
+ }
+ ASSERT_NOT_REACHED();
+ return "";
+}
+
+static String upperCursorWhereFragment(const IDBKey& key, String comparisonOperator, String qualifiedTableName = "")
+{
+ switch (key.type()) {
+ case IDBKey::StringType:
+ return "(" + qualifiedTableName + "keyString " + comparisonOperator + " ? OR " + qualifiedTableName + "keyString IS NULL) AND ";
+ case IDBKey::DateType:
+ return "(" + qualifiedTableName + "keyDate " + comparisonOperator + " ? OR " + qualifiedTableName + "keyDate IS NULL) AND " + qualifiedTableName + "keyString IS NULL AND ";
+ case IDBKey::NumberType:
+ return "(" + qualifiedTableName + "keyNumber " + comparisonOperator + " ? OR " + qualifiedTableName + "keyNumber IS NULL) AND " + qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND ";
+ case IDBKey::NullType:
+ if (comparisonOperator == "<")
+ return "0 != 0 AND ";
+ return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL AND ";
+ }
+ ASSERT_NOT_REACHED();
+ return "";
+}
+
+String IDBBackingStore::getObjectStoreRecord(int64_t objectStoreId, const IDBKey& key)
+{
+ SQLiteStatement query(m_db, "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE objectStoreId = ? AND " + whereSyntaxForKey(key));
+ bool ok = query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ query.bindInt64(1, objectStoreId);
+ bindKeyToQuery(query, 2, key);
+ if (query.step() != SQLResultRow)
+ return String(); // Null String means record not found.
+
+ ASSERT((key.type() == IDBKey::StringType) != query.isColumnNull(0));
+ ASSERT((key.type() == IDBKey::DateType) != query.isColumnNull(1));
+ ASSERT((key.type() == IDBKey::NumberType) != query.isColumnNull(2));
+
+ String record = query.getColumnBlobAsString(3);
+ ASSERT(query.step() != SQLResultRow);
+
+ return record;
+}
+
+static void bindKeyToQueryWithNulls(SQLiteStatement& query, int baseColumn, const IDBKey& key)
+{
+ switch (key.type()) {
+ case IDBKey::StringType:
+ query.bindText(baseColumn + 0, key.string());
+ query.bindNull(baseColumn + 1);
+ query.bindNull(baseColumn + 2);
+ break;
+ case IDBKey::DateType:
+ query.bindNull(baseColumn + 0);
+ query.bindDouble(baseColumn + 1, key.date());
+ query.bindNull(baseColumn + 2);
+ break;
+ case IDBKey::NumberType:
+ query.bindNull(baseColumn + 0);
+ query.bindNull(baseColumn + 1);
+ query.bindDouble(baseColumn + 2, key.number());
+ break;
+ case IDBKey::NullType:
+ query.bindNull(baseColumn + 0);
+ query.bindNull(baseColumn + 1);
+ query.bindNull(baseColumn + 2);
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+bool IDBBackingStore::putObjectStoreRecord(int64_t objectStoreId, const IDBKey& key, const String& value, int64_t& rowId, bool invalidRowId)
+{
+ String sql = !invalidRowId ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?"
+ : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)";
+ SQLiteStatement query(m_db, sql);
+ if (query.prepare() != SQLResultOk)
+ return false;
+
+ bindKeyToQueryWithNulls(query, 1, key);
+ query.bindBlob(4, value);
+ if (!invalidRowId)
+ query.bindInt64(5, rowId);
+ else
+ query.bindInt64(5, objectStoreId);
+
+ if (query.step() != SQLResultDone)
+ return false;
+
+ if (invalidRowId)
+ rowId = m_db.lastInsertRowID();
+
+ return true;
+}
+
+void IDBBackingStore::clearObjectStore(int64_t objectStoreId)
+{
+ doDelete(m_db, "DELETE FROM IndexData WHERE objectStoreDataId IN (SELECT id FROM ObjectStoreData WHERE objectStoreId = ?)", objectStoreId);
+ doDelete(m_db, "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStoreId);
+}
+
+void IDBBackingStore::deleteObjectStoreRecord(int64_t, int64_t objectStoreDataId)
+{
+ SQLiteStatement osQuery(m_db, "DELETE FROM ObjectStoreData WHERE id = ?");
+ bool ok = osQuery.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ osQuery.bindInt64(1, objectStoreDataId);
+
+ ok = osQuery.step() == SQLResultDone;
+ ASSERT_UNUSED(ok, ok);
+
+ SQLiteStatement indexQuery(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ?");
+ ok = indexQuery.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ indexQuery.bindInt64(1, objectStoreDataId);
+
+ ok = indexQuery.step() == SQLResultDone;
+ ASSERT_UNUSED(ok, ok);
+}
+
+double IDBBackingStore::nextAutoIncrementNumber(int64_t objectStoreId)
+{
+ SQLiteStatement query(m_db, "SELECT max(keyNumber) + 1 FROM ObjectStoreData WHERE objectStoreId = ? AND keyString IS NULL AND keyDate IS NULL");
+ bool ok = query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok);
+
+ query.bindInt64(1, objectStoreId);
+
+ if (query.step() != SQLResultRow || query.isColumnNull(0))
+ return 1;
+
+ return query.getColumnDouble(0);
+}
+
+bool IDBBackingStore::keyExistsInObjectStore(int64_t objectStoreId, const IDBKey& key, int64_t& foundObjectStoreDataId)
+{
+ String sql = String("SELECT id FROM ObjectStoreData WHERE objectStoreId = ? AND ") + whereSyntaxForKey(key);
+ SQLiteStatement query(m_db, sql);
+ bool ok = query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ query.bindInt64(1, objectStoreId);
+ bindKeyToQuery(query, 2, key);
+
+ if (query.step() != SQLResultRow)
+ return false;
+
+ foundObjectStoreDataId = query.getColumnInt64(0);
+ return true;
+}
+
+bool IDBBackingStore::forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback& callback)
+{
+ SQLiteStatement query(m_db, "SELECT id, value FROM ObjectStoreData WHERE objectStoreId = ?");
+ if (query.prepare() != SQLResultOk)
+ return false;
+
+ query.bindInt64(1, objectStoreId);
+
+ while (query.step() == SQLResultRow) {
+ int64_t objectStoreDataId = query.getColumnInt64(0);
+ String value = query.getColumnBlobAsString(1);
+ if (!callback.callback(objectStoreDataId, value))
+ return false;
+ }
+
+ return true;
+}
+
+void IDBBackingStore::getIndexes(int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags)
+{
+ SQLiteStatement query(m_db, "SELECT id, name, keyPath, isUnique FROM Indexes WHERE objectStoreId = ?");
+ bool ok = query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ ASSERT(foundIds.isEmpty());
+ ASSERT(foundNames.isEmpty());
+ ASSERT(foundKeyPaths.isEmpty());
+ ASSERT(foundUniqueFlags.isEmpty());
+
+ query.bindInt64(1, objectStoreId);
+
+ while (query.step() == SQLResultRow) {
+ foundIds.append(query.getColumnInt64(0));
+ foundNames.append(query.getColumnText(1));
+ foundKeyPaths.append(query.getColumnText(2));
+ foundUniqueFlags.append(!!query.getColumnInt(3));
+ }
+}
+
+bool IDBBackingStore::createIndex(int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId)
+{
+ SQLiteStatement query(m_db, "INSERT INTO Indexes (objectStoreId, name, keyPath, isUnique) VALUES (?, ?, ?, ?)");
+ if (query.prepare() != SQLResultOk)
+ return false;
+
+ query.bindInt64(1, objectStoreId);
+ query.bindText(2, name);
+ query.bindText(3, keyPath);
+ query.bindInt(4, static_cast<int>(isUnique));
+
+ if (query.step() != SQLResultDone)
+ return false;
+
+ indexId = m_db.lastInsertRowID();
+ return true;
+}
+
+void IDBBackingStore::deleteIndex(int64_t indexId)
+{
+ doDelete(m_db, "DELETE FROM Indexes WHERE id = ?", indexId);
+ doDelete(m_db, "DELETE FROM IndexData WHERE indexId = ?", indexId);
+}
+
+bool IDBBackingStore::putIndexDataForRecord(int64_t indexId, const IDBKey& key, int64_t objectStoreDataId)
+{
+ SQLiteStatement query(m_db, "INSERT INTO IndexData (keyString, keyDate, keyNumber, indexId, objectStoreDataId) VALUES (?, ?, ?, ?, ?)");
+ if (query.prepare() != SQLResultOk)
+ return false;
+
+ bindKeyToQueryWithNulls(query, 1, key);
+ query.bindInt64(4, indexId);
+ query.bindInt64(5, objectStoreDataId);
+
+ return query.step() == SQLResultDone;
+}
+
+bool IDBBackingStore::deleteIndexDataForRecord(int64_t objectStoreDataId)
+{
+ SQLiteStatement query(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ?");
+ if (query.prepare() != SQLResultOk)
+ return false;
+
+ query.bindInt64(1, objectStoreDataId);
+ return query.step() == SQLResultDone;
+}
+
+String IDBBackingStore::getObjectViaIndex(int64_t indexId, const IDBKey& key)
+{
+ String sql = String("SELECT ")
+ + "ObjectStoreData.value "
+ + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id "
+ + "WHERE IndexData.indexId = ? AND " + whereSyntaxForKey(key, "IndexData.")
+ + "ORDER BY IndexData.id LIMIT 1"; // Order by insertion order when all else fails.
+ SQLiteStatement query(m_db, sql);
+ bool ok = query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ query.bindInt64(1, indexId);
+ bindKeyToQuery(query, 2, key);
+
+ if (query.step() != SQLResultRow)
+ return String();
+
+ String foundValue = query.getColumnBlobAsString(0);
+ ASSERT(query.step() != SQLResultRow);
+ return foundValue;
+}
+
+static PassRefPtr<IDBKey> keyFromQuery(SQLiteStatement& query, int baseColumn)
+{
+ if (query.columnCount() <= baseColumn)
+ return 0;
+
+ if (!query.isColumnNull(baseColumn))
+ return IDBKey::createString(query.getColumnText(baseColumn));
+
+ if (!query.isColumnNull(baseColumn + 1))
+ return IDBKey::createDate(query.getColumnDouble(baseColumn + 1));
+
+ if (!query.isColumnNull(baseColumn + 2))
+ return IDBKey::createNumber(query.getColumnDouble(baseColumn + 2));
+
+ return IDBKey::createNull();
+}
+
+PassRefPtr<IDBKey> IDBBackingStore::getPrimaryKeyViaIndex(int64_t indexId, const IDBKey& key)
+{
+ String sql = String("SELECT ")
+ + "ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber "
+ + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id "
+ + "WHERE IndexData.indexId = ? AND " + whereSyntaxForKey(key, "IndexData.")
+ + "ORDER BY IndexData.id LIMIT 1"; // Order by insertion order when all else fails.
+ SQLiteStatement query(m_db, sql);
+ bool ok = query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ query.bindInt64(1, indexId);
+ bindKeyToQuery(query, 2, key);
+
+ if (query.step() != SQLResultRow)
+ return 0;
+
+ RefPtr<IDBKey> foundKey = keyFromQuery(query, 0);
+ ASSERT(query.step() != SQLResultRow);
+ return foundKey.release();
+}
+
+bool IDBBackingStore::keyExistsInIndex(int64_t indexId, const IDBKey& key)
+{
+ String sql = String("SELECT id FROM IndexData WHERE indexId = ? AND ") + whereSyntaxForKey(key);
+ SQLiteStatement query(m_db, sql);
+ bool ok = query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ query.bindInt64(1, indexId);
+ bindKeyToQuery(query, 2, key);
+
+ return query.step() == SQLResultRow;
+}
+
+namespace {
+
+class CursorImplCommon : public IDBBackingStore::Cursor {
+public:
+ CursorImplCommon(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint, bool iterateForward)
+ : m_query(sqliteDatabase, query)
+ , m_db(sqliteDatabase)
+ , m_uniquenessConstraint(uniquenessConstraint)
+ , m_iterateForward(iterateForward)
+ {
+ }
+ virtual ~CursorImplCommon() {}
+
+ // IDBBackingStore::Cursor
+ virtual bool continueFunction(const IDBKey*);
+ virtual PassRefPtr<IDBKey> key() { return m_currentKey; }
+ virtual PassRefPtr<IDBKey> primaryKey() { return m_currentKey; }
+ virtual String value() = 0;
+ virtual int64_t objectStoreDataId() = 0;
+ virtual int64_t indexDataId() = 0;
+
+ virtual void loadCurrentRow() = 0;
+ virtual bool currentRowExists() = 0;
+
+ SQLiteStatement m_query;
+
+protected:
+ SQLiteDatabase& m_db;
+ bool m_uniquenessConstraint;
+ bool m_iterateForward;
+ int64_t m_currentId;
+ RefPtr<IDBKey> m_currentKey;
+};
+
+bool CursorImplCommon::continueFunction(const IDBKey* key)
+{
+ while (true) {
+ if (m_query.step() != SQLResultRow)
+ return false;
+
+ RefPtr<IDBKey> oldKey = m_currentKey;
+ loadCurrentRow();
+
+ // Skip if this entry has been deleted from the object store.
+ if (!currentRowExists())
+ continue;
+
+ // If a key was supplied, we must loop until we find a key greater than or equal to it (or hit the end).
+ if (key) {
+ if (m_iterateForward) {
+ if (m_currentKey->isLessThan(key))
+ continue;
+ } else {
+ if (key->isLessThan(m_currentKey.get()))
+ continue;
+ }
+ }
+
+ // If we don't have a uniqueness constraint, we can stop now.
+ if (!m_uniquenessConstraint)
+ break;
+ if (!m_currentKey->isEqual(oldKey.get()))
+ break;
+ }
+
+ return true;
+}
+
+class ObjectStoreCursorImpl : public CursorImplCommon {
+public:
+ ObjectStoreCursorImpl(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint, bool iterateForward)
+ : CursorImplCommon(sqliteDatabase, query, uniquenessConstraint, iterateForward)
+ {
+ }
+
+ // CursorImplCommon.
+ virtual String value() { return m_currentValue; }
+ virtual int64_t objectStoreDataId() { return m_currentId; }
+ virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; }
+ virtual void loadCurrentRow();
+ virtual bool currentRowExists();
+
+private:
+ String m_currentValue;
+};
+
+void ObjectStoreCursorImpl::loadCurrentRow()
+{
+ m_currentId = m_query.getColumnInt64(0);
+ m_currentKey = keyFromQuery(m_query, 1);
+ m_currentValue = m_query.getColumnBlobAsString(4);
+}
+
+bool ObjectStoreCursorImpl::currentRowExists()
+{
+ String sql = "SELECT id FROM ObjectStoreData WHERE id = ?";
+ SQLiteStatement statement(m_db, sql);
+
+ bool ok = statement.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok);
+
+ statement.bindInt64(1, m_currentId);
+ return statement.step() == SQLResultRow;
+}
+
+class IndexKeyCursorImpl : public CursorImplCommon {
+public:
+ IndexKeyCursorImpl(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint, bool iterateForward)
+ : CursorImplCommon(sqliteDatabase, query, uniquenessConstraint, iterateForward)
+ {
+ }
+
+ // CursorImplCommon
+ virtual PassRefPtr<IDBKey> primaryKey() { return m_currentPrimaryKey; }
+ virtual String value() { ASSERT_NOT_REACHED(); return String(); }
+ virtual int64_t objectStoreDataId() { ASSERT_NOT_REACHED(); return 0; }
+ virtual int64_t indexDataId() { return m_currentId; }
+ virtual void loadCurrentRow();
+ virtual bool currentRowExists();
+
+private:
+ RefPtr<IDBKey> m_currentPrimaryKey;
+};
+
+void IndexKeyCursorImpl::loadCurrentRow()
+{
+ m_currentId = m_query.getColumnInt64(0);
+ m_currentKey = keyFromQuery(m_query, 1);
+ m_currentPrimaryKey = keyFromQuery(m_query, 4);
+}
+
+bool IndexKeyCursorImpl::currentRowExists()
+{
+ String sql = "SELECT id FROM IndexData WHERE id = ?";
+ SQLiteStatement statement(m_db, sql);
+
+ bool ok = statement.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok);
+
+ statement.bindInt64(1, m_currentId);
+ return statement.step() == SQLResultRow;
+}
+
+class IndexCursorImpl : public CursorImplCommon {
+public:
+ IndexCursorImpl(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint, bool iterateForward)
+ : CursorImplCommon(sqliteDatabase, query, uniquenessConstraint, iterateForward)
+ {
+ }
+
+ // CursorImplCommon
+ virtual PassRefPtr<IDBKey> primaryKey() { return m_currentPrimaryKey; }
+ virtual String value() { return m_currentValue; }
+ virtual int64_t objectStoreDataId() { ASSERT_NOT_REACHED(); return 0; }
+ virtual int64_t indexDataId() { return m_currentId; }
+ virtual void loadCurrentRow();
+ virtual bool currentRowExists();
+
+private:
+ RefPtr<IDBKey> m_currentPrimaryKey;
+ String m_currentValue;
+};
+
+void IndexCursorImpl::loadCurrentRow()
+{
+ m_currentId = m_query.getColumnInt64(0);
+ m_currentKey = keyFromQuery(m_query, 1);
+ m_currentValue = m_query.getColumnBlobAsString(4);
+ m_currentPrimaryKey = keyFromQuery(m_query, 5);
+}
+
+bool IndexCursorImpl::currentRowExists()
+{
+ String sql = "SELECT id FROM IndexData WHERE id = ?";
+ SQLiteStatement statement(m_db, sql);
+
+ bool ok = statement.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok);
+
+ statement.bindInt64(1, m_currentId);
+ return statement.step() == SQLResultRow;
+}
+
+} // namespace
+
+PassRefPtr<IDBBackingStore::Cursor> IDBBackingStore::openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange* range, IDBCursor::Direction direction)
+{
+ bool lowerBound = range && range->lower();
+ bool upperBound = range && range->upper();
+
+ String sql = "SELECT id, keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE ";
+ if (lowerBound)
+ sql += lowerCursorWhereFragment(*range->lower(), range->lowerOpen() ? "<" : "<=");
+ if (upperBound)
+ sql += upperCursorWhereFragment(*range->upper(), range->upperOpen() ? "<" : "<=");
+ sql += "objectStoreId = ? ORDER BY ";
+
+ if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE)
+ sql += "keyString, keyDate, keyNumber";
+ else
+ sql += "keyString DESC, keyDate DESC, keyNumber DESC";
+
+ RefPtr<ObjectStoreCursorImpl> cursor = adoptRef(new ObjectStoreCursorImpl(m_db, sql, direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::PREV_NO_DUPLICATE,
+ direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT));
+
+ bool ok = cursor->m_query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ int currentColumn = 1;
+ if (lowerBound)
+ currentColumn += bindKeyToQuery(cursor->m_query, currentColumn, *range->lower());
+ if (upperBound)
+ currentColumn += bindKeyToQuery(cursor->m_query, currentColumn, *range->upper());
+ cursor->m_query.bindInt64(currentColumn, objectStoreId);
+
+ if (cursor->m_query.step() != SQLResultRow)
+ return 0;
+
+ cursor->loadCurrentRow();
+ return cursor.release();
+}
+
+PassRefPtr<IDBBackingStore::Cursor> IDBBackingStore::openIndexKeyCursor(int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
+{
+ String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ")
+ + ("ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ")
+ + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id WHERE ";
+
+ bool lowerBound = range && range->lower();
+ bool upperBound = range && range->upper();
+
+ if (lowerBound)
+ sql += lowerCursorWhereFragment(*range->lower(), range->lowerOpen() ? "<" : "<=", "IndexData.");
+ if (upperBound)
+ sql += upperCursorWhereFragment(*range->upper(), range->upperOpen() ? "<" : "<=", "IndexData.");
+ sql += "IndexData.indexId = ? ORDER BY ";
+
+ if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE)
+ sql += "IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, IndexData.id";
+ else
+ sql += "IndexData.keyString DESC, IndexData.keyDate DESC, IndexData.keyNumber DESC, IndexData.id DESC";
+
+ RefPtr<IndexKeyCursorImpl> cursor = adoptRef(new IndexKeyCursorImpl(m_db, sql, direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::PREV_NO_DUPLICATE,
+ direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT));
+
+ bool ok = cursor->m_query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ int indexColumn = 1;
+ if (lowerBound)
+ indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->lower());
+ if (upperBound)
+ indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->upper());
+ cursor->m_query.bindInt64(indexColumn, indexId);
+
+ if (cursor->m_query.step() != SQLResultRow)
+ return 0;
+
+ cursor->loadCurrentRow();
+ return cursor.release();
+}
+
+PassRefPtr<IDBBackingStore::Cursor> IDBBackingStore::openIndexCursor(int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
+{
+ String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ")
+ + ("ObjectStoreData.value, ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ")
+ + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id WHERE ";
+
+ bool lowerBound = range && range->lower();
+ bool upperBound = range && range->upper();
+
+ if (lowerBound)
+ sql += lowerCursorWhereFragment(*range->lower(), range->lowerOpen() ? "<" : "<=", "IndexData.");
+ if (upperBound)
+ sql += upperCursorWhereFragment(*range->upper(), range->upperOpen() ? "<" : "<=", "IndexData.");
+ sql += "IndexData.indexId = ? ORDER BY ";
+
+ if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE)
+ sql += "IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, IndexData.id";
+ else
+ sql += "IndexData.keyString DESC, IndexData.keyDate DESC, IndexData.keyNumber DESC, IndexData.id DESC";
+
+ RefPtr<IndexCursorImpl> cursor = adoptRef(new IndexCursorImpl(m_db, sql, direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::PREV_NO_DUPLICATE,
+ direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT));
+
+ bool ok = cursor->m_query.prepare() == SQLResultOk;
+ ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+ int indexColumn = 1;
+ if (lowerBound)
+ indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->lower());
+ if (upperBound)
+ indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->upper());
+ cursor->m_query.bindInt64(indexColumn, indexId);
+
+ if (cursor->m_query.step() != SQLResultRow)
+ return 0;
+
+ cursor->loadCurrentRow();
+ return cursor.release();
+}
+
+namespace {
+
+class TransactionImpl : public IDBBackingStore::Transaction {
+public:
+ TransactionImpl(SQLiteDatabase& db)
+ : m_transaction(db)
+ {
+ }
+
+ // IDBBackingStore::Transaction
+ virtual void begin() { m_transaction.begin(); }
+ virtual void commit() { m_transaction.commit(); }
+ virtual void rollback() { m_transaction.rollback(); }
+
+private:
+ SQLiteTransaction m_transaction;
+};
+
+} // namespace
+
+PassRefPtr<IDBBackingStore::Transaction> IDBBackingStore::createTransaction()
+{
+ return adoptRef(new TransactionImpl(m_db));
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/storage/IDBBackingStore.h b/Source/WebCore/storage/IDBBackingStore.h
new file mode 100644
index 0000000..e75fee1
--- /dev/null
+++ b/Source/WebCore/storage/IDBBackingStore.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef IDBBackingStore_h
+#define IDBBackingStore_h
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBCursor.h"
+#include "SQLiteDatabase.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class IDBFactoryBackendImpl;
+class IDBKey;
+class IDBKeyRange;
+class SecurityOrigin;
+
+class IDBBackingStore : public RefCounted<IDBBackingStore> {
+public:
+ static PassRefPtr<IDBBackingStore> open(SecurityOrigin*, const String& pathBase, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl*);
+ ~IDBBackingStore();
+
+ bool extractIDBDatabaseMetaData(const String& name, String& foundVersion, int64_t& foundId);
+ bool setIDBDatabaseMetaData(const String& name, const String& version, int64_t& rowId, bool invalidRowId);
+
+ void getObjectStores(int64_t databaseId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundAutoIncrementFlags);
+ bool createObjectStore(const String& name, const String& keyPath, bool autoIncrement, int64_t databaseId, int64_t& assignedObjectStoreId);
+ void deleteObjectStore(int64_t objectStoreId);
+ String getObjectStoreRecord(int64_t objectStoreId, const IDBKey&);
+ bool putObjectStoreRecord(int64_t objectStoreId, const IDBKey&, const String& value, int64_t& rowId, bool invalidRowId);
+ void clearObjectStore(int64_t objectStoreId);
+ void deleteObjectStoreRecord(int64_t objectStoreId, int64_t objectStoreDataId);
+ double nextAutoIncrementNumber(int64_t objectStoreId);
+ bool keyExistsInObjectStore(int64_t objectStoreId, const IDBKey&, int64_t& foundObjectStoreDataId);
+
+ class ObjectStoreRecordCallback {
+ public:
+ virtual bool callback(int64_t objectStoreDataId, const String& value) = 0;
+ virtual ~ObjectStoreRecordCallback() {};
+ };
+ bool forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback&);
+
+ void getIndexes(int64_t objectStoreId, Vector<int64_t>& foundIds, Vector<String>& foundNames, Vector<String>& foundKeyPaths, Vector<bool>& foundUniqueFlags);
+ bool createIndex(int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId);
+ void deleteIndex(int64_t indexId);
+ bool putIndexDataForRecord(int64_t indexId, const IDBKey&, int64_t objectStoreDataId);
+ bool deleteIndexDataForRecord(int64_t objectStoreDataId);
+ String getObjectViaIndex(int64_t indexId, const IDBKey&);
+ PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t indexId, const IDBKey&);
+ bool keyExistsInIndex(int64_t indexId, const IDBKey&);
+
+ class Cursor : public RefCounted<Cursor> {
+ public:
+ virtual bool continueFunction(const IDBKey* = 0) = 0;
+ virtual PassRefPtr<IDBKey> key() = 0;
+ virtual PassRefPtr<IDBKey> primaryKey() = 0;
+ virtual String value() = 0;
+ virtual int64_t objectStoreDataId() = 0;
+ virtual int64_t indexDataId() = 0;
+ virtual ~Cursor() {};
+ };
+
+ PassRefPtr<Cursor> openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction);
+ PassRefPtr<Cursor> openIndexKeyCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction);
+ PassRefPtr<Cursor> openIndexCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction);
+
+ class Transaction : public RefCounted<Transaction> {
+ public:
+ virtual void begin() = 0;
+ virtual void commit() = 0;
+ virtual void rollback() = 0;
+ };
+ PassRefPtr<Transaction> createTransaction();
+
+private:
+ IDBBackingStore(String identifier, IDBFactoryBackendImpl* factory);
+
+ SQLiteDatabase m_db;
+ String m_identifier;
+ RefPtr<IDBFactoryBackendImpl> m_factory;
+};
+
+} // namespace WebCore
+
+#endif
+
+#endif // IDBBackingStore_h
diff --git a/Source/WebCore/storage/IDBCallbacks.h b/Source/WebCore/storage/IDBCallbacks.h
index 29fb1c4..f4e1b98 100644
--- a/Source/WebCore/storage/IDBCallbacks.h
+++ b/Source/WebCore/storage/IDBCallbacks.h
@@ -53,9 +53,9 @@ public:
virtual void onSuccess(PassRefPtr<IDBDatabaseBackendInterface>) = 0;
virtual void onSuccess(PassRefPtr<IDBIndexBackendInterface>) = 0;
virtual void onSuccess(PassRefPtr<IDBKey>) = 0;
- virtual void onSuccess(PassRefPtr<IDBObjectStoreBackendInterface>) = 0;
virtual void onSuccess(PassRefPtr<IDBTransactionBackendInterface>) = 0;
virtual void onSuccess(PassRefPtr<SerializedScriptValue>) = 0;
+ virtual void onBlocked() = 0;
};
} // namespace WebCore
diff --git a/Source/WebCore/storage/IDBCompleteEvent.h b/Source/WebCore/storage/IDBCompleteEvent.h
deleted file mode 100644
index c004a72..0000000
--- a/Source/WebCore/storage/IDBCompleteEvent.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef IDBCompleteEvent_h
-#define IDBCompleteEvent_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBEvent.h"
-#include "PlatformString.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class IDBCompleteEvent : public IDBEvent {
-public:
- static PassRefPtr<IDBCompleteEvent> create(PassRefPtr<IDBAny> source);
- // FIXME: Need to allow creation of these events from JS.
- virtual ~IDBCompleteEvent();
-
- virtual bool isIDBCompleteEvent() const { return true; }
-
-private:
- IDBCompleteEvent(PassRefPtr<IDBAny> source);
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBCompleteEvent_h
diff --git a/Source/WebCore/storage/IDBCursor.cpp b/Source/WebCore/storage/IDBCursor.cpp
index 531cae1..9b1ebf6 100644
--- a/Source/WebCore/storage/IDBCursor.cpp
+++ b/Source/WebCore/storage/IDBCursor.cpp
@@ -39,13 +39,20 @@
namespace WebCore {
-IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBTransaction* transaction)
+PassRefPtr<IDBCursor> IDBCursor::create(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
+{
+ return adoptRef(new IDBCursor(backend, request, source, transaction));
+}
+
+IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
: m_backend(backend)
, m_request(request)
+ , m_source(source)
, m_transaction(transaction)
{
ASSERT(m_backend);
ASSERT(m_request);
+ ASSERT(m_source->type() == IDBAny::IDBObjectStoreType || m_source->type() == IDBAny::IDBIndexType);
ASSERT(m_transaction);
}
@@ -63,17 +70,29 @@ PassRefPtr<IDBKey> IDBCursor::key() const
return m_backend->key();
}
-PassRefPtr<IDBAny> IDBCursor::value() const
+PassRefPtr<IDBKey> IDBCursor::primaryKey() const
+{
+ return m_backend->primaryKey();
+}
+
+PassRefPtr<SerializedScriptValue> IDBCursor::value() const
{
return m_backend->value();
}
+IDBAny* IDBCursor::source() const
+{
+ return m_source.get();
+}
+
PassRefPtr<IDBRequest> IDBCursor::update(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, ExceptionCode& ec)
{
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
m_backend->update(value, request, ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
+ }
return request.release();
}
@@ -91,8 +110,10 @@ PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context
{
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
m_backend->deleteFunction(request, ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
+ }
return request.release();
}
diff --git a/Source/WebCore/storage/IDBCursor.h b/Source/WebCore/storage/IDBCursor.h
index 9f5ffad..9ade9c8 100644
--- a/Source/WebCore/storage/IDBCursor.h
+++ b/Source/WebCore/storage/IDBCursor.h
@@ -29,6 +29,7 @@
#if ENABLE(INDEXED_DATABASE)
#include "ExceptionCode.h"
+#include "IDBKey.h"
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
@@ -38,7 +39,6 @@ namespace WebCore {
class IDBAny;
class IDBCallbacks;
class IDBCursorBackendInterface;
-class IDBKey;
class IDBRequest;
class IDBTransaction;
class ScriptExecutionContext;
@@ -52,11 +52,8 @@ public:
PREV = 2,
PREV_NO_DUPLICATE = 3,
};
- static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBTransaction* transaction)
- {
- return adoptRef(new IDBCursor(backend, request, transaction));
- }
- ~IDBCursor();
+ static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*);
+ virtual ~IDBCursor();
// FIXME: Try to modify the code generator so this is unneeded.
void continueFunction(ExceptionCode& ec) { continueFunction(0, ec); }
@@ -64,16 +61,21 @@ public:
// Implement the IDL
unsigned short direction() const;
PassRefPtr<IDBKey> key() const;
- PassRefPtr<IDBAny> value() const;
+ PassRefPtr<IDBKey> primaryKey() const;
+ PassRefPtr<SerializedScriptValue> value() const;
+ IDBAny* source() const;
+
PassRefPtr<IDBRequest> update(ScriptExecutionContext*, PassRefPtr<SerializedScriptValue>, ExceptionCode&);
void continueFunction(PassRefPtr<IDBKey>, ExceptionCode&);
PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, ExceptionCode&);
-private:
- explicit IDBCursor(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBTransaction*);
+protected:
+ IDBCursor(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*);
+private:
RefPtr<IDBCursorBackendInterface> m_backend;
RefPtr<IDBRequest> m_request;
+ RefPtr<IDBAny> m_source;
RefPtr<IDBTransaction> m_transaction;
};
diff --git a/Source/WebCore/storage/IDBCursor.idl b/Source/WebCore/storage/IDBCursor.idl
index 2e1459f..1f44cd2 100644
--- a/Source/WebCore/storage/IDBCursor.idl
+++ b/Source/WebCore/storage/IDBCursor.idl
@@ -35,7 +35,8 @@ module storage {
readonly attribute unsigned short direction;
readonly attribute IDBKey key;
- readonly attribute IDBAny value;
+ readonly attribute IDBKey primaryKey;
+ readonly attribute IDBAny source;
[CallWith=ScriptExecutionContext] IDBRequest update(in SerializedScriptValue value)
raises (IDBDatabaseException);
diff --git a/Source/WebCore/storage/IDBCursorBackendImpl.cpp b/Source/WebCore/storage/IDBCursorBackendImpl.cpp
index 089bb67..6ae3616 100644
--- a/Source/WebCore/storage/IDBCursorBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBCursorBackendImpl.cpp
@@ -29,32 +29,25 @@
#if ENABLE(INDEXED_DATABASE)
#include "CrossThreadTask.h"
+#include "IDBBackingStore.h"
#include "IDBCallbacks.h"
-#include "IDBDatabaseBackendImpl.h"
#include "IDBDatabaseError.h"
#include "IDBDatabaseException.h"
-#include "IDBIndexBackendImpl.h"
#include "IDBKeyRange.h"
#include "IDBObjectStoreBackendImpl.h"
#include "IDBRequest.h"
-#include "IDBSQLiteDatabase.h"
#include "IDBTransactionBackendInterface.h"
-#include "SQLiteDatabase.h"
-#include "SQLiteStatement.h"
#include "SerializedScriptValue.h"
namespace WebCore {
-IDBCursorBackendImpl::IDBCursorBackendImpl(IDBSQLiteDatabase* database, PassRefPtr<IDBKeyRange> keyRange, IDBCursor::Direction direction, PassOwnPtr<SQLiteStatement> query, bool isSerializedScriptValueCursor, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore)
- : m_database(database)
- , m_keyRange(keyRange)
+IDBCursorBackendImpl::IDBCursorBackendImpl(PassRefPtr<IDBBackingStore::Cursor> cursor, IDBCursor::Direction direction, CursorType cursorType, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore)
+ : m_cursor(cursor)
, m_direction(direction)
- , m_query(query)
- , m_isSerializedScriptValueCursor(isSerializedScriptValueCursor)
+ , m_cursorType(cursorType)
, m_transaction(transaction)
, m_objectStore(objectStore)
{
- loadCurrentRow();
}
IDBCursorBackendImpl::~IDBCursorBackendImpl()
@@ -68,25 +61,28 @@ unsigned short IDBCursorBackendImpl::direction() const
PassRefPtr<IDBKey> IDBCursorBackendImpl::key() const
{
- return m_currentKey;
+ return m_cursor->key();
}
-PassRefPtr<IDBAny> IDBCursorBackendImpl::value() const
+PassRefPtr<IDBKey> IDBCursorBackendImpl::primaryKey() const
{
- if (m_isSerializedScriptValueCursor)
- return IDBAny::create(m_currentSerializedScriptValue.get());
- return IDBAny::create(m_currentIDBKeyValue.get());
+ return m_cursor->primaryKey();
+}
+
+PassRefPtr<SerializedScriptValue> IDBCursorBackendImpl::value() const
+{
+ ASSERT(m_cursorType != IndexKeyCursor);
+ return SerializedScriptValue::createFromWire(m_cursor->value());
}
void IDBCursorBackendImpl::update(PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBCallbacks> callbacks, ExceptionCode& ec)
{
- if (!m_query || m_currentId == InvalidId || !m_isSerializedScriptValueCursor) {
+ if (!m_cursor || m_cursorType == IndexKeyCursor) {
ec = IDBDatabaseException::NOT_ALLOWED_ERR;
return;
}
- RefPtr<IDBKey> key = m_currentIDBKeyValue ? m_currentIDBKeyValue : m_currentKey;
- m_objectStore->put(value, key.release(), IDBObjectStoreBackendInterface::CursorUpdate, callbacks, m_transaction.get(), ec);
+ m_objectStore->put(value, m_cursor->primaryKey(), IDBObjectStoreBackendInterface::CursorUpdate, callbacks, m_transaction.get(), ec);
}
void IDBCursorBackendImpl::continueFunction(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec)
@@ -98,51 +94,17 @@ void IDBCursorBackendImpl::continueFunction(PassRefPtr<IDBKey> prpKey, PassRefPt
ec = IDBDatabaseException::NOT_ALLOWED_ERR;
}
-bool IDBCursorBackendImpl::currentRowExists()
-{
- String sql = m_currentIDBKeyValue ? "SELECT id FROM IndexData WHERE id = ?" : "SELECT id FROM ObjectStoreData WHERE id = ?";
- SQLiteStatement statement(m_database->db(), sql);
-
- bool ok = statement.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok);
-
- statement.bindInt64(1, m_currentId);
- return statement.step() == SQLResultRow;
-}
-
// IMPORTANT: If this ever 1) fires an 'error' event and 2) it's possible to fire another event afterwards,
// IDBRequest::hasPendingActivity() will need to be modified to handle this!!!
void IDBCursorBackendImpl::continueFunctionInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl> prpCursor, PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> callbacks)
{
RefPtr<IDBCursorBackendImpl> cursor = prpCursor;
RefPtr<IDBKey> key = prpKey;
- while (true) {
- if (!cursor->m_query || cursor->m_query->step() != SQLResultRow) {
- cursor->m_query = 0;
- cursor->m_currentId = InvalidId;
- cursor->m_currentKey = 0;
- cursor->m_currentSerializedScriptValue = 0;
- cursor->m_currentIDBKeyValue = 0;
- callbacks->onSuccess(SerializedScriptValue::nullValue());
- return;
- }
-
- RefPtr<IDBKey> oldKey = cursor->m_currentKey;
- cursor->loadCurrentRow();
-
- // Skip if this entry has been deleted from the object store.
- if (!cursor->currentRowExists())
- continue;
-
- // If a key was supplied, we must loop until we find that key (or hit the end).
- if (key && !key->isEqual(cursor->m_currentKey.get()))
- continue;
-
- // If we don't have a uniqueness constraint, we can stop now.
- if (cursor->m_direction == IDBCursor::NEXT || cursor->m_direction == IDBCursor::PREV)
- break;
- if (!cursor->m_currentKey->isEqual(oldKey.get()))
- break;
+
+ if (!cursor->m_cursor || !cursor->m_cursor->continueFunction(key.get())) {
+ cursor->m_cursor = 0;
+ callbacks->onSuccess(SerializedScriptValue::nullValue());
+ return;
}
callbacks->onSuccess(cursor.get());
@@ -150,31 +112,12 @@ void IDBCursorBackendImpl::continueFunctionInternal(ScriptExecutionContext*, Pas
void IDBCursorBackendImpl::deleteFunction(PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec)
{
- if (!m_query || m_currentId == InvalidId || !m_isSerializedScriptValueCursor) {
+ if (!m_cursor || m_cursorType == IndexKeyCursor) {
ec = IDBDatabaseException::NOT_ALLOWED_ERR;
return;
}
- RefPtr<IDBKey> key = m_currentIDBKeyValue ? m_currentIDBKeyValue : m_currentKey;
- m_objectStore->deleteFunction(key.release(), prpCallbacks, m_transaction.get(), ec);
-}
-
-
-void IDBCursorBackendImpl::loadCurrentRow()
-{
- // The column numbers depend on the query in IDBObjectStoreBackendImpl::openCursorInternal or
- // IDBIndexBackendImpl::openCursorInternal.
- m_currentId = m_query->getColumnInt64(0);
- m_currentKey = IDBKey::fromQuery(*m_query, 1);
- if (m_isSerializedScriptValueCursor)
- m_currentSerializedScriptValue = SerializedScriptValue::createFromWire(m_query->getColumnBlobAsString(4));
-
- m_currentIDBKeyValue = IDBKey::fromQuery(*m_query, 5);
-}
-
-SQLiteDatabase& IDBCursorBackendImpl::database() const
-{
- return m_database->db();
+ m_objectStore->deleteFunction(m_cursor->primaryKey(), prpCallbacks, m_transaction.get(), ec);
}
} // namespace WebCore
diff --git a/Source/WebCore/storage/IDBCursorBackendImpl.h b/Source/WebCore/storage/IDBCursorBackendImpl.h
index f459139..2ce3881 100644
--- a/Source/WebCore/storage/IDBCursorBackendImpl.h
+++ b/Source/WebCore/storage/IDBCursorBackendImpl.h
@@ -29,6 +29,7 @@
#if ENABLE(INDEXED_DATABASE)
+#include "IDBBackingStore.h"
#include "IDBCursor.h"
#include "IDBCursorBackendInterface.h"
#include <wtf/OwnPtr.h>
@@ -41,56 +42,34 @@ class IDBDatabaseBackendImpl;
class IDBIndexBackendImpl;
class IDBKeyRange;
class IDBObjectStoreBackendInterface;
-class IDBSQLiteDatabase;
+class IDBBackingStore;
class IDBTransactionBackendInterface;
-class SQLiteDatabase;
-class SQLiteStatement;
class SerializedScriptValue;
class IDBCursorBackendImpl : public IDBCursorBackendInterface {
public:
- static PassRefPtr<IDBCursorBackendImpl> create(IDBSQLiteDatabase* database, PassRefPtr<IDBKeyRange> keyRange, IDBCursor::Direction direction, PassOwnPtr<SQLiteStatement> query, bool isSerializedScriptValueCursor, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore)
+ static PassRefPtr<IDBCursorBackendImpl> create(PassRefPtr<IDBBackingStore::Cursor> cursor, IDBCursor::Direction direction, CursorType cursorType, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore)
{
- return adoptRef(new IDBCursorBackendImpl(database, keyRange, direction, query, isSerializedScriptValueCursor, transaction, objectStore));
+ return adoptRef(new IDBCursorBackendImpl(cursor, direction, cursorType, transaction, objectStore));
}
virtual ~IDBCursorBackendImpl();
virtual unsigned short direction() const;
virtual PassRefPtr<IDBKey> key() const;
- virtual PassRefPtr<IDBAny> value() const;
+ virtual PassRefPtr<IDBKey> primaryKey() const;
+ virtual PassRefPtr<SerializedScriptValue> value() const;
virtual void update(PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBCallbacks>, ExceptionCode&);
virtual void continueFunction(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, ExceptionCode&);
virtual void deleteFunction(PassRefPtr<IDBCallbacks>, ExceptionCode&);
private:
- IDBCursorBackendImpl(IDBSQLiteDatabase*, PassRefPtr<IDBKeyRange>, IDBCursor::Direction, PassOwnPtr<SQLiteStatement> query, bool isSerializedScriptValueCursor, IDBTransactionBackendInterface*, IDBObjectStoreBackendInterface*);
-
- bool currentRowExists();
- void loadCurrentRow();
- SQLiteDatabase& database() const;
+ IDBCursorBackendImpl(PassRefPtr<IDBBackingStore::Cursor>, IDBCursor::Direction, CursorType, IDBTransactionBackendInterface*, IDBObjectStoreBackendInterface*);
static void continueFunctionInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl>, PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>);
- static const int64_t InvalidId = -1;
-
- RefPtr<IDBSQLiteDatabase> m_database;
-
- RefPtr<IDBKeyRange> m_keyRange;
+ RefPtr<IDBBackingStore::Cursor> m_cursor;
IDBCursor::Direction m_direction;
- OwnPtr<SQLiteStatement> m_query;
- bool m_isSerializedScriptValueCursor;
- int64_t m_currentId;
-
- // The key in the objectStore or index that this cursor iterates over.
- RefPtr<IDBKey> m_currentKey;
-
- // m_isSerializedScriptValueCursor will only be available for object cursors.
- RefPtr<SerializedScriptValue> m_currentSerializedScriptValue;
-
- // FIXME: make the primary key available via script for all types of cursors.
- // For cursors on indices, this is the key in the objectstore that corresponds to the current entry in the index.
- RefPtr<IDBKey> m_currentIDBKeyValue;
-
+ CursorType m_cursorType;
RefPtr<IDBTransactionBackendInterface> m_transaction;
RefPtr<IDBObjectStoreBackendInterface> m_objectStore;
};
diff --git a/Source/WebCore/storage/IDBCursorBackendInterface.h b/Source/WebCore/storage/IDBCursorBackendInterface.h
index 0d132ca..6cdab42 100644
--- a/Source/WebCore/storage/IDBCursorBackendInterface.h
+++ b/Source/WebCore/storage/IDBCursorBackendInterface.h
@@ -45,9 +45,17 @@ class IDBCursorBackendInterface : public ThreadSafeShared<IDBCursorBackendInterf
public:
virtual ~IDBCursorBackendInterface() {}
+ enum CursorType {
+ InvalidCursorType = 0,
+ IndexCursor,
+ IndexKeyCursor,
+ ObjectStoreCursor
+ };
+
virtual unsigned short direction() const = 0;
virtual PassRefPtr<IDBKey> key() const = 0;
- virtual PassRefPtr<IDBAny> value() const = 0;
+ virtual PassRefPtr<IDBKey> primaryKey() const = 0;
+ virtual PassRefPtr<SerializedScriptValue> value() const = 0;
virtual void update(PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0;
virtual void continueFunction(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0;
diff --git a/Source/WebCore/storage/IDBErrorEvent.cpp b/Source/WebCore/storage/IDBCursorWithValue.cpp
index e576fa8..d3c3bd8 100644
--- a/Source/WebCore/storage/IDBErrorEvent.cpp
+++ b/Source/WebCore/storage/IDBCursorWithValue.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,9 +10,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -27,32 +24,29 @@
*/
#include "config.h"
-#include "IDBErrorEvent.h"
+#include "IDBCursorWithValue.h"
#if ENABLE(INDEXED_DATABASE)
-#include "EventNames.h"
-#include "IDBAny.h"
-#include "IDBDatabaseError.h"
+#include "IDBCursorBackendInterface.h"
+#include "IDBKey.h"
namespace WebCore {
-PassRefPtr<IDBErrorEvent> IDBErrorEvent::create(PassRefPtr<IDBAny> source, const IDBDatabaseError& error)
+PassRefPtr<IDBCursorWithValue> IDBCursorWithValue::create(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
{
- return adoptRef(new IDBErrorEvent(source, error));
+ return adoptRef(new IDBCursorWithValue(backend, request, source, transaction));
}
-IDBErrorEvent::IDBErrorEvent(PassRefPtr<IDBAny> source, const IDBDatabaseError& error)
- : IDBEvent(eventNames().errorEvent, source, true)
- , m_code(error.code())
- , m_message(error.message())
+IDBCursorWithValue::IDBCursorWithValue(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBAny* source, IDBTransaction* transaction)
+ : IDBCursor(backend, request, source, transaction)
{
}
-IDBErrorEvent::~IDBErrorEvent()
+IDBCursorWithValue::~IDBCursorWithValue()
{
}
} // namespace WebCore
-#endif
+#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/storage/IDBSQLiteDatabase.h b/Source/WebCore/storage/IDBCursorWithValue.h
index 0556506..d0610bd 100644
--- a/Source/WebCore/storage/IDBSQLiteDatabase.h
+++ b/Source/WebCore/storage/IDBCursorWithValue.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,40 +23,29 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBSQLiteDatabase_h
-#define IDBSQLiteDatabase_h
+#ifndef IDBCursorWithValue_h
+#define IDBCursorWithValue_h
#if ENABLE(INDEXED_DATABASE)
-#include "SQLiteDatabase.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
+#include "IDBCursor.h"
namespace WebCore {
-class IDBFactoryBackendImpl;
-
-class IDBSQLiteDatabase : public RefCounted<IDBSQLiteDatabase> {
+class IDBCursorWithValue : public IDBCursor {
public:
- static PassRefPtr<IDBSQLiteDatabase> create(String identifier, IDBFactoryBackendImpl* factory)
- {
- return adoptRef(new IDBSQLiteDatabase(identifier, factory));
- }
- ~IDBSQLiteDatabase();
+ static PassRefPtr<IDBCursorWithValue> create(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*);
+ virtual ~IDBCursorWithValue();
- SQLiteDatabase& db() { return m_db; }
+ // The value attribute defined in the IDL is simply implemented in IDBCursor (but not exposed via
+ // its IDL). This is to make the implementation more simple while matching what the spec says.
private:
- IDBSQLiteDatabase(String identifier, IDBFactoryBackendImpl* factory);
-
- SQLiteDatabase m_db;
- String m_identifier;
- RefPtr<IDBFactoryBackendImpl> m_factory;
+ IDBCursorWithValue(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBAny* source, IDBTransaction*);
};
} // namespace WebCore
#endif
-#endif // IDBSQLiteDatabase_h
+#endif // IDBCursorWithValue_h
diff --git a/Source/WebCore/storage/IDBEvent.idl b/Source/WebCore/storage/IDBCursorWithValue.idl
index 4dd552e..811215a 100644
--- a/Source/WebCore/storage/IDBEvent.idl
+++ b/Source/WebCore/storage/IDBCursorWithValue.idl
@@ -10,9 +10,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -30,7 +27,7 @@ module storage {
interface [
Conditional=INDEXED_DATABASE
- ] IDBEvent : Event {
- readonly attribute IDBAny source;
+ ] IDBCursorWithValue : IDBCursor {
+ readonly attribute SerializedScriptValue value;
};
}
diff --git a/Source/WebCore/storage/IDBDatabase.cpp b/Source/WebCore/storage/IDBDatabase.cpp
index 9a5eb6c..fe8d350 100644
--- a/Source/WebCore/storage/IDBDatabase.cpp
+++ b/Source/WebCore/storage/IDBDatabase.cpp
@@ -26,13 +26,17 @@
#include "config.h"
#include "IDBDatabase.h"
+#include "Document.h"
+#include "EventQueue.h"
#include "IDBAny.h"
#include "IDBDatabaseError.h"
#include "IDBDatabaseException.h"
+#include "IDBEventDispatcher.h"
#include "IDBFactoryBackendInterface.h"
#include "IDBIndex.h"
#include "IDBObjectStore.h"
-#include "IDBRequest.h"
+#include "IDBVersionChangeEvent.h"
+#include "IDBVersionChangeRequest.h"
#include "IDBTransaction.h"
#include "ScriptExecutionContext.h"
#include <limits>
@@ -58,7 +62,6 @@ IDBDatabase::IDBDatabase(ScriptExecutionContext* context, PassRefPtr<IDBDatabase
IDBDatabase::~IDBDatabase()
{
- ASSERT(m_stopped);
}
void IDBDatabase::setSetVersionTransaction(IDBTransaction* transaction)
@@ -97,10 +100,10 @@ void IDBDatabase::deleteObjectStore(const String& name, ExceptionCode& ec)
m_backend->deleteObjectStore(name, m_setVersionTransaction->backend(), ec);
}
-PassRefPtr<IDBRequest> IDBDatabase::setVersion(ScriptExecutionContext* context, const String& version, ExceptionCode& ec)
+PassRefPtr<IDBVersionChangeRequest> IDBDatabase::setVersion(ScriptExecutionContext* context, const String& version, ExceptionCode& ec)
{
- RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), 0);
- m_backend->setVersion(version, request, ec);
+ RefPtr<IDBVersionChangeRequest> request = IDBVersionChangeRequest::create(context, IDBAny::create(this), version);
+ m_backend->setVersion(version, request, this, ec);
return request;
}
@@ -136,7 +139,27 @@ PassRefPtr<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* cont
void IDBDatabase::close()
{
+ if (m_noNewTransactions)
+ return;
+
+ ASSERT(scriptExecutionContext()->isDocument());
+ EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue();
+ // Remove any pending versionchange events scheduled to fire on this
+ // connection. They would have been scheduled by the backend when another
+ // connection called setVersion, but the frontend connection is being
+ // closed before they could fire.
+ for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
+ bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get());
+ ASSERT_UNUSED(removed, removed);
+ }
+
m_noNewTransactions = true;
+ m_backend->close(this);
+}
+
+void IDBDatabase::onVersionChange(const String& version)
+{
+ enqueueEvent(IDBVersionChangeEvent::create(version, eventNames().versionchangeEvent));
}
bool IDBDatabase::hasPendingActivity() const
@@ -149,6 +172,30 @@ bool IDBDatabase::hasPendingActivity() const
return !m_stopped || ActiveDOMObject::hasPendingActivity();
}
+void IDBDatabase::open()
+{
+ m_backend->open(this);
+}
+
+void IDBDatabase::enqueueEvent(PassRefPtr<Event> event)
+{
+ ASSERT(scriptExecutionContext()->isDocument());
+ EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue();
+ event->setTarget(this);
+ eventQueue->enqueueEvent(event.get());
+ m_enqueuedEvents.append(event);
+}
+
+bool IDBDatabase::dispatchEvent(PassRefPtr<Event> event)
+{
+ ASSERT(event->type() == eventNames().versionchangeEvent);
+ for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
+ if (m_enqueuedEvents[i].get() == event.get())
+ m_enqueuedEvents.remove(i);
+ }
+ return EventTarget::dispatchEvent(event.get());
+}
+
void IDBDatabase::stop()
{
// Stop fires at a deterministic time, so we need to call close in it.
diff --git a/Source/WebCore/storage/IDBDatabase.h b/Source/WebCore/storage/IDBDatabase.h
index f90ddd3..51f1d23 100644
--- a/Source/WebCore/storage/IDBDatabase.h
+++ b/Source/WebCore/storage/IDBDatabase.h
@@ -32,6 +32,7 @@
#include "EventTarget.h"
#include "ExceptionCode.h"
#include "IDBDatabaseBackendInterface.h"
+#include "IDBDatabaseCallbacks.h"
#include "IDBObjectStore.h"
#include "IDBTransaction.h"
#include "OptionsObject.h"
@@ -43,10 +44,10 @@
namespace WebCore {
-class IDBAny;
-class IDBRequest;
+class IDBVersionChangeRequest;
+class ScriptExecutionContext;
-class IDBDatabase : public RefCounted<IDBDatabase>, public EventTarget, public ActiveDOMObject {
+class IDBDatabase : public IDBDatabaseCallbacks, public EventTarget, public ActiveDOMObject {
public:
static PassRefPtr<IDBDatabase> create(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendInterface>);
~IDBDatabase();
@@ -66,11 +67,15 @@ public:
PassRefPtr<IDBObjectStore> createObjectStore(const String& name, const OptionsObject&, ExceptionCode&);
void deleteObjectStore(const String& name, ExceptionCode&);
- PassRefPtr<IDBRequest> setVersion(ScriptExecutionContext*, const String& version, ExceptionCode&);
+ PassRefPtr<IDBVersionChangeRequest> setVersion(ScriptExecutionContext*, const String& version, ExceptionCode&);
void close();
DEFINE_ATTRIBUTE_EVENT_LISTENER(abort);
DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(versionchange);
+
+ // IDBDatabaseCallbacks
+ virtual void onVersionChange(const String& requestedVersion);
// ActiveDOMObject
virtual bool hasPendingActivity() const;
@@ -80,8 +85,14 @@ public:
virtual IDBDatabase* toIDBDatabase() { return this; }
virtual ScriptExecutionContext* scriptExecutionContext() const;
- using RefCounted<IDBDatabase>::ref;
- using RefCounted<IDBDatabase>::deref;
+
+ void open();
+ void enqueueEvent(PassRefPtr<Event>);
+ bool dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) { return EventTarget::dispatchEvent(event, ec); }
+ virtual bool dispatchEvent(PassRefPtr<Event>);
+
+ using RefCounted<IDBDatabaseCallbacks>::ref;
+ using RefCounted<IDBDatabaseCallbacks>::deref;
private:
IDBDatabase(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendInterface>);
@@ -99,6 +110,10 @@ private:
bool m_stopped;
EventTargetData m_eventTargetData;
+
+ // Keep track of the versionchange events waiting to be fired on this
+ // database so that we can cancel them if the database closes.
+ Vector<RefPtr<Event> > m_enqueuedEvents;
};
} // namespace WebCore
diff --git a/Source/WebCore/storage/IDBDatabase.idl b/Source/WebCore/storage/IDBDatabase.idl
index 3922144..a07fda7 100644
--- a/Source/WebCore/storage/IDBDatabase.idl
+++ b/Source/WebCore/storage/IDBDatabase.idl
@@ -35,12 +35,13 @@ module storage {
attribute EventListener onabort;
attribute EventListener onerror;
+ attribute EventListener onversionchange;
IDBObjectStore createObjectStore(in DOMString name, in [Optional] OptionsObject options)
raises (IDBDatabaseException);
void deleteObjectStore(in DOMString name)
raises (IDBDatabaseException);
- [CallWith=ScriptExecutionContext] IDBRequest setVersion(in DOMString version)
+ [CallWith=ScriptExecutionContext] IDBVersionChangeRequest setVersion(in DOMString version)
raises (IDBDatabaseException);
[CallWith=ScriptExecutionContext] IDBTransaction transaction(in [Optional] DOMStringList storeNames, in [Optional] unsigned short mode)
raises (IDBDatabaseException);
@@ -53,7 +54,7 @@ module storage {
void removeEventListener(in DOMString type,
in EventListener listener,
in boolean useCapture);
- boolean dispatchEvent(in Event evt)
+ boolean dispatchEvent(in Event evt)
raises(EventException);
};
diff --git a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp
index 133c5ae..94e305b 100644
--- a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp
@@ -30,65 +30,39 @@
#include "CrossThreadTask.h"
#include "DOMStringList.h"
+#include "IDBBackingStore.h"
#include "IDBDatabaseException.h"
#include "IDBFactoryBackendImpl.h"
#include "IDBObjectStoreBackendImpl.h"
-#include "IDBSQLiteDatabase.h"
#include "IDBTransactionBackendImpl.h"
#include "IDBTransactionCoordinator.h"
-#include "SQLiteStatement.h"
-#include "SQLiteTransaction.h"
namespace WebCore {
-static bool extractMetaData(SQLiteDatabase& sqliteDatabase, const String& name, String& foundVersion, int64& foundId)
-{
- SQLiteStatement databaseQuery(sqliteDatabase, "SELECT id, version FROM Databases WHERE name = ?");
- if (databaseQuery.prepare() != SQLResultOk) {
- ASSERT_NOT_REACHED();
- return false;
+class IDBDatabaseBackendImpl::PendingSetVersionCall : public RefCounted<PendingSetVersionCall> {
+public:
+ static PassRefPtr<PendingSetVersionCall> create(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
+ {
+ return adoptRef(new PendingSetVersionCall(version, callbacks, databaseCallbacks));
}
- databaseQuery.bindText(1, name);
- if (databaseQuery.step() != SQLResultRow)
- return false;
-
- foundId = databaseQuery.getColumnInt64(0);
- foundVersion = databaseQuery.getColumnText(1);
-
- if (databaseQuery.step() == SQLResultRow)
- ASSERT_NOT_REACHED();
- return true;
-}
-
-static bool setMetaData(SQLiteDatabase& sqliteDatabase, const String& name, const String& version, int64_t& rowId)
-{
- ASSERT(!name.isNull());
- ASSERT(!version.isNull());
-
- String sql = rowId != IDBDatabaseBackendImpl::InvalidId ? "UPDATE Databases SET name = ?, version = ? WHERE id = ?"
- : "INSERT INTO Databases (name, description, version) VALUES (?, '', ?)";
- SQLiteStatement query(sqliteDatabase, sql);
- if (query.prepare() != SQLResultOk) {
- ASSERT_NOT_REACHED();
- return false;
+ String version() { return m_version; }
+ PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
+ PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks() { return m_databaseCallbacks; }
+
+private:
+ PendingSetVersionCall(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
+ : m_version(version)
+ , m_callbacks(callbacks)
+ , m_databaseCallbacks(databaseCallbacks)
+ {
}
+ String m_version;
+ RefPtr<IDBCallbacks> m_callbacks;
+ RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
+};
- query.bindText(1, name);
- query.bindText(2, version);
- if (rowId != IDBDatabaseBackendImpl::InvalidId)
- query.bindInt64(3, rowId);
-
- if (query.step() != SQLResultDone)
- return false;
-
- if (rowId == IDBDatabaseBackendImpl::InvalidId)
- rowId = sqliteDatabase.lastInsertRowID();
-
- return true;
-}
-
-IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBSQLiteDatabase* sqliteDatabase, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
- : m_sqliteDatabase(sqliteDatabase)
+IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBBackingStore* backingStore, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
+ : m_backingStore(backingStore)
, m_id(InvalidId)
, m_name(name)
, m_version("")
@@ -98,9 +72,9 @@ IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBSQLiteData
{
ASSERT(!m_name.isNull());
- bool success = extractMetaData(m_sqliteDatabase->db(), m_name, m_version, m_id);
+ bool success = m_backingStore->extractIDBDatabaseMetaData(m_name, m_version, m_id);
ASSERT_UNUSED(success, success == (m_id != InvalidId));
- if (!setMetaData(m_sqliteDatabase->db(), m_name, m_version, m_id))
+ if (!m_backingStore->setIDBDatabaseMetaData(m_name, m_version, m_id, m_id == InvalidId))
ASSERT_NOT_REACHED(); // FIXME: Need better error handling.
loadObjectStores();
}
@@ -110,9 +84,9 @@ IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
m_factory->removeIDBDatabaseBackend(m_identifier);
}
-SQLiteDatabase& IDBDatabaseBackendImpl::sqliteDatabase() const
+PassRefPtr<IDBBackingStore> IDBDatabaseBackendImpl::backingStore() const
{
- return m_sqliteDatabase->db();
+ return m_backingStore;
}
PassRefPtr<DOMStringList> IDBDatabaseBackendImpl::objectStoreNames() const
@@ -132,7 +106,7 @@ PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObject
return 0;
}
- RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_sqliteDatabase.get(), name, keyPath, autoIncrement);
+ RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_backingStore.get(), name, keyPath, autoIncrement);
ASSERT(objectStore->name() == name);
RefPtr<IDBDatabaseBackendImpl> database = this;
@@ -149,21 +123,14 @@ PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObject
void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
- SQLiteStatement insert(database->sqliteDatabase(), "INSERT INTO ObjectStores (name, keyPath, doAutoIncrement, databaseId) VALUES (?, ?, ?, ?)");
- if (insert.prepare() != SQLResultOk) {
- transaction->abort();
- return;
- }
- insert.bindText(1, objectStore->name());
- insert.bindText(2, objectStore->keyPath());
- insert.bindInt(3, static_cast<int>(objectStore->autoIncrement()));
- insert.bindInt64(4, database->id());
- if (insert.step() != SQLResultDone) {
+ int64_t objectStoreId;
+
+ if (!database->m_backingStore->createObjectStore(objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement(), database->id(), objectStoreId)) {
transaction->abort();
return;
}
- int64_t id = database->sqliteDatabase().lastInsertRowID();
- objectStore->setId(id);
+
+ objectStore->setId(objectStoreId);
transaction->didCompleteTaskEvents();
}
@@ -172,16 +139,6 @@ PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(c
return m_objectStores.get(name);
}
-static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id)
-{
- SQLiteStatement deleteQuery(db, sql);
- bool ok = deleteQuery.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling.
- deleteQuery.bindInt64(1, id);
- ok = deleteQuery.step() == SQLResultDone;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling.
-}
-
void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
{
RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name);
@@ -201,19 +158,31 @@ void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactio
void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
- doDelete(database->sqliteDatabase(), "DELETE FROM ObjectStores WHERE id = ?", objectStore->id());
- doDelete(database->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStore->id());
- doDelete(database->sqliteDatabase(), "DELETE FROM IndexData WHERE indexId IN (SELECT id FROM Indexes WHERE objectStoreId = ?)", objectStore->id());
- doDelete(database->sqliteDatabase(), "DELETE FROM Indexes WHERE objectStoreId = ?", objectStore->id());
-
+ database->m_backingStore->deleteObjectStore(objectStore->id());
transaction->didCompleteTaskEvents();
}
-void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec)
+void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, ExceptionCode& ec)
{
- RefPtr<IDBDatabaseBackendImpl> database = this;
RefPtr<IDBCallbacks> callbacks = prpCallbacks;
+ RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
+ if (!m_databaseCallbacksSet.contains(databaseCallbacks)) {
+ callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::ABORT_ERR, "Connection was closed before set version transaction was created"));
+ return;
+ }
+ for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
+ if (*it != databaseCallbacks)
+ (*it)->onVersionChange(version);
+ }
+ if (m_databaseCallbacksSet.size() > 1) {
+ callbacks->onBlocked();
+ RefPtr<PendingSetVersionCall> pendingSetVersionCall = PendingSetVersionCall::create(version, callbacks, databaseCallbacks);
+ m_pendingSetVersionCalls.append(pendingSetVersionCall);
+ return;
+ }
+
RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
+ RefPtr<IDBDatabaseBackendImpl> database = this;
RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStoreNames.get(), IDBTransaction::VERSION_CHANGE, this);
if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::setVersionInternal, database, version, callbacks, transaction),
createCallbackTask(&IDBDatabaseBackendImpl::resetVersion, database, m_version))) {
@@ -225,7 +194,7 @@ void IDBDatabaseBackendImpl::setVersionInternal(ScriptExecutionContext*, PassRef
{
int64_t databaseId = database->id();
database->m_version = version;
- if (!setMetaData(database->m_sqliteDatabase->db(), database->m_name, database->m_version, databaseId)) {
+ if (!database->m_backingStore->setIDBDatabaseMetaData(database->m_name, database->m_version, databaseId, databaseId == InvalidId)) {
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
transaction->abort();
@@ -247,29 +216,43 @@ PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(D
return IDBTransactionBackendImpl::create(objectStoreNames, mode, this);
}
-void IDBDatabaseBackendImpl::close()
+void IDBDatabaseBackendImpl::open(PassRefPtr<IDBDatabaseCallbacks> callbacks)
{
- // FIXME: Implement.
+ m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(callbacks));
}
-void IDBDatabaseBackendImpl::loadObjectStores()
+void IDBDatabaseBackendImpl::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
{
- SQLiteStatement objectStoresQuery(sqliteDatabase(), "SELECT id, name, keyPath, doAutoIncrement FROM ObjectStores WHERE databaseId = ?");
- bool ok = objectStoresQuery.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
- objectStoresQuery.bindInt64(1, m_id);
-
- while (objectStoresQuery.step() == SQLResultRow) {
- int64_t id = objectStoresQuery.getColumnInt64(0);
- String name = objectStoresQuery.getColumnText(1);
- String keyPath = objectStoresQuery.getColumnText(2);
- bool autoIncrement = !!objectStoresQuery.getColumnInt(3);
+ RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
+ ASSERT(m_databaseCallbacksSet.contains(callbacks));
+ m_databaseCallbacksSet.remove(callbacks);
+ if (m_databaseCallbacksSet.size() > 1)
+ return;
- m_objectStores.set(name, IDBObjectStoreBackendImpl::create(m_sqliteDatabase.get(), id, name, keyPath, autoIncrement));
+ while (!m_pendingSetVersionCalls.isEmpty()) {
+ ExceptionCode ec = 0;
+ RefPtr<PendingSetVersionCall> pendingSetVersionCall = m_pendingSetVersionCalls.takeFirst();
+ setVersion(pendingSetVersionCall->version(), pendingSetVersionCall->callbacks(), pendingSetVersionCall->databaseCallbacks(), ec);
+ ASSERT(!ec);
}
}
+void IDBDatabaseBackendImpl::loadObjectStores()
+{
+ Vector<int64_t> ids;
+ Vector<String> names;
+ Vector<String> keyPaths;
+ Vector<bool> autoIncrementFlags;
+ m_backingStore->getObjectStores(m_id, ids, names, keyPaths, autoIncrementFlags);
+
+ ASSERT(names.size() == ids.size());
+ ASSERT(keyPaths.size() == ids.size());
+ ASSERT(autoIncrementFlags.size() == ids.size());
+
+ for (size_t i = 0; i < ids.size(); i++)
+ m_objectStores.set(names[i], IDBObjectStoreBackendImpl::create(m_backingStore.get(), ids[i], names[i], keyPaths[i], autoIncrementFlags[i]));
+}
+
void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
{
ASSERT(database->m_objectStores.contains(objectStore->name()));
diff --git a/Source/WebCore/storage/IDBDatabaseBackendImpl.h b/Source/WebCore/storage/IDBDatabaseBackendImpl.h
index 8942968..f779125 100644
--- a/Source/WebCore/storage/IDBDatabaseBackendImpl.h
+++ b/Source/WebCore/storage/IDBDatabaseBackendImpl.h
@@ -28,31 +28,33 @@
#include "IDBCallbacks.h"
#include "IDBDatabase.h"
+#include <wtf/Deque.h>
#include <wtf/HashMap.h>
-#include <wtf/text/StringHash.h>
+#include <wtf/ListHashSet.h>
#if ENABLE(INDEXED_DATABASE)
namespace WebCore {
+class IDBBackingStore;
+class IDBDatabase;
class IDBFactoryBackendImpl;
class IDBObjectStoreBackendImpl;
-class IDBSQLiteDatabase;
class IDBTransactionCoordinator;
-class SQLiteDatabase;
class IDBDatabaseBackendImpl : public IDBDatabaseBackendInterface {
public:
- static PassRefPtr<IDBDatabaseBackendImpl> create(const String& name, IDBSQLiteDatabase* database, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
+ static PassRefPtr<IDBDatabaseBackendImpl> create(const String& name, IDBBackingStore* database, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
{
return adoptRef(new IDBDatabaseBackendImpl(name, database, coordinator, factory, uniqueIdentifier));
}
virtual ~IDBDatabaseBackendImpl();
- SQLiteDatabase& sqliteDatabase() const;
+ PassRefPtr<IDBBackingStore> backingStore() const;
static const int64_t InvalidId = 0;
int64_t id() const { return m_id; }
+ void open(PassRefPtr<IDBDatabaseCallbacks>);
virtual String name() const { return m_name; }
virtual String version() const { return m_version; }
@@ -60,15 +62,15 @@ public:
virtual PassRefPtr<IDBObjectStoreBackendInterface> createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface*, ExceptionCode&);
virtual void deleteObjectStore(const String& name, IDBTransactionBackendInterface*, ExceptionCode&);
- virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, ExceptionCode&);
+ virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, ExceptionCode&);
virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* objectStoreNames, unsigned short mode, ExceptionCode&);
- virtual void close();
+ virtual void close(PassRefPtr<IDBDatabaseCallbacks>);
PassRefPtr<IDBObjectStoreBackendInterface> objectStore(const String& name);
IDBTransactionCoordinator* transactionCoordinator() const { return m_transactionCoordinator.get(); }
private:
- IDBDatabaseBackendImpl(const String& name, IDBSQLiteDatabase* database, IDBTransactionCoordinator*, IDBFactoryBackendImpl*, const String& uniqueIdentifier);
+ IDBDatabaseBackendImpl(const String& name, IDBBackingStore* database, IDBTransactionCoordinator*, IDBFactoryBackendImpl*, const String& uniqueIdentifier);
void loadObjectStores();
@@ -81,7 +83,7 @@ private:
static void addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl>, PassRefPtr<IDBObjectStoreBackendImpl>);
static void resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl>, const String& version);
- RefPtr<IDBSQLiteDatabase> m_sqliteDatabase;
+ RefPtr<IDBBackingStore> m_backingStore;
int64 m_id;
String m_name;
String m_version;
@@ -94,6 +96,12 @@ private:
ObjectStoreMap m_objectStores;
RefPtr<IDBTransactionCoordinator> m_transactionCoordinator;
+
+ class PendingSetVersionCall;
+ Deque<RefPtr<PendingSetVersionCall> > m_pendingSetVersionCalls;
+
+ typedef ListHashSet<RefPtr<IDBDatabaseCallbacks> > DatabaseCallbacksSet;
+ DatabaseCallbacksSet m_databaseCallbacksSet;
};
} // namespace WebCore
diff --git a/Source/WebCore/storage/IDBDatabaseBackendInterface.h b/Source/WebCore/storage/IDBDatabaseBackendInterface.h
index 9cc7230..ad6fdc7 100644
--- a/Source/WebCore/storage/IDBDatabaseBackendInterface.h
+++ b/Source/WebCore/storage/IDBDatabaseBackendInterface.h
@@ -38,9 +38,9 @@ namespace WebCore {
class DOMStringList;
class Frame;
class IDBCallbacks;
+class IDBDatabaseCallbacks;
class IDBObjectStoreBackendInterface;
class IDBTransactionBackendInterface;
-class IDBTransactionCallbacks;
// This class is shared by IDBDatabase (async) and IDBDatabaseSync (sync).
// This is implemented by IDBDatabaseBackendImpl and optionally others (in order to proxy
@@ -56,9 +56,11 @@ public:
virtual PassRefPtr<IDBObjectStoreBackendInterface> createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface*, ExceptionCode&) = 0;
virtual void deleteObjectStore(const String& name, IDBTransactionBackendInterface*, ExceptionCode&) = 0;
- virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0;
+ virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBDatabaseCallbacks>, ExceptionCode&) = 0;
virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* storeNames, unsigned short mode, ExceptionCode&) = 0;
- virtual void close() = 0;
+ virtual void close(PassRefPtr<IDBDatabaseCallbacks>) = 0;
+
+ virtual void open(PassRefPtr<IDBDatabaseCallbacks>) = 0;
};
} // namespace WebCore
diff --git a/Source/WebCore/storage/IDBSQLiteDatabase.cpp b/Source/WebCore/storage/IDBDatabaseCallbacks.h
index e881917..b3f244f 100644
--- a/Source/WebCore/storage/IDBSQLiteDatabase.cpp
+++ b/Source/WebCore/storage/IDBDatabaseCallbacks.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,26 +23,25 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include "config.h"
-#include "IDBSQLiteDatabase.h"
+#ifndef IDBDatabaseCallbacks_h
+#define IDBDatabaseCallbacks_h
#if ENABLE(INDEXED_DATABASE)
-#include "IDBFactoryBackendImpl.h"
+#include "PlatformString.h"
+#include <wtf/RefCounted.h>
namespace WebCore {
-IDBSQLiteDatabase::IDBSQLiteDatabase(String identifier, IDBFactoryBackendImpl* factory)
- : m_identifier(identifier)
- , m_factory(factory)
-{
-}
+class IDBDatabaseCallbacks : public RefCounted<IDBDatabaseCallbacks> {
+public:
+ virtual ~IDBDatabaseCallbacks() { }
-IDBSQLiteDatabase::~IDBSQLiteDatabase()
-{
- m_factory->removeSQLiteDatabase(m_identifier);
-}
+ virtual void onVersionChange(const String& version) = 0;
+};
} // namespace WebCore
-#endif // ENABLE(INDEXED_DATABASE)
+#endif
+
+#endif // IDBDatabaseCallbacks_h
diff --git a/Source/WebCore/storage/IDBDatabaseException.h b/Source/WebCore/storage/IDBDatabaseException.h
index 174fe5b..21991ee 100644
--- a/Source/WebCore/storage/IDBDatabaseException.h
+++ b/Source/WebCore/storage/IDBDatabaseException.h
@@ -43,6 +43,7 @@ public:
static const int IDBDatabaseExceptionMax = 1299;
enum IDBDatabaseExceptionCode {
+ NO_ERR = IDBDatabaseExceptionOffset + 0,
UNKNOWN_ERR = IDBDatabaseExceptionOffset + 1,
NON_TRANSIENT_ERR = IDBDatabaseExceptionOffset + 2,
NOT_FOUND_ERR = IDBDatabaseExceptionOffset + 3,
@@ -54,7 +55,8 @@ public:
TRANSIENT_ERR = IDBDatabaseExceptionOffset + 9,
TIMEOUT_ERR = IDBDatabaseExceptionOffset + 10,
DEADLOCK_ERR = IDBDatabaseExceptionOffset + 11,
- READ_ONLY_ERR = IDBDatabaseExceptionOffset + 12
+ READ_ONLY_ERR = IDBDatabaseExceptionOffset + 12,
+ ABORT_ERR = IDBDatabaseExceptionOffset + 13
};
static int ErrorCodeToExceptionCode(int errorCode)
diff --git a/Source/WebCore/storage/IDBDatabaseException.idl b/Source/WebCore/storage/IDBDatabaseException.idl
index 9027e05..d603057 100644
--- a/Source/WebCore/storage/IDBDatabaseException.idl
+++ b/Source/WebCore/storage/IDBDatabaseException.idl
@@ -39,6 +39,7 @@ module storage {
[DontEnum] DOMString toString();
#endif
+ const unsigned short NO_ERR = 0;
const unsigned short UNKNOWN_ERR = 1;
const unsigned short NON_TRANSIENT_ERR = 2;
const unsigned short NOT_FOUND_ERR = 3;
@@ -51,6 +52,7 @@ module storage {
const unsigned short TIMEOUT_ERR = 10;
const unsigned short DEADLOCK_ERR = 11;
const unsigned short READ_ONLY_ERR = 12;
+ const unsigned short ABORT_ERR = 13;
};
}
diff --git a/Source/WebCore/storage/IDBErrorEvent.h b/Source/WebCore/storage/IDBErrorEvent.h
deleted file mode 100644
index 648da8b..0000000
--- a/Source/WebCore/storage/IDBErrorEvent.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef IDBErrorEvent_h
-#define IDBErrorEvent_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBEvent.h"
-#include "PlatformString.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class IDBAny;
-class IDBDatabaseError;
-
-class IDBErrorEvent : public IDBEvent {
-public:
- static PassRefPtr<IDBErrorEvent> create(PassRefPtr<IDBAny> source, const IDBDatabaseError&);
- // FIXME: Need to allow creation of these events from JS.
- virtual ~IDBErrorEvent();
-
- unsigned short code() const { return m_code; }
- String message() { return m_message; }
-
- virtual bool isIDBErrorEvent() const { return true; }
-
-private:
- IDBErrorEvent(PassRefPtr<IDBAny> source, const IDBDatabaseError&);
-
- unsigned short m_code;
- String m_message;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBEvent_h
diff --git a/Source/WebCore/storage/IDBEvent.h b/Source/WebCore/storage/IDBEvent.h
deleted file mode 100644
index d28885b..0000000
--- a/Source/WebCore/storage/IDBEvent.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef IDBEvent_h
-#define IDBEvent_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "Event.h"
-#include "EventTarget.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
-#include <wtf/Vector.h>
-
-namespace WebCore {
-
-class IDBAny;
-
-class IDBEvent : public Event {
-public:
- virtual ~IDBEvent();
-
- PassRefPtr<IDBAny> source();
- bool dispatch(Vector<RefPtr<EventTarget> >&); // The target first and then its ancestors in order of how the event bubbles.
-
-protected:
- IDBEvent(const AtomicString& type, PassRefPtr<IDBAny> source, bool canBubble);
-
-private:
- RefPtr<IDBAny> m_source;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBEvent_h
diff --git a/Source/WebCore/storage/IDBEvent.cpp b/Source/WebCore/storage/IDBEventDispatcher.cpp
index a7f3db1..7263467 100644
--- a/Source/WebCore/storage/IDBEvent.cpp
+++ b/Source/WebCore/storage/IDBEventDispatcher.cpp
@@ -27,53 +27,39 @@
*/
#include "config.h"
-#include "IDBEvent.h"
+#include "IDBEventDispatcher.h"
#if ENABLE(INDEXED_DATABASE)
-#include "IDBAny.h"
+#include "Event.h"
+#include "EventTarget.h"
namespace WebCore {
-IDBEvent::IDBEvent(const AtomicString& type, PassRefPtr<IDBAny> source, bool canBubble)
- : Event(type, canBubble, true)
- , m_source(source)
-{
-}
-
-IDBEvent::~IDBEvent()
-{
-}
-
-PassRefPtr<IDBAny> IDBEvent::source()
-{
- return m_source;
-}
-
-bool IDBEvent::dispatch(Vector<RefPtr<EventTarget> >& eventTargets)
+bool IDBEventDispatcher::dispatch(Event* event, Vector<RefPtr<EventTarget> >& eventTargets)
{
size_t size = eventTargets.size();
ASSERT(size);
- setEventPhase(Event::CAPTURING_PHASE);
+ event->setEventPhase(Event::CAPTURING_PHASE);
for (size_t i = size - 1; i; --i) { // Don't do the first element.
- setCurrentTarget(eventTargets[i].get());
- eventTargets[i]->fireEventListeners(this);
- if (propagationStopped())
+ event->setCurrentTarget(eventTargets[i].get());
+ eventTargets[i]->fireEventListeners(event);
+ if (event->propagationStopped())
goto doneDispatching;
}
- setEventPhase(Event::AT_TARGET);
- setCurrentTarget(eventTargets[0].get());
- eventTargets[0]->fireEventListeners(this);
- if (propagationStopped() || !bubbles() || cancelBubble())
+ event->setEventPhase(Event::AT_TARGET);
+ event->setCurrentTarget(eventTargets[0].get());
+ eventTargets[0]->fireEventListeners(event);
+ if (event->propagationStopped() || !event->bubbles() || event->cancelBubble())
goto doneDispatching;
- setEventPhase(Event::BUBBLING_PHASE);
+ event->setEventPhase(Event::BUBBLING_PHASE);
for (size_t i = 1; i < size; ++i) { // Don't do the first element.
- setCurrentTarget(eventTargets[i].get());
- eventTargets[i]->fireEventListeners(this);
- if (propagationStopped() || cancelBubble())
+ event->setCurrentTarget(eventTargets[i].get());
+ eventTargets[i]->fireEventListeners(event);
+ if (event->propagationStopped() || event->cancelBubble())
goto doneDispatching;
}
@@ -93,12 +79,12 @@ bool IDBEvent::dispatch(Vector<RefPtr<EventTarget> >& eventTargets)
//
// (I think that so far webkit hasn't implemented the window.onerror
// feature yet, so you probably don't want to fire the separate error
- // event on the window until that has been implemented)."
+ // event on the window until that has been implemented)." -- Jonas Sicking
doneDispatching:
- setCurrentTarget(0);
- setEventPhase(0);
- return !defaultPrevented();
+ event->setCurrentTarget(0);
+ event->setEventPhase(0);
+ return !event->defaultPrevented();
}
} // namespace WebCore
diff --git a/Source/WebCore/storage/IDBAbortEvent.cpp b/Source/WebCore/storage/IDBEventDispatcher.h
index 980d656..00bf154 100644
--- a/Source/WebCore/storage/IDBAbortEvent.cpp
+++ b/Source/WebCore/storage/IDBEventDispatcher.h
@@ -26,30 +26,29 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include "config.h"
-#include "IDBAbortEvent.h"
+#ifndef IDBEventDispatcher_h
+#define IDBEventDispatcher_h
#if ENABLE(INDEXED_DATABASE)
-#include "EventNames.h"
-#include "IDBAny.h"
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
namespace WebCore {
-PassRefPtr<IDBAbortEvent> IDBAbortEvent::create(PassRefPtr<IDBAny> source)
-{
- return adoptRef(new IDBAbortEvent(source));
-}
+class Event;
+class EventTarget;
-IDBAbortEvent::IDBAbortEvent(PassRefPtr<IDBAny> source)
- : IDBEvent(eventNames().abortEvent, source, true)
-{
-}
+class IDBEventDispatcher {
+public:
+ static bool dispatch(Event*, Vector<RefPtr<EventTarget> >&); // The target first and then its ancestors in order of how the event bubbles.
-IDBAbortEvent::~IDBAbortEvent()
-{
-}
+private:
+ IDBEventDispatcher();
+};
} // namespace WebCore
-#endif
+#endif // ENABLE(INDEXED_DATABASE)
+
+#endif // IDBEventDispatcher_h
diff --git a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp
index 0e883cf..9127455 100644
--- a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp
@@ -30,13 +30,10 @@
#include "IDBFactoryBackendImpl.h"
#include "DOMStringList.h"
-#include "FileSystem.h"
+#include "IDBBackingStore.h"
#include "IDBDatabaseBackendImpl.h"
#include "IDBDatabaseException.h"
-#include "IDBSQLiteDatabase.h"
#include "IDBTransactionCoordinator.h"
-#include "SQLiteStatement.h"
-#include "SQLiteTransaction.h"
#include "SecurityOrigin.h"
#include <wtf/Threading.h>
#include <wtf/UnusedParam.h>
@@ -45,11 +42,11 @@
namespace WebCore {
-IDBFactoryBackendImpl::IDBFactoryBackendImpl()
+IDBFactoryBackendImpl::IDBFactoryBackendImpl()
: m_transactionCoordinator(IDBTransactionCoordinator::create())
{
}
-
+
IDBFactoryBackendImpl::~IDBFactoryBackendImpl()
{
}
@@ -60,151 +57,16 @@ void IDBFactoryBackendImpl::removeIDBDatabaseBackend(const String& uniqueIdentif
m_databaseBackendMap.remove(uniqueIdentifier);
}
-void IDBFactoryBackendImpl::removeSQLiteDatabase(const String& uniqueIdentifier)
-{
- ASSERT(m_sqliteDatabaseMap.contains(uniqueIdentifier));
- m_sqliteDatabaseMap.remove(uniqueIdentifier);
-}
-
-static PassRefPtr<IDBSQLiteDatabase> openSQLiteDatabase(SecurityOrigin* securityOrigin, const String& pathBase, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl* factory)
-{
- String path = ":memory:";
- if (!pathBase.isEmpty()) {
- if (!makeAllDirectories(pathBase)) {
- // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out.
- LOG_ERROR("Unabled to create LocalStorage database path %s", pathBase.utf8().data());
- return 0;
- }
-
- path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb");
- }
-
- RefPtr<IDBSQLiteDatabase> sqliteDatabase = IDBSQLiteDatabase::create(fileIdentifier, factory);
- if (!sqliteDatabase->db().open(path)) {
- // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out.
- LOG_ERROR("Failed to open database file %s for IndexedDB", path.utf8().data());
- return 0;
- }
-
- // FIXME: Error checking?
- sqliteDatabase->db().setMaximumSize(maximumSize);
- sqliteDatabase->db().turnOnIncrementalAutoVacuum();
-
- return sqliteDatabase.release();
-}
-
-static bool runCommands(SQLiteDatabase& sqliteDatabase, const char** commands, size_t numberOfCommands)
-{
- SQLiteTransaction transaction(sqliteDatabase, false);
- transaction.begin();
- for (size_t i = 0; i < numberOfCommands; ++i) {
- if (!sqliteDatabase.executeCommand(commands[i])) {
- LOG_ERROR("Failed to run the following command for IndexedDB: %s", commands[i]);
- return false;
- }
- }
- transaction.commit();
- return true;
-}
-
-static bool createTables(SQLiteDatabase& sqliteDatabase)
-{
- if (sqliteDatabase.tableExists("Databases"))
- return true;
- static const char* commands[] = {
- "CREATE TABLE Databases (id INTEGER PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL, version TEXT NOT NULL)",
- "CREATE UNIQUE INDEX Databases_name ON Databases(name)",
-
- "CREATE TABLE ObjectStores (id INTEGER PRIMARY KEY, name TEXT NOT NULL, keyPath TEXT, doAutoIncrement INTEGER NOT NULL, databaseId INTEGER NOT NULL REFERENCES Databases(id))",
- "CREATE UNIQUE INDEX ObjectStores_composit ON ObjectStores(databaseId, name)",
-
- "CREATE TABLE Indexes (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), name TEXT NOT NULL, keyPath TEXT, isUnique INTEGER NOT NULL)",
- "CREATE UNIQUE INDEX Indexes_composit ON Indexes(objectStoreId, name)",
-
- "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, value TEXT NOT NULL)",
- "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)",
-
- "CREATE TABLE IndexData (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))",
- "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)",
- "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId)",
- "CREATE INDEX IndexData_indexId ON IndexData(indexId)",
- };
-
- return runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0]));
-}
-
-static bool createMetaDataTable(SQLiteDatabase& sqliteDatabase)
+void IDBFactoryBackendImpl::addIDBBackingStore(const String& uniqueIdentifier, IDBBackingStore* backingStore)
{
- static const char* commands[] = {
- "CREATE TABLE MetaData (name TEXT PRIMARY KEY, value NONE)",
- "INSERT INTO MetaData VALUES ('version', 1)",
- };
-
- return runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0]));
+ ASSERT(!m_backingStoreMap.contains(uniqueIdentifier));
+ m_backingStoreMap.set(uniqueIdentifier, backingStore);
}
-static bool getDatabaseVersion(SQLiteDatabase& sqliteDatabase, int* databaseVersion)
+void IDBFactoryBackendImpl::removeIDBBackingStore(const String& uniqueIdentifier)
{
- SQLiteStatement query(sqliteDatabase, "SELECT value FROM MetaData WHERE name = 'version'");
- if (query.prepare() != SQLResultOk || query.step() != SQLResultRow)
- return false;
-
- *databaseVersion = query.getColumnInt(0);
- return query.finalize() == SQLResultOk;
-}
-
-static bool migrateDatabase(SQLiteDatabase& sqliteDatabase)
-{
- if (!sqliteDatabase.tableExists("MetaData")) {
- if (!createMetaDataTable(sqliteDatabase))
- return false;
- }
-
- int databaseVersion;
- if (!getDatabaseVersion(sqliteDatabase, &databaseVersion))
- return false;
-
- if (databaseVersion == 1) {
- static const char* commands[] = {
- "DROP TABLE IF EXISTS ObjectStoreData2",
- "CREATE TABLE ObjectStoreData2 (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, keyNumber REAL, value TEXT NOT NULL)",
- "INSERT INTO ObjectStoreData2 SELECT * FROM ObjectStoreData",
- "DROP TABLE ObjectStoreData", // This depends on SQLite not enforcing referential consistency.
- "ALTER TABLE ObjectStoreData2 RENAME TO ObjectStoreData",
- "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)",
- "DROP TABLE IF EXISTS IndexData2", // This depends on SQLite not enforcing referential consistency.
- "CREATE TABLE IndexData2 (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate REAL, keyNumber REAL, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))",
- "INSERT INTO IndexData2 SELECT * FROM IndexData",
- "DROP TABLE IndexData",
- "ALTER TABLE IndexData2 RENAME TO IndexData",
- "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)",
- "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId)",
- "CREATE INDEX IndexData_indexId ON IndexData(indexId)",
- "UPDATE MetaData SET value = 2 WHERE name = 'version'",
- };
-
- if (!runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0])))
- return false;
-
- databaseVersion = 2;
- }
-
- if (databaseVersion == 2) {
- // We need to make the ObjectStoreData.value be a BLOB instead of TEXT.
- static const char* commands[] = {
- "DROP TABLE IF EXISTS ObjectStoreData", // This drops associated indices.
- "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, keyNumber REAL, value BLOB NOT NULL)",
- "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)",
- "UPDATE MetaData SET value = 3 WHERE name = 'version'",
- };
-
- if (!runCommands(sqliteDatabase, commands, sizeof(commands) / sizeof(commands[0])))
- return false;
-
- databaseVersion = 3;
- }
-
- return true;
+ ASSERT(m_backingStoreMap.contains(uniqueIdentifier));
+ m_backingStoreMap.remove(uniqueIdentifier);
}
void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> securityOrigin, Frame*, const String& dataDir, int64_t maximumSize)
@@ -219,22 +81,19 @@ void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> ca
// FIXME: Everything from now on should be done on another thread.
- RefPtr<IDBSQLiteDatabase> sqliteDatabase;
- SQLiteDatabaseMap::iterator it2 = m_sqliteDatabaseMap.find(fileIdentifier);
- if (it2 != m_sqliteDatabaseMap.end())
- sqliteDatabase = it2->second;
+ RefPtr<IDBBackingStore> backingStore;
+ IDBBackingStoreMap::iterator it2 = m_backingStoreMap.find(fileIdentifier);
+ if (it2 != m_backingStoreMap.end())
+ backingStore = it2->second;
else {
- sqliteDatabase = openSQLiteDatabase(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this);
-
- if (!sqliteDatabase || !createTables(sqliteDatabase->db()) || !migrateDatabase(sqliteDatabase->db())) {
+ backingStore = IDBBackingStore::open(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this);
+ if (!backingStore) {
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
- m_sqliteDatabaseMap.set(fileIdentifier, 0);
return;
}
- m_sqliteDatabaseMap.set(fileIdentifier, sqliteDatabase.get());
}
- RefPtr<IDBDatabaseBackendImpl> databaseBackend = IDBDatabaseBackendImpl::create(name, sqliteDatabase.get(), m_transactionCoordinator.get(), this, uniqueIdentifier);
+ RefPtr<IDBDatabaseBackendImpl> databaseBackend = IDBDatabaseBackendImpl::create(name, backingStore.get(), m_transactionCoordinator.get(), this, uniqueIdentifier);
callbacks->onSuccess(databaseBackend.get());
m_databaseBackendMap.set(uniqueIdentifier, databaseBackend.get());
}
diff --git a/Source/WebCore/storage/IDBFactoryBackendImpl.h b/Source/WebCore/storage/IDBFactoryBackendImpl.h
index dcaf848..071bcf8 100644
--- a/Source/WebCore/storage/IDBFactoryBackendImpl.h
+++ b/Source/WebCore/storage/IDBFactoryBackendImpl.h
@@ -38,8 +38,8 @@ namespace WebCore {
class DOMStringList;
+class IDBBackingStore;
class IDBDatabaseBackendImpl;
-class IDBSQLiteDatabase;
class IDBTransactionCoordinator;
class IDBFactoryBackendImpl : public IDBFactoryBackendInterface {
@@ -52,7 +52,8 @@ public:
// Notifications from weak pointers.
void removeIDBDatabaseBackend(const String& uniqueIdentifier);
- void removeSQLiteDatabase(const String& uniqueIdentifier);
+ void addIDBBackingStore(const String& uniqueIdentifier, IDBBackingStore*);
+ void removeIDBBackingStore(const String& uniqueIdentifier);
virtual void open(const String& name, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, Frame*, const String& dataDir, int64_t maximumSize);
@@ -62,8 +63,8 @@ private:
typedef HashMap<String, IDBDatabaseBackendImpl*> IDBDatabaseBackendMap;
IDBDatabaseBackendMap m_databaseBackendMap;
- typedef HashMap<String, IDBSQLiteDatabase*> SQLiteDatabaseMap;
- SQLiteDatabaseMap m_sqliteDatabaseMap;
+ typedef HashMap<String, IDBBackingStore*> IDBBackingStoreMap;
+ IDBBackingStoreMap m_backingStoreMap;
RefPtr<IDBTransactionCoordinator> m_transactionCoordinator;
@@ -76,4 +77,3 @@ private:
#endif
#endif // IDBFactoryBackendImpl_h
-
diff --git a/Source/WebCore/storage/IDBIndex.cpp b/Source/WebCore/storage/IDBIndex.cpp
index 615767b..5f2ecf2 100644
--- a/Source/WebCore/storage/IDBIndex.cpp
+++ b/Source/WebCore/storage/IDBIndex.cpp
@@ -33,6 +33,7 @@
#include "IDBIndexBackendInterface.h"
#include "IDBKey.h"
#include "IDBKeyRange.h"
+#include "IDBObjectStore.h"
#include "IDBRequest.h"
#include "IDBTransaction.h"
@@ -40,11 +41,13 @@ namespace WebCore {
static const unsigned short defaultDirection = IDBCursor::NEXT;
-IDBIndex::IDBIndex(PassRefPtr<IDBIndexBackendInterface> backend, IDBTransaction* transaction)
+IDBIndex::IDBIndex(PassRefPtr<IDBIndexBackendInterface> backend, IDBObjectStore* objectStore, IDBTransaction* transaction)
: m_backend(backend)
+ , m_objectStore(objectStore)
, m_transaction(transaction)
{
ASSERT(m_backend);
+ ASSERT(m_objectStore);
ASSERT(m_transaction);
}
@@ -61,9 +64,12 @@ PassRefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, Pas
}
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
+ request->setCursorType(IDBCursorBackendInterface::IndexCursor);
m_backend->openCursor(keyRange, direction, request, m_transaction->backend(), ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
+ }
return request;
}
@@ -76,9 +82,12 @@ PassRefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context,
}
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
+ request->setCursorType(IDBCursorBackendInterface::IndexKeyCursor);
m_backend->openKeyCursor(keyRange, direction, request, m_transaction->backend(), ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
+ }
return request;
}
@@ -86,8 +95,10 @@ PassRefPtr<IDBRequest> IDBIndex::get(ScriptExecutionContext* context, PassRefPtr
{
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
m_backend->get(key, request, m_transaction->backend(), ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
+ }
return request;
}
@@ -95,8 +106,10 @@ PassRefPtr<IDBRequest> IDBIndex::getKey(ScriptExecutionContext* context, PassRef
{
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
m_backend->getKey(key, request, m_transaction->backend(), ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
+ }
return request;
}
diff --git a/Source/WebCore/storage/IDBIndex.h b/Source/WebCore/storage/IDBIndex.h
index 10b46f1..2af1805 100644
--- a/Source/WebCore/storage/IDBIndex.h
+++ b/Source/WebCore/storage/IDBIndex.h
@@ -37,17 +37,19 @@
namespace WebCore {
+class IDBObjectStore;
+
class IDBIndex : public RefCounted<IDBIndex> {
public:
- static PassRefPtr<IDBIndex> create(PassRefPtr<IDBIndexBackendInterface> backend, IDBTransaction* transaction)
+ static PassRefPtr<IDBIndex> create(PassRefPtr<IDBIndexBackendInterface> backend, IDBObjectStore* objectStore, IDBTransaction* transaction)
{
- return adoptRef(new IDBIndex(backend, transaction));
+ return adoptRef(new IDBIndex(backend, objectStore, transaction));
}
~IDBIndex();
// Implement the IDL
String name() const { return m_backend->name(); }
- String storeName() const { return m_backend->storeName(); }
+ IDBObjectStore* objectStore() const { return m_objectStore.get(); }
String keyPath() const { return m_backend->keyPath(); }
bool unique() const { return m_backend->unique(); }
@@ -64,9 +66,10 @@ public:
PassRefPtr<IDBRequest> getKey(ScriptExecutionContext*, PassRefPtr<IDBKey>, ExceptionCode&);
private:
- IDBIndex(PassRefPtr<IDBIndexBackendInterface>, IDBTransaction*);
+ IDBIndex(PassRefPtr<IDBIndexBackendInterface>, IDBObjectStore*, IDBTransaction*);
RefPtr<IDBIndexBackendInterface> m_backend;
+ RefPtr<IDBObjectStore> m_objectStore;
RefPtr<IDBTransaction> m_transaction;
};
diff --git a/Source/WebCore/storage/IDBIndex.idl b/Source/WebCore/storage/IDBIndex.idl
index 13089cc..6db50e1 100644
--- a/Source/WebCore/storage/IDBIndex.idl
+++ b/Source/WebCore/storage/IDBIndex.idl
@@ -29,7 +29,7 @@ module storage {
Conditional=INDEXED_DATABASE
] IDBIndex {
readonly attribute DOMString name;
- readonly attribute DOMString storeName;
+ readonly attribute IDBObjectStore objectStore;
readonly attribute DOMString keyPath;
readonly attribute boolean unique;
diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.cpp b/Source/WebCore/storage/IDBIndexBackendImpl.cpp
index 6eba189..62e5364 100644
--- a/Source/WebCore/storage/IDBIndexBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBIndexBackendImpl.cpp
@@ -29,6 +29,7 @@
#if ENABLE(INDEXED_DATABASE)
#include "CrossThreadTask.h"
+#include "IDBBackingStore.h"
#include "IDBCallbacks.h"
#include "IDBCursorBackendImpl.h"
#include "IDBDatabaseBackendImpl.h"
@@ -36,14 +37,11 @@
#include "IDBKey.h"
#include "IDBKeyRange.h"
#include "IDBObjectStoreBackendImpl.h"
-#include "IDBSQLiteDatabase.h"
-#include "SQLiteDatabase.h"
-#include "SQLiteStatement.h"
namespace WebCore {
-IDBIndexBackendImpl::IDBIndexBackendImpl(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique)
- : m_database(database)
+IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique)
+ : m_backingStore(backingStore)
, m_id(id)
, m_name(name)
, m_storeName(storeName)
@@ -52,8 +50,8 @@ IDBIndexBackendImpl::IDBIndexBackendImpl(IDBSQLiteDatabase* database, int64_t id
{
}
-IDBIndexBackendImpl::IDBIndexBackendImpl(IDBSQLiteDatabase* database, const String& name, const String& storeName, const String& keyPath, bool unique)
- : m_database(database)
+IDBIndexBackendImpl::IDBIndexBackendImpl(IDBBackingStore* backingStore, const String& name, const String& storeName, const String& keyPath, bool unique)
+ : m_backingStore(backingStore)
, m_id(InvalidId)
, m_name(name)
, m_storeName(storeName)
@@ -66,40 +64,26 @@ IDBIndexBackendImpl::~IDBIndexBackendImpl()
{
}
-void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBKeyRange> range, unsigned short untypedDirection, bool objectCursor, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
+void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBKeyRange> range, unsigned short untypedDirection, IDBCursorBackendInterface::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
- // Several files depend on this order of selects.
- String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ")
- + ("ObjectStoreData.value, ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ")
- + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id WHERE ";
-
- bool lowerBound = range && range->lower();
- bool upperBound = range && range->upper();
-
- if (lowerBound)
- sql += range->lower()->lowerCursorWhereFragment(range->lowerWhereClauseComparisonOperator(), "IndexData.");
- if (upperBound)
- sql += range->upper()->upperCursorWhereFragment(range->upperWhereClauseComparisonOperator(), "IndexData.");
- sql += "IndexData.indexId = ? ORDER BY ";
-
IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(untypedDirection);
- if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE)
- sql += "IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, IndexData.id";
- else
- sql += "IndexData.keyString DESC, IndexData.keyDate DESC, IndexData.keyNumber DESC, IndexData.id DESC";
-
- OwnPtr<SQLiteStatement> query = adoptPtr(new SQLiteStatement(index->sqliteDatabase(), sql));
- bool ok = query->prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
- int indexColumn = 1;
- if (lowerBound)
- indexColumn += range->lower()->bind(*query, indexColumn);
- if (upperBound)
- indexColumn += range->upper()->bind(*query, indexColumn);
- query->bindInt64(indexColumn, index->id());
-
- if (query->step() != SQLResultRow) {
+
+ RefPtr<IDBBackingStore::Cursor> backingStoreCursor;
+
+ switch (cursorType) {
+ case IDBCursorBackendInterface::IndexKeyCursor:
+ backingStoreCursor = index->m_backingStore->openIndexKeyCursor(index->id(), range.get(), direction);
+ break;
+ case IDBCursorBackendInterface::IndexCursor:
+ backingStoreCursor = index->m_backingStore->openIndexCursor(index->id(), range.get(), direction);
+ break;
+ case IDBCursorBackendInterface::ObjectStoreCursor:
+ case IDBCursorBackendInterface::InvalidCursorType:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ if (!backingStoreCursor) {
callbacks->onSuccess(SerializedScriptValue::nullValue());
return;
}
@@ -108,7 +92,7 @@ void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr
RefPtr<IDBObjectStoreBackendInterface> objectStore = transaction->objectStore(index->m_storeName, ec);
ASSERT(objectStore && !ec);
- RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(index->m_database.get(), range, direction, query.release(), objectCursor, transaction.get(), objectStore.get());
+ RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(backingStoreCursor.get(), direction, cursorType, transaction.get(), objectStore.get());
callbacks->onSuccess(cursor.release());
}
@@ -118,7 +102,7 @@ void IDBIndexBackendImpl::openCursor(PassRefPtr<IDBKeyRange> prpKeyRange, unsign
RefPtr<IDBKeyRange> keyRange = prpKeyRange;
RefPtr<IDBCallbacks> callbacks = prpCallbacks;
RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
- if (!transaction->scheduleTask(createCallbackTask(&openCursorInternal, index, keyRange, direction, true, callbacks, transaction)))
+ if (!transaction->scheduleTask(createCallbackTask(&openCursorInternal, index, keyRange, direction, IDBCursorBackendInterface::IndexCursor, callbacks, transaction)))
ec = IDBDatabaseException::NOT_ALLOWED_ERR;
}
@@ -128,33 +112,28 @@ void IDBIndexBackendImpl::openKeyCursor(PassRefPtr<IDBKeyRange> prpKeyRange, uns
RefPtr<IDBKeyRange> keyRange = prpKeyRange;
RefPtr<IDBCallbacks> callbacks = prpCallbacks;
RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
- if (!transaction->scheduleTask(createCallbackTask(&openCursorInternal, index, keyRange, direction, false, callbacks, transaction)))
+ if (!transaction->scheduleTask(createCallbackTask(&openCursorInternal, index, keyRange, direction, IDBCursorBackendInterface::IndexKeyCursor, callbacks, transaction)))
ec = IDBDatabaseException::NOT_ALLOWED_ERR;
}
void IDBIndexBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBKey> key, bool getObject, PassRefPtr<IDBCallbacks> callbacks)
{
- String sql = String("SELECT ")
- + (getObject ? "ObjectStoreData.value " : "ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ")
- + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id "
- + "WHERE IndexData.indexId = ? AND " + key->whereSyntax("IndexData.")
- + "ORDER BY IndexData.id LIMIT 1"; // Order by insertion order when all else fails.
- SQLiteStatement query(index->sqliteDatabase(), sql);
- bool ok = query.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
- query.bindInt64(1, index->id());
- key->bind(query, 2);
- if (query.step() != SQLResultRow) {
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index."));
- return;
+ // FIXME: Split getInternal into two functions, getting rid off |getObject|.
+ if (getObject) {
+ String value = index->m_backingStore->getObjectViaIndex(index->id(), *key);
+ if (value.isNull()) {
+ callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index."));
+ return;
+ }
+ callbacks->onSuccess(SerializedScriptValue::createFromWire(value));
+ } else {
+ RefPtr<IDBKey> keyResult = index->m_backingStore->getPrimaryKeyViaIndex(index->id(), *key);
+ if (!keyResult) {
+ callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index."));
+ return;
+ }
+ callbacks->onSuccess(keyResult.get());
}
-
- if (getObject)
- callbacks->onSuccess(SerializedScriptValue::createFromWire(query.getColumnText(0)));
- else
- callbacks->onSuccess(IDBKey::fromQuery(query, 0));
- ASSERT(query.step() != SQLResultRow);
}
void IDBIndexBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
@@ -175,34 +154,12 @@ void IDBIndexBackendImpl::getKey(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallba
ec = IDBDatabaseException::NOT_ALLOWED_ERR;
}
-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_database->db();
+ return !m_backingStore->keyExistsInIndex(m_id, *key);
}
} // namespace WebCore
diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.h b/Source/WebCore/storage/IDBIndexBackendImpl.h
index e640b5a..e2a06b8 100644
--- a/Source/WebCore/storage/IDBIndexBackendImpl.h
+++ b/Source/WebCore/storage/IDBIndexBackendImpl.h
@@ -26,27 +26,27 @@
#ifndef IDBIndexBackendImpl_h
#define IDBIndexBackendImpl_h
-#include "IDBIndexBackendInterface.h"
-
#if ENABLE(INDEXED_DATABASE)
+#include "IDBCursorBackendInterface.h"
+#include "IDBIndexBackendInterface.h"
+
namespace WebCore {
+class IDBBackingStore;
class IDBKey;
class IDBObjectStoreBackendImpl;
-class IDBSQLiteDatabase;
-class SQLiteDatabase;
class ScriptExecutionContext;
class IDBIndexBackendImpl : public IDBIndexBackendInterface {
public:
- static PassRefPtr<IDBIndexBackendImpl> create(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique)
+ static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique)
{
- return adoptRef(new IDBIndexBackendImpl(database, id, name, storeName, keyPath, unique));
+ return adoptRef(new IDBIndexBackendImpl(backingStore, id, name, storeName, keyPath, unique));
}
- static PassRefPtr<IDBIndexBackendImpl> create(IDBSQLiteDatabase* database, const String& name, const String& storeName, const String& keyPath, bool unique)
+ static PassRefPtr<IDBIndexBackendImpl> create(IDBBackingStore* backingStore, const String& name, const String& storeName, const String& keyPath, bool unique)
{
- return adoptRef(new IDBIndexBackendImpl(database, name, storeName, keyPath, unique));
+ return adoptRef(new IDBIndexBackendImpl(backingStore, name, storeName, keyPath, unique));
}
virtual ~IDBIndexBackendImpl();
@@ -56,6 +56,7 @@ public:
return m_id;
}
void setId(int64_t id) { m_id = id; }
+ bool hasValidId() const { return m_id != InvalidId; };
bool addingKeyAllowed(IDBKey*);
@@ -71,17 +72,15 @@ public:
virtual void getKey(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&);
private:
- IDBIndexBackendImpl(IDBSQLiteDatabase*, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique);
- IDBIndexBackendImpl(IDBSQLiteDatabase*, const String& name, const String& storeName, const String& keyPath, bool unique);
-
- SQLiteDatabase& sqliteDatabase() const;
+ IDBIndexBackendImpl(IDBBackingStore*, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique);
+ IDBIndexBackendImpl(IDBBackingStore*, const String& name, const String& storeName, const String& keyPath, bool unique);
- static void openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKeyRange>, unsigned short direction, bool objectCursor, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>);
+ static void openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKeyRange>, unsigned short direction, IDBCursorBackendInterface::CursorType, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>);
static void getInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKey>, bool getObject, PassRefPtr<IDBCallbacks>);
static const int64_t InvalidId = 0;
- RefPtr<IDBSQLiteDatabase> m_database;
+ RefPtr<IDBBackingStore> m_backingStore;
int64_t m_id;
String m_name;
diff --git a/Source/WebCore/storage/IDBKey.cpp b/Source/WebCore/storage/IDBKey.cpp
index 5f45543..35156e8 100644
--- a/Source/WebCore/storage/IDBKey.cpp
+++ b/Source/WebCore/storage/IDBKey.cpp
@@ -28,9 +28,6 @@
#if ENABLE(INDEXED_DATABASE)
-#include "SQLiteStatement.h"
-#include "SerializedScriptValue.h"
-
namespace WebCore {
IDBKey::IDBKey()
@@ -42,35 +39,21 @@ IDBKey::~IDBKey()
{
}
-PassRefPtr<IDBKey> IDBKey::fromQuery(SQLiteStatement& query, int baseColumn)
-{
- if (query.columnCount() <= baseColumn)
- return 0;
-
- if (!query.isColumnNull(baseColumn))
- return IDBKey::createString(query.getColumnText(baseColumn));
-
- if (!query.isColumnNull(baseColumn + 1))
- return IDBKey::createDate(query.getColumnDouble(baseColumn + 1));
-
- if (!query.isColumnNull(baseColumn + 2))
- return IDBKey::createNumber(query.getColumnDouble(baseColumn + 2));
-
- return IDBKey::createNull();
-}
-
-bool IDBKey::isEqual(IDBKey* other)
+bool IDBKey::isLessThan(const IDBKey* other) const
{
- if (!other || other->m_type != m_type)
+ ASSERT(other);
+ if (other->m_type < m_type)
+ return true;
+ if (other->m_type > m_type)
return false;
switch (m_type) {
case StringType:
- return other->m_string == m_string;
+ return codePointCompare(other->m_string, m_string) > 0;
case DateType:
- return other->m_date == m_date;
+ return other->m_date > m_date;
case NumberType:
- return other->m_number == m_number;
+ return other->m_number > m_number;
case NullType:
return true;
}
@@ -79,106 +62,24 @@ bool IDBKey::isEqual(IDBKey* other)
return false;
}
-String IDBKey::whereSyntax(String qualifiedTableName) const
-{
- switch (m_type) {
- case IDBKey::StringType:
- return qualifiedTableName + "keyString = ? AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL ";
- case IDBKey::NumberType:
- return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber = ? ";
- case IDBKey::DateType:
- return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate = ? AND " + qualifiedTableName + "keyNumber IS NULL ";
- case IDBKey::NullType:
- return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL ";
- }
-
- ASSERT_NOT_REACHED();
- return "";
-}
-
-String IDBKey::lowerCursorWhereFragment(String comparisonOperator, String qualifiedTableName)
+bool IDBKey::isEqual(const IDBKey* other) const
{
- switch (m_type) {
- case StringType:
- return "? " + comparisonOperator + " " + qualifiedTableName + "keyString AND ";
- case DateType:
- return "(? " + comparisonOperator + " " + qualifiedTableName + "keyDate OR NOT " + qualifiedTableName + "keyString IS NULL) AND ";
- case NumberType:
- return "(? " + comparisonOperator + " " + qualifiedTableName + "keyNumber OR NOT " + qualifiedTableName + "keyString IS NULL OR NOT " + qualifiedTableName + "keyDate IS NULL) AND ";
- case NullType:
- if (comparisonOperator == "<")
- return "NOT(" + qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL) AND ";
- return ""; // If it's =, the upper bound half will do the constraining. If it's <=, then that's a no-op.
- }
- ASSERT_NOT_REACHED();
- return "";
-}
+ if (!other || other->m_type != m_type)
+ return false;
-String IDBKey::upperCursorWhereFragment(String comparisonOperator, String qualifiedTableName)
-{
switch (m_type) {
case StringType:
- return "(" + qualifiedTableName + "keyString " + comparisonOperator + " ? OR " + qualifiedTableName + "keyString IS NULL) AND ";
+ return other->m_string == m_string;
case DateType:
- return "(" + qualifiedTableName + "keyDate " + comparisonOperator + " ? OR " + qualifiedTableName + "keyDate IS NULL) AND " + qualifiedTableName + "keyString IS NULL AND ";
+ return other->m_date == m_date;
case NumberType:
- return "(" + qualifiedTableName + "keyNumber " + comparisonOperator + " ? OR " + qualifiedTableName + "keyNumber IS NULL) AND " + qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND ";
+ return other->m_number == m_number;
case NullType:
- if (comparisonOperator == "<")
- return "0 != 0 AND ";
- return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL AND ";
- }
- 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::DateType:
- query.bindDouble(column, m_date);
- return 1;
- case IDBKey::NumberType:
- query.bindDouble(column, m_number);
- return 1;
- case IDBKey::NullType:
- return 0;
+ return true;
}
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::DateType:
- query.bindNull(baseColumn + 0);
- query.bindDouble(baseColumn + 1, m_date);
- query.bindNull(baseColumn + 2);
- break;
- case IDBKey::NumberType:
- query.bindNull(baseColumn + 0);
- query.bindNull(baseColumn + 1);
- query.bindDouble(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();
- }
+ return false;
}
} // namespace WebCore
diff --git a/Source/WebCore/storage/IDBKey.h b/Source/WebCore/storage/IDBKey.h
index 9118743..5816ce3 100644
--- a/Source/WebCore/storage/IDBKey.h
+++ b/Source/WebCore/storage/IDBKey.h
@@ -34,8 +34,6 @@
namespace WebCore {
-class SQLiteStatement;
-
class IDBKey : public ThreadSafeShared<IDBKey> {
public:
static PassRefPtr<IDBKey> createNull()
@@ -99,14 +97,8 @@ public:
return m_number;
}
- static PassRefPtr<IDBKey> fromQuery(SQLiteStatement& query, int baseColumn);
-
- bool isEqual(IDBKey* other);
- String whereSyntax(String qualifiedTableName = "") const;
- String lowerCursorWhereFragment(String comparisonOperator, String qualifiedTableName = "");
- String upperCursorWhereFragment(String comparisonOperator, String qualifiedTableName = "");
- int bind(SQLiteStatement& query, int column) const;
- void bindWithNulls(SQLiteStatement& query, int baseColumn) const;
+ bool isLessThan(const IDBKey* other) const;
+ bool isEqual(const IDBKey* other) const;
using ThreadSafeShared<IDBKey>::ref;
using ThreadSafeShared<IDBKey>::deref;
diff --git a/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp b/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp
index b7c45c3..4f0fcee 100644
--- a/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp
@@ -37,4 +37,10 @@ void IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(const Ve
// FIXME: Implement this method once JSC supports WireFormat for SerializedScriptValue.
}
+PassRefPtr<SerializedScriptValue> IDBKeyPathBackendImpl::injectIDBKeyIntoSerializedValue(PassRefPtr<IDBKey> key, PassRefPtr<SerializedScriptValue> value, const String& keyPath)
+{
+ // FIXME: Implement this method once JSC supports WireFormat for SerializedScriptValue.
+ return 0;
+}
+
#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/storage/IDBKeyPathBackendImpl.h b/Source/WebCore/storage/IDBKeyPathBackendImpl.h
index 32af5e3..1c76232 100644
--- a/Source/WebCore/storage/IDBKeyPathBackendImpl.h
+++ b/Source/WebCore/storage/IDBKeyPathBackendImpl.h
@@ -38,6 +38,7 @@ class SerializedScriptValue;
class IDBKeyPathBackendImpl {
public:
static void createIDBKeysFromSerializedValuesAndKeyPath(const Vector<RefPtr<SerializedScriptValue>, 0>& values, const String& keyPath, Vector<RefPtr<IDBKey>, 0>& keys);
+ static PassRefPtr<SerializedScriptValue> injectIDBKeyIntoSerializedValue(PassRefPtr<IDBKey>, PassRefPtr<SerializedScriptValue>, const String& keyPath);
};
}
diff --git a/Source/WebCore/storage/IDBKeyRange.cpp b/Source/WebCore/storage/IDBKeyRange.cpp
index 142b3bd..4f4144b 100644
--- a/Source/WebCore/storage/IDBKeyRange.cpp
+++ b/Source/WebCore/storage/IDBKeyRange.cpp
@@ -56,31 +56,11 @@ PassRefPtr<IDBKeyRange> IDBKeyRange::upperBound(PassRefPtr<IDBKey> bound, bool o
return IDBKeyRange::create(0, bound, false, open);
}
-PassRefPtr<IDBKeyRange> IDBKeyRange::bound(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, const OptionsObject& options)
+PassRefPtr<IDBKeyRange> IDBKeyRange::bound(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, bool lowerOpen, bool upperOpen)
{
- bool lowerOpen = false;
- bool upperOpen = false;
- options.getKeyBool("lowerOpen", lowerOpen);
- options.getKeyBool("upperOpen", upperOpen);
return IDBKeyRange::create(lower, upper, lowerOpen, upperOpen);
}
-String IDBKeyRange::lowerWhereClauseComparisonOperator() const
-{
- ASSERT(m_lower);
- if (m_lowerOpen)
- return "<";
- return "<=";
-}
-
-String IDBKeyRange::upperWhereClauseComparisonOperator() const
-{
- ASSERT(m_upper);
- if (m_upperOpen)
- return "<";
- return "<=";
-}
-
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
diff --git a/Source/WebCore/storage/IDBKeyRange.h b/Source/WebCore/storage/IDBKeyRange.h
index 8af48fe..636a5c1 100644
--- a/Source/WebCore/storage/IDBKeyRange.h
+++ b/Source/WebCore/storage/IDBKeyRange.h
@@ -43,19 +43,15 @@ public:
}
~IDBKeyRange() { }
-
PassRefPtr<IDBKey> lower() const { return m_lower; }
PassRefPtr<IDBKey> upper() const { return m_upper; }
bool lowerOpen() const { return m_lowerOpen; }
bool upperOpen() const { return m_upperOpen; }
- String lowerWhereClauseComparisonOperator() const;
- String upperWhereClauseComparisonOperator() const;
-
static PassRefPtr<IDBKeyRange> only(PassRefPtr<IDBKey> value);
static PassRefPtr<IDBKeyRange> lowerBound(PassRefPtr<IDBKey> bound, bool open = false);
static PassRefPtr<IDBKeyRange> upperBound(PassRefPtr<IDBKey> bound, bool open = false);
- static PassRefPtr<IDBKeyRange> bound(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, const OptionsObject& = OptionsObject());
+ static PassRefPtr<IDBKeyRange> bound(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, bool lowerOpen = false, bool upperOpen = false);
private:
IDBKeyRange(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, bool lowerOpen, bool upperOpen);
diff --git a/Source/WebCore/storage/IDBKeyRange.idl b/Source/WebCore/storage/IDBKeyRange.idl
index d7fa075..6dd4c85 100644
--- a/Source/WebCore/storage/IDBKeyRange.idl
+++ b/Source/WebCore/storage/IDBKeyRange.idl
@@ -37,7 +37,7 @@ module storage {
[ClassMethod] IDBKeyRange only(in IDBKey value);
[ClassMethod] IDBKeyRange lowerBound(in IDBKey bound, in [Optional] boolean open);
[ClassMethod] IDBKeyRange upperBound(in IDBKey bound, in [Optional] boolean open);
- [ClassMethod] IDBKeyRange bound(in IDBKey lower, in IDBKey upper, in [Optional] OptionsObject options);
+ [ClassMethod] IDBKeyRange bound(in IDBKey lower, in IDBKey upper, in [Optional] boolean lowerOpen, in [Optional] boolean upperOpen);
};
}
diff --git a/Source/WebCore/storage/IDBObjectStore.cpp b/Source/WebCore/storage/IDBObjectStore.cpp
index 53ae279..1a3e741 100644
--- a/Source/WebCore/storage/IDBObjectStore.cpp
+++ b/Source/WebCore/storage/IDBObjectStore.cpp
@@ -71,8 +71,10 @@ PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, Pass
{
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
m_objectStore->get(key, request, m_transaction->backend(), ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
+ }
return request.release();
}
@@ -80,36 +82,44 @@ PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, Pass
{
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
m_objectStore->put(value, key, IDBObjectStoreBackendInterface::AddOnly, request, m_transaction->backend(), ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
- return request;
+ }
+ return request.release();
}
PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, ExceptionCode& ec)
{
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
m_objectStore->put(value, key, IDBObjectStoreBackendInterface::AddOrUpdate, request, m_transaction->backend(), ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
- return request;
+ }
+ return request.release();
}
PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec)
{
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
m_objectStore->deleteFunction(key, request, m_transaction->backend(), ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
- return request;
+ }
+ return request.release();
}
PassRefPtr<IDBRequest> IDBObjectStore::clear(ScriptExecutionContext* context, ExceptionCode& ec)
{
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
m_objectStore->clear(request, m_transaction->backend(), ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
- return request;
+ }
+ return request.release();
}
PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const String& keyPath, const OptionsObject& options, ExceptionCode& ec)
@@ -121,7 +131,7 @@ PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const Strin
ASSERT(!index != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa.
if (!index)
return 0;
- return IDBIndex::create(index.release(), m_transaction.get());
+ return IDBIndex::create(index.release(), this, m_transaction.get());
}
PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec)
@@ -130,7 +140,7 @@ PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec
ASSERT(!index != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa.
if (!index)
return 0;
- return IDBIndex::create(index.release(), m_transaction.get());
+ return IDBIndex::create(index.release(), this, m_transaction.get());
}
void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec)
@@ -147,9 +157,12 @@ PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* contex
}
RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get());
+ request->setCursorType(IDBCursorBackendInterface::ObjectStoreCursor);
m_objectStore->openCursor(range, direction, request, m_transaction->backend(), ec);
- if (ec)
+ if (ec) {
+ request->markEarlyDeath();
return 0;
+ }
return request.release();
}
diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp
index 921bbab..0433ed7 100644
--- a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp
@@ -30,6 +30,7 @@
#include "CrossThreadTask.h"
#include "DOMStringList.h"
+#include "IDBBackingStore.h"
#include "IDBBindingUtilities.h"
#include "IDBCallbacks.h"
#include "IDBCursorBackendImpl.h"
@@ -40,12 +41,8 @@
#include "IDBKeyPath.h"
#include "IDBKeyPathBackendImpl.h"
#include "IDBKeyRange.h"
-#include "IDBSQLiteDatabase.h"
#include "IDBTransactionBackendInterface.h"
#include "ScriptExecutionContext.h"
-#include "SQLiteDatabase.h"
-#include "SQLiteStatement.h"
-#include "SQLiteTransaction.h"
namespace WebCore {
@@ -53,8 +50,8 @@ IDBObjectStoreBackendImpl::~IDBObjectStoreBackendImpl()
{
}
-IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& keyPath, bool autoIncrement)
- : m_database(database)
+IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, int64_t id, const String& name, const String& keyPath, bool autoIncrement)
+ : m_backingStore(backingStore)
, m_id(id)
, m_name(name)
, m_keyPath(keyPath)
@@ -64,8 +61,8 @@ IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBSQLiteDatabase* database
loadIndexes();
}
-IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBSQLiteDatabase* database, const String& name, const String& keyPath, bool autoIncrement)
- : m_database(database)
+IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBBackingStore* backingStore, const String& name, const String& keyPath, bool autoIncrement)
+ : m_backingStore(backingStore)
, m_id(InvalidId)
, m_name(name)
, m_keyPath(keyPath)
@@ -82,17 +79,6 @@ PassRefPtr<DOMStringList> IDBObjectStoreBackendImpl::indexNames() const
return indexNames.release();
}
-static String whereClause(IDBKey* key)
-{
- return "WHERE objectStoreId = ? AND " + key->whereSyntax();
-}
-
-static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key)
-{
- query.bindInt64(1, id);
- key->bind(query, 2);
-}
-
void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
{
RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
@@ -104,22 +90,13 @@ void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCal
void IDBObjectStoreBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
{
- SQLiteStatement query(objectStore->sqliteDatabase(), "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData " + whereClause(key.get()));
- bool ok = query.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
- bindWhereClause(query, objectStore->id(), key.get());
- if (query.step() != SQLResultRow) {
+ String wireData = objectStore->m_backingStore->getObjectStoreRecord(objectStore->id(), *key);
+ if (wireData.isNull()) {
callbacks->onSuccess(SerializedScriptValue::undefinedValue());
return;
}
- ASSERT((key->type() == IDBKey::StringType) != query.isColumnNull(0));
- ASSERT((key->type() == IDBKey::DateType) != query.isColumnNull(1));
- ASSERT((key->type() == IDBKey::NumberType) != query.isColumnNull(2));
-
- callbacks->onSuccess(SerializedScriptValue::createFromWire(query.getColumnBlobAsString(3)));
- ASSERT(query.step() != SQLResultRow);
+ callbacks->onSuccess(SerializedScriptValue::createFromWire(wireData));
}
static PassRefPtr<IDBKey> fetchKeyFromKeyPath(SerializedScriptValue* value, const String& keyPath)
@@ -134,49 +111,9 @@ static PassRefPtr<IDBKey> fetchKeyFromKeyPath(SerializedScriptValue* value, cons
return keys[0].release();
}
-static bool putObjectStoreData(SQLiteDatabase& db, IDBKey* key, SerializedScriptValue* value, int64_t objectStoreId, int64_t& dataRowId)
+static PassRefPtr<SerializedScriptValue> injectKeyIntoKeyPath(PassRefPtr<IDBKey> key, PassRefPtr<SerializedScriptValue> value, const String& keyPath)
{
- String sql = dataRowId != IDBObjectStoreBackendImpl::InvalidId ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?"
- : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)";
- SQLiteStatement query(db, sql);
- if (query.prepare() != SQLResultOk)
- return false;
- key->bindWithNulls(query, 1);
- query.bindBlob(4, value->toWireString());
- if (dataRowId != IDBDatabaseBackendImpl::InvalidId)
- query.bindInt64(5, dataRowId);
- else
- query.bindInt64(5, objectStoreId);
-
- if (query.step() != SQLResultDone)
- return false;
-
- if (dataRowId == IDBDatabaseBackendImpl::InvalidId)
- dataRowId = db.lastInsertRowID();
-
- return true;
-}
-
-static bool deleteIndexData(SQLiteDatabase& db, int64_t objectStoreDataId)
-{
- SQLiteStatement deleteQuery(db, "DELETE FROM IndexData WHERE objectStoreDataId = ?");
- if (deleteQuery.prepare() != SQLResultOk)
- return false;
- deleteQuery.bindInt64(1, objectStoreDataId);
-
- return deleteQuery.step() == SQLResultDone;
-}
-
-static bool putIndexData(SQLiteDatabase& db, IDBKey* key, int64_t indexId, int64_t objectStoreDataId)
-{
- SQLiteStatement putQuery(db, "INSERT INTO IndexData (keyString, keyDate, keyNumber, indexId, objectStoreDataId) VALUES (?, ?, ?, ?, ?)");
- if (putQuery.prepare() != SQLResultOk)
- return false;
- key->bindWithNulls(putQuery, 1);
- putQuery.bindInt64(4, indexId);
- putQuery.bindInt64(5, objectStoreDataId);
-
- return putQuery.step() == SQLResultDone;
+ return IDBKeyPathBackendImpl::injectIDBKeyIntoSerializedValue(key, value, keyPath);
}
void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
@@ -197,7 +134,7 @@ void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue,
ec = IDBDatabaseException::NOT_ALLOWED_ERR;
}
-PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::selectKeyForPut(IDBObjectStoreBackendImpl* objectStore, SerializedScriptValue* value, IDBKey* key, PutMode putMode, IDBCallbacks* callbacks)
+PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::selectKeyForPut(IDBObjectStoreBackendImpl* objectStore, IDBKey* key, PutMode putMode, IDBCallbacks* callbacks, RefPtr<SerializedScriptValue>& value)
{
if (putMode == CursorUpdate)
ASSERT(key);
@@ -220,19 +157,24 @@ PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::selectKeyForPut(IDBObjectStoreBack
if (!hasKeyPath)
return objectStore->genAutoIncrementKey();
- RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value, objectStore->m_keyPath);
+ RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath);
if (keyPathKey) {
objectStore->resetAutoIncrementKeyCache();
return keyPathKey;
}
- // FIXME: Generate auto increment key, and inject it through the key path.
- callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Adding data to object stores with auto increment and in-line keys not yet supported."));
- return 0;
+ RefPtr<IDBKey> autoIncKey = objectStore->genAutoIncrementKey();
+ RefPtr<SerializedScriptValue> valueAfterInjection = injectKeyIntoKeyPath(autoIncKey, value, objectStore->m_keyPath);
+ if (!valueAfterInjection) {
+ callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "The generated key could not be inserted into the object using the keyPath."));
+ return 0;
+ }
+ value = valueAfterInjection;
+ return autoIncKey.release();
}
if (hasKeyPath) {
- RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value, objectStore->m_keyPath);
+ RefPtr<IDBKey> keyPathKey = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath);
if (!keyPathKey) {
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "The key could not be fetched from the keyPath."));
@@ -258,7 +200,7 @@ PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::selectKeyForPut(IDBObjectStoreBack
void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
RefPtr<SerializedScriptValue> value = prpValue;
- RefPtr<IDBKey> key = selectKeyForPut(objectStore.get(), value.get(), prpKey.get(), putMode, callbacks.get());
+ RefPtr<IDBKey> key = selectKeyForPut(objectStore.get(), prpKey.get(), putMode, callbacks.get(), value);
if (!key)
return;
@@ -285,12 +227,9 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<
indexKeys.append(key.release());
}
- SQLiteStatement getQuery(objectStore->sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key.get()));
- bool ok = getQuery.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+ int64_t dataRowId = InvalidId;
+ bool isExistingValue = objectStore->m_backingStore->keyExistsInObjectStore(objectStore->id(), *key, dataRowId);
- bindWhereClause(getQuery, objectStore->id(), key.get());
- bool isExistingValue = getQuery.step() == SQLResultRow;
if (putMode == AddOnly && isExistingValue) {
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store."));
return;
@@ -298,15 +237,14 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<
// Before this point, don't do any mutation. After this point, rollback the transaction in case of error.
- int64_t dataRowId = isExistingValue ? getQuery.getColumnInt(0) : InvalidId;
- if (!putObjectStoreData(objectStore->sqliteDatabase(), key.get(), value.get(), objectStore->id(), dataRowId)) {
+ if (!objectStore->m_backingStore->putObjectStoreRecord(objectStore->id(), *key, value->toWireString(), dataRowId, dataRowId == InvalidId)) {
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
transaction->abort();
return;
}
- if (!deleteIndexData(objectStore->sqliteDatabase(), dataRowId)) {
+ if (!objectStore->m_backingStore->deleteIndexDataForRecord(dataRowId)) {
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
transaction->abort();
@@ -315,7 +253,9 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<
int i = 0;
for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it, ++i) {
- if (!putIndexData(objectStore->sqliteDatabase(), indexKeys[i].get(), it->second->id(), dataRowId)) {
+ if (!it->second->hasValidId())
+ continue; // The index object has been created, but does not exist in the database yet.
+ if (!objectStore->m_backingStore->putIndexDataForRecord(it->second->id(), *indexKeys[i], dataRowId)) {
// FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
transaction->abort();
@@ -343,35 +283,13 @@ void IDBObjectStoreBackendImpl::deleteFunction(PassRefPtr<IDBKey> prpKey, PassRe
void IDBObjectStoreBackendImpl::deleteInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
{
- SQLiteStatement idQuery(objectStore->sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key.get()));
- bool ok = idQuery.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
- bindWhereClause(idQuery, objectStore->id(), key.get());
- if (idQuery.step() != SQLResultRow) {
+ int64_t id;
+ if (!objectStore->m_backingStore->keyExistsInObjectStore(objectStore->id(), *key, id)) {
callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the object store."));
return;
}
- int64_t id = idQuery.getColumnInt64(0);
-
- SQLiteStatement osQuery(objectStore->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE id = ?");
- ok = osQuery.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
- osQuery.bindInt64(1, id);
-
- ok = osQuery.step() == SQLResultDone;
- ASSERT_UNUSED(ok, ok);
-
- SQLiteStatement indexQuery(objectStore->sqliteDatabase(), "DELETE FROM IndexData WHERE objectStoreDataId = ?");
- ok = indexQuery.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
- indexQuery.bindInt64(1, id);
-
- ok = indexQuery.step() == SQLResultDone;
- ASSERT_UNUSED(ok, ok);
-
+ objectStore->m_backingStore->deleteObjectStoreRecord(objectStore->id(), id);
callbacks->onSuccess(SerializedScriptValue::nullValue());
}
@@ -389,22 +307,46 @@ void IDBObjectStoreBackendImpl::clear(PassRefPtr<IDBCallbacks> prpCallbacks, IDB
ec = IDBDatabaseException::NOT_ALLOWED_ERR;
}
-static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id)
+void IDBObjectStoreBackendImpl::clearInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBCallbacks> callbacks)
{
- SQLiteStatement deleteQuery(db, sql);
- bool ok = deleteQuery.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling.
- deleteQuery.bindInt64(1, id);
- ok = deleteQuery.step() == SQLResultDone;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling.
+ objectStore->m_backingStore->clearObjectStore(objectStore->id());
+ callbacks->onSuccess(SerializedScriptValue::undefinedValue());
}
-void IDBObjectStoreBackendImpl::clearInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBCallbacks> callbacks)
-{
- doDelete(objectStore->sqliteDatabase(), "DELETE FROM IndexData WHERE objectStoreDataId IN (SELECT id FROM ObjectStoreData WHERE objectStoreId = ?)", objectStore->id());
- doDelete(objectStore->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStore->id());
+namespace {
+class PopulateIndexCallback : public IDBBackingStore::ObjectStoreRecordCallback {
+public:
+ PopulateIndexCallback(IDBBackingStore& backingStore, const String& indexKeyPath, int64_t indexId)
+ : m_backingStore(backingStore)
+ , m_indexKeyPath(indexKeyPath)
+ , m_indexId(indexId)
+ {
+ }
- callbacks->onSuccess(SerializedScriptValue::undefinedValue());
+ virtual bool callback(int64_t objectStoreDataId, const String& value)
+ {
+ RefPtr<SerializedScriptValue> objectValue = SerializedScriptValue::createFromWire(value);
+ RefPtr<IDBKey> indexKey = fetchKeyFromKeyPath(objectValue.get(), m_indexKeyPath);
+
+ if (!m_backingStore.putIndexDataForRecord(m_indexId, *indexKey, objectStoreDataId))
+ return false;
+
+ return true;
+ }
+
+private:
+ IDBBackingStore& m_backingStore;
+ const String& m_indexKeyPath;
+ int64_t m_indexId;
+};
+}
+
+static bool populateIndex(IDBBackingStore& backingStore, int64_t objectStoreId, int64_t indexId, const String& indexKeyPath)
+{
+ PopulateIndexCallback callback(backingStore, indexKeyPath, indexId);
+ if (!backingStore.forEachObjectStoreRecord(objectStoreId, callback))
+ return false;
+ return true;
}
PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(const String& name, const String& keyPath, bool unique, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
@@ -418,7 +360,7 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(cons
return 0;
}
- RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_database.get(), name, m_name, keyPath, unique);
+ RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_backingStore.get(), name, m_name, keyPath, unique);
ASSERT(index->name() == name);
RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
@@ -435,21 +377,19 @@ PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(cons
void IDBObjectStoreBackendImpl::createIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
- SQLiteStatement insert(objectStore->sqliteDatabase(), "INSERT INTO Indexes (objectStoreId, name, keyPath, isUnique) VALUES (?, ?, ?, ?)");
- if (insert.prepare() != SQLResultOk) {
+ int64_t id;
+ if (!objectStore->m_backingStore->createIndex(objectStore->m_id, index->name(), index->keyPath(), index->unique(), id)) {
transaction->abort();
return;
}
- insert.bindInt64(1, objectStore->m_id);
- insert.bindText(2, index->name());
- insert.bindText(3, index->keyPath());
- insert.bindInt(4, static_cast<int>(index->unique()));
- if (insert.step() != SQLResultDone) {
+
+ index->setId(id);
+
+ if (!populateIndex(*objectStore->m_backingStore, objectStore->m_id, id, index->keyPath())) {
transaction->abort();
return;
}
- int64_t id = objectStore->sqliteDatabase().lastInsertRowID();
- index->setId(id);
+
transaction->didCompleteTaskEvents();
}
@@ -488,9 +428,7 @@ void IDBObjectStoreBackendImpl::deleteIndex(const String& name, IDBTransactionBa
void IDBObjectStoreBackendImpl::deleteIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
- doDelete(objectStore->sqliteDatabase(), "DELETE FROM Indexes WHERE id = ?", index->id());
- doDelete(objectStore->sqliteDatabase(), "DELETE FROM IndexData WHERE indexId = ?", index->id());
-
+ objectStore->m_backingStore->deleteIndex(index->id());
transaction->didCompleteTaskEvents();
}
@@ -506,64 +444,32 @@ void IDBObjectStoreBackendImpl::openCursor(PassRefPtr<IDBKeyRange> prpRange, uns
void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> range, unsigned short tmpDirection, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
{
- bool lowerBound = range && range->lower();
- bool upperBound = range && range->upper();
-
- // Several files depend on this order of selects.
- String sql = "SELECT id, keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE ";
- if (lowerBound)
- sql += range->lower()->lowerCursorWhereFragment(range->lowerWhereClauseComparisonOperator());
- if (upperBound)
- sql += range->upper()->upperCursorWhereFragment(range->upperWhereClauseComparisonOperator());
- sql += "objectStoreId = ? ORDER BY ";
-
IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(tmpDirection);
- if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE)
- sql += "keyString, keyDate, keyNumber";
- else
- sql += "keyString DESC, keyDate DESC, keyNumber DESC";
-
- OwnPtr<SQLiteStatement> query = adoptPtr(new SQLiteStatement(objectStore->sqliteDatabase(), sql));
- bool ok = query->prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
- int currentColumn = 1;
- if (lowerBound)
- currentColumn += range->lower()->bind(*query, currentColumn);
- if (upperBound)
- currentColumn += range->upper()->bind(*query, currentColumn);
- query->bindInt64(currentColumn, objectStore->id());
-
- if (query->step() != SQLResultRow) {
+
+ RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->m_backingStore->openObjectStoreCursor(objectStore->id(), range.get(), direction);
+ if (!backingStoreCursor) {
callbacks->onSuccess(SerializedScriptValue::nullValue());
return;
}
- RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(objectStore->m_database.get(), range, direction, query.release(), true, transaction.get(), objectStore.get());
+ RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(backingStoreCursor.release(), direction, IDBCursorBackendInterface::ObjectStoreCursor, transaction.get(), objectStore.get());
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);
+ Vector<int64_t> ids;
+ Vector<String> names;
+ Vector<String> keyPaths;
+ Vector<bool> uniqueFlags;
+ m_backingStore->getIndexes(m_id, ids, names, keyPaths, uniqueFlags);
- 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);
+ ASSERT(names.size() == ids.size());
+ ASSERT(keyPaths.size() == ids.size());
+ ASSERT(uniqueFlags.size() == ids.size());
- m_indexes.set(name, IDBIndexBackendImpl::create(m_database.get(), id, name, m_name, keyPath, unique));
- }
-}
-
-SQLiteDatabase& IDBObjectStoreBackendImpl::sqliteDatabase() const
-{
- return m_database->db();
+ for (size_t i = 0; i < ids.size(); i++)
+ m_indexes.set(names[i], IDBIndexBackendImpl::create(m_backingStore.get(), ids[i], names[i], m_name, keyPaths[i], uniqueFlags[i]));
}
void IDBObjectStoreBackendImpl::removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index)
@@ -584,19 +490,7 @@ PassRefPtr<IDBKey> IDBObjectStoreBackendImpl::genAutoIncrementKey()
if (m_autoIncrementNumber > 0)
return IDBKey::createNumber(m_autoIncrementNumber++);
- String sql = "SELECT max(keyNumber) + 1 FROM ObjectStoreData WHERE objectStoreId = ? AND keyString IS NULL AND keyDate IS NULL";
-
- SQLiteStatement query(sqliteDatabase(), sql);
- bool ok = query.prepare() == SQLResultOk;
- ASSERT_UNUSED(ok, ok);
-
- query.bindInt64(1, id());
-
- if (query.step() != SQLResultRow || query.isColumnNull(0))
- m_autoIncrementNumber = 1;
- else
- m_autoIncrementNumber = static_cast<int>(query.getColumnDouble(0));
-
+ m_autoIncrementNumber = static_cast<int>(m_backingStore->nextAutoIncrementNumber(id()));
return IDBKey::createNumber(m_autoIncrementNumber++);
}
diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h
index b54f9fd..4479d1e 100644
--- a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h
+++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h
@@ -34,22 +34,21 @@
namespace WebCore {
+class IDBBackingStore;
class IDBDatabaseBackendImpl;
class IDBIndexBackendImpl;
-class IDBSQLiteDatabase;
class IDBTransactionBackendInterface;
-class SQLiteDatabase;
class ScriptExecutionContext;
class IDBObjectStoreBackendImpl : public IDBObjectStoreBackendInterface {
public:
- static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& keyPath, bool autoIncrement)
+ static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, int64_t id, const String& name, const String& keyPath, bool autoIncrement)
{
- return adoptRef(new IDBObjectStoreBackendImpl(database, id, name, keyPath, autoIncrement));
+ return adoptRef(new IDBObjectStoreBackendImpl(backingStore, id, name, keyPath, autoIncrement));
}
- static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBSQLiteDatabase* database, const String& name, const String& keyPath, bool autoIncrement)
+ static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBBackingStore* backingStore, const String& name, const String& keyPath, bool autoIncrement)
{
- return adoptRef(new IDBObjectStoreBackendImpl(database, name, keyPath, autoIncrement));
+ return adoptRef(new IDBObjectStoreBackendImpl(backingStore, name, keyPath, autoIncrement));
}
virtual ~IDBObjectStoreBackendImpl();
@@ -78,14 +77,13 @@ public:
virtual void openCursor(PassRefPtr<IDBKeyRange> range, unsigned short direction, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&);
private:
- IDBObjectStoreBackendImpl(IDBSQLiteDatabase*, int64_t id, const String& name, const String& keyPath, bool autoIncrement);
- IDBObjectStoreBackendImpl(IDBSQLiteDatabase*, const String& name, const String& keyPath, bool autoIncrement);
+ IDBObjectStoreBackendImpl(IDBBackingStore*, int64_t id, const String& name, const String& keyPath, bool autoIncrement);
+ IDBObjectStoreBackendImpl(IDBBackingStore*, const String& name, const String& keyPath, bool autoIncrement);
void loadIndexes();
- SQLiteDatabase& sqliteDatabase() const;
PassRefPtr<IDBKey> genAutoIncrementKey();
void resetAutoIncrementKeyCache() { m_autoIncrementNumber = -1; }
- static PassRefPtr<IDBKey> selectKeyForPut(IDBObjectStoreBackendImpl*, SerializedScriptValue*, IDBKey*, PutMode, IDBCallbacks*);
+ static PassRefPtr<IDBKey> selectKeyForPut(IDBObjectStoreBackendImpl*, IDBKey*, PutMode, IDBCallbacks*, RefPtr<SerializedScriptValue>&);
static void getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>);
static void putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, PutMode, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>);
@@ -99,7 +97,7 @@ private:
static void removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBIndexBackendImpl>);
static void addIndexToMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBIndexBackendImpl>);
- RefPtr<IDBSQLiteDatabase> m_database;
+ RefPtr<IDBBackingStore> m_backingStore;
int64_t m_id;
String m_name;
diff --git a/Source/WebCore/storage/IDBRequest.cpp b/Source/WebCore/storage/IDBRequest.cpp
index e7498ec..e1837fc 100644
--- a/Source/WebCore/storage/IDBRequest.cpp
+++ b/Source/WebCore/storage/IDBRequest.cpp
@@ -36,13 +36,12 @@
#include "EventListener.h"
#include "EventNames.h"
#include "EventQueue.h"
-#include "IDBCursor.h"
+#include "IDBCursorWithValue.h"
#include "IDBDatabase.h"
+#include "IDBEventDispatcher.h"
#include "IDBIndex.h"
-#include "IDBErrorEvent.h"
-#include "IDBObjectStore.h"
#include "IDBPendingTransactionMonitor.h"
-#include "IDBSuccessEvent.h"
+#include "IDBTransaction.h"
namespace WebCore {
@@ -53,47 +52,157 @@ PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassR
IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
: ActiveDOMObject(context, this)
+ , m_errorCode(0)
, m_source(source)
, m_transaction(transaction)
, m_readyState(LOADING)
, m_finished(false)
+ , m_cursorType(IDBCursorBackendInterface::InvalidCursorType)
{
- if (m_transaction)
+ if (m_transaction) {
+ m_transaction->registerRequest(this);
IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend());
+ }
}
IDBRequest::~IDBRequest()
{
+ ASSERT(m_readyState == DONE || m_readyState == EarlyDeath);
+ if (m_transaction)
+ m_transaction->unregisterRequest(this);
+}
+
+PassRefPtr<IDBAny> IDBRequest::result(ExceptionCode& ec) const
+{
+ if (m_readyState != DONE) {
+ ec = IDBDatabaseException::NOT_ALLOWED_ERR;
+ return 0;
+ }
+ return m_result;
+}
+
+unsigned short IDBRequest::errorCode(ExceptionCode& ec) const
+{
+ if (m_readyState != DONE) {
+ ec = IDBDatabaseException::NOT_ALLOWED_ERR;
+ return 0;
+ }
+ return m_errorCode;
+}
+
+String IDBRequest::webkitErrorMessage(ExceptionCode& ec) const
+{
+ if (m_readyState != DONE) {
+ ec = IDBDatabaseException::NOT_ALLOWED_ERR;
+ return String();
+ }
+ return m_errorMessage;
+}
+
+PassRefPtr<IDBAny> IDBRequest::source() const
+{
+ return m_source;
+}
+
+PassRefPtr<IDBTransaction> IDBRequest::transaction() const
+{
+ return m_transaction;
+}
+
+unsigned short IDBRequest::readyState() const
+{
+ ASSERT(m_readyState == LOADING || m_readyState == DONE);
+ return m_readyState;
+}
+
+void IDBRequest::markEarlyDeath()
+{
+ ASSERT(m_readyState == LOADING);
+ m_readyState = EarlyDeath;
}
bool IDBRequest::resetReadyState(IDBTransaction* transaction)
{
ASSERT(!m_finished);
ASSERT(scriptExecutionContext());
+ ASSERT(transaction == m_transaction);
if (m_readyState != DONE)
return false;
- m_transaction = transaction;
m_readyState = LOADING;
+ m_result.clear();
+ m_errorCode = 0;
+ m_errorMessage = String();
IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend());
return true;
}
+IDBAny* IDBRequest::source()
+{
+ return m_source.get();
+}
+
+void IDBRequest::abort()
+{
+ if (m_readyState != LOADING) {
+ ASSERT(m_readyState == DONE);
+ return;
+ }
+
+ ASSERT(scriptExecutionContext()->isDocument());
+ EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue();
+ for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
+ bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get());
+ ASSERT_UNUSED(removed, removed);
+ }
+ m_enqueuedEvents.clear();
+
+ m_errorCode = 0;
+ m_errorMessage = String();
+ m_result.clear();
+ onError(IDBDatabaseError::create(IDBDatabaseException::ABORT_ERR, "The transaction was aborted, so the request cannot be fulfilled."));
+}
+
+void IDBRequest::setCursorType(IDBCursorBackendInterface::CursorType cursorType)
+{
+ ASSERT(m_cursorType == IDBCursorBackendInterface::InvalidCursorType);
+ m_cursorType = cursorType;
+}
+
void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error)
{
- enqueueEvent(IDBErrorEvent::create(m_source, *error));
+ ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
+ m_errorCode = error->code();
+ m_errorMessage = error->message();
+ enqueueEvent(Event::create(eventNames().errorEvent, true, true));
+}
+
+static PassRefPtr<Event> createSuccessEvent()
+{
+ return Event::create(eventNames().successEvent, false, false);
}
void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend)
{
- enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(IDBCursor::create(backend, this, m_transaction.get()))));
+ ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
+ ASSERT(m_cursorType != IDBCursorBackendInterface::InvalidCursorType);
+ if (m_cursorType == IDBCursorBackendInterface::IndexKeyCursor)
+ m_result = IDBAny::create(IDBCursor::create(backend, this, m_source.get(), m_transaction.get()));
+ else
+ m_result = IDBAny::create(IDBCursorWithValue::create(backend, this, m_source.get(), m_transaction.get()));
+ enqueueEvent(createSuccessEvent());
}
void IDBRequest::onSuccess(PassRefPtr<IDBDatabaseBackendInterface> backend)
{
- enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(IDBDatabase::create(scriptExecutionContext(), backend))));
+ ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
+ RefPtr<IDBDatabase> idbDatabase = IDBDatabase::create(scriptExecutionContext(), backend);
+ idbDatabase->open();
+
+ m_result = IDBAny::create(idbDatabase.release());
+ enqueueEvent(createSuccessEvent());
}
void IDBRequest::onSuccess(PassRefPtr<IDBIndexBackendInterface> backend)
@@ -103,16 +212,14 @@ void IDBRequest::onSuccess(PassRefPtr<IDBIndexBackendInterface> backend)
void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
{
- enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(idbKey)));
-}
-
-void IDBRequest::onSuccess(PassRefPtr<IDBObjectStoreBackendInterface> backend)
-{
- ASSERT_NOT_REACHED(); // FIXME: This method should go away.
+ ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
+ m_result = IDBAny::create(idbKey);
+ enqueueEvent(createSuccessEvent());
}
void IDBRequest::onSuccess(PassRefPtr<IDBTransactionBackendInterface> prpBackend)
{
+ ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
if (!scriptExecutionContext())
return;
@@ -125,12 +232,16 @@ void IDBRequest::onSuccess(PassRefPtr<IDBTransactionBackendInterface> prpBackend
m_source->idbDatabase()->setSetVersionTransaction(frontend.get());
IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend());
- enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(frontend.release())));
+
+ m_result = IDBAny::create(frontend.release());
+ enqueueEvent(createSuccessEvent());
}
void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptValue)
{
- enqueueEvent(IDBSuccessEvent::create(m_source, IDBAny::create(serializedScriptValue)));
+ ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
+ m_result = IDBAny::create(serializedScriptValue);
+ enqueueEvent(createSuccessEvent());
}
bool IDBRequest::hasPendingActivity() const
@@ -141,6 +252,11 @@ bool IDBRequest::hasPendingActivity() const
return !m_finished || ActiveDOMObject::hasPendingActivity();
}
+void IDBRequest::onBlocked()
+{
+ ASSERT_NOT_REACHED();
+}
+
ScriptExecutionContext* IDBRequest::scriptExecutionContext() const
{
return ActiveDOMObject::scriptExecutionContext();
@@ -149,10 +265,17 @@ ScriptExecutionContext* IDBRequest::scriptExecutionContext() const
bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
{
ASSERT(!m_finished);
+ ASSERT(m_enqueuedEvents.size());
ASSERT(scriptExecutionContext());
ASSERT(event->target() == this);
ASSERT(m_readyState < DONE);
- m_readyState = DONE;
+ if (event->type() != eventNames().blockedEvent)
+ m_readyState = DONE;
+
+ for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
+ if (m_enqueuedEvents[i].get() == event.get())
+ m_enqueuedEvents.remove(i);
+ }
Vector<RefPtr<EventTarget> > targets;
targets.append(this);
@@ -165,26 +288,29 @@ bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
targets.append(m_transaction->db());
}
- ASSERT(event->isIDBErrorEvent() || event->isIDBSuccessEvent());
- bool dontPreventDefault = static_cast<IDBEvent*>(event.get())->dispatch(targets);
+ // FIXME: When we allow custom event dispatching, this will probably need to change.
+ ASSERT(event->type() == eventNames().successEvent || event->type() == eventNames().errorEvent || event->type() == eventNames().blockedEvent);
+ bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
- // If the event's result was of type IDBCursor, then it's possible for us to
- // fire again (unless the transaction completes).
- if (event->isIDBSuccessEvent()) {
- RefPtr<IDBAny> any = static_cast<IDBSuccessEvent*>(event.get())->result();
- if (any->type() != IDBAny::IDBCursorType)
- m_finished = true;
- } else
+ // If the result was of type IDBCursor, then we'll fire again.
+ if (m_result && m_result->type() != IDBAny::IDBCursorType && m_result->type() != IDBAny::IDBCursorWithValueType)
m_finished = true;
if (m_transaction) {
- if (dontPreventDefault && event->isIDBErrorEvent())
+ // If an error event and the default wasn't prevented...
+ if (dontPreventDefault && event->type() == eventNames().errorEvent)
m_transaction->backend()->abort();
m_transaction->backend()->didCompleteTaskEvents();
}
return dontPreventDefault;
}
+void IDBRequest::uncaughtExceptionInEventHandler()
+{
+ if (m_transaction)
+ m_transaction->backend()->abort();
+}
+
void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
{
ASSERT(!m_finished);
@@ -195,7 +321,8 @@ void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
ASSERT(scriptExecutionContext()->isDocument());
EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue();
event->setTarget(this);
- eventQueue->enqueueEvent(event);
+ eventQueue->enqueueEvent(event.get());
+ m_enqueuedEvents.append(event);
}
EventTargetData* IDBRequest::eventTargetData()
diff --git a/Source/WebCore/storage/IDBRequest.h b/Source/WebCore/storage/IDBRequest.h
index 5c31318..b6b4e5b 100644
--- a/Source/WebCore/storage/IDBRequest.h
+++ b/Source/WebCore/storage/IDBRequest.h
@@ -36,12 +36,12 @@
#include "EventListener.h"
#include "EventNames.h"
#include "EventTarget.h"
+#include "ExceptionCode.h"
#include "IDBAny.h"
#include "IDBCallbacks.h"
namespace WebCore {
-class IDBEvent;
class IDBTransaction;
class IDBRequest : public IDBCallbacks, public EventTarget, public ActiveDOMObject {
@@ -49,16 +49,28 @@ public:
static PassRefPtr<IDBRequest> create(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransaction*);
virtual ~IDBRequest();
+ PassRefPtr<IDBAny> result(ExceptionCode&) const;
+ unsigned short errorCode(ExceptionCode&) const;
+ String webkitErrorMessage(ExceptionCode&) const;
+ PassRefPtr<IDBAny> source() const;
+ PassRefPtr<IDBTransaction> transaction() const;
+
// Defined in the IDL
enum ReadyState {
LOADING = 1,
- DONE = 2
+ DONE = 2,
+ EarlyDeath = 3
};
- unsigned short readyState() const { return m_readyState; }
+ unsigned short readyState() const;
+
DEFINE_ATTRIBUTE_EVENT_LISTENER(success);
DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
+ void markEarlyDeath();
bool resetReadyState(IDBTransaction*);
+ void setCursorType(IDBCursorBackendInterface::CursorType);
+ IDBAny* source();
+ void abort();
// IDBCallbacks
virtual void onError(PassRefPtr<IDBDatabaseError>);
@@ -66,9 +78,9 @@ public:
virtual void onSuccess(PassRefPtr<IDBCursorBackendInterface>);
virtual void onSuccess(PassRefPtr<IDBIndexBackendInterface>);
virtual void onSuccess(PassRefPtr<IDBKey>);
- virtual void onSuccess(PassRefPtr<IDBObjectStoreBackendInterface>);
virtual void onSuccess(PassRefPtr<IDBTransactionBackendInterface>);
virtual void onSuccess(PassRefPtr<SerializedScriptValue>);
+ virtual void onBlocked();
// ActiveDOMObject
virtual bool hasPendingActivity() const;
@@ -78,15 +90,19 @@ public:
virtual ScriptExecutionContext* scriptExecutionContext() const;
virtual bool dispatchEvent(PassRefPtr<Event>);
bool dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) { return EventTarget::dispatchEvent(event, ec); }
+ virtual void uncaughtExceptionInEventHandler();
using ThreadSafeShared<IDBCallbacks>::ref;
using ThreadSafeShared<IDBCallbacks>::deref;
-private:
+protected:
IDBRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransaction*);
-
void enqueueEvent(PassRefPtr<Event>);
+ RefPtr<IDBAny> m_result;
+ unsigned short m_errorCode;
+ String m_errorMessage;
+private:
// EventTarget
virtual void refEventTarget() { ref(); }
virtual void derefEventTarget() { deref(); }
@@ -98,6 +114,10 @@ private:
ReadyState m_readyState;
bool m_finished; // Is it possible that we'll fire any more events? If not, we're finished.
+ Vector<RefPtr<Event> > m_enqueuedEvents;
+
+ // Only used if the result type will be a cursor.
+ IDBCursorBackendInterface::CursorType m_cursorType;
EventTargetData m_eventTargetData;
};
diff --git a/Source/WebCore/storage/IDBRequest.idl b/Source/WebCore/storage/IDBRequest.idl
index 58872f0..6ce2036 100644
--- a/Source/WebCore/storage/IDBRequest.idl
+++ b/Source/WebCore/storage/IDBRequest.idl
@@ -32,6 +32,14 @@ module storage {
Conditional=INDEXED_DATABASE,
EventTarget
] IDBRequest {
+ readonly attribute IDBAny result
+ getter raises (IDBDatabaseException);
+ readonly attribute unsigned short errorCode
+ getter raises (IDBDatabaseException);
+ readonly attribute [ConvertNullStringTo=Undefined] DOMString webkitErrorMessage
+ getter raises (IDBDatabaseException);
+ readonly attribute IDBAny source;
+ readonly attribute IDBTransaction transaction;
// States
const unsigned short LOADING = 1;
diff --git a/Source/WebCore/storage/IDBSuccessEvent.h b/Source/WebCore/storage/IDBSuccessEvent.h
deleted file mode 100644
index 5be660a..0000000
--- a/Source/WebCore/storage/IDBSuccessEvent.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef IDBSuccessEvent_h
-#define IDBSuccessEvent_h
-
-#if ENABLE(INDEXED_DATABASE)
-
-#include "IDBEvent.h"
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
-
-namespace WebCore {
-
-class IDBAny;
-
-class IDBSuccessEvent : public IDBEvent {
-public:
- static PassRefPtr<IDBSuccessEvent> create(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result);
- // FIXME: Need to allow creation of these events from JS.
- virtual ~IDBSuccessEvent();
-
- PassRefPtr<IDBAny> result();
-
- virtual bool isIDBSuccessEvent() const { return true; }
-
-private:
- IDBSuccessEvent(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result);
-
- RefPtr<IDBAny> m_result;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(INDEXED_DATABASE)
-
-#endif // IDBEvent_h
diff --git a/Source/WebCore/storage/IDBTransaction.cpp b/Source/WebCore/storage/IDBTransaction.cpp
index 1f696b3..710bb76 100644
--- a/Source/WebCore/storage/IDBTransaction.cpp
+++ b/Source/WebCore/storage/IDBTransaction.cpp
@@ -31,10 +31,9 @@
#include "Document.h"
#include "EventException.h"
#include "EventQueue.h"
-#include "IDBAbortEvent.h"
-#include "IDBCompleteEvent.h"
#include "IDBDatabase.h"
#include "IDBDatabaseException.h"
+#include "IDBEventDispatcher.h"
#include "IDBIndex.h"
#include "IDBObjectStore.h"
#include "IDBObjectStoreBackendInterface.h"
@@ -104,14 +103,31 @@ void IDBTransaction::abort()
m_backend->abort();
}
+void IDBTransaction::registerRequest(IDBRequest* request)
+{
+ m_childRequests.add(request);
+}
+
+void IDBTransaction::unregisterRequest(IDBRequest* request)
+{
+ // If we aborted the request, it will already have been removed.
+ m_childRequests.remove(request);
+}
+
void IDBTransaction::onAbort()
{
- enqueueEvent(IDBAbortEvent::create(IDBAny::create(this)));
+ while (!m_childRequests.isEmpty()) {
+ IDBRequest* request = *m_childRequests.begin();
+ m_childRequests.remove(request);
+ request->abort();
+ }
+
+ enqueueEvent(Event::create(eventNames().abortEvent, true, false));
}
void IDBTransaction::onComplete()
{
- enqueueEvent(IDBCompleteEvent::create(IDBAny::create(this)));
+ enqueueEvent(Event::create(eventNames().completeEvent, false, false));
}
bool IDBTransaction::hasPendingActivity() const
@@ -139,8 +155,9 @@ bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event)
targets.append(this);
targets.append(db());
- ASSERT(event->isIDBAbortEvent() || event->isIDBCompleteEvent());
- return static_cast<IDBEvent*>(event.get())->dispatch(targets);
+ // FIXME: When we allow custom event dispatching, this will probably need to change.
+ ASSERT(event->type() == eventNames().completeEvent || event->type() == eventNames().abortEvent);
+ return IDBEventDispatcher::dispatch(event.get(), targets);
}
bool IDBTransaction::canSuspend() const
diff --git a/Source/WebCore/storage/IDBTransaction.h b/Source/WebCore/storage/IDBTransaction.h
index ff4feb6..b5778ad 100644
--- a/Source/WebCore/storage/IDBTransaction.h
+++ b/Source/WebCore/storage/IDBTransaction.h
@@ -62,6 +62,9 @@ public:
PassRefPtr<IDBObjectStore> objectStore(const String& name, ExceptionCode&);
void abort();
+ void registerRequest(IDBRequest*);
+ void unregisterRequest(IDBRequest*);
+
DEFINE_ATTRIBUTE_EVENT_LISTENER(abort);
DEFINE_ATTRIBUTE_EVENT_LISTENER(complete);
DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
@@ -100,6 +103,8 @@ private:
unsigned short m_mode;
bool m_finished; // Is it possible that we'll fire any more events or allow any new transactions? If not, we're finished.
+ ListHashSet<IDBRequest*> m_childRequests;
+
EventTargetData m_eventTargetData;
};
diff --git a/Source/WebCore/storage/IDBTransactionBackendImpl.cpp b/Source/WebCore/storage/IDBTransactionBackendImpl.cpp
index 1357838..b5b883f 100644
--- a/Source/WebCore/storage/IDBTransactionBackendImpl.cpp
+++ b/Source/WebCore/storage/IDBTransactionBackendImpl.cpp
@@ -28,10 +28,10 @@
#if ENABLE(INDEXED_DATABASE)
+#include "IDBBackingStore.h"
#include "IDBDatabaseBackendImpl.h"
#include "IDBDatabaseException.h"
#include "IDBTransactionCoordinator.h"
-#include "SQLiteDatabase.h"
namespace WebCore {
@@ -45,7 +45,7 @@ IDBTransactionBackendImpl::IDBTransactionBackendImpl(DOMStringList* objectStores
, m_mode(mode)
, m_state(Unused)
, m_database(database)
- , m_transaction(new SQLiteTransaction(database->sqliteDatabase()))
+ , m_transaction(database->backingStore()->createTransaction())
, m_taskTimer(this, &IDBTransactionBackendImpl::taskTimerFired)
, m_taskEventTimer(this, &IDBTransactionBackendImpl::taskEventTimerFired)
, m_pendingEvents(0)
diff --git a/Source/WebCore/storage/IDBTransactionBackendImpl.h b/Source/WebCore/storage/IDBTransactionBackendImpl.h
index 1297e74..912a239 100644
--- a/Source/WebCore/storage/IDBTransactionBackendImpl.h
+++ b/Source/WebCore/storage/IDBTransactionBackendImpl.h
@@ -29,9 +29,9 @@
#if ENABLE(INDEXED_DATABASE)
#include "DOMStringList.h"
+#include "IDBBackingStore.h"
#include "IDBTransactionBackendInterface.h"
#include "IDBTransactionCallbacks.h"
-#include "SQLiteTransaction.h"
#include "Timer.h"
#include <wtf/Deque.h>
#include <wtf/RefPtr.h>
@@ -81,7 +81,7 @@ private:
TaskQueue m_taskQueue;
TaskQueue m_abortTaskQueue;
- OwnPtr<SQLiteTransaction> m_transaction;
+ RefPtr<IDBBackingStore::Transaction> m_transaction;
// FIXME: delete the timer once we have threads instead.
Timer<IDBTransactionBackendImpl> m_taskTimer;
diff --git a/Source/WebCore/storage/IDBTransactionBackendInterface.h b/Source/WebCore/storage/IDBTransactionBackendInterface.h
index 9b1fce6..1b370af 100644
--- a/Source/WebCore/storage/IDBTransactionBackendInterface.h
+++ b/Source/WebCore/storage/IDBTransactionBackendInterface.h
@@ -38,7 +38,6 @@ namespace WebCore {
class IDBObjectStoreBackendInterface;
class IDBTransactionCallbacks;
-class SQLiteDatabase;
// This class is shared by IDBTransaction (async) and IDBTransactionSync (sync).
// This is implemented by IDBTransactionBackendImpl and optionally others (in order to proxy
@@ -61,4 +60,3 @@ public:
#endif
#endif // IDBTransactionBackendInterface_h
-
diff --git a/Source/WebCore/storage/IDBTransactionCoordinator.cpp b/Source/WebCore/storage/IDBTransactionCoordinator.cpp
index f867f42..edaff2c 100644
--- a/Source/WebCore/storage/IDBTransactionCoordinator.cpp
+++ b/Source/WebCore/storage/IDBTransactionCoordinator.cpp
@@ -31,7 +31,6 @@
#include "IDBDatabaseBackendImpl.h"
#include "IDBObjectStoreBackendInterface.h"
#include "IDBTransactionBackendImpl.h"
-#include "SQLiteDatabase.h"
#include "ScriptExecutionContext.h"
namespace WebCore {
diff --git a/Source/WebCore/storage/IDBCompleteEvent.cpp b/Source/WebCore/storage/IDBVersionChangeEvent.cpp
index 20ee57a..5fd9d8b 100644
--- a/Source/WebCore/storage/IDBCompleteEvent.cpp
+++ b/Source/WebCore/storage/IDBVersionChangeEvent.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,9 +10,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -27,7 +24,7 @@
*/
#include "config.h"
-#include "IDBCompleteEvent.h"
+#include "IDBVersionChangeEvent.h"
#if ENABLE(INDEXED_DATABASE)
@@ -36,20 +33,26 @@
namespace WebCore {
-PassRefPtr<IDBCompleteEvent> IDBCompleteEvent::create(PassRefPtr<IDBAny> source)
+PassRefPtr<IDBVersionChangeEvent> IDBVersionChangeEvent::create(const String& version, const AtomicString& eventType)
{
- return adoptRef(new IDBCompleteEvent(source));
+ return adoptRef(new IDBVersionChangeEvent(version, eventType));
}
-IDBCompleteEvent::IDBCompleteEvent(PassRefPtr<IDBAny> source)
- : IDBEvent(eventNames().completeEvent, source, false)
+IDBVersionChangeEvent::IDBVersionChangeEvent(const String& version, const AtomicString& eventType)
+ : Event(eventType, false /*canBubble*/, false /*cancelable*/)
+ , m_version(version)
{
}
-IDBCompleteEvent::~IDBCompleteEvent()
+IDBVersionChangeEvent::~IDBVersionChangeEvent()
{
}
+String IDBVersionChangeEvent::version()
+{
+ return m_version;
+}
+
} // namespace WebCore
#endif
diff --git a/Source/WebCore/storage/IDBAbortEvent.h b/Source/WebCore/storage/IDBVersionChangeEvent.h
index fc27989..efd360f 100644
--- a/Source/WebCore/storage/IDBAbortEvent.h
+++ b/Source/WebCore/storage/IDBVersionChangeEvent.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,9 +10,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -26,32 +23,38 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IDBAbortEvent_h
-#define IDBAbortEvent_h
+#ifndef IDBVersionChangeEvent_h
+#define IDBVersionChangeEvent_h
#if ENABLE(INDEXED_DATABASE)
-#include "IDBEvent.h"
+#include "Event.h"
#include "PlatformString.h"
#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
namespace WebCore {
-class IDBAbortEvent : public IDBEvent {
+class IDBAny;
+
+class IDBVersionChangeEvent : public Event {
public:
- static PassRefPtr<IDBAbortEvent> create(PassRefPtr<IDBAny> source);
+ static PassRefPtr<IDBVersionChangeEvent> create(const String& version, const AtomicString& eventType);
// FIXME: Need to allow creation of these events from JS.
- virtual ~IDBAbortEvent();
+ virtual ~IDBVersionChangeEvent();
+
+ virtual bool isIDBVersionChangeEvent() const { return true; }
- virtual bool isIDBAbortEvent() const { return true; }
+ virtual String version();
private:
- IDBAbortEvent(PassRefPtr<IDBAny> source);
+ IDBVersionChangeEvent(const String& version, const AtomicString& eventType);
+
+ String m_version;
};
} // namespace WebCore
#endif // ENABLE(INDEXED_DATABASE)
-#endif // IDBAbortEvent_h
+#endif // IDBVersionChangeEvent_h
diff --git a/Source/WebCore/storage/IDBSuccessEvent.idl b/Source/WebCore/storage/IDBVersionChangeEvent.idl
index b4ea7d2..c6a4171 100644
--- a/Source/WebCore/storage/IDBSuccessEvent.idl
+++ b/Source/WebCore/storage/IDBVersionChangeEvent.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,9 +10,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -30,7 +27,7 @@ module storage {
interface [
Conditional=INDEXED_DATABASE
- ] IDBSuccessEvent : IDBEvent {
- readonly attribute IDBAny result;
+ ] IDBVersionChangeEvent : Event {
+ readonly attribute DOMString version;
};
}
diff --git a/Source/WebCore/storage/IDBSuccessEvent.cpp b/Source/WebCore/storage/IDBVersionChangeRequest.cpp
index 110b78b..8c5416e 100644
--- a/Source/WebCore/storage/IDBSuccessEvent.cpp
+++ b/Source/WebCore/storage/IDBVersionChangeRequest.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,9 +10,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -27,33 +24,34 @@
*/
#include "config.h"
-#include "IDBSuccessEvent.h"
+#include "IDBVersionChangeRequest.h"
#if ENABLE(INDEXED_DATABASE)
-#include "EventNames.h"
-#include "IDBAny.h"
+#include "IDBVersionChangeEvent.h"
+#include "ScriptExecutionContext.h"
namespace WebCore {
-PassRefPtr<IDBSuccessEvent> IDBSuccessEvent::create(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result)
+PassRefPtr<IDBVersionChangeRequest> IDBVersionChangeRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, const String& version)
{
- return adoptRef(new IDBSuccessEvent(source, result));
+ return adoptRef(new IDBVersionChangeRequest(context, source, version));
}
-IDBSuccessEvent::IDBSuccessEvent(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result)
- : IDBEvent(eventNames().successEvent, source, false)
- , m_result(result)
+IDBVersionChangeRequest::IDBVersionChangeRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, const String& version)
+ : IDBRequest(context, source, 0)
+ , m_version(version)
{
}
-IDBSuccessEvent::~IDBSuccessEvent()
+IDBVersionChangeRequest::~IDBVersionChangeRequest()
{
}
-PassRefPtr<IDBAny> IDBSuccessEvent::result()
+void IDBVersionChangeRequest::onBlocked()
{
- return m_result;
+ ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
+ enqueueEvent(IDBVersionChangeEvent::create(m_version, eventNames().blockedEvent));
}
} // namespace WebCore
diff --git a/Source/WebCore/storage/IDBVersionChangeRequest.h b/Source/WebCore/storage/IDBVersionChangeRequest.h
new file mode 100644
index 0000000..32cf4c0
--- /dev/null
+++ b/Source/WebCore/storage/IDBVersionChangeRequest.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef IDBVersionChangeRequest_h
+#define IDBVersionChangeRequest_h
+
+#if ENABLE(INDEXED_DATABASE)
+
+#include "IDBRequest.h"
+
+namespace WebCore {
+
+class IDBVersionChangeRequest : public IDBRequest {
+public:
+ static PassRefPtr<IDBVersionChangeRequest> create(ScriptExecutionContext*, PassRefPtr<IDBAny> source, const String& version);
+ virtual ~IDBVersionChangeRequest();
+
+ virtual void onBlocked();
+
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(blocked);
+
+private:
+ IDBVersionChangeRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source, const String& version);
+
+ String m_version;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(INDEXED_DATABASE)
+
+#endif // IDBRequest_h
diff --git a/Source/WebCore/storage/IDBErrorEvent.idl b/Source/WebCore/storage/IDBVersionChangeRequest.idl
index 5c58f6f..ffac735 100644
--- a/Source/WebCore/storage/IDBErrorEvent.idl
+++ b/Source/WebCore/storage/IDBVersionChangeRequest.idl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,9 +10,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -29,9 +26,9 @@
module storage {
interface [
- Conditional=INDEXED_DATABASE
- ] IDBErrorEvent : IDBEvent {
- readonly attribute unsigned short code;
- readonly attribute DOMString message;
+ Conditional=INDEXED_DATABASE,
+ EventTarget
+ ] IDBVersionChangeRequest : IDBRequest {
+ attribute EventListener onblocked;
};
}
diff --git a/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp b/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp
index 38b2983..e6a1ad5 100644
--- a/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp
+++ b/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp
@@ -28,7 +28,9 @@
#if ENABLE(INDEXED_DATABASE)
+#include "IDBKey.h"
#include "PlatformBridge.h"
+#include "SerializedScriptValue.h"
namespace WebCore {
@@ -37,6 +39,11 @@ void IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(const Ve
PlatformBridge::createIDBKeysFromSerializedValuesAndKeyPath(values, keyPath, keys);
}
+PassRefPtr<SerializedScriptValue> IDBKeyPathBackendImpl::injectIDBKeyIntoSerializedValue(PassRefPtr<IDBKey> key, PassRefPtr<SerializedScriptValue> value, const String& keyPath)
+{
+ return PlatformBridge::injectIDBKeyIntoSerializedValue(key, value, keyPath);
+}
+
} // namespace WebCore
#endif