Title: [199499] trunk/Source/WebCore
Revision
199499
Author
beid...@apple.com
Date
2016-04-13 09:28:40 -0700 (Wed, 13 Apr 2016)

Log Message

Modern IDB (Blob support): Support deleting stored blob files.
https://bugs.webkit.org/show_bug.cgi?id=156523

Reviewed by Alex Christensen.

No new tests (No testable change in behavior yet, current tests pass).

There's 3 points in time when we need to delete blob files (and records of them):
1 - When deleting a specific object store record.
2 - When deleting an entire object store.
3 - When deleting a whole database.

This patch does those three things.

* Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteObjectStore):
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteUnusedBlobFileRecords):
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::addRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::getRecord):
(WebCore::IDBServer::SQLiteIDBBackingStore::deleteBackingStore):
* Modules/indexeddb/server/SQLiteIDBBackingStore.h:

* Modules/indexeddb/server/SQLiteIDBTransaction.cpp:
(WebCore::IDBServer::SQLiteIDBTransaction::commit):
(WebCore::IDBServer::SQLiteIDBTransaction::deleteBlobFilesIfNecessary):
(WebCore::IDBServer::SQLiteIDBTransaction::addRemovedBlobFile):
* Modules/indexeddb/server/SQLiteIDBTransaction.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (199498 => 199499)


--- trunk/Source/WebCore/ChangeLog	2016-04-13 16:25:13 UTC (rev 199498)
+++ trunk/Source/WebCore/ChangeLog	2016-04-13 16:28:40 UTC (rev 199499)
@@ -1,3 +1,34 @@
+2016-04-13  Brady Eidson  <beid...@apple.com>
+
+        Modern IDB (Blob support): Support deleting stored blob files.
+        https://bugs.webkit.org/show_bug.cgi?id=156523
+
+        Reviewed by Alex Christensen.
+
+        No new tests (No testable change in behavior yet, current tests pass).
+
+        There's 3 points in time when we need to delete blob files (and records of them):
+        1 - When deleting a specific object store record.
+        2 - When deleting an entire object store.
+        3 - When deleting a whole database.
+        
+        This patch does those three things.
+
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp:
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteObjectStore):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteUnusedBlobFileRecords):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::addRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::getRecord):
+        (WebCore::IDBServer::SQLiteIDBBackingStore::deleteBackingStore):
+        * Modules/indexeddb/server/SQLiteIDBBackingStore.h:
+
+        * Modules/indexeddb/server/SQLiteIDBTransaction.cpp:
+        (WebCore::IDBServer::SQLiteIDBTransaction::commit):
+        (WebCore::IDBServer::SQLiteIDBTransaction::deleteBlobFilesIfNecessary):
+        (WebCore::IDBServer::SQLiteIDBTransaction::addRemovedBlobFile):
+        * Modules/indexeddb/server/SQLiteIDBTransaction.h:
+
 2016-04-13  Frederic Wang  <fw...@igalia.com>
 
         Fix two coding mistakes in MathMLInlineContainerElement::childrenChanged

Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp (199498 => 199499)


--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp	2016-04-13 16:25:13 UTC (rev 199498)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp	2016-04-13 16:28:40 UTC (rev 199499)
@@ -941,9 +941,24 @@
         }
     }
 
+    // Delete all unused Blob URL records.
+    {
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("DELETE FROM BlobRecords WHERE objectStoreRow NOT IN (SELECT recordID FROM Records)"));
+        if (sql.prepare() != SQLITE_OK
+            || sql.step() != SQLITE_DONE) {
+            LOG_ERROR("Could not delete Blob URL records(%i) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return { IDBDatabaseException::UnknownError, ASCIILiteral("Could not delete stored blob records for deleted object store") };
+        }
+    }
+
+    // Delete all unused Blob File records.
+    auto error = deleteUnusedBlobFileRecords(*transaction);
+    if (!error.isNull())
+        return error;
+
     m_databaseInfo->deleteObjectStore(objectStoreIdentifier);
 
-    return true;
+    return { };
 }
 
 IDBError SQLiteIDBBackingStore::clearObjectStore(const IDBResourceIdentifier& transactionIdentifier, uint64_t objectStoreID)
@@ -1251,6 +1266,47 @@
     return { };
 }
 
