2011-03-03 Hans Wennborg <hans@chromium.org>
authorhans@chromium.org <hans@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 3 Mar 2011 10:37:41 +0000 (10:37 +0000)
committerhans@chromium.org <hans@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 3 Mar 2011 10:37:41 +0000 (10:37 +0000)
        Reviewed by Jeremy Orlow.

        IndexedDB: Move SQL code, especially for cursors, to IDBBackingStore
        https://bugs.webkit.org/show_bug.cgi?id=55376

        Move SQL code from IDBKey, IDBKeyRange, IDBIndexBackendImpl,
        IDBObjectStoreBackendImpl, and especially IDBCursorBackendImpl.

        No new functionality, so no new tests.

        * storage/IDBBackingStore.cpp:
        (WebCore::lowerCursorWhereFragment):
        (WebCore::upperCursorWhereFragment):
        (WebCore::IDBBackingStore::deleteObjectStoreRecord):
        (WebCore::IDBBackingStore::keyExistsInObjectStore):
        (WebCore::IDBBackingStore::getObjectViaIndex):
        (WebCore::keyFromQuery):
        (WebCore::IDBBackingStore::getPrimaryKeyViaIndex):
        (WebCore::IDBBackingStore::keyExistsInIndex):
        (WebCore::CursorImplCommon::CursorImplCommon::continueInternal):
        (WebCore::CursorImplCommon::ObjectStoreCursorImpl::ObjectStoreCursorImpl):
        (WebCore::CursorImplCommon::ObjectStoreCursorImpl::objectStoreDataId):
        (WebCore::CursorImplCommon::ObjectStoreCursorImpl::key):
        (WebCore::CursorImplCommon::ObjectStoreCursorImpl::value):
        (WebCore::CursorImplCommon::ObjectStoreCursorImpl::continueFunction):
        (WebCore::IDBBackingStore::openObjectStoreCursor):
        (WebCore::ObjectStoreCursorImpl::loadCurrentRow):
        (WebCore::ObjectStoreCursorImpl::currentRowExists):
        (WebCore::IndexKeyCursorImpl::IndexKeyCursorImpl):
        (WebCore::IndexKeyCursorImpl::indexDataId):
        (WebCore::IndexKeyCursorImpl::key):
        (WebCore::IndexKeyCursorImpl::primaryKey):
        (WebCore::IndexKeyCursorImpl::continueFunction):
        (WebCore::IDBBackingStore::openIndexKeyCursor):
        (WebCore::IndexKeyCursorImpl::loadCurrentRow):
        (WebCore::IndexKeyCursorImpl::currentRowExists):
        (WebCore::IndexCursorImpl::IndexCursorImpl):
        (WebCore::IndexCursorImpl::indexDataId):
        (WebCore::IndexCursorImpl::key):
        (WebCore::IndexCursorImpl::primaryKey):
        (WebCore::IndexCursorImpl::value):
        (WebCore::IndexCursorImpl::continueFunction):
        (WebCore::IDBBackingStore::openIndexCursor):
        (WebCore::IndexCursorImpl::loadCurrentRow):
        (WebCore::IndexCursorImpl::currentRowExists):
        * storage/IDBBackingStore.h:
        (WebCore::IDBBackingStore::Cursor::~Cursor):
        * storage/IDBCursorBackendImpl.cpp:
        (WebCore::IDBCursorBackendImpl::IDBCursorBackendImpl):
        (WebCore::IDBCursorBackendImpl::key):
        (WebCore::IDBCursorBackendImpl::value):
        (WebCore::IDBCursorBackendImpl::update):
        (WebCore::IDBCursorBackendImpl::continueFunctionInternal):
        (WebCore::IDBCursorBackendImpl::deleteFunction):
        * storage/IDBCursorBackendImpl.h:
        (WebCore::IDBCursorBackendImpl::create):
        * storage/IDBIndexBackendImpl.cpp:
        (WebCore::IDBIndexBackendImpl::openCursorInternal):
        (WebCore::IDBIndexBackendImpl::getInternal):
        (WebCore::IDBIndexBackendImpl::addingKeyAllowed):
        * storage/IDBIndexBackendImpl.h:
        * storage/IDBKey.cpp:
        (WebCore::IDBKey::isEqual):
        * storage/IDBKey.h:
        * storage/IDBKeyRange.cpp:
        * storage/IDBKeyRange.h:
        * storage/IDBObjectStoreBackendImpl.cpp:
        (WebCore::IDBObjectStoreBackendImpl::putInternal):
        (WebCore::IDBObjectStoreBackendImpl::deleteInternal):
        (WebCore::IDBObjectStoreBackendImpl::openCursorInternal):
        * storage/IDBObjectStoreBackendImpl.h:

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

13 files changed:
Source/WebCore/ChangeLog
Source/WebCore/storage/IDBBackingStore.cpp
Source/WebCore/storage/IDBBackingStore.h
Source/WebCore/storage/IDBCursorBackendImpl.cpp
Source/WebCore/storage/IDBCursorBackendImpl.h
Source/WebCore/storage/IDBIndexBackendImpl.cpp
Source/WebCore/storage/IDBIndexBackendImpl.h
Source/WebCore/storage/IDBKey.cpp
Source/WebCore/storage/IDBKey.h
Source/WebCore/storage/IDBKeyRange.cpp
Source/WebCore/storage/IDBKeyRange.h
Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp
Source/WebCore/storage/IDBObjectStoreBackendImpl.h

index 9a36a66..267bf2a 100644 (file)
@@ -1,3 +1,77 @@
+2011-03-03  Hans Wennborg  <hans@chromium.org>
+
+        Reviewed by Jeremy Orlow.
+
+        IndexedDB: Move SQL code, especially for cursors, to IDBBackingStore
+        https://bugs.webkit.org/show_bug.cgi?id=55376
+
+        Move SQL code from IDBKey, IDBKeyRange, IDBIndexBackendImpl,
+        IDBObjectStoreBackendImpl, and especially IDBCursorBackendImpl.
+
+        No new functionality, so no new tests.
+
+        * storage/IDBBackingStore.cpp:
+        (WebCore::lowerCursorWhereFragment):
+        (WebCore::upperCursorWhereFragment):
+        (WebCore::IDBBackingStore::deleteObjectStoreRecord):
+        (WebCore::IDBBackingStore::keyExistsInObjectStore):
+        (WebCore::IDBBackingStore::getObjectViaIndex):
+        (WebCore::keyFromQuery):
+        (WebCore::IDBBackingStore::getPrimaryKeyViaIndex):
+        (WebCore::IDBBackingStore::keyExistsInIndex):
+        (WebCore::CursorImplCommon::CursorImplCommon::continueInternal):
+        (WebCore::CursorImplCommon::ObjectStoreCursorImpl::ObjectStoreCursorImpl):
+        (WebCore::CursorImplCommon::ObjectStoreCursorImpl::objectStoreDataId):
+        (WebCore::CursorImplCommon::ObjectStoreCursorImpl::key):
+        (WebCore::CursorImplCommon::ObjectStoreCursorImpl::value):
+        (WebCore::CursorImplCommon::ObjectStoreCursorImpl::continueFunction):
+        (WebCore::IDBBackingStore::openObjectStoreCursor):
+        (WebCore::ObjectStoreCursorImpl::loadCurrentRow):
+        (WebCore::ObjectStoreCursorImpl::currentRowExists):
+        (WebCore::IndexKeyCursorImpl::IndexKeyCursorImpl):
+        (WebCore::IndexKeyCursorImpl::indexDataId):
+        (WebCore::IndexKeyCursorImpl::key):
+        (WebCore::IndexKeyCursorImpl::primaryKey):
+        (WebCore::IndexKeyCursorImpl::continueFunction):
+        (WebCore::IDBBackingStore::openIndexKeyCursor):
+        (WebCore::IndexKeyCursorImpl::loadCurrentRow):
+        (WebCore::IndexKeyCursorImpl::currentRowExists):
+        (WebCore::IndexCursorImpl::IndexCursorImpl):
+        (WebCore::IndexCursorImpl::indexDataId):
+        (WebCore::IndexCursorImpl::key):
+        (WebCore::IndexCursorImpl::primaryKey):
+        (WebCore::IndexCursorImpl::value):
+        (WebCore::IndexCursorImpl::continueFunction):
+        (WebCore::IDBBackingStore::openIndexCursor):
+        (WebCore::IndexCursorImpl::loadCurrentRow):
+        (WebCore::IndexCursorImpl::currentRowExists):
+        * storage/IDBBackingStore.h:
+        (WebCore::IDBBackingStore::Cursor::~Cursor):
+        * storage/IDBCursorBackendImpl.cpp:
+        (WebCore::IDBCursorBackendImpl::IDBCursorBackendImpl):
+        (WebCore::IDBCursorBackendImpl::key):
+        (WebCore::IDBCursorBackendImpl::value):
+        (WebCore::IDBCursorBackendImpl::update):
+        (WebCore::IDBCursorBackendImpl::continueFunctionInternal):
+        (WebCore::IDBCursorBackendImpl::deleteFunction):
+        * storage/IDBCursorBackendImpl.h:
+        (WebCore::IDBCursorBackendImpl::create):
+        * storage/IDBIndexBackendImpl.cpp:
+        (WebCore::IDBIndexBackendImpl::openCursorInternal):
+        (WebCore::IDBIndexBackendImpl::getInternal):
+        (WebCore::IDBIndexBackendImpl::addingKeyAllowed):
+        * storage/IDBIndexBackendImpl.h:
+        * storage/IDBKey.cpp:
+        (WebCore::IDBKey::isEqual):
+        * storage/IDBKey.h:
+        * storage/IDBKeyRange.cpp:
+        * storage/IDBKeyRange.h:
+        * storage/IDBObjectStoreBackendImpl.cpp:
+        (WebCore::IDBObjectStoreBackendImpl::putInternal):
+        (WebCore::IDBObjectStoreBackendImpl::deleteInternal):
+        (WebCore::IDBObjectStoreBackendImpl::openCursorInternal):
+        * storage/IDBObjectStoreBackendImpl.h:
+
 2011-03-03  Peter Kasting  <pkasting@google.com>
 
         Reviewed by James Robinson.
