Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(204)

Side by Side Diff: components/offline_pages/offline_page_metadata_store_sql.cc

Issue 1834563002: initial add of SQL based storage (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address comments Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/offline_pages/offline_page_metadata_store_sql.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 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/offline_pages/offline_page_metadata_store_sql.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "components/offline_pages/offline_page_item.h"
16 #include "sql/connection.h"
17 #include "sql/error_delegate_util.h"
18 #include "sql/meta_table.h"
19 #include "sql/statement.h"
20 #include "sql/transaction.h"
21
22 namespace offline_pages {
23
24 namespace {
25
26 const int kCurrentVersion = 1;
27 const int kCompatibleVersion = 1;
28
29 // This is a macro instead of a const so that
30 // it can be used inline in other SQL statements below.
31 #define OFFLINE_PAGES_TABLE_NAME "offlinepages"
32
33 const char kOfflinePagesColumns[] =
34 "(offline_id INTEGER NOT NULL,"
35 " client_namespace VARCHAR(256),"
36 " client_id VARCHAR(256),"
37 " online_url VARCHAR(2048),"
38 " offline_url VARCHAR(2048),"
39 " version INTEGER,"
40 " creation_time INTEGER,"
41 " file_path VARCHAR(1024),"
42 " file_size INTEGER,"
43 " last_access_time INTEGER,"
44 " access_count INTEGER,"
45 " status INTEGER,"
46 " user_initiated BOOLEAN,"
47 " UNIQUE(offline_id))";
48
49 // This is cloned from //content/browser/appcache/appcache_database.cc
50 struct TableInfo {
51 const char* table_name;
52 const char* columns;
53 };
54
55 const TableInfo kOfflinePagesTable{OFFLINE_PAGES_TABLE_NAME,
56 kOfflinePagesColumns};
57
58 // This enum is used to define the indices for the columns in each row
59 // that hold the different pieces of offline page.
60 enum : int {
61 OP_OFFLINE_ID = 0,
62 OP_CLIENT_NAMESPACE,
63 OP_CLIENT_ID,
64 OP_ONLINE_URL,
65 OP_OFFLINE_URL,
66 OP_VERSION,
67 OP_CREATION_TIME,
68 OP_FILE_PATH,
69 OP_FILE_SIZE,
70 OP_LAST_ACCESS_TIME,
71 OP_ACCESS_COUNT,
72 OP_STATUS,
73 OP_USER_INITIATED
74 };
75
76 bool CreateTable(sql::Connection* db,
77 const char* table_name,
78 const char* columns) {
79 std::string sql("CREATE TABLE ");
80 sql += table_name;
81 sql += columns;
82 return db->Execute(sql.c_str());
83 }
84
85 bool CreateSchema(sql::MetaTable* meta_table, sql::Connection* db) {
86 // If you create a transaction but don't Commit() it is automatically
87 // rolled back by its destructor when it falls out of scope.
88 sql::Transaction transaction(db);
89 if (!transaction.Begin())
90 return false;
91
92 if (!meta_table->Init(db, kCurrentVersion, kCompatibleVersion))
93 return false;
94
95 if (!CreateTable(db, OFFLINE_PAGES_TABLE_NAME, kOfflinePagesColumns))
96 return false;
97
98 // TODO(bburns): Add indices here.
99 return transaction.Commit();
100 }
101
102 bool DeleteByOfflineId(sql::Connection* db, int64_t offline_id) {
103 const char kSql[] =
104 "DELETE FROM " OFFLINE_PAGES_TABLE_NAME " WHERE offline_id=?";
105 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
106 statement.BindInt64(0, offline_id);
107 return statement.Run();
108 }
109
110 // Create an offline page item from a SQL result. Expects complete rows with
111 // all columns present.
112 OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) {
113 int64_t id = statement->ColumnInt64(OP_OFFLINE_ID);
114 GURL url(statement->ColumnString(OP_ONLINE_URL));
115 ClientId client_id(statement->ColumnString(OP_CLIENT_NAMESPACE),
116 statement->ColumnString(OP_CLIENT_ID));
117 #if defined(OS_POSIX)
118 base::FilePath path(statement->ColumnString(OP_FILE_PATH));
119 #elif defined(OS_WIN)
120 base::FilePath path(base::UTF8ToWide(statement->ColumnString(OP_FILE_PATH)));
121 #endif
122 int64_t file_size = statement->ColumnInt64(OP_FILE_SIZE);
123 base::Time creation_time =
124 base::Time::FromInternalValue(statement->ColumnInt64(OP_CREATION_TIME));
125
126 OfflinePageItem item(url, id, client_id, path, file_size, creation_time);
127 item.last_access_time = base::Time::FromInternalValue(
128 statement->ColumnInt64(OP_LAST_ACCESS_TIME));
129 item.version = statement->ColumnInt(OP_VERSION);
130 item.access_count = statement->ColumnInt(OP_ACCESS_COUNT);
131 return item;
132 }
133
134 bool InsertOrReplace(sql::Connection* db, const OfflinePageItem& item) {
135 const char kSql[] =
136 "INSERT OR REPLACE INTO " OFFLINE_PAGES_TABLE_NAME
137 " (offline_id, online_url, client_namespace, client_id, file_path, "
138 "file_size, creation_time, last_access_time, version, access_count)"
139 " VALUES "
140 " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
141
142 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
143 statement.BindInt64(0, item.offline_id);
144 statement.BindString(1, item.url.spec());
145 statement.BindString(2, item.client_id.name_space);
146 statement.BindString(3, item.client_id.id);
147 std::string path_string;
148 #if defined(OS_POSIX)
149 path_string = item.file_path.value();
150 #elif defined(OS_WIN)
151 path_string = base::WideToUTF8(item.file_path.value());
152 #endif
153 statement.BindString(4, path_string);
154 statement.BindInt64(5, item.file_size);
155 statement.BindInt64(6, item.creation_time.ToInternalValue());
156 statement.BindInt64(7, item.last_access_time.ToInternalValue());
157 statement.BindInt(8, item.version);
158 statement.BindInt(9, item.access_count);
159 return statement.Run();
160 }
161
162 } // anonymous namespace
163
164 OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL(
165 scoped_refptr<base::SequencedTaskRunner> background_task_runner,
166 const base::FilePath& path)
167 : background_task_runner_(background_task_runner),
168 db_file_path_(path.AppendASCII("OfflinePages.db")),
169 use_in_memory_(false),
170 weak_ptr_factory_(this) {}
171
172 OfflinePageMetadataStoreSQL::~OfflinePageMetadataStoreSQL() {
173 if (db_.get() &&
174 !background_task_runner_->DeleteSoon(FROM_HERE, db_.release()))
175 DLOG(WARNING) << "Proto database will not be deleted.";
jianli 2016/04/01 22:59:52 nit: add {} to wrap this line since if line is not
176 }
177
178 void OfflinePageMetadataStoreSQL::LoadSync(
179 scoped_refptr<base::SingleThreadTaskRunner> runner,
180 const LoadCallback& callback) {
181 bool opened = false;
182 db_.reset(new sql::Connection);
183 meta_table_.reset(new sql::MetaTable);
184
185 if (use_in_memory_) {
186 opened = db_->OpenInMemory();
187 } else {
188 base::File::Error err;
189 if (!base::CreateDirectoryAndGetError(db_file_path_.DirName(), &err)) {
190 LOG(ERROR) << "Failed to create offline pages db directory: "
191 << base::File::ErrorToString(err);
192 } else {
193 opened = db_->Open(db_file_path_);
194 if (opened)
195 db_->Preload();
196 }
197 }
198 if (!opened) {
199 LOG(ERROR) << "Failed to open database";
200 NotifyLoadResult(runner, callback, STORE_INIT_FAILED,
201 std::vector<OfflinePageItem>());
202 return;
203 }
204
205 if (!sql::MetaTable::DoesTableExist(db_.get())) {
206 if (!CreateSchema(meta_table_.get(), db_.get())) {
207 LOG(ERROR) << "Failed to create schema";
208 NotifyLoadResult(runner, callback, STORE_INIT_FAILED,
209 std::vector<OfflinePageItem>());
210 return;
211 }
212 } else if (!meta_table_->Init(db_.get(), kCurrentVersion,
213 kCompatibleVersion)) {
214 LOG(ERROR) << "Failed to initialize database";
215 NotifyLoadResult(runner, callback, STORE_INIT_FAILED,
216 std::vector<OfflinePageItem>());
217 return;
218 }
219
220 if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
221 LOG(WARNING) << "Offline database is too new.";
222 NotifyLoadResult(runner, callback, STORE_INIT_FAILED,
223 std::vector<OfflinePageItem>());
224 return;
225 }
226
227 const char kSql[] = "SELECT * FROM " OFFLINE_PAGES_TABLE_NAME;
228
229 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
230
231 std::vector<OfflinePageItem> result;
232 while (statement.Step()) {
233 result.push_back(MakeOfflinePageItem(&statement));
234 }
235
236 if (statement.Succeeded()) {
237 NotifyLoadResult(runner, callback, LOAD_SUCCEEDED, result);
238 } else {
239 NotifyLoadResult(runner, callback, STORE_LOAD_FAILED,
240 std::vector<OfflinePageItem>());
241 }
242 }
243
244 void OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePageSync(
245 const OfflinePageItem& offline_page,
246 scoped_refptr<base::SingleThreadTaskRunner> runner,
247 const UpdateCallback& callback) {
248 DCHECK(db_.get());
249 // TODO(bburns): add UMA metrics here (and for levelDB).
250 bool ok = InsertOrReplace(db_.get(), offline_page);
251 runner->PostTask(FROM_HERE, base::Bind(callback, ok));
252 }
253
254 void OfflinePageMetadataStoreSQL::RemoveOfflinePagesSync(
255 const std::vector<int64_t>& offline_ids,
256 scoped_refptr<base::SingleThreadTaskRunner> runner,
257 const UpdateCallback& callback) {
258 DCHECK(db_.get());
259 // TODO(bburns): add UMA metrics here (and for levelDB).
260
261 // If you create a transaction but don't Commit() it is automatically
262 // rolled back by its destructor when it falls out of scope.
263 sql::Transaction transaction(db_.get());
264 if (!transaction.Begin()) {
265 runner->PostTask(FROM_HERE, base::Bind(callback, false));
266 return;
267 }
268 for (auto offline_id : offline_ids) {
269 if (!DeleteByOfflineId(db_.get(), offline_id)) {
270 runner->PostTask(FROM_HERE, base::Bind(callback, false));
271 return;
272 }
273 }
274
275 bool success = transaction.Commit();
276 runner->PostTask(FROM_HERE, base::Bind(callback, success));
277 }
278
279 void OfflinePageMetadataStoreSQL::ResetSync(
280 scoped_refptr<base::SingleThreadTaskRunner> runner,
281 const ResetCallback& callback) {
282 const char kSql[] = "DELETE * FROM " OFFLINE_PAGES_TABLE_NAME;
283 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
284 if (!statement.Run()) {
285 runner->PostTask(FROM_HERE, base::Bind(callback, false));
286 return;
287 }
288 meta_table_.reset();
289 db_.reset();
290 weak_ptr_factory_.InvalidateWeakPtrs();
291
292 runner->PostTask(FROM_HERE, base::Bind(callback, true));
293 }
294
295 void OfflinePageMetadataStoreSQL::NotifyLoadResult(
296 scoped_refptr<base::SingleThreadTaskRunner> runner,
297 const LoadCallback& callback,
298 LoadStatus status,
299 const std::vector<OfflinePageItem>& result) {
300 // TODO(bburns): Switch to SQL specific UMA metrics.
301 UMA_HISTOGRAM_ENUMERATION("OfflinePages.LoadStatus", status,
302 OfflinePageMetadataStore::LOAD_STATUS_COUNT);
303 if (status == LOAD_SUCCEEDED) {
304 UMA_HISTOGRAM_COUNTS("OfflinePages.SavedPageCount", result.size());
305 } else {
306 DVLOG(1) << "Offline pages database loading failed: " << status;
307 meta_table_.reset();
308 db_.reset();
309 }
310 runner->PostTask(FROM_HERE, base::Bind(callback, status, result));
311 }
312
313 void OfflinePageMetadataStoreSQL::Load(const LoadCallback& callback) {
314 background_task_runner_->PostTask(
315 FROM_HERE, base::Bind(&OfflinePageMetadataStoreSQL::LoadSync,
316 weak_ptr_factory_.GetWeakPtr(),
317 base::ThreadTaskRunnerHandle::Get(), callback));
318 }
319
320 void OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePage(
321 const OfflinePageItem& offline_page,
322 const UpdateCallback& callback) {
323 background_task_runner_->PostTask(
324 FROM_HERE,
325 base::Bind(&OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePageSync,
326 weak_ptr_factory_.GetWeakPtr(), offline_page,
327 base::ThreadTaskRunnerHandle::Get(), callback));
328 }
329
330 void OfflinePageMetadataStoreSQL::RemoveOfflinePages(
331 const std::vector<int64_t>& offline_ids,
332 const UpdateCallback& callback) {
333 if (offline_ids.size() == 0) {
334 // Nothing to do, but post a callback instead of calling directly
335 // to preserve the async style behavior to prevent bugs.
336 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
337 base::Bind(callback, true));
338 return;
339 }
340
341 background_task_runner_->PostTask(
342 FROM_HERE,
343 base::Bind(&OfflinePageMetadataStoreSQL::RemoveOfflinePagesSync,
344 weak_ptr_factory_.GetWeakPtr(), offline_ids,
345 base::ThreadTaskRunnerHandle::Get(), callback));
346 }
347
348 void OfflinePageMetadataStoreSQL::Reset(const ResetCallback& callback) {
349 background_task_runner_->PostTask(
350 FROM_HERE, base::Bind(&OfflinePageMetadataStoreSQL::ResetSync,
351 weak_ptr_factory_.GetWeakPtr(),
352 base::ThreadTaskRunnerHandle::Get(), callback));
353 }
354
355 void OfflinePageMetadataStoreSQL::SetInMemoryDatabaseForTesting(
356 bool in_memory) {
357 // Must be called prior to Load(...)
358 DCHECK(db_.get() == NULL);
359
360 use_in_memory_ = in_memory;
361 }
362
363 } // namespace offline_pages
OLDNEW
« no previous file with comments | « components/offline_pages/offline_page_metadata_store_sql.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698