1/* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "LevelDBDatabase.h" 28 29#if USE(LEVELDB) 30 31#include "HistogramSupport.h" 32#include "LevelDBComparator.h" 33#include "LevelDBIterator.h" 34#include "LevelDBSlice.h" 35#include "LevelDBWriteBatch.h" 36#include "Logging.h" 37#include "NotImplemented.h" 38#include <helpers/memenv/memenv.h> 39#include <leveldb/comparator.h> 40#include <leveldb/db.h> 41#include <leveldb/env.h> 42#include <leveldb/slice.h> 43#include <string> 44#include <wtf/PassOwnPtr.h> 45#include <wtf/text/CString.h> 46#include <wtf/text/WTFString.h> 47 48namespace leveldb { 49 50static Env* IDBEnv() 51{ 52 return leveldb::Env::Default(); 53} 54 55} 56 57namespace WebCore { 58 59static leveldb::Slice makeSlice(const Vector<char>& value) 60{ 61 return leveldb::Slice(value.data(), value.size()); 62} 63 64static leveldb::Slice makeSlice(const LevelDBSlice& s) 65{ 66 return leveldb::Slice(s.begin(), s.end() - s.begin()); 67} 68 69static LevelDBSlice makeLevelDBSlice(const leveldb::Slice& s) 70{ 71 return LevelDBSlice(s.data(), s.data() + s.size()); 72} 73 74class ComparatorAdapter : public leveldb::Comparator { 75public: 76 ComparatorAdapter(const LevelDBComparator* comparator) 77 : m_comparator(comparator) 78 { 79 } 80 81 virtual int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const 82 { 83 return m_comparator->compare(makeLevelDBSlice(a), makeLevelDBSlice(b)); 84 } 85 86 virtual const char* Name() const { return m_comparator->name(); } 87 88 // FIXME: Support the methods below in the future. 89 virtual void FindShortestSeparator(std::string* /* start */, const leveldb::Slice& /* limit */) const { } 90 virtual void FindShortSuccessor(std::string* /* key */) const { } 91 92private: 93 const LevelDBComparator* m_comparator; 94}; 95 96LevelDBSnapshot::LevelDBSnapshot(LevelDBDatabase* db) 97 : m_db(db->m_db.get()) 98 , m_snapshot(m_db->GetSnapshot()) 99{ 100} 101 102LevelDBSnapshot::~LevelDBSnapshot() 103{ 104 m_db->ReleaseSnapshot(m_snapshot); 105} 106 107LevelDBDatabase::LevelDBDatabase() 108{ 109} 110 111LevelDBDatabase::~LevelDBDatabase() 112{ 113 // m_db's destructor uses m_comparatorAdapter; order of deletion is important. 114 m_db.clear(); 115 m_comparatorAdapter.clear(); 116 m_env.clear(); 117} 118 119static leveldb::Status openDB(leveldb::Comparator* comparator, leveldb::Env* env, const String& path, leveldb::DB** db) 120{ 121 leveldb::Options options; 122 options.comparator = comparator; 123 options.create_if_missing = true; 124 options.paranoid_checks = true; 125 // 20 max_open_files is the minimum LevelDB allows. 126 options.max_open_files = 20; 127 options.env = env; 128 129 return leveldb::DB::Open(options, path.utf8().data(), db); 130} 131 132bool LevelDBDatabase::destroy(const String& fileName) 133{ 134 leveldb::Options options; 135 options.env = leveldb::IDBEnv(); 136 const leveldb::Status s = leveldb::DestroyDB(fileName.utf8().data(), options); 137 return s.ok(); 138} 139 140static void histogramLevelDBError(const char* histogramName, const leveldb::Status& s) 141{ 142 ASSERT(!s.ok()); 143 enum { 144 LevelDBNotFound, 145 LevelDBCorruption, 146 LevelDBIOError, 147 LevelDBOther, 148 LevelDBMaxError 149 }; 150 int levelDBError = LevelDBOther; 151 if (s.IsNotFound()) 152 levelDBError = LevelDBNotFound; 153 else if (s.IsCorruption()) 154 levelDBError = LevelDBCorruption; 155 else if (s.IsIOError()) 156 levelDBError = LevelDBIOError; 157 HistogramSupport::histogramEnumeration(histogramName, levelDBError, LevelDBMaxError); 158} 159 160PassOwnPtr<LevelDBDatabase> LevelDBDatabase::open(const String& fileName, const LevelDBComparator* comparator) 161{ 162 OwnPtr<ComparatorAdapter> comparatorAdapter = adoptPtr(new ComparatorAdapter(comparator)); 163 164 leveldb::DB* db; 165 const leveldb::Status s = openDB(comparatorAdapter.get(), leveldb::IDBEnv(), fileName, &db); 166 167 if (!s.ok()) { 168 histogramLevelDBError("WebCore.IndexedDB.LevelDBOpenErrors", s); 169 170 LOG_ERROR("Failed to open LevelDB database from %s: %s", fileName.ascii().data(), s.ToString().c_str()); 171 return nullptr; 172 } 173 174 OwnPtr<LevelDBDatabase> result = adoptPtr(new LevelDBDatabase); 175 result->m_db = adoptPtr(db); 176 result->m_comparatorAdapter = comparatorAdapter.release(); 177 result->m_comparator = comparator; 178 179 return result.release(); 180} 181 182PassOwnPtr<LevelDBDatabase> LevelDBDatabase::openInMemory(const LevelDBComparator* comparator) 183{ 184 OwnPtr<ComparatorAdapter> comparatorAdapter = adoptPtr(new ComparatorAdapter(comparator)); 185 OwnPtr<leveldb::Env> inMemoryEnv = adoptPtr(leveldb::NewMemEnv(leveldb::IDBEnv())); 186 187 leveldb::DB* db; 188 const leveldb::Status s = openDB(comparatorAdapter.get(), inMemoryEnv.get(), String(), &db); 189 190 if (!s.ok()) { 191 LOG_ERROR("Failed to open in-memory LevelDB database: %s", s.ToString().c_str()); 192 return nullptr; 193 } 194 195 OwnPtr<LevelDBDatabase> result = adoptPtr(new LevelDBDatabase); 196 result->m_env = inMemoryEnv.release(); 197 result->m_db = adoptPtr(db); 198 result->m_comparatorAdapter = comparatorAdapter.release(); 199 result->m_comparator = comparator; 200 201 return result.release(); 202} 203 204bool LevelDBDatabase::put(const LevelDBSlice& key, const Vector<char>& value) 205{ 206 leveldb::WriteOptions writeOptions; 207 writeOptions.sync = true; 208 209 const leveldb::Status s = m_db->Put(writeOptions, makeSlice(key), makeSlice(value)); 210 if (s.ok()) 211 return true; 212 LOG_ERROR("LevelDB put failed: %s", s.ToString().c_str()); 213 return false; 214} 215 216bool LevelDBDatabase::remove(const LevelDBSlice& key) 217{ 218 leveldb::WriteOptions writeOptions; 219 writeOptions.sync = true; 220 221 const leveldb::Status s = m_db->Delete(writeOptions, makeSlice(key)); 222 if (s.ok()) 223 return true; 224 if (s.IsNotFound()) 225 return false; 226 LOG_ERROR("LevelDB remove failed: %s", s.ToString().c_str()); 227 return false; 228} 229 230bool LevelDBDatabase::safeGet(const LevelDBSlice& key, Vector<char>& value, bool& found, const LevelDBSnapshot* snapshot) 231{ 232 found = false; 233 std::string result; 234 leveldb::ReadOptions readOptions; 235 readOptions.verify_checksums = true; // FIXME: Disable this if the performance impact is too great. 236 readOptions.snapshot = snapshot ? snapshot->m_snapshot : 0; 237 238 const leveldb::Status s = m_db->Get(readOptions, makeSlice(key), &result); 239 if (s.ok()) { 240 found = true; 241 value.clear(); 242 value.append(result.c_str(), result.length()); 243 return true; 244 } 245 if (s.IsNotFound()) 246 return true; 247 LOG_ERROR("LevelDB get failed: %s", s.ToString().c_str()); 248 return false; 249} 250 251bool LevelDBDatabase::write(LevelDBWriteBatch& writeBatch) 252{ 253 leveldb::WriteOptions writeOptions; 254 writeOptions.sync = true; 255 256 const leveldb::Status s = m_db->Write(writeOptions, writeBatch.m_writeBatch.get()); 257 if (s.ok()) 258 return true; 259 histogramLevelDBError("WebCore.IndexedDB.LevelDBWriteErrors", s); 260 LOG_ERROR("LevelDB write failed: %s", s.ToString().c_str()); 261 return false; 262} 263 264namespace { 265class IteratorImpl : public LevelDBIterator { 266public: 267 ~IteratorImpl() { }; 268 269 virtual bool isValid() const; 270 virtual void seekToLast(); 271 virtual void seek(const LevelDBSlice& target); 272 virtual void next(); 273 virtual void prev(); 274 virtual LevelDBSlice key() const; 275 virtual LevelDBSlice value() const; 276 277private: 278 friend class WebCore::LevelDBDatabase; 279 IteratorImpl(PassOwnPtr<leveldb::Iterator>); 280 void checkStatus(); 281 282 OwnPtr<leveldb::Iterator> m_iterator; 283}; 284} 285 286IteratorImpl::IteratorImpl(PassOwnPtr<leveldb::Iterator> it) 287 : m_iterator(it) 288{ 289} 290 291void IteratorImpl::checkStatus() 292{ 293 const leveldb::Status s = m_iterator->status(); 294 if (!s.ok()) 295 LOG_ERROR("LevelDB iterator error: %s", s.ToString().c_str()); 296} 297 298bool IteratorImpl::isValid() const 299{ 300 return m_iterator->Valid(); 301} 302 303void IteratorImpl::seekToLast() 304{ 305 m_iterator->SeekToLast(); 306 checkStatus(); 307} 308 309void IteratorImpl::seek(const LevelDBSlice& target) 310{ 311 m_iterator->Seek(makeSlice(target)); 312 checkStatus(); 313} 314 315void IteratorImpl::next() 316{ 317 ASSERT(isValid()); 318 m_iterator->Next(); 319 checkStatus(); 320} 321 322void IteratorImpl::prev() 323{ 324 ASSERT(isValid()); 325 m_iterator->Prev(); 326 checkStatus(); 327} 328 329LevelDBSlice IteratorImpl::key() const 330{ 331 ASSERT(isValid()); 332 return makeLevelDBSlice(m_iterator->key()); 333} 334 335LevelDBSlice IteratorImpl::value() const 336{ 337 ASSERT(isValid()); 338 return makeLevelDBSlice(m_iterator->value()); 339} 340 341PassOwnPtr<LevelDBIterator> LevelDBDatabase::createIterator(const LevelDBSnapshot* snapshot) 342{ 343 leveldb::ReadOptions readOptions; 344 readOptions.verify_checksums = true; // FIXME: Disable this if the performance impact is too great. 345 readOptions.snapshot = snapshot ? snapshot->m_snapshot : 0; 346 OwnPtr<leveldb::Iterator> i = adoptPtr(m_db->NewIterator(readOptions)); 347 if (!i) // FIXME: Double check if we actually need to check this. 348 return nullptr; 349 return adoptPtr(new IteratorImpl(i.release())); 350} 351 352const LevelDBComparator* LevelDBDatabase::comparator() const 353{ 354 return m_comparator; 355} 356 357} 358 359#endif 360