summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvishwath@google.com <vishwath@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-09 02:40:35 +0000
committervishwath@google.com <vishwath@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-09 02:40:35 +0000
commit49c24fe8219c35d947e061c2d6aa102f8b04928a (patch)
treec9e1c6e436f4de1009e0428c5af3f3f5cb7f6c3d
parent01fda60d8a2ddcf7e317675364872fbe697fdb54 (diff)
downloadchromium_src-49c24fe8219c35d947e061c2d6aa102f8b04928a.zip
chromium_src-49c24fe8219c35d947e061c2d6aa102f8b04928a.tar.gz
chromium_src-49c24fe8219c35d947e061c2d6aa102f8b04928a.tar.bz2
Changed DB to store node positions as Ordinals.
As part of the effort to move away from int64 based node positions, changed the DB to store server_position_in_node as an ordinal (represented by a varchar) instead. Also updated unittests and the latest db version number (now at 81). BUG=145412 Review URL: https://chromiumcodereview.appspot.com/10989063 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@160774 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--sync/engine/build_commit_command.cc9
-rw-r--r--sync/engine/process_commit_response_command.cc14
-rw-r--r--sync/engine/process_updates_command.cc8
-rw-r--r--sync/engine/syncer.cc11
-rw-r--r--sync/engine/syncer_unittest.cc15
-rw-r--r--sync/engine/syncer_util.cc10
-rw-r--r--sync/syncable/directory.cc19
-rw-r--r--sync/syncable/directory_backing_store.cc86
-rw-r--r--sync/syncable/directory_backing_store.h1
-rw-r--r--sync/syncable/directory_backing_store_unittest.cc295
-rw-r--r--sync/syncable/entry.cc13
-rw-r--r--sync/syncable/entry.h4
-rw-r--r--sync/syncable/entry_kernel.cc9
-rw-r--r--sync/syncable/entry_kernel.h41
-rw-r--r--sync/syncable/mutable_entry.cc29
-rw-r--r--sync/syncable/mutable_entry.h2
-rw-r--r--sync/syncable/syncable_columns.h10
-rw-r--r--sync/syncable/syncable_enum_conversions.cc12
-rw-r--r--sync/syncable/syncable_enum_conversions.h2
-rw-r--r--sync/syncable/syncable_enum_conversions_unittest.cc5
-rw-r--r--sync/syncable/syncable_id.h3
-rw-r--r--sync/syncable/syncable_unittest.cc43
-rw-r--r--sync/test/test_directory_backing_store.h2
23 files changed, 578 insertions, 65 deletions
diff --git a/sync/engine/build_commit_command.cc b/sync/engine/build_commit_command.cc
index 8e51ddb..bfa5b9c 100644
--- a/sync/engine/build_commit_command.cc
+++ b/sync/engine/build_commit_command.cc
@@ -22,6 +22,11 @@
#include "sync/syncable/write_transaction.h"
#include "sync/util/time.h"
+// TODO(vishwath): Remove this include after node positions have
+// shifted to completely using Ordinals.
+// See http://crbug.com/145412 .
+#include "sync/internal_api/public/base/node_ordinal.h"
+
using std::set;
using std::string;
using std::vector;
@@ -31,7 +36,7 @@ namespace syncer {
using sessions::SyncSession;
using syncable::Entry;
using syncable::IS_DEL;
-using syncable::SERVER_POSITION_IN_PARENT;
+using syncable::SERVER_ORDINAL_IN_PARENT;
using syncable::IS_UNAPPLIED_UPDATE;
using syncable::IS_UNSYNCED;
using syncable::Id;
@@ -231,7 +236,7 @@ int64 BuildCommitCommand::FindAnchorPosition(syncable::IdField direction,
syncable::GET_BY_ID,
next_id);
if (!next_entry.Get(IS_UNSYNCED) && !next_entry.Get(IS_UNAPPLIED_UPDATE)) {
- return next_entry.Get(SERVER_POSITION_IN_PARENT);
+ return NodeOrdinalToInt64(next_entry.Get(SERVER_ORDINAL_IN_PARENT));
}
next_id = next_entry.Get(direction);
}
diff --git a/sync/engine/process_commit_response_command.cc b/sync/engine/process_commit_response_command.cc
index 2f84619..200a710 100644
--- a/sync/engine/process_commit_response_command.cc
+++ b/sync/engine/process_commit_response_command.cc
@@ -22,6 +22,11 @@
#include "sync/syncable/write_transaction.h"
#include "sync/util/time.h"
+// TODO(vishwath): Remove this include after node positions have
+// shifted to completely using Ordinals.
+// See http://crbug.com/145412 .
+#include "sync/internal_api/public/base/node_ordinal.h"
+
using std::set;
using std::string;
using std::vector;
@@ -45,7 +50,7 @@ using syncable::IS_UNSYNCED;
using syncable::PARENT_ID;
using syncable::SERVER_IS_DEL;
using syncable::SERVER_PARENT_ID;
-using syncable::SERVER_POSITION_IN_PARENT;
+using syncable::SERVER_ORDINAL_IN_PARENT;
using syncable::SERVER_VERSION;
using syncable::SYNCER;
using syncable::SYNCING;
@@ -368,8 +373,9 @@ void ProcessCommitResponseCommand::UpdateServerFieldsAfterCommit(
ProtoTimeToTime(committed_entry.mtime()));
local_entry->Put(syncable::SERVER_CTIME,
ProtoTimeToTime(committed_entry.ctime()));
- local_entry->Put(syncable::SERVER_POSITION_IN_PARENT,
- entry_response.position_in_parent());
+ local_entry->Put(syncable::SERVER_ORDINAL_IN_PARENT,
+ Int64ToNodeOrdinal(entry_response.position_in_parent()));
+
// TODO(nick): The server doesn't set entry_response.server_parent_id in
// practice; to update SERVER_PARENT_ID appropriately here we'd need to
// get the post-commit ID of the parent indicated by
@@ -417,7 +423,7 @@ void ProcessCommitResponseCommand::OverrideClientFieldsAfterCommit(
if (entry_response.has_position_in_parent()) {
// The SERVER_ field should already have been written.
DCHECK_EQ(entry_response.position_in_parent(),
- local_entry->Get(SERVER_POSITION_IN_PARENT));
+ NodeOrdinalToInt64(local_entry->Get(SERVER_ORDINAL_IN_PARENT)));
// We just committed successfully, so we assume that the position
// value we got applies to the PARENT_ID we submitted.
diff --git a/sync/engine/process_updates_command.cc b/sync/engine/process_updates_command.cc
index de6be0f..0bf03f1 100644
--- a/sync/engine/process_updates_command.cc
+++ b/sync/engine/process_updates_command.cc
@@ -19,6 +19,11 @@
#include "sync/syncable/write_transaction.h"
#include "sync/util/cryptographer.h"
+// TODO(vishwath): Remove this include after node positions have
+// shifted to completely using Ordinals.
+// See http://crbug.com/145412 .
+#include "sync/internal_api/public/base/node_ordinal.h"
+
using std::vector;
namespace syncer {
@@ -152,7 +157,8 @@ ServerUpdateProcessingResult ProcessUpdatesCommand::ProcessUpdate(
(SyncableIdFromProto(update.parent_id_string()) ==
target_entry.Get(syncable::SERVER_PARENT_ID)) &&
(update.position_in_parent() ==
- target_entry.Get(syncable::SERVER_POSITION_IN_PARENT)) &&
+ NodeOrdinalToInt64(
+ target_entry.Get(syncable::SERVER_ORDINAL_IN_PARENT))) &&
update.has_specifics() && update.specifics().has_encrypted() &&
!cryptographer->CanDecrypt(update.specifics().encrypted())) {
sync_pb::EntitySpecifics prev_specifics =
diff --git a/sync/engine/syncer.cc b/sync/engine/syncer.cc
index af8e3a9..8e178cb 100644
--- a/sync/engine/syncer.cc
+++ b/sync/engine/syncer.cc
@@ -27,6 +27,11 @@
#include "sync/syncable/mutable_entry.h"
#include "sync/syncable/syncable-inl.h"
+// TODO(vishwath): Remove this include after node positions have
+// shifted to completely using Ordinals.
+// See http://crbug.com/145412 .
+#include "sync/internal_api/public/base/node_ordinal.h"
+
using base::Time;
using base::TimeDelta;
using sync_pb::ClientCommand;
@@ -43,7 +48,7 @@ using syncable::SERVER_IS_DIR;
using syncable::SERVER_MTIME;
using syncable::SERVER_NON_UNIQUE_NAME;
using syncable::SERVER_PARENT_ID;
-using syncable::SERVER_POSITION_IN_PARENT;
+using syncable::SERVER_ORDINAL_IN_PARENT;
using syncable::SERVER_SPECIFICS;
using syncable::SERVER_VERSION;
@@ -241,7 +246,7 @@ void CopyServerFields(syncable::Entry* src, syncable::MutableEntry* dest) {
dest->Put(SERVER_IS_DEL, src->Get(SERVER_IS_DEL));
dest->Put(IS_UNAPPLIED_UPDATE, src->Get(IS_UNAPPLIED_UPDATE));
dest->Put(SERVER_SPECIFICS, src->Get(SERVER_SPECIFICS));
- dest->Put(SERVER_POSITION_IN_PARENT, src->Get(SERVER_POSITION_IN_PARENT));
+ dest->Put(SERVER_ORDINAL_IN_PARENT, src->Get(SERVER_ORDINAL_IN_PARENT));
}
void ClearServerData(syncable::MutableEntry* entry) {
@@ -254,7 +259,7 @@ void ClearServerData(syncable::MutableEntry* entry) {
entry->Put(SERVER_IS_DEL, false);
entry->Put(IS_UNAPPLIED_UPDATE, false);
entry->Put(SERVER_SPECIFICS, sync_pb::EntitySpecifics::default_instance());
- entry->Put(SERVER_POSITION_IN_PARENT, 0);
+ entry->Put(SERVER_ORDINAL_IN_PARENT, Int64ToNodeOrdinal(0));
}
} // namespace syncer
diff --git a/sync/engine/syncer_unittest.cc b/sync/engine/syncer_unittest.cc
index c57def1..2519a6a 100644
--- a/sync/engine/syncer_unittest.cc
+++ b/sync/engine/syncer_unittest.cc
@@ -33,6 +33,7 @@
#include "sync/engine/traffic_recorder.h"
#include "sync/internal_api/public/base/model_type.h"
#include "sync/internal_api/public/engine/model_safe_worker.h"
+#include "sync/internal_api/public/base/node_ordinal.h"
#include "sync/protocol/bookmark_specifics.pb.h"
#include "sync/protocol/nigori_specifics.pb.h"
#include "sync/protocol/preference_specifics.pb.h"
@@ -96,7 +97,7 @@ using syncable::PREV_ID;
using syncable::BASE_SERVER_SPECIFICS;
using syncable::SERVER_IS_DEL;
using syncable::SERVER_PARENT_ID;
-using syncable::SERVER_POSITION_IN_PARENT;
+using syncable::SERVER_ORDINAL_IN_PARENT;
using syncable::SERVER_SPECIFICS;
using syncable::SERVER_VERSION;
using syncable::UNIQUE_CLIENT_TAG;
@@ -2224,7 +2225,7 @@ TEST_F(SyncerTest, CommitsUpdateDoesntAlterEntry) {
SyncShareNudge();
syncable::Id id;
int64 version;
- int64 server_position_in_parent;
+ NodeOrdinal server_ordinal_in_parent;
{
syncable::ReadTransaction trans(FROM_HERE, directory());
Entry entry(&trans, syncable::GET_BY_HANDLE, entry_metahandle);
@@ -2232,14 +2233,16 @@ TEST_F(SyncerTest, CommitsUpdateDoesntAlterEntry) {
id = entry.Get(ID);
EXPECT_TRUE(id.ServerKnows());
version = entry.Get(BASE_VERSION);
- server_position_in_parent = entry.Get(SERVER_POSITION_IN_PARENT);
+ server_ordinal_in_parent = entry.Get(SERVER_ORDINAL_IN_PARENT);
}
sync_pb::SyncEntity* update = mock_server_->AddUpdateFromLastCommit();
EXPECT_EQ("Pete", update->name());
EXPECT_EQ(id.GetServerId(), update->id_string());
EXPECT_EQ(root_id_.GetServerId(), update->parent_id_string());
EXPECT_EQ(version, update->version());
- EXPECT_EQ(server_position_in_parent, update->position_in_parent());
+ EXPECT_EQ(
+ NodeOrdinalToInt64(server_ordinal_in_parent),
+ update->position_in_parent());
SyncShareNudge();
{
syncable::ReadTransaction trans(FROM_HERE, directory());
@@ -4490,7 +4493,9 @@ class SyncerPositionUpdateTest : public SyncerTest {
Entry entry_with_id(&trans, GET_BY_ID, id);
EXPECT_TRUE(entry_with_id.good());
EXPECT_EQ(prev_id, entry_with_id.Get(PREV_ID));
- EXPECT_EQ(i->first, entry_with_id.Get(SERVER_POSITION_IN_PARENT));
+ EXPECT_EQ(
+ i->first,
+ NodeOrdinalToInt64(entry_with_id.Get(SERVER_ORDINAL_IN_PARENT)));
if (next == position_map_.end()) {
EXPECT_EQ(Id(), entry_with_id.Get(NEXT_ID));
} else {
diff --git a/sync/engine/syncer_util.cc b/sync/engine/syncer_util.cc
index 0dad3f8..cdc1b7a 100644
--- a/sync/engine/syncer_util.cc
+++ b/sync/engine/syncer_util.cc
@@ -29,6 +29,11 @@
#include "sync/util/cryptographer.h"
#include "sync/util/time.h"
+// TODO(vishwath): Remove this include after node positions have
+// shifted to completely uing Ordinals.
+// See http://crbug.com/145412 .
+#include "sync/internal_api/public/base/node_ordinal.h"
+
namespace syncer {
using syncable::BASE_VERSION;
@@ -58,7 +63,7 @@ using syncable::SERVER_IS_DIR;
using syncable::SERVER_MTIME;
using syncable::SERVER_NON_UNIQUE_NAME;
using syncable::SERVER_PARENT_ID;
-using syncable::SERVER_POSITION_IN_PARENT;
+using syncable::SERVER_ORDINAL_IN_PARENT;
using syncable::SERVER_SPECIFICS;
using syncable::SERVER_VERSION;
using syncable::UNIQUE_CLIENT_TAG;
@@ -351,7 +356,8 @@ void UpdateServerFieldsFromUpdate(
target);
}
if (update.has_position_in_parent())
- target->Put(SERVER_POSITION_IN_PARENT, update.position_in_parent());
+ target->Put(SERVER_ORDINAL_IN_PARENT,
+ Int64ToNodeOrdinal(update.position_in_parent()));
target->Put(SERVER_IS_DEL, update.deleted());
// We only mark the entry as unapplied if its version is greater than the
diff --git a/sync/syncable/directory.cc b/sync/syncable/directory.cc
index 644a4e2..4ac17a5 100644
--- a/sync/syncable/directory.cc
+++ b/sync/syncable/directory.cc
@@ -8,6 +8,7 @@
#include "base/perftimer.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
+#include "sync/internal_api/public/base/node_ordinal.h"
#include "sync/internal_api/public/util/unrecoverable_error_handler.h"
#include "sync/syncable/base_transaction.h"
#include "sync/syncable/entry.h"
@@ -50,10 +51,10 @@ bool ParentIdAndHandleIndexer::Comparator::operator() (
if (cmp != 0)
return cmp < 0;
- int64 a_position = a->ref(SERVER_POSITION_IN_PARENT);
- int64 b_position = b->ref(SERVER_POSITION_IN_PARENT);
- if (a_position != b_position)
- return a_position < b_position;
+ const NodeOrdinal& a_position = a->ref(SERVER_ORDINAL_IN_PARENT);
+ const NodeOrdinal& b_position = b->ref(SERVER_ORDINAL_IN_PARENT);
+ if (!a_position.Equals(b_position))
+ return a_position.LessThan(b_position);
cmp = a->ref(ID).compare(b->ref(ID));
return cmp < 0;
@@ -1132,8 +1133,11 @@ Id Directory::ComputePrevIdFromServerPosition(
// Find the natural insertion point in the parent_id_child_index, and
// work back from there, filtering out ineligible candidates.
- ParentIdChildIndex::iterator sibling = LocateInParentChildIndex(lock,
- parent_id, entry->ref(SERVER_POSITION_IN_PARENT), entry->ref(ID));
+ ParentIdChildIndex::iterator sibling = LocateInParentChildIndex(
+ lock,
+ parent_id,
+ NodeOrdinalToInt64(entry->ref(SERVER_ORDINAL_IN_PARENT)),
+ entry->ref(ID));
ParentIdChildIndex::iterator first_sibling =
GetParentChildIndexLowerBound(lock, parent_id);
@@ -1176,7 +1180,8 @@ Directory::ParentIdChildIndex::iterator Directory::LocateInParentChildIndex(
int64 position_in_parent,
const Id& item_id_for_tiebreaking) {
kernel_->needle.put(PARENT_ID, parent_id);
- kernel_->needle.put(SERVER_POSITION_IN_PARENT, position_in_parent);
+ kernel_->needle.put(SERVER_ORDINAL_IN_PARENT,
+ Int64ToNodeOrdinal(position_in_parent));
kernel_->needle.put(ID, item_id_for_tiebreaking);
return kernel_->parent_id_child_index->lower_bound(&kernel_->needle);
}
diff --git a/sync/syncable/directory_backing_store.cc b/sync/syncable/directory_backing_store.cc
index 9b8e0db..fde0279 100644
--- a/sync/syncable/directory_backing_store.cc
+++ b/sync/syncable/directory_backing_store.cc
@@ -22,6 +22,7 @@
#include "sql/connection.h"
#include "sql/statement.h"
#include "sql/transaction.h"
+#include "sync/internal_api/public/base/node_ordinal.h"
#include "sync/protocol/bookmark_specifics.pb.h"
#include "sync/protocol/sync.pb.h"
#include "sync/syncable/syncable-inl.h"
@@ -39,7 +40,7 @@ static const string::size_type kUpdateStatementBufferSize = 2048;
// Increment this version whenever updating DB tables.
extern const int32 kCurrentDBVersion; // Global visibility for our unittest.
-const int32 kCurrentDBVersion = 80;
+const int32 kCurrentDBVersion = 81;
// Iterate over the fields of |entry| and bind each to |statement| for
// updating. Returns the number of args bound.
@@ -69,12 +70,17 @@ void BindFields(const EntryKernel& entry,
entry.ref(static_cast<ProtoField>(i)).SerializeToString(&temp);
statement->BindBlob(index++, temp.data(), temp.length());
}
+ for( ; i < ORDINAL_FIELDS_END; ++i) {
+ temp = entry.ref(static_cast<OrdinalField>(i)).ToInternalValue();
+ statement->BindBlob(index++, temp.data(), temp.length());
+ }
}
// The caller owns the returned EntryKernel*. Assumes the statement currently
-// points to a valid row in the metas table.
-EntryKernel* UnpackEntry(sql::Statement* statement) {
- EntryKernel* kernel = new EntryKernel();
+// points to a valid row in the metas table. Returns NULL to indicate that
+// it detected a corruption in the data on unpacking.
+scoped_ptr<EntryKernel> UnpackEntry(sql::Statement* statement) {
+ scoped_ptr<EntryKernel> kernel(new EntryKernel());
DCHECK_EQ(statement->ColumnCount(), static_cast<int>(FIELD_COUNT));
int i = 0;
for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) {
@@ -99,7 +105,21 @@ EntryKernel* UnpackEntry(sql::Statement* statement) {
kernel->mutable_ref(static_cast<ProtoField>(i)).ParseFromArray(
statement->ColumnBlob(i), statement->ColumnByteLength(i));
}
- return kernel;
+ for( ; i < ORDINAL_FIELDS_END; ++i) {
+ std::string temp;
+ statement->ColumnBlobAsString(i, &temp);
+ NodeOrdinal unpacked_ord(temp);
+
+ // Its safe to assume that an invalid ordinal is a sign that
+ // some external corruption has occurred. Return NULL to force
+ // a re-download of the sync data.
+ if(!unpacked_ord.IsValid()) {
+ DVLOG(1) << "Unpacked invalid ordinal. Signaling that the DB is corrupt";
+ return scoped_ptr<EntryKernel>(NULL);
+ }
+ kernel->mutable_ref(static_cast<OrdinalField>(i)) = unpacked_ord;
+ }
+ return kernel.Pass();
}
namespace {
@@ -124,7 +144,7 @@ string ComposeCreateTableColumnSpecs() {
void AppendColumnList(std::string* output) {
const char* joiner = " ";
// Be explicit in SELECT order to match up with UnpackEntry.
- for (int i = BEGIN_FIELDS; i < BEGIN_FIELDS + FIELD_COUNT; ++i) {
+ for (int i = BEGIN_FIELDS; i < FIELD_COUNT; ++i) {
output->append(joiner);
output->append(ColumnName(i));
joiner = ", ";
@@ -323,6 +343,13 @@ bool DirectoryBackingStore::InitializeTables() {
version_on_disk = 80;
}
+ // Version 81 replaces the int64 server_position_in_parent_field
+ // with a blob server_ordinal_in_parent field.
+ if (version_on_disk == 80) {
+ if (MigrateVersion80To81())
+ version_on_disk = 81;
+ }
+
// If one of the migrations requested it, drop columns that aren't current.
// It's only safe to do this after migrating all the way to the current
// version.
@@ -428,8 +455,11 @@ bool DirectoryBackingStore::LoadEntries(MetahandlesIndex* entry_bucket) {
sql::Statement s(db_->GetUniqueStatement(select.c_str()));
while (s.Step()) {
- EntryKernel *kernel = UnpackEntry(&s);
- entry_bucket->insert(kernel);
+ scoped_ptr<EntryKernel> kernel = UnpackEntry(&s);
+ // A null kernel is evidence of external data corruption.
+ if (!kernel.get())
+ return false;
+ entry_bucket->insert(kernel.release());
}
return s.Succeeded();
}
@@ -503,7 +533,7 @@ bool DirectoryBackingStore::SaveEntryToDB(const EntryKernel& entry) {
values.append("VALUES ");
const char* separator = "( ";
int i = 0;
- for (i = BEGIN_FIELDS; i < PROTO_FIELDS_END; ++i) {
+ for (i = BEGIN_FIELDS; i < FIELD_COUNT; ++i) {
query.append(separator);
values.append(separator);
separator = ", ";
@@ -968,6 +998,7 @@ bool DirectoryBackingStore::MigrateVersion78To79() {
SetVersion(79);
return true;
}
+
bool DirectoryBackingStore::MigrateVersion79To80() {
if (!db_->Execute(
"ALTER TABLE share_info ADD COLUMN bag_of_chips BLOB"))
@@ -982,6 +1013,36 @@ bool DirectoryBackingStore::MigrateVersion79To80() {
return true;
}
+bool DirectoryBackingStore::MigrateVersion80To81() {
+ if(!db_->Execute(
+ "ALTER TABLE metas ADD COLUMN server_ordinal_in_parent BLOB"))
+ return false;
+
+ sql::Statement get_positions(db_->GetUniqueStatement(
+ "SELECT metahandle, server_position_in_parent FROM metas"));
+
+ sql::Statement put_ordinals(db_->GetUniqueStatement(
+ "UPDATE metas SET server_ordinal_in_parent = ?"
+ "WHERE metahandle = ?"));
+
+ while(get_positions.Step()) {
+ int64 metahandle = get_positions.ColumnInt64(0);
+ int64 position = get_positions.ColumnInt64(1);
+
+ const std::string& ordinal = Int64ToNodeOrdinal(position).ToInternalValue();
+ put_ordinals.BindBlob(0, ordinal.data(), ordinal.length());
+ put_ordinals.BindInt64(1, metahandle);
+
+ if(!put_ordinals.Run())
+ return false;
+ put_ordinals.Reset(true);
+ }
+
+ SetVersion(81);
+ needs_column_refresh_ = true;
+ return true;
+}
+
bool DirectoryBackingStore::CreateTables() {
DVLOG(1) << "First run, creating tables";
// Create two little tables share_version and share_info
@@ -1044,10 +1105,13 @@ bool DirectoryBackingStore::CreateTables() {
const int64 now = TimeToProtoTime(base::Time::Now());
sql::Statement s(db_->GetUniqueStatement(
"INSERT INTO metas "
- "( id, metahandle, is_dir, ctime, mtime) "
- "VALUES ( \"r\", 1, 1, ?, ?)"));
+ "( id, metahandle, is_dir, ctime, mtime, server_ordinal_in_parent) "
+ "VALUES ( \"r\", 1, 1, ?, ?, ?)"));
s.BindInt64(0, now);
s.BindInt64(1, now);
+ const std::string ord =
+ NodeOrdinal::CreateInitialOrdinal().ToInternalValue();
+ s.BindBlob(2, ord.data(), ord.length());
if (!s.Run())
return false;
diff --git a/sync/syncable/directory_backing_store.h b/sync/syncable/directory_backing_store.h
index a441a86..21bc8c2 100644
--- a/sync/syncable/directory_backing_store.h
+++ b/sync/syncable/directory_backing_store.h
@@ -155,6 +155,7 @@ class DirectoryBackingStore : public base::NonThreadSafe {
bool MigrateVersion77To78();
bool MigrateVersion78To79();
bool MigrateVersion79To80();
+ bool MigrateVersion80To81();
scoped_ptr<sql::Connection> db_;
sql::Statement save_entry_statement_;
diff --git a/sync/syncable/directory_backing_store_unittest.cc b/sync/syncable/directory_backing_store_unittest.cc
index 1864ba9..7174191 100644
--- a/sync/syncable/directory_backing_store_unittest.cc
+++ b/sync/syncable/directory_backing_store_unittest.cc
@@ -14,6 +14,7 @@
#include "base/string_number_conversions.h"
#include "sql/connection.h"
#include "sql/statement.h"
+#include "sync/internal_api/public/base/node_ordinal.h"
#include "sync/protocol/bookmark_specifics.pb.h"
#include "sync/protocol/sync.pb.h"
#include "sync/syncable/directory_backing_store.h"
@@ -63,6 +64,8 @@ class MigrationTest : public testing::TestWithParam<int> {
void SetUpVersion77Database(sql::Connection* connection);
void SetUpVersion78Database(sql::Connection* connection);
void SetUpVersion79Database(sql::Connection* connection);
+ void SetUpVersion80Database(sql::Connection* connection);
+ void SetUpVersion81Database(sql::Connection* connection);
void SetUpCurrentDatabaseAndCheckVersion(sql::Connection* connection) {
SetUpVersion77Database(connection); // Prepopulates data.
@@ -1635,7 +1638,7 @@ void MigrationTest::SetUpVersion79Database(sql::Connection* connection) {
ASSERT_TRUE(connection->BeginTransaction());
ASSERT_TRUE(connection->Execute(
"CREATE TABLE share_version (id VARCHAR(128) primary key, data INT);"
- "INSERT INTO 'share_version' VALUES('nick@chromium.org',78);"
+ "INSERT INTO 'share_version' VALUES('nick@chromium.org',79);"
"CREATE TABLE models (model_id BLOB primary key, progress_marker BLOB, in"
"itial_sync_ended BOOLEAN default 0);"
"INSERT INTO 'models' VALUES(X'C2881000',X'0888810218B605',1);"
@@ -1725,6 +1728,237 @@ void MigrationTest::SetUpVersion79Database(sql::Connection* connection) {
ASSERT_TRUE(connection->CommitTransaction());
}
+void MigrationTest::SetUpVersion80Database(sql::Connection* connection) {
+ ASSERT_TRUE(connection->is_open());
+ ASSERT_TRUE(connection->BeginTransaction());
+ ASSERT_TRUE(connection->Execute(
+ "CREATE TABLE share_version (id VARCHAR(128) primary key, data INT);"
+ "INSERT INTO 'share_version' VALUES('nick@chromium.org',80);"
+ "CREATE TABLE models (model_id BLOB primary key, progress_marker BLOB, in"
+ "itial_sync_ended BOOLEAN default 0);"
+ "INSERT INTO 'models' VALUES(X'C2881000',X'0888810218B605',1);"
+ "CREATE TABLE 'metas'(metahandle bigint primary key ON CONFLICT FAIL,base"
+ "_version bigint default -1,server_version bigint default 0,server_po"
+ "sition_in_parent bigint default 0,local_external_id bigint default 0"
+ ",mtime bigint default 0,server_mtime bigint default 0,ctime bigint d"
+ "efault 0,server_ctime bigint default 0,id varchar(255) default 'r',p"
+ "arent_id varchar(255) default 'r',server_parent_id varchar(255) defa"
+ "ult 'r',prev_id varchar(255) default 'r',next_id varchar(255) defaul"
+ "t 'r',is_unsynced bit default 0,is_unapplied_update bit default 0,is"
+ "_del bit default 0,is_dir bit default 0,server_is_dir bit default 0,"
+ "server_is_del bit default 0,non_unique_name varchar,server_non_uniqu"
+ "e_name varchar(255),unique_server_tag varchar,unique_client_tag varc"
+ "har,specifics blob,server_specifics blob, base_server_specifics BLOB"
+ ");"
+ "INSERT INTO 'metas' VALUES(1,-1,0,0,0," META_PROTO_TIMES_VALS(1) ",'r','"
+ "r','r','r','r',0,0,0,1,0,0,NULL,NULL,NULL,NULL,X'',X'',NULL);"
+ "INSERT INTO 'metas' VALUES(2,669,669,-2097152,4,"
+ META_PROTO_TIMES_VALS(2) ",'s_ID_2','s_ID_9','s_ID_9','s_ID_2','s_ID_"
+ "2',0,0,1,0,0,1,'Deleted Item','Deleted Item',NULL,NULL,X'C28810220A1"
+ "6687474703A2F2F7777772E676F6F676C652E636F6D2F12084141534741534741',X"
+ "'C28810260A17687474703A2F2F7777772E676F6F676C652E636F6D2F32120B41534"
+ "14447414447414447',NULL);"
+ "INSERT INTO 'metas' VALUES(4,681,681,-3145728,3,"
+ META_PROTO_TIMES_VALS(4) ",'s_ID_4','s_ID_9','s_ID_9','s_ID_4','s_ID_"
+ "4',0,0,1,0,0,1,'Welcome to Chromium','Welcome to Chromium',NULL,NULL"
+ ",X'C28810350A31687474703A2F2F7777772E676F6F676C652E636F6D2F6368726F6"
+ "D652F696E746C2F656E2F77656C636F6D652E68746D6C1200',X'C28810350A31687"
+ "474703A2F2F7777772E676F6F676C652E636F6D2F6368726F6D652F696E746C2F656"
+ "E2F77656C636F6D652E68746D6C1200',NULL);"
+ "INSERT INTO 'metas' VALUES(5,677,677,1048576,7,"
+ META_PROTO_TIMES_VALS(5) ",'s_ID_5','s_ID_9','s_ID_9','s_ID_5','s_ID_"
+ "5',0,0,1,0,0,1,'Google','Google',NULL,NULL,X'C28810220A16687474703A2"
+ "F2F7777772E676F6F676C652E636F6D2F12084147415347415347',X'C28810220A1"
+ "6687474703A2F2F7777772E676F6F676C652E636F6D2F12084147464447415347',N"
+ "ULL);"
+ "INSERT INTO 'metas' VALUES(6,694,694,-4194304,6,"
+ META_PROTO_TIMES_VALS(6) ",'s_ID_6','s_ID_9','s_ID_9','r','r',0,0,0,1"
+ ",1,0,'The Internet','The Internet',NULL,NULL,X'C2881000',X'C2881000'"
+ ",NULL);"
+ "INSERT INTO 'metas' VALUES(7,663,663,1048576,0,"
+ META_PROTO_TIMES_VALS(7) ",'s_ID_7','r','r','r','r',0,0,0,1,1,0,'Goog"
+ "le Chrome','Google Chrome','google_chrome',NULL,NULL,NULL,NULL);"
+ "INSERT INTO 'metas' VALUES(8,664,664,1048576,0,"
+ META_PROTO_TIMES_VALS(8) ",'s_ID_8','s_ID_7','s_ID_7','r','r',0,0,0,1"
+ ",1,0,'Bookmarks','Bookmarks','google_chrome_bookmarks',NULL,X'C28810"
+ "00',X'C2881000',NULL);"
+ "INSERT INTO 'metas' VALUES(9,665,665,1048576,1,"
+ META_PROTO_TIMES_VALS(9) ",'s_ID_9','s_ID_8','s_ID_8','r','s_ID_10',0"
+ ",0,0,1,1,0,'Bookmark Bar','Bookmark Bar','bookmark_bar',NULL,X'C2881"
+ "000',X'C2881000',NULL);"
+ "INSERT INTO 'metas' VALUES(10,666,666,2097152,2,"
+ META_PROTO_TIMES_VALS(10) ",'s_ID_10','s_ID_8','s_ID_8','s_ID_9','r',"
+ "0,0,0,1,1,0,'Other Bookmarks','Other Bookmarks','other_bookmarks',NU"
+ "LL,X'C2881000',X'C2881000',NULL);"
+ "INSERT INTO 'metas' VALUES(11,683,683,-1048576,8,"
+ META_PROTO_TIMES_VALS(11) ",'s_ID_11','s_ID_6','s_ID_6','r','s_ID_13'"
+ ",0,0,0,0,0,0,'Home (The Chromium Projects)','Home (The Chromium Proj"
+ "ects)',NULL,NULL,X'C28810220A18687474703A2F2F6465762E6368726F6D69756"
+ "D2E6F72672F1206414741545741',X'C28810290A1D687474703A2F2F6465762E636"
+ "8726F6D69756D2E6F72672F6F7468657212084146414756415346',NULL);"
+ "INSERT INTO 'metas' VALUES(12,685,685,0,9,"
+ META_PROTO_TIMES_VALS(12) ",'s_ID_12','s_ID_6','s_ID_6','s_ID_13','s_"
+ "ID_14',0,0,0,1,1,0,'Extra Bookmarks','Extra Bookmarks',NULL,NULL,X'C"
+ "2881000',X'C2881000',NULL);"
+ "INSERT INTO 'metas' VALUES(13,687,687,-917504,10,"
+ META_PROTO_TIMES_VALS(13) ",'s_ID_13','s_ID_6','s_ID_6','s_ID_11','s_"
+ "ID_12',0,0,0,0,0,0,'ICANN | Internet Corporation for Assigned Names "
+ "and Numbers','ICANN | Internet Corporation for Assigned Names and Nu"
+ "mbers',NULL,NULL,X'C28810240A15687474703A2F2F7777772E6963616E6E2E636"
+ "F6D2F120B504E474158463041414646',X'C28810200A15687474703A2F2F7777772"
+ "E6963616E6E2E636F6D2F120744414146415346',NULL);"
+ "INSERT INTO 'metas' VALUES(14,692,692,1048576,11,"
+ META_PROTO_TIMES_VALS(14) ",'s_ID_14','s_ID_6','s_ID_6','s_ID_12','r'"
+ ",0,0,0,0,0,0,'The WebKit Open Source Project','The WebKit Open Sourc"
+ "e Project',NULL,NULL,X'C288101A0A12687474703A2F2F7765626B69742E6F726"
+ "72F1204504E4758',X'C288101C0A13687474703A2F2F7765626B69742E6F72672F7"
+ "81205504E473259',NULL);"
+ "CREATE TABLE 'share_info' (id TEXT primary key, name TEXT, store_birthda"
+ "y TEXT, db_create_version TEXT, db_create_time INT, next_id INT defa"
+ "ult -2, cache_guid TEXT , notification_state BLOB, bag_of_chips "
+ "blob);"
+ "INSERT INTO 'share_info' VALUES('nick@chromium.org','nick@chromium.org',"
+ "'c27e9f59-08ca-46f8-b0cc-f16a2ed778bb','Unknown',1263522064,"
+ "-131078,'9010788312004066376x-6609234393368420856x',NULL, NULL);"
+ ));
+ ASSERT_TRUE(connection->CommitTransaction());
+}
+
+
+// Helper definitions to create the version 81 DB tables.
+namespace {
+
+const int V80_ROW_COUNT = 13;
+const int64 V80_POSITIONS[V80_ROW_COUNT] = {
+ 0,
+ -2097152,
+ -3145728,
+ 1048576,
+ -4194304,
+ 1048576,
+ 1048576,
+ 1048576,
+ 2097152,
+ -1048576,
+ 0,
+ -917504,
+ 1048576
+};
+
+std::string V81_Ordinal(int n) {
+ return Int64ToNodeOrdinal(V80_POSITIONS[n]).ToInternalValue();
+}
+
+} //namespace
+
+// Unlike the earlier versions, the rows for version 81 are generated
+// programmatically to accurately handle unprintable characters for the
+// server_ordinal_in_parent field.
+void MigrationTest::SetUpVersion81Database(sql::Connection* connection) {
+ ASSERT_TRUE(connection->is_open());
+ ASSERT_TRUE(connection->BeginTransaction());
+ ASSERT_TRUE(connection->Execute(
+ "CREATE TABLE share_version (id VARCHAR(128) primary key, data INT);"
+ "INSERT INTO 'share_version' VALUES('nick@chromium.org',81);"
+ "CREATE TABLE models (model_id BLOB primary key, progress_marker BLOB, in"
+ "itial_sync_ended BOOLEAN default 0);"
+ "INSERT INTO 'models' VALUES(X'C2881000',X'0888810218B605',1);"
+ "CREATE TABLE 'metas'(metahandle bigint primary key ON CONFLICT FAIL,base"
+ "_version bigint default -1,server_version bigint default 0, "
+ "local_external_id bigint default 0"
+ ",mtime bigint default 0,server_mtime bigint default 0,ctime bigint d"
+ "efault 0,server_ctime bigint default 0,id varchar(255) default 'r',p"
+ "arent_id varchar(255) default 'r',server_parent_id varchar(255) defa"
+ "ult 'r',prev_id varchar(255) default 'r',next_id varchar(255) defaul"
+ "t 'r',is_unsynced bit default 0,is_unapplied_update bit default 0,is"
+ "_del bit default 0,is_dir bit default 0,server_is_dir bit default 0,"
+ "server_is_del bit default 0,non_unique_name varchar,server_non_uniqu"
+ "e_name varchar(255),unique_server_tag varchar,unique_client_tag varc"
+ "har,specifics blob,server_specifics blob, base_server_specifics BLOB"
+ ", server_ordinal_in_parent blob);"
+ "CREATE TABLE 'share_info' (id TEXT primary key, name TEXT, store_birthda"
+ "y TEXT, db_create_version TEXT, db_create_time INT, next_id INT defa"
+ "ult -2, cache_guid TEXT , notification_state BLOB, bag_of_chips "
+ "blob);"
+ "INSERT INTO 'share_info' VALUES('nick@chromium.org','nick@chromium.org',"
+ "'c27e9f59-08ca-46f8-b0cc-f16a2ed778bb','Unknown',1263522064,"
+ "-131078,'9010788312004066376x-6609234393368420856x',NULL, NULL);"));
+
+ const char* insert_stmts[V80_ROW_COUNT] = {
+ "INSERT INTO 'metas' VALUES(1,-1,0,0," META_PROTO_TIMES_VALS(1) ",'r','"
+ "r','r','r','r',0,0,0,1,0,0,NULL,NULL,NULL,NULL,X'',X'',NULL,?);",
+ "INSERT INTO 'metas' VALUES(2,669,669,4,"
+ META_PROTO_TIMES_VALS(2) ",'s_ID_2','s_ID_9','s_ID_9','s_ID_2','s_ID_"
+ "2',0,0,1,0,0,1,'Deleted Item','Deleted Item',NULL,NULL,X'C28810220A1"
+ "6687474703A2F2F7777772E676F6F676C652E636F6D2F12084141534741534741',X"
+ "'C28810260A17687474703A2F2F7777772E676F6F676C652E636F6D2F32120B41534"
+ "14447414447414447',NULL,?);",
+ "INSERT INTO 'metas' VALUES(4,681,681,3,"
+ META_PROTO_TIMES_VALS(4) ",'s_ID_4','s_ID_9','s_ID_9','s_ID_4','s_ID_"
+ "4',0,0,1,0,0,1,'Welcome to Chromium','Welcome to Chromium',NULL,NULL"
+ ",X'C28810350A31687474703A2F2F7777772E676F6F676C652E636F6D2F6368726F6"
+ "D652F696E746C2F656E2F77656C636F6D652E68746D6C1200',X'C28810350A31687"
+ "474703A2F2F7777772E676F6F676C652E636F6D2F6368726F6D652F696E746C2F656"
+ "E2F77656C636F6D652E68746D6C1200',NULL,?);",
+ "INSERT INTO 'metas' VALUES(5,677,677,7,"
+ META_PROTO_TIMES_VALS(5) ",'s_ID_5','s_ID_9','s_ID_9','s_ID_5','s_ID_"
+ "5',0,0,1,0,0,1,'Google','Google',NULL,NULL,X'C28810220A16687474703A2"
+ "F2F7777772E676F6F676C652E636F6D2F12084147415347415347',X'C28810220A1"
+ "6687474703A2F2F7777772E676F6F676C652E636F6D2F12084147464447415347',N"
+ "ULL,?);",
+ "INSERT INTO 'metas' VALUES(6,694,694,6,"
+ META_PROTO_TIMES_VALS(6) ",'s_ID_6','s_ID_9','s_ID_9','r','r',0,0,0,1"
+ ",1,0,'The Internet','The Internet',NULL,NULL,X'C2881000',X'C2881000'"
+ ",NULL,?);",
+ "INSERT INTO 'metas' VALUES(7,663,663,0,"
+ META_PROTO_TIMES_VALS(7) ",'s_ID_7','r','r','r','r',0,0,0,1,1,0,'Goog"
+ "le Chrome','Google Chrome','google_chrome',NULL,NULL,NULL,NULL,?);",
+ "INSERT INTO 'metas' VALUES(8,664,664,0,"
+ META_PROTO_TIMES_VALS(8) ",'s_ID_8','s_ID_7','s_ID_7','r','r',0,0,0,1"
+ ",1,0,'Bookmarks','Bookmarks','google_chrome_bookmarks',NULL,X'C28810"
+ "00',X'C2881000',NULL,?);",
+ "INSERT INTO 'metas' VALUES(9,665,665,1,"
+ META_PROTO_TIMES_VALS(9) ",'s_ID_9','s_ID_8','s_ID_8','r','s_ID_10',0"
+ ",0,0,1,1,0,'Bookmark Bar','Bookmark Bar','bookmark_bar',NULL,X'C2881"
+ "000',X'C2881000',NULL,?);",
+ "INSERT INTO 'metas' VALUES(10,666,666,2,"
+ META_PROTO_TIMES_VALS(10) ",'s_ID_10','s_ID_8','s_ID_8','s_ID_9','r',"
+ "0,0,0,1,1,0,'Other Bookmarks','Other Bookmarks','other_bookmarks',NU"
+ "LL,X'C2881000',X'C2881000',NULL,?);",
+ "INSERT INTO 'metas' VALUES(11,683,683,8,"
+ META_PROTO_TIMES_VALS(11) ",'s_ID_11','s_ID_6','s_ID_6','r','s_ID_13'"
+ ",0,0,0,0,0,0,'Home (The Chromium Projects)','Home (The Chromium Proj"
+ "ects)',NULL,NULL,X'C28810220A18687474703A2F2F6465762E6368726F6D69756"
+ "D2E6F72672F1206414741545741',X'C28810290A1D687474703A2F2F6465762E636"
+ "8726F6D69756D2E6F72672F6F7468657212084146414756415346',NULL,?);",
+ "INSERT INTO 'metas' VALUES(12,685,685,9,"
+ META_PROTO_TIMES_VALS(12) ",'s_ID_12','s_ID_6','s_ID_6','s_ID_13','s_"
+ "ID_14',0,0,0,1,1,0,'Extra Bookmarks','Extra Bookmarks',NULL,NULL,X'C"
+ "2881000',X'C2881000',NULL,?);",
+ "INSERT INTO 'metas' VALUES(13,687,687,10,"
+ META_PROTO_TIMES_VALS(13) ",'s_ID_13','s_ID_6','s_ID_6','s_ID_11','s_"
+ "ID_12',0,0,0,0,0,0,'ICANN | Internet Corporation for Assigned Names "
+ "and Numbers','ICANN | Internet Corporation for Assigned Names and Nu"
+ "mbers',NULL,NULL,X'C28810240A15687474703A2F2F7777772E6963616E6E2E636"
+ "F6D2F120B504E474158463041414646',X'C28810200A15687474703A2F2F7777772"
+ "E6963616E6E2E636F6D2F120744414146415346',NULL,?);",
+ "INSERT INTO 'metas' VALUES(14,692,692,11,"
+ META_PROTO_TIMES_VALS(14) ",'s_ID_14','s_ID_6','s_ID_6','s_ID_12','r'"
+ ",0,0,0,0,0,0,'The WebKit Open Source Project','The WebKit Open Sourc"
+ "e Project',NULL,NULL,X'C288101A0A12687474703A2F2F7765626B69742E6F726"
+ "72F1204504E4758',X'C288101C0A13687474703A2F2F7765626B69742E6F72672F7"
+ "81205504E473259',NULL,?);" };
+
+ for (int i = 0; i < V80_ROW_COUNT; i++) {
+ sql::Statement s(connection->GetUniqueStatement(insert_stmts[i]));
+ std::string ord = V81_Ordinal(i);
+ s.BindBlob(0, ord.data(), ord.length());
+ ASSERT_TRUE(s.Run());
+ s.Reset(true);
+ }
+ ASSERT_TRUE(connection->CommitTransaction());
+}
+
TEST_F(DirectoryBackingStoreTest, MigrateVersion67To68) {
sql::Connection connection;
ASSERT_TRUE(connection.OpenInMemory());
@@ -2050,6 +2284,7 @@ TEST_F(DirectoryBackingStoreTest, MigrateVersion78To79) {
STLElementDeleter<MetahandlesIndex> deleter(&entry_bucket);
Directory::KernelLoadInfo load_info;
+ s.Clear();
ASSERT_TRUE(dbs->Load(&entry_bucket, &load_info));
EXPECT_LE(load_info.kernel_info.next_id, kInitialNextId - 65536);
}
@@ -2079,6 +2314,61 @@ TEST_F(DirectoryBackingStoreTest, MigrateVersion79To80) {
EXPECT_EQ(serialized_chip_bag, load_info.kernel_info.bag_of_chips);
}
+TEST_F(DirectoryBackingStoreTest, MigrateVersion80To81) {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.OpenInMemory());
+ SetUpVersion80Database(&connection);
+
+ sql::Statement s(connection.GetUniqueStatement(
+ "SELECT metahandle, server_position_in_parent "
+ "FROM metas WHERE unique_server_tag = 'google_chrome'"));
+ ASSERT_TRUE(s.Step());
+ ASSERT_EQ(sql::COLUMN_TYPE_INTEGER, s.ColumnType(1));
+
+ scoped_ptr<TestDirectoryBackingStore> dbs(
+ new TestDirectoryBackingStore(GetUsername(), &connection));
+ ASSERT_TRUE(dbs->MigrateVersion80To81());
+ ASSERT_EQ(81, dbs->GetVersion());
+
+ // Test that ordinal values are preserved correctly.
+ sql::Statement new_s(connection.GetUniqueStatement(
+ "SELECT metahandle, server_ordinal_in_parent "
+ "FROM metas WHERE unique_server_tag = 'google_chrome'"));
+ ASSERT_TRUE(new_s.Step());
+ ASSERT_EQ(sql::COLUMN_TYPE_BLOB, new_s.ColumnType(1));
+
+ std::string expected_ordinal = Int64ToNodeOrdinal(1048576).ToInternalValue();
+ std::string actual_ordinal;
+ new_s.ColumnBlobAsString(1, &actual_ordinal);
+ ASSERT_EQ(expected_ordinal, actual_ordinal);
+}
+
+TEST_F(DirectoryBackingStoreTest, DetectInvalidOrdinal) {
+ sql::Connection connection;
+ ASSERT_TRUE(connection.OpenInMemory());
+ SetUpVersion81Database(&connection);
+
+ scoped_ptr<TestDirectoryBackingStore> dbs(
+ new TestDirectoryBackingStore(GetUsername(), &connection));
+ ASSERT_EQ(81, dbs->GetVersion());
+
+ // Insert row with bad ordinal.
+ const int64 now = TimeToProtoTime(base::Time::Now());
+ sql::Statement s(connection.GetUniqueStatement(
+ "INSERT INTO metas "
+ "( id, metahandle, is_dir, ctime, mtime, server_ordinal_in_parent) "
+ "VALUES( \"c-invalid\", 9999, 1, ?, ?, \" \")"));
+ s.BindInt64(0, now);
+ s.BindInt64(1, now);
+ ASSERT_TRUE(s.Run());
+
+ // Trying to unpack this entry should signal that the DB is corrupted.
+ MetahandlesIndex entry_bucket;
+ Directory::KernelLoadInfo kernel_load_info;
+ ASSERT_EQ(FAILED_DATABASE_CORRUPT,
+ dbs->Load(&entry_bucket, &kernel_load_info));
+}
+
TEST_P(MigrationTest, ToCurrentVersion) {
sql::Connection connection;
ASSERT_TRUE(connection.OpenInMemory());
@@ -2122,6 +2412,9 @@ TEST_P(MigrationTest, ToCurrentVersion) {
case 79:
SetUpVersion79Database(&connection);
break;
+ case 80:
+ SetUpVersion80Database(&connection);
+ break;
default:
// If you see this error, it may mean that you've increased the
// database version number but you haven't finished adding unit tests
diff --git a/sync/syncable/entry.cc b/sync/syncable/entry.cc
index ba64299..5593426 100644
--- a/sync/syncable/entry.cc
+++ b/sync/syncable/entry.cc
@@ -6,7 +6,7 @@
#include <iomanip>
-#include "net/base/escape.h"
+#include "base/json/string_escape.h"
#include "sync/syncable/base_transaction.h"
#include "sync/syncable/blob.h"
#include "sync/syncable/directory.h"
@@ -127,9 +127,16 @@ std::ostream& operator<<(std::ostream& os, const Entry& entry) {
os << g_metas_columns[i].name << ": " << field << ", ";
}
for ( ; i < PROTO_FIELDS_END; ++i) {
+ std::string escaped_str;
+ base::JsonDoubleQuote(
+ kernel->ref(static_cast<ProtoField>(i)).SerializeAsString(),
+ false,
+ &escaped_str);
+ os << g_metas_columns[i].name << ": " << escaped_str << ", ";
+ }
+ for ( ; i < ORDINAL_FIELDS_END; ++i) {
os << g_metas_columns[i].name << ": "
- << net::EscapePath(
- kernel->ref(static_cast<ProtoField>(i)).SerializeAsString())
+ << kernel->ref(static_cast<OrdinalField>(i)).ToDebugString()
<< ", ";
}
os << "TempFlags: ";
diff --git a/sync/syncable/entry.h b/sync/syncable/entry.h
index a755758..1a2609c 100644
--- a/sync/syncable/entry.h
+++ b/sync/syncable/entry.h
@@ -91,6 +91,10 @@ class Entry {
DCHECK(kernel_);
return kernel_->ref(field);
}
+ inline const NodeOrdinal& Get(OrdinalField field) const {
+ DCHECK(kernel_);
+ return kernel_->ref(field);
+ }
inline bool Get(BitTemp field) const {
DCHECK(kernel_);
return kernel_->ref(field);
diff --git a/sync/syncable/entry_kernel.cc b/sync/syncable/entry_kernel.cc
index 550fbb2..ab3de95 100644
--- a/sync/syncable/entry_kernel.cc
+++ b/sync/syncable/entry_kernel.cc
@@ -69,6 +69,10 @@ StringValue* IdToValue(const Id& id) {
return id.ToValue();
}
+StringValue* OrdinalToValue(const NodeOrdinal& ord) {
+ return Value::CreateStringValue(ord.ToDebugString());
+}
+
} // namespace
DictionaryValue* EntryKernel::ToValue() const {
@@ -123,6 +127,11 @@ DictionaryValue* EntryKernel::ToValue() const {
&GetProtoFieldString, &EntitySpecificsToValue,
PROTO_FIELDS_BEGIN, PROTO_FIELDS_END - 1);
+ // Ordinal fields
+ SetFieldValues(*this, kernel_info,
+ &GetOrdinalFieldString, &OrdinalToValue,
+ ORDINAL_FIELDS_BEGIN, ORDINAL_FIELDS_END - 1);
+
// Bit temps.
SetFieldValues(*this, kernel_info,
&GetBitTempString, &Value::CreateBooleanValue,
diff --git a/sync/syncable/entry_kernel.h b/sync/syncable/entry_kernel.h
index 383ffd7..e8c0924 100644
--- a/sync/syncable/entry_kernel.h
+++ b/sync/syncable/entry_kernel.h
@@ -8,6 +8,7 @@
#include "base/time.h"
#include "base/values.h"
#include "sync/internal_api/public/base/model_type.h"
+#include "sync/internal_api/public/base/node_ordinal.h"
#include "sync/internal_api/public/util/immutable.h"
#include "sync/protocol/sync.pb.h"
#include "sync/syncable/metahandle_set.h"
@@ -21,8 +22,8 @@ namespace syncable {
// - EntryKernel struct in this file
// - syncable_columns.h
// - syncable_enum_conversions{.h,.cc,_unittest.cc}
-// - EntryKernel::EntryKernel(), EntryKernel::ToValue(), operator<<
-// for Entry in syncable.cc
+// - EntryKernel::EntryKernel(), EntryKernel::ToValue() in entry_kernel.cc
+// - operator<< in Entry.cc
// - BindFields() and UnpackEntry() in directory_backing_store.cc
// - TestSimpleFieldsPreservedDuringSaveChanges in syncable_unittest.cc
@@ -47,14 +48,8 @@ enum BaseVersion {
enum Int64Field {
SERVER_VERSION = BASE_VERSION + 1,
-
- // A numeric position value that indicates the relative ordering of
- // this object among its siblings.
- SERVER_POSITION_IN_PARENT,
-
LOCAL_EXTERNAL_ID, // ID of an item in the external local storage that this
// entry is associated with. (such as bookmarks.js)
-
INT64_FIELDS_END
};
@@ -143,9 +138,22 @@ enum ProtoField {
};
enum {
- FIELD_COUNT = PROTO_FIELDS_END,
+ PROTO_FIELDS_COUNT = PROTO_FIELDS_END - PROTO_FIELDS_BEGIN,
+ ORDINAL_FIELDS_BEGIN = PROTO_FIELDS_END
+};
+
+enum OrdinalField {
+ // An Ordinal that identifies the relative ordering of this object
+ // among its siblings.
+ SERVER_ORDINAL_IN_PARENT = ORDINAL_FIELDS_BEGIN,
+ ORDINAL_FIELDS_END
+};
+
+enum {
+ ORDINAL_FIELDS_COUNT = ORDINAL_FIELDS_END - ORDINAL_FIELDS_BEGIN,
+ FIELD_COUNT = ORDINAL_FIELDS_END - BEGIN_FIELDS,
// Past this point we have temporaries, stored in memory only.
- BEGIN_TEMPS = PROTO_FIELDS_END,
+ BEGIN_TEMPS = ORDINAL_FIELDS_END,
BIT_TEMPS_BEGIN = BEGIN_TEMPS,
};
@@ -160,9 +168,6 @@ enum {
BIT_TEMPS_COUNT = BIT_TEMPS_END - BIT_TEMPS_BEGIN
};
-enum {
- PROTO_FIELDS_COUNT = PROTO_FIELDS_END - PROTO_FIELDS_BEGIN
-};
struct EntryKernel {
@@ -172,6 +177,7 @@ struct EntryKernel {
int64 int64_fields[INT64_FIELDS_COUNT];
base::Time time_fields[TIME_FIELDS_COUNT];
Id id_fields[ID_FIELDS_COUNT];
+ NodeOrdinal ordinal_fields[ORDINAL_FIELDS_COUNT];
std::bitset<BIT_FIELDS_COUNT> bit_fields;
std::bitset<BIT_TEMPS_COUNT> bit_temps;
@@ -239,6 +245,9 @@ struct EntryKernel {
inline void put(ProtoField field, const sync_pb::EntitySpecifics& value) {
specifics_fields[field - PROTO_FIELDS_BEGIN].CopyFrom(value);
}
+ inline void put(OrdinalField field, const NodeOrdinal& value) {
+ ordinal_fields[field - ORDINAL_FIELDS_BEGIN] = value;
+ }
inline void put(BitTemp field, bool value) {
bit_temps[field - BIT_TEMPS_BEGIN] = value;
}
@@ -274,6 +283,9 @@ struct EntryKernel {
inline const sync_pb::EntitySpecifics& ref(ProtoField field) const {
return specifics_fields[field - PROTO_FIELDS_BEGIN];
}
+ inline const NodeOrdinal& ref(OrdinalField field) const {
+ return ordinal_fields[field - ORDINAL_FIELDS_BEGIN];
+ }
inline bool ref(BitTemp field) const {
return bit_temps[field - BIT_TEMPS_BEGIN];
}
@@ -288,6 +300,9 @@ struct EntryKernel {
inline Id& mutable_ref(IdField field) {
return id_fields[field - ID_FIELDS_BEGIN];
}
+ inline NodeOrdinal& mutable_ref(OrdinalField field) {
+ return ordinal_fields[field - ORDINAL_FIELDS_BEGIN];
+ }
ModelType GetServerModelType() const;
diff --git a/sync/syncable/mutable_entry.cc b/sync/syncable/mutable_entry.cc
index 869320f..5090ad1a9 100644
--- a/sync/syncable/mutable_entry.cc
+++ b/sync/syncable/mutable_entry.cc
@@ -5,6 +5,7 @@
#include "sync/syncable/mutable_entry.h"
#include "base/memory/scoped_ptr.h"
+#include "sync/internal_api/public/base/node_ordinal.h"
#include "sync/syncable/directory.h"
#include "sync/syncable/scoped_index_updater.h"
#include "sync/syncable/scoped_kernel_lock.h"
@@ -41,6 +42,7 @@ void MutableEntry::Init(WriteTransaction* trans, const Id& parent_id,
kernel->put(MTIME, now);
// We match the database defaults here
kernel->put(BASE_VERSION, CHANGES_VERSION);
+ kernel->put(SERVER_ORDINAL_IN_PARENT, NodeOrdinal::CreateInitialOrdinal());
if (!trans->directory()->InsertEntry(trans, kernel.get())) {
return; // We failed inserting, nothing more to do.
}
@@ -66,6 +68,7 @@ MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem,
kernel->put(ID, id);
kernel->put(META_HANDLE, trans->directory_->NextMetahandle());
kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles);
+ kernel->put(SERVER_ORDINAL_IN_PARENT, NodeOrdinal::CreateInitialOrdinal());
kernel->put(IS_DEL, true);
// We match the database defaults here
kernel->put(BASE_VERSION, CHANGES_VERSION);
@@ -146,13 +149,7 @@ bool MutableEntry::Put(Int64Field field, const int64& value) {
write_transaction_->SaveOriginal(kernel_);
if (kernel_->ref(field) != value) {
ScopedKernelLock lock(dir());
- if (SERVER_POSITION_IN_PARENT == field) {
- ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(lock, kernel_,
- dir()->kernel_->parent_id_child_index);
- kernel_->put(field, value);
- } else {
- kernel_->put(field, value);
- }
+ kernel_->put(field, value);
kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
}
return true;
@@ -190,6 +187,24 @@ bool MutableEntry::Put(IdField field, const Id& value) {
return true;
}
+bool MutableEntry::Put(OrdinalField field, const NodeOrdinal& value) {
+ DCHECK(kernel_);
+ DCHECK(value.IsValid());
+ write_transaction_->SaveOriginal(kernel_);
+ if(!kernel_->ref(field).Equals(value)) {
+ ScopedKernelLock lock(dir());
+ if (SERVER_ORDINAL_IN_PARENT == field) {
+ ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(
+ lock, kernel_, dir()->kernel_->parent_id_child_index);
+ kernel_->put(field, value);
+ } else {
+ kernel_->put(field, value);
+ }
+ kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
+ }
+ return true;
+}
+
void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) {
write_transaction_->SaveOriginal(kernel_);
dir()->ReindexParentId(write_transaction(), kernel_, parent_id);
diff --git a/sync/syncable/mutable_entry.h b/sync/syncable/mutable_entry.h
index 60968e9..f01fcf6 100644
--- a/sync/syncable/mutable_entry.h
+++ b/sync/syncable/mutable_entry.h
@@ -5,6 +5,7 @@
#ifndef SYNC_SYNCABLE_MUTABLE_ENTRY_H_
#define SYNC_SYNCABLE_MUTABLE_ENTRY_H_
+#include "sync/internal_api/public/base/node_ordinal.h"
#include "sync/syncable/entry.h"
#include "sync/syncable/metahandle_set.h"
@@ -49,6 +50,7 @@ class MutableEntry : public Entry {
bool Put(Int64Field field, const int64& value);
bool Put(TimeField field, const base::Time& value);
bool Put(IdField field, const Id& value);
+ bool Put(OrdinalField field, const NodeOrdinal& value);
// Do a simple property-only update if the PARENT_ID field. Use with caution.
//
diff --git a/sync/syncable/syncable_columns.h b/sync/syncable/syncable_columns.h
index a6152c4..f5d48e0 100644
--- a/sync/syncable/syncable_columns.h
+++ b/sync/syncable/syncable_columns.h
@@ -16,14 +16,13 @@ struct ColumnSpec {
const char* spec;
};
-// Must be in exact same order as fields in syncable.
+// Must be in exact same order as fields in entry_kernel.h.
static const ColumnSpec g_metas_columns[] = {
//////////////////////////////////////
// int64s
{"metahandle", "bigint primary key ON CONFLICT FAIL"},
{"base_version", "bigint default " CHANGES_VERSION_STRING},
{"server_version", "bigint default 0"},
- {"server_position_in_parent", "bigint default 0"},
// This is the item ID that we store for the embedding application.
{"local_external_id", "bigint default 0"},
// These timestamps are kept in the same format as that of the
@@ -54,10 +53,13 @@ static const ColumnSpec g_metas_columns[] = {
{"unique_server_tag", "varchar"},
{"unique_client_tag", "varchar"},
//////////////////////////////////////
- // Blobs.
+ // Blobs (serialized protos).
{"specifics", "blob"},
{"server_specifics", "blob"},
- {"base_server_specifics", "blob"}
+ {"base_server_specifics", "blob"},
+ //////////////////////////////////////
+ // Blobs (ordinals).
+ {"server_ordinal_in_parent", "blob"},
};
// At least enforce that there are equal number of column names and fields.
diff --git a/sync/syncable/syncable_enum_conversions.cc b/sync/syncable/syncable_enum_conversions.cc
index 9a8b892..3fdd7a8 100644
--- a/sync/syncable/syncable_enum_conversions.cc
+++ b/sync/syncable/syncable_enum_conversions.cc
@@ -49,7 +49,6 @@ const char* GetInt64FieldString(Int64Field int64_field) {
BASE_VERSION + 1, INT64_FIELDS_END - 1);
switch (int64_field) {
ENUM_CASE(SERVER_VERSION);
- ENUM_CASE(SERVER_POSITION_IN_PARENT);
ENUM_CASE(LOCAL_EXTERNAL_ID);
case INT64_FIELDS_END: break;
}
@@ -148,6 +147,17 @@ const char* GetProtoFieldString(ProtoField proto_field) {
return "";
}
+const char* GetOrdinalFieldString(OrdinalField ordinal_field) {
+ ASSERT_ENUM_BOUNDS(SERVER_ORDINAL_IN_PARENT, SERVER_ORDINAL_IN_PARENT,
+ ORDINAL_FIELDS_BEGIN, ORDINAL_FIELDS_END - 1);
+ switch(ordinal_field) {
+ ENUM_CASE(SERVER_ORDINAL_IN_PARENT);
+ case ORDINAL_FIELDS_END: break;
+ }
+ NOTREACHED();
+ return "";
+}
+
const char* GetBitTempString(BitTemp bit_temp) {
ASSERT_ENUM_BOUNDS(SYNCING, SYNCING,
BIT_TEMPS_BEGIN, BIT_TEMPS_END - 1);
diff --git a/sync/syncable/syncable_enum_conversions.h b/sync/syncable/syncable_enum_conversions.h
index 6127a33..5ab66f0 100644
--- a/sync/syncable/syncable_enum_conversions.h
+++ b/sync/syncable/syncable_enum_conversions.h
@@ -38,6 +38,8 @@ const char* GetStringFieldString(StringField string_field);
const char* GetProtoFieldString(ProtoField proto_field);
+const char* GetOrdinalFieldString(OrdinalField ordinal_field);
+
const char* GetBitTempString(BitTemp bit_temp);
} // namespace syncable
diff --git a/sync/syncable/syncable_enum_conversions_unittest.cc b/sync/syncable/syncable_enum_conversions_unittest.cc
index 636d555..eac4a37 100644
--- a/sync/syncable/syncable_enum_conversions_unittest.cc
+++ b/sync/syncable/syncable_enum_conversions_unittest.cc
@@ -77,6 +77,11 @@ TEST_F(SyncableEnumConversionsTest, GetProtoFieldString) {
GetProtoFieldString, PROTO_FIELDS_BEGIN, PROTO_FIELDS_END - 1);
}
+TEST_F(SyncableEnumConversionsTest, GetOrdinalFieldString) {
+ TestEnumStringFunction(
+ GetOrdinalFieldString, ORDINAL_FIELDS_BEGIN, ORDINAL_FIELDS_END - 1);
+}
+
TEST_F(SyncableEnumConversionsTest, GetBitTempString) {
TestEnumStringFunction(
GetBitTempString, BIT_TEMPS_BEGIN, BIT_TEMPS_END - 1);
diff --git a/sync/syncable/syncable_id.h b/sync/syncable/syncable_id.h
index 95157ab..64fc7f6 100644
--- a/sync/syncable/syncable_id.h
+++ b/sync/syncable/syncable_id.h
@@ -10,6 +10,7 @@
#include <sstream>
#include <string>
+#include "base/memory/scoped_ptr.h"
#include "base/hash_tables.h"
class MockConnectionManager;
@@ -111,7 +112,7 @@ class Id {
static Id GetLeastIdForLexicographicComparison();
private:
- friend EntryKernel* UnpackEntry(sql::Statement* statement);
+ friend scoped_ptr<EntryKernel> UnpackEntry(sql::Statement* statement);
friend void BindFields(const EntryKernel& entry,
sql::Statement* statement);
friend std::ostream& operator<<(std::ostream& out, const Id& id);
diff --git a/sync/syncable/syncable_unittest.cc b/sync/syncable/syncable_unittest.cc
index f69347b..65cedc3 100644
--- a/sync/syncable/syncable_unittest.cc
+++ b/sync/syncable/syncable_unittest.cc
@@ -4,6 +4,7 @@
#include <string>
+#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/file_path.h"
#include "base/file_util.h"
@@ -17,6 +18,7 @@
#include "base/test/values_test_util.h"
#include "base/threading/platform_thread.h"
#include "base/values.h"
+#include "sync/internal_api/public/base/node_ordinal.h"
#include "sync/protocol/bookmark_specifics.pb.h"
#include "sync/syncable/directory_backing_store.h"
#include "sync/syncable/directory_change_delegate.h"
@@ -1366,6 +1368,39 @@ TEST_F(SyncableDirectoryTest, OldClientLeftUnsyncedDeletedLocalItem) {
}
}
+TEST_F(SyncableDirectoryTest, OrdinalWithNullSurvivesSaveAndReload) {
+ TestIdFactory id_factory;
+ Id null_child_id;
+ const char null_cstr[] = "\0null\0test";
+ std::string null_str(null_cstr, arraysize(null_cstr) - 1);
+ NodeOrdinal null_ord = NodeOrdinal(null_str);
+
+ {
+ WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get());
+
+ MutableEntry parent(&trans, CREATE, id_factory.root(), "parent");
+ parent.Put(IS_DIR, true);
+ parent.Put(IS_UNSYNCED, true);
+
+ MutableEntry child(&trans, CREATE, parent.Get(ID), "child");
+ child.Put(IS_UNSYNCED, true);
+ child.Put(SERVER_ORDINAL_IN_PARENT, null_ord);
+
+ null_child_id = child.Get(ID);
+ }
+
+ EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
+
+ {
+ ReadTransaction trans(FROM_HERE, dir_.get());
+
+ Entry null_ordinal_child(&trans, GET_BY_ID, null_child_id);
+ EXPECT_TRUE(
+ null_ord.Equals(null_ordinal_child.Get(SERVER_ORDINAL_IN_PARENT)));
+ }
+
+}
+
// An OnDirectoryBackingStore that can be set to always fail SaveChanges.
class TestBackingStore : public OnDiskDirectoryBackingStore {
public:
@@ -1714,6 +1749,14 @@ TEST_F(OnDiskSyncableDirectoryTest,
update_post_save.ref((ProtoField)i).SerializeAsString())
<< "Blob field #" << i << " changed during save/load";
}
+ for ( ; i < ORDINAL_FIELDS_END; ++i) {
+ EXPECT_EQ(create_pre_save.ref((OrdinalField)i).ToInternalValue(),
+ create_post_save.ref((OrdinalField)i).ToInternalValue())
+ << "Blob field #" << i << " changed during save/load";
+ EXPECT_EQ(update_pre_save.ref((OrdinalField)i).ToInternalValue(),
+ update_post_save.ref((OrdinalField)i).ToInternalValue())
+ << "Blob field #" << i << " changed during save/load";
+ }
}
TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailure) {
diff --git a/sync/test/test_directory_backing_store.h b/sync/test/test_directory_backing_store.h
index 612e728..b0e230a 100644
--- a/sync/test/test_directory_backing_store.h
+++ b/sync/test/test_directory_backing_store.h
@@ -42,6 +42,8 @@ class TestDirectoryBackingStore : public DirectoryBackingStore {
FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion77To78);
FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion78To79);
FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion79To80);
+ FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion80To81);
+ FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, DetectInvalidOrdinal);
FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, ModelTypeIds);
FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, Corruption);
FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, DeleteEntries);