// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/webdata/web_database.h"

#include <algorithm>
#include <limits>
#include <set>
#include <string>

#include "app/l10n_util.h"
#include "app/sql/statement.h"
#include "app/sql/transaction.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/tuple.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/autofill_profile.h"
#include "chrome/browser/autofill/autofill_type.h"
#include "chrome/browser/autofill/credit_card.h"
#include "chrome/browser/diagnostics/sqlite_diagnostics.h"
#include "chrome/browser/guid.h"
#include "chrome/browser/history/history_database.h"
#include "chrome/browser/password_manager/encryptor.h"
#include "chrome/browser/search_engines/template_url.h"
#include "chrome/browser/webdata/autofill_change.h"
#include "chrome/common/notification_service.h"
#include "gfx/codec/png_codec.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "webkit/glue/form_field.h"
#include "webkit/glue/password_form.h"

// Encryptor is now in place for Windows and Mac.  The Linux implementation
// currently obfuscates only.  Mac Encryptor implementation can block the
// active thread while presenting UI to the user.  See |encryptor_mac.mm| for
// details.
// For details on the Linux work see:
//   http://crbug.com/25404

using webkit_glue::FormField;
using webkit_glue::PasswordForm;

////////////////////////////////////////////////////////////////////////////////
//
// Schema
//   Note: The database stores time in seconds, UTC.
//
// keywords                 Most of the columns mirror that of a field in
//                          TemplateURL. See TemplateURL for more details.
//   id
//   short_name
//   keyword
//   favicon_url
//   url
//   show_in_default_list
//   safe_for_autoreplace
//   originating_url
//   date_created           This column was added after we allowed keywords.
//                          Keywords created before we started tracking
//                          creation date have a value of 0 for this.
//   usage_count
//   input_encodings        Semicolon separated list of supported input
//                          encodings, may be empty.
//   suggest_url
//   prepopulate_id         See TemplateURL::prepopulate_id.
//   autogenerate_keyword
//   logo_id                See TemplateURL::logo_id
//   created_by_policy      See TemplateURL::created_by_policy.  This was added
//                          in version 26.
//   instant_url            See TemplateURL::instant_url.  This was added
//                          in version 29.
//
// logins
//   origin_url
//   action_url
//   username_element
//   username_value
//   password_element
//   password_value
//   submit_element
//   signon_realm        The authority (scheme, host, port).
//   ssl_valid           SSL status of page containing the form at first
//                       impression.
//   preferred           MRU bit.
//   date_created        This column was added after logins support. "Legacy"
//                       entries have a value of 0.
//   blacklisted_by_user Tracks whether or not the user opted to 'never
//                       remember'
//                       passwords for this site.
//
// autofill
//   name                The name of the input as specified in the html.
//   value               The literal contents of the text field.
//   value_lower         The contents of the text field made lower_case.
//   pair_id             An ID number unique to the row in the table.
//   count               How many times the user has entered the string |value|
//                       in a field of name |name|.
//
// autofill_dates        This table associates a row to each separate time the
//                       user submits a form containing a certain name/value
//                       pair.  The |pair_id| should match the |pair_id| field
//                       in the appropriate row of the autofill table.
//   pair_id
//   date_created
//
// autofill_profiles    This table contains AutoFill profile data added by the
//                      user with the AutoFill dialog.  Most of the columns are
//                      standard entries in a contact information form.
//
//   guid               A guid string to uniquely identify the profile.
//                      Added in version 31.
//   label              The label of the profile.  Presented to the user when
//                      selecting profiles.
//   first_name
//   middle_name
//   last_name
//   email
//   company_name
//   address_line_1
//   address_line_2
//   city
//   state
//   zipcode
//   country
//   phone
//   fax
//   date_modified      The date on which this profile was last modified.
//                      Added in version 30.
//
// credit_cards         This table contains credit card data added by the user
//                      with the AutoFill dialog.  Most of the columns are
//                      standard entries in a credit card form.
//
//   guid               A guid string to uniquely identify the profile.
//                      Added in version 31.
//   label              The label of the credit card.  Presented to the user
//                      when selecting credit cards.
//   name_on_card
//   expiration_month
//   expiration_year
//   card_number_encrypted Stores encrypted credit card number.
//   date_modified      The date on which this entry was last modified.
//                      Added in version 30.
//
// web_app_icons
//   url         URL of the web app.
//   width       Width of the image.
//   height      Height of the image.
//   image       PNG encoded image data.
//
// web_apps
//   url                 URL of the web app.
//   has_all_images      Do we have all the images?
//
////////////////////////////////////////////////////////////////////////////////

using base::Time;

namespace {

typedef std::vector<Tuple3<int64, string16, string16> > AutofillElementList;

// Current version number.  Note: when changing the current version number,
// corresponding changes must happen in the unit tests, and new migration test
// added.  See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
const int kCurrentVersionNumber = 32;
const int kCompatibleVersionNumber = 32;

// ID of the url column in keywords.
const int kUrlIdPosition = 16;

// Keys used in the meta table.
const char* kDefaultSearchProviderKey = "Default Search Provider ID";
const char* kBuiltinKeywordVersion = "Builtin Keyword Version";

// The maximum length allowed for form data.
const size_t kMaxDataLength = 1024;

void BindURLToStatement(const TemplateURL& url, sql::Statement* s) {
  s->BindString(0, WideToUTF8(url.short_name()));
  s->BindString(1, WideToUTF8(url.keyword()));
  GURL favicon_url = url.GetFavIconURL();
  if (!favicon_url.is_valid()) {
    s->BindString(2, std::string());
  } else {
    s->BindString(2, history::HistoryDatabase::GURLToDatabaseURL(
                       url.GetFavIconURL()));
  }
  s->BindString(3, url.url() ? url.url()->url() : std::string());
  s->BindInt(4, url.safe_for_autoreplace() ? 1 : 0);
  if (!url.originating_url().is_valid()) {
    s->BindString(5, std::string());
  } else {
    s->BindString(5, history::HistoryDatabase::GURLToDatabaseURL(
        url.originating_url()));
  }
  s->BindInt64(6, url.date_created().ToTimeT());
  s->BindInt(7, url.usage_count());
  s->BindString(8, JoinString(url.input_encodings(), ';'));
  s->BindInt(9, url.show_in_default_list() ? 1 : 0);
  s->BindString(10, url.suggestions_url() ? url.suggestions_url()->url() :
                std::string());
  s->BindInt(11, url.prepopulate_id());
  s->BindInt(12, url.autogenerate_keyword() ? 1 : 0);
  s->BindInt(13, url.logo_id());
  s->BindBool(14, url.created_by_policy());
  s->BindString(15, url.instant_url() ? url.instant_url()->url() :
                std::string());
}

void InitPasswordFormFromStatement(PasswordForm* form, sql::Statement* s) {
  std::string tmp;
  string16 decrypted_password;
  tmp = s->ColumnString(0);
  form->origin = GURL(tmp);
  tmp = s->ColumnString(1);
  form->action = GURL(tmp);
  form->username_element = s->ColumnString16(2);
  form->username_value = s->ColumnString16(3);
  form->password_element = s->ColumnString16(4);

  int encrypted_password_len = s->ColumnByteLength(5);
  std::string encrypted_password;
  if (encrypted_password_len) {
    encrypted_password.resize(encrypted_password_len);
    memcpy(&encrypted_password[0], s->ColumnBlob(5), encrypted_password_len);
    Encryptor::DecryptString16(encrypted_password, &decrypted_password);
  }

  form->password_value = decrypted_password;
  form->submit_element = s->ColumnString16(6);
  tmp = s->ColumnString(7);
  form->signon_realm = tmp;
  form->ssl_valid = (s->ColumnInt(8) > 0);
  form->preferred = (s->ColumnInt(9) > 0);
  form->date_created = Time::FromTimeT(s->ColumnInt64(10));
  form->blacklisted_by_user = (s->ColumnInt(11) > 0);
  int scheme_int = s->ColumnInt(12);
  DCHECK((scheme_int >= 0) && (scheme_int <= PasswordForm::SCHEME_OTHER));
  form->scheme = static_cast<PasswordForm::Scheme>(scheme_int);
}

// TODO(jhawkins): This is a temporary stop-gap measure designed to prevent
// a malicious site from DOS'ing the browser with extremely large profile
// data.  The correct solution is to parse this data asynchronously.
// See http://crbug.com/49332.
string16 LimitDataSize(const string16& data) {
  if (data.size() > kMaxDataLength)
    return data.substr(0, kMaxDataLength);

  return data;
}

void BindAutoFillProfileToStatement(const AutoFillProfile& profile,
                                    sql::Statement* s) {
  DCHECK(guid::IsValidGUID(profile.guid()));
  s->BindString(0, profile.guid());
  s->BindString16(1, profile.Label());

  string16 text = profile.GetFieldText(AutoFillType(NAME_FIRST));
  s->BindString16(2, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(NAME_MIDDLE));
  s->BindString16(3, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(NAME_LAST));
  s->BindString16(4, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(EMAIL_ADDRESS));
  s->BindString16(5, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(COMPANY_NAME));
  s->BindString16(6, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1));
  s->BindString16(7, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2));
  s->BindString16(8, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_CITY));
  s->BindString16(9, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_STATE));
  s->BindString16(10, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_ZIP));
  s->BindString16(11, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(ADDRESS_HOME_COUNTRY));
  s->BindString16(12, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(PHONE_HOME_WHOLE_NUMBER));
  s->BindString16(13, LimitDataSize(text));
  text = profile.GetFieldText(AutoFillType(PHONE_FAX_WHOLE_NUMBER));
  s->BindString16(14, LimitDataSize(text));
  s->BindInt64(15, Time::Now().ToTimeT());
}

