+2006-08-21 Brady Eidson <beidson@apple.com>
+
+ Reviewed by Maciej
+
+ <rdar://4690949> - New IconDB: Need to prune unretained icons on startup
+
+ Added a flag to track whether or not the initial pruning has taken place on startup
+ If that flag is not set, IconURL retain counts will be tracked in a temporary db table
+ in addition to the in-memory hash. Then when the timer fires after initial retains
+ are complete, we prune those icons not in the retain table, prune dangling PageURL
+ references, delete the temporary table, and set the flag - and carry on as normal
+
+ * loader/icon/IconDatabase.cpp:
+ (WebCore::IconDatabase::IconDatabase): initialize the flag
+ (WebCore::IconDatabase::open): changed the schema of the temporary table
+ (WebCore::IconDatabase::retainIconURL): store the icon retain to the temp table if starting up
+ (WebCore::IconDatabase::releaseIconURL): ditto
+ (WebCore::IconDatabase::pruneUnretainedIconsOnStartup): remove all icons *not* in the retain table, then
+ wipe all the PageURLs who no longer point to a valid IconURL
+ * loader/icon/IconDatabase.h:
+
2006-08-21 Alexey Proskuryakov <ap@nypop.com>
Reviewed by Eric.
, m_privateBrowsingEnabled(false)
, m_startupTimer(this, &IconDatabase::pruneUnretainedIconsOnStartup)
, m_updateTimer(this, &IconDatabase::updateDatabase)
+ , m_initialPruningComplete(false)
{
}
// We're going to track an icon's retain count in a temp table in memory so we can cross reference it to to the on disk tables
bool result;
- result = m_mainDB.executeCommand("CREATE TEMP TABLE PageRetain (url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,count INTEGER NOT NULL ON CONFLICT FAIL);");
+ result = m_mainDB.executeCommand("CREATE TEMP TABLE IconRetain (url TEXT);");
// Creating an in-memory temp table should never, ever, ever fail
ASSERT(result);
m_iconURLToRetainCount.set(iconURL, 1);
if (m_iconURLsPendingDeletion.contains(iconURL))
m_iconURLsPendingDeletion.remove(iconURL);
- }
+
+ // If we haven't done initial pruning, we store this retain record in the temporary in-memory table
+ // Note we only keep the URL in the temporary table, not the full retain count, because for pruning-considerations
+ // we only care *if* an icon is retained - not the full count. This call to retainIconURL incremented the IconURL's
+ // retain count from 0 to 1 therefore we may store it in the temporary table
+ if (!m_initialPruningComplete) {
+ String escapedIconURL = iconURL;
+ escapedIconURL.replace('\'', "''");
+ if (!m_mainDB.executeCommand("INSERT INTO IconRetain VALUES ('" + escapedIconURL + "');"))
+ LOG_ERROR("Failed to record icon retention in temporary table for IconURL %s", iconURL.ascii().data());
+ }
+ }
}
void IconDatabase::releaseIconURL(const String& iconURL)
// And since we delay the actual deletion of icons, so lets add it to that queue
m_iconURLsPendingDeletion.add(iconURL);
+
+ // If we haven't done initial pruning, we remove this retain record from the temporary in-memory table
+ // Note we only keep the URL in the temporary table, not the full retain count, because for pruning-considerations
+ // we only care *if* an icon is retained - not the full count. This call to retainIconURL decremented the IconURL's
+ // retain count from 1 to 0 therefore we may remove it from the temporary table
+ if (!m_initialPruningComplete) {
+ String escapedIconURL = iconURL;
+ escapedIconURL.replace('\'', "''");
+ if (!m_mainDB.executeCommand("DELETE FROM IconRetain WHERE url='" + escapedIconURL + "';"))
+ LOG_ERROR("Failed to delete record of icon retention from temporary table for IconURL %s", iconURL.ascii().data());
+ }
}
void IconDatabase::forgetPageURL(const String& pageURL)
return addIconForIconURLQuery(db, iconURL);
}
-void IconDatabase::pruneUnreferencedIcons(int numberToPrune)
-{
- if (!numberToPrune || !isOpen())
- return;
-
- if (numberToPrune > 0) {
- if (!m_mainDB.executeCommand(String::sprintf("DELETE FROM Icon WHERE Icon.iconID IN (SELECT Icon.iconID FROM Icon WHERE Icon.iconID NOT IN(SELECT PageURL.iconID FROM PageURL) LIMIT %i);", numberToPrune)))
- LOG_ERROR("Failed to prune %i unreferenced icons from the DB - %s", numberToPrune, m_mainDB.lastErrorMsg());
- } else {
- if (!m_mainDB.executeCommand("DELETE FROM Icon WHERE Icon.iconID IN (SELECT Icon.iconID FROM Icon WHERE Icon.iconID NOT IN(SELECT PageURL.iconID FROM PageURL));"))
- LOG_ERROR("Failed to prune all unreferenced icons from the DB - %s", m_mainDB.lastErrorMsg());
- }
-}
-
void IconDatabase::pruneUnretainedIconsOnStartup(Timer<IconDatabase>*)
{
if (!isOpen())
return;
+ // This function should only be called once per run, and ideally only via the timer
+ // on program startup
+ ASSERT(!m_initialPruningComplete);
+
#ifndef NDEBUG
CFTimeInterval start = CFAbsoluteTimeGetCurrent();
#endif
- // FIXME - rdar://4690949 - Need to prune unretained pageURLs here
- LOG_ERROR("pruneUnretainedIconsOnStartup is still unimplemented");
- pruneUnreferencedIcons(-1);
-
+ // rdar://4690949 - Need to prune unretained iconURLs here, then prune out all pageURLs that reference
+ // nonexistent icons
+
+ // First wipe all IconURLs that aren't retained
+ if (!m_mainDB.executeCommand("DELETE FROM Icon WHERE Icon.URL NOT IN (SELECT * FROM IconRetain);"))
+ LOG_ERROR("Failed to execute SQL to prune unretained icons from the on-disk tables");
+ // Then wipe all PageURLs whose IconURLs are now missing
+ if (!m_mainDB.executeCommand("DELETE FROM PageURL WHERE NOT EXISTS (SELECT * FROM Icon WHERE PageURL.iconID = Icon.iconID);"))
+ LOG_ERROR("Failed to prune dangling PageURLs from the DB");
+ // Finally wipe the temporary IconRetain table
+ if (!m_mainDB.executeCommand("DROP TABLE IconRetain;"))
+ LOG_ERROR("Failed to remove temporary IconRetain table");
+
+ m_initialPruningComplete = true;
+
#ifndef NDEBUG
CFTimeInterval duration = CFAbsoluteTimeGetCurrent() - start;
LOG(IconDatabase, "Pruning unretained icons took %.4f seconds", duration);