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

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

Issue 2343743002: [Offline pages] Updating the UpdateCallback in OPMStoreSQL (Closed)
Patch Set: Created 4 years, 3 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
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/offline_pages/offline_page_metadata_store_sql.h" 5 #include "components/offline_pages/offline_page_metadata_store_sql.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/files/file_path.h" 8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h" 9 #include "base/files/file_util.h"
10 #include "base/location.h" 10 #include "base/location.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/metrics/histogram_macros.h" 12 #include "base/metrics/histogram_macros.h"
13 #include "base/sequenced_task_runner.h" 13 #include "base/sequenced_task_runner.h"
14 #include "base/strings/utf_string_conversions.h" 14 #include "base/strings/utf_string_conversions.h"
15 #include "base/threading/thread_task_runner_handle.h" 15 #include "base/threading/thread_task_runner_handle.h"
16 #include "components/offline_pages/offline_page_item.h" 16 #include "components/offline_pages/offline_page_item.h"
17 #include "sql/connection.h" 17 #include "sql/connection.h"
18 #include "sql/statement.h" 18 #include "sql/statement.h"
19 #include "sql/transaction.h" 19 #include "sql/transaction.h"
20 20
21 namespace offline_pages { 21 namespace offline_pages {
22 22
23 using StoreState = OfflinePageMetadataStore::StoreState; 23 using StoreState = OfflinePageMetadataStore::StoreState;
24 using ItemActionStatus = OfflinePageMetadataStore::ItemActionStatus;
24 25
25 namespace { 26 namespace {
26 27
27 // This is a macro instead of a const so that 28 // This is a macro instead of a const so that
28 // it can be used inline in other SQL statements below. 29 // it can be used inline in other SQL statements below.
29 #define OFFLINE_PAGES_TABLE_NAME "offlinepages_v1" 30 #define OFFLINE_PAGES_TABLE_NAME "offlinepages_v1"
30 31
31 bool CreateOfflinePagesTable(sql::Connection* db) { 32 bool CreateOfflinePagesTable(sql::Connection* db) {
32 const char kSql[] = "CREATE TABLE IF NOT EXISTS " OFFLINE_PAGES_TABLE_NAME 33 const char kSql[] = "CREATE TABLE IF NOT EXISTS " OFFLINE_PAGES_TABLE_NAME
33 "(offline_id INTEGER PRIMARY KEY NOT NULL," 34 "(offline_id INTEGER PRIMARY KEY NOT NULL,"
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 statement.BindString(4, GetUTF8StringFromPath(item.file_path)); 202 statement.BindString(4, GetUTF8StringFromPath(item.file_path));
202 statement.BindInt64(5, item.file_size); 203 statement.BindInt64(5, item.file_size);
203 statement.BindInt64(6, item.creation_time.ToInternalValue()); 204 statement.BindInt64(6, item.creation_time.ToInternalValue());
204 statement.BindInt64(7, item.last_access_time.ToInternalValue()); 205 statement.BindInt64(7, item.last_access_time.ToInternalValue());
205 statement.BindInt(8, item.access_count); 206 statement.BindInt(8, item.access_count);
206 statement.BindInt64(9, item.expiration_time.ToInternalValue()); 207 statement.BindInt64(9, item.expiration_time.ToInternalValue());
207 statement.BindString16(10, item.title); 208 statement.BindString16(10, item.title);
208 return statement.Run() && db->GetLastChangeCount() > 0; 209 return statement.Run() && db->GetLastChangeCount() > 0;
209 } 210 }
210 211
211 bool InsertOrReplace(sql::Connection* db, const OfflinePageItem& item) { 212 bool Update(sql::Connection* db, const OfflinePageItem& item) {
212 const char kSql[] = 213 const char kSql[] =
213 "INSERT OR REPLACE INTO " OFFLINE_PAGES_TABLE_NAME 214 "UPDATE OR IGNORE " OFFLINE_PAGES_TABLE_NAME
214 " (offline_id, online_url, client_namespace, client_id, file_path, " 215 " SET online_url = ?, client_namespace = ?, client_id = ?, file_path = ?,"
215 "file_size, creation_time, last_access_time, access_count, " 216 " file_size = ?, creation_time = ?, last_access_time = ?,"
216 "expiration_time, title)" 217 " access_count = ?, expiration_time = ?, title = ?"
217 " VALUES " 218 " WHERE offline_id = ?";
218 " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
219 219
220 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); 220 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
221 statement.BindInt64(0, item.offline_id); 221 statement.BindString(0, item.url.spec());
222 statement.BindString(1, item.url.spec()); 222 statement.BindString(1, item.client_id.name_space);
223 statement.BindString(2, item.client_id.name_space); 223 statement.BindString(2, item.client_id.id);
224 statement.BindString(3, item.client_id.id); 224 statement.BindString(3, GetUTF8StringFromPath(item.file_path));
225 statement.BindString(4, GetUTF8StringFromPath(item.file_path)); 225 statement.BindInt64(4, item.file_size);
226 statement.BindInt64(5, item.file_size); 226 statement.BindInt64(5, item.creation_time.ToInternalValue());
227 statement.BindInt64(6, item.creation_time.ToInternalValue()); 227 statement.BindInt64(6, item.last_access_time.ToInternalValue());
228 statement.BindInt64(7, item.last_access_time.ToInternalValue()); 228 statement.BindInt(7, item.access_count);
229 statement.BindInt(8, item.access_count); 229 statement.BindInt64(8, item.expiration_time.ToInternalValue());
230 statement.BindInt64(9, item.expiration_time.ToInternalValue()); 230 statement.BindString16(9, item.title);
231 statement.BindString16(10, item.title); 231 statement.BindInt64(10, item.offline_id);
232 return statement.Run(); 232 return statement.Run() && db->GetLastChangeCount() > 0;
233 } 233 }
234 234
235 bool InitDatabase(sql::Connection* db, base::FilePath path) { 235 bool InitDatabase(sql::Connection* db, base::FilePath path) {
236 db->set_page_size(4096); 236 db->set_page_size(4096);
237 db->set_cache_size(500); 237 db->set_cache_size(500);
238 db->set_histogram_tag("OfflinePageMetadata"); 238 db->set_histogram_tag("OfflinePageMetadata");
239 db->set_exclusive_locking(); 239 db->set_exclusive_locking();
240 240
241 base::File::Error err; 241 base::File::Error err;
242 if (!base::CreateDirectoryAndGetError(path.DirName(), &err)) { 242 if (!base::CreateDirectoryAndGetError(path.DirName(), &err)) {
(...skipping 29 matching lines...) Expand all
272 void OpenConnectionSync(sql::Connection* db, 272 void OpenConnectionSync(sql::Connection* db,
273 scoped_refptr<base::SingleThreadTaskRunner> runner, 273 scoped_refptr<base::SingleThreadTaskRunner> runner,
274 const base::FilePath& path, 274 const base::FilePath& path,
275 const base::Callback<void(StoreState)>& callback) { 275 const base::Callback<void(StoreState)>& callback) {
276 StoreState state = InitDatabase(db, path) 276 StoreState state = InitDatabase(db, path)
277 ? OfflinePageMetadataStore::LOADED 277 ? OfflinePageMetadataStore::LOADED
278 : OfflinePageMetadataStore::FAILED_INITIALIZATION; 278 : OfflinePageMetadataStore::FAILED_INITIALIZATION;
279 runner->PostTask(FROM_HERE, base::Bind(callback, state)); 279 runner->PostTask(FROM_HERE, base::Bind(callback, state));
280 } 280 }
281 281
282 bool GetPageByOfflineIdSync(sql::Connection* db,
283 int64_t offline_id,
284 OfflinePageItem* item) {
285 const char kSql[] =
286 "SELECT * FROM " OFFLINE_PAGES_TABLE_NAME " WHERE offline_id = ?";
287 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
288 statement.BindInt64(0, offline_id);
289
290 bool found = false;
291 while (statement.Step()) {
Pete Williamson 2016/09/15 18:05:43 Nit - why use "while" here if offline_id is unique
fgorski 2016/09/19 23:24:30 Done.
292 *item = MakeOfflinePageItem(&statement);
293 found = true;
294 }
295
296 return found;
297 }
298
282 void GetOfflinePagesSync( 299 void GetOfflinePagesSync(
283 sql::Connection* db, 300 sql::Connection* db,
284 scoped_refptr<base::SingleThreadTaskRunner> runner, 301 scoped_refptr<base::SingleThreadTaskRunner> runner,
285 const OfflinePageMetadataStore::LoadCallback& callback) { 302 const OfflinePageMetadataStore::LoadCallback& callback) {
286 const char kSql[] = "SELECT * FROM " OFFLINE_PAGES_TABLE_NAME; 303 const char kSql[] = "SELECT * FROM " OFFLINE_PAGES_TABLE_NAME;
287 304
288 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); 305 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
289 306
290 std::vector<OfflinePageItem> result; 307 std::vector<OfflinePageItem> result;
291 while (statement.Step()) 308 while (statement.Step())
292 result.push_back(MakeOfflinePageItem(&statement)); 309 result.push_back(MakeOfflinePageItem(&statement));
293 310
294 if (statement.Succeeded()) { 311 if (statement.Succeeded()) {
295 NotifyLoadResult(runner, callback, OfflinePageMetadataStore::LOAD_SUCCEEDED, 312 NotifyLoadResult(runner, callback, OfflinePageMetadataStore::LOAD_SUCCEEDED,
296 result); 313 result);
297 } else { 314 } else {
298 result.clear(); 315 result.clear();
299 NotifyLoadResult(runner, callback, 316 NotifyLoadResult(runner, callback,
300 OfflinePageMetadataStore::STORE_LOAD_FAILED, result); 317 OfflinePageMetadataStore::STORE_LOAD_FAILED, result);
301 } 318 }
302 } 319 }
303 320
304 void AddOfflinePageSync(sql::Connection* db, 321 void AddOfflinePageSync(sql::Connection* db,
305 scoped_refptr<base::SingleThreadTaskRunner> runner, 322 scoped_refptr<base::SingleThreadTaskRunner> runner,
306 const OfflinePageItem& offline_page, 323 const OfflinePageItem& offline_page,
307 const OfflinePageMetadataStore::AddCallback& callback) { 324 const OfflinePageMetadataStore::AddCallback& callback) {
308 // TODO(fgorski): Only insert should happen here.
309 bool success = Insert(db, offline_page); 325 bool success = Insert(db, offline_page);
310 runner->PostTask( 326 runner->PostTask(
311 FROM_HERE, 327 FROM_HERE,
312 base::Bind(callback, success ? OfflinePageMetadataStore::SUCCESS 328 base::Bind(callback, success ? ItemActionStatus::SUCCESS
313 : OfflinePageMetadataStore::ALREADY_EXISTS)); 329 : ItemActionStatus::ALREADY_EXISTS));
330 }
331
332 void PostStoreErrorForAllPages(
333 scoped_refptr<base::SingleThreadTaskRunner> runner,
334 const std::vector<OfflinePageItem>& pages,
335 const OfflinePageMetadataStore::UpdateCallback& callback) {
336 std::map<int64_t, ItemActionStatus> failed_results;
337 for (const auto& page : pages)
338 failed_results[page.offline_id] = ItemActionStatus::STORE_ERROR;
339 runner->PostTask(FROM_HERE, base::Bind(callback, failed_results,
340 std::vector<OfflinePageItem>()));
341 }
342
343 void PostStoreErrorForAllIds(
344 scoped_refptr<base::SingleThreadTaskRunner> runner,
345 const std::vector<int64_t>& offline_ids,
346 const OfflinePageMetadataStore::UpdateCallback& callback) {
347 std::map<int64_t, ItemActionStatus> failed_results;
348 for (const auto& offline_id : offline_ids)
349 failed_results[offline_id] = ItemActionStatus::STORE_ERROR;
350 runner->PostTask(FROM_HERE, base::Bind(callback, failed_results,
351 std::vector<OfflinePageItem>()));
314 } 352 }
315 353
316 void UpdateOfflinePagesSync( 354 void UpdateOfflinePagesSync(
317 sql::Connection* db, 355 sql::Connection* db,
318 scoped_refptr<base::SingleThreadTaskRunner> runner, 356 scoped_refptr<base::SingleThreadTaskRunner> runner,
319 const std::vector<OfflinePageItem>& pages, 357 const std::vector<OfflinePageItem>& pages,
320 const OfflinePageMetadataStore::UpdateCallback& callback) { 358 const OfflinePageMetadataStore::UpdateCallback& callback) {
321 // TODO(fgorski): Update the callback to provide information about all items 359 // TODO(fgorski): Update the callback to provide information about all items
322 // and not just a high level boolean. 360 // and not just a high level boolean.
361 std::map<int64_t, ItemActionStatus> failed_results;
362 std::vector<OfflinePageItem> updated_items;
363
323 sql::Transaction transaction(db); 364 sql::Transaction transaction(db);
324 if (!transaction.Begin()) { 365 if (!transaction.Begin()) {
325 runner->PostTask(FROM_HERE, base::Bind(callback, false)); 366 PostStoreErrorForAllPages(runner, pages, callback);
326 return; 367 return;
327 } 368 }
328 369
329 // TODO(fgorski): Switch to only accept updates and not create new items. 370 for (auto& page : pages) {
Pete Williamson 2016/09/15 18:05:43 Should this be const auto& ?
fgorski 2016/09/19 23:24:30 Done.
330 for (auto& page : pages) 371 bool updated = Update(db, page);
331 InsertOrReplace(db, page); 372 if (updated)
373 updated_items.push_back(page);
374 else
375 failed_results[page.offline_id] = ItemActionStatus::DOESNT_EXIST;
376 }
332 377
333 bool result = transaction.Commit(); 378 if (!transaction.Commit()) {
334 runner->PostTask(FROM_HERE, base::Bind(callback, result)); 379 PostStoreErrorForAllPages(runner, pages, callback);
380 return;
381 }
382 runner->PostTask(FROM_HERE,
383 base::Bind(callback, failed_results, updated_items));
335 } 384 }
336 385
337 void RemoveOfflinePagesSync( 386 void RemoveOfflinePagesSync(
338 const std::vector<int64_t>& offline_ids, 387 const std::vector<int64_t>& offline_ids,
339 sql::Connection* db, 388 sql::Connection* db,
340 scoped_refptr<base::SingleThreadTaskRunner> runner, 389 scoped_refptr<base::SingleThreadTaskRunner> runner,
341 const OfflinePageMetadataStore::UpdateCallback& callback) { 390 const OfflinePageMetadataStore::UpdateCallback& callback) {
342 // TODO(bburns): add UMA metrics here (and for levelDB). 391 // TODO(fgorski): Perhaps add metrics here.
392 std::map<int64_t, ItemActionStatus> failed_results;
393 std::vector<OfflinePageItem> removed_items;
343 394
344 // If you create a transaction but don't Commit() it is automatically 395 // If you create a transaction but don't Commit() it is automatically
345 // rolled back by its destructor when it falls out of scope. 396 // rolled back by its destructor when it falls out of scope.
346 sql::Transaction transaction(db); 397 sql::Transaction transaction(db);
347 if (!transaction.Begin()) { 398 if (!transaction.Begin()) {
348 runner->PostTask(FROM_HERE, base::Bind(callback, false)); 399 PostStoreErrorForAllIds(runner, offline_ids, callback);
349 return; 400 return;
350 } 401 }
351 402
352 for (auto offline_id : offline_ids) { 403 for (auto offline_id : offline_ids) {
Pete Williamson 2016/09/15 18:05:43 const?
fgorski 2016/09/19 23:24:30 Switched to int64_t instead.
353 if (!DeleteByOfflineId(db, offline_id)) { 404 OfflinePageItem page;
354 runner->PostTask(FROM_HERE, base::Bind(callback, false)); 405 if (!GetPageByOfflineIdSync(db, offline_id, &page)) {
Pete Williamson 2016/09/15 18:05:43 Do we need this SQL operation when we can just che
fgorski 2016/09/19 23:24:30 Yes, because we want to return the deleted items f
355 return; 406 failed_results[offline_id] = ItemActionStatus::DOESNT_EXIST;
407 continue;
356 } 408 }
409
410 bool removed = DeleteByOfflineId(db, offline_id);
411 DCHECK(removed); // No reason to fail at this point.
412 // TODO(fgorski): Perhaps provide separate status for delete failure that is
413 // not STORE_ERROR or DOESNT_EXIST.
414 if (removed)
415 removed_items.push_back(page);
416 else
417 failed_results[offline_id] = ItemActionStatus::DOESNT_EXIST;
357 } 418 }
358 419
359 bool success = transaction.Commit(); 420 if (!transaction.Commit()) {
360 runner->PostTask(FROM_HERE, base::Bind(callback, success)); 421 PostStoreErrorForAllIds(runner, offline_ids, callback);
422 return;
423 }
424
425 runner->PostTask(FROM_HERE,
426 base::Bind(callback, failed_results, removed_items));
361 } 427 }
362 428
363 void ResetSync(sql::Connection* db, 429 void ResetSync(sql::Connection* db,
364 const base::FilePath& db_file_path, 430 const base::FilePath& db_file_path,
365 scoped_refptr<base::SingleThreadTaskRunner> runner, 431 scoped_refptr<base::SingleThreadTaskRunner> runner,
366 const base::Callback<void(StoreState)>& callback) { 432 const base::Callback<void(StoreState)>& callback) {
367 // This method deletes the content of the whole store and reinitializes it. 433 // This method deletes the content of the whole store and reinitializes it.
368 bool success = db->Raze(); 434 bool success = db->Raze();
369 db->Close(); 435 db->Close();
370 StoreState state; 436 StoreState state;
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
412 478
413 background_task_runner_->PostTask( 479 background_task_runner_->PostTask(
414 FROM_HERE, 480 FROM_HERE,
415 base::Bind(&AddOfflinePageSync, db_.get(), 481 base::Bind(&AddOfflinePageSync, db_.get(),
416 base::ThreadTaskRunnerHandle::Get(), offline_page, callback)); 482 base::ThreadTaskRunnerHandle::Get(), offline_page, callback));
417 } 483 }
418 484
419 void OfflinePageMetadataStoreSQL::UpdateOfflinePages( 485 void OfflinePageMetadataStoreSQL::UpdateOfflinePages(
420 const std::vector<OfflinePageItem>& pages, 486 const std::vector<OfflinePageItem>& pages,
421 const UpdateCallback& callback) { 487 const UpdateCallback& callback) {
422 if (!CheckDb(base::Bind(callback, false))) 488 if (!db_.get()) {
489 PostStoreErrorForAllPages(base::ThreadTaskRunnerHandle::Get(), pages,
490 callback);
423 return; 491 return;
492 }
424 493
425 background_task_runner_->PostTask( 494 background_task_runner_->PostTask(
426 FROM_HERE, 495 FROM_HERE,
427 base::Bind(&UpdateOfflinePagesSync, db_.get(), 496 base::Bind(&UpdateOfflinePagesSync, db_.get(),
428 base::ThreadTaskRunnerHandle::Get(), pages, callback)); 497 base::ThreadTaskRunnerHandle::Get(), pages, callback));
429 } 498 }
430 499
431 void OfflinePageMetadataStoreSQL::RemoveOfflinePages( 500 void OfflinePageMetadataStoreSQL::RemoveOfflinePages(
432 const std::vector<int64_t>& offline_ids, 501 const std::vector<int64_t>& offline_ids,
433 const UpdateCallback& callback) { 502 const UpdateCallback& callback) {
434 DCHECK(db_.get()); 503 DCHECK(db_.get());
435 504
436 if (offline_ids.empty()) { 505 if (offline_ids.empty()) {
437 // Nothing to do, but post a callback instead of calling directly 506 // Nothing to do, but post a callback instead of calling directly
438 // to preserve the async style behavior to prevent bugs. 507 // to preserve the async style behavior to prevent bugs.
439 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, 508 // TODO(fgorski): Refactor to accept item action status.
440 base::Bind(callback, true)); 509 // It works not because the list of offline IDs is empty.
510 PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), offline_ids,
Pete Williamson 2016/09/15 18:05:43 Is "STORE_ERROR" the right error for an empty list
fgorski 2016/09/19 23:24:30 Done.
511 callback);
441 return; 512 return;
442 } 513 }
443 514
444 background_task_runner_->PostTask( 515 background_task_runner_->PostTask(
445 FROM_HERE, base::Bind(&RemoveOfflinePagesSync, offline_ids, db_.get(), 516 FROM_HERE, base::Bind(&RemoveOfflinePagesSync, offline_ids, db_.get(),
446 base::ThreadTaskRunnerHandle::Get(), callback)); 517 base::ThreadTaskRunnerHandle::Get(), callback));
447 } 518 }
448 519
449 void OfflinePageMetadataStoreSQL::Reset(const ResetCallback& callback) { 520 void OfflinePageMetadataStoreSQL::Reset(const ResetCallback& callback) {
450 if (!CheckDb(base::Bind(callback, false))) 521 if (!CheckDb(base::Bind(callback, false)))
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
496 DCHECK(db_.get()); 567 DCHECK(db_.get());
497 if (!db_.get()) { 568 if (!db_.get()) {
498 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, 569 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
499 base::Bind(callback)); 570 base::Bind(callback));
500 return false; 571 return false;
501 } 572 }
502 return true; 573 return true;
503 } 574 }
504 575
505 } // namespace offline_pages 576 } // namespace offline_pages
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698