#include "IconDatabase.h"
#include "Image.h"
#include "Logging.h"
#include "PlatformString.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
const char* DefaultIconDatabaseFilename = "/icon.db";
namespace WebCore {
IconDatabase* IconDatabase::m_sharedInstance = 0;
const int IconDatabase::currentDatabaseVersion = 3;
IconDatabase* IconDatabase::sharedIconDatabase()
{
if (!m_sharedInstance) {
m_sharedInstance = new IconDatabase();
}
return m_sharedInstance;
}
IconDatabase::IconDatabase()
: m_privateBrowsingEnabled(false)
, m_startupTimer(this, &IconDatabase::pruneUnretainedIcons)
{
close();
}
bool IconDatabase::open(const String& databasePath)
{
close();
String dbFilename = databasePath + DefaultIconDatabaseFilename;
if (!m_db.open(dbFilename)) {
LOG(IconDatabase, "Unable to open icon database at path %s", dbFilename.ascii().data());
return false;
}
if (!isValidDatabase()) {
LOG(IconDatabase, "%s is missing or in an invalid state - reconstructing", dbFilename.ascii().data());
clearDatabase();
recreateDatabase();
}
bool result;
result = m_db.executeCommand("CREATE TEMP TABLE PageRetain (url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,count INTEGER NOT NULL ON CONFLICT FAIL);");
ASSERT(result);
m_db.setSynchronous(SQLDatabase::SyncOff);
m_db.setFullsync(false);
if (isOpen())
m_startupTimer.startOneShot(0);
return isOpen();
}
void IconDatabase::close()
{
m_db.close();
}
bool IconDatabase::isValidDatabase()
{
if (!m_db.tableExists("Icon") || !m_db.tableExists("PageURL") || !m_db.tableExists("IconResource") || !m_db.tableExists("IconDatabaseInfo")) {
return false;
}
if (SQLStatement(m_db, "SELECT value FROM IconDatabaseInfo WHERE key = 'Version';").getColumnInt(0) < currentDatabaseVersion) {
LOG(IconDatabase, "DB version is not found or below expected valid version");
return false;
}
return true;
}
void IconDatabase::clearDatabase()
{
String query = "SELECT name FROM sqlite_master WHERE type='table';";
Vector<String> tables;
if (!SQLStatement(m_db, query).returnTextResults16(0, tables)) {
LOG(IconDatabase, "Unable to retrieve list of tables from database");
return;
}
for (Vector<String>::iterator table = tables.begin(); table != tables.end(); ++table ) {
if (!m_db.executeCommand("DROP TABLE " + *table)) {
LOG(IconDatabase, "Unable to drop table %s", (*table).ascii().data());
}
}
deletePrivateTables();
}
void IconDatabase::recreateDatabase()
{
if (!m_db.executeCommand("CREATE TABLE IconDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
LOG_ERROR("Could not create IconDatabaseInfo table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
if (!m_db.executeCommand(String("INSERT INTO IconDatabaseInfo VALUES ('Version', ") + String::number(currentDatabaseVersion) + ");")) {
LOG_ERROR("Could not insert icon database version into IconDatabaseInfo table (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
if (!m_db.executeCommand("CREATE TABLE PageURL (url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,iconID INTEGER NOT NULL ON CONFLICT FAIL);")) {
LOG_ERROR("Could not create PageURL table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
if (!m_db.executeCommand("CREATE TABLE Icon (iconID INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL UNIQUE ON CONFLICT FAIL, expires INTEGER);")) {
LOG_ERROR("Could not create Icon table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
if (!m_db.executeCommand("CREATE TABLE IconResource (iconID integer NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,data BLOB, touch INTEGER);")) {
LOG_ERROR("Could not create IconResource table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
if (!m_db.executeCommand("CREATE TRIGGER create_icon_resource AFTER INSERT ON Icon BEGIN INSERT INTO IconResource (iconID, data) VALUES (new.iconID, NULL); END;")) {
LOG_ERROR("Unable to create create_icon_resource trigger in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
if (!m_db.executeCommand("CREATE TRIGGER delete_icon_resource AFTER DELETE ON Icon BEGIN DELETE FROM IconResource WHERE IconResource.iconID = old.iconID; END;")) {
LOG_ERROR("Unable to create delete_icon_resource trigger in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
m_db.close();
return;
}
}
void IconDatabase::createPrivateTables()
{
if (!m_db.executeCommand("CREATE TEMP TABLE TempPageURL (url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,iconID INTEGER NOT NULL ON CONFLICT FAIL);"))
LOG_ERROR("Could not create TempPageURL table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
if (!m_db.executeCommand("CREATE TEMP TABLE TempIcon (iconID INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL UNIQUE ON CONFLICT FAIL, expires INTEGER);"))
LOG_ERROR("Could not create TempIcon table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
if (!m_db.executeCommand("CREATE TEMP TABLE TempIconResource (iconID INTERGER NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,data BLOB, touch INTEGER);"))
LOG_ERROR("Could not create TempIconResource table in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
if (!m_db.executeCommand("CREATE TEMP TRIGGER temp_create_icon_resource AFTER INSERT ON TempIcon BEGIN INSERT INTO TempIconResource (iconID, data) VALUES (new.iconID, NULL); END;"))
LOG_ERROR("Unable to create temp_create_icon_resource trigger in icon.db (%i) - %s", m_db.lastError(), m_db.lastErrorMsg());
}
void IconDatabase::deletePrivateTables()
{
m_db.executeCommand("DROP TABLE TempPageURL;");
m_db.executeCommand("DROP TABLE TempIcon;");
m_db.executeCommand("DROP TABLE TempIconResource;");
}
#ifdef BLOB_WORKAROUND
unsigned char hexToUnsignedChar(UChar h, UChar l)
{
unsigned char c;
if (h >= '0' && h <= '9')
c = h - '0';
else if (h >= 'A' && h <= 'F')
c = h - 'A' + 10;
else {
LOG_ERROR("Failed to parse TEXT result from SQL BLOB query");
return 0;
}
c *= 16;
if (l >= '0' && l <= '9')
c += l - '0';
else if (l >= 'A' && l <= 'F')
c += l - 'A' + 10;
else {
LOG_ERROR("Failed to parse TEXT result from SQL BLOB query");
return 0;
}
return c;
}
Vector<unsigned char> hexStringToVector(const String& s)
{
LOG(IconDatabase, "hexStringToVector() - s.length is %i", s.length());
if (s[0] != 'X' || s[1] != '\'') {
LOG(IconDatabase, "hexStringToVector() - string is invalid SQL HEX-string result - %s", s.ascii().data());
return Vector<unsigned char>();
}
Vector<unsigned char> result;
result.reserveCapacity(s.length() / 2);
const UChar* data = s.characters() + 2;
int count = 0;
while (data[0] != '\'') {
if (data[1] == '\'') {
LOG_ERROR("Invalid HEX TEXT data for BLOB query");
return Vector<unsigned char>();
}
result.append(hexToUnsignedChar(data[0], data[1]));
data++;
data++;
count++;
}
LOG(IconDatabase, "Finished atoi() - %i iterations, result size %i", count, result.size());
return result;
}
#endif
Vector<unsigned char> IconDatabase::imageDataForIconID(int id)
{
#ifdef BLOB_WORKAROUND
String blob = SQLStatement(m_db, String::sprintf("SELECT quote(data) FROM IconResource WHERE iconid = %i", id)).getColumnText(0);
if (blob.isEmpty())
return Vector<unsigned char>();
return hexStringToVector(blob);
#else
return SQLStatement(m_db, String::sprintf("SELECT data FROM IconResource WHERE iconid = %i", id)).getColumnBlobAsVector(0);
#endif
}
Vector<unsigned char> IconDatabase::imageDataForIconURL(const String& _iconURL)
{
String iconURL = _iconURL;
iconURL.replace('\'', "''");
#ifdef BLOB_WORKAROUND
LOG(IconDatabase, "imageDataForIconURL called with BLOB_WORKAROUND set");
String blob;
if (m_privateBrowsingEnabled) {
blob = SQLStatement(m_db, "SELECT quote(TempIconResource.data) FROM TempIconResource, TempIcon WHERE TempIcon.url = '" + iconURL + "' AND TempIconResource.iconID = TempIcon.iconID;").getColumnText(0);
if (!blob.isEmpty()) {
LOG(IconDatabase, "Icon data pulled from temp tables");
return hexStringToVector(blob);
}
}
blob = SQLStatement(m_db, "SELECT quote(IconResource.data) FROM IconResource, Icon WHERE Icon.url = '" + iconURL + "' AND IconResource.iconID = Icon.iconID;").getColumnText(0);
if (blob.isEmpty())
return Vector<unsigned char>();
return hexStringToVector(blob);
#else
LOG(IconDatabase, "imageDataForIconURL called using preferred method");
if (m_privateBrowsingEnabled) {
Vector<unsigned char> blob = SQLStatement(m_db, "SELECT TempIconResource.data FROM TempIconResource, TempIcon WHERE TempIcon.url = '" + iconURL + "' AND TempIconResource.iconID = TempIcon.iconID;").getColumnBlobAsVector(0);
if (!blob.isEmpty()) {
LOG(IconDatabase, "Icon data pulled from temp tables");
return blob;
}
}
return SQLStatement(m_db, "SELECT IconResource.data FROM IconResource, Icon WHERE Icon.url = '" + iconURL + "' AND IconResource.iconID = Icon.iconID;").getColumnBlobAsVector(0);
#endif
}
Vector<unsigned char> IconDatabase::imageDataForPageURL(const String& _pageURL)
{
String pageURL = _pageURL;
pageURL.replace('\'', "''");
#ifdef BLOB_WORKAROUND
String blob;
if (m_privateBrowsingEnabled) {
blob = SQLStatement(m_db, "SELECT quote(TempIconResource.data) FROM TempIconResource, TempPageURL WHERE TempPageURL.url = '" + pageURL + "' AND TempIconResource.iconID = TempPageURL.iconID;").getColumnText(0);
if (!blob.isEmpty()) {
LOG(IconDatabase, "Icon data pulled from temp tables");
return hexStringToVector(blob);
}
}
blob = SQLStatement(m_db, "SELECT quote(IconResource.data) FROM IconResource, PageURL WHERE PageURL.url = '" + pageURL + "' AND IconResource.iconID = PageURL.iconID;").getColumnText(0);
if (blob.isEmpty())
return Vector<unsigned char>();
return hexStringToVector(blob);
#else
LOG(IconDatabase, "imageDataForIconURL called using preferred method");
if (m_privateBrowsingEnabled) {
Vector<unsigned char> blob = SQLStatement(m_db, "SELECT TempIconResource.data FROM TempIconResource, TempPageURL WHERE TempPageURL.url = '" + pageURL + "' AND TempIconResource.iconID = TempPageURL.iconID;").getColumnBlobAsVector(0);
if (!blob.isEmpty()) {
LOG(IconDatabase, "Icon data pulled from temp tables");
return blob;
}
}
return SQLStatement(m_db, "SELECT IconResource.data FROM IconResource, PageURL WHERE PageURL.url = '" + pageURL + "' AND IconResource.iconID = PageURL.iconID;").getColumnBlobAsVector(0);
#endif
}
void IconDatabase::setPrivateBrowsingEnabled(bool flag)
{
if (m_privateBrowsingEnabled == flag)
return;
m_privateBrowsingEnabled = flag;
if (!m_privateBrowsingEnabled)
deletePrivateTables();
else
createPrivateTables();
}
Image* IconDatabase::iconForPageURL(const String& url, const IntSize& size, bool cache)
{
if (m_pageURLToSiteIcons.contains(url))
return m_pageURLToSiteIcons.get(url)->getImage(size);
String iconURL = iconURLForPageURL(url);
if (iconURL.isEmpty())
return 0;
if (m_iconURLToSiteIcons.contains(iconURL)) {
SiteIcon* icon = m_iconURLToSiteIcons.get(iconURL);
m_pageURLToSiteIcons.set(url, icon);
return icon->getImage(size);
}
SiteIcon* icon = new SiteIcon(iconURL);
m_pageURLToSiteIcons.set(url, icon);
m_iconURLToSiteIcons.set(iconURL, icon);
return icon->getImage(size);
}
String IconDatabase::iconURLForPageURL(const String& _pageURL)
{
if (_pageURL.isEmpty())
return String();
String pageURL = _pageURL;
pageURL.replace('\'', "''");
if (m_privateBrowsingEnabled) {
String iconURL = SQLStatement(m_db, "SELECT TempIcon.url FROM TempIcon, TempPageURL WHERE TempPageURL.url = '" + pageURL + "' AND TempIcon.iconID = TempPageURL.iconID").getColumnText16(0);
if (!iconURL.isEmpty())
return iconURL;
}
return SQLStatement(m_db, "SELECT Icon.url FROM Icon, PageURL WHERE PageURL.url = '" + pageURL + "' AND Icon.iconID = PageURL.iconID").getColumnText16(0);
}
Image* IconDatabase::defaultIcon(const IntSize& size)
{
return 0;
}
void IconDatabase::retainIconForURL(const String& _url)
{
if (_url.isEmpty())
return;
String url = _url;
url.replace('\'', "''");
int retainCount = SQLStatement(m_db, "SELECT count FROM PageRetain WHERE url = '" + url + "';").getColumnInt(0);
ASSERT(retainCount > -1);
if (!m_db.executeCommand("INSERT INTO PageRetain VALUES ('" + url + "', " + String::number(retainCount + 1) + ");"))
LOG_ERROR("Failed to increment retain count for url %s", _url.ascii().data());
LOG(IconDatabase, "URL %s now has a retain count of %i", _url.ascii().data(), retainCount + 1);
}
void IconDatabase::releaseIconForURL(const String& _url)
{
if (_url.isEmpty())
return;
String url = _url;
url.replace('\'', "''");
SQLStatement sql(m_db, "SELECT count FROM PageRetain WHERE url = '" + url + "';");
switch (sql.prepareAndStep()) {
case SQLITE_ROW:
break;
case SQLITE_DONE:
LOG_ERROR("Released icon for url %s that had not been retained", _url.ascii().data());
return;
default:
LOG_ERROR("Error retrieving retain count for url %s", _url.ascii().data());
return;
}
int retainCount = sql.getColumnInt(0);
sql.finalize();
if (retainCount < 1) {
LOG_ERROR("Attempting to release icon for URL %s - already fully released", _url.ascii().data());
return;
}
--retainCount;
if (!m_db.executeCommand("INSERT INTO PageRetain VALUES ('" + url + "', " + String::number(retainCount) + ");"))
LOG_ERROR("Failed to decrement retain count for url %s", _url.ascii().data());
LOG(IconDatabase, "URL %s now has a retain count of %i", _url.ascii().data(), retainCount);
if (retainCount)
return;
String iconURL = iconURLForPageURL(_url);
if (!m_db.executeCommand("DELETE FROM PageRetain WHERE url = '" + url + "';"))
LOG_ERROR("Failed to delete retain record for url %s", _url.ascii().data());
if (m_privateBrowsingEnabled)
if (!m_db.executeCommand("DELETE FROM TempPageURL WHERE url = '" + url + "';"))
LOG_ERROR("Failed to delete record of PageURL %s from private browsing tables", _url.ascii().data());
if (!m_db.executeCommand("DELETE FROM PageURL WHERE url = '" + url + "';"))
LOG_ERROR("Failed to delete record of PageURL %s from on-disk tables", _url.ascii().data());
if (iconURL.isEmpty())
return;
retainCount = totalRetainCountForIconURL(iconURL);
if (retainCount)
return;
LOG(IconDatabase, "No retainers for Icon URL %s - forgetting icon altogether", iconURL.ascii().data());
forgetIconForIconURLFromDatabase(iconURL);
SiteIcon* icon1;
SiteIcon* icon2;
if ((icon1 = m_pageURLToSiteIcons.get(_url)))
m_pageURLToSiteIcons.remove(_url);
if ((icon2 = m_iconURLToSiteIcons.get(iconURL)))
m_iconURLToSiteIcons.remove(iconURL);
if (icon1 && icon2) {
ASSERT(icon1 == icon2);
delete icon1;
icon1 = icon2 = 0;
}
if (icon1)
delete icon1;
else if (icon2)
delete icon2;
}
int IconDatabase::totalRetainCountForIconURL(const String& _iconURL)
{
if (_iconURL.isEmpty())
return 0;
String iconURL = _iconURL;
iconURL.replace('\'', "''");
int retainCount = SQLStatement(m_db, "SELECT sum(count) FROM PageRetain WHERE url IN(SELECT PageURL.url FROM PageURL, Icon WHERE PageURL.iconID = Icon.iconID AND Icon.url = '" + iconURL + "');").getColumnInt(0);
LOG(IconDatabase, "The total retain count for URL %s is %i", _iconURL.ascii().data(), retainCount);
return retainCount;
}
void IconDatabase::forgetIconForIconURLFromDatabase(const String& _iconURL)
{
if (_iconURL.isEmpty())
return;
String iconURL = _iconURL;
iconURL.replace('\'', "''");
int64_t iconID;
if (m_privateBrowsingEnabled) {
iconID = establishTemporaryIconIDForIconURL(iconURL, false);
if (iconID) {
if (!m_db.executeCommand(String::sprintf("DELETE FROM TempIcon WHERE iconID = %lli", iconID)))
LOG_ERROR("Unable to drop Icon at URL %s from table TemporaryIcon", iconURL.ascii().data());
if (!m_db.executeCommand(String::sprintf("DELETE FROM TempPageURL WHERE iconID = %lli", iconID)))
LOG_ERROR("Unable to drop temporary PageURLs pointing at Icon URL %s", iconURL.ascii().data());
}
}
iconID = establishIconIDForIconURL(iconURL, false);
if (iconID) {
if (!m_db.executeCommand(String::sprintf("DELETE FROM Icon WHERE iconID = %lli", iconID)))
LOG_ERROR("Unable to drop Icon at URL %s from table Icon", iconURL.ascii().data());
if (!m_db.executeCommand(String::sprintf("DELETE FROM PageURL WHERE iconID = %lli", iconID)))
LOG_ERROR("Unable to drop PageURLs pointing at Icon URL %s", iconURL.ascii().data());
}
}
void IconDatabase::setIconDataForIconURL(const void* data, int size, const String& _iconURL)
{
ASSERT(size > -1);
if (size)
ASSERT(data);
else
data = 0;
if (_iconURL.isEmpty()) {
LOG_ERROR("Attempt to set icon for blank url");
return;
}
String iconURL = _iconURL;
iconURL.replace('\'', "''");
int64_t iconID;
String resourceTable;
if (m_privateBrowsingEnabled) {
iconID = establishTemporaryIconIDForEscapedIconURL(iconURL);
if (!iconID) {
LOG(IconDatabase, "Failed to establish an iconID for URL %s in the private browsing table", _iconURL.ascii().data());
return;
}
resourceTable = "TempIconResource";
} else {
iconID = establishIconIDForEscapedIconURL(iconURL);
if (!iconID) {
LOG(IconDatabase, "Failed to establish an iconID for URL %s in the on-disk table", _iconURL.ascii().data());
return;
}
resourceTable = "IconResource";
}
performSetIconDataForIconID(iconID, resourceTable, data, size);
}
void IconDatabase::performSetIconDataForIconID(int64_t iconID, const String& resourceTable, const void* data, int size)
{
ASSERT(iconID);
ASSERT(!resourceTable.isEmpty());
if (data)
ASSERT(size > 0);
SQLStatement sql(m_db, "UPDATE " + resourceTable + " SET data = ? WHERE iconID = ?;");
sql.prepare();
if (data)
sql.bindBlob(1, data, size);
sql.bindInt64(2, iconID);
if (sql.step() != SQLITE_DONE)
LOG_ERROR("Unable to set icon resource data in table %s for iconID %lli", resourceTable.ascii().data(), iconID);
LOG(IconDatabase, "Icon data set in table %s for iconID %lli", resourceTable.ascii().data(), iconID);
return;
}
int64_t IconDatabase::establishTemporaryIconIDForIconURL(const String& _iconURL, bool create)
{
if (_iconURL.isEmpty())
return 0;
String iconURL = _iconURL;
iconURL.replace('\'', "''");
return establishTemporaryIconIDForEscapedIconURL(iconURL, create);
}
int64_t IconDatabase::establishTemporaryIconIDForEscapedIconURL(const String& iconURL, bool create)
{
int64_t iconID = 0;
SQLStatement sql(m_db, "SELECT iconID FROM TempIcon WHERE url = '" + iconURL + "';");
sql.prepare();
if (sql.step() == SQLITE_ROW) {
iconID = sql.getColumnInt64(0);
} else {
sql.finalize();
if (create)
if (m_db.executeCommand("INSERT INTO TempIcon (url) VALUES ('" + iconURL + "');"))
iconID = m_db.lastInsertRowID();
}
return iconID;
}
int64_t IconDatabase::establishIconIDForIconURL(const String& _iconURL, bool create)
{
if (_iconURL.isEmpty())
return 0;
String iconURL = _iconURL;
iconURL.replace('\'', "''");
return establishIconIDForEscapedIconURL(iconURL, create);
}
int64_t IconDatabase::establishIconIDForEscapedIconURL(const String& iconURL, bool create)
{
int64_t iconID = 0;
SQLStatement sql(m_db, "SELECT iconID FROM Icon WHERE url = '" + iconURL + "';");
sql.prepare();
if (sql.step() == SQLITE_ROW) {
iconID = sql.getColumnInt64(0);
} else {
sql.finalize();
if (create)
if (m_db.executeCommand("INSERT INTO Icon (url) VALUES ('" + iconURL + "');"))
iconID = m_db.lastInsertRowID();
}
return iconID;
}
void IconDatabase::setHaveNoIconForIconURL(const String& _iconURL)
{
setIconDataForIconURL(0, 0, _iconURL);
}
void IconDatabase::setIconURLForPageURL(const String& _iconURL, const String& _pageURL)
{
ASSERT(!_iconURL.isEmpty());
ASSERT(!_pageURL.isEmpty());
String iconURL = _iconURL;
iconURL.replace('\'',"''");
String pageURL = _pageURL;
pageURL.replace('\'',"''");
int64_t iconID;
String pageTable;
if (m_privateBrowsingEnabled) {
iconID = establishTemporaryIconIDForEscapedIconURL(iconURL);
pageTable = "TempPageURL";
} else {
iconID = establishIconIDForEscapedIconURL(iconURL);
pageTable = "PageURL";
}
performSetIconURLForPageURL(iconID, pageTable, pageURL);
}
void IconDatabase::performSetIconURLForPageURL(int64_t iconID, const String& pageTable, const String& pageURL)
{
ASSERT(iconID);
if (m_db.returnsAtLeastOneResult("SELECT url FROM " + pageTable + " WHERE url = '" + pageURL + "';")) {
if (!m_db.executeCommand("UPDATE " + pageTable + " SET iconID = " + String::number(iconID) + " WHERE url = '" + pageURL + "';"))
LOG_ERROR("Failed to update record in %s - %s", pageTable.ascii().data(), m_db.lastErrorMsg());
} else
if (!m_db.executeCommand("INSERT INTO " + pageTable + " (url, iconID) VALUES ('" + pageURL + "', " + String::number(iconID) + ");"))
LOG_ERROR("Failed to insert record into %s - %s", pageTable.ascii().data(), m_db.lastErrorMsg());
}
void IconDatabase::pruneUnreferencedIcons(int numberToPrune)
{
if (!numberToPrune || !isOpen())
return;
if (numberToPrune > 0) {
if (!m_db.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_db.lastErrorMsg());
} else {
if (!m_db.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_db.lastErrorMsg());
}
}
void IconDatabase::pruneUnretainedIcons(Timer<IconDatabase>* timer)
{
if (!isOpen())
return;
#ifndef NDEBUG
double start = CFAbsoluteTimeGetCurrent();
#endif
if (!m_db.executeCommand("DELETE FROM PageURL WHERE PageURL.url NOT IN(SELECT url FROM PageRetain WHERE count > 0);"))
LOG_ERROR("Failed to delete unretained PageURLs from DB - %s", m_db.lastErrorMsg());
pruneUnreferencedIcons(-1);
#ifndef NDEBUG
double duration = CFAbsoluteTimeGetCurrent() - start;
LOG(IconDatabase, "Pruning unretained icons took %d seconds", duration);
if (duration > 1.0)
LOG_ERROR("Pruning unretained icons took %d seconds - this is much too long!", duration);
#endif
}
bool IconDatabase::hasIconForIconURL(const String& _url)
{
if (m_iconURLToSiteIcons.contains(_url))
return true;
String url = _url;
url.replace('\'', "''");
String query = "SELECT IconResource.data FROM IconResource, Icon WHERE Icon.url = '" + url + "' AND IconResource.iconID = Icon.iconID;";
int size = 0;
const void* data = SQLStatement(m_db, query).getColumnBlob(0, size);
if (data && size)
return true;
if (m_privateBrowsingEnabled) {
query = "SELECT TempIconResource.data FROM TempIconResource, TempIcon WHERE TempIcon.url = '" + url + "' AND TempIconResource.iconID = TempIcon.iconID;";
size = 0;
data = SQLStatement(m_db, query).getColumnBlob(0, size);
LOG(IconDatabase, "Checking for icon for IconURL %s in temporary tables", _url.ascii().data());
return data && size;
}
return false;
}
IconDatabase::~IconDatabase()
{
m_db.close();
}
}