Index: components/enhanced_bookmarks/persistent_image_store.cc |
diff --git a/components/enhanced_bookmarks/persistent_image_store.cc b/components/enhanced_bookmarks/persistent_image_store.cc |
index 0108c85fc6549013e878284c10b4f4925af4e04c..1061b69f8b9fd01632589160f90269666e5353a7 100644 |
--- a/components/enhanced_bookmarks/persistent_image_store.cc |
+++ b/components/enhanced_bookmarks/persistent_image_store.cc |
@@ -5,6 +5,7 @@ |
#include "components/enhanced_bookmarks/persistent_image_store.h" |
#include "base/files/file.h" |
+#include "base/logging.h" |
#include "components/enhanced_bookmarks/image_store_util.h" |
#include "sql/statement.h" |
#include "sql/transaction.h" |
@@ -12,6 +13,11 @@ |
#include "url/gurl.h" |
namespace { |
+// Current version number. Databases are written at the "current" version |
+// number, but any previous version that can read the "compatible" one can make |
+// do with our database without *too* many bad effects. |
+const int kCurrentVersionNumber = 2; |
+const int kCompatibleVersionNumber = 1; |
bool InitTables(sql::Connection& db) { |
const char kTableSql[] = |
@@ -20,7 +26,8 @@ bool InitTables(sql::Connection& db) { |
"image_url LONGVARCHAR NOT NULL," |
"image_data BLOB," |
"width INTEGER," |
- "height INTEGER" |
+ "height INTEGER," |
+ "image_dominant_color INTEGER" |
")"; |
if (!db.Execute(kTableSql)) |
return false; |
@@ -35,7 +42,60 @@ bool InitIndices(sql::Connection& db) { |
return true; |
} |
+// V1 didn't store the dominant color of an image. Creates the column to store |
+// a dominant color in the database. The value will be filled when queried for a |
+// one time cost. |
+bool MigrateImagesWithNoDominantColor(sql::Connection& db) { |
+ if (!db.DoesTableExist("images_by_url")) { |
+ NOTREACHED() << "images_by_url table should exist before migration."; |
+ return false; |
+ } |
+ |
+ if (!db.DoesColumnExist("images_by_url", "image_dominant_color")) { |
+ // The initial version doesn't have the image_dominant_color column, it is |
+ // added to the table here. |
+ if (!db.Execute( |
+ "ALTER TABLE images_by_url " |
+ "ADD COLUMN image_dominant_color INTEGER")) { |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+sql::InitStatus EnsureCurrentVersion(sql::Connection& db, |
+ sql::MetaTable& meta_table) { |
+ // 1- Newer databases than designed for can't be read. |
+ if (meta_table.GetCompatibleVersionNumber() > kCurrentVersionNumber) { |
+ LOG(WARNING) << "Image DB is too new."; |
+ return sql::INIT_TOO_NEW; |
+ } |
+ |
+ int cur_version = meta_table.GetVersionNumber(); |
+ |
+ // 2- Put migration code here. |
+ |
+ if (cur_version == 1) { |
+ if (!MigrateImagesWithNoDominantColor(db)) { |
+ LOG(WARNING) << "Unable to update image DB to version 1."; |
+ return sql::INIT_FAILURE; |
+ } |
+ ++cur_version; |
+ meta_table.SetVersionNumber(cur_version); |
+ meta_table.SetCompatibleVersionNumber( |
+ std::min(cur_version, kCompatibleVersionNumber)); |
+ } |
+ |
+ // 3- When the version is too old, just try to continue anyway, there should |
+ // not be a released product that makes a database too old to handle. |
+ LOG_IF(WARNING, cur_version < kCurrentVersionNumber) |
+ << "Image DB version " << cur_version << " is too old to handle."; |
+ |
+ return sql::INIT_OK; |
+} |
+ |
sql::InitStatus OpenDatabaseImpl(sql::Connection& db, |
+ sql::MetaTable& meta_table, |
const base::FilePath& db_path) { |
DCHECK(!db.is_open()); |
@@ -44,22 +104,33 @@ sql::InitStatus OpenDatabaseImpl(sql::Connection& db, |
// TODO(noyau): Set error callback? |
// Run the database in exclusive mode. Nobody else should be accessing the |
- // database while we're running, and this will give somewhat improved perf. |
+ // database while running, and this will give somewhat improved performance. |
db.set_exclusive_locking(); |
if (!db.Open(db_path)) |
return sql::INIT_FAILURE; |
- // Scope initialization in a transaction so we can't be partially initialized. |
+ // Scope initialization in a transaction so it can't be partially initialized. |
sql::Transaction transaction(&db); |
if (!transaction.Begin()) |
return sql::INIT_FAILURE; |
+ // Initialize the meta table. |
+ int cur_version = meta_table.DoesTableExist(&db) |
+ ? kCurrentVersionNumber |
+ : 1; // Only v1 didn't have a meta table. |
+ if (!meta_table.Init(&db, cur_version, |
+ std::min(cur_version, kCompatibleVersionNumber))) |
+ return sql::INIT_FAILURE; |
+ |
// Create the tables. |
- if (!InitTables(db) || |
- !InitIndices(db)) { |
+ if (!InitTables(db) || !InitIndices(db)) |
return sql::INIT_FAILURE; |
- } |
+ |
+ // Check the version. |
+ sql::InitStatus version_status = EnsureCurrentVersion(db, meta_table); |
+ if (version_status != sql::INIT_OK) |
+ return version_status; |
// Initialization is complete. |
if (!transaction.Commit()) |
@@ -90,24 +161,25 @@ bool PersistentImageStore::HasKey(const GURL& page_url) { |
return !!count; |
} |
-void PersistentImageStore::Insert(const GURL& page_url, |
- const GURL& image_url, |
- const gfx::Image& image) { |
+void PersistentImageStore::Insert( |
+ const GURL& page_url, |
+ const enhanced_bookmarks::ImageRecord& record) { |
DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
if (OpenDatabase() != sql::INIT_OK) |
return; |
Erase(page_url); // Remove previous image for this url, if any. |
- sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, |
+ sql::Statement statement(db_.GetCachedStatement( |
+ SQL_FROM_HERE, |
"INSERT INTO images_by_url " |
- "(page_url, image_url, image_data, width, height)" |
- "VALUES (?, ?, ?, ?, ?)")); |
+ "(page_url, image_url, image_data, width, height, image_dominant_color)" |
+ "VALUES (?, ?, ?, ?, ?, ?)")); |
statement.BindString(0, page_url.possibly_invalid_spec()); |
- statement.BindString(1, image_url.possibly_invalid_spec()); |
+ statement.BindString(1, record.url.possibly_invalid_spec()); |
scoped_refptr<base::RefCountedMemory> image_bytes = |
- enhanced_bookmarks::BytesForImage(image); |
+ enhanced_bookmarks::BytesForImage(record.image); |
// Insert an empty image in case encoding fails. |
if (!image_bytes.get()) |
@@ -117,8 +189,9 @@ void PersistentImageStore::Insert(const GURL& page_url, |
statement.BindBlob(2, image_bytes->front(), (int)image_bytes->size()); |
- statement.BindInt(3, image.Size().width()); |
- statement.BindInt(4, image.Size().height()); |
+ statement.BindInt(3, record.image.Size().width()); |
+ statement.BindInt(4, record.image.Size().height()); |
+ statement.BindInt(5, record.dominant_color); |
statement.Run(); |
} |
@@ -133,27 +206,59 @@ void PersistentImageStore::Erase(const GURL& page_url) { |
statement.Run(); |
} |
-std::pair<gfx::Image, GURL> PersistentImageStore::Get(const GURL& page_url) { |
+enhanced_bookmarks::ImageRecord PersistentImageStore::Get( |
+ const GURL& page_url) { |
DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ enhanced_bookmarks::ImageRecord image_record; |
if (OpenDatabase() != sql::INIT_OK) |
- return std::make_pair(gfx::Image(), GURL()); |
+ return image_record; |
- sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, |
- "SELECT image_data, image_url FROM images_by_url WHERE page_url = ?")); |
+ bool stored_image_record_needs_update = false; |
- statement.BindString(0, page_url.possibly_invalid_spec()); |
+ // Scope the SELECT statement. |
+ { |
+ sql::Statement statement(db_.GetCachedStatement( |
+ SQL_FROM_HERE, |
+ "SELECT image_data, image_url, image_dominant_color FROM images_by_url " |
+ "WHERE page_url = ?")); |
- while (statement.Step()) { |
+ statement.BindString(0, page_url.possibly_invalid_spec()); |
+ |
+ if (!statement.Step()) |
+ return image_record; |
+ |
+ // Image. |
if (statement.ColumnByteLength(0) > 0) { |
scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes()); |
statement.ColumnBlobAsVector(0, &data->data()); |
+ image_record.image = enhanced_bookmarks::ImageForBytes(data); |
+ } |
- return std::make_pair(enhanced_bookmarks::ImageForBytes(data), |
- GURL(statement.ColumnString(1))); |
+ // URL. |
+ image_record.url = GURL(statement.ColumnString(1)); |
+ |
+ // Dominant color. |
+ if (statement.ColumnType(2) != sql::COLUMN_TYPE_NULL) { |
+ image_record.dominant_color = SkColor(statement.ColumnInt(2)); |
+ } else { |
+ // The dominant color was not computed when the image was first |
+ // stored. |
+ // Compute it now. |
+ image_record.dominant_color = |
+ enhanced_bookmarks::DominantColorForImage(image_record.image); |
+ stored_image_record_needs_update = true; |
} |
+ |
+ // Make sure there is only one record for page_url. |
+ DCHECK(!statement.Step()); |
+ } |
+ |
+ if (stored_image_record_needs_update) { |
+ Erase(page_url); |
+ Insert(page_url, image_record); |
} |
- return std::make_pair(gfx::Image(), GURL()); |
+ return image_record; |
} |
gfx::Size PersistentImageStore::GetSize(const GURL& page_url) { |
@@ -217,7 +322,7 @@ sql::InitStatus PersistentImageStore::OpenDatabase() { |
sql::InitStatus status = sql::INIT_FAILURE; |
for (size_t i = 0; i < kAttempts; ++i) { |
- status = OpenDatabaseImpl(db_, path_); |
+ status = OpenDatabaseImpl(db_, meta_table_, path_); |
if (status == sql::INIT_OK) |
return status; |