OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/browser/history/visit_database.h" | 5 #include "chrome/browser/history/visit_database.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <limits> | 8 #include <limits> |
9 #include <map> | 9 #include <map> |
10 #include <set> | 10 #include <set> |
11 | 11 |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "base/message_loop.h" | 13 #include "base/message_loop.h" |
14 #include "base/metrics/histogram.h" | 14 #include "base/metrics/histogram.h" |
15 #include "base/string_number_conversions.h" | 15 #include "base/string_number_conversions.h" |
16 #include "base/timer.h" | 16 #include "base/timer.h" |
17 #include "base/stl_util.h" | 17 #include "base/stl_util.h" |
18 #include "chrome/browser/history/url_database.h" | 18 #include "chrome/browser/history/url_database.h" |
19 #include "chrome/browser/history/visit_filter.h" | 19 #include "chrome/browser/history/visit_filter.h" |
20 #include "chrome/common/url_constants.h" | 20 #include "chrome/common/url_constants.h" |
21 #include "content/public/common/page_transition_types.h" | 21 #include "content/public/common/page_transition_types.h" |
22 #include "sql/statement.h" | 22 #include "sql/statement.h" |
23 | 23 |
24 // Rows, in order, of the visit table. | 24 // Rows, in order, of the visit table. |
25 #define HISTORY_VISIT_ROW_FIELDS \ | 25 #define HISTORY_VISIT_ROW_FIELDS \ |
26 " id,url,visit_time,from_visit,transition,segment_id,is_indexed " | 26 " id,url,visit_time,from_visit,transition,segment_id,is_indexed," \ |
| 27 "visit_duration " |
27 | 28 |
28 namespace history { | 29 namespace history { |
29 | 30 |
30 // Performs analysis of all local browsing data in the visits table to | 31 // Performs analysis of all local browsing data in the visits table to |
31 // assess the feasibility of performing prerendering on that data. | 32 // assess the feasibility of performing prerendering on that data. |
32 // Will emulate prerendering based on a simple heuristic: On each | 33 // Will emulate prerendering based on a simple heuristic: On each |
33 // pageview, pick most likely next page viewed in the next 5 minutes | 34 // pageview, pick most likely next page viewed in the next 5 minutes |
34 // based on historical data; keep a maximum of 5 prerenders; evict | 35 // based on historical data; keep a maximum of 5 prerenders; evict |
35 // prerenders older than 5 minutes or based on likelihood. Will report | 36 // prerenders older than 5 minutes or based on likelihood. Will report |
36 // back hypothetical prerender rate & accuracy via histograms. | 37 // back hypothetical prerender rate & accuracy via histograms. |
(...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 visit_analysis_->Init(); | 338 visit_analysis_->Init(); |
338 if (!GetDB().DoesTableExist("visits")) { | 339 if (!GetDB().DoesTableExist("visits")) { |
339 if (!GetDB().Execute("CREATE TABLE visits(" | 340 if (!GetDB().Execute("CREATE TABLE visits(" |
340 "id INTEGER PRIMARY KEY," | 341 "id INTEGER PRIMARY KEY," |
341 "url INTEGER NOT NULL," // key of the URL this corresponds to | 342 "url INTEGER NOT NULL," // key of the URL this corresponds to |
342 "visit_time INTEGER NOT NULL," | 343 "visit_time INTEGER NOT NULL," |
343 "from_visit INTEGER," | 344 "from_visit INTEGER," |
344 "transition INTEGER DEFAULT 0 NOT NULL," | 345 "transition INTEGER DEFAULT 0 NOT NULL," |
345 "segment_id INTEGER," | 346 "segment_id INTEGER," |
346 // True when we have indexed data for this visit. | 347 // True when we have indexed data for this visit. |
347 "is_indexed BOOLEAN)")) | 348 "is_indexed BOOLEAN," |
| 349 "visit_duration INTEGER DEFAULT 0 NOT NULL)")) |
348 return false; | 350 return false; |
349 } else if (!GetDB().DoesColumnExist("visits", "is_indexed")) { | 351 } else if (!GetDB().DoesColumnExist("visits", "is_indexed")) { |
350 // Old versions don't have the is_indexed column, we can just add that and | 352 // Old versions don't have the is_indexed column, we can just add that and |
351 // not worry about different database revisions, since old ones will | 353 // not worry about different database revisions, since old ones will |
352 // continue to work. | 354 // continue to work. |
353 // | 355 // |
354 // TODO(brettw) this should be removed once we think everybody has been | 356 // TODO(brettw) this should be removed once we think everybody has been |
355 // updated (added early Mar 2008). | 357 // updated (added early Mar 2008). |
356 if (!GetDB().Execute("ALTER TABLE visits ADD COLUMN is_indexed BOOLEAN")) | 358 if (!GetDB().Execute("ALTER TABLE visits ADD COLUMN is_indexed BOOLEAN")) |
357 return false; | 359 return false; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 // Must be in sync with HISTORY_VISIT_ROW_FIELDS. | 402 // Must be in sync with HISTORY_VISIT_ROW_FIELDS. |
401 // static | 403 // static |
402 void VisitDatabase::FillVisitRow(sql::Statement& statement, VisitRow* visit) { | 404 void VisitDatabase::FillVisitRow(sql::Statement& statement, VisitRow* visit) { |
403 visit->visit_id = statement.ColumnInt64(0); | 405 visit->visit_id = statement.ColumnInt64(0); |
404 visit->url_id = statement.ColumnInt64(1); | 406 visit->url_id = statement.ColumnInt64(1); |
405 visit->visit_time = base::Time::FromInternalValue(statement.ColumnInt64(2)); | 407 visit->visit_time = base::Time::FromInternalValue(statement.ColumnInt64(2)); |
406 visit->referring_visit = statement.ColumnInt64(3); | 408 visit->referring_visit = statement.ColumnInt64(3); |
407 visit->transition = content::PageTransitionFromInt(statement.ColumnInt(4)); | 409 visit->transition = content::PageTransitionFromInt(statement.ColumnInt(4)); |
408 visit->segment_id = statement.ColumnInt64(5); | 410 visit->segment_id = statement.ColumnInt64(5); |
409 visit->is_indexed = !!statement.ColumnInt(6); | 411 visit->is_indexed = !!statement.ColumnInt(6); |
| 412 visit->visit_duration = |
| 413 base::TimeDelta::FromInternalValue(statement.ColumnInt64(7)); |
410 } | 414 } |
411 | 415 |
412 // static | 416 // static |
413 bool VisitDatabase::FillVisitVector(sql::Statement& statement, | 417 bool VisitDatabase::FillVisitVector(sql::Statement& statement, |
414 VisitVector* visits) { | 418 VisitVector* visits) { |
415 if (!statement.is_valid()) | 419 if (!statement.is_valid()) |
416 return false; | 420 return false; |
417 | 421 |
418 while (statement.Step()) { | 422 while (statement.Step()) { |
419 history::VisitRow visit; | 423 history::VisitRow visit; |
420 FillVisitRow(statement, &visit); | 424 FillVisitRow(statement, &visit); |
421 visits->push_back(visit); | 425 visits->push_back(visit); |
422 } | 426 } |
423 | 427 |
424 return statement.Succeeded(); | 428 return statement.Succeeded(); |
425 } | 429 } |
426 | 430 |
427 VisitID VisitDatabase::AddVisit(VisitRow* visit, VisitSource source) { | 431 VisitID VisitDatabase::AddVisit(VisitRow* visit, VisitSource source) { |
428 visit_analysis_->AddVisit(visit); | 432 visit_analysis_->AddVisit(visit); |
429 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 433 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
430 "INSERT INTO visits " | 434 "INSERT INTO visits " |
431 "(url, visit_time, from_visit, transition, segment_id, is_indexed) " | 435 "(url, visit_time, from_visit, transition, segment_id, is_indexed, " |
432 "VALUES (?,?,?,?,?,?)")); | 436 "visit_duration) VALUES (?,?,?,?,?,?,?)")); |
433 statement.BindInt64(0, visit->url_id); | 437 statement.BindInt64(0, visit->url_id); |
434 statement.BindInt64(1, visit->visit_time.ToInternalValue()); | 438 statement.BindInt64(1, visit->visit_time.ToInternalValue()); |
435 statement.BindInt64(2, visit->referring_visit); | 439 statement.BindInt64(2, visit->referring_visit); |
436 statement.BindInt64(3, visit->transition); | 440 statement.BindInt64(3, visit->transition); |
437 statement.BindInt64(4, visit->segment_id); | 441 statement.BindInt64(4, visit->segment_id); |
438 statement.BindInt64(5, visit->is_indexed); | 442 statement.BindInt64(5, visit->is_indexed); |
| 443 statement.BindInt64(6, visit->visit_duration.ToInternalValue()); |
439 | 444 |
440 if (!statement.Run()) { | 445 if (!statement.Run()) { |
441 VLOG(0) << "Failed to execute visit insert statement: " | 446 VLOG(0) << "Failed to execute visit insert statement: " |
442 << "url_id = " << visit->url_id; | 447 << "url_id = " << visit->url_id; |
443 return 0; | 448 return 0; |
444 } | 449 } |
445 | 450 |
446 visit->visit_id = GetDB().GetLastInsertRowId(); | 451 visit->visit_id = GetDB().GetLastInsertRowId(); |
447 | 452 |
448 if (source != SOURCE_BROWSED) { | 453 if (source != SOURCE_BROWSED) { |
449 // Record the source of this visit when it is not browsed. | 454 // Record the source of this visit when it is not browsed. |
450 sql::Statement statement1(GetDB().GetCachedStatement(SQL_FROM_HERE, | 455 sql::Statement statement1(GetDB().GetCachedStatement(SQL_FROM_HERE, |
451 "INSERT INTO visit_source (id, source) VALUES (?,?)")); | 456 "INSERT INTO visit_source (id, source) VALUES (?,?)")); |
452 statement1.BindInt64(0, visit->visit_id); | 457 statement1.BindInt64(0, visit->visit_id); |
453 statement1.BindInt64(1, source); | 458 statement1.BindInt64(1, source); |
454 | 459 |
455 if (!statement1.Run()) { | 460 if (!statement1.Run()) { |
456 VLOG(0) << "Failed to execute visit_source insert statement: " | 461 VLOG(0) << "Failed to execute visit_source insert statement: " |
457 << "url_id = " << visit->visit_id; | 462 << "id = " << visit->visit_id; |
458 return 0; | 463 return 0; |
459 } | 464 } |
460 } | 465 } |
461 | 466 |
462 return visit->visit_id; | 467 return visit->visit_id; |
463 } | 468 } |
464 | 469 |
465 void VisitDatabase::DeleteVisit(const VisitRow& visit) { | 470 void VisitDatabase::DeleteVisit(const VisitRow& visit) { |
466 // Patch around this visit. Any visits that this went to will now have their | 471 // Patch around this visit. Any visits that this went to will now have their |
467 // "source" be the deleted visit's source. | 472 // "source" be the deleted visit's source. |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
507 } | 512 } |
508 | 513 |
509 bool VisitDatabase::UpdateVisitRow(const VisitRow& visit) { | 514 bool VisitDatabase::UpdateVisitRow(const VisitRow& visit) { |
510 // Don't store inconsistent data to the database. | 515 // Don't store inconsistent data to the database. |
511 DCHECK_NE(visit.visit_id, visit.referring_visit); | 516 DCHECK_NE(visit.visit_id, visit.referring_visit); |
512 if (visit.visit_id == visit.referring_visit) | 517 if (visit.visit_id == visit.referring_visit) |
513 return false; | 518 return false; |
514 | 519 |
515 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 520 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
516 "UPDATE visits SET " | 521 "UPDATE visits SET " |
517 "url=?,visit_time=?,from_visit=?,transition=?,segment_id=?,is_indexed=? " | 522 "url=?,visit_time=?,from_visit=?,transition=?,segment_id=?,is_indexed=?," |
518 "WHERE id=?")); | 523 "visit_duration=? WHERE id=?")); |
519 statement.BindInt64(0, visit.url_id); | 524 statement.BindInt64(0, visit.url_id); |
520 statement.BindInt64(1, visit.visit_time.ToInternalValue()); | 525 statement.BindInt64(1, visit.visit_time.ToInternalValue()); |
521 statement.BindInt64(2, visit.referring_visit); | 526 statement.BindInt64(2, visit.referring_visit); |
522 statement.BindInt64(3, visit.transition); | 527 statement.BindInt64(3, visit.transition); |
523 statement.BindInt64(4, visit.segment_id); | 528 statement.BindInt64(4, visit.segment_id); |
524 statement.BindInt64(5, visit.is_indexed); | 529 statement.BindInt64(5, visit.is_indexed); |
525 statement.BindInt64(6, visit.visit_id); | 530 statement.BindInt64(6, visit.visit_duration.ToInternalValue()); |
| 531 statement.BindInt64(7, visit.visit_id); |
526 | 532 |
527 return statement.Run(); | 533 return statement.Run(); |
528 } | 534 } |
529 | 535 |
530 bool VisitDatabase::GetVisitsForURL(URLID url_id, VisitVector* visits) { | 536 bool VisitDatabase::GetVisitsForURL(URLID url_id, VisitVector* visits) { |
531 visits->clear(); | 537 visits->clear(); |
532 | 538 |
533 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 539 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
534 "SELECT" HISTORY_VISIT_ROW_FIELDS | 540 "SELECT" HISTORY_VISIT_ROW_FIELDS |
535 "FROM visits " | 541 "FROM visits " |
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
835 | 841 |
836 // Get the source entries out of the query result. | 842 // Get the source entries out of the query result. |
837 while (statement.Step()) { | 843 while (statement.Step()) { |
838 std::pair<VisitID, VisitSource> source_entry(statement.ColumnInt64(0), | 844 std::pair<VisitID, VisitSource> source_entry(statement.ColumnInt64(0), |
839 static_cast<VisitSource>(statement.ColumnInt(1))); | 845 static_cast<VisitSource>(statement.ColumnInt(1))); |
840 sources->insert(source_entry); | 846 sources->insert(source_entry); |
841 } | 847 } |
842 } | 848 } |
843 } | 849 } |
844 | 850 |
| 851 bool VisitDatabase::MigrateVisitsWithoutDuration() { |
| 852 if (!GetDB().DoesTableExist("visits")) { |
| 853 NOTREACHED() << " Visits table should exist before migration"; |
| 854 return false; |
| 855 } |
| 856 |
| 857 if (!GetDB().DoesColumnExist("visits", "visit_duration")) { |
| 858 // Old versions don't have the visit_duration column, we modify the table |
| 859 // to add that field. |
| 860 if (!GetDB().Execute("ALTER TABLE visits " |
| 861 "ADD COLUMN visit_duration INTEGER DEFAULT 0 NOT NULL")) |
| 862 return false; |
| 863 } |
| 864 return true; |
| 865 } |
| 866 |
845 } // namespace history | 867 } // namespace history |
OLD | NEW |