index e73b35f..818b282 100644 (file)
@@ -31,6 +31,7 @@
 #include "FileSystem.h"
 #include "IDBFactoryBackendImpl.h"
 #include "IDBKey.h"
+#include "IDBKeyRange.h"
 #include "SQLiteDatabase.h"
 #include "SQLiteStatement.h"
 #include "SQLiteTransaction.h"
@@ -336,6 +337,42 @@ static int bindKeyToQuery(SQLiteStatement& query, int column, const IDBKey& key)
     return 0;
 }
 
+static String lowerCursorWhereFragment(const IDBKey& key, String comparisonOperator, String qualifiedTableName = "")
+{
+    switch (key.type()) {
+    case IDBKey::StringType:
+        return "? " + comparisonOperator + " " + qualifiedTableName + "keyString  AND  ";
+    case IDBKey::DateType:
+        return "(? " + comparisonOperator + " " + qualifiedTableName + "keyDate  OR NOT " + qualifiedTableName + "keyString IS NULL)  AND  ";
+    case IDBKey::NumberType:
+        return "(? " + comparisonOperator + " " + qualifiedTableName + "keyNumber  OR  NOT " + qualifiedTableName + "keyString IS NULL  OR  NOT " + qualifiedTableName + "keyDate IS NULL)  AND  ";
+    case IDBKey::NullType:
+        if (comparisonOperator == "<")
+            return "NOT(" + qualifiedTableName + "keyString IS NULL  AND  " + qualifiedTableName + "keyDate IS NULL  AND  " + qualifiedTableName + "keyNumber IS NULL)  AND  ";
+        return ""; // If it's =, the upper bound half will do the constraining. If it's <=, then that's a no-op.
+    }
+    ASSERT_NOT_REACHED();
+    return "";
+}
+
+static String upperCursorWhereFragment(const IDBKey& key, String comparisonOperator, String qualifiedTableName = "")
+{
+    switch (key.type()) {
+    case IDBKey::StringType:
+        return "(" + qualifiedTableName + "keyString " + comparisonOperator + " ?  OR  " + qualifiedTableName + "keyString IS NULL)  AND  ";
+    case IDBKey::DateType:
+        return "(" + qualifiedTableName + "keyDate " + comparisonOperator + " ? OR " + qualifiedTableName + "keyDate IS NULL)  AND  " + qualifiedTableName + "keyString IS NULL  AND  ";
+    case IDBKey::NumberType:
+        return "(" + qualifiedTableName + "keyNumber " + comparisonOperator + " ? OR " + qualifiedTableName + "keyNumber IS NULL)  AND  " + qualifiedTableName + "keyString IS NULL  AND  " + qualifiedTableName + "keyDate IS NULL  AND  ";
+    case IDBKey::NullType:
+        if (comparisonOperator == "<")
+            return "0 != 0  AND  ";
+        return qualifiedTableName + "keyString IS NULL  AND  " + qualifiedTableName + "keyDate IS NULL  AND  " + qualifiedTableName + "keyNumber IS NULL  AND  ";
+    }
+    ASSERT_NOT_REACHED();
+    return "";
+}
+
 String IDBBackingStore::getObjectStoreRecord(int64_t objectStoreId, const IDBKey& key)
 {
     SQLiteStatement query(m_db, "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE objectStoreId = ? AND " + whereSyntaxForKey(key));
@@ -415,6 +452,27 @@ void IDBBackingStore::clearObjectStore(int64_t objectStoreId)
     doDelete(m_db, "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStoreId);
 }
 
+void IDBBackingStore::deleteObjectStoreRecord(int64_t, int64_t objectStoreDataId)
+{
+    SQLiteStatement osQuery(m_db, "DELETE FROM ObjectStoreData WHERE id = ?");
+    bool ok = osQuery.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+    osQuery.bindInt64(1, objectStoreDataId);
+
+    ok = osQuery.step() == SQLResultDone;
+    ASSERT_UNUSED(ok, ok);
+
+    SQLiteStatement indexQuery(m_db, "DELETE FROM IndexData WHERE objectStoreDataId = ?");
+    ok = indexQuery.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+    indexQuery.bindInt64(1, objectStoreDataId);
+
+    ok = indexQuery.step() == SQLResultDone;
+    ASSERT_UNUSED(ok, ok);
+}
+
 double IDBBackingStore::nextAutoIncrementNumber(int64_t objectStoreId)
 {
     SQLiteStatement query(m_db, "SELECT max(keyNumber) + 1 FROM ObjectStoreData WHERE objectStoreId = ? AND keyString IS NULL AND keyDate IS NULL");
@@ -429,6 +487,23 @@ double IDBBackingStore::nextAutoIncrementNumber(int64_t objectStoreId)
     return query.getColumnDouble(0);
 }
 
+bool IDBBackingStore::keyExistsInObjectStore(int64_t objectStoreId, const IDBKey& key, int64_t& foundObjectStoreDataId)
+{
+    String sql = String("SELECT id FROM ObjectStoreData WHERE objectStoreId = ? AND ") + whereSyntaxForKey(key);
+    SQLiteStatement query(m_db, sql);
+    bool ok = query.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+    query.bindInt64(1, objectStoreId);
+    bindKeyToQuery(query, 2, key);
+
+    if (query.step() != SQLResultRow)
+        return false;
+
+    foundObjectStoreDataId = query.getColumnInt64(0);
+    return true;
+}
+
 bool IDBBackingStore::forEachObjectStoreRecord(int64_t objectStoreId, ObjectStoreRecordCallback& callback)
 {
     SQLiteStatement query(m_db, "SELECT id, value FROM ObjectStoreData WHERE objectStoreId = ?");
@@ -515,6 +590,370 @@ bool IDBBackingStore::deleteIndexDataForRecord(int64_t objectStoreDataId)
     return query.step() == SQLResultDone;
 }
 
+String IDBBackingStore::getObjectViaIndex(int64_t indexId, const IDBKey& key)
+{
+    String sql = String("SELECT ")
+                 + "ObjectStoreData.value "
+                 + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id "
+                 + "WHERE IndexData.indexId = ?  AND  " + whereSyntaxForKey(key, "IndexData.")
+                 + "ORDER BY IndexData.id LIMIT 1"; // Order by insertion order when all else fails.
+    SQLiteStatement query(m_db, sql);
+    bool ok = query.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+    query.bindInt64(1, indexId);
+    bindKeyToQuery(query, 2, key);
+
+    if (query.step() != SQLResultRow)
+        return String();
+
+    String foundValue = query.getColumnBlobAsString(0);
+    ASSERT(query.step() != SQLResultRow);
+    return foundValue;
+}
+
+static PassRefPtr<IDBKey> keyFromQuery(SQLiteStatement& query, int baseColumn)
+{
+    if (query.columnCount() <= baseColumn)
+        return 0;
+
+    if (!query.isColumnNull(baseColumn))
+        return IDBKey::createString(query.getColumnText(baseColumn));
+
+    if (!query.isColumnNull(baseColumn + 1))
+        return IDBKey::createDate(query.getColumnDouble(baseColumn + 1));
+
+    if (!query.isColumnNull(baseColumn + 2))
+        return IDBKey::createNumber(query.getColumnDouble(baseColumn + 2));
+
+    return IDBKey::createNull();
+}
+
+PassRefPtr<IDBKey> IDBBackingStore::getPrimaryKeyViaIndex(int64_t indexId, const IDBKey& key)
+{
+    String sql = String("SELECT ")
+                 + "ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber "
+                 + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id "
+                 + "WHERE IndexData.indexId = ?  AND  " + whereSyntaxForKey(key, "IndexData.")
+                 + "ORDER BY IndexData.id LIMIT 1"; // Order by insertion order when all else fails.
+    SQLiteStatement query(m_db, sql);
+    bool ok = query.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+    query.bindInt64(1, indexId);
+    bindKeyToQuery(query, 2, key);
+
+    if (query.step() != SQLResultRow)
+        return false;
+
+    RefPtr<IDBKey> foundKey = keyFromQuery(query, 0);
+    ASSERT(query.step() != SQLResultRow);
+    return foundKey.release();
+}
+
+bool IDBBackingStore::keyExistsInIndex(int64_t indexId, const IDBKey& key)
+{
+    String sql = String("SELECT id FROM IndexData WHERE indexId = ? AND ") + whereSyntaxForKey(key);
+    SQLiteStatement query(m_db, sql);
+    bool ok = query.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+    query.bindInt64(1, indexId);
+    bindKeyToQuery(query, 2, key);
+
+    return query.step() == SQLResultRow;
+}
+
+namespace {
+
+class CursorImplCommon : public IDBBackingStore::Cursor {
+public:
+    CursorImplCommon(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint)
+        : m_query(sqliteDatabase, query)
+        , m_db(sqliteDatabase)
+        , m_uniquenessConstraint(uniquenessConstraint)
+    {
+    }
+    virtual ~CursorImplCommon() {}
+
+    // IDBBackingStore::Cursor
+    virtual bool continueFunction(const IDBKey*);
+    virtual PassRefPtr<IDBKey> key() { return m_currentKey; }
+    virtual PassRefPtr<IDBKey> primaryKey() { return m_currentKey; }
+    virtual String value() = 0;
+    virtual int64_t objectStoreDataId() = 0;
+    virtual int64_t indexDataId() = 0;
+
+    virtual void loadCurrentRow() = 0;
+    virtual bool currentRowExists() = 0;
+
+    SQLiteStatement m_query;
+
+protected:
+    SQLiteDatabase& m_db;
+    bool m_uniquenessConstraint;
+    int64_t m_currentId;
+    RefPtr<IDBKey> m_currentKey;
+};
+
+bool CursorImplCommon::continueFunction(const IDBKey* key)
+{
+    while (true) {
+        if (m_query.step() != SQLResultRow)
+            return false;
+
+        RefPtr<IDBKey> oldKey = m_currentKey;
+        loadCurrentRow();
+
+        // Skip if this entry has been deleted from the object store.
+        if (!currentRowExists())
+            continue;
+
+        // If a key was supplied, we must loop until we find that key (or hit the end).
+        if (key && !key->isEqual(m_currentKey.get()))
+            continue;
+
+        // If we don't have a uniqueness constraint, we can stop now.
+        if (!m_uniquenessConstraint)
+            break;
+        if (!m_currentKey->isEqual(oldKey.get()))
+            break;
+    }
+
+    return true;
+}
+
+class ObjectStoreCursorImpl : public CursorImplCommon {
+public:
+    ObjectStoreCursorImpl(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint)
+        : CursorImplCommon(sqliteDatabase, query, uniquenessConstraint)
+    {
+    }
+
+    // CursorImplCommon.
+    virtual String value() { return m_currentValue; }
+    virtual int64_t objectStoreDataId() { return m_currentId; }
+    virtual int64_t indexDataId() { ASSERT_NOT_REACHED(); return 0; }
+    virtual void loadCurrentRow();
+    virtual bool currentRowExists();
+
+private:
+    String m_currentValue;
+};
+
+void ObjectStoreCursorImpl::loadCurrentRow()
+{
+    m_currentId = m_query.getColumnInt64(0);
+    m_currentKey = keyFromQuery(m_query, 1);
+    m_currentValue = m_query.getColumnBlobAsString(4);
+}
+
+bool ObjectStoreCursorImpl::currentRowExists()
+{
+    String sql = "SELECT id FROM ObjectStoreData WHERE id = ?";
+    SQLiteStatement statement(m_db, sql);
+
+    bool ok = statement.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok);
+
+    statement.bindInt64(1, m_currentId);
+    return statement.step() == SQLResultRow;
+}
+
+class IndexKeyCursorImpl : public CursorImplCommon {
+public:
+    IndexKeyCursorImpl(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint)
+        : CursorImplCommon(sqliteDatabase, query, uniquenessConstraint)
+    {
+    }
+
+    // CursorImplCommon
+    virtual PassRefPtr<IDBKey> primaryKey() { return m_currentPrimaryKey; }
+    virtual String value() { ASSERT_NOT_REACHED(); return String(); }
+    virtual int64_t objectStoreDataId() { ASSERT_NOT_REACHED(); return 0; }
+    virtual int64_t indexDataId() { return m_currentId; }
+    virtual void loadCurrentRow();
+    virtual bool currentRowExists();
+
+private:
+    RefPtr<IDBKey> m_currentPrimaryKey;
+};
+
+void IndexKeyCursorImpl::loadCurrentRow()
+{
+    m_currentId = m_query.getColumnInt64(0);
+    m_currentKey = keyFromQuery(m_query, 1);
+    m_currentPrimaryKey = keyFromQuery(m_query, 4);
+}
+
+bool IndexKeyCursorImpl::currentRowExists()
+{
+    String sql = "SELECT id FROM IndexData WHERE id = ?";
+    SQLiteStatement statement(m_db, sql);
+
+    bool ok = statement.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok);
+
+    statement.bindInt64(1, m_currentId);
+    return statement.step() == SQLResultRow;
+}
+
+class IndexCursorImpl : public CursorImplCommon {
+public:
+    IndexCursorImpl(SQLiteDatabase& sqliteDatabase, String query, bool uniquenessConstraint)
+        : CursorImplCommon(sqliteDatabase, query, uniquenessConstraint)
+    {
+    }
+
+    // CursorImplCommon
+    virtual PassRefPtr<IDBKey> primaryKey() { return m_currentPrimaryKey; }
+    virtual String value() { return m_currentValue; }
+    virtual int64_t objectStoreDataId() { ASSERT_NOT_REACHED(); return 0; }
+    virtual int64_t indexDataId() { return m_currentId; }
+    virtual void loadCurrentRow();
+    virtual bool currentRowExists();
+
+private:
+    RefPtr<IDBKey> m_currentPrimaryKey;
+    String m_currentValue;
+};
+
+void IndexCursorImpl::loadCurrentRow()
+{
+    m_currentId = m_query.getColumnInt64(0);
+    m_currentKey = keyFromQuery(m_query, 1);
+    m_currentValue = m_query.getColumnBlobAsString(4);
+    m_currentPrimaryKey = keyFromQuery(m_query, 5);
+}
+
+bool IndexCursorImpl::currentRowExists()
+{
+    String sql = "SELECT id FROM IndexData WHERE id = ?";
+    SQLiteStatement statement(m_db, sql);
+
+    bool ok = statement.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok);
+
+    statement.bindInt64(1, m_currentId);
+    return statement.step() == SQLResultRow;
+}
+
+} // namespace
+
+PassRefPtr<IDBBackingStore::Cursor> IDBBackingStore::openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange* range, IDBCursor::Direction direction)
+{
+    bool lowerBound = range && range->lower();
+    bool upperBound = range && range->upper();
+
+    String sql = "SELECT id, keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE ";
+    if (lowerBound)
+        sql += lowerCursorWhereFragment(*range->lower(), range->lowerOpen() ? "<" : "<=");
+    if (upperBound)
+        sql += upperCursorWhereFragment(*range->upper(), range->upperOpen() ? "<" : "<=");
+    sql += "objectStoreId = ? ORDER BY ";
+
+    if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE)
+        sql += "keyString, keyDate, keyNumber";
+    else
+        sql += "keyString DESC, keyDate DESC, keyNumber DESC";
+
+    RefPtr<ObjectStoreCursorImpl> cursor = adoptRef(new ObjectStoreCursorImpl(m_db, sql, direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::PREV_NO_DUPLICATE));
+
+    bool ok = cursor->m_query.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+    int currentColumn = 1;
+    if (lowerBound)
+        currentColumn += bindKeyToQuery(cursor->m_query, currentColumn, *range->lower());
+    if (upperBound)
+        currentColumn += bindKeyToQuery(cursor->m_query, currentColumn, *range->upper());
+    cursor->m_query.bindInt64(currentColumn, objectStoreId);
+
+    if (cursor->m_query.step() != SQLResultRow)
+        return 0;
+
+    cursor->loadCurrentRow();
+    return cursor.release();
+}
+
+PassRefPtr<IDBBackingStore::Cursor> IDBBackingStore::openIndexKeyCursor(int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
+{
+    String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ")
+                 + ("ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ")
+                 + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id WHERE ";
+
+    bool lowerBound = range && range->lower();
+    bool upperBound = range && range->upper();
+
+    if (lowerBound)
+        sql += lowerCursorWhereFragment(*range->lower(), range->lowerOpen() ? "<" : "<=", "IndexData.");
+    if (upperBound)
+        sql += upperCursorWhereFragment(*range->upper(), range->upperOpen() ? "<" : "<=", "IndexData.");
+    sql += "IndexData.indexId = ? ORDER BY ";
+
+    if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE)
+        sql += "IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, IndexData.id";
+    else
+        sql += "IndexData.keyString DESC, IndexData.keyDate DESC, IndexData.keyNumber DESC, IndexData.id DESC";
+
+    RefPtr<IndexKeyCursorImpl> cursor = adoptRef(new IndexKeyCursorImpl(m_db, sql, direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::PREV_NO_DUPLICATE));
+
+    bool ok = cursor->m_query.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+    int indexColumn = 1;
+    if (lowerBound)
+        indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->lower());
+    if (upperBound)
+        indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->upper());
+    cursor->m_query.bindInt64(indexColumn, indexId);
+
+    if (cursor->m_query.step() != SQLResultRow)
+        return 0;
+
+    cursor->loadCurrentRow();
+    return cursor.release();
+}
+
+PassRefPtr<IDBBackingStore::Cursor> IDBBackingStore::openIndexCursor(int64_t indexId, const IDBKeyRange* range, IDBCursor::Direction direction)
+{
+    String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ")
+                 + ("ObjectStoreData.value, ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ")
+                 + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id WHERE ";
+
+    bool lowerBound = range && range->lower();
+    bool upperBound = range && range->upper();
+
+    if (lowerBound)
+        sql += lowerCursorWhereFragment(*range->lower(), range->lowerOpen() ? "<" : "<=", "IndexData.");
+    if (upperBound)
+        sql += upperCursorWhereFragment(*range->upper(), range->upperOpen() ? "<" : "<=", "IndexData.");
+    sql += "IndexData.indexId = ? ORDER BY ";
+
+    if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE)
+        sql += "IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, IndexData.id";
+    else
+        sql += "IndexData.keyString DESC, IndexData.keyDate DESC, IndexData.keyNumber DESC, IndexData.id DESC";
+
+    RefPtr<IndexCursorImpl> cursor = adoptRef(new IndexCursorImpl(m_db, sql, direction == IDBCursor::NEXT_NO_DUPLICATE || direction == IDBCursor::PREV_NO_DUPLICATE));
+
+    bool ok = cursor->m_query.prepare() == SQLResultOk;
+    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+
+    int indexColumn = 1;
+    if (lowerBound)
+        indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->lower());
+    if (upperBound)
+        indexColumn += bindKeyToQuery(cursor->m_query, indexColumn, *range->upper());
+    cursor->m_query.bindInt64(indexColumn, indexId);
+
+    if (cursor->m_query.step() != SQLResultRow)
+        return 0;
+
+    cursor->loadCurrentRow();
+    return cursor.release();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(INDEXED_DATABASE)
index 11a7e58..65dade0 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(INDEXED_DATABASE)
 
