<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[209960] trunk/Source/WebCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/209960">209960</a></dd>
<dt>Author</dt> <dd>beidson@apple.com</dd>
<dt>Date</dt> <dd>2016-12-16 23:29:17 -0800 (Fri, 16 Dec 2016)</dd>
</dl>
<h3>Log Message</h3>
<pre>IndexedDB: Refactor SQLiteIDBCursor to prepare for cursor prefetching.
https://bugs.webkit.org/show_bug.cgi?id=165978
Reviewed by Alex Christensen.
No new tests (Refactor, no behavior change).
In preparation for cursor prefetching, we need to shift the cursor off of keeping "the current record"
and onto keeping "a deque of fetched records", the first of which is "the current record".
This patch does just that, but with no behavior change; The deque only ever holds 0 or 1 records.
* Modules/indexeddb/server/SQLiteIDBCursor.cpp:
(WebCore::IDBServer::SQLiteIDBCursor::currentData):
(WebCore::IDBServer::SQLiteIDBCursor::objectStoreRecordsChanged):
(WebCore::IDBServer::SQLiteIDBCursor::resetAndRebindStatement):
(WebCore::IDBServer::SQLiteIDBCursor::prefetch):
(WebCore::IDBServer::SQLiteIDBCursor::advance):
(WebCore::IDBServer::SQLiteIDBCursor::fetch):
(WebCore::IDBServer::SQLiteIDBCursor::fetchNextRecord):
(WebCore::IDBServer::SQLiteIDBCursor::markAsErrored):
(WebCore::IDBServer::SQLiteIDBCursor::internalFetchNextRecord):
(WebCore::IDBServer::SQLiteIDBCursor::iterate):
(WebCore::IDBServer::SQLiteIDBCursor::currentKey):
(WebCore::IDBServer::SQLiteIDBCursor::currentPrimaryKey):
(WebCore::IDBServer::SQLiteIDBCursor::currentValue):
(WebCore::IDBServer::SQLiteIDBCursor::didComplete):
(WebCore::IDBServer::SQLiteIDBCursor::didError):
(WebCore::IDBServer::SQLiteIDBCursor::currentRecordRowID):
(WebCore::IDBServer::SQLiteIDBCursor::advanceUnique): Deleted.
(WebCore::IDBServer::SQLiteIDBCursor::advanceOnce): Deleted.
(WebCore::IDBServer::SQLiteIDBCursor::internalAdvanceOnce): Deleted.
* Modules/indexeddb/server/SQLiteIDBCursor.h:
(WebCore::IDBServer::SQLiteIDBCursor::SQLiteCursorRecord::isTerminalRecord):
(WebCore::IDBServer::SQLiteIDBCursor::currentRecordRowID): Deleted.
(WebCore::IDBServer::SQLiteIDBCursor::currentKey): Deleted.
(WebCore::IDBServer::SQLiteIDBCursor::currentPrimaryKey): Deleted.
(WebCore::IDBServer::SQLiteIDBCursor::currentValue): Deleted.
(WebCore::IDBServer::SQLiteIDBCursor::didComplete): Deleted.
(WebCore::IDBServer::SQLiteIDBCursor::didError): Deleted.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbserverSQLiteIDBCursorcpp">trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp</a></li>
<li><a href="#trunkSourceWebCoreModulesindexeddbserverSQLiteIDBCursorh">trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (209959 => 209960)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-12-17 06:42:39 UTC (rev 209959)
+++ trunk/Source/WebCore/ChangeLog        2016-12-17 07:29:17 UTC (rev 209960)
</span><span class="lines">@@ -1,3 +1,47 @@
</span><ins>+2016-12-16 Brady Eidson <beidson@apple.com>
+
+ IndexedDB: Refactor SQLiteIDBCursor to prepare for cursor prefetching.
+ https://bugs.webkit.org/show_bug.cgi?id=165978
+
+ Reviewed by Alex Christensen.
+
+ No new tests (Refactor, no behavior change).
+
+ In preparation for cursor prefetching, we need to shift the cursor off of keeping "the current record"
+ and onto keeping "a deque of fetched records", the first of which is "the current record".
+
+ This patch does just that, but with no behavior change; The deque only ever holds 0 or 1 records.
+
+ * Modules/indexeddb/server/SQLiteIDBCursor.cpp:
+ (WebCore::IDBServer::SQLiteIDBCursor::currentData):
+ (WebCore::IDBServer::SQLiteIDBCursor::objectStoreRecordsChanged):
+ (WebCore::IDBServer::SQLiteIDBCursor::resetAndRebindStatement):
+ (WebCore::IDBServer::SQLiteIDBCursor::prefetch):
+ (WebCore::IDBServer::SQLiteIDBCursor::advance):
+ (WebCore::IDBServer::SQLiteIDBCursor::fetch):
+ (WebCore::IDBServer::SQLiteIDBCursor::fetchNextRecord):
+ (WebCore::IDBServer::SQLiteIDBCursor::markAsErrored):
+ (WebCore::IDBServer::SQLiteIDBCursor::internalFetchNextRecord):
+ (WebCore::IDBServer::SQLiteIDBCursor::iterate):
+ (WebCore::IDBServer::SQLiteIDBCursor::currentKey):
+ (WebCore::IDBServer::SQLiteIDBCursor::currentPrimaryKey):
+ (WebCore::IDBServer::SQLiteIDBCursor::currentValue):
+ (WebCore::IDBServer::SQLiteIDBCursor::didComplete):
+ (WebCore::IDBServer::SQLiteIDBCursor::didError):
+ (WebCore::IDBServer::SQLiteIDBCursor::currentRecordRowID):
+ (WebCore::IDBServer::SQLiteIDBCursor::advanceUnique): Deleted.
+ (WebCore::IDBServer::SQLiteIDBCursor::advanceOnce): Deleted.
+ (WebCore::IDBServer::SQLiteIDBCursor::internalAdvanceOnce): Deleted.
+
+ * Modules/indexeddb/server/SQLiteIDBCursor.h:
+ (WebCore::IDBServer::SQLiteIDBCursor::SQLiteCursorRecord::isTerminalRecord):
+ (WebCore::IDBServer::SQLiteIDBCursor::currentRecordRowID): Deleted.
+ (WebCore::IDBServer::SQLiteIDBCursor::currentKey): Deleted.
+ (WebCore::IDBServer::SQLiteIDBCursor::currentPrimaryKey): Deleted.
+ (WebCore::IDBServer::SQLiteIDBCursor::currentValue): Deleted.
+ (WebCore::IDBServer::SQLiteIDBCursor::didComplete): Deleted.
+ (WebCore::IDBServer::SQLiteIDBCursor::didError): Deleted.
+
</ins><span class="cx"> 2016-12-16 Darin Adler <darin@apple.com>
</span><span class="cx">
</span><span class="cx"> Remove all custom bindings from media streams, using dictionaries instead
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbserverSQLiteIDBCursorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp (209959 => 209960)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp        2016-12-17 06:42:39 UTC (rev 209959)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.cpp        2016-12-17 07:29:17 UTC (rev 209960)
</span><span class="lines">@@ -101,13 +101,16 @@
</span><span class="cx">
</span><span class="cx"> void SQLiteIDBCursor::currentData(IDBGetResult& result)
</span><span class="cx"> {
</span><del>- if (m_currentRecord.completed) {
- ASSERT(!m_currentRecord.errored);
</del><ins>+ ASSERT(!m_fetchedRecords.isEmpty());
+
+ auto& currentRecord = m_fetchedRecords.first();
+ if (currentRecord.completed) {
+ ASSERT(!currentRecord.errored);
</ins><span class="cx"> result = { };
</span><span class="cx"> return;
</span><span class="cx"> }
</span><span class="cx">
</span><del>- result = { m_currentRecord.record.key, m_currentRecord.record.primaryKey, m_currentRecord.record.value ? *m_currentRecord.record.value : IDBValue() };
</del><ins>+ result = { currentRecord.record.key, currentRecord.record.primaryKey, currentRecord.record.value ? *currentRecord.record.value : IDBValue() };
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> static String buildIndexStatement(const IDBKeyRangeData& keyRange, IndexedDB::CursorDirection cursorDirection)
</span><span class="lines">@@ -206,31 +209,18 @@
</span><span class="cx">
</span><span class="cx"> void SQLiteIDBCursor::objectStoreRecordsChanged()
</span><span class="cx"> {
</span><ins>+ if (m_statementNeedsReset)
+ return;
+
</ins><span class="cx"> // If ObjectStore or Index contents changed, we need to reset the statement and bind new parameters to it.
</span><span class="cx"> // This is to pick up any changes that might exist.
</span><ins>+ // We also need to throw away any fetched records as they may no longer be valid.
</ins><span class="cx">
</span><span class="cx"> m_statementNeedsReset = true;
</span><del>-}
</del><ins>+ ASSERT(!m_fetchedRecords.isEmpty());
</ins><span class="cx">
</span><del>-void SQLiteIDBCursor::resetAndRebindStatement()
-{
- ASSERT(!m_currentLowerKey.isNull());
- ASSERT(!m_currentUpperKey.isNull());
- ASSERT(m_transaction->sqliteTransaction());
- ASSERT(m_statement);
- ASSERT(m_statementNeedsReset);
-
- m_statementNeedsReset = false;
-
- // If this cursor never fetched any records, we don't need to reset the statement.
- if (m_currentRecord.record.key.isNull())
- return;
-
- // Otherwise update the lower key or upper key used for the cursor range.
- // This is so the cursor can pick up where we left off.
- // We might also have to change the statement from closed to open so we don't refetch the current key a second time.
</del><span class="cx"> if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate) {
</span><del>- m_currentLowerKey = m_currentRecord.record.key;
</del><ins>+ m_currentLowerKey = m_fetchedRecords.first().record.key;
</ins><span class="cx"> if (!m_keyRange.lowerOpen) {
</span><span class="cx"> m_keyRange.lowerOpen = true;
</span><span class="cx"> m_keyRange.lowerKey = m_currentLowerKey;
</span><span class="lines">@@ -237,7 +227,7 @@
</span><span class="cx"> m_statement = nullptr;
</span><span class="cx"> }
</span><span class="cx"> } else {
</span><del>- m_currentUpperKey = m_currentRecord.record.key;
</del><ins>+ m_currentUpperKey = m_fetchedRecords.first().record.key;
</ins><span class="cx"> if (!m_keyRange.upperOpen) {
</span><span class="cx"> m_keyRange.upperOpen = true;
</span><span class="cx"> m_keyRange.upperKey = m_currentUpperKey;
</span><span class="lines">@@ -245,6 +235,18 @@
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ m_fetchedRecords.clear();
+}
+
+void SQLiteIDBCursor::resetAndRebindStatement()
+{
+ ASSERT(!m_currentLowerKey.isNull());
+ ASSERT(!m_currentUpperKey.isNull());
+ ASSERT(m_transaction->sqliteTransaction());
+ ASSERT(m_statementNeedsReset);
+
+ m_statementNeedsReset = false;
+
</ins><span class="cx"> if (!m_statement && !establishStatement()) {
</span><span class="cx"> LOG_ERROR("Unable to establish new statement for cursor iteration");
</span><span class="cx"> return;
</span><span class="lines">@@ -289,25 +291,55 @@
</span><span class="cx"> return true;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+void SQLiteIDBCursor::prefetch()
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ if (m_fetchedRecords.last().errored || m_fetchedRecords.last().completed)
+ return;
+
+ m_currentKeyForUniqueness = m_fetchedRecords.last().record.key;
+ fetch();
+}
+
</ins><span class="cx"> bool SQLiteIDBCursor::advance(uint64_t count)
</span><span class="cx"> {
</span><del>- bool isUnique = m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate || m_cursorDirection == IndexedDB::CursorDirection::PrevNoDuplicate;
</del><ins>+ ASSERT(count);
</ins><span class="cx">
</span><del>- if (m_currentRecord.completed) {
</del><ins>+ if (!m_fetchedRecords.isEmpty() && m_fetchedRecords.first().isTerminalRecord()) {
</ins><span class="cx"> LOG_ERROR("Attempt to advance a completed cursor");
</span><span class="cx"> return false;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ if (!m_fetchedRecords.isEmpty())
+ m_currentKeyForUniqueness = m_fetchedRecords.last().record.key;
+
+ // Drop already-fetched records up to `count` to see if we've already fetched the record we're looking for.
+ for (size_t i = 0; i < count && !m_fetchedRecords.isEmpty(); ++i) {
+ if (m_fetchedRecords.first().isTerminalRecord())
+ break;
+
+ m_fetchedRecords.removeFirst();
+ }
+
+ // If we still have any records left, the first record is our new current record.
+ if (!m_fetchedRecords.isEmpty())
+ return !m_fetchedRecords.first().isTerminalRecord();
+
+ ASSERT(m_fetchedRecords.isEmpty());
+
</ins><span class="cx"> for (uint64_t i = 0; i < count; ++i) {
</span><del>- if (!isUnique) {
- if (!advanceOnce())
- return false;
- } else {
- if (!advanceUnique())
- return false;
</del><ins>+ if (!m_fetchedRecords.isEmpty()) {
+ ASSERT(m_fetchedRecords.size() == 1);
+ m_currentKeyForUniqueness = m_fetchedRecords.first().record.key;
+ m_fetchedRecords.removeFirst();
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- if (m_currentRecord.completed)
</del><ins>+ if (!fetch())
+ return false;
+
+ ASSERT(!m_fetchedRecords.isEmpty());
+ ASSERT(!m_fetchedRecords.first().errored);
+ if (m_fetchedRecords.first().completed)
</ins><span class="cx"> break;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -314,16 +346,22 @@
</span><span class="cx"> return true;
</span><span class="cx"> }
</span><span class="cx">
</span><del>-bool SQLiteIDBCursor::advanceUnique()
</del><ins>+bool SQLiteIDBCursor::fetch()
</ins><span class="cx"> {
</span><del>- IDBKeyData currentKey = m_currentRecord.record.key;
</del><ins>+ ASSERT(m_fetchedRecords.isEmpty() || !m_fetchedRecords.last().isTerminalRecord());
</ins><span class="cx">
</span><del>- while (!m_currentRecord.completed) {
- if (!advanceOnce())
</del><ins>+ m_fetchedRecords.append({ });
+
+ bool isUnique = m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate || m_cursorDirection == IndexedDB::CursorDirection::PrevNoDuplicate;
+ if (!isUnique)
+ return fetchNextRecord(m_fetchedRecords.last());
+
+ while (!m_fetchedRecords.last().completed) {
+ if (!fetchNextRecord(m_fetchedRecords.last()))
</ins><span class="cx"> return false;
</span><span class="cx">
</span><span class="cx"> // If the new current key is different from the old current key, we're done.
</span><del>- if (currentKey.compare(m_currentRecord.record.key))
</del><ins>+ if (m_currentKeyForUniqueness.compare(m_fetchedRecords.last().record.key))
</ins><span class="cx"> return true;
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -330,60 +368,61 @@
</span><span class="cx"> return false;
</span><span class="cx"> }
</span><span class="cx">
</span><del>-bool SQLiteIDBCursor::advanceOnce()
</del><ins>+bool SQLiteIDBCursor::fetchNextRecord(SQLiteCursorRecord& record)
</ins><span class="cx"> {
</span><span class="cx"> if (m_statementNeedsReset)
</span><span class="cx"> resetAndRebindStatement();
</span><span class="cx">
</span><del>- AdvanceResult result;
</del><ins>+ FetchResult result;
</ins><span class="cx"> do {
</span><del>- result = internalAdvanceOnce();
- } while (result == AdvanceResult::ShouldAdvanceAgain);
</del><ins>+ result = internalFetchNextRecord(record);
+ } while (result == FetchResult::ShouldFetchAgain);
</ins><span class="cx">
</span><del>- return result == AdvanceResult::Success;
</del><ins>+ return result == FetchResult::Success;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-void SQLiteIDBCursor::markAsErrored()
</del><ins>+void SQLiteIDBCursor::markAsErrored(SQLiteCursorRecord& record)
</ins><span class="cx"> {
</span><del>- m_currentRecord.completed = true;
- m_currentRecord.errored = true;
- m_currentRecord.rowID = 0;
</del><ins>+ record.record = { };
+ record.completed = true;
+ record.errored = true;
+ record.rowID = 0;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>-SQLiteIDBCursor::AdvanceResult SQLiteIDBCursor::internalAdvanceOnce()
</del><ins>+SQLiteIDBCursor::FetchResult SQLiteIDBCursor::internalFetchNextRecord(SQLiteCursorRecord& record)
</ins><span class="cx"> {
</span><span class="cx"> ASSERT(m_transaction->sqliteTransaction());
</span><span class="cx"> ASSERT(m_statement);
</span><del>- ASSERT(!m_currentRecord.completed);
</del><ins>+ ASSERT(!m_fetchedRecords.isEmpty());
+ ASSERT(!m_fetchedRecords.last().isTerminalRecord());
</ins><span class="cx">
</span><del>- m_currentRecord.record.value = nullptr;
</del><ins>+ record.record.value = nullptr;
</ins><span class="cx">
</span><span class="cx"> int result = m_statement->step();
</span><span class="cx"> if (result == SQLITE_DONE) {
</span><span class="cx"> // When a cursor reaches its end, that is indicated by having undefined keys/values
</span><del>- m_currentRecord = { };
- m_currentRecord.completed = true;
- m_currentRecord.rowID = 0;
</del><ins>+ record = { };
+ record.completed = true;
</ins><span class="cx">
</span><del>- return AdvanceResult::Success;
</del><ins>+ return FetchResult::Success;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> if (result != SQLITE_ROW) {
</span><span class="cx"> LOG_ERROR("Error advancing cursor - (%i) %s", result, m_transaction->sqliteTransaction()->database().lastErrorMsg());
</span><del>- markAsErrored();
- return AdvanceResult::Failure;
</del><ins>+ markAsErrored(record);
+ return FetchResult::Failure;
</ins><span class="cx"> }
</span><span class="cx">
</span><del>- m_currentRecord.rowID = m_statement->getColumnInt64(0);
- ASSERT(m_currentRecord.rowID);
</del><ins>+ record.rowID = m_statement->getColumnInt64(0);
+ ASSERT(record.rowID);
</ins><span class="cx">
</span><span class="cx"> Vector<uint8_t> keyData;
</span><span class="cx"> m_statement->getColumnBlobAsVector(1, keyData);
</span><span class="cx">
</span><del>- if (!deserializeIDBKeyData(keyData.data(), keyData.size(), m_currentRecord.record.key)) {
</del><ins>+ if (!deserializeIDBKeyData(keyData.data(), keyData.size(), record.record.key)) {
</ins><span class="cx"> LOG_ERROR("Unable to deserialize key data from database while advancing cursor");
</span><del>- markAsErrored();
- return AdvanceResult::Failure;
</del><ins>+ markAsErrored(record);
+ return FetchResult::Failure;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> m_statement->getColumnBlobAsVector(2, keyData);
</span><span class="lines">@@ -390,23 +429,23 @@
</span><span class="cx">
</span><span class="cx"> // The primaryKey of an ObjectStore cursor is the same as its key.
</span><span class="cx"> if (m_indexID == IDBIndexInfo::InvalidId) {
</span><del>- m_currentRecord.record.primaryKey = m_currentRecord.record.key;
</del><ins>+ record.record.primaryKey = record.record.key;
</ins><span class="cx">
</span><span class="cx"> Vector<String> blobURLs, blobFilePaths;
</span><del>- auto error = m_transaction->backingStore().getBlobRecordsForObjectStoreRecord(m_currentRecord.rowID, blobURLs, blobFilePaths);
</del><ins>+ auto error = m_transaction->backingStore().getBlobRecordsForObjectStoreRecord(record.rowID, blobURLs, blobFilePaths);
</ins><span class="cx"> if (!error.isNull()) {
</span><span class="cx"> LOG_ERROR("Unable to fetch blob records from database while advancing cursor");
</span><del>- markAsErrored();
- return AdvanceResult::Failure;
</del><ins>+ markAsErrored(record);
+ return FetchResult::Failure;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> if (m_cursorType == IndexedDB::CursorType::KeyAndValue)
</span><del>- m_currentRecord.record.value = std::make_unique<IDBValue>(ThreadSafeDataBuffer::adoptVector(keyData), blobURLs, blobFilePaths);
</del><ins>+ record.record.value = std::make_unique<IDBValue>(ThreadSafeDataBuffer::adoptVector(keyData), blobURLs, blobFilePaths);
</ins><span class="cx"> } else {
</span><del>- if (!deserializeIDBKeyData(keyData.data(), keyData.size(), m_currentRecord.record.primaryKey)) {
</del><ins>+ if (!deserializeIDBKeyData(keyData.data(), keyData.size(), record.record.primaryKey)) {
</ins><span class="cx"> LOG_ERROR("Unable to deserialize value data from database while advancing index cursor");
</span><del>- markAsErrored();
- return AdvanceResult::Failure;
</del><ins>+ markAsErrored(record);
+ return FetchResult::Failure;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> SQLiteStatement objectStoreStatement(m_statement->database(), "SELECT value FROM Records WHERE key = CAST(? AS TEXT) and objectStoreID = ?;");
</span><span class="lines">@@ -415,8 +454,8 @@
</span><span class="cx"> || objectStoreStatement.bindBlob(1, keyData.data(), keyData.size()) != SQLITE_OK
</span><span class="cx"> || objectStoreStatement.bindInt64(2, m_objectStoreID) != SQLITE_OK) {
</span><span class="cx"> LOG_ERROR("Could not create index cursor statement into object store records (%i) '%s'", m_statement->database().lastError(), m_statement->database().lastErrorMsg());
</span><del>- markAsErrored();
- return AdvanceResult::Failure;
</del><ins>+ markAsErrored(record);
+ return FetchResult::Failure;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> int result = objectStoreStatement.step();
</span><span class="lines">@@ -423,20 +462,20 @@
</span><span class="cx">
</span><span class="cx"> if (result == SQLITE_ROW) {
</span><span class="cx"> objectStoreStatement.getColumnBlobAsVector(0, keyData);
</span><del>- m_currentRecord.record.value = std::make_unique<IDBValue>(ThreadSafeDataBuffer::adoptVector(keyData));
</del><ins>+ record.record.value = std::make_unique<IDBValue>(ThreadSafeDataBuffer::adoptVector(keyData));
</ins><span class="cx"> } else if (result == SQLITE_DONE) {
</span><span class="cx"> // This indicates that the record we're trying to retrieve has been removed from the object store.
</span><span class="cx"> // Skip over it.
</span><del>- return AdvanceResult::ShouldAdvanceAgain;
</del><ins>+ return FetchResult::ShouldFetchAgain;
</ins><span class="cx"> } else {
</span><span class="cx"> LOG_ERROR("Could not step index cursor statement into object store records (%i) '%s'", m_statement->database().lastError(), m_statement->database().lastErrorMsg());
</span><del>- markAsErrored();
- return AdvanceResult::Failure;
</del><ins>+ markAsErrored(record);
+ return FetchResult::Failure;
</ins><span class="cx">
</span><span class="cx"> }
</span><span class="cx"> }
</span><span class="cx">
</span><del>- return AdvanceResult::Success;
</del><ins>+ return FetchResult::Success;
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> bool SQLiteIDBCursor::iterate(const IDBKeyData& targetKey, const IDBKeyData& targetPrimaryKey)
</span><span class="lines">@@ -445,20 +484,21 @@
</span><span class="cx"> ASSERT(m_statement);
</span><span class="cx">
</span><span class="cx"> bool result = advance(1);
</span><ins>+ ASSERT(!m_fetchedRecords.isEmpty());
</ins><span class="cx">
</span><span class="cx"> // Iterating with no key is equivalent to advancing 1 step.
</span><span class="cx"> if (targetKey.isNull() || !result)
</span><span class="cx"> return result;
</span><span class="cx">
</span><del>- while (!m_currentRecord.completed) {
</del><ins>+ while (!m_fetchedRecords.first().isTerminalRecord()) {
</ins><span class="cx"> if (!result)
</span><span class="cx"> return false;
</span><span class="cx">
</span><span class="cx"> // Search for the next key >= the target if the cursor is a Next cursor, or the next key <= if the cursor is a Previous cursor.
</span><span class="cx"> if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate) {
</span><del>- if (m_currentRecord.record.key.compare(targetKey) >= 0)
</del><ins>+ if (m_fetchedRecords.first().record.key.compare(targetKey) >= 0)
</ins><span class="cx"> break;
</span><del>- } else if (m_currentRecord.record.key.compare(targetKey) <= 0)
</del><ins>+ } else if (m_fetchedRecords.first().record.key.compare(targetKey) <= 0)
</ins><span class="cx"> break;
</span><span class="cx">
</span><span class="cx"> result = advance(1);
</span><span class="lines">@@ -465,15 +505,15 @@
</span><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> if (targetPrimaryKey.isValid()) {
</span><del>- while (!m_currentRecord.completed && !m_currentRecord.record.key.compare(targetKey)) {
</del><ins>+ while (!m_fetchedRecords.first().isTerminalRecord() && !m_fetchedRecords.first().record.key.compare(targetKey)) {
</ins><span class="cx"> if (!result)
</span><span class="cx"> return false;
</span><span class="cx">
</span><span class="cx"> // Search for the next primary key >= the primary target if the cursor is a Next cursor, or the next key <= if the cursor is a Previous cursor.
</span><span class="cx"> if (m_cursorDirection == IndexedDB::CursorDirection::Next || m_cursorDirection == IndexedDB::CursorDirection::NextNoDuplicate) {
</span><del>- if (m_currentRecord.record.primaryKey.compare(targetPrimaryKey) >= 0)
</del><ins>+ if (m_fetchedRecords.first().record.primaryKey.compare(targetPrimaryKey) >= 0)
</ins><span class="cx"> break;
</span><del>- } else if (m_currentRecord.record.primaryKey.compare(targetPrimaryKey) <= 0)
</del><ins>+ } else if (m_fetchedRecords.first().record.primaryKey.compare(targetPrimaryKey) <= 0)
</ins><span class="cx"> break;
</span><span class="cx">
</span><span class="cx"> result = advance(1);
</span><span class="lines">@@ -483,6 +523,43 @@
</span><span class="cx"> return result;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+const IDBKeyData& SQLiteIDBCursor::currentKey() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().record.key;
+}
+
+const IDBKeyData& SQLiteIDBCursor::currentPrimaryKey() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().record.primaryKey;
+}
+
+IDBValue* SQLiteIDBCursor::currentValue() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().record.value.get();
+}
+
+bool SQLiteIDBCursor::didComplete() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().completed;
+}
+
+bool SQLiteIDBCursor::didError() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().errored;
+}
+
+int64_t SQLiteIDBCursor::currentRecordRowID() const
+{
+ ASSERT(!m_fetchedRecords.isEmpty());
+ return m_fetchedRecords.first().rowID;
+}
+
+
</ins><span class="cx"> } // namespace IDBServer
</span><span class="cx"> } // namespace WebCore
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoreModulesindexeddbserverSQLiteIDBCursorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h (209959 => 209960)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h        2016-12-17 06:42:39 UTC (rev 209959)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBCursor.h        2016-12-17 07:29:17 UTC (rev 209960)
</span><span class="lines">@@ -34,6 +34,7 @@
</span><span class="cx"> #include "IDBResourceIdentifier.h"
</span><span class="cx"> #include "IDBValue.h"
</span><span class="cx"> #include "SQLiteStatement.h"
</span><ins>+#include <wtf/Deque.h>
</ins><span class="cx"> #include <wtf/Noncopyable.h>
</span><span class="cx">
</span><span class="cx"> namespace WebCore {
</span><span class="lines">@@ -60,17 +61,18 @@
</span><span class="cx"> SQLiteIDBTransaction* transaction() const { return m_transaction; }
</span><span class="cx">
</span><span class="cx"> int64_t objectStoreID() const { return m_objectStoreID; }
</span><del>- int64_t currentRecordRowID() const { return m_currentRecord.rowID; }
</del><ins>+ int64_t currentRecordRowID() const;
</ins><span class="cx">
</span><del>- const IDBKeyData& currentKey() const { return m_currentRecord.record.key; }
- const IDBKeyData& currentPrimaryKey() const { return m_currentRecord.record.primaryKey; }
- IDBValue* currentValue() const { return m_currentRecord.record.value.get(); }
</del><ins>+ const IDBKeyData& currentKey() const;
+ const IDBKeyData& currentPrimaryKey() const;
+ IDBValue* currentValue() const;
</ins><span class="cx">
</span><span class="cx"> bool advance(uint64_t count);
</span><span class="cx"> bool iterate(const IDBKeyData& targetKey, const IDBKeyData& targetPrimaryKey);
</span><ins>+ void prefetch();
</ins><span class="cx">
</span><del>- bool didComplete() const { return m_currentRecord.completed; }
- bool didError() const { return m_currentRecord.errored; }
</del><ins>+ bool didComplete() const;
+ bool didError() const;
</ins><span class="cx">
</span><span class="cx"> void objectStoreRecordsChanged();
</span><span class="cx">
</span><span class="lines">@@ -83,18 +85,26 @@
</span><span class="cx">
</span><span class="cx"> void resetAndRebindStatement();
</span><span class="cx">
</span><del>- void markAsErrored();
-
- enum class AdvanceResult {
</del><ins>+ enum class FetchResult {
</ins><span class="cx"> Success,
</span><span class="cx"> Failure,
</span><del>- ShouldAdvanceAgain
</del><ins>+ ShouldFetchAgain
</ins><span class="cx"> };
</span><span class="cx">
</span><del>- AdvanceResult internalAdvanceOnce();
- bool advanceOnce();
- bool advanceUnique();
</del><ins>+ bool fetch();
</ins><span class="cx">
</span><ins>+ struct SQLiteCursorRecord {
+ IDBCursorRecord record;
+ bool completed { false };
+ bool errored { false };
+ int64_t rowID { 0 };
+ bool isTerminalRecord() const { return completed || errored; }
+ };
+ bool fetchNextRecord(SQLiteCursorRecord&);
+ FetchResult internalFetchNextRecord(SQLiteCursorRecord&);
+
+ void markAsErrored(SQLiteCursorRecord&);
+
</ins><span class="cx"> SQLiteIDBTransaction* m_transaction;
</span><span class="cx"> IDBResourceIdentifier m_cursorIdentifier;
</span><span class="cx"> int64_t m_objectStoreID;
</span><span class="lines">@@ -106,17 +116,11 @@
</span><span class="cx"> IDBKeyData m_currentLowerKey;
</span><span class="cx"> IDBKeyData m_currentUpperKey;
</span><span class="cx">
</span><del>- struct SQLiteCursorRecord {
- IDBCursorRecord record;
- bool completed { false };
- bool errored { false };
- int64_t rowID { 0 };
- };
</del><ins>+ Deque<SQLiteCursorRecord> m_fetchedRecords;
+ IDBKeyData m_currentKeyForUniqueness;
</ins><span class="cx">
</span><del>- SQLiteCursorRecord m_currentRecord;
-
</del><span class="cx"> std::unique_ptr<SQLiteStatement> m_statement;
</span><del>- bool m_statementNeedsReset { false };
</del><ins>+ bool m_statementNeedsReset { true };
</ins><span class="cx"> int64_t m_boundID { 0 };
</span><span class="cx">
</span><span class="cx"> bool m_backingStoreCursor { false };
</span></span></pre>
</div>
</div>
</body>
</html>