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

Side by Side Diff: components/offline_pages/background/request_queue_store_sql.cc

Issue 2489443002: Move all components/offline_pages/ files into component/offline_pages/core (Closed)
Patch Set: more rebase Created 4 years 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
(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/background/request_queue_store_sql.h"
6
7 #include <unordered_set>
8
9 #include "base/bind.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/sequenced_task_runner.h"
15 #include "base/threading/thread_task_runner_handle.h"
16 #include "components/offline_pages/background/save_page_request.h"
17 #include "sql/connection.h"
18 #include "sql/statement.h"
19 #include "sql/transaction.h"
20
21 namespace offline_pages {
22
23 template class StoreUpdateResult<SavePageRequest>;
24
25 namespace {
26
27 using StoreStateCallback = base::Callback<void(StoreState)>;
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 REQUEST_QUEUE_TABLE_NAME "request_queue_v1"
32 const bool kUserRequested = true;
33
34 bool CreateRequestQueueTable(sql::Connection* db) {
35 const char kSql[] = "CREATE TABLE IF NOT EXISTS " REQUEST_QUEUE_TABLE_NAME
36 " (request_id INTEGER PRIMARY KEY NOT NULL,"
37 " creation_time INTEGER NOT NULL,"
38 " activation_time INTEGER NOT NULL DEFAULT 0,"
39 " last_attempt_time INTEGER NOT NULL DEFAULT 0,"
40 " started_attempt_count INTEGER NOT NULL,"
41 " completed_attempt_count INTEGER NOT NULL,"
42 " state INTEGER NOT NULL DEFAULT 0,"
43 " url VARCHAR NOT NULL,"
44 " client_namespace VARCHAR NOT NULL,"
45 " client_id VARCHAR NOT NULL"
46 ")";
47 return db->Execute(kSql);
48 }
49
50 bool CreateSchema(sql::Connection* db) {
51 // If there is not already a state column, we need to drop the old table. We
52 // are choosing to drop instead of upgrade since the feature is not yet
53 // released, so we don't use a transaction to protect existing data, or try to
54 // migrate it.
55 if (!db->DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "state")) {
56 if (!db->Execute("DROP TABLE IF EXISTS " REQUEST_QUEUE_TABLE_NAME))
57 return false;
58 }
59
60 if (!CreateRequestQueueTable(db))
61 return false;
62
63 // TODO(fgorski): Add indices here.
64 return true;
65 }
66
67 // Create a save page request from a SQL result. Expects complete rows with
68 // all columns present. Columns are in order they are defined in select query
69 // in |GetOneRequest| method.
70 std::unique_ptr<SavePageRequest> MakeSavePageRequest(
71 const sql::Statement& statement) {
72 const int64_t id = statement.ColumnInt64(0);
73 const base::Time creation_time =
74 base::Time::FromInternalValue(statement.ColumnInt64(1));
75 const base::Time activation_time =
76 base::Time::FromInternalValue(statement.ColumnInt64(2));
77 const base::Time last_attempt_time =
78 base::Time::FromInternalValue(statement.ColumnInt64(3));
79 const int64_t started_attempt_count = statement.ColumnInt64(4);
80 const int64_t completed_attempt_count = statement.ColumnInt64(5);
81 const SavePageRequest::RequestState state =
82 static_cast<SavePageRequest::RequestState>(statement.ColumnInt64(6));
83 const GURL url(statement.ColumnString(7));
84 const ClientId client_id(statement.ColumnString(8),
85 statement.ColumnString(9));
86
87 DVLOG(2) << "making save page request - id " << id << " url " << url
88 << " client_id " << client_id.name_space << "-" << client_id.id
89 << " creation time " << creation_time << " user requested "
90 << kUserRequested;
91
92 std::unique_ptr<SavePageRequest> request(new SavePageRequest(
93 id, url, client_id, creation_time, activation_time, kUserRequested));
94 request->set_last_attempt_time(last_attempt_time);
95 request->set_started_attempt_count(started_attempt_count);
96 request->set_completed_attempt_count(completed_attempt_count);
97 request->set_request_state(state);
98 return request;
99 }
100
101 // Get a request for a specific id.
102 std::unique_ptr<SavePageRequest> GetOneRequest(sql::Connection* db,
103 const int64_t request_id) {
104 const char kSql[] =
105 "SELECT request_id, creation_time, activation_time,"
106 " last_attempt_time, started_attempt_count, completed_attempt_count,"
107 " state, url, client_namespace, client_id"
108 " FROM " REQUEST_QUEUE_TABLE_NAME " WHERE request_id=?";
109
110 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
111 statement.BindInt64(0, request_id);
112
113 if (statement.Step())
114 return MakeSavePageRequest(statement);
115 return std::unique_ptr<SavePageRequest>(nullptr);
116 }
117
118 ItemActionStatus DeleteRequestById(sql::Connection* db, int64_t request_id) {
119 const char kSql[] =
120 "DELETE FROM " REQUEST_QUEUE_TABLE_NAME " WHERE request_id=?";
121 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
122 statement.BindInt64(0, request_id);
123 if (!statement.Run())
124 return ItemActionStatus::STORE_ERROR;
125 else if (db->GetLastChangeCount() == 0)
126 return ItemActionStatus::NOT_FOUND;
127 return ItemActionStatus::SUCCESS;
128 }
129
130 ItemActionStatus Insert(sql::Connection* db, const SavePageRequest& request) {
131 const char kSql[] =
132 "INSERT OR IGNORE INTO " REQUEST_QUEUE_TABLE_NAME
133 " (request_id, creation_time, activation_time,"
134 " last_attempt_time, started_attempt_count, completed_attempt_count,"
135 " state, url, client_namespace, client_id)"
136 " VALUES "
137 " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
138
139 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
140 statement.BindInt64(0, request.request_id());
141 statement.BindInt64(1, request.creation_time().ToInternalValue());
142 statement.BindInt64(2, request.activation_time().ToInternalValue());
143 statement.BindInt64(3, request.last_attempt_time().ToInternalValue());
144 statement.BindInt64(4, request.started_attempt_count());
145 statement.BindInt64(5, request.completed_attempt_count());
146 statement.BindInt64(6, static_cast<int64_t>(request.request_state()));
147 statement.BindString(7, request.url().spec());
148 statement.BindString(8, request.client_id().name_space);
149 statement.BindString(9, request.client_id().id);
150
151 if (!statement.Run())
152 return ItemActionStatus::STORE_ERROR;
153 if (db->GetLastChangeCount() == 0)
154 return ItemActionStatus::ALREADY_EXISTS;
155 return ItemActionStatus::SUCCESS;
156 }
157
158 ItemActionStatus Update(sql::Connection* db, const SavePageRequest& request) {
159 const char kSql[] =
160 "UPDATE OR IGNORE " REQUEST_QUEUE_TABLE_NAME
161 " SET creation_time = ?, activation_time = ?, last_attempt_time = ?,"
162 " started_attempt_count = ?, completed_attempt_count = ?, state = ?,"
163 " url = ?, client_namespace = ?, client_id = ?"
164 " WHERE request_id = ?";
165
166 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
167 statement.BindInt64(0, request.creation_time().ToInternalValue());
168 statement.BindInt64(1, request.activation_time().ToInternalValue());
169 statement.BindInt64(2, request.last_attempt_time().ToInternalValue());
170 statement.BindInt64(3, request.started_attempt_count());
171 statement.BindInt64(4, request.completed_attempt_count());
172 statement.BindInt64(5, static_cast<int64_t>(request.request_state()));
173 statement.BindString(6, request.url().spec());
174 statement.BindString(7, request.client_id().name_space);
175 statement.BindString(8, request.client_id().id);
176 statement.BindInt64(9, request.request_id());
177
178 if (!statement.Run())
179 return ItemActionStatus::STORE_ERROR;
180 if (db->GetLastChangeCount() == 0)
181 return ItemActionStatus::NOT_FOUND;
182 return ItemActionStatus::SUCCESS;
183 }
184
185 void PostStoreUpdateResultForIds(
186 scoped_refptr<base::SingleThreadTaskRunner> runner,
187 StoreState store_state,
188 const std::vector<int64_t>& item_ids,
189 ItemActionStatus action_status,
190 const RequestQueueStore::UpdateCallback& callback) {
191 std::unique_ptr<UpdateRequestsResult> result(
192 new UpdateRequestsResult(store_state));
193 for (const auto& item_id : item_ids)
194 result->item_statuses.push_back(std::make_pair(item_id, action_status));
195 runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
196 }
197
198 void PostStoreErrorForAllRequests(
199 scoped_refptr<base::SingleThreadTaskRunner> runner,
200 const std::vector<SavePageRequest>& items,
201 const RequestQueueStore::UpdateCallback& callback) {
202 std::vector<int64_t> item_ids;
203 for (const auto& item : items)
204 item_ids.push_back(item.request_id());
205 PostStoreUpdateResultForIds(runner, StoreState::LOADED, item_ids,
206 ItemActionStatus::STORE_ERROR, callback);
207 }
208
209 void PostStoreErrorForAllIds(
210 scoped_refptr<base::SingleThreadTaskRunner> runner,
211 const std::vector<int64_t>& item_ids,
212 const RequestQueueStore::UpdateCallback& callback) {
213 PostStoreUpdateResultForIds(runner, StoreState::LOADED, item_ids,
214 ItemActionStatus::STORE_ERROR, callback);
215 }
216
217 bool InitDatabase(sql::Connection* db, const base::FilePath& path) {
218 db->set_page_size(4096);
219 db->set_cache_size(500);
220 db->set_histogram_tag("BackgroundRequestQueue");
221 db->set_exclusive_locking();
222
223 base::File::Error err;
224 if (!base::CreateDirectoryAndGetError(path.DirName(), &err))
225 return false;
226 if (!db->Open(path))
227 return false;
228 db->Preload();
229
230 return CreateSchema(db);
231 }
232
233 void GetRequestsSync(sql::Connection* db,
234 scoped_refptr<base::SingleThreadTaskRunner> runner,
235 const RequestQueueStore::GetRequestsCallback& callback) {
236 const char kSql[] =
237 "SELECT request_id, creation_time, activation_time,"
238 " last_attempt_time, started_attempt_count, completed_attempt_count,"
239 " state, url, client_namespace, client_id"
240 " FROM " REQUEST_QUEUE_TABLE_NAME;
241
242 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
243
244 std::vector<std::unique_ptr<SavePageRequest>> requests;
245 while (statement.Step())
246 requests.push_back(MakeSavePageRequest(statement));
247
248 runner->PostTask(FROM_HERE, base::Bind(callback, statement.Succeeded(),
249 base::Passed(&requests)));
250 }
251
252 void GetRequestsByIdsSync(sql::Connection* db,
253 scoped_refptr<base::SingleThreadTaskRunner> runner,
254 const std::vector<int64_t>& request_ids,
255 const RequestQueueStore::UpdateCallback& callback) {
256 // TODO(fgorski): Perhaps add metrics here.
257 std::unique_ptr<UpdateRequestsResult> result(
258 new UpdateRequestsResult(StoreState::LOADED));
259
260 // If you create a transaction but don't Commit() it is automatically
261 // rolled back by its destructor when it falls out of scope.
262 sql::Transaction transaction(db);
263 if (!transaction.Begin()) {
264 PostStoreErrorForAllIds(runner, request_ids, callback);
265 return;
266 }
267
268 // Make sure not to include the same request multiple times, preserving the
269 // order of non-duplicated IDs in the result.
270 std::unordered_set<int64_t> processed_ids;
271 for (int64_t request_id : request_ids) {
272 if (!processed_ids.insert(request_id).second)
273 continue;
274 std::unique_ptr<SavePageRequest> request = GetOneRequest(db, request_id);
275 if (request.get())
276 result->updated_items.push_back(*request);
277 ItemActionStatus status =
278 request.get() ? ItemActionStatus::SUCCESS : ItemActionStatus::NOT_FOUND;
279 result->item_statuses.push_back(std::make_pair(request_id, status));
280 }
281
282 if (!transaction.Commit()) {
283 PostStoreErrorForAllIds(runner, request_ids, callback);
284 return;
285 }
286
287 runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
288 }
289
290 void AddRequestSync(sql::Connection* db,
291 scoped_refptr<base::SingleThreadTaskRunner> runner,
292 const SavePageRequest& request,
293 const RequestQueueStore::AddCallback& callback) {
294 // TODO(fgorski): add UMA metrics here.
295 ItemActionStatus status = Insert(db, request);
296 runner->PostTask(FROM_HERE, base::Bind(callback, status));
297 }
298
299 void UpdateRequestsSync(sql::Connection* db,
300 scoped_refptr<base::SingleThreadTaskRunner> runner,
301 const std::vector<SavePageRequest>& requests,
302 const RequestQueueStore::UpdateCallback& callback) {
303 // TODO(fgorski): add UMA metrics here.
304 std::unique_ptr<UpdateRequestsResult> result(
305 new UpdateRequestsResult(StoreState::LOADED));
306
307 sql::Transaction transaction(db);
308 if (!transaction.Begin()) {
309 PostStoreErrorForAllRequests(runner, requests, callback);
310 return;
311 }
312
313 for (const auto& request : requests) {
314 ItemActionStatus status = Update(db, request);
315 result->item_statuses.push_back(
316 std::make_pair(request.request_id(), status));
317 if (status == ItemActionStatus::SUCCESS)
318 result->updated_items.push_back(request);
319 }
320
321 if (!transaction.Commit()) {
322 PostStoreErrorForAllRequests(runner, requests, callback);
323 return;
324 }
325
326 runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
327 }
328
329 void RemoveRequestsSync(sql::Connection* db,
330 scoped_refptr<base::SingleThreadTaskRunner> runner,
331 const std::vector<int64_t>& request_ids,
332 const RequestQueueStore::UpdateCallback& callback) {
333 // TODO(fgorski): Perhaps add metrics here.
334 std::unique_ptr<UpdateRequestsResult> result(
335 new UpdateRequestsResult(StoreState::LOADED));
336
337 // If you create a transaction but don't Commit() it is automatically
338 // rolled back by its destructor when it falls out of scope.
339 sql::Transaction transaction(db);
340 if (!transaction.Begin()) {
341 PostStoreErrorForAllIds(runner, request_ids, callback);
342 return;
343 }
344
345 // Read the request before we delete it, and if the delete worked, put it on
346 // the queue of requests that got deleted.
347 for (int64_t request_id : request_ids) {
348 std::unique_ptr<SavePageRequest> request = GetOneRequest(db, request_id);
349 ItemActionStatus status = DeleteRequestById(db, request_id);
350 result->item_statuses.push_back(std::make_pair(request_id, status));
351 if (status == ItemActionStatus::SUCCESS)
352 result->updated_items.push_back(*request);
353 }
354
355 if (!transaction.Commit()) {
356 PostStoreErrorForAllIds(runner, request_ids, callback);
357 return;
358 }
359
360 runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result)));
361 }
362
363 void OpenConnectionSync(sql::Connection* db,
364 scoped_refptr<base::SingleThreadTaskRunner> runner,
365 const base::FilePath& path,
366 const StoreStateCallback& callback) {
367 StoreState state =
368 InitDatabase(db, path) ? StoreState::LOADED : StoreState::FAILED_LOADING;
369 runner->PostTask(FROM_HERE, base::Bind(callback, state));
370 }
371
372 void ResetSync(sql::Connection* db,
373 const base::FilePath& db_file_path,
374 scoped_refptr<base::SingleThreadTaskRunner> runner,
375 const StoreStateCallback& callback) {
376 // This method deletes the content of the whole store and reinitializes it.
377 bool success = db->Raze();
378 db->Close();
379 StoreState state;
380 if (!success)
381 state = StoreState::FAILED_RESET;
382 if (InitDatabase(db, db_file_path))
383 state = StoreState::LOADED;
384 else
385 state = StoreState::FAILED_LOADING;
386
387 runner->PostTask(FROM_HERE, base::Bind(callback, state));
388 }
389
390 } // anonymous namespace
391
392 RequestQueueStoreSQL::RequestQueueStoreSQL(
393 scoped_refptr<base::SequencedTaskRunner> background_task_runner,
394 const base::FilePath& path)
395 : background_task_runner_(std::move(background_task_runner)),
396 db_file_path_(path.AppendASCII("RequestQueue.db")),
397 weak_ptr_factory_(this) {
398 OpenConnection();
399 }
400
401 RequestQueueStoreSQL::~RequestQueueStoreSQL() {
402 if (db_.get())
403 background_task_runner_->DeleteSoon(FROM_HERE, db_.release());
404 }
405
406 bool RequestQueueStoreSQL::CheckDb(const base::Closure& callback) {
407 DCHECK(db_.get());
408 if (!db_.get()) {
409 // Nothing to do, but post a callback instead of calling directly
410 // to preserve the async style behavior to prevent bugs.
411 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
412 return false;
413 }
414 return true;
415 }
416
417 void RequestQueueStoreSQL::GetRequests(const GetRequestsCallback& callback) {
418 DCHECK(db_.get());
419 std::vector<std::unique_ptr<SavePageRequest>> requests;
420 if (!CheckDb(base::Bind(callback, false, base::Passed(&requests))))
421 return;
422
423 background_task_runner_->PostTask(
424 FROM_HERE, base::Bind(&GetRequestsSync, db_.get(),
425 base::ThreadTaskRunnerHandle::Get(), callback));
426 }
427
428 void RequestQueueStoreSQL::GetRequestsByIds(
429 const std::vector<int64_t>& request_ids,
430 const UpdateCallback& callback) {
431 if (!db_.get()) {
432 PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), request_ids,
433 callback);
434 return;
435 }
436
437 background_task_runner_->PostTask(
438 FROM_HERE,
439 base::Bind(&GetRequestsByIdsSync, db_.get(),
440 base::ThreadTaskRunnerHandle::Get(), request_ids, callback));
441 }
442
443 void RequestQueueStoreSQL::AddRequest(const SavePageRequest& request,
444 const AddCallback& callback) {
445 if (!CheckDb(base::Bind(callback, ItemActionStatus::STORE_ERROR)))
446 return;
447
448 background_task_runner_->PostTask(
449 FROM_HERE,
450 base::Bind(&AddRequestSync, db_.get(),
451 base::ThreadTaskRunnerHandle::Get(), request, callback));
452 }
453
454 void RequestQueueStoreSQL::UpdateRequests(
455 const std::vector<SavePageRequest>& requests,
456 const UpdateCallback& callback) {
457 if (!db_.get()) {
458 PostStoreErrorForAllRequests(base::ThreadTaskRunnerHandle::Get(), requests,
459 callback);
460 return;
461 }
462
463 background_task_runner_->PostTask(
464 FROM_HERE,
465 base::Bind(&UpdateRequestsSync, db_.get(),
466 base::ThreadTaskRunnerHandle::Get(), requests, callback));
467 }
468
469 void RequestQueueStoreSQL::RemoveRequests(
470 const std::vector<int64_t>& request_ids,
471 const UpdateCallback& callback) {
472 if (!db_.get()) {
473 PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), request_ids,
474 callback);
475 return;
476 }
477
478 background_task_runner_->PostTask(
479 FROM_HERE,
480 base::Bind(&RemoveRequestsSync, db_.get(),
481 base::ThreadTaskRunnerHandle::Get(), request_ids, callback));
482 }
483
484 void RequestQueueStoreSQL::Reset(const ResetCallback& callback) {
485 DCHECK(db_.get());
486 if (!CheckDb(base::Bind(callback, false)))
487 return;
488
489 background_task_runner_->PostTask(
490 FROM_HERE,
491 base::Bind(&ResetSync, db_.get(), db_file_path_,
492 base::ThreadTaskRunnerHandle::Get(),
493 base::Bind(&RequestQueueStoreSQL::OnResetDone,
494 weak_ptr_factory_.GetWeakPtr(), callback)));
495 }
496
497 void RequestQueueStoreSQL::OpenConnection() {
498 DCHECK(!db_);
499 db_.reset(new sql::Connection());
500 background_task_runner_->PostTask(
501 FROM_HERE,
502 base::Bind(&OpenConnectionSync, db_.get(),
503 base::ThreadTaskRunnerHandle::Get(), db_file_path_,
504 base::Bind(&RequestQueueStoreSQL::OnOpenConnectionDone,
505 weak_ptr_factory_.GetWeakPtr())));
506 }
507
508 void RequestQueueStoreSQL::OnOpenConnectionDone(StoreState state) {
509 DCHECK(db_.get());
510
511 state_ = state;
512
513 // Unfortunately we were not able to open DB connection.
514 if (state_ != StoreState::LOADED)
515 db_.reset();
516 }
517
518 void RequestQueueStoreSQL::OnResetDone(const ResetCallback& callback,
519 StoreState state) {
520 // Complete connection initialization post reset.
521 OnOpenConnectionDone(state);
522 callback.Run(state == StoreState::LOADED);
523 }
524
525 StoreState RequestQueueStoreSQL::state() const {
526 return state_;
527 }
528
529 } // namespace offline_pages
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698