Process Alt-Svc headers.
[chromium-blink-merge.git] / storage / browser / quota / quota_database.cc
blobb7b4ad31eeea7cc46fc403b6f6e88d6eeeba77f7
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.
5 #include "storage/browser/quota/quota_database.h"
7 #include <vector>
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/files/file_util.h"
12 #include "sql/connection.h"
13 #include "sql/meta_table.h"
14 #include "sql/statement.h"
15 #include "sql/transaction.h"
16 #include "storage/browser/quota/special_storage_policy.h"
18 namespace storage {
19 namespace {
21 // Definitions for database schema.
23 const int kCurrentVersion = 4;
24 const int kCompatibleVersion = 2;
26 const char kHostQuotaTable[] = "HostQuotaTable";
27 const char kOriginInfoTable[] = "OriginInfoTable";
28 const char kIsOriginTableBootstrapped[] = "IsOriginTableBootstrapped";
30 bool VerifyValidQuotaConfig(const char* key) {
31 return (key != NULL &&
32 (!strcmp(key, QuotaDatabase::kDesiredAvailableSpaceKey) ||
33 !strcmp(key, QuotaDatabase::kTemporaryQuotaOverrideKey)));
36 const int kCommitIntervalMs = 30000;
38 } // anonymous namespace
40 // static
41 const char QuotaDatabase::kDesiredAvailableSpaceKey[] = "DesiredAvailableSpace";
42 const char QuotaDatabase::kTemporaryQuotaOverrideKey[] =
43 "TemporaryQuotaOverride";
45 const QuotaDatabase::TableSchema QuotaDatabase::kTables[] = {
46 { kHostQuotaTable,
47 "(host TEXT NOT NULL,"
48 " type INTEGER NOT NULL,"
49 " quota INTEGER DEFAULT 0,"
50 " UNIQUE(host, type))" },
51 { kOriginInfoTable,
52 "(origin TEXT NOT NULL,"
53 " type INTEGER NOT NULL,"
54 " used_count INTEGER DEFAULT 0,"
55 " last_access_time INTEGER DEFAULT 0,"
56 " last_modified_time INTEGER DEFAULT 0,"
57 " UNIQUE(origin, type))" },
60 // static
61 const QuotaDatabase::IndexSchema QuotaDatabase::kIndexes[] = {
62 { "HostIndex",
63 kHostQuotaTable,
64 "(host)",
65 false },
66 { "OriginInfoIndex",
67 kOriginInfoTable,
68 "(origin)",
69 false },
70 { "OriginLastAccessTimeIndex",
71 kOriginInfoTable,
72 "(last_access_time)",
73 false },
74 { "OriginLastModifiedTimeIndex",
75 kOriginInfoTable,
76 "(last_modified_time)",
77 false },
80 struct QuotaDatabase::QuotaTableImporter {
81 bool Append(const QuotaTableEntry& entry) {
82 entries.push_back(entry);
83 return true;
85 std::vector<QuotaTableEntry> entries;
88 // Clang requires explicit out-of-line constructors for them.
89 QuotaDatabase::QuotaTableEntry::QuotaTableEntry()
90 : type(kStorageTypeUnknown),
91 quota(0) {
94 QuotaDatabase::QuotaTableEntry::QuotaTableEntry(
95 const std::string& host,
96 StorageType type,
97 int64 quota)
98 : host(host),
99 type(type),
100 quota(quota) {
103 QuotaDatabase::OriginInfoTableEntry::OriginInfoTableEntry()
104 : type(kStorageTypeUnknown),
105 used_count(0) {
108 QuotaDatabase::OriginInfoTableEntry::OriginInfoTableEntry(
109 const GURL& origin,
110 StorageType type,
111 int used_count,
112 const base::Time& last_access_time,
113 const base::Time& last_modified_time)
114 : origin(origin),
115 type(type),
116 used_count(used_count),
117 last_access_time(last_access_time),
118 last_modified_time(last_modified_time) {
121 // QuotaDatabase ------------------------------------------------------------
122 QuotaDatabase::QuotaDatabase(const base::FilePath& path)
123 : db_file_path_(path),
124 is_recreating_(false),
125 is_disabled_(false) {
128 QuotaDatabase::~QuotaDatabase() {
129 if (db_) {
130 db_->CommitTransaction();
134 void QuotaDatabase::CloseConnection() {
135 meta_table_.reset();
136 db_.reset();
139 bool QuotaDatabase::GetHostQuota(
140 const std::string& host, StorageType type, int64* quota) {
141 DCHECK(quota);
142 if (!LazyOpen(false))
143 return false;
145 const char* kSql =
146 "SELECT quota"
147 " FROM HostQuotaTable"
148 " WHERE host = ? AND type = ?";
150 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
151 statement.BindString(0, host);
152 statement.BindInt(1, static_cast<int>(type));
154 if (!statement.Step())
155 return false;
157 *quota = statement.ColumnInt64(0);
158 return true;
161 bool QuotaDatabase::SetHostQuota(
162 const std::string& host, StorageType type, int64 quota) {
163 DCHECK_GE(quota, 0);
164 if (!LazyOpen(true))
165 return false;
167 const char* kSql =
168 "INSERT OR REPLACE INTO HostQuotaTable"
169 " (quota, host, type)"
170 " VALUES (?, ?, ?)";
171 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
172 statement.BindInt64(0, quota);
173 statement.BindString(1, host);
174 statement.BindInt(2, static_cast<int>(type));
176 if (!statement.Run())
177 return false;
179 ScheduleCommit();
180 return true;
183 bool QuotaDatabase::SetOriginLastAccessTime(
184 const GURL& origin, StorageType type, base::Time last_access_time) {
185 if (!LazyOpen(true))
186 return false;
188 sql::Statement statement;
190 int used_count = 1;
191 if (FindOriginUsedCount(origin, type, &used_count)) {
192 ++used_count;
193 const char* kSql =
194 "UPDATE OriginInfoTable"
195 " SET used_count = ?, last_access_time = ?"
196 " WHERE origin = ? AND type = ?";
197 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
198 } else {
199 const char* kSql =
200 "INSERT INTO OriginInfoTable"
201 " (used_count, last_access_time, origin, type)"
202 " VALUES (?, ?, ?, ?)";
203 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
205 statement.BindInt(0, used_count);
206 statement.BindInt64(1, last_access_time.ToInternalValue());
207 statement.BindString(2, origin.spec());
208 statement.BindInt(3, static_cast<int>(type));
210 if (!statement.Run())
211 return false;
213 ScheduleCommit();
214 return true;
217 bool QuotaDatabase::SetOriginLastModifiedTime(
218 const GURL& origin, StorageType type, base::Time last_modified_time) {
219 if (!LazyOpen(true))
220 return false;
222 sql::Statement statement;
224 int dummy;
225 if (FindOriginUsedCount(origin, type, &dummy)) {
226 const char* kSql =
227 "UPDATE OriginInfoTable"
228 " SET last_modified_time = ?"
229 " WHERE origin = ? AND type = ?";
230 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
231 } else {
232 const char* kSql =
233 "INSERT INTO OriginInfoTable"
234 " (last_modified_time, origin, type) VALUES (?, ?, ?)";
235 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
237 statement.BindInt64(0, last_modified_time.ToInternalValue());
238 statement.BindString(1, origin.spec());
239 statement.BindInt(2, static_cast<int>(type));
241 if (!statement.Run())
242 return false;
244 ScheduleCommit();
245 return true;
248 bool QuotaDatabase::RegisterInitialOriginInfo(
249 const std::set<GURL>& origins, StorageType type) {
250 if (!LazyOpen(true))
251 return false;
253 typedef std::set<GURL>::const_iterator itr_type;
254 for (itr_type itr = origins.begin(), end = origins.end();
255 itr != end; ++itr) {
256 const char* kSql =
257 "INSERT OR IGNORE INTO OriginInfoTable"
258 " (origin, type) VALUES (?, ?)";
259 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
260 statement.BindString(0, itr->spec());
261 statement.BindInt(1, static_cast<int>(type));
263 if (!statement.Run())
264 return false;
267 ScheduleCommit();
268 return true;
271 bool QuotaDatabase::DeleteHostQuota(
272 const std::string& host, StorageType type) {
273 if (!LazyOpen(false))
274 return false;
276 const char* kSql =
277 "DELETE FROM HostQuotaTable"
278 " WHERE host = ? AND type = ?";
280 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
281 statement.BindString(0, host);
282 statement.BindInt(1, static_cast<int>(type));
284 if (!statement.Run())
285 return false;
287 ScheduleCommit();
288 return true;
291 bool QuotaDatabase::DeleteOriginInfo(
292 const GURL& origin, StorageType type) {
293 if (!LazyOpen(false))
294 return false;
296 const char* kSql =
297 "DELETE FROM OriginInfoTable"
298 " WHERE origin = ? AND type = ?";
300 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
301 statement.BindString(0, origin.spec());
302 statement.BindInt(1, static_cast<int>(type));
304 if (!statement.Run())
305 return false;
307 ScheduleCommit();
308 return true;
311 bool QuotaDatabase::GetQuotaConfigValue(const char* key, int64* value) {
312 if (!LazyOpen(false))
313 return false;
314 DCHECK(VerifyValidQuotaConfig(key));
315 return meta_table_->GetValue(key, value);
318 bool QuotaDatabase::SetQuotaConfigValue(const char* key, int64 value) {
319 if (!LazyOpen(true))
320 return false;
321 DCHECK(VerifyValidQuotaConfig(key));
322 return meta_table_->SetValue(key, value);
325 bool QuotaDatabase::GetLRUOrigin(
326 StorageType type,
327 const std::set<GURL>& exceptions,
328 SpecialStoragePolicy* special_storage_policy,
329 GURL* origin) {
330 DCHECK(origin);
331 if (!LazyOpen(false))
332 return false;
334 const char* kSql = "SELECT origin FROM OriginInfoTable"
335 " WHERE type = ?"
336 " ORDER BY last_access_time ASC";
338 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
339 statement.BindInt(0, static_cast<int>(type));
341 while (statement.Step()) {
342 GURL url(statement.ColumnString(0));
343 if (exceptions.find(url) != exceptions.end())
344 continue;
345 if (special_storage_policy &&
346 (special_storage_policy->IsStorageDurable(url) ||
347 special_storage_policy->IsStorageUnlimited(url)))
348 continue;
349 *origin = url;
350 return true;
353 *origin = GURL();
354 return statement.Succeeded();
357 bool QuotaDatabase::GetOriginsModifiedSince(
358 StorageType type, std::set<GURL>* origins, base::Time modified_since) {
359 DCHECK(origins);
360 if (!LazyOpen(false))
361 return false;
363 const char* kSql = "SELECT origin FROM OriginInfoTable"
364 " WHERE type = ? AND last_modified_time >= ?";
366 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
367 statement.BindInt(0, static_cast<int>(type));
368 statement.BindInt64(1, modified_since.ToInternalValue());
370 origins->clear();
371 while (statement.Step())
372 origins->insert(GURL(statement.ColumnString(0)));
374 return statement.Succeeded();
377 bool QuotaDatabase::IsOriginDatabaseBootstrapped() {
378 if (!LazyOpen(true))
379 return false;
381 int flag = 0;
382 return meta_table_->GetValue(kIsOriginTableBootstrapped, &flag) && flag;
385 bool QuotaDatabase::SetOriginDatabaseBootstrapped(bool bootstrap_flag) {
386 if (!LazyOpen(true))
387 return false;
389 return meta_table_->SetValue(kIsOriginTableBootstrapped, bootstrap_flag);
392 void QuotaDatabase::Commit() {
393 if (!db_)
394 return;
396 if (timer_.IsRunning())
397 timer_.Stop();
399 db_->CommitTransaction();
400 db_->BeginTransaction();
403 void QuotaDatabase::ScheduleCommit() {
404 if (timer_.IsRunning())
405 return;
406 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCommitIntervalMs),
407 this, &QuotaDatabase::Commit);
410 bool QuotaDatabase::FindOriginUsedCount(
411 const GURL& origin, StorageType type, int* used_count) {
412 DCHECK(used_count);
413 if (!LazyOpen(false))
414 return false;
416 const char* kSql =
417 "SELECT used_count FROM OriginInfoTable"
418 " WHERE origin = ? AND type = ?";
420 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
421 statement.BindString(0, origin.spec());
422 statement.BindInt(1, static_cast<int>(type));
424 if (!statement.Step())
425 return false;
427 *used_count = statement.ColumnInt(0);
428 return true;
431 bool QuotaDatabase::LazyOpen(bool create_if_needed) {
432 if (db_)
433 return true;
435 // If we tried and failed once, don't try again in the same session
436 // to avoid creating an incoherent mess on disk.
437 if (is_disabled_)
438 return false;
440 bool in_memory_only = db_file_path_.empty();
441 if (!create_if_needed &&
442 (in_memory_only || !base::PathExists(db_file_path_))) {
443 return false;
446 db_.reset(new sql::Connection);
447 meta_table_.reset(new sql::MetaTable);
449 db_->set_histogram_tag("Quota");
451 bool opened = false;
452 if (in_memory_only) {
453 opened = db_->OpenInMemory();
454 } else if (!base::CreateDirectory(db_file_path_.DirName())) {
455 LOG(ERROR) << "Failed to create quota database directory.";
456 } else {
457 opened = db_->Open(db_file_path_);
458 if (opened)
459 db_->Preload();
462 if (!opened || !EnsureDatabaseVersion()) {
463 LOG(ERROR) << "Failed to open the quota database.";
464 is_disabled_ = true;
465 db_.reset();
466 meta_table_.reset();
467 return false;
470 // Start a long-running transaction.
471 db_->BeginTransaction();
473 return true;
476 bool QuotaDatabase::EnsureDatabaseVersion() {
477 static const size_t kTableCount = arraysize(kTables);
478 static const size_t kIndexCount = arraysize(kIndexes);
479 if (!sql::MetaTable::DoesTableExist(db_.get()))
480 return CreateSchema(db_.get(), meta_table_.get(),
481 kCurrentVersion, kCompatibleVersion,
482 kTables, kTableCount,
483 kIndexes, kIndexCount);
485 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
486 return false;
488 if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
489 LOG(WARNING) << "Quota database is too new.";
490 return false;
493 if (meta_table_->GetVersionNumber() < kCurrentVersion) {
494 if (!UpgradeSchema(meta_table_->GetVersionNumber()))
495 return ResetSchema();
498 #ifndef NDEBUG
499 DCHECK(sql::MetaTable::DoesTableExist(db_.get()));
500 for (size_t i = 0; i < kTableCount; ++i) {
501 DCHECK(db_->DoesTableExist(kTables[i].table_name));
503 #endif
505 return true;
508 // static
509 bool QuotaDatabase::CreateSchema(
510 sql::Connection* database,
511 sql::MetaTable* meta_table,
512 int schema_version, int compatible_version,
513 const TableSchema* tables, size_t tables_size,
514 const IndexSchema* indexes, size_t indexes_size) {
515 // TODO(kinuko): Factor out the common code to create databases.
516 sql::Transaction transaction(database);
517 if (!transaction.Begin())
518 return false;
520 if (!meta_table->Init(database, schema_version, compatible_version))
521 return false;
523 for (size_t i = 0; i < tables_size; ++i) {
524 std::string sql("CREATE TABLE ");
525 sql += tables[i].table_name;
526 sql += tables[i].columns;
527 if (!database->Execute(sql.c_str())) {
528 VLOG(1) << "Failed to execute " << sql;
529 return false;
533 for (size_t i = 0; i < indexes_size; ++i) {
534 std::string sql;
535 if (indexes[i].unique)
536 sql += "CREATE UNIQUE INDEX ";
537 else
538 sql += "CREATE INDEX ";
539 sql += indexes[i].index_name;
540 sql += " ON ";
541 sql += indexes[i].table_name;
542 sql += indexes[i].columns;
543 if (!database->Execute(sql.c_str())) {
544 VLOG(1) << "Failed to execute " << sql;
545 return false;
549 return transaction.Commit();
552 bool QuotaDatabase::ResetSchema() {
553 DCHECK(!db_file_path_.empty());
554 DCHECK(base::PathExists(db_file_path_));
555 VLOG(1) << "Deleting existing quota data and starting over.";
557 db_.reset();
558 meta_table_.reset();
560 if (!sql::Connection::Delete(db_file_path_))
561 return false;
563 // So we can't go recursive.
564 if (is_recreating_)
565 return false;
567 base::AutoReset<bool> auto_reset(&is_recreating_, true);
568 return LazyOpen(true);
571 bool QuotaDatabase::UpgradeSchema(int current_version) {
572 if (current_version == 2) {
573 QuotaTableImporter importer;
574 typedef std::vector<QuotaTableEntry> QuotaTableEntries;
575 if (!DumpQuotaTable(base::Bind(&QuotaTableImporter::Append,
576 base::Unretained(&importer)))) {
577 return false;
579 ResetSchema();
580 for (QuotaTableEntries::const_iterator iter = importer.entries.begin();
581 iter != importer.entries.end(); ++iter) {
582 if (!SetHostQuota(iter->host, iter->type, iter->quota))
583 return false;
585 Commit();
586 return true;
588 return false;
591 bool QuotaDatabase::DumpQuotaTable(const QuotaTableCallback& callback) {
592 if (!LazyOpen(true))
593 return false;
595 const char* kSql = "SELECT * FROM HostQuotaTable";
596 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
598 while (statement.Step()) {
599 QuotaTableEntry entry = QuotaTableEntry(
600 statement.ColumnString(0),
601 static_cast<StorageType>(statement.ColumnInt(1)),
602 statement.ColumnInt64(2));
604 if (!callback.Run(entry))
605 return true;
608 return statement.Succeeded();
611 bool QuotaDatabase::DumpOriginInfoTable(
612 const OriginInfoTableCallback& callback) {
614 if (!LazyOpen(true))
615 return false;
617 const char* kSql = "SELECT * FROM OriginInfoTable";
618 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
620 while (statement.Step()) {
621 OriginInfoTableEntry entry(
622 GURL(statement.ColumnString(0)),
623 static_cast<StorageType>(statement.ColumnInt(1)),
624 statement.ColumnInt(2),
625 base::Time::FromInternalValue(statement.ColumnInt64(3)),
626 base::Time::FromInternalValue(statement.ColumnInt64(4)));
628 if (!callback.Run(entry))
629 return true;
632 return statement.Succeeded();
635 bool operator<(const QuotaDatabase::QuotaTableEntry& lhs,
636 const QuotaDatabase::QuotaTableEntry& rhs) {
637 if (lhs.host < rhs.host) return true;
638 if (rhs.host < lhs.host) return false;
639 if (lhs.type < rhs.type) return true;
640 if (rhs.type < lhs.type) return false;
641 return lhs.quota < rhs.quota;
644 bool operator<(const QuotaDatabase::OriginInfoTableEntry& lhs,
645 const QuotaDatabase::OriginInfoTableEntry& rhs) {
646 if (lhs.origin < rhs.origin) return true;
647 if (rhs.origin < lhs.origin) return false;
648 if (lhs.type < rhs.type) return true;
649 if (rhs.type < lhs.type) return false;
650 if (lhs.used_count < rhs.used_count) return true;
651 if (rhs.used_count < lhs.used_count) return false;
652 return lhs.last_access_time < rhs.last_access_time;
655 } // namespace storage