AutoFillProfile* AutoFillProfileFromStatement(const sql::Statement& s) {
  AutoFillProfile* profile = new AutoFillProfile;
  profile->set_guid(s.ColumnString(0));
  DCHECK(guid::IsValidGUID(profile->guid()));
  profile->set_label(s.ColumnString16(1));

  profile->SetInfo(AutoFillType(NAME_FIRST),
                   s.ColumnString16(2));
  profile->SetInfo(AutoFillType(NAME_MIDDLE),
                   s.ColumnString16(3));
  profile->SetInfo(AutoFillType(NAME_LAST),
                   s.ColumnString16(4));
  profile->SetInfo(AutoFillType(EMAIL_ADDRESS),
                   s.ColumnString16(5));
  profile->SetInfo(AutoFillType(COMPANY_NAME),
                   s.ColumnString16(6));
  profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE1),
                   s.ColumnString16(7));
  profile->SetInfo(AutoFillType(ADDRESS_HOME_LINE2),
                   s.ColumnString16(8));
  profile->SetInfo(AutoFillType(ADDRESS_HOME_CITY),
                   s.ColumnString16(9));
  profile->SetInfo(AutoFillType(ADDRESS_HOME_STATE),
                   s.ColumnString16(10));
  profile->SetInfo(AutoFillType(ADDRESS_HOME_ZIP),
                   s.ColumnString16(11));
  profile->SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY),
                   s.ColumnString16(12));
  profile->SetInfo(AutoFillType(PHONE_HOME_WHOLE_NUMBER),
                   s.ColumnString16(13));
  profile->SetInfo(AutoFillType(PHONE_FAX_WHOLE_NUMBER),
                   s.ColumnString16(14));
  // Intentionally skip column 15, which stores the profile's modification date.

  return profile;
}

void BindCreditCardToStatement(const CreditCard& credit_card,
                               sql::Statement* s) {
  DCHECK(guid::IsValidGUID(credit_card.guid()));
  s->BindString(0, credit_card.guid());
  s->BindString16(1, credit_card.Label());

  string16 text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NAME));
  s->BindString16(2, LimitDataSize(text));
  text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_EXP_MONTH));
  s->BindString16(3, LimitDataSize(text));
  text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR));
  s->BindString16(4, LimitDataSize(text));
  text = credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NUMBER));
  std::string encrypted_data;
  Encryptor::EncryptString16(text, &encrypted_data);
  s->BindBlob(5, encrypted_data.data(),
              static_cast<int>(encrypted_data.length()));
  s->BindInt64(6, Time::Now().ToTimeT());
}

CreditCard* CreditCardFromStatement(const sql::Statement& s) {
  CreditCard* credit_card = new CreditCard;

  credit_card->set_guid(s.ColumnString(0));
  DCHECK(guid::IsValidGUID(credit_card->guid()));
  credit_card->set_label(s.ColumnString16(1));

  credit_card->SetInfo(AutoFillType(CREDIT_CARD_NAME),
                       s.ColumnString16(2));
  credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH),
                       s.ColumnString16(3));
  credit_card->SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR),
                       s.ColumnString16(4));
  int encrypted_number_len = s.ColumnByteLength(5);
  string16 credit_card_number;
  if (encrypted_number_len) {
    std::string encrypted_number;
    encrypted_number.resize(encrypted_number_len);
    memcpy(&encrypted_number[0], s.ColumnBlob(5), encrypted_number_len);
    Encryptor::DecryptString16(encrypted_number, &credit_card_number);
  }
  credit_card->SetInfo(AutoFillType(CREDIT_CARD_NUMBER), credit_card_number);
  // Intentionally skip column 6, which stores the modification date.

  return credit_card;
}

}  // namespace

WebDatabase::WebDatabase() {
}

WebDatabase::~WebDatabase() {
}

void WebDatabase::BeginTransaction() {
  db_.BeginTransaction();
}

void WebDatabase::CommitTransaction() {
  db_.CommitTransaction();
}

sql::InitStatus WebDatabase::Init(const FilePath& db_name) {
  // When running in unit tests, there is already a NotificationService object.
  // Since only one can exist at a time per thread, check first.
  if (!NotificationService::current())
    notification_service_.reset(new NotificationService);

  // Set the exceptional sqlite error handler.
  db_.set_error_delegate(GetErrorHandlerForWebDb());

  // We don't store that much data in the tables so use a small page size.
  // This provides a large benefit for empty tables (which is very likely with
  // the tables we create).
  db_.set_page_size(2048);

  // We shouldn't have much data and what access we currently have is quite
  // infrequent. So we go with a small cache size.
  db_.set_cache_size(32);

  // Run the database in exclusive mode. Nobody else should be accessing the
  // database while we're running, and this will give somewhat improved perf.
  db_.set_exclusive_locking();

  if (!db_.Open(db_name))
    return sql::INIT_FAILURE;

  // Initialize various tables
  sql::Transaction transaction(&db_);
  if (!transaction.Begin())
    return sql::INIT_FAILURE;

  // Version check.
  if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber))
    return sql::INIT_FAILURE;
  if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
    LOG(WARNING) << "Web database is too new.";
    return sql::INIT_TOO_NEW;
  }

  // Initialize the tables.
  if (!InitKeywordsTable() || !InitLoginsTable() || !InitWebAppIconsTable() ||
      !InitWebAppsTable() || !InitAutofillTable() ||
      !InitAutofillDatesTable() || !InitAutoFillProfilesTable() ||
      !InitCreditCardsTable() || !InitTokenServiceTable()) {
    LOG(WARNING) << "Unable to initialize the web database.";
    return sql::INIT_FAILURE;
  }

  // If the file on disk is an older database version, bring it up to date.
  // If the migration fails we return an error to caller and do not commit
  // the migration.
  sql::InitStatus migration_status = MigrateOldVersionsAsNeeded();
  if (migration_status != sql::INIT_OK)
    return migration_status;

  return transaction.Commit() ? sql::INIT_OK : sql::INIT_FAILURE;
}

bool WebDatabase::SetWebAppImage(const GURL& url, const SkBitmap& image) {
  // Don't bother with a cached statement since this will be a relatively
  // infrequent operation.
  sql::Statement s(db_.GetUniqueStatement(
      "INSERT OR REPLACE INTO web_app_icons "
      "(url, width, height, image) VALUES (?, ?, ?, ?)"));
  if (!s)
    return false;

  std::vector<unsigned char> image_data;
  gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data);

  s.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url));
  s.BindInt(1, image.width());
  s.BindInt(2, image.height());
  s.BindBlob(3, &image_data.front(), static_cast<int>(image_data.size()));
  return s.Run();
}

bool WebDatabase::GetWebAppImages(const GURL& url,
                                  std::vector<SkBitmap>* images) {
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT image FROM web_app_icons WHERE url=?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  s.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url));
  while (s.Step()) {
    SkBitmap image;
    int col_bytes = s.ColumnByteLength(0);
    if (col_bytes > 0) {
      if (gfx::PNGCodec::Decode(
              reinterpret_cast<const unsigned char*>(s.ColumnBlob(0)),
              col_bytes, &image)) {
        images->push_back(image);
      } else {
        // Should only have valid image data in the db.
        NOTREACHED();
      }
    }
  }
  return true;
}

bool WebDatabase::SetWebAppHasAllImages(const GURL& url,
                                        bool has_all_images) {
  sql::Statement s(db_.GetUniqueStatement(
      "INSERT OR REPLACE INTO web_apps (url, has_all_images) VALUES (?, ?)"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  s.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url));
  s.BindInt(1, has_all_images ? 1 : 0);
  return s.Run();
}

bool WebDatabase::GetWebAppHasAllImages(const GURL& url) {
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT has_all_images FROM web_apps WHERE url=?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  s.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url));
  return (s.Step() && s.ColumnInt(0) == 1);
}

bool WebDatabase::RemoveWebApp(const GURL& url) {
  sql::Statement delete_s(db_.GetUniqueStatement(
      "DELETE FROM web_app_icons WHERE url = ?"));
  if (!delete_s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  delete_s.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url));
  if (!delete_s.Run())
    return false;

  sql::Statement delete_s2(db_.GetUniqueStatement(
      "DELETE FROM web_apps WHERE url = ?"));
  if (!delete_s2) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  delete_s2.BindString(0, history::HistoryDatabase::GURLToDatabaseURL(url));
  return delete_s2.Run();
}

