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