IDB: ObjectStore cursor advance() support
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Jan 2014 22:23:24 +0000 (22:23 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Jan 2014 22:23:24 +0000 (22:23 +0000)
<rdar://problem/15779645> and https://bugs.webkit.org/show_bug.cgi?id=127866

Reviewed by Sam Weinig.

Source/WebCore:

Add IDBKeyData sorting for database collation:
* Modules/indexeddb/IDBKeyData.cpp:
(WebCore::IDBKeyData::compare):
* Modules/indexeddb/IDBKeyData.h:

* WebCore.exp.in:

* platform/sql/SQLiteTransaction.h:
(WebCore::SQLiteTransaction::database):

Source/WebKit2:

Plumb calls through to the backing store:
* DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp:
(WebKit::UniqueIDBDatabase::openCursorInBackingStore):
(WebKit::UniqueIDBDatabase::advanceCursorInBackingStore):
(WebKit::UniqueIDBDatabase::iterateCursorInBackingStore):

* DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h:
* DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp:
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::createAndPopulateInitialMetadata): Update schema
  for Records table.
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::getOrEstablishMetadata):
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::establishTransaction):
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::resetTransaction):
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::putRecord):
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::openCursor): Create a SQLiteIDBCursor object.
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::advanceCursor): Call through to that object.
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::iterateCursor): Ditto.
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::idbKeyCollate): Renamed for clarity.
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::unregisterCursor): Cleanup open cursors.
* DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h:

* DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp: Added.
(WebKit::SQLiteIDBCursor::maybeCreate):
(WebKit::SQLiteIDBCursor::SQLiteIDBCursor):
(WebKit::SQLiteIDBCursor::establishStatement):
(WebKit::SQLiteIDBCursor::createIndexCursorStatement): Stubbed for now.
(WebKit::SQLiteIDBCursor::createObjectStoreCursorStatement): Create the appropriate query for
  the passed in IDBKeyRange.
(WebKit::SQLiteIDBCursor::advance): Step through that query.
(WebKit::SQLiteIDBCursor::iterate): Stubbed for now.
* DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h: Added.
(WebKit::SQLiteIDBCursor::identifier):
(WebKit::SQLiteIDBCursor::transaction):
(WebKit::SQLiteIDBCursor::currentKey):
(WebKit::SQLiteIDBCursor::currentPrimaryKey):
(WebKit::SQLiteIDBCursor::currentValue):

Add infrastructure for a SQLiteIDBTransaction to keep track of (and clean up) its open cursors:
* DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.cpp:
(WebKit::SQLiteIDBTransaction::SQLiteIDBTransaction):
(WebKit::SQLiteIDBTransaction::~SQLiteIDBTransaction):
(WebKit::SQLiteIDBTransaction::reset):
(WebKit::SQLiteIDBTransaction::openCursor):
(WebKit::SQLiteIDBTransaction::clearCursors):
* DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.h:
(WebKit::SQLiteIDBTransaction::create):
(WebKit::SQLiteIDBTransaction::sqliteTransaction):

Add accessors for the two fields:
* DatabaseProcess/IndexedDB/IDBIdentifier.h:
(WebKit::IDBIdentifier::connection):
(WebKit::IDBIdentifier::id):

* WebKit2.xcodeproj/project.pbxproj:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@163113 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Modules/indexeddb/IDBKeyData.cpp
Source/WebCore/Modules/indexeddb/IDBKeyData.h
Source/WebCore/WebCore.exp.in
Source/WebCore/platform/sql/SQLiteTransaction.h
Source/WebKit2/ChangeLog
Source/WebKit2/DatabaseProcess/IndexedDB/IDBIdentifier.h
Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp
Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h
Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp [new file with mode: 0644]
Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h [new file with mode: 0644]
Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.cpp
Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.h
Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp
Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h
Source/WebKit2/WebKit2.xcodeproj/project.pbxproj

index 7430e98..392117b 100644 (file)
@@ -1,3 +1,20 @@
+2014-01-30  Brady Eidson  <beidson@apple.com>
+
+        IDB: ObjectStore cursor advance() support
+        <rdar://problem/15779645> and https://bugs.webkit.org/show_bug.cgi?id=127866
+
+        Reviewed by Sam Weinig.
+
+        Add IDBKeyData sorting for database collation:
+        * Modules/indexeddb/IDBKeyData.cpp:
+        (WebCore::IDBKeyData::compare):
+        * Modules/indexeddb/IDBKeyData.h:
+
+        * WebCore.exp.in:
+
+        * platform/sql/SQLiteTransaction.h:
+        (WebCore::SQLiteTransaction::database):
+
 2014-01-30  David Kilzer  <ddkilzer@apple.com>
 
         De-virtual-ize CachedResource::isImage()
index ee7f5e7..c8883b9 100644 (file)
@@ -200,6 +200,52 @@ bool IDBKeyData::decode(KeyedDecoder& decoder, IDBKeyData& result)
     return decoder.decodeObjects("array", result.arrayValue, arrayFunction);
 }
 
+int IDBKeyData::compare(const IDBKeyData& other)
+{
+    if (type == IDBKey::InvalidType) {
+        if (other.type != IDBKey::InvalidType)
+            return -1;
+        if (other.type == IDBKey::InvalidType)
+            return 0;
+    } else if (other.type == IDBKey::InvalidType)
+        return 1;
+
+    // The IDBKey::Type enum is in reverse sort order.
+    if (type != other.type)
+        return type < other.type ? 1 : -1;
+
+    // The types are the same, so handle actual value comparison.
+    switch (type) {
+    case IDBKey::InvalidType:
+        // InvalidType should have been fully handled above
+        ASSERT_NOT_REACHED();
+        return 0;
+    case IDBKey::ArrayType:
+        for (size_t i = 0; i < arrayValue.size() && i < other.arrayValue.size(); ++i) {
+            if (int result = arrayValue[i].compare(other.arrayValue[i]))
+                return result;
+        }
+        if (arrayValue.size() < other.arrayValue.size())
+            return -1;
+        if (arrayValue.size() > other.arrayValue.size())
+            return 1;
+        return 0;
+    case IDBKey::StringType:
+        return codePointCompare(stringValue, other.stringValue);
+    case IDBKey::DateType:
+    case IDBKey::NumberType:
+        if (numberValue == other.numberValue)
+            return 0;
+        return numberValue > other.numberValue ? 1 : -1;
+    case IDBKey::MinType:
+        ASSERT_NOT_REACHED();
+        return 0;
+    }
+
+    ASSERT_NOT_REACHED();
+    return 0;
+}
+
 }
 
 #endif // ENABLE(INDEXED_DATABASE)
