6 #include <unordered_set>
10 DB::DB(const string &filename)
12 int ret = sqlite3_open(filename.c_str(), &db);
13 if (ret != SQLITE_OK) {
14 fprintf(stderr, "%s: %s\n", filename.c_str(), sqlite3_errmsg(db));
19 CREATE TABLE IF NOT EXISTS state (state BLOB);
20 )", nullptr, nullptr, nullptr); // Ignore errors.
23 CREATE TABLE IF NOT EXISTS settings (settings BLOB);
24 )", nullptr, nullptr, nullptr); // Ignore errors.
28 )", nullptr, nullptr, nullptr); // Ignore errors.
32 )", nullptr, nullptr, nullptr); // Ignore errors.
35 CREATE TABLE IF NOT EXISTS filev2 (
36 file INTEGER NOT NULL PRIMARY KEY,
37 filename VARCHAR NOT NULL UNIQUE,
41 )", nullptr, nullptr, nullptr); // Ignore errors.
43 sqlite3_exec(db, "PRAGMA journal_mode=WAL", nullptr, nullptr, nullptr); // Ignore errors.
44 sqlite3_exec(db, "PRAGMA synchronous=NORMAL", nullptr, nullptr, nullptr); // Ignore errors.
47 StateProto DB::get_state()
52 int ret = sqlite3_prepare_v2(db, "SELECT state FROM state", -1, &stmt, 0);
53 if (ret != SQLITE_OK) {
54 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
58 ret = sqlite3_step(stmt);
59 if (ret == SQLITE_ROW) {
60 bool ok = state.ParseFromArray(sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
62 fprintf(stderr, "State in database is corrupted!\n");
65 } else if (ret != SQLITE_DONE) {
66 fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
70 ret = sqlite3_finalize(stmt);
71 if (ret != SQLITE_OK) {
72 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
79 void DB::store_state(const StateProto &state)
82 state.SerializeToString(&serialized);
84 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
85 if (ret != SQLITE_OK) {
86 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
90 ret = sqlite3_exec(db, "DELETE FROM state", nullptr, nullptr, nullptr);
91 if (ret != SQLITE_OK) {
92 fprintf(stderr, "DELETE: %s\n", sqlite3_errmsg(db));
97 ret = sqlite3_prepare_v2(db, "INSERT INTO state VALUES (?)", -1, &stmt, 0);
98 if (ret != SQLITE_OK) {
99 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
103 sqlite3_bind_blob(stmt, 1, serialized.data(), serialized.size(), SQLITE_STATIC);
105 ret = sqlite3_step(stmt);
106 if (ret == SQLITE_ROW) {
107 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
111 ret = sqlite3_finalize(stmt);
112 if (ret != SQLITE_OK) {
113 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
117 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
118 if (ret != SQLITE_OK) {
119 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));
124 SettingsProto DB::get_settings()
126 SettingsProto settings;
129 int ret = sqlite3_prepare_v2(db, "SELECT settings FROM settings", -1, &stmt, 0);
130 if (ret != SQLITE_OK) {
131 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
135 ret = sqlite3_step(stmt);
136 if (ret == SQLITE_ROW) {
137 bool ok = settings.ParseFromArray(sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
139 fprintf(stderr, "State in database is corrupted!\n");
142 } else if (ret != SQLITE_DONE) {
143 fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
147 ret = sqlite3_finalize(stmt);
148 if (ret != SQLITE_OK) {
149 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
156 void DB::store_settings(const SettingsProto &settings)
159 settings.SerializeToString(&serialized);
161 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
162 if (ret != SQLITE_OK) {
163 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
167 ret = sqlite3_exec(db, "DELETE FROM settings", nullptr, nullptr, nullptr);
168 if (ret != SQLITE_OK) {
169 fprintf(stderr, "DELETE: %s\n", sqlite3_errmsg(db));
174 ret = sqlite3_prepare_v2(db, "INSERT INTO settings VALUES (?)", -1, &stmt, 0);
175 if (ret != SQLITE_OK) {
176 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
180 sqlite3_bind_blob(stmt, 1, serialized.data(), serialized.size(), SQLITE_STATIC);
182 ret = sqlite3_step(stmt);
183 if (ret == SQLITE_ROW) {
184 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
188 ret = sqlite3_finalize(stmt);
189 if (ret != SQLITE_OK) {
190 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
194 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
195 if (ret != SQLITE_OK) {
196 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));
201 vector<DB::FrameOnDiskAndStreamIdx> DB::load_frame_file(const string &filename, size_t size, unsigned filename_idx)
203 FileContentsProto file_contents;
206 int ret = sqlite3_prepare_v2(db, "SELECT frames FROM filev2 WHERE filename=? AND size=?", -1, &stmt, 0);
207 if (ret != SQLITE_OK) {
208 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
212 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
213 sqlite3_bind_int64(stmt, 2, size);
215 ret = sqlite3_step(stmt);
216 if (ret == SQLITE_ROW) {
217 bool ok = file_contents.ParseFromArray(sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
219 fprintf(stderr, "Frame list in database is corrupted!\n");
222 } else if (ret != SQLITE_DONE) {
223 fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
227 ret = sqlite3_finalize(stmt);
228 if (ret != SQLITE_OK) {
229 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
233 vector<FrameOnDiskAndStreamIdx> frames;
234 for (const StreamContentsProto &stream : file_contents.stream()) {
235 FrameOnDiskAndStreamIdx frame;
236 frame.stream_idx = stream.stream_idx();
237 for (int i = 0; i < stream.pts_size(); ++i) {
238 frame.frame.filename_idx = filename_idx;
239 frame.frame.pts = stream.pts(i);
240 frame.frame.offset = stream.offset(i);
241 frame.frame.size = stream.file_size(i);
242 frames.push_back(frame);
249 void DB::store_frame_file(const string &filename, size_t size, const vector<FrameOnDiskAndStreamIdx> &frames)
251 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
252 if (ret != SQLITE_OK) {
253 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
257 // Delete any existing instances with this filename.
260 ret = sqlite3_prepare_v2(db, "DELETE FROM filev2 WHERE filename=?", -1, &stmt, 0);
261 if (ret != SQLITE_OK) {
262 fprintf(stderr, "DELETE prepare: %s\n", sqlite3_errmsg(db));
266 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
268 ret = sqlite3_step(stmt);
269 if (ret == SQLITE_ROW) {
270 fprintf(stderr, "DELETE step: %s\n", sqlite3_errmsg(db));
274 ret = sqlite3_finalize(stmt);
275 if (ret != SQLITE_OK) {
276 fprintf(stderr, "DELETE finalize: %s\n", sqlite3_errmsg(db));
280 // Create the protobuf blob for the new row.
281 FileContentsProto file_contents;
282 unordered_set<unsigned> seen_stream_idx; // Usually only one.
283 for (const FrameOnDiskAndStreamIdx &frame : frames) {
284 seen_stream_idx.insert(frame.stream_idx);
286 for (unsigned stream_idx : seen_stream_idx) {
287 StreamContentsProto *stream = file_contents.add_stream();
288 stream->set_stream_idx(stream_idx);
289 stream->mutable_pts()->Reserve(frames.size());
290 stream->mutable_offset()->Reserve(frames.size());
291 stream->mutable_file_size()->Reserve(frames.size());
292 for (const FrameOnDiskAndStreamIdx &frame : frames) {
293 if (frame.stream_idx != stream_idx) {
296 stream->add_pts(frame.frame.pts);
297 stream->add_offset(frame.frame.offset);
298 stream->add_file_size(frame.frame.size);
302 file_contents.SerializeToString(&serialized);
304 // Insert the new row.
305 ret = sqlite3_prepare_v2(db, "INSERT INTO filev2 (filename, size, frames) VALUES (?, ?, ?)", -1, &stmt, 0);
306 if (ret != SQLITE_OK) {
307 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
311 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
312 sqlite3_bind_int64(stmt, 2, size);
313 sqlite3_bind_blob(stmt, 3, serialized.data(), serialized.size(), SQLITE_STATIC);
315 ret = sqlite3_step(stmt);
316 if (ret == SQLITE_ROW) {
317 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
321 ret = sqlite3_finalize(stmt);
322 if (ret != SQLITE_OK) {
323 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
328 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
329 if (ret != SQLITE_OK) {
330 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));
335 void DB::clean_unused_frame_files(const vector<string> &used_filenames)
337 int ret = sqlite3_exec(db, "BEGIN", nullptr, nullptr, nullptr);
338 if (ret != SQLITE_OK) {
339 fprintf(stderr, "BEGIN: %s\n", sqlite3_errmsg(db));
343 ret = sqlite3_exec(db, R"(
344 CREATE TEMPORARY TABLE used_filenames ( filename VARCHAR NOT NULL PRIMARY KEY )
345 )", nullptr, nullptr, nullptr);
347 if (ret != SQLITE_OK) {
348 fprintf(stderr, "CREATE TEMPORARY TABLE: %s\n", sqlite3_errmsg(db));
352 // Insert the new rows.
354 ret = sqlite3_prepare_v2(db, "INSERT INTO used_filenames (filename) VALUES (?)", -1, &stmt, 0);
355 if (ret != SQLITE_OK) {
356 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
360 for (const string &filename : used_filenames) {
361 sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), SQLITE_STATIC);
363 ret = sqlite3_step(stmt);
364 if (ret == SQLITE_ROW) {
365 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
369 ret = sqlite3_reset(stmt);
370 if (ret == SQLITE_ROW) {
371 fprintf(stderr, "INSERT reset: %s\n", sqlite3_errmsg(db));
376 ret = sqlite3_finalize(stmt);
377 if (ret != SQLITE_OK) {
378 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
382 ret = sqlite3_exec(db, R"(
383 DELETE FROM filev2 WHERE filename NOT IN ( SELECT filename FROM used_filenames )
384 )", nullptr, nullptr, nullptr);
386 if (ret != SQLITE_OK) {
387 fprintf(stderr, "DELETE: %s\n", sqlite3_errmsg(db));
391 ret = sqlite3_exec(db, R"(
392 DROP TABLE used_filenames
393 )", nullptr, nullptr, nullptr);
395 if (ret != SQLITE_OK) {
396 fprintf(stderr, "DROP TABLE: %s\n", sqlite3_errmsg(db));
401 ret = sqlite3_exec(db, "COMMIT", nullptr, nullptr, nullptr);
402 if (ret != SQLITE_OK) {
403 fprintf(stderr, "COMMIT: %s\n", sqlite3_errmsg(db));