Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 : : * Author: Thun Lu <thun.lu@zohomail.cn>
4 : : * Repo: https://github.com/thun-res/vlink
5 : : * _ __ __ _ __
6 : : * | | / / / / (_) ____ / /__
7 : : * | | / / / / / / / __ \ / //_/
8 : : * | |/ / / /___ / / / / / / / ,<
9 : : * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 : : *
11 : : * Licensed under the Apache License, Version 2.0 (the "License");
12 : : * you may not use this file except in compliance with the License.
13 : : * You may obtain a copy of the License at
14 : : *
15 : : * http://www.apache.org/licenses/LICENSE-2.0
16 : : *
17 : : * Unless required by applicable law or agreed to in writing, software
18 : : * distributed under the License is distributed on an "AS IS" BASIS,
19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 : : * See the License for the specific language governing permissions and
21 : : * limitations under the License.
22 : : */
23 : :
24 : : #include "./extension/vdb_reader.h"
25 : :
26 : : #include <algorithm>
27 : : #include <atomic>
28 : : #include <cmath>
29 : : #include <cstdint>
30 : : #include <cstring>
31 : : #include <filesystem>
32 : : #include <fstream>
33 : : #include <memory>
34 : : #include <mutex>
35 : : #include <shared_mutex>
36 : : #include <string>
37 : : #include <string_view>
38 : : #include <unordered_map>
39 : : #include <utility>
40 : : #include <vector>
41 : :
42 : : #include "./base/condition_variable.h"
43 : : #include "./base/elapsed_timer.h"
44 : : #include "./base/helpers.h"
45 : : #include "./base/logger.h"
46 : : #include "./version.h"
47 : :
48 : : // json
49 : : #include <nlohmann/json.hpp>
50 : :
51 : : #ifdef VLINK_ENABLE_SQLITE
52 : : #include <sqlite3.h>
53 : : #endif
54 : :
55 : : #define ENABLE_DATABASE_TABLE_CHECK 0
56 : :
57 : : namespace vlink {
58 : :
59 : 4887 : [[maybe_unused]] static constexpr int get_column(int column) noexcept { return column; }
60 : :
61 : : [[maybe_unused]] static constexpr size_t kMaxTaskSize = 50000U;
62 : :
63 : : #ifdef VLINK_ENABLE_SQLITE
64 : : struct SqliteStmtFinalizer final {
65 : 296 : void operator()(::sqlite3_stmt* stmt) const noexcept {
66 [ + - ]: 296 : if (stmt) {
67 : 296 : ::sqlite3_finalize(stmt);
68 : : }
69 : 296 : }
70 : : };
71 : :
72 : : using SqliteStmtPtr = std::unique_ptr<::sqlite3_stmt, SqliteStmtFinalizer>;
73 : :
74 : 1251 : static std::string sqlite_column_text_or_empty(::sqlite3_stmt* stmt, int column) {
75 : 1251 : const auto* text = ::sqlite3_column_text(stmt, column);
76 : :
77 [ - + ]: 1251 : if (!text) {
78 : : return {}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
79 : : }
80 : :
81 [ + - ]: 1251 : return {reinterpret_cast<const char*>(text), static_cast<size_t>(::sqlite3_column_bytes(stmt, column))};
82 : : }
83 : : #endif
84 : :
85 : : // VDBReader::Impl
86 : : struct VDBReader::Impl final { // NOLINT(clang-analyzer-optin.performance.Padding)
87 : : std::atomic<BagReader::Status> status{BagReader::kStopped};
88 : : std::atomic_bool stop_flag{false};
89 : : std::atomic_bool pause_flag{false};
90 : : std::atomic_bool pause_next_flag{false};
91 : : std::atomic_bool jump_flag{false};
92 : : std::atomic<int64_t> pause_elapsed{0};
93 : : std::atomic<int64_t> offset_elapsed{0};
94 : : std::atomic<int64_t> real_elapsed{0};
95 : : std::atomic<int64_t> extra_elapsed{0};
96 : : std::atomic<int64_t> begin_time{0};
97 : : std::atomic<double> rate{1.0};
98 : : std::atomic<int> times{1};
99 : : std::atomic_bool is_pending{false};
100 : : std::atomic<int> split_index{0};
101 : :
102 : : bool read_only{false};
103 : : bool try_to_fix{false};
104 : : bool enable_compress{false};
105 : :
106 : : std::string path;
107 : : BagReader::Info info;
108 : : std::vector<BagReader::Info::UrlMeta> raw_url_metas;
109 : : std::mutex mtx;
110 : : ConditionVariable cv;
111 : :
112 : : BagReader::Config config;
113 : : std::mutex config_mtx;
114 : : std::shared_mutex time_mtx;
115 : :
116 : : ElapsedTimer elapsed_timer{ElapsedTimer::kMicro};
117 : : ElapsedTimer pause_elapsed_timer{ElapsedTimer::kMicro};
118 : : ElapsedTimer offset_timer{ElapsedTimer::kMicro};
119 : : ElapsedTimer real_timer{ElapsedTimer::kMicro};
120 : :
121 : : BagReader::StatusCallback status_callback;
122 : : BagReader::ReadyCallback ready_callback;
123 : : BagReader::FinishCallback finish_callback;
124 : :
125 : : std::string update_sql_default_str;
126 : : std::string update_sql_time_str;
127 : :
128 : : int64_t total_start_timestamp_ns{-1};
129 : : bool total_has_completed{false};
130 : :
131 : : // database
132 : : #ifdef VLINK_ENABLE_SQLITE
133 : :
134 : : // WrapperFile
135 : : struct WrapperFile final {
136 : : std::string path;
137 : : ::sqlite3* db{nullptr};
138 : : ::sqlite3_stmt* stmt{nullptr};
139 : : int index{0};
140 : : int64_t start_timestamp_ns{0};
141 : : int64_t begin{0};
142 : : int64_t end{0};
143 : : std::unordered_map<std::string, int> url_to_id_map;
144 : : std::unordered_map<int, std::string> id_to_url_map;
145 : : bool has_idx_elapsed{false};
146 : : bool has_idx_url{false};
147 : : bool has_schema{false};
148 : : bool has_completed{false};
149 : : bool is_channel_broken{false};
150 : :
151 : 133 : WrapperFile() {
152 [ + - ]: 133 : url_to_id_map.reserve(128);
153 [ + - ]: 133 : id_to_url_map.reserve(128);
154 : 133 : }
155 : : };
156 : :
157 : : std::vector<WrapperFile> file_list;
158 : :
159 : : SqliteStmtPtr cursor_stmt;
160 : : int cursor_file_index{0};
161 : : int64_t cursor_begin_us{0};
162 : : int64_t cursor_end_us{0};
163 : : BagReader::Config cursor_config;
164 : : #endif
165 : : };
166 : :
167 : : // VDBReader
168 : 109 : VDBReader::VDBReader(const std::string& path, bool read_only, bool try_to_fix)
169 [ + - ]: 109 : : BagReader(path, read_only, try_to_fix), impl_(std::make_unique<Impl>()) {
170 [ + - + - ]: 109 : set_name("VDBReader");
171 : :
172 [ + - + - ]: 109 : url_ser_map().reserve(128);
173 [ + - + - ]: 109 : url_schema_type_map().reserve(128);
174 [ + - ]: 109 : impl_->update_sql_default_str.reserve(256);
175 [ + - ]: 109 : impl_->update_sql_time_str.reserve(256);
176 : :
177 : 109 : impl_->read_only = read_only;
178 : 109 : impl_->try_to_fix = try_to_fix;
179 : :
180 [ + + ]: 109 : open(path);
181 : :
182 : : #ifndef VLINK_ENABLE_SQLITE
183 : : VLOG_F("VDBReader: The compile macro VLINK_ENABLE_SQLITE is not turned on.");
184 : : #endif
185 : 119 : }
186 : :
187 : 99 : VDBReader::~VDBReader() {
188 [ + + ]: 99 : if (!impl_->stop_flag.load(std::memory_order_relaxed)) {
189 : 98 : do_stop();
190 : : }
191 : :
192 : 99 : quit(true);
193 : :
194 : 99 : impl_->cv.notify_one();
195 : :
196 : 99 : wait_for_quit();
197 : :
198 : 99 : detach_plugin();
199 : :
200 : 99 : close();
201 : 99 : }
202 : :
203 : 8 : void VDBReader::bind_bag_interface(const std::shared_ptr<BagPluginInterface>& bag_interface) {
204 : 8 : BagReader::bind_bag_interface(bag_interface);
205 : 8 : impl_->info.url_metas = impl_->raw_url_metas;
206 : 8 : process_url_metas(impl_->info.url_metas);
207 : 8 : rebuild_url_meta_lookup(impl_->info.url_metas);
208 : 8 : }
209 : :
210 : 2 : void VDBReader::register_status_callback(StatusCallback&& status_callback) {
211 : 2 : impl_->status_callback = std::move(status_callback);
212 : 2 : }
213 : :
214 : 4 : void VDBReader::register_ready_callback(ReadyCallback&& ready_callback) {
215 : 4 : impl_->ready_callback = std::move(ready_callback);
216 : 4 : }
217 : :
218 : 9 : void VDBReader::register_finish_callback(FinishCallback&& finish_callback) {
219 : 9 : impl_->finish_callback = std::move(finish_callback);
220 : 9 : }
221 : :
222 : 10 : void VDBReader::register_output_callback(OutputCallback&& output_callback) {
223 : 10 : BagReader::register_output_callback(std::move(output_callback));
224 : 10 : }
225 : :
226 : 16 : void VDBReader::play(const Config& config) {
227 : : #ifdef VLINK_ENABLE_SQLITE
228 : :
229 [ + - - + ]: 16 : if VUNLIKELY (is_busy()) {
230 [ # # # # ]: 0 : VLOG_W("VDBReader: Is busy.");
231 : : }
232 : :
233 [ + + ]: 16 : if (config.skip_blank) {
234 : 1 : impl_->begin_time.store(std::max(config.begin_time, impl_->info.blank_duration), std::memory_order_relaxed);
235 : : } else {
236 : 15 : impl_->begin_time.store(config.begin_time, std::memory_order_relaxed);
237 : : }
238 : :
239 [ + + ]: 16 : if (config.rate <= 0) {
240 : 1 : impl_->rate.store(1, std::memory_order_relaxed);
241 : : } else {
242 : 15 : impl_->rate.store(config.rate, std::memory_order_relaxed);
243 : : }
244 : :
245 : 16 : impl_->times.store(config.times, std::memory_order_relaxed);
246 : :
247 : 32 : impl_->real_elapsed.store(impl_->begin_time.load(std::memory_order_relaxed) * 1000U, std::memory_order_relaxed);
248 : 16 : impl_->is_pending.store(true, std::memory_order_relaxed);
249 : :
250 : : {
251 [ + - ]: 16 : std::unique_lock lock(impl_->mtx);
252 : 16 : impl_->stop_flag.store(false, std::memory_order_relaxed);
253 : 16 : impl_->pause_flag.store(false, std::memory_order_relaxed);
254 : 16 : impl_->pause_next_flag.store(false, std::memory_order_relaxed);
255 : 16 : impl_->jump_flag.store(false, std::memory_order_relaxed);
256 : 16 : }
257 : :
258 : 16 : Config config_copy;
259 : :
260 : : {
261 [ + - ]: 16 : std::unique_lock lock(impl_->config_mtx);
262 [ + - ]: 16 : impl_->config = config;
263 [ + - ]: 16 : config_copy = impl_->config;
264 : 16 : }
265 : :
266 [ + - + - ]: 32 : post_task([this, config_copy = std::move(config_copy)]() { read(config_copy); });
267 : : #else
268 : : (void)config;
269 : : #endif
270 : 16 : }
271 : :
272 : 4 : void VDBReader::stop() { do_stop(); }
273 : :
274 : 2 : void VDBReader::pause() {
275 : : #ifdef VLINK_ENABLE_SQLITE
276 : : {
277 [ + - ]: 2 : std::unique_lock lock(impl_->mtx);
278 : 2 : impl_->pause_flag.store(true, std::memory_order_relaxed);
279 : 2 : }
280 : :
281 : 2 : impl_->cv.notify_one();
282 : : #endif
283 : 2 : }
284 : :
285 : 2 : void VDBReader::resume() {
286 : : #ifdef VLINK_ENABLE_SQLITE
287 : : {
288 [ + - ]: 2 : std::unique_lock lock(impl_->mtx);
289 : 2 : impl_->pause_flag.store(false, std::memory_order_relaxed);
290 : 2 : }
291 : :
292 : 2 : impl_->cv.notify_one();
293 : : #endif
294 : 2 : }
295 : :
296 : 4 : void VDBReader::pause_to_next() {
297 : : #ifdef VLINK_ENABLE_SQLITE
298 : : {
299 [ + - ]: 4 : std::unique_lock lock(impl_->mtx);
300 : :
301 [ + + ]: 4 : if (!impl_->pause_flag.load(std::memory_order_relaxed)) {
302 : 1 : return;
303 : : }
304 : :
305 : 3 : impl_->pause_next_flag.store(true, std::memory_order_relaxed);
306 [ + + ]: 4 : }
307 : :
308 : 3 : impl_->cv.notify_one();
309 : : #endif
310 : : }
311 : :
312 : 6 : void VDBReader::jump(int64_t begin_time, double rate, int times, bool force_to_play) {
313 : : #ifdef VLINK_ENABLE_SQLITE
314 : :
315 [ + + ]: 6 : if (begin_time < 0) {
316 : 1 : begin_time = 0;
317 [ + + ]: 5 : } else if (begin_time > impl_->info.total_duration) {
318 : 1 : begin_time = std::max<int64_t>(0, impl_->info.total_duration - 100);
319 : : }
320 : :
321 : 6 : impl_->real_elapsed.store(begin_time * 1000U, std::memory_order_relaxed);
322 : 6 : impl_->is_pending.store(true, std::memory_order_relaxed);
323 : :
324 : 6 : bool last_pause_flag = impl_->pause_flag.load(std::memory_order_relaxed);
325 : :
326 : : {
327 [ + - ]: 6 : std::unique_lock lock(impl_->mtx);
328 : 6 : impl_->stop_flag.store(false, std::memory_order_relaxed);
329 : 6 : impl_->pause_flag.store(false, std::memory_order_relaxed);
330 : 6 : impl_->pause_next_flag.store(false, std::memory_order_relaxed);
331 : 6 : impl_->jump_flag.store(true, std::memory_order_relaxed);
332 : 6 : }
333 : :
334 : 6 : impl_->cv.notify_one();
335 : :
336 [ + + ]: 12 : for (const auto& wrapper_file : impl_->file_list) {
337 [ + - ]: 6 : if (wrapper_file.db) {
338 [ + - ]: 6 : ::sqlite3_interrupt(wrapper_file.db);
339 : : }
340 : : }
341 : :
342 [ + - ]: 6 : wait_for_idle();
343 : :
344 : 6 : impl_->begin_time.store(begin_time, std::memory_order_relaxed);
345 : :
346 [ + + ]: 6 : if (rate <= 0) {
347 : 3 : impl_->rate.store(1, std::memory_order_relaxed);
348 : : } else {
349 : 3 : impl_->rate.store(rate, std::memory_order_relaxed);
350 : : }
351 : :
352 : 6 : impl_->times.store(times, std::memory_order_relaxed);
353 : :
354 : : {
355 [ + - ]: 6 : std::unique_lock lock(impl_->mtx);
356 : 6 : impl_->stop_flag.store(false, std::memory_order_relaxed);
357 [ + + ]: 7 : impl_->pause_flag.store(force_to_play ? false : last_pause_flag, std::memory_order_relaxed);
358 : 6 : impl_->pause_next_flag.store(false, std::memory_order_relaxed);
359 : 6 : impl_->jump_flag.store(false, std::memory_order_relaxed);
360 : 6 : }
361 : :
362 : 6 : Config config_copy;
363 : :
364 : : {
365 [ + - ]: 6 : std::unique_lock lock(impl_->config_mtx);
366 [ + - ]: 6 : config_copy = impl_->config;
367 : 6 : }
368 : :
369 [ + - + - ]: 12 : post_task([this, config_copy = std::move(config_copy)]() { read(config_copy); });
370 : : #else
371 : : (void)begin_time;
372 : : (void)rate;
373 : : (void)times;
374 : : (void)force_to_play;
375 : : #endif
376 : 7 : }
377 : :
378 : 57 : std::future<bool> VDBReader::check() {
379 : : #ifdef VLINK_ENABLE_SQLITE
380 : :
381 [ + + ]: 57 : if VUNLIKELY (is_busy()) {
382 [ + - + - ]: 2 : VLOG_W("VDBReader: Is busy.");
383 : : }
384 : :
385 : 837 : return invoke_task([this]() {
386 : 57 : int ret = 0;
387 : :
388 [ + + ]: 57 : if (!impl_->total_has_completed) {
389 [ + - + - ]: 2 : VLOG_W("VDBReader: Incomplete data detected.");
390 : 1 : return false;
391 : : }
392 : :
393 [ + + ]: 133 : for (auto& wrapper_file : impl_->file_list) {
394 [ - + ]: 77 : if VUNLIKELY (!wrapper_file.db) {
395 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
396 : : }
397 : :
398 [ + + ]: 77 : if (wrapper_file.stmt) {
399 [ + - ]: 2 : ::sqlite3_finalize(wrapper_file.stmt);
400 : 2 : wrapper_file.stmt = nullptr;
401 : : }
402 : :
403 : 77 : ::sqlite3_stmt* integrity_stmt = nullptr;
404 [ + - ]: 77 : ret = ::sqlite3_prepare_v2(wrapper_file.db, "PRAGMA integrity_check;", -1, &integrity_stmt, nullptr);
405 : 77 : SqliteStmtPtr integrity_stmt_guard(integrity_stmt);
406 : :
407 [ + - - + ]: 77 : if VUNLIKELY (is_ready_to_quit()) {
408 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
409 [ - + ]: 77 : } else if VUNLIKELY (ret != SQLITE_OK) {
410 : : CLOG_W("Failed to prepare integrity check: %s.", // LCOV_EXCL_LINE GCOVR_EXCL_LINE
411 : : ::sqlite3_errmsg(wrapper_file.db)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
412 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
413 : : }
414 : :
415 : 77 : bool has_integrity_result = false;
416 : 77 : int step_ret = SQLITE_OK;
417 : : for (;;) {
418 [ + - ]: 154 : step_ret = ::sqlite3_step(integrity_stmt);
419 : :
420 [ + + ]: 154 : if (step_ret != SQLITE_ROW) {
421 : 77 : break;
422 : : }
423 : :
424 : 77 : has_integrity_result = true;
425 [ + - ]: 77 : const auto integrity_result = sqlite_column_text_or_empty(integrity_stmt, get_column(0));
426 : :
427 [ + - - + ]: 77 : if VUNLIKELY (integrity_result != "ok") {
428 : : CLOG_W("Failed integrity check: %s.", integrity_result.c_str()); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
429 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
430 : : }
431 [ + - ]: 154 : }
432 : :
433 [ + - - + : 77 : if VUNLIKELY (step_ret != SQLITE_DONE || !has_integrity_result) {
- + ]
434 : : CLOG_W("Failed to read integrity check: %s.", // LCOV_EXCL_LINE GCOVR_EXCL_LINE
435 : : ::sqlite3_errmsg(wrapper_file.db)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
436 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
437 : : }
438 : :
439 : 77 : integrity_stmt_guard.reset();
440 : :
441 [ + - ]: 77 : ret = ::sqlite3_prepare_v2(wrapper_file.db, "SELECT * FROM VLinkDatas;", -1, &wrapper_file.stmt, nullptr);
442 : :
443 [ + - - + ]: 77 : if VUNLIKELY (is_ready_to_quit()) {
444 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
445 [ - + ]: 77 : } else if VUNLIKELY (ret != SQLITE_OK) {
446 : : CLOG_W("Failed to prepare datas table: %s.", // LCOV_EXCL_LINE GCOVR_EXCL_LINE
447 : : ::sqlite3_errmsg(wrapper_file.db)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
448 : :
449 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
450 : : }
451 : :
452 [ + - ]: 77 : ::sqlite3_step(wrapper_file.stmt);
453 [ + - ]: 77 : }
454 : :
455 : 56 : bool is_ok = true;
456 : :
457 [ + + ]: 56 : if VUNLIKELY (impl_->info.total_duration < impl_->info.blank_duration) {
458 [ + - + - ]: 2 : VLOG_W("VDBReader: Invalid duration, blank=", impl_->info.blank_duration, " total=", impl_->info.total_duration,
459 : : ".");
460 : 1 : is_ok = false;
461 : : }
462 : :
463 [ + + + + : 56 : if VUNLIKELY (impl_->info.message_count > 0 && impl_->info.url_metas.empty()) {
+ + ]
464 [ + - + - ]: 4 : VLOG_W("VDBReader: Message count is ", impl_->info.message_count, " but url meta list is empty.");
465 : 2 : is_ok = false;
466 : : }
467 : :
468 : 56 : size_t total_count = 0;
469 : 56 : size_t total_raw_size = 0;
470 : :
471 [ + + ]: 153 : for (const auto& url_meta : impl_->info.url_metas) {
472 : 97 : total_count += url_meta.count;
473 : 97 : total_raw_size += url_meta.size;
474 : :
475 [ - + ]: 97 : if VUNLIKELY (!url_meta.valid) {
476 : : CLOG_W("VDBReader: Invalid url meta detected at index=%d.", url_meta.index); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
477 : : is_ok = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
478 : : }
479 : :
480 [ + + ]: 97 : if VUNLIKELY (url_meta.url.empty()) {
481 [ + - + - ]: 4 : CLOG_W("VDBReader: Empty url detected at index=%d.", url_meta.index);
482 : 2 : is_ok = false;
483 : : }
484 : :
485 [ + + ]: 97 : if VUNLIKELY (url_meta.url_type.empty()) {
486 [ + - + - ]: 4 : CLOG_W("VDBReader: Empty url_type detected for url=%s.", url_meta.url.c_str());
487 : 2 : is_ok = false;
488 : : }
489 : :
490 [ + - + + : 97 : if VUNLIKELY (url_meta.count > 0 && url_meta.ser_type.empty()) {
+ + ]
491 [ + - + - ]: 6 : CLOG_W("VDBReader: Empty ser_type detected for url=%s.", url_meta.url.c_str());
492 : 3 : is_ok = false;
493 : : }
494 : :
495 [ - + ]: 97 : if VUNLIKELY (!SchemaData::is_valid_type(url_meta.schema_type)) {
496 : : CLOG_W("VDBReader: Invalid schema_type=%d detected for url=%s.", // LCOV_EXCL_LINE GCOVR_EXCL_LINE
497 : : static_cast<int>(url_meta.schema_type), // LCOV_EXCL_LINE GCOVR_EXCL_LINE
498 : : url_meta.url.c_str());
499 : : is_ok = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
500 : : }
501 : :
502 : 97 : auto inferred_schema_type = SchemaData::infer_ser_type(url_meta.ser_type);
503 : :
504 [ + + + + : 97 : if VUNLIKELY (url_meta.schema_type == SchemaType::kUnknown && inferred_schema_type != SchemaType::kUnknown) {
+ + ]
505 : 1 : const auto schema_label = SchemaData::convert_type(inferred_schema_type);
506 [ + - + - ]: 2 : CLOG_W("VDBReader: Missing schema_type for url=%s, inferred=%.*s.", url_meta.url.c_str(),
507 : : static_cast<int>(schema_label.size()), schema_label.data());
508 : 1 : is_ok = false;
509 : : }
510 : :
511 [ + - + + : 97 : if VUNLIKELY (!std::isfinite(url_meta.loss) ||
+ + + + +
+ + + +
+ ]
512 : : (url_meta.loss != -1.0 && (url_meta.loss < 0.0 || url_meta.loss > 1.0))) {
513 [ + - + - ]: 8 : CLOG_W("VDBReader: Invalid loss=%f detected for url=%s.", url_meta.loss, url_meta.url.c_str());
514 : 4 : is_ok = false;
515 : : }
516 : :
517 [ + + ]: 97 : if VUNLIKELY (url_meta.freq < 0.0) {
518 [ + - + - ]: 6 : CLOG_W("VDBReader: Invalid freq=%f detected for url=%s.", url_meta.freq, url_meta.url.c_str());
519 : 3 : is_ok = false;
520 : : }
521 : : }
522 : :
523 [ + + + + : 111 : if ((!impl_->info.url_metas.empty() || impl_->info.message_count != 0) &&
+ + ]
524 [ + + ]: 55 : total_count != static_cast<size_t>(impl_->info.message_count)) {
525 [ + - + - ]: 10 : VLOG_W("VDBReader: Message count mismatch, header=", impl_->info.message_count, " metas=", total_count, ".");
526 : 5 : is_ok = false;
527 : : }
528 : :
529 [ + + - + : 109 : if ((!impl_->info.url_metas.empty() || impl_->info.total_raw_size != 0) &&
- + ]
530 [ - + ]: 53 : total_raw_size != static_cast<size_t>(impl_->info.total_raw_size)) {
531 [ # # # # ]: 0 : VLOG_W("VDBReader: Raw size mismatch, header=", impl_->info.total_raw_size,
532 : : " metas=", total_raw_size, // LCOV_EXCL_LINE GCOVR_EXCL_LINE
533 : : "."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
534 : : is_ok = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
535 : : }
536 : :
537 [ + - + + ]: 84 : for (const auto& schema_data : detect_schema()) {
538 [ - + ]: 28 : if VUNLIKELY (schema_data.name.empty()) {
539 : : CLOG_W("VDBReader: Empty schema name detected."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
540 : : is_ok = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
541 : : }
542 : :
543 [ + + ]: 28 : if VUNLIKELY (schema_data.encoding.empty()) {
544 [ + - + - ]: 2 : CLOG_W("VDBReader: Empty schema encoding detected for name=%s.", schema_data.name.c_str());
545 : 1 : is_ok = false;
546 : : }
547 : :
548 [ + - + + : 28 : if VUNLIKELY (!SchemaData::is_valid_type(schema_data.schema_type) ||
+ + ]
549 : : schema_data.schema_type == SchemaType::kUnknown) {
550 [ + - + - ]: 4 : CLOG_W("VDBReader: Invalid schema_type=%d detected for schema=%s.", static_cast<int>(schema_data.schema_type),
551 : : schema_data.name.c_str());
552 : 2 : is_ok = false;
553 : : }
554 : 56 : }
555 : :
556 : 56 : return is_ok;
557 [ + - ]: 57 : });
558 : : #else
559 : : return std::future<bool>();
560 : : #endif
561 : : }
562 : :
563 : 5 : std::future<bool> VDBReader::reindex() {
564 : : #ifdef VLINK_ENABLE_SQLITE
565 : :
566 [ + + ]: 5 : if VUNLIKELY (is_busy()) {
567 [ + - + - ]: 2 : VLOG_W("VDBReader: Is busy.");
568 : : }
569 : :
570 : 41 : return invoke_task([this]() {
571 : 5 : int ret = 0;
572 : 5 : char* err_msg = nullptr;
573 : :
574 [ + + ]: 11 : for (auto& wrapper_file : impl_->file_list) {
575 [ - + ]: 6 : if VUNLIKELY (!wrapper_file.db) {
576 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
577 : : }
578 : :
579 [ + + ]: 6 : if (wrapper_file.stmt) {
580 [ + - ]: 5 : ::sqlite3_finalize(wrapper_file.stmt);
581 : 5 : wrapper_file.stmt = nullptr;
582 : : }
583 : :
584 [ + - ]: 6 : ret = ::sqlite3_exec(wrapper_file.db, "DROP INDEX IF EXISTS idx_elapsed;", nullptr, nullptr, &err_msg);
585 : :
586 [ + - + - : 6 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
587 : : // LCOV_EXCL_START GCOVR_EXCL_START
588 : : CLOG_W("Failed to drop idx_elapsed: %s.", err_msg);
589 : :
590 : : if (err_msg) {
591 : : ::sqlite3_free(err_msg);
592 : : err_msg = nullptr;
593 : : }
594 : :
595 : : return false;
596 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
597 : : }
598 : :
599 [ + - ]: 6 : ret = ::sqlite3_exec(wrapper_file.db, "DROP INDEX IF EXISTS idx_url;", nullptr, nullptr, &err_msg);
600 : :
601 [ + - + - : 6 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
602 : : // LCOV_EXCL_START GCOVR_EXCL_START
603 : : CLOG_W("Failed to drop idx_url: %s.", err_msg);
604 : :
605 : : if (err_msg) {
606 : : ::sqlite3_free(err_msg);
607 : : err_msg = nullptr;
608 : : }
609 : :
610 : : return false;
611 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
612 : : }
613 : :
614 [ + - ]: 6 : ret = ::sqlite3_exec(wrapper_file.db, "DROP INDEX IF EXISTS idx_elapsed_url;", nullptr, nullptr, &err_msg);
615 : :
616 [ + - + - : 6 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
617 : : // LCOV_EXCL_START GCOVR_EXCL_START
618 : : CLOG_W("Failed to drop idx_elapsed_url: %s.", err_msg);
619 : :
620 : : if (err_msg) {
621 : : ::sqlite3_free(err_msg);
622 : : err_msg = nullptr;
623 : : }
624 : :
625 : : return false;
626 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
627 : : }
628 : :
629 [ + - ]: 6 : ret = ::sqlite3_exec(wrapper_file.db, "CREATE INDEX idx_elapsed_url ON VLinkDatas(elapsed, url);", nullptr,
630 : : nullptr, &err_msg);
631 : :
632 [ + - - + ]: 6 : if VUNLIKELY (is_ready_to_quit()) {
633 : : // LCOV_EXCL_START GCOVR_EXCL_START
634 : : if (err_msg) {
635 : : ::sqlite3_free(err_msg);
636 : : err_msg = nullptr;
637 : : }
638 : :
639 : : return false;
640 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
641 [ - + ]: 6 : } else if VUNLIKELY (ret != SQLITE_OK) {
642 : : // LCOV_EXCL_START GCOVR_EXCL_START
643 : : CLOG_W("Failed to create idx_elapsed_url: %s.", err_msg);
644 : :
645 : : if (err_msg) {
646 : : ::sqlite3_free(err_msg);
647 : : err_msg = nullptr;
648 : : }
649 : :
650 : : return false;
651 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
652 : : }
653 : :
654 [ + - ]: 6 : ret = ::sqlite3_exec(wrapper_file.db, "PRAGMA optimize;", nullptr, nullptr, &err_msg);
655 : :
656 [ + - - + ]: 6 : if VUNLIKELY (is_ready_to_quit()) {
657 : : // LCOV_EXCL_START GCOVR_EXCL_START
658 : : if (err_msg) {
659 : : ::sqlite3_free(err_msg);
660 : : err_msg = nullptr;
661 : : }
662 : :
663 : : return false;
664 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
665 [ - + ]: 6 : } else if VUNLIKELY (ret != SQLITE_OK) {
666 : : // LCOV_EXCL_START GCOVR_EXCL_START
667 : : CLOG_W("Failed to optimize: %s.", err_msg);
668 : :
669 : : if (err_msg) {
670 : : ::sqlite3_free(err_msg);
671 : : err_msg = nullptr;
672 : : }
673 : :
674 : : return false;
675 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
676 : : }
677 : :
678 [ + - ]: 6 : ret = ::sqlite3_prepare_v2(wrapper_file.db, "SELECT * FROM VLinkDatas;", -1, &wrapper_file.stmt, nullptr);
679 : :
680 [ + - - + ]: 6 : if VUNLIKELY (is_ready_to_quit()) {
681 : : // LCOV_EXCL_START GCOVR_EXCL_START
682 : : if (err_msg) {
683 : : ::sqlite3_free(err_msg);
684 : : err_msg = nullptr;
685 : : }
686 : :
687 : : return false;
688 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
689 [ - + ]: 6 : } else if VUNLIKELY (ret != SQLITE_OK) {
690 : : // LCOV_EXCL_START GCOVR_EXCL_START
691 : : CLOG_W("Failed to prepare datas table: %s.", ::sqlite3_errmsg(wrapper_file.db));
692 : :
693 : : if (err_msg) {
694 : : ::sqlite3_free(err_msg);
695 : : err_msg = nullptr;
696 : : }
697 : :
698 : : return false;
699 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
700 : : }
701 : :
702 [ + - ]: 6 : ::sqlite3_step(wrapper_file.stmt);
703 : :
704 [ - + ]: 6 : if (err_msg) {
705 : : ::sqlite3_free(err_msg); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
706 : : err_msg = nullptr; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
707 : : }
708 : : }
709 : :
710 : 5 : return true;
711 [ + - ]: 5 : });
712 : : #else
713 : : return std::future<bool>();
714 : : #endif
715 : : }
716 : :
717 : 6 : std::future<bool> VDBReader::fix(bool rebuild) {
718 : : #ifdef VLINK_ENABLE_SQLITE
719 : :
720 [ + + ]: 6 : if VUNLIKELY (is_busy()) {
721 [ + - + - ]: 2 : VLOG_W("VDBReader: Is busy.");
722 : : }
723 : :
724 : 101 : return invoke_task([this, rebuild]() {
725 : 6 : int ret = 0;
726 : 6 : char* err_msg = nullptr;
727 : :
728 [ + + ]: 14 : for (auto& wrapper_file : impl_->file_list) {
729 [ - + ]: 8 : if VUNLIKELY (!wrapper_file.db) {
730 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
731 : : }
732 : :
733 [ + - ]: 8 : if (wrapper_file.stmt) {
734 [ + - ]: 8 : ::sqlite3_finalize(wrapper_file.stmt);
735 : 8 : wrapper_file.stmt = nullptr;
736 : : }
737 : :
738 : : // opt
739 : : {
740 [ + - ]: 8 : ret = ::sqlite3_exec(wrapper_file.db, "PRAGMA synchronous = OFF;", nullptr, nullptr, &err_msg);
741 : :
742 [ + - + - : 8 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
743 : : // LCOV_EXCL_START GCOVR_EXCL_START
744 : : CLOG_W("Failed to set synchronous: %s.", err_msg);
745 : :
746 : : if (err_msg) {
747 : : ::sqlite3_free(err_msg);
748 : : err_msg = nullptr;
749 : : }
750 : :
751 : : return false;
752 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
753 : : }
754 : :
755 [ + - ]: 8 : ret = ::sqlite3_exec(wrapper_file.db, "PRAGMA temp_store = MEMORY;", nullptr, nullptr, &err_msg);
756 : :
757 [ + - + - : 8 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
758 : : // LCOV_EXCL_START GCOVR_EXCL_START
759 : : CLOG_W("Failed to set temp_store: %s.", err_msg);
760 : :
761 : : if (err_msg) {
762 : : ::sqlite3_free(err_msg);
763 : : err_msg = nullptr;
764 : : }
765 : :
766 : : return false;
767 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
768 : : }
769 : :
770 [ + - ]: 8 : ret = ::sqlite3_exec(wrapper_file.db, "PRAGMA page_size = 16384;", nullptr, nullptr, &err_msg);
771 : :
772 [ + - + - : 8 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
773 : : // LCOV_EXCL_START GCOVR_EXCL_START
774 : : CLOG_W("Failed to set page_size: %s.", err_msg);
775 : :
776 : : if (err_msg) {
777 : : ::sqlite3_free(err_msg);
778 : : err_msg = nullptr;
779 : : }
780 : :
781 : : return false;
782 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
783 : : }
784 : :
785 [ + - ]: 8 : ret = ::sqlite3_exec(wrapper_file.db, "PRAGMA cache_size = 8192;", nullptr, nullptr, &err_msg);
786 : :
787 [ + - + - : 8 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
788 : : // LCOV_EXCL_START GCOVR_EXCL_START
789 : : CLOG_W("Failed to set cache_size: %s.", err_msg);
790 : :
791 : : if (err_msg) {
792 : : ::sqlite3_free(err_msg);
793 : : err_msg = nullptr;
794 : : }
795 : :
796 : : return false;
797 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
798 : : }
799 : :
800 [ + - ]: 8 : ret = ::sqlite3_exec(wrapper_file.db, "PRAGMA journal_mode = OFF;", nullptr, nullptr, &err_msg);
801 : :
802 [ + - + - : 8 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
803 : : // LCOV_EXCL_START GCOVR_EXCL_START
804 : : CLOG_W("Failed to restore journal mode: %s.", err_msg);
805 : :
806 : : if (err_msg) {
807 : : ::sqlite3_free(err_msg);
808 : : err_msg = nullptr;
809 : : }
810 : :
811 : : return false;
812 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
813 : : }
814 : :
815 [ + - ]: 8 : ret = ::sqlite3_exec(wrapper_file.db, "PRAGMA automatic_index = OFF;", nullptr, nullptr, &err_msg);
816 : :
817 [ + - + - : 8 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
818 : : // LCOV_EXCL_START GCOVR_EXCL_START
819 : : CLOG_W("Failed to set automatic_index: %s.", err_msg);
820 : :
821 : : if (err_msg) {
822 : : ::sqlite3_free(err_msg);
823 : : err_msg = nullptr;
824 : : }
825 : :
826 : : return false;
827 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
828 : : }
829 : :
830 [ + - ]: 8 : ret = ::sqlite3_exec(wrapper_file.db, "PRAGMA locking_mode = EXCLUSIVE;", nullptr, nullptr, &err_msg);
831 : :
832 [ + - - + ]: 8 : if VUNLIKELY (is_ready_to_quit()) {
833 : : // LCOV_EXCL_START GCOVR_EXCL_START
834 : : if (err_msg) {
835 : : ::sqlite3_free(err_msg);
836 : : err_msg = nullptr;
837 : : }
838 : :
839 : : return false;
840 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
841 [ - + ]: 8 : } else if VUNLIKELY (ret != SQLITE_OK) {
842 : : // LCOV_EXCL_START GCOVR_EXCL_START
843 : : CLOG_W("Failed to set locking_mode: %s.", err_msg);
844 : :
845 : : if (err_msg) {
846 : : ::sqlite3_free(err_msg);
847 : : err_msg = nullptr;
848 : : }
849 : :
850 : : return false;
851 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
852 : : }
853 : : }
854 : :
855 [ + + ]: 8 : if (rebuild) {
856 [ + - ]: 3 : ret = ::sqlite3_exec(wrapper_file.db, "VACUUM;", nullptr, nullptr, &err_msg);
857 : :
858 [ + - - + ]: 3 : if VUNLIKELY (is_ready_to_quit()) {
859 : : // LCOV_EXCL_START GCOVR_EXCL_START
860 : : if (err_msg) {
861 : : ::sqlite3_free(err_msg);
862 : : err_msg = nullptr;
863 : : }
864 : :
865 : : return false;
866 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
867 [ - + ]: 3 : } else if VUNLIKELY (ret != SQLITE_OK) {
868 : : // LCOV_EXCL_START GCOVR_EXCL_START
869 : : CLOG_W("Failed to vacuum: %s.", err_msg);
870 : :
871 : : if (err_msg) {
872 : : ::sqlite3_free(err_msg);
873 : : err_msg = nullptr;
874 : : }
875 : :
876 : : return false;
877 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
878 : : }
879 : :
880 [ + - ]: 3 : ret = ::sqlite3_exec(wrapper_file.db, "DROP INDEX IF EXISTS idx_elapsed;", nullptr, nullptr, &err_msg);
881 : :
882 [ + - + - : 3 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
883 : : // LCOV_EXCL_START GCOVR_EXCL_START
884 : : CLOG_W("Failed to drop idx_elapsed: %s.", err_msg);
885 : :
886 : : if (err_msg) {
887 : : ::sqlite3_free(err_msg);
888 : : err_msg = nullptr;
889 : : }
890 : :
891 : : return false;
892 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
893 : : }
894 : :
895 [ + - ]: 3 : ret = ::sqlite3_exec(wrapper_file.db, "DROP INDEX IF EXISTS idx_url;", nullptr, nullptr, &err_msg);
896 : :
897 [ + - + - : 3 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
898 : : // LCOV_EXCL_START GCOVR_EXCL_START
899 : : CLOG_W("Failed to drop idx_url: %s.", err_msg);
900 : :
901 : : if (err_msg) {
902 : : ::sqlite3_free(err_msg);
903 : : err_msg = nullptr;
904 : : }
905 : :
906 : : return false;
907 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
908 : : }
909 : :
910 [ + - ]: 3 : ret = ::sqlite3_exec(wrapper_file.db, "DROP INDEX IF EXISTS idx_elapsed_url;", nullptr, nullptr, &err_msg);
911 : :
912 [ + - + - : 3 : if VUNLIKELY (!is_ready_to_quit() && ret != SQLITE_OK) {
- + - + ]
913 : : // LCOV_EXCL_START GCOVR_EXCL_START
914 : : CLOG_W("Failed to drop idx_elapsed_url: %s.", err_msg);
915 : :
916 : : if (err_msg) {
917 : : ::sqlite3_free(err_msg);
918 : : err_msg = nullptr;
919 : : }
920 : :
921 : : return false;
922 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
923 : : }
924 : :
925 [ + - ]: 3 : ret = ::sqlite3_exec(wrapper_file.db, "CREATE INDEX idx_elapsed_url ON VLinkDatas(elapsed, url);", nullptr,
926 : : nullptr, &err_msg);
927 : :
928 [ + - - + ]: 3 : if VUNLIKELY (is_ready_to_quit()) {
929 : : // LCOV_EXCL_START GCOVR_EXCL_START
930 : : if (err_msg) {
931 : : ::sqlite3_free(err_msg);
932 : : err_msg = nullptr;
933 : : }
934 : :
935 : : return false;
936 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
937 [ - + ]: 3 : } else if VUNLIKELY (ret != SQLITE_OK) {
938 : : // LCOV_EXCL_START GCOVR_EXCL_START
939 : : CLOG_W("Failed to create idx_elapsed_url: %s.", err_msg);
940 : :
941 : : if (err_msg) {
942 : : ::sqlite3_free(err_msg);
943 : : err_msg = nullptr;
944 : : }
945 : :
946 : : return false;
947 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
948 : : }
949 : : }
950 : :
951 [ + - ]: 8 : ret = ::sqlite3_exec(wrapper_file.db, "PRAGMA optimize;", nullptr, nullptr, &err_msg);
952 : :
953 [ + - - + ]: 8 : if VUNLIKELY (is_ready_to_quit()) {
954 : : // LCOV_EXCL_START GCOVR_EXCL_START
955 : : if (err_msg) {
956 : : ::sqlite3_free(err_msg);
957 : : err_msg = nullptr;
958 : : }
959 : :
960 : : return false;
961 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
962 [ - + ]: 8 : } else if VUNLIKELY (ret != SQLITE_OK) {
963 : : // LCOV_EXCL_START GCOVR_EXCL_START
964 : : CLOG_W("Failed to optimize: %s.", err_msg);
965 : :
966 : : if (err_msg) {
967 : : ::sqlite3_free(err_msg);
968 : : err_msg = nullptr;
969 : : }
970 : :
971 : : return false;
972 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
973 : : }
974 : :
975 [ + - ]: 8 : ret = ::sqlite3_prepare_v2(wrapper_file.db, "SELECT * FROM VLinkDatas;", -1, &wrapper_file.stmt, nullptr);
976 : :
977 [ + - - + ]: 8 : if VUNLIKELY (is_ready_to_quit()) {
978 : : // LCOV_EXCL_START GCOVR_EXCL_START
979 : : if (err_msg) {
980 : : ::sqlite3_free(err_msg);
981 : : err_msg = nullptr;
982 : : }
983 : :
984 : : return false;
985 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
986 [ - + ]: 8 : } else if VUNLIKELY (ret != SQLITE_OK) {
987 : : // LCOV_EXCL_START GCOVR_EXCL_START
988 : : CLOG_W("Failed to prepare datas table: %s.", ::sqlite3_errmsg(wrapper_file.db));
989 : :
990 : : if (err_msg) {
991 : : ::sqlite3_free(err_msg);
992 : : err_msg = nullptr;
993 : : }
994 : :
995 : : return false;
996 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
997 : : }
998 : :
999 [ + - ]: 8 : ::sqlite3_step(wrapper_file.stmt);
1000 : :
1001 [ - + ]: 8 : if (err_msg) {
1002 : : ::sqlite3_free(err_msg); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1003 : : err_msg = nullptr; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1004 : : }
1005 : : }
1006 : :
1007 : 6 : return true;
1008 [ + - ]: 6 : });
1009 : : #else
1010 : : (void)rebuild;
1011 : : return std::future<bool>();
1012 : : #endif
1013 : : }
1014 : :
1015 : 10 : void VDBReader::tag(const std::string& tag_name) {
1016 : : #ifdef VLINK_ENABLE_SQLITE
1017 : :
1018 [ + + ]: 10 : if VUNLIKELY (is_busy()) {
1019 [ + - + - ]: 2 : VLOG_W("VDBReader: Is busy.");
1020 : : }
1021 : :
1022 [ + - + - ]: 10 : post_task([this, tag_name]() {
1023 : 10 : int ret = 0;
1024 : 10 : std::string update_tag_sql;
1025 : :
1026 [ + + ]: 26 : for (auto& wrapper_file : impl_->file_list) {
1027 [ - + ]: 16 : if VUNLIKELY (!wrapper_file.db) {
1028 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1029 : : }
1030 : :
1031 [ + + ]: 16 : if (wrapper_file.stmt) {
1032 [ + - ]: 14 : ::sqlite3_finalize(wrapper_file.stmt);
1033 : 14 : wrapper_file.stmt = nullptr;
1034 : : }
1035 : :
1036 [ + - ]: 16 : update_tag_sql = "ALTER TABLE VLinkHeader ADD COLUMN tag TEXT;";
1037 : :
1038 [ + - ]: 16 : ret = sqlite3_exec(wrapper_file.db, update_tag_sql.c_str(), nullptr, nullptr, nullptr);
1039 : :
1040 : : (void)ret;
1041 : :
1042 : 16 : ::sqlite3_stmt* update_tag_stmt = nullptr;
1043 [ + - ]: 16 : ret = ::sqlite3_prepare_v2(wrapper_file.db, "UPDATE VLinkHeader SET tag = ?;", -1, &update_tag_stmt, nullptr);
1044 : 16 : SqliteStmtPtr update_tag_stmt_guard(update_tag_stmt);
1045 : :
1046 [ + - ]: 16 : if VLIKELY (ret == SQLITE_OK) {
1047 [ + - ]: 16 : ret = ::sqlite3_bind_text(update_tag_stmt, 1, tag_name.c_str(), static_cast<int>(tag_name.size()),
1048 : : SQLITE_TRANSIENT); // NOLINT(performance-no-int-to-ptr)
1049 : : }
1050 : :
1051 [ + - ]: 16 : if VLIKELY (ret == SQLITE_OK) {
1052 [ + - ]: 16 : ret = ::sqlite3_step(update_tag_stmt);
1053 : : }
1054 : :
1055 [ - + ]: 16 : if VUNLIKELY (ret != SQLITE_DONE) {
1056 : : CLOG_W("Failed to set tag: %s.", ::sqlite3_errmsg(wrapper_file.db)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1057 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1058 : : }
1059 : :
1060 : 16 : update_tag_stmt_guard.reset();
1061 : :
1062 [ + - ]: 16 : ret = ::sqlite3_prepare_v2(wrapper_file.db, "SELECT * FROM VLinkDatas;", -1, &wrapper_file.stmt, nullptr);
1063 : :
1064 [ - + ]: 16 : if VUNLIKELY (ret != SQLITE_OK) {
1065 : : CLOG_W("Failed to prepare datas table: %s.", // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1066 : : ::sqlite3_errmsg(wrapper_file.db)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1067 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1068 : : }
1069 : :
1070 [ + - ]: 16 : ::sqlite3_step(wrapper_file.stmt);
1071 : :
1072 [ + - ]: 16 : impl_->info.tag_name = tag_name;
1073 [ + - ]: 16 : }
1074 : :
1075 : : try {
1076 : : #ifdef _WIN32
1077 : : std::filesystem::path file_path(Helpers::string_to_wstring(impl_->path));
1078 : : std::string suffix = Helpers::path_to_string(file_path.extension());
1079 : : #else
1080 [ + - ]: 10 : std::filesystem::path file_path(impl_->path);
1081 [ + - + - ]: 10 : std::string suffix = file_path.extension().string();
1082 : : #endif
1083 : :
1084 : 55 : std::transform(suffix.begin(), suffix.end(), suffix.begin(), [](unsigned char c) { return std::tolower(c); });
1085 : :
1086 [ + + ]: 10 : if (suffix == ".vdbx") {
1087 : : try {
1088 : 5 : nlohmann::ordered_json root_json;
1089 : 5 : nlohmann::ordered_json header_json;
1090 : :
1091 : : {
1092 [ + - ]: 5 : std::ifstream file(file_path);
1093 : :
1094 [ + + ]: 5 : file >> root_json;
1095 : :
1096 [ + - ]: 4 : file.close();
1097 : 5 : }
1098 : :
1099 [ + - + - ]: 4 : header_json = root_json["VLinkHeader"];
1100 : :
1101 [ + - + - ]: 4 : header_json["tag"] = tag_name;
1102 : :
1103 [ + - ]: 4 : root_json["VLinkHeader"] = std::move(header_json);
1104 : :
1105 : : {
1106 [ + - ]: 4 : std::ofstream filex(impl_->path, std::ios::out | std::ios::trunc);
1107 : :
1108 [ + - + - ]: 4 : if VLIKELY (filex.is_open()) {
1109 [ + - + - ]: 4 : filex << root_json.dump(4);
1110 [ + - ]: 4 : filex.close();
1111 : : }
1112 : 4 : }
1113 [ - + ]: 7 : } catch (nlohmann::json::exception& e) {
1114 [ + - + - ]: 2 : VLOG_W("VDBReader: JSON parse error, ", e.what(), ".");
1115 : 1 : }
1116 : : }
1117 [ - - ]: 10 : } catch (std::filesystem::filesystem_error& e) {
1118 : : VLOG_F("VDBReader: Filesystem error, ", e.what(), "."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1119 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1120 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1121 [ + - ]: 10 : });
1122 : : #else
1123 : : (void)tag_name;
1124 : : #endif
1125 : 10 : }
1126 : :
1127 : 5 : int64_t VDBReader::get_timestamp() const {
1128 [ + - ]: 5 : std::shared_lock time_lock(impl_->time_mtx);
1129 : :
1130 [ + + ]: 5 : if (impl_->status.load(std::memory_order_relaxed) == kPlaying) {
1131 [ - + ]: 1 : if (impl_->is_pending.load(std::memory_order_relaxed)) {
1132 : : return impl_->real_elapsed.load(std::memory_order_relaxed) / 1000U; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1133 : : } else {
1134 : 1 : return (impl_->real_elapsed.load(std::memory_order_relaxed) +
1135 : 1 : (impl_->real_timer.get() * impl_->rate.load(std::memory_order_relaxed))) /
1136 : 1 : 1000U;
1137 : : }
1138 [ + + ]: 4 : } else if (impl_->status.load(std::memory_order_relaxed) == kPaused) {
1139 : 2 : return (impl_->real_elapsed.load(std::memory_order_relaxed) +
1140 : 2 : ((impl_->real_timer.get() - impl_->pause_elapsed_timer.get()) *
1141 : 2 : impl_->rate.load(std::memory_order_relaxed))) /
1142 : 2 : 1000U;
1143 : : } else {
1144 : 2 : return 0;
1145 : : }
1146 : 5 : }
1147 : :
1148 : 5 : int64_t VDBReader::get_real_timestamp() const {
1149 [ + + + + : 9 : if (impl_->status.load(std::memory_order_relaxed) == kPlaying ||
+ + ]
1150 : 4 : impl_->status.load(std::memory_order_relaxed) == kPaused) {
1151 : 6 : return impl_->real_elapsed.load(std::memory_order_relaxed) / 1000U;
1152 : : } else {
1153 : 2 : return 0;
1154 : : }
1155 : : }
1156 : :
1157 : 10 : VDBReader::Status VDBReader::get_status() const { return impl_->status.load(std::memory_order_relaxed); }
1158 : :
1159 : 67 : const BagReader::Info& VDBReader::get_info() const { return impl_->info; }
1160 : :
1161 : 60 : std::vector<SchemaData> VDBReader::detect_schema() {
1162 : : #ifdef VLINK_ENABLE_SQLITE
1163 : 60 : std::vector<SchemaData> schema_list;
1164 : 60 : std::unordered_map<std::string, size_t> schema_index_map;
1165 : :
1166 [ + + ]: 60 : if (!impl_->info.has_schema) {
1167 : 32 : return schema_list;
1168 : : }
1169 : :
1170 [ + - ]: 28 : schema_index_map.reserve(impl_->info.url_metas.size());
1171 : :
1172 [ + + ]: 58 : for (auto& wrapper_file : impl_->file_list) {
1173 : 30 : ::sqlite3_stmt* schema_stmt = nullptr;
1174 : :
1175 [ + - ]: 30 : int ret = ::sqlite3_prepare_v2(wrapper_file.db, "SELECT ser, encoding, data FROM VLinkSchemas;", -1, &schema_stmt,
1176 : : nullptr);
1177 : :
1178 [ - + ]: 30 : if VUNLIKELY (ret != SQLITE_OK) {
1179 : : // LCOV_EXCL_START GCOVR_EXCL_START
1180 : : CLOG_E("Failed to prepare schema table: %s.", ::sqlite3_errmsg(wrapper_file.db));
1181 : :
1182 : : if (schema_stmt) {
1183 : : ::sqlite3_finalize(schema_stmt);
1184 : : schema_stmt = nullptr;
1185 : : }
1186 : :
1187 : : return schema_list;
1188 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1189 : : }
1190 : :
1191 : 30 : const uint8_t* data = nullptr;
1192 : 30 : size_t size = 0;
1193 : :
1194 [ + - + + ]: 64 : while (::sqlite3_step(schema_stmt) == SQLITE_ROW) {
1195 : 34 : SchemaData schema;
1196 [ + - ]: 34 : schema.name = sqlite_column_text_or_empty(schema_stmt, get_column(0));
1197 [ + - ]: 34 : schema.encoding = sqlite_column_text_or_empty(schema_stmt, get_column(1));
1198 : 34 : schema.schema_type = SchemaData::resolve_type(SchemaType::kUnknown, schema.name, schema.encoding);
1199 : :
1200 [ + - ]: 34 : data = static_cast<const uint8_t*>(::sqlite3_column_blob(schema_stmt, get_column(2)));
1201 [ + - ]: 34 : size = ::sqlite3_column_bytes(schema_stmt, get_column(2));
1202 : :
1203 [ + + + - : 34 : if (!schema.name.empty() && data && size > 0) {
+ - + + ]
1204 [ + - ]: 33 : std::string schema_key = schema.name;
1205 [ + - ]: 33 : schema_key.push_back('\x1F');
1206 [ + - ]: 33 : schema_key.append(SchemaData::convert_type(schema.schema_type));
1207 : :
1208 [ + - ]: 33 : auto schema_index_iter = schema_index_map.find(schema_key);
1209 : :
1210 [ + + ]: 33 : if (schema_index_iter == schema_index_map.end()) {
1211 : 31 : schema.data = Bytes::deep_copy(data, size);
1212 [ + - ]: 31 : schema_index_map.emplace(schema_key, schema_list.size());
1213 [ + - ]: 31 : schema_list.emplace_back(std::move(schema));
1214 : : } else {
1215 : 2 : auto& current_schema = schema_list[schema_index_iter->second];
1216 : :
1217 [ - + - - : 2 : if (current_schema.encoding.empty() && !schema.encoding.empty()) {
- + ]
1218 : : current_schema.encoding = schema.encoding; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1219 : : }
1220 : :
1221 [ - + ]: 2 : if (current_schema.data.empty()) {
1222 : : current_schema.data = Bytes::deep_copy(data, size); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1223 : : }
1224 : : }
1225 : 33 : }
1226 : 34 : }
1227 : :
1228 [ + - ]: 30 : if VLIKELY (schema_stmt) {
1229 [ + - ]: 30 : ::sqlite3_finalize(schema_stmt);
1230 : 30 : schema_stmt = nullptr;
1231 : : }
1232 : : }
1233 : :
1234 : 28 : return schema_list;
1235 : : #else
1236 : : return std::vector<SchemaData>();
1237 : : #endif
1238 : 60 : }
1239 : :
1240 : 11 : bool VDBReader::is_split_mode() const { return impl_->info.split_count > 0; }
1241 : :
1242 : 4 : int VDBReader::get_split_index() const { return impl_->split_index.load(std::memory_order_relaxed); }
1243 : :
1244 : 6 : bool VDBReader::is_jumping() const { return impl_->jump_flag.load(std::memory_order_relaxed); }
1245 : :
1246 : 100 : size_t VDBReader::get_max_task_count() const { return kMaxTaskSize; }
1247 : :
1248 : 92 : void VDBReader::on_begin() { MessageLoop::on_begin(); }
1249 : :
1250 : 92 : void VDBReader::on_end() { MessageLoop::on_end(); }
1251 : :
1252 : 48 : void VDBReader::update_status(Status status) {
1253 : 48 : bool has_changed = false;
1254 : :
1255 [ + + ]: 48 : if (status == kStopped) {
1256 [ + + ]: 19 : if (impl_->status.load(std::memory_order_relaxed) != kStopped) {
1257 : 18 : impl_->status.store(kStopped, std::memory_order_relaxed);
1258 : 18 : has_changed = true;
1259 : : }
1260 [ + + ]: 29 : } else if (status == kPaused) {
1261 [ + + ]: 5 : if (impl_->status.load(std::memory_order_relaxed) != kPaused) {
1262 : 4 : impl_->status.store(kPaused, std::memory_order_relaxed);
1263 : 4 : has_changed = true;
1264 : : }
1265 [ + - ]: 24 : } else if (status == kPlaying) {
1266 [ + + ]: 24 : if (impl_->status.load(std::memory_order_relaxed) != kPlaying) {
1267 : 21 : impl_->status.store(kPlaying, std::memory_order_relaxed);
1268 : 21 : has_changed = true;
1269 : : }
1270 : : }
1271 : :
1272 [ + + ]: 48 : if (has_changed) {
1273 [ + + ]: 43 : if VLIKELY (impl_->status_callback) {
1274 : 17 : impl_->status_callback(impl_->status.load(std::memory_order_relaxed));
1275 : : }
1276 : : }
1277 : 48 : }
1278 : :
1279 : 102 : void VDBReader::do_stop() {
1280 : : #ifdef VLINK_ENABLE_SQLITE
1281 : : {
1282 [ + - ]: 102 : std::unique_lock lock(impl_->mtx);
1283 : 102 : impl_->stop_flag.store(true, std::memory_order_relaxed);
1284 : 102 : impl_->pause_flag.store(false, std::memory_order_relaxed);
1285 : 102 : impl_->pause_next_flag.store(false, std::memory_order_relaxed);
1286 : 102 : impl_->jump_flag.store(false, std::memory_order_relaxed);
1287 : 102 : }
1288 : :
1289 : 102 : impl_->cv.notify_one();
1290 : :
1291 [ + + ]: 231 : for (const auto& wrapper_file : impl_->file_list) {
1292 [ + - ]: 129 : if (wrapper_file.db) {
1293 [ + - ]: 129 : ::sqlite3_interrupt(wrapper_file.db);
1294 : : }
1295 : : }
1296 : : #endif
1297 : 102 : }
1298 : :
1299 : 4 : void VDBReader::do_pause() {
1300 [ + - ]: 4 : std::unique_lock lock(impl_->mtx);
1301 : :
1302 [ + - ]: 4 : while (impl_->pause_flag.load(std::memory_order_relaxed)) {
1303 : 4 : impl_->pause_elapsed_timer.restart();
1304 [ + - ]: 4 : update_status(kPaused);
1305 : :
1306 [ + - ]: 4 : impl_->cv.wait(lock, [this]() -> bool {
1307 [ + + + + ]: 19 : return impl_->stop_flag.load(std::memory_order_relaxed) || !impl_->pause_flag.load(std::memory_order_relaxed) ||
1308 [ + - ]: 8 : impl_->pause_next_flag.load(std::memory_order_relaxed) ||
1309 [ + - - + ]: 17 : impl_->jump_flag.load(std::memory_order_relaxed) || is_ready_to_quit();
1310 : : });
1311 : :
1312 : 4 : impl_->pause_elapsed.fetch_add(impl_->pause_elapsed_timer.get(), std::memory_order_relaxed);
1313 : :
1314 : : {
1315 [ + - ]: 4 : std::lock_guard time_lock(impl_->time_mtx);
1316 : 4 : impl_->real_timer.restart();
1317 : :
1318 [ - + ]: 8 : if (impl_->offset_elapsed.load(std::memory_order_relaxed) > 0) {
1319 : : // LCOV_EXCL_START GCOVR_EXCL_START
1320 : : impl_->real_elapsed.fetch_add((impl_->offset_timer.get() - impl_->pause_elapsed_timer.get() -
1321 : : impl_->extra_elapsed.load(std::memory_order_relaxed)) *
1322 : : impl_->rate.load(std::memory_order_relaxed),
1323 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1324 : : std::memory_order_relaxed);
1325 : : }
1326 : 4 : }
1327 : :
1328 [ + - ]: 4 : update_status(kPlaying);
1329 : :
1330 [ + - ]: 4 : if (impl_->pause_next_flag.load(std::memory_order_relaxed)) {
1331 : 8 : impl_->pause_elapsed.fetch_sub(impl_->offset_elapsed.load(std::memory_order_relaxed), std::memory_order_relaxed);
1332 : 4 : break;
1333 : : } else if (impl_->offset_elapsed.load(std::memory_order_relaxed) > 0) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1334 : : impl_->offset_timer.restart(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1335 : :
1336 : : impl_->cv.wait_for( // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1337 : : lock,
1338 : : // LCOV_EXCL_START GCOVR_EXCL_START
1339 : : std::chrono::microseconds(impl_->offset_elapsed.load(std::memory_order_relaxed)), [this]() -> bool {
1340 : : return impl_->stop_flag.load(std::memory_order_relaxed) ||
1341 : : impl_->pause_flag.load(std::memory_order_relaxed) ||
1342 : : impl_->pause_next_flag.load(std::memory_order_relaxed) ||
1343 : : impl_->jump_flag.load(std::memory_order_relaxed) || is_ready_to_quit();
1344 : : });
1345 : :
1346 : : if VUNLIKELY (impl_->pause_flag.load(std::memory_order_relaxed)) {
1347 : : impl_->offset_elapsed.fetch_sub(impl_->offset_timer.get(), std::memory_order_relaxed);
1348 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1349 : : } else {
1350 : : impl_->offset_elapsed.store(0, std::memory_order_relaxed); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1351 : : }
1352 : : }
1353 : : }
1354 : 4 : }
1355 : :
1356 : 133 : bool VDBReader::prepare_file(void* file) {
1357 : : #ifdef VLINK_ENABLE_SQLITE
1358 : 133 : auto* wrapper_file = static_cast<Impl::WrapperFile*>(file);
1359 : :
1360 : 133 : wrapper_file->has_completed = true;
1361 : :
1362 : 133 : int ret = 0;
1363 : :
1364 : : // opt busy_timeout
1365 [ + - ]: 133 : ::sqlite3_busy_timeout(wrapper_file->db, 100);
1366 : :
1367 : : #if ENABLE_DATABASE_TABLE_CHECK
1368 : : {
1369 : : // search count
1370 : : ::sqlite3_stmt* search_stmt = nullptr;
1371 : : bool need_rebuild_header = false;
1372 : : ret = ::sqlite3_prepare_v2(wrapper_file->db, "PRAGMA table_info(VLinkHeader);", -1, &search_stmt, nullptr);
1373 : :
1374 : : if VUNLIKELY (ret != SQLITE_OK) {
1375 : : wrapper_file->has_completed = false;
1376 : :
1377 : : if (!impl_->try_to_fix || impl_->read_only) {
1378 : : CLOG_F("Failed to search header table: %s.", ::sqlite3_errmsg(wrapper_file->db));
1379 : : return false;
1380 : : }
1381 : :
1382 : : need_rebuild_header = true;
1383 : : } else {
1384 : : const char* expected_columns[] = {
1385 : : "major", "minor", "patch", "count", "duration", "accuracy", "compress",
1386 : : "process", "date", "tag", "complete", "timezone", "start_timestamp",
1387 : : };
1388 : :
1389 : : const char* expected_types[] = {
1390 : : "INTEGER", "INTEGER", "INTEGER", "INTEGER", "INTEGER", "TEXT", "TEXT",
1391 : : "TEXT", "TEXT", "TEXT", "INTEGER", "INTEGER", "INTEGER",
1392 : : };
1393 : :
1394 : : size_t column_index = 0;
1395 : : int invalid_header_num = 0;
1396 : : int step_ret = SQLITE_OK;
1397 : : for (;;) {
1398 : : step_ret = ::sqlite3_step(search_stmt);
1399 : :
1400 : : if (step_ret != SQLITE_ROW) {
1401 : : break;
1402 : : }
1403 : :
1404 : : if (column_index >= sizeof(expected_columns) / sizeof(expected_columns[0])) {
1405 : : need_rebuild_header = true;
1406 : : invalid_header_num = 1;
1407 : : break;
1408 : : }
1409 : :
1410 : : const auto* column_name = reinterpret_cast<const char*>(::sqlite3_column_text(search_stmt, get_column(1)));
1411 : : const auto* column_type = reinterpret_cast<const char*>(::sqlite3_column_text(search_stmt, get_column(2)));
1412 : :
1413 : : if (!column_name || std::strcmp(column_name, expected_columns[column_index]) != 0) {
1414 : : need_rebuild_header = true;
1415 : : invalid_header_num = 2;
1416 : : break;
1417 : : }
1418 : :
1419 : : if (!column_type || std::strcmp(column_type, expected_types[column_index]) != 0) {
1420 : : need_rebuild_header = true;
1421 : : invalid_header_num = 3;
1422 : : break;
1423 : : }
1424 : :
1425 : : ++column_index;
1426 : : }
1427 : :
1428 : : if (!need_rebuild_header && step_ret != SQLITE_DONE) {
1429 : : if VLIKELY (search_stmt) {
1430 : : ::sqlite3_finalize(search_stmt);
1431 : : search_stmt = nullptr;
1432 : : }
1433 : :
1434 : : wrapper_file->has_completed = false;
1435 : : CLOG_F("Failed to inspect header table: %s.", ::sqlite3_errmsg(wrapper_file->db));
1436 : : return false;
1437 : : }
1438 : :
1439 : : if (!need_rebuild_header) {
1440 : : need_rebuild_header = (column_index != sizeof(expected_columns) / sizeof(expected_columns[0]));
1441 : : if (need_rebuild_header) {
1442 : : invalid_header_num = 4;
1443 : : }
1444 : : }
1445 : :
1446 : : if (need_rebuild_header) {
1447 : : if (!impl_->try_to_fix || impl_->read_only) {
1448 : : CLOG_F("VDBReader: Table [VLinkHeader] is incompatible, num=%d.", invalid_header_num);
1449 : : return false;
1450 : : }
1451 : :
1452 : : CLOG_W("VDBReader: Table [VLinkHeader] is incompatible, num=%d. Try to rebuild.", invalid_header_num);
1453 : : }
1454 : : }
1455 : :
1456 : : if VLIKELY (search_stmt) {
1457 : : ::sqlite3_finalize(search_stmt);
1458 : : search_stmt = nullptr;
1459 : : }
1460 : :
1461 : : if (need_rebuild_header) {
1462 : : wrapper_file->has_completed = false;
1463 : : char* err_msg = nullptr;
1464 : :
1465 : : ret = ::sqlite3_exec(wrapper_file->db, "DROP TABLE IF EXISTS VLinkHeader;", nullptr, nullptr, &err_msg);
1466 : : if VUNLIKELY (ret != SQLITE_OK) {
1467 : : CLOG_F("Failed to drop VLinkHeader: %s.", err_msg);
1468 : :
1469 : : if (err_msg) {
1470 : : ::sqlite3_free(err_msg);
1471 : : err_msg = nullptr;
1472 : : }
1473 : :
1474 : : return false;
1475 : : }
1476 : :
1477 : : ret = ::sqlite3_exec(
1478 : : wrapper_file->db,
1479 : : "CREATE TABLE IF NOT EXISTS VLinkHeader(major INTEGER, minor INTEGER, patch INTEGER, count INTEGER, "
1480 : : "duration INTEGER, accuracy TEXT, compress TEXT, process TEXT, date TEXT, tag TEXT, complete INTEGER, "
1481 : : "timezone INTEGER, start_timestamp INTEGER);",
1482 : : nullptr, nullptr, &err_msg);
1483 : : if VUNLIKELY (ret != SQLITE_OK) {
1484 : : CLOG_F("Failed to create header table: %s.", err_msg);
1485 : :
1486 : : if (err_msg) {
1487 : : ::sqlite3_free(err_msg);
1488 : : err_msg = nullptr;
1489 : : }
1490 : :
1491 : : return false;
1492 : : }
1493 : :
1494 : : ::sqlite3_stmt* create_header_stmt = nullptr;
1495 : : ret = ::sqlite3_prepare_v2(
1496 : : wrapper_file->db,
1497 : : "INSERT INTO VLinkHeader (major, minor, patch, count, duration, accuracy, compress, "
1498 : : "process, date, tag, complete, timezone, start_timestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
1499 : : -1, &create_header_stmt, nullptr);
1500 : :
1501 : : if VUNLIKELY (ret != SQLITE_OK) {
1502 : : CLOG_F("Failed to prepare header table: %s.", ::sqlite3_errmsg(wrapper_file->db));
1503 : :
1504 : : if (err_msg) {
1505 : : ::sqlite3_free(err_msg);
1506 : : err_msg = nullptr;
1507 : : }
1508 : :
1509 : : return false;
1510 : : }
1511 : :
1512 : : ::sqlite3_bind_int(create_header_stmt, get_column(0), VLINK_VERSION_MAJOR);
1513 : : ::sqlite3_bind_int(create_header_stmt, get_column(1), VLINK_VERSION_MINOR);
1514 : : ::sqlite3_bind_int(create_header_stmt, get_column(2), VLINK_VERSION_PATCH);
1515 : : ::sqlite3_bind_int64(create_header_stmt, get_column(3), 0);
1516 : : ::sqlite3_bind_int64(create_header_stmt, get_column(4), 0);
1517 : : ::sqlite3_bind_text(create_header_stmt, get_column(5), "MicroSecond", -1, SQLITE_STATIC);
1518 : : ::sqlite3_bind_text(create_header_stmt, get_column(6), "None", -1, SQLITE_STATIC);
1519 : : ::sqlite3_bind_text(create_header_stmt, get_column(7), "None", -1, SQLITE_STATIC);
1520 : : ::sqlite3_bind_text(create_header_stmt, get_column(8), "None", -1, SQLITE_STATIC);
1521 : : ::sqlite3_bind_text(create_header_stmt, get_column(9), "None", -1, SQLITE_STATIC);
1522 : : ::sqlite3_bind_int(create_header_stmt, get_column(10), 0);
1523 : : ::sqlite3_bind_int(create_header_stmt, get_column(11), 0);
1524 : : ::sqlite3_bind_int64(create_header_stmt, get_column(12), 0);
1525 : :
1526 : : ret = ::sqlite3_step(create_header_stmt);
1527 : :
1528 : : if VUNLIKELY (ret != SQLITE_DONE) {
1529 : : CLOG_F("Failed to insert header table: %s.", ::sqlite3_errmsg(wrapper_file->db));
1530 : :
1531 : : if (err_msg) {
1532 : : ::sqlite3_free(err_msg);
1533 : : err_msg = nullptr;
1534 : : }
1535 : :
1536 : : return false;
1537 : : }
1538 : :
1539 : : if VLIKELY (create_header_stmt) {
1540 : : ::sqlite3_finalize(create_header_stmt);
1541 : : create_header_stmt = nullptr;
1542 : : }
1543 : :
1544 : : if (err_msg) {
1545 : : ::sqlite3_free(err_msg);
1546 : : err_msg = nullptr;
1547 : : }
1548 : : }
1549 : :
1550 : : ::sqlite3_stmt* schema_info_stmt = nullptr;
1551 : : ret = ::sqlite3_prepare_v2(wrapper_file->db, "PRAGMA table_info(VLinkSchemas);", -1, &schema_info_stmt, nullptr);
1552 : :
1553 : : if VUNLIKELY (ret != SQLITE_OK) {
1554 : : wrapper_file->has_completed = false;
1555 : : CLOG_F("Failed to search schema table: %s.", ::sqlite3_errmsg(wrapper_file->db));
1556 : : return false;
1557 : : }
1558 : :
1559 : : {
1560 : : const char* expected_columns[] = {"ser", "encoding", "data"};
1561 : :
1562 : : const char* expected_types[] = {"TEXT", "TEXT", "BLOB"};
1563 : :
1564 : : size_t column_index = 0;
1565 : : bool invalid_schema_table = false;
1566 : : int invalid_schema_num = 0;
1567 : : int step_ret = SQLITE_OK;
1568 : :
1569 : : for (;;) {
1570 : : step_ret = ::sqlite3_step(schema_info_stmt);
1571 : :
1572 : : if (step_ret != SQLITE_ROW) {
1573 : : break;
1574 : : }
1575 : :
1576 : : if (column_index >= sizeof(expected_columns) / sizeof(expected_columns[0])) {
1577 : : invalid_schema_table = true;
1578 : : invalid_schema_num = 1;
1579 : : break;
1580 : : }
1581 : :
1582 : : const auto* column_name = reinterpret_cast<const char*>(::sqlite3_column_text(schema_info_stmt, get_column(1)));
1583 : : const auto* column_type = reinterpret_cast<const char*>(::sqlite3_column_text(schema_info_stmt, get_column(2)));
1584 : :
1585 : : if (!column_name || std::strcmp(column_name, expected_columns[column_index]) != 0) {
1586 : : invalid_schema_table = true;
1587 : : invalid_schema_num = 2;
1588 : : break;
1589 : : }
1590 : :
1591 : : if (!column_type || std::strcmp(column_type, expected_types[column_index]) != 0) {
1592 : : invalid_schema_table = true;
1593 : : invalid_schema_num = 3;
1594 : : break;
1595 : : }
1596 : :
1597 : : ++column_index;
1598 : : }
1599 : :
1600 : : if (!invalid_schema_table && step_ret != SQLITE_DONE) {
1601 : : if VLIKELY (schema_info_stmt) {
1602 : : ::sqlite3_finalize(schema_info_stmt);
1603 : : schema_info_stmt = nullptr;
1604 : : }
1605 : :
1606 : : wrapper_file->has_completed = false;
1607 : : CLOG_F("Failed to inspect schema table: %s.", ::sqlite3_errmsg(wrapper_file->db));
1608 : : return false;
1609 : : }
1610 : :
1611 : : if (!invalid_schema_table) {
1612 : : invalid_schema_table = (column_index != sizeof(expected_columns) / sizeof(expected_columns[0]));
1613 : : if (invalid_schema_table) {
1614 : : invalid_schema_num = 4;
1615 : : }
1616 : : }
1617 : :
1618 : : if VLIKELY (schema_info_stmt) {
1619 : : ::sqlite3_finalize(schema_info_stmt);
1620 : : schema_info_stmt = nullptr;
1621 : : }
1622 : :
1623 : : if VUNLIKELY (invalid_schema_table) {
1624 : : wrapper_file->has_completed = false;
1625 : : CLOG_F("VDBReader: Table [VLinkSchemas] is incompatible, num=%d.", invalid_schema_num);
1626 : : return false;
1627 : : }
1628 : : }
1629 : :
1630 : : ::sqlite3_stmt* urls_info_stmt = nullptr;
1631 : : ret = ::sqlite3_prepare_v2(wrapper_file->db, "PRAGMA table_info(VLinkUrls);", -1, &urls_info_stmt, nullptr);
1632 : :
1633 : : if VUNLIKELY (ret != SQLITE_OK) {
1634 : : wrapper_file->has_completed = false;
1635 : : CLOG_F("Failed to search urls table: %s.", ::sqlite3_errmsg(wrapper_file->db));
1636 : : return false;
1637 : : }
1638 : :
1639 : : {
1640 : : const char* expected_columns[] = {"id", "url", "type", "ser", "encoding", "count", "loss", "size", "freq"};
1641 : :
1642 : : const char* expected_types[] = {"INTEGER", "TEXT", "TEXT", "TEXT", "TEXT", "INTEGER", "REAL", "INTEGER", "REAL"};
1643 : :
1644 : : size_t column_index = 0;
1645 : : bool invalid_urls_table = false;
1646 : : int invalid_urls_num = 0;
1647 : : int step_ret = SQLITE_OK;
1648 : :
1649 : : for (;;) {
1650 : : step_ret = ::sqlite3_step(urls_info_stmt);
1651 : :
1652 : : if (step_ret != SQLITE_ROW) {
1653 : : break;
1654 : : }
1655 : :
1656 : : if (column_index >= sizeof(expected_columns) / sizeof(expected_columns[0])) {
1657 : : invalid_urls_table = true;
1658 : : invalid_urls_num = 1;
1659 : : break;
1660 : : }
1661 : :
1662 : : const auto* column_name = reinterpret_cast<const char*>(::sqlite3_column_text(urls_info_stmt, get_column(1)));
1663 : : const auto* column_type = reinterpret_cast<const char*>(::sqlite3_column_text(urls_info_stmt, get_column(2)));
1664 : :
1665 : : if (!column_name || std::strcmp(column_name, expected_columns[column_index]) != 0) {
1666 : : invalid_urls_table = true;
1667 : : invalid_urls_num = 2;
1668 : : break;
1669 : : }
1670 : :
1671 : : if (!column_type || std::strcmp(column_type, expected_types[column_index]) != 0) {
1672 : : invalid_urls_table = true;
1673 : : invalid_urls_num = 3;
1674 : : break;
1675 : : }
1676 : :
1677 : : ++column_index;
1678 : : }
1679 : :
1680 : : if (!invalid_urls_table && step_ret != SQLITE_DONE) {
1681 : : if VLIKELY (urls_info_stmt) {
1682 : : ::sqlite3_finalize(urls_info_stmt);
1683 : : urls_info_stmt = nullptr;
1684 : : }
1685 : :
1686 : : wrapper_file->has_completed = false;
1687 : : CLOG_F("Failed to inspect urls table: %s.", ::sqlite3_errmsg(wrapper_file->db));
1688 : : return false;
1689 : : }
1690 : :
1691 : : if (!invalid_urls_table) {
1692 : : invalid_urls_table = (column_index != sizeof(expected_columns) / sizeof(expected_columns[0]));
1693 : : if (invalid_urls_table) {
1694 : : invalid_urls_num = 4;
1695 : : }
1696 : : }
1697 : :
1698 : : if VLIKELY (urls_info_stmt) {
1699 : : ::sqlite3_finalize(urls_info_stmt);
1700 : : urls_info_stmt = nullptr;
1701 : : }
1702 : :
1703 : : if VUNLIKELY (invalid_urls_table) {
1704 : : wrapper_file->has_completed = false;
1705 : : CLOG_F("VDBReader: Table [VLinkUrls] is incompatible, num=%d.", invalid_urls_num);
1706 : : return false;
1707 : : }
1708 : : }
1709 : :
1710 : : ::sqlite3_stmt* datas_info_stmt = nullptr;
1711 : : ret = ::sqlite3_prepare_v2(wrapper_file->db, "PRAGMA table_info(VLinkDatas);", -1, &datas_info_stmt, nullptr);
1712 : :
1713 : : if VUNLIKELY (ret != SQLITE_OK) {
1714 : : wrapper_file->has_completed = false;
1715 : : CLOG_F("Failed to search datas table: %s.", ::sqlite3_errmsg(wrapper_file->db));
1716 : : return false;
1717 : : }
1718 : :
1719 : : {
1720 : : const char* expected_columns[] = {"elapsed", "url", "action", "data"};
1721 : :
1722 : : const char* expected_types[] = {"INTEGER", "INTEGER", "TEXT", "BLOB"};
1723 : :
1724 : : size_t column_index = 0;
1725 : : bool invalid_datas_table = false;
1726 : : int invalid_datas_num = 0;
1727 : : int step_ret = SQLITE_OK;
1728 : :
1729 : : for (;;) {
1730 : : step_ret = ::sqlite3_step(datas_info_stmt);
1731 : :
1732 : : if (step_ret != SQLITE_ROW) {
1733 : : break;
1734 : : }
1735 : :
1736 : : if (column_index >= sizeof(expected_columns) / sizeof(expected_columns[0])) {
1737 : : invalid_datas_table = true;
1738 : : invalid_datas_num = 1;
1739 : : break;
1740 : : }
1741 : :
1742 : : const auto* column_name = reinterpret_cast<const char*>(::sqlite3_column_text(datas_info_stmt, get_column(1)));
1743 : : const auto* column_type = reinterpret_cast<const char*>(::sqlite3_column_text(datas_info_stmt, get_column(2)));
1744 : :
1745 : : if (!column_name || std::strcmp(column_name, expected_columns[column_index]) != 0) {
1746 : : invalid_datas_table = true;
1747 : : invalid_datas_num = 2;
1748 : : break;
1749 : : }
1750 : :
1751 : : if (!column_type || std::strcmp(column_type, expected_types[column_index]) != 0) {
1752 : : invalid_datas_table = true;
1753 : : invalid_datas_num = 3;
1754 : : break;
1755 : : }
1756 : :
1757 : : ++column_index;
1758 : : }
1759 : :
1760 : : if (!invalid_datas_table && step_ret != SQLITE_DONE) {
1761 : : if VLIKELY (datas_info_stmt) {
1762 : : ::sqlite3_finalize(datas_info_stmt);
1763 : : datas_info_stmt = nullptr;
1764 : : }
1765 : :
1766 : : wrapper_file->has_completed = false;
1767 : : CLOG_F("Failed to inspect datas table: %s.", ::sqlite3_errmsg(wrapper_file->db));
1768 : : return false;
1769 : : }
1770 : :
1771 : : if (!invalid_datas_table) {
1772 : : invalid_datas_table = (column_index != sizeof(expected_columns) / sizeof(expected_columns[0]));
1773 : : if (invalid_datas_table) {
1774 : : invalid_datas_num = 4;
1775 : : }
1776 : : }
1777 : :
1778 : : if VLIKELY (datas_info_stmt) {
1779 : : ::sqlite3_finalize(datas_info_stmt);
1780 : : datas_info_stmt = nullptr;
1781 : : }
1782 : :
1783 : : if VUNLIKELY (invalid_datas_table) {
1784 : : wrapper_file->has_completed = false;
1785 : : CLOG_F("VDBReader: Table [VLinkDatas] is incompatible, num=%d.", invalid_datas_num);
1786 : : return false;
1787 : : }
1788 : : }
1789 : : }
1790 : : #endif
1791 : :
1792 : : // prepare header table
1793 : 133 : ::sqlite3_stmt* header_stmt = nullptr;
1794 [ + - ]: 133 : ret = ::sqlite3_prepare_v2(
1795 : : wrapper_file->db,
1796 : : "SELECT major, minor, patch, count, duration, accuracy, compress, process, date, tag, complete, timezone, "
1797 : : "start_timestamp FROM VLinkHeader LIMIT 1;",
1798 : : -1, &header_stmt, nullptr);
1799 : :
1800 [ + + ]: 133 : if VUNLIKELY (ret != SQLITE_OK) {
1801 : 2 : wrapper_file->has_completed = false;
1802 [ + - + - : 4 : CLOG_F("Failed to prepare header table: %s.", ::sqlite3_errmsg(wrapper_file->db));
- + ]
1803 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1804 : : }
1805 : :
1806 : 131 : SqliteStmtPtr header_stmt_guard(header_stmt);
1807 : :
1808 [ + - ]: 131 : ret = ::sqlite3_step(header_stmt);
1809 : :
1810 [ - + ]: 131 : if VUNLIKELY (ret != SQLITE_ROW) {
1811 : : wrapper_file->has_completed = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1812 : : CLOG_F("Failed to get header table: %s.", ::sqlite3_errmsg(wrapper_file->db)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1813 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1814 : : }
1815 : :
1816 [ + - ]: 131 : int major = ::sqlite3_column_int(header_stmt, get_column(0));
1817 [ + - ]: 131 : int minor = ::sqlite3_column_int(header_stmt, get_column(1));
1818 [ + - ]: 131 : int patch = ::sqlite3_column_int(header_stmt, get_column(2));
1819 : :
1820 [ - + ]: 131 : if VUNLIKELY (major != VLINK_VERSION_MAJOR) {
1821 : : wrapper_file->has_completed = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1822 : : VLOG_F("VDBReader: Database version is incompatible."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1823 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1824 : : }
1825 : :
1826 [ + - + - : 131 : impl_->info.version = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch);
+ - + - +
- + - +
- ]
1827 [ + - ]: 131 : impl_->info.storage_type = "SQLite3";
1828 [ + - ]: 131 : impl_->info.message_count = ::sqlite3_column_int64(header_stmt, get_column(3));
1829 [ + - ]: 131 : impl_->info.total_duration = ::sqlite3_column_int64(header_stmt, get_column(4)) / 1000U;
1830 [ + - ]: 131 : impl_->info.time_accuracy = sqlite_column_text_or_empty(header_stmt, get_column(5));
1831 [ + - ]: 131 : impl_->info.compression_type = sqlite_column_text_or_empty(header_stmt, get_column(6));
1832 [ + - ]: 131 : impl_->info.process_name = sqlite_column_text_or_empty(header_stmt, get_column(7));
1833 [ + - ]: 131 : impl_->info.date_time = sqlite_column_text_or_empty(header_stmt, get_column(8));
1834 : :
1835 [ + - ]: 131 : const char* tag_name_str = reinterpret_cast<const char*>(::sqlite3_column_text(header_stmt, get_column(9)));
1836 : :
1837 [ + + ]: 131 : if (tag_name_str) {
1838 [ + - ]: 130 : impl_->info.tag_name = tag_name_str;
1839 : : } else {
1840 [ + - ]: 1 : impl_->info.tag_name = "Empty";
1841 : : }
1842 : :
1843 [ + - ]: 131 : impl_->info.has_completed = (::sqlite3_column_int(header_stmt, get_column(10)) == 1);
1844 [ + - ]: 131 : impl_->info.timezone = ::sqlite3_column_int(header_stmt, get_column(11));
1845 [ + - ]: 131 : impl_->info.start_timestamp = ::sqlite3_column_int64(header_stmt, get_column(12));
1846 : :
1847 [ + + ]: 131 : if VUNLIKELY (impl_->info.start_timestamp < 0) {
1848 : 1 : impl_->info.start_timestamp = 0;
1849 : 1 : wrapper_file->has_completed = false;
1850 : :
1851 [ + - ]: 1 : if (impl_->read_only) {
1852 [ + - + - ]: 2 : VLOG_E("VDBReader: Invalid start_timestamp.");
1853 : : }
1854 : : }
1855 : :
1856 [ + + + + ]: 274 : if (impl_->info.compression_type.empty() || impl_->info.compression_type == "None" ||
1857 [ + - + + : 274 : impl_->info.compression_type == "NONE" || impl_->info.compression_type == "none") {
+ + ]
1858 : 121 : impl_->enable_compress = false;
1859 : : } else {
1860 : 10 : impl_->enable_compress = true;
1861 : : }
1862 : :
1863 [ + - + + ]: 131 : if VUNLIKELY (impl_->info.time_accuracy != "MicroSecond") {
1864 : 1 : wrapper_file->has_completed = false;
1865 [ + - - + ]: 2 : VLOG_F("VDBReader: Database accuracy is not supported.");
1866 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1867 : : }
1868 : :
1869 : 130 : header_stmt_guard.reset();
1870 : 130 : header_stmt = nullptr;
1871 : :
1872 : : // prepare schema table
1873 : 130 : ::sqlite3_stmt* schema_stmt = nullptr;
1874 : :
1875 [ + - ]: 130 : ret = ::sqlite3_prepare_v2(wrapper_file->db,
1876 : : "SELECT 1 FROM sqlite_master WHERE type='table' AND name='VLinkSchemas' LIMIT 1;", -1,
1877 : : &schema_stmt, nullptr);
1878 : :
1879 [ + - ]: 130 : if (ret == SQLITE_OK) {
1880 [ + - + - ]: 130 : if (::sqlite3_step(schema_stmt) == SQLITE_ROW) {
1881 : 130 : sqlite3_stmt* count_stmt = nullptr;
1882 : :
1883 [ + - ]: 130 : ret = ::sqlite3_prepare_v2(wrapper_file->db, "SELECT EXISTS(SELECT 1 FROM VLinkSchemas LIMIT 1);", -1,
1884 : : &count_stmt, nullptr);
1885 : :
1886 [ + - ]: 130 : if (ret == SQLITE_OK) {
1887 [ + - + - ]: 130 : if (::sqlite3_step(count_stmt) == SQLITE_ROW) {
1888 [ + - ]: 130 : int exists = ::sqlite3_column_int(count_stmt, 0);
1889 : :
1890 [ + + ]: 130 : if (exists) {
1891 : 38 : wrapper_file->has_schema = true;
1892 : : }
1893 : : }
1894 : : }
1895 : :
1896 [ + - ]: 130 : if VLIKELY (count_stmt) {
1897 [ + - ]: 130 : ::sqlite3_finalize(count_stmt);
1898 : 130 : count_stmt = nullptr;
1899 : : }
1900 : : }
1901 : : }
1902 : :
1903 [ + - ]: 130 : if VLIKELY (schema_stmt) {
1904 [ + - ]: 130 : ::sqlite3_finalize(schema_stmt);
1905 : 130 : schema_stmt = nullptr;
1906 : : }
1907 : :
1908 : : // prepare urls table
1909 : 130 : ::sqlite3_stmt* urls_stmt = nullptr;
1910 [ + - ]: 130 : ret = ::sqlite3_prepare_v2(wrapper_file->db,
1911 : : "SELECT id, url, type, ser, encoding, count, loss, size, freq FROM VLinkUrls;", -1,
1912 : : &urls_stmt, nullptr);
1913 : :
1914 [ + + ]: 130 : if VUNLIKELY (ret != SQLITE_OK) {
1915 : 1 : wrapper_file->has_completed = false;
1916 [ + - + - : 2 : CLOG_F("Failed to prepare urls table: %s.", ::sqlite3_errmsg(wrapper_file->db));
- + ]
1917 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1918 : : }
1919 : :
1920 [ + - ]: 129 : url_ser_map().clear();
1921 [ + - ]: 129 : url_schema_type_map().clear();
1922 : 129 : impl_->info.url_metas.clear();
1923 : 129 : impl_->raw_url_metas.clear();
1924 : :
1925 : 129 : impl_->info.total_raw_size = 0;
1926 : :
1927 [ + - + + ]: 323 : while (::sqlite3_step(urls_stmt) == SQLITE_ROW) {
1928 : 194 : Info::UrlMeta url_meta;
1929 : 194 : url_meta.valid = true;
1930 [ + - ]: 194 : url_meta.index = ::sqlite3_column_int(urls_stmt, get_column(0));
1931 [ + - ]: 194 : url_meta.url = sqlite_column_text_or_empty(urls_stmt, get_column(1));
1932 [ + - ]: 194 : url_meta.url_type = sqlite_column_text_or_empty(urls_stmt, get_column(2));
1933 [ + - ]: 194 : url_meta.ser_type = sqlite_column_text_or_empty(urls_stmt, get_column(3));
1934 [ + - ]: 194 : const auto* encoding_label = reinterpret_cast<const char*>(::sqlite3_column_text(urls_stmt, get_column(4)));
1935 [ + - ]: 194 : url_meta.schema_type = SchemaData::resolve_type(SchemaData::convert_encoding(encoding_label ? encoding_label : ""),
1936 : : url_meta.ser_type); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1937 : :
1938 [ + - ]: 194 : url_meta.count = static_cast<size_t>(::sqlite3_column_int64(urls_stmt, get_column(5)));
1939 [ + - ]: 194 : url_meta.loss = ::sqlite3_column_double(urls_stmt, get_column(6));
1940 [ + - ]: 194 : url_meta.size = ::sqlite3_column_int64(urls_stmt, get_column(7));
1941 [ + - ]: 194 : url_meta.freq = ::sqlite3_column_double(urls_stmt, get_column(8));
1942 : :
1943 : 194 : impl_->info.total_raw_size += url_meta.size;
1944 : :
1945 [ + - ]: 194 : wrapper_file->id_to_url_map.emplace(url_meta.index, url_meta.url);
1946 [ + - ]: 194 : wrapper_file->url_to_id_map.emplace(url_meta.url, url_meta.index);
1947 : :
1948 [ + - + - ]: 194 : url_ser_map().emplace(url_meta.url, url_meta.ser_type);
1949 [ + - + - ]: 194 : url_schema_type_map().emplace(url_meta.url, url_meta.schema_type);
1950 [ + - ]: 194 : impl_->info.url_metas.emplace_back(std::move(url_meta));
1951 : 194 : }
1952 : :
1953 [ + - ]: 129 : std::sort(impl_->info.url_metas.begin(), impl_->info.url_metas.end());
1954 [ + - ]: 129 : impl_->raw_url_metas = impl_->info.url_metas;
1955 [ + - ]: 129 : rebuild_url_meta_lookup(impl_->info.url_metas);
1956 : :
1957 [ + - ]: 129 : if VLIKELY (urls_stmt) {
1958 [ + - ]: 129 : ::sqlite3_finalize(urls_stmt);
1959 : 129 : urls_stmt = nullptr;
1960 : : }
1961 : :
1962 : : // get idx_elapsed
1963 : 129 : ::sqlite3_stmt* idx_elapsed_stmt = nullptr;
1964 : :
1965 [ + - ]: 129 : ret = ::sqlite3_prepare_v2(
1966 : : wrapper_file->db,
1967 : : "SELECT 1 FROM sqlite_master WHERE tbl_name='VLinkDatas' AND type='index' AND name='idx_elapsed' LIMIT 1;", -1,
1968 : : &idx_elapsed_stmt, nullptr);
1969 : :
1970 [ + - ]: 129 : if (ret == SQLITE_OK) {
1971 [ + - - + ]: 129 : if (::sqlite3_step(idx_elapsed_stmt) == SQLITE_ROW) {
1972 : : wrapper_file->has_idx_elapsed = true; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1973 : : }
1974 : : }
1975 : :
1976 [ + - ]: 129 : if VLIKELY (idx_elapsed_stmt) {
1977 [ + - ]: 129 : ::sqlite3_finalize(idx_elapsed_stmt);
1978 : 129 : idx_elapsed_stmt = nullptr;
1979 : : }
1980 : :
1981 : : // get idx_elapsed_url
1982 : 129 : ::sqlite3_stmt* idx_elapsed_url_stmt = nullptr;
1983 : :
1984 [ + - ]: 129 : ret = ::sqlite3_prepare_v2(
1985 : : wrapper_file->db,
1986 : : "SELECT 1 FROM sqlite_master WHERE tbl_name='VLinkDatas' AND type='index' AND name='idx_elapsed_url' LIMIT 1;",
1987 : : -1, &idx_elapsed_url_stmt, nullptr);
1988 : :
1989 [ + - ]: 129 : if (ret == SQLITE_OK) {
1990 [ + - + + ]: 129 : if (::sqlite3_step(idx_elapsed_url_stmt) == SQLITE_ROW) {
1991 : 126 : wrapper_file->has_idx_elapsed = true;
1992 : 126 : wrapper_file->has_idx_url = true;
1993 : : }
1994 : : }
1995 : :
1996 [ + - ]: 129 : if VLIKELY (idx_elapsed_url_stmt) {
1997 [ + - ]: 129 : ::sqlite3_finalize(idx_elapsed_url_stmt);
1998 : 129 : idx_elapsed_url_stmt = nullptr;
1999 : : }
2000 : :
2001 : 129 : impl_->info.has_idx_elapsed = wrapper_file->has_idx_elapsed;
2002 : 129 : impl_->info.has_idx_url = wrapper_file->has_idx_url;
2003 : 129 : impl_->info.has_schema = wrapper_file->has_schema;
2004 : :
2005 : : // prepare datas
2006 : :
2007 [ + + ]: 129 : if (wrapper_file->has_idx_elapsed) {
2008 [ + - ]: 126 : ret = ::sqlite3_prepare_v2(wrapper_file->db, "SELECT * FROM VLinkDatas ORDER BY elapsed LIMIT 1;", -1,
2009 : : &wrapper_file->stmt, nullptr);
2010 : : } else {
2011 [ + - ]: 3 : ret = ::sqlite3_prepare_v2(wrapper_file->db, "SELECT * FROM VLinkDatas LIMIT 1;", -1, &wrapper_file->stmt, nullptr);
2012 : : }
2013 : :
2014 [ + + ]: 129 : if VUNLIKELY (ret != SQLITE_OK) {
2015 : 1 : wrapper_file->has_completed = false;
2016 [ + - + - : 2 : CLOG_F("Failed to prepare datas table: %s.", ::sqlite3_errmsg(wrapper_file->db));
- + ]
2017 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2018 : : }
2019 : :
2020 : : // get blank time
2021 [ + - ]: 128 : ::sqlite3_step(wrapper_file->stmt);
2022 [ + - ]: 128 : impl_->info.blank_duration = ::sqlite3_column_int64(wrapper_file->stmt, get_column(0)) / 1000U;
2023 : :
2024 [ - + ]: 128 : if VUNLIKELY (impl_->info.blank_duration < 0) {
2025 : : impl_->info.blank_duration = 0; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2026 : : }
2027 : :
2028 [ + - ]: 128 : if VLIKELY (wrapper_file->stmt) {
2029 [ + - ]: 128 : ::sqlite3_finalize(wrapper_file->stmt);
2030 : 128 : wrapper_file->stmt = nullptr;
2031 : : }
2032 : :
2033 : : // update duration
2034 : :
2035 [ + + + + : 128 : if (wrapper_file->has_idx_elapsed && impl_->info.has_completed) {
+ + ]
2036 [ + - ]: 125 : ret = ::sqlite3_prepare_v2(wrapper_file->db, "SELECT * FROM VLinkDatas ORDER BY elapsed DESC LIMIT 1;", -1,
2037 : : &wrapper_file->stmt, nullptr);
2038 : :
2039 [ - + ]: 125 : if VUNLIKELY (ret != SQLITE_OK) {
2040 : : // LCOV_EXCL_START GCOVR_EXCL_START
2041 : : wrapper_file->has_completed = false;
2042 : : CLOG_F("Failed to prepare datas table: %s.", ::sqlite3_errmsg(wrapper_file->db));
2043 : : return false;
2044 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2045 : : }
2046 : :
2047 : : // get duration time
2048 [ + - ]: 125 : ::sqlite3_step(wrapper_file->stmt);
2049 [ + - ]: 125 : int64_t total_duration = ::sqlite3_column_int64(wrapper_file->stmt, get_column(0)) / 1000U;
2050 : :
2051 [ + + ]: 125 : if (total_duration > impl_->info.blank_duration) {
2052 : 47 : impl_->info.total_duration = total_duration;
2053 : : }
2054 : :
2055 [ + - ]: 125 : if VLIKELY (wrapper_file->stmt) {
2056 [ + - ]: 125 : ::sqlite3_finalize(wrapper_file->stmt);
2057 : 125 : wrapper_file->stmt = nullptr;
2058 : : }
2059 : : }
2060 : 128 : return true;
2061 : : #else
2062 : : (void)file;
2063 : : VLOG_F("VDBReader: The compile macro VLINK_ENABLE_SQLITE is not turned on.");
2064 : : return false;
2065 : : #endif
2066 : 131 : }
2067 : :
2068 : 109 : void VDBReader::open(const std::string& path) {
2069 : : #ifdef VLINK_ENABLE_SQLITE
2070 [ + - ]: 109 : close();
2071 : :
2072 : 266 : auto to_open = [this](Impl::WrapperFile& wrapper_file) -> bool {
2073 [ + + ]: 133 : if (impl_->read_only) {
2074 : 15 : int ret = ::sqlite3_open_v2(wrapper_file.path.c_str(), &wrapper_file.db, SQLITE_OPEN_READONLY, nullptr);
2075 : :
2076 [ - + ]: 15 : if VUNLIKELY (ret != SQLITE_OK) {
2077 : : // LCOV_EXCL_START GCOVR_EXCL_START
2078 : : CLOG_F("Failed to open database [%s].", wrapper_file.path.c_str());
2079 : :
2080 : : if (wrapper_file.db) {
2081 : : ::sqlite3_close_v2(wrapper_file.db);
2082 : : wrapper_file.db = nullptr;
2083 : : }
2084 : :
2085 : : return false;
2086 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2087 : : }
2088 : : } else {
2089 : 118 : int ret = ::sqlite3_open_v2(wrapper_file.path.c_str(), &wrapper_file.db,
2090 : : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nullptr);
2091 : :
2092 [ - + ]: 118 : if VUNLIKELY (ret != SQLITE_OK) {
2093 : : // LCOV_EXCL_START GCOVR_EXCL_START
2094 : : CLOG_F("Failed to open database [%s].", wrapper_file.path.c_str());
2095 : :
2096 : : if (wrapper_file.db) {
2097 : : ::sqlite3_close_v2(wrapper_file.db);
2098 : : wrapper_file.db = nullptr;
2099 : : }
2100 : :
2101 : : return false;
2102 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2103 : : }
2104 : : }
2105 : :
2106 [ - + ]: 133 : if VUNLIKELY (!prepare_file(&wrapper_file)) {
2107 : : // LCOV_EXCL_START GCOVR_EXCL_START
2108 : : if (wrapper_file.stmt) {
2109 : : ::sqlite3_finalize(wrapper_file.stmt);
2110 : : wrapper_file.stmt = nullptr;
2111 : : }
2112 : :
2113 : : if (wrapper_file.db) {
2114 : : ::sqlite3_close_v2(wrapper_file.db);
2115 : : wrapper_file.db = nullptr;
2116 : : }
2117 : :
2118 : : return false;
2119 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2120 : : }
2121 : :
2122 : 128 : return true;
2123 : 109 : };
2124 : :
2125 [ + - ]: 109 : impl_->path = path;
2126 : :
2127 : 109 : impl_->total_start_timestamp_ns = -1;
2128 : 109 : impl_->total_has_completed = true;
2129 : :
2130 : : try {
2131 : : #ifdef _WIN32
2132 : : std::filesystem::path file_path(Helpers::string_to_wstring(path));
2133 : :
2134 : : impl_->info.file_name = Helpers::path_to_string(file_path.filename());
2135 : :
2136 : : impl_->info.file_size = 0;
2137 : :
2138 : : std::string suffix = Helpers::path_to_string(file_path.extension());
2139 : : #else
2140 [ + - ]: 109 : std::filesystem::path file_path(path);
2141 : :
2142 [ + - + - ]: 109 : impl_->info.file_name = file_path.filename().string();
2143 : :
2144 : 109 : impl_->info.file_size = 0;
2145 : :
2146 [ + - + - ]: 109 : std::string suffix = file_path.extension().string();
2147 : : #endif
2148 : :
2149 [ + - + + ]: 109 : if VUNLIKELY (!std::filesystem::exists(file_path)) {
2150 [ + - - + ]: 2 : CLOG_F("Database [%s] does not exist.", path.c_str());
2151 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2152 : : }
2153 : :
2154 : 108 : std::filesystem::path parent_path;
2155 : :
2156 : : try {
2157 [ + - ]: 108 : parent_path = file_path.parent_path();
2158 : : } catch (std::filesystem::filesystem_error&) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2159 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2160 : :
2161 : 572 : std::transform(suffix.begin(), suffix.end(), suffix.begin(), [](unsigned char c) { return std::tolower(c); });
2162 : :
2163 [ + + ]: 108 : if (suffix == ".vdbx") {
2164 : : try {
2165 : 32 : int64_t blank_duration = -1;
2166 : :
2167 : 32 : nlohmann::json root_json;
2168 : :
2169 : : {
2170 [ + - ]: 32 : std::ifstream filex(file_path);
2171 : :
2172 [ + + ]: 32 : filex >> root_json;
2173 : :
2174 [ + - ]: 31 : filex.close();
2175 : 32 : }
2176 : :
2177 [ + - + - ]: 31 : nlohmann::json header_json = root_json["VLinkHeader"];
2178 [ + - + - ]: 31 : nlohmann::json urls_json = root_json["VLinkUrls"];
2179 [ + - + - ]: 31 : nlohmann::json files_json = root_json["VLinkFiles"];
2180 : :
2181 : 31 : impl_->info.file_size = 0;
2182 : :
2183 : 31 : int file_index = 0;
2184 : :
2185 [ + + ]: 31 : if (!files_json.empty()) {
2186 [ + - ]: 30 : impl_->file_list.reserve(files_json.size());
2187 : :
2188 : 30 : impl_->info.has_idx_elapsed = true;
2189 : 30 : impl_->info.has_idx_url = true;
2190 : 30 : impl_->info.has_schema = false;
2191 : : } else {
2192 : 1 : impl_->info.has_idx_elapsed = false;
2193 : 1 : impl_->info.has_idx_url = false;
2194 : 1 : impl_->info.has_schema = false;
2195 : : }
2196 : :
2197 : 31 : std::filesystem::path file_db;
2198 : 31 : std::string file_db_str;
2199 : :
2200 [ + - + - : 88 : for (const auto& file_info : files_json) {
+ - + + ]
2201 : : #ifdef _WIN32
2202 : :
2203 : : if (parent_path.empty()) {
2204 : : file_db = std::filesystem::path(Helpers::string_to_wstring(file_info));
2205 : : } else {
2206 : : file_db = parent_path / std::filesystem::path(Helpers::string_to_wstring(file_info));
2207 : : }
2208 : : #else
2209 : :
2210 [ + + ]: 58 : if (parent_path.empty()) {
2211 [ + - ]: 6 : file_db = std::filesystem::path(file_info);
2212 : : } else {
2213 [ + - + - ]: 52 : file_db = parent_path / std::filesystem::path(file_info);
2214 : : }
2215 : : #endif
2216 : :
2217 [ + - ]: 58 : file_db_str = file_db.string();
2218 : :
2219 [ + - + + ]: 58 : if VUNLIKELY (!std::filesystem::exists(file_db)) {
2220 [ + - - + ]: 2 : CLOG_F("Database [%s] does not exist.", file_db_str.c_str());
2221 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2222 : : }
2223 : :
2224 [ + - ]: 57 : Impl::WrapperFile wrapper_file;
2225 [ + - ]: 57 : wrapper_file.path = file_db_str;
2226 : 57 : wrapper_file.index = file_index;
2227 : :
2228 [ + - - + ]: 57 : if VUNLIKELY (!to_open(wrapper_file)) {
2229 : : // LCOV_EXCL_START GCOVR_EXCL_START
2230 : : CLOG_W("VDBReader: Skipping invalid database [%s].", file_db_str.c_str());
2231 : : continue;
2232 : : }
2233 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2234 : :
2235 [ - + ]: 57 : if (!wrapper_file.has_idx_elapsed) {
2236 : : impl_->info.has_idx_elapsed = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2237 : : }
2238 : :
2239 [ - + ]: 57 : if (!wrapper_file.has_idx_url) {
2240 : : impl_->info.has_idx_url = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2241 : : }
2242 : :
2243 [ + + ]: 57 : if (wrapper_file.has_schema) {
2244 : 2 : impl_->info.has_schema = true;
2245 : : }
2246 : :
2247 : 57 : std::error_code db_size_ec;
2248 : 57 : std::uintmax_t file_size = std::filesystem::file_size(file_db, db_size_ec);
2249 : :
2250 [ - + ]: 57 : if VUNLIKELY (db_size_ec) {
2251 : : CLOG_W("VDBReader: file_size failed for [%s]: %s.", file_db_str.c_str(), // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2252 : : db_size_ec.message().c_str()); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2253 : : file_size = 0; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2254 : : }
2255 : :
2256 : 57 : impl_->info.file_size += file_size;
2257 : :
2258 : 57 : wrapper_file.start_timestamp_ns = impl_->info.start_timestamp * 1000'000;
2259 : 57 : wrapper_file.begin = impl_->info.blank_duration;
2260 : 57 : wrapper_file.end = impl_->info.total_duration;
2261 : :
2262 [ + + ]: 57 : if (impl_->total_start_timestamp_ns < 0) {
2263 : 29 : impl_->total_start_timestamp_ns = wrapper_file.start_timestamp_ns;
2264 : : }
2265 : :
2266 [ - + ]: 57 : if (!wrapper_file.has_completed) {
2267 : : impl_->total_has_completed = false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2268 : : }
2269 : :
2270 [ + + ]: 57 : if (blank_duration < 0) {
2271 : 29 : blank_duration = impl_->info.blank_duration;
2272 : : }
2273 : :
2274 [ + - ]: 57 : impl_->file_list.emplace_back(std::move(wrapper_file));
2275 : 57 : ++file_index;
2276 [ + - ]: 57 : }
2277 : :
2278 : 30 : impl_->info.split_count = impl_->file_list.size();
2279 : :
2280 [ + + ]: 30 : if VUNLIKELY (impl_->file_list.empty()) {
2281 [ + - - + ]: 2 : VLOG_F("VDBReader: DB list is empty.");
2282 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2283 : : }
2284 : :
2285 [ + - + - ]: 29 : int version_major = header_json["major"];
2286 [ + - + - ]: 29 : int version_minor = header_json["minor"];
2287 [ + - + - ]: 29 : int version_patch = header_json["patch"];
2288 : :
2289 : 29 : impl_->info.version =
2290 [ + - + - : 58 : std::to_string(version_major) + "." + std::to_string(version_minor) + "." + std::to_string(version_patch);
+ - + - +
- + - +
- ]
2291 [ + - ]: 29 : impl_->info.storage_type = "SQLite3";
2292 [ + - + - ]: 29 : impl_->info.message_count = header_json["count"];
2293 [ + - + - ]: 29 : impl_->info.total_duration = header_json["duration"];
2294 : 29 : impl_->info.total_duration /= 1000U;
2295 [ + - + - ]: 29 : impl_->info.time_accuracy = header_json["accuracy"];
2296 [ + - + - ]: 29 : impl_->info.compression_type = header_json["compress"];
2297 [ + - + - ]: 29 : impl_->info.process_name = header_json["process"];
2298 [ + - + - ]: 29 : impl_->info.date_time = header_json["date"];
2299 : :
2300 [ + - + + ]: 29 : if (header_json.contains("start_timestamp")) {
2301 [ + - + - ]: 28 : impl_->info.start_timestamp = header_json["start_timestamp"];
2302 : : } else {
2303 : 1 : impl_->info.start_timestamp = Helpers::convert_date_to_timestamp(impl_->info.date_time) / 1000'000;
2304 : : }
2305 : :
2306 [ + + ]: 29 : if VUNLIKELY (impl_->info.start_timestamp < 0) {
2307 : 1 : impl_->info.start_timestamp = 0;
2308 : :
2309 [ + - ]: 1 : if (impl_->read_only) {
2310 [ + - + - ]: 2 : VLOG_E("VDBReader: Invalid start_timestamp.");
2311 : : }
2312 : : }
2313 : :
2314 [ + - + + ]: 29 : if (header_json.contains("tag")) {
2315 [ + - + - ]: 28 : impl_->info.tag_name = header_json["tag"];
2316 : : } else {
2317 [ + - ]: 1 : impl_->info.tag_name = "Empty";
2318 : : }
2319 : :
2320 [ + - + + ]: 29 : if (header_json.contains("complete")) {
2321 [ + - + - ]: 28 : impl_->info.has_completed = header_json["complete"];
2322 : : } else {
2323 : 1 : impl_->info.has_completed = true;
2324 : : }
2325 : :
2326 [ + - + + ]: 29 : if (header_json.contains("timezone")) {
2327 [ + - + - ]: 28 : impl_->info.timezone = header_json["timezone"];
2328 : : } else {
2329 : 1 : impl_->info.timezone = 480;
2330 : : }
2331 : :
2332 [ + - + + ]: 29 : if (header_json.contains("split_by_size")) {
2333 [ + - + - ]: 28 : impl_->info.split_by_size = header_json["split_by_size"];
2334 : : }
2335 : :
2336 [ + - + + ]: 29 : if (header_json.contains("split_by_time")) {
2337 [ + - + - ]: 28 : impl_->info.split_by_time = header_json["split_by_time"];
2338 : : }
2339 : :
2340 : 29 : impl_->info.blank_duration = blank_duration;
2341 : :
2342 [ + + + + ]: 63 : if (impl_->info.compression_type.empty() || impl_->info.compression_type == "None" ||
2343 [ + - + + : 63 : impl_->info.compression_type == "NONE" || impl_->info.compression_type == "none") {
+ + ]
2344 : 26 : impl_->enable_compress = false;
2345 : : } else {
2346 : 3 : impl_->enable_compress = true;
2347 : : }
2348 : :
2349 [ + - + + ]: 29 : if VUNLIKELY (impl_->info.time_accuracy != "MicroSecond") {
2350 [ + - - + ]: 2 : VLOG_F("VDBReader: Database accuracy is not supported.");
2351 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2352 : : }
2353 : :
2354 [ + - ]: 28 : url_ser_map().clear();
2355 [ + - ]: 28 : url_schema_type_map().clear();
2356 : 28 : impl_->info.url_metas.clear();
2357 : 28 : impl_->raw_url_metas.clear();
2358 : :
2359 [ + - ]: 28 : impl_->info.url_metas.reserve(urls_json.size());
2360 : :
2361 : 28 : impl_->info.total_raw_size = 0;
2362 : :
2363 [ + - + - : 56 : for (const auto& url_info : urls_json) {
+ - + + ]
2364 : 28 : Info::UrlMeta url_meta;
2365 : :
2366 : 28 : url_meta.valid = true;
2367 [ + - + - ]: 28 : url_meta.index = url_info["index"];
2368 [ + - + - ]: 28 : url_meta.url = url_info["url"];
2369 [ + - + - ]: 28 : url_meta.url_type = url_info["type"];
2370 [ + - + - ]: 28 : url_meta.ser_type = url_info["ser"];
2371 : :
2372 [ + - + + ]: 28 : if (url_info.contains("encoding")) {
2373 : 54 : url_meta.schema_type = SchemaData::resolve_type(
2374 [ + - + - ]: 54 : SchemaData::convert_encoding(url_info["encoding"].get<std::string>()), url_meta.ser_type);
2375 : : } else {
2376 : 1 : url_meta.schema_type = SchemaType::kUnknown;
2377 : : }
2378 : :
2379 [ + - + - ]: 28 : url_meta.count = url_info["count"];
2380 [ + - + - ]: 28 : url_meta.loss = url_info["loss"];
2381 : :
2382 [ + - + + ]: 28 : if (url_info.contains("size")) {
2383 [ + - + - ]: 27 : url_meta.size = url_info["size"];
2384 : : }
2385 : :
2386 [ + - + + ]: 28 : if (url_info.contains("freq")) {
2387 [ + - + - ]: 27 : url_meta.freq = url_info["freq"];
2388 : : }
2389 : :
2390 : 28 : impl_->info.total_raw_size += url_meta.size;
2391 : :
2392 [ + - + - ]: 28 : url_ser_map().emplace(url_meta.url, url_meta.ser_type);
2393 [ + - + - ]: 28 : url_schema_type_map().emplace(url_meta.url, url_meta.schema_type);
2394 [ + - ]: 28 : impl_->info.url_metas.emplace_back(std::move(url_meta));
2395 : 28 : }
2396 : :
2397 [ + - ]: 28 : std::sort(impl_->info.url_metas.begin(), impl_->info.url_metas.end());
2398 [ + - ]: 28 : impl_->raw_url_metas = impl_->info.url_metas;
2399 [ + - ]: 28 : rebuild_url_meta_lookup(impl_->info.url_metas);
2400 [ + - + - : 51 : } catch (nlohmann::json::exception& e) {
+ - + - +
- + - +
+ ]
2401 [ + - - + ]: 2 : VLOG_F("VDBReader: JSON parse error, ", e.what(), ".");
2402 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2403 : 1 : }
2404 : : } else {
2405 [ + - ]: 76 : Impl::WrapperFile wrapper_file;
2406 [ + - ]: 76 : wrapper_file.path = impl_->path;
2407 : 76 : wrapper_file.index = 0;
2408 : :
2409 [ + + - + ]: 76 : if VUNLIKELY (!to_open(wrapper_file)) {
2410 : : CLOG_F("VDBReader: Failed to prepare database [%s].", path.c_str()); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2411 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2412 : : }
2413 : :
2414 : 71 : impl_->info.file_size = 0;
2415 : :
2416 : 71 : std::error_code db_size_ec;
2417 : 71 : std::uintmax_t file_size = std::filesystem::file_size(file_path, db_size_ec);
2418 : :
2419 [ - + ]: 71 : if VUNLIKELY (db_size_ec) {
2420 : : CLOG_W("VDBReader: file_size failed for [%s]: %s.", path.c_str(), // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2421 : : db_size_ec.message().c_str()); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2422 : : file_size = 0; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2423 : : }
2424 : :
2425 : 71 : impl_->info.file_size += file_size;
2426 : :
2427 : 71 : wrapper_file.start_timestamp_ns = impl_->info.start_timestamp * 1000'000;
2428 : 71 : wrapper_file.begin = impl_->info.blank_duration;
2429 : 71 : wrapper_file.end = impl_->info.total_duration;
2430 : :
2431 [ + - ]: 71 : if (impl_->total_start_timestamp_ns < 0) {
2432 : 71 : impl_->total_start_timestamp_ns = wrapper_file.start_timestamp_ns;
2433 : : }
2434 : :
2435 [ + + ]: 71 : if (!wrapper_file.has_completed) {
2436 : 1 : impl_->total_has_completed = false;
2437 : : }
2438 : :
2439 [ + - ]: 71 : impl_->file_list.emplace_back(std::move(wrapper_file));
2440 [ + - ]: 76 : }
2441 [ + - + - : 138 : } catch (std::filesystem::filesystem_error& e) {
+ - + - ]
2442 : : VLOG_F("VDBReader: Filesystem error, ", e.what(), "."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2443 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2444 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2445 : : #else
2446 : : (void)path;
2447 : : VLOG_F("VDBReader: The compile macro VLINK_ENABLE_SQLITE is not turned on.");
2448 : : #endif
2449 : : }
2450 : :
2451 : 208 : void VDBReader::close() {
2452 : : #ifdef VLINK_ENABLE_SQLITE
2453 : 208 : impl_->cursor_stmt.reset();
2454 : :
2455 [ + + ]: 334 : for (auto& wrapper_file : impl_->file_list) {
2456 [ + + ]: 126 : if (wrapper_file.stmt) {
2457 [ + - ]: 88 : ::sqlite3_finalize(wrapper_file.stmt);
2458 : 88 : wrapper_file.stmt = nullptr;
2459 : : }
2460 : :
2461 [ + - ]: 126 : if (wrapper_file.db) {
2462 [ + - ]: 126 : int ret = ::sqlite3_close_v2(wrapper_file.db);
2463 : :
2464 [ - + ]: 126 : if VUNLIKELY (ret != SQLITE_OK) {
2465 : : CLOG_W("Failed to close database: %s.", ::sqlite3_errmsg(wrapper_file.db)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2466 : : }
2467 : :
2468 : 126 : wrapper_file.db = nullptr;
2469 : : }
2470 : : }
2471 : :
2472 : 208 : impl_->file_list.clear();
2473 : : #endif
2474 : 208 : }
2475 : :
2476 : 24 : int VDBReader::get_reset_index(const Config& config) {
2477 : : #ifdef VLINK_ENABLE_SQLITE
2478 : 24 : impl_->is_pending.store(true, std::memory_order_relaxed);
2479 : :
2480 : 24 : int ret = 0;
2481 : 24 : int start_index = -1;
2482 : :
2483 : 24 : int64_t last_time = impl_->begin_time.load(std::memory_order_relaxed);
2484 : :
2485 : 24 : impl_->update_sql_default_str = "SELECT elapsed, url, action, data FROM VLinkDatas ORDER BY rowid;";
2486 : :
2487 : 24 : impl_->update_sql_time_str = "SELECT elapsed, url, action, data FROM VLinkDatas WHERE elapsed >= ";
2488 [ + - ]: 48 : impl_->update_sql_time_str.append(std::to_string(impl_->begin_time.load(std::memory_order_relaxed) * 1000));
2489 : 24 : impl_->update_sql_time_str.append(" ORDER BY rowid;");
2490 : :
2491 : 24 : std::string* select_sql = nullptr;
2492 : :
2493 [ + + ]: 48 : for (auto& wrapper_file : impl_->file_list) {
2494 [ + - + - ]: 24 : if (wrapper_file.has_idx_elapsed && wrapper_file.has_idx_url) {
2495 [ + + ]: 24 : if (config.filter_urls.empty()) {
2496 [ + - ]: 19 : impl_->update_sql_default_str = "SELECT elapsed, url, action, data FROM VLinkDatas ORDER BY elapsed;";
2497 : :
2498 [ + - ]: 19 : impl_->update_sql_time_str = "SELECT elapsed, url, action, data FROM VLinkDatas WHERE elapsed >= ";
2499 [ + - + - ]: 38 : impl_->update_sql_time_str.append(std::to_string(impl_->begin_time.load(std::memory_order_relaxed) * 1000));
2500 [ + - ]: 19 : impl_->update_sql_time_str.append(" ORDER BY elapsed;");
2501 : : } else {
2502 [ + - ]: 5 : std::string id_list_str = " url IN (";
2503 : 5 : bool id_appended = false;
2504 : :
2505 [ + + ]: 20 : for (const auto& [url, id] : wrapper_file.url_to_id_map) {
2506 [ + - + + ]: 15 : if (!match_playback_url_filter(url, config.filter_urls)) {
2507 : 11 : continue;
2508 : : }
2509 : :
2510 [ + - + - ]: 4 : id_list_str.append(std::to_string(id));
2511 [ + - ]: 4 : id_list_str.append(",");
2512 : 4 : id_appended = true;
2513 : : }
2514 : :
2515 [ + + ]: 5 : if (id_appended) {
2516 : 4 : id_list_str.pop_back();
2517 [ + - ]: 4 : id_list_str.append(")");
2518 : : } else {
2519 [ + - ]: 1 : id_list_str = " url IN (NULL)";
2520 : : }
2521 : :
2522 [ + - ]: 5 : impl_->update_sql_default_str = "SELECT elapsed, url, action, data FROM VLinkDatas WHERE";
2523 [ + - ]: 5 : impl_->update_sql_default_str.append(id_list_str);
2524 [ + - + - ]: 5 : impl_->update_sql_default_str.append(wrapper_file.has_idx_elapsed
2525 : : ? " ORDER BY elapsed;"
2526 : : : " ORDER BY rowid;"); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2527 : :
2528 [ + - ]: 5 : impl_->update_sql_time_str = "SELECT elapsed, url, action, data FROM VLinkDatas WHERE";
2529 [ + - ]: 5 : impl_->update_sql_time_str.append(id_list_str);
2530 [ + - ]: 5 : impl_->update_sql_time_str.append(" AND elapsed >= ");
2531 [ + - + - ]: 10 : impl_->update_sql_time_str.append(std::to_string(impl_->begin_time.load(std::memory_order_relaxed) * 1000));
2532 [ + - + - ]: 5 : impl_->update_sql_time_str.append(wrapper_file.has_idx_elapsed
2533 : : ? " ORDER BY elapsed;"
2534 : : : " ORDER BY rowid;"); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2535 : 5 : }
2536 : : }
2537 : :
2538 [ + - + - : 72 : if (start_index < 0 && impl_->begin_time.load(std::memory_order_relaxed) >= last_time &&
+ - ]
2539 [ + - ]: 48 : impl_->begin_time.load(std::memory_order_relaxed) <= wrapper_file.end) {
2540 [ + + ]: 48 : if (impl_->begin_time.load(std::memory_order_relaxed) > 0) {
2541 : 2 : select_sql = &impl_->update_sql_time_str;
2542 : : } else {
2543 : 22 : select_sql = &impl_->update_sql_default_str;
2544 : : }
2545 : :
2546 : 24 : start_index = wrapper_file.index;
2547 : : } else {
2548 : : select_sql = &impl_->update_sql_default_str; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2549 : : }
2550 : :
2551 [ + + ]: 24 : if VLIKELY (wrapper_file.stmt) {
2552 [ + - ]: 14 : ::sqlite3_finalize(wrapper_file.stmt);
2553 : 14 : wrapper_file.stmt = nullptr;
2554 : : }
2555 : :
2556 [ - + ]: 24 : if VLIKELY (!select_sql) {
2557 : : VLOG_E("VDBReader: Failed to prepare select sql str."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2558 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2559 : : }
2560 : :
2561 [ + - ]: 24 : ret = ::sqlite3_prepare_v2(wrapper_file.db, select_sql->c_str(), -1, &wrapper_file.stmt, nullptr);
2562 : :
2563 [ - + ]: 24 : if VUNLIKELY (ret != SQLITE_OK) {
2564 : : // LCOV_EXCL_START GCOVR_EXCL_START
2565 : : CLOG_W("Failed to prepare datas table: %s.", ::sqlite3_errmsg(wrapper_file.db));
2566 : :
2567 : : start_index = -1;
2568 : :
2569 : : break;
2570 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2571 : : }
2572 : :
2573 [ + - ]: 24 : ::sqlite3_step(wrapper_file.stmt);
2574 [ + - ]: 24 : ::sqlite3_reset(wrapper_file.stmt);
2575 : :
2576 : 24 : last_time = wrapper_file.end;
2577 : : }
2578 : :
2579 : 24 : impl_->is_pending.store(false, std::memory_order_relaxed);
2580 : :
2581 : 24 : return start_index;
2582 : : #else
2583 : : (void)config;
2584 : : return -1;
2585 : : #endif
2586 : : }
2587 : :
2588 : 72 : bool VDBReader::prepare_cursor_stmt(int file_index) {
2589 : : #ifdef VLINK_ENABLE_SQLITE
2590 : 72 : impl_->cursor_stmt.reset();
2591 : :
2592 [ + - - + : 72 : if VUNLIKELY (file_index < 0 || file_index >= static_cast<int>(impl_->file_list.size())) {
- + ]
2593 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2594 : : }
2595 : :
2596 [ + - ]: 72 : auto& wrapper_file = impl_->file_list.at(file_index);
2597 : :
2598 [ - + ]: 72 : if VUNLIKELY (!wrapper_file.db) {
2599 : : VLOG_W("VDBReader: Cursor target db is empty."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2600 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2601 : : }
2602 : :
2603 : 72 : std::string where;
2604 : :
2605 [ + + ]: 72 : if (!impl_->cursor_config.filter_urls.empty()) {
2606 [ + - ]: 11 : std::string id_list = "url IN (";
2607 : 11 : bool id_appended = false;
2608 : :
2609 [ + + ]: 32 : for (const auto& [url, id] : wrapper_file.url_to_id_map) {
2610 [ + - + + ]: 21 : if (!match_playback_url_filter(url, impl_->cursor_config.filter_urls)) {
2611 : 16 : continue;
2612 : : }
2613 : :
2614 [ + - + - ]: 5 : id_list.append(std::to_string(id));
2615 [ + - ]: 5 : id_list.append(",");
2616 : 5 : id_appended = true;
2617 : : }
2618 : :
2619 [ + + ]: 11 : if (id_appended) {
2620 : 5 : id_list.pop_back();
2621 [ + - ]: 5 : id_list.append(")");
2622 : : } else {
2623 [ + - ]: 6 : id_list = "url IN (NULL)";
2624 : : }
2625 : :
2626 : 11 : where = std::move(id_list);
2627 : 11 : }
2628 : :
2629 [ + + ]: 72 : if (impl_->cursor_begin_us > 0) {
2630 [ + + ]: 10 : if (!where.empty()) {
2631 [ + - ]: 5 : where.append(" AND ");
2632 : : }
2633 : :
2634 [ + - ]: 10 : where.append("elapsed >= ");
2635 [ + - + - ]: 10 : where.append(std::to_string(impl_->cursor_begin_us));
2636 : : }
2637 : :
2638 [ + - ]: 72 : std::string select_sql = "SELECT elapsed, url, action, data FROM VLinkDatas";
2639 : :
2640 [ + + ]: 72 : if (!where.empty()) {
2641 [ + - ]: 16 : select_sql.append(" WHERE ");
2642 [ + - ]: 16 : select_sql.append(where);
2643 : : }
2644 : :
2645 : 72 : const bool order_by_elapsed = wrapper_file.has_idx_elapsed;
2646 : :
2647 [ + + + - ]: 72 : select_sql.append(order_by_elapsed ? " ORDER BY elapsed;" : " ORDER BY rowid;");
2648 : :
2649 : 72 : ::sqlite3_stmt* stmt = nullptr;
2650 [ + - ]: 72 : const int ret = ::sqlite3_prepare_v2(wrapper_file.db, select_sql.c_str(), -1, &stmt, nullptr);
2651 : :
2652 [ - + ]: 72 : if VUNLIKELY (ret != SQLITE_OK) {
2653 : : CLOG_W("VDBReader: Failed to prepare cursor stmt: %s.", // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2654 : : ::sqlite3_errmsg(wrapper_file.db)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2655 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2656 : : }
2657 : :
2658 : 72 : impl_->cursor_stmt.reset(stmt);
2659 : 72 : impl_->cursor_file_index = file_index;
2660 : :
2661 : 72 : return true;
2662 : : #else
2663 : : (void)file_index;
2664 : : return false;
2665 : : #endif
2666 : 72 : }
2667 : :
2668 : 59 : bool VDBReader::do_open_cursor(const Config& config) {
2669 : : #ifdef VLINK_ENABLE_SQLITE
2670 : 59 : impl_->cursor_stmt.reset();
2671 : 59 : impl_->cursor_config = config;
2672 [ + + ]: 59 : impl_->cursor_begin_us = config.begin_time > 0 ? config.begin_time * 1000 : 0;
2673 [ + + ]: 59 : impl_->cursor_end_us = config.end_time > 0 ? config.end_time * 1000 : 0;
2674 : 59 : impl_->cursor_file_index = 0;
2675 : :
2676 [ - + ]: 59 : if VUNLIKELY (impl_->file_list.empty()) {
2677 : : VLOG_W("VDBReader: Cursor cannot find any data."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2678 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2679 : : }
2680 : :
2681 : 59 : return prepare_cursor_stmt(0);
2682 : : #else
2683 : : (void)config;
2684 : : return false;
2685 : : #endif
2686 : : }
2687 : :
2688 : 161 : bool VDBReader::do_read_next(Frame& out, bool& is_error) {
2689 : : #ifdef VLINK_ENABLE_SQLITE
2690 : 161 : is_error = false;
2691 : :
2692 : : while (true) {
2693 [ - + ]: 176 : if VUNLIKELY (!impl_->cursor_stmt) {
2694 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2695 : : }
2696 : :
2697 : 176 : const int step = ::sqlite3_step(impl_->cursor_stmt.get());
2698 : :
2699 [ + + ]: 176 : if (step == SQLITE_ROW) {
2700 [ + - ]: 108 : const int64_t timestamp = ::sqlite3_column_int64(impl_->cursor_stmt.get(), get_column(0));
2701 : :
2702 [ + + + + : 108 : if (impl_->cursor_end_us > 0 && timestamp > impl_->cursor_end_us) {
+ + ]
2703 : 106 : return false;
2704 : : }
2705 : :
2706 [ + - ]: 104 : const int url_id = ::sqlite3_column_int(impl_->cursor_stmt.get(), get_column(1));
2707 [ + - ]: 104 : auto& wrapper_file = impl_->file_list.at(impl_->cursor_file_index);
2708 [ + - ]: 104 : auto iter = wrapper_file.id_to_url_map.find(url_id);
2709 : :
2710 [ + + - + : 104 : if VUNLIKELY (iter == wrapper_file.id_to_url_map.end() || iter->second.empty()) {
+ + ]
2711 : 2 : continue;
2712 : : }
2713 : :
2714 : 103 : const auto& url = iter->second;
2715 : :
2716 : 103 : std::string output_url;
2717 : :
2718 [ + - + + ]: 103 : if VUNLIKELY (!convert_playback_url(url, output_url)) {
2719 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2720 : : }
2721 : :
2722 [ + + + - : 102 : if (!impl_->cursor_config.filter_urls.empty() && impl_->cursor_config.filter_urls.count(output_url) == 0U) {
- + - + ]
2723 : 0 : continue;
2724 : : }
2725 : :
2726 : 102 : std::string_view action_str;
2727 : : const auto* action_ptr =
2728 [ + - ]: 102 : reinterpret_cast<const char*>(::sqlite3_column_text(impl_->cursor_stmt.get(), get_column(2)));
2729 : :
2730 [ + + ]: 102 : if (action_ptr != nullptr) {
2731 : 100 : action_str = std::string_view(
2732 [ + - ]: 100 : action_ptr, static_cast<size_t>(::sqlite3_column_bytes(impl_->cursor_stmt.get(), get_column(2))));
2733 : : }
2734 : :
2735 [ + - ]: 102 : const auto* data = static_cast<const uint8_t*>(::sqlite3_column_blob(impl_->cursor_stmt.get(), get_column(3)));
2736 [ + - ]: 102 : const int size = ::sqlite3_column_bytes(impl_->cursor_stmt.get(), get_column(3));
2737 : :
2738 : 102 : out.timestamp = timestamp;
2739 : 102 : out.url = std::move(output_url);
2740 : 102 : out.ser_type.clear();
2741 : 102 : out.schema_type = SchemaType::kUnknown;
2742 [ + - ]: 102 : out.action_type = convert_action(action_str);
2743 : :
2744 [ + + + + : 102 : if VUNLIKELY (impl_->enable_compress && Bytes::is_compress_data(data, size)) {
+ + ]
2745 : 11 : out.data = Bytes::uncompress_data(data, size, false);
2746 : : } else {
2747 : 91 : out.data = Bytes::shallow_copy(data, size);
2748 : : }
2749 : :
2750 [ + - ]: 102 : fill_frame_meta(out);
2751 : :
2752 : 102 : return true;
2753 [ + + + - ]: 171 : } else if (step == SQLITE_DONE) {
2754 [ + + ]: 68 : if (impl_->cursor_file_index + 1 < static_cast<int>(impl_->file_list.size())) {
2755 [ - + ]: 13 : if VUNLIKELY (!prepare_cursor_stmt(impl_->cursor_file_index + 1)) {
2756 : : is_error = true; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2757 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2758 : : }
2759 : :
2760 : 13 : continue;
2761 : : }
2762 : :
2763 : 55 : return false;
2764 : : } else {
2765 : : // LCOV_EXCL_START GCOVR_EXCL_START
2766 : : CLOG_W("VDBReader: Cursor step failed: %s.", ::sqlite3_errmsg(::sqlite3_db_handle(impl_->cursor_stmt.get())));
2767 : : is_error = true;
2768 : : return false;
2769 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2770 : : }
2771 : 15 : }
2772 : : #else
2773 : : (void)out;
2774 : : is_error = false;
2775 : : return false;
2776 : : #endif
2777 : : }
2778 : :
2779 : 22 : void VDBReader::read(const Config& config) {
2780 : : #ifdef VLINK_ENABLE_SQLITE
2781 : 22 : int loop_times = 0;
2782 : :
2783 : 22 : reset_plugin();
2784 : :
2785 [ + + ]: 22 : if (config.auto_pause) {
2786 : 1 : impl_->pause_flag.store(true, std::memory_order_relaxed);
2787 : : }
2788 : :
2789 : 22 : bool is_interrupted = false;
2790 : :
2791 : : do {
2792 : 24 : bool is_end = false;
2793 : :
2794 : : // prepare
2795 [ + - ]: 24 : int start_index = get_reset_index(config);
2796 : :
2797 [ + + ]: 24 : if (impl_->ready_callback) {
2798 [ + - ]: 12 : impl_->ready_callback();
2799 : : }
2800 : :
2801 [ + - - + : 24 : if VUNLIKELY (start_index < 0 || start_index > static_cast<int>(impl_->file_list.size()) - 1) {
- + ]
2802 : : // LCOV_EXCL_START GCOVR_EXCL_START
2803 : : VLOG_W("VDBReader: Cannot find any data for play.");
2804 : :
2805 : : update_status(kStopped);
2806 : :
2807 : : if (config.auto_quit) {
2808 : : quit();
2809 : : }
2810 : :
2811 : : return;
2812 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
2813 : : }
2814 : :
2815 : : {
2816 [ + - ]: 24 : std::lock_guard time_lock(impl_->time_mtx);
2817 : 24 : impl_->pause_elapsed.store(0, std::memory_order_relaxed);
2818 : 24 : impl_->offset_elapsed.store(0, std::memory_order_relaxed);
2819 : 48 : impl_->real_elapsed.store(impl_->begin_time.load(std::memory_order_relaxed) * 1000U, std::memory_order_relaxed);
2820 : :
2821 : 24 : impl_->elapsed_timer.restart();
2822 : 24 : impl_->pause_elapsed_timer.restart();
2823 : 24 : impl_->offset_timer.restart();
2824 : 24 : impl_->real_timer.restart();
2825 : 24 : }
2826 : :
2827 [ + + ]: 24 : if (impl_->stop_flag.load(std::memory_order_relaxed)) {
2828 : 1 : is_interrupted = true;
2829 [ + - ]: 1 : update_status(kStopped);
2830 : 9 : break;
2831 [ + + ]: 23 : } else if (impl_->jump_flag.load(std::memory_order_relaxed)) {
2832 : 2 : break;
2833 [ + + ]: 21 : } else if (impl_->pause_flag.load(std::memory_order_relaxed)) {
2834 : 1 : impl_->pause_elapsed_timer.restart();
2835 [ + - ]: 1 : update_status(kPaused);
2836 [ + - ]: 1 : do_pause();
2837 : : {
2838 [ + - ]: 1 : std::lock_guard time_lock(impl_->time_mtx);
2839 : 1 : impl_->pause_elapsed.store(0, std::memory_order_relaxed);
2840 : 1 : impl_->offset_elapsed.store(0, std::memory_order_relaxed);
2841 : 2 : impl_->real_elapsed.store(impl_->begin_time.load(std::memory_order_relaxed) * 1000U, std::memory_order_relaxed);
2842 : :
2843 : 1 : impl_->elapsed_timer.restart();
2844 : 1 : impl_->pause_elapsed_timer.restart();
2845 : 1 : impl_->offset_timer.restart();
2846 : 1 : impl_->real_timer.restart();
2847 : 1 : }
2848 : :
2849 : : } else {
2850 [ + - ]: 20 : update_status(kPlaying);
2851 : : }
2852 : :
2853 : 21 : int64_t elapsed = 0;
2854 : 21 : int64_t timestamp = 0;
2855 : 21 : int64_t last_timestamp = 0;
2856 : 21 : int url_id = -1;
2857 : 21 : const uint8_t* data = nullptr;
2858 : 21 : int size = 0;
2859 : 21 : Bytes decompressed_data;
2860 : :
2861 : : // process files
2862 [ + + ]: 38 : for (int index = start_index; index < static_cast<int>(impl_->file_list.size()); ++index) {
2863 : 21 : impl_->split_index.store(index, std::memory_order_relaxed);
2864 : :
2865 [ + - ]: 42 : auto& wrapper_file = impl_->file_list.at(impl_->split_index.load(std::memory_order_relaxed));
2866 : :
2867 [ + - - + : 21 : if VUNLIKELY (!wrapper_file.db || !wrapper_file.stmt) {
- + ]
2868 : : VLOG_W("VDBReader: Target db or stmt is empty."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2869 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2870 : : }
2871 : :
2872 : : // process datas
2873 [ + - + + ]: 76 : for ([[maybe_unused]] int row = 0; ::sqlite3_step(wrapper_file.stmt) == SQLITE_ROW; ++row) {
2874 [ + - ]: 59 : timestamp = ::sqlite3_column_int64(wrapper_file.stmt, get_column(0));
2875 : :
2876 : 59 : impl_->offset_timer.restart();
2877 : :
2878 [ - + ]: 59 : if VUNLIKELY (last_timestamp > timestamp + 10'000U) {
2879 : : VLOG_W("VDBReader: The database timestamp is incorrect."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2880 : : }
2881 : :
2882 : 59 : last_timestamp = timestamp;
2883 : :
2884 [ - + ]: 118 : if (timestamp < impl_->begin_time.load(std::memory_order_relaxed) * 1000U) {
2885 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2886 : : }
2887 : :
2888 [ + + - + ]: 59 : if (config.end_time > 0 && timestamp > config.end_time * 1000U) {
2889 : : timestamp = config.end_time * 1000U; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2890 : : is_end = true; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2891 : : }
2892 : :
2893 [ + - ]: 59 : url_id = ::sqlite3_column_int(wrapper_file.stmt, get_column(1));
2894 : :
2895 [ + - ]: 59 : auto iter = wrapper_file.id_to_url_map.find(url_id);
2896 : :
2897 [ - + ]: 59 : if VUNLIKELY (iter == wrapper_file.id_to_url_map.end()) {
2898 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2899 : : }
2900 : :
2901 : 59 : const auto& url = iter->second;
2902 : :
2903 [ - + ]: 59 : if VUNLIKELY (url.empty()) {
2904 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2905 : : }
2906 : :
2907 [ + - - + ]: 59 : if VUNLIKELY (!match_playback_url_filter(url, config.filter_urls)) {
2908 : : continue; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2909 : : }
2910 : :
2911 [ + - + - : 118 : if (impl_->stop_flag.load(std::memory_order_relaxed) || impl_->jump_flag.load(std::memory_order_relaxed) ||
- + ]
2912 [ + - - + ]: 59 : is_ready_to_quit()) {
2913 : : is_interrupted = true; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2914 : 4 : break;
2915 : : }
2916 : :
2917 : 59 : std::string_view action_str;
2918 [ + - ]: 59 : const auto* action_ptr = reinterpret_cast<const char*>(::sqlite3_column_text(wrapper_file.stmt, get_column(2)));
2919 : :
2920 [ + - ]: 59 : if (action_ptr != nullptr) {
2921 : 59 : action_str = std::string_view(action_ptr,
2922 [ + - ]: 59 : static_cast<size_t>(::sqlite3_column_bytes(wrapper_file.stmt, get_column(2))));
2923 : : }
2924 : :
2925 [ + - ]: 59 : data = static_cast<const uint8_t*>(::sqlite3_column_blob(wrapper_file.stmt, get_column(3)));
2926 [ + - ]: 59 : size = ::sqlite3_column_bytes(wrapper_file.stmt, get_column(3));
2927 : :
2928 [ + + + + : 59 : if VUNLIKELY (impl_->enable_compress && Bytes::is_compress_data(data, size)) {
+ + ]
2929 : 1 : decompressed_data = Bytes::uncompress_data(data, size, false);
2930 : :
2931 : 1 : data = decompressed_data.data();
2932 : 1 : size = decompressed_data.size();
2933 : : }
2934 : :
2935 : 59 : elapsed =
2936 : 59 : (timestamp / impl_->rate.load(std::memory_order_relaxed)) -
2937 : 59 : (impl_->elapsed_timer.get() - impl_->pause_elapsed.load(std::memory_order_relaxed)) -
2938 : 118 : (impl_->begin_time.load(std::memory_order_relaxed) * 1000U / impl_->rate.load(std::memory_order_relaxed));
2939 : :
2940 : 59 : impl_->extra_elapsed.store(impl_->offset_timer.get(), std::memory_order_relaxed);
2941 : :
2942 : : {
2943 [ + - ]: 59 : std::unique_lock lock(impl_->mtx);
2944 : :
2945 [ + + ]: 59 : if (config.force_delay > 0) {
2946 [ + - ]: 35 : impl_->cv.wait_for(lock, std::chrono::milliseconds(config.force_delay), [this]() -> bool {
2947 [ + - ]: 140 : return impl_->stop_flag.load(std::memory_order_relaxed) ||
2948 [ + + ]: 138 : impl_->pause_next_flag.load(std::memory_order_relaxed) ||
2949 [ + + - + ]: 209 : impl_->jump_flag.load(std::memory_order_relaxed) || is_ready_to_quit();
2950 : : });
2951 [ + + + - ]: 24 : } else if (config.force_delay < 0 && elapsed > 0) {
2952 : 1 : impl_->offset_timer.restart();
2953 : :
2954 [ + - ]: 1 : impl_->cv.wait_for(lock, std::chrono::microseconds(elapsed), [this]() -> bool {
2955 [ + - ]: 4 : return impl_->stop_flag.load(std::memory_order_relaxed) ||
2956 [ + - ]: 4 : impl_->pause_next_flag.load(std::memory_order_relaxed) ||
2957 [ + - ]: 4 : impl_->jump_flag.load(std::memory_order_relaxed) ||
2958 [ + - - + ]: 6 : impl_->pause_flag.load(std::memory_order_relaxed) || is_ready_to_quit();
2959 : : });
2960 : :
2961 [ - + ]: 1 : if VUNLIKELY (impl_->pause_flag.load(std::memory_order_relaxed)) {
2962 : : impl_->offset_elapsed.store(elapsed - impl_->offset_timer.get(), // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2963 : : std::memory_order_relaxed); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2964 : : }
2965 : : }
2966 : 59 : }
2967 : :
2968 [ + + + + : 114 : if (impl_->stop_flag.load(std::memory_order_relaxed) || impl_->jump_flag.load(std::memory_order_relaxed) ||
+ + ]
2969 [ + - - + ]: 55 : is_ready_to_quit()) {
2970 : 4 : is_interrupted = true;
2971 : 4 : break;
2972 [ + + ]: 55 : } else if (impl_->pause_flag.load(std::memory_order_relaxed)) {
2973 [ + - ]: 3 : do_pause();
2974 : 3 : impl_->pause_next_flag.store(false, std::memory_order_relaxed);
2975 : :
2976 [ + - + - : 6 : if (impl_->stop_flag.load(std::memory_order_relaxed) || impl_->jump_flag.load(std::memory_order_relaxed) ||
- + ]
2977 [ + - - + ]: 3 : is_ready_to_quit()) {
2978 : : is_interrupted = true; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2979 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2980 : : }
2981 : : }
2982 : :
2983 : : {
2984 [ + - ]: 55 : std::lock_guard time_lock(impl_->time_mtx);
2985 : 55 : impl_->real_timer.restart();
2986 : 55 : impl_->real_elapsed.store(timestamp, std::memory_order_relaxed);
2987 : 55 : }
2988 : :
2989 [ - + ]: 55 : if (is_end) {
2990 : : break; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
2991 : : }
2992 : :
2993 : 55 : Frame frame;
2994 : 55 : frame.timestamp = timestamp;
2995 [ + - ]: 55 : frame.url = url;
2996 [ + - ]: 55 : frame.action_type = convert_action(action_str);
2997 : 55 : frame.data = Bytes::shallow_copy(data, size);
2998 : :
2999 [ + - ]: 55 : BagReader::process_output(frame);
3000 : 55 : }
3001 : :
3002 [ + + + - ]: 21 : if (is_interrupted || is_end) {
3003 : : break;
3004 : : }
3005 : : }
3006 : :
3007 [ + + ]: 21 : if (is_interrupted) {
3008 [ + + ]: 4 : if (impl_->stop_flag.load(std::memory_order_relaxed)) {
3009 [ + - ]: 2 : update_status(kStopped);
3010 : : }
3011 : :
3012 : 4 : break;
3013 : : }
3014 : :
3015 [ + + ]: 17 : if (!impl_->jump_flag.load(std::memory_order_relaxed)) {
3016 [ + - ]: 16 : update_status(kStopped);
3017 : :
3018 [ + + ]: 16 : if (config.skip_blank) {
3019 : 1 : impl_->begin_time.store(std::max(config.begin_time, impl_->info.blank_duration), std::memory_order_relaxed);
3020 : : } else {
3021 : 15 : impl_->begin_time.store(config.begin_time, std::memory_order_relaxed);
3022 : : }
3023 : : }
3024 : :
3025 [ + + + + : 32 : if (impl_->stop_flag.load(std::memory_order_relaxed) || impl_->jump_flag.load(std::memory_order_relaxed) ||
+ + ]
3026 [ + - - + ]: 15 : is_ready_to_quit()) {
3027 : 2 : is_interrupted = true;
3028 : 2 : break;
3029 : : }
3030 : :
3031 [ + - ]: 15 : flush_plugin();
3032 [ + - + + : 66 : } while (impl_->times.load(std::memory_order_relaxed) <= 0 ||
- + + ]
3033 [ + - ]: 30 : (impl_->times.load(std::memory_order_relaxed) > 0 &&
3034 [ + + ]: 30 : ++loop_times < impl_->times.load(std::memory_order_relaxed)));
3035 : :
3036 [ + + ]: 22 : if (impl_->stop_flag.load(std::memory_order_relaxed)) {
3037 : 4 : is_interrupted = true;
3038 : : }
3039 : :
3040 [ + + + + : 22 : if (!impl_->jump_flag.load(std::memory_order_relaxed) && impl_->finish_callback) {
+ + ]
3041 : 16 : impl_->finish_callback(is_interrupted);
3042 : : }
3043 : :
3044 [ + + + + : 22 : if (!impl_->jump_flag.load(std::memory_order_relaxed) && config.auto_quit) {
+ + ]
3045 : 1 : quit();
3046 : : }
3047 : : #else
3048 : : (void)config;
3049 : : #endif
3050 : : }
3051 : :
3052 : : } // namespace vlink
|