Skip to main content

xmtp_db/encrypted_store/group_intent/
prepared.rs

1//! Exact prepared envelope bytes for an existing logical intent.
2
3use diesel::prelude::*;
4
5use crate::schema::group_intents as intents;
6use crate::stream_storage::stream_transaction;
7use crate::{ConnectionExt, NotFound, StorageError};
8
9/// Exact attempt bytes kept across retries of one logical intent.
10pub trait QueryPreparedEnvelope: ConnectionExt + Sized {
11    /// Read the persisted attempt. None means no attempt is currently prepared.
12    fn prepared_envelopes(&self, intent_id: super::ID) -> Result<Option<Vec<u8>>, StorageError> {
13        self.raw_query(|conn| {
14            intents::table
15                .find(intent_id)
16                .select(intents::prepared_envelopes)
17                .first::<Option<Vec<u8>>>(conn)
18                .optional()
19        })?
20        .ok_or_else(|| NotFound::IntentById(intent_id).into())
21    }
22
23    /// Replace only the exact attempt the caller read under the state writer.
24    /// Late publish replies must use this check before attaching receipt metadata.
25    fn compare_and_set_prepared_envelopes(
26        &self,
27        intent_id: super::ID,
28        expected: Option<&[u8]>,
29        replacement: Option<&[u8]>,
30    ) -> Result<bool, StorageError> {
31        stream_transaction(self, |conn| {
32            let current = intents::table
33                .find(intent_id)
34                .select(intents::prepared_envelopes)
35                .first::<Option<Vec<u8>>>(conn)
36                .optional()?
37                .ok_or(NotFound::IntentById(intent_id))?;
38            if current.as_deref() != expected {
39                return Ok(false);
40            }
41            diesel::update(intents::table.find(intent_id))
42                .set(intents::prepared_envelopes.eq(replacement))
43                .execute(conn)?;
44            Ok(true)
45        })
46    }
47}
48
49impl<C: ConnectionExt> QueryPreparedEnvelope for C {}