+#include "IDBCursor.h"
 #include "SQLiteDatabase.h"
 #include <wtf/PassRefPtr.h>
 #include <wtf/RefCounted.h>
@@ -38,6 +39,7 @@ namespace WebCore {
 
 class IDBFactoryBackendImpl;
 class IDBKey;
+class IDBKeyRange;
 class SecurityOrigin;
 
 class IDBBackingStore : public RefCounted<IDBBackingStore> {
@@ -54,7 +56,9 @@ public:
     String getObjectStoreRecord(int64_t objectStoreId, const IDBKey&);
     bool putObjectStoreRecord(int64_t objectStoreId, const IDBKey&, const String& value, int64_t& rowId, bool invalidRowId);
     void clearObjectStore(int64_t objectStoreId);
+    void deleteObjectStoreRecord(int64_t objectStoreId, int64_t objectStoreDataId);
     double nextAutoIncrementNumber(int64_t objectStoreId);
+    bool keyExistsInObjectStore(int64_t objectStoreId, const IDBKey&, int64_t& foundObjectStoreDataId);
 
     class ObjectStoreRecordCallback {
     public:
@@ -68,6 +72,24 @@ public:
     void deleteIndex(int64_t indexId);
     bool putIndexDataForRecord(int64_t indexId, const IDBKey&, int64_t objectStoreDataId);
     bool deleteIndexDataForRecord(int64_t objectStoreDataId);
+    String getObjectViaIndex(int64_t indexId, const IDBKey&);
+    PassRefPtr<IDBKey> getPrimaryKeyViaIndex(int64_t indexId, const IDBKey&);
+    bool keyExistsInIndex(int64_t indexId, const IDBKey&);
+
+    class Cursor : public RefCounted<Cursor> {
+    public:
+        virtual bool continueFunction(const IDBKey* = 0) = 0;
+        virtual PassRefPtr<IDBKey> key() = 0;
+        virtual PassRefPtr<IDBKey> primaryKey() = 0;
+        virtual String value() = 0;
+        virtual int64_t objectStoreDataId() = 0;
+        virtual int64_t indexDataId() = 0;
+        virtual ~Cursor() {};
+    };
+
+    PassRefPtr<Cursor> openObjectStoreCursor(int64_t objectStoreId, const IDBKeyRange*, IDBCursor::Direction);
+    PassRefPtr<Cursor> openIndexKeyCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction);
+    PassRefPtr<Cursor> openIndexCursor(int64_t indexId, const IDBKeyRange*, IDBCursor::Direction);
 
     SQLiteDatabase& db() { return m_db; }
 
index 842388b..6ae3616 100644 (file)
 #include "CrossThreadTask.h"
 #include "IDBBackingStore.h"
 #include "IDBCallbacks.h"
-#include "IDBDatabaseBackendImpl.h"
 #include "IDBDatabaseError.h"
 #include "IDBDatabaseException.h"
-#include "IDBIndexBackendImpl.h"
 #include "IDBKeyRange.h"
 #include "IDBObjectStoreBackendImpl.h"
 #include "IDBRequest.h"
 #include "IDBTransactionBackendInterface.h"
-#include "SQLiteDatabase.h"
-#include "SQLiteStatement.h"
 #include "SerializedScriptValue.h"
 
 namespace WebCore {
 
-IDBCursorBackendImpl::IDBCursorBackendImpl(IDBBackingStore* backingStore, PassRefPtr<IDBKeyRange> keyRange, IDBCursor::Direction direction, PassOwnPtr<SQLiteStatement> query, CursorType cursorType, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore)
-    : m_backingStore(backingStore)
-    , m_keyRange(keyRange)
+IDBCursorBackendImpl::IDBCursorBackendImpl(PassRefPtr<IDBBackingStore::Cursor> cursor, IDBCursor::Direction direction, CursorType cursorType, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore)
+    : m_cursor(cursor)
     , m_direction(direction)
-    , m_query(query)
     , m_cursorType(cursorType)
     , m_transaction(transaction)
     , m_objectStore(objectStore)
 {
-    loadCurrentRow();
 }
 
 IDBCursorBackendImpl::~IDBCursorBackendImpl()
@@ -68,28 +61,28 @@ unsigned short IDBCursorBackendImpl::direction() const
 
 PassRefPtr<IDBKey> IDBCursorBackendImpl::key() const
 {
-    return m_currentKey;
+    return m_cursor->key();
 }
 
 PassRefPtr<IDBKey> IDBCursorBackendImpl::primaryKey() const
 {
-    return m_currentPrimaryKey;
+    return m_cursor->primaryKey();
 }
 
 PassRefPtr<SerializedScriptValue> IDBCursorBackendImpl::value() const
 {
     ASSERT(m_cursorType != IndexKeyCursor);
-    return m_currentValue;
+    return SerializedScriptValue::createFromWire(m_cursor->value());
 }
 
 void IDBCursorBackendImpl::update(PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBCallbacks> callbacks, ExceptionCode& ec)
 {
-    if (!m_query || m_currentId == InvalidId || m_cursorType == IndexKeyCursor) {
+    if (!m_cursor || m_cursorType == IndexKeyCursor) {
         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
         return;
     }
 
-    m_objectStore->put(value, m_currentPrimaryKey, IDBObjectStoreBackendInterface::CursorUpdate, callbacks, m_transaction.get(), ec);
+    m_objectStore->put(value, m_cursor->primaryKey(), IDBObjectStoreBackendInterface::CursorUpdate, callbacks, m_transaction.get(), ec);
 }
 
 void IDBCursorBackendImpl::continueFunction(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec)
@@ -101,51 +94,17 @@ void IDBCursorBackendImpl::continueFunction(PassRefPtr<IDBKey> prpKey, PassRefPt
         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
 }
 
-bool IDBCursorBackendImpl::currentRowExists()
-{
-    String sql = m_cursorType == ObjectStoreCursor ? "SELECT id FROM ObjectStoreData WHERE id = ?" : "SELECT id FROM IndexData WHERE id = ?";
-    SQLiteStatement statement(m_backingStore->db(), sql);
-
-    bool ok = statement.prepare() == SQLResultOk;
-    ASSERT_UNUSED(ok, ok);
-
-    statement.bindInt64(1, m_currentId);
-    return statement.step() == SQLResultRow;
-}
-
 // IMPORTANT: If this ever 1) fires an 'error' event and 2) it's possible to fire another event afterwards,
 //            IDBRequest::hasPendingActivity() will need to be modified to handle this!!!
 void IDBCursorBackendImpl::continueFunctionInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl> prpCursor, PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> callbacks)
 {
     RefPtr<IDBCursorBackendImpl> cursor = prpCursor;
     RefPtr<IDBKey> key = prpKey;
-    while (true) {
-        if (!cursor->m_query || cursor->m_query->step() != SQLResultRow) {
-            cursor->m_query = 0;
-            cursor->m_currentId = InvalidId;
-            cursor->m_currentKey = 0;
-            cursor->m_currentPrimaryKey = 0;
-            cursor->m_currentValue = 0;
-            callbacks->onSuccess(SerializedScriptValue::nullValue());
-            return;
-        }
-
-        RefPtr<IDBKey> oldKey = cursor->m_currentKey;
-        cursor->loadCurrentRow();
-
-        // Skip if this entry has been deleted from the object store.
-        if (!cursor->currentRowExists())
-            continue;
-
-        // If a key was supplied, we must loop until we find that key (or hit the end).
-        if (key && !key->isEqual(cursor->m_currentKey.get()))
-            continue;
-
-        // If we don't have a uniqueness constraint, we can stop now.
-        if (cursor->m_direction == IDBCursor::NEXT || cursor->m_direction == IDBCursor::PREV)
-            break;
-        if (!cursor->m_currentKey->isEqual(oldKey.get()))
-            break;
+
+    if (!cursor->m_cursor || !cursor->m_cursor->continueFunction(key.get())) {
+        cursor->m_cursor = 0;
+        callbacks->onSuccess(SerializedScriptValue::nullValue());
+        return;
     }
 
     callbacks->onSuccess(cursor.get());
@@ -153,27 +112,12 @@ void IDBCursorBackendImpl::continueFunctionInternal(ScriptExecutionContext*, Pas
 
 void IDBCursorBackendImpl::deleteFunction(PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec)
 {
-    if (!m_query || m_currentId == InvalidId || m_cursorType == IndexKeyCursor) {
+    if (!m_cursor || m_cursorType == IndexKeyCursor) {
         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
         return;
     }
 
-    m_objectStore->deleteFunction(m_currentPrimaryKey, prpCallbacks, m_transaction.get(), ec);
-}
-
-
-void IDBCursorBackendImpl::loadCurrentRow()
-{
-    // The column numbers depend on the query in IDBObjectStoreBackendImpl::openCursorInternal and/or IDBIndexBackendImpl::openCursorInternal.
-    m_currentId = m_query->getColumnInt64(0);
-    m_currentKey = IDBKey::fromQuery(*m_query, 1);
-    m_currentValue = SerializedScriptValue::createFromWire(m_query->getColumnBlobAsString(4));
-    m_currentPrimaryKey = IDBKey::fromQuery(*m_query, 5);
-}
-
-SQLiteDatabase& IDBCursorBackendImpl::database() const
-{
-    return m_backingStore->db();
+    m_objectStore->deleteFunction(m_cursor->primaryKey(), prpCallbacks, m_transaction.get(), ec);
 }
 
 } // namespace WebCore
index a46f7bf..44e88bd 100644 (file)
@@ -29,6 +29,7 @@
 
 #if ENABLE(INDEXED_DATABASE)
 
+#include "IDBBackingStore.h"
 #include "IDBCursor.h"
 #include "IDBCursorBackendInterface.h"
 #include <wtf/OwnPtr.h>
@@ -49,9 +50,9 @@ class SerializedScriptValue;
 
 class IDBCursorBackendImpl : public IDBCursorBackendInterface {
 public:
-    static PassRefPtr<IDBCursorBackendImpl> create(IDBBackingStore* backingStore, PassRefPtr<IDBKeyRange> keyRange, IDBCursor::Direction direction, PassOwnPtr<SQLiteStatement> query, CursorType cursorType, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore)
+    static PassRefPtr<IDBCursorBackendImpl> create(PassRefPtr<IDBBackingStore::Cursor> cursor, IDBCursor::Direction direction, CursorType cursorType, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore)
     {
-        return adoptRef(new IDBCursorBackendImpl(backingStore, keyRange, direction, query, cursorType, transaction, objectStore));
+        return adoptRef(new IDBCursorBackendImpl(cursor, direction, cursorType, transaction, objectStore));
     }
     virtual ~IDBCursorBackendImpl();
 
@@ -64,28 +65,13 @@ public:
     virtual void deleteFunction(PassRefPtr<IDBCallbacks>, ExceptionCode&);
 
 private:
-    IDBCursorBackendImpl(IDBBackingStore*, PassRefPtr<IDBKeyRange>, IDBCursor::Direction, PassOwnPtr<SQLiteStatement> query, CursorType, IDBTransactionBackendInterface*, IDBObjectStoreBackendInterface*);
-
-    bool currentRowExists();
-    void loadCurrentRow();
-    SQLiteDatabase& database() const;
+    IDBCursorBackendImpl(PassRefPtr<IDBBackingStore::Cursor>, IDBCursor::Direction, CursorType, IDBTransactionBackendInterface*, IDBObjectStoreBackendInterface*);
 
     static void continueFunctionInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl>, PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>);
 
-    static const int64_t InvalidId = -1;
-
-    RefPtr<IDBBackingStore> m_backingStore;
-
-    RefPtr<IDBKeyRange> m_keyRange;
+    RefPtr<IDBBackingStore::Cursor> m_cursor;
     IDBCursor::Direction m_direction;
-    OwnPtr<SQLiteStatement> m_query;
     CursorType m_cursorType;
-    int64_t m_currentId;
-
-    RefPtr<IDBKey> m_currentKey;
-    RefPtr<IDBKey> m_currentPrimaryKey;
-    RefPtr<SerializedScriptValue> m_currentValue;
-
     RefPtr<IDBTransactionBackendInterface> m_transaction;
     RefPtr<IDBObjectStoreBackendInterface> m_objectStore;
 };
index dd9d953..62e5364 100644 (file)
@@ -37,8 +37,6 @@
 #include "IDBKey.h"
 #include "IDBKeyRange.h"
 #include "IDBObjectStoreBackendImpl.h"
-#include "SQLiteDatabase.h"
-#include "SQLiteStatement.h"
 
 namespace WebCore {
 
@@ -68,38 +66,24 @@ IDBIndexBackendImpl::~IDBIndexBackendImpl()
 
 void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBKeyRange> range, unsigned short untypedDirection, IDBCursorBackendInterface::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
 {
-    // Several files depend on this order of selects.
-    String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ")
-                 + ("ObjectStoreData.value, ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ")
-                 + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id WHERE ";
-
-    bool lowerBound = range && range->lower();
-    bool upperBound = range && range->upper();
-    
-    if (lowerBound)
-        sql += range->lower()->lowerCursorWhereFragment(range->lowerWhereClauseComparisonOperator(), "IndexData.");
-    if (upperBound)
-        sql += range->upper()->upperCursorWhereFragment(range->upperWhereClauseComparisonOperator(), "IndexData.");
-    sql += "IndexData.indexId = ? ORDER BY ";
-
     IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(untypedDirection);
-    if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE)
-        sql += "IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, IndexData.id";
-    else
-        sql += "IndexData.keyString DESC, IndexData.keyDate DESC, IndexData.keyNumber DESC, IndexData.id DESC";
-
-    OwnPtr<SQLiteStatement> query = adoptPtr(new SQLiteStatement(index->sqliteDatabase(), sql));
-    bool ok = query->prepare() == SQLResultOk;
-    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
-    int indexColumn = 1;
-    if (lowerBound)
-        indexColumn += range->lower()->bind(*query, indexColumn);
-    if (upperBound)
-        indexColumn += range->upper()->bind(*query, indexColumn);
-    query->bindInt64(indexColumn, index->id());
-
-    if (query->step() != SQLResultRow) {
+
+    RefPtr<IDBBackingStore::Cursor> backingStoreCursor;
+
+    switch (cursorType) {
+    case IDBCursorBackendInterface::IndexKeyCursor:
+        backingStoreCursor = index->m_backingStore->openIndexKeyCursor(index->id(), range.get(), direction);
+        break;
+    case IDBCursorBackendInterface::IndexCursor:
+        backingStoreCursor = index->m_backingStore->openIndexCursor(index->id(), range.get(), direction);
+        break;
+    case IDBCursorBackendInterface::ObjectStoreCursor:
+    case IDBCursorBackendInterface::InvalidCursorType:
+        ASSERT_NOT_REACHED();
+        break;
+    }
+
+    if (!backingStoreCursor) {
         callbacks->onSuccess(SerializedScriptValue::nullValue());
         return;
     }
@@ -108,7 +92,7 @@ void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr
     RefPtr<IDBObjectStoreBackendInterface> objectStore = transaction->objectStore(index->m_storeName, ec);
     ASSERT(objectStore && !ec);
 
-    RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(index->m_backingStore.get(), range, direction, query.release(), cursorType, transaction.get(), objectStore.get());
+    RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(backingStoreCursor.get(), direction, cursorType, transaction.get(), objectStore.get());
     callbacks->onSuccess(cursor.release());
 }
 
@@ -134,27 +118,22 @@ void IDBIndexBackendImpl::openKeyCursor(PassRefPtr<IDBKeyRange> prpKeyRange, uns
 
 void IDBIndexBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBKey> key, bool getObject, PassRefPtr<IDBCallbacks> callbacks)
 {
-    String sql = String("SELECT ")
-                 + (getObject ? "ObjectStoreData.value " : "ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ")
-                 + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id "
-                 + "WHERE IndexData.indexId = ?  AND  " + key->whereSyntax("IndexData.")
-                 + "ORDER BY IndexData.id LIMIT 1"; // Order by insertion order when all else fails.
-    SQLiteStatement query(index->sqliteDatabase(), sql);
-    bool ok = query.prepare() == SQLResultOk;
-    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
-    query.bindInt64(1, index->id());
-    key->bind(query, 2);
-    if (query.step() != SQLResultRow) {
-        callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index."));
-        return;
+    // FIXME: Split getInternal into two functions, getting rid off |getObject|.
+    if (getObject) {
+        String value = index->m_backingStore->getObjectViaIndex(index->id(), *key);
+        if (value.isNull()) {
+            callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index."));
+            return;
+        }
+        callbacks->onSuccess(SerializedScriptValue::createFromWire(value));
+    } else {
+        RefPtr<IDBKey> keyResult = index->m_backingStore->getPrimaryKeyViaIndex(index->id(), *key);
+        if (!keyResult) {
+            callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index."));
+            return;
+        }
+        callbacks->onSuccess(keyResult.get());
     }
-
-    if (getObject)
-        callbacks->onSuccess(SerializedScriptValue::createFromWire(query.getColumnText(0)));
-    else
-        callbacks->onSuccess(IDBKey::fromQuery(query, 0));
-    ASSERT(query.step() != SQLResultRow);
 }
 
 void IDBIndexBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
@@ -175,34 +154,12 @@ void IDBIndexBackendImpl::getKey(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallba
         ec = IDBDatabaseException::NOT_ALLOWED_ERR;
 }
 
-static String whereClause(IDBKey* key)
-{
-    return "WHERE indexId = ?  AND  " + key->whereSyntax();
-}
-
-static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key)
-{
-    query.bindInt64(1, id);
-    key->bind(query, 2);
-}
-
 bool IDBIndexBackendImpl::addingKeyAllowed(IDBKey* key)
 {
     if (!m_unique)
         return true;
 
-    SQLiteStatement query(sqliteDatabase(), "SELECT id FROM IndexData " + whereClause(key));
-    bool ok = query.prepare() == SQLResultOk;
-    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-    bindWhereClause(query, m_id, key);
-    bool existingValue = query.step() == SQLResultRow;
-
-    return !existingValue;
-}
-
-SQLiteDatabase& IDBIndexBackendImpl::sqliteDatabase() const
-{
-    return m_backingStore->db();
+    return !m_backingStore->keyExistsInIndex(m_id, *key);
 }
 
 } // namespace WebCore
index 2420a6d..ca380cf 100644 (file)
@@ -76,8 +76,6 @@ private:
     IDBIndexBackendImpl(IDBBackingStore*, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique);
     IDBIndexBackendImpl(IDBBackingStore*, const String& name, const String& storeName, const String& keyPath, bool unique);
 
-    SQLiteDatabase& sqliteDatabase() const;
-
     static void openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKeyRange>, unsigned short direction, IDBCursorBackendInterface::CursorType, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>);
     static void getInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKey>, bool getObject, PassRefPtr<IDBCallbacks>);
 
index 5f45543..bcbf3dd 100644 (file)
@@ -28,9 +28,6 @@
 
 #if ENABLE(INDEXED_DATABASE)
 
-#include "SQLiteStatement.h"
-#include "SerializedScriptValue.h"
-
 namespace WebCore {
 
 IDBKey::IDBKey()
@@ -42,24 +39,7 @@ IDBKey::~IDBKey()
 {
 }
 
-PassRefPtr<IDBKey> IDBKey::fromQuery(SQLiteStatement& query, int baseColumn)
-{
-    if (query.columnCount() <= baseColumn)
-        return 0;
-
-    if (!query.isColumnNull(baseColumn))
-        return IDBKey::createString(query.getColumnText(baseColumn));
-
-    if (!query.isColumnNull(baseColumn + 1))
-        return IDBKey::createDate(query.getColumnDouble(baseColumn + 1));
-
-    if (!query.isColumnNull(baseColumn + 2))
-        return IDBKey::createNumber(query.getColumnDouble(baseColumn + 2));
-
-    return IDBKey::createNull();
-}
-
-bool IDBKey::isEqual(IDBKey* other)
+bool IDBKey::isEqual(IDBKey* other) const
 {
     if (!other || other->m_type != m_type)
         return false;
@@ -79,108 +59,6 @@ bool IDBKey::isEqual(IDBKey* other)
     return false;
 }
 
-String IDBKey::whereSyntax(String qualifiedTableName) const
-{
-    switch (m_type) {
-    case IDBKey::StringType:
-        return qualifiedTableName + "keyString = ?  AND  " + qualifiedTableName + "keyDate IS NULL  AND  " + qualifiedTableName + "keyNumber IS NULL  ";
-    case IDBKey::NumberType:
-        return qualifiedTableName + "keyString IS NULL  AND  " + qualifiedTableName + "keyDate IS NULL  AND  " + qualifiedTableName + "keyNumber = ?  ";
-    case IDBKey::DateType:
-        return qualifiedTableName + "keyString IS NULL  AND  " + qualifiedTableName + "keyDate = ?  AND  " + qualifiedTableName + "keyNumber IS NULL  ";
-    case IDBKey::NullType:
-        return qualifiedTableName + "keyString IS NULL  AND  " + qualifiedTableName + "keyDate IS NULL  AND  " + qualifiedTableName + "keyNumber IS NULL  ";
-    }
-
-    ASSERT_NOT_REACHED();
-    return "";
-}
-
-String IDBKey::lowerCursorWhereFragment(String comparisonOperator, String qualifiedTableName)
-{
-    switch (m_type) {
-    case StringType:
-        return "? " + comparisonOperator + " " + qualifiedTableName + "keyString  AND  ";
-    case DateType:
-        return "(? " + comparisonOperator + " " + qualifiedTableName + "keyDate  OR NOT " + qualifiedTableName + "keyString IS NULL)  AND  ";
-    case NumberType:
-        return "(? " + comparisonOperator + " " + qualifiedTableName + "keyNumber  OR  NOT " + qualifiedTableName + "keyString IS NULL  OR  NOT " + qualifiedTableName + "keyDate IS NULL)  AND  ";
-    case NullType:
-        if (comparisonOperator == "<")
-            return "NOT(" + qualifiedTableName + "keyString IS NULL  AND  " + qualifiedTableName + "keyDate IS NULL  AND  " + qualifiedTableName + "keyNumber IS NULL)  AND  ";
-        return ""; // If it's =, the upper bound half will do the constraining. If it's <=, then that's a no-op.
-    }
-    ASSERT_NOT_REACHED();
-    return "";
-}
-
-String IDBKey::upperCursorWhereFragment(String comparisonOperator, String qualifiedTableName)
-{
-    switch (m_type) {
-    case StringType:
-        return "(" + qualifiedTableName + "keyString " + comparisonOperator + " ?  OR  " + qualifiedTableName + "keyString IS NULL)  AND  ";
-    case DateType:
-        return "(" + qualifiedTableName + "keyDate " + comparisonOperator + " ? OR " + qualifiedTableName + "keyDate IS NULL)  AND  " + qualifiedTableName + "keyString IS NULL  AND  ";
-    case NumberType:
-        return "(" + qualifiedTableName + "keyNumber " + comparisonOperator + " ? OR " + qualifiedTableName + "keyNumber IS NULL)  AND  " + qualifiedTableName + "keyString IS NULL  AND  " + qualifiedTableName + "keyDate IS NULL  AND  ";
-    case NullType:
-        if (comparisonOperator == "<")
-            return "0 != 0  AND  ";
-        return qualifiedTableName + "keyString IS NULL  AND  " + qualifiedTableName + "keyDate IS NULL  AND  " + qualifiedTableName + "keyNumber IS NULL  AND  ";
-    }
-    ASSERT_NOT_REACHED();
-    return "";
-}
-
-// Returns the number of items bound.
-int IDBKey::bind(SQLiteStatement& query, int column) const
-{
-    switch (m_type) {
-    case IDBKey::StringType:
-        query.bindText(column, m_string);
-        return 1;
-    case IDBKey::DateType:
-        query.bindDouble(column, m_date);
-        return 1;
-    case IDBKey::NumberType:
-        query.bindDouble(column, m_number);
-        return 1;
-    case IDBKey::NullType:
-        return 0;
-    }
-
-    ASSERT_NOT_REACHED();
-    return 0;
-}
-
-void IDBKey::bindWithNulls(SQLiteStatement& query, int baseColumn) const
-{
-    switch (m_type) {
-    case IDBKey::StringType:
-        query.bindText(baseColumn + 0, m_string);
-        query.bindNull(baseColumn + 1);
-        query.bindNull(baseColumn + 2);
-        break;
-    case IDBKey::DateType:
-        query.bindNull(baseColumn + 0);
-        query.bindDouble(baseColumn + 1, m_date);
-        query.bindNull(baseColumn + 2);
-        break;
-    case IDBKey::NumberType:
-        query.bindNull(baseColumn + 0);
-        query.bindNull(baseColumn + 1);
-        query.bindDouble(baseColumn + 2, m_number);
-        break;
-    case IDBKey::NullType:
-        query.bindNull(baseColumn + 0);
-        query.bindNull(baseColumn + 1);
-        query.bindNull(baseColumn + 2);
-        break;
-    default:
-        ASSERT_NOT_REACHED();
-    }
-}
-
 } // namespace WebCore
 
 #endif
index 9118743..effb012 100644 (file)
@@ -99,14 +99,7 @@ public:
         return m_number;
     }
 
-    static PassRefPtr<IDBKey> fromQuery(SQLiteStatement& query, int baseColumn);
-
-    bool isEqual(IDBKey* other);
-    String whereSyntax(String qualifiedTableName = "") const;
-    String lowerCursorWhereFragment(String comparisonOperator, String qualifiedTableName = "");
-    String upperCursorWhereFragment(String comparisonOperator, String qualifiedTableName = "");
-    int bind(SQLiteStatement& query, int column) const;
-    void bindWithNulls(SQLiteStatement& query, int baseColumn) const;
+    bool isEqual(IDBKey* other) const;
 
     using ThreadSafeShared<IDBKey>::ref;
     using ThreadSafeShared<IDBKey>::deref;
index 3464785..4f4144b 100644 (file)
@@ -61,22 +61,6 @@ PassRefPtr<IDBKeyRange> IDBKeyRange::bound(PassRefPtr<IDBKey> lower, PassRefPtr<
     return IDBKeyRange::create(lower, upper, lowerOpen, upperOpen);
 }
 
-String IDBKeyRange::lowerWhereClauseComparisonOperator() const
-{
-    ASSERT(m_lower);
-    if (m_lowerOpen)
-        return "<";
-    return "<=";
-}
-
-String IDBKeyRange::upperWhereClauseComparisonOperator() const
-{
-    ASSERT(m_upper);
-    if (m_upperOpen)
-        return "<";
-    return "<=";
-}
-
 } // namespace WebCore
 
 #endif // ENABLE(INDEXED_DATABASE)
index 5a68b10..636a5c1 100644 (file)
@@ -43,15 +43,11 @@ public:
     }
     ~IDBKeyRange() { }
 
