Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: components/offline_pages/offline_page_metadata_store_sql.cc

Issue 1834563002: initial add of SQL based storage (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address comments. Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "components/offline_pages/offline_page_metadata_store_sql.h"
6
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/location.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "components/offline_pages/offline_page_item.h"
15 #include "sql/connection.h"
16 #include "sql/error_delegate_util.h"
17 #include "sql/meta_table.h"
18 #include "sql/statement.h"
19 #include "sql/transaction.h"
20
21 namespace offline_pages {
22
23 namespace {
24
25 const int kCurrentVersion = 1;
26 const int kCompatibleVersion = 1;
27
28 // this is a #define instead of a const so that
fgorski 2016/03/29 06:50:18 nit: sentence case.
bburns 2016/03/30 16:54:40 Done.
29 // I can use it inline in other SQL statements below
fgorski 2016/03/29 06:50:19 so it can be used.
bburns 2016/03/30 16:54:40 Done.
30 #define kOfflinePagesTableName "offlinepages"
31 const char kOfflinePagesColumns[] =
32 "(offline_id INTEGER PRIMARY_KEY,"
33 " client_namespace VARCHAR(256),"
34 " client_id VARCHAR(256),"
35 " online_url VARCHAR(2048),"
36 " offline_url VARCHAR(2048),"
37 " version INTEGER,"
38 " creation_time INTEGER,"
39 " file_path VARCHAR(1024),"
40 " file_size INTEGER,"
41 " last_access_time INTEGER,"
42 " access_count INTEGER,"
43 " status INTEGER,"
44 " user_initiated BOOLEAN)";
45
46 // This is cloned from //content/browser/appcache/appcache_database.cc
47 struct TableInfo {
48 const char* table_name;
49 const char* columns;
50 };
51
52 const TableInfo kOfflinePagesTable{kOfflinePagesTableName,
53 kOfflinePagesColumns};
54
55 enum : int {
56 OP_OFFLINE_ID = 0,
57 OP_CLIENT_NAMESPACE,
58 OP_CLIENT_ID,
59 OP_ONLINE_URL,
60 OP_OFFLINE_URL,
61 OP_VERSION,
62 OP_CREATION_TIME,
63 OP_FILE_PATH,
64 OP_FILE_SIZE,
65 OP_LAST_ACCESS_TIME,
66 OP_ACCESS_COUNT,
67 OP_STATUS,
68 OP_USER_INITIATED
69 };
70
71 bool CreateTable(sql::Connection* db,
72 const char* table_name,
73 const char* columns) {
74 std::string sql("CREATE TABLE ");
75 sql += table_name;
76 sql += columns;
77 return db->Execute(sql.c_str());
78 }
79
80 bool CreateSchema(sql::MetaTable* meta_table, sql::Connection* db) {
81 // If you create a transaction but don't Commit() it is automatically
82 // rolled back by its destructor when it falls out of scope.
83 sql::Transaction transaction(db);
84 if (!transaction.Begin())
85 return false;
86
87 if (!meta_table->Init(db, kCurrentVersion, kCompatibleVersion))
88 return false;
89
90 if (!CreateTable(db, kOfflinePagesTableName, kOfflinePagesColumns))
91 return false;
92
93 // TODO(bburns): indexes here
94 return transaction.Commit();
95 }
96
97 bool DropTable(sql::Connection* db, const char* table_name) {
98 std::string sql("DROP TABLE ");
99 sql += table_name;
100 return db->Execute(sql.c_str());
101 }
102
103 bool DeleteByOfflineId(sql::Connection* db, int64_t offline_id) {
104 const char sql[] =
105 "DELETE FROM " kOfflinePagesTableName " WHERE offline_id=?";
106 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, sql));
107 statement.BindInt64(0, offline_id);
108 return db->Execute(sql);
109 }
110
111 // Create an offline page item from a SQL result. Expects complete rows with
112 // all columns present.
113 OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) {
114 int64_t id = statement->ColumnInt64(OP_OFFLINE_ID);
115 GURL url(statement->ColumnString(OP_ONLINE_URL));
116 ClientId client_id(statement->ColumnString(OP_CLIENT_NAMESPACE),
117 statement->ColumnString(OP_CLIENT_ID));
118 base::FilePath path(statement->ColumnString(OP_FILE_PATH));
fgorski 2016/03/29 06:50:19 From the level db store impl: #if defined(OS_POSIX
bburns 2016/03/30 16:54:39 Is this really required? It seems like something
fgorski 2016/03/30 17:19:31 It's actually file path that is weird. class BASE
119 int64_t file_size = statement->ColumnInt64(OP_FILE_SIZE);
120 base::Time creation_time =
121 base::Time::FromInternalValue(statement->ColumnInt64(OP_CREATION_TIME));
122
123 OfflinePageItem item(url, id, client_id, path, file_size, creation_time);
124 item.last_access_time = base::Time::FromInternalValue(
125 statement->ColumnInt64(OP_LAST_ACCESS_TIME));
126 item.version = statement->ColumnInt64(OP_VERSION);
127 return item;
128 }
129
130 bool InsertOrReplace(sql::Connection* db, const OfflinePageItem& item) {
131 const char sql[] =
fgorski 2016/03/29 06:50:19 since it is a const, kSql
bburns 2016/03/30 16:54:40 Done.
132 "INSERT OR REPLACE INTO " kOfflinePagesTableName
133 " (offline_id, online_url, client_namespace, client_id, file_path, "
134 "file_size, creation_time, last_access_time, version)"
135 " VALUES "
136 " (?, ?, ?, ?, ?, ?, ?, ?, ?)";
137
138 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, sql));
139 statement.BindInt64(0, item.offline_id);
140 statement.BindString(1, item.url.spec());
141 statement.BindString(2, item.client_id.name_space);
142 statement.BindString(3, item.client_id.id);
143 statement.BindString(4, item.file_path.value());
144 statement.BindInt64(5, item.file_size);
145 statement.BindInt64(6, item.creation_time.ToInternalValue());
146 statement.BindInt64(7, item.last_access_time.ToInternalValue());
147 statement.BindInt64(8, item.version);
148 return statement.Run();
149 }
150 } // anonymous namespace
151
152 OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL(
153 scoped_refptr<base::SequencedTaskRunner> background_task_runner,
154 const base::FilePath& path,
155 bool in_memory)
156 : background_task_runner_(background_task_runner),
157 db_file_path_(path),
158 use_in_memory_(in_memory),
159 weak_ptr_factory_(this) {}
160
161 OfflinePageMetadataStoreSQL::~OfflinePageMetadataStoreSQL() {}
162
163 void OfflinePageMetadataStoreSQL::LoadSync(
164 scoped_refptr<base::SingleThreadTaskRunner> runner,
165 const LoadCallback& callback) {
166 bool opened = false;
167 db_.reset(new sql::Connection);
168 meta_table_.reset(new sql::MetaTable);
169
170 if (use_in_memory_) {
171 opened = db_->OpenInMemory();
172 } else if (!base::CreateDirectory(db_file_path_.DirName())) {
fgorski 2016/03/29 06:50:18 since you are logging here, probably makes sense t
bburns 2016/03/30 16:54:40 Done.
173 LOG(ERROR) << "Failed to create offline pages db directory.";
174 } else {
175 opened = db_->Open(db_file_path_);
176 if (opened)
177 db_->Preload();
178 }
179 if (!opened) {
180 NotifyLoadResult(callback, runner, STORE_INIT_FAILED,
181 std::vector<OfflinePageItem>());
182 }
183
184 if (!sql::MetaTable::DoesTableExist(db_.get())) {
185 if (!CreateSchema(meta_table_.get(), db_.get())) {
186 NotifyLoadResult(callback, runner, STORE_INIT_FAILED,
187 std::vector<OfflinePageItem>());
fgorski 2016/03/29 06:50:19 do you intend to stop execution here? if so return
bburns 2016/03/30 16:54:40 Done.
188 }
189 }
190
191 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
192 NotifyLoadResult(callback, runner, STORE_INIT_FAILED,
fgorski 2016/03/29 06:50:19 this is a single line, but a multiline body, surro
bburns 2016/03/30 16:54:40 Done.
193 std::vector<OfflinePageItem>());
194
195 if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
196 LOG(WARNING) << "Offline database is too new.";
197 NotifyLoadResult(callback, runner, STORE_INIT_FAILED,
198 std::vector<OfflinePageItem>());
fgorski 2016/03/29 06:50:19 return missing
bburns 2016/03/30 16:54:40 Done.
199 }
200
201 const char sql[] = "SELECT * FROM " kOfflinePagesTableName;
202
203 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, sql));
204
205 std::vector<OfflinePageItem> result;
206 while (statement.Step()) {
207 result.push_back(MakeOfflinePageItem(&statement));
208 }
209
210 if (!statement.Succeeded()) {
211 NotifyLoadResult(callback, runner, STORE_LOAD_FAILED,
212 std::vector<OfflinePageItem>());
213 } else {
214 NotifyLoadResult(callback, runner, LOAD_SUCCEEDED, result);
215 }
216 }
217
218 void OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePageSync(
219 const OfflinePageItem& offline_page,
220 scoped_refptr<base::SingleThreadTaskRunner> runner,
221 const UpdateCallback& callback) {
222 bool ok = InsertOrReplace(db_.get(), offline_page);
223 runner->PostTask(FROM_HERE, base::Bind(callback, ok));
224 }
225
226 void OfflinePageMetadataStoreSQL::RemoveOfflinePagesSync(
227 const std::vector<int64_t>& offline_ids,
228 scoped_refptr<base::SingleThreadTaskRunner> runner,
229 const UpdateCallback& callback) {
230 // If you create a transaction but don't Commit() it is automatically
231 // rolled back by its destructor when it falls out of scope.
232 sql::Transaction transaction(db_.get());
233
234 for (auto offline_id : offline_ids) {
235 if (!DeleteByOfflineId(db_.get(), offline_id)) {
236 runner->PostTask(FROM_HERE, base::Bind(callback, false));
237 return;
238 }
239 }
240
241 if (!transaction.Commit()) {
fgorski 2016/03/29 06:50:18 This posts the result twice upon failure, because
bburns 2016/03/30 16:54:40 Done.
242 runner->PostTask(FROM_HERE, base::Bind(callback, false));
243 }
244 runner->PostTask(FROM_HERE, base::Bind(callback, true));
245 }
246
247 void OfflinePageMetadataStoreSQL::Reset(const ResetCallback& callback) {
248 bool success = DropTable(db_.get(), kOfflinePagesTableName);
fgorski 2016/03/29 06:50:19 shouldn't DDL be called on the background thread?
bburns 2016/03/30 16:54:39 If you want. Drop table seemed like it was so fas
fgorski 2016/03/30 17:19:31 Let's stay consistent with choice of threads. If y
Scott Hess - ex-Googler 2016/04/12 21:57:23 While SQLite _can_ be thread-safe in certain ways,
249 callback.Run(success);
250 }
251
252 void OfflinePageMetadataStoreSQL::NotifyLoadResult(
253 const LoadCallback& callback,
fgorski 2016/03/29 06:50:19 I don't mean to argue either order is better: call
bburns 2016/03/30 16:54:39 Done.
254 scoped_refptr<base::SingleThreadTaskRunner> runner,
255 LoadStatus status,
256 const std::vector<OfflinePageItem>& result) {
257 UMA_HISTOGRAM_ENUMERATION("OfflinePages.LoadStatus", status,
258 OfflinePageMetadataStore::LOAD_STATUS_COUNT);
259 if (status == LOAD_SUCCEEDED) {
260 UMA_HISTOGRAM_COUNTS("OfflinePages.SavedPageCount", result.size());
261 } else {
262 DVLOG(1) << "Offline pages database loading failed: " << status;
263 db_.reset();
264 }
265 runner->PostTask(FROM_HERE, base::Bind(callback, status, result));
266 }
267
268 void OfflinePageMetadataStoreSQL::Load(const LoadCallback& callback) {
269 background_task_runner_->PostTask(
270 FROM_HERE, base::Bind(&OfflinePageMetadataStoreSQL::LoadSync,
271 weak_ptr_factory_.GetWeakPtr(),
272 base::ThreadTaskRunnerHandle::Get(), callback));
273 }
274
275 void OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePage(
276 const OfflinePageItem& offline_page,
277 const UpdateCallback& callback) {
278 background_task_runner_->PostTask(
279 FROM_HERE,
280 base::Bind(&OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePageSync,
281 weak_ptr_factory_.GetWeakPtr(), offline_page,
282 base::ThreadTaskRunnerHandle::Get(), callback));
283 }
284
285 void OfflinePageMetadataStoreSQL::RemoveOfflinePages(
286 const std::vector<int64_t>& offline_ids,
287 const UpdateCallback& callback) {
288 if (offline_ids.size() == 0) {
289 // Nothing to do.
fgorski 2016/03/29 06:50:19 Good shortcut, but for safety you can post it to t
bburns 2016/03/30 16:54:40 Done.
fgorski 2016/03/30 17:19:31 I meant something like: base::ThreadTaskRunnerHand
290 callback.Run(true);
291 return;
292 }
293
294 background_task_runner_->PostTask(
295 FROM_HERE,
296 base::Bind(&OfflinePageMetadataStoreSQL::RemoveOfflinePagesSync,
297 weak_ptr_factory_.GetWeakPtr(), offline_ids,
298 base::ThreadTaskRunnerHandle::Get(), callback));
299 }
300
301 } // namespace offline_pages
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698