1 /* LBS: An LFS-inspired filesystem backup system
2 * Copyright (C) 2007 Michael Vrable
4 * When creating backup snapshots, maintain a local database of data blocks and
5 * checksums, in addition to the data contents (which may be stored remotely).
6 * This database is consulted when attempting to build incremental snapshots,
7 * as it says which objects can be reused.
9 * The database is implemented as an SQLite3 database, but this implementation
10 * detail is kept internal to this file, so that the storage format may be
25 void LocalDb::Open(const char *path, const char *snapshot_name)
29 rc = sqlite3_open(path, &db);
31 fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
33 throw IOException("Error opening local database");
36 rc = sqlite3_exec(db, "begin", NULL, NULL, NULL);
38 fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
40 throw IOException("Error starting transaction");
43 /* Insert this snapshot into the database, and determine the integer key
44 * which will be used to identify it. */
46 static const char s[] =
47 "insert into snapshots(name, timestamp) "
48 "values (?, julianday('now'))";
51 rc = sqlite3_prepare_v2(db, s, strlen(s), &stmt, &tail);
52 if (rc != SQLITE_OK) {
54 throw IOException("Error adding snapshot");
57 sqlite3_bind_text(stmt, 1, snapshot_name, strlen(snapshot_name),
60 rc = sqlite3_step(stmt);
61 if (rc != SQLITE_DONE) {
63 throw IOException("Database execution error!");
66 snapshotid = sqlite3_last_insert_rowid(db);
67 sqlite3_finalize(stmt);
68 if (snapshotid == 0) {
70 throw IOException("Find snapshot id");
77 rc = sqlite3_exec(db, "commit", NULL, NULL, NULL);
78 if (rc != SQLITE_OK) {
79 fprintf(stderr, "Can't commit database!\n");
84 int64_t LocalDb::SegmentToId(const string &segment)
88 static const char s1[] =
89 "insert or ignore into segments(segment) values (?);";
90 static const char s2[] =
91 "select segmentid from segments where segment = ?";
95 rc = sqlite3_prepare_v2(db, s1, strlen(s1), &stmt, &tail);
96 if (rc != SQLITE_OK) {
97 throw IOException("Find id by segment name");
99 sqlite3_bind_text(stmt, 1, segment.c_str(), segment.size(),
101 rc = sqlite3_step(stmt);
102 if (rc != SQLITE_DONE) {
103 throw IOException("Could not execute INSERT statement!");
105 sqlite3_finalize(stmt);
107 rc = sqlite3_prepare_v2(db, s2, strlen(s2), &stmt, &tail);
108 if (rc != SQLITE_OK) {
109 throw IOException("Find id by segment name");
112 sqlite3_bind_text(stmt, 1, segment.c_str(), segment.size(),
115 rc = sqlite3_step(stmt);
116 if (rc == SQLITE_DONE) {
117 throw IOException("No segment found by id");
118 } else if (rc == SQLITE_ROW) {
119 result = sqlite3_column_int64(stmt, 0);
121 throw IOException("Error executing find segment by id query");
124 sqlite3_finalize(stmt);
129 string LocalDb::IdToSegment(int64_t segmentid)
133 static const char s[] =
134 "select segment from segments where segmentid = ?";
138 rc = sqlite3_prepare_v2(db, s, strlen(s), &stmt, &tail);
139 if (rc != SQLITE_OK) {
140 throw IOException("Find segment by id");
143 sqlite3_bind_int64(stmt, 1, segmentid);
145 rc = sqlite3_step(stmt);
146 if (rc == SQLITE_DONE) {
147 throw IOException("No segment found by id");
148 } else if (rc == SQLITE_ROW) {
149 result = (const char *)sqlite3_column_text(stmt, 0);
151 throw IOException("Error executing find segment by id query");
154 sqlite3_finalize(stmt);
159 void LocalDb::StoreObject(const ObjectReference& ref,
160 const string &checksum, int64_t size)
164 static const char s[] =
166 "block_index(segmentid, object, checksum, size, timestamp) "
167 "values (?, ?, ?, ?, julianday('now'))";
170 rc = sqlite3_prepare_v2(db, s, strlen(s), &stmt, &tail);
171 if (rc != SQLITE_OK) {
175 sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
176 string obj = ref.get_sequence();
177 sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
178 sqlite3_bind_text(stmt, 3, checksum.c_str(), checksum.size(),
180 sqlite3_bind_int64(stmt, 4, size);
182 rc = sqlite3_step(stmt);
183 if (rc != SQLITE_DONE) {
184 fprintf(stderr, "Could not execute INSERT statement!\n");
187 sqlite3_finalize(stmt);
190 ObjectReference LocalDb::FindObject(const string &checksum, int64_t size)
194 static const char s[] =
195 "select segmentid, object from block_index "
196 "where checksum = ? and size = ? and expired is null";
201 rc = sqlite3_prepare_v2(db, s, strlen(s), &stmt, &tail);
202 if (rc != SQLITE_OK) {
206 sqlite3_bind_text(stmt, 1, checksum.c_str(), checksum.size(),
208 sqlite3_bind_int64(stmt, 2, size);
210 rc = sqlite3_step(stmt);
211 if (rc == SQLITE_DONE) {
212 } else if (rc == SQLITE_ROW) {
213 ref = ObjectReference(IdToSegment(sqlite3_column_int64(stmt, 0)),
214 (const char *)sqlite3_column_text(stmt, 1));
216 fprintf(stderr, "Could not execute SELECT statement!\n");
219 sqlite3_finalize(stmt);
224 bool LocalDb::IsOldObject(const string &checksum, int64_t size)
228 static const char s[] =
229 "select segmentid, object from block_index "
230 "where checksum = ? and size = ?";
235 rc = sqlite3_prepare_v2(db, s, strlen(s), &stmt, &tail);
236 if (rc != SQLITE_OK) {
240 sqlite3_bind_text(stmt, 1, checksum.c_str(), checksum.size(),
242 sqlite3_bind_int64(stmt, 2, size);
244 rc = sqlite3_step(stmt);
245 if (rc == SQLITE_DONE) {
247 } else if (rc == SQLITE_ROW) {
250 fprintf(stderr, "Could not execute SELECT statement!\n");
253 sqlite3_finalize(stmt);
258 void LocalDb::UseObject(const ObjectReference& ref)
262 static const char s[] =
263 "insert or ignore into snapshot_contents "
264 "select blockid, ? as snapshotid from block_index "
265 "where segmentid = ? and object = ?";
268 rc = sqlite3_prepare_v2(db, s, strlen(s), &stmt, &tail);
269 if (rc != SQLITE_OK) {
273 sqlite3_bind_int64(stmt, 1, snapshotid);
274 sqlite3_bind_int64(stmt, 2, SegmentToId(ref.get_segment()));
275 string obj = ref.get_sequence();
276 sqlite3_bind_text(stmt, 3, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
278 rc = sqlite3_step(stmt);
279 if (rc != SQLITE_DONE) {
280 fprintf(stderr, "Could not execute INSERT statement!\n");
283 sqlite3_finalize(stmt);