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
3// found in the LICENSE file.
4
5#include "chrome/browser/predictors/autocomplete_action_predictor_table.h"
6
7#include "base/guid.h"
8#include "base/logging.h"
9#include "base/metrics/histogram.h"
10#include "base/strings/stringprintf.h"
11#include "base/strings/utf_string_conversions.h"
12#include "content/public/browser/browser_thread.h"
13#include "sql/statement.h"
14
15namespace {
16
17// TODO(shishir): Rename the table for consistency.
18const char kAutocompletePredictorTableName[] = "network_action_predictor";
19
20// The maximum length allowed for strings in the database.
21const size_t kMaxDataLength = 2048;
22
23void BindRowToStatement(
24    const predictors::AutocompleteActionPredictorTable::Row& row,
25    sql::Statement* statement) {
26  DCHECK(base::IsValidGUID(row.id));
27  statement->BindString(0, row.id);
28  statement->BindString16(1, row.user_text.substr(0, kMaxDataLength));
29  statement->BindString(2, row.url.spec().substr(0, kMaxDataLength));
30  statement->BindInt(3, row.number_of_hits);
31  statement->BindInt(4, row.number_of_misses);
32}
33
34bool StepAndInitializeRow(
35    sql::Statement* statement,
36    predictors::AutocompleteActionPredictorTable::Row* row) {
37  if (!statement->Step())
38    return false;
39
40  row->id = statement->ColumnString(0);
41  row->user_text = statement->ColumnString16(1);
42  row->url = GURL(statement->ColumnString(2));
43  row->number_of_hits = statement->ColumnInt(3);
44  row->number_of_misses = statement->ColumnInt(4);
45  return true;
46}
47
48}  // namespace
49
50namespace predictors {
51
52AutocompleteActionPredictorTable::Row::Row()
53    : number_of_hits(0),
54      number_of_misses(0) {
55}
56
57AutocompleteActionPredictorTable::Row::Row(const Row::Id& id,
58                                           const base::string16& user_text,
59                                           const GURL& url,
60                                           int number_of_hits,
61                                           int number_of_misses)
62    : id(id),
63      user_text(user_text),
64      url(url),
65      number_of_hits(number_of_hits),
66      number_of_misses(number_of_misses) {
67}
68
69AutocompleteActionPredictorTable::Row::Row(const Row& row)
70    : id(row.id),
71      user_text(row.user_text),
72      url(row.url),
73      number_of_hits(row.number_of_hits),
74      number_of_misses(row.number_of_misses) {
75}
76
77
78void AutocompleteActionPredictorTable::GetRow(const Row::Id& id, Row* row) {
79  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB));
80  if (CantAccessDatabase())
81    return;
82
83  sql::Statement statement(DB()->GetCachedStatement(SQL_FROM_HERE,
84      base::StringPrintf("SELECT * FROM %s WHERE id=?",
85                         kAutocompletePredictorTableName).c_str()));
86  statement.BindString(0, id);
87
88  bool success = StepAndInitializeRow(&statement, row);
89  DCHECK(success) << "Failed to get row " << id << " from "
90                  << kAutocompletePredictorTableName;
91}
92
93void AutocompleteActionPredictorTable::GetAllRows(Rows* row_buffer) {
94  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB));
95  if (CantAccessDatabase())
96    return;
97
98  row_buffer->clear();
99
100  sql::Statement statement(DB()->GetCachedStatement(SQL_FROM_HERE,
101      base::StringPrintf(
102          "SELECT * FROM %s", kAutocompletePredictorTableName).c_str()));
103  if (!statement.is_valid())
104    return;
105
106  Row row;
107  while (StepAndInitializeRow(&statement, &row))
108    row_buffer->push_back(row);
109}
110
111void AutocompleteActionPredictorTable::AddRow(
112    const AutocompleteActionPredictorTable::Row& row) {
113  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB));
114  if (CantAccessDatabase())
115    return;
116
117  AddAndUpdateRows(Rows(1, row), Rows());
118}
119
120void AutocompleteActionPredictorTable::UpdateRow(
121    const AutocompleteActionPredictorTable::Row& row) {
122  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB));
123  if (CantAccessDatabase())
124    return;
125
126  AddAndUpdateRows(Rows(), Rows(1, row));
127}
128
129void AutocompleteActionPredictorTable::AddAndUpdateRows(
130    const Rows& rows_to_add,
131    const Rows& rows_to_update) {
132  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB));
133  if (CantAccessDatabase())
134    return;
135
136  if (!DB()->BeginTransaction())
137    return;
138  for (Rows::const_iterator it = rows_to_add.begin();
139       it != rows_to_add.end(); ++it) {
140    sql::Statement statement(DB()->GetCachedStatement(SQL_FROM_HERE,
141        base::StringPrintf(
142            "INSERT INTO %s "
143            "(id, user_text, url, number_of_hits, number_of_misses) "
144            "VALUES (?,?,?,?,?)", kAutocompletePredictorTableName).c_str()));
145    if (!statement.is_valid()) {
146      DB()->RollbackTransaction();
147      return;
148    }
149
150    BindRowToStatement(*it, &statement);
151    if (!statement.Run()) {
152      DB()->RollbackTransaction();
153      return;
154    }
155  }
156  for (Rows::const_iterator it = rows_to_update.begin();
157       it != rows_to_update.end(); ++it) {
158    sql::Statement statement(DB()->GetCachedStatement(SQL_FROM_HERE,
159        base::StringPrintf(
160            "UPDATE %s "
161            "SET id=?, user_text=?, url=?, number_of_hits=?, number_of_misses=?"
162            " WHERE id=?1", kAutocompletePredictorTableName).c_str()));
163    if (!statement.is_valid()) {
164      DB()->RollbackTransaction();
165      return;
166    }
167
168    BindRowToStatement(*it, &statement);
169    if (!statement.Run()) {
170      DB()->RollbackTransaction();
171      return;
172    }
173    DCHECK_GT(DB()->GetLastChangeCount(), 0);
174  }
175  DB()->CommitTransaction();
176}
177
178void AutocompleteActionPredictorTable::DeleteRows(
179    const std::vector<Row::Id>& id_list) {
180  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB));
181  if (CantAccessDatabase())
182    return;
183
184  if (!DB()->BeginTransaction())
185    return;
186  for (std::vector<Row::Id>::const_iterator it = id_list.begin();
187       it != id_list.end(); ++it) {
188    sql::Statement statement(DB()->GetCachedStatement(SQL_FROM_HERE,
189        base::StringPrintf(
190            "DELETE FROM %s WHERE id=?",
191            kAutocompletePredictorTableName).c_str()));
192    if (!statement.is_valid()) {
193      DB()->RollbackTransaction();
194      return;
195    }
196
197    statement.BindString(0, *it);
198    if (!statement.Run()) {
199      DB()->RollbackTransaction();
200      return;
201    }
202  }
203  DB()->CommitTransaction();
204}
205
206void AutocompleteActionPredictorTable::DeleteAllRows() {
207  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB));
208  if (CantAccessDatabase())
209    return;
210
211  sql::Statement statement(DB()->GetCachedStatement(SQL_FROM_HERE,
212      base::StringPrintf("DELETE FROM %s",
213                         kAutocompletePredictorTableName).c_str()));
214  if (!statement.is_valid())
215    return;
216
217  statement.Run();
218}
219
220AutocompleteActionPredictorTable::AutocompleteActionPredictorTable()
221    : PredictorTableBase() {
222}
223
224AutocompleteActionPredictorTable::~AutocompleteActionPredictorTable() {
225}
226
227void AutocompleteActionPredictorTable::CreateTableIfNonExistent() {
228  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB));
229  if (CantAccessDatabase())
230    return;
231
232  if (DB()->DoesTableExist(kAutocompletePredictorTableName))
233    return;
234
235  bool success = DB()->Execute(base::StringPrintf(
236      "CREATE TABLE %s ( "
237      "id TEXT PRIMARY KEY, "
238      "user_text TEXT, "
239      "url TEXT, "
240      "number_of_hits INTEGER, "
241      "number_of_misses INTEGER)", kAutocompletePredictorTableName).c_str());
242  if (!success)
243    ResetDB();
244}
245
246void AutocompleteActionPredictorTable::LogDatabaseStats()  {
247  CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB));
248  if (CantAccessDatabase())
249    return;
250
251  sql::Statement count_statement(DB()->GetUniqueStatement(
252      base::StringPrintf("SELECT count(id) FROM %s",
253                         kAutocompletePredictorTableName).c_str()));
254  if (!count_statement.is_valid() || !count_statement.Step())
255    return;
256  UMA_HISTOGRAM_COUNTS("AutocompleteActionPredictor.DatabaseRowCount",
257                       count_statement.ColumnInt(0));
258}
259
260}  // namespace predictors
261