From c9c362d5ffd81defb8d677ee7c758036e901eee5 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 22 Nov 2025 01:12:35 +0000 Subject: [PATCH] api: get_existing_msg_ids() This API allows to check if the message with given ID exists and distinguish between message not existing and database error. It might also be faster than checking messages one by one if multiple messages need to be checked because of using a single SQL transaction. --- deltachat-jsonrpc/src/api.rs | 17 +++++++++++++++-- src/message.rs | 27 +++++++++++++++++++++++++++ src/message/message_tests.rs | 24 ++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/deltachat-jsonrpc/src/api.rs b/deltachat-jsonrpc/src/api.rs index 1603f341a..8f3b27fa4 100644 --- a/deltachat-jsonrpc/src/api.rs +++ b/deltachat-jsonrpc/src/api.rs @@ -21,9 +21,9 @@ use deltachat::context::get_info; use deltachat::ephemeral::Timer; use deltachat::imex; use deltachat::location; -use deltachat::message::get_msg_read_receipts; use deltachat::message::{ - self, delete_msgs_ex, markseen_msgs, Message, MessageState, MsgId, Viewtype, + self, delete_msgs_ex, get_existing_msg_ids, get_msg_read_receipts, markseen_msgs, Message, + MessageState, MsgId, Viewtype, }; use deltachat::peer_channels::{ leave_webxdc_realtime, send_webxdc_realtime_advertisement, send_webxdc_realtime_data, @@ -1295,6 +1295,19 @@ impl CommandApi { .collect()) } + /// Checks if the messages with given IDs exist. + /// + /// Returns IDs of existing messages. + async fn get_existing_msg_ids(&self, account_id: u32, msg_ids: Vec) -> Result> { + let context = self.get_context(account_id).await?; + let msg_ids: Vec = msg_ids.into_iter().map(MsgId::new).collect(); + let existing_msg_ids = get_existing_msg_ids(&context, &msg_ids).await?; + Ok(existing_msg_ids + .into_iter() + .map(|msg_id| msg_id.to_u32()) + .collect()) + } + async fn get_message_list_items( &self, account_id: u32, diff --git a/src/message.rs b/src/message.rs index 2f7252a37..aedfded94 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1868,6 +1868,33 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> Result<()> Ok(()) } +/// Checks if the messages with given IDs exist. +/// +/// Returns IDs of existing messages. +pub async fn get_existing_msg_ids(context: &Context, ids: &[MsgId]) -> Result> { + let query_only = true; + let res = context + .sql + .transaction_ex(query_only, |transaction| { + let mut res: Vec = Vec::new(); + for id in ids { + if transaction.query_one( + "SELECT COUNT(*) > 0 FROM msgs WHERE id=? AND chat_id!=3", + (id,), + |row| { + let exists: bool = row.get(0)?; + Ok(exists) + }, + )? { + res.push(*id); + } + } + Ok(res) + }) + .await?; + Ok(res) +} + pub(crate) async fn update_msg_state( context: &Context, msg_id: MsgId, diff --git a/src/message/message_tests.rs b/src/message/message_tests.rs index 66f2c6f09..be3ed01d9 100644 --- a/src/message/message_tests.rs +++ b/src/message/message_tests.rs @@ -764,3 +764,27 @@ async fn test_load_unknown_viewtype() -> Result<()> { assert_eq!(bob_msg.get_viewtype(), Viewtype::Unknown); Ok(()) } + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_get_existing_msg_ids() -> Result<()> { + let mut tcm = TestContextManager::new(); + let alice = &tcm.alice().await; + let bob = &tcm.bob().await; + + let msg1_id = tcm.send_recv(alice, bob, "Hello 1!").await.id; + let msg2_id = tcm.send_recv(alice, bob, "Hello 2!").await.id; + let msg3_id = tcm.send_recv(alice, bob, "Hello 3!").await.id; + let msg4_id = tcm.send_recv(alice, bob, "Hello 4!").await.id; + + assert_eq!( + get_existing_msg_ids(bob, &[msg1_id, msg2_id, msg3_id, msg4_id]).await?, + vec![msg1_id, msg2_id, msg3_id, msg4_id] + ); + delete_msgs(bob, &[msg1_id, msg3_id]).await?; + assert_eq!( + get_existing_msg_ids(bob, &[msg1_id, msg2_id, msg3_id, msg4_id]).await?, + vec![msg2_id, msg4_id] + ); + + Ok(()) +}