1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/visit_database.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <limits>
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <map>
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <set>
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "app/sql/statement.h"
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h"
143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_number_conversions.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/history/url_database.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/url_constants.h"
17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/page_transition_types.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Rows, in order, of the visit table.
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#define HISTORY_VISIT_ROW_FIELDS \
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  " id,url,visit_time,from_visit,transition,segment_id,is_indexed "
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace history {
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochVisitDatabase::VisitDatabase() {
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochVisitDatabase::~VisitDatabase() {
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool VisitDatabase::InitVisitTable() {
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!GetDB().DoesTableExist("visits")) {
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!GetDB().Execute("CREATE TABLE visits("
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "id INTEGER PRIMARY KEY,"
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "url INTEGER NOT NULL," // key of the URL this corresponds to
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "visit_time INTEGER NOT NULL,"
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "from_visit INTEGER,"
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "transition INTEGER DEFAULT 0 NOT NULL,"
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "segment_id INTEGER,"
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // True when we have indexed data for this visit.
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "is_indexed BOOLEAN)"))
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (!GetDB().DoesColumnExist("visits", "is_indexed")) {
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Old versions don't have the is_indexed column, we can just add that and
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // not worry about different database revisions, since old ones will
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // continue to work.
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    //
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // TODO(brettw) this should be removed once we think everybody has been
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // updated (added early Mar 2008).
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!GetDB().Execute("ALTER TABLE visits ADD COLUMN is_indexed BOOLEAN"))
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Visit source table contains the source information for all the visits. To
553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // save space, we do not record those user browsed visits which would be the
563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // majority in this table. Only other sources are recorded.
573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Due to the tight relationship between visit_source and visits table, they
583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // should be created and dropped at the same time.
593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!GetDB().DoesTableExist("visit_source")) {
603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (!GetDB().Execute("CREATE TABLE visit_source("
613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                         "id INTEGER PRIMARY KEY,source INTEGER NOT NULL)"))
623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        return false;
633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Index over url so we can quickly find visits for a page. This will just
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // fail if it already exists and we'll ignore it.
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetDB().Execute("CREATE INDEX visits_url_index ON visits (url)");
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create an index over from visits so that we can efficiently find
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // referrers and redirects. Ignore failures because it likely already exists.
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetDB().Execute("CREATE INDEX visits_from_index ON visits (from_visit)");
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create an index over time so that we can efficiently find the visits in a
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // given time range (most history views are time-based). Ignore failures
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // because it likely already exists.
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetDB().Execute("CREATE INDEX visits_time_index ON visits (visit_time)");
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool VisitDatabase::DropVisitTable() {
823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  GetDB().Execute("DROP TABLE visit_source");
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This will also drop the indices over the table.
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return GetDB().Execute("DROP TABLE visits");
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Must be in sync with HISTORY_VISIT_ROW_FIELDS.
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid VisitDatabase::FillVisitRow(sql::Statement& statement, VisitRow* visit) {
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visit->visit_id = statement.ColumnInt64(0);
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visit->url_id = statement.ColumnInt64(1);
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visit->visit_time = base::Time::FromInternalValue(statement.ColumnInt64(2));
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visit->referring_visit = statement.ColumnInt64(3);
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visit->transition = PageTransition::FromInt(statement.ColumnInt(4));
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visit->segment_id = statement.ColumnInt64(5);
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visit->is_indexed = !!statement.ColumnInt(6);
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid VisitDatabase::FillVisitVector(sql::Statement& statement,
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    VisitVector* visits) {
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  while (statement.Step()) {
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    history::VisitRow visit;
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FillVisitRow(statement, &visit);
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    visits->push_back(visit);
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1093345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickVisitID VisitDatabase::AddVisit(VisitRow* visit, VisitSource source) {
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "INSERT INTO visits "
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "(url, visit_time, from_visit, transition, segment_id, is_indexed) "
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "VALUES (?,?,?,?,?,?)"));
11472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!statement) {
11572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    VLOG(0) << "Failed to build visit insert statement:  "
11672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen            << "url_id = " << visit->url_id;
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
11872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, visit->url_id);
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(1, visit->visit_time.ToInternalValue());
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(2, visit->referring_visit);
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(3, visit->transition);
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(4, visit->segment_id);
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(5, visit->is_indexed);
12672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
12772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!statement.Run()) {
12872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    VLOG(0) << "Failed to execute visit insert statement:  "
12972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen            << "url_id = " << visit->url_id;
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
13172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visit->visit_id = GetDB().GetLastInsertRowId();
1343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (source != SOURCE_BROWSED) {
1363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // Record the source of this visit when it is not browsed.
1373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    sql::Statement statement1(GetDB().GetCachedStatement(SQL_FROM_HERE,
1383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        "INSERT INTO visit_source (id, source) VALUES (?,?)"));
13972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (!statement1.is_valid()) {
14072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      VLOG(0) << "Failed to build visit_source insert statement:  "
14172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen              << "url_id = " << visit->visit_id;
1423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return 0;
14372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
1443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    statement1.BindInt64(0, visit->visit_id);
1463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    statement1.BindInt64(1, source);
14772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (!statement1.Run()) {
14872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      VLOG(0) << "Failed to execute visit_source insert statement:  "
14972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen              << "url_id = " << visit->visit_id;
1503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return 0;
15172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
1523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
1533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return visit->visit_id;
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid VisitDatabase::DeleteVisit(const VisitRow& visit) {
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Patch around this visit. Any visits that this went to will now have their
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // "source" be the deleted visit's source.
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement update_chain(GetDB().GetCachedStatement(SQL_FROM_HERE,
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "UPDATE visits SET from_visit=? WHERE from_visit=?"));
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!update_chain)
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  update_chain.BindInt64(0, visit.referring_visit);
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  update_chain.BindInt64(1, visit.visit_id);
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  update_chain.Run();
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Now delete the actual visit.
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement del(GetDB().GetCachedStatement(SQL_FROM_HERE,
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "DELETE FROM visits WHERE id=?"));
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!del)
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  del.BindInt64(0, visit.visit_id);
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  del.Run();
1753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Try to delete the entry in visit_source table as well.
1773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // If the visit was browsed, there is no corresponding entry in visit_source
1783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // table, and nothing will be deleted.
1793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  del.Assign(GetDB().GetCachedStatement(SQL_FROM_HERE,
1803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick             "DELETE FROM visit_source WHERE id=?"));
1813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!del.is_valid())
1823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return;
1833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  del.BindInt64(0, visit.visit_id);
1843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  del.Run();
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool VisitDatabase::GetRowForVisit(VisitID visit_id, VisitRow* out_visit) {
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits WHERE id=?"));
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, visit_id);
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement.Step())
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FillVisitRow(statement, out_visit);
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We got a different visit than we asked for, something is wrong.
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK_EQ(visit_id, out_visit->visit_id);
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (visit_id != out_visit->visit_id)
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool VisitDatabase::UpdateVisitRow(const VisitRow& visit) {
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Don't store inconsistent data to the database.
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK_NE(visit.visit_id, visit.referring_visit);
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (visit.visit_id == visit.referring_visit)
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "UPDATE visits SET "
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "url=?,visit_time=?,from_visit=?,transition=?,segment_id=?,is_indexed=? "
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "WHERE id=?"));
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, visit.url_id);
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(1, visit.visit_time.ToInternalValue());
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(2, visit.referring_visit);
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(3, visit.transition);
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(4, visit.segment_id);
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(5, visit.is_indexed);
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(6, visit.visit_id);
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return statement.Run();
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool VisitDatabase::GetVisitsForURL(URLID url_id, VisitVector* visits) {
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visits->clear();
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT" HISTORY_VISIT_ROW_FIELDS
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "FROM visits "
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "WHERE url=? "
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "ORDER BY visit_time ASC"));
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, url_id);
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FillVisitVector(statement, visits);
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid VisitDatabase::GetAllVisitsInRange(base::Time begin_time,
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        base::Time end_time,
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        int max_results,
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        VisitVector* visits) {
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visits->clear();
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits "
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "WHERE visit_time >= ? AND visit_time < ?"
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "ORDER BY visit_time LIMIT ?"));
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // See GetVisibleVisitsInRange for more info on how these times are bound.
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int64 end = end_time.ToInternalValue();
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, begin_time.ToInternalValue());
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(1, end ? end : std::numeric_limits<int64>::max());
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(2,
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      max_results ? max_results : std::numeric_limits<int64>::max());
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FillVisitVector(statement, visits);
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid VisitDatabase::GetVisitsInRangeForTransition(
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    base::Time begin_time,
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    base::Time end_time,
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int max_results,
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    PageTransition::Type transition,
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    VisitVector* visits) {
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(visits);
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visits->clear();
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits "
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "WHERE visit_time >= ? AND visit_time < ? "
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "AND (transition & ?) == ?"
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "ORDER BY visit_time LIMIT ?"));
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // See GetVisibleVisitsInRange for more info on how these times are bound.
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int64 end = end_time.ToInternalValue();
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, begin_time.ToInternalValue());
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(1, end ? end : std::numeric_limits<int64>::max());
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(2, PageTransition::CORE_MASK);
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(3, transition);
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(4,
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      max_results ? max_results : std::numeric_limits<int64>::max());
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FillVisitVector(statement, visits);
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid VisitDatabase::GetVisibleVisitsInRange(base::Time begin_time,
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            base::Time end_time,
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            int max_count,
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            VisitVector* visits) {
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visits->clear();
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The visit_time values can be duplicated in a redirect chain, so we sort
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // by id too, to ensure a consistent ordering just in case.
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits "
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "WHERE visit_time >= ? AND visit_time < ? "
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "AND (transition & ?) != 0 "  // CHAIN_END
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "AND (transition & ?) NOT IN (?, ?, ?) "  // NO SUBFRAME or
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                // KEYWORD_GENERATED
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "ORDER BY visit_time DESC, id DESC"));
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Note that we use min/max values for querying unlimited ranges of time using
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the same statement. Since the time has an index, this will be about the
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // same amount of work as just doing a query for everything with no qualifier.
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int64 end = end_time.ToInternalValue();
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, begin_time.ToInternalValue());
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(1, end ? end : std::numeric_limits<int64>::max());
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(2, PageTransition::CHAIN_END);
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(3, PageTransition::CORE_MASK);
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(4, PageTransition::AUTO_SUBFRAME);
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(5, PageTransition::MANUAL_SUBFRAME);
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(6, PageTransition::KEYWORD_GENERATED);
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::set<URLID> found_urls;
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  while (statement.Step()) {
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    VisitRow visit;
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FillVisitRow(statement, &visit);
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Make sure the URL this visit corresponds to is unique.
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (found_urls.find(visit.url_id) != found_urls.end())
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    found_urls.insert(visit.url_id);
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    visits->push_back(visit);
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (max_count > 0 && static_cast<int>(visits->size()) >= max_count)
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochVisitID VisitDatabase::GetMostRecentVisitForURL(URLID url_id,
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                VisitRow* visit_row) {
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The visit_time values can be duplicated in a redirect chain, so we sort
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // by id too, to ensure a consistent ordering just in case.
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT" HISTORY_VISIT_ROW_FIELDS "FROM visits "
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "WHERE url=? "
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "ORDER BY visit_time DESC, id DESC "
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "LIMIT 1"));
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, url_id);
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement.Step())
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;  // No visits for this URL.
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (visit_row) {
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FillVisitRow(statement, visit_row);
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return visit_row->visit_id;
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return statement.ColumnInt64(0);
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool VisitDatabase::GetMostRecentVisitsForURL(URLID url_id,
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                              int max_results,
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                              VisitVector* visits) {
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  visits->clear();
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The visit_time values can be duplicated in a redirect chain, so we sort
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // by id too, to ensure a consistent ordering just in case.
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT" HISTORY_VISIT_ROW_FIELDS
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "FROM visits "
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "WHERE url=? "
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "ORDER BY visit_time DESC, id DESC "
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "LIMIT ?"));
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, url_id);
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(1, max_results);
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FillVisitVector(statement, visits);
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool VisitDatabase::GetRedirectFromVisit(VisitID from_visit,
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         VisitID* to_visit,
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         GURL* to_url) {
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT v.id,u.url "
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "FROM visits v JOIN urls u ON v.url = u.id "
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "WHERE v.from_visit = ? "
394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "AND (v.transition & ?) != 0"));  // IS_REDIRECT_MASK
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt64(0, from_visit);
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindInt(1, PageTransition::IS_REDIRECT_MASK);
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement.Step())
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;  // No redirect from this visit.
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (to_visit)
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *to_visit = statement.ColumnInt64(0);
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (to_url)
406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *to_url = GURL(statement.ColumnString(1));
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool VisitDatabase::GetRedirectToVisit(VisitID to_visit,
411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       VisitID* from_visit,
412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                       GURL* from_url) {
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  VisitRow row;
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!GetRowForVisit(to_visit, &row))
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (from_visit)
418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *from_visit = row.referring_visit;
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (from_url) {
421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "SELECT u.url "
423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "FROM visits v JOIN urls u ON v.url = u.id "
424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        "WHERE v.id = ?"));
425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    statement.BindInt64(0, row.referring_visit);
426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!statement.Step())
428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *from_url = GURL(statement.ColumnString(0));
431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool VisitDatabase::GetVisitCountToHost(const GURL& url,
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        int* count,
437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        base::Time* first_visit) {
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!url.SchemeIs(chrome::kHttpScheme) && !url.SchemeIs(chrome::kHttpsScheme))
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We need to search for URLs with a matching host/port. One way to query for
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // this is to use the LIKE operator, eg 'url LIKE http://google.com/%'. This
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // is inefficient though in that it doesn't use the index and each entry must
444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // be visited. The same query can be executed by using >= and < operator.
445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The query becomes:
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // 'url >= http://google.com/' and url < http://google.com0'.
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // 0 is used as it is one character greater than '/'.
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GURL search_url(url);
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const std::string host_query_min = search_url.GetOrigin().spec();
450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (host_query_min.empty())
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string host_query_max = host_query_min;
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  host_query_max[host_query_max.size() - 1] = '0';
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT MIN(v.visit_time), COUNT(*) "
459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "FROM visits v INNER JOIN urls u ON v.url = u.id "
460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "WHERE (u.url >= ? AND u.url < ?)"));
461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement)
462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindString(0, host_query_min);
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  statement.BindString(1, host_query_max);
466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement.Step()) {
468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We've never been to this page before.
469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *count = 0;
470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *first_visit = base::Time::FromInternalValue(statement.ColumnInt64(0));
474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *count = statement.ColumnInt(1);
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool VisitDatabase::GetStartDate(base::Time* first_visit) {
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      "SELECT MIN(visit_time) FROM visits WHERE visit_time != 0"));
481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!statement || !statement.Step() || statement.ColumnInt64(0) == 0) {
482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *first_visit = base::Time::Now();
483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *first_visit = base::Time::FromInternalValue(statement.ColumnInt64(0));
486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
4893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid VisitDatabase::GetVisitsSource(const VisitVector& visits,
4903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    VisitSourceMap* sources) {
4913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(sources);
4923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  sources->clear();
4933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // We query the source in batch. Here defines the batch size.
4953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const size_t batch_size = 500;
4963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  size_t visits_size = visits.size();
4973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  size_t start_index = 0, end_index = 0;
4993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  while (end_index < visits_size) {
5003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    start_index = end_index;
5013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    end_index = end_index + batch_size < visits_size ? end_index + batch_size
5023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                                     : visits_size;
5033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
5043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // Compose the sql statement with a list of ids.
5053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    std::string sql = "SELECT id,source FROM visit_source ";
5063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    sql.append("WHERE id IN (");
5073345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // Append all the ids in the statement.
5083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    for (size_t j = start_index; j < end_index; j++) {
5093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      if (j != start_index)
5103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        sql.push_back(',');
5113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      sql.append(base::Int64ToString(visits[j].visit_id));
5123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
5133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    sql.append(") ORDER BY id");
5143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    sql::Statement statement(GetDB().GetUniqueStatement(sql.c_str()));
5153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (!statement)
5163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return;
5173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
5183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // Get the source entries out of the query result.
5193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    while (statement.Step()) {
5203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      std::pair<VisitID, VisitSource> source_entry(statement.ColumnInt64(0),
5213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          static_cast<VisitSource>(statement.ColumnInt(1)));
5223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      sources->insert(source_entry);
5233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
5243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
5253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
5263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace history
528