index 6a4f64a..ae83ef2 100644 (file)
@@ -52,6 +52,12 @@ struct IDBKeyData {
     void encode(KeyedEncoder&) const;
     static bool decode(KeyedDecoder&, IDBKeyData&);
 
+    // compare() has the same semantics as strcmp().
+    //   - Returns negative if this IDBKeyData is less than other.
+    //   - Returns positive if this IDBKeyData is greater than other.
+    //   - Returns zero if this IDBKeyData is equal to other.
+    int compare(const IDBKeyData& other);
+
     IDBKey::Type type;
     Vector<IDBKeyData> arrayValue;
     String stringValue;
index b16e23d..f201d70 100644 (file)
@@ -3086,6 +3086,7 @@ __ZNK7WebCore11IDBKeyRange9isOnlyKeyEv
 __ZNK7WebCore15IDBKeyRangeData22maybeCreateIDBKeyRangeEv
 __ZNK7WebCore6IDBKey7isValidEv
 __ZN7WebCore10IDBKeyData6decodeERNS_12KeyedDecoderERS0_
+__ZN7WebCore10IDBKeyData7compareERKS0_
 __ZN7WebCore10IDBKeyDataC1EPKNS_6IDBKeyE
 __ZN7WebCore10IDBKeyPathC1ERKN3WTF6StringE
 __ZN7WebCore10IDBKeyPathC1ERKN3WTF6VectorINS1_6StringELm0ENS1_15CrashOnOverflowEEE
index 0546f4f..777c7ea 100644 (file)
@@ -46,6 +46,9 @@ public:
     
     bool inProgress() const { return m_inProgress; }
     bool wasRolledBackBySqlite() const;
+
+    SQLiteDatabase& database() const { return m_db; }
+
 private:
     SQLiteDatabase& m_db;
     bool m_inProgress;
index a120c09..4a25aba 100644 (file)
@@ -1,3 +1,65 @@
+2014-01-30  Brady Eidson  <beidson@apple.com>
+
+        IDB: ObjectStore cursor advance() support
+        <rdar://problem/15779645> and https://bugs.webkit.org/show_bug.cgi?id=127866
+
+        Reviewed by Sam Weinig.
+
+        Plumb calls through to the backing store:
+        * DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp:
+        (WebKit::UniqueIDBDatabase::openCursorInBackingStore):
+        (WebKit::UniqueIDBDatabase::advanceCursorInBackingStore):
+        (WebKit::UniqueIDBDatabase::iterateCursorInBackingStore):
+
+        * DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h:
+        * DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp:
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::createAndPopulateInitialMetadata): Update schema
+          for Records table.
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::getOrEstablishMetadata):
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::establishTransaction):
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::resetTransaction):
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::putRecord):
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::openCursor): Create a SQLiteIDBCursor object.
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::advanceCursor): Call through to that object.
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::iterateCursor): Ditto.
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::idbKeyCollate): Renamed for clarity.
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::unregisterCursor): Cleanup open cursors.
+        * DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h:
+
+        * DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp: Added.
+        (WebKit::SQLiteIDBCursor::maybeCreate):
+        (WebKit::SQLiteIDBCursor::SQLiteIDBCursor):
+        (WebKit::SQLiteIDBCursor::establishStatement):
+        (WebKit::SQLiteIDBCursor::createIndexCursorStatement): Stubbed for now.
+        (WebKit::SQLiteIDBCursor::createObjectStoreCursorStatement): Create the appropriate query for
+          the passed in IDBKeyRange.
+        (WebKit::SQLiteIDBCursor::advance): Step through that query.
+        (WebKit::SQLiteIDBCursor::iterate): Stubbed for now.
+        * DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h: Added.
+        (WebKit::SQLiteIDBCursor::identifier):
+        (WebKit::SQLiteIDBCursor::transaction):
+        (WebKit::SQLiteIDBCursor::currentKey):
+        (WebKit::SQLiteIDBCursor::currentPrimaryKey):
+        (WebKit::SQLiteIDBCursor::currentValue):
+
+        Add infrastructure for a SQLiteIDBTransaction to keep track of (and clean up) its open cursors:
+        * DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.cpp:
+        (WebKit::SQLiteIDBTransaction::SQLiteIDBTransaction):
+        (WebKit::SQLiteIDBTransaction::~SQLiteIDBTransaction):
+        (WebKit::SQLiteIDBTransaction::reset):
+        (WebKit::SQLiteIDBTransaction::openCursor):
+        (WebKit::SQLiteIDBTransaction::clearCursors):
+        * DatabaseProcess/IndexedDB/sqlite/SQLiteIDBTransaction.h:
+        (WebKit::SQLiteIDBTransaction::create):
+        (WebKit::SQLiteIDBTransaction::sqliteTransaction):
+
+        Add accessors for the two fields:
+        * DatabaseProcess/IndexedDB/IDBIdentifier.h:
+        (WebKit::IDBIdentifier::connection):
+        (WebKit::IDBIdentifier::id):
+
+        * WebKit2.xcodeproj/project.pbxproj:
+
 2014-01-30  Anders Carlsson  <andersca@apple.com>
 
         Implement policy response delegate handling
index ab13139..205ef7e 100644 (file)
@@ -81,6 +81,14 @@ public:
         return !m_connection && m_identifier == -1;
     }
 
+    DatabaseProcessIDBConnection& connection() const
+    {
+        ASSERT(m_connection);
+        return *m_connection;
+    }
+
+    int64_t id() const { return m_identifier; }
+
 private:
     // If any members are added that cannot be safely copied across threads, isolatedCopy() must be updated.
     DatabaseProcessIDBConnection* m_connection;
index 8f336c1..8f37a2f 100644 (file)
@@ -36,7 +36,6 @@
 #include "UniqueIDBDatabaseBackingStoreSQLite.h"
 #include "WebCrossThreadCopier.h"
 #include <WebCore/FileSystem.h>