-
     PassRefPtr<IDBKey> lower() const { return m_lower; }
     PassRefPtr<IDBKey> upper() const { return m_upper; }
     bool lowerOpen() const { return m_lowerOpen; }
     bool upperOpen() const { return m_upperOpen; }
 
-    String lowerWhereClauseComparisonOperator() const;
-    String upperWhereClauseComparisonOperator() const;
-
     static PassRefPtr<IDBKeyRange> only(PassRefPtr<IDBKey> value);
     static PassRefPtr<IDBKeyRange> lowerBound(PassRefPtr<IDBKey> bound, bool open = false);
     static PassRefPtr<IDBKeyRange> upperBound(PassRefPtr<IDBKey> bound, bool open = false);
index ec7007e..0433ed7 100644 (file)
@@ -43,9 +43,6 @@
 #include "IDBKeyRange.h"
 #include "IDBTransactionBackendInterface.h"
 #include "ScriptExecutionContext.h"
-#include "SQLiteDatabase.h"
-#include "SQLiteStatement.h"
-#include "SQLiteTransaction.h"
 
 namespace WebCore {
 
@@ -82,17 +79,6 @@ PassRefPtr<DOMStringList> IDBObjectStoreBackendImpl::indexNames() const
     return indexNames.release();
 }
 
