IDB: Key generator support
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Jan 2014 08:19:40 +0000 (08:19 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Jan 2014 08:19:40 +0000 (08:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=127871

Reviewed by Tim Horton.

* DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp:
(WebKit::UniqueIDBDatabase::putRecordInBackingStore): Update for storing/retrieving integers instead of IDBKeys.
* DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h:

* DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp:
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::createAndPopulateInitialMetadata): Create a keygen table.
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::createObjectStore): Put a record in the keygen table for
  this object store if necessary.
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::deleteObjectStore): Delete the entry in the keygen table.
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::generateKeyNumber): Pull the current number from the table.
(WebKit::UniqueIDBDatabaseBackingStoreSQLite::updateKeyGeneratorNumber): Update the number in the table.
* DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h:

* WebProcess/Databases/IndexedDB/WebIDBServerConnection.cpp:
(WebKit::WebIDBServerConnection::put): Null keys are acceptable for autoIncrement object stores.

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

Source/WebKit2/ChangeLog
Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp
Source/WebKit2/DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h
Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp
Source/WebKit2/DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h
Source/WebKit2/WebProcess/Databases/IndexedDB/WebIDBServerConnection.cpp

index 5e3cb9c..ef3aa0c 100644 (file)
@@ -1,5 +1,28 @@
 2014-01-30  Brady Eidson  <beidson@apple.com>
 
+        IDB: Key generator support
+        https://bugs.webkit.org/show_bug.cgi?id=127871
+
+        Reviewed by Tim Horton.
+
+        * DatabaseProcess/IndexedDB/UniqueIDBDatabase.cpp:
+        (WebKit::UniqueIDBDatabase::putRecordInBackingStore): Update for storing/retrieving integers instead of IDBKeys.
+        * DatabaseProcess/IndexedDB/UniqueIDBDatabaseBackingStore.h:
+
+        * DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.cpp:
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::createAndPopulateInitialMetadata): Create a keygen table.
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::createObjectStore): Put a record in the keygen table for
+          this object store if necessary.
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::deleteObjectStore): Delete the entry in the keygen table.
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::generateKeyNumber): Pull the current number from the table.
+        (WebKit::UniqueIDBDatabaseBackingStoreSQLite::updateKeyGeneratorNumber): Update the number in the table.
+        * DatabaseProcess/IndexedDB/sqlite/UniqueIDBDatabaseBackingStoreSQLite.h:
+
+        * WebProcess/Databases/IndexedDB/WebIDBServerConnection.cpp:
+        (WebKit::WebIDBServerConnection::put): Null keys are acceptable for autoIncrement object stores.
+
+2014-01-30  Brady Eidson  <beidson@apple.com>
+
         IDB: Implement IDBObjectStore.count()
         https://bugs.webkit.org/show_bug.cgi?id=127888
 
index 38ac36e..5790a7f 100644 (file)
@@ -753,9 +753,14 @@ void UniqueIDBDatabase::putRecordInBackingStore(uint64_t requestID, const IDBIde
 
     bool keyWasGenerated = false;
     RefPtr<IDBKey> key;
+    int64_t keyNumber = 0;
 
     if (putMode != IDBDatabaseBackend::CursorUpdate && objectStoreMetadata.autoIncrement && keyData.isNull) {
-        key = m_backingStore->generateKey(transaction, objectStoreMetadata.id);
+        if (!m_backingStore->generateKeyNumber(transaction, objectStoreMetadata.id, keyNumber)) {
+            postMainThreadTask(createAsyncTask(*this, &UniqueIDBDatabase::didPutRecordInBackingStore, requestID, IDBKeyData(), IDBDatabaseException::UnknownError, ASCIILiteral("Internal backing store error checking for key existence")));
+            return;
+        }
+        key = IDBKey::createNumber(keyNumber);
         keyWasGenerated = true;
     } else
         key = keyData.maybeCreateIDBKey();
@@ -785,7 +790,7 @@ void UniqueIDBDatabase::putRecordInBackingStore(uint64_t requestID, const IDBIde
     // FIXME: The LevelDB port updates index keys here. Necessary?
 
     if (putMode != IDBDatabaseBackend::CursorUpdate && objectStoreMetadata.autoIncrement && key->type() == IDBKey::NumberType) {
-        if (!m_backingStore->updateKeyGenerator(transaction, objectStoreMetadata.id, *key, keyWasGenerated)) {
+        if (!m_backingStore->updateKeyGeneratorNumber(transaction, objectStoreMetadata.id, keyNumber, keyWasGenerated)) {
             postMainThreadTask(createAsyncTask(*this, &UniqueIDBDatabase::didPutRecordInBackingStore, requestID, IDBKeyData(), IDBDatabaseException::UnknownError, ASCIILiteral("Internal backing store error updating key generator")));
             return;
         }
index 0350001..435fb09 100644 (file)
@@ -63,10 +63,11 @@ public:
     virtual bool createIndex(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBIndexMetadata&) = 0;
     virtual bool deleteIndex(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID) = 0;
 
-    virtual PassRefPtr<WebCore::IDBKey> generateKey(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID) = 0;
+    virtual bool generateKeyNumber(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t& generatedKey) = 0;
+    virtual bool updateKeyGeneratorNumber(const IDBIdentifier& transactionIdentifier, int64_t objectStoreId, int64_t keyNumber, bool checkCurrent) = 0;
+
     virtual bool keyExistsInObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKey&, bool& keyExists) = 0;
     virtual bool putRecord(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKey&, const uint8_t* valueBuffer, size_t valueSize) = 0;
-    virtual bool updateKeyGenerator(const IDBIdentifier& transactionIdentifier, int64_t objectStoreId, const WebCore::IDBKey&, bool checkCurrent) = 0;
 
     virtual bool getKeyRecordFromObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKey&, RefPtr<WebCore::SharedBuffer>& result) = 0;
     virtual bool getKeyRangeRecordFromObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKeyRange&, RefPtr<WebCore::SharedBuffer>& result, RefPtr<WebCore::IDBKey>& resultKey) = 0;
index 75223dd..f2e23f4 100644 (file)
@@ -98,6 +98,12 @@ std::unique_ptr<IDBDatabaseMetadata> UniqueIDBDatabaseBackingStoreSQLite::create
         return nullptr;
     }
 
