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