9 DB::DB(const string &filename)
11 int ret = sqlite3_open(filename.c_str(), &db);
12 if (ret != SQLITE_OK) {
13 fprintf(stderr, "%s: %s\n", filename.c_str(), sqlite3_errmsg(db));
18 CREATE TABLE IF NOT EXISTS state (state BLOB);
19 )", nullptr, nullptr, nullptr); // Ignore errors.
23 )", nullptr, nullptr, nullptr); // Ignore errors.
27 )", nullptr, nullptr, nullptr); // Ignore errors.
30 CREATE TABLE IF NOT EXISTS filev2 (
31 file INTEGER NOT NULL PRIMARY KEY,
32 filename VARCHAR NOT NULL UNIQUE,
36 )", nullptr, nullptr, nullptr); // Ignore errors.
38 sqlite3_exec(db, "PRAGMA journal_mode=WAL", nullptr, nullptr, nullptr); // Ignore errors.
39 sqlite3_exec(db, "PRAGMA synchronous=NORMAL", nullptr, nullptr, nullptr); // Ignore errors.
42 StateProto DB::get_state()
47 int ret = sqlite3_prepare_v2(db, "SELECT state FROM state", -1, &stmt, 0);
48 if (ret != SQLITE_OK) {
49 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
53 ret = sqlite3_step(stmt);
54 if (ret == SQLITE_ROW) {
55 bool ok = state.ParseFromArray(sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
57 fprintf(stderr, "State in database is corrupted!\n");
60 } else if (ret != SQLITE_DONE) {
61 fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
65 ret = sqlite3_finalize(stmt);
66 if (ret != SQLITE_OK) {
67 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
74 void DB::store_state(const StateProto &state)
77 state.SerializeToString(&serialized);
79 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
80 if (ret != SQLITE_OK) {
81 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
85 ret = sqlite3_exec(db, "DELETE FROM state", nullptr, nullptr, nullptr);
86 if (ret != SQLITE_OK) {
87 fprintf(stderr, "DELETE: %s\n", sqlite3_errmsg(db));
92 ret = sqlite3_prepare_v2(db, "INSERT INTO state VALUES (?)", -1, &stmt, 0);
93 if (ret != SQLITE_OK) {
94 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
98 sqlite3_bind_blob(stmt, 1, serialized.data(), serialized.size(), SQLITE_STATIC);
100 ret = sqlite3_step(stmt);
101 if (ret == SQLITE_ROW) {
102 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
106 ret = sqlite3_finalize(stmt);
107 if (ret != SQLITE_OK) {
108 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
112 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
113 if (ret != SQLITE_OK) {
114 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));
119 vector<DB::FrameOnDiskAndStreamIdx> DB::load_frame_file(const string &filename, size_t size, unsigned filename_idx)
121 FileContentsProto file_contents;
124 int ret = sqlite3_prepare_v2(db, "SELECT frames FROM filev2 WHERE filename=? AND size=?", -1, &stmt, 0);
125 if (ret != SQLITE_OK) {
126 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
130 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
131 sqlite3_bind_int64(stmt, 2, size);
133 ret = sqlite3_step(stmt);
134 if (ret == SQLITE_ROW) {
135 bool ok = file_contents.ParseFromArray(sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
137 fprintf(stderr, "Frame list in database is corrupted!\n");
140 } else if (ret != SQLITE_DONE) {
141 fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
145 ret = sqlite3_finalize(stmt);
146 if (ret != SQLITE_OK) {
147 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
151 vector<FrameOnDiskAndStreamIdx> frames;
152 for (const StreamContentsProto &stream : file_contents.stream()) {
153 FrameOnDiskAndStreamIdx frame;
154 frame.stream_idx = stream.stream_idx();
155 for (int i = 0; i < stream.pts_size(); ++i) {
156 frame.frame.filename_idx = filename_idx;
157 frame.frame.pts = stream.pts(i);
158 frame.frame.offset = stream.offset(i);
159 frame.frame.size = stream.file_size(i);
160 frames.push_back(frame);
167 void DB::store_frame_file(const string &filename, size_t size, const vector<FrameOnDiskAndStreamIdx> &frames)
169 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
170 if (ret != SQLITE_OK) {
171 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
175 // Delete any existing instances with this filename.
178 ret = sqlite3_prepare_v2(db, "DELETE FROM filev2 WHERE filename=?", -1, &stmt, 0);
179 if (ret != SQLITE_OK) {
180 fprintf(stderr, "DELETE prepare: %s\n", sqlite3_errmsg(db));
184 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
186 ret = sqlite3_step(stmt);
187 if (ret == SQLITE_ROW) {
188 fprintf(stderr, "DELETE step: %s\n", sqlite3_errmsg(db));
192 ret = sqlite3_finalize(stmt);
193 if (ret != SQLITE_OK) {
194 fprintf(stderr, "DELETE finalize: %s\n", sqlite3_errmsg(db));
198 // Create the protobuf blob for the new row.
199 FileContentsProto file_contents;
200 unordered_set<unsigned> seen_stream_idx; // Usually only one.
201 for (const FrameOnDiskAndStreamIdx &frame : frames) {
202 seen_stream_idx.insert(frame.stream_idx);
204 for (unsigned stream_idx : seen_stream_idx) {
205 StreamContentsProto *stream = file_contents.add_stream();
206 stream->set_stream_idx(stream_idx);
207 stream->mutable_pts()->Reserve(frames.size());
208 stream->mutable_offset()->Reserve(frames.size());
209 stream->mutable_file_size()->Reserve(frames.size());
210 for (const FrameOnDiskAndStreamIdx &frame : frames) {
211 if (frame.stream_idx != stream_idx) {
214 stream->add_pts(frame.frame.pts);
215 stream->add_offset(frame.frame.offset);
216 stream->add_file_size(frame.frame.size);
220 file_contents.SerializeToString(&serialized);
222 // Insert the new row.
223 ret = sqlite3_prepare_v2(db, "INSERT INTO filev2 (filename, size, frames) VALUES (?, ?, ?)", -1, &stmt, 0);
224 if (ret != SQLITE_OK) {
225 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
229 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
230 sqlite3_bind_int64(stmt, 2, size);
231 sqlite3_bind_blob(stmt, 3, serialized.data(), serialized.size(), SQLITE_STATIC);
233 ret = sqlite3_step(stmt);
234 if (ret == SQLITE_ROW) {
235 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
239 ret = sqlite3_finalize(stmt);
240 if (ret != SQLITE_OK) {
241 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
246 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
247 if (ret != SQLITE_OK) {
248 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));
253 void DB::clean_unused_frame_files(const vector<string> &used_filenames)
255 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
256 if (ret != SQLITE_OK) {
257 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
261 ret = sqlite3_exec(db, R"(
262 CREATE TEMPORARY TABLE used_filenames ( filename VARCHAR NOT NULL PRIMARY KEY )
263 )", nullptr, nullptr, nullptr);
265 if (ret != SQLITE_OK) {
266 fprintf(stderr, "CREATE TEMPORARY TABLE: %s\n", sqlite3_errmsg(db));
270 // Insert the new rows.
272 ret = sqlite3_prepare_v2(db, "INSERT INTO used_filenames (filename) VALUES (?)", -1, &stmt, 0);
273 if (ret != SQLITE_OK) {
274 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
278 for (const string &filename : used_filenames) {
279 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
281 ret = sqlite3_step(stmt);
282 if (ret == SQLITE_ROW) {
283 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
287 ret = sqlite3_reset(stmt);
288 if (ret == SQLITE_ROW) {
289 fprintf(stderr, "INSERT reset: %s\n", sqlite3_errmsg(db));
294 ret = sqlite3_finalize(stmt);
295 if (ret != SQLITE_OK) {
296 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
300 ret = sqlite3_exec(db, R"(
301 DELETE FROM filev2 WHERE filename NOT IN ( SELECT filename FROM used_filenames )
302 )", nullptr, nullptr, nullptr);
304 if (ret != SQLITE_OK) {
305 fprintf(stderr, "DELETE: %s\n", sqlite3_errmsg(db));
309 ret = sqlite3_exec(db, R"(
310 DROP TABLE used_filenames
311 )", nullptr, nullptr, nullptr);
313 if (ret != SQLITE_OK) {
314 fprintf(stderr, "DROP TABLE: %s\n", sqlite3_errmsg(db));
319 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
320 if (ret != SQLITE_OK) {
321 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));