+IDBError SQLiteIDBBackingStore::deleteUnusedBlobFileRecords(SQLiteIDBTransaction& transaction)
+{
+    LOG(IndexedDB, "SQLiteIDBBackingStore::deleteUnusedBlobFileRecords");
+
+    // Gather the set of blob URLs and filenames that are no longer in use.
+    HashSet<String> removedBlobFilenames;
+    {
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT fileName FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)"));
+
+        if (sql.prepare() != SQLITE_OK) {
+            LOG_ERROR("Error deleting stored blobs (%i) (Could not gather unused blobURLs) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") };
+        }
+
+        int result = sql.step();
+        while (result == SQLITE_ROW)
+            removedBlobFilenames.add(sql.getColumnText(1));
+
+        if (result != SQLITE_DONE) {
+            LOG_ERROR("Error deleting stored blobs (%i) (Could not gather unused blobURLs) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") };
+        }
+    }
+
+    // Remove the blob records that are no longer in use.
+    if (!removedBlobFilenames.isEmpty()) {
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("DELETE FROM BlobFiles WHERE blobURL NOT IN (SELECT blobURL FROM BlobRecords)"));
+
+        if (sql.prepare() != SQLITE_OK
+            || sql.step() != SQLITE_DONE) {
+            LOG_ERROR("Error deleting stored blobs (%i) (Could not delete blobFile records) - %s", m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return { IDBDatabaseException::UnknownError, ASCIILiteral("Error deleting stored blobs") };
+        }
+    }
+
+    for (auto& file : removedBlobFilenames)
+        transaction.addRemovedBlobFile(file);
+
+    return { };
+}
+
 IDBError SQLiteIDBBackingStore::deleteRecord(SQLiteIDBTransaction& transaction, int64_t objectStoreID, const IDBKeyData& keyData)
 {
     LOG(IndexedDB, "SQLiteIDBBackingStore::deleteRecord - key %s, object store %" PRIu64, keyData.loggingString().utf8().data(), objectStoreID);
@@ -1267,6 +1323,53 @@
         return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to serialize IDBKeyData to be removed from the database") };
     }
 
+    // Get the record ID
+    int64_t recordID;
+    {
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT recordID FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);"));
+
+        if (sql.prepare() != SQLITE_OK
+            || sql.bindInt64(1, objectStoreID) != SQLITE_OK
+            || sql.bindBlob(2, keyBuffer->data(), keyBuffer->size()) != SQLITE_OK) {
+            LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
+        }
+
+        int result = sql.step();
+
+        // If there's no record ID, there's no record to delete.
+        if (result == SQLITE_DONE)
+            return { };
+
+        if (result != SQLITE_ROW) {
+            LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (unable to fetch record ID) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
+        }
+
+        recordID = sql.getColumnInt64(0);
+    }
+
+    if (recordID < 1) {
+        LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (record ID is invalid) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+        return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
+    }
+
+    // Delete the blob records for this object store record.
+    {
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("DELETE FROM BlobRecords WHERE objectStoreRow = ?;"));
+
+        if (sql.prepare() != SQLITE_OK
+            || sql.bindInt64(1, recordID) != SQLITE_OK
+            || sql.step() != SQLITE_DONE) {
+            LOG_ERROR("Could not delete record from object store %" PRIi64 " (%i) (Could not delete BlobRecords records) - %s", objectStoreID, m_sqliteDB->lastError(), m_sqliteDB->lastErrorMsg());
+            return { IDBDatabaseException::UnknownError, ASCIILiteral("Failed to delete record from object store") };
+        }
+    }
+
+    auto error = deleteUnusedBlobFileRecords(transaction);
+    if (!error.isNull())
+        return error;
+
     // Delete record from object store
     {
         SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("DELETE FROM Records WHERE objectStoreID = ? AND key = CAST(? AS TEXT);"));
@@ -1464,6 +1567,8 @@
             LOG_ERROR("Indexing new object store record failed, but unable to remove the object store record itself");
             return { IDBDatabaseException::UnknownError, ASCIILiteral("Indexing new object store record failed, but unable to remove the object store record itself") };
         }
+
+        return error;
     }
 
     const Vector<String>& blobURLs = value.blobURLs();
@@ -1862,6 +1967,32 @@
 
     LOG(IndexedDB, "SQLiteIDBBackingStore::deleteBackingStore deleting file '%s' on disk", dbFilename.utf8().data());
 
