Distinguish failed avatar reads from committed empty blobs

This commit is contained in:
evgeny committed 2026-10-27 11:57:30 +01:00
1 parent 95e88ac66f
commit 905d30ceaf
2 files changed
+35 -8

No files matched your search

+31 -7
View File
@@ -10,6 +10,7 @@
#include <QJsonObject>
#include <QCoreApplication>
#include "chat/chat_poll.h"
#include "chat/group_avatar.h"
/* ── helpers ── */
@@ -119,14 +120,37 @@ QList<ChannelRow> DbManager::getChannels(ChannelSortMode sort) const {
return list;
}
QByteArray DbManager::getChannelAvatar(const QString& channelId) const {
sqlite3_stmt* st = prepareOrNull("SELECT avatar_data FROM channels WHERE channel_id=?"); if (!st) return {};
const QByteArray id = channelId.toLatin1(); sqlite3_bind_text(st, 1, id.constData(), id.size(), SQLITE_TRANSIENT);
QByteArray DbManager::getChannelAvatar(const QString& channelId, bool* ok) const {
if (ok) *ok = false;
if (!m_db) { GUI_WARN("group avatar: database unavailable channel=%s", qPrintable(channelId)); return {}; }
sqlite3_stmt* st = nullptr;
const char* stage = "prepare";
int rc = sqlite3_prepare_v2(m_db, "SELECT avatar_data FROM channels WHERE channel_id=?", -1, &st, nullptr);
const QByteArray id = channelId.toLatin1();
if (rc == SQLITE_OK) { stage = "bind"; rc = sqlite3_bind_text(st, 1, id.constData(), id.size(), SQLITE_TRANSIENT); }
if (rc == SQLITE_OK) { stage = "step"; rc = sqlite3_step(st); }
QByteArray bytes;
int rc = sqlite3_step(st);
if (rc == SQLITE_ROW) bytes = colBlob(st, 0);
else if (rc != SQLITE_DONE) GUI_ERROR("group avatar: database read failed channel=%s rc=%d", qPrintable(channelId), rc);
sqlite3_finalize(st); return bytes;
int type = -1, length = 0;
if (rc == SQLITE_ROW) {
stage = "blob"; type = sqlite3_column_type(st, 0); length = sqlite3_column_bytes(st, 0);
if (type != SQLITE_BLOB) rc = SQLITE_CORRUPT;
else if (length > GROUP_AVATAR_MAX) rc = SQLITE_TOOBIG;
else {
const char* blob = static_cast<const char*>(sqlite3_column_blob(st, 0));
if (length && !blob) rc = SQLITE_NOMEM;
else bytes = QByteArray(blob, length);
}
}
const int final = sqlite3_finalize(st);
if ((rc != SQLITE_ROW && rc != SQLITE_DONE) || final != SQLITE_OK) {
GUI_ERROR("group avatar: read channel=%s stage=%s rc=%d finalize=%d type=%d bytes=%d: %s",
qPrintable(channelId), stage, rc, final, type, length, sqlite3_errmsg(m_db)); return {};
}
if (rc == SQLITE_DONE) {
GUI_DEBUG("group avatar: channel disappeared during read channel=%s", qPrintable(channelId)); return {};
}
if (ok) *ok = true;
return bytes;
}
bool DbManager::isChannelMember(const QString& chId, quint64 nodeId) const {
+4 -1
View File
@@ -101,7 +101,10 @@ public:
/* ── Каналы ── */
QList<ChannelRow> getChannels(ChannelSortMode sort = ChannelSortMode::Alphabetical) const;
QByteArray getChannelAvatar(const QString& channelId) const;
/* Читает BLOB аватара канала <=16 KiB. Возвращает самостоятельные байты; при ошибке/нет строки — пустой массив.
* Необязательный ok отличает успешный пустой BLOB (удаление) от отказа; bind/step/finalize проверяются.
* Хеш/версию и формат JPEG проверяет GroupAvatarManager, запись не меняется. */
QByteArray getChannelAvatar(const QString& channelId, bool* ok = nullptr) const;
QString getChannelLastMessage(const QString& channelId) const;
int getUnreadCount(const QString& channelId, qint64 lastReadMsgId) const;