blob: 0ccf50d98a44e89fe45883abd6e72293604a0006 [file] [log] [blame]
// Copyright 2013 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 "components/webdata/common/web_database_backend.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "components/webdata/common/web_data_request_manager.h"
#include "components/webdata/common/web_database.h"
#include "components/webdata/common/web_database_table.h"
#include "sql/error_delegate_util.h"
using base::Bind;
using base::FilePath;
WebDatabaseBackend::WebDatabaseBackend(
const FilePath& path,
Delegate* delegate,
const scoped_refptr<base::SingleThreadTaskRunner>& db_thread)
: base::RefCountedDeleteOnSequence<WebDatabaseBackend>(db_thread),
db_path_(path),
request_manager_(new WebDataRequestManager()),
init_status_(sql::INIT_FAILURE),
init_complete_(false),
catastrophic_error_occurred_(false),
delegate_(delegate) {}
void WebDatabaseBackend::AddTable(std::unique_ptr<WebDatabaseTable> table) {
DCHECK(!db_);
tables_.push_back(std::move(table));
}
void WebDatabaseBackend::InitDatabase() {
LoadDatabaseIfNecessary();
if (delegate_) {
delegate_->DBLoaded(init_status_, diagnostics_);
}
}
void WebDatabaseBackend::ShutdownDatabase() {
if (db_ && init_status_ == sql::INIT_OK)
db_->CommitTransaction();
db_.reset(nullptr);
init_complete_ = true; // Ensures the init sequence is not re-run.
init_status_ = sql::INIT_FAILURE;
}
void WebDatabaseBackend::DBWriteTaskWrapper(
const WebDatabaseService::WriteTask& task,
std::unique_ptr<WebDataRequest> request) {
if (!request->IsActive())
return;
ExecuteWriteTask(task);
request_manager_->RequestCompleted(std::move(request), nullptr);
}
void WebDatabaseBackend::ExecuteWriteTask(
const WebDatabaseService::WriteTask& task) {
LoadDatabaseIfNecessary();
if (db_ && init_status_ == sql::INIT_OK) {
WebDatabase::State state = task.Run(db_.get());
if (state == WebDatabase::COMMIT_NEEDED)
Commit();
}
}
void WebDatabaseBackend::DBReadTaskWrapper(
const WebDatabaseService::ReadTask& task,
std::unique_ptr<WebDataRequest> request) {
if (!request->IsActive())
return;
std::unique_ptr<WDTypedResult> result = ExecuteReadTask(task);
request_manager_->RequestCompleted(std::move(request), std::move(result));
}
std::unique_ptr<WDTypedResult> WebDatabaseBackend::ExecuteReadTask(
const WebDatabaseService::ReadTask& task) {
LoadDatabaseIfNecessary();
if (db_ && init_status_ == sql::INIT_OK) {
return task.Run(db_.get());
}
return nullptr;
}
WebDatabaseBackend::~WebDatabaseBackend() {
ShutdownDatabase();
}
void WebDatabaseBackend::LoadDatabaseIfNecessary() {
if (init_complete_ || db_path_.empty())
return;
init_complete_ = true;
db_.reset(new WebDatabase());
for (const auto& table : tables_)
db_->AddTable(table.get());
// Unretained to avoid a ref loop since we own |db_|.
db_->set_error_callback(base::Bind(&WebDatabaseBackend::DatabaseErrorCallback,
base::Unretained(this)));
diagnostics_.clear();
catastrophic_error_occurred_ = false;
init_status_ = db_->Init(db_path_);
if (init_status_ != sql::INIT_OK) {
LOG(ERROR) << "Cannot initialize the web database: " << init_status_;
db_.reset();
return;
}
// A catastrophic error might have happened and recovered.
if (catastrophic_error_occurred_) {
init_status_ = sql::INIT_OK_WITH_DATA_LOSS;
LOG(WARNING)
<< "Webdata recovered from a catastrophic error. Data loss possible.";
}
db_->BeginTransaction();
}
void WebDatabaseBackend::DatabaseErrorCallback(int error,
sql::Statement* statement) {
// We ignore any further error callbacks after the first catastrophic error.
if (!catastrophic_error_occurred_ && sql::IsErrorCatastrophic(error)) {
catastrophic_error_occurred_ = true;
diagnostics_ = db_->GetDiagnosticInfo(error, statement);
diagnostics_ += sql::GetCorruptFileDiagnosticsInfo(db_path_);
db_->GetSQLConnection()->RazeAndClose();
}
}
void WebDatabaseBackend::Commit() {
DCHECK(db_);
DCHECK_EQ(sql::INIT_OK, init_status_);
db_->CommitTransaction();
db_->BeginTransaction();
}