Initial support for efficient sub-file incrementals.
[cumulus.git] / localdb.cc
1 /* Cumulus: Smart Filesystem Backup to Dumb Servers
2  *
3  * Copyright (C) 2007-2008  The Regents of the University of California
4  * Written by Michael Vrable <mvrable@cs.ucsd.edu>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 /* When creating backup snapshots, maintain a local database of data blocks and
22  * checksums, in addition to the data contents (which may be stored remotely).
23  * This database is consulted when attempting to build incremental snapshots,
24  * as it says which objects can be reused.
25  *
26  * The database is implemented as an SQLite3 database, but this implementation
27  * detail is kept internal to this file, so that the storage format may be
28  * changed later. */
29
30 #include <assert.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <sqlite3.h>
34
35 #include <string>
36
37 #include "localdb.h"
38 #include "store.h"
39
40 using std::string;
41
42 /* Helper function to prepare a statement for execution in the current
43  * database. */
44 sqlite3_stmt *LocalDb::Prepare(const char *sql)
45 {
46     sqlite3_stmt *stmt;
47     int rc;
48     const char *tail;
49
50     rc = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, &tail);
51     if (rc != SQLITE_OK) {
52         ReportError(rc);
53         throw IOException(string("Error preparing statement: ") + sql);
54     }
55
56     return stmt;
57 }
58
59 void LocalDb::ReportError(int rc)
60 {
61     fprintf(stderr, "Result code: %d\n", rc);
62     fprintf(stderr, "Error message: %s\n", sqlite3_errmsg(db));
63 }
64
65 void LocalDb::Open(const char *path, const char *snapshot_name,
66                    const char *snapshot_scheme, double intent)
67 {
68     int rc;
69
70     rc = sqlite3_open(path, &db);
71     if (rc) {
72         fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
73         sqlite3_close(db);
74         throw IOException("Error opening local database");
75     }
76
77     rc = sqlite3_exec(db, "begin", NULL, NULL, NULL);
78     if (rc) {
79         fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
80         sqlite3_close(db);
81         throw IOException("Error starting transaction");
82     }
83
84     sqlite3_extended_result_codes(db, 1);
85
86     /* Insert this snapshot into the database, and determine the integer key
87      * which will be used to identify it. */
88     sqlite3_stmt *stmt = Prepare("insert into "
89                                  "snapshots(name, scheme, timestamp, intent) "
90                                  "values (?, ?, julianday('now'), ?)");
91     sqlite3_bind_text(stmt, 1, snapshot_name, strlen(snapshot_name),
92                       SQLITE_TRANSIENT);
93     if (snapshot_scheme == NULL)
94         sqlite3_bind_null(stmt, 2);
95     else
96         sqlite3_bind_text(stmt, 2, snapshot_scheme, strlen(snapshot_scheme),
97                           SQLITE_TRANSIENT);
98     sqlite3_bind_double(stmt, 3, intent);
99
100     rc = sqlite3_step(stmt);
101     if (rc != SQLITE_DONE) {
102         ReportError(rc);
103         sqlite3_close(db);
104         throw IOException("Database execution error!");
105     }
106
107     snapshotid = sqlite3_last_insert_rowid(db);
108     sqlite3_finalize(stmt);
109     if (snapshotid == 0) {
110         ReportError(rc);
111         sqlite3_close(db);
112         throw IOException("Find snapshot id");
113     }
114
115     /* Create a temporary table which will be used to keep track of the objects
116      * used by this snapshot.  When the database is closed, we will summarize
117      * the results of this table into segments_used. */
118     rc = sqlite3_exec(db,
119                       "create temporary table snapshot_refs ("
120                       "    segmentid integer not null,"
121                       "    object text not null,"
122                       "    size integer not null"
123                       ")", NULL, NULL, NULL);
124     if (rc != SQLITE_OK) {
125         ReportError(rc);
126         sqlite3_close(db);
127         throw IOException("Database initialization");
128     }
129     rc = sqlite3_exec(db,
130                       "create unique index snapshot_refs_index "
131                       "on snapshot_refs(segmentid, object)",
132                       NULL, NULL, NULL);
133     if (rc != SQLITE_OK) {
134         ReportError(rc);
135         sqlite3_close(db);
136         throw IOException("Database initialization");
137     }
138 }
139
140 void LocalDb::Close()
141 {
142     int rc;
143
144     /* Summarize the snapshot_refs table into segments_used. */
145     sqlite3_stmt *stmt = Prepare(
146         "insert or replace into segments_used "
147         "select ? as snapshotid, segmentid, max(utilization) from ("
148         "    select segmentid, cast(used as real) / size as utilization "
149         "    from "
150         "    (select segmentid, sum(size) as used from snapshot_refs "
151         "       group by segmentid) "
152         "    join segments using (segmentid) "
153         "  union "
154         "    select segmentid, utilization from segments_used "
155         "    where snapshotid = ? "
156         ") group by segmentid"
157     );
158     sqlite3_bind_int64(stmt, 1, snapshotid);
159     sqlite3_bind_int64(stmt, 2, snapshotid);
160     rc = sqlite3_step(stmt);
161     if (rc != SQLITE_OK && rc != SQLITE_DONE) {
162         ReportError(rc);
163         sqlite3_close(db);
164         fprintf(stderr, "DATABASE ERROR: Unable to create segment summary!\n");
165     }
166     sqlite3_finalize(stmt);
167
168     /* Commit changes to the database and close. */
169     rc = sqlite3_exec(db, "commit", NULL, NULL, NULL);
170     if (rc != SQLITE_OK) {
171         fprintf(stderr, "DATABASE ERROR: Can't commit database!\n");
172         ReportError(rc);
173     }
174     sqlite3_close(db);
175 }
176
177 int64_t LocalDb::SegmentToId(const string &segment)
178 {
179     int rc;
180     sqlite3_stmt *stmt;
181     int64_t result;
182
183     stmt = Prepare("insert or ignore into segments(segment) values (?)");
184     sqlite3_bind_text(stmt, 1, segment.c_str(), segment.size(),
185                       SQLITE_TRANSIENT);
186     rc = sqlite3_step(stmt);
187     if (rc != SQLITE_DONE) {
188         throw IOException("Could not execute INSERT statement!");
189     }
190     sqlite3_finalize(stmt);
191
192     stmt = Prepare("select segmentid from segments where segment = ?");
193     sqlite3_bind_text(stmt, 1, segment.c_str(), segment.size(),
194                       SQLITE_TRANSIENT);
195
196     rc = sqlite3_step(stmt);
197     if (rc == SQLITE_DONE) {
198         throw IOException("No segment found by id");
199     } else if (rc == SQLITE_ROW) {
200         result = sqlite3_column_int64(stmt, 0);
201     } else {
202         throw IOException("Error executing find segment by id query");
203     }
204
205     sqlite3_finalize(stmt);
206
207     return result;
208 }
209
210 string LocalDb::IdToSegment(int64_t segmentid)
211 {
212     int rc;
213     sqlite3_stmt *stmt;
214     string result;
215
216     stmt = Prepare("select segment from segments where segmentid = ?");
217     sqlite3_bind_int64(stmt, 1, segmentid);
218
219     rc = sqlite3_step(stmt);
220     if (rc == SQLITE_DONE) {
221         throw IOException("No segment found by id");
222     } else if (rc == SQLITE_ROW) {
223         result = (const char *)sqlite3_column_text(stmt, 0);
224     } else {
225         throw IOException("Error executing find segment by id query");
226     }
227
228     sqlite3_finalize(stmt);
229
230     return result;
231 }
232
233 void LocalDb::StoreObject(const ObjectReference& ref,
234                           const string &checksum, int64_t size,
235                           double age)
236 {
237     int rc;
238     sqlite3_stmt *stmt;
239
240     if (age == 0.0) {
241         stmt = Prepare("insert into block_index("
242                        "segmentid, object, checksum, size, timestamp) "
243                        "values (?, ?, ?, ?, julianday('now'))");
244     } else {
245         stmt = Prepare("insert into block_index("
246                        "segmentid, object, checksum, size, timestamp) "
247                        "values (?, ?, ?, ?, ?)");
248     }
249
250     sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
251     string obj = ref.get_sequence();
252     sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
253     sqlite3_bind_text(stmt, 3, checksum.c_str(), checksum.size(),
254                       SQLITE_TRANSIENT);
255     sqlite3_bind_int64(stmt, 4, size);
256     if (age != 0.0)
257         sqlite3_bind_double(stmt, 5, age);
258
259     rc = sqlite3_step(stmt);
260     if (rc != SQLITE_DONE) {
261         fprintf(stderr, "Could not execute INSERT statement!\n");
262         ReportError(rc);
263     }
264
265     sqlite3_finalize(stmt);
266
267     if (age != 0.0) {
268         stmt = Prepare("update segments "
269                        "set mtime = coalesce(max(mtime, ?), ?) "
270                        "where segmentid = ?");
271         sqlite3_bind_double(stmt, 1, age);
272         sqlite3_bind_double(stmt, 2, age);
273         sqlite3_bind_int64(stmt, 3, SegmentToId(ref.get_segment()));
274         rc = sqlite3_step(stmt);
275         sqlite3_finalize(stmt);
276     }
277 }
278
279 ObjectReference LocalDb::FindObject(const string &checksum, int64_t size)
280 {
281     int rc;
282     sqlite3_stmt *stmt;
283     ObjectReference ref;
284
285     stmt = Prepare("select segmentid, object from block_index "
286                    "where checksum = ? and size = ? and expired is null");
287     sqlite3_bind_text(stmt, 1, checksum.c_str(), checksum.size(),
288                       SQLITE_TRANSIENT);
289     sqlite3_bind_int64(stmt, 2, size);
290
291     rc = sqlite3_step(stmt);
292     if (rc == SQLITE_DONE) {
293     } else if (rc == SQLITE_ROW) {
294         ref = ObjectReference(IdToSegment(sqlite3_column_int64(stmt, 0)),
295                               (const char *)sqlite3_column_text(stmt, 1));
296         ref.set_range(0, size);
297     } else {
298         fprintf(stderr, "Could not execute SELECT statement!\n");
299         ReportError(rc);
300     }
301
302     sqlite3_finalize(stmt);
303
304     return ref;
305 }
306
307 bool LocalDb::IsOldObject(const string &checksum, int64_t size, double *age,
308                           int *group)
309 {
310     int rc;
311     sqlite3_stmt *stmt;
312     bool found = false;
313
314     stmt = Prepare("select segmentid, object, timestamp, expired "
315                    "from block_index where checksum = ? and size = ?");
316     sqlite3_bind_text(stmt, 1, checksum.c_str(), checksum.size(),
317                       SQLITE_TRANSIENT);
318     sqlite3_bind_int64(stmt, 2, size);
319
320     rc = sqlite3_step(stmt);
321     if (rc == SQLITE_DONE) {
322         found = false;
323     } else if (rc == SQLITE_ROW) {
324         found = true;
325         *age = sqlite3_column_double(stmt, 2);
326         *group = sqlite3_column_int(stmt, 3);
327     } else {
328         fprintf(stderr, "Could not execute SELECT statement!\n");
329         ReportError(rc);
330     }
331
332     sqlite3_finalize(stmt);
333
334     return found;
335 }
336
337 /* Does this object still exist in the database (and not expired)? */
338 bool LocalDb::IsAvailable(const ObjectReference &ref)
339 {
340     int rc;
341     sqlite3_stmt *stmt;
342     bool found = false;
343
344     // Special objects (such as the zero object) aren't stored in segments, and
345     // so are always available.
346     if (!ref.is_normal())
347         return true;
348
349     stmt = Prepare("select count(*) from block_index "
350                    "where segmentid = ? and object = ? and expired is null");
351     sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
352     sqlite3_bind_text(stmt, 2, ref.get_sequence().c_str(),
353                       ref.get_sequence().size(), SQLITE_TRANSIENT);
354
355     rc = sqlite3_step(stmt);
356     if (rc == SQLITE_DONE) {
357         found = false;
358     } else if (rc == SQLITE_ROW) {
359         if (sqlite3_column_int(stmt, 0) > 0)
360             found = true;
361     } else {
362         fprintf(stderr, "Could not execute SELECT statement!\n");
363         ReportError(rc);
364     }
365
366     sqlite3_finalize(stmt);
367
368     return found;
369 }
370
371 void LocalDb::UseObject(const ObjectReference& ref)
372 {
373     int rc;
374     sqlite3_stmt *stmt;
375
376     if (!ref.is_normal())
377         return;
378
379     stmt = Prepare("insert or ignore into snapshot_refs "
380                    "select segmentid, object, size from block_index "
381                    "where segmentid = ? and object = ?");
382     sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
383     string obj = ref.get_sequence();
384     sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
385
386     rc = sqlite3_step(stmt);
387     if (rc != SQLITE_DONE) {
388         fprintf(stderr, "Could not execute INSERT statement!\n");
389         ReportError(rc);
390     }
391
392     sqlite3_finalize(stmt);
393 }
394
395 void LocalDb::UseSegment(const std::string &segment, double utilization)
396 {
397     int rc;
398     sqlite3_stmt *stmt;
399
400     stmt = Prepare("insert or replace "
401                    "into segments_used(snapshotid, segmentid, utilization) "
402                    "values (?, ?, ?)");
403     sqlite3_bind_int64(stmt, 1, snapshotid);
404     sqlite3_bind_int64(stmt, 2, SegmentToId(segment));
405     sqlite3_bind_double(stmt, 3, utilization);
406
407     rc = sqlite3_step(stmt);
408     if (rc != SQLITE_DONE) {
409         fprintf(stderr, "Could not insert segment use record!\n");
410         ReportError(rc);
411     }
412
413     sqlite3_finalize(stmt);
414 }
415
416 void LocalDb::SetSegmentChecksum(const std::string &segment,
417                                  const std::string &path,
418                                  const std::string &checksum,
419                                  int size)
420 {
421     int rc;
422     sqlite3_stmt *stmt;
423
424     stmt = Prepare("update segments set path = ?, checksum = ?, size = ?, "
425                    "mtime = coalesce(mtime, julianday('now')) "
426                    "where segmentid = ?");
427     sqlite3_bind_text(stmt, 1, path.c_str(), path.size(),
428                       SQLITE_TRANSIENT);
429     sqlite3_bind_text(stmt, 2, checksum.c_str(), checksum.size(),
430                       SQLITE_TRANSIENT);
431     sqlite3_bind_int64(stmt, 3, size);
432     sqlite3_bind_int64(stmt, 4, SegmentToId(segment));
433
434     rc = sqlite3_step(stmt);
435     if (rc != SQLITE_DONE) {
436         fprintf(stderr, "Could not update segment checksum in database!\n");
437         ReportError(rc);
438     }
439
440     sqlite3_finalize(stmt);
441 }
442
443 bool LocalDb::GetSegmentChecksum(const string &segment,
444                                  string *seg_path,
445                                  string *seg_checksum)
446 {
447     int rc;
448     sqlite3_stmt *stmt;
449     ObjectReference ref;
450     int found = false;
451
452     stmt = Prepare("select path, checksum from segments where segment = ?");
453     sqlite3_bind_text(stmt, 1, segment.c_str(), segment.size(),
454                       SQLITE_TRANSIENT);
455
456     rc = sqlite3_step(stmt);
457     if (rc == SQLITE_DONE) {
458     } else if (rc == SQLITE_ROW) {
459         found = true;
460         const char *val;
461
462         val = (const char *)sqlite3_column_text(stmt, 0);
463         if (val == NULL)
464             found = false;
465         else
466             *seg_path = val;
467
468         val = (const char *)sqlite3_column_text(stmt, 1);
469         if (val == NULL)
470             found = false;
471         else
472             *seg_checksum = val;
473     } else {
474         fprintf(stderr, "Could not execute SELECT statement!\n");
475         ReportError(rc);
476     }
477
478     sqlite3_finalize(stmt);
479
480     return found;
481 }
482
483 /* Look up and return the packed representation of the subblock chunk
484  * signatures.  Returns true if signatures were found for the specified object,
485  * and if so sets *buf to point at a buffer of memory (allocated with malloc;
486  * the caller should free it), and *len to the length of the buffer. */
487 bool LocalDb::LoadChunkSignatures(ObjectReference ref,
488                                   void **buf, size_t *len,
489                                   string *algorithm)
490 {
491     int rc;
492     sqlite3_stmt *stmt;
493     int found = false;
494
495     stmt = Prepare("select algorithm, signatures from subblock_signatures "
496                    "where blockid = (select blockid from block_index "
497                    "                 where segmentid = ? and object = ?)");
498     sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
499     string obj = ref.get_sequence();
500     sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
501
502     rc = sqlite3_step(stmt);
503     if (rc == SQLITE_DONE) {
504     } else if (rc == SQLITE_ROW) {
505         const void *data = sqlite3_column_blob(stmt, 0);
506         *len = sqlite3_column_bytes(stmt, 0);
507
508         if (*len > 0) {
509             *buf = malloc(*len);
510             if (*buf != NULL) {
511                 memcpy(*buf, data, *len);
512                 *algorithm = (const char *)sqlite3_column_text(stmt, 1);
513                 found = true;
514             }
515         }
516     } else {
517         fprintf(stderr, "Could not execute SELECT statement!\n");
518         ReportError(rc);
519     }
520
521     sqlite3_finalize(stmt);
522
523     return found;
524 }
525
526 /* Store the subblock chunk signatures for a specified object.  The object
527  * itself must have already been indexed in the database. */
528 void LocalDb::StoreChunkSignatures(ObjectReference ref,
529                                    const void *buf, size_t len,
530                                    const string& algorithm)
531 {
532     int rc;
533     sqlite3_stmt *stmt;
534
535     stmt = Prepare("select blockid from block_index "
536                    "where segmentid = ? and object = ?");
537     sqlite3_bind_int64(stmt, 1, SegmentToId(ref.get_segment()));
538     string obj = ref.get_sequence();
539     sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
540
541     rc = sqlite3_step(stmt);
542     if (rc != SQLITE_ROW) {
543         fprintf(stderr,
544                 "Could not determine blockid in StoreChunkSignatures!\n");
545         ReportError(rc);
546         throw IOException("Error getting blockid");
547     }
548     int64_t blockid = sqlite3_column_int64(stmt, 0);
549     sqlite3_finalize(stmt);
550
551     stmt = Prepare("insert or replace "
552                    "into subblock_signatures(blockid, algorithm, signatures) "
553                    "values (?, ?, ?)");
554     sqlite3_bind_int64(stmt, 1, blockid);
555     sqlite3_bind_text(stmt, 2, algorithm.c_str(), algorithm.size(),
556                       SQLITE_TRANSIENT);
557     sqlite3_bind_blob(stmt, 3, buf, len, SQLITE_TRANSIENT);
558
559     rc = sqlite3_step(stmt);
560     if (rc != SQLITE_DONE) {
561         fprintf(stderr, "Could not insert sub-block checksums!\n");
562         ReportError(rc);
563     }
564
565     sqlite3_finalize(stmt);
566 }