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)
152 stmt = Prepare("insert into "
153 "block_index(segmentid, object, checksum, size, timestamp) "
154 "values (?, ?, ?, ?, julianday('now'))");
156 sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
157 string obj = ref.get_sequence();
158 sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
159 sqlite3_bind_text(stmt, 3, checksum.c_str(), checksum.size(),
161 sqlite3_bind_int64(stmt, 4, size);
163 rc = sqlite3_step(stmt);
164 if (rc != SQLITE_DONE) {
165 fprintf(stderr, "Could not execute INSERT statement!\n");
168 sqlite3_finalize(stmt);
171 ObjectReference LocalDb::FindObject(const string &checksum, int64_t size)
177 stmt = Prepare("select segmentid, object from block_index "
178 "where checksum = ? and size = ? and expired is null");
179 sqlite3_bind_text(stmt, 1, checksum.c_str(), checksum.size(),
181 sqlite3_bind_int64(stmt, 2, size);
183 rc = sqlite3_step(stmt);
184 if (rc == SQLITE_DONE) {
185 } else if (rc == SQLITE_ROW) {
186 ref = ObjectReference(IdToSegment(sqlite3_column_int64(stmt, 0)),
187 (const char *)sqlite3_column_text(stmt, 1));
189 fprintf(stderr, "Could not execute SELECT statement!\n");
192 sqlite3_finalize(stmt);
197 bool LocalDb::IsOldObject(const string &checksum, int64_t size)
203 stmt = Prepare("select segmentid, object from block_index "
204 "where checksum = ? and size = ?");
205 sqlite3_bind_text(stmt, 1, checksum.c_str(), checksum.size(),
207 sqlite3_bind_int64(stmt, 2, size);
209 rc = sqlite3_step(stmt);
210 if (rc == SQLITE_DONE) {
212 } else if (rc == SQLITE_ROW) {
215 fprintf(stderr, "Could not execute SELECT statement!\n");
218 sqlite3_finalize(stmt);
223 void LocalDb::UseObject(const ObjectReference& ref)
228 stmt = Prepare("insert or ignore into snapshot_contents "
229 "select blockid, ? as snapshotid from block_index "
230 "where segmentid = ? and object = ?");
231 sqlite3_bind_int64(stmt, 1, snapshotid);
232 sqlite3_bind_int64(stmt, 2, SegmentToId(ref.get_segment()));
233 string obj = ref.get_sequence();
234 sqlite3_bind_text(stmt, 3, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
236 rc = sqlite3_step(stmt);
237 if (rc != SQLITE_DONE) {
238 fprintf(stderr, "Could not execute INSERT statement!\n");
241 sqlite3_finalize(stmt);