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.
22 CREATE TABLE IF NOT EXISTS settings (settings BLOB);
23 )", nullptr, nullptr, nullptr); // Ignore errors.
27 )", nullptr, nullptr, nullptr); // Ignore errors.
31 )", nullptr, nullptr, nullptr); // Ignore errors.
34 CREATE TABLE IF NOT EXISTS filev2 (
35 file INTEGER NOT NULL PRIMARY KEY,
36 filename VARCHAR NOT NULL UNIQUE,
40 )", nullptr, nullptr, nullptr); // Ignore errors.
42 sqlite3_exec(db, "PRAGMA journal_mode=WAL", nullptr, nullptr, nullptr); // Ignore errors.
43 sqlite3_exec(db, "PRAGMA synchronous=NORMAL", nullptr, nullptr, nullptr); // Ignore errors.
46 StateProto DB::get_state()
51 int ret = sqlite3_prepare_v2(db, "SELECT state FROM state", -1, &stmt, 0);
52 if (ret != SQLITE_OK) {
53 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
57 ret = sqlite3_step(stmt);
58 if (ret == SQLITE_ROW) {
59 bool ok = state.ParseFromArray(sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
61 fprintf(stderr, "State in database is corrupted!\n");
64 } else if (ret != SQLITE_DONE) {
65 fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
69 ret = sqlite3_finalize(stmt);
70 if (ret != SQLITE_OK) {
71 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
78 void DB::store_state(const StateProto &state)
81 state.SerializeToString(&serialized);
83 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
84 if (ret != SQLITE_OK) {
85 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
89 ret = sqlite3_exec(db, "DELETE FROM state", nullptr, nullptr, nullptr);
90 if (ret != SQLITE_OK) {
91 fprintf(stderr, "DELETE: %s\n", sqlite3_errmsg(db));
96 ret = sqlite3_prepare_v2(db, "INSERT INTO state VALUES (?)", -1, &stmt, 0);
97 if (ret != SQLITE_OK) {
98 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
102 sqlite3_bind_blob(stmt, 1, serialized.data(), serialized.size(), SQLITE_STATIC);
104 ret = sqlite3_step(stmt);
105 if (ret == SQLITE_ROW) {
106 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
110 ret = sqlite3_finalize(stmt);
111 if (ret != SQLITE_OK) {
112 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
116 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
117 if (ret != SQLITE_OK) {
118 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));
123 SettingsProto DB::get_settings()
125 SettingsProto settings;
128 int ret = sqlite3_prepare_v2(db, "SELECT settings FROM settings", -1, &stmt, 0);
129 if (ret != SQLITE_OK) {
130 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
134 ret = sqlite3_step(stmt);
135 if (ret == SQLITE_ROW) {
136 bool ok = settings.ParseFromArray(sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
138 fprintf(stderr, "State in database is corrupted!\n");
141 } else if (ret != SQLITE_DONE) {
142 fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
146 ret = sqlite3_finalize(stmt);
147 if (ret != SQLITE_OK) {
148 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
155 void DB::store_settings(const SettingsProto &settings)
158 settings.SerializeToString(&serialized);
160 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
161 if (ret != SQLITE_OK) {
162 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
166 ret = sqlite3_exec(db, "DELETE FROM settings", nullptr, nullptr, nullptr);
167 if (ret != SQLITE_OK) {
168 fprintf(stderr, "DELETE: %s\n", sqlite3_errmsg(db));
173 ret = sqlite3_prepare_v2(db, "INSERT INTO settings VALUES (?)", -1, &stmt, 0);
174 if (ret != SQLITE_OK) {
175 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
179 sqlite3_bind_blob(stmt, 1, serialized.data(), serialized.size(), SQLITE_STATIC);
181 ret = sqlite3_step(stmt);
182 if (ret == SQLITE_ROW) {
183 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
187 ret = sqlite3_finalize(stmt);
188 if (ret != SQLITE_OK) {
189 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
193 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
194 if (ret != SQLITE_OK) {
195 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));
200 vector<DB::FrameOnDiskAndStreamIdx> DB::load_frame_file(const string &filename, size_t size, unsigned filename_idx)
202 FileContentsProto file_contents;
205 int ret = sqlite3_prepare_v2(db, "SELECT frames FROM filev2 WHERE filename=? AND size=?", -1, &stmt, 0);
206 if (ret != SQLITE_OK) {
207 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
211 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
212 sqlite3_bind_int64(stmt, 2, size);
214 ret = sqlite3_step(stmt);
215 if (ret == SQLITE_ROW) {
216 bool ok = file_contents.ParseFromArray(sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
218 fprintf(stderr, "Frame list in database is corrupted!\n");
221 } else if (ret != SQLITE_DONE) {
222 fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
226 ret = sqlite3_finalize(stmt);
227 if (ret != SQLITE_OK) {
228 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
232 vector<FrameOnDiskAndStreamIdx> frames;
233 for (const StreamContentsProto &stream : file_contents.stream()) {
234 FrameOnDiskAndStreamIdx frame;
235 frame.stream_idx = stream.stream_idx();
236 for (int i = 0; i < stream.pts_size(); ++i) {
237 frame.frame.filename_idx = filename_idx;
238 frame.frame.pts = stream.pts(i);
239 frame.frame.offset = stream.offset(i);
240 frame.frame.size = stream.file_size(i);
241 frames.push_back(frame);
248 void DB::store_frame_file(const string &filename, size_t size, const vector<FrameOnDiskAndStreamIdx> &frames)
250 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
251 if (ret != SQLITE_OK) {
252 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
256 // Delete any existing instances with this filename.
259 ret = sqlite3_prepare_v2(db, "DELETE FROM filev2 WHERE filename=?", -1, &stmt, 0);
260 if (ret != SQLITE_OK) {
261 fprintf(stderr, "DELETE prepare: %s\n", sqlite3_errmsg(db));
265 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
267 ret = sqlite3_step(stmt);
268 if (ret == SQLITE_ROW) {
269 fprintf(stderr, "DELETE step: %s\n", sqlite3_errmsg(db));
273 ret = sqlite3_finalize(stmt);
274 if (ret != SQLITE_OK) {
275 fprintf(stderr, "DELETE finalize: %s\n", sqlite3_errmsg(db));
279 // Create the protobuf blob for the new row.
280 FileContentsProto file_contents;
281 unordered_set<unsigned> seen_stream_idx; // Usually only one.
282 for (const FrameOnDiskAndStreamIdx &frame : frames) {
283 seen_stream_idx.insert(frame.stream_idx);
285 for (unsigned stream_idx : seen_stream_idx) {
286 StreamContentsProto *stream = file_contents.add_stream();
287 stream->set_stream_idx(stream_idx);
288 stream->mutable_pts()->Reserve(frames.size());
289 stream->mutable_offset()->Reserve(frames.size());
290 stream->mutable_file_size()->Reserve(frames.size());
291 for (const FrameOnDiskAndStreamIdx &frame : frames) {
292 if (frame.stream_idx != stream_idx) {
295 stream->add_pts(frame.frame.pts);
296 stream->add_offset(frame.frame.offset);
297 stream->add_file_size(frame.frame.size);
301 file_contents.SerializeToString(&serialized);
303 // Insert the new row.
304 ret = sqlite3_prepare_v2(db, "INSERT INTO filev2 (filename, size, frames) VALUES (?, ?, ?)", -1, &stmt, 0);
305 if (ret != SQLITE_OK) {
306 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
310 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
311 sqlite3_bind_int64(stmt, 2, size);
312 sqlite3_bind_blob(stmt, 3, serialized.data(), serialized.size(), SQLITE_STATIC);
314 ret = sqlite3_step(stmt);
315 if (ret == SQLITE_ROW) {
316 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
320 ret = sqlite3_finalize(stmt);
321 if (ret != SQLITE_OK) {
322 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
327 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
328 if (ret != SQLITE_OK) {
329 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));
334 void DB::clean_unused_frame_files(const vector<string> &used_filenames)
336 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
337 if (ret != SQLITE_OK) {
338 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
342 ret = sqlite3_exec(db, R"(
343 CREATE TEMPORARY TABLE used_filenames ( filename VARCHAR NOT NULL PRIMARY KEY )
344 )", nullptr, nullptr, nullptr);
346 if (ret != SQLITE_OK) {
347 fprintf(stderr, "CREATE TEMPORARY TABLE: %s\n", sqlite3_errmsg(db));
351 // Insert the new rows.
353 ret = sqlite3_prepare_v2(db, "INSERT INTO used_filenames (filename) VALUES (?)", -1, &stmt, 0);
354 if (ret != SQLITE_OK) {
355 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
359 for (const string &filename : used_filenames) {
360 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
362 ret = sqlite3_step(stmt);
363 if (ret == SQLITE_ROW) {
364 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
368 ret = sqlite3_reset(stmt);
369 if (ret == SQLITE_ROW) {
370 fprintf(stderr, "INSERT reset: %s\n", sqlite3_errmsg(db));
375 ret = sqlite3_finalize(stmt);
376 if (ret != SQLITE_OK) {
377 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
381 ret = sqlite3_exec(db, R"(
382 DELETE FROM filev2 WHERE filename NOT IN ( SELECT filename FROM used_filenames )
383 )", nullptr, nullptr, nullptr);
385 if (ret != SQLITE_OK) {
386 fprintf(stderr, "DELETE: %s\n", sqlite3_errmsg(db));
390 ret = sqlite3_exec(db, R"(
391 DROP TABLE used_filenames
392 )", nullptr, nullptr, nullptr);
394 if (ret != SQLITE_OK) {
395 fprintf(stderr, "DROP TABLE: %s\n", sqlite3_errmsg(db));
400 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
401 if (ret != SQLITE_OK) {
402 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));