Add timestamps to block when they are inserted into the local database.
[cumulus.git] / localdb.cc
1 /* LBS: An LFS-inspired filesystem backup system
2  * Copyright (C) 2007  Michael Vrable
3  *
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.
8  *
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
11  * changed later. */
12
13 #include <assert.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <sqlite3.h>
17
18 #include <string>
19
20 #include "localdb.h"
21 #include "store.h"
22
23 using std::string;
24
25 void LocalDb::Open(const char *path, const char *snapshot_name)
26 {
27     int rc;
28
29     snapshot = snapshot_name;
30
31     rc = sqlite3_open(path, &db);
32     if (rc) {
33         fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
34         sqlite3_close(db);
35         throw IOException("Error opening local database");
36     }
37
38     rc = sqlite3_exec(db, "begin", NULL, NULL, NULL);
39     if (rc) {
40         fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
41         sqlite3_close(db);
42         throw IOException("Error starting transaction");
43     }
44 }
45
46 void LocalDb::Close()
47 {
48     int rc;
49     rc = sqlite3_exec(db, "commit", NULL, NULL, NULL);
50     if (rc != SQLITE_OK) {
51         fprintf(stderr, "Can't commit database!\n");
52     }
53     sqlite3_close(db);
54 }
55
56 void LocalDb::StoreObject(const ObjectReference& ref,
57                           const string &checksum, int64_t size)
58 {
59     int rc;
60     sqlite3_stmt *stmt;
61     static const char s[] =
62         "insert into block_index(segment, object, checksum, size, timestamp) "
63         "values (?, ?, ?, ?, julianday('now'))";
64     const char *tail;
65
66     rc = sqlite3_prepare_v2(db, s, strlen(s), &stmt, &tail);
67     if (rc != SQLITE_OK) {
68         return;
69     }
70
71     string seg = ref.get_segment();
72     sqlite3_bind_text(stmt, 1, seg.c_str(), seg.size(), SQLITE_TRANSIENT);
73     string obj = ref.get_sequence();
74     sqlite3_bind_text(stmt, 2, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
75     sqlite3_bind_text(stmt, 3, checksum.c_str(), checksum.size(),
76                       SQLITE_TRANSIENT);
77     sqlite3_bind_int64(stmt, 4, size);
78
79     rc = sqlite3_step(stmt);
80     if (rc != SQLITE_DONE) {
81         fprintf(stderr, "Could not execute INSERT statement!\n");
82     }
83
84     sqlite3_finalize(stmt);
85 }
86
87 ObjectReference LocalDb::FindObject(const string &checksum, int64_t size)
88 {
89     int rc;
90     sqlite3_stmt *stmt;
91     static const char s[] =
92         "select segment, object from block_index "
93         "where checksum = ? and size = ?";
94     const char *tail;
95
96     ObjectReference ref;
97
98     rc = sqlite3_prepare_v2(db, s, strlen(s), &stmt, &tail);
99     if (rc != SQLITE_OK) {
100         return ref;
101     }
102
103     sqlite3_bind_text(stmt, 1, checksum.c_str(), checksum.size(),
104                       SQLITE_TRANSIENT);
105     sqlite3_bind_int64(stmt, 2, size);
106
107     rc = sqlite3_step(stmt);
108     if (rc == SQLITE_DONE) {
109     } else if (rc == SQLITE_ROW) {
110         ref = ObjectReference((const char *)sqlite3_column_text(stmt, 0),
111                               (const char *)sqlite3_column_text(stmt, 1));
112     } else {
113         fprintf(stderr, "Could not execute SELECT statement!\n");
114     }
115
116     sqlite3_finalize(stmt);
117
118     return ref;
119 }
120
121 void LocalDb::UseObject(const ObjectReference& ref)
122 {
123     int rc;
124     sqlite3_stmt *stmt;
125     static const char s[] =
126         "insert into snapshot_contents "
127         "select blockid, ? as snapshot from block_index "
128         "where segment = ? and object = ?";
129     const char *tail;
130
131     rc = sqlite3_prepare_v2(db, s, strlen(s), &stmt, &tail);
132     if (rc != SQLITE_OK) {
133         return;
134     }
135
136     sqlite3_bind_text(stmt, 1, snapshot.c_str(), snapshot.size(),
137                       SQLITE_TRANSIENT);
138     string seg = ref.get_segment();
139     sqlite3_bind_text(stmt, 2, seg.c_str(), seg.size(), SQLITE_TRANSIENT);
140     string obj = ref.get_sequence();
141     sqlite3_bind_text(stmt, 3, obj.c_str(), obj.size(), SQLITE_TRANSIENT);
142
143     rc = sqlite3_step(stmt);
144     if (rc != SQLITE_DONE) {
145         fprintf(stderr, "Could not execute INSERT statement!\n");
146     }
147
148     sqlite3_finalize(stmt);
149 }