Commit 5a910a6e authored by ericu@chromium.org's avatar ericu@chromium.org

Add most of the metadata-handling code for blobs. It's not quite all there, but

this is the biggest chunk I can pull out vaguely cleanly.  It does contain a
couple of fake calls to keep the compiler happy.
This CL also makes SetUpMetadata a member in order to ease testing in a later CL.

This depends on https://codereview.chromium.org/261843004/.

BUG=108012

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=270016

Review URL: https://codereview.chromium.org/266333002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@273048 0039d316-1c4b-4281-b951-d872f2087c98
parent 13a16eb5
...@@ -128,6 +128,8 @@ enum IndexedDBBackingStoreErrorSource { ...@@ -128,6 +128,8 @@ enum IndexedDBBackingStoreErrorSource {
CLEAR_OBJECT_STORE, CLEAR_OBJECT_STORE,
READ_BLOB_JOURNAL, READ_BLOB_JOURNAL,
DECODE_BLOB_JOURNAL, DECODE_BLOB_JOURNAL,
GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER,
GET_BLOB_INFO_FOR_RECORD,
INTERNAL_ERROR_MAX, INTERNAL_ERROR_MAX,
}; };
...@@ -304,7 +306,8 @@ const char* IndexedDBBackingStore::Comparator::Name() const { ...@@ -304,7 +306,8 @@ const char* IndexedDBBackingStore::Comparator::Name() const {
// 0 - Initial version. // 0 - Initial version.
// 1 - Adds UserIntVersion to DatabaseMetaData. // 1 - Adds UserIntVersion to DatabaseMetaData.
// 2 - Adds DataVersion to to global metadata. // 2 - Adds DataVersion to to global metadata.
static const int64 kLatestKnownSchemaVersion = 2; // 3 - Adds metadata needed for blob support.
static const int64 kLatestKnownSchemaVersion = 3;
WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) { WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
int64 db_schema_version = 0; int64 db_schema_version = 0;
bool found = false; bool found = false;
...@@ -341,15 +344,16 @@ WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) { ...@@ -341,15 +344,16 @@ WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
return true; return true;
} }
WARN_UNUSED_RESULT static bool SetUpMetadata( // TODO(ericu): Move this down into the member section of this file. I'm
LevelDBDatabase* db, // leaving it here for this CL as it's easier to see the diffs in place.
const std::string& origin_identifier) { WARN_UNUSED_RESULT bool IndexedDBBackingStore::SetUpMetadata() {
const uint32 latest_known_data_version = const uint32 latest_known_data_version =
blink::kSerializedScriptValueVersion; blink::kSerializedScriptValueVersion;
const std::string schema_version_key = SchemaVersionKey::Encode(); const std::string schema_version_key = SchemaVersionKey::Encode();
const std::string data_version_key = DataVersionKey::Encode(); const std::string data_version_key = DataVersionKey::Encode();
scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db); scoped_refptr<LevelDBTransaction> transaction =
new LevelDBTransaction(db_.get());
int64 db_schema_version = 0; int64 db_schema_version = 0;
int64 db_data_version = 0; int64 db_data_version = 0;
...@@ -366,6 +370,12 @@ WARN_UNUSED_RESULT static bool SetUpMetadata( ...@@ -366,6 +370,12 @@ WARN_UNUSED_RESULT static bool SetUpMetadata(
PutInt(transaction.get(), schema_version_key, db_schema_version); PutInt(transaction.get(), schema_version_key, db_schema_version);
db_data_version = latest_known_data_version; db_data_version = latest_known_data_version;
PutInt(transaction.get(), data_version_key, db_data_version); PutInt(transaction.get(), data_version_key, db_data_version);
// If a blob directory already exists for this database, blow it away. It's
// leftover from a partially-purged previous generation of data.
if (!base::DeleteFile(blob_path_, true)) {
INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
return false;
}
} else { } else {
// Upgrade old backing store. // Upgrade old backing store.
DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion); DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion);
...@@ -373,10 +383,10 @@ WARN_UNUSED_RESULT static bool SetUpMetadata( ...@@ -373,10 +383,10 @@ WARN_UNUSED_RESULT static bool SetUpMetadata(
db_schema_version = 1; db_schema_version = 1;
PutInt(transaction.get(), schema_version_key, db_schema_version); PutInt(transaction.get(), schema_version_key, db_schema_version);
const std::string start_key = const std::string start_key =
DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier); DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
const std::string stop_key = const std::string stop_key =
DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier); DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
scoped_ptr<LevelDBIterator> it = db->CreateIterator(); scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
for (s = it->Seek(start_key); for (s = it->Seek(start_key);
s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0; s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
s = it->Next()) { s = it->Next()) {
...@@ -404,6 +414,13 @@ WARN_UNUSED_RESULT static bool SetUpMetadata( ...@@ -404,6 +414,13 @@ WARN_UNUSED_RESULT static bool SetUpMetadata(
db_data_version = blink::kSerializedScriptValueVersion; db_data_version = blink::kSerializedScriptValueVersion;
PutInt(transaction.get(), data_version_key, db_data_version); PutInt(transaction.get(), data_version_key, db_data_version);
} }
if (db_schema_version < 3) {
db_schema_version = 3;
if (!base::DeleteFile(blob_path_, true)) {
INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
return false;
}
}
} }
if (!s.ok()) { if (!s.ok()) {
...@@ -480,6 +497,55 @@ class DefaultLevelDBFactory : public LevelDBFactory { ...@@ -480,6 +497,55 @@ class DefaultLevelDBFactory : public LevelDBFactory {
} }
}; };
static bool GetBlobKeyGeneratorCurrentNumber(
LevelDBTransaction* leveldb_transaction,
int64 database_id,
int64* blob_key_generator_current_number) {
const std::string key_gen_key = DatabaseMetaDataKey::Encode(
database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
// Default to initial number if not found.
int64 cur_number = DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber;
std::string data;
bool found = false;
bool ok = leveldb_transaction->Get(key_gen_key, &data, &found).ok();
if (!ok) {
INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
return false;
}
if (found) {
StringPiece slice(data);
if (!DecodeVarInt(&slice, &cur_number) || !slice.empty() ||
!DatabaseMetaDataKey::IsValidBlobKey(cur_number)) {
INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
return false;
}
}
*blob_key_generator_current_number = cur_number;
return true;
}
static bool UpdateBlobKeyGeneratorCurrentNumber(
LevelDBTransaction* leveldb_transaction,
int64 database_id,
int64 blob_key_generator_current_number) {
#ifndef NDEBUG
int64 old_number;
if (!GetBlobKeyGeneratorCurrentNumber(
leveldb_transaction, database_id, &old_number))
return false;
DCHECK_LT(old_number, blob_key_generator_current_number);
#endif
DCHECK(
DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number));
const std::string key = DatabaseMetaDataKey::Encode(
database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
PutVarInt(leveldb_transaction, key, blob_key_generator_current_number);
return true;
}
// TODO(ericu): Error recovery. If we persistently can't read the // TODO(ericu): Error recovery. If we persistently can't read the
// blob journal, the safe thing to do is to clear it and leak the blobs, // blob journal, the safe thing to do is to clear it and leak the blobs,
// though that may be costly. Still, database/directory deletion should always // though that may be costly. Still, database/directory deletion should always
...@@ -573,6 +639,64 @@ static leveldb::Status MergeDatabaseIntoLiveBlobJournal( ...@@ -573,6 +639,64 @@ static leveldb::Status MergeDatabaseIntoLiveBlobJournal(
return leveldb::Status::OK(); return leveldb::Status::OK();
} }
// Blob Data is encoded as a series of:
// { is_file [bool], key [int64 as varInt],
// type [string-with-length, may be empty],
// (for Blobs only) size [int64 as varInt]
// (for Files only) fileName [string-with-length]
// }
// There is no length field; just read until you run out of data.
static std::string EncodeBlobData(
const std::vector<IndexedDBBlobInfo*>& blob_info) {
std::string ret;
std::vector<IndexedDBBlobInfo*>::const_iterator iter;
for (iter = blob_info.begin(); iter != blob_info.end(); ++iter) {
const IndexedDBBlobInfo& info = **iter;
EncodeBool(info.is_file(), &ret);
EncodeVarInt(info.key(), &ret);
EncodeStringWithLength(info.type(), &ret);
if (info.is_file())
EncodeStringWithLength(info.file_name(), &ret);
else
EncodeVarInt(info.size(), &ret);
}
return ret;
}
static bool DecodeBlobData(const std::string& data,
std::vector<IndexedDBBlobInfo>* output) {
std::vector<IndexedDBBlobInfo> ret;
output->clear();
StringPiece slice(data);
while (!slice.empty()) {
bool is_file;
int64 key;
base::string16 type;
int64 size;
base::string16 file_name;
if (!DecodeBool(&slice, &is_file))
return false;
if (!DecodeVarInt(&slice, &key) ||
!DatabaseMetaDataKey::IsValidBlobKey(key))
return false;
if (!DecodeStringWithLength(&slice, &type))
return false;
if (is_file) {
if (!DecodeStringWithLength(&slice, &file_name))
return false;
ret.push_back(IndexedDBBlobInfo(key, type, file_name));
} else {
if (!DecodeVarInt(&slice, &size) || size < 0)
return false;
ret.push_back(IndexedDBBlobInfo(type, static_cast<uint64>(size), key));
}
}
output->swap(ret);
return true;
}
IndexedDBBackingStore::IndexedDBBackingStore( IndexedDBBackingStore::IndexedDBBackingStore(
IndexedDBFactory* indexed_db_factory, IndexedDBFactory* indexed_db_factory,
const GURL& origin_url, const GURL& origin_url,
...@@ -589,7 +713,8 @@ IndexedDBBackingStore::IndexedDBBackingStore( ...@@ -589,7 +713,8 @@ IndexedDBBackingStore::IndexedDBBackingStore(
task_runner_(task_runner), task_runner_(task_runner),
db_(db.Pass()), db_(db.Pass()),
comparator_(comparator.Pass()), comparator_(comparator.Pass()),
active_blob_registry_(this) {} active_blob_registry_(this) {
}
IndexedDBBackingStore::~IndexedDBBackingStore() { IndexedDBBackingStore::~IndexedDBBackingStore() {
if (!blob_path_.empty() && !child_process_ids_granted_.empty()) { if (!blob_path_.empty() && !child_process_ids_granted_.empty()) {
...@@ -638,6 +763,7 @@ enum IndexedDBBackingStoreOpenResult { ...@@ -638,6 +763,7 @@ enum IndexedDBBackingStoreOpenResult {
INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG, INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY, INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION, INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
INDEXED_DB_BACKING_STORE_OPEN_MAX, INDEXED_DB_BACKING_STORE_OPEN_MAX,
}; };
...@@ -923,13 +1049,22 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open( ...@@ -923,13 +1049,22 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
return scoped_refptr<IndexedDBBackingStore>(); return scoped_refptr<IndexedDBBackingStore>();
} }
return Create(indexed_db_factory, scoped_refptr<IndexedDBBackingStore> backing_store =
Create(indexed_db_factory,
origin_url, origin_url,
blob_path, blob_path,
request_context, request_context,
db.Pass(), db.Pass(),
comparator.Pass(), comparator.Pass(),
task_runner); task_runner);
if (clean_journal && backing_store &&
!backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
HistogramOpenStatus(
INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR, origin_url);
return scoped_refptr<IndexedDBBackingStore>();
}
return backing_store;
} }
// static // static
...@@ -986,8 +1121,7 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create( ...@@ -986,8 +1121,7 @@ scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
db.Pass(), db.Pass(),
comparator.Pass(), comparator.Pass(),
task_runner)); task_runner));
if (!SetUpMetadata(backing_store->db_.get(), if (!backing_store->SetUpMetadata())
backing_store->origin_identifier_))
return scoped_refptr<IndexedDBBackingStore>(); return scoped_refptr<IndexedDBBackingStore>();
return backing_store; return backing_store;
...@@ -1084,6 +1218,29 @@ leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData( ...@@ -1084,6 +1218,29 @@ leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData(
INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA); INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
} }
// We don't cache this, we just check it if it's there.
int64 blob_key_generator_current_number =
DatabaseMetaDataKey::kInvalidBlobKey;
s = GetVarInt(
db_.get(),
DatabaseMetaDataKey::Encode(
metadata->id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
&blob_key_generator_current_number,
found);
if (!s.ok()) {
INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
return s;
}
if (!*found) {
// This database predates blob support.
*found = true;
} else if (!DatabaseMetaDataKey::IsValidBlobKey(
blob_key_generator_current_number)) {
INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
return InternalInconsistencyStatus();
}
return s; return s;
} }
...@@ -1137,6 +1294,12 @@ leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData( ...@@ -1137,6 +1294,12 @@ leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData(
DatabaseMetaDataKey::Encode(*row_id, DatabaseMetaDataKey::Encode(*row_id,
DatabaseMetaDataKey::USER_INT_VERSION), DatabaseMetaDataKey::USER_INT_VERSION),
int_version); int_version);
PutVarInt(
transaction.get(),
DatabaseMetaDataKey::Encode(
*row_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber);
s = transaction->Commit(); s = transaction->Commit();
if (!s.ok()) if (!s.ok())
INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA); INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA);
...@@ -1169,15 +1332,48 @@ static leveldb::Status DeleteRange(LevelDBTransaction* transaction, ...@@ -1169,15 +1332,48 @@ static leveldb::Status DeleteRange(LevelDBTransaction* transaction,
return s; return s;
} }
static leveldb::Status DeleteBlobsInObjectStore(
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
int64 object_store_id) {
std::string start_key, end_key;
start_key =
BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id);
end_key =
BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id);
scoped_ptr<LevelDBIterator> it = transaction->transaction()->CreateIterator();
leveldb::Status s = it->Seek(start_key);
for (; s.ok() && it->IsValid() && CompareKeys(it->Key(), end_key) < 0;
s = it->Next()) {
StringPiece key_piece(it->Key());
std::string user_key =
BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece);
if (!user_key.size()) {
INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
return InternalInconsistencyStatus();
}
transaction->PutBlobInfo(
database_id, object_store_id, user_key, NULL, NULL);
}
return s;
}
leveldb::Status IndexedDBBackingStore::DeleteDatabase( leveldb::Status IndexedDBBackingStore::DeleteDatabase(
const base::string16& name) { const base::string16& name) {
IDB_TRACE("IndexedDBBackingStore::DeleteDatabase"); IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
scoped_ptr<LevelDBDirectTransaction> transaction = scoped_ptr<LevelDBDirectTransaction> transaction =
LevelDBDirectTransaction::Create(db_.get()); LevelDBDirectTransaction::Create(db_.get());
leveldb::Status s;
s = CleanUpBlobJournal(BlobJournalKey::Encode());
if (!s.ok())
return s;
IndexedDBDatabaseMetadata metadata; IndexedDBDatabaseMetadata metadata;
bool success = false; bool success = false;
leveldb::Status s = GetIDBDatabaseMetaData(name, &metadata, &success); s = GetIDBDatabaseMetaData(name, &metadata, &success);
if (!s.ok()) if (!s.ok())
return s; return s;
if (!success) if (!success)
...@@ -1200,16 +1396,30 @@ leveldb::Status IndexedDBBackingStore::DeleteDatabase( ...@@ -1200,16 +1396,30 @@ leveldb::Status IndexedDBBackingStore::DeleteDatabase(
const std::string key = DatabaseNameKey::Encode(origin_identifier_, name); const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
transaction->Remove(key); transaction->Remove(key);
// TODO(ericu): Put the real calls to the blob journal code here. For now, bool need_cleanup = false;
// I've inserted fake calls so that we don't get "you didn't use this static if (active_blob_registry()->MarkDeletedCheckIfUsed(
// function" compiler errors. metadata.id, DatabaseMetaDataKey::kAllBlobsKey)) {
s = MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
if (!s.ok())
return s;
} else {
UpdateBlobJournalWithDatabase(transaction.get(), metadata.id);
need_cleanup = true;
}
// TODO(ericu): Remove these fake calls, added to avoid "defined but unused"
// compiler errors until the code that makes the real calls can be added.
if (false) { if (false) {
std::vector<IndexedDBBlobInfo*> fake;
EncodeBlobData(fake);
scoped_refptr<LevelDBTransaction> fake_transaction = scoped_refptr<LevelDBTransaction> fake_transaction =
new LevelDBTransaction(NULL); new LevelDBTransaction(NULL);
BlobJournalType fake_journal; BlobJournalType fake_journal;
MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
UpdateBlobJournalWithDatabase(transaction.get(), metadata.id);
MergeBlobsIntoLiveBlobJournal(fake_transaction.get(), fake_journal); MergeBlobsIntoLiveBlobJournal(fake_transaction.get(), fake_journal);
UpdateBlobKeyGeneratorCurrentNumber(fake_transaction.get(), 0, 0);
int64 arg;
GetBlobKeyGeneratorCurrentNumber(fake_transaction.get(), 0, &arg);
} }
s = transaction->Commit(); s = transaction->Commit();
...@@ -1217,6 +1427,10 @@ leveldb::Status IndexedDBBackingStore::DeleteDatabase( ...@@ -1217,6 +1427,10 @@ leveldb::Status IndexedDBBackingStore::DeleteDatabase(
INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE); INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
return s; return s;
} }
if (need_cleanup)
CleanUpBlobJournal(BlobJournalKey::Encode());
db_->Compact(start_key, stop_key); db_->Compact(start_key, stop_key);
return s; return s;
} }
...@@ -1525,6 +1739,12 @@ leveldb::Status IndexedDBBackingStore::DeleteObjectStore( ...@@ -1525,6 +1739,12 @@ leveldb::Status IndexedDBBackingStore::DeleteObjectStore(
return InternalInconsistencyStatus(); return InternalInconsistencyStatus();
} }
s = DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
if (!s.ok()) {
INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
return s;
}
s = DeleteRange( s = DeleteRange(
leveldb_transaction, leveldb_transaction,
ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0), ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0),
...@@ -1593,7 +1813,7 @@ leveldb::Status IndexedDBBackingStore::GetRecord( ...@@ -1593,7 +1813,7 @@ leveldb::Status IndexedDBBackingStore::GetRecord(
} }
record->bits = slice.as_string(); record->bits = slice.as_string();
return s; return transaction->GetBlobInfoForRecord(database_id, leveldb_key, record);
} }
WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber( WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber(
...@@ -1689,9 +1909,11 @@ leveldb::Status IndexedDBBackingStore::ClearObjectStore( ...@@ -1689,9 +1909,11 @@ leveldb::Status IndexedDBBackingStore::ClearObjectStore(
leveldb::Status s = leveldb::Status s =
DeleteRange(transaction->transaction(), start_key, stop_key); DeleteRange(transaction->transaction(), start_key, stop_key);
if (!s.ok()) if (!s.ok()) {
INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE); INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE);
return s; return s;
}
return DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
} }
leveldb::Status IndexedDBBackingStore::DeleteRecord( leveldb::Status IndexedDBBackingStore::DeleteRecord(
...@@ -2290,6 +2512,70 @@ leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal( ...@@ -2290,6 +2512,70 @@ leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
return journal_transaction->Commit(); return journal_transaction->Commit();
} }
leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
int64 database_id,
const std::string& object_store_data_key,
IndexedDBValue* value) {
BlobChangeRecord* change_record = NULL;
BlobChangeMap::const_iterator blob_iter =
blob_change_map_.find(object_store_data_key);
if (blob_iter != blob_change_map_.end()) {
change_record = blob_iter->second;
} else {
blob_iter = incognito_blob_map_.find(object_store_data_key);
if (blob_iter != incognito_blob_map_.end())
change_record = blob_iter->second;
}
if (change_record) {
// Either we haven't written the blob to disk yet or we're in incognito
// mode, so we have to send back the one they sent us. This change record
// includes the original UUID.
value->blob_info = change_record->blob_info();
return leveldb::Status::OK();
}
BlobEntryKey blob_entry_key;
StringPiece leveldb_key_piece(object_store_data_key);
if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
&blob_entry_key)) {
NOTREACHED();
return InternalInconsistencyStatus();
}
scoped_ptr<LevelDBIterator> it = transaction()->CreateIterator();
std::string encoded_key = blob_entry_key.Encode();
leveldb::Status s = it->Seek(encoded_key);
if (!s.ok())
return s;
if (it->IsValid() && CompareKeys(it->Key(), encoded_key) == 0) {
if (!DecodeBlobData(it->Value().as_string(), &value->blob_info)) {
INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
return InternalInconsistencyStatus();
}
std::vector<IndexedDBBlobInfo>::iterator iter;
for (iter = value->blob_info.begin(); iter != value->blob_info.end();
++iter) {
iter->set_file_path(
backing_store_->GetBlobFileName(database_id, iter->key()));
iter->set_mark_used_callback(
backing_store_->active_blob_registry()->GetAddBlobRefCallback(
database_id, iter->key()));
iter->set_release_callback(
backing_store_->active_blob_registry()->GetFinalReleaseCallback(
database_id, iter->key()));
if (iter->is_file()) {
base::File::Info info;
if (base::GetFileInfo(iter->file_path(), &info)) {
// This should always work, but it isn't fatal if it doesn't; it just
// means a potential slow synchronous call from the renderer later.
iter->set_last_modified(info.last_modified);
iter->set_size(info.size);
}
}
}
}
return leveldb::Status::OK();
}
void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() { void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
CleanUpBlobJournal(BlobJournalKey::Encode()); CleanUpBlobJournal(BlobJournalKey::Encode());
} }
...@@ -2613,11 +2899,13 @@ leveldb::Status IndexedDBBackingStore::KeyExistsInIndex( ...@@ -2613,11 +2899,13 @@ leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
IndexedDBBackingStore::Cursor::Cursor( IndexedDBBackingStore::Cursor::Cursor(
const IndexedDBBackingStore::Cursor* other) const IndexedDBBackingStore::Cursor* other)
: transaction_(other->transaction_), : backing_store_(other->backing_store_),
transaction_(other->transaction_),
database_id_(other->database_id_),
cursor_options_(other->cursor_options_), cursor_options_(other->cursor_options_),
current_key_(new IndexedDBKey(*other->current_key_)) { current_key_(new IndexedDBKey(*other->current_key_)) {
if (other->iterator_) { if (other->iterator_) {
iterator_ = transaction_->CreateIterator(); iterator_ = transaction_->transaction()->CreateIterator();
if (other->iterator_->IsValid()) { if (other->iterator_->IsValid()) {
leveldb::Status s = iterator_->Seek(other->iterator_->Key()); leveldb::Status s = iterator_->Seek(other->iterator_->Key());
...@@ -2627,13 +2915,20 @@ IndexedDBBackingStore::Cursor::Cursor( ...@@ -2627,13 +2915,20 @@ IndexedDBBackingStore::Cursor::Cursor(
} }
} }
IndexedDBBackingStore::Cursor::Cursor(LevelDBTransaction* transaction, IndexedDBBackingStore::Cursor::Cursor(
scoped_refptr<IndexedDBBackingStore> backing_store,
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
const CursorOptions& cursor_options) const CursorOptions& cursor_options)
: transaction_(transaction), cursor_options_(cursor_options) {} : backing_store_(backing_store),
transaction_(transaction),
database_id_(database_id),
cursor_options_(cursor_options) {
}
IndexedDBBackingStore::Cursor::~Cursor() {} IndexedDBBackingStore::Cursor::~Cursor() {}
bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) { bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
iterator_ = transaction_->CreateIterator(); iterator_ = transaction_->transaction()->CreateIterator();
if (cursor_options_.forward) if (cursor_options_.forward)
*s = iterator_->Seek(cursor_options_.low_key); *s = iterator_->Seek(cursor_options_.low_key);
else else
...@@ -2816,9 +3111,14 @@ IndexedDBBackingStore::Cursor::record_identifier() const { ...@@ -2816,9 +3111,14 @@ IndexedDBBackingStore::Cursor::record_identifier() const {
class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor { class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
public: public:
ObjectStoreKeyCursorImpl( ObjectStoreKeyCursorImpl(
LevelDBTransaction* transaction, scoped_refptr<IndexedDBBackingStore> backing_store,
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
: IndexedDBBackingStore::Cursor(transaction, cursor_options) {} : IndexedDBBackingStore::Cursor(backing_store,
transaction,
database_id,
cursor_options) {}
virtual Cursor* Clone() OVERRIDE { virtual Cursor* Clone() OVERRIDE {
return new ObjectStoreKeyCursorImpl(this); return new ObjectStoreKeyCursorImpl(this);
...@@ -2875,9 +3175,14 @@ bool ObjectStoreKeyCursorImpl::LoadCurrentRow() { ...@@ -2875,9 +3175,14 @@ bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor { class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
public: public:
ObjectStoreCursorImpl( ObjectStoreCursorImpl(
LevelDBTransaction* transaction, scoped_refptr<IndexedDBBackingStore> backing_store,
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
: IndexedDBBackingStore::Cursor(transaction, cursor_options) {} : IndexedDBBackingStore::Cursor(backing_store,
transaction,
database_id,
cursor_options) {}
virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); } virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); }
...@@ -2926,6 +3231,11 @@ bool ObjectStoreCursorImpl::LoadCurrentRow() { ...@@ -2926,6 +3231,11 @@ bool ObjectStoreCursorImpl::LoadCurrentRow() {
EncodeIDBKey(*current_key_, &encoded_key); EncodeIDBKey(*current_key_, &encoded_key);
record_identifier_.Reset(encoded_key, version); record_identifier_.Reset(encoded_key, version);
if (!transaction_->GetBlobInfoForRecord(database_id_,
iterator_->Key().as_string(),
&current_value_).ok()) {
return false;
}
current_value_.bits = value_slice.as_string(); current_value_.bits = value_slice.as_string();
return true; return true;
} }
...@@ -2933,9 +3243,14 @@ bool ObjectStoreCursorImpl::LoadCurrentRow() { ...@@ -2933,9 +3243,14 @@ bool ObjectStoreCursorImpl::LoadCurrentRow() {
class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor { class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
public: public:
IndexKeyCursorImpl( IndexKeyCursorImpl(
LevelDBTransaction* transaction, scoped_refptr<IndexedDBBackingStore> backing_store,
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
: IndexedDBBackingStore::Cursor(transaction, cursor_options) {} : IndexedDBBackingStore::Cursor(backing_store,
transaction,
database_id,
cursor_options) {}
virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); } virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); }
...@@ -3008,13 +3323,14 @@ bool IndexKeyCursorImpl::LoadCurrentRow() { ...@@ -3008,13 +3323,14 @@ bool IndexKeyCursorImpl::LoadCurrentRow() {
std::string result; std::string result;
bool found = false; bool found = false;
leveldb::Status s = transaction_->Get(primary_leveldb_key, &result, &found); leveldb::Status s =
transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
if (!s.ok()) { if (!s.ok()) {
INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW); INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false; return false;
} }
if (!found) { if (!found) {
transaction_->Remove(iterator_->Key()); transaction_->transaction()->Remove(iterator_->Key());
return false; return false;
} }
if (!result.size()) { if (!result.size()) {
...@@ -3030,7 +3346,7 @@ bool IndexKeyCursorImpl::LoadCurrentRow() { ...@@ -3030,7 +3346,7 @@ bool IndexKeyCursorImpl::LoadCurrentRow() {
} }
if (object_store_data_version != index_data_version) { if (object_store_data_version != index_data_version) {
transaction_->Remove(iterator_->Key()); transaction_->transaction()->Remove(iterator_->Key());
return false; return false;
} }
...@@ -3040,9 +3356,14 @@ bool IndexKeyCursorImpl::LoadCurrentRow() { ...@@ -3040,9 +3356,14 @@ bool IndexKeyCursorImpl::LoadCurrentRow() {
class IndexCursorImpl : public IndexedDBBackingStore::Cursor { class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
public: public:
IndexCursorImpl( IndexCursorImpl(
LevelDBTransaction* transaction, scoped_refptr<IndexedDBBackingStore> backing_store,
IndexedDBBackingStore::Transaction* transaction,
int64 database_id,
const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options) const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
: IndexedDBBackingStore::Cursor(transaction, cursor_options) {} : IndexedDBBackingStore::Cursor(backing_store,
transaction,
database_id,
cursor_options) {}
virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); } virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); }
...@@ -3108,6 +3429,7 @@ bool IndexCursorImpl::LoadCurrentRow() { ...@@ -3108,6 +3429,7 @@ bool IndexCursorImpl::LoadCurrentRow() {
return false; return false;
} }
DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
primary_leveldb_key_ = primary_leveldb_key_ =
ObjectStoreDataKey::Encode(index_data_key.DatabaseId(), ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
index_data_key.ObjectStoreId(), index_data_key.ObjectStoreId(),
...@@ -3115,13 +3437,14 @@ bool IndexCursorImpl::LoadCurrentRow() { ...@@ -3115,13 +3437,14 @@ bool IndexCursorImpl::LoadCurrentRow() {
std::string result; std::string result;
bool found = false; bool found = false;
leveldb::Status s = transaction_->Get(primary_leveldb_key_, &result, &found); leveldb::Status s =
transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
if (!s.ok()) { if (!s.ok()) {
INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW); INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
return false; return false;
} }
if (!found) { if (!found) {
transaction_->Remove(iterator_->Key()); transaction_->transaction()->Remove(iterator_->Key());
return false; return false;
} }
if (!result.size()) { if (!result.size()) {
...@@ -3137,12 +3460,14 @@ bool IndexCursorImpl::LoadCurrentRow() { ...@@ -3137,12 +3460,14 @@ bool IndexCursorImpl::LoadCurrentRow() {
} }
if (object_store_data_version != index_data_version) { if (object_store_data_version != index_data_version) {
transaction_->Remove(iterator_->Key()); transaction_->transaction()->Remove(iterator_->Key());
return false; return false;
} }
current_value_.bits = slice.as_string(); current_value_.bits = slice.as_string();
return true; return transaction_->GetBlobInfoForRecord(database_id_,
primary_leveldb_key_,
&current_value_).ok();
} }
bool ObjectStoreCursorOptions( bool ObjectStoreCursorOptions(
...@@ -3309,8 +3634,8 @@ IndexedDBBackingStore::OpenObjectStoreCursor( ...@@ -3309,8 +3634,8 @@ IndexedDBBackingStore::OpenObjectStoreCursor(
direction, direction,
&cursor_options)) &cursor_options))
return scoped_ptr<IndexedDBBackingStore::Cursor>(); return scoped_ptr<IndexedDBBackingStore::Cursor>();
scoped_ptr<ObjectStoreCursorImpl> cursor( scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl(
new ObjectStoreCursorImpl(leveldb_transaction, cursor_options)); this, transaction, database_id, cursor_options));
if (!cursor->FirstSeek(s)) if (!cursor->FirstSeek(s))
return scoped_ptr<IndexedDBBackingStore::Cursor>(); return scoped_ptr<IndexedDBBackingStore::Cursor>();
...@@ -3336,8 +3661,8 @@ IndexedDBBackingStore::OpenObjectStoreKeyCursor( ...@@ -3336,8 +3661,8 @@ IndexedDBBackingStore::OpenObjectStoreKeyCursor(
direction, direction,
&cursor_options)) &cursor_options))
return scoped_ptr<IndexedDBBackingStore::Cursor>(); return scoped_ptr<IndexedDBBackingStore::Cursor>();
scoped_ptr<ObjectStoreKeyCursorImpl> cursor( scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl(
new ObjectStoreKeyCursorImpl(leveldb_transaction, cursor_options)); this, transaction, database_id, cursor_options));
if (!cursor->FirstSeek(s)) if (!cursor->FirstSeek(s))
return scoped_ptr<IndexedDBBackingStore::Cursor>(); return scoped_ptr<IndexedDBBackingStore::Cursor>();
...@@ -3366,7 +3691,7 @@ IndexedDBBackingStore::OpenIndexKeyCursor( ...@@ -3366,7 +3691,7 @@ IndexedDBBackingStore::OpenIndexKeyCursor(
&cursor_options)) &cursor_options))
return scoped_ptr<IndexedDBBackingStore::Cursor>(); return scoped_ptr<IndexedDBBackingStore::Cursor>();
scoped_ptr<IndexKeyCursorImpl> cursor( scoped_ptr<IndexKeyCursorImpl> cursor(
new IndexKeyCursorImpl(leveldb_transaction, cursor_options)); new IndexKeyCursorImpl(this, transaction, database_id, cursor_options));
if (!cursor->FirstSeek(s)) if (!cursor->FirstSeek(s))
return scoped_ptr<IndexedDBBackingStore::Cursor>(); return scoped_ptr<IndexedDBBackingStore::Cursor>();
...@@ -3394,7 +3719,7 @@ IndexedDBBackingStore::OpenIndexCursor( ...@@ -3394,7 +3719,7 @@ IndexedDBBackingStore::OpenIndexCursor(
&cursor_options)) &cursor_options))
return scoped_ptr<IndexedDBBackingStore::Cursor>(); return scoped_ptr<IndexedDBBackingStore::Cursor>();
scoped_ptr<IndexCursorImpl> cursor( scoped_ptr<IndexCursorImpl> cursor(
new IndexCursorImpl(leveldb_transaction, cursor_options)); new IndexCursorImpl(this, transaction, database_id, cursor_options));
if (!cursor->FirstSeek(s)) if (!cursor->FirstSeek(s))
return scoped_ptr<IndexedDBBackingStore::Cursor>(); return scoped_ptr<IndexedDBBackingStore::Cursor>();
......
...@@ -308,7 +308,9 @@ class CONTENT_EXPORT IndexedDBBackingStore ...@@ -308,7 +308,9 @@ class CONTENT_EXPORT IndexedDBBackingStore
virtual bool LoadCurrentRow() = 0; virtual bool LoadCurrentRow() = 0;
protected: protected:
Cursor(LevelDBTransaction* transaction, Cursor(scoped_refptr<IndexedDBBackingStore> backing_store,
Transaction* transaction,
int64 database_id,
const CursorOptions& cursor_options); const CursorOptions& cursor_options);
explicit Cursor(const IndexedDBBackingStore::Cursor* other); explicit Cursor(const IndexedDBBackingStore::Cursor* other);
...@@ -319,7 +321,9 @@ class CONTENT_EXPORT IndexedDBBackingStore ...@@ -319,7 +321,9 @@ class CONTENT_EXPORT IndexedDBBackingStore
bool IsPastBounds() const; bool IsPastBounds() const;
bool HaveEnteredRange() const; bool HaveEnteredRange() const;
LevelDBTransaction* transaction_; IndexedDBBackingStore* backing_store_;
Transaction* transaction_;
int64 database_id_;
const CursorOptions cursor_options_; const CursorOptions cursor_options_;
scoped_ptr<LevelDBIterator> iterator_; scoped_ptr<LevelDBIterator> iterator_;
scoped_ptr<IndexedDBKey> current_key_; scoped_ptr<IndexedDBKey> current_key_;
...@@ -399,6 +403,11 @@ class CONTENT_EXPORT IndexedDBBackingStore ...@@ -399,6 +403,11 @@ class CONTENT_EXPORT IndexedDBBackingStore
LevelDBTransaction* transaction() { return transaction_; } LevelDBTransaction* transaction() { return transaction_; }
leveldb::Status GetBlobInfoForRecord(
int64 database_id,
const std::string& object_store_data_key,
IndexedDBValue* value);
// This holds a BlobEntryKey and the encoded IndexedDBBlobInfo vector stored // This holds a BlobEntryKey and the encoded IndexedDBBlobInfo vector stored
// under that key. // under that key.
typedef std::vector<std::pair<BlobEntryKey, std::string> > typedef std::vector<std::pair<BlobEntryKey, std::string> >
...@@ -475,6 +484,8 @@ class CONTENT_EXPORT IndexedDBBackingStore ...@@ -475,6 +484,8 @@ class CONTENT_EXPORT IndexedDBBackingStore
bool is_incognito() const { return !indexed_db_factory_; } bool is_incognito() const { return !indexed_db_factory_; }
bool SetUpMetadata();
virtual bool WriteBlobFile( virtual bool WriteBlobFile(
int64 database_id, int64 database_id,
const Transaction::WriteDescriptor& descriptor, const Transaction::WriteDescriptor& descriptor,
......
...@@ -46,7 +46,8 @@ ...@@ -46,7 +46,8 @@
// <0, 0, 0, 2> => SerializedScriptValue version [DataVersionKey] // <0, 0, 0, 2> => SerializedScriptValue version [DataVersionKey]
// <0, 0, 0, 3> // <0, 0, 0, 3>
// => Blob journal // => Blob journal
// The format of the journal is: {database_id, blobKey}*. // The format of the journal is:
// {database_id (var int), blobKey (var int)}*.
// If the blobKey is kAllBlobsKey, the whole database should be deleted. // If the blobKey is kAllBlobsKey, the whole database should be deleted.
// [BlobJournalKey] // [BlobJournalKey]
// <0, 0, 0, 4> => Live blob journal; same format. [LiveBlobJournalKey] // <0, 0, 0, 4> => Live blob journal; same format. [LiveBlobJournalKey]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment