| OLD | NEW |
| 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; |
| 24 |
| 23 namespace { | 25 namespace { |
| 24 | 26 |
| 25 // This is a macro instead of a const so that | 27 // This is a macro instead of a const so that |
| 26 // it can be used inline in other SQL statements below. | 28 // it can be used inline in other SQL statements below. |
| 27 #define OFFLINE_PAGES_TABLE_NAME "offlinepages_v1" | 29 #define OFFLINE_PAGES_TABLE_NAME "offlinepages_v1" |
| 28 | 30 |
| 29 // New columns should be added at the end of the list in order to avoid | 31 // New columns should be added at the end of the list in order to avoid |
| 30 // complicated table upgrade. | 32 // complicated table upgrade. |
| 31 const char kOfflinePagesColumns[] = | 33 const char kOfflinePagesColumns[] = |
| 32 "(offline_id INTEGER PRIMARY KEY NOT NULL," | 34 "(offline_id INTEGER PRIMARY KEY NOT NULL," |
| (...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 242 } | 244 } |
| 243 if (!db->Open(path)) { | 245 if (!db->Open(path)) { |
| 244 LOG(ERROR) << "Failed to open database"; | 246 LOG(ERROR) << "Failed to open database"; |
| 245 return false; | 247 return false; |
| 246 } | 248 } |
| 247 db->Preload(); | 249 db->Preload(); |
| 248 | 250 |
| 249 return CreateSchema(db); | 251 return CreateSchema(db); |
| 250 } | 252 } |
| 251 | 253 |
| 254 void NotifyLoadResult(scoped_refptr<base::SingleThreadTaskRunner> runner, |
| 255 const OfflinePageMetadataStore::LoadCallback& callback, |
| 256 OfflinePageMetadataStore::LoadStatus status, |
| 257 const std::vector<OfflinePageItem>& result) { |
| 258 // TODO(bburns): Switch to SQL specific UMA metrics. |
| 259 UMA_HISTOGRAM_ENUMERATION("OfflinePages.LoadStatus", status, |
| 260 OfflinePageMetadataStore::LOAD_STATUS_COUNT); |
| 261 if (status == OfflinePageMetadataStore::LOAD_SUCCEEDED) { |
| 262 UMA_HISTOGRAM_COUNTS("OfflinePages.SavedPageCount", |
| 263 static_cast<int32_t>(result.size())); |
| 264 } else { |
| 265 DVLOG(1) << "Offline pages database loading failed: " << status; |
| 266 } |
| 267 runner->PostTask(FROM_HERE, base::Bind(callback, status, result)); |
| 268 } |
| 269 |
| 270 void OpenConnectionSync(sql::Connection* db, |
| 271 scoped_refptr<base::SingleThreadTaskRunner> runner, |
| 272 const base::FilePath& path, |
| 273 const base::Callback<void(StoreState)>& callback) { |
| 274 StoreState state = InitDatabase(db, path) |
| 275 ? OfflinePageMetadataStore::LOADED |
| 276 : OfflinePageMetadataStore::FAILED_INITIALIZATION; |
| 277 runner->PostTask(FROM_HERE, base::Bind(callback, state)); |
| 278 } |
| 279 |
| 280 void GetOfflinePagesSync( |
| 281 sql::Connection* db, |
| 282 scoped_refptr<base::SingleThreadTaskRunner> runner, |
| 283 const OfflinePageMetadataStore::LoadCallback& callback) { |
| 284 const char kSql[] = "SELECT * FROM " OFFLINE_PAGES_TABLE_NAME; |
| 285 |
| 286 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); |
| 287 |
| 288 std::vector<OfflinePageItem> result; |
| 289 while (statement.Step()) |
| 290 result.push_back(MakeOfflinePageItem(&statement)); |
| 291 |
| 292 if (statement.Succeeded()) { |
| 293 NotifyLoadResult(runner, callback, OfflinePageMetadataStore::LOAD_SUCCEEDED, |
| 294 result); |
| 295 } else { |
| 296 result.clear(); |
| 297 NotifyLoadResult(runner, callback, |
| 298 OfflinePageMetadataStore::STORE_LOAD_FAILED, result); |
| 299 } |
| 300 } |
| 301 |
| 302 void ResetSync(sql::Connection* db, |
| 303 const base::FilePath& db_file_path, |
| 304 scoped_refptr<base::SingleThreadTaskRunner> runner, |
| 305 const base::Callback<void(StoreState)>& callback) { |
| 306 // This method deletes the content of the whole store and reinitializes it. |
| 307 bool success = db->Raze(); |
| 308 db->Close(); |
| 309 StoreState state; |
| 310 if (success) { |
| 311 state = InitDatabase(db, db_file_path) |
| 312 ? OfflinePageMetadataStore::LOADED |
| 313 : OfflinePageMetadataStore::FAILED_INITIALIZATION; |
| 314 } else { |
| 315 state = OfflinePageMetadataStore::FAILED_RESET; |
| 316 } |
| 317 runner->PostTask(FROM_HERE, base::Bind(callback, state)); |
| 318 } |
| 319 |
| 252 } // anonymous namespace | 320 } // anonymous namespace |
| 253 | 321 |
| 254 OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL( | 322 OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL( |
| 255 scoped_refptr<base::SequencedTaskRunner> background_task_runner, | 323 scoped_refptr<base::SequencedTaskRunner> background_task_runner, |
| 256 const base::FilePath& path) | 324 const base::FilePath& path) |
| 257 : background_task_runner_(std::move(background_task_runner)), | 325 : background_task_runner_(std::move(background_task_runner)), |
| 258 db_file_path_(path.AppendASCII("OfflinePages.db")) {} | 326 db_file_path_(path.AppendASCII("OfflinePages.db")), |
| 327 state_(NOT_LOADED), |
| 328 weak_ptr_factory_(this) { |
| 329 OpenConnection(); |
| 330 } |
| 259 | 331 |
| 260 OfflinePageMetadataStoreSQL::~OfflinePageMetadataStoreSQL() { | 332 OfflinePageMetadataStoreSQL::~OfflinePageMetadataStoreSQL() { |
| 261 if (db_.get() && | 333 if (db_.get() && |
| 262 !background_task_runner_->DeleteSoon(FROM_HERE, db_.release())) { | 334 !background_task_runner_->DeleteSoon(FROM_HERE, db_.release())) { |
| 263 DLOG(WARNING) << "SQL database will not be deleted."; | 335 DLOG(WARNING) << "SQL database will not be deleted."; |
| 264 } | 336 } |
| 265 } | 337 } |
| 266 | 338 |
| 267 void OfflinePageMetadataStoreSQL::LoadSync( | |
| 268 sql::Connection* db, | |
| 269 const base::FilePath& path, | |
| 270 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
| 271 const LoadCallback& callback) { | |
| 272 if (!InitDatabase(db, path)) { | |
| 273 NotifyLoadResult(runner, callback, STORE_INIT_FAILED, | |
| 274 std::vector<OfflinePageItem>()); | |
| 275 return; | |
| 276 } | |
| 277 | |
| 278 const char kSql[] = "SELECT * FROM " OFFLINE_PAGES_TABLE_NAME; | |
| 279 | |
| 280 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
| 281 | |
| 282 std::vector<OfflinePageItem> result; | |
| 283 while (statement.Step()) { | |
| 284 result.push_back(MakeOfflinePageItem(&statement)); | |
| 285 } | |
| 286 | |
| 287 if (statement.Succeeded()) { | |
| 288 NotifyLoadResult(runner, callback, LOAD_SUCCEEDED, result); | |
| 289 } else { | |
| 290 NotifyLoadResult(runner, callback, STORE_LOAD_FAILED, | |
| 291 std::vector<OfflinePageItem>()); | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 void OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePageSync( | 339 void OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePageSync( |
| 296 const OfflinePageItem& offline_page, | 340 const OfflinePageItem& offline_page, |
| 297 sql::Connection* db, | 341 sql::Connection* db, |
| 298 scoped_refptr<base::SingleThreadTaskRunner> runner, | 342 scoped_refptr<base::SingleThreadTaskRunner> runner, |
| 299 const UpdateCallback& callback) { | 343 const UpdateCallback& callback) { |
| 300 // TODO(bburns): add UMA metrics here (and for levelDB). | 344 // TODO(bburns): add UMA metrics here (and for levelDB). |
| 301 bool ok = InsertOrReplace(db, offline_page); | 345 bool ok = InsertOrReplace(db, offline_page); |
| 302 runner->PostTask(FROM_HERE, base::Bind(callback, ok)); | 346 runner->PostTask(FROM_HERE, base::Bind(callback, ok)); |
| 303 } | 347 } |
| 304 | 348 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 320 if (!DeleteByOfflineId(db, offline_id)) { | 364 if (!DeleteByOfflineId(db, offline_id)) { |
| 321 runner->PostTask(FROM_HERE, base::Bind(callback, false)); | 365 runner->PostTask(FROM_HERE, base::Bind(callback, false)); |
| 322 return; | 366 return; |
| 323 } | 367 } |
| 324 } | 368 } |
| 325 | 369 |
| 326 bool success = transaction.Commit(); | 370 bool success = transaction.Commit(); |
| 327 runner->PostTask(FROM_HERE, base::Bind(callback, success)); | 371 runner->PostTask(FROM_HERE, base::Bind(callback, success)); |
| 328 } | 372 } |
| 329 | 373 |
| 330 void OfflinePageMetadataStoreSQL::ResetSync( | 374 void OfflinePageMetadataStoreSQL::GetOfflinePages( |
| 331 std::unique_ptr<sql::Connection> db, | 375 const LoadCallback& callback) { |
| 332 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
| 333 const ResetCallback& callback) { | |
| 334 const char kSql[] = "DELETE FROM " OFFLINE_PAGES_TABLE_NAME; | |
| 335 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
| 336 runner->PostTask(FROM_HERE, base::Bind(callback, statement.Run())); | |
| 337 } | |
| 338 | |
| 339 void OfflinePageMetadataStoreSQL::NotifyLoadResult( | |
| 340 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
| 341 const LoadCallback& callback, | |
| 342 LoadStatus status, | |
| 343 const std::vector<OfflinePageItem>& result) { | |
| 344 // TODO(bburns): Switch to SQL specific UMA metrics. | |
| 345 UMA_HISTOGRAM_ENUMERATION("OfflinePages.LoadStatus", status, | |
| 346 OfflinePageMetadataStore::LOAD_STATUS_COUNT); | |
| 347 if (status == LOAD_SUCCEEDED) { | |
| 348 UMA_HISTOGRAM_COUNTS("OfflinePages.SavedPageCount", | |
| 349 static_cast<int32_t>(result.size())); | |
| 350 } else { | |
| 351 DVLOG(1) << "Offline pages database loading failed: " << status; | |
| 352 } | |
| 353 runner->PostTask(FROM_HERE, base::Bind(callback, status, result)); | |
| 354 } | |
| 355 | |
| 356 void OfflinePageMetadataStoreSQL::Load(const LoadCallback& callback) { | |
| 357 db_.reset(new sql::Connection()); | |
| 358 background_task_runner_->PostTask( | 376 background_task_runner_->PostTask( |
| 359 FROM_HERE, | 377 FROM_HERE, base::Bind(&GetOfflinePagesSync, db_.get(), |
| 360 base::Bind(&OfflinePageMetadataStoreSQL::LoadSync, db_.get(), | 378 base::ThreadTaskRunnerHandle::Get(), callback)); |
| 361 db_file_path_, base::ThreadTaskRunnerHandle::Get(), callback)); | |
| 362 } | 379 } |
| 363 | 380 |
| 364 void OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePage( | 381 void OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePage( |
| 365 const OfflinePageItem& offline_page, | 382 const OfflinePageItem& offline_page, |
| 366 const UpdateCallback& callback) { | 383 const UpdateCallback& callback) { |
| 367 DCHECK(db_.get()); | 384 DCHECK(db_.get()); |
| 368 background_task_runner_->PostTask( | 385 background_task_runner_->PostTask( |
| 369 FROM_HERE, | 386 FROM_HERE, |
| 370 base::Bind(&OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePageSync, | 387 base::Bind(&OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePageSync, |
| 371 offline_page, db_.get(), base::ThreadTaskRunnerHandle::Get(), | 388 offline_page, db_.get(), base::ThreadTaskRunnerHandle::Get(), |
| (...skipping 14 matching lines...) Expand all Loading... |
| 386 } | 403 } |
| 387 | 404 |
| 388 background_task_runner_->PostTask( | 405 background_task_runner_->PostTask( |
| 389 FROM_HERE, | 406 FROM_HERE, |
| 390 base::Bind(&OfflinePageMetadataStoreSQL::RemoveOfflinePagesSync, | 407 base::Bind(&OfflinePageMetadataStoreSQL::RemoveOfflinePagesSync, |
| 391 offline_ids, db_.get(), base::ThreadTaskRunnerHandle::Get(), | 408 offline_ids, db_.get(), base::ThreadTaskRunnerHandle::Get(), |
| 392 callback)); | 409 callback)); |
| 393 } | 410 } |
| 394 | 411 |
| 395 void OfflinePageMetadataStoreSQL::Reset(const ResetCallback& callback) { | 412 void OfflinePageMetadataStoreSQL::Reset(const ResetCallback& callback) { |
| 413 if (!CheckDb(base::Bind(callback, false))) |
| 414 return; |
| 415 |
| 396 background_task_runner_->PostTask( | 416 background_task_runner_->PostTask( |
| 397 FROM_HERE, | 417 FROM_HERE, |
| 398 base::Bind(&OfflinePageMetadataStoreSQL::ResetSync, base::Passed(&db_), | 418 base::Bind(&ResetSync, db_.get(), db_file_path_, |
| 399 base::ThreadTaskRunnerHandle::Get(), callback)); | 419 base::ThreadTaskRunnerHandle::Get(), |
| 420 base::Bind(&OfflinePageMetadataStoreSQL::OnResetDone, |
| 421 weak_ptr_factory_.GetWeakPtr(), callback))); |
| 422 } |
| 423 |
| 424 StoreState OfflinePageMetadataStoreSQL::state() const { |
| 425 return state_; |
| 426 } |
| 427 |
| 428 void OfflinePageMetadataStoreSQL::OpenConnection() { |
| 429 DCHECK(!db_); |
| 430 db_.reset(new sql::Connection()); |
| 431 background_task_runner_->PostTask( |
| 432 FROM_HERE, |
| 433 base::Bind(&OpenConnectionSync, db_.get(), |
| 434 base::ThreadTaskRunnerHandle::Get(), db_file_path_, |
| 435 base::Bind(&OfflinePageMetadataStoreSQL::OnOpenConnectionDone, |
| 436 weak_ptr_factory_.GetWeakPtr()))); |
| 437 } |
| 438 |
| 439 void OfflinePageMetadataStoreSQL::OnOpenConnectionDone(StoreState state) { |
| 440 DCHECK(db_.get()); |
| 441 |
| 442 state_ = state; |
| 443 |
| 444 // Unfortunately we were not able to open DB connection. |
| 445 if (state != OfflinePageMetadataStore::LOADED) |
| 446 db_.reset(); |
| 447 |
| 448 // TODO(fgorski): This might be a place to start store recovery. Alternatively |
| 449 // that can be attempted in the OfflinePageModel. |
| 450 } |
| 451 |
| 452 void OfflinePageMetadataStoreSQL::OnResetDone(const ResetCallback& callback, |
| 453 StoreState state) { |
| 454 OnOpenConnectionDone(state); |
| 455 callback.Run(state == LOADED); |
| 456 } |
| 457 |
| 458 bool OfflinePageMetadataStoreSQL::CheckDb(const base::Closure& callback) { |
| 459 DCHECK(db_.get()); |
| 460 if (!db_.get()) { |
| 461 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
| 462 base::Bind(callback)); |
| 463 return false; |
| 464 } |
| 465 return true; |
| 400 } | 466 } |
| 401 | 467 |
| 402 } // namespace offline_pages | 468 } // namespace offline_pages |
| OLD | NEW |