IF (ENABLE_LEVELDB)
LIST(APPEND WebCore_SOURCES
platform/leveldb/LevelDBDatabase.cpp
- platform/leveldb/LevelDBIterator.cpp
+ platform/leveldb/LevelDBTransaction.cpp
+ platform/leveldb/LevelDBWriteBatch.cpp
)
ENDIF ()
+2011-05-25 Hans Wennborg <hans@chromium.org>
+
+ Reviewed by Steve Block.
+
+ IndexedDB: Support LevelDB transactions.
+ https://bugs.webkit.org/show_bug.cgi?id=61270
+
+ Introduce LevelDBTransaction, which implements in-memory transaction
+ support on top of LevelDB, and hook this up for IndexedDB to use.
+
+ This is all covered by existing tests.
+
+ * CMakeLists.txt:
+ * GNUmakefile.list.am:
+ * WebCore.gypi:
+ * WebCore.pro:
+ * WebCore.xcodeproj/project.pbxproj:
+ * platform/leveldb/LevelDBDatabase.cpp:
+ (WebCore::LevelDBDatabase::open):
+ (WebCore::LevelDBDatabase::put):
+ (WebCore::LevelDBDatabase::remove):
+ (WebCore::LevelDBDatabase::write):
+ (WebCore::IteratorImpl::IteratorImpl):
+ (WebCore::IteratorImpl::isValid):
+ (WebCore::IteratorImpl::seekToLast):
+ (WebCore::IteratorImpl::seek):
+ (WebCore::IteratorImpl::next):
+ (WebCore::IteratorImpl::prev):
+ (WebCore::IteratorImpl::key):
+ (WebCore::IteratorImpl::value):
+ (WebCore::LevelDBDatabase::createIterator):
+ (WebCore::LevelDBDatabase::comparator):
+ * platform/leveldb/LevelDBDatabase.h:
+ * platform/leveldb/LevelDBIterator.h:
+ (WebCore::LevelDBIterator::~LevelDBIterator):
+ * platform/leveldb/LevelDBTransaction.cpp: Added.
+ (WebCore::LevelDBTransaction::create):
+ (WebCore::LevelDBTransaction::LevelDBTransaction):
+ (WebCore::LevelDBTransaction::clearTree):
+ (WebCore::LevelDBTransaction::~LevelDBTransaction):
+ (WebCore::makeVector):
+ (WebCore::LevelDBTransaction::set):
+ (WebCore::LevelDBTransaction::put):
+ (WebCore::LevelDBTransaction::remove):
+ (WebCore::LevelDBTransaction::get):
+ (WebCore::LevelDBTransaction::commit):
+ (WebCore::LevelDBTransaction::rollback):
+ (WebCore::LevelDBTransaction::createIterator):
+ (WebCore::LevelDBTransaction::TreeIterator::create):
+ (WebCore::LevelDBTransaction::TreeIterator::isValid):
+ (WebCore::LevelDBTransaction::TreeIterator::seekToLast):
+ (WebCore::LevelDBTransaction::TreeIterator::seek):
+ (WebCore::LevelDBTransaction::TreeIterator::next):
+ (WebCore::LevelDBTransaction::TreeIterator::prev):
+ (WebCore::LevelDBTransaction::TreeIterator::key):
+ (WebCore::LevelDBTransaction::TreeIterator::value):
+ (WebCore::LevelDBTransaction::TreeIterator::isDeleted):
+ (WebCore::LevelDBTransaction::TreeIterator::reset):
+ (WebCore::LevelDBTransaction::TreeIterator::~TreeIterator):
+ (WebCore::LevelDBTransaction::TreeIterator::TreeIterator):
+ (WebCore::LevelDBTransaction::TransactionIterator::create):
+ (WebCore::LevelDBTransaction::TransactionIterator::TransactionIterator):
+ (WebCore::LevelDBTransaction::TransactionIterator::isValid):
+ (WebCore::LevelDBTransaction::TransactionIterator::seekToLast):
+ (WebCore::LevelDBTransaction::TransactionIterator::seek):
+ (WebCore::LevelDBTransaction::TransactionIterator::next):
+ (WebCore::LevelDBTransaction::TransactionIterator::prev):
+ (WebCore::LevelDBTransaction::TransactionIterator::key):
+ (WebCore::LevelDBTransaction::TransactionIterator::value):
+ (WebCore::LevelDBTransaction::TransactionIterator::handleConflictsAndDeletes):
+ (WebCore::LevelDBTransaction::TransactionIterator::setCurrentIteratorToSmallestKey):
+ (WebCore::LevelDBTransaction::TransactionIterator::setCurrentIteratorToLargestKey):
+ (WebCore::LevelDBTransaction::registerIterator):
+ (WebCore::LevelDBTransaction::unregisterIterator):
+ (WebCore::LevelDBTransaction::resetIterators):
+ * platform/leveldb/LevelDBTransaction.h: Added.
+ (WebCore::LevelDBTransaction::AVLTreeAbstractor::get_less):
+ (WebCore::LevelDBTransaction::AVLTreeAbstractor::set_less):
+ (WebCore::LevelDBTransaction::AVLTreeAbstractor::get_greater):
+ (WebCore::LevelDBTransaction::AVLTreeAbstractor::set_greater):
+ (WebCore::LevelDBTransaction::AVLTreeAbstractor::get_balance_factor):
+ (WebCore::LevelDBTransaction::AVLTreeAbstractor::set_balance_factor):
+ (WebCore::LevelDBTransaction::AVLTreeAbstractor::compare_key_key):
+ (WebCore::LevelDBTransaction::AVLTreeAbstractor::compare_key_node):
+ (WebCore::LevelDBTransaction::AVLTreeAbstractor::compare_node_node):
+ (WebCore::LevelDBTransaction::AVLTreeAbstractor::null):
+ (WebCore::LevelDBTransaction::TransactionIterator::~TransactionIterator):
+ * platform/leveldb/LevelDBWriteBatch.cpp:
+ (WebCore::LevelDBWriteBatch::create):
+ (WebCore::LevelDBWriteBatch::LevelDBWriteBatch):
+ (WebCore::LevelDBWriteBatch::~LevelDBWriteBatch):
+ (WebCore::makeSlice):
+ (WebCore::LevelDBWriteBatch::put):
+ (WebCore::LevelDBWriteBatch::remove):
+ (WebCore::LevelDBWriteBatch::clear):
+ * storage/IDBLevelDBBackingStore.cpp:
+ (WebCore::getInt):
+ (WebCore::putInt):
+ (WebCore::getString):
+ (WebCore::putString):
+ (WebCore::getNewObjectStoreId):
+ (WebCore::IDBLevelDBBackingStore::createObjectStore):
+ (WebCore::deleteRange):
+ (WebCore::IDBLevelDBBackingStore::deleteObjectStore):
+ (WebCore::IDBLevelDBBackingStore::getObjectStoreRecord):
+ (WebCore::getNewVersionNumber):
+ (WebCore::IDBLevelDBBackingStore::putObjectStoreRecord):
+ (WebCore::IDBLevelDBBackingStore::clearObjectStore):
+ (WebCore::IDBLevelDBBackingStore::deleteObjectStoreRecord):
+ (WebCore::IDBLevelDBBackingStore::nextAutoIncrementNumber):
+ (WebCore::IDBLevelDBBackingStore::keyExistsInObjectStore):
+ (WebCore::IDBLevelDBBackingStore::forEachObjectStoreRecord):
+ (WebCore::getNewIndexId):
+ (WebCore::IDBLevelDBBackingStore::createIndex):
+ (WebCore::IDBLevelDBBackingStore::putIndexDataForRecord):
+ (WebCore::findGreatestKeyLessThan):
+ (WebCore::versionExists):
+ (WebCore::IDBLevelDBBackingStore::getPrimaryKeyViaIndex):
+ (WebCore::IDBLevelDBBackingStore::keyExistsInIndex):
+ (WebCore::findLastIndexKeyEqualTo):
+ (WebCore::IDBLevelDBBackingStore::openObjectStoreCursor):
+ (WebCore::IDBLevelDBBackingStore::openIndexKeyCursor):
+ (WebCore::IDBLevelDBBackingStore::openIndexCursor):
+ (WebCore::IDBLevelDBBackingStore::createTransaction):
+ (WebCore::IDBLevelDBBackingStore::Transaction::create):
+ (WebCore::IDBLevelDBBackingStore::Transaction::Transaction):
+ (WebCore::IDBLevelDBBackingStore::Transaction::begin):
+ (WebCore::IDBLevelDBBackingStore::Transaction::commit):
+ (WebCore::IDBLevelDBBackingStore::Transaction::rollback):
+ * storage/IDBLevelDBBackingStore.h:
+ * storage/IDBTransactionBackendImpl.cpp:
+ (WebCore::IDBTransactionBackendImpl::abort):
+
2011-05-26 Shane Stephens <shanestephens@google.com>
Reviewed by James Robinson.
Source/WebCore/platform/leveldb/LevelDBComparator.h \
Source/WebCore/platform/leveldb/LevelDBDatabase.cpp \
Source/WebCore/platform/leveldb/LevelDBDatabase.h \
- Source/WebCore/platform/leveldb/LevelDBIterator.cpp \
Source/WebCore/platform/leveldb/LevelDBIterator.h \
Source/WebCore/platform/leveldb/LevelDBSlice.h \
+ Source/WebCore/platform/leveldb/LevelDBTransaction.h \
+ Source/WebCore/platform/leveldb/LevelDBTransaction.cpp \
+ Source/WebCore/platform/leveldb/LevelDBWriteBatch.h \
+ Source/WebCore/platform/leveldb/LevelDBWriteBatch.cpp \
Source/WebCore/platform/LinkHash.cpp \
Source/WebCore/platform/LinkHash.h \
Source/WebCore/platform/LocalizedStrings.h \
'platform/leveldb/LevelDBComparator.h',
'platform/leveldb/LevelDBDatabase.cpp',
'platform/leveldb/LevelDBDatabase.h',
- 'platform/leveldb/LevelDBIterator.cpp',
'platform/leveldb/LevelDBIterator.h',
'platform/leveldb/LevelDBSlice.h',
+ 'platform/leveldb/LevelDBTransaction.cpp',
+ 'platform/leveldb/LevelDBTransaction.h',
+ 'platform/leveldb/LevelDBWriteBatch.cpp',
+ 'platform/leveldb/LevelDBWriteBatch.h',
'platform/mac/BlockExceptions.h',
'platform/mac/ClipboardMac.h',
'platform/mac/EmptyProtocolDefinitions.h',
platform/Language.cpp \
platform/Length.cpp \
platform/text/LineEnding.cpp \
- platform/leveldb/LevelDBComparator.h \
platform/leveldb/LevelDBDatabase.cpp \
- platform/leveldb/LevelDBDatabase.h \
- platform/leveldb/LevelDBIterator.cpp \
- platform/leveldb/LevelDBIterator.h \
- platform/leveldb/LevelDBSlice.h \
+ platform/leveldb/LevelDBTransaction.cpp \
+ platform/leveldb/LevelDBWriteBatch.cpp \
platform/LinkHash.cpp \
platform/Logging.cpp \
platform/MemoryPressureHandler.cpp \
platform/KillRing.h \
platform/KURL.h \
platform/Length.h \
+ platform/leveldb/LevelDBComparator.h \
+ platform/leveldb/LevelDBDatabase.h \
+ platform/leveldb/LevelDBIterator.h \
+ platform/leveldb/LevelDBSlice.h \
+ platform/leveldb/LevelDBTransaction.h \
+ platform/leveldb/LevelDBWriteBatch.h \
platform/text/BidiRunList.h \
platform/text/LineEnding.h \
platform/text/TextCheckerClient.h \
8AF4E55611DC5A36000ED3DE /* PerformanceNavigation.h in Headers */ = {isa = PBXBuildFile; fileRef = 8AF4E55311DC5A36000ED3DE /* PerformanceNavigation.h */; };
8AF4E55B11DC5A63000ED3DE /* PerformanceTiming.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8AF4E55811DC5A63000ED3DE /* PerformanceTiming.cpp */; };
8AF4E55C11DC5A63000ED3DE /* PerformanceTiming.h in Headers */ = {isa = PBXBuildFile; fileRef = 8AF4E55911DC5A63000ED3DE /* PerformanceTiming.h */; };
+ 8C0E334C138A92C7008DA94F /* LevelDBTransaction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C0E3348138A92C7008DA94F /* LevelDBTransaction.cpp */; };
+ 8C0E334D138A92C7008DA94F /* LevelDBTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C0E3349138A92C7008DA94F /* LevelDBTransaction.h */; };
+ 8C0E334E138A92C7008DA94F /* LevelDBWriteBatch.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C0E334A138A92C7008DA94F /* LevelDBWriteBatch.cpp */; };
+ 8C0E334F138A92C7008DA94F /* LevelDBWriteBatch.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C0E334B138A92C7008DA94F /* LevelDBWriteBatch.h */; };
8C6EA61911EF7E0400FD8EE3 /* RuntimeEnabledFeatures.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C6EA61711EF7E0400FD8EE3 /* RuntimeEnabledFeatures.cpp */; };
8C6EA61A11EF7E0400FD8EE3 /* RuntimeEnabledFeatures.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C6EA61811EF7E0400FD8EE3 /* RuntimeEnabledFeatures.h */; };
8CADF2A9135C7B36009EF43F /* LevelDBComparator.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CADF2A3135C7B36009EF43F /* LevelDBComparator.h */; };
8CADF2AA135C7B36009EF43F /* LevelDBDatabase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CADF2A4135C7B36009EF43F /* LevelDBDatabase.cpp */; };
8CADF2AB135C7B36009EF43F /* LevelDBDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CADF2A5135C7B36009EF43F /* LevelDBDatabase.h */; };
- 8CADF2AC135C7B36009EF43F /* LevelDBIterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CADF2A6135C7B36009EF43F /* LevelDBIterator.cpp */; };
8CADF2AD135C7B36009EF43F /* LevelDBIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CADF2A7135C7B36009EF43F /* LevelDBIterator.h */; };
8CADF2AE135C7B36009EF43F /* LevelDBSlice.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CADF2A8135C7B36009EF43F /* LevelDBSlice.h */; };
8F67561B1288B17B0047ACA3 /* EventQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 8F6756191288B17B0047ACA3 /* EventQueue.h */; };
8AF4E55811DC5A63000ED3DE /* PerformanceTiming.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PerformanceTiming.cpp; sourceTree = "<group>"; };
8AF4E55911DC5A63000ED3DE /* PerformanceTiming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PerformanceTiming.h; sourceTree = "<group>"; };
8AF4E55A11DC5A63000ED3DE /* PerformanceTiming.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PerformanceTiming.idl; sourceTree = "<group>"; };
+ 8C0E3348138A92C7008DA94F /* LevelDBTransaction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LevelDBTransaction.cpp; sourceTree = "<group>"; };
+ 8C0E3349138A92C7008DA94F /* LevelDBTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LevelDBTransaction.h; sourceTree = "<group>"; };
+ 8C0E334A138A92C7008DA94F /* LevelDBWriteBatch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LevelDBWriteBatch.cpp; sourceTree = "<group>"; };
+ 8C0E334B138A92C7008DA94F /* LevelDBWriteBatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LevelDBWriteBatch.h; sourceTree = "<group>"; };
8C6EA61711EF7E0400FD8EE3 /* RuntimeEnabledFeatures.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RuntimeEnabledFeatures.cpp; path = generic/RuntimeEnabledFeatures.cpp; sourceTree = "<group>"; };
8C6EA61811EF7E0400FD8EE3 /* RuntimeEnabledFeatures.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RuntimeEnabledFeatures.h; path = generic/RuntimeEnabledFeatures.h; sourceTree = "<group>"; };
8CADF2A3135C7B36009EF43F /* LevelDBComparator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LevelDBComparator.h; sourceTree = "<group>"; };
8CADF2A4135C7B36009EF43F /* LevelDBDatabase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LevelDBDatabase.cpp; sourceTree = "<group>"; };
8CADF2A5135C7B36009EF43F /* LevelDBDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LevelDBDatabase.h; sourceTree = "<group>"; };
- 8CADF2A6135C7B36009EF43F /* LevelDBIterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LevelDBIterator.cpp; sourceTree = "<group>"; };
8CADF2A7135C7B36009EF43F /* LevelDBIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LevelDBIterator.h; sourceTree = "<group>"; };
8CADF2A8135C7B36009EF43F /* LevelDBSlice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LevelDBSlice.h; sourceTree = "<group>"; };
8F6756191288B17B0047ACA3 /* EventQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventQueue.h; sourceTree = "<group>"; };
8CADF2A3135C7B36009EF43F /* LevelDBComparator.h */,
8CADF2A4135C7B36009EF43F /* LevelDBDatabase.cpp */,
8CADF2A5135C7B36009EF43F /* LevelDBDatabase.h */,
- 8CADF2A6135C7B36009EF43F /* LevelDBIterator.cpp */,
8CADF2A7135C7B36009EF43F /* LevelDBIterator.h */,
8CADF2A8135C7B36009EF43F /* LevelDBSlice.h */,
+ 8C0E3348138A92C7008DA94F /* LevelDBTransaction.cpp */,
+ 8C0E3349138A92C7008DA94F /* LevelDBTransaction.h */,
+ 8C0E334A138A92C7008DA94F /* LevelDBWriteBatch.cpp */,
+ 8C0E334B138A92C7008DA94F /* LevelDBWriteBatch.h */,
);
path = leveldb;
sourceTree = "<group>";
B12D236613560330002A28D4 /* JSTrackList.h in Headers */,
B12D236A1356033F002A28D4 /* JSExclusiveTrackList.h in Headers */,
B12D236E1356034D002A28D4 /* JSMultipleTrackList.h in Headers */,
+ 8C0E334D138A92C7008DA94F /* LevelDBTransaction.h in Headers */,
+ 8C0E334F138A92C7008DA94F /* LevelDBWriteBatch.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
51B2417B0D931F3F00E83F5C /* LegacyWebArchiveMac.mm in Sources */,
BCE65BEA0EACDF16007E4533 /* Length.cpp in Sources */,
8CADF2AA135C7B36009EF43F /* LevelDBDatabase.cpp in Sources */,
- 8CADF2AC135C7B36009EF43F /* LevelDBIterator.cpp in Sources */,
84730D901248F0B300D3A9C9 /* LightSource.cpp in Sources */,
89B5EAA111E8003D00F2367E /* LineEnding.cpp in Sources */,
A7AD2F870EC89D07008AB002 /* LinkHash.cpp in Sources */,
B12D236513560330002A28D4 /* JSTrackList.cpp in Sources */,
B12D23691356033F002A28D4 /* JSExclusiveTrackList.cpp in Sources */,
B12D236D1356034D002A28D4 /* JSMultipleTrackList.cpp in Sources */,
+ 8C0E334C138A92C7008DA94F /* LevelDBTransaction.cpp in Sources */,
+ 8C0E334E138A92C7008DA94F /* LevelDBWriteBatch.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
#include "LevelDBComparator.h"
#include "LevelDBIterator.h"
#include "LevelDBSlice.h"
+#include "LevelDBWriteBatch.h"
#include <leveldb/comparator.h>
#include <leveldb/db.h>
#include <leveldb/slice.h>
result->m_db = adoptPtr(db);
result->m_comparatorAdapter = comparatorAdapter.release();
+ result->m_comparator = comparator;
return result.release();
}
-
bool LevelDBDatabase::put(const LevelDBSlice& key, const Vector<char>& value)
{
leveldb::WriteOptions writeOptions;
- writeOptions.sync = false;
+ writeOptions.sync = true;
return m_db->Put(writeOptions, makeSlice(key), makeSlice(value)).ok();
}
bool LevelDBDatabase::remove(const LevelDBSlice& key)
{
leveldb::WriteOptions writeOptions;
- writeOptions.sync = false;
+ writeOptions.sync = true;
return m_db->Delete(writeOptions, makeSlice(key)).ok();
}
return true;
}
+bool LevelDBDatabase::write(LevelDBWriteBatch& writeBatch)
+{
+ leveldb::WriteOptions writeOptions;
+ writeOptions.sync = true;
+
+ return m_db->Write(writeOptions, writeBatch.m_writeBatch.get()).ok();
+}
+
+namespace {
+class IteratorImpl : public LevelDBIterator {
+public:
+ ~IteratorImpl() { };
+
+ virtual bool isValid() const;
+ virtual void seekToLast();
+ virtual void seek(const LevelDBSlice& target);
+ virtual void next();
+ virtual void prev();
+ virtual LevelDBSlice key() const;
+ virtual LevelDBSlice value() const;
+
+private:
+ friend class WebCore::LevelDBDatabase;
+ IteratorImpl(PassOwnPtr<leveldb::Iterator>);
+
+ OwnPtr<leveldb::Iterator> m_iterator;
+};
+}
+
+IteratorImpl::IteratorImpl(PassOwnPtr<leveldb::Iterator> it)
+ : m_iterator(it)
+{
+}
+
+bool IteratorImpl::isValid() const
+{
+ return m_iterator->Valid();
+}
+
+void IteratorImpl::seekToLast()
+{
+ m_iterator->SeekToLast();
+}
+
+void IteratorImpl::seek(const LevelDBSlice& target)
+{
+ m_iterator->Seek(makeSlice(target));
+}
+
+void IteratorImpl::next()
+{
+ ASSERT(isValid());
+ m_iterator->Next();
+}
+
+void IteratorImpl::prev()
+{
+ ASSERT(isValid());
+ m_iterator->Prev();
+}
+
+LevelDBSlice IteratorImpl::key() const
+{
+ ASSERT(isValid());
+ return makeLevelDBSlice(m_iterator->key());
+}
+
+LevelDBSlice IteratorImpl::value() const
+{
+ ASSERT(isValid());
+ return makeLevelDBSlice(m_iterator->value());
+}
+
PassOwnPtr<LevelDBIterator> LevelDBDatabase::createIterator()
{
OwnPtr<leveldb::Iterator> i = adoptPtr(m_db->NewIterator(leveldb::ReadOptions()));
if (!i) // FIXME: Double check if we actually need to check this.
return nullptr;
- return adoptPtr(new LevelDBIterator(i.release()));
+ return adoptPtr(new IteratorImpl(i.release()));
+}
+
+const LevelDBComparator* LevelDBDatabase::comparator() const
+{
+ return m_comparator;
}
}
class LevelDBComparator;
class LevelDBIterator;
class LevelDBSlice;
+class LevelDBWriteBatch;
class LevelDBDatabase {
public:
bool put(const LevelDBSlice& key, const Vector<char>& value);
bool remove(const LevelDBSlice& key);
bool get(const LevelDBSlice& key, Vector<char>& value);
+ bool write(LevelDBWriteBatch&);
PassOwnPtr<LevelDBIterator> createIterator();
+ const LevelDBComparator* comparator() const;
private:
LevelDBDatabase();
OwnPtr<leveldb::DB> m_db;
+ const LevelDBComparator* m_comparator;
OwnPtr<leveldb::Comparator> m_comparatorAdapter;
};
#if ENABLE(LEVELDB)
#include "LevelDBSlice.h"
-#include "PlatformString.h"
-#include <OwnPtr.h>
-#include <Vector.h>
-
-namespace leveldb {
-class Iterator;
-}
namespace WebCore {
class LevelDBIterator {
public:
- ~LevelDBIterator();
-
- bool isValid() const;
- void seekToLast();
- void seek(const Vector<char>& target);
- void next();
- void prev();
- LevelDBSlice key() const;
- LevelDBSlice value() const;
-
-private:
- LevelDBIterator(PassOwnPtr<leveldb::Iterator>);
- friend class LevelDBDatabase;
-
- OwnPtr<leveldb::Iterator> m_iterator;
+ virtual ~LevelDBIterator() { };
+ virtual bool isValid() const = 0;
+ virtual void seekToLast() = 0;
+ virtual void seek(const LevelDBSlice& target) = 0;
+ virtual void next() = 0;
+ virtual void prev() = 0;
+ virtual LevelDBSlice key() const = 0;
+ virtual LevelDBSlice value() const = 0;
};
-
} // namespace WebCore
#endif // ENABLE(LEVELDB)
--- /dev/null
+/*
+ * 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 "LevelDBTransaction.h"
+
+#include "LevelDBDatabase.h"
+#include "LevelDBSlice.h"
+#include "LevelDBWriteBatch.h"
+
+#if ENABLE(INDEXED_DATABASE)
+#if ENABLE(LEVELDB)
+
+namespace WebCore {
+
+PassRefPtr<LevelDBTransaction> LevelDBTransaction::create(LevelDBDatabase* db)
+{
+ return adoptRef(new LevelDBTransaction(db));
+}
+
+LevelDBTransaction::LevelDBTransaction(LevelDBDatabase* db)
+ : m_db(db)
+ , m_comparator(db->comparator())
+ , m_finished(false)
+{
+ m_tree.abstractor().m_comparator = m_comparator;
+}
+
+void LevelDBTransaction::clearTree()
+{
+ TreeType::Iterator iterator;
+ iterator.start_iter_least(m_tree);
+
+ while (*iterator) {
+ delete *iterator;
+ ++iterator;
+ }
+ m_tree.purge();
+}
+
+LevelDBTransaction::~LevelDBTransaction()
+{
+ clearTree();
+}
+
+static void initVector(const LevelDBSlice& slice, Vector<char>* vector)
+{
+ vector->clear();
+ vector->append(slice.begin(), slice.end() - slice.begin());
+}
+
+bool LevelDBTransaction::set(const LevelDBSlice& key, const Vector<char>& value, bool deleted)
+{
+ ASSERT(!m_finished);
+ bool newNode = false;
+ AVLTreeNode* node = m_tree.search(key);
+
+ if (!node) {
+ node = new AVLTreeNode;
+ initVector(key, &node->key);
+ m_tree.insert(node);
+ newNode = true;
+ }
+ node->value = value;
+ node->deleted = deleted;
+
+ if (newNode)
+ resetIterators();
+ return true;
+}
+
+bool LevelDBTransaction::put(const LevelDBSlice& key, const Vector<char>& value)
+{
+ return set(key, value, false);
+}
+
+bool LevelDBTransaction::remove(const LevelDBSlice& key)
+{
+ return set(key, Vector<char>(), true);
+}
+
+bool LevelDBTransaction::get(const LevelDBSlice& key, Vector<char>& value)
+{
+ ASSERT(!m_finished);
+ AVLTreeNode* node = m_tree.search(key);
+
+ if (node) {
+ if (node->deleted)
+ return false;
+
+ value = node->value;
+ return true;
+ }
+
+ return m_db->get(key, value);
+}
+
+bool LevelDBTransaction::commit()
+{
+ ASSERT(!m_finished);
+ OwnPtr<LevelDBWriteBatch> writeBatch = LevelDBWriteBatch::create();
+
+ TreeType::Iterator iterator;
+ iterator.start_iter_least(m_tree);
+
+ while (*iterator) {
+ AVLTreeNode* node = *iterator;
+ if (!node->deleted)
+ writeBatch->put(node->key, node->value);
+ else
+ writeBatch->remove(node->key);
+ ++iterator;
+ }
+
+ if (!m_db->write(*writeBatch))
+ return false;
+
+ clearTree();
+ m_finished = true;
+ return true;
+}
+
+void LevelDBTransaction::rollback()
+{
+ ASSERT(!m_finished);
+ m_finished = true;
+ clearTree();
+}
+
+PassOwnPtr<LevelDBIterator> LevelDBTransaction::createIterator()
+{
+ return TransactionIterator::create(this);
+}
+
+PassOwnPtr<LevelDBTransaction::TreeIterator> LevelDBTransaction::TreeIterator::create(LevelDBTransaction* transaction)
+{
+ return adoptPtr(new TreeIterator(transaction));
+}
+
+bool LevelDBTransaction::TreeIterator::isValid() const
+{
+ return *m_iterator;
+}
+
+void LevelDBTransaction::TreeIterator::seekToLast()
+{
+ m_iterator.start_iter_greatest(*m_tree);
+ if (isValid())
+ m_key = (*m_iterator)->key;
+}
+
+void LevelDBTransaction::TreeIterator::seek(const LevelDBSlice& target)
+{
+ m_iterator.start_iter(*m_tree, target, TreeType::EQUAL);
+ if (!isValid())
+ m_iterator.start_iter(*m_tree, target, TreeType::GREATER);
+
+ if (isValid())
+ m_key = (*m_iterator)->key;
+}
+
+void LevelDBTransaction::TreeIterator::next()
+{
+ ASSERT(isValid());
+ ++m_iterator;
+ if (isValid()) {
+ ASSERT(m_transaction->m_comparator->compare((*m_iterator)->key, m_key) > 0);
+ m_key = (*m_iterator)->key;
+ }
+}
+
+void LevelDBTransaction::TreeIterator::prev()
+{
+ ASSERT(isValid());
+ --m_iterator;
+ if (isValid()) {
+ ASSERT(m_tree->abstractor().m_comparator->compare((*m_iterator)->key, m_key) < 0);
+ m_key = (*m_iterator)->key;
+ }
+}
+
+LevelDBSlice LevelDBTransaction::TreeIterator::key() const
+{
+ ASSERT(isValid());
+ return m_key;
+}
+
+LevelDBSlice LevelDBTransaction::TreeIterator::value() const
+{
+ ASSERT(isValid());
+ ASSERT(!isDeleted());
+ return (*m_iterator)->value;
+}
+
+bool LevelDBTransaction::TreeIterator::isDeleted() const
+{
+ ASSERT(isValid());
+ return (*m_iterator)->deleted;
+}
+
+void LevelDBTransaction::TreeIterator::reset()
+{
+ // FIXME: Be lazy: set a flag and do the actual reset next time we use the iterator.
+ if (isValid()) {
+ m_iterator.start_iter(*m_tree, m_key, TreeType::EQUAL);
+ ASSERT(isValid());
+ }
+}
+
+LevelDBTransaction::TreeIterator::~TreeIterator()
+{
+ m_transaction->unregisterIterator(this);
+}
+
+LevelDBTransaction::TreeIterator::TreeIterator(LevelDBTransaction* transaction)
+ : m_tree(&transaction->m_tree)
+ , m_transaction(transaction)
+{
+ transaction->registerIterator(this);
+}
+
+PassOwnPtr<LevelDBTransaction::TransactionIterator> LevelDBTransaction::TransactionIterator::create(PassRefPtr<LevelDBTransaction> transaction)
+{
+ return adoptPtr(new TransactionIterator(transaction));
+}
+
+LevelDBTransaction::TransactionIterator::TransactionIterator(PassRefPtr<LevelDBTransaction> transaction)
+ : m_transaction(transaction)
+ , m_comparator(m_transaction->m_comparator)
+ , m_treeIterator(TreeIterator::create(m_transaction.get()))
+ , m_dbIterator(m_transaction->m_db->createIterator())
+ , m_current(0)
+ , m_direction(kForward)
+{
+}
+
+bool LevelDBTransaction::TransactionIterator::isValid() const
+{
+ return m_current;
+}
+
+void LevelDBTransaction::TransactionIterator::seekToLast()
+{
+ m_treeIterator->seekToLast();
+ m_dbIterator->seekToLast();
+ m_direction = kReverse;
+
+ handleConflictsAndDeletes();
+ setCurrentIteratorToLargestKey();
+}
+
+void LevelDBTransaction::TransactionIterator::seek(const LevelDBSlice& target)
+{
+ m_treeIterator->seek(target);
+ m_dbIterator->seek(target);
+ m_direction = kForward;
+
+ handleConflictsAndDeletes();
+ setCurrentIteratorToSmallestKey();
+}
+
+void LevelDBTransaction::TransactionIterator::next()
+{
+ ASSERT(isValid());
+
+ if (m_direction != kForward) {
+ // Ensure the non-current iterator is positioned after key().
+
+ LevelDBIterator* nonCurrent = (m_current == m_dbIterator.get()) ? m_treeIterator.get() : m_dbIterator.get();
+
+ nonCurrent->seek(key());
+ if (nonCurrent->isValid() && !m_comparator->compare(nonCurrent->key(), key()))
+ nonCurrent->next(); // Take an extra step so the non-current key is strictly greater than key().
+
+ ASSERT(!nonCurrent->isValid() || m_comparator->compare(nonCurrent->key(), key()) > 0);
+
+ m_direction = kForward;
+ }
+
+ m_current->next();
+ handleConflictsAndDeletes();
+ setCurrentIteratorToSmallestKey();
+}
+
+void LevelDBTransaction::TransactionIterator::prev()
+{
+ ASSERT(isValid());
+
+ if (m_direction != kReverse) {
+ // Ensure the non-current iterator is positioned before key().
+
+ LevelDBIterator* nonCurrent = (m_current == m_dbIterator.get()) ? m_treeIterator.get() : m_dbIterator.get();
+
+ nonCurrent->seek(key());
+ if (nonCurrent->isValid()) {
+ // Iterator is at first entry >= key().
+ // Step back once to entry < key.
+ // This is why we don't check for the keys being the same before
+ // stepping, like we do in next() above.
+ nonCurrent->prev();
+ } else
+ nonCurrent->seekToLast(); // Iterator has no entries >= key(). Position at last entry.
+
+ ASSERT(!nonCurrent->isValid() || m_comparator->compare(nonCurrent->key(), key()) < 0);
+
+ m_direction = kReverse;
+ }
+
+ m_current->prev();
+ handleConflictsAndDeletes();
+ setCurrentIteratorToLargestKey();
+}
+
+LevelDBSlice LevelDBTransaction::TransactionIterator::key() const
+{
+ ASSERT(isValid());
+ return m_current->key();
+}
+
+LevelDBSlice LevelDBTransaction::TransactionIterator::value() const
+{
+ ASSERT(isValid());
+ return m_current->value();
+}
+
+void LevelDBTransaction::TransactionIterator::handleConflictsAndDeletes()
+{
+ bool loop = true;
+
+ while (loop) {
+ loop = false;
+
+ if (m_treeIterator->isValid() && m_dbIterator->isValid() && !m_comparator->compare(m_treeIterator->key(), m_dbIterator->key())) {
+ // For equal keys, the tree iterator takes precedence, so move the database iterator another step.
+ if (m_direction == kForward)
+ m_dbIterator->next();
+ else
+ m_dbIterator->prev();
+ }
+
+ if (m_treeIterator->isValid() && m_treeIterator->isDeleted()) {
+ // If the tree iterator is on a delete marker, take another step.
+ if (m_direction == kForward)
+ m_treeIterator->next();
+ else
+ m_treeIterator->prev();
+
+ loop = true;
+ }
+ }
+}
+
+void LevelDBTransaction::TransactionIterator::setCurrentIteratorToSmallestKey()
+{
+ LevelDBIterator* smallest = 0;
+
+ if (m_treeIterator->isValid())
+ smallest = m_treeIterator.get();
+
+ if (m_dbIterator->isValid()) {
+ if (!smallest || m_comparator->compare(m_dbIterator->key(), smallest->key()) < 0)
+ smallest = m_dbIterator.get();
+ }
+
+ m_current = smallest;
+}
+
+void LevelDBTransaction::TransactionIterator::setCurrentIteratorToLargestKey()
+{
+ LevelDBIterator* largest = 0;
+
+ if (m_treeIterator->isValid())
+ largest = m_treeIterator.get();
+
+ if (m_dbIterator->isValid()) {
+ if (!largest || m_comparator->compare(m_dbIterator->key(), largest->key()) > 0)
+ largest = m_dbIterator.get();
+ }
+
+ m_current = largest;
+}
+
+void LevelDBTransaction::registerIterator(TreeIterator* iterator)
+{
+ ASSERT(!m_treeIterators.contains(iterator));
+ m_treeIterators.add(iterator);
+}
+
+void LevelDBTransaction::unregisterIterator(TreeIterator* iterator)
+{
+ ASSERT(m_treeIterators.contains(iterator));
+ m_treeIterators.remove(iterator);
+}
+
+void LevelDBTransaction::resetIterators()
+{
+ for (HashSet<TreeIterator*>::iterator i = m_treeIterators.begin(); i != m_treeIterators.end(); ++i) {
+ TreeIterator* treeIterator = *i;
+ treeIterator->reset();
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(LEVELDB)
+#endif // ENABLE(INDEXED_DATABASE)
--- /dev/null
+/*
+ * 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 LevelDBTransaction_h
+#define LevelDBTransaction_h
+
+#if ENABLE(INDEXED_DATABASE)
+#if ENABLE(LEVELDB)
+
+#include "LevelDBComparator.h"
+#include "LevelDBIterator.h"
+#include "LevelDBSlice.h"
+#include <wtf/AVLTree.h>
+#include <wtf/HashSet.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class LevelDBDatabase;
+
+using WTF::AVLTree;
+
+class LevelDBTransaction : public RefCounted<LevelDBTransaction> {
+public:
+ static PassRefPtr<LevelDBTransaction> create(LevelDBDatabase*);
+
+ ~LevelDBTransaction();
+ bool put(const LevelDBSlice& key, const Vector<char>& value);
+ bool remove(const LevelDBSlice& key);
+ bool get(const LevelDBSlice& key, Vector<char>& value);
+ bool commit();
+ void rollback();
+
+ PassOwnPtr<LevelDBIterator> createIterator();
+
+private:
+ LevelDBTransaction(LevelDBDatabase*);
+
+ struct AVLTreeNode {
+ Vector<char> key;
+ Vector<char> value;
+ bool deleted;
+
+ AVLTreeNode* less;
+ AVLTreeNode* greater;
+ int balanceFactor;
+ };
+
+ struct AVLTreeAbstractor {
+ typedef AVLTreeNode* handle;
+ typedef size_t size;
+ typedef LevelDBSlice key;
+
+ handle get_less(handle h) { return h->less; }
+ void set_less(handle h, handle less) { h->less = less; }
+ handle get_greater(handle h) { return h->greater; }
+ void set_greater(handle h, handle greater) { h->greater = greater; }
+
+ int get_balance_factor(handle h) { return h->balanceFactor; }
+ void set_balance_factor(handle h, int bf) { h->balanceFactor = bf; }
+
+ int compare_key_key(const key& ka, const key& kb) { return m_comparator->compare(ka, kb); }
+ int compare_key_node(const key& k, handle h) { return compare_key_key(k, h->key); }
+ int compare_node_node(handle ha, handle hb) { return compare_key_key(ha->key, hb->key); }
+
+ static handle null() { return 0; }
+
+ const LevelDBComparator* m_comparator;
+ };
+
+ typedef AVLTree<AVLTreeAbstractor> TreeType;
+
+ class TreeIterator : public LevelDBIterator {
+ public:
+ static PassOwnPtr<TreeIterator> create(LevelDBTransaction*);
+ ~TreeIterator();
+
+ virtual bool isValid() const;
+ virtual void seekToLast();
+ virtual void seek(const LevelDBSlice&);
+ virtual void next();
+ virtual void prev();
+ virtual LevelDBSlice key() const;
+ virtual LevelDBSlice value() const;
+ bool isDeleted() const;
+ void reset();
+
+ private:
+ TreeIterator(LevelDBTransaction*);
+ mutable TreeType::Iterator m_iterator; // Dereferencing this is non-const.
+ TreeType* m_tree;
+ LevelDBTransaction* m_transaction;
+ Vector<char> m_key;
+ };
+
+ class TransactionIterator : public LevelDBIterator {
+ public:
+ ~TransactionIterator() { };
+ static PassOwnPtr<TransactionIterator> create(PassRefPtr<LevelDBTransaction>);
+
+ virtual bool isValid() const;
+ virtual void seekToLast();
+ virtual void seek(const LevelDBSlice& target);
+ virtual void next();
+ virtual void prev();
+ virtual LevelDBSlice key() const;
+ virtual LevelDBSlice value() const;
+
+ private:
+ TransactionIterator(PassRefPtr<LevelDBTransaction>);
+ void handleConflictsAndDeletes();
+ void setCurrentIteratorToSmallestKey();
+ void setCurrentIteratorToLargestKey();
+
+ RefPtr<LevelDBTransaction> m_transaction;
+ const LevelDBComparator* m_comparator;
+ OwnPtr<TreeIterator> m_treeIterator;
+ OwnPtr<LevelDBIterator> m_dbIterator;
+ LevelDBIterator* m_current;
+
+ enum Direction {
+ kForward,
+ kReverse
+ };
+ Direction m_direction;
+ };
+
+ bool set(const LevelDBSlice& key, const Vector<char>& value, bool deleted);
+ void clearTree();
+ void registerIterator(TreeIterator*);
+ void unregisterIterator(TreeIterator*);
+ void resetIterators();
+
+ LevelDBDatabase* m_db;
+ const LevelDBComparator* m_comparator;
+ TreeType m_tree;
+ bool m_finished;
+ HashSet<TreeIterator*> m_treeIterators;
+};
+
+}
+
+#endif // ENABLE(LEVELDB)
+#endif // ENABLE(INDEXED_DATABASE)
+
+#endif // LevelDBTransaction_h
*/
#include "config.h"
-#include "LevelDBIterator.h"
+#include "LevelDBWriteBatch.h"
#if ENABLE(LEVELDB)
-#include <leveldb/iterator.h>
+#include "LevelDBSlice.h"
#include <leveldb/slice.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/text/CString.h>
-#include <wtf/text/WTFString.h>
+#include <leveldb/write_batch.h>
namespace WebCore {
-LevelDBIterator::~LevelDBIterator()
+PassOwnPtr<LevelDBWriteBatch> LevelDBWriteBatch::create()
{
+ return adoptPtr(new LevelDBWriteBatch);
}
-LevelDBIterator::LevelDBIterator(PassOwnPtr<leveldb::Iterator> it)
- : m_iterator(it)
+LevelDBWriteBatch::LevelDBWriteBatch()
+ : m_writeBatch(adoptPtr(new leveldb::WriteBatch))
{
}
-static leveldb::Slice makeSlice(const Vector<char>& value)
+LevelDBWriteBatch::~LevelDBWriteBatch()
{
- return leveldb::Slice(value.data(), value.size());
}
-static LevelDBSlice makeLevelDBSlice(leveldb::Slice s)
+static leveldb::Slice makeSlice(const LevelDBSlice& s)
{
- return LevelDBSlice(s.data(), s.data() + s.size());
+ return leveldb::Slice(s.begin(), s.end() - s.begin());
}
-bool LevelDBIterator::isValid() const
+void LevelDBWriteBatch::put(const LevelDBSlice& key, const LevelDBSlice& value)
{
- return m_iterator->Valid();
+ m_writeBatch->Put(makeSlice(key), makeSlice(value));
}
-void LevelDBIterator::seekToLast()
+void LevelDBWriteBatch::remove(const LevelDBSlice& key)
{
- m_iterator->SeekToLast();
+ m_writeBatch->Delete(makeSlice(key));
}
-void LevelDBIterator::seek(const Vector<char>& target)
+void LevelDBWriteBatch::clear()
{
- m_iterator->Seek(makeSlice(target));
+ m_writeBatch->Clear();
}
-void LevelDBIterator::next()
-{
- m_iterator->Next();
-}
-
-void LevelDBIterator::prev()
-{
- m_iterator->Prev();
}
-LevelDBSlice LevelDBIterator::key() const
-{
- return makeLevelDBSlice(m_iterator->key());
-}
-
-LevelDBSlice LevelDBIterator::value() const
-{
- return makeLevelDBSlice(m_iterator->value());
-}
-
-} // namespace WebCore
-
-#endif // ENABLE(LEVELDB)
+#endif
--- /dev/null
+/*
+ * 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 LevelDBWriteBatch_h
+#define LevelDBWriteBatch_h
+
+#if ENABLE(LEVELDB)
+
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace leveldb {
+class WriteBatch;
+}
+
+namespace WebCore {
+
+class LevelDBSlice;
+
+// Wrapper around leveldb::WriteBatch.
+// This class holds a collection of updates to apply atomically to a database.
+class LevelDBWriteBatch {
+public:
+ static PassOwnPtr<LevelDBWriteBatch> create();
+ ~LevelDBWriteBatch();
+
+ void put(const LevelDBSlice& key, const LevelDBSlice& value);
+ void remove(const LevelDBSlice& key); // Add remove operation to the batch.
+ void clear();
+
+private:
+ friend class LevelDBDatabase;
+ LevelDBWriteBatch();
+
+ OwnPtr<leveldb::WriteBatch> m_writeBatch;
+};
+
+}
+
+#endif // ENABLE(LEVELDB)
+#endif // LevelDBWriteBatch_h
#include "LevelDBDatabase.h"
#include "LevelDBIterator.h"
#include "LevelDBSlice.h"
+#include "LevelDBTransaction.h"
#include "SecurityOrigin.h"
#ifndef INT64_MAX
using namespace IDBLevelDBCoding;
-static bool getInt(LevelDBDatabase* db, const Vector<char>& key, int64_t& foundInt)
+template <typename DBOrTransaction>
+static bool getInt(DBOrTransaction* db, const Vector<char>& key, int64_t& foundInt)
{
Vector<char> result;
if (!db->get(key, result))
return true;
}
-static bool putInt(LevelDBDatabase* db, const Vector<char>& key, int64_t value)
+template <typename DBOrTransaction>
+static bool putInt(DBOrTransaction* db, const Vector<char>& key, int64_t value)
{
return db->put(key, encodeInt(value));
}
-static bool getString(LevelDBDatabase* db, const Vector<char>& key, String& foundString)
+template <typename DBOrTransaction>
+static bool getString(DBOrTransaction* db, const Vector<char>& key, String& foundString)
{
Vector<char> result;
if (!db->get(key, result))
return true;
}
-static bool putString(LevelDBDatabase* db, const Vector<char> key, const String& value)
+template <typename DBOrTransaction>
+static bool putString(DBOrTransaction* db, const Vector<char> key, const String& value)
{
if (!db->put(key, encodeString(value)))
return false;
}
}
-static int64_t getNewObjectStoreId(LevelDBDatabase* db, int64_t databaseId)
+static int64_t getNewObjectStoreId(LevelDBTransaction* transaction, int64_t databaseId)
{
const Vector<char> freeListStartKey = ObjectStoreFreeListKey::encode(databaseId, 0);
const Vector<char> freeListStopKey = ObjectStoreFreeListKey::encode(databaseId, INT64_MAX);
- OwnPtr<LevelDBIterator> it = db->createIterator();
+ OwnPtr<LevelDBIterator> it = transaction->createIterator();
for (it->seek(freeListStartKey); it->isValid() && compareKeys(it->key(), freeListStopKey) < 0; it->next()) {
const char* p = it->key().begin();
const char* limit = it->key().end();
p = ObjectStoreFreeListKey::decode(p, limit, &freeListKey);
ASSERT(p);
- bool ok = db->remove(it->key());
+ bool ok = transaction->remove(it->key());
ASSERT_UNUSED(ok, ok);
return freeListKey.objectStoreId();
int64_t maxObjectStoreId = -1;
const Vector<char> maxObjectStoreIdKey = DatabaseMetaDataKey::encode(databaseId, DatabaseMetaDataKey::kMaxObjectStoreId);
- if (!getInt(db, maxObjectStoreIdKey, maxObjectStoreId))
+ if (!getInt(transaction, maxObjectStoreIdKey, maxObjectStoreId))
maxObjectStoreId = 0;
ASSERT(maxObjectStoreId >= 0);
int64_t objectStoreId = maxObjectStoreId + 1;
- bool ok = putInt(db, maxObjectStoreIdKey, objectStoreId);
+ bool ok = putInt(transaction, maxObjectStoreIdKey, objectStoreId);
ASSERT_UNUSED(ok, ok);
return objectStoreId;
bool IDBLevelDBBackingStore::createObjectStore(int64_t databaseId, const String& name, const String& keyPath, bool autoIncrement, int64_t& assignedObjectStoreId)
{
- int64_t objectStoreId = getNewObjectStoreId(m_db.get(), databaseId);
+ ASSERT(m_currentTransaction);
+ int64_t objectStoreId = getNewObjectStoreId(m_currentTransaction.get(), databaseId);
const Vector<char> nameKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0);
const Vector<char> keyPathKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 1);
const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 5);
const Vector<char> namesKey = ObjectStoreNamesKey::encode(databaseId, name);
- bool ok = putString(m_db.get(), nameKey, name);
+ bool ok = putString(m_currentTransaction.get(), nameKey, name);
if (!ok) {
LOG_ERROR("Internal Indexed DB error.");
return false;
}
- ok = putString(m_db.get(), keyPathKey, keyPath);
+ ok = putString(m_currentTransaction.get(), keyPathKey, keyPath);
if (!ok) {
LOG_ERROR("Internal Indexed DB error.");
return false;
}
- ok = putInt(m_db.get(), autoIncrementKey, autoIncrement);
+ ok = putInt(m_currentTransaction.get(), autoIncrementKey, autoIncrement);
if (!ok) {
LOG_ERROR("Internal Indexed DB error.");
return false;
}
- ok = putInt(m_db.get(), evictableKey, false);
+ ok = putInt(m_currentTransaction.get(), evictableKey, false);
if (!ok) {
LOG_ERROR("Internal Indexed DB error.");
return false;
}
- ok = putInt(m_db.get(), lastVersionKey, 1);
+ ok = putInt(m_currentTransaction.get(), lastVersionKey, 1);
if (!ok) {
LOG_ERROR("Internal Indexed DB error.");
return false;
}
- ok = putInt(m_db.get(), maxIndexIdKey, kMinimumIndexId);
+ ok = putInt(m_currentTransaction.get(), maxIndexIdKey, kMinimumIndexId);
if (!ok) {
LOG_ERROR("Internal Indexed DB error.");
return false;
}
- ok = putInt(m_db.get(), namesKey, objectStoreId);
+ ok = putInt(m_currentTransaction.get(), namesKey, objectStoreId);
if (!ok) {
LOG_ERROR("Internal Indexed DB error.");
return false;
return true;
}
-static bool deleteRange(LevelDBDatabase* db, const Vector<char>& begin, const Vector<char>& end)
+static bool deleteRange(LevelDBTransaction* transaction, const Vector<char>& begin, const Vector<char>& end)
{
- // FIXME: LevelDB may be able to provide a bulk operation that we can do first.
- OwnPtr<LevelDBIterator> it = db->createIterator();
+ OwnPtr<LevelDBIterator> it = transaction->createIterator();
for (it->seek(begin); it->isValid() && compareKeys(it->key(), end) < 0; it->next()) {
- if (!db->remove(it->key()))
+ if (!transaction->remove(it->key()))
return false;
}
void IDBLevelDBBackingStore::deleteObjectStore(int64_t databaseId, int64_t objectStoreId)
{
+ ASSERT(m_currentTransaction);
+
String objectStoreName;
- getString(m_db.get(), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), objectStoreName);
+ getString(m_currentTransaction.get(), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), objectStoreName);
- if (!deleteRange(m_db.get(), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 6)))
+ if (!deleteRange(m_currentTransaction.get(), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 0), ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 6)))
return; // FIXME: Report error.
- putString(m_db.get(), ObjectStoreFreeListKey::encode(databaseId, objectStoreId), "");
- m_db->remove(ObjectStoreNamesKey::encode(databaseId, objectStoreName));
+ putString(m_currentTransaction.get(), ObjectStoreFreeListKey::encode(databaseId, objectStoreId), "");
+ m_currentTransaction->remove(ObjectStoreNamesKey::encode(databaseId, objectStoreName));
- if (!deleteRange(m_db.get(), IndexFreeListKey::encode(databaseId, objectStoreId, 0), IndexFreeListKey::encode(databaseId, objectStoreId, INT64_MAX)))
+ if (!deleteRange(m_currentTransaction.get(), IndexFreeListKey::encode(databaseId, objectStoreId, 0), IndexFreeListKey::encode(databaseId, objectStoreId, INT64_MAX)))
return; // FIXME: Report error.
- if (!deleteRange(m_db.get(), IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0), IndexMetaDataKey::encode(databaseId, objectStoreId, INT64_MAX, 0)))
+ if (!deleteRange(m_currentTransaction.get(), IndexMetaDataKey::encode(databaseId, objectStoreId, 0, 0), IndexMetaDataKey::encode(databaseId, objectStoreId, INT64_MAX, 0)))
return; // FIXME: Report error.
clearObjectStore(databaseId, objectStoreId);
const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
Vector<char> data;
- if (!m_db->get(leveldbKey, data))
+ ASSERT(m_currentTransaction);
+ if (!m_currentTransaction->get(leveldbKey, data))
return String();
int64_t version;
};
}
-static int64_t getNewVersionNumber(LevelDBDatabase* db, int64_t databaseId, int64_t objectStoreId)
+static int64_t getNewVersionNumber(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId)
{
const Vector<char> lastVersionKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 4);
int64_t lastVersion = -1;
- if (!getInt(db, lastVersionKey, lastVersion))
+ if (!getInt(transaction, lastVersionKey, lastVersion))
lastVersion = 0;
ASSERT(lastVersion >= 0);
int64_t version = lastVersion + 1;
- bool ok = putInt(db, lastVersionKey, version);
+ bool ok = putInt(transaction, lastVersionKey, version);
ASSERT_UNUSED(ok, ok);
ASSERT(version > lastVersion); // FIXME: Think about how we want to handle the overflow scenario.
bool IDBLevelDBBackingStore::putObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const IDBKey& key, const String& value, ObjectStoreRecordIdentifier* recordIdentifier)
{
- int64_t version = getNewVersionNumber(m_db.get(), databaseId, objectStoreId);
+ ASSERT(m_currentTransaction);
+ int64_t version = getNewVersionNumber(m_currentTransaction.get(), databaseId, objectStoreId);
const Vector<char> objectStoredataKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
Vector<char> v;
v.append(encodeVarInt(version));
v.append(encodeString(value));
- if (!m_db->put(objectStoredataKey, v))
+ if (!m_currentTransaction->put(objectStoredataKey, v))
return false;
const Vector<char> existsEntryKey = ExistsEntryKey::encode(databaseId, objectStoreId, key);
- if (!m_db->put(existsEntryKey, encodeInt(version)))
+ if (!m_currentTransaction->put(existsEntryKey, encodeInt(version)))
return false;
LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<LevelDBRecordIdentifier*>(recordIdentifier);
void IDBLevelDBBackingStore::clearObjectStore(int64_t databaseId, int64_t objectStoreId)
{
+ ASSERT(m_currentTransaction);
const Vector<char> startKey = KeyPrefix(databaseId, objectStoreId, 0).encode();
const Vector<char> stopKey = KeyPrefix(databaseId, objectStoreId + 1, 0).encode();
- deleteRange(m_db.get(), startKey, stopKey);
+ deleteRange(m_currentTransaction.get(), startKey, stopKey);
}
PassRefPtr<IDBBackingStore::ObjectStoreRecordIdentifier> IDBLevelDBBackingStore::createInvalidRecordIdentifier()
void IDBLevelDBBackingStore::deleteObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, const ObjectStoreRecordIdentifier* recordIdentifier)
{
+ ASSERT(m_currentTransaction);
const LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<const LevelDBRecordIdentifier*>(recordIdentifier);
const Vector<char> key = ObjectStoreDataKey::encode(databaseId, objectStoreId, levelDBRecordIdentifier->primaryKey());
- m_db->remove(key);
+ m_currentTransaction->remove(key);
}
double IDBLevelDBBackingStore::nextAutoIncrementNumber(int64_t databaseId, int64_t objectStoreId)
{
+ ASSERT(m_currentTransaction);
const Vector<char> startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey());
const Vector<char> stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey());
- OwnPtr<LevelDBIterator> it = m_db->createIterator();
+ OwnPtr<LevelDBIterator> it = m_currentTransaction->createIterator();
int maxNumericKey = 0;
bool IDBLevelDBBackingStore::keyExistsInObjectStore(int64_t databaseId, int64_t objectStoreId, const IDBKey& key, ObjectStoreRecordIdentifier* foundRecordIdentifier)
{
+ ASSERT(m_currentTransaction);
const Vector<char> leveldbKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, key);
Vector<char> data;
- if (!m_db->get(leveldbKey, data))
+ if (!m_currentTransaction->get(leveldbKey, data))
return false;
int64_t version;
bool IDBLevelDBBackingStore::forEachObjectStoreRecord(int64_t databaseId, int64_t objectStoreId, ObjectStoreRecordCallback& callback)
{
+ ASSERT(m_currentTransaction);
const Vector<char> startKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, minIDBKey());
const Vector<char> stopKey = ObjectStoreDataKey::encode(databaseId, objectStoreId, maxIDBKey());
- OwnPtr<LevelDBIterator> it = m_db->createIterator();
+ OwnPtr<LevelDBIterator> it = m_currentTransaction->createIterator();
for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) {
const char *p = it->key().begin();
const char *limit = it->key().end();
}
}
-static int64_t getNewIndexId(LevelDBDatabase* db, int64_t databaseId, int64_t objectStoreId)
+static int64_t getNewIndexId(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId)
{
const Vector<char> startKey = IndexFreeListKey::encode(databaseId, objectStoreId, 0);
const Vector<char> stopKey = IndexFreeListKey::encode(databaseId, objectStoreId, INT64_MAX);
- OwnPtr<LevelDBIterator> it = db->createIterator();
+ OwnPtr<LevelDBIterator> it = transaction->createIterator();
for (it->seek(startKey); it->isValid() && compareKeys(it->key(), stopKey) < 0; it->next()) {
const char* p = it->key().begin();
const char* limit = it->key().end();
p = IndexFreeListKey::decode(p, limit, &freeListKey);
ASSERT(p);
- bool ok = db->remove(it->key());
+ bool ok = transaction->remove(it->key());
ASSERT_UNUSED(ok, ok);
ASSERT(freeListKey.indexId() >= kMinimumIndexId);
int64_t maxIndexId = -1;
const Vector<char> maxIndexIdKey = ObjectStoreMetaDataKey::encode(databaseId, objectStoreId, 5);
- if (!getInt(db, maxIndexIdKey, maxIndexId))
+ if (!getInt(transaction, maxIndexIdKey, maxIndexId))
maxIndexId = kMinimumIndexId;
ASSERT(maxIndexId >= 0);
int64_t indexId = maxIndexId + 1;
- bool ok = putInt(db, maxIndexIdKey, indexId);
+ bool ok = putInt(transaction, maxIndexIdKey, indexId);
if (!ok)
return false;
bool IDBLevelDBBackingStore::createIndex(int64_t databaseId, int64_t objectStoreId, const String& name, const String& keyPath, bool isUnique, int64_t& indexId)
{
- indexId = getNewIndexId(m_db.get(), databaseId, objectStoreId);
+ ASSERT(m_currentTransaction);
+ indexId = getNewIndexId(m_currentTransaction.get(), databaseId, objectStoreId);
const Vector<char> nameKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 0);
const Vector<char> uniqueKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 1);
const Vector<char> keyPathKey = IndexMetaDataKey::encode(databaseId, objectStoreId, indexId, 2);
- bool ok = putString(m_db.get(), nameKey, name);
+ bool ok = putString(m_currentTransaction.get(), nameKey, name);
if (!ok) {
LOG_ERROR("Internal Indexed DB error.");
return false;
}
- ok = putInt(m_db.get(), uniqueKey, isUnique);
+ ok = putInt(m_currentTransaction.get(), uniqueKey, isUnique);
if (!ok) {
LOG_ERROR("Internal Indexed DB error.");
return false;
}
- ok = putString(m_db.get(), keyPathKey, keyPath);
+ ok = putString(m_currentTransaction.get(), keyPathKey, keyPath);
if (!ok) {
LOG_ERROR("Internal Indexed DB error.");
return false;
ASSERT(indexId >= kMinimumIndexId);
const LevelDBRecordIdentifier* levelDBRecordIdentifier = static_cast<const LevelDBRecordIdentifier*>(recordIdentifier);
- const int64_t globalSequenceNumber = getNewVersionNumber(m_db.get(), databaseId, objectStoreId);
+ ASSERT(m_currentTransaction);
+ const int64_t globalSequenceNumber = getNewVersionNumber(m_currentTransaction.get(), databaseId, objectStoreId);
const Vector<char> indexDataKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, globalSequenceNumber);
Vector<char> data;
data.append(encodeVarInt(levelDBRecordIdentifier->version()));
data.append(levelDBRecordIdentifier->primaryKey());
- return m_db->put(indexDataKey, data);
+ return m_currentTransaction->put(indexDataKey, data);
}
-static bool findGreatestKeyLessThan(LevelDBDatabase* db, const Vector<char>& target, Vector<char>& foundKey)
+static bool findGreatestKeyLessThan(LevelDBTransaction* transaction, const Vector<char>& target, Vector<char>& foundKey)
{
- OwnPtr<LevelDBIterator> it = db->createIterator();
+ OwnPtr<LevelDBIterator> it = transaction->createIterator();
it->seek(target);
if (!it->isValid()) {
return getObjectStoreRecord(databaseId, objectStoreId, *primaryKey);
}
-static bool versionExists(LevelDBDatabase* db, int64_t databaseId, int64_t objectStoreId, int64_t version, const Vector<char>& encodedPrimaryKey)
+static bool versionExists(LevelDBTransaction* transaction, int64_t databaseId, int64_t objectStoreId, int64_t version, const Vector<char>& encodedPrimaryKey)
{
const Vector<char> key = ExistsEntryKey::encode(databaseId, objectStoreId, encodedPrimaryKey);
Vector<char> data;
- if (!db->get(key, data))
+ if (!transaction->get(key, data))
return false;
return decodeInt(data.begin(), data.end()) == version;
PassRefPtr<IDBKey> IDBLevelDBBackingStore::getPrimaryKeyViaIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key)
{
+ ASSERT(m_currentTransaction);
const Vector<char> leveldbKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, 0);
- OwnPtr<LevelDBIterator> it = m_db->createIterator();
+ OwnPtr<LevelDBIterator> it = m_currentTransaction->createIterator();
it->seek(leveldbKey);
for (;;) {
Vector<char> encodedPrimaryKey;
encodedPrimaryKey.append(p, it->value().end() - p);
- if (!versionExists(m_db.get(), databaseId, objectStoreId, version, encodedPrimaryKey)) {
+ if (!versionExists(m_currentTransaction.get(), databaseId, objectStoreId, version, encodedPrimaryKey)) {
// Delete stale index data entry and continue.
- m_db->remove(it->key());
+ m_currentTransaction->remove(it->key());
it->next();
continue;
}
bool IDBLevelDBBackingStore::keyExistsInIndex(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKey& key)
{
+ ASSERT(m_currentTransaction);
const Vector<char> levelDBKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, key, 0);
- OwnPtr<LevelDBIterator> it = m_db->createIterator();
+ OwnPtr<LevelDBIterator> it = m_currentTransaction->createIterator();
bool found = false;
bool firstSeek();
protected:
- CursorImplCommon(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
- : m_db(db)
+ CursorImplCommon(LevelDBTransaction* transaction, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ : m_transaction(transaction)
, m_lowKey(lowKey)
, m_lowOpen(lowOpen)
, m_highKey(highKey)
}
virtual ~CursorImplCommon() {}
- LevelDBDatabase* m_db;
+ LevelDBTransaction* m_transaction;
OwnPtr<LevelDBIterator> m_iterator;
Vector<char> m_lowKey;
bool m_lowOpen;
bool CursorImplCommon::firstSeek()
{
- m_iterator = m_db->createIterator();
+ m_iterator = m_transaction->createIterator();
if (m_forward)
m_iterator->seek(m_lowKey);
return false;
Vector<char> trash;
- if (!m_db->get(m_iterator->key(), trash))
+ if (!m_transaction->get(m_iterator->key(), trash))
continue;
if (m_forward && m_highOpen && compareIndexKeys(m_iterator->key(), m_highKey) >= 0) // high key not included in range
class ObjectStoreCursorImpl : public CursorImplCommon {
public:
- static PassRefPtr<ObjectStoreCursorImpl> create(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ static PassRefPtr<ObjectStoreCursorImpl> create(LevelDBTransaction* transaction, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
{
- return adoptRef(new ObjectStoreCursorImpl(db, lowKey, lowOpen, highKey, highOpen, forward));
+ return adoptRef(new ObjectStoreCursorImpl(transaction, lowKey, lowOpen, highKey, highOpen, forward));
}
// CursorImplCommon
virtual bool loadCurrentRow();
private:
- ObjectStoreCursorImpl(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
- : CursorImplCommon(db, lowKey, lowOpen, highKey, highOpen, forward)
+ ObjectStoreCursorImpl(LevelDBTransaction* transaction, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ : CursorImplCommon(transaction, lowKey, lowOpen, highKey, highOpen, forward)
{
}
class IndexKeyCursorImpl : public CursorImplCommon {
public:
- static PassRefPtr<IndexKeyCursorImpl> create(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ static PassRefPtr<IndexKeyCursorImpl> create(LevelDBTransaction* transaction, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
{
- return adoptRef(new IndexKeyCursorImpl(db, lowKey, lowOpen, highKey, highOpen, forward));
+ return adoptRef(new IndexKeyCursorImpl(transaction, lowKey, lowOpen, highKey, highOpen, forward));
}
// CursorImplCommon
virtual bool loadCurrentRow();
private:
- IndexKeyCursorImpl(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
- : CursorImplCommon(db, lowKey, lowOpen, highKey, highOpen, forward)
+ IndexKeyCursorImpl(LevelDBTransaction* transaction, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ : CursorImplCommon(transaction, lowKey, lowOpen, highKey, highOpen, forward)
{
}
Vector<char> primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey);
Vector<char> result;
- if (!m_db->get(primaryLevelDBKey, result))
+ if (!m_transaction->get(primaryLevelDBKey, result))
return false;
int64_t objectStoreDataVersion;
return false;
if (objectStoreDataVersion != indexDataVersion) { // FIXME: This is probably not very well covered by the layout tests.
- m_db->remove(m_iterator->key());
+ m_transaction->remove(m_iterator->key());
return false;
}
class IndexCursorImpl : public CursorImplCommon {
public:
- static PassRefPtr<IndexCursorImpl> create(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ static PassRefPtr<IndexCursorImpl> create(LevelDBTransaction* transaction, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
{
- return adoptRef(new IndexCursorImpl(db, lowKey, lowOpen, highKey, highOpen, forward));
+ return adoptRef(new IndexCursorImpl(transaction, lowKey, lowOpen, highKey, highOpen, forward));
}
// CursorImplCommon
bool loadCurrentRow();
private:
- IndexCursorImpl(LevelDBDatabase* db, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
- : CursorImplCommon(db, lowKey, lowOpen, highKey, highOpen, forward)
+ IndexCursorImpl(LevelDBTransaction* transaction, const Vector<char>& lowKey, bool lowOpen, const Vector<char>& highKey, bool highOpen, bool forward)
+ : CursorImplCommon(transaction, lowKey, lowOpen, highKey, highOpen, forward)
{
}
m_primaryLevelDBKey = ObjectStoreDataKey::encode(indexDataKey.databaseId(), indexDataKey.objectStoreId(), *m_primaryKey);
Vector<char> result;
- if (!m_db->get(m_primaryLevelDBKey, result))
+ if (!m_transaction->get(m_primaryLevelDBKey, result))
return false;
int64_t objectStoreDataVersion;
return false;
if (objectStoreDataVersion != indexDataVersion) {
- m_db->remove(m_iterator->key());
+ m_transaction->remove(m_iterator->key());
return false;
}
}
-static bool findLastIndexKeyEqualTo(LevelDBDatabase* db, const Vector<char>& target, Vector<char>& foundKey)
+static bool findLastIndexKeyEqualTo(LevelDBTransaction* transaction, const Vector<char>& target, Vector<char>& foundKey)
{
- OwnPtr<LevelDBIterator> it = db->createIterator();
+ OwnPtr<LevelDBIterator> it = transaction->createIterator();
it->seek(target);
if (!it->isValid())
PassRefPtr<IDBBackingStore::Cursor> IDBLevelDBBackingStore::openObjectStoreCursor(int64_t databaseId, int64_t objectStoreId, const IDBKeyRange* range, IDBCursor::Direction direction)
{
+ ASSERT(m_currentTransaction);
bool lowerBound = range && range->lower();
bool upperBound = range && range->upper();
bool forward = (direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT);
upperOpen = true; // Not included.
if (!forward) { // We need a key that exists.
- if (!findGreatestKeyLessThan(m_db.get(), stopKey, stopKey))
+ if (!findGreatestKeyLessThan(m_currentTransaction.get(), stopKey, stopKey))
return 0;
upperOpen = false;
}
upperOpen = range->upperOpen();
}
- RefPtr<ObjectStoreCursorImpl> cursor = ObjectStoreCursorImpl::create(m_db.get(), startKey, lowerOpen, stopKey, upperOpen, forward);
+ RefPtr<ObjectStoreCursorImpl> cursor = ObjectStoreCursorImpl::create(m_currentTransaction.get(), startKey, lowerOpen, stopKey, upperOpen, forward);
if (!cursor->firstSeek())
return 0;
PassRefPtr<IDBBackingStore::Cursor> IDBLevelDBBackingStore::openIndexKeyCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
{
+ ASSERT(m_currentTransaction);
bool lowerBound = range && range->lower();
bool upperBound = range && range->upper();
bool forward = (direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT);
upperOpen = false; // Included.
if (!forward) { // We need a key that exists.
- if (!findGreatestKeyLessThan(m_db.get(), stopKey, stopKey))
+ if (!findGreatestKeyLessThan(m_currentTransaction.get(), stopKey, stopKey))
return 0;
upperOpen = false;
}
} else {
stopKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->upper(), 0);
- if (!findLastIndexKeyEqualTo(m_db.get(), stopKey, stopKey)) // Seek to the *last* key in the set of non-unique keys.
+ if (!findLastIndexKeyEqualTo(m_currentTransaction.get(), stopKey, stopKey)) // Seek to the *last* key in the set of non-unique keys.
return 0;
upperOpen = range->upperOpen();
}
- RefPtr<IndexKeyCursorImpl> cursor = IndexKeyCursorImpl::create(m_db.get(), startKey, lowerOpen, stopKey, upperOpen, forward);
+ RefPtr<IndexKeyCursorImpl> cursor = IndexKeyCursorImpl::create(m_currentTransaction.get(), startKey, lowerOpen, stopKey, upperOpen, forward);
if (!cursor->firstSeek())
return 0;
PassRefPtr<IDBBackingStore::Cursor> IDBLevelDBBackingStore::openIndexCursor(int64_t databaseId, int64_t objectStoreId, int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
{
+ ASSERT(m_currentTransaction);
bool lowerBound = range && range->lower();
bool upperBound = range && range->upper();
bool forward = (direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::NEXT);
upperOpen = false; // Included.
if (!forward) { // We need a key that exists.
- if (!findGreatestKeyLessThan(m_db.get(), stopKey, stopKey))
+ if (!findGreatestKeyLessThan(m_currentTransaction.get(), stopKey, stopKey))
return 0;
upperOpen = false;
}
} else {
stopKey = IndexDataKey::encode(databaseId, objectStoreId, indexId, *range->upper(), 0);
- if (!findLastIndexKeyEqualTo(m_db.get(), stopKey, stopKey)) // Seek to the *last* key in the set of non-unique keys.
+ if (!findLastIndexKeyEqualTo(m_currentTransaction.get(), stopKey, stopKey)) // Seek to the *last* key in the set of non-unique keys.
return 0;
upperOpen = range->upperOpen();
}
- RefPtr<IndexCursorImpl> cursor = IndexCursorImpl::create(m_db.get(), startKey, lowerOpen, stopKey, upperOpen, forward);
+ RefPtr<IndexCursorImpl> cursor = IndexCursorImpl::create(m_currentTransaction.get(), startKey, lowerOpen, stopKey, upperOpen, forward);
if (!cursor->firstSeek())
return 0;
return cursor.release();
}
-namespace {
-class DummyTransaction : public IDBBackingStore::Transaction {
-public:
- virtual void begin() {}
- virtual void commit() {}
- virtual void rollback() {}
-};
+PassRefPtr<IDBBackingStore::Transaction> IDBLevelDBBackingStore::createTransaction()
+{
+ return Transaction::create(this);
}
-PassRefPtr<IDBBackingStore::Transaction> IDBLevelDBBackingStore::createTransaction()
+PassRefPtr<IDBLevelDBBackingStore::Transaction> IDBLevelDBBackingStore::Transaction::create(IDBLevelDBBackingStore* backingStore)
+{
+ return adoptRef(new Transaction(backingStore));
+}
+
+IDBLevelDBBackingStore::Transaction::Transaction(IDBLevelDBBackingStore* backingStore)
+ : m_backingStore(backingStore)
+{
+}
+
+void IDBLevelDBBackingStore::Transaction::begin()
+{
+ ASSERT(!m_backingStore->m_currentTransaction);
+ m_backingStore->m_currentTransaction = LevelDBTransaction::create(m_backingStore->m_db.get());
+}
+
+void IDBLevelDBBackingStore::Transaction::commit()
+{
+ ASSERT(m_backingStore->m_currentTransaction);
+ m_backingStore->m_currentTransaction->commit();
+ m_backingStore->m_currentTransaction.clear();
+}
+
+void IDBLevelDBBackingStore::Transaction::rollback()
{
- // FIXME: We need to implement a transaction abstraction that allows for roll-backs, and write tests for it.
- return adoptRef(new DummyTransaction());
+ ASSERT(m_backingStore->m_currentTransaction);
+ m_backingStore->m_currentTransaction->rollback();
+ m_backingStore->m_currentTransaction.clear();
}
// FIXME: deleteDatabase should be part of IDBBackingStore.
class LevelDBComparator;
class LevelDBDatabase;
+class LevelDBTransaction;
class IDBLevelDBBackingStore : public IDBBackingStore {
public:
RefPtr<IDBFactoryBackendImpl> m_factory;
OwnPtr<LevelDBDatabase> m_db;
OwnPtr<LevelDBComparator> m_comparator;
+ RefPtr<LevelDBTransaction> m_currentTransaction;
+
+ class Transaction : public IDBBackingStore::Transaction {
+ public:
+ static PassRefPtr<Transaction> create(IDBLevelDBBackingStore*);
+ virtual void begin();
+ virtual void commit();
+ virtual void rollback();
+
+ private:
+ Transaction(IDBLevelDBBackingStore*);
+ IDBLevelDBBackingStore* m_backingStore;
+ };
};
} // namespace WebCore
if (m_state == Finished)
return;
+ bool wasRunning = m_state == Running;
+
// The last reference to this object may be released while performing the
// abort steps below. We therefore take a self reference to keep ourselves
// alive while executing this method.
m_taskEventTimer.stop();
closeOpenCursors();
- m_transaction->rollback();
+ if (wasRunning)
+ m_transaction->rollback();
// Run the abort tasks, if any.
while (!m_abortTaskQueue.isEmpty()) {