-#include <WebCore/IDBDatabaseBackend.h>
 #include <WebCore/IDBDatabaseMetadata.h>
 #include <WebCore/IDBGetResult.h>
 #include <WebCore/IDBKeyData.h>
@@ -877,11 +876,22 @@ void UniqueIDBDatabase::didGetRecordFromBackingStore(uint64_t requestID, const I
     request->completeRequest(result, errorCode, errorMessage);
 }
 
-void UniqueIDBDatabase::openCursorInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection, IndexedDB::CursorType, IDBDatabaseBackend::TaskType, const IDBKeyRangeData&)
+void UniqueIDBDatabase::openCursorInBackingStore(uint64_t requestID, const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection cursorDirection, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, const IDBKeyRangeData& keyRange)
 {
-    // FIXME: Implement
+    ASSERT(!isMainThread());
+    ASSERT(m_backingStore);
+
+    int64_t cursorID = 0;
+    int32_t errorCode = 0;
+    String errorMessage;
+    bool success = m_backingStore->openCursor(transactionIdentifier, objectStoreID, indexID, cursorDirection, cursorType, taskType, keyRange, cursorID);
+
+    if (!success) {
+        errorCode = IDBDatabaseException::UnknownError;
+        errorMessage = ASCIILiteral("Unknown error opening cursor in backing store");
+    }
 
-    postMainThreadTask(createAsyncTask(*this, &UniqueIDBDatabase::didOpenCursorInBackingStore, requestID, 0, IDBDatabaseException::UnknownError, ASCIILiteral("advancing cursors in backing store not supported yet")));
+    postMainThreadTask(createAsyncTask(*this, &UniqueIDBDatabase::didOpenCursorInBackingStore, requestID, cursorID, errorCode, errorMessage));
 }
 
 void UniqueIDBDatabase::didOpenCursorInBackingStore(uint64_t requestID, int64_t cursorID, uint32_t errorCode, const String& errorMessage)
@@ -894,9 +904,19 @@ void UniqueIDBDatabase::didOpenCursorInBackingStore(uint64_t requestID, int64_t
 
 void UniqueIDBDatabase::advanceCursorInBackingStore(uint64_t requestID, const IDBIdentifier& cursorIdentifier, uint64_t count)
 {
-    // FIXME: Implement
+    IDBKeyData key;
+    IDBKeyData primaryKey;
+    Vector<char> value;
+    int32_t errorCode = 0;
+    String errorMessage;
+    bool success = m_backingStore->advanceCursor(cursorIdentifier, count, key, primaryKey, value);
+
+    if (!success) {
+        errorCode = IDBDatabaseException::UnknownError;
+        errorMessage = ASCIILiteral("Unknown error advancing cursor in backing store");
+    }
 
-    postMainThreadTask(createAsyncTask(*this, &UniqueIDBDatabase::didAdvanceCursorInBackingStore, requestID, IDBKeyData(), IDBKeyData(), Vector<char>(), IDBDatabaseException::UnknownError, ASCIILiteral("advancing cursors in backing store not supported yet")));
+    postMainThreadTask(createAsyncTask(*this, &UniqueIDBDatabase::didAdvanceCursorInBackingStore, requestID, key, primaryKey, value, errorCode, errorMessage));
 }
 
 void UniqueIDBDatabase::didAdvanceCursorInBackingStore(uint64_t requestID, const IDBKeyData& key, const IDBKeyData& primaryKey, const Vector<char>& value, uint32_t errorCode, const String& errorMessage)
@@ -907,11 +927,21 @@ void UniqueIDBDatabase::didAdvanceCursorInBackingStore(uint64_t requestID, const
     request->completeRequest(key, primaryKey, SharedBuffer::create(value.data(), value.size()), errorCode, errorMessage);
 }
 
-void UniqueIDBDatabase::iterateCursorInBackingStore(uint64_t requestID, const IDBIdentifier& cursorIdentifier, const IDBKeyData&)
+void UniqueIDBDatabase::iterateCursorInBackingStore(uint64_t requestID, const IDBIdentifier& cursorIdentifier, const IDBKeyData& iterateKey)
 {
-    // FIXME: Implement
+    IDBKeyData key;
+    IDBKeyData primaryKey;
+    Vector<char> value;
+    int32_t errorCode = 0;
+    String errorMessage;
+    bool success = m_backingStore->iterateCursor(cursorIdentifier, iterateKey, key, primaryKey, value);
+
+    if (!success) {
+        errorCode = IDBDatabaseException::UnknownError;
+        errorMessage = ASCIILiteral("Unknown error iterating cursor in backing store");
+    }
 
-    postMainThreadTask(createAsyncTask(*this, &UniqueIDBDatabase::didIterateCursorInBackingStore, requestID, IDBKeyData(), IDBKeyData(), Vector<char>(), IDBDatabaseException::UnknownError, ASCIILiteral("iterating cursors in backing store not supported yet")));
+    postMainThreadTask(createAsyncTask(*this, &UniqueIDBDatabase::didIterateCursorInBackingStore, requestID, key, primaryKey, value, errorCode, errorMessage));
 }
 
 void UniqueIDBDatabase::didIterateCursorInBackingStore(uint64_t requestID, const IDBKeyData& key, const IDBKeyData& primaryKey, const Vector<char>& value, uint32_t errorCode, const String& errorMessage)
index 435fb09..084ad86 100644 (file)
@@ -71,6 +71,10 @@ public:
 
     virtual bool getKeyRecordFromObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKey&, RefPtr<WebCore::SharedBuffer>& result) = 0;
     virtual bool getKeyRangeRecordFromObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKeyRange&, RefPtr<WebCore::SharedBuffer>& result, RefPtr<WebCore::IDBKey>& resultKey) = 0;
+
+    virtual bool openCursor(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, WebCore::IndexedDB::CursorDirection, WebCore::IndexedDB::CursorType, WebCore::IDBDatabaseBackend::TaskType, const WebCore::IDBKeyRangeData&, int64_t& cursorID) = 0;
+    virtual bool advanceCursor(const IDBIdentifier& cursorIdentifier, uint64_t count, WebCore::IDBKeyData&, WebCore::IDBKeyData&, Vector<char>&) = 0;
+    virtual bool iterateCursor(const IDBIdentifier& cursorIdentifier, const WebCore::IDBKeyData&, WebCore::IDBKeyData&, WebCore::IDBKeyData&, Vector<char>&) = 0;
 };
 
 } // namespace WebKit
