| Index: sql/connection.cc
|
| diff --git a/sql/connection.cc b/sql/connection.cc
|
| index 073b032cde62157b69a91512a357ce450eb1e4d5..788329bafd33632c6a27d9f81f3ab760cabe4c59 100644
|
| --- a/sql/connection.cc
|
| +++ b/sql/connection.cc
|
| @@ -318,6 +318,7 @@ Connection::Connection()
|
| needs_rollback_(false),
|
| in_memory_(false),
|
| poisoned_(false),
|
| + mmap_alt_status_(false),
|
| mmap_disabled_(false),
|
| mmap_enabled_(false),
|
| total_changes_at_last_release_(0),
|
| @@ -840,6 +841,49 @@ std::string Connection::CollectCorruptionInfo() {
|
| return debug_info;
|
| }
|
|
|
| +bool Connection::GetMmapAltStatus(int64_t* status) {
|
| + // The [meta] version uses a missing table as a signal for a fresh database.
|
| + // That will not work for the view, which would not exist in either a new or
|
| + // an existing database. A new database _should_ be only one page long, so
|
| + // just don't bother optimizing this case (start at offset 0).
|
| + // TODO(shess): Could the [meta] case also get simpler, then?
|
| + if (!DoesViewExist("MmapStatus")) {
|
| + *status = 0;
|
| + return true;
|
| + }
|
| +
|
| + const char* kMmapStatusSql = "SELECT * FROM MmapStatus";
|
| + Statement s(GetUniqueStatement(kMmapStatusSql));
|
| + if (s.Step())
|
| + *status = s.ColumnInt64(0);
|
| + return s.Succeeded();
|
| +}
|
| +
|
| +bool Connection::SetMmapAltStatus(int64_t status) {
|
| + if (!BeginTransaction())
|
| + return false;
|
| +
|
| + // View may not exist on first run.
|
| + if (!Execute("DROP VIEW IF EXISTS MmapStatus")) {
|
| + RollbackTransaction();
|
| + return false;
|
| + }
|
| +
|
| + // Views live in the schema, so they cannot be parameterized. For an integer
|
| + // value, this construct should be safe from SQL injection, if the value
|
| + // becomes more complicated use "SELECT quote(?)" to generate a safe quoted
|
| + // value.
|
| + const std::string createViewSql =
|
| + base::StringPrintf("CREATE VIEW MmapStatus (value) AS SELECT %" PRId64,
|
| + status);
|
| + if (!Execute(createViewSql.c_str())) {
|
| + RollbackTransaction();
|
| + return false;
|
| + }
|
| +
|
| + return CommitTransaction();
|
| +}
|
| +
|
| size_t Connection::GetAppropriateMmapSize() {
|
| AssertIOAllowed();
|
|
|
| @@ -854,27 +898,27 @@ size_t Connection::GetAppropriateMmapSize() {
|
| // percentile of Chrome databases in the wild, so this should be good.
|
| const size_t kMmapEverything = 256 * 1024 * 1024;
|
|
|
| - // If the database doesn't have a place to track progress, assume the best.
|
| - // This will happen when new databases are created, or if a database doesn't
|
| - // use a meta table. sql::MetaTable::Init() will preload kMmapSuccess.
|
| - // TODO(shess): Databases not using meta include:
|
| - // DOMStorageDatabase (localstorage)
|
| - // ActivityDatabase (extensions activity log)
|
| - // PredictorDatabase (prefetch and autocomplete predictor data)
|
| - // SyncDirectory (sync metadata storage)
|
| - // For now, these all have mmap disabled to allow other databases to get the
|
| - // default-enable path. sqlite-diag could be an alternative for all but
|
| - // DOMStorageDatabase, which creates many small databases.
|
| - // http://crbug.com/537742
|
| - if (!MetaTable::DoesTableExist(this)) {
|
| - RecordOneEvent(EVENT_MMAP_META_MISSING);
|
| - return kMmapEverything;
|
| - }
|
| -
|
| + // Progress information is tracked in the [meta] table for databases which use
|
| + // sql::MetaTable, otherwise it is tracked in a special view.
|
| + // TODO(shess): Move all cases to the view implementation.
|
| int64_t mmap_ofs = 0;
|
| - if (!MetaTable::GetMmapStatus(this, &mmap_ofs)) {
|
| - RecordOneEvent(EVENT_MMAP_META_FAILURE_READ);
|
| - return 0;
|
| + if (mmap_alt_status_) {
|
| + if (!GetMmapAltStatus(&mmap_ofs)) {
|
| + RecordOneEvent(EVENT_MMAP_STATUS_FAILURE_READ);
|
| + return 0;
|
| + }
|
| + } else {
|
| + // If [meta] doesn't exist, yet, it's a new database, assume the best.
|
| + // sql::MetaTable::Init() will preload kMmapSuccess.
|
| + if (!MetaTable::DoesTableExist(this)) {
|
| + RecordOneEvent(EVENT_MMAP_META_MISSING);
|
| + return kMmapEverything;
|
| + }
|
| +
|
| + if (!MetaTable::GetMmapStatus(this, &mmap_ofs)) {
|
| + RecordOneEvent(EVENT_MMAP_META_FAILURE_READ);
|
| + return 0;
|
| + }
|
| }
|
|
|
| // Database read failed in the past, don't memory map.
|
| @@ -949,9 +993,16 @@ size_t Connection::GetAppropriateMmapSize() {
|
| event = EVENT_MMAP_FAILED_NEW;
|
| }
|
|
|
| - if (!MetaTable::SetMmapStatus(this, mmap_ofs)) {
|
| - RecordOneEvent(EVENT_MMAP_META_FAILURE_UPDATE);
|
| - return 0;
|
| + if (mmap_alt_status_) {
|
| + if (!SetMmapAltStatus(mmap_ofs)) {
|
| + RecordOneEvent(EVENT_MMAP_STATUS_FAILURE_UPDATE);
|
| + return 0;
|
| + }
|
| + } else {
|
| + if (!MetaTable::SetMmapStatus(this, mmap_ofs)) {
|
| + RecordOneEvent(EVENT_MMAP_META_FAILURE_UPDATE);
|
| + return 0;
|
| + }
|
| }
|
|
|
| RecordOneEvent(event);
|
| @@ -1503,15 +1554,19 @@ bool Connection::IsSQLValid(const char* sql) {
|
| return true;
|
| }
|
|
|
| +bool Connection::DoesIndexExist(const char* index_name) const {
|
| + return DoesSchemaItemExist(index_name, "index");
|
| +}
|
| +
|
| bool Connection::DoesTableExist(const char* table_name) const {
|
| - return DoesTableOrIndexExist(table_name, "table");
|
| + return DoesSchemaItemExist(table_name, "table");
|
| }
|
|
|
| -bool Connection::DoesIndexExist(const char* index_name) const {
|
| - return DoesTableOrIndexExist(index_name, "index");
|
| +bool Connection::DoesViewExist(const char* view_name) const {
|
| + return DoesSchemaItemExist(view_name, "view");
|
| }
|
|
|
| -bool Connection::DoesTableOrIndexExist(
|
| +bool Connection::DoesSchemaItemExist(
|
| const char* name, const char* type) const {
|
| const char* kSql =
|
| "SELECT name FROM sqlite_master WHERE type=? AND name=? COLLATE NOCASE";
|
|
|