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

Side by Side Diff: webkit/browser/quota/quota_database.cc

Issue 539143002: Migrate webkit/browser/ to storage/browser/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix android build Created 6 years, 3 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
« no previous file with comments | « webkit/browser/quota/quota_database.h ('k') | webkit/browser/quota/quota_manager.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "webkit/browser/quota/quota_database.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/auto_reset.h"
11 #include "base/bind.h"
12 #include "base/files/file_util.h"
13 #include "base/time/time.h"
14 #include "sql/connection.h"
15 #include "sql/meta_table.h"
16 #include "sql/statement.h"
17 #include "sql/transaction.h"
18 #include "url/gurl.h"
19 #include "webkit/browser/quota/special_storage_policy.h"
20
21 namespace storage {
22 namespace {
23
24 // Definitions for database schema.
25
26 const int kCurrentVersion = 4;
27 const int kCompatibleVersion = 2;
28
29 const char kHostQuotaTable[] = "HostQuotaTable";
30 const char kOriginInfoTable[] = "OriginInfoTable";
31 const char kIsOriginTableBootstrapped[] = "IsOriginTableBootstrapped";
32
33 bool VerifyValidQuotaConfig(const char* key) {
34 return (key != NULL &&
35 (!strcmp(key, QuotaDatabase::kDesiredAvailableSpaceKey) ||
36 !strcmp(key, QuotaDatabase::kTemporaryQuotaOverrideKey)));
37 }
38
39 const int kCommitIntervalMs = 30000;
40
41 } // anonymous namespace
42
43 // static
44 const char QuotaDatabase::kDesiredAvailableSpaceKey[] = "DesiredAvailableSpace";
45 const char QuotaDatabase::kTemporaryQuotaOverrideKey[] =
46 "TemporaryQuotaOverride";
47
48 const QuotaDatabase::TableSchema QuotaDatabase::kTables[] = {
49 { kHostQuotaTable,
50 "(host TEXT NOT NULL,"
51 " type INTEGER NOT NULL,"
52 " quota INTEGER DEFAULT 0,"
53 " UNIQUE(host, type))" },
54 { kOriginInfoTable,
55 "(origin TEXT NOT NULL,"
56 " type INTEGER NOT NULL,"
57 " used_count INTEGER DEFAULT 0,"
58 " last_access_time INTEGER DEFAULT 0,"
59 " last_modified_time INTEGER DEFAULT 0,"
60 " UNIQUE(origin, type))" },
61 };
62
63 // static
64 const QuotaDatabase::IndexSchema QuotaDatabase::kIndexes[] = {
65 { "HostIndex",
66 kHostQuotaTable,
67 "(host)",
68 false },
69 { "OriginInfoIndex",
70 kOriginInfoTable,
71 "(origin)",
72 false },
73 { "OriginLastAccessTimeIndex",
74 kOriginInfoTable,
75 "(last_access_time)",
76 false },
77 { "OriginLastModifiedTimeIndex",
78 kOriginInfoTable,
79 "(last_modified_time)",
80 false },
81 };
82
83 struct QuotaDatabase::QuotaTableImporter {
84 bool Append(const QuotaTableEntry& entry) {
85 entries.push_back(entry);
86 return true;
87 }
88 std::vector<QuotaTableEntry> entries;
89 };
90
91 // Clang requires explicit out-of-line constructors for them.
92 QuotaDatabase::QuotaTableEntry::QuotaTableEntry()
93 : type(kStorageTypeUnknown),
94 quota(0) {
95 }
96
97 QuotaDatabase::QuotaTableEntry::QuotaTableEntry(
98 const std::string& host,
99 StorageType type,
100 int64 quota)
101 : host(host),
102 type(type),
103 quota(quota) {
104 }
105
106 QuotaDatabase::OriginInfoTableEntry::OriginInfoTableEntry()
107 : type(kStorageTypeUnknown),
108 used_count(0) {
109 }
110
111 QuotaDatabase::OriginInfoTableEntry::OriginInfoTableEntry(
112 const GURL& origin,
113 StorageType type,
114 int used_count,
115 const base::Time& last_access_time,
116 const base::Time& last_modified_time)
117 : origin(origin),
118 type(type),
119 used_count(used_count),
120 last_access_time(last_access_time),
121 last_modified_time(last_modified_time) {
122 }
123
124 // QuotaDatabase ------------------------------------------------------------
125 QuotaDatabase::QuotaDatabase(const base::FilePath& path)
126 : db_file_path_(path),
127 is_recreating_(false),
128 is_disabled_(false) {
129 }
130
131 QuotaDatabase::~QuotaDatabase() {
132 if (db_) {
133 db_->CommitTransaction();
134 }
135 }
136
137 void QuotaDatabase::CloseConnection() {
138 meta_table_.reset();
139 db_.reset();
140 }
141
142 bool QuotaDatabase::GetHostQuota(
143 const std::string& host, StorageType type, int64* quota) {
144 DCHECK(quota);
145 if (!LazyOpen(false))
146 return false;
147
148 const char* kSql =
149 "SELECT quota"
150 " FROM HostQuotaTable"
151 " WHERE host = ? AND type = ?";
152
153 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
154 statement.BindString(0, host);
155 statement.BindInt(1, static_cast<int>(type));
156
157 if (!statement.Step())
158 return false;
159
160 *quota = statement.ColumnInt64(0);
161 return true;
162 }
163
164 bool QuotaDatabase::SetHostQuota(
165 const std::string& host, StorageType type, int64 quota) {
166 DCHECK_GE(quota, 0);
167 if (!LazyOpen(true))
168 return false;
169
170 const char* kSql =
171 "INSERT OR REPLACE INTO HostQuotaTable"
172 " (quota, host, type)"
173 " VALUES (?, ?, ?)";
174 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
175 statement.BindInt64(0, quota);
176 statement.BindString(1, host);
177 statement.BindInt(2, static_cast<int>(type));
178
179 if (!statement.Run())
180 return false;
181
182 ScheduleCommit();
183 return true;
184 }
185
186 bool QuotaDatabase::SetOriginLastAccessTime(
187 const GURL& origin, StorageType type, base::Time last_access_time) {
188 if (!LazyOpen(true))
189 return false;
190
191 sql::Statement statement;
192
193 int used_count = 1;
194 if (FindOriginUsedCount(origin, type, &used_count)) {
195 ++used_count;
196 const char* kSql =
197 "UPDATE OriginInfoTable"
198 " SET used_count = ?, last_access_time = ?"
199 " WHERE origin = ? AND type = ?";
200 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
201 } else {
202 const char* kSql =
203 "INSERT INTO OriginInfoTable"
204 " (used_count, last_access_time, origin, type)"
205 " VALUES (?, ?, ?, ?)";
206 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
207 }
208 statement.BindInt(0, used_count);
209 statement.BindInt64(1, last_access_time.ToInternalValue());
210 statement.BindString(2, origin.spec());
211 statement.BindInt(3, static_cast<int>(type));
212
213 if (!statement.Run())
214 return false;
215
216 ScheduleCommit();
217 return true;
218 }
219
220 bool QuotaDatabase::SetOriginLastModifiedTime(
221 const GURL& origin, StorageType type, base::Time last_modified_time) {
222 if (!LazyOpen(true))
223 return false;
224
225 sql::Statement statement;
226
227 int dummy;
228 if (FindOriginUsedCount(origin, type, &dummy)) {
229 const char* kSql =
230 "UPDATE OriginInfoTable"
231 " SET last_modified_time = ?"
232 " WHERE origin = ? AND type = ?";
233 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
234 } else {
235 const char* kSql =
236 "INSERT INTO OriginInfoTable"
237 " (last_modified_time, origin, type) VALUES (?, ?, ?)";
238 statement.Assign(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
239 }
240 statement.BindInt64(0, last_modified_time.ToInternalValue());
241 statement.BindString(1, origin.spec());
242 statement.BindInt(2, static_cast<int>(type));
243
244 if (!statement.Run())
245 return false;
246
247 ScheduleCommit();
248 return true;
249 }
250
251 bool QuotaDatabase::RegisterInitialOriginInfo(
252 const std::set<GURL>& origins, StorageType type) {
253 if (!LazyOpen(true))
254 return false;
255
256 typedef std::set<GURL>::const_iterator itr_type;
257 for (itr_type itr = origins.begin(), end = origins.end();
258 itr != end; ++itr) {
259 const char* kSql =
260 "INSERT OR IGNORE INTO OriginInfoTable"
261 " (origin, type) VALUES (?, ?)";
262 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
263 statement.BindString(0, itr->spec());
264 statement.BindInt(1, static_cast<int>(type));
265
266 if (!statement.Run())
267 return false;
268 }
269
270 ScheduleCommit();
271 return true;
272 }
273
274 bool QuotaDatabase::DeleteHostQuota(
275 const std::string& host, StorageType type) {
276 if (!LazyOpen(false))
277 return false;
278
279 const char* kSql =
280 "DELETE FROM HostQuotaTable"
281 " WHERE host = ? AND type = ?";
282
283 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
284 statement.BindString(0, host);
285 statement.BindInt(1, static_cast<int>(type));
286
287 if (!statement.Run())
288 return false;
289
290 ScheduleCommit();
291 return true;
292 }
293
294 bool QuotaDatabase::DeleteOriginInfo(
295 const GURL& origin, StorageType type) {
296 if (!LazyOpen(false))
297 return false;
298
299 const char* kSql =
300 "DELETE FROM OriginInfoTable"
301 " WHERE origin = ? AND type = ?";
302
303 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
304 statement.BindString(0, origin.spec());
305 statement.BindInt(1, static_cast<int>(type));
306
307 if (!statement.Run())
308 return false;
309
310 ScheduleCommit();
311 return true;
312 }
313
314 bool QuotaDatabase::GetQuotaConfigValue(const char* key, int64* value) {
315 if (!LazyOpen(false))
316 return false;
317 DCHECK(VerifyValidQuotaConfig(key));
318 return meta_table_->GetValue(key, value);
319 }
320
321 bool QuotaDatabase::SetQuotaConfigValue(const char* key, int64 value) {
322 if (!LazyOpen(true))
323 return false;
324 DCHECK(VerifyValidQuotaConfig(key));
325 return meta_table_->SetValue(key, value);
326 }
327
328 bool QuotaDatabase::GetLRUOrigin(
329 StorageType type,
330 const std::set<GURL>& exceptions,
331 SpecialStoragePolicy* special_storage_policy,
332 GURL* origin) {
333 DCHECK(origin);
334 if (!LazyOpen(false))
335 return false;
336
337 const char* kSql = "SELECT origin FROM OriginInfoTable"
338 " WHERE type = ?"
339 " ORDER BY last_access_time ASC";
340
341 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
342 statement.BindInt(0, static_cast<int>(type));
343
344 while (statement.Step()) {
345 GURL url(statement.ColumnString(0));
346 if (exceptions.find(url) != exceptions.end())
347 continue;
348 if (special_storage_policy &&
349 special_storage_policy->IsStorageUnlimited(url))
350 continue;
351 *origin = url;
352 return true;
353 }
354
355 *origin = GURL();
356 return statement.Succeeded();
357 }
358
359 bool QuotaDatabase::GetOriginsModifiedSince(
360 StorageType type, std::set<GURL>* origins, base::Time modified_since) {
361 DCHECK(origins);
362 if (!LazyOpen(false))
363 return false;
364
365 const char* kSql = "SELECT origin FROM OriginInfoTable"
366 " WHERE type = ? AND last_modified_time >= ?";
367
368 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
369 statement.BindInt(0, static_cast<int>(type));
370 statement.BindInt64(1, modified_since.ToInternalValue());
371
372 origins->clear();
373 while (statement.Step())
374 origins->insert(GURL(statement.ColumnString(0)));
375
376 return statement.Succeeded();
377 }
378
379 bool QuotaDatabase::IsOriginDatabaseBootstrapped() {
380 if (!LazyOpen(true))
381 return false;
382
383 int flag = 0;
384 return meta_table_->GetValue(kIsOriginTableBootstrapped, &flag) && flag;
385 }
386
387 bool QuotaDatabase::SetOriginDatabaseBootstrapped(bool bootstrap_flag) {
388 if (!LazyOpen(true))
389 return false;
390
391 return meta_table_->SetValue(kIsOriginTableBootstrapped, bootstrap_flag);
392 }
393
394 void QuotaDatabase::Commit() {
395 if (!db_)
396 return;
397
398 if (timer_.IsRunning())
399 timer_.Stop();
400
401 db_->CommitTransaction();
402 db_->BeginTransaction();
403 }
404
405 void QuotaDatabase::ScheduleCommit() {
406 if (timer_.IsRunning())
407 return;
408 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCommitIntervalMs),
409 this, &QuotaDatabase::Commit);
410 }
411
412 bool QuotaDatabase::FindOriginUsedCount(
413 const GURL& origin, StorageType type, int* used_count) {
414 DCHECK(used_count);
415 if (!LazyOpen(false))
416 return false;
417
418 const char* kSql =
419 "SELECT used_count FROM OriginInfoTable"
420 " WHERE origin = ? AND type = ?";
421
422 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
423 statement.BindString(0, origin.spec());
424 statement.BindInt(1, static_cast<int>(type));
425
426 if (!statement.Step())
427 return false;
428
429 *used_count = statement.ColumnInt(0);
430 return true;
431 }
432
433 bool QuotaDatabase::LazyOpen(bool create_if_needed) {
434 if (db_)
435 return true;
436
437 // If we tried and failed once, don't try again in the same session
438 // to avoid creating an incoherent mess on disk.
439 if (is_disabled_)
440 return false;
441
442 bool in_memory_only = db_file_path_.empty();
443 if (!create_if_needed &&
444 (in_memory_only || !base::PathExists(db_file_path_))) {
445 return false;
446 }
447
448 db_.reset(new sql::Connection);
449 meta_table_.reset(new sql::MetaTable);
450
451 db_->set_histogram_tag("Quota");
452
453 bool opened = false;
454 if (in_memory_only) {
455 opened = db_->OpenInMemory();
456 } else if (!base::CreateDirectory(db_file_path_.DirName())) {
457 LOG(ERROR) << "Failed to create quota database directory.";
458 } else {
459 opened = db_->Open(db_file_path_);
460 if (opened)
461 db_->Preload();
462 }
463
464 if (!opened || !EnsureDatabaseVersion()) {
465 LOG(ERROR) << "Failed to open the quota database.";
466 is_disabled_ = true;
467 db_.reset();
468 meta_table_.reset();
469 return false;
470 }
471
472 // Start a long-running transaction.
473 db_->BeginTransaction();
474
475 return true;
476 }
477
478 bool QuotaDatabase::EnsureDatabaseVersion() {
479 static const size_t kTableCount = ARRAYSIZE_UNSAFE(kTables);
480 static const size_t kIndexCount = ARRAYSIZE_UNSAFE(kIndexes);
481 if (!sql::MetaTable::DoesTableExist(db_.get()))
482 return CreateSchema(db_.get(), meta_table_.get(),
483 kCurrentVersion, kCompatibleVersion,
484 kTables, kTableCount,
485 kIndexes, kIndexCount);
486
487 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
488 return false;
489
490 if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
491 LOG(WARNING) << "Quota database is too new.";
492 return false;
493 }
494
495 if (meta_table_->GetVersionNumber() < kCurrentVersion) {
496 if (!UpgradeSchema(meta_table_->GetVersionNumber()))
497 return ResetSchema();
498 }
499
500 #ifndef NDEBUG
501 DCHECK(sql::MetaTable::DoesTableExist(db_.get()));
502 for (size_t i = 0; i < kTableCount; ++i) {
503 DCHECK(db_->DoesTableExist(kTables[i].table_name));
504 }
505 #endif
506
507 return true;
508 }
509
510 // static
511 bool QuotaDatabase::CreateSchema(
512 sql::Connection* database,
513 sql::MetaTable* meta_table,
514 int schema_version, int compatible_version,
515 const TableSchema* tables, size_t tables_size,
516 const IndexSchema* indexes, size_t indexes_size) {
517 // TODO(kinuko): Factor out the common code to create databases.
518 sql::Transaction transaction(database);
519 if (!transaction.Begin())
520 return false;
521
522 if (!meta_table->Init(database, schema_version, compatible_version))
523 return false;
524
525 for (size_t i = 0; i < tables_size; ++i) {
526 std::string sql("CREATE TABLE ");
527 sql += tables[i].table_name;
528 sql += tables[i].columns;
529 if (!database->Execute(sql.c_str())) {
530 VLOG(1) << "Failed to execute " << sql;
531 return false;
532 }
533 }
534
535 for (size_t i = 0; i < indexes_size; ++i) {
536 std::string sql;
537 if (indexes[i].unique)
538 sql += "CREATE UNIQUE INDEX ";
539 else
540 sql += "CREATE INDEX ";
541 sql += indexes[i].index_name;
542 sql += " ON ";
543 sql += indexes[i].table_name;
544 sql += indexes[i].columns;
545 if (!database->Execute(sql.c_str())) {
546 VLOG(1) << "Failed to execute " << sql;
547 return false;
548 }
549 }
550
551 return transaction.Commit();
552 }
553
554 bool QuotaDatabase::ResetSchema() {
555 DCHECK(!db_file_path_.empty());
556 DCHECK(base::PathExists(db_file_path_));
557 VLOG(1) << "Deleting existing quota data and starting over.";
558
559 db_.reset();
560 meta_table_.reset();
561
562 if (!sql::Connection::Delete(db_file_path_))
563 return false;
564
565 // So we can't go recursive.
566 if (is_recreating_)
567 return false;
568
569 base::AutoReset<bool> auto_reset(&is_recreating_, true);
570 return LazyOpen(true);
571 }
572
573 bool QuotaDatabase::UpgradeSchema(int current_version) {
574 if (current_version == 2) {
575 QuotaTableImporter importer;
576 typedef std::vector<QuotaTableEntry> QuotaTableEntries;
577 if (!DumpQuotaTable(base::Bind(&QuotaTableImporter::Append,
578 base::Unretained(&importer)))) {
579 return false;
580 }
581 ResetSchema();
582 for (QuotaTableEntries::const_iterator iter = importer.entries.begin();
583 iter != importer.entries.end(); ++iter) {
584 if (!SetHostQuota(iter->host, iter->type, iter->quota))
585 return false;
586 }
587 Commit();
588 return true;
589 }
590 return false;
591 }
592
593 bool QuotaDatabase::DumpQuotaTable(const QuotaTableCallback& callback) {
594 if (!LazyOpen(true))
595 return false;
596
597 const char* kSql = "SELECT * FROM HostQuotaTable";
598 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
599
600 while (statement.Step()) {
601 QuotaTableEntry entry = QuotaTableEntry(
602 statement.ColumnString(0),
603 static_cast<StorageType>(statement.ColumnInt(1)),
604 statement.ColumnInt64(2));
605
606 if (!callback.Run(entry))
607 return true;
608 }
609
610 return statement.Succeeded();
611 }
612
613 bool QuotaDatabase::DumpOriginInfoTable(
614 const OriginInfoTableCallback& callback) {
615
616 if (!LazyOpen(true))
617 return false;
618
619 const char* kSql = "SELECT * FROM OriginInfoTable";
620 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
621
622 while (statement.Step()) {
623 OriginInfoTableEntry entry(
624 GURL(statement.ColumnString(0)),
625 static_cast<StorageType>(statement.ColumnInt(1)),
626 statement.ColumnInt(2),
627 base::Time::FromInternalValue(statement.ColumnInt64(3)),
628 base::Time::FromInternalValue(statement.ColumnInt64(4)));
629
630 if (!callback.Run(entry))
631 return true;
632 }
633
634 return statement.Succeeded();
635 }
636
637 bool operator<(const QuotaDatabase::QuotaTableEntry& lhs,
638 const QuotaDatabase::QuotaTableEntry& rhs) {
639 if (lhs.host < rhs.host) return true;
640 if (rhs.host < lhs.host) return false;
641 if (lhs.type < rhs.type) return true;
642 if (rhs.type < lhs.type) return false;
643 return lhs.quota < rhs.quota;
644 }
645
646 bool operator<(const QuotaDatabase::OriginInfoTableEntry& lhs,
647 const QuotaDatabase::OriginInfoTableEntry& rhs) {
648 if (lhs.origin < rhs.origin) return true;
649 if (rhs.origin < lhs.origin) return false;
650 if (lhs.type < rhs.type) return true;
651 if (rhs.type < lhs.type) return false;
652 if (lhs.used_count < rhs.used_count) return true;
653 if (rhs.used_count < lhs.used_count) return false;
654 return lhs.last_access_time < rhs.last_access_time;
655 }
656
657 } // namespace storage
OLDNEW
« no previous file with comments | « webkit/browser/quota/quota_database.h ('k') | webkit/browser/quota/quota_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698