diff --git a/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp b/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.cpp
new file mode 100644 (file)
index 0000000..2b3005c
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2014 Apple 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 INC. 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 INC. 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 "SQLiteIDBCursor.h"
+
+#if ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)
+
+#include "IDBSerialization.h"
+#include "Logging.h"
+#include "SQLiteIDBTransaction.h"
+#include <WebCore/SQLiteStatement.h>
+#include <WebCore/SQLiteTransaction.h>
+#include <sqlite3.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+std::unique_ptr<SQLiteIDBCursor> SQLiteIDBCursor::maybeCreate(SQLiteIDBTransaction* transaction, const IDBIdentifier& cursorIdentifier, int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection cursorDirection, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, const IDBKeyRangeData& keyRange)
+{
+    auto cursor = std::unique_ptr<SQLiteIDBCursor>(new SQLiteIDBCursor(transaction, cursorIdentifier, objectStoreID, indexID, cursorDirection, cursorType, taskType, keyRange));
+
+    if (!cursor->establishStatement())
+        return nullptr;
+
+    return cursor;
+}
+
+SQLiteIDBCursor::SQLiteIDBCursor(SQLiteIDBTransaction* transaction, const IDBIdentifier& cursorIdentifier, int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection cursorDirection, IndexedDB::CursorType, IDBDatabaseBackend::TaskType, const IDBKeyRangeData& keyRange)
+    : m_transaction(transaction)
+    , m_cursorIdentifier(cursorIdentifier)
+    , m_objectStoreID(objectStoreID)
+    , m_indexID(indexID)
+    , m_cursorDirection(cursorDirection)
+    , m_keyRange(keyRange)
+    , m_completed(false)
+{
+    ASSERT(m_objectStoreID);
+}
+
+bool SQLiteIDBCursor::establishStatement()
+{
+    if (m_indexID != IDBIndexMetadata::InvalidId)
+        return createIndexCursorStatement();
+
+    return createObjectStoreCursorStatement();
+}
+
+bool SQLiteIDBCursor::createIndexCursorStatement()
+{
+    LOG_ERROR("Index cursor not yet supported (index id is %lli)", m_indexID);
+    return false;
+}
+
+bool SQLiteIDBCursor::createObjectStoreCursorStatement()
+{
+    ASSERT(m_transaction->sqliteTransaction());
+    SQLiteDatabase& database = m_transaction->sqliteTransaction()->database();
+
+    String lowerSQL;
+    if (!m_keyRange.lowerKey.isNull)
+        lowerSQL = m_keyRange.lowerOpen ? ASCIILiteral("AND key >= CAST(? AS TEXT)") : ASCIILiteral("AND key > CAST(? AS TEXT)");
+
+    String upperSQL;
+    if (!m_keyRange.upperKey.isNull)
+        lowerSQL = m_keyRange.upperOpen ? ASCIILiteral("AND key <= CAST(? AS TEXT)") : ASCIILiteral("AND key < CAST(? AS TEXT)");
+
+    String orderSQL;
+    if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate)
+        orderSQL = ASCIILiteral(" ORDER BY key;");
+    else
+        orderSQL = ASCIILiteral(" ORDER BY key DESC;");
+
+    String sql = ASCIILiteral("SELECT key, value FROM Records WHERE objectStoreID = ?") + lowerSQL + upperSQL + orderSQL;
+
+    LOG(IDB, "Creating cursor with SQL query: \"%s\"", sql.utf8().data());
+
+    m_statement = std::make_unique<SQLiteStatement>(database, sql);
+
+    if (m_statement->prepare() != SQLResultOk
+        || m_statement->bindInt64(1, m_objectStoreID) != SQLResultOk) {
+        LOG_ERROR("Could not create cursor statement");
+        return false;
+    }
+
+    int nextBindArgument = 2;
+
+    if (!lowerSQL.isEmpty()) {
+        RefPtr<SharedBuffer> buffer = serializeIDBKeyData(m_keyRange.lowerKey);
+        if (m_statement->bindBlob(nextBindArgument++, buffer->data(), buffer->size()) != SQLResultOk) {
+            LOG_ERROR("Could not create cursor statement");
+            return false;
+        }
+    }
+    if (!upperSQL.isEmpty()) {
+        RefPtr<SharedBuffer> buffer = serializeIDBKeyData(m_keyRange.upperKey);
+        if (m_statement->bindBlob(nextBindArgument, buffer->data(), buffer->size()) != SQLResultOk) {
+            LOG_ERROR("Could not create cursor statement");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool SQLiteIDBCursor::advance(uint64_t count)
+{
+    ASSERT(m_transaction->sqliteTransaction());
+    ASSERT(m_statement);
+
+    if (m_completed) {
+        LOG_ERROR("Attempt to advance a completed cursor");
+        return false;
+    }
+
+    int result = m_statement->step();
+    if (result == SQLResultDone) {
+        m_completed = true;
+        return true;
+    }
+
+    if (result != SQLResultRow) {
+        LOG_ERROR("Error advancing cursor - (%i) %s", result, m_transaction->sqliteTransaction()->database().lastErrorMsg());
+        m_completed = true;
+        return false;
+    }
+
+    Vector<char> keyData;
+    m_statement->getColumnBlobAsVector(0, keyData);
+
+    IDBKeyData key;
+    if (!deserializeIDBKeyData(reinterpret_cast<const uint8_t*>(keyData.data()), keyData.size(), key)) {
+        LOG_ERROR("Unable to deserialize key data from database while advancing cursor");
+        m_completed = true;
+        return false;
+    }
+
+    m_statement->getColumnBlobAsVector(1, keyData);
+
+    m_currentKey = key;
+    m_currentPrimaryKey = key;
+    m_currentValue = keyData;
+
+    return true;
+}
+
+bool SQLiteIDBCursor::iterate(const WebCore::IDBKeyData& targetKey)
+{
+    ASSERT(m_transaction->sqliteTransaction());
+    ASSERT(m_statement);
+
+    LOG_ERROR("IDBCursor.iterate not supported yet");
+    return false;
+}
+
+} // namespace WebKit
+
+#endif // ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)
diff --git a/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h b/Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/SQLiteIDBCursor.h
new file mode 100644 (file)
index 0000000..6030cf5
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 Apple 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 INC. 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 INC. 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 SQLiteIDBCursor_h
+#define SQLiteIDBCursor_h
+
+#if ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)
+
+#include "IDBIdentifier.h"
+#include <WebCore/IDBDatabaseBackend.h>
+#include <WebCore/IDBKeyData.h>
+#include <WebCore/IDBKeyRangeData.h>
+#include <WebCore/SQLiteStatement.h>
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+namespace IndexedDB {
+enum class CursorDirection;
+enum class CursorType;
+}
+
+}
+
+namespace WebKit {
+
+class SQLiteIDBTransaction;
+
+class SQLiteIDBCursor {
+    WTF_MAKE_NONCOPYABLE(SQLiteIDBCursor);
+
+public:
+    static std::unique_ptr<SQLiteIDBCursor> maybeCreate(SQLiteIDBTransaction*, const IDBIdentifier& cursorIdentifier, int64_t objectStoreID, int64_t indexID, WebCore::IndexedDB::CursorDirection, WebCore::IndexedDB::CursorType, WebCore::IDBDatabaseBackend::TaskType, const WebCore::IDBKeyRangeData&);
+
+    const IDBIdentifier& identifier() const { return m_cursorIdentifier; }
+    SQLiteIDBTransaction* transaction() const { return m_transaction; }
+
+    const WebCore::IDBKeyData& currentKey() const { return m_currentKey; }
+    const WebCore::IDBKeyData& currentPrimaryKey() const { return m_currentPrimaryKey; }
+    const Vector<char>& currentValue() const { return m_currentValue; }
+
+    bool advance(uint64_t count);
+    bool iterate(const WebCore::IDBKeyData& targetKey);
+
+private:
+    SQLiteIDBCursor(SQLiteIDBTransaction*, const IDBIdentifier& cursorIdentifier, int64_t objectStoreID, int64_t indexID, WebCore::IndexedDB::CursorDirection, WebCore::IndexedDB::CursorType, WebCore::IDBDatabaseBackend::TaskType, const WebCore::IDBKeyRangeData&);
+
+    bool establishStatement();
+    bool createIndexCursorStatement();
+    bool createObjectStoreCursorStatement();
+
+    SQLiteIDBTransaction* m_transaction;
+    IDBIdentifier m_cursorIdentifier;
+    int64_t m_objectStoreID;
+    int64_t m_indexID;
+    WebCore::IndexedDB::CursorDirection m_cursorDirection;
+    WebCore::IDBKeyRangeData m_keyRange;
+
+    WebCore::IDBKeyData m_currentKey;
+    WebCore::IDBKeyData m_currentPrimaryKey;
+    Vector<char> m_currentValue;
+
+    std::unique_ptr<WebCore::SQLiteStatement> m_statement;
+
+    bool m_completed;
+};
+
+} // namespace WebKit
+
+#endif // ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)
+#endif // SQLiteIDBCursor_h
index d82386e..d9da196 100644 (file)
 
 #if ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)
 
