Skip to content

Commit c2aff6e

Browse files
committed
feat: MsgId::get_html: Make only one db query
Merge `message::get_mime_headers()` into `MsgId::get_html()` for that, it's unlikely that it will be used elsewhere.
1 parent 8292495 commit c2aff6e

3 files changed

Lines changed: 68 additions & 80 deletions

File tree

src/html.rs

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@
99
1010
use std::mem;
1111

12-
use anyhow::{Context as _, Result};
12+
use anyhow::{Context as _, Result, ensure};
1313
use base64::Engine as _;
1414
use mailparse::ParsedContentType;
1515
use mime::Mime;
1616

1717
use crate::context::Context;
1818
use crate::headerdef::{HeaderDef, HeaderDefMap};
1919
use crate::log::warn;
20-
use crate::message::{self, Message, MsgId};
20+
use crate::message::{Message, MsgId};
2121
use crate::mimeparser::parse_message_id;
22-
use crate::param::Param::SendHtml;
22+
use crate::param::{Param::SendHtml, Params};
2323
use crate::plaintext::PlainText;
24+
use crate::sql;
25+
use crate::tools::{buf_compress, buf_decompress};
2426

2527
impl Message {
2628
/// Check if the message can be retrieved as HTML.
@@ -258,28 +260,71 @@ impl MsgId {
258260
/// NB: we do not save raw mime unconditionally in the database to save space.
259261
/// The corresponding ffi-function is `dc_get_msg_html()`.
260262
pub async fn get_html(self, context: &Context) -> Result<Option<String>> {
261-
// If there are many concurrent db readers, going to the queue earlier makes sense.
262-
let (param, rawmime) = tokio::join!(
263-
self.get_param(context),
264-
message::get_mime_headers(context, self)
265-
);
266-
if let Some(html) = param?.get(SendHtml) {
263+
let (param, headers, compressed) = context
264+
.sql
265+
.query_row(
266+
"SELECT param, mime_headers, mime_compressed FROM msgs WHERE id=?",
267+
(self,),
268+
|row| {
269+
let param: String = row.get(0)?;
270+
let param: Params = param.parse().unwrap_or_default();
271+
let headers = sql::row_get_vec(row, 1)?;
272+
let compressed: bool = row.get(2)?;
273+
Ok((param, headers, compressed))
274+
},
275+
)
276+
.await?;
277+
if let Some(html) = param.get(SendHtml) {
267278
return Ok(Some(html.to_string()));
268279
}
280+
let from_rawmime = |rawmime: Vec<u8>| {
281+
if !rawmime.is_empty() {
282+
match HtmlMsgParser::from_bytes(context, &rawmime) {
283+
Err(err) => {
284+
warn!(context, "get_html: parser error: {:#}", err);
285+
Ok(None)
286+
}
287+
Ok((parser, _)) => Ok(Some(parser.html)),
288+
}
289+
} else {
290+
warn!(context, "get_html: no mime for {}", self);
291+
Ok(None)
292+
}
293+
};
269294

270-
let rawmime = rawmime?;
271-
if !rawmime.is_empty() {
272-
match HtmlMsgParser::from_bytes(context, &rawmime) {
273-
Err(err) => {
274-
warn!(context, "get_html: parser error: {:#}", err);
275-
Ok(None)
295+
if compressed {
296+
return from_rawmime(buf_decompress(&headers)?).await;
297+
}
298+
let headers2 = headers.clone();
299+
let compressed = match tokio::task::block_in_place(move || buf_compress(&headers2)) {
300+
Err(e) => {
301+
warn!(context, "get_mime_headers: buf_compress() failed: {}", e);
302+
return from_rawmime(headers).await;
303+
}
304+
Ok(o) => o,
305+
};
306+
let update = |conn: &mut rusqlite::Connection| {
307+
match conn.execute(
308+
"
309+
UPDATE msgs SET mime_headers=?, mime_compressed=1
310+
WHERE id=? AND mime_headers!='' AND mime_compressed=0",
311+
(compressed, self),
312+
) {
313+
Ok(rows_updated) => ensure!(rows_updated <= 1),
314+
Err(e) => {
315+
warn!(context, "get_mime_headers: UPDATE failed: {}", e);
316+
return Err(e.into());
276317
}
277-
Ok((parser, _)) => Ok(Some(parser.html)),
278318
}
279-
} else {
280-
warn!(context, "get_html: no mime for {}", self);
281-
Ok(None)
319+
Ok(())
320+
};
321+
if let Err(e) = context.sql.call_write(update).await {
322+
warn!(
323+
context,
324+
"get_mime_headers: failed to update mime_headers: {}", e
325+
);
282326
}
327+
return from_rawmime(headers).await;
283328
}
284329
}
285330

src/message.rs

Lines changed: 2 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,12 @@ use crate::log::warn;
3030
use crate::mimeparser::{SystemMessage, parse_message_id};
3131
use crate::param::{Param, Params};
3232
use crate::reaction::get_msg_reactions;
33-
use crate::sql;
3433
use crate::summary::Summary;
3534
use crate::sync::SyncData;
3635
use crate::tools::create_outgoing_rfc724_mid;
3736
use crate::tools::{
38-
buf_compress, buf_decompress, get_filebytes, get_filemeta, gm2local_offset, read_file,
39-
sanitize_filename, time, timestamp_to_str,
37+
get_filebytes, get_filemeta, gm2local_offset, read_file, sanitize_filename, time,
38+
timestamp_to_str,
4039
};
4140

4241
/// Message ID, including reserved IDs.
@@ -1595,62 +1594,6 @@ pub(crate) fn guess_msgtype_from_path_suffix(path: &Path) -> Option<(Viewtype, &
15951594
Some(info)
15961595
}
15971596

1598-
/// Get the raw mime-headers of the given message.
1599-
/// Raw headers are saved for large messages
1600-
/// that need a "Show full message..."
1601-
/// to see HTML part.
1602-
///
1603-
/// Returns an empty vector if there are no headers saved for the given message.
1604-
pub(crate) async fn get_mime_headers(context: &Context, msg_id: MsgId) -> Result<Vec<u8>> {
1605-
let (headers, compressed) = context
1606-
.sql
1607-
.query_row(
1608-
"SELECT mime_headers, mime_compressed FROM msgs WHERE id=?",
1609-
(msg_id,),
1610-
|row| {
1611-
let headers = sql::row_get_vec(row, 0)?;
1612-
let compressed: bool = row.get(1)?;
1613-
Ok((headers, compressed))
1614-
},
1615-
)
1616-
.await?;
1617-
if compressed {
1618-
return buf_decompress(&headers);
1619-
}
1620-
1621-
let headers2 = headers.clone();
1622-
let compressed = match tokio::task::block_in_place(move || buf_compress(&headers2)) {
1623-
Err(e) => {
1624-
warn!(context, "get_mime_headers: buf_compress() failed: {}", e);
1625-
return Ok(headers);
1626-
}
1627-
Ok(o) => o,
1628-
};
1629-
let update = |conn: &mut rusqlite::Connection| {
1630-
match conn.execute(
1631-
"\
1632-
UPDATE msgs SET mime_headers=?, mime_compressed=1 \
1633-
WHERE id=? AND mime_headers!='' AND mime_compressed=0",
1634-
(compressed, msg_id),
1635-
) {
1636-
Ok(rows_updated) => ensure!(rows_updated <= 1),
1637-
Err(e) => {
1638-
warn!(context, "get_mime_headers: UPDATE failed: {}", e);
1639-
return Err(e.into());
1640-
}
1641-
}
1642-
Ok(())
1643-
};
1644-
if let Err(e) = context.sql.call_write(update).await {
1645-
warn!(
1646-
context,
1647-
"get_mime_headers: failed to update mime_headers: {}", e
1648-
);
1649-
}
1650-
1651-
Ok(headers)
1652-
}
1653-
16541597
/// Delete a single message from the database, including references in other tables.
16551598
/// This may be called in batches; the final events are emitted in delete_msgs_locally_done() then.
16561599
pub(crate) async fn delete_msg_locally(context: &Context, msg: &Message) -> Result<()> {

src/receive_imf/receive_imf_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,8 +1684,8 @@ async fn test_save_mime_headers_off() -> anyhow::Result<()> {
16841684

16851685
let msg = bob.recv_msg(&alice.pop_sent_msg().await).await;
16861686
assert_eq!(msg.get_text(), "hi!");
1687-
let mime = message::get_mime_headers(&bob, msg.id).await?;
1688-
assert!(mime.is_empty());
1687+
let html = msg.id.get_html(&bob).await?;
1688+
assert!(html.is_none());
16891689
Ok(())
16901690
}
16911691

0 commit comments

Comments
 (0)