Home | History | Annotate | Download | only in history
      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 "chrome/browser/history/in_memory_database.h"
      6 
      7 #include "base/files/file_path.h"
      8 #include "base/logging.h"
      9 #include "base/metrics/histogram.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "base/time/time.h"
     12 #include "build/build_config.h"
     13 
     14 namespace history {
     15 
     16 InMemoryDatabase::InMemoryDatabase() : URLDatabase() {
     17 }
     18 
     19 InMemoryDatabase::~InMemoryDatabase() {
     20 }
     21 
     22 bool InMemoryDatabase::InitDB() {
     23   // Set the database page size to 4K for better performance.
     24   db_.set_page_size(4096);
     25 
     26   if (!db_.OpenInMemory()) {
     27     NOTREACHED() << "Cannot open databse " << GetDB().GetErrorMessage();
     28     return false;
     29   }
     30 
     31   // No reason to leave data behind in memory when rows are removed.
     32   ignore_result(db_.Execute("PRAGMA auto_vacuum=1"));
     33 
     34   // Ensure this is really an in-memory-only cache.
     35   ignore_result(db_.Execute("PRAGMA temp_store=MEMORY"));
     36 
     37   // Create the URL table, but leave it empty for now.
     38   if (!CreateURLTable(false)) {
     39     NOTREACHED() << "Unable to create table";
     40     db_.Close();
     41     return false;
     42   }
     43 
     44   // Create the keyword search terms table.
     45   if (!InitKeywordSearchTermsTable()) {
     46     NOTREACHED() << "Unable to create keyword search terms";
     47     db_.Close();
     48     return false;
     49   }
     50 
     51   return true;
     52 }
     53 
     54 bool InMemoryDatabase::InitFromScratch() {
     55   if (!InitDB())
     56     return false;
     57 
     58   // InitDB doesn't create the index so in the disk-loading case, it can be
     59   // added afterwards.
     60   CreateMainURLIndex();
     61   CreateKeywordSearchTermsIndices();
     62   return true;
     63 }
     64 
     65 bool InMemoryDatabase::InitFromDisk(const base::FilePath& history_name) {
     66   if (!InitDB())
     67     return false;
     68 
     69   // Attach to the history database on disk.  (We can't ATTACH in the middle of
     70   // a transaction.)
     71   sql::Statement attach(GetDB().GetUniqueStatement("ATTACH ? AS history"));
     72 #if defined(OS_POSIX)
     73   attach.BindString(0, history_name.value());
     74 #else
     75   attach.BindString(0, base::WideToUTF8(history_name.value()));
     76 #endif
     77   if (!attach.Run())
     78     return false;
     79 
     80   // Copy URL data to memory.
     81   base::TimeTicks begin_load = base::TimeTicks::Now();
     82   if (!db_.Execute(
     83       "INSERT INTO urls SELECT * FROM history.urls WHERE typed_count > 0")) {
     84     // Unable to get data from the history database. This is OK, the file may
     85     // just not exist yet.
     86   }
     87   base::TimeTicks end_load = base::TimeTicks::Now();
     88   UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBPopulate",
     89                              end_load - begin_load);
     90   UMA_HISTOGRAM_COUNTS("History.InMemoryDBItemCount", db_.GetLastChangeCount());
     91 
     92   {
     93     // This calculation should be fast (since it's on an in-memory DB with
     94     // an average of only 35 rows).
     95     sql::Statement visit_count(db_.GetUniqueStatement(
     96         "SELECT sum(visit_count) FROM urls"));
     97     if (visit_count.Step()) {
     98       UMA_HISTOGRAM_COUNTS("History.InMemoryTypedUrlVisitCount",
     99                            visit_count.ColumnInt(0));
    100     }
    101   }
    102 
    103   // Insert keyword search related URLs.
    104   begin_load = base::TimeTicks::Now();
    105   if (!db_.Execute(
    106       "INSERT OR IGNORE INTO urls SELECT u.id, u.url, u.title, u.visit_count, "
    107       "u.typed_count, u.last_visit_time, u.hidden, u.favicon_id "
    108       "FROM history.urls u JOIN history.keyword_search_terms kst "
    109       "WHERE u.typed_count = 0 AND u.id = kst.url_id")) {
    110     // Unable to get data from the history database. This is OK, the file may
    111     // just not exist yet.
    112   }
    113   end_load = base::TimeTicks::Now();
    114   UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBKeywordURLPopulate",
    115                              end_load - begin_load);
    116   UMA_HISTOGRAM_COUNTS("History.InMemoryDBKeywordURLItemCount",
    117                        db_.GetLastChangeCount());
    118 
    119   // Copy search terms to memory.
    120   begin_load = base::TimeTicks::Now();
    121   if (!db_.Execute(
    122       "INSERT INTO keyword_search_terms SELECT * FROM "
    123       "history.keyword_search_terms")) {
    124     // Unable to get data from the history database. This is OK, the file may
    125     // just not exist yet.
    126   }
    127   end_load = base::TimeTicks::Now();
    128   UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBKeywordTermsPopulate",
    129                              end_load - begin_load);
    130   UMA_HISTOGRAM_COUNTS("History.InMemoryDBKeywordTermsCount",
    131                        db_.GetLastChangeCount());
    132 
    133   // Detach from the history database on disk.
    134   if (!db_.Execute("DETACH history")) {
    135     NOTREACHED() << "Unable to detach from history database.";
    136     return false;
    137   }
    138 
    139   // Index the table, this is faster than creating the index first and then
    140   // inserting into it.
    141   CreateMainURLIndex();
    142   CreateKeywordSearchTermsIndices();
    143 
    144   return true;
    145 }
    146 
    147 sql::Connection& InMemoryDatabase::GetDB() {
    148   return db_;
    149 }
    150 
    151 }  // namespace history
    152