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 -------------------------------------------------------------------
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";
33 const char* table_name
;
38 const char* index_name
;
39 const char* table_name
;
44 const TableInfo kTables
[] = {
46 "(group_id INTEGER PRIMARY KEY,"
49 " creation_time INTEGER,"
50 " last_access_time INTEGER)" },
53 "(cache_id INTEGER PRIMARY KEY,"
55 " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
56 " update_time INTEGER,"
57 " cache_size INTEGER)" }, // intentionally not normalized
63 " response_id INTEGER,"
64 " response_size INTEGER)" },
68 " origin TEXT," // intentionally not normalized
70 " namespace_url TEXT,"
71 " target_url TEXT)" },
73 { kOnlineWhiteListsTable
,
75 " namespace_url TEXT)" },
77 { kDeletableResponseIdsTable
,
78 "(response_id INTEGER NOT NULL)" },
81 const IndexInfo kIndexes
[] = {
82 { "GroupsOriginIndex",
87 { "GroupsManifestIndex",
97 { "EntriesCacheIndex",
102 { "EntriesCacheAndUrlIndex",
107 { "EntriesResponseIdIndex",
112 { "NamespacesCacheIndex",
117 { "NamespacesOriginIndex",
122 { "NamespacesCacheAndUrlIndex",
124 "(cache_id, namespace_url)",
127 { "OnlineWhiteListCacheIndex",
128 kOnlineWhiteListsTable
,
132 { "DeletableResponsesIdIndex",
133 kDeletableResponseIdsTable
,
138 const int kTableCount
= ARRAYSIZE_UNSAFE(kTables
);
139 const int kIndexCount
= ARRAYSIZE_UNSAFE(kIndexes
);
141 class HistogramUniquifier
{
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
;
154 return db
->Execute(sql
.c_str());
157 bool CreateIndex(sql::Connection
* db
, const IndexInfo
& info
) {
160 sql
+= "CREATE UNIQUE INDEX ";
162 sql
+= "CREATE INDEX ";
163 sql
+= info
.index_name
;
165 sql
+= info
.table_name
;
167 return db
->Execute(sql
.c_str());
172 // AppCacheDatabase ----------------------------------------------------------
175 AppCacheDatabase::GroupRecord::GroupRecord()
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.";
207 ResetConnectionAndTables();
210 int64
AppCacheDatabase::GetOriginUsage(const GURL
& origin
) {
211 std::vector
<CacheRecord
> records
;
212 if (!FindCachesForOrigin(origin
, &records
))
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
;
224 bool AppCacheDatabase::GetAllOriginUsage(std::map
<GURL
, int64
>* usage_map
) {
225 std::set
<GURL
> origins
;
226 if (!FindOriginsWithGroups(&origins
))
228 for (std::set
<GURL
>::const_iterator origin
= origins
.begin();
229 origin
!= origins
.end(); ++origin
) {
230 (*usage_map
)[*origin
] = GetOriginUsage(*origin
);
235 bool AppCacheDatabase::FindOriginsWithGroups(std::set
<GURL
>* origins
) {
236 DCHECK(origins
&& origins
->empty());
237 if (!LazyOpen(false))
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
);
259 *last_response_id
= 0;
260 *last_deletable_response_rowid
= 0;
262 if (!LazyOpen(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";
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
)) {
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
;
297 bool AppCacheDatabase::FindGroup(int64 group_id
, GroupRecord
* record
) {
299 if (!LazyOpen(false))
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())
313 ReadGroupRecord(statement
, record
);
314 DCHECK(record
->group_id
== group_id
);
318 bool AppCacheDatabase::FindGroupForManifestUrl(
319 const GURL
& manifest_url
, GroupRecord
* record
) {
321 if (!LazyOpen(false))
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())
335 ReadGroupRecord(statement
, record
);
336 DCHECK(record
->manifest_url
== manifest_url
);
340 bool AppCacheDatabase::FindGroupsForOrigin(
341 const GURL
& origin
, std::vector
<GroupRecord
>* records
) {
342 DCHECK(records
&& records
->empty());
343 if (!LazyOpen(false))
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
) {
365 if (!LazyOpen(false))
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())
380 ReadGroupRecord(statement
, record
);
384 bool AppCacheDatabase::UpdateGroupLastAccessTime(
385 int64 group_id
, base::Time time
) {
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
) {
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))
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
) {
433 if (!LazyOpen(false))
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())
446 ReadCacheRecord(statement
, record
);
450 bool AppCacheDatabase::FindCacheForGroup(int64 group_id
, CacheRecord
* record
) {
452 if (!LazyOpen(false))
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())
465 ReadCacheRecord(statement
, record
);
469 bool AppCacheDatabase::FindCachesForOrigin(
470 const GURL
& origin
, std::vector
<CacheRecord
>* records
) {
472 std::vector
<GroupRecord
> group_records
;
473 if (!FindGroupsForOrigin(origin
, &group_records
))
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
);
486 bool AppCacheDatabase::InsertCache(const CacheRecord
* record
) {
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))
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))
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))
547 "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
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
) {
565 if (!LazyOpen(false))
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())
579 ReadEntryRecord(statement
, record
);
580 DCHECK(record
->cache_id
== cache_id
);
581 DCHECK(record
->url
== url
);
585 bool AppCacheDatabase::InsertEntry(const EntryRecord
* record
) {
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
) {
607 sql::Transaction
transaction(db_
.get());
608 if (!transaction
.Begin())
610 std::vector
<EntryRecord
>::const_iterator iter
= records
.begin();
611 while (iter
!= records
.end()) {
612 if (!InsertEntry(&(*iter
)))
616 return transaction
.Commit();
619 bool AppCacheDatabase::DeleteEntriesForCache(int64 cache_id
) {
620 if (!LazyOpen(false))
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))
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(
650 std::vector
<NamespaceRecord
>* intercepts
,
651 std::vector
<NamespaceRecord
>* fallbacks
) {
652 DCHECK(intercepts
&& intercepts
->empty());
653 DCHECK(fallbacks
&& fallbacks
->empty());
654 if (!LazyOpen(false))
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(
671 std::vector
<NamespaceRecord
>* intercepts
,
672 std::vector
<NamespaceRecord
>* fallbacks
) {
673 DCHECK(intercepts
&& intercepts
->empty());
674 DCHECK(fallbacks
&& fallbacks
->empty());
675 if (!LazyOpen(false))
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
) {
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
) {
714 sql::Transaction
transaction(db_
.get());
715 if (!transaction
.Begin())
717 std::vector
<NamespaceRecord
>::const_iterator iter
= records
.begin();
718 while (iter
!= records
.end()) {
719 if (!InsertNamespace(&(*iter
)))
723 return transaction
.Commit();
726 bool AppCacheDatabase::DeleteNamespacesForCache(int64 cache_id
) {
727 if (!LazyOpen(false))
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))
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
) {
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
) {
779 sql::Transaction
transaction(db_
.get());
780 if (!transaction
.Begin())
782 std::vector
<OnlineWhiteListRecord
>::const_iterator iter
= records
.begin();
783 while (iter
!= records
.end()) {
784 if (!InsertOnlineWhiteList(&(*iter
)))
788 return transaction
.Commit();
791 bool AppCacheDatabase::DeleteOnlineWhiteListForCache(int64 cache_id
) {
792 if (!LazyOpen(false))
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))
810 "SELECT response_id FROM DeletableResponseIds "
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
) {
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
) {
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
) {
844 sql::Transaction
transaction(db_
.get());
845 if (!transaction
.Begin())
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())
855 statement
.Reset(true);
859 return transaction
.Commit();
862 bool AppCacheDatabase::RunUniqueStatementWithInt64Result(
863 const char* sql
, int64
* result
) {
865 sql::Statement
statement(db_
->GetUniqueStatement(sql
));
866 if (!statement
.Step()) {
869 *result
= statement
.ColumnInt64(0);
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))
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);
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
) {
960 // If we tried and failed once, don't try again in the same session
961 // to avoid creating an incoherent mess on disk.
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_
))) {
972 db_
.reset(new sql::Connection
);
973 meta_table_
.reset(new sql::MetaTable
);
975 db_
->set_error_delegate(GetErrorHandlerForAppCacheDb());
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.";
983 opened
= db_
->Open(db_file_path_
);
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())
1004 AppCacheHistograms::CountInitResult(AppCacheHistograms::INIT_OK
);
1008 bool AppCacheDatabase::EnsureDatabaseVersion() {
1009 if (!sql::MetaTable::DoesTableExist(db_
.get()))
1010 return CreateSchema();
1012 if (!meta_table_
->Init(db_
.get(), kCurrentVersion
, kCompatibleVersion
))
1015 if (meta_table_
->GetCompatibleVersionNumber() > kCurrentVersion
) {
1016 LOG(WARNING
) << "AppCache database is too new.";
1020 if (meta_table_
->GetVersionNumber() < kCurrentVersion
)
1021 return UpgradeSchema();
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
));
1036 bool AppCacheDatabase::CreateSchema() {
1037 sql::Transaction
transaction(db_
.get());
1038 if (!transaction
.Begin())
1041 if (!meta_table_
->Init(db_
.get(), kCurrentVersion
, kCompatibleVersion
))
1044 for (int i
= 0; i
< kTableCount
; ++i
) {
1045 if (!CreateTable(db_
.get(), kTables
[i
]))
1049 for (int i
= 0; i
< kIndexCount
; ++i
) {
1050 if (!CreateIndex(db_
.get(), kIndexes
[i
]))
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])) {
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
));
1075 "INSERT INTO Namespaces"
1076 " SELECT cache_id, origin, 0, namespace_url, fallback_entry_url"
1077 " FROM FallbackNameSpaces")) {
1081 // Drop the old table, indexes on that table are also removed by this.
1082 if (!db_
->Execute("DROP TABLE FallbackNameSpaces"))
1085 // Create new indexes.
1086 if (!CreateIndex(db_
.get(), kIndexes
[6]) ||
1087 !CreateIndex(db_
.get(), kIndexes
[7]) ||
1088 !CreateIndex(db_
.get(), kIndexes
[8])) {
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();
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
)) {
1122 // Make sure the steps above actually deleted things.
1123 if (file_util::PathExists(db_file_path_
))
1126 // So we can't go recursive.
1130 base::AutoReset
<bool> auto_reset(&is_recreating_
, true);
1131 return LazyOpen(true);
1134 } // namespace appcache