Revert "Merged all Chromoting Host code into remoting_core.dll (Windows)."
[chromium-blink-merge.git] / webkit / quota / quota_database.cc
blobaaf6245a9612de22a56285402967707f2c3b6c24
1 // Copyright (c) 2012 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 "webkit/quota/quota_database.h"
7 #include <string>
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/file_util.h"
12 #include "base/time.h"
13 #include "googleurl/src/gurl.h"
14 #include "sql/connection.h"
15 #include "sql/meta_table.h"
16 #include "sql/statement.h"
17 #include "sql/transaction.h"
18 #include "webkit/quota/special_storage_policy.h"
20 namespace quota {
21 namespace {
23 // Definitions for database schema.
25 const int kCurrentVersion = 4;
26 const int kCompatibleVersion = 2;
28 const char kHostQuotaTable[] = "HostQuotaTable";
29 const char kOriginInfoTable[] = "OriginInfoTable";
30 const char kIsOriginTableBootstrapped[] = "IsOriginTableBootstrapped";
32 bool VerifyValidQuotaConfig(const char* key) {
33 return (key != NULL &&
34 (!strcmp(key, QuotaDatabase::kDesiredAvailableSpaceKey) ||
35 !strcmp(key, QuotaDatabase::kTemporaryQuotaOverrideKey)));
38 const int kCommitIntervalMs = 30000;
40 } // anonymous namespace
42 // static
43 const char QuotaDatabase::kDesiredAvailableSpaceKey[] = "DesiredAvailableSpace";
44 const char QuotaDatabase::kTemporaryQuotaOverrideKey[] =
45 "TemporaryQuotaOverride";
47 const QuotaDatabase::TableSchema QuotaDatabase::kTables[] = {
48 { kHostQuotaTable,
49 "(host TEXT NOT NULL,"
50 " type INTEGER NOT NULL,"
51 " quota INTEGER DEFAULT 0,"
52 " UNIQUE(host, type))" },
53 { kOriginInfoTable,
54 "(origin TEXT NOT NULL,"
55 " type INTEGER NOT NULL,"
56 " used_count INTEGER DEFAULT 0,"
57 " last_access_time INTEGER DEFAULT 0,"
58 " last_modified_time INTEGER DEFAULT 0,"
59 " UNIQUE(origin, type))" },
62 // static
63 const QuotaDatabase::IndexSchema QuotaDatabase::kIndexes[] = {
64 { "HostIndex",
65 kHostQuotaTable,
66 "(host)",
67 false },
68 { "OriginInfoIndex",
69 kOriginInfoTable,
70 "(origin)",
71 false },
72 { "OriginLastAccessTimeIndex",
73 kOriginInfoTable,
74 "(last_access_time)",
75 false },
76 { "OriginLastModifiedTimeIndex",
77 kOriginInfoTable,
78 "(last_modified_time)",
79 false },
82 struct QuotaDatabase::QuotaTableImporter {
83 bool Append(const QuotaTableEntry& entry) {
84 entries.push_back(entry);
85 return true;
87 std::vector<QuotaTableEntry> entries;
90 // Clang requires explicit out-of-line constructors for them.
91 QuotaDatabase::QuotaTableEntry::QuotaTableEntry()
92 : type(kStorageTypeUnknown),
93 quota(0) {
96 QuotaDatabase::QuotaTableEntry::QuotaTableEntry(
97 const std::string& host,
98 StorageType type,
99 int64 quota)
100 : host(host),
101 type(type),
102 quota(quota) {
105 QuotaDatabase::OriginInfoTableEntry::OriginInfoTableEntry()
106 : type(kStorageTypeUnknown),
107 used_count(0) {
110 QuotaDatabase::OriginInfoTableEntry::OriginInfoTableEntry(
111 const GURL& origin,
112 StorageType type,
113 int used_count,
114 const base::Time& last_access_time,
115 const base::Time& last_modified_time)
116 : origin(origin),
117 type(type),
118 used_count(used_count),
119 last_access_time(last_access_time),
120 last_modified_time(last_modified_time) {
123 // QuotaDatabase ------------------------------------------------------------
124 QuotaDatabase::QuotaDatabase(const FilePath& path)
125 : db_file_path_(path),
126 is_recreating_(false),
127 is_disabled_(false) {
130 QuotaDatabase::~QuotaDatabase() {
131 if (db_.get()) {
132 db_->CommitTransaction();
136 void QuotaDatabase::CloseConnection() {
137 meta_table_.reset();
138 db_.reset();
141 bool QuotaDatabase::GetHostQuota(
142 const std::string& host, StorageType type, int64* quota) {
143 DCHECK(quota);
144 if (!LazyOpen(false))
145 return false;
147 const char* kSql =
148 "SELECT quota"
149 " FROM HostQuotaTable"
150 " WHERE host = ? AND type = ?";
152 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
153 statement.BindString(0, host);
154 statement.BindInt(1, static_cast<int>(type));
156 if (!statement.Step())
157 return false;
159 *quota = statement.ColumnInt64(0);
160 return true;
163 bool QuotaDatabase::SetHostQuota(
164 const std::string& host, StorageType type, int64 quota) {
165 DCHECK_GE(quota, 0);
166 if (!LazyOpen(true))
167 return false;
169 const char* kSql =
170 "INSERT OR REPLACE INTO HostQuotaTable"
171 " (quota, host, type)"
172 " VALUES (?, ?, ?)";
173 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
174 statement.BindInt64(0, quota);
175 statement.BindString(1, host);
176 statement.BindInt(2, static_cast<int>(type));
178 if (!statement.Run())
179 return false;
181 ScheduleCommit();
182 return true;
185 bool QuotaDatabase::SetOriginLastAccessTime(
186 const GURL& origin, StorageType type, base::Time last_access_time) {
187 if (!LazyOpen(true))
188 return false;
190 sql::Statement statement;
192 int used_count = 1;
193 if (FindOriginUsedCount(origin, type, &used_count)) {
194 ++used_count;
195 const char* kSql =
196 "UPDATE OriginInfoTable"
197 " SET used_count = ?, last_access_time = ?"
198 " WHERE origin = ? AND type = ?";
199 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
200 } else {
201 const char* kSql =
202 "INSERT INTO OriginInfoTable"
203 " (used_count, last_access_time, origin, type)"
204 " VALUES (?, ?, ?, ?)";
205 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
207 statement.BindInt(0, used_count);
208 statement.BindInt64(1, last_access_time.ToInternalValue());
209 statement.BindString(2, origin.spec());
210 statement.BindInt(3, static_cast<int>(type));
212 if (!statement.Run())
213 return false;
215 ScheduleCommit();
216 return true;
219 bool QuotaDatabase::SetOriginLastModifiedTime(
220 const GURL& origin, StorageType type, base::Time last_modified_time) {
221 if (!LazyOpen(true))
222 return false;
224 sql::Statement statement;
226 int dummy;
227 if (FindOriginUsedCount(origin, type, &dummy)) {
228 const char* kSql =
229 "UPDATE OriginInfoTable"
230 " SET last_modified_time = ?"
231 " WHERE origin = ? AND type = ?";
232 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
233 } else {
234 const char* kSql =
235 "INSERT INTO OriginInfoTable"
236 " (last_modified_time, origin, type) VALUES (?, ?, ?)";
237 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
239 statement.BindInt64(0, last_modified_time.ToInternalValue());
240 statement.BindString(1, origin.spec());
241 statement.BindInt(2, static_cast<int>(type));
243 if (!statement.Run())
244 return false;
246 ScheduleCommit();
247 return true;
250 bool QuotaDatabase::RegisterInitialOriginInfo(
251 const std::set<GURL>& origins, StorageType type) {
252 if (!LazyOpen(true))
253 return false;
255 typedef std::set<GURL>::const_iterator itr_type;
256 for (itr_type itr = origins.begin(), end = origins.end();
257 itr != end; ++itr) {
258 const char* kSql =
259 "INSERT OR IGNORE INTO OriginInfoTable"
260 " (origin, type) VALUES (?, ?)";
261 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
262 statement.BindString(0, itr->spec());
263 statement.BindInt(1, static_cast<int>(type));
265 if (!statement.Run())
266 return false;
269 ScheduleCommit();
270 return true;
273 bool QuotaDatabase::DeleteHostQuota(
274 const std::string& host, StorageType type) {
275 if (!LazyOpen(false))
276 return false;
278 const char* kSql =
279 "DELETE FROM HostQuotaTable"
280 " WHERE host = ? AND type = ?";
282 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
283 statement.BindString(0, host);
284 statement.BindInt(1, static_cast<int>(type));
286 if (!statement.Run())
287 return false;
289 ScheduleCommit();
290 return true;
293 bool QuotaDatabase::DeleteOriginInfo(
294 const GURL& origin, StorageType type) {
295 if (!LazyOpen(false))
296 return false;
298 const char* kSql =
299 "DELETE FROM OriginInfoTable"
300 " WHERE origin = ? AND type = ?";
302 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
303 statement.BindString(0, origin.spec());
304 statement.BindInt(1, static_cast<int>(type));
306 if (!statement.Run())
307 return false;
309 ScheduleCommit();
310 return true;
313 bool QuotaDatabase::GetQuotaConfigValue(const char* key, int64* value) {
314 if (!LazyOpen(false))
315 return false;
316 DCHECK(VerifyValidQuotaConfig(key));
317 return meta_table_->GetValue(key, value);
320 bool QuotaDatabase::SetQuotaConfigValue(const char* key, int64 value) {
321 if (!LazyOpen(true))
322 return false;
323 DCHECK(VerifyValidQuotaConfig(key));
324 return meta_table_->SetValue(key, value);
327 bool QuotaDatabase::GetLRUOrigin(
328 StorageType type,
329 const std::set<GURL>& exceptions,
330 SpecialStoragePolicy* special_storage_policy,
331 GURL* origin) {
332 DCHECK(origin);
333 if (!LazyOpen(false))
334 return false;
336 const char* kSql = "SELECT origin FROM OriginInfoTable"
337 " WHERE type = ?"
338 " ORDER BY last_access_time ASC";
340 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
341 statement.BindInt(0, static_cast<int>(type));
343 while (statement.Step()) {
344 GURL url(statement.ColumnString(0));
345 if (exceptions.find(url) != exceptions.end())
346 continue;
347 if (special_storage_policy &&
348 special_storage_policy->IsStorageUnlimited(url))
349 continue;
350 *origin = url;
351 return true;
354 *origin = GURL();
355 return statement.Succeeded();
358 bool QuotaDatabase::GetOriginsModifiedSince(
359 StorageType type, std::set<GURL>* origins, base::Time modified_since) {
360 DCHECK(origins);
361 if (!LazyOpen(false))
362 return false;
364 const char* kSql = "SELECT origin FROM OriginInfoTable"
365 " WHERE type = ? AND last_modified_time >= ?";
367 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
368 statement.BindInt(0, static_cast<int>(type));
369 statement.BindInt64(1, modified_since.ToInternalValue());
371 origins->clear();
372 while (statement.Step())
373 origins->insert(GURL(statement.ColumnString(0)));
375 return statement.Succeeded();
378 bool QuotaDatabase::IsOriginDatabaseBootstrapped() {
379 if (!LazyOpen(true))
380 return false;
382 int flag = 0;
383 return meta_table_->GetValue(kIsOriginTableBootstrapped, &flag) && flag;
386 bool QuotaDatabase::SetOriginDatabaseBootstrapped(bool bootstrap_flag) {
387 if (!LazyOpen(true))
388 return false;
390 return meta_table_->SetValue(kIsOriginTableBootstrapped, bootstrap_flag);
393 void QuotaDatabase::Commit() {
394 if (!db_.get())
395 return;
397 if (timer_.IsRunning())
398 timer_.Stop();
400 db_->CommitTransaction();
401 db_->BeginTransaction();
404 void QuotaDatabase::ScheduleCommit() {
405 if (timer_.IsRunning())
406 return;
407 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCommitIntervalMs),
408 this, &QuotaDatabase::Commit);
411 bool QuotaDatabase::FindOriginUsedCount(
412 const GURL& origin, StorageType type, int* used_count) {
413 DCHECK(used_count);
414 if (!LazyOpen(false))
415 return false;
417 const char* kSql =
418 "SELECT used_count FROM OriginInfoTable"
419 " WHERE origin = ? AND type = ?";
421 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
422 statement.BindString(0, origin.spec());
423 statement.BindInt(1, static_cast<int>(type));
425 if (!statement.Step())
426 return false;
428 *used_count = statement.ColumnInt(0);
429 return true;
432 bool QuotaDatabase::LazyOpen(bool create_if_needed) {
433 if (db_.get())
434 return true;
436 // If we tried and failed once, don't try again in the same session
437 // to avoid creating an incoherent mess on disk.
438 if (is_disabled_)
439 return false;
441 bool in_memory_only = db_file_path_.empty();
442 if (!create_if_needed &&
443 (in_memory_only || !file_util::PathExists(db_file_path_))) {
444 return false;
447 db_.reset(new sql::Connection);
448 meta_table_.reset(new sql::MetaTable);
450 db_->set_error_histogram_name("Sqlite.Quota.Error");
452 bool opened = false;
453 if (in_memory_only) {
454 opened = db_->OpenInMemory();
455 } else if (!file_util::CreateDirectory(db_file_path_.DirName())) {
456 LOG(ERROR) << "Failed to create quota database directory.";
457 } else {
458 opened = db_->Open(db_file_path_);
459 if (opened)
460 db_->Preload();
463 if (!opened || !EnsureDatabaseVersion()) {
464 LOG(ERROR) << "Failed to open the quota database.";
465 is_disabled_ = true;
466 db_.reset();
467 meta_table_.reset();
468 return false;
471 // Start a long-running transaction.
472 db_->BeginTransaction();
474 return true;
477 bool QuotaDatabase::EnsureDatabaseVersion() {
478 static const size_t kTableCount = ARRAYSIZE_UNSAFE(kTables);
479 static const size_t kIndexCount = ARRAYSIZE_UNSAFE(kIndexes);
480 if (!sql::MetaTable::DoesTableExist(db_.get()))
481 return CreateSchema(db_.get(), meta_table_.get(),
482 kCurrentVersion, kCompatibleVersion,
483 kTables, kTableCount,
484 kIndexes, kIndexCount);
486 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
487 return false;
489 if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
490 LOG(WARNING) << "Quota database is too new.";
491 return false;
494 if (meta_table_->GetVersionNumber() < kCurrentVersion) {
495 if (!UpgradeSchema(meta_table_->GetVersionNumber()))
496 return ResetSchema();
499 #ifndef NDEBUG
500 DCHECK(sql::MetaTable::DoesTableExist(db_.get()));
501 for (size_t i = 0; i < kTableCount; ++i) {
502 DCHECK(db_->DoesTableExist(kTables[i].table_name));
504 #endif
506 return true;
509 // static
510 bool QuotaDatabase::CreateSchema(
511 sql::Connection* database,
512 sql::MetaTable* meta_table,
513 int schema_version, int compatible_version,
514 const TableSchema* tables, size_t tables_size,
515 const IndexSchema* indexes, size_t indexes_size) {
516 // TODO(kinuko): Factor out the common code to create databases.
517 sql::Transaction transaction(database);
518 if (!transaction.Begin())
519 return false;
521 if (!meta_table->Init(database, schema_version, compatible_version))
522 return false;
524 for (size_t i = 0; i < tables_size; ++i) {
525 std::string sql("CREATE TABLE ");
526 sql += tables[i].table_name;
527 sql += tables[i].columns;
528 if (!database->Execute(sql.c_str())) {
529 VLOG(1) << "Failed to execute " << sql;
530 return false;
534 for (size_t i = 0; i < indexes_size; ++i) {
535 std::string sql;
536 if (indexes[i].unique)
537 sql += "CREATE UNIQUE INDEX ";
538 else
539 sql += "CREATE INDEX ";
540 sql += indexes[i].index_name;
541 sql += " ON ";
542 sql += indexes[i].table_name;
543 sql += indexes[i].columns;
544 if (!database->Execute(sql.c_str())) {
545 VLOG(1) << "Failed to execute " << sql;
546 return false;
550 return transaction.Commit();
553 bool QuotaDatabase::ResetSchema() {
554 DCHECK(!db_file_path_.empty());
555 DCHECK(file_util::PathExists(db_file_path_));
556 VLOG(1) << "Deleting existing quota data and starting over.";
558 db_.reset();
559 meta_table_.reset();
561 if (!file_util::Delete(db_file_path_, true))
562 return false;
564 // Make sure the steps above actually deleted things.
565 if (file_util::PathExists(db_file_path_))
566 return false;
568 // So we can't go recursive.
569 if (is_recreating_)
570 return false;
572 base::AutoReset<bool> auto_reset(&is_recreating_, true);
573 return LazyOpen(true);
576 bool QuotaDatabase::UpgradeSchema(int current_version) {
577 if (current_version == 2) {
578 QuotaTableImporter importer;
579 typedef std::vector<QuotaTableEntry> QuotaTableEntries;
580 if (!DumpQuotaTable(new QuotaTableCallback(base::Bind(
581 &QuotaTableImporter::Append, base::Unretained(&importer)))))
582 return false;
583 ResetSchema();
584 for (QuotaTableEntries::const_iterator iter = importer.entries.begin();
585 iter != importer.entries.end(); ++iter) {
586 if (!SetHostQuota(iter->host, iter->type, iter->quota))
587 return false;
589 Commit();
590 return true;
592 return false;
595 bool QuotaDatabase::DumpQuotaTable(QuotaTableCallback* callback) {
596 scoped_ptr<QuotaTableCallback> callback_deleter(callback);
597 if (!LazyOpen(true))
598 return false;
600 const char* kSql = "SELECT * FROM HostQuotaTable";
601 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
603 while (statement.Step()) {
604 QuotaTableEntry entry = QuotaTableEntry(
605 statement.ColumnString(0),
606 static_cast<StorageType>(statement.ColumnInt(1)),
607 statement.ColumnInt64(2));
609 if (!callback->Run(entry))
610 return true;
613 return statement.Succeeded();
616 bool QuotaDatabase::DumpOriginInfoTable(
617 OriginInfoTableCallback* callback) {
618 scoped_ptr<OriginInfoTableCallback> callback_deleter(callback);
620 if (!LazyOpen(true))
621 return false;
623 const char* kSql = "SELECT * FROM OriginInfoTable";
624 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
626 while (statement.Step()) {
627 OriginInfoTableEntry entry(
628 GURL(statement.ColumnString(0)),
629 static_cast<StorageType>(statement.ColumnInt(1)),
630 statement.ColumnInt(2),
631 base::Time::FromInternalValue(statement.ColumnInt64(3)),
632 base::Time::FromInternalValue(statement.ColumnInt64(4)));
634 if (!callback->Run(entry))
635 return true;
638 return statement.Succeeded();
641 bool operator<(const QuotaDatabase::QuotaTableEntry& lhs,
642 const QuotaDatabase::QuotaTableEntry& rhs) {
643 if (lhs.host < rhs.host) return true;
644 if (rhs.host < lhs.host) return false;
645 if (lhs.type < rhs.type) return true;
646 if (rhs.type < lhs.type) return false;
647 return lhs.quota < rhs.quota;
650 bool operator<(const QuotaDatabase::OriginInfoTableEntry& lhs,
651 const QuotaDatabase::OriginInfoTableEntry& rhs) {
652 if (lhs.origin < rhs.origin) return true;
653 if (rhs.origin < lhs.origin) return false;
654 if (lhs.type < rhs.type) return true;
655 if (rhs.type < lhs.type) return false;
656 if (lhs.used_count < rhs.used_count) return true;
657 if (rhs.used_count < lhs.used_count) return false;
658 return lhs.last_access_time < rhs.last_access_time;
661 } // quota namespace