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 /* Helper function to prepare a statement for execution in the current
27 sqlite3_stmt *LocalDb::Prepare(const char *sql)
33 rc = sqlite3_prepare(db, sql, strlen(sql), &stmt, &tail);
34 if (rc != SQLITE_OK) {
35 throw IOException(string("Error preparing statement: ") + sql);
41 void LocalDb::Open(const char *path, const char *snapshot_name)
45 rc = sqlite3_open(path, &db);
47 fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
49 throw IOException("Error opening local database");
52 rc = sqlite3_exec(db, "begin", NULL, NULL, NULL);
54 fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
56 throw IOException("Error starting transaction");
59 /* Insert this snapshot into the database, and determine the integer key
60 * which will be used to identify it. */
61 sqlite3_stmt *stmt = Prepare("insert into snapshots(name, timestamp) "
62 "values (?, julianday('now'))");
63 sqlite3_bind_text(stmt, 1, snapshot_name, strlen(snapshot_name),
66 rc = sqlite3_step(stmt);
67 if (rc != SQLITE_DONE) {
69 throw IOException("Database execution error!");
72 snapshotid = sqlite3_last_insert_rowid(db);
73 sqlite3_finalize(stmt);
74 if (snapshotid == 0) {
76 throw IOException("Find snapshot id");
83 rc = sqlite3_exec(db, "commit", NULL, NULL, NULL);
84 if (rc != SQLITE_OK) {
85 fprintf(stderr, "Can't commit database!\n");
90 int64_t LocalDb::SegmentToId(const string &segment)
96 stmt = Prepare("insert or ignore into segments(segment) values (?)");
97 sqlite3_bind_text(stmt, 1, segment.c_str(), segment.size(),
99 rc = sqlite3_step(stmt);
100 if (rc != SQLITE_DONE) {
101 throw IOException("Could not execute INSERT statement!");
103 sqlite3_finalize(stmt);
105 stmt = Prepare("select segmentid from segments where segment = ?");
106 sqlite3_bind_text(stmt, 1, segment.c_str(), segment.size(),
109 rc = sqlite3_step(stmt);
110 if (rc == SQLITE_DONE) {
111 throw IOException("No segment found by id");
112 } else if (rc == SQLITE_ROW) {
113 result = sqlite3_column_int64(stmt, 0);
115 throw IOException("Error executing find segment by id query");
118 sqlite3_finalize(stmt);
123 string LocalDb::IdToSegment(int64_t segmentid)
129 stmt = Prepare("select segment from segments where segmentid = ?");
130 sqlite3_bind_int64(stmt, 1, segmentid);
132 rc = sqlite3_step(stmt);
133 if (rc == SQLITE_DONE) {
134 throw IOException("No segment found by id");
135 } else if (rc == SQLITE_ROW) {
136 result = (const char *)sqlite3_column_text(stmt, 0);
138 throw IOException("Error executing find segment by id query");
141 sqlite3_finalize(stmt);
146 void LocalDb::StoreObject(const ObjectReference& ref,
147 const string &checksum, int64_t size,
154 stmt = Prepare("insert into block_index("
155 "segmentid, object, checksum, size, timestamp) "
156 "values (?, ?, ?, ?, julianday('now'))");
158 stmt = Prepare("insert into block_index("
159 "segmentid, object, checksum, size, timestamp) "
160 "values (?, ?, ?, ?, ?)");
163 sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
164 string obj = ref.get_sequence();
165 sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
166 sqlite3_bind_text(stmt, 3, checksum.c_str(), checksum.size(),
168 sqlite3_bind_int64(stmt, 4, size);
170 sqlite3_bind_double(stmt, 5, age);
172 rc = sqlite3_step(stmt);
173 if (rc != SQLITE_DONE) {
174 fprintf(stderr, "Could not execute INSERT statement!\n");
177 sqlite3_finalize(stmt);
180 ObjectReference LocalDb::FindObject(const string &checksum, int64_t size)
186 stmt = Prepare("select segmentid, object from block_index "
187 "where checksum = ? and size = ? and expired is null");
188 sqlite3_bind_text(stmt, 1, checksum.c_str(), checksum.size(),
190 sqlite3_bind_int64(stmt, 2, size);
192 rc = sqlite3_step(stmt);
193 if (rc == SQLITE_DONE) {
194 } else if (rc == SQLITE_ROW) {
195 ref = ObjectReference(IdToSegment(sqlite3_column_int64(stmt, 0)),
196 (const char *)sqlite3_column_text(stmt, 1));
198 fprintf(stderr, "Could not execute SELECT statement!\n");
201 sqlite3_finalize(stmt);
206 bool LocalDb::IsOldObject(const string &checksum, int64_t size, double *age)
212 stmt = Prepare("select segmentid, object, timestamp from block_index "
213 "where checksum = ? and size = ?");
214 sqlite3_bind_text(stmt, 1, checksum.c_str(), checksum.size(),
216 sqlite3_bind_int64(stmt, 2, size);
218 rc = sqlite3_step(stmt);
219 if (rc == SQLITE_DONE) {
221 } else if (rc == SQLITE_ROW) {
223 *age = sqlite3_column_double(stmt, 2);
225 fprintf(stderr, "Could not execute SELECT statement!\n");
228 sqlite3_finalize(stmt);
233 /* Does this object still exist in the database (and not expired)? */
234 bool LocalDb::IsAvailable(const ObjectReference &ref)
240 stmt = Prepare("select count(*) from block_index "
241 "where segmentid = ? and object = ? and expired is null");
242 sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
243 sqlite3_bind_text(stmt, 2, ref.get_sequence().c_str(),
244 ref.get_sequence().size(), SQLITE_TRANSIENT);
246 rc = sqlite3_step(stmt);
247 if (rc == SQLITE_DONE) {
249 } else if (rc == SQLITE_ROW) {
250 if (sqlite3_column_int(stmt, 0) > 0)
253 fprintf(stderr, "Could not execute SELECT statement!\n");
256 sqlite3_finalize(stmt);
261 void LocalDb::UseObject(const ObjectReference& ref)
266 stmt = Prepare("insert or ignore into snapshot_contents "
267 "select blockid, ? as snapshotid from block_index "
268 "where segmentid = ? and object = ?");
269 sqlite3_bind_int64(stmt, 1, snapshotid);
270 sqlite3_bind_int64(stmt, 2, SegmentToId(ref.get_segment()));
271 string obj = ref.get_sequence();
272 sqlite3_bind_text(stmt, 3, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
274 rc = sqlite3_step(stmt);
275 if (rc != SQLITE_DONE) {
276 fprintf(stderr, "Could not execute INSERT statement!\n");
279 sqlite3_finalize(stmt);