summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync/syncable/directory_backing_store.h
blob: 36322e839878a9f37bd42230c39185798b2b528d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_SYNC_SYNCABLE_DIRECTORY_BACKING_STORE_H_
#define CHROME_BROWSER_SYNC_SYNCABLE_DIRECTORY_BACKING_STORE_H_

#include <set>
#include <string>

#include "base/file_path.h"
#include "base/gtest_prod_util.h"
#include "chrome/browser/sync/syncable/dir_open_result.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/syncable/syncable.h"

extern "C" {
struct sqlite3;
struct sqlite3_stmt;
}

class SQLStatement;

namespace sync_pb {
class EntitySpecifics;
}

namespace syncable {

struct ColumnSpec;
typedef Directory::MetahandlesIndex MetahandlesIndex;

// Provides sqlite3-based persistence for a syncable::Directory object. You can
// load all the persisted data to prime a syncable::Directory on startup by
// invoking Load.  The only other thing you (or more correctly, a Directory)
// can do here is save any changes that have occurred since calling Load, which
// can be done periodically as often as desired*
//
// * If you only ever use a DirectoryBackingStore (DBS) from a single thread
// then you can stop reading now. This is implemented using sqlite3, which
// requires that each thread accesses a DB via a handle (sqlite3*) opened by
// sqlite_open for that thread and only that thread.  To avoid complicated TLS
// logic to swap handles in-and-out as different threads try to get a hold of a
// DBS, the DBS does two things:
// 1.  Uses a separate handle for Load()ing which is closed as soon as loading
//     finishes, and
// 2.  Requires that SaveChanges *only* be called from a single thread, and that
//     thread *must* be the thread that owns / is responsible for destroying
//     the DBS.
// This way, any thread may open a Directory (which today can be either the
// AuthWatcherThread or SyncCoreThread) and Load its DBS.  The first time
// SaveChanges is called a new sqlite3 handle is created, and it will get closed
// when the DBS is destroyed, which is the reason for the requirement that the
// thread that "uses" the DBS is the thread that destroys it.
class DirectoryBackingStore {
 public:
  DirectoryBackingStore(const std::string& dir_name,
                        const FilePath& backing_filepath);

  virtual ~DirectoryBackingStore();

  // Loads and drops all currently persisted meta entries into |entry_bucket|
  // and loads appropriate persisted kernel info into |info_bucket|.
  // NOTE: On success (return value of OPENED), the buckets are populated with
  // newly allocated items, meaning ownership is bestowed upon the caller.
  DirOpenResult Load(MetahandlesIndex* entry_bucket,
                     Directory::KernelLoadInfo* kernel_load_info);

  // Updates the on-disk store with the input |snapshot| as a database
  // transaction.  Does NOT open any syncable transactions as this would cause
  // opening transactions elsewhere to block on synchronous I/O.
  // DO NOT CALL THIS FROM MORE THAN ONE THREAD EVER.  Also, whichever thread
  // calls SaveChanges *must* be the thread that owns/destroys |this|.
  virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot);

 private:
  FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion67To68);
  FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion68To69);
  FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion69To70);
  FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion70To71);
  FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion71To72);
  FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, ModelTypeIds);
  FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, Corruption);
  FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, DeleteEntries);
  FRIEND_TEST_ALL_PREFIXES(MigrationTest, ToCurrentVersion);
  friend class MigrationTest;

  // General Directory initialization and load helpers.
  DirOpenResult InitializeTables();
  // Returns an sqlite return code, usually SQLITE_DONE.
  int CreateTables();

  // Create 'share_info' or 'temp_share_info' depending on value of
  // is_temporary.  Returns an sqlite return code, SQLITE_DONE on success.
  int CreateShareInfoTable(bool is_temporary);
  // Create 'metas' or 'temp_metas' depending on value of is_temporary.
  // Returns an sqlite return code, SQLITE_DONE on success.
  int CreateMetasTable(bool is_temporary);
  // Returns an sqlite return code, SQLITE_DONE on success.
  int CreateModelsTable();

  // We don't need to load any synced and applied deleted entries, we can
  // in fact just purge them forever on startup.
  bool DropDeletedEntries();
  // Drops a table if it exists, harmless if the table did not already exist.
  int SafeDropTable(const char* table_name);

  // Load helpers for entries and attributes.
  bool LoadEntries(MetahandlesIndex* entry_bucket);
  bool LoadInfo(Directory::KernelLoadInfo* info);

  // Save/update helpers for entries.  Return false if sqlite commit fails.
  bool SaveEntryToDB(const EntryKernel& entry);
  bool SaveNewEntryToDB(const EntryKernel& entry);
  bool UpdateEntryToDB(const EntryKernel& entry);

  // Creates a new sqlite3 handle to the backing database. Sets sqlite operation
  // timeout preferences and registers our overridden sqlite3 operators for
  // said handle.  Returns true on success, false if the sqlite open operation
  // did not succeed.
  bool OpenAndConfigureHandleHelper(sqlite3** handle) const;
  // Initialize and destroy load_dbhandle_.  Broken out for testing.
  bool BeginLoad();
  void EndLoad();

  // Close save_dbhandle_.  Broken out for testing.
  void EndSave();

  // Removes each entry whose metahandle is in |handles| from the database.
  // Does synchronous I/O.  Returns false on error.
  bool DeleteEntries(const MetahandleSet& handles);

  // Lazy creation of save_dbhandle_ for use by SaveChanges code path.
  sqlite3* LazyGetSaveHandle();

  // Drop all tables in preparation for reinitialization.
  void DropAllTables();

  // Serialization helpers for syncable::ModelType.  These convert between
  // the ModelType enum and the values we persist in the database to identify
  // a model.  We persist a default instance of the specifics protobuf as the
  // ID, rather than the enum value.
  static ModelType ModelIdToModelTypeEnum(const string& model_id);
  static string ModelTypeEnumToModelId(ModelType model_type);

  // Migration utilities.
  bool AddColumn(const ColumnSpec* column);
  bool RefreshColumns();
  bool SetVersion(int version);
  int GetVersion();
  bool MigrateToSpecifics(const char* old_columns,
                          const char* specifics_column,
                          void(*handler_function) (
                              SQLStatement* old_value_query,
                              int old_value_column,
                              sync_pb::EntitySpecifics* mutable_new_value));

  // Individual version migrations.
  bool MigrateVersion67To68();
  bool MigrateVersion68To69();
  bool MigrateVersion69To70();
  bool MigrateVersion70To71();
  bool MigrateVersion71To72();

  // The handle to our sqlite on-disk store for initialization and loading, and
  // for saving changes periodically via SaveChanges, respectively.
  // TODO(timsteele): We should only have one handle here.  The reason we need
  // two at the moment is because the DB can be opened by either the AuthWatcher
  // or SyncCore threads, but SaveChanges is always called by the latter.  We
  // need to change initialization so the DB is only accessed from one thread.
  sqlite3* load_dbhandle_;
  sqlite3* save_dbhandle_;

  std::string dir_name_;
  FilePath backing_filepath_;

  // Set to true if migration left some old columns around that need to be
  // discarded.
  bool needs_column_refresh_;

  DISALLOW_COPY_AND_ASSIGN(DirectoryBackingStore);
};

}  // namespace syncable

#endif  // CHROME_BROWSER_SYNC_SYNCABLE_DIRECTORY_BACKING_STORE_H_