+#include "SQLiteIDBCursor.h"
+#include "UniqueIDBDatabaseBackingStoreSQLite.h"
 #include <WebCore/IndexedDB.h>
 #include <WebCore/SQLiteTransaction.h>
 
 using namespace WebCore;
 
+static int64_t nextCursorID = 1;
+
 namespace WebKit {
 
-SQLiteIDBTransaction::SQLiteIDBTransaction(const IDBIdentifier& transactionIdentifier, IndexedDB::TransactionMode mode)
+SQLiteIDBTransaction::SQLiteIDBTransaction(UniqueIDBDatabaseBackingStoreSQLite& backingStore, const IDBIdentifier& transactionIdentifier, IndexedDB::TransactionMode mode)
     : m_identifier(transactionIdentifier)
     , m_mode(mode)
+    , m_backingStore(backingStore)
 {
 }
 
@@ -44,6 +49,9 @@ SQLiteIDBTransaction::~SQLiteIDBTransaction()
 {
     if (inProgress())
         m_sqliteTransaction->rollback();
+
+    // Explicitly clear cursors, as that also unregisters them from the backing store.
+    clearCursors();
 }
 
 
@@ -71,6 +79,7 @@ bool SQLiteIDBTransaction::commit()
 bool SQLiteIDBTransaction::reset()
 {
     m_sqliteTransaction = nullptr;
+    clearCursors();
 
     return true;
 }
@@ -84,6 +93,36 @@ bool SQLiteIDBTransaction::rollback()
     return true;
 }
 
