2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "IconDatabase.h"
31 #include "PlatformString.h"
34 #include <sys/types.h>
38 // FIXME - Make sure we put a private browsing consideration in that uses the temporary tables anytime private browsing would be an issue.
40 // FIXME - One optimization to be made when this is no longer in flux is to make query construction smarter - that is queries that are created from
41 // multiple strings and numbers should be handled differently than with String + String + String + etc.
45 IconDatabase* IconDatabase::m_sharedInstance = 0;
47 // This version number is in the DB and marks the current generation of the schema
48 // Theoretically once the switch is flipped this should never change
49 // Currently, an out-of-date schema causes the DB to be wiped and reset. This isn't
50 // so bad during development but in the future, we would need to write a conversion
51 // function to advance older released schemas to "current"
52 const int IconDatabase::currentDatabaseVersion = 4;
54 // Icons expire once a day
55 const int IconDatabase::iconExpirationTime = 60*60*24;
56 // Absent icons are rechecked once a week
57 const int IconDatabase::missingIconExpirationTime = 60*60*24*7;
59 const String& IconDatabase::defaultDatabaseFilename()
61 static String defaultDatabaseFilename = "icon.db";
62 return defaultDatabaseFilename;
65 // Query - Checks for at least 1 entry in the PageURL table
66 bool pageURLTableIsEmptyQuery(SQLDatabase&);
67 // Query - Returns the time stamp for an Icon entry
68 int timeStampForIconURLQuery(SQLDatabase&, const String& iconURL);
69 // Query - Returns the IconURL for a PageURL
70 String iconURLForPageURLQuery(SQLDatabase&, const String& pageURL);
71 // Query - Checks for the existence of the given IconURL in the Icon table
72 bool hasIconForIconURLQuery(SQLDatabase& db, const String& iconURL);
73 // Query - Deletes a PageURL from the PageURL table
74 void forgetPageURLQuery(SQLDatabase& db, const String& pageURL);
75 // Query - Sets the Icon.iconID for a PageURL in the PageURL table
76 void setIconIDForPageURLQuery(SQLDatabase& db, int64_t, const String&);
77 // Query - Returns the iconID for the given IconURL
78 int64_t getIconIDForIconURLQuery(SQLDatabase& db, const String& iconURL);
79 // Query - Creates the Icon entry for the given IconURL and returns the resulting iconID
80 int64_t addIconForIconURLQuery(SQLDatabase& db, const String& iconURL);
81 // Query - Returns the image data from the given database for the given IconURL
82 Vector<unsigned char> imageDataForIconURLQuery(SQLDatabase& db, const String& iconURL);
84 IconDatabase* IconDatabase::sharedIconDatabase()
86 if (!m_sharedInstance) {
87 m_sharedInstance = new IconDatabase();
89 return m_sharedInstance;
92 IconDatabase::IconDatabase()
93 : m_currentDB(&m_mainDB)
94 , m_privateBrowsingEnabled(false)
95 , m_startupTimer(this, &IconDatabase::pruneUnretainedIconsOnStartup)
96 , m_pruneTimer(this, &IconDatabase::pruneIconsPendingDeletion)
101 bool IconDatabase::open(const String& databasePath)
104 LOG_ERROR("Attempt to reopen the IconDatabase which is already open. Must close it first.");
108 // First we'll formulate the full path for the database file
110 if (databasePath[databasePath.length()] == '/')
111 dbFilename = databasePath + defaultDatabaseFilename();
113 dbFilename = databasePath + "/" + defaultDatabaseFilename();
115 // Now, we'll see if we can open the on-disk table
116 // If we can't, this ::open() failed and we should bail now
117 if (!m_mainDB.open(dbFilename)) {
118 LOG(IconDatabase, "Unable to open icon database at path %s", dbFilename.ascii().data());
122 if (!isValidDatabase(m_mainDB)) {
123 LOG(IconDatabase, "%s is missing or in an invalid state - reconstructing", dbFilename.ascii().data());
124 clearDatabaseTables(m_mainDB);
125 createDatabaseTables(m_mainDB);
128 // 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
130 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);");
131 // Creating an in-memory temp table should never, ever, ever fail
134 // These are actually two different SQLite config options - not my fault they are named confusingly ;)
135 m_mainDB.setSynchronous(SQLDatabase::SyncOff);
136 m_mainDB.setFullsync(false);
138 // Open the in-memory table for private browsing
139 if (!m_privateBrowsingDB.open(":memory:"))
140 LOG_ERROR("Unabled to open in-memory database for private browsing - %s", m_privateBrowsingDB.lastErrorMsg());
142 // Only if we successfully remained open will we start our "initial purge timer"
143 // rdar://4690949 - when we have deferred reads and writes all the way in, the prunetimer
144 // will become "deferredTimer" or something along those lines, and will be set only when
145 // a deferred read/write is queued
147 m_startupTimer.startOneShot(0);
148 m_pruneTimer.startRepeating(10);
154 void IconDatabase::close()
157 m_privateBrowsingDB.close();
160 bool IconDatabase::isEmpty()
162 if (m_privateBrowsingEnabled)
163 if (!pageURLTableIsEmptyQuery(m_privateBrowsingDB))
166 return pageURLTableIsEmptyQuery(m_mainDB);
169 bool IconDatabase::isValidDatabase(SQLDatabase& db)
171 // These two tables should always exist in a valid db
172 if (!db.tableExists("Icon") || !db.tableExists("PageURL") || !db.tableExists("IconDatabaseInfo"))
175 if (SQLStatement(db, "SELECT value FROM IconDatabaseInfo WHERE key = 'Version';").getColumnInt(0) < currentDatabaseVersion) {
176 LOG(IconDatabase, "DB version is not found or below expected valid version");
183 void IconDatabase::clearDatabaseTables(SQLDatabase& db)
185 String query = "SELECT name FROM sqlite_master WHERE type='table';";
186 Vector<String> tables;
187 if (!SQLStatement(db, query).returnTextResults16(0, tables)) {
188 LOG(IconDatabase, "Unable to retrieve list of tables from database");
192 for (Vector<String>::iterator table = tables.begin(); table != tables.end(); ++table ) {
193 if (!db.executeCommand("DROP TABLE " + *table)) {
194 LOG(IconDatabase, "Unable to drop table %s", (*table).ascii().data());
199 void IconDatabase::createDatabaseTables(SQLDatabase& db)
201 if (!db.executeCommand("CREATE TABLE PageURL (url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,iconID INTEGER NOT NULL ON CONFLICT FAIL);")) {
202 LOG_ERROR("Could not create PageURL table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
206 if (!db.executeCommand("CREATE TABLE Icon (iconID INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL UNIQUE ON CONFLICT FAIL, stamp INTEGER, data BLOB);")) {
207 LOG_ERROR("Could not create Icon table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
211 if (!db.executeCommand("CREATE TRIGGER update_icon_timestamp AFTER UPDATE ON Icon BEGIN UPDATE Icon SET stamp = strftime('%s','now') WHERE iconID = new.iconID; END;")) {
212 LOG_ERROR("Could not create timestamp updater in database (%i) - %s", db.lastError(), db.lastErrorMsg());
216 if (!db.executeCommand("CREATE TABLE IconDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) {
217 LOG_ERROR("Could not create IconDatabaseInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg());
221 if (!db.executeCommand(String("INSERT INTO IconDatabaseInfo VALUES ('Version', ") + String::number(currentDatabaseVersion) + ");")) {
222 LOG_ERROR("Could not insert icon database version into IconDatabaseInfo table (%i) - %s", db.lastError(), db.lastErrorMsg());
228 Vector<unsigned char> IconDatabase::imageDataForIconURL(const String& iconURL)
230 // If private browsing is enabled, we'll check there first as the most up-to-date data for an icon will be there
231 if (m_privateBrowsingEnabled) {
232 Vector<unsigned char> blob = imageDataForIconURLQuery(m_privateBrowsingDB, iconURL);
237 // It wasn't found there, so lets check the main tables
238 return imageDataForIconURLQuery(m_mainDB, iconURL);
241 void IconDatabase::setPrivateBrowsingEnabled(bool flag)
243 if (m_privateBrowsingEnabled == flag)
246 m_privateBrowsingEnabled = flag;
248 if (m_privateBrowsingEnabled) {
249 createDatabaseTables(m_privateBrowsingDB);
250 m_currentDB = &m_privateBrowsingDB;
252 clearDatabaseTables(m_privateBrowsingDB);
253 m_currentDB = &m_mainDB;
257 Image* IconDatabase::iconForPageURL(const String& pageURL, const IntSize& size, bool cache)
259 // See if we even have an IconURL for this PageURL...
260 String iconURL = iconURLForPageURL(pageURL);
261 if (iconURL.isEmpty())
264 // If we do, maybe we have a SiteIcon for this IconURL
265 if (m_iconURLToSiteIcons.contains(iconURL)) {
266 SiteIcon* icon = m_iconURLToSiteIcons.get(iconURL);
267 return icon->getImage(size);
270 // If we don't have either, we have to create the SiteIcon
271 SiteIcon* icon = new SiteIcon(iconURL);
272 m_iconURLToSiteIcons.set(iconURL, icon);
273 return icon->getImage(size);
276 // FIXME 4667425 - this check needs to see if the icon's data is empty or not and apply
277 // iconExpirationTime to present icons, and missingIconExpirationTime for missing icons
278 bool IconDatabase::isIconExpiredForIconURL(const String& iconURL)
280 if (iconURL.isEmpty())
284 if (m_privateBrowsingEnabled) {
285 stamp = timeStampForIconURLQuery(m_privateBrowsingDB, iconURL);
287 return (time(NULL) - stamp) > iconExpirationTime;
290 stamp = timeStampForIconURLQuery(m_mainDB, iconURL);
292 return (time(NULL) - stamp) > iconExpirationTime;
296 String IconDatabase::iconURLForPageURL(const String& pageURL)
298 if (pageURL.isEmpty())
301 if (m_pageURLToIconURLMap.contains(pageURL))
302 return m_pageURLToIconURLMap.get(pageURL);
304 // Try the private browsing database because if any PageURL's IconURL was updated during privated browsing,
305 // the most up-to-date iconURL would be there
306 if (m_privateBrowsingEnabled) {
307 String iconURL = iconURLForPageURLQuery(m_privateBrowsingDB, pageURL);
308 if (!iconURL.isEmpty()) {
309 m_pageURLToIconURLMap.set(pageURL, iconURL);
314 String iconURL = iconURLForPageURLQuery(m_mainDB, pageURL);
315 if (!iconURL.isEmpty())
316 m_pageURLToIconURLMap.set(pageURL, iconURL);
320 Image* IconDatabase::defaultIcon(const IntSize& size)
325 void IconDatabase::retainIconForPageURL(const String& pageURL)
327 if (pageURL.isEmpty())
332 // If we don't have the retain count for this page, we need to setup records of its retain
333 // Otherwise, get the count and increment it
334 if (!m_pageURLToRetainCount.contains(pageURL)) {
335 // If this pageURL is marked for deletion, bring it back from the brink
336 m_pageURLsPendingDeletion.remove(pageURL);
338 // The new retain count will be 1
341 // If we have an iconURL for this pageURL, we'll now retain the iconURL
342 String iconURL = iconURLForPageURL(pageURL);
343 if (!iconURL.isEmpty())
344 retainIconURL(iconURL);
347 retainCount = m_pageURLToRetainCount.get(pageURL) + 1;
349 m_pageURLToRetainCount.set(pageURL, retainCount);
352 void IconDatabase::releaseIconForPageURL(const String& pageURL)
354 if (pageURL.isEmpty())
357 // Check if this pageURL is actually retained
358 if(!m_pageURLToRetainCount.contains(pageURL)) {
359 LOG_ERROR("Attempting to release icon for URL %s which is not retained", pageURL.ascii().data());
363 // Get its retain count
364 int retainCount = m_pageURLToRetainCount.get(pageURL);
365 ASSERT(retainCount > 0);
367 // If it still has a positive retain count, store the new count and bail
369 m_pageURLToRetainCount.set(pageURL, retainCount);
373 LOG(IconDatabase, "No more retainers for PageURL %s", pageURL.ascii().data());
375 // Otherwise, remove all record of the retain count
376 m_pageURLToRetainCount.remove(pageURL);
378 // Then mark this pageURL for deletion
379 m_pageURLsPendingDeletion.add(pageURL);
381 // Grab the iconURL and release it
382 String iconURL = iconURLForPageURL(pageURL);
383 if (!iconURL.isEmpty())
384 releaseIconURL(iconURL);
387 void IconDatabase::retainIconURL(const String& iconURL)
389 ASSERT(!iconURL.isEmpty());
391 if (m_iconURLToRetainCount.contains(iconURL)) {
392 ASSERT(m_iconURLToRetainCount.get(iconURL) > 0);
393 m_iconURLToRetainCount.set(iconURL, m_iconURLToRetainCount.get(iconURL) + 1);
395 m_iconURLToRetainCount.set(iconURL, 1);
396 if (m_iconURLsPendingDeletion.contains(iconURL))
397 m_iconURLsPendingDeletion.remove(iconURL);
401 void IconDatabase::releaseIconURL(const String& iconURL)
403 ASSERT(!iconURL.isEmpty());
405 // If the iconURL has no retain count, we can bail
406 if (!m_iconURLToRetainCount.contains(iconURL))
409 // Otherwise, decrement it
410 int retainCount = m_iconURLToRetainCount.get(iconURL) - 1;
411 ASSERT(retainCount > -1);
413 // If the icon is still retained, store the count and bail
415 m_iconURLToRetainCount.set(iconURL, retainCount);
419 LOG(IconDatabase, "No more retainers for IconURL %s", iconURL.ascii().data());
421 // Otherwise, this icon is toast. Remove all traces of its retain count...
422 m_iconURLToRetainCount.remove(iconURL);
424 // And since we delay the actual deletion of icons, so lets add it to that queue
425 m_iconURLsPendingDeletion.add(iconURL);
428 void IconDatabase::forgetPageURL(const String& pageURL)
430 // Remove the PageURL->IconURL mapping
431 m_pageURLToIconURLMap.remove(pageURL);
433 // And remove this pageURL from the DB
434 forgetPageURLQuery(*m_currentDB, pageURL);
437 bool IconDatabase::isIconURLRetained(const String& iconURL)
439 if (iconURL.isEmpty())
442 return m_iconURLToRetainCount.contains(iconURL);
445 void IconDatabase::forgetIconForIconURLFromDatabase(const String& iconURL)
447 if (iconURL.isEmpty())
450 // For private browsing safety, since this alters the database, we only forget from the current database
451 // If we're in private browsing and the Icon also exists in the main database, it will be pruned on the next startup
452 int64_t iconID = establishIconIDForIconURL(*m_currentDB, iconURL, false);
454 // If we didn't actually have an icon for this iconURL... well, thats a screwy condition we should track down, but also
455 // something we could move on from
458 LOG_ERROR("Attempting to forget icon for IconURL %s, though we don't have it in the database", iconURL.ascii().data());
462 String escapedIconURL = iconURL;
463 escapedIconURL.replace('\'', "''");
465 if (!m_currentDB->executeCommand(String::sprintf("DELETE FROM Icon WHERE Icon.iconID = %lli;", iconID)))
466 LOG_ERROR("Unable to drop Icon for IconURL", iconURL.ascii().data());
467 if (!m_currentDB->executeCommand(String::sprintf("DELETE FROM PageURL WHERE PageURL.iconID = %lli", iconID)))
468 LOG_ERROR("Unable to drop all PageURL for IconURL", iconURL.ascii().data());
471 void IconDatabase::setIconDataForIconURL(const void* data, int size, const String& iconURL)
479 if (iconURL.isEmpty())
482 // First, if we already have a SiteIcon in memory, let's update its image data
483 if (m_iconURLToSiteIcons.contains(iconURL))
484 m_iconURLToSiteIcons.get(iconURL)->manuallySetImageData((unsigned char*)data, size);
486 // Next, we actually commit the image data to the database
487 // Start by making sure there's an entry for this IconURL in the current database
488 int64_t iconID = establishIconIDForIconURL(*m_currentDB, iconURL, true);
491 // First we create and prepare the SQLStatement
492 // The following statement also works to set the icon data to NULL because sqlite defaults unbound ? parameters to NULL
493 SQLStatement sql(*m_currentDB, "UPDATE Icon SET data = ? WHERE iconID = ?;");
496 // Then we bind the icondata and iconID to the SQLStatement
498 sql.bindBlob(1, data, size);
499 sql.bindInt64(2, iconID);
501 // Finally we step and make sure the step was successful
502 if (sql.step() != SQLITE_DONE)
503 LOG_ERROR("Unable to set icon data for iconURL %s", iconURL.ascii().data());
508 void IconDatabase::setHaveNoIconForIconURL(const String& iconURL)
510 setIconDataForIconURL(0, 0, iconURL);
513 void IconDatabase::setIconURLForPageURL(const String& iconURL, const String& pageURL)
515 ASSERT(!iconURL.isEmpty());
516 ASSERT(!pageURL.isEmpty());
518 // If the urls already map to each other, bail.
519 // This happens surprisingly often, and seems to cream iBench performance
520 if (m_pageURLToIconURLMap.get(pageURL) == iconURL)
523 // If this pageURL is retained, we have some work to do on the IconURL retain counts
524 if (m_pageURLToRetainCount.contains(pageURL)) {
525 String oldIconURL = m_pageURLToIconURLMap.get(pageURL);
526 if (!oldIconURL.isEmpty())
527 releaseIconURL(oldIconURL);
528 retainIconURL(iconURL);
531 // Cache the pageURL->iconURL map
532 m_pageURLToIconURLMap.set(pageURL, iconURL);
534 // Store it in the database
535 int64_t iconID = establishIconIDForIconURL(*m_currentDB, iconURL);
537 LOG_ERROR("Failed to establish an ID for iconURL %s", iconURL.ascii().data());
540 setIconIDForPageURLQuery(*m_currentDB, iconID, pageURL);
543 int64_t IconDatabase::establishIconIDForIconURL(SQLDatabase& db, const String& iconURL, bool createIfNecessary)
545 String escapedIconURL = iconURL;
546 escapedIconURL.replace('\'', "''");
548 // Get the iconID thats already in this database and return it - or return 0 if we're read-only
549 int64_t iconID = getIconIDForIconURLQuery(db, iconURL);
550 if (iconID || !createIfNecessary)
553 // Create the icon table entry for the iconURL
554 return addIconForIconURLQuery(db, iconURL);
557 void IconDatabase::pruneUnreferencedIcons(int numberToPrune)
559 if (!numberToPrune || !isOpen())
562 if (numberToPrune > 0) {
563 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)))
564 LOG_ERROR("Failed to prune %i unreferenced icons from the DB - %s", numberToPrune, m_mainDB.lastErrorMsg());
566 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));"))
567 LOG_ERROR("Failed to prune all unreferenced icons from the DB - %s", m_mainDB.lastErrorMsg());
571 void IconDatabase::pruneUnretainedIconsOnStartup(Timer<IconDatabase>*)
576 // FIXME - The PageURL delete and the pruneunreferenced icons need to be in an atomic transaction
578 CFTimeInterval start = CFAbsoluteTimeGetCurrent();
581 // FIXME - rdar://4690949 - Need to prune unretained pageURLs here
582 LOG_ERROR("pruneUnretainedIconsOnStartup is still unimplemented");
583 pruneUnreferencedIcons(-1);
586 CFTimeInterval duration = CFAbsoluteTimeGetCurrent() - start;
587 LOG(IconDatabase, "Pruning unretained icons took %.4f seconds", duration);
589 LOG_ERROR("Pruning unretained icons took %.4f seconds - this is much too long!", duration);
593 void IconDatabase::pruneIconsPendingDeletion(Timer<IconDatabase>*)
596 CFTimeInterval start = CFAbsoluteTimeGetCurrent();
599 // First lets wipe all the pageURLs
600 HashSet<String>::iterator i = m_pageURLsPendingDeletion.begin();
601 for (;i != m_pageURLsPendingDeletion.end(); ++i) {
603 LOG(IconDatabase, "Deleted PageURL %s", (*i).ascii().data());
605 m_pageURLsPendingDeletion.clear();
607 // Then get rid of all traces of the icons and IconURLs
609 for (i = m_iconURLsPendingDeletion.begin();i != m_iconURLsPendingDeletion.end(); ++i) {
610 // Forget the SiteIcon
611 icon = m_iconURLToSiteIcons.get(*i);
612 m_iconURLToSiteIcons.remove(*i);
615 // Forget the IconURL from the database
616 forgetIconForIconURLFromDatabase(*i);
617 LOG(IconDatabase, "Deleted icon %s", (*i).ascii().data());
619 m_iconURLsPendingDeletion.clear();
622 CFTimeInterval duration = CFAbsoluteTimeGetCurrent() - start;
623 LOG(IconDatabase, "Wiping the items pending-deletion took %.4f seconds", duration);
625 LOG_ERROR("Wiping the items pending-deletion took %.4f seconds - this is much too long!", duration);
629 bool IconDatabase::hasEntryForIconURL(const String& iconURL)
631 if (iconURL.isEmpty())
634 // First check the in memory mapped icons...
635 if (m_iconURLToSiteIcons.contains(iconURL))
638 // Then we'll check the main database
639 if (hasIconForIconURLQuery(m_mainDB, iconURL))
642 // Finally, the last resort - check the private browsing database
643 if (m_privateBrowsingEnabled)
644 if (hasIconForIconURLQuery(m_privateBrowsingDB, iconURL))
647 // We must not have this iconURL!
651 IconDatabase::~IconDatabase()
657 // Query helper functions
658 bool pageURLTableIsEmptyQuery(SQLDatabase& db)
660 return !(SQLStatement(db, "SELECT iconID FROM PageURL LIMIT 1;").returnsAtLeastOneResult());
663 Vector<unsigned char> imageDataForIconURLQuery(SQLDatabase& db, const String& iconURL)
665 String escapedIconURL = iconURL;
666 escapedIconURL.replace('\'', "''");
667 return SQLStatement(db, "SELECT Icon.data FROM Icon WHERE Icon.url = '" + escapedIconURL + "';").getColumnBlobAsVector(0);
670 int timeStampForIconURLQuery(SQLDatabase& db, const String& iconURL)
672 String escapedIconURL = iconURL;
673 escapedIconURL.replace('\'', "''");
674 return SQLStatement(db, "SELECT Icon.stamp FROM Icon WHERE Icon.url = '" + escapedIconURL + "';").getColumnInt(0);
678 String iconURLForPageURLQuery(SQLDatabase& db, const String& pageURL)
680 String escapedPageURL = pageURL;
681 escapedPageURL.replace('\'', "''");
682 return SQLStatement(db, "SELECT Icon.url FROM Icon, PageURL WHERE PageURL.url = '" + escapedPageURL + "' AND Icon.iconID = PageURL.iconID").getColumnText16(0);
685 void forgetPageURLQuery(SQLDatabase& db, const String& pageURL)
687 String escapedPageURL = pageURL;
688 escapedPageURL.replace('\'', "''");
690 db.executeCommand("DELETE FROM PageURL WHERE url = '" + escapedPageURL + "';");
693 void setIconIDForPageURLQuery(SQLDatabase& db, int64_t iconID, const String& pageURL)
695 String escapedPageURL = pageURL;
696 escapedPageURL.replace('\'', "''");
697 if (!db.executeCommand("INSERT INTO PageURL (url, iconID) VALUES ('" + escapedPageURL + "', " + String::number(iconID) + ");"))
698 LOG_ERROR("Failed to set iconid %lli for PageURL %s", iconID, pageURL.ascii().data());
701 int64_t getIconIDForIconURLQuery(SQLDatabase& db, const String& iconURL)
703 String escapedIconURL = iconURL;
704 escapedIconURL.replace('\'', "''");
705 return SQLStatement(db, "SELECT Icon.iconID FROM Icon WHERE Icon.url = '" + escapedIconURL + "';").getColumnInt64(0);
708 int64_t addIconForIconURLQuery(SQLDatabase& db, const String& iconURL)
710 String escapedIconURL = iconURL;
711 escapedIconURL.replace('\'', "''");
712 if (db.executeCommand("INSERT INTO Icon (url) VALUES ('" + escapedIconURL + "');"))
713 return db.lastInsertRowID();
717 bool hasIconForIconURLQuery(SQLDatabase& db, const String& iconURL)
719 String escapedIconURL = iconURL;
720 escapedIconURL.replace('\'', "''");
721 return SQLStatement(db, "SELECT Icon.iconID FROM Icon WHERE Icon.url = '" + escapedIconURL + "';").returnsAtLeastOneResult();
724 } //namespace WebCore