1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
/*
Copyright (C) 2019-2024 Selwin van Dijk
This file is part of signalbackup-tools.
signalbackup-tools is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
signalbackup-tools is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with signalbackup-tools. If not, see <https://www.gnu.org/licenses/>.
*/
#include "signalbackup.ih"
void SignalBackup::makeIdsUnique(SignalBackup *source)
{
Logger::message(__FUNCTION__);
Logger::message(" Adjusting indexes in tables...");
for (auto const &dbl : s_databaselinks)
{
// skip if table/column does not exist, or if skip is set
if ((dbl.flags & SKIP) ||
!d_database.containsTable(dbl.table) ||
!source->d_database.containsTable(dbl.table) ||
!source->d_database.tableContainsColumn(dbl.table, dbl.column))
continue;
{
SqliteDB::QueryResults results;
source->d_database.exec("SELECT * FROM " + dbl.table, &results);
if (results.rows() == 0)
continue;
else if (dbl.flags & WARN)
Logger::warning("Found entries in a usually empty table. Trying to deal with it, but problems may occur.");
}
long long int offsetvalue = getMaxUsedId(dbl.table, dbl.column) + 1 - source->getMinUsedId(dbl.table, dbl.column);
source->setMinimumId(dbl.table, offsetvalue, dbl.column);
for (auto const &c : dbl.connections)
{
if (source->d_databaseversion >= c.mindbvversion && source->d_databaseversion <= c.maxdbvversion)
{
if (!source->d_database.containsTable(c.table) || !source->d_database.tableContainsColumn(c.table, c.column))
continue;
if (!c.json_path.empty())
{
source->d_database.exec("UPDATE " + c.table + " SET " + c.column +
" = json_replace(" + c.column + ", " + c.json_path + ", json_extract(" + c.column + ", " + c.json_path + ") + ?) "
"WHERE json_extract(" + c.column + ", " + c.json_path + ") IS NOT NULL", offsetvalue);
}
else if ((c.flags & SET_UNIQUELY))
{
// set all values negative
source->d_database.exec("UPDATE " + c.table + " SET " + c.column + " = " + c.column + " * -1" +
(c.whereclause.empty() ? "" : " WHERE " + c.whereclause));
// set to wanted value
source->d_database.exec("UPDATE " + c.table + " SET " + c.column + " = " + c.column + " * -1 + ?"
+ (c.whereclause.empty() ? "" : " WHERE " + c.whereclause), offsetvalue);
}
else
source->d_database.exec("UPDATE " + c.table + " SET " + c.column + " = " + c.column + " + ? "
+ (c.whereclause.empty() ? "" : " WHERE " + c.whereclause), offsetvalue);
int count = source->d_database.changed();
if (count)
Logger::message(" Adjusted '", c.table, ".", c.column, "' to match changes in '", dbl.table, "' : ", count);
}
}
if (dbl.table == d_part_table)
{
// update rowid's in d_attachments
std::map<std::pair<uint64_t, int64_t>, DeepCopyingUniquePtr<AttachmentFrame>> newattdb;
for (auto &att : source->d_attachments)
{
AttachmentFrame *af = reinterpret_cast<AttachmentFrame *>(att.second.release());
af->setRowId(af->rowId() + offsetvalue);
int64_t attachmentid = af->attachmentId();
newattdb.emplace(std::make_pair(af->rowId(), attachmentid ? attachmentid : -1), af);
}
source->d_attachments.clear();
source->d_attachments = std::move(newattdb);
}
else if (dbl.table == "sticker")
{
// update id's in d_stickers
std::map<uint64_t, DeepCopyingUniquePtr<StickerFrame>> newsdb;
for (auto &s : source->d_stickers)
{
StickerFrame *sf = reinterpret_cast<StickerFrame *>(s.second.release());
sf->setRowId(sf->rowId() + offsetvalue);
newsdb.emplace(std::make_pair(sf->rowId(), sf));
}
source->d_stickers.clear();
source->d_stickers = std::move(newsdb);
}
else if (dbl.table == "recipient")
{
source->updateGroupMembers(offsetvalue);
// in groups, during the v1 -> v2 update, members may have been removed from the group, these messages
// are of type "GV1_MIGRATION_TYPE" and have a body that looks like '_id,_id,...|_id,_id,_id,...' (I think, I have
// not seen one with more than 1 id). These id_s must also be updated.
source->updateGV1MigrationMessage(offsetvalue);
//update (old-style)reaction authors
source->updateReactionAuthors(offsetvalue);
source->updateAvatars(offsetvalue);
source->updateSnippetExtrasRecipient(offsetvalue);
}
// compact table if requested
if (!(dbl.flags & NO_COMPACT))
source->compactIds(dbl.table, dbl.column);
}
/*
CHECK!
These are the tables that are imported by importThread(),
check if they are all handled properly
*/
// get tables
std::string q("SELECT sql, name, type FROM sqlite_master");
SqliteDB::QueryResults results;
source->d_database.exec(q, &results);
std::vector<std::string> tables;
for (unsigned int i = 0; i < results.rows(); ++i)
{
if (!results.isNull(i, 0))
{
//Logger::message("Dealing with: ", results.getValueAs<std::string>(i, 1));
if (results.valueHasType<std::string>(i, 1) &&
(results.getValueAs<std::string>(i, 1) != "sms_fts" &&
STRING_STARTS_WITH(results.getValueAs<std::string>(i, 1), "sms_fts")))
;//Logger::message("Skipping ", results[i][1].second, " because it is sms_ftssecrettable");
else if (results.valueHasType<std::string>(i, 1) &&
(results.getValueAs<std::string>(i, 1) != d_mms_table + "_fts" &&
STRING_STARTS_WITH(results.getValueAs<std::string>(i, 1), d_mms_table + "_fts")))
;//Logger::message("Skipping ", results[i][1].second, " because it is mms_ftssecrettable");
else if (results.valueHasType<std::string>(i, 1) &&
(results.getValueAs<std::string>(i, 1) != "emoji_search" &&
STRING_STARTS_WITH(results.getValueAs<std::string>(i, 1), "emoji_search")))
;//Logger::message("Skipping ", results.getValueAs<std::string>(i, 1), " because it is emoji_search_ftssecrettable");
else if (results.valueHasType<std::string>(i, 1) &&
STRING_STARTS_WITH(results.getValueAs<std::string>(i, 1), "sqlite_"))
;
else
if (results.valueHasType<std::string>(i, 2) && results.getValueAs<std::string>(i, 2) == "table")
tables.emplace_back(results.getValueAs<std::string>(i, 1));
}
}
for (std::string const &table : tables)
{
if (table == "signed_prekeys" ||
table == "one_time_prekeys" ||
table == "sessions" ||
//table == "job_spec" || // this is in the official export. But it makes testing more difficult. it
//table == "constraint_spec" || // should be ok to export these (if present in source), since we are only
//table == "dependency_spec" || // dealing with exported backups (not from live installations) -> they should
//table == "emoji_search" || // have been excluded + the official import should be able to deal with them
STRING_STARTS_WITH(table, "sms_fts") ||
STRING_STARTS_WITH(table, d_mms_table + "_fts") ||
STRING_STARTS_WITH(table, "sqlite_"))
continue;
if (std::find_if(s_databaselinks.begin(), s_databaselinks.end(), [table](DatabaseLink const &d){ return d.table == table; }) == s_databaselinks.end())
Logger::warning("Found table unhandled by ", __FUNCTION__ , " : ", table);
}
}
|