1ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// Copyright 2013 The Chromium Authors. All rights reserved.
2ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
3ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch// found in the LICENSE file.
4ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
5ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "chrome/browser/extensions/activity_log/database_string_table.h"
6ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
7ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "base/strings/stringprintf.h"
8ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "sql/connection.h"
9ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch#include "sql/statement.h"
10ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
11ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochusing base::StringPrintf;
12ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
13ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochnamespace extensions {
14ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
15424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// A target maximum size (in number of entries) for the mapping tables.  If the
16424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)// cache would grow larger than this, the size should be reduced.
17424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)static const size_t kMaximumCacheSize = 1000;
18424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
19ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben MurdochDatabaseStringTable::DatabaseStringTable(const std::string& table)
20ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    : table_(table) {}
21ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
22ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben MurdochDatabaseStringTable::~DatabaseStringTable() {}
23ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
24ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochbool DatabaseStringTable::Initialize(sql::Connection* connection) {
25ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!connection->DoesTableExist(table_.c_str())) {
26ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return connection->Execute(StringPrintf(
27ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        "CREATE TABLE %s (id INTEGER PRIMARY KEY, value TEXT NOT NULL); "
28ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        "CREATE UNIQUE INDEX %s_index ON %s(value)",
29ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        table_.c_str(),
30ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        table_.c_str(),
31ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        table_.c_str()).c_str());
32ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  } else {
33ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return true;
34ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
35ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
36ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
37ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochbool DatabaseStringTable::StringToInt(sql::Connection* connection,
38ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                      const std::string& value,
39ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                      int64* id) {
40ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  std::map<std::string, int64>::const_iterator lookup =
41ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      value_to_id_.find(value);
42ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (lookup != value_to_id_.end()) {
43ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    *id = lookup->second;
44ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return true;
45ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
46ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
47424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // We will be adding data to the cache below--check the cache size now and
48424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // reduce it if needed.
49424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  PruneCache();
50424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
51ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // Operate on the assumption that the cache does a good job on
52ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // frequently-used strings--if there is a cache miss, first act on the
53ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // assumption that the string is not in the database either.
54ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  sql::Statement update(connection->GetUniqueStatement(
55ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      StringPrintf("INSERT OR IGNORE INTO %s(value) VALUES (?)", table_.c_str())
56ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          .c_str()));
57ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  update.BindString(0, value);
58ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!update.Run())
59ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return false;
60ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
61ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (connection->GetLastChangeCount() == 1) {
62ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    *id = connection->GetLastInsertRowId();
63ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    id_to_value_[*id] = value;
64ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    value_to_id_[value] = *id;
65ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return true;
66ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
67ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
68ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // The specified string may have already existed in the database, in which
69ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // case the insert above will have been ignored.  If this happens, do a
70ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  // lookup to find the old value.
71ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  sql::Statement query(connection->GetUniqueStatement(
72ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      StringPrintf("SELECT id FROM %s WHERE value = ?", table_.c_str())
73ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          .c_str()));
74ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  query.BindString(0, value);
75ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!query.Step())
76ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return false;
77ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  *id = query.ColumnInt64(0);
78ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  id_to_value_[*id] = value;
79ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  value_to_id_[value] = *id;
80ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  return true;
81ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
82ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
83ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochbool DatabaseStringTable::IntToString(sql::Connection* connection,
84ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                      int64 id,
85ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch                                      std::string* value) {
86ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  std::map<int64, std::string>::const_iterator lookup =
87ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      id_to_value_.find(id);
88ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (lookup != id_to_value_.end()) {
89ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    *value = lookup->second;
90ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return true;
91ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
92ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
93424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // We will be adding data to the cache below--check the cache size now and
94424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // reduce it if needed.
95424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  PruneCache();
96424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
97ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  sql::Statement query(connection->GetUniqueStatement(
98ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      StringPrintf("SELECT value FROM %s WHERE id = ?", table_.c_str())
99ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          .c_str()));
100ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  query.BindInt64(0, id);
101ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (!query.Step())
102ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    return false;
103ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
104ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  *value = query.ColumnString(0);
105ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  id_to_value_[id] = *value;
106ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  value_to_id_[*value] = id;
107ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  return true;
108ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
109ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
110ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid DatabaseStringTable::ClearCache() {
111ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  id_to_value_.clear();
112ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  value_to_id_.clear();
113ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
114ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
115424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)void DatabaseStringTable::PruneCache() {
116424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  if (id_to_value_.size() <= kMaximumCacheSize &&
117424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)      value_to_id_.size() <= kMaximumCacheSize)
118424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)    return;
119424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
120424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // TODO(mvrable): Perhaps implement a more intelligent caching policy.  For
121424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // now, to limit memory usage we simply clear the entire cache when it would
122424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // become too large.  Data will be brought back in from the database as
123424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  // needed.
124424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)  ClearCache();
125424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}
126424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)
127ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}  // namespace extensions
128