blob: 115263e24c831c30fb7fc898e29700bbfe997495 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/android/explore_sites/get_catalog_task.h"
#include "chrome/browser/android/explore_sites/explore_sites_schema.h"
#include "sql/database.h"
#include "sql/meta_table.h"
#include "sql/statement.h"
#include "sql/transaction.h"
namespace explore_sites {
namespace {
static const char kSelectCategorySql[] = R"(SELECT category_id, type, label
FROM categories
WHERE version_token = ?
ORDER BY category_id ASC;)";
static const char kSelectSiteSql[] = R"(SELECT site_id, url, title
FROM sites
WHERE category_id = ?)";
const char kDeleteSiteSql[] = R"(DELETE FROM sites
WHERE category_id NOT IN
(SELECT category_id FROM categories WHERE version_token = ?);)";
const char kDeleteCategorySql[] =
"DELETE FROM categories WHERE version_token <> ?;";
} // namespace
std::string UpdateCurrentCatalogIfNewer(sql::MetaTable* meta_table,
std::string current_version_token) {
DCHECK(meta_table);
std::string downloading_version_token;
if (!meta_table->GetValue("downloading_catalog",
&downloading_version_token)) {
return current_version_token;
}
if (!meta_table->SetValue("current_catalog", downloading_version_token))
return "";
meta_table->DeleteKey("downloading_catalog");
return downloading_version_token;
}
void RemoveOutdatedCatalogEntries(sql::Database* db,
std::string version_token) {
sql::Statement delete_sites(
db->GetCachedStatement(SQL_FROM_HERE, kDeleteSiteSql));
delete_sites.BindString(0, version_token);
delete_sites.Run();
sql::Statement delete_categories(
db->GetCachedStatement(SQL_FROM_HERE, kDeleteCategorySql));
delete_categories.BindString(0, version_token);
delete_categories.Run();
}
std::unique_ptr<GetCatalogTask::CategoryList> GetCatalogSync(
bool update_current,
sql::Database* db) {
DCHECK(db);
sql::MetaTable meta_table;
if (!ExploreSitesSchema::InitMetaTable(db, &meta_table))
return nullptr;
// If we are downloading a catalog that is the same version as the one
// currently in use, don't change it. This is an error, should have been
// caught before we got here.
std::string catalog_timestamp;
meta_table.GetValue("current_catalog", &catalog_timestamp);
if (update_current) {
sql::Transaction transaction(db);
transaction.Begin();
catalog_timestamp =
UpdateCurrentCatalogIfNewer(&meta_table, catalog_timestamp);
if (catalog_timestamp == "")
return nullptr;
RemoveOutdatedCatalogEntries(db, catalog_timestamp);
if (!transaction.Commit())
return nullptr;
}
sql::Statement category_statement(
db->GetCachedStatement(SQL_FROM_HERE, kSelectCategorySql));
category_statement.BindString(0, catalog_timestamp);
auto result = std::make_unique<GetCatalogTask::CategoryList>();
while (category_statement.Step()) {
result->emplace_back(category_statement.ColumnInt(0), // category_id
catalog_timestamp,
category_statement.ColumnInt(1), // type
category_statement.ColumnString(2)); // label
}
if (!category_statement.Succeeded())
return nullptr;
for (auto& category : *result) {
sql::Statement site_statement(
db->GetCachedStatement(SQL_FROM_HERE, kSelectSiteSql));
site_statement.BindInt64(0, category.category_id);
while (site_statement.Step()) {
category.sites.emplace_back(site_statement.ColumnInt(0), // site_id
category.category_id,
GURL(site_statement.ColumnString(1)), // url
site_statement.ColumnString(2)); // title
}
if (!site_statement.Succeeded())
return nullptr;
}
return result;
}
GetCatalogTask::GetCatalogTask(ExploreSitesStore* store,
bool update_current,
CatalogCallback callback)
: store_(store),
update_current_(update_current),
callback_(std::move(callback)),
weak_ptr_factory_(this) {}
GetCatalogTask::~GetCatalogTask() = default;
void GetCatalogTask::Run() {
store_->Execute(base::BindOnce(&GetCatalogSync, update_current_),
base::BindOnce(&GetCatalogTask::FinishedExecuting,
weak_ptr_factory_.GetWeakPtr()),
std::unique_ptr<CategoryList>());
}
void GetCatalogTask::FinishedExecuting(
std::unique_ptr<CategoryList> categories) {
TaskComplete();
DVLOG(1) << "Finished getting the catalog, result: " << categories.get();
std::move(callback_).Run(std::move(categories));
}
} // namespace explore_sites