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