Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 "components/offline_pages/offline_page_item.h" | |
| 14 #include "sql/connection.h" | |
| 15 #include "sql/error_delegate_util.h" | |
| 16 #include "sql/meta_table.h" | |
| 17 #include "sql/statement.h" | |
| 18 #include "sql/transaction.h" | |
| 19 | |
| 20 namespace offline_pages { | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 const int kCurrentVersion = 1; | |
| 25 const int kCompatibleVersion = 1; | |
| 26 | |
| 27 const char kTableName[] = "offlinepages"; | |
|
fgorski
2016/03/24 16:27:54
kOfflinePagesTable(Name)
bburns
2016/03/25 23:07:37
Done.
| |
| 28 const char kColumns[] = | |
|
fgorski
2016/03/24 16:27:55
kOfflinePagesTableColumns
bburns
2016/03/25 23:07:38
Done.
| |
| 29 "(offline_id INTEGER PRIMARY_KEY," | |
| 30 " client_namespace VARCHAR(256)," | |
| 31 " client_id VARCHAR(256)," | |
| 32 " online_url VARCHAR(2048)," | |
| 33 " offline_url VARCHAR(2048)," | |
| 34 " version INTEGER," | |
| 35 " creation_time INTEGER," | |
| 36 " file_path VARCHAR(1024)," | |
| 37 " file_size INTEGER," | |
| 38 " last_access_time INTEGER," | |
| 39 " access_count INTEGER," | |
| 40 " status INTEGER," | |
| 41 " user_initiated BOOLEAN)"; | |
| 42 | |
| 43 #define OFFLINE_ID 0 | |
|
fgorski
2016/03/24 16:27:55
Add table prefix, e.g. OP_
It will come in handy
Dmitry Titov
2016/03/24 23:22:20
I believe we this form is preferable lately in Chr
bburns
2016/03/25 23:07:37
Done.
bburns
2016/03/25 23:07:38
Done.
| |
| 44 #define CLIENT_NAMESPACE 1 | |
| 45 #define CLIENT_ID 2 | |
| 46 #define ONLINE_URL 3 | |
| 47 #define OFFLINE_URL 4 | |
| 48 #define VERSION 5 | |
| 49 #define CREATION_TIME 6 | |
| 50 #define FILE_PATH 7 | |
| 51 #define FILE_SIZE 8 | |
| 52 #define LAST_ACCESS 9 | |
| 53 #define ACCESS_COUNT 10 | |
| 54 #define STATUS 11 | |
| 55 #define USER_INITIATED 12 | |
| 56 | |
| 57 bool CreateTable(sql::Connection* db, | |
| 58 const char* table_name, | |
| 59 const char* columns) { | |
| 60 std::string sql("CREATE TABLE "); | |
| 61 sql += table_name; | |
| 62 sql += columns; | |
| 63 return db->Execute(sql.c_str()); | |
| 64 } | |
| 65 | |
| 66 bool CreateSchema(sql::MetaTable* meta_table, sql::Connection* db) { | |
| 67 sql::Transaction transaction(db); | |
| 68 if (!transaction.Begin()) | |
| 69 return false; | |
| 70 | |
| 71 if (!meta_table->Init(db, kCurrentVersion, kCompatibleVersion)) | |
| 72 return false; | |
| 73 | |
| 74 if (!CreateTable(db, kTableName, kColumns)) | |
| 75 return false; | |
| 76 | |
| 77 // TODO: indexes here | |
|
Dmitry Titov
2016/03/24 23:22:20
// TODO(bburns): ....
bburns
2016/03/25 23:07:37
Done.
| |
| 78 return transaction.Commit(); | |
| 79 } | |
| 80 | |
| 81 bool DropTable(sql::Connection* db, const char* table_name) { | |
| 82 std::string sql("DROP TABLE "); | |
| 83 sql += table_name; | |
| 84 return db->Execute(sql.c_str()); | |
| 85 } | |
| 86 | |
| 87 bool DeleteOfflineId(sql::Connection* db, int64_t offline_id) { | |
|
fgorski
2016/03/24 16:27:55
DeleteByOfflineId
bburns
2016/03/25 23:07:37
Done.
| |
| 88 std::string sql("DELETE FROM "); | |
|
fgorski
2016/03/24 16:27:54
this whole statement should be a const with " offl
bburns
2016/03/25 23:07:38
Done.
| |
| 89 sql += kTableName; | |
| 90 sql += " WHERE offline_id="; | |
| 91 sql += offline_id; | |
| 92 return db->Execute(sql.c_str()); | |
| 93 } | |
| 94 | |
| 95 OfflinePageItem MakeOfflinePageItem(sql::Statement* statement) { | |
| 96 int64_t id = statement->ColumnInt64(OFFLINE_ID); | |
| 97 GURL url(statement->ColumnString(ONLINE_URL)); | |
| 98 ClientId client_id(statement->ColumnString(CLIENT_NAMESPACE), | |
| 99 statement->ColumnString(CLIENT_ID)); | |
| 100 base::FilePath path(statement->ColumnString(FILE_PATH)); | |
| 101 int64_t file_size = statement->ColumnInt64(FILE_SIZE); | |
| 102 base::Time creation = | |
|
Dmitry Titov
2016/03/24 23:22:20
creationTime
bburns
2016/03/25 23:07:37
Done.
| |
| 103 base::Time::FromDoubleT(statement->ColumnInt64(CREATION_TIME)); | |
|
fgorski
2016/03/24 16:27:54
base::Time::FromInternalValue(statement->ColumnInt
bburns
2016/03/25 23:07:37
Done.
| |
| 104 | |
| 105 return OfflinePageItem(url, id, client_id, path, file_size, creation); | |
| 106 } | |
| 107 | |
| 108 bool InsertOrReplace(sql::Connection* db, const OfflinePageItem& item) { | |
| 109 std::string sql = "INSERT OR REPLACE INTO "; | |
|
fgorski
2016/03/24 16:27:55
extract as constant the whole sql statement.
bburns
2016/03/25 23:07:37
Done.
| |
| 110 sql += kTableName; | |
| 111 sql += | |
| 112 " (offline_id, online_url, client_namespace, client_id, file_path, " | |
| 113 "file_size, creation_time)" | |
|
fgorski
2016/03/24 16:27:55
what about other fields?
bburns
2016/03/25 23:07:38
added a couple more. The database is actually a s
| |
| 114 " VALUES " | |
| 115 " (?, ?, ?, ?, ?, ?, ?)"; | |
| 116 | |
| 117 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
| 118 statement.BindInt64(OFFLINE_ID, item.offline_id); | |
| 119 statement.BindString(ONLINE_URL, item.url.spec()); | |
| 120 statement.BindString(CLIENT_NAMESPACE, item.client_id.name_space); | |
| 121 statement.BindString(CLIENT_ID, item.client_id.id); | |
| 122 statement.BindString(FILE_PATH, item.file_path.value()); | |
| 123 statement.BindInt64(FILE_SIZE, item.file_size); | |
| 124 statement.BindInt64(CREATION_TIME, (int64_t)item.creation_time.ToDoubleT()); | |
|
fgorski
2016/03/24 16:27:55
item.creation_time.ToInternalValue();
bburns
2016/03/25 23:07:37
Done.
| |
| 125 | |
| 126 return statement.Run(); | |
| 127 } | |
| 128 | |
| 129 } // anonymous namespace | |
| 130 | |
| 131 OfflinePageMetadataStoreSQL::OfflinePageMetadataStoreSQL( | |
| 132 scoped_refptr<base::SequencedTaskRunner> background_task_runner, | |
| 133 const base::FilePath& path) | |
| 134 : background_task_runner_(background_task_runner), | |
| 135 db_file_path_(path), | |
| 136 weak_ptr_factory_(this) {} | |
| 137 | |
| 138 OfflinePageMetadataStoreSQL::~OfflinePageMetadataStoreSQL() {} | |
| 139 | |
| 140 void OfflinePageMetadataStoreSQL::LoadSync(const LoadCallback& callback) { | |
| 141 // TODO: should this be async? | |
|
fgorski
2016/03/24 16:27:54
nit: TODO(bburns): ;)
bburns
2016/03/25 23:07:37
Removed.
| |
| 142 bool opened = false; | |
| 143 db_.reset(new sql::Connection); | |
| 144 meta_table_.reset(new sql::MetaTable); | |
| 145 | |
| 146 if (use_in_memory_) { | |
| 147 opened = db_->OpenInMemory(); | |
| 148 } else if (!base::CreateDirectory(db_file_path_.DirName())) { | |
| 149 LOG(ERROR) << "Failed to create appcache directory."; | |
|
fgorski
2016/03/24 16:27:54
I admire your Copy-Paste Fu ;)
also, I actually l
bburns
2016/03/25 23:07:38
Done.
| |
| 150 } else { | |
| 151 opened = db_->Open(db_file_path_); | |
| 152 if (opened) | |
| 153 db_->Preload(); | |
| 154 } | |
| 155 if (!opened) { | |
| 156 NotifyLoadResult(callback, STORE_INIT_FAILED, | |
| 157 std::vector<OfflinePageItem>()); | |
| 158 } | |
| 159 | |
| 160 if (!sql::MetaTable::DoesTableExist(db_.get())) { | |
| 161 if (!CreateSchema(meta_table_.get(), db_.get())) { | |
| 162 NotifyLoadResult(callback, STORE_INIT_FAILED, | |
| 163 std::vector<OfflinePageItem>()); | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion)) | |
| 168 NotifyLoadResult(callback, STORE_INIT_FAILED, | |
|
fgorski
2016/03/24 16:27:54
seems like we will not be able to distinguish thes
bburns
2016/03/25 23:07:37
This is still TBD, but will add before final revie
fgorski
2016/03/29 06:50:18
At least put a visible TODO for now, please
| |
| 169 std::vector<OfflinePageItem>()); | |
| 170 | |
| 171 if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) { | |
| 172 LOG(WARNING) << "Offline database is too new."; | |
| 173 NotifyLoadResult(callback, STORE_INIT_FAILED, | |
| 174 std::vector<OfflinePageItem>()); | |
| 175 } | |
| 176 | |
| 177 std::string sql("SELECT * FROM "); | |
|
fgorski
2016/03/24 16:27:54
extract const char []
bburns
2016/03/25 23:07:37
Done.
| |
| 178 sql += kTableName; | |
| 179 | |
| 180 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
| 181 | |
| 182 std::vector<OfflinePageItem> result; | |
| 183 while (statement.Step()) { | |
| 184 result.push_back(MakeOfflinePageItem(&statement)); | |
| 185 } | |
| 186 | |
| 187 if (!statement.Succeeded()) { | |
| 188 NotifyLoadResult(callback, STORE_INIT_FAILED, | |
|
fgorski
2016/03/24 16:27:55
STORE_LOAD_FAILED?
bburns
2016/03/25 23:07:37
Done.
| |
| 189 std::vector<OfflinePageItem>()); | |
| 190 } | |
|
fgorski
2016/03/24 16:27:55
When do we notify of LOAD_SUCCEEDED?
bburns
2016/03/25 23:07:37
Done.
| |
| 191 } | |
| 192 | |
| 193 void OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePageSync( | |
| 194 const OfflinePageItem& offline_page, | |
| 195 const UpdateCallback& callback) { | |
| 196 bool ok = InsertOrReplace(db_.get(), offline_page); | |
| 197 callback.Run(ok); | |
|
fgorski
2016/03/24 16:27:54
you are on the background_task_runner_ here I don'
bburns
2016/03/25 23:07:38
Done.
| |
| 198 } | |
| 199 | |
| 200 void OfflinePageMetadataStoreSQL::RemoveOfflinePagesSync( | |
| 201 const std::vector<int64_t>& offline_ids, | |
| 202 const UpdateCallback& callback) { | |
| 203 sql::Transaction transaction(db_.get()); | |
| 204 | |
| 205 for (auto offline_id : offline_ids) { | |
| 206 if (!DeleteOfflineId(db_.get(), offline_id)) { | |
|
fgorski
2016/03/24 16:27:54
will this cancel the whole transaction if nothing
bburns
2016/03/25 23:07:37
Fixed with an early exit.
| |
| 207 callback.Run(false); | |
| 208 return; | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 if (!transaction.Commit()) { | |
| 213 callback.Run(false); | |
| 214 } | |
| 215 callback.Run(true); | |
| 216 } | |
| 217 | |
| 218 void OfflinePageMetadataStoreSQL::Reset(const ResetCallback& callback) { | |
| 219 bool success = DropTable(db_.get(), kTableName); | |
| 220 callback.Run(success); | |
| 221 } | |
| 222 | |
| 223 void OfflinePageMetadataStoreSQL::NotifyLoadResult( | |
| 224 const LoadCallback& callback, | |
| 225 LoadStatus status, | |
| 226 const std::vector<OfflinePageItem>& result) { | |
|
fgorski
2016/03/24 16:27:55
probably worth adding UMA histogram for load time.
bburns
2016/03/25 23:07:37
As above working on getting UMA together.
| |
| 227 UMA_HISTOGRAM_ENUMERATION("OfflinePages.LoadStatus", status, | |
| 228 OfflinePageMetadataStore::LOAD_STATUS_COUNT); | |
| 229 if (status == LOAD_SUCCEEDED) { | |
| 230 UMA_HISTOGRAM_COUNTS("OfflinePages.SavedPageCount", result.size()); | |
| 231 } else { | |
| 232 DVLOG(1) << "Offline pages database loading failed: " << status; | |
| 233 db_.reset(); | |
| 234 } | |
| 235 callback.Run(status, result); | |
| 236 } | |
| 237 | |
| 238 void OfflinePageMetadataStoreSQL::Load(const LoadCallback& callback) { | |
| 239 background_task_runner_->PostTask( | |
| 240 FROM_HERE, base::Bind(&OfflinePageMetadataStoreSQL::LoadSync, | |
| 241 weak_ptr_factory_.GetWeakPtr(), callback)); | |
|
fgorski
2016/03/24 16:27:54
applies to next 3 calls: which thread is callback
bburns
2016/03/25 23:07:37
Done.
| |
| 242 } | |
| 243 | |
| 244 void OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePage( | |
| 245 const OfflinePageItem& offline_page, | |
| 246 const UpdateCallback& callback) { | |
| 247 background_task_runner_->PostTask( | |
| 248 FROM_HERE, | |
| 249 base::Bind(&OfflinePageMetadataStoreSQL::AddOrUpdateOfflinePageSync, | |
| 250 weak_ptr_factory_.GetWeakPtr(), offline_page, callback)); | |
| 251 } | |
| 252 | |
| 253 void OfflinePageMetadataStoreSQL::RemoveOfflinePages( | |
| 254 const std::vector<int64_t>& offline_ids, | |
| 255 const UpdateCallback& callback) { | |
| 256 background_task_runner_->PostTask( | |
| 257 FROM_HERE, | |
| 258 base::Bind(&OfflinePageMetadataStoreSQL::RemoveOfflinePagesSync, | |
| 259 weak_ptr_factory_.GetWeakPtr(), offline_ids, callback)); | |
| 260 } | |
| 261 | |
| 262 } // namespace offline_pages | |
| OLD | NEW |