+SQLiteIDBCursor* SQLiteIDBTransaction::openCursor(int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection cursorDirection, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, const IDBKeyRangeData& keyRange)
+{
+    ASSERT(m_sqliteTransaction);
+    if (!m_sqliteTransaction->inProgress())
+        return nullptr;
+
+    IDBIdentifier cursorIdentifier(m_identifier.connection(), nextCursorID++);
+
+    auto addResult = m_cursors.add(cursorIdentifier, SQLiteIDBCursor::maybeCreate(this, cursorIdentifier, objectStoreID, indexID, cursorDirection, cursorType, taskType, keyRange));
+
+    ASSERT(addResult.isNewEntry);
+
+    // It is possible the cursor failed to create and we just stored a null value.
+    if (!addResult.iterator->value) {
+        m_cursors.remove(addResult.iterator);
+        return nullptr;
+    }
+
+    return addResult.iterator->value.get();
+}
+
+void SQLiteIDBTransaction::clearCursors()
+{
+    // Iterate over the keys instead of each key/value pair because std::unique_ptr<> can't be iterated over directly.
+    for (auto key : m_cursors.keys())
+        m_backingStore.unregisterCursor(m_cursors.get(key));
+
+    m_cursors.clear();
+}
+
 bool SQLiteIDBTransaction::inProgress() const
 {
     return m_sqliteTransaction && m_sqliteTransaction->inProgress();
index b9050b5..df1bf16 100644 (file)
@@ -29,6 +29,8 @@
 #if ENABLE(INDEXED_DATABASE) && ENABLE(DATABASE_PROCESS)
 
 #include "IDBIdentifier.h"
+#include <WebCore/IDBDatabaseBackend.h>
+#include <wtf/HashMap.h>
 #include <wtf/Noncopyable.h>
 
 namespace WebCore {
@@ -37,19 +39,26 @@ class SQLiteDatabase;
 class SQLiteTransaction;
 
 namespace IndexedDB {
+enum class CursorDirection;
+enum class CursorType;
 enum class TransactionMode;
 }
 
+struct IDBKeyRangeData;
+
 }
 
 namespace WebKit {
 
+class SQLiteIDBCursor;
+class UniqueIDBDatabaseBackingStoreSQLite;
+
 class SQLiteIDBTransaction {
     WTF_MAKE_NONCOPYABLE(SQLiteIDBTransaction);
 public:
-    static std::unique_ptr<SQLiteIDBTransaction> create(const IDBIdentifier& transactionIdentifier, WebCore::IndexedDB::TransactionMode mode)
+    static std::unique_ptr<SQLiteIDBTransaction> create(UniqueIDBDatabaseBackingStoreSQLite& backingStore, const IDBIdentifier& transactionIdentifier, WebCore::IndexedDB::TransactionMode mode)
     {
-        return std::unique_ptr<SQLiteIDBTransaction>(new SQLiteIDBTransaction(transactionIdentifier, mode));
+        return std::unique_ptr<SQLiteIDBTransaction>(new SQLiteIDBTransaction(backingStore, transactionIdentifier, mode));
     }
 
     ~SQLiteIDBTransaction();
@@ -61,16 +70,24 @@ public:
     bool reset();
     bool rollback();
 
+    SQLiteIDBCursor* openCursor(int64_t objectStoreID, int64_t indexID, WebCore::IndexedDB::CursorDirection, WebCore::IndexedDB::CursorType, WebCore::IDBDatabaseBackend::TaskType, const WebCore::IDBKeyRangeData&);
+
     WebCore::IndexedDB::TransactionMode mode() const { return m_mode; }
     bool inProgress() const;
 
+    WebCore::SQLiteTransaction* sqliteTransaction() const { return m_sqliteTransaction.get(); }
+
 private:
-    SQLiteIDBTransaction(const IDBIdentifier& transactionIdentifier, WebCore::IndexedDB::TransactionMode);
+    SQLiteIDBTransaction(UniqueIDBDatabaseBackingStoreSQLite&, const IDBIdentifier& transactionIdentifier, WebCore::IndexedDB::TransactionMode);
+
+    void clearCursors();
 
     IDBIdentifier m_identifier;
     WebCore::IndexedDB::TransactionMode m_mode;
 
+    UniqueIDBDatabaseBackingStoreSQLite& m_backingStore;
     std::unique_ptr<WebCore::SQLiteTransaction> m_sqliteTransaction;
+    HashMap<IDBIdentifier, std::unique_ptr<SQLiteIDBCursor>> m_cursors;
 };
 
 } // namespace WebKit
index f2e23f4..79c5c2f 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "ArgumentDecoder.h"
 #include "IDBSerialization.h"
+#include "SQLiteIDBCursor.h"
 #include "SQLiteIDBTransaction.h"
 #include <WebCore/FileSystem.h>
 #include <WebCore/IDBDatabaseMetadata.h>
@@ -92,7 +93,7 @@ std::unique_ptr<IDBDatabaseMetadata> UniqueIDBDatabaseBackingStoreSQLite::create
         return nullptr;
     }
 