+    if (!m_sqliteDB->executeCommand("CREATE TABLE KeyGenerators (objectStoreID INTEGER NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, currentKey INTEGER NOT NULL ON CONFLICT FAIL);")) {
+        LOG_ERROR("Could not create KeyGenerators table in database (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+        m_sqliteDB = nullptr;
+        return nullptr;
+    }
+
     {
         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO IDBDatabaseInfo VALUES ('MetadataVersion', ?);"));
         if (sql.prepare() != SQLResultOk
@@ -381,16 +387,28 @@ bool UniqueIDBDatabaseBackingStoreSQLite::createObjectStore(const IDBIdentifier&
         return false;
     }
 
-    SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO ObjectStoreInfo VALUES (?, ?, ?, ?, ?);"));
-    if (sql.prepare() != SQLResultOk
-        || sql.bindInt64(1, metadata.id) != SQLResultOk
-        || sql.bindText(2, metadata.name) != SQLResultOk
-        || sql.bindBlob(3, keyPathBlob->data(), keyPathBlob->size()) != SQLResultOk
-        || sql.bindInt(4, metadata.autoIncrement) != SQLResultOk
-        || sql.bindInt64(5, metadata.maxIndexId) != SQLResultOk
-        || sql.step() != SQLResultDone) {
-        LOG_ERROR("Could not add object store '%s' to ObjectStoreInfo table (%i) - %s", metadata.name.utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
-        return false;
+    {
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO ObjectStoreInfo VALUES (?, ?, ?, ?, ?);"));
+        if (sql.prepare() != SQLResultOk
+            || sql.bindInt64(1, metadata.id) != SQLResultOk
+            || sql.bindText(2, metadata.name) != SQLResultOk
+            || sql.bindBlob(3, keyPathBlob->data(), keyPathBlob->size()) != SQLResultOk
+            || sql.bindInt(4, metadata.autoIncrement) != SQLResultOk
+            || sql.bindInt64(5, metadata.maxIndexId) != SQLResultOk
+            || sql.step() != SQLResultDone) {
+            LOG_ERROR("Could not add object store '%s' to ObjectStoreInfo table (%i) - %s", metadata.name.utf8().data(), m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return false;
+        }
+    }
+
+    {
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO KeyGenerators VALUES (?, 0);"));
+        if (sql.prepare() != SQLResultOk
+            || sql.bindInt64(1, metadata.id) != SQLResultOk
+            || sql.step() != SQLResultDone) {
+            LOG_ERROR("Could not seed initial key generator value for ObjectStoreInfo table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return false;
+        }
     }
 
     return true;
@@ -423,6 +441,17 @@ bool UniqueIDBDatabaseBackingStoreSQLite::deleteObjectStore(const IDBIdentifier&
         }
     }
 
+    // Delete the ObjectStore's key generator record if there is one.
+    {
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("DELETE FROM KeyGenerators WHERE objectStoreID = ?;"));
+        if (sql.prepare() != SQLResultOk
+            || sql.bindInt64(1, objectStoreID) != SQLResultOk
+            || sql.step() != SQLResultDone) {
+            LOG_ERROR("Could not delete object store from KeyGenerators table (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return false;
+        }
+    }
+
     // Delete all associated Index records
     {
         Vector<int64_t> indexIDs;
@@ -554,10 +583,77 @@ bool UniqueIDBDatabaseBackingStoreSQLite::deleteIndex(const IDBIdentifier& trans
     return true;
 }
 
-PassRefPtr<IDBKey> UniqueIDBDatabaseBackingStoreSQLite::generateKey(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID)
+bool UniqueIDBDatabaseBackingStoreSQLite::generateKeyNumber(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t& generatedKey)
 {
-    // FIXME (<rdar://problem/15877909>): Implement
-    return nullptr;
+    ASSERT(!isMainThread());
+    ASSERT(m_sqliteDB);
+    ASSERT(m_sqliteDB->isOpen());
+
+    // The IndexedDatabase spec defines the max key generator value as 2^53;
+    static const int64_t maxGeneratorValue = 9007199254740992LL;
+
+    SQLiteIDBTransaction* transaction = m_transactions.get(transactionIdentifier);
+    if (!transaction || !transaction->inProgress()) {
+        LOG_ERROR("Attempt to generate key in database without an established, in-progress transaction");
+        return false;
+    }
+    if (transaction->mode() == IndexedDB::TransactionMode::ReadOnly) {
+        LOG_ERROR("Attempt to generate key in database during read-only transaction");
+        return false;
+    }
+
+    int64_t currentValue;
+    {
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT currentKey FROM KeyGenerators WHERE objectStoreID = ?;"));
+        if (sql.prepare() != SQLResultOk
+            || sql.bindInt64(1, objectStoreID) != SQLResultOk) {
+            LOG_ERROR("Could not delete index id %lli from IndexInfo table (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return false;
+        }
+        int result = sql.step();
+        if (result != SQLResultRow) {
+            LOG_ERROR("Could not retreive key generator value for object store, but it should be there.");
+            return false;
+        }
+
+        currentValue = sql.getColumnInt64(0);
+    }
+
+    if (currentValue < 0 || currentValue > maxGeneratorValue)
+        return false;
+
+    generatedKey = currentValue + 1;
+    return true;
+}
+
+bool UniqueIDBDatabaseBackingStoreSQLite::updateKeyGeneratorNumber(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t keyNumber, bool)
+{
+    ASSERT(!isMainThread());
+    ASSERT(m_sqliteDB);
+    ASSERT(m_sqliteDB->isOpen());
+
+    SQLiteIDBTransaction* transaction = m_transactions.get(transactionIdentifier);
+    if (!transaction || !transaction->inProgress()) {
+        LOG_ERROR("Attempt to update key generator in database without an established, in-progress transaction");
+        return false;
+    }
+    if (transaction->mode() == IndexedDB::TransactionMode::ReadOnly) {
+        LOG_ERROR("Attempt to update key generator in database during read-only transaction");
+        return false;
+    }
+
+    {
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("INSERT INTO KeyGenerators VALUES (?, ?);"));
+        if (sql.prepare() != SQLResultOk
+            || sql.bindInt64(1, objectStoreID) != SQLResultOk
+            || sql.bindInt64(2, keyNumber) != SQLResultOk
+            || sql.step() != SQLResultDone) {
+            LOG_ERROR("Could not update key generator value (%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return false;
+        }
+    }
+
+    return true;
 }
 
 bool UniqueIDBDatabaseBackingStoreSQLite::keyExistsInObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const IDBKey&, bool& keyExists)
@@ -602,12 +698,6 @@ bool UniqueIDBDatabaseBackingStoreSQLite::putRecord(const IDBIdentifier& transac
     return true;
 }
 
-bool UniqueIDBDatabaseBackingStoreSQLite::updateKeyGenerator(const IDBIdentifier& transactionIdentifier, int64_t objectStoreId, const IDBKey&, bool checkCurrent)
-{
-    // FIXME (<rdar://problem/15877909>): Implement
-    return false;
-}
-
 bool UniqueIDBDatabaseBackingStoreSQLite::getKeyRecordFromObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const IDBKey& key, RefPtr<SharedBuffer>& result)
 {
     ASSERT(!isMainThread());
index 261d16e..5c62f7b 100644 (file)
@@ -67,10 +67,11 @@ public:
     virtual bool createIndex(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBIndexMetadata&) override;
     virtual bool deleteIndex(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t indexID) override;
 
-    virtual PassRefPtr<WebCore::IDBKey> generateKey(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID) override;
+    virtual bool generateKeyNumber(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, int64_t& generatedKey) override;
+    virtual bool updateKeyGeneratorNumber(const IDBIdentifier& transactionIdentifier, int64_t objectStoreId, int64_t keyNumber, bool checkCurrent) override;
+
     virtual bool keyExistsInObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKey&, bool& keyExists) override;
     virtual bool putRecord(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKey&, const uint8_t* valueBuffer, size_t valueSize) override;
-    virtual bool updateKeyGenerator(const IDBIdentifier& transactionIdentifier, int64_t objectStoreId, const WebCore::IDBKey&, bool checkCurrent) override;
 
     virtual bool getKeyRecordFromObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKey&, RefPtr<WebCore::SharedBuffer>& result) override;
     virtual bool getKeyRangeRecordFromObjectStore(const IDBIdentifier& transactionIdentifier, int64_t objectStoreID, const WebCore::IDBKeyRange&, RefPtr<WebCore::SharedBuffer>& result, RefPtr<WebCore::IDBKey>& resultKey) override;
index e0efdcb..ada805a 100644 (file)
@@ -381,7 +381,6 @@ void WebIDBServerConnection::put(IDBTransactionBackend& transaction, const PutOp
 
     LOG(IDB, "WebProcess put request ID %llu", requestID);
 
-    ASSERT(operation.key());
     ASSERT(operation.value());
 
     IPC::DataReference value(reinterpret_cast<const uint8_t*>(operation.value()->data()), operation.value()->size());