1 // Copyright 2013 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 "components/webdata/common/web_database_backend.h"
6
7 #include <algorithm>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "components/webdata/common/web_data_request_manager.h"
13 #include "components/webdata/common/web_database.h"
14 #include "components/webdata/common/web_database_table.h"
15 #include "sql/error_delegate_util.h"
16
WebDatabaseBackend(const base::FilePath & path,std::unique_ptr<Delegate> delegate,const scoped_refptr<base::SingleThreadTaskRunner> & db_thread)17 WebDatabaseBackend::WebDatabaseBackend(
18 const base::FilePath& path,
19 std::unique_ptr<Delegate> delegate,
20 const scoped_refptr<base::SingleThreadTaskRunner>& db_thread)
21 : base::RefCountedDeleteOnSequence<WebDatabaseBackend>(db_thread),
22 db_path_(path),
23 delegate_(std::move(delegate)) {}
24
AddTable(std::unique_ptr<WebDatabaseTable> table)25 void WebDatabaseBackend::AddTable(std::unique_ptr<WebDatabaseTable> table) {
26 DCHECK(!db_);
27 tables_.push_back(std::move(table));
28 }
29
InitDatabase()30 void WebDatabaseBackend::InitDatabase() {
31 LoadDatabaseIfNecessary();
32 if (delegate_)
33 delegate_->DBLoaded(init_status_, diagnostics_);
34 }
35
ShutdownDatabase()36 void WebDatabaseBackend::ShutdownDatabase() {
37 if (db_ && init_status_ == sql::INIT_OK)
38 db_->CommitTransaction();
39 db_.reset();
40 init_complete_ = true; // Ensures the init sequence is not re-run.
41 init_status_ = sql::INIT_FAILURE;
42 }
43
DBWriteTaskWrapper(WebDatabaseService::WriteTask task,std::unique_ptr<WebDataRequest> request)44 void WebDatabaseBackend::DBWriteTaskWrapper(
45 WebDatabaseService::WriteTask task,
46 std::unique_ptr<WebDataRequest> request) {
47 if (!request->IsActive())
48 return;
49
50 ExecuteWriteTask(std::move(task));
51 request_manager_->RequestCompleted(std::move(request), nullptr);
52 }
53
ExecuteWriteTask(WebDatabaseService::WriteTask task)54 void WebDatabaseBackend::ExecuteWriteTask(WebDatabaseService::WriteTask task) {
55 LoadDatabaseIfNecessary();
56 if (db_ && init_status_ == sql::INIT_OK) {
57 WebDatabase::State state = std::move(task).Run(db_.get());
58 if (state == WebDatabase::COMMIT_NEEDED)
59 Commit();
60 }
61 }
62
DBReadTaskWrapper(WebDatabaseService::ReadTask task,std::unique_ptr<WebDataRequest> request)63 void WebDatabaseBackend::DBReadTaskWrapper(
64 WebDatabaseService::ReadTask task,
65 std::unique_ptr<WebDataRequest> request) {
66 if (!request->IsActive())
67 return;
68
69 std::unique_ptr<WDTypedResult> result = ExecuteReadTask(std::move(task));
70 request_manager_->RequestCompleted(std::move(request), std::move(result));
71 }
72
ExecuteReadTask(WebDatabaseService::ReadTask task)73 std::unique_ptr<WDTypedResult> WebDatabaseBackend::ExecuteReadTask(
74 WebDatabaseService::ReadTask task) {
75 LoadDatabaseIfNecessary();
76 return (db_ && init_status_ == sql::INIT_OK) ? std::move(task).Run(db_.get())
77 : nullptr;
78 }
79
~WebDatabaseBackend()80 WebDatabaseBackend::~WebDatabaseBackend() {
81 ShutdownDatabase();
82 }
83
LoadDatabaseIfNecessary()84 void WebDatabaseBackend::LoadDatabaseIfNecessary() {
85 if (init_complete_ || db_path_.empty())
86 return;
87
88 init_complete_ = true;
89 db_ = std::make_unique<WebDatabase>();
90
91 for (const auto& table : tables_)
92 db_->AddTable(table.get());
93
94 // Unretained to avoid a ref loop since we own |db_|.
95 db_->set_error_callback(base::BindRepeating(
96 &WebDatabaseBackend::DatabaseErrorCallback, base::Unretained(this)));
97 diagnostics_.clear();
98 catastrophic_error_occurred_ = false;
99 init_status_ = db_->Init(db_path_);
100
101 if (init_status_ != sql::INIT_OK) {
102 db_.reset();
103 return;
104 }
105
106 // A catastrophic error might have happened and recovered.
107 if (catastrophic_error_occurred_)
108 init_status_ = sql::INIT_OK_WITH_DATA_LOSS;
109 db_->BeginTransaction();
110 }
111
DatabaseErrorCallback(int error,sql::Statement * statement)112 void WebDatabaseBackend::DatabaseErrorCallback(int error,
113 sql::Statement* statement) {
114 // We ignore any further error callbacks after the first catastrophic error.
115 if (!catastrophic_error_occurred_ && sql::IsErrorCatastrophic(error)) {
116 catastrophic_error_occurred_ = true;
117 diagnostics_ = db_->GetDiagnosticInfo(error, statement);
118 diagnostics_ += sql::GetCorruptFileDiagnosticsInfo(db_path_);
119
120 db_->GetSQLConnection()->RazeAndClose();
121 }
122 }
123
Commit()124 void WebDatabaseBackend::Commit() {
125 DCHECK(db_);
126 DCHECK_EQ(sql::INIT_OK, init_status_);
127 db_->CommitTransaction();
128 db_->BeginTransaction();
129 }
130