-    if (!m_sqliteDB->executeCommand("CREATE TABLE Records (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key BLOB COLLATE IDBKEY NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value BLOB NOT NULL ON CONFLICT FAIL);")) {
+    if (!m_sqliteDB->executeCommand("CREATE TABLE Records (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL, key TEXT COLLATE IDBKEY NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, value NOT NULL ON CONFLICT FAIL);")) {
         LOG_ERROR("Could not create Records table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
         m_sqliteDB = nullptr;
         return nullptr;
@@ -254,9 +255,8 @@ std::unique_ptr<IDBDatabaseMetadata> UniqueIDBDatabaseBackingStoreSQLite::getOrE
     if (!m_sqliteDB)
         return nullptr;
 
-    RefPtr<UniqueIDBDatabaseBackingStoreSQLite> protector(this);
     m_sqliteDB->setCollationFunction("IDBKEY", [this](int aLength, const void* a, int bLength, const void* b) {
-        return collate(aLength, a, bLength, b);
+        return idbKeyCollate(aLength, a, bLength, b);
     });
 
     std::unique_ptr<IDBDatabaseMetadata> metadata = extractExistingMetadata();
@@ -276,7 +276,7 @@ bool UniqueIDBDatabaseBackingStoreSQLite::establishTransaction(const IDBIdentifi
 {
     ASSERT(!isMainThread());
 
-    if (!m_transactions.add(transactionIdentifier, SQLiteIDBTransaction::create(transactionIdentifier, mode)).isNewEntry) {
+    if (!m_transactions.add(transactionIdentifier, SQLiteIDBTransaction::create(*this, transactionIdentifier, mode)).isNewEntry) {
         LOG_ERROR("Attempt to establish transaction identifier that already exists");
         return false;
     }
@@ -314,7 +314,7 @@ bool UniqueIDBDatabaseBackingStoreSQLite::resetTransaction(const IDBIdentifier&
 {
     ASSERT(!isMainThread());
 
-    SQLiteIDBTransaction* transaction = m_transactions.get(transactionIdentifier);
+    std::unique_ptr<SQLiteIDBTransaction> transaction = m_transactions.take(transactionIdentifier);
     if (!transaction) {
         LOG_ERROR("Attempt to reset a transaction that hasn't been established");
         return false;
@@ -684,7 +684,7 @@ bool UniqueIDBDatabaseBackingStoreSQLite::putRecord(const IDBIdentifier& transac
         return false;
     }
     {
-        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO Records VALUES (?, ?, ?);"));
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO Records VALUES (?, CAST(? AS TEXT), ?);"));
         if (sql.prepare() != SQLResultOk
             || sql.bindInt64(1, objectStoreID) != SQLResultOk
             || sql.bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLResultOk
@@ -798,10 +798,109 @@ bool UniqueIDBDatabaseBackingStoreSQLite::getKeyRangeRecordFromObjectStore(const
     return true;
 }
 
-int UniqueIDBDatabaseBackingStoreSQLite::collate(int aLength, const void* a, int bLength, const void* b)
+bool UniqueIDBDatabaseBackingStoreSQLite::openCursor(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, IndexedDB::CursorDirection cursorDirection, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, const IDBKeyRangeData& keyRange, int64_t& cursorID)
+{
+    ASSERT(!isMainThread());
+    ASSERT(m_sqliteDB);
+    ASSERT(m_sqliteDB->isOpen());
+
+    SQLiteIDBTransaction* transaction = m_transactions.get(transactionIdentifier);
+    if (!transaction || !transaction->inProgress()) {
+        LOG_ERROR("Attempt to open a cursor in database without an established, in-progress transaction");
+        return false;
+    }
+
+    SQLiteIDBCursor* cursor = transaction->openCursor(objectStoreID, indexID, cursorDirection, cursorType, taskType, keyRange);
+    if (!cursor)
+        return false;
+
+    m_cursors.set(cursor->identifier(), cursor);
+    cursorID = cursor->identifier().id();
+
+    return true;
+}
+
+bool UniqueIDBDatabaseBackingStoreSQLite::advanceCursor(const IDBIdentifier& cursorIdentifier, uint64_t count, IDBKeyData& key, IDBKeyData& primaryKey, Vector<char>& value)
+{
+    ASSERT(!isMainThread());
+    ASSERT(m_sqliteDB);
+    ASSERT(m_sqliteDB->isOpen());
+
+    SQLiteIDBCursor* cursor = m_cursors.get(cursorIdentifier);
+    if (!cursor) {
+        LOG_ERROR("Attempt to advance a cursor that doesn't exist");
+        return false;
+    }
+    if (!cursor->transaction() || !cursor->transaction()->inProgress()) {
+        LOG_ERROR("Attempt to advance a cursor without an established, in-progress transaction");
+        return false;
+    }
+
+    if (!cursor->advance(count)) {
+        LOG_ERROR("Attempt to advance cursor %lli steps failed", count);
+        return false;
+    }
+
+    key = cursor->currentKey();
+    primaryKey = cursor->currentPrimaryKey();
+    value = cursor->currentValue();
+
+    return true;
+}
+
+bool UniqueIDBDatabaseBackingStoreSQLite::iterateCursor(const IDBIdentifier& cursorIdentifier, const IDBKeyData& targetKey, IDBKeyData& key, IDBKeyData& primaryKey, Vector<char>& value)
+{
+    ASSERT(!isMainThread());
+    ASSERT(m_sqliteDB);
+    ASSERT(m_sqliteDB->isOpen());
+
+    SQLiteIDBCursor* cursor = m_cursors.get(cursorIdentifier);
+    if (!cursor) {
+        LOG_ERROR("Attempt to iterate a cursor that doesn't exist");
+        return false;
+    }
+    if (!cursor->transaction() || !cursor->transaction()->inProgress()) {
+        LOG_ERROR("Attempt to iterate a cursor without an established, in-progress transaction");
+        return false;
+    }
+
+    if (!cursor->iterate(targetKey)) {
+        LOG_ERROR("Attempt to iterate cursor failed");
+        return false;
+    }
+
+    key = cursor->currentKey();
+    primaryKey = cursor->currentPrimaryKey();
+    value = cursor->currentValue();
+
+    return true;
+}
+
+int UniqueIDBDatabaseBackingStoreSQLite::idbKeyCollate(int aLength, const void* aBuffer, int bLength, const void* bBuffer)
+{
+    IDBKeyData a, b;
+    if (!deserializeIDBKeyData(static_cast<const uint8_t*>(aBuffer), aLength, a)) {
+        LOG_ERROR("Unable to deserialize key A in collation function.");
+
+        // There's no way to indicate an error to SQLite - we have to return a sorting decision.
+        // We arbitrarily choose "A > B"
+        return 1;
+    }
+    if (!deserializeIDBKeyData(static_cast<const uint8_t*>(bBuffer), bLength, b)) {
+        LOG_ERROR("Unable to deserialize key B in collation function.");
+
+        // There's no way to indicate an error to SQLite - we have to return a sorting decision.
+        // We arbitrarily choose "A > B"
+        return 1;
+    }
+
+    return a.compare(b);
+}
+
+void UniqueIDBDatabaseBackingStoreSQLite::unregisterCursor(SQLiteIDBCursor* cursor)
 {
-    // FIXME: Implement
-    return 0;
+    ASSERT(m_cursors.contains(cursor->identifier()));
+    m_cursors.remove(cursor->identifier());
 }
 
 } // namespace WebKit
index 5c62f7b..13e0413 100644 (file)
@@ -41,6 +41,7 @@ struct IDBDatabaseMetadata;
 
 namespace WebKit {
 
+class SQLiteIDBCursor;
 class SQLiteIDBTransaction;
 
 class UniqueIDBDatabaseBackingStoreSQLite final : public UniqueIDBDatabaseBackingStore {
@@ -76,6 +77,12 @@ public:
     virtual bool getKeyRecordFromObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKey&, RefPtr<WebCore::SharedBuffer>& result) override;
     virtual bool getKeyRangeRecordFromObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKeyRange&, RefPtr<WebCore::SharedBuffer>& result, RefPtr<WebCore::IDBKey>& resultKey) override;
 
+    virtual bool openCursor(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID, WebCore::IndexedDB::CursorDirection, WebCore::IndexedDB::CursorType, WebCore::IDBDatabaseBackend::TaskType, const WebCore::IDBKeyRangeData&, int64_t& cursorID) override;
+    virtual bool advanceCursor(const IDBIdentifier& cursorIdentifier, uint64_t count, WebCore::IDBKeyData&, WebCore::IDBKeyData&, Vector<char>&) override;
+    virtual bool iterateCursor(const IDBIdentifier& cursorIdentifier, const WebCore::IDBKeyData&, WebCore::IDBKeyData&, WebCore::IDBKeyData&, Vector<char>&) override;
+
+    void unregisterCursor(SQLiteIDBCursor*);
+
 private:
     UniqueIDBDatabaseBackingStoreSQLite(const UniqueIDBDatabaseIdentifier&, const String& databaseDirectory);
 
@@ -83,7 +90,7 @@ private:
     std::unique_ptr<WebCore::IDBDatabaseMetadata> extractExistingMetadata();
     std::unique_ptr<WebCore::IDBDatabaseMetadata> createAndPopulateInitialMetadata();
 
-    int collate(int aLength, const void* a, int bLength, const void* b);
+    int idbKeyCollate(int aLength, const void* a, int bLength, const void* b);
 
     UniqueIDBDatabaseIdentifier m_identifier;
     String m_absoluteDatabaseDirectory;
@@ -91,6 +98,7 @@ private:
     std::unique_ptr<WebCore::SQLiteDatabase> m_sqliteDB;
 
     HashMap<IDBIdentifier, std::unique_ptr<SQLiteIDBTransaction>> m_transactions;
+    HashMap<IDBIdentifier, SQLiteIDBCursor*> m_cursors;
 };
 
 } // namespace WebKit
index 08121aa..e53c17a 100644 (file)
                51032F19180F73BB00961BB7 /* WebToDatabaseProcessConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 51032F17180F73BB00961BB7 /* WebToDatabaseProcessConnection.h */; };
                51032F1D180F791700961BB7 /* DatabaseToWebProcessConnectionMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51032F1B180F791700961BB7 /* DatabaseToWebProcessConnectionMessageReceiver.cpp */; };
                51032F1E180F791700961BB7 /* DatabaseToWebProcessConnectionMessages.h in Headers */ = {isa = PBXBuildFile; fileRef = 51032F1C180F791700961BB7 /* DatabaseToWebProcessConnectionMessages.h */; };
+               51064D35189781C4004B2FEB /* SQLiteIDBCursor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51064D33189781C4004B2FEB /* SQLiteIDBCursor.cpp */; };
+               51064D36189781C4004B2FEB /* SQLiteIDBCursor.h in Headers */ = {isa = PBXBuildFile; fileRef = 51064D34189781C4004B2FEB /* SQLiteIDBCursor.h */; };
                510AFFB916542048001BA05E /* WebResourceLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 510AFFB716542048001BA05E /* WebResourceLoader.cpp */; };
                510AFFBA16542048001BA05E /* WebResourceLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = 510AFFB816542048001BA05E /* WebResourceLoader.h */; };
                510CC8491613C85C00D03ED3 /* NetworkProcess.app in Copy Files */ = {isa = PBXBuildFile; fileRef = 510CC80E1613C79900D03ED3 /* NetworkProcess.app */; };
                5105B0D4162F7A7A00E27709 /* NetworkProcessConnection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = NetworkProcessConnection.cpp; path = Network/NetworkProcessConnection.cpp; sourceTree = "<group>"; };
                5105B0D5162F7A7A00E27709 /* NetworkProcessConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetworkProcessConnection.h; path = Network/NetworkProcessConnection.h; sourceTree = "<group>"; };
                5105B0F31630872E00E27709 /* NetworkProcessProxy.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = NetworkProcessProxy.messages.in; sourceTree = "<group>"; };
+               51064D33189781C4004B2FEB /* SQLiteIDBCursor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SQLiteIDBCursor.cpp; sourceTree = "<group>"; };
+               51064D34189781C4004B2FEB /* SQLiteIDBCursor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SQLiteIDBCursor.h; sourceTree = "<group>"; };
                510AFFB716542048001BA05E /* WebResourceLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebResourceLoader.cpp; path = Network/WebResourceLoader.cpp; sourceTree = "<group>"; };
                510AFFB816542048001BA05E /* WebResourceLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebResourceLoader.h; path = Network/WebResourceLoader.h; sourceTree = "<group>"; };
                510AFFCE16542CBD001BA05E /* WebResourceLoader.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; name = WebResourceLoader.messages.in; path = Network/WebResourceLoader.messages.in; sourceTree = "<group>"; };
                51654EF0184EF241007DC837 /* sqlite */ = {
                        isa = PBXGroup;
                        children = (
+                               51064D33189781C4004B2FEB /* SQLiteIDBCursor.cpp */,
+                               51064D34189781C4004B2FEB /* SQLiteIDBCursor.h */,
                                511D8200185BC217001AED56 /* SQLiteIDBTransaction.cpp */,
                                511D81FD185BBDEE001AED56 /* SQLiteIDBTransaction.h */,
                                51654EFB184EF33F007DC837 /* UniqueIDBDatabaseBackingStoreSQLite.cpp */,
                                51654F00184EF34A007DC837 /* UniqueIDBDatabaseBackingStore.h in Headers */,
                                BC032D7B10F4378D0058C15A /* WebDragClient.h in Headers */,
                                BCA0EF9F12332642007D3CFB /* WebEditCommandProxy.h in Headers */,
+                               51064D36189781C4004B2FEB /* SQLiteIDBCursor.h in Headers */,
                                BC032D7D10F4378D0058C15A /* WebEditorClient.h in Headers */,
                                0F5947A8187B517600437857 /* RemoteScrollingCoordinatorMessages.h in Headers */,
                                516A4A5D120A2CCD00C05B7F /* APIError.h in Headers */,
                                BC1BE1E112D54A410004A228 /* WebGeolocationClient.cpp in Sources */,
                                BC0E5FE612D697160012A72A /* WebGeolocationManager.cpp in Sources */,
                                BC0E606112D6BA910012A72A /* WebGeolocationManagerMessageReceiver.cpp in Sources */,
+                               51064D35189781C4004B2FEB /* SQLiteIDBCursor.cpp in Sources */,
                                BC54CACC12D64291005C67B0 /* WebGeolocationManagerProxy.cpp in Sources */,
                                BC0E618212D6CB1D0012A72A /* WebGeolocationManagerProxyMessageReceiver.cpp in Sources */,
                                BC0E607412D6BC200012A72A /* WebGeolocationPosition.cpp in Sources */,