- add sources.
[platform/framework/web/crosswalk.git] / src / webkit / browser / appcache / appcache_database.cc
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.
4
5 #include "webkit/browser/appcache/appcache_database.h"
6
7 #include "base/auto_reset.h"
8 #include "base/command_line.h"
9 #include "base/file_util.h"
10 #include "base/logging.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "sql/connection.h"
13 #include "sql/meta_table.h"
14 #include "sql/statement.h"
15 #include "sql/transaction.h"
16 #include "webkit/browser/appcache/appcache_entry.h"
17 #include "webkit/browser/appcache/appcache_histograms.h"
18
19 namespace appcache {
20
21 // Schema -------------------------------------------------------------------
22 namespace {
23
24 #if defined(APPCACHE_USE_SIMPLE_CACHE)
25 const int kCurrentVersion = 6;
26 const int kCompatibleVersion = 6;
27 #else
28 const int kCurrentVersion = 5;
29 const int kCompatibleVersion = 5;
30 #endif
31
32 // A mechanism to run experiments that may affect in data being persisted
33 // in different ways such that when the experiment is toggled on/off via
34 // cmd line flags, the database gets reset. The active flags are stored at
35 // the time of database creation and compared when reopening. If different
36 // the database is reset.
37 const char kExperimentFlagsKey[] = "ExperimentFlags";
38
39 const char kGroupsTable[] = "Groups";
40 const char kCachesTable[] = "Caches";
41 const char kEntriesTable[] = "Entries";
42 const char kNamespacesTable[] = "Namespaces";
43 const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
44 const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
45
46 struct TableInfo {
47   const char* table_name;
48   const char* columns;
49 };
50
51 struct IndexInfo {
52   const char* index_name;
53   const char* table_name;
54   const char* columns;
55   bool unique;
56 };
57
58 const TableInfo kTables[] = {
59   { kGroupsTable,
60     "(group_id INTEGER PRIMARY KEY,"
61     " origin TEXT,"
62     " manifest_url TEXT,"
63     " creation_time INTEGER,"
64     " last_access_time INTEGER)" },
65
66   { kCachesTable,
67     "(cache_id INTEGER PRIMARY KEY,"
68     " group_id INTEGER,"
69     " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
70     " update_time INTEGER,"
71     " cache_size INTEGER)" },  // intentionally not normalized
72
73   { kEntriesTable,
74     "(cache_id INTEGER,"
75     " url TEXT,"
76     " flags INTEGER,"
77     " response_id INTEGER,"
78     " response_size INTEGER)" },
79
80   { kNamespacesTable,
81     "(cache_id INTEGER,"
82     " origin TEXT,"  // intentionally not normalized
83     " type INTEGER,"
84     " namespace_url TEXT,"
85     " target_url TEXT,"
86     " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" },
87
88   { kOnlineWhiteListsTable,
89     "(cache_id INTEGER,"
90     " namespace_url TEXT,"
91     " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" },
92
93   { kDeletableResponseIdsTable,
94     "(response_id INTEGER NOT NULL)" },
95 };
96
97 const IndexInfo kIndexes[] = {
98   { "GroupsOriginIndex",
99     kGroupsTable,
100     "(origin)",
101     false },
102
103   { "GroupsManifestIndex",
104     kGroupsTable,
105     "(manifest_url)",
106     true },
107
108   { "CachesGroupIndex",
109     kCachesTable,
110     "(group_id)",
111     false },
112
113   { "EntriesCacheIndex",
114     kEntriesTable,
115     "(cache_id)",
116     false },
117
118   { "EntriesCacheAndUrlIndex",
119     kEntriesTable,
120     "(cache_id, url)",
121     true },
122
123   { "EntriesResponseIdIndex",
124     kEntriesTable,
125     "(response_id)",
126     true },
127
128   { "NamespacesCacheIndex",
129     kNamespacesTable,
130     "(cache_id)",
131     false },
132
133   { "NamespacesOriginIndex",
134     kNamespacesTable,
135     "(origin)",
136     false },
137
138   { "NamespacesCacheAndUrlIndex",
139     kNamespacesTable,
140     "(cache_id, namespace_url)",
141     true },
142
143   { "OnlineWhiteListCacheIndex",
144     kOnlineWhiteListsTable,
145     "(cache_id)",
146     false },
147
148   { "DeletableResponsesIdIndex",
149     kDeletableResponseIdsTable,
150     "(response_id)",
151     true },
152 };
153
154 const int kTableCount = ARRAYSIZE_UNSAFE(kTables);
155 const int kIndexCount = ARRAYSIZE_UNSAFE(kIndexes);
156
157 bool CreateTable(sql::Connection* db, const TableInfo& info) {
158   std::string sql("CREATE TABLE ");
159   sql += info.table_name;
160   sql += info.columns;
161   return db->Execute(sql.c_str());
162 }
163
164 bool CreateIndex(sql::Connection* db, const IndexInfo& info) {
165   std::string sql;
166   if (info.unique)
167     sql += "CREATE UNIQUE INDEX ";
168   else
169     sql += "CREATE INDEX ";
170   sql += info.index_name;
171   sql += " ON ";
172   sql += info.table_name;
173   sql += info.columns;
174   return db->Execute(sql.c_str());
175 }
176
177 std::string GetActiveExperimentFlags() {
178   if (CommandLine::ForCurrentProcess()->HasSwitch(kEnableExecutableHandlers))
179     return std::string("executableHandlersEnabled");
180   return std::string();
181 }
182
183 }  // anon namespace
184
185 // AppCacheDatabase ----------------------------------------------------------
186
187 AppCacheDatabase::GroupRecord::GroupRecord()
188     : group_id(0) {
189 }
190
191 AppCacheDatabase::GroupRecord::~GroupRecord() {
192 }
193
194 AppCacheDatabase::NamespaceRecord::NamespaceRecord()
195     : cache_id(0) {
196 }
197
198 AppCacheDatabase::NamespaceRecord::~NamespaceRecord() {
199 }
200
201
202 AppCacheDatabase::AppCacheDatabase(const base::FilePath& path)
203     : db_file_path_(path), is_disabled_(false), is_recreating_(false) {
204 }
205
206 AppCacheDatabase::~AppCacheDatabase() {
207 }
208
209 void AppCacheDatabase::CloseConnection() {
210   // We can't close the connection for an in-memory database w/o
211   // losing all of the data, so we don't do that.
212   if (!db_file_path_.empty())
213     ResetConnectionAndTables();
214 }
215
216 void AppCacheDatabase::Disable() {
217   VLOG(1) << "Disabling appcache database.";
218   is_disabled_ = true;
219   ResetConnectionAndTables();
220 }
221
222 int64 AppCacheDatabase::GetOriginUsage(const GURL& origin) {
223   std::vector<CacheRecord> records;
224   if (!FindCachesForOrigin(origin, &records))
225     return 0;
226
227   int64 origin_usage = 0;
228   std::vector<CacheRecord>::const_iterator iter = records.begin();
229   while (iter != records.end()) {
230     origin_usage += iter->cache_size;
231     ++iter;
232   }
233   return origin_usage;
234 }
235
236 bool AppCacheDatabase::GetAllOriginUsage(std::map<GURL, int64>* usage_map) {
237   std::set<GURL> origins;
238   if (!FindOriginsWithGroups(&origins))
239     return false;
240   for (std::set<GURL>::const_iterator origin = origins.begin();
241        origin != origins.end(); ++origin) {
242     (*usage_map)[*origin] = GetOriginUsage(*origin);
243   }
244   return true;
245 }
246
247 bool AppCacheDatabase::FindOriginsWithGroups(std::set<GURL>* origins) {
248   DCHECK(origins && origins->empty());
249   if (!LazyOpen(false))
250     return false;
251
252   const char* kSql =
253       "SELECT DISTINCT(origin) FROM Groups";
254
255   sql::Statement statement(db_->GetUniqueStatement(kSql));
256
257   while (statement.Step())
258     origins->insert(GURL(statement.ColumnString(0)));
259
260   return statement.Succeeded();
261 }
262
263 bool AppCacheDatabase::FindLastStorageIds(
264     int64* last_group_id, int64* last_cache_id, int64* last_response_id,
265     int64* last_deletable_response_rowid) {
266   DCHECK(last_group_id && last_cache_id && last_response_id &&
267          last_deletable_response_rowid);
268
269   *last_group_id = 0;
270   *last_cache_id = 0;
271   *last_response_id = 0;
272   *last_deletable_response_rowid = 0;
273
274   if (!LazyOpen(false))
275     return false;
276
277   const char* kMaxGroupIdSql = "SELECT MAX(group_id) FROM Groups";
278   const char* kMaxCacheIdSql = "SELECT MAX(cache_id) FROM Caches";
279   const char* kMaxResponseIdFromEntriesSql =
280       "SELECT MAX(response_id) FROM Entries";
281   const char* kMaxResponseIdFromDeletablesSql =
282       "SELECT MAX(response_id) FROM DeletableResponseIds";
283   const char* kMaxDeletableResponseRowIdSql =
284       "SELECT MAX(rowid) FROM DeletableResponseIds";
285   int64 max_group_id;
286   int64 max_cache_id;
287   int64 max_response_id_from_entries;
288   int64 max_response_id_from_deletables;
289   int64 max_deletable_response_rowid;
290   if (!RunUniqueStatementWithInt64Result(kMaxGroupIdSql, &max_group_id) ||
291       !RunUniqueStatementWithInt64Result(kMaxCacheIdSql, &max_cache_id) ||
292       !RunUniqueStatementWithInt64Result(kMaxResponseIdFromEntriesSql,
293                                          &max_response_id_from_entries) ||
294       !RunUniqueStatementWithInt64Result(kMaxResponseIdFromDeletablesSql,
295                                          &max_response_id_from_deletables) ||
296       !RunUniqueStatementWithInt64Result(kMaxDeletableResponseRowIdSql,
297                                          &max_deletable_response_rowid)) {
298     return false;
299   }
300
301   *last_group_id = max_group_id;
302   *last_cache_id = max_cache_id;
303   *last_response_id = std::max(max_response_id_from_entries,
304                                max_response_id_from_deletables);
305   *last_deletable_response_rowid = max_deletable_response_rowid;
306   return true;
307 }
308
309 bool AppCacheDatabase::FindGroup(int64 group_id, GroupRecord* record) {
310   DCHECK(record);
311   if (!LazyOpen(false))
312     return false;
313
314   const char* kSql =
315       "SELECT group_id, origin, manifest_url,"
316       "       creation_time, last_access_time"
317       "  FROM Groups WHERE group_id = ?";
318
319   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
320
321   statement.BindInt64(0, group_id);
322   if (!statement.Step())
323     return false;
324
325   ReadGroupRecord(statement, record);
326   DCHECK(record->group_id == group_id);
327   return true;
328 }
329
330 bool AppCacheDatabase::FindGroupForManifestUrl(
331     const GURL& manifest_url, GroupRecord* record) {
332   DCHECK(record);
333   if (!LazyOpen(false))
334     return false;
335
336   const char* kSql =
337       "SELECT group_id, origin, manifest_url,"
338       "       creation_time, last_access_time"
339       "  FROM Groups WHERE manifest_url = ?";
340
341   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
342   statement.BindString(0, manifest_url.spec());
343
344   if (!statement.Step())
345     return false;
346
347   ReadGroupRecord(statement, record);
348   DCHECK(record->manifest_url == manifest_url);
349   return true;
350 }
351
352 bool AppCacheDatabase::FindGroupsForOrigin(
353     const GURL& origin, std::vector<GroupRecord>* records) {
354   DCHECK(records && records->empty());
355   if (!LazyOpen(false))
356     return false;
357
358   const char* kSql =
359       "SELECT group_id, origin, manifest_url,"
360       "       creation_time, last_access_time"
361       "   FROM Groups WHERE origin = ?";
362
363   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
364   statement.BindString(0, origin.spec());
365
366   while (statement.Step()) {
367     records->push_back(GroupRecord());
368     ReadGroupRecord(statement, &records->back());
369     DCHECK(records->back().origin == origin);
370   }
371
372   return statement.Succeeded();
373 }
374
375 bool AppCacheDatabase::FindGroupForCache(int64 cache_id, GroupRecord* record) {
376   DCHECK(record);
377   if (!LazyOpen(false))
378     return false;
379
380   const char* kSql =
381       "SELECT g.group_id, g.origin, g.manifest_url,"
382       "       g.creation_time, g.last_access_time"
383       "  FROM Groups g, Caches c"
384       "  WHERE c.cache_id = ? AND c.group_id = g.group_id";
385
386   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
387   statement.BindInt64(0, cache_id);
388
389   if (!statement.Step())
390     return false;
391
392   ReadGroupRecord(statement, record);
393   return true;
394 }
395
396 bool AppCacheDatabase::UpdateGroupLastAccessTime(
397     int64 group_id, base::Time time) {
398   if (!LazyOpen(true))
399     return false;
400
401   const char* kSql =
402       "UPDATE Groups SET last_access_time = ? WHERE group_id = ?";
403
404   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
405   statement.BindInt64(0, time.ToInternalValue());
406   statement.BindInt64(1, group_id);
407
408   return statement.Run() && db_->GetLastChangeCount();
409 }
410
411 bool AppCacheDatabase::InsertGroup(const GroupRecord* record) {
412   if (!LazyOpen(true))
413     return false;
414
415   const char* kSql =
416       "INSERT INTO Groups"
417       "  (group_id, origin, manifest_url, creation_time, last_access_time)"
418       "  VALUES(?, ?, ?, ?, ?)";
419
420   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
421   statement.BindInt64(0, record->group_id);
422   statement.BindString(1, record->origin.spec());
423   statement.BindString(2, record->manifest_url.spec());
424   statement.BindInt64(3, record->creation_time.ToInternalValue());
425   statement.BindInt64(4, record->last_access_time.ToInternalValue());
426
427   return statement.Run();
428 }
429
430 bool AppCacheDatabase::DeleteGroup(int64 group_id) {
431   if (!LazyOpen(false))
432     return false;
433
434   const char* kSql =
435       "DELETE FROM Groups WHERE group_id = ?";
436
437   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
438   statement.BindInt64(0, group_id);
439
440   return statement.Run();
441 }
442
443 bool AppCacheDatabase::FindCache(int64 cache_id, CacheRecord* record) {
444   DCHECK(record);
445   if (!LazyOpen(false))
446     return false;
447
448   const char* kSql =
449       "SELECT cache_id, group_id, online_wildcard, update_time, cache_size"
450       " FROM Caches WHERE cache_id = ?";
451
452   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
453   statement.BindInt64(0, cache_id);
454
455   if (!statement.Step())
456     return false;
457
458   ReadCacheRecord(statement, record);
459   return true;
460 }
461
462 bool AppCacheDatabase::FindCacheForGroup(int64 group_id, CacheRecord* record) {
463   DCHECK(record);
464   if (!LazyOpen(false))
465     return false;
466
467   const char* kSql =
468       "SELECT cache_id, group_id, online_wildcard, update_time, cache_size"
469       "  FROM Caches WHERE group_id = ?";
470
471   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
472   statement.BindInt64(0, group_id);
473
474   if (!statement.Step())
475     return false;
476
477   ReadCacheRecord(statement, record);
478   return true;
479 }
480
481 bool AppCacheDatabase::FindCachesForOrigin(
482     const GURL& origin, std::vector<CacheRecord>* records) {
483   DCHECK(records);
484   std::vector<GroupRecord> group_records;
485   if (!FindGroupsForOrigin(origin, &group_records))
486     return false;
487
488   CacheRecord cache_record;
489   std::vector<GroupRecord>::const_iterator iter = group_records.begin();
490   while (iter != group_records.end()) {
491     if (FindCacheForGroup(iter->group_id, &cache_record))
492       records->push_back(cache_record);
493     ++iter;
494   }
495   return true;
496 }
497
498 bool AppCacheDatabase::InsertCache(const CacheRecord* record) {
499   if (!LazyOpen(true))
500     return false;
501
502   const char* kSql =
503       "INSERT INTO Caches (cache_id, group_id, online_wildcard,"
504       "                    update_time, cache_size)"
505       "  VALUES(?, ?, ?, ?, ?)";
506
507   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
508   statement.BindInt64(0, record->cache_id);
509   statement.BindInt64(1, record->group_id);
510   statement.BindBool(2, record->online_wildcard);
511   statement.BindInt64(3, record->update_time.ToInternalValue());
512   statement.BindInt64(4, record->cache_size);
513
514   return statement.Run();
515 }
516
517 bool AppCacheDatabase::DeleteCache(int64 cache_id) {
518   if (!LazyOpen(false))
519     return false;
520
521   const char* kSql =
522       "DELETE FROM Caches WHERE cache_id = ?";
523
524   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
525   statement.BindInt64(0, cache_id);
526
527   return statement.Run();
528 }
529
530 bool AppCacheDatabase::FindEntriesForCache(
531     int64 cache_id, std::vector<EntryRecord>* records) {
532   DCHECK(records && records->empty());
533   if (!LazyOpen(false))
534     return false;
535
536   const char* kSql =
537       "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
538       "  WHERE cache_id = ?";
539
540   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
541   statement.BindInt64(0, cache_id);
542
543   while (statement.Step()) {
544     records->push_back(EntryRecord());
545     ReadEntryRecord(statement, &records->back());
546     DCHECK(records->back().cache_id == cache_id);
547   }
548
549   return statement.Succeeded();
550 }
551
552 bool AppCacheDatabase::FindEntriesForUrl(
553     const GURL& url, std::vector<EntryRecord>* records) {
554   DCHECK(records && records->empty());
555   if (!LazyOpen(false))
556     return false;
557
558   const char* kSql =
559       "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
560       "  WHERE url = ?";
561
562   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
563   statement.BindString(0, url.spec());
564
565   while (statement.Step()) {
566     records->push_back(EntryRecord());
567     ReadEntryRecord(statement, &records->back());
568     DCHECK(records->back().url == url);
569   }
570
571   return statement.Succeeded();
572 }
573
574 bool AppCacheDatabase::FindEntry(
575     int64 cache_id, const GURL& url, EntryRecord* record) {
576   DCHECK(record);
577   if (!LazyOpen(false))
578     return false;
579
580   const char* kSql =
581       "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
582       "  WHERE cache_id = ? AND url = ?";
583
584   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
585   statement.BindInt64(0, cache_id);
586   statement.BindString(1, url.spec());
587
588   if (!statement.Step())
589     return false;
590
591   ReadEntryRecord(statement, record);
592   DCHECK(record->cache_id == cache_id);
593   DCHECK(record->url == url);
594   return true;
595 }
596
597 bool AppCacheDatabase::InsertEntry(const EntryRecord* record) {
598   if (!LazyOpen(true))
599     return false;
600
601   const char* kSql =
602       "INSERT INTO Entries (cache_id, url, flags, response_id, response_size)"
603       "  VALUES(?, ?, ?, ?, ?)";
604
605   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
606   statement.BindInt64(0, record->cache_id);
607   statement.BindString(1, record->url.spec());
608   statement.BindInt(2, record->flags);
609   statement.BindInt64(3, record->response_id);
610   statement.BindInt64(4, record->response_size);
611
612   return statement.Run();
613 }
614
615 bool AppCacheDatabase::InsertEntryRecords(
616     const std::vector<EntryRecord>& records) {
617   if (records.empty())
618     return true;
619   sql::Transaction transaction(db_.get());
620   if (!transaction.Begin())
621     return false;
622   std::vector<EntryRecord>::const_iterator iter = records.begin();
623   while (iter != records.end()) {
624     if (!InsertEntry(&(*iter)))
625       return false;
626     ++iter;
627   }
628   return transaction.Commit();
629 }
630
631 bool AppCacheDatabase::DeleteEntriesForCache(int64 cache_id) {
632   if (!LazyOpen(false))
633     return false;
634
635   const char* kSql =
636       "DELETE FROM Entries WHERE cache_id = ?";
637
638   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
639   statement.BindInt64(0, cache_id);
640
641   return statement.Run();
642 }
643
644 bool AppCacheDatabase::AddEntryFlags(
645     const GURL& entry_url, int64 cache_id, int additional_flags) {
646   if (!LazyOpen(false))
647     return false;
648
649   const char* kSql =
650       "UPDATE Entries SET flags = flags | ? WHERE cache_id = ? AND url = ?";
651
652   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
653   statement.BindInt(0, additional_flags);
654   statement.BindInt64(1, cache_id);
655   statement.BindString(2, entry_url.spec());
656
657   return statement.Run() && db_->GetLastChangeCount();
658 }
659
660 bool AppCacheDatabase::FindNamespacesForOrigin(
661     const GURL& origin,
662     std::vector<NamespaceRecord>* intercepts,
663     std::vector<NamespaceRecord>* fallbacks) {
664   DCHECK(intercepts && intercepts->empty());
665   DCHECK(fallbacks && fallbacks->empty());
666   if (!LazyOpen(false))
667     return false;
668
669   const char* kSql =
670       "SELECT cache_id, origin, type, namespace_url, target_url, is_pattern"
671       "  FROM Namespaces WHERE origin = ?";
672
673   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
674   statement.BindString(0, origin.spec());
675
676   ReadNamespaceRecords(&statement, intercepts, fallbacks);
677
678   return statement.Succeeded();
679 }
680
681 bool AppCacheDatabase::FindNamespacesForCache(
682     int64 cache_id,
683     std::vector<NamespaceRecord>* intercepts,
684     std::vector<NamespaceRecord>* fallbacks) {
685   DCHECK(intercepts && intercepts->empty());
686   DCHECK(fallbacks && fallbacks->empty());
687   if (!LazyOpen(false))
688     return false;
689
690   const char* kSql =
691       "SELECT cache_id, origin, type, namespace_url, target_url, is_pattern"
692       "  FROM Namespaces WHERE cache_id = ?";
693
694   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
695   statement.BindInt64(0, cache_id);
696
697   ReadNamespaceRecords(&statement, intercepts, fallbacks);
698
699   return statement.Succeeded();
700 }
701
702 bool AppCacheDatabase::InsertNamespace(
703     const NamespaceRecord* record) {
704   if (!LazyOpen(true))
705     return false;
706
707   const char* kSql =
708       "INSERT INTO Namespaces"
709       "  (cache_id, origin, type, namespace_url, target_url, is_pattern)"
710       "  VALUES (?, ?, ?, ?, ?, ?)";
711
712   // Note: quick and dirty storage for the 'executable' bit w/o changing
713   // schemas, we use the high bit of 'type' field.
714   int type_with_executable_bit = record->namespace_.type;
715   if (record->namespace_.is_executable) {
716     type_with_executable_bit |= 0x8000000;
717     DCHECK(CommandLine::ForCurrentProcess()->HasSwitch(
718         kEnableExecutableHandlers));
719   }
720
721   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
722   statement.BindInt64(0, record->cache_id);
723   statement.BindString(1, record->origin.spec());
724   statement.BindInt(2, type_with_executable_bit);
725   statement.BindString(3, record->namespace_.namespace_url.spec());
726   statement.BindString(4, record->namespace_.target_url.spec());
727   statement.BindBool(5, record->namespace_.is_pattern);
728   return statement.Run();
729 }
730
731 bool AppCacheDatabase::InsertNamespaceRecords(
732     const std::vector<NamespaceRecord>& records) {
733   if (records.empty())
734     return true;
735   sql::Transaction transaction(db_.get());
736   if (!transaction.Begin())
737     return false;
738   std::vector<NamespaceRecord>::const_iterator iter = records.begin();
739   while (iter != records.end()) {
740     if (!InsertNamespace(&(*iter)))
741       return false;
742     ++iter;
743   }
744   return transaction.Commit();
745 }
746
747 bool AppCacheDatabase::DeleteNamespacesForCache(int64 cache_id) {
748   if (!LazyOpen(false))
749     return false;
750
751   const char* kSql =
752       "DELETE FROM Namespaces WHERE cache_id = ?";
753
754   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
755   statement.BindInt64(0, cache_id);
756
757   return statement.Run();
758 }
759
760 bool AppCacheDatabase::FindOnlineWhiteListForCache(
761     int64 cache_id, std::vector<OnlineWhiteListRecord>* records) {
762   DCHECK(records && records->empty());
763   if (!LazyOpen(false))
764     return false;
765
766   const char* kSql =
767       "SELECT cache_id, namespace_url, is_pattern FROM OnlineWhiteLists"
768       "  WHERE cache_id = ?";
769
770   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
771   statement.BindInt64(0, cache_id);
772
773   while (statement.Step()) {
774     records->push_back(OnlineWhiteListRecord());
775     this->ReadOnlineWhiteListRecord(statement, &records->back());
776     DCHECK(records->back().cache_id == cache_id);
777   }
778   return statement.Succeeded();
779 }
780
781 bool AppCacheDatabase::InsertOnlineWhiteList(
782     const OnlineWhiteListRecord* record) {
783   if (!LazyOpen(true))
784     return false;
785
786   const char* kSql =
787       "INSERT INTO OnlineWhiteLists (cache_id, namespace_url, is_pattern)"
788       "  VALUES (?, ?, ?)";
789
790   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
791   statement.BindInt64(0, record->cache_id);
792   statement.BindString(1, record->namespace_url.spec());
793   statement.BindBool(2, record->is_pattern);
794
795   return statement.Run();
796 }
797
798 bool AppCacheDatabase::InsertOnlineWhiteListRecords(
799     const std::vector<OnlineWhiteListRecord>& records) {
800   if (records.empty())
801     return true;
802   sql::Transaction transaction(db_.get());
803   if (!transaction.Begin())
804     return false;
805   std::vector<OnlineWhiteListRecord>::const_iterator iter = records.begin();
806   while (iter != records.end()) {
807     if (!InsertOnlineWhiteList(&(*iter)))
808       return false;
809     ++iter;
810   }
811   return transaction.Commit();
812 }
813
814 bool AppCacheDatabase::DeleteOnlineWhiteListForCache(int64 cache_id) {
815   if (!LazyOpen(false))
816     return false;
817
818   const char* kSql =
819       "DELETE FROM OnlineWhiteLists WHERE cache_id = ?";
820
821   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
822   statement.BindInt64(0, cache_id);
823
824   return statement.Run();
825 }
826
827 bool AppCacheDatabase::GetDeletableResponseIds(
828     std::vector<int64>* response_ids, int64 max_rowid, int limit) {
829   if (!LazyOpen(false))
830     return false;
831
832   const char* kSql =
833       "SELECT response_id FROM DeletableResponseIds "
834       "  WHERE rowid <= ?"
835       "  LIMIT ?";
836
837   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
838   statement.BindInt64(0, max_rowid);
839   statement.BindInt64(1, limit);
840
841   while (statement.Step())
842     response_ids->push_back(statement.ColumnInt64(0));
843   return statement.Succeeded();
844 }
845
846 bool AppCacheDatabase::InsertDeletableResponseIds(
847     const std::vector<int64>& response_ids) {
848   const char* kSql =
849       "INSERT INTO DeletableResponseIds (response_id) VALUES (?)";
850   return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids);
851 }
852
853 bool AppCacheDatabase::DeleteDeletableResponseIds(
854     const std::vector<int64>& response_ids) {
855   const char* kSql =
856       "DELETE FROM DeletableResponseIds WHERE response_id = ?";
857   return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids);
858 }
859
860 bool AppCacheDatabase::RunCachedStatementWithIds(
861     const sql::StatementID& statement_id, const char* sql,
862     const std::vector<int64>& ids) {
863   DCHECK(sql);
864   if (!LazyOpen(true))
865     return false;
866
867   sql::Transaction transaction(db_.get());
868   if (!transaction.Begin())
869     return false;
870
871   sql::Statement statement(db_->GetCachedStatement(statement_id, sql));
872
873   std::vector<int64>::const_iterator iter = ids.begin();
874   while (iter != ids.end()) {
875     statement.BindInt64(0, *iter);
876     if (!statement.Run())
877       return false;
878     statement.Reset(true);
879     ++iter;
880   }
881
882   return transaction.Commit();
883 }
884
885 bool AppCacheDatabase::RunUniqueStatementWithInt64Result(
886     const char* sql, int64* result) {
887   DCHECK(sql);
888   sql::Statement statement(db_->GetUniqueStatement(sql));
889   if (!statement.Step()) {
890     return false;
891   }
892   *result = statement.ColumnInt64(0);
893   return true;
894 }
895
896 bool AppCacheDatabase::FindResponseIdsForCacheHelper(
897     int64 cache_id, std::vector<int64>* ids_vector,
898     std::set<int64>* ids_set) {
899   DCHECK(ids_vector || ids_set);
900   DCHECK(!(ids_vector && ids_set));
901   if (!LazyOpen(false))
902     return false;
903
904   const char* kSql =
905       "SELECT response_id FROM Entries WHERE cache_id = ?";
906
907   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
908
909   statement.BindInt64(0, cache_id);
910   while (statement.Step()) {
911     int64 id = statement.ColumnInt64(0);
912     if (ids_set)
913       ids_set->insert(id);
914     else
915       ids_vector->push_back(id);
916   }
917
918   return statement.Succeeded();
919 }
920
921 void AppCacheDatabase::ReadGroupRecord(
922     const sql::Statement& statement, GroupRecord* record) {
923   record->group_id = statement.ColumnInt64(0);
924   record->origin = GURL(statement.ColumnString(1));
925   record->manifest_url = GURL(statement.ColumnString(2));
926   record->creation_time =
927       base::Time::FromInternalValue(statement.ColumnInt64(3));
928   record->last_access_time =
929       base::Time::FromInternalValue(statement.ColumnInt64(4));
930 }
931
932 void AppCacheDatabase::ReadCacheRecord(
933     const sql::Statement& statement, CacheRecord* record) {
934   record->cache_id = statement.ColumnInt64(0);
935   record->group_id = statement.ColumnInt64(1);
936   record->online_wildcard = statement.ColumnBool(2);
937   record->update_time =
938       base::Time::FromInternalValue(statement.ColumnInt64(3));
939   record->cache_size = statement.ColumnInt64(4);
940 }
941
942 void AppCacheDatabase::ReadEntryRecord(
943     const sql::Statement& statement, EntryRecord* record) {
944   record->cache_id = statement.ColumnInt64(0);
945   record->url = GURL(statement.ColumnString(1));
946   record->flags = statement.ColumnInt(2);
947   record->response_id = statement.ColumnInt64(3);
948   record->response_size = statement.ColumnInt64(4);
949 }
950
951 void AppCacheDatabase::ReadNamespaceRecords(
952       sql::Statement* statement,
953       NamespaceRecordVector* intercepts,
954       NamespaceRecordVector* fallbacks) {
955   while (statement->Step()) {
956     NamespaceType type = static_cast<NamespaceType>(statement->ColumnInt(2));
957     NamespaceRecordVector* records =
958         (type == FALLBACK_NAMESPACE) ? fallbacks : intercepts;
959     records->push_back(NamespaceRecord());
960     ReadNamespaceRecord(statement, &records->back());
961   }
962 }
963
964 void AppCacheDatabase::ReadNamespaceRecord(
965     const sql::Statement* statement, NamespaceRecord* record) {
966   record->cache_id = statement->ColumnInt64(0);
967   record->origin = GURL(statement->ColumnString(1));
968   int type_with_executable_bit = statement->ColumnInt(2);
969   record->namespace_.namespace_url = GURL(statement->ColumnString(3));
970   record->namespace_.target_url = GURL(statement->ColumnString(4));
971   record->namespace_.is_pattern = statement->ColumnBool(5);
972
973   // Note: quick and dirty storage for the 'executable' bit w/o changing
974   // schemas, we use the high bit of 'type' field.
975   record->namespace_.type = static_cast<NamespaceType>
976       (type_with_executable_bit & 0x7ffffff);
977   record->namespace_.is_executable =
978       (type_with_executable_bit & 0x80000000) != 0;
979   DCHECK(!record->namespace_.is_executable ||
980       CommandLine::ForCurrentProcess()->HasSwitch(kEnableExecutableHandlers));
981 }
982
983 void AppCacheDatabase::ReadOnlineWhiteListRecord(
984     const sql::Statement& statement, OnlineWhiteListRecord* record) {
985   record->cache_id = statement.ColumnInt64(0);
986   record->namespace_url = GURL(statement.ColumnString(1));
987   record->is_pattern = statement.ColumnBool(2);
988 }
989
990 bool AppCacheDatabase::LazyOpen(bool create_if_needed) {
991   if (db_)
992     return true;
993
994   // If we tried and failed once, don't try again in the same session
995   // to avoid creating an incoherent mess on disk.
996   if (is_disabled_)
997     return false;
998
999   // Avoid creating a database at all if we can.
1000   bool use_in_memory_db = db_file_path_.empty();
1001   if (!create_if_needed &&
1002       (use_in_memory_db || !base::PathExists(db_file_path_))) {
1003     return false;
1004   }
1005
1006   db_.reset(new sql::Connection);
1007   meta_table_.reset(new sql::MetaTable);
1008
1009   db_->set_histogram_tag("AppCache");
1010
1011   bool opened = false;
1012   if (use_in_memory_db) {
1013     opened = db_->OpenInMemory();
1014   } else if (!file_util::CreateDirectory(db_file_path_.DirName())) {
1015     LOG(ERROR) << "Failed to create appcache directory.";
1016   } else {
1017     opened = db_->Open(db_file_path_);
1018     if (opened)
1019       db_->Preload();
1020   }
1021
1022   if (!opened || !EnsureDatabaseVersion()) {
1023     LOG(ERROR) << "Failed to open the appcache database.";
1024     AppCacheHistograms::CountInitResult(
1025         AppCacheHistograms::SQL_DATABASE_ERROR);
1026
1027     // We're unable to open the database. This is a fatal error
1028     // which we can't recover from. We try to handle it by deleting
1029     // the existing appcache data and starting with a clean slate in
1030     // this browser session.
1031     if (!use_in_memory_db && DeleteExistingAndCreateNewDatabase())
1032       return true;
1033
1034     Disable();
1035     return false;
1036   }
1037
1038   AppCacheHistograms::CountInitResult(AppCacheHistograms::INIT_OK);
1039   return true;
1040 }
1041
1042 bool AppCacheDatabase::EnsureDatabaseVersion() {
1043   if (!sql::MetaTable::DoesTableExist(db_.get()))
1044     return CreateSchema();
1045
1046   if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
1047     return false;
1048
1049   if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
1050     LOG(WARNING) << "AppCache database is too new.";
1051     return false;
1052   }
1053
1054   std::string stored_flags;
1055   meta_table_->GetValue(kExperimentFlagsKey, &stored_flags);
1056   if (stored_flags != GetActiveExperimentFlags())
1057     return false;
1058
1059   if (meta_table_->GetVersionNumber() < kCurrentVersion)
1060     return UpgradeSchema();
1061
1062 #ifndef NDEBUG
1063   DCHECK(sql::MetaTable::DoesTableExist(db_.get()));
1064   for (int i = 0; i < kTableCount; ++i) {
1065     DCHECK(db_->DoesTableExist(kTables[i].table_name));
1066   }
1067   for (int i = 0; i < kIndexCount; ++i) {
1068     DCHECK(db_->DoesIndexExist(kIndexes[i].index_name));
1069   }
1070 #endif
1071
1072   return true;
1073 }
1074
1075 bool AppCacheDatabase::CreateSchema() {
1076   sql::Transaction transaction(db_.get());
1077   if (!transaction.Begin())
1078     return false;
1079
1080   if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
1081     return false;
1082
1083   if (!meta_table_->SetValue(kExperimentFlagsKey,
1084                              GetActiveExperimentFlags())) {
1085     return false;
1086   }
1087
1088   for (int i = 0; i < kTableCount; ++i) {
1089     if (!CreateTable(db_.get(), kTables[i]))
1090       return false;
1091   }
1092
1093   for (int i = 0; i < kIndexCount; ++i) {
1094     if (!CreateIndex(db_.get(), kIndexes[i]))
1095       return false;
1096   }
1097
1098   return transaction.Commit();
1099 }
1100
1101 bool AppCacheDatabase::UpgradeSchema() {
1102 #if defined(APPCACHE_USE_SIMPLE_CACHE)
1103   return DeleteExistingAndCreateNewDatabase();
1104 #else
1105   if (meta_table_->GetVersionNumber() == 3) {
1106     // version 3 was pre 12/17/2011
1107     DCHECK_EQ(strcmp(kNamespacesTable, kTables[3].table_name), 0);
1108     DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[6].table_name), 0);
1109     DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[7].table_name), 0);
1110     DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[8].table_name), 0);
1111
1112     const TableInfo kNamespaceTable_v4 = {
1113         kNamespacesTable,
1114         "(cache_id INTEGER,"
1115         " origin TEXT,"  // intentionally not normalized
1116         " type INTEGER,"
1117         " namespace_url TEXT,"
1118         " target_url TEXT)"
1119     };
1120
1121     // Migrate from the old FallbackNameSpaces to the newer Namespaces table,
1122     // but without the is_pattern column added in v5.
1123     sql::Transaction transaction(db_.get());
1124     if (!transaction.Begin() ||
1125         !CreateTable(db_.get(), kNamespaceTable_v4)) {
1126       return false;
1127     }
1128
1129     // Move data from the old table to the new table, setting the
1130     // 'type' for all current records to the value for FALLBACK_NAMESPACE.
1131     DCHECK_EQ(0, static_cast<int>(FALLBACK_NAMESPACE));
1132     if (!db_->Execute(
1133             "INSERT INTO Namespaces"
1134             "  SELECT cache_id, origin, 0, namespace_url, fallback_entry_url"
1135             "  FROM FallbackNameSpaces")) {
1136       return false;
1137     }
1138
1139     // Drop the old table, indexes on that table are also removed by this.
1140     if (!db_->Execute("DROP TABLE FallbackNameSpaces"))
1141       return false;
1142
1143     // Create new indexes.
1144     if (!CreateIndex(db_.get(), kIndexes[6]) ||
1145         !CreateIndex(db_.get(), kIndexes[7]) ||
1146         !CreateIndex(db_.get(), kIndexes[8])) {
1147       return false;
1148     }
1149
1150     meta_table_->SetVersionNumber(4);
1151     meta_table_->SetCompatibleVersionNumber(4);
1152     if (!transaction.Commit())
1153       return false;
1154   }
1155
1156   if (meta_table_->GetVersionNumber() == 4) {
1157     // version 4 pre 3/30/2013
1158     // Add the is_pattern column to the Namespaces and OnlineWhitelists tables.
1159     DCHECK_EQ(strcmp(kNamespacesTable, "Namespaces"), 0);
1160     sql::Transaction transaction(db_.get());
1161     if (!transaction.Begin())
1162       return false;
1163     if (!db_->Execute(
1164             "ALTER TABLE Namespaces ADD COLUMN"
1165             "  is_pattern INTEGER CHECK(is_pattern IN (0, 1))")) {
1166       return false;
1167     }
1168     if (!db_->Execute(
1169             "ALTER TABLE OnlineWhitelists ADD COLUMN"
1170             "  is_pattern INTEGER CHECK(is_pattern IN (0, 1))")) {
1171       return false;
1172     }
1173     meta_table_->SetVersionNumber(5);
1174     meta_table_->SetCompatibleVersionNumber(5);
1175     return transaction.Commit();
1176   }
1177
1178   // If there is no upgrade path for the version on disk to the current
1179   // version, nuke everything and start over.
1180   return DeleteExistingAndCreateNewDatabase();
1181 #endif
1182 }
1183
1184 void AppCacheDatabase::ResetConnectionAndTables() {
1185   meta_table_.reset();
1186   db_.reset();
1187 }
1188
1189 bool AppCacheDatabase::DeleteExistingAndCreateNewDatabase() {
1190   DCHECK(!db_file_path_.empty());
1191   DCHECK(base::PathExists(db_file_path_));
1192   VLOG(1) << "Deleting existing appcache data and starting over.";
1193
1194   ResetConnectionAndTables();
1195
1196   // This also deletes the disk cache data.
1197   base::FilePath directory = db_file_path_.DirName();
1198   if (!base::DeleteFile(directory, true) ||
1199       !file_util::CreateDirectory(directory)) {
1200     return false;
1201   }
1202
1203   // Make sure the steps above actually deleted things.
1204   if (base::PathExists(db_file_path_))
1205     return false;
1206
1207   // So we can't go recursive.
1208   if (is_recreating_)
1209     return false;
1210
1211   base::AutoReset<bool> auto_reset(&is_recreating_, true);
1212   return LazyOpen(true);
1213 }
1214
1215 }  // namespace appcache