1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/telegram/TdDb.h"
8
9 #include "td/telegram/DialogDb.h"
10 #include "td/telegram/files/FileDb.h"
11 #include "td/telegram/Global.h"
12 #include "td/telegram/logevent/LogEvent.h"
13 #include "td/telegram/MessagesDb.h"
14 #include "td/telegram/Td.h"
15 #include "td/telegram/TdParameters.h"
16 #include "td/telegram/Version.h"
17
18 #include "td/db/binlog/Binlog.h"
19 #include "td/db/binlog/ConcurrentBinlog.h"
20 #include "td/db/BinlogKeyValue.h"
21 #include "td/db/SqliteConnectionSafe.h"
22 #include "td/db/SqliteDb.h"
23 #include "td/db/SqliteKeyValue.h"
24 #include "td/db/SqliteKeyValueAsync.h"
25 #include "td/db/SqliteKeyValueSafe.h"
26
27 #include "td/actor/MultiPromise.h"
28
29 #include "td/utils/common.h"
30 #include "td/utils/format.h"
31 #include "td/utils/logging.h"
32 #include "td/utils/misc.h"
33 #include "td/utils/port/path.h"
34 #include "td/utils/Random.h"
35 #include "td/utils/SliceBuilder.h"
36 #include "td/utils/StringBuilder.h"
37
38 #include <algorithm>
39
40 namespace td {
41
42 namespace {
43
get_binlog_path(const TdParameters & parameters)44 std::string get_binlog_path(const TdParameters ¶meters) {
45 return PSTRING() << parameters.database_directory << "td" << (parameters.use_test_dc ? "_test" : "") << ".binlog";
46 }
47
get_sqlite_path(const TdParameters & parameters)48 std::string get_sqlite_path(const TdParameters ¶meters) {
49 const string db_name = "db" + (parameters.use_test_dc ? string("_test") : string());
50 return parameters.database_directory + db_name + ".sqlite";
51 }
52
check_encryption(string path)53 Result<TdDb::EncryptionInfo> check_encryption(string path) {
54 Binlog binlog;
55 auto status = binlog.init(std::move(path), Binlog::Callback());
56 if (status.is_error() && status.code() != Binlog::Error::WrongPassword) {
57 LOG(WARNING) << "Failed to check binlog: " << status;
58 return Status::Error(400, status.message());
59 }
60 TdDb::EncryptionInfo info;
61 info.is_encrypted = binlog.get_info().wrong_password;
62 binlog.close(false /*need_sync*/).ensure();
63 return info;
64 }
65
init_binlog(Binlog & binlog,string path,BinlogKeyValue<Binlog> & binlog_pmc,BinlogKeyValue<Binlog> & config_pmc,TdDb::Events & events,DbKey key)66 Status init_binlog(Binlog &binlog, string path, BinlogKeyValue<Binlog> &binlog_pmc, BinlogKeyValue<Binlog> &config_pmc,
67 TdDb::Events &events, DbKey key) {
68 auto callback = [&](const BinlogEvent &event) {
69 switch (event.type_) {
70 case LogEvent::HandlerType::SecretChats:
71 events.to_secret_chats_manager.push_back(event.clone());
72 break;
73 case LogEvent::HandlerType::Users:
74 events.user_events.push_back(event.clone());
75 break;
76 case LogEvent::HandlerType::Chats:
77 events.chat_events.push_back(event.clone());
78 break;
79 case LogEvent::HandlerType::Channels:
80 events.channel_events.push_back(event.clone());
81 break;
82 case LogEvent::HandlerType::SecretChatInfos:
83 events.secret_chat_events.push_back(event.clone());
84 break;
85 case LogEvent::HandlerType::WebPages:
86 events.web_page_events.push_back(event.clone());
87 break;
88 case LogEvent::HandlerType::SetPollAnswer:
89 case LogEvent::HandlerType::StopPoll:
90 events.to_poll_manager.push_back(event.clone());
91 break;
92 case LogEvent::HandlerType::SendMessage:
93 case LogEvent::HandlerType::DeleteMessage:
94 case LogEvent::HandlerType::DeleteMessagesOnServer:
95 case LogEvent::HandlerType::ReadHistoryOnServer:
96 case LogEvent::HandlerType::ReadMessageContentsOnServer:
97 case LogEvent::HandlerType::ForwardMessages:
98 case LogEvent::HandlerType::SendBotStartMessage:
99 case LogEvent::HandlerType::SendScreenshotTakenNotificationMessage:
100 case LogEvent::HandlerType::SendInlineQueryResultMessage:
101 case LogEvent::HandlerType::DeleteDialogHistoryOnServer:
102 case LogEvent::HandlerType::ReadAllDialogMentionsOnServer:
103 case LogEvent::HandlerType::DeleteAllChannelMessagesFromSenderOnServer:
104 case LogEvent::HandlerType::ToggleDialogIsPinnedOnServer:
105 case LogEvent::HandlerType::ReorderPinnedDialogsOnServer:
106 case LogEvent::HandlerType::SaveDialogDraftMessageOnServer:
107 case LogEvent::HandlerType::UpdateDialogNotificationSettingsOnServer:
108 case LogEvent::HandlerType::UpdateScopeNotificationSettingsOnServer:
109 case LogEvent::HandlerType::ResetAllNotificationSettingsOnServer:
110 case LogEvent::HandlerType::ToggleDialogReportSpamStateOnServer:
111 case LogEvent::HandlerType::RegetDialog:
112 case LogEvent::HandlerType::GetChannelDifference:
113 case LogEvent::HandlerType::ReadHistoryInSecretChat:
114 case LogEvent::HandlerType::ToggleDialogIsMarkedAsUnreadOnServer:
115 case LogEvent::HandlerType::SetDialogFolderIdOnServer:
116 case LogEvent::HandlerType::DeleteScheduledMessagesOnServer:
117 case LogEvent::HandlerType::ToggleDialogIsBlockedOnServer:
118 case LogEvent::HandlerType::ReadMessageThreadHistoryOnServer:
119 case LogEvent::HandlerType::BlockMessageSenderFromRepliesOnServer:
120 case LogEvent::HandlerType::UnpinAllDialogMessagesOnServer:
121 case LogEvent::HandlerType::DeleteAllCallMessagesOnServer:
122 case LogEvent::HandlerType::DeleteDialogMessagesByDateOnServer:
123 events.to_messages_manager.push_back(event.clone());
124 break;
125 case LogEvent::HandlerType::AddMessagePushNotification:
126 case LogEvent::HandlerType::EditMessagePushNotification:
127 events.to_notification_manager.push_back(event.clone());
128 break;
129 case LogEvent::HandlerType::BinlogPmcMagic:
130 binlog_pmc.external_init_handle(event);
131 break;
132 case LogEvent::HandlerType::ConfigPmcMagic:
133 config_pmc.external_init_handle(event);
134 break;
135 default:
136 LOG(FATAL) << "Unsupported log event type " << event.type_;
137 }
138 };
139
140 auto binlog_info = binlog.init(std::move(path), callback, std::move(key));
141 if (binlog_info.is_error()) {
142 return binlog_info.move_as_error();
143 }
144 return Status::OK();
145 }
146
147 } // namespace
148
get_file_db_shared()149 std::shared_ptr<FileDbInterface> TdDb::get_file_db_shared() {
150 return file_db_;
151 }
152
get_sqlite_connection_safe()153 std::shared_ptr<SqliteConnectionSafe> &TdDb::get_sqlite_connection_safe() {
154 return sql_connection_;
155 }
156
get_binlog_impl(const char * file,int line)157 BinlogInterface *TdDb::get_binlog_impl(const char *file, int line) {
158 LOG_CHECK(binlog_) << G()->close_flag() << " " << file << " " << line;
159 return binlog_.get();
160 }
161
get_binlog_pmc_shared()162 std::shared_ptr<KeyValueSyncInterface> TdDb::get_binlog_pmc_shared() {
163 CHECK(binlog_pmc_);
164 return binlog_pmc_;
165 }
166
get_config_pmc_shared()167 std::shared_ptr<KeyValueSyncInterface> TdDb::get_config_pmc_shared() {
168 CHECK(config_pmc_);
169 return config_pmc_;
170 }
171
get_binlog_pmc()172 KeyValueSyncInterface *TdDb::get_binlog_pmc() {
173 CHECK(binlog_pmc_);
174 return binlog_pmc_.get();
175 }
176
get_config_pmc()177 KeyValueSyncInterface *TdDb::get_config_pmc() {
178 CHECK(config_pmc_);
179 return config_pmc_.get();
180 }
181
get_sqlite_sync_pmc()182 SqliteKeyValue *TdDb::get_sqlite_sync_pmc() {
183 CHECK(common_kv_safe_);
184 return &common_kv_safe_->get();
185 }
186
get_sqlite_pmc()187 SqliteKeyValueAsyncInterface *TdDb::get_sqlite_pmc() {
188 CHECK(common_kv_async_);
189 return common_kv_async_.get();
190 }
191
get_messages_db_sync()192 MessagesDbSyncInterface *TdDb::get_messages_db_sync() {
193 return &messages_db_sync_safe_->get();
194 }
get_messages_db_async()195 MessagesDbAsyncInterface *TdDb::get_messages_db_async() {
196 return messages_db_async_.get();
197 }
get_dialog_db_sync()198 DialogDbSyncInterface *TdDb::get_dialog_db_sync() {
199 return &dialog_db_sync_safe_->get();
200 }
get_dialog_db_async()201 DialogDbAsyncInterface *TdDb::get_dialog_db_async() {
202 return dialog_db_async_.get();
203 }
204
binlog_path() const205 CSlice TdDb::binlog_path() const {
206 return binlog_->get_path();
207 }
sqlite_path() const208 CSlice TdDb::sqlite_path() const {
209 return sqlite_path_;
210 }
211
flush_all()212 void TdDb::flush_all() {
213 LOG(INFO) << "Flush all databases";
214 if (messages_db_async_) {
215 messages_db_async_->force_flush();
216 }
217 binlog_->force_flush();
218 }
219
close_all(Promise<> on_finished)220 void TdDb::close_all(Promise<> on_finished) {
221 LOG(INFO) << "Close all databases";
222 do_close(std::move(on_finished), false /*destroy_flag*/);
223 }
224
close_and_destroy_all(Promise<> on_finished)225 void TdDb::close_and_destroy_all(Promise<> on_finished) {
226 LOG(INFO) << "Destroy all databases";
227 do_close(std::move(on_finished), true /*destroy_flag*/);
228 }
229
do_close(Promise<> on_finished,bool destroy_flag)230 void TdDb::do_close(Promise<> on_finished, bool destroy_flag) {
231 MultiPromiseActorSafe mpas{"TdDbCloseMultiPromiseActor"};
232 mpas.add_promise(PromiseCreator::lambda(
233 [promise = std::move(on_finished), sql_connection = std::move(sql_connection_), destroy_flag](Unit) mutable {
234 if (sql_connection) {
235 LOG_CHECK(sql_connection.unique()) << sql_connection.use_count();
236 if (destroy_flag) {
237 sql_connection->close_and_destroy();
238 } else {
239 sql_connection->close();
240 }
241 sql_connection.reset();
242 }
243 promise.set_value(Unit());
244 }));
245 auto lock = mpas.get_promise();
246
247 if (file_db_) {
248 file_db_->close(mpas.get_promise());
249 file_db_.reset();
250 }
251
252 common_kv_safe_.reset();
253 if (common_kv_async_) {
254 common_kv_async_->close(mpas.get_promise());
255 }
256
257 messages_db_sync_safe_.reset();
258 if (messages_db_async_) {
259 messages_db_async_->close(mpas.get_promise());
260 }
261
262 dialog_db_sync_safe_.reset();
263 if (dialog_db_async_) {
264 dialog_db_async_->close(mpas.get_promise());
265 }
266
267 // binlog_pmc is dependent on binlog_ and anyway it doesn't support close_and_destroy
268 CHECK(binlog_pmc_.unique());
269 binlog_pmc_.reset();
270 CHECK(config_pmc_.unique());
271 config_pmc_.reset();
272
273 if (binlog_) {
274 if (destroy_flag) {
275 binlog_->close_and_destroy(mpas.get_promise());
276 } else {
277 binlog_->close(mpas.get_promise());
278 }
279 binlog_.reset();
280 }
281
282 lock.set_value(Unit());
283 }
284
init_sqlite(int32 scheduler_id,const TdParameters & parameters,const DbKey & key,const DbKey & old_key,BinlogKeyValue<Binlog> & binlog_pmc)285 Status TdDb::init_sqlite(int32 scheduler_id, const TdParameters ¶meters, const DbKey &key, const DbKey &old_key,
286 BinlogKeyValue<Binlog> &binlog_pmc) {
287 CHECK(!parameters.use_message_db || parameters.use_chat_info_db);
288 CHECK(!parameters.use_chat_info_db || parameters.use_file_db);
289
290 const string sql_database_path = get_sqlite_path(parameters);
291
292 bool use_sqlite = parameters.use_file_db;
293 bool use_file_db = parameters.use_file_db;
294 bool use_dialog_db = parameters.use_message_db;
295 bool use_message_db = parameters.use_message_db;
296 if (!use_sqlite) {
297 unlink(sql_database_path).ignore();
298 return Status::OK();
299 }
300
301 sqlite_path_ = sql_database_path;
302 TRY_RESULT(db_instance, SqliteDb::change_key(sqlite_path_, true, key, old_key));
303 sql_connection_ = std::make_shared<SqliteConnectionSafe>(sql_database_path, key, db_instance.get_cipher_version());
304 sql_connection_->set(std::move(db_instance));
305 auto &db = sql_connection_->get();
306 TRY_STATUS(db.exec("PRAGMA journal_mode=WAL"));
307 TRY_STATUS(db.exec("PRAGMA secure_delete=1"));
308
309 // Init databases
310 // Do initialization once and before everything else to avoid "database is locked" error.
311 // Must be in a transaction
312
313 // NB: when database is dropped we should also drop corresponding binlog events
314 TRY_STATUS(db.exec("BEGIN TRANSACTION"));
315
316 // Get 'PRAGMA user_version'
317 TRY_RESULT(user_version, db.user_version());
318 LOG(INFO) << "Got PRAGMA user_version = " << user_version;
319
320 // init DialogDb
321 bool dialog_db_was_created = false;
322 if (use_dialog_db) {
323 TRY_STATUS(init_dialog_db(db, user_version, binlog_pmc, dialog_db_was_created));
324 } else {
325 TRY_STATUS(drop_dialog_db(db, user_version));
326 }
327
328 // init MessagesDb
329 if (use_message_db) {
330 TRY_STATUS(init_messages_db(db, user_version));
331 } else {
332 TRY_STATUS(drop_messages_db(db, user_version));
333 }
334
335 // init filesDb
336 if (use_file_db) {
337 TRY_STATUS(init_file_db(db, user_version));
338 } else {
339 TRY_STATUS(drop_file_db(db, user_version));
340 }
341
342 // Update 'PRAGMA user_version'
343 auto db_version = current_db_version();
344 if (db_version != user_version) {
345 LOG(WARNING) << "Set PRAGMA user_version = " << db_version;
346 TRY_STATUS(db.set_user_version(db_version));
347 }
348
349 if (dialog_db_was_created) {
350 binlog_pmc.erase_by_prefix("pinned_dialog_ids");
351 binlog_pmc.erase_by_prefix("last_server_dialog_date");
352 binlog_pmc.erase_by_prefix("unread_message_count");
353 binlog_pmc.erase_by_prefix("unread_dialog_count");
354 binlog_pmc.erase("sponsored_dialog_id");
355 binlog_pmc.erase_by_prefix("top_dialogs");
356 }
357 if (user_version == 0) {
358 binlog_pmc.erase("next_contacts_sync_date");
359 binlog_pmc.erase("saved_contact_count");
360 binlog_pmc.erase("old_featured_sticker_set_count");
361 binlog_pmc.erase("invalidate_old_featured_sticker_sets");
362 }
363 binlog_pmc.force_sync({});
364
365 TRY_STATUS(db.exec("COMMIT TRANSACTION"));
366
367 file_db_ = create_file_db(sql_connection_, scheduler_id);
368
369 common_kv_safe_ = std::make_shared<SqliteKeyValueSafe>("common", sql_connection_);
370 common_kv_async_ = create_sqlite_key_value_async(common_kv_safe_, scheduler_id);
371
372 if (use_dialog_db) {
373 dialog_db_sync_safe_ = create_dialog_db_sync(sql_connection_);
374 dialog_db_async_ = create_dialog_db_async(dialog_db_sync_safe_, scheduler_id);
375 }
376
377 if (use_message_db) {
378 messages_db_sync_safe_ = create_messages_db_sync(sql_connection_);
379 messages_db_async_ = create_messages_db_async(messages_db_sync_safe_, scheduler_id);
380 }
381
382 return Status::OK();
383 }
384
init(int32 scheduler_id,const TdParameters & parameters,DbKey key,Events & events)385 Status TdDb::init(int32 scheduler_id, const TdParameters ¶meters, DbKey key, Events &events) {
386 // Init pmc
387 Binlog *binlog_ptr = nullptr;
388 auto binlog = std::shared_ptr<Binlog>(new Binlog, [&](Binlog *ptr) { binlog_ptr = ptr; });
389
390 auto binlog_pmc = make_unique<BinlogKeyValue<Binlog>>();
391 auto config_pmc = make_unique<BinlogKeyValue<Binlog>>();
392 binlog_pmc->external_init_begin(static_cast<int32>(LogEvent::HandlerType::BinlogPmcMagic));
393 config_pmc->external_init_begin(static_cast<int32>(LogEvent::HandlerType::ConfigPmcMagic));
394
395 bool encrypt_binlog = !key.is_empty();
396 VLOG(td_init) << "Start binlog loading";
397 TRY_STATUS(init_binlog(*binlog, get_binlog_path(parameters), *binlog_pmc, *config_pmc, events, std::move(key)));
398 VLOG(td_init) << "Finish binlog loading";
399
400 binlog_pmc->external_init_finish(binlog);
401 VLOG(td_init) << "Finish initialization of binlog PMC";
402 config_pmc->external_init_finish(binlog);
403 VLOG(td_init) << "Finish initialization of config PMC";
404
405 DbKey new_sqlite_key;
406 DbKey old_sqlite_key;
407 bool encrypt_sqlite = encrypt_binlog;
408 bool drop_sqlite_key = false;
409 auto sqlite_key = binlog_pmc->get("sqlite_key");
410 if (encrypt_sqlite) {
411 if (sqlite_key.empty()) {
412 sqlite_key = string(32, ' ');
413 Random::secure_bytes(sqlite_key);
414 binlog_pmc->set("sqlite_key", sqlite_key);
415 binlog_pmc->force_sync(Auto());
416 }
417 new_sqlite_key = DbKey::raw_key(std::move(sqlite_key));
418 } else {
419 if (!sqlite_key.empty()) {
420 old_sqlite_key = DbKey::raw_key(std::move(sqlite_key));
421 drop_sqlite_key = true;
422 }
423 }
424 VLOG(td_init) << "Start to init database";
425 auto init_sqlite_status = init_sqlite(scheduler_id, parameters, new_sqlite_key, old_sqlite_key, *binlog_pmc);
426 VLOG(td_init) << "Finish to init database";
427 if (init_sqlite_status.is_error()) {
428 LOG(ERROR) << "Destroy bad SQLite database because of " << init_sqlite_status;
429 if (sql_connection_ != nullptr) {
430 sql_connection_->get().close();
431 }
432 SqliteDb::destroy(get_sqlite_path(parameters)).ignore();
433 TRY_STATUS(init_sqlite(scheduler_id, parameters, new_sqlite_key, old_sqlite_key, *binlog_pmc));
434 }
435 if (drop_sqlite_key) {
436 binlog_pmc->erase("sqlite_key");
437 binlog_pmc->force_sync(Auto());
438 }
439
440 VLOG(td_init) << "Create concurrent_binlog_pmc";
441 auto concurrent_binlog_pmc = std::make_shared<BinlogKeyValue<ConcurrentBinlog>>();
442 concurrent_binlog_pmc->external_init_begin(binlog_pmc->get_magic());
443 concurrent_binlog_pmc->external_init_handle(std::move(*binlog_pmc));
444
445 VLOG(td_init) << "Create concurrent_config_pmc";
446 auto concurrent_config_pmc = std::make_shared<BinlogKeyValue<ConcurrentBinlog>>();
447 concurrent_config_pmc->external_init_begin(config_pmc->get_magic());
448 concurrent_config_pmc->external_init_handle(std::move(*config_pmc));
449
450 binlog.reset();
451 binlog_pmc.reset();
452 config_pmc.reset();
453
454 CHECK(binlog_ptr != nullptr);
455 VLOG(td_init) << "Create concurrent_binlog";
456 auto concurrent_binlog = std::make_shared<ConcurrentBinlog>(unique_ptr<Binlog>(binlog_ptr), scheduler_id);
457
458 VLOG(td_init) << "Init concurrent_binlog_pmc";
459 concurrent_binlog_pmc->external_init_finish(concurrent_binlog);
460 VLOG(td_init) << "Init concurrent_config_pmc";
461 concurrent_config_pmc->external_init_finish(concurrent_binlog);
462
463 binlog_pmc_ = std::move(concurrent_binlog_pmc);
464 config_pmc_ = std::move(concurrent_config_pmc);
465 binlog_ = std::move(concurrent_binlog);
466
467 return Status::OK();
468 }
469
470 TdDb::TdDb() = default;
471 TdDb::~TdDb() = default;
472
open(int32 scheduler_id,const TdParameters & parameters,DbKey key,Events & events)473 Result<unique_ptr<TdDb>> TdDb::open(int32 scheduler_id, const TdParameters ¶meters, DbKey key, Events &events) {
474 auto db = make_unique<TdDb>();
475 TRY_STATUS(db->init(scheduler_id, parameters, std::move(key), events));
476 return std::move(db);
477 }
478
check_encryption(const TdParameters & parameters)479 Result<TdDb::EncryptionInfo> TdDb::check_encryption(const TdParameters ¶meters) {
480 return ::td::check_encryption(get_binlog_path(parameters));
481 }
482
change_key(DbKey key,Promise<> promise)483 void TdDb::change_key(DbKey key, Promise<> promise) {
484 get_binlog()->change_key(std::move(key), std::move(promise));
485 }
486
destroy(const TdParameters & parameters)487 Status TdDb::destroy(const TdParameters ¶meters) {
488 SqliteDb::destroy(get_sqlite_path(parameters)).ignore();
489 Binlog::destroy(get_binlog_path(parameters)).ignore();
490 return Status::OK();
491 }
492
with_db_path(const std::function<void (CSlice)> & callback)493 void TdDb::with_db_path(const std::function<void(CSlice)> &callback) {
494 SqliteDb::with_db_path(sqlite_path(), callback);
495 callback(binlog_path());
496 }
497
get_stats()498 Result<string> TdDb::get_stats() {
499 auto sb = StringBuilder({}, true);
500 auto &sql = sql_connection_->get();
501 auto run_query = [&](CSlice query, Slice desc) -> Status {
502 TRY_RESULT(stmt, sql.get_statement(query));
503 TRY_STATUS(stmt.step());
504 CHECK(stmt.has_row());
505 auto key_size = stmt.view_int64(0);
506 auto value_size = stmt.view_int64(1);
507 auto count = stmt.view_int64(2);
508 sb << query << "\n";
509 sb << desc << ":\n";
510 sb << format::as_size(key_size + value_size) << "\t";
511 sb << format::as_size(key_size) << "\t";
512 sb << format::as_size(value_size) << "\t";
513 sb << format::as_size((key_size + value_size) / (count ? count : 1)) << "\t";
514 sb << "\n";
515 return Status::OK();
516 };
517 auto run_kv_query = [&](Slice mask, Slice table = Slice("common")) {
518 return run_query(PSLICE() << "SELECT SUM(length(k)), SUM(length(v)), COUNT(*) FROM " << table << " WHERE k like '"
519 << mask << "'",
520 PSLICE() << table << ":" << mask);
521 };
522 TRY_STATUS(run_query("SELECT 0, SUM(length(data)), COUNT(*) FROM messages WHERE 1", "messages"));
523 TRY_STATUS(run_query("SELECT 0, SUM(length(data)), COUNT(*) FROM dialogs WHERE 1", "dialogs"));
524 TRY_STATUS(run_kv_query("%", "common"));
525 TRY_STATUS(run_kv_query("%", "files"));
526 TRY_STATUS(run_kv_query("wp%"));
527 TRY_STATUS(run_kv_query("wpurl%"));
528 TRY_STATUS(run_kv_query("wpiv%"));
529 TRY_STATUS(run_kv_query("us%"));
530 TRY_STATUS(run_kv_query("ch%"));
531 TRY_STATUS(run_kv_query("ss%"));
532 TRY_STATUS(run_kv_query("gr%"));
533
534 vector<int32> prev(1);
535 size_t count = 0;
536 int32 max_bad_to = 0;
537 size_t bad_count = 0;
538 file_db_->pmc().get_by_range("file0", "file:", [&](Slice key, Slice value) {
539 if (value.substr(0, 2) != "@@") {
540 return true;
541 }
542 count++;
543 auto from = to_integer<int32>(key.substr(4));
544 auto to = to_integer<int32>(value.substr(2));
545 if (from <= to) {
546 LOG(DEBUG) << "Have forward reference from " << from << " to " << to;
547 if (to > max_bad_to) {
548 max_bad_to = to;
549 }
550 bad_count++;
551 return true;
552 }
553 if (static_cast<size_t>(from) >= prev.size()) {
554 prev.resize(from + 1);
555 }
556 if (static_cast<size_t>(to) >= prev.size()) {
557 prev.resize(to + 1);
558 }
559 prev[from] = to;
560 return true;
561 });
562 for (size_t i = 1; i < prev.size(); i++) {
563 if (!prev[i]) {
564 continue;
565 }
566 prev[i] = prev[prev[i]] + 1;
567 }
568 sb << "Max file database depth out of " << prev.size() << '/' << count
569 << " elements: " << *std::max_element(prev.begin(), prev.end()) << "\n";
570 sb << "Have " << bad_count << " forward references with maximum reference to " << max_bad_to;
571
572 return sb.as_cslice().str();
573 }
574
575 } // namespace td
576