[content shell] implement testRunner.overridePreference
[chromium-blink-merge.git] / webkit / appcache / appcache_database.cc
blob77a8f15c9ac307a486f845bfea937ce420714570
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/appcache/appcache_database.h"
7 #include "base/auto_reset.h"
8 #include "base/file_util.h"
9 #include "base/logging.h"
10 #include "base/utf_string_conversions.h"
11 #include "sql/connection.h"
12 #include "sql/diagnostic_error_delegate.h"
13 #include "sql/meta_table.h"
14 #include "sql/statement.h"
15 #include "sql/transaction.h"
16 #include "webkit/appcache/appcache_entry.h"
17 #include "webkit/appcache/appcache_histograms.h"
19 // Schema -------------------------------------------------------------------
20 namespace {
22 const int kCurrentVersion = 4;
23 const int kCompatibleVersion = 4;
25 const char kGroupsTable[] = "Groups";
26 const char kCachesTable[] = "Caches";
27 const char kEntriesTable[] = "Entries";
28 const char kNamespacesTable[] = "Namespaces";
29 const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
30 const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
32 struct TableInfo {
33 const char* table_name;
34 const char* columns;
37 struct IndexInfo {
38 const char* index_name;
39 const char* table_name;
40 const char* columns;
41 bool unique;
44 const TableInfo kTables[] = {
45 { kGroupsTable,
46 "(group_id INTEGER PRIMARY KEY,"
47 " origin TEXT,"
48 " manifest_url TEXT,"
49 " creation_time INTEGER,"
50 " last_access_time INTEGER)" },
52 { kCachesTable,
53 "(cache_id INTEGER PRIMARY KEY,"
54 " group_id INTEGER,"
55 " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
56 " update_time INTEGER,"
57 " cache_size INTEGER)" }, // intentionally not normalized
59 { kEntriesTable,
60 "(cache_id INTEGER,"
61 " url TEXT,"
62 " flags INTEGER,"
63 " response_id INTEGER,"
64 " response_size INTEGER)" },
66 { kNamespacesTable,
67 "(cache_id INTEGER,"
68 " origin TEXT," // intentionally not normalized
69 " type INTEGER,"
70 " namespace_url TEXT,"
71 " target_url TEXT)" },
73 { kOnlineWhiteListsTable,
74 "(cache_id INTEGER,"
75 " namespace_url TEXT)" },
77 { kDeletableResponseIdsTable,
78 "(response_id INTEGER NOT NULL)" },
81 const IndexInfo kIndexes[] = {
82 { "GroupsOriginIndex",
83 kGroupsTable,
84 "(origin)",
85 false },
87 { "GroupsManifestIndex",
88 kGroupsTable,
89 "(manifest_url)",
90 true },
92 { "CachesGroupIndex",
93 kCachesTable,
94 "(group_id)",
95 false },
97 { "EntriesCacheIndex",
98 kEntriesTable,
99 "(cache_id)",
100 false },
102 { "EntriesCacheAndUrlIndex",
103 kEntriesTable,
104 "(cache_id, url)",
105 true },
107 { "EntriesResponseIdIndex",
108 kEntriesTable,
109 "(response_id)",
110 true },
112 { "NamespacesCacheIndex",
113 kNamespacesTable,
114 "(cache_id)",
115 false },
117 { "NamespacesOriginIndex",
118 kNamespacesTable,
119 "(origin)",
120 false },
122 { "NamespacesCacheAndUrlIndex",
123 kNamespacesTable,
124 "(cache_id, namespace_url)",
125 true },
127 { "OnlineWhiteListCacheIndex",
128 kOnlineWhiteListsTable,
129 "(cache_id)",
130 false },
132 { "DeletableResponsesIdIndex",
133 kDeletableResponseIdsTable,
134 "(response_id)",
135 true },
138 const int kTableCount = ARRAYSIZE_UNSAFE(kTables);
139 const int kIndexCount = ARRAYSIZE_UNSAFE(kIndexes);
141 class HistogramUniquifier {
142 public:
143 static const char* name() { return "Sqlite.AppCache.Error"; }
146 sql::ErrorDelegate* GetErrorHandlerForAppCacheDb() {
147 return new sql::DiagnosticErrorDelegate<HistogramUniquifier>();
150 bool CreateTable(sql::Connection* db, const TableInfo& info) {
151 std::string sql("CREATE TABLE ");
152 sql += info.table_name;
153 sql += info.columns;
154 return db->Execute(sql.c_str());
157 bool CreateIndex(sql::Connection* db, const IndexInfo& info) {
158 std::string sql;
159 if (info.unique)
160 sql += "CREATE UNIQUE INDEX ";
161 else
162 sql += "CREATE INDEX ";
163 sql += info.index_name;
164 sql += " ON ";
165 sql += info.table_name;
166 sql += info.columns;
167 return db->Execute(sql.c_str());
170 } // anon namespace
172 // AppCacheDatabase ----------------------------------------------------------
173 namespace appcache {
175 AppCacheDatabase::GroupRecord::GroupRecord()
176 : group_id(0) {
179 AppCacheDatabase::GroupRecord::~GroupRecord() {
182 AppCacheDatabase::NamespaceRecord::NamespaceRecord()
183 : cache_id(0), type(FALLBACK_NAMESPACE) {
186 AppCacheDatabase::NamespaceRecord::~NamespaceRecord() {
190 AppCacheDatabase::AppCacheDatabase(const FilePath& path)
191 : db_file_path_(path), is_disabled_(false), is_recreating_(false) {
194 AppCacheDatabase::~AppCacheDatabase() {
197 void AppCacheDatabase::CloseConnection() {
198 // We can't close the connection for an in-memory database w/o
199 // losing all of the data, so we don't do that.
200 if (!db_file_path_.empty())
201 ResetConnectionAndTables();
204 void AppCacheDatabase::Disable() {
205 VLOG(1) << "Disabling appcache database.";
206 is_disabled_ = true;
207 ResetConnectionAndTables();
210 int64 AppCacheDatabase::GetOriginUsage(const GURL& origin) {
211 std::vector<CacheRecord> records;
212 if (!FindCachesForOrigin(origin, &records))
213 return 0;
215 int64 origin_usage = 0;
216 std::vector<CacheRecord>::const_iterator iter = records.begin();
217 while (iter != records.end()) {
218 origin_usage += iter->cache_size;
219 ++iter;
221 return origin_usage;
224 bool AppCacheDatabase::GetAllOriginUsage(std::map<GURL, int64>* usage_map) {
225 std::set<GURL> origins;
226 if (!FindOriginsWithGroups(&origins))
227 return false;
228 for (std::set<GURL>::const_iterator origin = origins.begin();
229 origin != origins.end(); ++origin) {
230 (*usage_map)[*origin] = GetOriginUsage(*origin);
232 return true;
235 bool AppCacheDatabase::FindOriginsWithGroups(std::set<GURL>* origins) {
236 DCHECK(origins && origins->empty());
237 if (!LazyOpen(false))
238 return false;
240 const char* kSql =
241 "SELECT DISTINCT(origin) FROM Groups";
243 sql::Statement statement(db_->GetUniqueStatement(kSql));
245 while (statement.Step())
246 origins->insert(GURL(statement.ColumnString(0)));
248 return statement.Succeeded();
251 bool AppCacheDatabase::FindLastStorageIds(
252 int64* last_group_id, int64* last_cache_id, int64* last_response_id,
253 int64* last_deletable_response_rowid) {
254 DCHECK(last_group_id && last_cache_id && last_response_id &&
255 last_deletable_response_rowid);
257 *last_group_id = 0;
258 *last_cache_id = 0;
259 *last_response_id = 0;
260 *last_deletable_response_rowid = 0;
262 if (!LazyOpen(false))
263 return false;
265 const char* kMaxGroupIdSql = "SELECT MAX(group_id) FROM Groups";
266 const char* kMaxCacheIdSql = "SELECT MAX(cache_id) FROM Caches";
267 const char* kMaxResponseIdFromEntriesSql =
268 "SELECT MAX(response_id) FROM Entries";
269 const char* kMaxResponseIdFromDeletablesSql =
270 "SELECT MAX(response_id) FROM DeletableResponseIds";
271 const char* kMaxDeletableResponseRowIdSql =
272 "SELECT MAX(rowid) FROM DeletableResponseIds";
273 int64 max_group_id;
274 int64 max_cache_id;
275 int64 max_response_id_from_entries;
276 int64 max_response_id_from_deletables;
277 int64 max_deletable_response_rowid;
278 if (!RunUniqueStatementWithInt64Result(kMaxGroupIdSql, &max_group_id) ||
279 !RunUniqueStatementWithInt64Result(kMaxCacheIdSql, &max_cache_id) ||
280 !RunUniqueStatementWithInt64Result(kMaxResponseIdFromEntriesSql,
281 &max_response_id_from_entries) ||
282 !RunUniqueStatementWithInt64Result(kMaxResponseIdFromDeletablesSql,
283 &max_response_id_from_deletables) ||
284 !RunUniqueStatementWithInt64Result(kMaxDeletableResponseRowIdSql,
285 &max_deletable_response_rowid)) {
286 return false;
289 *last_group_id = max_group_id;
290 *last_cache_id = max_cache_id;
291 *last_response_id = std::max(max_response_id_from_entries,
292 max_response_id_from_deletables);
293 *last_deletable_response_rowid = max_deletable_response_rowid;
294 return true;
297 bool AppCacheDatabase::FindGroup(int64 group_id, GroupRecord* record) {
298 DCHECK(record);
299 if (!LazyOpen(false))
300 return false;
302 const char* kSql =
303 "SELECT group_id, origin, manifest_url,"
304 " creation_time, last_access_time"
305 " FROM Groups WHERE group_id = ?";
307 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
309 statement.BindInt64(0, group_id);
310 if (!statement.Step())
311 return false;
313 ReadGroupRecord(statement, record);
314 DCHECK(record->group_id == group_id);
315 return true;
318 bool AppCacheDatabase::FindGroupForManifestUrl(
319 const GURL& manifest_url, GroupRecord* record) {
320 DCHECK(record);
321 if (!LazyOpen(false))
322 return false;
324 const char* kSql =
325 "SELECT group_id, origin, manifest_url,"
326 " creation_time, last_access_time"
327 " FROM Groups WHERE manifest_url = ?";
329 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
330 statement.BindString(0, manifest_url.spec());
332 if (!statement.Step())
333 return false;
335 ReadGroupRecord(statement, record);
336 DCHECK(record->manifest_url == manifest_url);
337 return true;
340 bool AppCacheDatabase::FindGroupsForOrigin(
341 const GURL& origin, std::vector<GroupRecord>* records) {
342 DCHECK(records && records->empty());
343 if (!LazyOpen(false))
344 return false;
346 const char* kSql =
347 "SELECT group_id, origin, manifest_url,"
348 " creation_time, last_access_time"
349 " FROM Groups WHERE origin = ?";
351 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
352 statement.BindString(0, origin.spec());
354 while (statement.Step()) {
355 records->push_back(GroupRecord());
356 ReadGroupRecord(statement, &records->back());
357 DCHECK(records->back().origin == origin);
360 return statement.Succeeded();
363 bool AppCacheDatabase::FindGroupForCache(int64 cache_id, GroupRecord* record) {
364 DCHECK(record);
365 if (!LazyOpen(false))
366 return false;
368 const char* kSql =
369 "SELECT g.group_id, g.origin, g.manifest_url,"
370 " g.creation_time, g.last_access_time"
371 " FROM Groups g, Caches c"
372 " WHERE c.cache_id = ? AND c.group_id = g.group_id";
374 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
375 statement.BindInt64(0, cache_id);
377 if (!statement.Step())
378 return false;
380 ReadGroupRecord(statement, record);
381 return true;
384 bool AppCacheDatabase::UpdateGroupLastAccessTime(
385 int64 group_id, base::Time time) {
386 if (!LazyOpen(true))
387 return false;
389 const char* kSql =
390 "UPDATE Groups SET last_access_time = ? WHERE group_id = ?";
392 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
393 statement.BindInt64(0, time.ToInternalValue());
394 statement.BindInt64(1, group_id);
396 return statement.Run() && db_->GetLastChangeCount();
399 bool AppCacheDatabase::InsertGroup(const GroupRecord* record) {
400 if (!LazyOpen(true))
401 return false;
403 const char* kSql =
404 "INSERT INTO Groups"
405 " (group_id, origin, manifest_url, creation_time, last_access_time)"
406 " VALUES(?, ?, ?, ?, ?)";
408 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
409 statement.BindInt64(0, record->group_id);
410 statement.BindString(1, record->origin.spec());
411 statement.BindString(2, record->manifest_url.spec());
412 statement.BindInt64(3, record->creation_time.ToInternalValue());
413 statement.BindInt64(4, record->last_access_time.ToInternalValue());
415 return statement.Run();
418 bool AppCacheDatabase::DeleteGroup(int64 group_id) {
419 if (!LazyOpen(false))
420 return false;
422 const char* kSql =
423 "DELETE FROM Groups WHERE group_id = ?";
425 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
426 statement.BindInt64(0, group_id);
428 return statement.Run();
431 bool AppCacheDatabase::FindCache(int64 cache_id, CacheRecord* record) {
432 DCHECK(record);
433 if (!LazyOpen(false))
434 return false;
436 const char* kSql =
437 "SELECT cache_id, group_id, online_wildcard, update_time, cache_size"
438 " FROM Caches WHERE cache_id = ?";
440 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
441 statement.BindInt64(0, cache_id);
443 if (!statement.Step())
444 return false;
446 ReadCacheRecord(statement, record);
447 return true;
450 bool AppCacheDatabase::FindCacheForGroup(int64 group_id, CacheRecord* record) {
451 DCHECK(record);
452 if (!LazyOpen(false))
453 return false;
455 const char* kSql =
456 "SELECT cache_id, group_id, online_wildcard, update_time, cache_size"
457 " FROM Caches WHERE group_id = ?";
459 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
460 statement.BindInt64(0, group_id);
462 if (!statement.Step())
463 return false;
465 ReadCacheRecord(statement, record);
466 return true;
469 bool AppCacheDatabase::FindCachesForOrigin(
470 const GURL& origin, std::vector<CacheRecord>* records) {
471 DCHECK(records);
472 std::vector<GroupRecord> group_records;
473 if (!FindGroupsForOrigin(origin, &group_records))
474 return false;
476 CacheRecord cache_record;
477 std::vector<GroupRecord>::const_iterator iter = group_records.begin();
478 while (iter != group_records.end()) {
479 if (FindCacheForGroup(iter->group_id, &cache_record))
480 records->push_back(cache_record);
481 ++iter;
483 return true;
486 bool AppCacheDatabase::InsertCache(const CacheRecord* record) {
487 if (!LazyOpen(true))
488 return false;
490 const char* kSql =
491 "INSERT INTO Caches (cache_id, group_id, online_wildcard,"
492 " update_time, cache_size)"
493 " VALUES(?, ?, ?, ?, ?)";
495 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
496 statement.BindInt64(0, record->cache_id);
497 statement.BindInt64(1, record->group_id);
498 statement.BindBool(2, record->online_wildcard);
499 statement.BindInt64(3, record->update_time.ToInternalValue());
500 statement.BindInt64(4, record->cache_size);
502 return statement.Run();
505 bool AppCacheDatabase::DeleteCache(int64 cache_id) {
506 if (!LazyOpen(false))
507 return false;
509 const char* kSql =
510 "DELETE FROM Caches WHERE cache_id = ?";
512 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
513 statement.BindInt64(0, cache_id);
515 return statement.Run();
518 bool AppCacheDatabase::FindEntriesForCache(
519 int64 cache_id, std::vector<EntryRecord>* records) {
520 DCHECK(records && records->empty());
521 if (!LazyOpen(false))
522 return false;
524 const char* kSql =
525 "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
526 " WHERE cache_id = ?";
528 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
529 statement.BindInt64(0, cache_id);
531 while (statement.Step()) {
532 records->push_back(EntryRecord());
533 ReadEntryRecord(statement, &records->back());
534 DCHECK(records->back().cache_id == cache_id);
537 return statement.Succeeded();
540 bool AppCacheDatabase::FindEntriesForUrl(
541 const GURL& url, std::vector<EntryRecord>* records) {
542 DCHECK(records && records->empty());
543 if (!LazyOpen(false))
544 return false;
546 const char* kSql =
547 "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
548 " WHERE url = ?";
550 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
551 statement.BindString(0, url.spec());
553 while (statement.Step()) {
554 records->push_back(EntryRecord());
555 ReadEntryRecord(statement, &records->back());
556 DCHECK(records->back().url == url);
559 return statement.Succeeded();
562 bool AppCacheDatabase::FindEntry(
563 int64 cache_id, const GURL& url, EntryRecord* record) {
564 DCHECK(record);
565 if (!LazyOpen(false))
566 return false;
568 const char* kSql =
569 "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
570 " WHERE cache_id = ? AND url = ?";
572 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
573 statement.BindInt64(0, cache_id);
574 statement.BindString(1, url.spec());
576 if (!statement.Step())
577 return false;
579 ReadEntryRecord(statement, record);
580 DCHECK(record->cache_id == cache_id);
581 DCHECK(record->url == url);
582 return true;
585 bool AppCacheDatabase::InsertEntry(const EntryRecord* record) {
586 if (!LazyOpen(true))
587 return false;
589 const char* kSql =
590 "INSERT INTO Entries (cache_id, url, flags, response_id, response_size)"
591 " VALUES(?, ?, ?, ?, ?)";
593 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
594 statement.BindInt64(0, record->cache_id);
595 statement.BindString(1, record->url.spec());
596 statement.BindInt(2, record->flags);
597 statement.BindInt64(3, record->response_id);
598 statement.BindInt64(4, record->response_size);
600 return statement.Run();
603 bool AppCacheDatabase::InsertEntryRecords(
604 const std::vector<EntryRecord>& records) {
605 if (records.empty())
606 return true;
607 sql::Transaction transaction(db_.get());
608 if (!transaction.Begin())
609 return false;
610 std::vector<EntryRecord>::const_iterator iter = records.begin();
611 while (iter != records.end()) {
612 if (!InsertEntry(&(*iter)))
613 return false;
614 ++iter;
616 return transaction.Commit();
619 bool AppCacheDatabase::DeleteEntriesForCache(int64 cache_id) {
620 if (!LazyOpen(false))
621 return false;
623 const char* kSql =
624 "DELETE FROM Entries WHERE cache_id = ?";
626 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
627 statement.BindInt64(0, cache_id);
629 return statement.Run();
632 bool AppCacheDatabase::AddEntryFlags(
633 const GURL& entry_url, int64 cache_id, int additional_flags) {
634 if (!LazyOpen(false))
635 return false;
637 const char* kSql =
638 "UPDATE Entries SET flags = flags | ? WHERE cache_id = ? AND url = ?";
640 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
641 statement.BindInt(0, additional_flags);
642 statement.BindInt64(1, cache_id);
643 statement.BindString(2, entry_url.spec());
645 return statement.Run() && db_->GetLastChangeCount();
648 bool AppCacheDatabase::FindNamespacesForOrigin(
649 const GURL& origin,
650 std::vector<NamespaceRecord>* intercepts,
651 std::vector<NamespaceRecord>* fallbacks) {
652 DCHECK(intercepts && intercepts->empty());
653 DCHECK(fallbacks && fallbacks->empty());
654 if (!LazyOpen(false))
655 return false;
657 const char* kSql =
658 "SELECT cache_id, origin, type, namespace_url, target_url"
659 " FROM Namespaces WHERE origin = ?";
661 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
662 statement.BindString(0, origin.spec());
664 ReadNamespaceRecords(&statement, intercepts, fallbacks);
666 return statement.Succeeded();
669 bool AppCacheDatabase::FindNamespacesForCache(
670 int64 cache_id,
671 std::vector<NamespaceRecord>* intercepts,
672 std::vector<NamespaceRecord>* fallbacks) {
673 DCHECK(intercepts && intercepts->empty());
674 DCHECK(fallbacks && fallbacks->empty());
675 if (!LazyOpen(false))
676 return false;
678 const char* kSql =
679 "SELECT cache_id, origin, type, namespace_url, target_url"
680 " FROM Namespaces WHERE cache_id = ?";
682 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
683 statement.BindInt64(0, cache_id);
685 ReadNamespaceRecords(&statement, intercepts, fallbacks);
687 return statement.Succeeded();
690 bool AppCacheDatabase::InsertNamespace(
691 const NamespaceRecord* record) {
692 if (!LazyOpen(true))
693 return false;
695 const char* kSql =
696 "INSERT INTO Namespaces"
697 " (cache_id, origin, type, namespace_url, target_url)"
698 " VALUES (?, ?, ?, ?, ?)";
700 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
701 statement.BindInt64(0, record->cache_id);
702 statement.BindString(1, record->origin.spec());
703 statement.BindInt(2, record->type);
704 statement.BindString(3, record->namespace_url.spec());
705 statement.BindString(4, record->target_url.spec());
707 return statement.Run();
710 bool AppCacheDatabase::InsertNamespaceRecords(
711 const std::vector<NamespaceRecord>& records) {
712 if (records.empty())
713 return true;
714 sql::Transaction transaction(db_.get());
715 if (!transaction.Begin())
716 return false;
717 std::vector<NamespaceRecord>::const_iterator iter = records.begin();
718 while (iter != records.end()) {
719 if (!InsertNamespace(&(*iter)))
720 return false;
721 ++iter;
723 return transaction.Commit();
726 bool AppCacheDatabase::DeleteNamespacesForCache(int64 cache_id) {
727 if (!LazyOpen(false))
728 return false;
730 const char* kSql =
731 "DELETE FROM Namespaces WHERE cache_id = ?";
733 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
734 statement.BindInt64(0, cache_id);
736 return statement.Run();
739 bool AppCacheDatabase::FindOnlineWhiteListForCache(
740 int64 cache_id, std::vector<OnlineWhiteListRecord>* records) {
741 DCHECK(records && records->empty());
742 if (!LazyOpen(false))
743 return false;
745 const char* kSql =
746 "SELECT cache_id, namespace_url FROM OnlineWhiteLists"
747 " WHERE cache_id = ?";
749 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
750 statement.BindInt64(0, cache_id);
752 while (statement.Step()) {
753 records->push_back(OnlineWhiteListRecord());
754 this->ReadOnlineWhiteListRecord(statement, &records->back());
755 DCHECK(records->back().cache_id == cache_id);
757 return statement.Succeeded();
760 bool AppCacheDatabase::InsertOnlineWhiteList(
761 const OnlineWhiteListRecord* record) {
762 if (!LazyOpen(true))
763 return false;
765 const char* kSql =
766 "INSERT INTO OnlineWhiteLists (cache_id, namespace_url) VALUES (?, ?)";
768 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
769 statement.BindInt64(0, record->cache_id);
770 statement.BindString(1, record->namespace_url.spec());
772 return statement.Run();
775 bool AppCacheDatabase::InsertOnlineWhiteListRecords(
776 const std::vector<OnlineWhiteListRecord>& records) {
777 if (records.empty())
778 return true;
779 sql::Transaction transaction(db_.get());
780 if (!transaction.Begin())
781 return false;
782 std::vector<OnlineWhiteListRecord>::const_iterator iter = records.begin();
783 while (iter != records.end()) {
784 if (!InsertOnlineWhiteList(&(*iter)))
785 return false;
786 ++iter;
788 return transaction.Commit();
791 bool AppCacheDatabase::DeleteOnlineWhiteListForCache(int64 cache_id) {
792 if (!LazyOpen(false))
793 return false;
795 const char* kSql =
796 "DELETE FROM OnlineWhiteLists WHERE cache_id = ?";
798 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
799 statement.BindInt64(0, cache_id);
801 return statement.Run();
804 bool AppCacheDatabase::GetDeletableResponseIds(
805 std::vector<int64>* response_ids, int64 max_rowid, int limit) {
806 if (!LazyOpen(false))
807 return false;
809 const char* kSql =
810 "SELECT response_id FROM DeletableResponseIds "
811 " WHERE rowid <= ?"
812 " LIMIT ?";
814 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
815 statement.BindInt64(0, max_rowid);
816 statement.BindInt64(1, limit);
818 while (statement.Step())
819 response_ids->push_back(statement.ColumnInt64(0));
820 return statement.Succeeded();
823 bool AppCacheDatabase::InsertDeletableResponseIds(
824 const std::vector<int64>& response_ids) {
825 const char* kSql =
826 "INSERT INTO DeletableResponseIds (response_id) VALUES (?)";
827 return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids);
830 bool AppCacheDatabase::DeleteDeletableResponseIds(
831 const std::vector<int64>& response_ids) {
832 const char* kSql =
833 "DELETE FROM DeletableResponseIds WHERE response_id = ?";
834 return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids);
837 bool AppCacheDatabase::RunCachedStatementWithIds(
838 const sql::StatementID& statement_id, const char* sql,
839 const std::vector<int64>& ids) {
840 DCHECK(sql);
841 if (!LazyOpen(true))
842 return false;
844 sql::Transaction transaction(db_.get());
845 if (!transaction.Begin())
846 return false;
848 sql::Statement statement(db_->GetCachedStatement(statement_id, sql));
850 std::vector<int64>::const_iterator iter = ids.begin();
851 while (iter != ids.end()) {
852 statement.BindInt64(0, *iter);
853 if (!statement.Run())
854 return false;
855 statement.Reset(true);
856 ++iter;
859 return transaction.Commit();
862 bool AppCacheDatabase::RunUniqueStatementWithInt64Result(
863 const char* sql, int64* result) {
864 DCHECK(sql);
865 sql::Statement statement(db_->GetUniqueStatement(sql));
866 if (!statement.Step()) {
867 return false;
869 *result = statement.ColumnInt64(0);
870 return true;
873 bool AppCacheDatabase::FindResponseIdsForCacheHelper(
874 int64 cache_id, std::vector<int64>* ids_vector,
875 std::set<int64>* ids_set) {
876 DCHECK(ids_vector || ids_set);
877 DCHECK(!(ids_vector && ids_set));
878 if (!LazyOpen(false))
879 return false;
881 const char* kSql =
882 "SELECT response_id FROM Entries WHERE cache_id = ?";
884 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
886 statement.BindInt64(0, cache_id);
887 while (statement.Step()) {
888 int64 id = statement.ColumnInt64(0);
889 if (ids_set)
890 ids_set->insert(id);
891 else
892 ids_vector->push_back(id);
895 return statement.Succeeded();
898 void AppCacheDatabase::ReadGroupRecord(
899 const sql::Statement& statement, GroupRecord* record) {
900 record->group_id = statement.ColumnInt64(0);
901 record->origin = GURL(statement.ColumnString(1));
902 record->manifest_url = GURL(statement.ColumnString(2));
903 record->creation_time =
904 base::Time::FromInternalValue(statement.ColumnInt64(3));
905 record->last_access_time =
906 base::Time::FromInternalValue(statement.ColumnInt64(4));
909 void AppCacheDatabase::ReadCacheRecord(
910 const sql::Statement& statement, CacheRecord* record) {
911 record->cache_id = statement.ColumnInt64(0);
912 record->group_id = statement.ColumnInt64(1);
913 record->online_wildcard = statement.ColumnBool(2);
914 record->update_time =
915 base::Time::FromInternalValue(statement.ColumnInt64(3));
916 record->cache_size = statement.ColumnInt64(4);
919 void AppCacheDatabase::ReadEntryRecord(
920 const sql::Statement& statement, EntryRecord* record) {
921 record->cache_id = statement.ColumnInt64(0);
922 record->url = GURL(statement.ColumnString(1));
923 record->flags = statement.ColumnInt(2);
924 record->response_id = statement.ColumnInt64(3);
925 record->response_size = statement.ColumnInt64(4);
928 void AppCacheDatabase::ReadNamespaceRecords(
929 sql::Statement* statement,
930 NamespaceRecordVector* intercepts,
931 NamespaceRecordVector* fallbacks) {
932 while (statement->Step()) {
933 NamespaceType type = static_cast<NamespaceType>(statement->ColumnInt(2));
934 NamespaceRecordVector* records =
935 (type == FALLBACK_NAMESPACE) ? fallbacks : intercepts;
936 records->push_back(NamespaceRecord());
937 ReadNamespaceRecord(statement, &records->back());
941 void AppCacheDatabase::ReadNamespaceRecord(
942 const sql::Statement* statement, NamespaceRecord* record) {
943 record->cache_id = statement->ColumnInt64(0);
944 record->origin = GURL(statement->ColumnString(1));
945 record->type = static_cast<NamespaceType>(statement->ColumnInt(2));
946 record->namespace_url = GURL(statement->ColumnString(3));
947 record->target_url = GURL(statement->ColumnString(4));
950 void AppCacheDatabase::ReadOnlineWhiteListRecord(
951 const sql::Statement& statement, OnlineWhiteListRecord* record) {
952 record->cache_id = statement.ColumnInt64(0);
953 record->namespace_url = GURL(statement.ColumnString(1));
956 bool AppCacheDatabase::LazyOpen(bool create_if_needed) {
957 if (db_.get())
958 return true;
960 // If we tried and failed once, don't try again in the same session
961 // to avoid creating an incoherent mess on disk.
962 if (is_disabled_)
963 return false;
965 // Avoid creating a database at all if we can.
966 bool use_in_memory_db = db_file_path_.empty();
967 if (!create_if_needed &&
968 (use_in_memory_db || !file_util::PathExists(db_file_path_))) {
969 return false;
972 db_.reset(new sql::Connection);
973 meta_table_.reset(new sql::MetaTable);
975 db_->set_error_delegate(GetErrorHandlerForAppCacheDb());
977 bool opened = false;
978 if (use_in_memory_db) {
979 opened = db_->OpenInMemory();
980 } else if (!file_util::CreateDirectory(db_file_path_.DirName())) {
981 LOG(ERROR) << "Failed to create appcache directory.";
982 } else {
983 opened = db_->Open(db_file_path_);
984 if (opened)
985 db_->Preload();
988 if (!opened || !EnsureDatabaseVersion()) {
989 LOG(ERROR) << "Failed to open the appcache database.";
990 AppCacheHistograms::CountInitResult(
991 AppCacheHistograms::SQL_DATABASE_ERROR);
993 // We're unable to open the database. This is a fatal error
994 // which we can't recover from. We try to handle it by deleting
995 // the existing appcache data and starting with a clean slate in
996 // this browser session.
997 if (!use_in_memory_db && DeleteExistingAndCreateNewDatabase())
998 return true;
1000 Disable();
1001 return false;
1004 AppCacheHistograms::CountInitResult(AppCacheHistograms::INIT_OK);
1005 return true;
1008 bool AppCacheDatabase::EnsureDatabaseVersion() {
1009 if (!sql::MetaTable::DoesTableExist(db_.get()))
1010 return CreateSchema();
1012 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
1013 return false;
1015 if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
1016 LOG(WARNING) << "AppCache database is too new.";
1017 return false;
1020 if (meta_table_->GetVersionNumber() < kCurrentVersion)
1021 return UpgradeSchema();
1023 #ifndef NDEBUG
1024 DCHECK(sql::MetaTable::DoesTableExist(db_.get()));
1025 for (int i = 0; i < kTableCount; ++i) {
1026 DCHECK(db_->DoesTableExist(kTables[i].table_name));
1028 for (int i = 0; i < kIndexCount; ++i) {
1029 DCHECK(db_->DoesIndexExist(kIndexes[i].index_name));
1031 #endif
1033 return true;
1036 bool AppCacheDatabase::CreateSchema() {
1037 sql::Transaction transaction(db_.get());
1038 if (!transaction.Begin())
1039 return false;
1041 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
1042 return false;
1044 for (int i = 0; i < kTableCount; ++i) {
1045 if (!CreateTable(db_.get(), kTables[i]))
1046 return false;
1049 for (int i = 0; i < kIndexCount; ++i) {
1050 if (!CreateIndex(db_.get(), kIndexes[i]))
1051 return false;
1054 return transaction.Commit();
1057 bool AppCacheDatabase::UpgradeSchema() {
1058 if (meta_table_->GetVersionNumber() == 3) {
1059 DCHECK_EQ(strcmp(kNamespacesTable, kTables[3].table_name), 0);
1060 DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[6].table_name), 0);
1061 DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[7].table_name), 0);
1062 DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[8].table_name), 0);
1064 // Migrate from the old "FallbackNameSpaces" to the new "Namespaces" table.
1065 sql::Transaction transaction(db_.get());
1066 if (!transaction.Begin() ||
1067 !CreateTable(db_.get(), kTables[3])) {
1068 return false;
1071 // Move data from the old table to the new table, setting the
1072 // 'type' for all current records to the value for FALLBACK_NAMESPACE.
1073 DCHECK_EQ(0, static_cast<int>(FALLBACK_NAMESPACE));
1074 if (!db_->Execute(
1075 "INSERT INTO Namespaces"
1076 " SELECT cache_id, origin, 0, namespace_url, fallback_entry_url"
1077 " FROM FallbackNameSpaces")) {
1078 return false;
1081 // Drop the old table, indexes on that table are also removed by this.
1082 if (!db_->Execute("DROP TABLE FallbackNameSpaces"))
1083 return false;
1085 // Create new indexes.
1086 if (!CreateIndex(db_.get(), kIndexes[6]) ||
1087 !CreateIndex(db_.get(), kIndexes[7]) ||
1088 !CreateIndex(db_.get(), kIndexes[8])) {
1089 return false;
1092 // Finally bump the version numbers and commit it.
1093 meta_table_->SetVersionNumber(4);
1094 meta_table_->SetCompatibleVersionNumber(4);
1095 return transaction.Commit();
1098 // If there is no upgrade path for the version on disk to the current
1099 // version, nuke everything and start over.
1100 return DeleteExistingAndCreateNewDatabase();
1103 void AppCacheDatabase::ResetConnectionAndTables() {
1104 meta_table_.reset();
1105 db_.reset();
1108 bool AppCacheDatabase::DeleteExistingAndCreateNewDatabase() {
1109 DCHECK(!db_file_path_.empty());
1110 DCHECK(file_util::PathExists(db_file_path_));
1111 VLOG(1) << "Deleting existing appcache data and starting over.";
1113 ResetConnectionAndTables();
1115 // This also deletes the disk cache data.
1116 FilePath directory = db_file_path_.DirName();
1117 if (!file_util::Delete(directory, true) ||
1118 !file_util::CreateDirectory(directory)) {
1119 return false;
1122 // Make sure the steps above actually deleted things.
1123 if (file_util::PathExists(db_file_path_))
1124 return false;
1126 // So we can't go recursive.
1127 if (is_recreating_)
1128 return false;
1130 base::AutoReset<bool> auto_reset(&is_recreating_, true);
1131 return LazyOpen(true);
1134 } // namespace appcache