This source file includes following definitions.
- FillIconMapping
- RecordInvalidStructure
- DumpWithoutCrashing2000
- ReportCorrupt
- ReportError
- GenerateDiagnostics
- InitTables
- InitIndices
- RecordRecoveryEvent
- RecoverDatabaseOrRaze
- DatabaseErrorCallback
- GetNextIconMapping
- ComputeDatabaseMetrics
- BeginTransaction
- CommitTransaction
- RollbackTransaction
- Vacuum
- TrimMemory
- GetFaviconBitmapIDSizes
- GetFaviconBitmaps
- GetFaviconBitmap
- AddFaviconBitmap
- SetFaviconBitmap
- SetFaviconBitmapLastUpdateTime
- DeleteFaviconBitmap
- SetFaviconOutOfDate
- GetFaviconIDForFaviconURL
- GetFaviconHeader
- AddFavicon
- AddFavicon
- DeleteFavicon
- GetIconMappingsForPageURL
- GetIconMappingsForPageURL
- AddIconMapping
- UpdateIconMapping
- DeleteIconMappings
- DeleteIconMapping
- HasMappingFor
- CloneIconMappings
- InitIconMappingEnumerator
- RetainDataForPageUrls
- OpenDatabase
- InitImpl
- CantUpgradeToVersion
- UpgradeToVersion6
- UpgradeToVersion7
- IsFaviconDBStructureIncorrect
#include "chrome/browser/history/thumbnail_database.h"
#include <algorithm>
#include <string>
#include "base/bind.h"
#include "base/debug/alias.h"
#include "base/debug/dump_without_crashing.h"
#include "base/file_util.h"
#include "base/format_macros.h"
#include "base/memory/ref_counted_memory.h"
#include "base/metrics/histogram.h"
#include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "chrome/browser/history/url_database.h"
#include "chrome/common/chrome_version_info.h"
#include "sql/recovery.h"
#include "sql/statement.h"
#include "sql/transaction.h"
#include "third_party/sqlite/sqlite3.h"
#if defined(OS_MACOSX)
#include "base/mac/mac_util.h"
#endif
namespace {
const int kCurrentVersionNumber = 7;
const int kCompatibleVersionNumber = 7;
const int kDeprecatedVersionNumber = 4;
void FillIconMapping(const sql::Statement& statement,
const GURL& page_url,
history::IconMapping* icon_mapping) {
icon_mapping->mapping_id = statement.ColumnInt64(0);
icon_mapping->icon_id = statement.ColumnInt64(1);
icon_mapping->icon_type =
static_cast<chrome::IconType>(statement.ColumnInt(2));
icon_mapping->icon_url = GURL(statement.ColumnString(3));
icon_mapping->page_url = page_url;
}
enum InvalidStructureType {
STRUCTURE_EVENT_FAVICON = 1,
STRUCTURE_EVENT_VERSION4,
STRUCTURE_EVENT_VERSION5,
STRUCTURE_EVENT_MAX,
};
void RecordInvalidStructure(InvalidStructureType invalid_type) {
UMA_HISTOGRAM_ENUMERATION("History.InvalidFaviconsDBStructure",
invalid_type, STRUCTURE_EVENT_MAX);
}
void DumpWithoutCrashing2000(const std::string& debug_info) {
char debug_buf[2000];
base::strlcpy(debug_buf, debug_info.c_str(), arraysize(debug_buf));
base::debug::Alias(&debug_buf);
base::debug::DumpWithoutCrashing();
}
void ReportCorrupt(sql::Connection* db, size_t startup_kb) {
std::string debug_info;
base::StringAppendF(&debug_info, "SQLITE_CORRUPT, integrity_check:\n");
const size_t kMaxIntegrityCheckSize = 8192;
if (startup_kb > kMaxIntegrityCheckSize) {
base::StringAppendF(&debug_info, "too big %" PRIuS "\n", startup_kb);
} else {
std::vector<std::string> messages;
const base::TimeTicks before = base::TimeTicks::Now();
db->FullIntegrityCheck(&messages);
base::StringAppendF(&debug_info, "# %" PRIx64 " ms, %" PRIuS " records\n",
(base::TimeTicks::Now() - before).InMilliseconds(),
messages.size());
const size_t kMaxMessages = 20;
for (size_t i = 0; i < kMaxMessages && i < messages.size(); ++i) {
base::StringAppendF(&debug_info, "%s\n", messages[i].c_str());
}
}
DumpWithoutCrashing2000(debug_info);
}
void ReportError(sql::Connection* db, int error) {
std::string debug_info;
base::StringAppendF(&debug_info, "db error: %d/%s\n",
db->GetErrorCode(), db->GetErrorMessage());
base::StringAppendF(&debug_info, "errno: %d\n", db->GetLastErrno());
if (error == SQLITE_ERROR) {
const char* kVersionSql = "SELECT value FROM meta WHERE key = 'version'";
if (db->IsSQLValid(kVersionSql)) {
sql::Statement statement(db->GetUniqueStatement(kVersionSql));
if (statement.Step()) {
debug_info += "version: ";
debug_info += statement.ColumnString(0);
debug_info += '\n';
} else if (statement.Succeeded()) {
debug_info += "version: none\n";
} else {
debug_info += "version: error\n";
}
} else {
debug_info += "version: invalid\n";
}
debug_info += "schema:\n";
const char* kSchemaSql = "SELECT COALESCE(sql, name) FROM sqlite_master";
sql::Statement statement(db->GetUniqueStatement(kSchemaSql));
while (statement.Step()) {
debug_info += statement.ColumnString(0);
debug_info += '\n';
}
if (!statement.Succeeded())
debug_info += "error\n";
}
DumpWithoutCrashing2000(debug_info);
}
void GenerateDiagnostics(sql::Connection* db,
size_t startup_kb,
int extended_error) {
int error = (extended_error & 0xFF);
static const uint64 kReportsPerMillion = 50000;
static bool reported = false;
if (reported)
return;
uint64 rand = base::RandGenerator(1000000);
if (error == SQLITE_CORRUPT) {
reported = true;
static const uint64 kCorruptReportsPerMillion = 10000;
if (rand < kCorruptReportsPerMillion)
ReportCorrupt(db, startup_kb);
} else if (error == SQLITE_READONLY) {
reported = true;
if (rand < kReportsPerMillion)
ReportError(db, extended_error);
} else {
if (rand < kReportsPerMillion) {
reported = true;
ReportError(db, extended_error);
}
}
}
bool InitTables(sql::Connection* db) {
const char kIconMappingSql[] =
"CREATE TABLE IF NOT EXISTS icon_mapping"
"("
"id INTEGER PRIMARY KEY,"
"page_url LONGVARCHAR NOT NULL,"
"icon_id INTEGER"
")";
if (!db->Execute(kIconMappingSql))
return false;
const char kFaviconsSql[] =
"CREATE TABLE IF NOT EXISTS favicons"
"("
"id INTEGER PRIMARY KEY,"
"url LONGVARCHAR NOT NULL,"
"icon_type INTEGER DEFAULT 1"
")";
if (!db->Execute(kFaviconsSql))
return false;
const char kFaviconBitmapsSql[] =
"CREATE TABLE IF NOT EXISTS favicon_bitmaps"
"("
"id INTEGER PRIMARY KEY,"
"icon_id INTEGER NOT NULL,"
"last_updated INTEGER DEFAULT 0,"
"image_data BLOB,"
"width INTEGER DEFAULT 0,"
"height INTEGER DEFAULT 0"
")";
if (!db->Execute(kFaviconBitmapsSql))
return false;
return true;
}
bool InitIndices(sql::Connection* db) {
const char kIconMappingUrlIndexSql[] =
"CREATE INDEX IF NOT EXISTS icon_mapping_page_url_idx"
" ON icon_mapping(page_url)";
const char kIconMappingIdIndexSql[] =
"CREATE INDEX IF NOT EXISTS icon_mapping_icon_id_idx"
" ON icon_mapping(icon_id)";
if (!db->Execute(kIconMappingUrlIndexSql) ||
!db->Execute(kIconMappingIdIndexSql)) {
return false;
}
const char kFaviconsIndexSql[] =
"CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)";
if (!db->Execute(kFaviconsIndexSql))
return false;
const char kFaviconBitmapsIndexSql[] =
"CREATE INDEX IF NOT EXISTS favicon_bitmaps_icon_id ON "
"favicon_bitmaps(icon_id)";
if (!db->Execute(kFaviconBitmapsIndexSql))
return false;
return true;
}
enum RecoveryEventType {
RECOVERY_EVENT_RECOVERED = 0,
RECOVERY_EVENT_FAILED_SCOPER,
RECOVERY_EVENT_FAILED_META_VERSION_ERROR,
RECOVERY_EVENT_FAILED_META_VERSION_NONE,
RECOVERY_EVENT_FAILED_META_WRONG_VERSION6,
RECOVERY_EVENT_FAILED_META_WRONG_VERSION5,
RECOVERY_EVENT_FAILED_META_WRONG_VERSION,
RECOVERY_EVENT_FAILED_RECOVER_META,
RECOVERY_EVENT_FAILED_META_INSERT,
RECOVERY_EVENT_FAILED_INIT,
RECOVERY_EVENT_FAILED_RECOVER_FAVICONS,
RECOVERY_EVENT_FAILED_FAVICONS_INSERT,
RECOVERY_EVENT_FAILED_RECOVER_FAVICON_BITMAPS,
RECOVERY_EVENT_FAILED_FAVICON_BITMAPS_INSERT,
RECOVERY_EVENT_FAILED_RECOVER_ICON_MAPPING,
RECOVERY_EVENT_FAILED_ICON_MAPPING_INSERT,
RECOVERY_EVENT_RECOVERED_VERSION6,
RECOVERY_EVENT_FAILED_META_INIT,
RECOVERY_EVENT_FAILED_META_VERSION,
RECOVERY_EVENT_DEPRECATED,
RECOVERY_EVENT_FAILED_V5_INITSCHEMA,
RECOVERY_EVENT_FAILED_V5_AUTORECOVER_FAVICONS,
RECOVERY_EVENT_FAILED_V5_AUTORECOVER_ICON_MAPPING,
RECOVERY_EVENT_RECOVERED_VERSION5,
RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS,
RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICON_BITMAPS,
RECOVERY_EVENT_FAILED_AUTORECOVER_ICON_MAPPING,
RECOVERY_EVENT_FAILED_COMMIT,
RECOVERY_EVENT_MAX,
};
void RecordRecoveryEvent(RecoveryEventType recovery_event) {
UMA_HISTOGRAM_ENUMERATION("History.FaviconsRecovery",
recovery_event, RECOVERY_EVENT_MAX);
}
void RecoverDatabaseOrRaze(sql::Connection* db, const base::FilePath& db_path) {
DCHECK_EQ(7, kCurrentVersionNumber);
db->reset_error_callback();
size_t favicons_rows_recovered = 0;
size_t favicon_bitmaps_rows_recovered = 0;
size_t icon_mapping_rows_recovered = 0;
int64 original_size = 0;
base::GetFileSize(db_path, &original_size);
scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
if (!recovery) {
RecordRecoveryEvent(RECOVERY_EVENT_FAILED_SCOPER);
return;
}
int version = 0;
if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) {
sql::Recovery::Rollback(recovery.Pass());
RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_VERSION);
return;
}
DCHECK_LE(kDeprecatedVersionNumber, 6);
if (version <= 6) {
sql::Recovery::Unrecoverable(recovery.Pass());
RecordRecoveryEvent(RECOVERY_EVENT_DEPRECATED);
return;
}
if (version != 7) {
sql::Recovery::Unrecoverable(recovery.Pass());
RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION);
return;
}
sql::MetaTable recover_meta_table;
if (!recover_meta_table.Init(recovery->db(), kCurrentVersionNumber,
kCompatibleVersionNumber)) {
sql::Recovery::Rollback(recovery.Pass());
RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT);
return;
}
if (!InitTables(recovery->db()) || !InitIndices(recovery->db())) {
sql::Recovery::Rollback(recovery.Pass());
RecordRecoveryEvent(RECOVERY_EVENT_FAILED_INIT);
return;
}
if (!recovery->AutoRecoverTable("favicons", 0, &favicons_rows_recovered)) {
sql::Recovery::Rollback(recovery.Pass());
RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS);
return;
}
if (!recovery->AutoRecoverTable("favicon_bitmaps", 0,
&favicon_bitmaps_rows_recovered)) {
sql::Recovery::Rollback(recovery.Pass());
RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICON_BITMAPS);
return;
}
if (!recovery->AutoRecoverTable("icon_mapping", 0,
&icon_mapping_rows_recovered)) {
sql::Recovery::Rollback(recovery.Pass());
RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_ICON_MAPPING);
return;
}
if (!sql::Recovery::Recovered(recovery.Pass())) {
RecordRecoveryEvent(RECOVERY_EVENT_FAILED_COMMIT);
return;
}
int64 final_size = 0;
if (original_size > 0 &&
base::GetFileSize(db_path, &final_size) &&
final_size > 0) {
int percentage = static_cast<int>(original_size * 100 / final_size);
UMA_HISTOGRAM_PERCENTAGE("History.FaviconsRecoveredPercentage",
std::max(100, percentage));
}
UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFavicons",
favicons_rows_recovered);
UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFaviconBitmaps",
favicon_bitmaps_rows_recovered);
UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsIconMapping",
icon_mapping_rows_recovered);
RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED);
}
void DatabaseErrorCallback(sql::Connection* db,
const base::FilePath& db_path,
size_t startup_kb,
int extended_error,
sql::Statement* stmt) {
chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
if (channel != chrome::VersionInfo::CHANNEL_STABLE &&
channel != chrome::VersionInfo::CHANNEL_BETA) {
GenerateDiagnostics(db, startup_kb, extended_error);
}
int error = (extended_error & 0xFF);
if (error == SQLITE_CORRUPT ||
error == SQLITE_CANTOPEN ||
error == SQLITE_NOTADB) {
RecoverDatabaseOrRaze(db, db_path);
}
if (!sql::Connection::ShouldIgnoreSqliteError(extended_error))
DLOG(FATAL) << db->GetErrorMessage();
}
}
namespace history {
ThumbnailDatabase::IconMappingEnumerator::IconMappingEnumerator() {
}
ThumbnailDatabase::IconMappingEnumerator::~IconMappingEnumerator() {
}
bool ThumbnailDatabase::IconMappingEnumerator::GetNextIconMapping(
IconMapping* icon_mapping) {
if (!statement_.Step())
return false;
FillIconMapping(statement_, GURL(statement_.ColumnString(4)), icon_mapping);
return true;
}
ThumbnailDatabase::ThumbnailDatabase() {
}
ThumbnailDatabase::~ThumbnailDatabase() {
}
sql::InitStatus ThumbnailDatabase::Init(const base::FilePath& db_name) {
const size_t kAttempts = 2;
sql::InitStatus status = sql::INIT_FAILURE;
for (size_t i = 0; i < kAttempts; ++i) {
status = InitImpl(db_name);
if (status == sql::INIT_OK)
return status;
meta_table_.Reset();
db_.Close();
}
return status;
}
void ThumbnailDatabase::ComputeDatabaseMetrics() {
sql::Statement favicon_count(
db_.GetCachedStatement(SQL_FROM_HERE, "SELECT COUNT(*) FROM favicons"));
UMA_HISTOGRAM_COUNTS_10000(
"History.NumFaviconsInDB",
favicon_count.Step() ? favicon_count.ColumnInt(0) : 0);
}
void ThumbnailDatabase::BeginTransaction() {
db_.BeginTransaction();
}
void ThumbnailDatabase::CommitTransaction() {
db_.CommitTransaction();
}
void ThumbnailDatabase::RollbackTransaction() {
db_.RollbackTransaction();
}
void ThumbnailDatabase::Vacuum() {
DCHECK(db_.transaction_nesting() == 0) <<
"Can not have a transaction when vacuuming.";
ignore_result(db_.Execute("VACUUM"));
}
void ThumbnailDatabase::TrimMemory(bool aggressively) {
db_.TrimMemory(aggressively);
}
bool ThumbnailDatabase::GetFaviconBitmapIDSizes(
chrome::FaviconID icon_id,
std::vector<FaviconBitmapIDSize>* bitmap_id_sizes) {
DCHECK(icon_id);
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"SELECT id, width, height FROM favicon_bitmaps WHERE icon_id=?"));
statement.BindInt64(0, icon_id);
bool result = false;
while (statement.Step()) {
result = true;
if (!bitmap_id_sizes)
return result;
FaviconBitmapIDSize bitmap_id_size;
bitmap_id_size.bitmap_id = statement.ColumnInt64(0);
bitmap_id_size.pixel_size = gfx::Size(statement.ColumnInt(1),
statement.ColumnInt(2));
bitmap_id_sizes->push_back(bitmap_id_size);
}
return result;
}
bool ThumbnailDatabase::GetFaviconBitmaps(
chrome::FaviconID icon_id,
std::vector<FaviconBitmap>* favicon_bitmaps) {
DCHECK(icon_id);
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"SELECT id, last_updated, image_data, width, height FROM favicon_bitmaps "
"WHERE icon_id=?"));
statement.BindInt64(0, icon_id);
bool result = false;
while (statement.Step()) {
result = true;
if (!favicon_bitmaps)
return result;
FaviconBitmap favicon_bitmap;
favicon_bitmap.bitmap_id = statement.ColumnInt64(0);
favicon_bitmap.icon_id = icon_id;
favicon_bitmap.last_updated =
base::Time::FromInternalValue(statement.ColumnInt64(1));
if (statement.ColumnByteLength(2) > 0) {
scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
statement.ColumnBlobAsVector(2, &data->data());
favicon_bitmap.bitmap_data = data;
}
favicon_bitmap.pixel_size = gfx::Size(statement.ColumnInt(3),
statement.ColumnInt(4));
favicon_bitmaps->push_back(favicon_bitmap);
}
return result;
}
bool ThumbnailDatabase::GetFaviconBitmap(
FaviconBitmapID bitmap_id,
base::Time* last_updated,
scoped_refptr<base::RefCountedMemory>* png_icon_data,
gfx::Size* pixel_size) {
DCHECK(bitmap_id);
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"SELECT last_updated, image_data, width, height FROM favicon_bitmaps "
"WHERE id=?"));
statement.BindInt64(0, bitmap_id);
if (!statement.Step())
return false;
if (last_updated)
*last_updated = base::Time::FromInternalValue(statement.ColumnInt64(0));
if (png_icon_data && statement.ColumnByteLength(1) > 0) {
scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
statement.ColumnBlobAsVector(1, &data->data());
*png_icon_data = data;
}
if (pixel_size) {
*pixel_size = gfx::Size(statement.ColumnInt(2),
statement.ColumnInt(3));
}
return true;
}
FaviconBitmapID ThumbnailDatabase::AddFaviconBitmap(
chrome::FaviconID icon_id,
const scoped_refptr<base::RefCountedMemory>& icon_data,
base::Time time,
const gfx::Size& pixel_size) {
DCHECK(icon_id);
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"INSERT INTO favicon_bitmaps (icon_id, image_data, last_updated, width, "
"height) VALUES (?, ?, ?, ?, ?)"));
statement.BindInt64(0, icon_id);
if (icon_data.get() && icon_data->size()) {
statement.BindBlob(1, icon_data->front(),
static_cast<int>(icon_data->size()));
} else {
statement.BindNull(1);
}
statement.BindInt64(2, time.ToInternalValue());
statement.BindInt(3, pixel_size.width());
statement.BindInt(4, pixel_size.height());
if (!statement.Run())
return 0;
return db_.GetLastInsertRowId();
}
bool ThumbnailDatabase::SetFaviconBitmap(
FaviconBitmapID bitmap_id,
scoped_refptr<base::RefCountedMemory> bitmap_data,
base::Time time) {
DCHECK(bitmap_id);
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"UPDATE favicon_bitmaps SET image_data=?, last_updated=? WHERE id=?"));
if (bitmap_data.get() && bitmap_data->size()) {
statement.BindBlob(0, bitmap_data->front(),
static_cast<int>(bitmap_data->size()));
} else {
statement.BindNull(0);
}
statement.BindInt64(1, time.ToInternalValue());
statement.BindInt64(2, bitmap_id);
return statement.Run();
}
bool ThumbnailDatabase::SetFaviconBitmapLastUpdateTime(
FaviconBitmapID bitmap_id,
base::Time time) {
DCHECK(bitmap_id);
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"UPDATE favicon_bitmaps SET last_updated=? WHERE id=?"));
statement.BindInt64(0, time.ToInternalValue());
statement.BindInt64(1, bitmap_id);
return statement.Run();
}
bool ThumbnailDatabase::DeleteFaviconBitmap(FaviconBitmapID bitmap_id) {
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM favicon_bitmaps WHERE id=?"));
statement.BindInt64(0, bitmap_id);
return statement.Run();
}
bool ThumbnailDatabase::SetFaviconOutOfDate(chrome::FaviconID icon_id) {
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"UPDATE favicon_bitmaps SET last_updated=? WHERE icon_id=?"));
statement.BindInt64(0, 0);
statement.BindInt64(1, icon_id);
return statement.Run();
}
chrome::FaviconID ThumbnailDatabase::GetFaviconIDForFaviconURL(
const GURL& icon_url,
int required_icon_type,
chrome::IconType* icon_type) {
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"SELECT id, icon_type FROM favicons WHERE url=? AND (icon_type & ? > 0) "
"ORDER BY icon_type DESC"));
statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url));
statement.BindInt(1, required_icon_type);
if (!statement.Step())
return 0;
if (icon_type)
*icon_type = static_cast<chrome::IconType>(statement.ColumnInt(1));
return statement.ColumnInt64(0);
}
bool ThumbnailDatabase::GetFaviconHeader(chrome::FaviconID icon_id,
GURL* icon_url,
chrome::IconType* icon_type) {
DCHECK(icon_id);
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"SELECT url, icon_type FROM favicons WHERE id=?"));
statement.BindInt64(0, icon_id);
if (!statement.Step())
return false;
if (icon_url)
*icon_url = GURL(statement.ColumnString(0));
if (icon_type)
*icon_type = static_cast<chrome::IconType>(statement.ColumnInt(1));
return true;
}
chrome::FaviconID ThumbnailDatabase::AddFavicon(
const GURL& icon_url,
chrome::IconType icon_type) {
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"INSERT INTO favicons (url, icon_type) VALUES (?, ?)"));
statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url));
statement.BindInt(1, icon_type);
if (!statement.Run())
return 0;
return db_.GetLastInsertRowId();
}
chrome::FaviconID ThumbnailDatabase::AddFavicon(
const GURL& icon_url,
chrome::IconType icon_type,
const scoped_refptr<base::RefCountedMemory>& icon_data,
base::Time time,
const gfx::Size& pixel_size) {
chrome::FaviconID icon_id = AddFavicon(icon_url, icon_type);
if (!icon_id || !AddFaviconBitmap(icon_id, icon_data, time, pixel_size))
return 0;
return icon_id;
}
bool ThumbnailDatabase::DeleteFavicon(chrome::FaviconID id) {
sql::Statement statement;
statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM favicons WHERE id = ?"));
statement.BindInt64(0, id);
if (!statement.Run())
return false;
statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM favicon_bitmaps WHERE icon_id = ?"));
statement.BindInt64(0, id);
return statement.Run();
}
bool ThumbnailDatabase::GetIconMappingsForPageURL(
const GURL& page_url,
int required_icon_types,
std::vector<IconMapping>* filtered_mapping_data) {
std::vector<IconMapping> mapping_data;
if (!GetIconMappingsForPageURL(page_url, &mapping_data))
return false;
bool result = false;
for (std::vector<IconMapping>::iterator m = mapping_data.begin();
m != mapping_data.end(); ++m) {
if (m->icon_type & required_icon_types) {
result = true;
if (!filtered_mapping_data)
return result;
required_icon_types = m->icon_type;
filtered_mapping_data->push_back(*m);
}
}
return result;
}
bool ThumbnailDatabase::GetIconMappingsForPageURL(
const GURL& page_url,
std::vector<IconMapping>* mapping_data) {
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type, "
"favicons.url "
"FROM icon_mapping "
"INNER JOIN favicons "
"ON icon_mapping.icon_id = favicons.id "
"WHERE icon_mapping.page_url=? "
"ORDER BY favicons.icon_type DESC"));
statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
bool result = false;
while (statement.Step()) {
result = true;
if (!mapping_data)
return result;
IconMapping icon_mapping;
FillIconMapping(statement, page_url, &icon_mapping);
mapping_data->push_back(icon_mapping);
}
return result;
}
IconMappingID ThumbnailDatabase::AddIconMapping(const GURL& page_url,
chrome::FaviconID icon_id) {
const char kSql[] =
"INSERT INTO icon_mapping (page_url, icon_id) VALUES (?, ?)";
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
statement.BindInt64(1, icon_id);
if (!statement.Run())
return 0;
return db_.GetLastInsertRowId();
}
bool ThumbnailDatabase::UpdateIconMapping(IconMappingID mapping_id,
chrome::FaviconID icon_id) {
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"UPDATE icon_mapping SET icon_id=? WHERE id=?"));
statement.BindInt64(0, icon_id);
statement.BindInt64(1, mapping_id);
return statement.Run();
}
bool ThumbnailDatabase::DeleteIconMappings(const GURL& page_url) {
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM icon_mapping WHERE page_url = ?"));
statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
return statement.Run();
}
bool ThumbnailDatabase::DeleteIconMapping(IconMappingID mapping_id) {
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM icon_mapping WHERE id=?"));
statement.BindInt64(0, mapping_id);
return statement.Run();
}
bool ThumbnailDatabase::HasMappingFor(chrome::FaviconID id) {
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"SELECT id FROM icon_mapping "
"WHERE icon_id=?"));
statement.BindInt64(0, id);
return statement.Step();
}
bool ThumbnailDatabase::CloneIconMappings(const GURL& old_page_url,
const GURL& new_page_url) {
sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
"SELECT icon_id FROM icon_mapping "
"WHERE page_url=?"));
if (!statement.is_valid())
return false;
statement.BindString(0, URLDatabase::GURLToDatabaseURL(new_page_url));
if (statement.Step())
return true;
statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
"INSERT INTO icon_mapping (page_url, icon_id) "
"SELECT ?, icon_id FROM icon_mapping "
"WHERE page_url = ?"));
statement.BindString(0, URLDatabase::GURLToDatabaseURL(new_page_url));
statement.BindString(1, URLDatabase::GURLToDatabaseURL(old_page_url));
return statement.Run();
}
bool ThumbnailDatabase::InitIconMappingEnumerator(
chrome::IconType type,
IconMappingEnumerator* enumerator) {
DCHECK(!enumerator->statement_.is_valid());
enumerator->statement_.Assign(db_.GetCachedStatement(
SQL_FROM_HERE,
"SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type, "
"favicons.url, icon_mapping.page_url "
"FROM icon_mapping JOIN favicons ON ("
"icon_mapping.icon_id = favicons.id) "
"WHERE favicons.icon_type = ?"));
enumerator->statement_.BindInt(0, type);
return enumerator->statement_.is_valid();
}
bool ThumbnailDatabase::RetainDataForPageUrls(
const std::vector<GURL>& urls_to_keep) {
sql::Transaction transaction(&db_);
if (!transaction.Begin())
return false;
{
const char kIconMappingCreate[] =
"CREATE TEMP TABLE icon_id_mapping "
"("
"new_icon_id INTEGER PRIMARY KEY,"
"old_icon_id INTEGER NOT NULL UNIQUE"
")";
if (!db_.Execute(kIconMappingCreate))
return false;
const char kIconMappingSql[] =
"INSERT OR IGNORE INTO temp.icon_id_mapping (old_icon_id) "
"SELECT icon_id FROM icon_mapping WHERE page_url = ?";
sql::Statement statement(db_.GetUniqueStatement(kIconMappingSql));
for (std::vector<GURL>::const_iterator
i = urls_to_keep.begin(); i != urls_to_keep.end(); ++i) {
statement.BindString(0, URLDatabase::GURLToDatabaseURL(*i));
if (!statement.Run())
return false;
statement.Reset(true);
}
}
const char kRenameIconMappingTable[] =
"ALTER TABLE icon_mapping RENAME TO old_icon_mapping";
const char kCopyIconMapping[] =
"INSERT INTO icon_mapping (page_url, icon_id) "
"SELECT old.page_url, mapping.new_icon_id "
"FROM old_icon_mapping AS old "
"JOIN temp.icon_id_mapping AS mapping "
"ON (old.icon_id = mapping.old_icon_id)";
const char kDropOldIconMappingTable[] = "DROP TABLE old_icon_mapping";
const char kRenameFaviconsTable[] =
"ALTER TABLE favicons RENAME TO old_favicons";
const char kCopyFavicons[] =
"INSERT INTO favicons (id, url, icon_type) "
"SELECT mapping.new_icon_id, old.url, old.icon_type "
"FROM old_favicons AS old "
"JOIN temp.icon_id_mapping AS mapping "
"ON (old.id = mapping.old_icon_id)";
const char kDropOldFaviconsTable[] = "DROP TABLE old_favicons";
const char kRenameFaviconBitmapsTable[] =
"ALTER TABLE favicon_bitmaps RENAME TO old_favicon_bitmaps";
const char kCopyFaviconBitmaps[] =
"INSERT INTO favicon_bitmaps "
" (icon_id, last_updated, image_data, width, height) "
"SELECT mapping.new_icon_id, old.last_updated, "
" old.image_data, old.width, old.height "
"FROM old_favicon_bitmaps AS old "
"JOIN temp.icon_id_mapping AS mapping "
"ON (old.icon_id = mapping.old_icon_id)";
const char kDropOldFaviconBitmapsTable[] =
"DROP TABLE old_favicon_bitmaps";
if (!db_.Execute(kRenameIconMappingTable) ||
!db_.Execute(kRenameFaviconsTable) ||
!db_.Execute(kRenameFaviconBitmapsTable)) {
return false;
}
if (!InitTables(&db_))
return false;
if (!db_.Execute(kCopyIconMapping) ||
!db_.Execute(kCopyFavicons) ||
!db_.Execute(kCopyFaviconBitmaps)) {
return false;
}
if (!db_.Execute(kDropOldIconMappingTable) ||
!db_.Execute(kDropOldFaviconsTable) ||
!db_.Execute(kDropOldFaviconBitmapsTable)) {
return false;
}
if (!InitIndices(&db_))
return false;
const char kIconMappingDrop[] = "DROP TABLE temp.icon_id_mapping";
if (!db_.Execute(kIconMappingDrop))
return false;
return transaction.Commit();
}
sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db,
const base::FilePath& db_name) {
size_t startup_kb = 0;
int64 size_64;
if (base::GetFileSize(db_name, &size_64))
startup_kb = static_cast<size_t>(size_64 / 1024);
db->set_histogram_tag("Thumbnail");
db->set_error_callback(base::Bind(&DatabaseErrorCallback,
db, db_name, startup_kb));
db->set_page_size(2048);
db->set_cache_size(32);
db->set_exclusive_locking();
if (!db->Open(db_name))
return sql::INIT_FAILURE;
return sql::INIT_OK;
}
sql::InitStatus ThumbnailDatabase::InitImpl(const base::FilePath& db_name) {
sql::InitStatus status = OpenDatabase(&db_, db_name);
if (status != sql::INIT_OK)
return status;
DCHECK_LT(kDeprecatedVersionNumber, kCurrentVersionNumber);
sql::MetaTable::RazeIfDeprecated(&db_, kDeprecatedVersionNumber);
sql::Transaction transaction(&db_);
if (!transaction.Begin())
return sql::INIT_FAILURE;
#if defined(OS_MACOSX)
base::mac::SetFileBackupExclusion(db_name);
#endif
ignore_result(db_.Execute("DROP TABLE IF EXISTS thumbnails"));
ignore_result(db_.Execute("DROP TABLE IF EXISTS temp_favicons"));
ignore_result(db_.Execute("DROP TABLE IF EXISTS temp_favicon_bitmaps"));
ignore_result(db_.Execute("DROP TABLE IF EXISTS temp_icon_mapping"));
if (!meta_table_.Init(&db_, kCurrentVersionNumber,
kCompatibleVersionNumber) ||
!InitTables(&db_) ||
!InitIndices(&db_)) {
return sql::INIT_FAILURE;
}
if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
LOG(WARNING) << "Thumbnail database is too new.";
return sql::INIT_TOO_NEW;
}
int cur_version = meta_table_.GetVersionNumber();
if (!db_.DoesColumnExist("favicons", "icon_type")) {
LOG(ERROR) << "Raze because of missing favicon.icon_type";
RecordInvalidStructure(STRUCTURE_EVENT_VERSION4);
db_.RazeAndClose();
return sql::INIT_FAILURE;
}
if (cur_version < 7 && !db_.DoesColumnExist("favicons", "sizes")) {
LOG(ERROR) << "Raze because of missing favicon.sizes";
RecordInvalidStructure(STRUCTURE_EVENT_VERSION5);
db_.RazeAndClose();
return sql::INIT_FAILURE;
}
if (cur_version == 5) {
++cur_version;
if (!UpgradeToVersion6())
return CantUpgradeToVersion(cur_version);
}
if (cur_version == 6) {
++cur_version;
if (!UpgradeToVersion7())
return CantUpgradeToVersion(cur_version);
}
LOG_IF(WARNING, cur_version < kCurrentVersionNumber) <<
"Thumbnail database version " << cur_version << " is too old to handle.";
if (!transaction.Commit())
return sql::INIT_FAILURE;
if (IsFaviconDBStructureIncorrect()) {
LOG(ERROR) << "Raze because of invalid favicon db structure.";
RecordInvalidStructure(STRUCTURE_EVENT_FAVICON);
db_.RazeAndClose();
return sql::INIT_FAILURE;
}
return sql::INIT_OK;
}
sql::InitStatus ThumbnailDatabase::CantUpgradeToVersion(int cur_version) {
LOG(WARNING) << "Unable to update to thumbnail database to version " <<
cur_version << ".";
db_.Close();
return sql::INIT_FAILURE;
}
bool ThumbnailDatabase::UpgradeToVersion6() {
bool success =
db_.Execute("INSERT INTO favicon_bitmaps (icon_id, last_updated, "
"image_data, width, height)"
"SELECT id, last_updated, image_data, 0, 0 FROM favicons") &&
db_.Execute("CREATE TABLE temp_favicons ("
"id INTEGER PRIMARY KEY,"
"url LONGVARCHAR NOT NULL,"
"icon_type INTEGER DEFAULT 1,"
"sizes LONGVARCHAR)") &&
db_.Execute("INSERT INTO temp_favicons (id, url, icon_type) "
"SELECT id, url, icon_type FROM favicons") &&
db_.Execute("DROP TABLE favicons") &&
db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons");
if (!success)
return false;
meta_table_.SetVersionNumber(6);
meta_table_.SetCompatibleVersionNumber(std::min(6, kCompatibleVersionNumber));
return true;
}
bool ThumbnailDatabase::UpgradeToVersion7() {
bool success =
db_.Execute("CREATE TABLE temp_favicons ("
"id INTEGER PRIMARY KEY,"
"url LONGVARCHAR NOT NULL,"
"icon_type INTEGER DEFAULT 1)") &&
db_.Execute("INSERT INTO temp_favicons (id, url, icon_type) "
"SELECT id, url, icon_type FROM favicons") &&
db_.Execute("DROP TABLE favicons") &&
db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons") &&
db_.Execute("CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)");
if (!success)
return false;
meta_table_.SetVersionNumber(7);
meta_table_.SetCompatibleVersionNumber(std::min(7, kCompatibleVersionNumber));
return true;
}
bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() {
return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons");
}
}