+    Vector<String> blobFiles;
+    {
+        bool errored = true;
+
+        SQLiteStatement sql(*m_sqliteDB, ASCIILiteral("SELECT fileName FROM BlobFiles;"));
+        if (sql.prepare() == SQLITE_OK) {
+            int result = sql.step();
+            while (result == SQLITE_ROW) {
+                blobFiles.append(sql.getColumnText(0));
+                result = sql.step();
+            }
+
+            if (result == SQLITE_DONE)
+                errored = false;
+        }
+
+        if (errored)
+            LOG_ERROR("Error getting all blob filenames to be deleted");
+    }
+
+    String databaseDirectory = fullDatabaseDirectory();
+    for (auto& file : blobFiles) {
+        String fullPath = pathByAppendingComponent(databaseDirectory, file);
+        deleteFile(fullPath);
+    }
+
     if (m_sqliteDB) {
         m_sqliteDB->close();
         m_sqliteDB = nullptr;

Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h (199498 => 199499)


--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h	2016-04-13 16:25:13 UTC (rev 199498)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h	2016-04-13 16:28:40 UTC (rev 199499)
@@ -105,6 +105,8 @@
     IDBError uncheckedPutIndexRecord(int64_t objectStoreID, int64_t indexID, const IDBKeyData& keyValue, const IDBKeyData& indexKey);
     IDBError uncheckedHasIndexRecord(const IDBIndexInfo&, const IDBKeyData&, bool& hasRecord);
 
+    IDBError deleteUnusedBlobFileRecords(SQLiteIDBTransaction&);
+
     JSC::VM& vm();
     JSC::JSGlobalObject& globalObject();
     void initializeVM();

Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp (199498 => 199499)


--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp	2016-04-13 16:25:13 UTC (rev 199498)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.cpp	2016-04-13 16:28:40 UTC (rev 199499)
@@ -30,6 +30,7 @@
 #include "FileSystem.h"
 #include "IDBCursorInfo.h"
 #include "IndexedDB.h"
+#include "Logging.h"
 #include "SQLiteIDBBackingStore.h"
 #include "SQLiteIDBCursor.h"
 #include "SQLiteTransaction.h"
@@ -68,6 +69,7 @@
 
 IDBError SQLiteIDBTransaction::commit()
 {
+    LOG(IndexedDB, "SQLiteIDBTransaction::commit");
     if (!m_sqliteTransaction || !m_sqliteTransaction->inProgress())
         return { IDBDatabaseException::UnknownError, ASCIILiteral("No SQLite transaction in progress to commit") };
 
@@ -76,6 +78,7 @@
     if (m_sqliteTransaction->inProgress())
         return { IDBDatabaseException::UnknownError, ASCIILiteral("Unable to commit SQLite transaction in database backend") };
 
+    deleteBlobFilesIfNecessary();
     moveBlobFilesIfNecessary();
 
     reset();
@@ -97,6 +100,18 @@
     m_blobTemporaryAndStoredFilenames.clear();
 }
 
+void SQLiteIDBTransaction::deleteBlobFilesIfNecessary()
+{
+    String databaseDirectory = m_backingStore.fullDatabaseDirectory();
+    for (auto& entry : m_blobRemovedFilenames) {
+        String fullPath = pathByAppendingComponent(databaseDirectory, entry);
+        m_backingStore.temporaryFileHandler().prepareForAccessToTemporaryFile(entry);
+        m_backingStore.temporaryFileHandler().accessToTemporaryFileComplete(entry);
+    }
+
+    m_blobRemovedFilenames.clear();
+}
+
 IDBError SQLiteIDBTransaction::abort()
 {
     for (auto& entry : m_blobTemporaryAndStoredFilenames) {
@@ -202,6 +217,13 @@
     m_blobTemporaryAndStoredFilenames.append({ temporaryPath, storedFilename });
 }
 
+void SQLiteIDBTransaction::addRemovedBlobFile(const String& removedFilename)
+{
+    ASSERT(!m_blobRemovedFilenames.contains(removedFilename));
+    m_blobRemovedFilenames.add(removedFilename);
+}
+
+
 } // namespace IDBServer
 } // namespace WebCore
 

Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h (199498 => 199499)


--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h	2016-04-13 16:25:13 UTC (rev 199498)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBTransaction.h	2016-04-13 16:28:40 UTC (rev 199499)
@@ -72,12 +72,14 @@
     SQLiteTransaction* sqliteTransaction() const { return m_sqliteTransaction.get(); }
 
     void addBlobFile(const String& temporaryPath, const String& storedFilename);
+    void addRemovedBlobFile(const String& removedFilename);
 
 private:
     void clearCursors();
     void reset();
 
     void moveBlobFilesIfNecessary();
+    void deleteBlobFilesIfNecessary();
 
     IDBTransactionInfo m_info;
 
@@ -86,6 +88,7 @@
     HashMap<IDBResourceIdentifier, std::unique_ptr<SQLiteIDBCursor>> m_cursors;
     HashSet<SQLiteIDBCursor*> m_backingStoreCursors;
     Vector<std::pair<String, String>> m_blobTemporaryAndStoredFilenames;
+    HashSet<String> m_blobRemovedFilenames;
 };
 
 } // namespace IDBServer
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to