bool WebDatabase::RemoveAllTokens() {
  sql::Statement s(db_.GetUniqueStatement(
      "DELETE FROM token_service"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  return s.Run();
}

bool WebDatabase::SetTokenForService(const std::string& service,
                                     const std::string& token) {
  // Don't bother with a cached statement since this will be a relatively
  // infrequent operation.
  sql::Statement s(db_.GetUniqueStatement(
      "INSERT OR REPLACE INTO token_service "
      "(service, encrypted_token) VALUES (?, ?)"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  std::string encrypted_token;

  bool encrypted = Encryptor::EncryptString(token, &encrypted_token);
  if (!encrypted) {
    return false;
  }

  s.BindString(0, service);
  s.BindBlob(1, encrypted_token.data(),
             static_cast<int>(encrypted_token.length()));
  return s.Run();
}

bool WebDatabase::GetAllTokens(std::map<std::string, std::string>* tokens) {
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT service, encrypted_token FROM token_service"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  while (s.Step()) {
    std::string encrypted_token;
    std::string decrypted_token;
    std::string service;
    service = s.ColumnString(0);
    bool entry_ok = !service.empty() &&
                    s.ColumnBlobAsString(1, &encrypted_token);
    if (entry_ok) {
      Encryptor::DecryptString(encrypted_token, &decrypted_token);
      (*tokens)[service] = decrypted_token;
    } else {
      NOTREACHED();
      return false;
    }
  }
  return true;
}

bool WebDatabase::InitKeywordsTable() {
  if (!db_.DoesTableExist("keywords")) {
    if (!db_.Execute("CREATE TABLE keywords ("
                     "id INTEGER PRIMARY KEY,"
                     "short_name VARCHAR NOT NULL,"
                     "keyword VARCHAR NOT NULL,"
                     "favicon_url VARCHAR NOT NULL,"
                     "url VARCHAR NOT NULL,"
                     "show_in_default_list INTEGER,"
                     "safe_for_autoreplace INTEGER,"
                     "originating_url VARCHAR,"
                     "date_created INTEGER DEFAULT 0,"
                     "usage_count INTEGER DEFAULT 0,"
                     "input_encodings VARCHAR,"
                     "suggest_url VARCHAR,"
                     "prepopulate_id INTEGER DEFAULT 0,"
                     "autogenerate_keyword INTEGER DEFAULT 0,"
                     "logo_id INTEGER DEFAULT 0,"
                     "created_by_policy INTEGER DEFAULT 0,"
                     "instant_url VARCHAR)")) {
      NOTREACHED();
      return false;
    }
  }
  return true;
}

bool WebDatabase::InitLoginsTable() {
  if (!db_.DoesTableExist("logins")) {
    if (!db_.Execute("CREATE TABLE logins ("
                     "origin_url VARCHAR NOT NULL, "
                     "action_url VARCHAR, "
                     "username_element VARCHAR, "
                     "username_value VARCHAR, "
                     "password_element VARCHAR, "
                     "password_value BLOB, "
                     "submit_element VARCHAR, "
                     "signon_realm VARCHAR NOT NULL,"
                     "ssl_valid INTEGER NOT NULL,"
                     "preferred INTEGER NOT NULL,"
                     "date_created INTEGER NOT NULL,"
                     "blacklisted_by_user INTEGER NOT NULL,"
                     "scheme INTEGER NOT NULL,"
                     "UNIQUE "
                     "(origin_url, username_element, "
                     "username_value, password_element, "
                     "submit_element, signon_realm))")) {
      NOTREACHED();
      return false;
    }
    if (!db_.Execute("CREATE INDEX logins_signon ON logins (signon_realm)")) {
      NOTREACHED();
      return false;
    }
  }

#if defined(OS_WIN)
  if (!db_.DoesTableExist("ie7_logins")) {
    if (!db_.Execute("CREATE TABLE ie7_logins ("
                     "url_hash VARCHAR NOT NULL, "
                     "password_value BLOB, "
                     "date_created INTEGER NOT NULL,"
                     "UNIQUE "
                     "(url_hash))")) {
      NOTREACHED();
      return false;
    }
    if (!db_.Execute("CREATE INDEX ie7_logins_hash ON "
                     "ie7_logins (url_hash)")) {
      NOTREACHED();
      return false;
    }
  }
#endif

  return true;
}

bool WebDatabase::InitAutofillTable() {
  if (!db_.DoesTableExist("autofill")) {
    if (!db_.Execute("CREATE TABLE autofill ("
                     "name VARCHAR, "
                     "value VARCHAR, "
                     "value_lower VARCHAR, "
                     "pair_id INTEGER PRIMARY KEY, "
                     "count INTEGER DEFAULT 1)")) {
      NOTREACHED();
      return false;
    }
    if (!db_.Execute("CREATE INDEX autofill_name ON autofill (name)")) {
       NOTREACHED();
       return false;
    }
    if (!db_.Execute("CREATE INDEX autofill_name_value_lower ON "
                     "autofill (name, value_lower)")) {
       NOTREACHED();
       return false;
    }
  }
  return true;
}

bool WebDatabase::InitAutofillDatesTable() {
  if (!db_.DoesTableExist("autofill_dates")) {
    if (!db_.Execute("CREATE TABLE autofill_dates ( "
                     "pair_id INTEGER DEFAULT 0, "
                     "date_created INTEGER DEFAULT 0)")) {
      NOTREACHED();
      return false;
    }
    if (!db_.Execute("CREATE INDEX autofill_dates_pair_id ON "
                     "autofill_dates (pair_id)")) {
      NOTREACHED();
      return false;
    }
  }
  return true;
}

bool WebDatabase::InitAutoFillProfilesTable() {
  if (!db_.DoesTableExist("autofill_profiles")) {
    if (!db_.Execute("CREATE TABLE autofill_profiles ( "
                     "guid VARCHAR PRIMARY KEY, "
                     "label VARCHAR, "
                     "first_name VARCHAR, "
                     "middle_name VARCHAR, "
                     "last_name VARCHAR, "
                     "email VARCHAR, "
                     "company_name VARCHAR, "
                     "address_line_1 VARCHAR, "
                     "address_line_2 VARCHAR, "
                     "city VARCHAR, "
                     "state VARCHAR, "
                     "zipcode VARCHAR, "
                     "country VARCHAR, "
                     "phone VARCHAR, "
                     "fax VARCHAR, "
                     "date_modified INTEGER NOT NULL DEFAULT 0)")) {
      NOTREACHED();
      return false;
    }
    if (!db_.Execute("CREATE INDEX autofill_profiles_label_index "
                     "ON autofill_profiles (label)")) {
      NOTREACHED();
      return false;
    }
  }
  return true;
}

bool WebDatabase::InitCreditCardsTable() {
  if (!db_.DoesTableExist("credit_cards")) {
    if (!db_.Execute("CREATE TABLE credit_cards ( "
                     "guid VARCHAR PRIMARY KEY, "
                     "label VARCHAR, "
                     "name_on_card VARCHAR, "
                     "expiration_month INTEGER, "
                     "expiration_year INTEGER, "
                     "card_number_encrypted BLOB, "
                     "date_modified INTEGER NOT NULL DEFAULT 0)")) {
      NOTREACHED();
      return false;
    }
    if (!db_.Execute("CREATE INDEX credit_cards_label_index "
                     "ON credit_cards (label)")) {
      NOTREACHED();
      return false;
    }
  }

  return true;
}

bool WebDatabase::InitWebAppIconsTable() {
  if (!db_.DoesTableExist("web_app_icons")) {
    if (!db_.Execute("CREATE TABLE web_app_icons ("
                     "url LONGVARCHAR,"
                     "width int,"
                     "height int,"
                     "image BLOB, UNIQUE (url, width, height))")) {
      NOTREACHED();
      return false;
    }
  }
  return true;
}

bool WebDatabase::InitWebAppsTable() {
  if (!db_.DoesTableExist("web_apps")) {
    if (!db_.Execute("CREATE TABLE web_apps ("
                     "url LONGVARCHAR UNIQUE,"
                     "has_all_images INTEGER NOT NULL)")) {
      NOTREACHED();
      return false;
    }
    if (!db_.Execute("CREATE INDEX web_apps_url_index ON web_apps (url)")) {
      NOTREACHED();
      return false;
    }
  }
  return true;
}

bool WebDatabase::InitTokenServiceTable() {
  if (!db_.DoesTableExist("token_service")) {
    if (!db_.Execute("CREATE TABLE token_service ("
                     "service VARCHAR PRIMARY KEY NOT NULL,"
                     "encrypted_token BLOB)")) {
      NOTREACHED();
      return false;
    }
  }
  return true;
}

bool WebDatabase::AddKeyword(const TemplateURL& url) {
  DCHECK(url.id());
  // Be sure to change kUrlIdPosition if you add columns
  sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
      "INSERT INTO keywords "
      "(short_name, keyword, favicon_url, url, safe_for_autoreplace, "
      "originating_url, date_created, usage_count, input_encodings, "
      "show_in_default_list, suggest_url, prepopulate_id, "
      "autogenerate_keyword, logo_id, created_by_policy, instant_url, "
      "id) VALUES "
      "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  BindURLToStatement(url, &s);
  s.BindInt64(kUrlIdPosition, url.id());
  if (!s.Run()) {
    NOTREACHED();
    return false;
  }
  return true;
}

bool WebDatabase::RemoveKeyword(TemplateURLID id) {
  DCHECK(id);
  sql::Statement s(db_.GetUniqueStatement("DELETE FROM keywords WHERE id = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  s.BindInt64(0, id);
  return s.Run();
}

bool WebDatabase::GetKeywords(std::vector<TemplateURL*>* urls) {
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT id, short_name, keyword, favicon_url, url, "
      "safe_for_autoreplace, originating_url, date_created, "
      "usage_count, input_encodings, show_in_default_list, "
      "suggest_url, prepopulate_id, autogenerate_keyword, logo_id, "
      "created_by_policy, instant_url "
      "FROM keywords ORDER BY id ASC"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  while (s.Step()) {
    TemplateURL* template_url = new TemplateURL();
    template_url->set_id(s.ColumnInt64(0));

    std::string tmp;
    tmp = s.ColumnString(1);
    DCHECK(!tmp.empty());
    template_url->set_short_name(UTF8ToWide(tmp));

    template_url->set_keyword(UTF8ToWide(s.ColumnString(2)));

    tmp = s.ColumnString(3);
    if (!tmp.empty())
      template_url->SetFavIconURL(GURL(tmp));

    template_url->SetURL(s.ColumnString(4), 0, 0);

    template_url->set_safe_for_autoreplace(s.ColumnInt(5) == 1);

    tmp = s.ColumnString(6);
    if (!tmp.empty())
      template_url->set_originating_url(GURL(tmp));

    template_url->set_date_created(Time::FromTimeT(s.ColumnInt64(7)));

    template_url->set_usage_count(s.ColumnInt(8));

    std::vector<std::string> encodings;
    base::SplitString(s.ColumnString(9), ';', &encodings);
    template_url->set_input_encodings(encodings);

    template_url->set_show_in_default_list(s.ColumnInt(10) == 1);

    template_url->SetSuggestionsURL(s.ColumnString(11), 0, 0);

    template_url->set_prepopulate_id(s.ColumnInt(12));

    template_url->set_autogenerate_keyword(s.ColumnInt(13) == 1);

    template_url->set_logo_id(s.ColumnInt(14));

    template_url->set_created_by_policy(s.ColumnBool(15));

    template_url->SetInstantURL(s.ColumnString(16), 0, 0);

    urls->push_back(template_url);
  }
  return s.Succeeded();
}

bool WebDatabase::UpdateKeyword(const TemplateURL& url) {
  DCHECK(url.id());
  // Be sure to change kUrlIdPosition if you add columns
  sql::Statement s(db_.GetUniqueStatement(
      "UPDATE keywords "
      "SET short_name=?, keyword=?, favicon_url=?, url=?, "
      "safe_for_autoreplace=?, originating_url=?, date_created=?, "
      "usage_count=?, input_encodings=?, show_in_default_list=?, "
      "suggest_url=?, prepopulate_id=?, autogenerate_keyword=?, "
      "logo_id=?, created_by_policy=?, instant_url=? WHERE id=?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  BindURLToStatement(url, &s);
  s.BindInt64(kUrlIdPosition, url.id());
  return s.Run();
}

bool WebDatabase::SetDefaultSearchProviderID(int64 id) {
  return meta_table_.SetValue(kDefaultSearchProviderKey, id);
}

int64 WebDatabase::GetDefaulSearchProviderID() {
  int64 value = 0;
  meta_table_.GetValue(kDefaultSearchProviderKey, &value);
  return value;
}

bool WebDatabase::SetBuitinKeywordVersion(int version) {
  return meta_table_.SetValue(kBuiltinKeywordVersion, version);
}

int WebDatabase::GetBuitinKeywordVersion() {
  int version = 0;
  meta_table_.GetValue(kBuiltinKeywordVersion, &version);
  return version;
}

bool WebDatabase::AddLogin(const PasswordForm& form) {
  sql::Statement s(db_.GetUniqueStatement(
      "INSERT OR REPLACE INTO logins "
      "(origin_url, action_url, username_element, username_value, "
      " password_element, password_value, submit_element, "
      " signon_realm, ssl_valid, preferred, date_created, "
      " blacklisted_by_user, scheme) "
      "VALUES "
      "(?,?,?,?,?,?,?,?,?,?,?,?,?)"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  std::string encrypted_password;
  s.BindString(0, form.origin.spec());
  s.BindString(1, form.action.spec());
  s.BindString16(2, form.username_element);
  s.BindString16(3, form.username_value);
  s.BindString16(4, form.password_element);
  Encryptor::EncryptString16(form.password_value, &encrypted_password);
  s.BindBlob(5, encrypted_password.data(),
             static_cast<int>(encrypted_password.length()));
  s.BindString16(6, form.submit_element);
  s.BindString(7, form.signon_realm);
  s.BindInt(8, form.ssl_valid);
  s.BindInt(9, form.preferred);
  s.BindInt64(10, form.date_created.ToTimeT());
  s.BindInt(11, form.blacklisted_by_user);
  s.BindInt(12, form.scheme);
  if (!s.Run()) {
    NOTREACHED();
    return false;
  }
  return true;
}

bool WebDatabase::UpdateLogin(const PasswordForm& form) {
  sql::Statement s(db_.GetUniqueStatement(
      "UPDATE logins SET "
      "action_url = ?, "
      "password_value = ?, "
      "ssl_valid = ?, "
      "preferred = ? "
      "WHERE origin_url = ? AND "
      "username_element = ? AND "
      "username_value = ? AND "
      "password_element = ? AND "
      "signon_realm = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString(0, form.action.spec());
  std::string encrypted_password;
  Encryptor::EncryptString16(form.password_value, &encrypted_password);
  s.BindBlob(1, encrypted_password.data(),
             static_cast<int>(encrypted_password.length()));
  s.BindInt(2, form.ssl_valid);
  s.BindInt(3, form.preferred);
  s.BindString(4, form.origin.spec());
  s.BindString16(5, form.username_element);
  s.BindString16(6, form.username_value);
  s.BindString16(7, form.password_element);
  s.BindString(8, form.signon_realm);

  if (!s.Run()) {
    NOTREACHED();
    return false;
  }
  return true;
}

bool WebDatabase::RemoveLogin(const PasswordForm& form) {
  // Remove a login by UNIQUE-constrained fields.
  sql::Statement s(db_.GetUniqueStatement(
      "DELETE FROM logins WHERE "
      "origin_url = ? AND "
      "username_element = ? AND "
      "username_value = ? AND "
      "password_element = ? AND "
      "submit_element = ? AND "
      "signon_realm = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  s.BindString(0, form.origin.spec());
  s.BindString16(1, form.username_element);
  s.BindString16(2, form.username_value);
  s.BindString16(3, form.password_element);
  s.BindString16(4, form.submit_element);
  s.BindString(5, form.signon_realm);

  if (!s.Run()) {
    NOTREACHED();
    return false;
  }
  return true;
}

bool WebDatabase::RemoveLoginsCreatedBetween(base::Time delete_begin,
                                             base::Time delete_end) {
  sql::Statement s1(db_.GetUniqueStatement(
      "DELETE FROM logins WHERE "
      "date_created >= ? AND date_created < ?"));
  if (!s1) {
    NOTREACHED() << "Statement 1 prepare failed";
    return false;
  }
  s1.BindInt64(0, delete_begin.ToTimeT());
  s1.BindInt64(1,
               delete_end.is_null() ?
                   std::numeric_limits<int64>::max() :
                   delete_end.ToTimeT());
  bool success = s1.Run();

#if defined(OS_WIN)
  sql::Statement s2(db_.GetUniqueStatement(
      "DELETE FROM ie7_logins WHERE date_created >= ? AND date_created < ?"));
  if (!s2) {
    NOTREACHED() << "Statement 2 prepare failed";
    return false;
  }
  s2.BindInt64(0, delete_begin.ToTimeT());
  s2.BindInt64(1,
               delete_end.is_null() ?
                   std::numeric_limits<int64>::max() :
                   delete_end.ToTimeT());
  success = success && s2.Run();
#endif

  return success;
}

bool WebDatabase::GetLogins(const PasswordForm& form,
                            std::vector<PasswordForm*>* forms) {
  DCHECK(forms);
  sql::Statement s(db_.GetUniqueStatement(
                "SELECT origin_url, action_url, "
                "username_element, username_value, "
                "password_element, password_value, "
                "submit_element, signon_realm, "
                "ssl_valid, preferred, "
                "date_created, blacklisted_by_user, scheme FROM logins "
                "WHERE signon_realm == ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString(0, form.signon_realm);

  while (s.Step()) {
    PasswordForm* new_form = new PasswordForm();
    InitPasswordFormFromStatement(new_form, &s);

    forms->push_back(new_form);
  }
  return s.Succeeded();
}

bool WebDatabase::GetAllLogins(std::vector<PasswordForm*>* forms,
                               bool include_blacklisted) {
  DCHECK(forms);
  std::string stmt = "SELECT origin_url, action_url, "
                     "username_element, username_value, "
                     "password_element, password_value, "
                     "submit_element, signon_realm, ssl_valid, preferred, "
                     "date_created, blacklisted_by_user, scheme FROM logins ";
  if (!include_blacklisted)
    stmt.append("WHERE blacklisted_by_user == 0 ");
  stmt.append("ORDER BY origin_url");

  sql::Statement s(db_.GetUniqueStatement(stmt.c_str()));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  while (s.Step()) {
    PasswordForm* new_form = new PasswordForm();
    InitPasswordFormFromStatement(new_form, &s);

    forms->push_back(new_form);
  }
  return s.Succeeded();
}

bool WebDatabase::AddFormFieldValues(const std::vector<FormField>& elements,
                                     std::vector<AutofillChange>* changes) {
  return AddFormFieldValuesTime(elements, changes, Time::Now());
}

bool WebDatabase::AddFormFieldValuesTime(const std::vector<FormField>& elements,
                                         std::vector<AutofillChange>* changes,
                                         base::Time time) {
  // Only add one new entry for each unique element name.  Use |seen_names| to
  // track this.  Add up to |kMaximumUniqueNames| unique entries per form.
  const size_t kMaximumUniqueNames = 256;
  std::set<string16> seen_names;
  bool result = true;
  for (std::vector<FormField>::const_iterator
       itr = elements.begin();
       itr != elements.end();
       itr++) {
    if (seen_names.size() >= kMaximumUniqueNames)
      break;
    if (seen_names.find(itr->name()) != seen_names.end())
      continue;
    result = result && AddFormFieldValueTime(*itr, changes, time);
    seen_names.insert(itr->name());
  }
  return result;
}

bool WebDatabase::ClearAutofillEmptyValueElements() {
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT pair_id FROM autofill WHERE TRIM(value)= \"\""));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  std::set<int64> ids;
  while (s.Step())
    ids.insert(s.ColumnInt64(0));

  bool success = true;
  for (std::set<int64>::const_iterator iter = ids.begin(); iter != ids.end();
       ++iter) {
    if (!RemoveFormElementForID(*iter))
      success = false;
  }

  return success;
}

bool WebDatabase::GetIDAndCountOfFormElement(
    const FormField& element,
    int64* pair_id,
    int* count) {
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT pair_id, count FROM autofill "
      "WHERE name = ? AND value = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString16(0, element.name());
  s.BindString16(1, element.value());

  *count = 0;

  if (s.Step()) {
    *pair_id = s.ColumnInt64(0);
    *count = s.ColumnInt(1);
  }

  return true;
}

bool WebDatabase::GetCountOfFormElement(int64 pair_id, int* count) {
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT count FROM autofill WHERE pair_id = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindInt64(0, pair_id);

  if (s.Step()) {
    *count = s.ColumnInt(0);
    return true;
  }
  return false;
}

bool WebDatabase::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) {
  DCHECK(entries);
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT name, value, date_created FROM autofill a JOIN "
      "autofill_dates ad ON a.pair_id=ad.pair_id"));

  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  bool first_entry = true;
  AutofillKey* current_key_ptr = NULL;
  std::vector<base::Time>* timestamps_ptr = NULL;
  string16 name, value;
  base::Time time;
  while (s.Step()) {
    name = s.ColumnString16(0);
    value = s.ColumnString16(1);
    time = Time::FromTimeT(s.ColumnInt64(2));

    if (first_entry) {
      current_key_ptr = new AutofillKey(name, value);

      timestamps_ptr = new std::vector<base::Time>;
      timestamps_ptr->push_back(time);

      first_entry = false;
    } else {
      // we've encountered the next entry
      if (current_key_ptr->name().compare(name) != 0 ||
          current_key_ptr->value().compare(value) != 0) {
        AutofillEntry entry(*current_key_ptr, *timestamps_ptr);
        entries->push_back(entry);

        delete current_key_ptr;
        delete timestamps_ptr;

        current_key_ptr = new AutofillKey(name, value);
        timestamps_ptr = new std::vector<base::Time>;
      }
      timestamps_ptr->push_back(time);
    }
  }
  // If there is at least one result returned, first_entry will be false.
  // For this case we need to do a final cleanup step.
  if (!first_entry) {
    AutofillEntry entry(*current_key_ptr, *timestamps_ptr);
    entries->push_back(entry);
    delete current_key_ptr;
    delete timestamps_ptr;
  }

  return s.Succeeded();
}

bool WebDatabase::GetAutofillTimestamps(const string16& name,
                                        const string16& value,
                                        std::vector<base::Time>* timestamps) {
  DCHECK(timestamps);
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT date_created FROM autofill a JOIN "
      "autofill_dates ad ON a.pair_id=ad.pair_id "
      "WHERE a.name = ? AND a.value = ?"));

  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString16(0, name);
  s.BindString16(1, value);
  while (s.Step()) {
    timestamps->push_back(Time::FromTimeT(s.ColumnInt64(0)));
  }

  return s.Succeeded();
}

bool WebDatabase::UpdateAutofillEntries(
    const std::vector<AutofillEntry>& entries) {
  if (!entries.size())
    return true;

  // Remove all existing entries.
  for (size_t i = 0; i < entries.size(); i++) {
    std::string sql = "SELECT pair_id FROM autofill "
                      "WHERE name = ? AND value = ?";
    sql::Statement s(db_.GetUniqueStatement(sql.c_str()));
    if (!s.is_valid()) {
      NOTREACHED() << "Statement prepare failed";
      return false;
    }

    s.BindString16(0, entries[i].key().name());
    s.BindString16(1, entries[i].key().value());
    if (s.Step()) {
      if (!RemoveFormElementForID(s.ColumnInt64(0)))
        return false;
    }
  }

  // Insert all the supplied autofill entries.
  for (size_t i = 0; i < entries.size(); i++) {
    if (!InsertAutofillEntry(entries[i]))
      return false;
  }

  return true;
}

bool WebDatabase::InsertAutofillEntry(const AutofillEntry& entry) {
  std::string sql = "INSERT INTO autofill (name, value, value_lower, count) "
                    "VALUES (?, ?, ?, ?)";
  sql::Statement s(db_.GetUniqueStatement(sql.c_str()));
  if (!s.is_valid()) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString16(0, entry.key().name());
  s.BindString16(1, entry.key().value());
  s.BindString16(2, l10n_util::ToLower(entry.key().value()));
  s.BindInt(3, entry.timestamps().size());

  if (!s.Run()) {
    NOTREACHED();
    return false;
  }

  int64 pair_id = db_.GetLastInsertRowId();
  for (size_t i = 0; i < entry.timestamps().size(); i++) {
    if (!InsertPairIDAndDate(pair_id, entry.timestamps()[i]))
      return false;
  }

  return true;
}

bool WebDatabase::InsertFormElement(const FormField& element,
                                    int64* pair_id) {
  sql::Statement s(db_.GetUniqueStatement(
      "INSERT INTO autofill (name, value, value_lower) VALUES (?,?,?)"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString16(0, element.name());
  s.BindString16(1, element.value());
  s.BindString16(2, l10n_util::ToLower(element.value()));

  if (!s.Run()) {
    NOTREACHED();
    return false;
  }

  *pair_id = db_.GetLastInsertRowId();
  return true;
}

bool WebDatabase::InsertPairIDAndDate(int64 pair_id,
                                      base::Time date_created) {
  sql::Statement s(db_.GetUniqueStatement(
      "INSERT INTO autofill_dates "
      "(pair_id, date_created) VALUES (?, ?)"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindInt64(0, pair_id);
  s.BindInt64(1, date_created.ToTimeT());

  if (!s.Run()) {
    NOTREACHED();
    return false;
  }

  return true;
}

bool WebDatabase::SetCountOfFormElement(int64 pair_id, int count) {
  sql::Statement s(db_.GetUniqueStatement(
      "UPDATE autofill SET count = ? WHERE pair_id = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindInt(0, count);
  s.BindInt64(1, pair_id);
  if (!s.Run()) {
    NOTREACHED();
    return false;
  }

  return true;
}

bool WebDatabase::AddFormFieldValue(const FormField& element,
                                    std::vector<AutofillChange>* changes) {
  return AddFormFieldValueTime(element, changes, base::Time::Now());
}

bool WebDatabase::AddFormFieldValueTime(const FormField& element,
                                        std::vector<AutofillChange>* changes,
                                        base::Time time) {
  int count = 0;
  int64 pair_id;

  if (!GetIDAndCountOfFormElement(element, &pair_id, &count))
    return false;

  if (count == 0 && !InsertFormElement(element, &pair_id))
    return false;

  if (!SetCountOfFormElement(pair_id, count + 1))
    return false;

  if (!InsertPairIDAndDate(pair_id, time))
    return false;

  AutofillChange::Type change_type =
      count == 0 ? AutofillChange::ADD : AutofillChange::UPDATE;
  changes->push_back(
      AutofillChange(change_type,
                     AutofillKey(element.name(), element.value())));
  return true;
}

bool WebDatabase::GetFormValuesForElementName(const string16& name,
                                              const string16& prefix,
                                              std::vector<string16>* values,
                                              int limit) {
  DCHECK(values);
  sql::Statement s;

  if (prefix.empty()) {
    s.Assign(db_.GetUniqueStatement(
        "SELECT value FROM autofill "
        "WHERE name = ? "
        "ORDER BY count DESC "
        "LIMIT ?"));
    if (!s) {
      NOTREACHED() << "Statement prepare failed";
      return false;
    }

    s.BindString16(0, name);
    s.BindInt(1, limit);
  } else {
    string16 prefix_lower = l10n_util::ToLower(prefix);
    string16 next_prefix = prefix_lower;
    next_prefix[next_prefix.length() - 1]++;

    s.Assign(db_.GetUniqueStatement(
        "SELECT value FROM autofill "
        "WHERE name = ? AND "
        "value_lower >= ? AND "
        "value_lower < ? "
        "ORDER BY count DESC "
        "LIMIT ?"));
    if (!s) {
      NOTREACHED() << "Statement prepare failed";
      return false;
    }

    s.BindString16(0, name);
    s.BindString16(1, prefix_lower);
    s.BindString16(2, next_prefix);
    s.BindInt(3, limit);
  }

  values->clear();
  while (s.Step())
    values->push_back(s.ColumnString16(0));
  return s.Succeeded();
}

bool WebDatabase::RemoveFormElementsAddedBetween(
    base::Time delete_begin,
    base::Time delete_end,
    std::vector<AutofillChange>* changes) {
  DCHECK(changes);
  // Query for the pair_id, name, and value of all form elements that
  // were used between the given times.
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT DISTINCT a.pair_id, a.name, a.value "
      "FROM autofill_dates ad JOIN autofill a ON ad.pair_id = a.pair_id "
      "WHERE ad.date_created >= ? AND ad.date_created < ?"));
  if (!s) {
    NOTREACHED() << "Statement 1 prepare failed";
    return false;
  }
  s.BindInt64(0, delete_begin.ToTimeT());
  s.BindInt64(1,
              delete_end.is_null() ?
                  std::numeric_limits<int64>::max() :
                  delete_end.ToTimeT());

  AutofillElementList elements;
  while (s.Step()) {
    elements.push_back(MakeTuple(s.ColumnInt64(0),
                                 s.ColumnString16(1),
                                 s.ColumnString16(2)));
  }

  if (!s.Succeeded()) {
    NOTREACHED();
    return false;
  }

  for (AutofillElementList::iterator itr = elements.begin();
       itr != elements.end(); itr++) {
    int how_many = 0;
    if (!RemoveFormElementForTimeRange(itr->a, delete_begin, delete_end,
                                       &how_many)) {
      return false;
    }
    bool was_removed = false;
    if (!AddToCountOfFormElement(itr->a, -how_many, &was_removed))
      return false;
    AutofillChange::Type change_type =
        was_removed ? AutofillChange::REMOVE : AutofillChange::UPDATE;
    changes->push_back(AutofillChange(change_type,
                                      AutofillKey(itr->b, itr->c)));
  }

  return true;
}

bool WebDatabase::RemoveFormElementForTimeRange(int64 pair_id,
                                                const Time delete_begin,
                                                const Time delete_end,
                                                int* how_many) {
  sql::Statement s(db_.GetUniqueStatement(
      "DELETE FROM autofill_dates WHERE pair_id = ? AND "
      "date_created >= ? AND date_created < ?"));
  if (!s) {
    NOTREACHED() << "Statement 1 prepare failed";
    return false;
  }
  s.BindInt64(0, pair_id);
  s.BindInt64(1, delete_begin.is_null() ? 0 : delete_begin.ToTimeT());
  s.BindInt64(2, delete_end.is_null() ? std::numeric_limits<int64>::max() :
                                        delete_end.ToTimeT());

  bool result = s.Run();
  if (how_many)
    *how_many = db_.GetLastChangeCount();

  return result;
}

bool WebDatabase::RemoveFormElement(const string16& name,
                                    const string16& value) {
  // Find the id for that pair.
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT pair_id FROM autofill WHERE  name = ? AND value= ?"));
  if (!s) {
    NOTREACHED() << "Statement 1 prepare failed";
    return false;
  }
  s.BindString16(0, name);
  s.BindString16(1, value);

  if (s.Step())
    return RemoveFormElementForID(s.ColumnInt64(0));
  return false;
}

bool WebDatabase::AddAutoFillProfile(const AutoFillProfile& profile) {
  sql::Statement s(db_.GetUniqueStatement(
      "INSERT INTO autofill_profiles"
      "(guid, label, first_name, middle_name, last_name, email,"
      " company_name, address_line_1, address_line_2, city, state, zipcode,"
      " country, phone, fax, date_modified)"
      "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  BindAutoFillProfileToStatement(profile, &s);

  if (!s.Run()) {
    NOTREACHED();
    return false;
  }

  return s.Succeeded();
}

bool WebDatabase::GetAutoFillProfileForLabel(const string16& label,
                                             AutoFillProfile** profile) {
  DCHECK(profile);
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT guid, label, first_name, middle_name, last_name, email, "
      "company_name, address_line_1, address_line_2, city, state, zipcode, "
      "country, phone, fax, date_modified "
      "FROM autofill_profiles "
      "WHERE label = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString16(0, label);
  if (!s.Step())
    return false;

  *profile = AutoFillProfileFromStatement(s);

  return s.Succeeded();
}

bool WebDatabase::GetAutoFillProfileForGUID(const std::string& guid,
                                            AutoFillProfile** profile) {
  DCHECK(guid::IsValidGUID(guid));
  DCHECK(profile);
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT guid, label, first_name, middle_name, last_name, email, "
      "company_name, address_line_1, address_line_2, city, state, zipcode, "
      "country, phone, fax, date_modified "
      "FROM autofill_profiles "
      "WHERE guid = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString(0, guid);
  if (!s.Step())
    return false;

  *profile = AutoFillProfileFromStatement(s);

  return s.Succeeded();
}

bool WebDatabase::GetAutoFillProfiles(
    std::vector<AutoFillProfile*>* profiles) {
  DCHECK(profiles);
  profiles->clear();

  sql::Statement s(db_.GetUniqueStatement(
      "SELECT guid, label, first_name, middle_name, last_name, email, "
      "company_name, address_line_1, address_line_2, city, state, zipcode, "
      "country, phone, fax, date_modified "
      "FROM autofill_profiles"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  while (s.Step())
    profiles->push_back(AutoFillProfileFromStatement(s));

  return s.Succeeded();
}

bool WebDatabase::UpdateAutoFillProfile(const AutoFillProfile& profile) {
  DCHECK(guid::IsValidGUID(profile.guid()));
  sql::Statement s(db_.GetUniqueStatement(
      "UPDATE autofill_profiles "
      "SET guid=?, label=?, first_name=?, middle_name=?, last_name=?, "
      "    email=?, company_name=?, address_line_1=?, address_line_2=?, "
      "    city=?, state=?, zipcode=?, country=?, phone=?, fax=?, "
      "    date_modified=? "
      "WHERE guid=?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  BindAutoFillProfileToStatement(profile, &s);
  s.BindString(16, profile.guid());
  bool result = s.Run();
  DCHECK_GT(db_.GetLastChangeCount(), 0);
  return result;
}

bool WebDatabase::RemoveAutoFillProfile(const std::string& guid) {
  DCHECK(guid::IsValidGUID(guid));
  sql::Statement s(db_.GetUniqueStatement(
      "DELETE FROM autofill_profiles WHERE guid = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString(0, guid);
  return s.Run();
}

bool WebDatabase::AddCreditCard(const CreditCard& credit_card) {
  sql::Statement s(db_.GetUniqueStatement(
      "INSERT INTO credit_cards"
      "(guid, label, name_on_card, expiration_month, expiration_year, "
      "card_number_encrypted, date_modified)"
      "VALUES (?,?,?,?,?,?,?)"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  BindCreditCardToStatement(credit_card, &s);

  if (!s.Run()) {
    NOTREACHED();
    return false;
  }

  DCHECK_GT(db_.GetLastChangeCount(), 0);
  return s.Succeeded();
}

bool WebDatabase::GetCreditCardForLabel(const string16& label,
                                        CreditCard** credit_card) {
  DCHECK(credit_card);
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT guid, label, name_on_card, expiration_month, expiration_year, "
      "card_number_encrypted, date_modified "
      "FROM credit_cards "
      "WHERE label = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString16(0, label);
  if (!s.Step())
    return false;

  *credit_card = CreditCardFromStatement(s);

  return s.Succeeded();
}

bool WebDatabase::GetCreditCardForGUID(const std::string& guid,
                                       CreditCard** credit_card) {
  DCHECK(guid::IsValidGUID(guid));
  sql::Statement s(db_.GetUniqueStatement(
      "SELECT guid, label, name_on_card, expiration_month, expiration_year, "
      "card_number_encrypted, date_modified "
      "FROM credit_cards "
      "WHERE guid = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString(0, guid);
  if (!s.Step())
    return false;

  *credit_card = CreditCardFromStatement(s);

  return s.Succeeded();
}

bool WebDatabase::GetCreditCards(
    std::vector<CreditCard*>* credit_cards) {
  DCHECK(credit_cards);
  credit_cards->clear();

  sql::Statement s(db_.GetUniqueStatement(
      "SELECT guid, label, name_on_card, expiration_month, expiration_year, "
      "card_number_encrypted, date_modified "
      "FROM credit_cards"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  while (s.Step())
    credit_cards->push_back(CreditCardFromStatement(s));

  return s.Succeeded();
}

bool WebDatabase::UpdateCreditCard(const CreditCard& credit_card) {
  DCHECK(guid::IsValidGUID(credit_card.guid()));
  sql::Statement s(db_.GetUniqueStatement(
      "UPDATE credit_cards "
      "SET guid=?, label=?, name_on_card=?, expiration_month=?, "
      "    expiration_year=?, card_number_encrypted=?, date_modified=? "
      "WHERE guid=?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  BindCreditCardToStatement(credit_card, &s);
  s.BindString(7, credit_card.guid());
  bool result = s.Run();
  DCHECK_GT(db_.GetLastChangeCount(), 0);
  return result;
}

bool WebDatabase::RemoveCreditCard(const std::string& guid) {
  DCHECK(guid::IsValidGUID(guid));
  sql::Statement s(db_.GetUniqueStatement(
      "DELETE FROM credit_cards WHERE guid = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }

  s.BindString(0, guid);
  return s.Run();
}

bool WebDatabase::RemoveAutoFillProfilesAndCreditCardsModifiedBetween(
    base::Time delete_begin,
    base::Time delete_end) {
  DCHECK(delete_end.is_null() || delete_begin < delete_end);

  time_t delete_begin_t = delete_begin.ToTimeT();
  time_t delete_end_t = delete_end.is_null() ?
      std::numeric_limits<time_t>::max() :
      delete_end.ToTimeT();

  // Remove AutoFill profiles in the time range.
  sql::Statement s_profiles(db_.GetUniqueStatement(
      "DELETE FROM autofill_profiles "
      "WHERE date_modified >= ? AND date_modified < ?"));
  if (!s_profiles) {
    NOTREACHED() << "AutoFill profiles statement prepare failed";
    return false;
  }

  s_profiles.BindInt64(0, delete_begin_t);
  s_profiles.BindInt64(1, delete_end_t);
  s_profiles.Run();

  if (!s_profiles.Succeeded()) {
    NOTREACHED();
    return false;
  }

  // Remove AutoFill profiles in the time range.
  sql::Statement s_credit_cards(db_.GetUniqueStatement(
      "DELETE FROM credit_cards "
      "WHERE date_modified >= ? AND date_modified < ?"));
  if (!s_credit_cards) {
    NOTREACHED() << "AutoFill credit cards statement prepare failed";
    return false;
  }

  s_credit_cards.BindInt64(0, delete_begin_t);
  s_credit_cards.BindInt64(1, delete_end_t);
  s_credit_cards.Run();

  if (!s_credit_cards.Succeeded()) {
    NOTREACHED();
    return false;
  }

  return true;
}

bool WebDatabase::AddToCountOfFormElement(int64 pair_id,
                                          int delta,
                                          bool* was_removed) {
  DCHECK(was_removed);
  int count = 0;
  *was_removed = false;

  if (!GetCountOfFormElement(pair_id, &count))
    return false;

  if (count + delta == 0) {
    if (!RemoveFormElementForID(pair_id))
      return false;
    *was_removed = true;
  } else {
    if (!SetCountOfFormElement(pair_id, count + delta))
      return false;
  }
  return true;
}

bool WebDatabase::RemoveFormElementForID(int64 pair_id) {
  sql::Statement s(db_.GetUniqueStatement(
      "DELETE FROM autofill WHERE pair_id = ?"));
  if (!s) {
    NOTREACHED() << "Statement prepare failed";
    return false;
  }
  s.BindInt64(0, pair_id);
  if (s.Run()) {
    return RemoveFormElementForTimeRange(pair_id, base::Time(), base::Time(),
                                         NULL);
  }
  return false;
}

sql::InitStatus WebDatabase::MigrateOldVersionsAsNeeded(){
  // Migrate if necessary.
  int current_version = meta_table_.GetVersionNumber();
  switch (current_version) {
    // Versions 1 - 19 are unhandled.  Version numbers greater than
    // kCurrentVersionNumber should have already been weeded out by the caller.
    default:
      // When the version is too old, we return failure error code.  The schema
      // is too out of date to migrate.
      // There should not be a released product that makes a database too old to
      // migrate. If we do encounter such a legacy database, we will need a
      // better solution to handle it (i.e., pop up a dialog to tell the user,
      // erase all their prefs and start over, etc.).
      LOG(WARNING) << "Web database version " << current_version <<
          " is too old to handle.";
      NOTREACHED();
      return sql::INIT_FAILURE;

    case 20:
      // Add the autogenerate_keyword column.
      if (!db_.Execute("ALTER TABLE keywords ADD COLUMN autogenerate_keyword "
                       "INTEGER DEFAULT 0")) {
        LOG(WARNING) << "Unable to update web database to version 21.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }
      meta_table_.SetVersionNumber(21);
      meta_table_.SetCompatibleVersionNumber(
          std::min(21, kCompatibleVersionNumber));
      // FALL THROUGH

    case 21:
      if (!ClearAutofillEmptyValueElements()) {
        LOG(WARNING) << "Failed to clean-up autofill DB.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }
      meta_table_.SetVersionNumber(22);
      // No change in the compatibility version number.

      // FALL THROUGH

    case 22:
      // Add the card_number_encrypted column if credit card table was not
      // created in this build (otherwise the column already exists).
      // WARNING: Do not change the order of the execution of the SQL
      // statements in this case!  Profile corruption and data migration
      // issues WILL OCCUR. (see http://crbug.com/10913)
      //
      // The problem is that if a user has a profile which was created before
      // r37036, when the credit_cards table was added, and then failed to
      // update this profile between the credit card addition and the addition
      // of the "encrypted" columns (44963), the next data migration will put
      // the user's profile in an incoherent state: The user will update from
      // a data profile set to be earlier than 22, and therefore pass through
      // this update case.  But because the user did not have a credit_cards
      // table before starting Chrome, it will have just been initialized
      // above, and so already have these columns -- and thus this data
      // update step will have failed.
      //
      // The false assumption in this case is that at this step in the
      // migration, the user has a credit card table, and that this
      // table does not include encrypted columns!
      // Because this case does not roll back the complete set of SQL
      // transactions properly in case of failure (that is, it does not
      // roll back the table initialization done above), the incoherent
      // profile will now see itself as being at version 22 -- but include a
      // fully initialized credit_cards table.  Every time Chrome runs, it
      // will try to update the web database and fail at this step, unless
      // we allow for the faulty assumption described above by checking for
      // the existence of the columns only AFTER we've executed the commands
      // to add them.
      if (!db_.DoesColumnExist("credit_cards", "card_number_encrypted")) {
        if (!db_.Execute("ALTER TABLE credit_cards ADD COLUMN "
                         "card_number_encrypted BLOB DEFAULT NULL")) {
          LOG(WARNING) << "Could not add card_number_encrypted to "
                          "credit_cards table.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }
      }

      if (!db_.DoesColumnExist("credit_cards", "verification_code_encrypted")) {
        if (!db_.Execute("ALTER TABLE credit_cards ADD COLUMN "
                         "verification_code_encrypted BLOB DEFAULT NULL")) {
          LOG(WARNING) << "Could not add verification_code_encrypted to "
                          "credit_cards table.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }
      }
      meta_table_.SetVersionNumber(23);
      // FALL THROUGH

    case 23: {
      // One-time cleanup for Chromium bug 38364.  In the presence of
      // multi-byte UTF-8 characters, that bug could cause AutoFill strings
      // to grow larger and more corrupt with each save.  The cleanup removes
      // any row with a string field larger than a reasonable size.  The string
      // fields examined here are precisely the ones that were subject to
      // corruption by the original bug.
      const std::string autofill_is_too_big =
          "max(length(name), length(value)) > 500";

      const std::string credit_cards_is_too_big =
          "max(length(label), length(name_on_card), length(type), "
          "    length(expiration_month), length(expiration_year), "
          "    length(billing_address), length(shipping_address) "
          ") > 500";

      const std::string autofill_profiles_is_too_big =
          "max(length(label), length(first_name), "
          "    length(middle_name), length(last_name), length(email), "
          "    length(company_name), length(address_line_1), "
          "    length(address_line_2), length(city), length(state), "
          "    length(zipcode), length(country), length(phone), "
          "    length(fax)) > 500";

      std::string query = "DELETE FROM autofill_dates WHERE pair_id IN ("
          "SELECT pair_id FROM autofill WHERE " + autofill_is_too_big + ")";
      if (!db_.Execute(query.c_str())) {
        LOG(WARNING) << "Unable to update web database to version 24.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }
      query = "DELETE FROM autofill WHERE " + autofill_is_too_big;
      if (!db_.Execute(query.c_str())) {
        LOG(WARNING) << "Unable to update web database to version 24.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }
      // Only delete from legacy credit card tables where specific columns
      // exist.
      if (db_.DoesColumnExist("credit_cards", "label") &&
          db_.DoesColumnExist("credit_cards", "name_on_card") &&
          db_.DoesColumnExist("credit_cards", "type") &&
          db_.DoesColumnExist("credit_cards", "expiration_month") &&
          db_.DoesColumnExist("credit_cards", "expiration_year") &&
          db_.DoesColumnExist("credit_cards", "billing_address") &&
          db_.DoesColumnExist("credit_cards", "shipping_address")) {
        query = "DELETE FROM credit_cards WHERE (" + credit_cards_is_too_big +
            ") OR label IN (SELECT label FROM autofill_profiles WHERE " +
            autofill_profiles_is_too_big + ")";
        if (!db_.Execute(query.c_str())) {
          LOG(WARNING) << "Unable to update web database to version 24.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }
      }
      query = "DELETE FROM autofill_profiles WHERE " +
          autofill_profiles_is_too_big;
      if (!db_.Execute(query.c_str())) {
        LOG(WARNING) << "Unable to update web database to version 24.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }

      meta_table_.SetVersionNumber(24);

      // FALL THROUGH
    }

    case 24:
      // Add the logo_id column if keyword table was not created in this build.
      if (!db_.Execute("ALTER TABLE keywords ADD COLUMN logo_id "
                       "INTEGER DEFAULT 0")) {
        LOG(WARNING) << "Unable to update web database to version 25.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }
      meta_table_.SetVersionNumber(25);
      meta_table_.SetCompatibleVersionNumber(
          std::min(25, kCompatibleVersionNumber));
      // FALL THROUGH

    case 25:
      // Add the created_by_policy column.
      if (!db_.Execute("ALTER TABLE keywords ADD COLUMN created_by_policy "
                       "INTEGER DEFAULT 0")) {
        LOG(WARNING) << "Unable to update web database to version 26.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }

      meta_table_.SetVersionNumber(26);
      meta_table_.SetCompatibleVersionNumber(
          std::min(26, kCompatibleVersionNumber));
      // FALL THROUGH

    case 26: {
      // Only migrate from legacy credit card tables where specific columns
      // exist.
      if (db_.DoesColumnExist("credit_cards", "unique_id") &&
          db_.DoesColumnExist("credit_cards", "billing_address") &&
          db_.DoesColumnExist("autofill_profiles", "unique_id")) {
        // Change the credit_cards.billing_address column from a string to an
        // int.  The stored string is the label of an address, so we have to
        // select the unique ID of this address using the label as a foreign
        // key into the |autofill_profiles| table.
        std::string stmt =
            "SELECT credit_cards.unique_id, autofill_profiles.unique_id "
            "FROM autofill_profiles, credit_cards "
            "WHERE credit_cards.billing_address = autofill_profiles.label";
        sql::Statement s(db_.GetUniqueStatement(stmt.c_str()));
        if (!s) {
          LOG(WARNING) << "Statement prepare failed";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        std::map<int, int> cc_billing_map;
        while (s.Step())
          cc_billing_map[s.ColumnInt(0)] = s.ColumnInt(1);

        // Windows already stores the IDs as strings in |billing_address|. Try
        // to convert those.
        if (cc_billing_map.empty()) {
          std::string stmt =
            "SELECT unique_id,billing_address FROM credit_cards";
          sql::Statement s(db_.GetUniqueStatement(stmt.c_str()));
          if (!s) {
            LOG(WARNING) << "Statement prepare failed";
            NOTREACHED();
            return sql::INIT_FAILURE;
          }

          while (s.Step()) {
            int id = 0;
            if (base::StringToInt(s.ColumnString(1), &id))
              cc_billing_map[s.ColumnInt(0)] = id;
          }
        }

        if (!db_.Execute("CREATE TABLE credit_cards_temp ( "
                         "label VARCHAR, "
                         "unique_id INTEGER PRIMARY KEY, "
                         "name_on_card VARCHAR, "
                         "type VARCHAR, "
                         "card_number VARCHAR, "
                         "expiration_month INTEGER, "
                         "expiration_year INTEGER, "
                         "verification_code VARCHAR, "
                         "billing_address INTEGER, "
                         "shipping_address VARCHAR, "
                         "card_number_encrypted BLOB, "
                         "verification_code_encrypted BLOB)")) {
          LOG(WARNING) << "Unable to update web database to version 27.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        if (!db_.Execute(
            "INSERT INTO credit_cards_temp "
            "SELECT label,unique_id,name_on_card,type,card_number,"
            "expiration_month,expiration_year,verification_code,0,"
            "shipping_address,card_number_encrypted,verification_code_encrypted "
            "FROM credit_cards")) {
          LOG(WARNING) << "Unable to update web database to version 27.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        if (!db_.Execute("DROP TABLE credit_cards")) {
          LOG(WARNING) << "Unable to update web database to version 27.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        if (!db_.Execute(
            "ALTER TABLE credit_cards_temp RENAME TO credit_cards")) {
          LOG(WARNING) << "Unable to update web database to version 27.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        for (std::map<int, int>::const_iterator iter = cc_billing_map.begin();
             iter != cc_billing_map.end(); ++iter) {
          sql::Statement s(db_.GetCachedStatement(
              SQL_FROM_HERE,
              "UPDATE credit_cards SET billing_address=? WHERE unique_id=?"));
          if (!s) {
            LOG(WARNING) << "Statement prepare failed";
            NOTREACHED();
            return sql::INIT_FAILURE;
          }

          s.BindInt(0, (*iter).second);
          s.BindInt(1, (*iter).first);

          if (!s.Run()) {
            LOG(WARNING) << "Unable to update web database to version 27.";
            NOTREACHED();
            return sql::INIT_FAILURE;
          }
        }
      }

      meta_table_.SetVersionNumber(27);
      meta_table_.SetCompatibleVersionNumber(
          std::min(27, kCompatibleVersionNumber));

      // FALL THROUGH
    }

    case 27:
      // Add supports_instant to keywords.
      if (!db_.Execute("ALTER TABLE keywords ADD COLUMN supports_instant "
                       "INTEGER DEFAULT 0")) {
        LOG(WARNING) << "Unable to update web database to version 28.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }
      meta_table_.SetVersionNumber(28);
      meta_table_.SetCompatibleVersionNumber(
          std::min(28, kCompatibleVersionNumber));

      // FALL THROUGH

    case 28:
      // Keywords loses the column supports_instant and gets instant_url.
      if (!db_.Execute("ALTER TABLE keywords ADD COLUMN instant_url "
                       "VARCHAR")) {
        LOG(WARNING) << "Unable to update web database to version 29.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }
      if (!db_.Execute("CREATE TABLE keywords_temp ("
                       "id INTEGER PRIMARY KEY,"
                       "short_name VARCHAR NOT NULL,"
                       "keyword VARCHAR NOT NULL,"
                       "favicon_url VARCHAR NOT NULL,"
                       "url VARCHAR NOT NULL,"
                       "show_in_default_list INTEGER,"
                       "safe_for_autoreplace INTEGER,"
                       "originating_url VARCHAR,"
                       "date_created INTEGER DEFAULT 0,"
                       "usage_count INTEGER DEFAULT 0,"
                       "input_encodings VARCHAR,"
                       "suggest_url VARCHAR,"
                       "prepopulate_id INTEGER DEFAULT 0,"
                       "autogenerate_keyword INTEGER DEFAULT 0,"
                       "logo_id INTEGER DEFAULT 0,"
                       "created_by_policy INTEGER DEFAULT 0,"
                       "instant_url VARCHAR)")) {
        LOG(WARNING) << "Unable to update web database to version 29.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }

      if (!db_.Execute(
          "INSERT INTO keywords_temp "
          "SELECT id, short_name, keyword, favicon_url, url, "
          "show_in_default_list, safe_for_autoreplace, originating_url, "
          "date_created, usage_count, input_encodings, suggest_url, "
          "prepopulate_id, autogenerate_keyword, logo_id, created_by_policy, "
          "instant_url FROM keywords")) {
        LOG(WARNING) << "Unable to update web database to version 29.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }

      if (!db_.Execute("DROP TABLE keywords")) {
        LOG(WARNING) << "Unable to update web database to version 29.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }

      if (!db_.Execute(
          "ALTER TABLE keywords_temp RENAME TO keywords")) {
        LOG(WARNING) << "Unable to update web database to version 29.";
        NOTREACHED();
        return sql::INIT_FAILURE;
      }

      meta_table_.SetVersionNumber(29);
      meta_table_.SetCompatibleVersionNumber(
          std::min(29, kCompatibleVersionNumber));

      // FALL THROUGH

    case 29:
      // Add date_modified to autofill_profiles.
      if (!db_.DoesColumnExist("autofill_profiles", "date_modified")) {
        if (!db_.Execute("ALTER TABLE autofill_profiles ADD COLUMN "
                         "date_modified INTEGER NON NULL DEFAULT 0")) {
          LOG(WARNING) << "Unable to update web database to version 30";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        sql::Statement s(db_.GetUniqueStatement(
            "UPDATE autofill_profiles SET date_modified=?"));
        if (!s) {
          LOG(WARNING) << "Unable to update web database to version 30.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        s.BindInt64(0, Time::Now().ToTimeT());

        if (!s.Run()) {
          LOG(WARNING) << "Unable to update web database to version 30.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

      }

      // Add date_modified to credit_cards.
      if (!db_.DoesColumnExist("credit_cards", "date_modified")) {
        if (!db_.Execute("ALTER TABLE credit_cards ADD COLUMN "
                         "date_modified INTEGER NON NULL DEFAULT 0")) {
          LOG(WARNING) << "Unable to update web database to version 30";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        sql::Statement s(db_.GetUniqueStatement(
            "UPDATE credit_cards SET date_modified=?"));
        if (!s) {
          LOG(WARNING) << "Unable to update web database to version 30.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        s.BindInt64(0, Time::Now().ToTimeT());

        if (!s.Run()) {
          LOG(WARNING) << "Unable to update web database to version 30.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }
      }

      meta_table_.SetVersionNumber(30);
      meta_table_.SetCompatibleVersionNumber(
          std::min(30, kCompatibleVersionNumber));

      // FALL THROUGH

    case 30:
      // Add |guid| column to |autofill_profiles| table.
      // Note that we need to check for the guid column's existence due to the
      // fact that for a version 22 database the |autofill_profiles| table
      // gets created fresh with |InitAutoFillProfilesTable|.
      if (!db_.DoesColumnExist("autofill_profiles", "guid")) {
        if (!db_.Execute("ALTER TABLE autofill_profiles ADD COLUMN "
                         "guid VARCHAR NOT NULL DEFAULT \"\"")) {
          LOG(WARNING) << "Unable to update web database to version 30.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        // Set all the |guid| fields to valid values.
        {
          sql::Statement s(db_.GetUniqueStatement("SELECT unique_id "
                                                  "FROM autofill_profiles"));

          if (!s) {
            LOG(WARNING) << "Unable to update web database to version 30.";
            NOTREACHED();
            return sql::INIT_FAILURE;
          }

          while (s.Step()) {
            sql::Statement update_s(
                db_.GetUniqueStatement("UPDATE autofill_profiles "
                                       "SET guid=? WHERE unique_id=?"));
            if (!update_s) {
              LOG(WARNING) << "Unable to update web database to version 30.";
              NOTREACHED();
              return sql::INIT_FAILURE;
            }
            update_s.BindString(0, guid::GenerateGUID());
            update_s.BindInt(1, s.ColumnInt(0));

            if (!update_s.Run()) {
              LOG(WARNING) << "Unable to update web database to version 30.";
              NOTREACHED();
              return sql::INIT_FAILURE;
            }
          }
        }
      }

      // Add |guid| column to |credit_cards| table.
      // Note that we need to check for the guid column's existence due to the
      // fact that for a version 22 database the |autofill_profiles| table
      // gets created fresh with |InitAutoFillProfilesTable|.
      if (!db_.DoesColumnExist("credit_cards", "guid")) {
        if (!db_.Execute("ALTER TABLE credit_cards ADD COLUMN "
                         "guid VARCHAR NOT NULL DEFAULT \"\"")) {
          LOG(WARNING) << "Unable to update web database to version 30.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        // Set all the |guid| fields to valid values.
        {
          sql::Statement s(db_.GetUniqueStatement("SELECT unique_id "
                                                  "FROM credit_cards"));
          if (!s) {
            LOG(WARNING) << "Unable to update web database to version 30.";
            NOTREACHED();
            return sql::INIT_FAILURE;
          }

          while (s.Step()) {
            sql::Statement update_s(
                db_.GetUniqueStatement("UPDATE credit_cards "
                                       "set guid=? WHERE unique_id=?"));
            if (!update_s) {
              LOG(WARNING) << "Unable to update web database to version 30.";
              NOTREACHED();
              return sql::INIT_FAILURE;
            }
            update_s.BindString(0, guid::GenerateGUID());
            update_s.BindInt(1, s.ColumnInt(0));

            if (!update_s.Run()) {
              LOG(WARNING) << "Unable to update web database to version 30.";
              NOTREACHED();
              return sql::INIT_FAILURE;
            }
          }
        }
      }

      meta_table_.SetVersionNumber(31);
      meta_table_.SetCompatibleVersionNumber(
          std::min(31, kCompatibleVersionNumber));

      // FALL THROUGH

    case 31:
      if (db_.DoesColumnExist("autofill_profiles", "unique_id")) {
        if (!db_.Execute("CREATE TABLE autofill_profiles_temp ( "
                         "guid VARCHAR PRIMARY KEY, "
                         "label VARCHAR, "
                         "first_name VARCHAR, "
                         "middle_name VARCHAR, "
                         "last_name VARCHAR, "
                         "email VARCHAR, "
                         "company_name VARCHAR, "
                         "address_line_1 VARCHAR, "
                         "address_line_2 VARCHAR, "
                         "city VARCHAR, "
                         "state VARCHAR, "
                         "zipcode VARCHAR, "
                         "country VARCHAR, "
                         "phone VARCHAR, "
                         "fax VARCHAR, "
                         "date_modified INTEGER NOT NULL DEFAULT 0)")) {
          LOG(WARNING) << "Unable to update web database to version 32.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        if (!db_.Execute(
            "INSERT INTO autofill_profiles_temp "
            "SELECT guid, label, first_name, middle_name, last_name, email, "
            "company_name, address_line_1, address_line_2, city, state, zipcode, "
            "country, phone, fax, date_modified "
            "FROM autofill_profiles")) {
          LOG(WARNING) << "Unable to update web database to version 32.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        if (!db_.Execute("DROP TABLE autofill_profiles")) {
          LOG(WARNING) << "Unable to update web database to version 32.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        if (!db_.Execute(
            "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) {
          LOG(WARNING) << "Unable to update web database to version 32.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }
      }

      if (db_.DoesColumnExist("credit_cards", "unique_id")) {
        if (!db_.Execute("CREATE TABLE credit_cards_temp ( "
                         "guid VARCHAR PRIMARY KEY, "
                         "label VARCHAR, "
                         "name_on_card VARCHAR, "
                         "expiration_month INTEGER, "
                         "expiration_year INTEGER, "
                         "card_number_encrypted BLOB, "
                         "date_modified INTEGER NOT NULL DEFAULT 0)")) {
          LOG(WARNING) << "Unable to update web database to version 32.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        if (!db_.Execute(
            "INSERT INTO credit_cards_temp "
            "SELECT guid, label, name_on_card, expiration_month, "
            "expiration_year, card_number_encrypted, date_modified "
            "FROM credit_cards")) {
          LOG(WARNING) << "Unable to update web database to version 32.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        if (!db_.Execute("DROP TABLE credit_cards")) {
          LOG(WARNING) << "Unable to update web database to version 32.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }

        if (!db_.Execute(
            "ALTER TABLE credit_cards_temp RENAME TO credit_cards")) {
          LOG(WARNING) << "Unable to update web database to version 32.";
          NOTREACHED();
          return sql::INIT_FAILURE;
        }
      }

      meta_table_.SetVersionNumber(32);
      meta_table_.SetCompatibleVersionNumber(
          std::min(32, kCompatibleVersionNumber));

      // FALL THROUGH

    // Add successive versions here.  Each should set the version number and
    // compatible version number as appropriate, then fall through to the next
    // case.

    case kCurrentVersionNumber:
      // No migration needed.
      return sql::INIT_OK;
  }
}