database_string_table.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
1// Copyright 2013 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/extensions/activity_log/database_string_table.h"
6
7#include "base/strings/stringprintf.h"
8#include "sql/connection.h"
9#include "sql/statement.h"
10
11using base::StringPrintf;
12
13namespace extensions {
14
15// A target maximum size (in number of entries) for the mapping tables.  If the
16// cache would grow larger than this, the size should be reduced.
17static const size_t kMaximumCacheSize = 1000;
18
19DatabaseStringTable::DatabaseStringTable(const std::string& table)
20    : table_(table) {}
21
22DatabaseStringTable::~DatabaseStringTable() {}
23
24bool DatabaseStringTable::Initialize(sql::Connection* connection) {
25  if (!connection->DoesTableExist(table_.c_str())) {
26    return connection->Execute(StringPrintf(
27        "CREATE TABLE %s (id INTEGER PRIMARY KEY, value TEXT NOT NULL); "
28        "CREATE UNIQUE INDEX %s_index ON %s(value)",
29        table_.c_str(),
30        table_.c_str(),
31        table_.c_str()).c_str());
32  } else {
33    return true;
34  }
35}
36
37bool DatabaseStringTable::StringToInt(sql::Connection* connection,
38                                      const std::string& value,
39                                      int64* id) {
40  std::map<std::string, int64>::const_iterator lookup =
41      value_to_id_.find(value);
42  if (lookup != value_to_id_.end()) {
43    *id = lookup->second;
44    return true;
45  }
46
47  // We will be adding data to the cache below--check the cache size now and
48  // reduce it if needed.
49  PruneCache();
50
51  // Operate on the assumption that the cache does a good job on
52  // frequently-used strings--if there is a cache miss, first act on the
53  // assumption that the string is not in the database either.
54  sql::Statement update(connection->GetUniqueStatement(
55      StringPrintf("INSERT OR IGNORE INTO %s(value) VALUES (?)", table_.c_str())
56          .c_str()));
57  update.BindString(0, value);
58  if (!update.Run())
59    return false;
60
61  if (connection->GetLastChangeCount() == 1) {
62    *id = connection->GetLastInsertRowId();
63    id_to_value_[*id] = value;
64    value_to_id_[value] = *id;
65    return true;
66  }
67
68  // The specified string may have already existed in the database, in which
69  // case the insert above will have been ignored.  If this happens, do a
70  // lookup to find the old value.
71  sql::Statement query(connection->GetUniqueStatement(
72      StringPrintf("SELECT id FROM %s WHERE value = ?", table_.c_str())
73          .c_str()));
74  query.BindString(0, value);
75  if (!query.Step())
76    return false;
77  *id = query.ColumnInt64(0);
78  id_to_value_[*id] = value;
79  value_to_id_[value] = *id;
80  return true;
81}
82
83bool DatabaseStringTable::IntToString(sql::Connection* connection,
84                                      int64 id,
85                                      std::string* value) {
86  std::map<int64, std::string>::const_iterator lookup =
87      id_to_value_.find(id);
88  if (lookup != id_to_value_.end()) {
89    *value = lookup->second;
90    return true;
91  }
92
93  // We will be adding data to the cache below--check the cache size now and
94  // reduce it if needed.
95  PruneCache();
96
97  sql::Statement query(connection->GetUniqueStatement(
98      StringPrintf("SELECT value FROM %s WHERE id = ?", table_.c_str())
99          .c_str()));
100  query.BindInt64(0, id);
101  if (!query.Step())
102    return false;
103
104  *value = query.ColumnString(0);
105  id_to_value_[id] = *value;
106  value_to_id_[*value] = id;
107  return true;
108}
109
110void DatabaseStringTable::ClearCache() {
111  id_to_value_.clear();
112  value_to_id_.clear();
113}
114
115void DatabaseStringTable::PruneCache() {
116  if (id_to_value_.size() <= kMaximumCacheSize &&
117      value_to_id_.size() <= kMaximumCacheSize)
118    return;
119
120  // TODO(mvrable): Perhaps implement a more intelligent caching policy.  For
121  // now, to limit memory usage we simply clear the entire cache when it would
122  // become too large.  Data will be brought back in from the database as
123  // needed.
124  ClearCache();
125}
126
127}  // namespace extensions
128