• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/history/thumbnail_database.h"
6 
7 #include <algorithm>
8 #include <string>
9 
10 #include "app/sql/statement.h"
11 #include "app/sql/transaction.h"
12 #include "base/command_line.h"
13 #include "base/file_util.h"
14 #include "base/memory/ref_counted_memory.h"
15 #include "base/time.h"
16 #include "base/string_util.h"
17 #include "base/utf_string_conversions.h"
18 #include "chrome/browser/diagnostics/sqlite_diagnostics.h"
19 #include "chrome/browser/history/history_publisher.h"
20 #include "chrome/browser/history/top_sites.h"
21 #include "chrome/browser/history/url_database.h"
22 #include "chrome/common/thumbnail_score.h"
23 #include "third_party/skia/include/core/SkBitmap.h"
24 #include "ui/gfx/codec/jpeg_codec.h"
25 
26 #if defined(OS_MACOSX)
27 #include "base/mac/mac_util.h"
28 #endif
29 
FillIconMapping(const sql::Statement & statement,const GURL & page_url,history::IconMapping * icon_mapping)30 static void FillIconMapping(const sql::Statement& statement,
31                             const GURL& page_url,
32                             history::IconMapping* icon_mapping) {
33   icon_mapping->mapping_id = statement.ColumnInt64(0);
34   icon_mapping->icon_id = statement.ColumnInt64(1);
35   icon_mapping->icon_type =
36       static_cast<history::IconType>(statement.ColumnInt(2));
37   icon_mapping->page_url = page_url;
38 }
39 
40 namespace history {
41 
42 // Version number of the database.
43 static const int kCurrentVersionNumber = 4;
44 static const int kCompatibleVersionNumber = 4;
45 
ThumbnailDatabase()46 ThumbnailDatabase::ThumbnailDatabase()
47     : history_publisher_(NULL),
48       use_top_sites_(false) {
49 }
50 
~ThumbnailDatabase()51 ThumbnailDatabase::~ThumbnailDatabase() {
52   // The DBCloseScoper will delete the DB and the cache.
53 }
54 
Init(const FilePath & db_name,const HistoryPublisher * history_publisher,URLDatabase * url_db)55 sql::InitStatus ThumbnailDatabase::Init(
56     const FilePath& db_name,
57     const HistoryPublisher* history_publisher,
58     URLDatabase* url_db) {
59   history_publisher_ = history_publisher;
60   sql::InitStatus status = OpenDatabase(&db_, db_name);
61   if (status != sql::INIT_OK)
62     return status;
63 
64   // Scope initialization in a transaction so we can't be partially initialized.
65   sql::Transaction transaction(&db_);
66   transaction.Begin();
67 
68 #if defined(OS_MACOSX)
69   // Exclude the thumbnails file and its journal from backups.
70   base::mac::SetFileBackupExclusion(db_name, true);
71   FilePath::StringType db_name_string(db_name.value());
72   db_name_string += "-journal";
73   FilePath db_journal_name(db_name_string);
74   base::mac::SetFileBackupExclusion(db_journal_name, true);
75 #endif
76 
77   // Create the tables.
78   if (!meta_table_.Init(&db_, kCurrentVersionNumber,
79                         kCompatibleVersionNumber) ||
80       !InitThumbnailTable() ||
81       !InitFaviconsTable(&db_, false) ||
82       !InitIconMappingTable(&db_, false)) {
83     db_.Close();
84     return sql::INIT_FAILURE;
85   }
86   InitFaviconsIndex();
87   InitIconMappingIndex();
88 
89   // Version check. We should not encounter a database too old for us to handle
90   // in the wild, so we try to continue in that case.
91   if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
92     LOG(WARNING) << "Thumbnail database is too new.";
93     return sql::INIT_TOO_NEW;
94   }
95 
96   int cur_version = meta_table_.GetVersionNumber();
97   if (cur_version == 2) {
98     if (!UpgradeToVersion3()) {
99       LOG(WARNING) << "Unable to update to thumbnail database to version 3.";
100       db_.Close();
101       return sql::INIT_FAILURE;
102     }
103     ++cur_version;
104   }
105 
106   if (cur_version == 3) {
107     if (!UpgradeToVersion4() || !MigrateIconMappingData(url_db)) {
108       LOG(WARNING) << "Unable to update to thumbnail database to version 4.";
109       db_.Close();
110       return sql::INIT_FAILURE;
111     }
112     ++cur_version;
113   }
114 
115   LOG_IF(WARNING, cur_version < kCurrentVersionNumber) <<
116       "Thumbnail database version " << cur_version << " is too old to handle.";
117 
118   // Initialization is complete.
119   if (!transaction.Commit()) {
120     db_.Close();
121     return sql::INIT_FAILURE;
122   }
123 
124   return sql::INIT_OK;
125 }
126 
OpenDatabase(sql::Connection * db,const FilePath & db_name)127 sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db,
128                                                 const FilePath& db_name) {
129   // Set the exceptional sqlite error handler.
130   db->set_error_delegate(GetErrorHandlerForThumbnailDb());
131 
132   // Thumbnails db now only stores favicons, so we don't need that big a page
133   // size or cache.
134   db->set_page_size(2048);
135   db->set_cache_size(32);
136 
137   // Run the database in exclusive mode. Nobody else should be accessing the
138   // database while we're running, and this will give somewhat improved perf.
139   db->set_exclusive_locking();
140 
141   if (!db->Open(db_name))
142     return sql::INIT_FAILURE;
143 
144   return sql::INIT_OK;
145 }
146 
InitThumbnailTable()147 bool ThumbnailDatabase::InitThumbnailTable() {
148   if (!db_.DoesTableExist("thumbnails")) {
149     use_top_sites_ = true;
150   }
151   return true;
152 }
153 
UpgradeToVersion3()154 bool ThumbnailDatabase::UpgradeToVersion3() {
155   if (use_top_sites_) {
156     meta_table_.SetVersionNumber(3);
157     meta_table_.SetCompatibleVersionNumber(
158         std::min(3, kCompatibleVersionNumber));
159     return true;  // Not needed after migration to TopSites.
160   }
161 
162   // sqlite doesn't like the "ALTER TABLE xxx ADD (column_one, two,
163   // three)" syntax, so list out the commands we need to execute:
164   const char* alterations[] = {
165     "ALTER TABLE thumbnails ADD boring_score DOUBLE DEFAULT 1.0",
166     "ALTER TABLE thumbnails ADD good_clipping INTEGER DEFAULT 0",
167     "ALTER TABLE thumbnails ADD at_top INTEGER DEFAULT 0",
168     "ALTER TABLE thumbnails ADD last_updated INTEGER DEFAULT 0",
169     NULL
170   };
171 
172   for (int i = 0; alterations[i] != NULL; ++i) {
173     if (!db_.Execute(alterations[i])) {
174       NOTREACHED();
175       return false;
176     }
177   }
178 
179   meta_table_.SetVersionNumber(3);
180   meta_table_.SetCompatibleVersionNumber(std::min(3, kCompatibleVersionNumber));
181   return true;
182 }
183 
RecreateThumbnailTable()184 bool ThumbnailDatabase::RecreateThumbnailTable() {
185   if (use_top_sites_)
186     return true;  // Not needed after migration to TopSites.
187 
188   if (!db_.Execute("DROP TABLE thumbnails"))
189     return false;
190   return InitThumbnailTable();
191 }
192 
InitFaviconsTable(sql::Connection * db,bool is_temporary)193 bool ThumbnailDatabase::InitFaviconsTable(sql::Connection* db,
194                                           bool is_temporary) {
195   // Note: if you update the schema, don't forget to update
196   // CopyToTemporaryFaviconTable as well.
197   const char* name = is_temporary ? "temp_favicons" : "favicons";
198   if (!db->DoesTableExist(name)) {
199     std::string sql;
200     sql.append("CREATE TABLE ");
201     sql.append(name);
202     sql.append("("
203                "id INTEGER PRIMARY KEY,"
204                "url LONGVARCHAR NOT NULL,"
205                "last_updated INTEGER DEFAULT 0,"
206                "image_data BLOB,"
207                "icon_type INTEGER DEFAULT 1)"); // Set the default as FAVICON
208                                                 // to be consistent with table
209                                                 // upgrade in
210                                                 // UpgradeToVersion4().
211     if (!db->Execute(sql.c_str()))
212       return false;
213   }
214   return true;
215 }
216 
InitFaviconsIndex()217 void ThumbnailDatabase::InitFaviconsIndex() {
218   // Add an index on the url column. We ignore errors. Since this is always
219   // called during startup, the index will normally already exist.
220   db_.Execute("CREATE INDEX favicons_url ON favicons(url)");
221 }
222 
BeginTransaction()223 void ThumbnailDatabase::BeginTransaction() {
224   db_.BeginTransaction();
225 }
226 
CommitTransaction()227 void ThumbnailDatabase::CommitTransaction() {
228   db_.CommitTransaction();
229 }
230 
Vacuum()231 void ThumbnailDatabase::Vacuum() {
232   DCHECK(db_.transaction_nesting() == 0) <<
233       "Can not have a transaction when vacuuming.";
234   db_.Execute("VACUUM");
235 }
236 
SetPageThumbnail(const GURL & url,URLID id,const SkBitmap & thumbnail,const ThumbnailScore & score,base::Time time)237 void ThumbnailDatabase::SetPageThumbnail(
238     const GURL& url,
239     URLID id,
240     const SkBitmap& thumbnail,
241     const ThumbnailScore& score,
242     base::Time time) {
243   if (use_top_sites_) {
244     LOG(WARNING) << "Use TopSites instead.";
245     return;  // Not possible after migration to TopSites.
246   }
247 
248   if (!thumbnail.isNull()) {
249     bool add_thumbnail = true;
250     ThumbnailScore current_score;
251     if (ThumbnailScoreForId(id, &current_score)) {
252       add_thumbnail = ShouldReplaceThumbnailWith(current_score, score);
253     }
254 
255     if (add_thumbnail) {
256       sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
257           "INSERT OR REPLACE INTO thumbnails "
258           "(url_id, boring_score, good_clipping, at_top, last_updated, data) "
259           "VALUES (?,?,?,?,?,?)"));
260       if (!statement)
261         return;
262 
263       // We use 90 quality (out of 100) which is pretty high, because
264       // we're very sensitive to artifacts for these small sized,
265       // highly detailed images.
266       std::vector<unsigned char> jpeg_data;
267       SkAutoLockPixels thumbnail_lock(thumbnail);
268       bool encoded = gfx::JPEGCodec::Encode(
269           reinterpret_cast<unsigned char*>(thumbnail.getAddr32(0, 0)),
270           gfx::JPEGCodec::FORMAT_SkBitmap, thumbnail.width(),
271           thumbnail.height(),
272           static_cast<int>(thumbnail.rowBytes()), 90,
273           &jpeg_data);
274 
275       if (encoded) {
276         statement.BindInt64(0, id);
277         statement.BindDouble(1, score.boring_score);
278         statement.BindBool(2, score.good_clipping);
279         statement.BindBool(3, score.at_top);
280         statement.BindInt64(4, score.time_at_snapshot.ToTimeT());
281         statement.BindBlob(5, &jpeg_data[0],
282                            static_cast<int>(jpeg_data.size()));
283         if (!statement.Run())
284           NOTREACHED() << db_.GetErrorMessage();
285       }
286 
287       // Publish the thumbnail to any indexers listening to us.
288       // The tests may send an invalid url. Hence avoid publishing those.
289       if (url.is_valid() && history_publisher_ != NULL)
290         history_publisher_->PublishPageThumbnail(jpeg_data, url, time);
291     }
292   } else {
293     if (!DeleteThumbnail(id) )
294       DLOG(WARNING) << "Unable to delete thumbnail";
295   }
296 }
297 
GetPageThumbnail(URLID id,std::vector<unsigned char> * data)298 bool ThumbnailDatabase::GetPageThumbnail(URLID id,
299                                          std::vector<unsigned char>* data) {
300   if (use_top_sites_) {
301     LOG(WARNING) << "Use TopSites instead.";
302     return false;  // Not possible after migration to TopSites.
303   }
304 
305   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
306       "SELECT data FROM thumbnails WHERE url_id=?"));
307   if (!statement)
308     return false;
309 
310   statement.BindInt64(0, id);
311   if (!statement.Step())
312     return false;  // don't have a thumbnail for this ID
313 
314   statement.ColumnBlobAsVector(0, data);
315   return true;
316 }
317 
DeleteThumbnail(URLID id)318 bool ThumbnailDatabase::DeleteThumbnail(URLID id) {
319   if (use_top_sites_) {
320     return true;  // Not possible after migration to TopSites.
321   }
322 
323   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
324       "DELETE FROM thumbnails WHERE url_id = ?"));
325   if (!statement)
326     return false;
327 
328   statement.BindInt64(0, id);
329   return statement.Run();
330 }
331 
ThumbnailScoreForId(URLID id,ThumbnailScore * score)332 bool ThumbnailDatabase::ThumbnailScoreForId(URLID id,
333                                             ThumbnailScore* score) {
334   if (use_top_sites_) {
335     LOG(WARNING) << "Use TopSites instead.";
336     return false;  // Not possible after migration to TopSites.
337   }
338 
339   // Fetch the current thumbnail's information to make sure we
340   // aren't replacing a good thumbnail with one that's worse.
341   sql::Statement select_statement(db_.GetCachedStatement(SQL_FROM_HERE,
342       "SELECT boring_score, good_clipping, at_top, last_updated "
343       "FROM thumbnails WHERE url_id=?"));
344   if (!select_statement) {
345     NOTREACHED() << "Couldn't build select statement!";
346   } else {
347     select_statement.BindInt64(0, id);
348     if (select_statement.Step()) {
349       double current_boring_score = select_statement.ColumnDouble(0);
350       bool current_clipping = select_statement.ColumnBool(1);
351       bool current_at_top = select_statement.ColumnBool(2);
352       base::Time last_updated =
353           base::Time::FromTimeT(select_statement.ColumnInt64(3));
354       *score = ThumbnailScore(current_boring_score, current_clipping,
355                               current_at_top, last_updated);
356       return true;
357     }
358   }
359 
360   return false;
361 }
362 
SetFavicon(URLID icon_id,scoped_refptr<RefCountedMemory> icon_data,base::Time time)363 bool ThumbnailDatabase::SetFavicon(URLID icon_id,
364                                    scoped_refptr<RefCountedMemory> icon_data,
365                                    base::Time time) {
366   DCHECK(icon_id);
367   if (icon_data->size()) {
368     sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
369         "UPDATE favicons SET image_data=?, last_updated=? WHERE id=?"));
370     if (!statement)
371       return 0;
372 
373     statement.BindBlob(0, icon_data->front(),
374                        static_cast<int>(icon_data->size()));
375     statement.BindInt64(1, time.ToTimeT());
376     statement.BindInt64(2, icon_id);
377     return statement.Run();
378   } else {
379     sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
380         "UPDATE favicons SET image_data=NULL, last_updated=? WHERE id=?"));
381     if (!statement)
382       return 0;
383 
384     statement.BindInt64(0, time.ToTimeT());
385     statement.BindInt64(1, icon_id);
386     return statement.Run();
387   }
388 }
389 
SetFaviconLastUpdateTime(FaviconID icon_id,base::Time time)390 bool ThumbnailDatabase::SetFaviconLastUpdateTime(FaviconID icon_id,
391                                                  base::Time time) {
392   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
393       "UPDATE favicons SET last_updated=? WHERE id=?"));
394   if (!statement)
395     return 0;
396 
397   statement.BindInt64(0, time.ToTimeT());
398   statement.BindInt64(1, icon_id);
399   return statement.Run();
400 }
401 
GetFaviconIDForFaviconURL(const GURL & icon_url,int required_icon_type,IconType * icon_type)402 FaviconID ThumbnailDatabase::GetFaviconIDForFaviconURL(const GURL& icon_url,
403                                                        int required_icon_type,
404                                                        IconType* icon_type) {
405   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
406       "SELECT id, icon_type FROM favicons WHERE url=? AND (icon_type & ? > 0) "
407       "ORDER BY icon_type DESC"));
408   if (!statement)
409     return 0;
410 
411   statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url));
412   statement.BindInt(1, required_icon_type);
413   if (!statement.Step())
414     return 0;  // not cached
415 
416   if (icon_type)
417     *icon_type = static_cast<IconType>(statement.ColumnInt(1));
418   return statement.ColumnInt64(0);
419 }
420 
GetFavicon(FaviconID icon_id,base::Time * last_updated,std::vector<unsigned char> * png_icon_data,GURL * icon_url)421 bool ThumbnailDatabase::GetFavicon(
422     FaviconID icon_id,
423     base::Time* last_updated,
424     std::vector<unsigned char>* png_icon_data,
425     GURL* icon_url) {
426   DCHECK(icon_id);
427 
428   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
429       "SELECT last_updated, image_data, url FROM favicons WHERE id=?"));
430   if (!statement)
431     return 0;
432 
433   statement.BindInt64(0, icon_id);
434 
435   if (!statement.Step())
436     return false;  // No entry for the id.
437 
438   *last_updated = base::Time::FromTimeT(statement.ColumnInt64(0));
439   if (statement.ColumnByteLength(1) > 0)
440     statement.ColumnBlobAsVector(1, png_icon_data);
441   if (icon_url)
442     *icon_url = GURL(statement.ColumnString(2));
443 
444   return true;
445 }
446 
AddFavicon(const GURL & icon_url,IconType icon_type)447 FaviconID ThumbnailDatabase::AddFavicon(const GURL& icon_url,
448                                         IconType icon_type) {
449 
450   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
451       "INSERT INTO favicons (url, icon_type) VALUES (?, ?)"));
452   if (!statement)
453     return 0;
454 
455   statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url));
456   statement.BindInt(1, icon_type);
457   if (!statement.Run())
458     return 0;
459   return db_.GetLastInsertRowId();
460 }
461 
DeleteFavicon(FaviconID id)462 bool ThumbnailDatabase::DeleteFavicon(FaviconID id) {
463   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
464       "DELETE FROM favicons WHERE id = ?"));
465   if (!statement)
466     return false;
467 
468   statement.BindInt64(0, id);
469   return statement.Run();
470 }
471 
GetIconMappingForPageURL(const GURL & page_url,IconType required_icon_type,IconMapping * icon_mapping)472 bool ThumbnailDatabase::GetIconMappingForPageURL(const GURL& page_url,
473                                                  IconType required_icon_type,
474                                                  IconMapping* icon_mapping) {
475   std::vector<IconMapping> icon_mappings;
476   if (!GetIconMappingsForPageURL(page_url, &icon_mappings))
477     return false;
478 
479   for (std::vector<IconMapping>::iterator m = icon_mappings.begin();
480       m != icon_mappings.end(); ++m) {
481     if (m->icon_type == required_icon_type) {
482       if (icon_mapping != NULL)
483         *icon_mapping = *m;
484       return true;
485     }
486   }
487 
488   return false;
489 }
490 
GetIconMappingsForPageURL(const GURL & page_url,std::vector<IconMapping> * mapping_data)491 bool ThumbnailDatabase::GetIconMappingsForPageURL(
492     const GURL& page_url,
493     std::vector<IconMapping>* mapping_data) {
494   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
495       "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type "
496       "FROM icon_mapping "
497       "INNER JOIN favicons "
498       "ON icon_mapping.icon_id = favicons.id "
499       "WHERE icon_mapping.page_url=? "
500       "ORDER BY favicons.icon_type DESC"));
501   if (!statement)
502     return false;
503 
504   statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
505 
506   bool result = false;
507   while (statement.Step()) {
508     result = true;
509     if (!mapping_data)
510       return result;
511 
512     IconMapping icon_mapping;
513     FillIconMapping(statement, page_url, &icon_mapping);
514     mapping_data->push_back(icon_mapping);
515   }
516   return result;
517 }
518 
AddIconMapping(const GURL & page_url,FaviconID icon_id)519 IconMappingID ThumbnailDatabase::AddIconMapping(const GURL& page_url,
520                                                 FaviconID icon_id) {
521   return AddIconMapping(page_url, icon_id, false);
522 }
523 
UpdateIconMapping(IconMappingID mapping_id,FaviconID icon_id)524 bool ThumbnailDatabase::UpdateIconMapping(IconMappingID mapping_id,
525                                           FaviconID icon_id) {
526   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
527       "UPDATE icon_mapping SET icon_id=? WHERE id=?"));
528   if (!statement)
529     return 0;
530 
531   statement.BindInt64(0, icon_id);
532   statement.BindInt64(1, mapping_id);
533   return statement.Run();
534 }
535 
DeleteIconMappings(const GURL & page_url)536 bool ThumbnailDatabase::DeleteIconMappings(const GURL& page_url) {
537   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
538       "DELETE FROM icon_mapping WHERE page_url = ?"));
539   if (!statement)
540     return false;
541 
542   statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
543   return statement.Run();
544 }
545 
HasMappingFor(FaviconID id)546 bool ThumbnailDatabase::HasMappingFor(FaviconID id) {
547   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
548       "SELECT id FROM icon_mapping "
549       "WHERE icon_id=?"));
550   if (!statement)
551     return false;
552 
553   statement.BindInt64(0, id);
554   return statement.Step();
555 }
556 
MigrateIconMappingData(URLDatabase * url_db)557 bool ThumbnailDatabase::MigrateIconMappingData(URLDatabase* url_db) {
558   URLDatabase::IconMappingEnumerator e;
559   if (!url_db->InitIconMappingEnumeratorForEverything(&e))
560     return false;
561 
562   IconMapping info;
563   while (e.GetNextIconMapping(&info)) {
564     // TODO: Using bulk insert to improve the performance.
565     if (!AddIconMapping(info.page_url, info.icon_id))
566       return false;
567   }
568   return true;
569 }
570 
AddToTemporaryIconMappingTable(const GURL & page_url,const FaviconID icon_id)571 IconMappingID ThumbnailDatabase::AddToTemporaryIconMappingTable(
572     const GURL& page_url, const FaviconID icon_id) {
573   return AddIconMapping(page_url, icon_id, true);
574 }
575 
CommitTemporaryIconMappingTable()576 bool ThumbnailDatabase::CommitTemporaryIconMappingTable() {
577   // Delete the old icon_mapping table.
578   if (!db_.Execute("DROP TABLE icon_mapping"))
579     return false;
580 
581   // Rename the temporary one.
582   if (!db_.Execute("ALTER TABLE temp_icon_mapping RENAME TO icon_mapping"))
583     return false;
584 
585   // The renamed table needs the index (the temporary table doesn't have one).
586   InitIconMappingIndex();
587 
588   return true;
589 }
590 
CopyToTemporaryFaviconTable(FaviconID source)591 FaviconID ThumbnailDatabase::CopyToTemporaryFaviconTable(FaviconID source) {
592   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
593       "INSERT INTO temp_favicons (url, last_updated, image_data, icon_type)"
594       "SELECT url, last_updated, image_data, icon_type "
595       "FROM favicons WHERE id = ?"));
596   if (!statement)
597     return 0;
598   statement.BindInt64(0, source);
599   if (!statement.Run())
600     return 0;
601 
602   // We return the ID of the newly inserted favicon.
603   return db_.GetLastInsertRowId();
604 }
605 
CommitTemporaryFaviconTable()606 bool ThumbnailDatabase::CommitTemporaryFaviconTable() {
607   // Delete the old favicons table.
608   if (!db_.Execute("DROP TABLE favicons"))
609     return false;
610 
611   // Rename the temporary one.
612   if (!db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons"))
613     return false;
614 
615   // The renamed table needs the index (the temporary table doesn't have one).
616   InitFaviconsIndex();
617   return true;
618 }
619 
NeedsMigrationToTopSites()620 bool ThumbnailDatabase::NeedsMigrationToTopSites() {
621   return !use_top_sites_;
622 }
623 
RenameAndDropThumbnails(const FilePath & old_db_file,const FilePath & new_db_file)624 bool ThumbnailDatabase::RenameAndDropThumbnails(const FilePath& old_db_file,
625                                                 const FilePath& new_db_file) {
626   // Init favicons table - same schema as the thumbnails.
627   sql::Connection favicons;
628   if (OpenDatabase(&favicons, new_db_file) != sql::INIT_OK)
629     return false;
630 
631   if (!InitFaviconsTable(&favicons, false) ||
632       !InitIconMappingTable(&favicons, false)) {
633     NOTREACHED() << "Couldn't init favicons and icon-mapping table.";
634     favicons.Close();
635     return false;
636   }
637   favicons.Close();
638 
639   // Can't attach within a transaction.
640   if (transaction_nesting())
641     CommitTransaction();
642 
643   // Attach new DB.
644   {
645     // This block is needed because otherwise the attach statement is
646     // never cleared from cache and we can't close the DB :P
647     sql::Statement attach(db_.GetUniqueStatement("ATTACH ? AS new_favicons"));
648     if (!attach) {
649       NOTREACHED() << "Unable to attach database.";
650       // Keep the transaction open, even though we failed.
651       BeginTransaction();
652       return false;
653     }
654 
655 #if defined(OS_POSIX)
656     attach.BindString(0, new_db_file.value());
657 #else
658     attach.BindString(0, WideToUTF8(new_db_file.value()));
659 #endif
660 
661     if (!attach.Run()) {
662       NOTREACHED() << db_.GetErrorMessage();
663       BeginTransaction();
664       return false;
665     }
666   }
667 
668   // Move favicons to the new DB.
669   if (!db_.Execute("INSERT OR REPLACE INTO new_favicons.favicons "
670                    "SELECT * FROM favicons")) {
671     NOTREACHED() << "Unable to copy favicons.";
672     BeginTransaction();
673     return false;
674   }
675 
676   if (!db_.Execute("DETACH new_favicons")) {
677     NOTREACHED() << "Unable to detach database.";
678     BeginTransaction();
679     return false;
680   }
681 
682   db_.Close();
683 
684   // Reset the DB to point to new file.
685   if (OpenDatabase(&db_, new_db_file) != sql::INIT_OK)
686     return false;
687 
688   file_util::Delete(old_db_file, false);
689 
690   InitFaviconsIndex();
691 
692   // Reopen the transaction.
693   BeginTransaction();
694   use_top_sites_ = true;
695   return true;
696 }
697 
InitIconMappingTable(sql::Connection * db,bool is_temporary)698 bool ThumbnailDatabase::InitIconMappingTable(sql::Connection* db,
699                                              bool is_temporary) {
700   const char* name = is_temporary ? "temp_icon_mapping" : "icon_mapping";
701   if (!db->DoesTableExist(name)) {
702     std::string sql;
703     sql.append("CREATE TABLE ");
704     sql.append(name);
705     sql.append("("
706                "id INTEGER PRIMARY KEY,"
707                "page_url LONGVARCHAR NOT NULL,"
708                "icon_id INTEGER)");
709     if (!db->Execute(sql.c_str()))
710       return false;
711   }
712   return true;
713 }
714 
InitIconMappingIndex()715 void ThumbnailDatabase::InitIconMappingIndex() {
716   // Add an index on the url column. We ignore errors. Since this is always
717   // called during startup, the index will normally already exist.
718   db_.Execute("CREATE INDEX icon_mapping_page_url_idx"
719               " ON icon_mapping(page_url)");
720   db_.Execute("CREATE INDEX icon_mapping_icon_id_idx ON icon_mapping(icon_id)");
721 }
722 
AddIconMapping(const GURL & page_url,FaviconID icon_id,bool is_temporary)723 IconMappingID ThumbnailDatabase::AddIconMapping(const GURL& page_url,
724                                                 FaviconID icon_id,
725                                                 bool is_temporary) {
726   const char* name = is_temporary ? "temp_icon_mapping" : "icon_mapping";
727   const char* statement_name =
728       is_temporary ? "add_temp_icon_mapping" : "add_icon_mapping";
729 
730   std::string sql;
731   sql.append("INSERT INTO ");
732   sql.append(name);
733   sql.append("(page_url, icon_id) VALUES (?, ?)");
734 
735   sql::Statement statement(
736       db_.GetCachedStatement(sql::StatementID(statement_name), sql.c_str()));
737   if (!statement)
738     return 0;
739 
740   statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
741   statement.BindInt64(1, icon_id);
742 
743   if (!statement.Run())
744     return 0;
745 
746   return db_.GetLastInsertRowId();
747 }
748 
UpgradeToVersion4()749 bool ThumbnailDatabase::UpgradeToVersion4() {
750   // Set the default icon type as favicon, so the current data are set
751   // correctly.
752   if (!db_.Execute("ALTER TABLE favicons ADD icon_type INTEGER DEFAULT 1")) {
753     NOTREACHED();
754     return false;
755   }
756   meta_table_.SetVersionNumber(4);
757   meta_table_.SetCompatibleVersionNumber(std::min(4, kCompatibleVersionNumber));
758   return true;
759 }
760 
761 }  // namespace history
762