|
|
|
|
@ -239,6 +239,7 @@ static MessageRow readMessage(sqlite3_stmt* stmt, quint64 myId) {
|
|
|
|
|
m.id = sqlite3_column_int64(stmt, 0); m.authorNodeId = (quint64)sqlite3_column_int64(stmt, 1); |
|
|
|
|
const auto json = QJsonDocument::fromJson(DbManager::colBlob(stmt, 2)).object(); |
|
|
|
|
m.contentType = json.value("ct").toString("text/plain"); m.data = json.value("d").toString().toUtf8(); |
|
|
|
|
if (m.contentType.isEmpty()) m.contentType = "text/plain"; |
|
|
|
|
const QByteArray reply = json.value("reply_to").toString().toLatin1(); |
|
|
|
|
if (!reply.isEmpty()) { |
|
|
|
|
m.replyTo = QByteArray::fromHex(reply); |
|
|
|
|
@ -255,21 +256,30 @@ static MessageRow readMessage(sqlite3_stmt* stmt, quint64 myId) {
|
|
|
|
|
/* Окно вокруг оригинала выбирается одним SQL snapshot, включая одинаковые timestamp. */ |
|
|
|
|
QList<MessageRow> DbManager::getMessages(const QString& chId, int limit, const QByteArray& around) const { |
|
|
|
|
QList<MessageRow> list; |
|
|
|
|
if (!around.isEmpty() && (around.size() != 32 || limit <= 0)) { |
|
|
|
|
GUI_ERROR("history: invalid anchor/window bytes=%d limit=%d", int(around.size()), limit); return list; |
|
|
|
|
} |
|
|
|
|
QString sql = QStringLiteral( |
|
|
|
|
"SELECT id, node_id, data, timestamp, chain_hash, author_signature, local_attrs, message_id" |
|
|
|
|
" FROM \"%1\" ORDER BY timestamp DESC, author_signature DESC LIMIT ?1 OFFSET " |
|
|
|
|
"max(0,(SELECT count(*) FROM \"%1\" n JOIN \"%1\" a ON a.message_id=?2 " |
|
|
|
|
"WHERE (n.timestamp,n.author_signature)>(a.timestamp,a.author_signature)) - ?1/2)").arg(msgTableName(chId)); |
|
|
|
|
" FROM \"%1\" ORDER BY timestamp DESC, author_signature DESC LIMIT ?1").arg(msgTableName(chId)); |
|
|
|
|
if (!around.isEmpty()) sql += QStringLiteral( |
|
|
|
|
" OFFSET min(max(0,(SELECT count(*) FROM \"%1\" n JOIN \"%1\" a ON a.message_id=?2 " |
|
|
|
|
"WHERE (n.timestamp,n.author_signature)>(a.timestamp,a.author_signature)) - ?1/2)," |
|
|
|
|
"max(0,(SELECT count(*) FROM \"%1\")-?1))").arg(msgTableName(chId)); |
|
|
|
|
sqlite3_stmt* stmt = prepareOrNull(sql.toUtf8().constData()); |
|
|
|
|
if (!stmt) return list; |
|
|
|
|
sqlite3_bind_int(stmt, 1, limit); |
|
|
|
|
sqlite3_bind_blob(stmt, 2, around.constData(), around.size(), SQLITE_TRANSIENT); |
|
|
|
|
if (!around.isEmpty()) sqlite3_bind_blob(stmt, 2, around.constData(), around.size(), SQLITE_TRANSIENT); |
|
|
|
|
uint64_t myId = 0; |
|
|
|
|
{ sqlite3_stmt* ms = prepareOrNull("SELECT node_id FROM local_identity WHERE id=1"); |
|
|
|
|
if (ms) { if (sqlite3_step(ms) == SQLITE_ROW) myId = (uint64_t)sqlite3_column_int64(ms, 0); sqlite3_finalize(ms); } } |
|
|
|
|
while (sqlite3_step(stmt) == SQLITE_ROW) { |
|
|
|
|
int rc; |
|
|
|
|
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { |
|
|
|
|
list.prepend(readMessage(stmt, myId)); |
|
|
|
|
} |
|
|
|
|
if (rc != SQLITE_DONE) GUI_ERROR("history: read failed ch=%s rc=%d", qPrintable(chId), rc); |
|
|
|
|
if (!around.isEmpty()) GUI_DEBUG("history: window loaded ch=%s anchor=%s rows=%d limit=%d", |
|
|
|
|
qPrintable(chId), around.toHex().constData(), int(list.size()), limit); |
|
|
|
|
sqlite3_finalize(stmt); |
|
|
|
|
return list; |
|
|
|
|
} |
|
|
|
|
|