-static String whereClause(IDBKey* key)
-{
-    return "WHERE objectStoreId = ?  AND  " + key->whereSyntax();
-}
-
-static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key)
-{
-    query.bindInt64(1, id);
-    key->bind(query, 2);
-}
-
 void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec)
 {
     RefPtr<IDBObjectStoreBackendImpl> objectStore = this;
@@ -241,12 +227,9 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<
         indexKeys.append(key.release());
     }
 
-    SQLiteStatement getQuery(objectStore->sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key.get()));
-    bool ok = getQuery.prepare() == SQLResultOk;
-    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
+    int64_t dataRowId = InvalidId;
+    bool isExistingValue = objectStore->m_backingStore->keyExistsInObjectStore(objectStore->id(), *key, dataRowId);
 
-    bindWhereClause(getQuery, objectStore->id(), key.get());
-    bool isExistingValue = getQuery.step() == SQLResultRow;
     if (putMode == AddOnly && isExistingValue) {
         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store."));
         return;
@@ -254,7 +237,6 @@ void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<
 
     // Before this point, don't do any mutation.  After this point, rollback the transaction in case of error.
 
-    int64_t dataRowId = isExistingValue ? getQuery.getColumnInt(0) : InvalidId;
     if (!objectStore->m_backingStore->putObjectStoreRecord(objectStore->id(), *key, value->toWireString(), dataRowId, dataRowId == InvalidId)) {
         // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors.
         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
@@ -301,35 +283,13 @@ void IDBObjectStoreBackendImpl::deleteFunction(PassRefPtr<IDBKey> prpKey, PassRe
 
 void IDBObjectStoreBackendImpl::deleteInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks)
 {
-    SQLiteStatement idQuery(objectStore->sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key.get()));
-    bool ok = idQuery.prepare() == SQLResultOk;
-    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-    bindWhereClause(idQuery, objectStore->id(), key.get());
-    if (idQuery.step() != SQLResultRow) {
+    int64_t id;
+    if (!objectStore->m_backingStore->keyExistsInObjectStore(objectStore->id(), *key, id)) {
         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the object store."));
         return;
     }
 
-    int64_t id = idQuery.getColumnInt64(0);
-
-    SQLiteStatement osQuery(objectStore->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE id = ?");
-    ok = osQuery.prepare() == SQLResultOk;
-    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
-    osQuery.bindInt64(1, id);
-
-    ok = osQuery.step() == SQLResultDone;
-    ASSERT_UNUSED(ok, ok);
-
-    SQLiteStatement indexQuery(objectStore->sqliteDatabase(), "DELETE FROM IndexData WHERE objectStoreDataId = ?");
-    ok = indexQuery.prepare() == SQLResultOk;
-    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
-    indexQuery.bindInt64(1, id);
-
-    ok = indexQuery.step() == SQLResultDone;
-    ASSERT_UNUSED(ok, ok);
-
+    objectStore->m_backingStore->deleteObjectStoreRecord(objectStore->id(), id);
     callbacks->onSuccess(SerializedScriptValue::nullValue());
 }
 
@@ -484,40 +444,15 @@ void IDBObjectStoreBackendImpl::openCursor(PassRefPtr<IDBKeyRange> prpRange, uns
 
 void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> range, unsigned short tmpDirection, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
 {
-    bool lowerBound = range && range->lower();
-    bool upperBound = range && range->upper();
-
-    // Several files depend on this order of selects.
-    String sql = "SELECT id, keyString, keyDate, keyNumber, value, keyString, keyDate, keyNumber FROM ObjectStoreData WHERE ";
-    if (lowerBound)
-        sql += range->lower()->lowerCursorWhereFragment(range->lowerWhereClauseComparisonOperator());
-    if (upperBound)
-        sql += range->upper()->upperCursorWhereFragment(range->upperWhereClauseComparisonOperator());
-    sql += "objectStoreId = ? ORDER BY ";
-
     IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(tmpDirection);
-    if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE)
-        sql += "keyString, keyDate, keyNumber";
-    else
-        sql += "keyString DESC, keyDate DESC, keyNumber DESC";
-
-    OwnPtr<SQLiteStatement> query = adoptPtr(new SQLiteStatement(objectStore->sqliteDatabase(), sql));
-    bool ok = query->prepare() == SQLResultOk;
-    ASSERT_UNUSED(ok, ok); // FIXME: Better error handling?
-
-    int currentColumn = 1;
-    if (lowerBound)
-        currentColumn += range->lower()->bind(*query, currentColumn);
-    if (upperBound)
-        currentColumn += range->upper()->bind(*query, currentColumn);
-    query->bindInt64(currentColumn, objectStore->id());
-
-    if (query->step() != SQLResultRow) {
+
+    RefPtr<IDBBackingStore::Cursor> backingStoreCursor = objectStore->m_backingStore->openObjectStoreCursor(objectStore->id(), range.get(), direction);
+    if (!backingStoreCursor) {
         callbacks->onSuccess(SerializedScriptValue::nullValue());
         return;
     }
 
-    RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(objectStore->m_backingStore.get(), range, direction, query.release(), IDBCursorBackendInterface::ObjectStoreCursor, transaction.get(), objectStore.get());
+    RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(backingStoreCursor.release(), direction, IDBCursorBackendInterface::ObjectStoreCursor, transaction.get(), objectStore.get());
     callbacks->onSuccess(cursor.release());
 }
 
@@ -537,11 +472,6 @@ void IDBObjectStoreBackendImpl::loadIndexes()
         m_indexes.set(names[i], IDBIndexBackendImpl::create(m_backingStore.get(), ids[i], names[i], m_name, keyPaths[i], uniqueFlags[i]));
 }
 
-SQLiteDatabase& IDBObjectStoreBackendImpl::sqliteDatabase() const 
-{
-    return m_backingStore->db();
-}
-
 void IDBObjectStoreBackendImpl::removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index)
 {
     ASSERT(objectStore->m_indexes.contains(index->name()));
index dc0a9a7..4fddabd 100644 (file)
@@ -82,7 +82,6 @@ private:
     IDBObjectStoreBackendImpl(IDBBackingStore*, const String& name, const String& keyPath, bool autoIncrement);
 
     void loadIndexes();
-    SQLiteDatabase& sqliteDatabase() const;
     PassRefPtr<IDBKey> genAutoIncrementKey();
     void resetAutoIncrementKeyCache() { m_autoIncrementNumber = -1; }
     static PassRefPtr<IDBKey> selectKeyForPut(IDBObjectStoreBackendImpl*, IDBKey*, PutMode, IDBCallbacks*, RefPtr<SerializedScriptValue>&);