1pub mod syncv4;
8mod tests;
9
10use arminius_lib_db::{Database, DatabaseError, ResultExt as _};
11use arminius_lib_events::{RoomEvent, Verified};
12use arminius_lib_model::{event::EventVerifiedRow, types::StreamId};
13use ruma::{
14 EventId, OwnedEventId, OwnedRoomId, RoomId, UInt, UserId,
15 api::client::filter::{RoomEventFilter, RoomFilter},
16 events::room::member::MembershipState,
17};
18
19pub const MAX_SYNCED_EVENTS: usize = 1000;
22
23const MAX_SYNCED_EVENTS_I64: i64 = 1000;
29
30pub const DEFAULT_SYNCED_EVENTS: i64 = 10;
33
34pub const MAX_MESSAGES: usize = 1000;
38
39const MAX_MESSAGES_I64: i64 = 1000;
45
46#[derive(Debug)]
48pub struct SyncEvent {
49 pub stream_id: StreamId,
51 pub event_id: OwnedEventId,
53 pub room_id: OwnedRoomId,
55 pub membership: MembershipState,
58 pub event: RoomEvent<Verified>,
60}
61
62#[rust_analyzer::prefer_underscore_import]
65pub trait SyncQueries: Database {
66 #[tracing::instrument(skip(self))]
69 async fn fetch_new_room_membership_event(
70 &mut self,
71 room_id: &RoomId,
72 user_id: &UserId,
73 from_stream_id: StreamId,
74 ) -> Result<Option<RoomEvent<Verified>>, DatabaseError> {
75 sqlx::query_as!(
76 EventVerifiedRow,
77 r#"
78 SELECT
79 events.event_json AS "event_json: Box<sqlx::types::JsonRawValue>",
80 rooms.room_version,
81 verification.attempted_at,
82 verification.signature_verification,
83 verification.soft_failed
84 FROM
85 events
86 JOIN rooms ON events.room_nid = rooms.nid
87 JOIN verification_metadata verification ON verification.event_nid = events.nid
88 WHERE
89 rooms.room_id = $1
90 AND (events.event_json ->> 'type') = 'm.room.member'
91 AND (events.event_json ->> 'state_key') = $2
92 AND events.stream_id > $3
93 AND events.stream_id <= max_safe_seq ('eventids')
94 ORDER BY
95 stream_id ASC
96 LIMIT 1
97 "#,
98 room_id.as_str(),
99 user_id.as_str(),
100 from_stream_id
101 )
102 .fetch_optional(self.connection().await?)
103 .await?
104 .map(EventVerifiedRow::try_into_event)
105 .transpose()
106 }
107
108 #[tracing::instrument(skip(self))]
111 async fn fetch_sync_room_events(
112 &mut self,
113 user: &UserId,
114 from_stream_id: StreamId,
115 room_filters: Option<&RoomFilter>,
116 ) -> Result<Vec<SyncEvent>, DatabaseError> {
117 let limit = room_filters
118 .and_then(|room_filter| room_filter.timeline.limit.map(i64::from))
119 .map_or(DEFAULT_SYNCED_EVENTS, |client_requested_limit| {
120 i64::min(MAX_SYNCED_EVENTS_I64, client_requested_limit)
121 });
122 let included_rooms = room_filters.and_then(|room_filter| {
123 room_filter.rooms.as_ref().map(|included_rooms| {
124 included_rooms.iter().map(OwnedRoomId::to_string).collect::<Vec<_>>()
125 })
126 });
127 let excluded_rooms = room_filters
128 .map(|room_filter| {
129 room_filter.not_rooms.iter().map(OwnedRoomId::to_string).collect::<Vec<_>>()
130 })
131 .unwrap_or_default();
132 let included_event_types =
133 room_filters.and_then(|room_filter| room_filter.timeline.types.clone());
134 let blocked_event_types = room_filters
135 .map(|room_filter| room_filter.timeline.not_types.clone())
136 .unwrap_or_default();
137
138 let included_senders = room_filters.and_then(|room_filter| {
139 room_filter
140 .clone()
141 .timeline
142 .senders
143 .map(|senders| senders.iter().map(ToString::to_string).collect::<Vec<_>>())
144 });
145 let excluded_senders = room_filters
146 .map(|room_filter| {
147 room_filter.timeline.not_senders.iter().map(ToString::to_string).collect::<Vec<_>>()
148 })
149 .unwrap_or_default();
150
151 sqlx::query_file!(
152 "src/sync.sql",
153 user.as_str(),
154 from_stream_id,
155 limit,
156 included_rooms.as_deref(),
157 &excluded_rooms,
158 included_event_types.as_deref(),
159 &blocked_event_types,
160 included_senders.as_deref(),
161 &excluded_senders
162 )
163 .fetch_all(self.connection().await?)
164 .await?
165 .into_iter()
166 .map(|row| -> Result<SyncEvent, DatabaseError> {
167 let event_row = EventVerifiedRow {
168 event_json: row.event_json,
169 room_version: row.room_version,
170 attempted_at: row.attempted_at,
171 signature_verification: row.signature_verification,
172 soft_failed: row.soft_failed,
173 };
174
175 let event = event_row.try_into_event()?;
176
177 Ok(SyncEvent {
178 stream_id: row.stream_id,
179 event_id: row.event_id.parse().invalid_data("Invalid event ID in database")?,
180 room_id: row.room_id.parse().invalid_data("Invalid room ID in database")?,
181 membership: MembershipState::from(row.membership),
182 event,
183 })
184 })
185 .collect::<Result<Vec<_>, _>>()
186 }
187
188 #[tracing::instrument(skip(self))]
194 async fn fetch_room_messages_forwards(
195 &mut self,
196 user: &UserId,
197 room: &RoomId,
198 from_event_id: Option<&EventId>,
199 limit: UInt,
200 room_filters: &RoomEventFilter,
201 ) -> Result<(Vec<RoomEvent<Verified>>, bool), DatabaseError> {
202 let limit = i64::min(i64::from(limit), MAX_MESSAGES_I64) + 1;
204 let included_senders = room_filters
205 .clone()
206 .senders
207 .map(|senders| senders.iter().map(ToString::to_string).collect::<Vec<_>>());
208 let excluded_senders =
209 room_filters.not_senders.iter().map(ToString::to_string).collect::<Vec<_>>();
210
211 let mut events = sqlx::query_as!(
214 EventVerifiedRow,
215 "
216 SELECT
217 event_json AS \"event_json!: Box<sqlx::types::JsonRawValue>\",
218 rooms.room_version,
219 verification.attempted_at,
220 verification.signature_verification,
221 verification.soft_failed
222 FROM
223 visible_timeline_events ($2, $1) events
224 JOIN event_ordering ON events.nid = event_ordering.event_nid
225 JOIN rooms ON rooms.nid = events.room_nid
226 JOIN verification_metadata verification ON verification.event_nid = events.nid
227 WHERE
228 -- The ordering value for the event should be greater than the one of from_event_id
229 -- if given
230 ($3::text IS NULL OR ordering > (
231 SELECT
232 ordering
233 FROM
234 event_ordering
235 JOIN events ON event_ordering.event_nid = events.nid
236 WHERE
237 events.event_id = $3::text))
238 -- The type should match the allowlist in the room filter if given
239 AND ($5::text[] IS NULL
240 OR events.event_json ->> 'type' = ANY ($5::text[]))
241 -- The type should not be in the denylist from the room filter
242 AND NOT (events.event_json ->> 'type' = ANY ($6::text[]))
243 -- The sender should match the allowlist given in the room filter
244 AND ($7::text[] IS NULL
245 OR events.event_json ->> 'sender' = ANY ($7::text[]))
246 -- The sender should not be in the denylist given in the room filter
247 AND NOT (events.event_json ->> 'sender' = ANY ($8::text[]))
248 AND events.stream_id <= max_safe_seq ('eventids')
249 ORDER BY
250 ordering ASC
251 LIMIT $4
252 ",
253 room.as_str(),
254 user.as_str(),
255 from_event_id.map(EventId::as_str),
256 limit,
257 room_filters.types.as_deref(),
258 &room_filters.not_types,
259 included_senders.as_deref(),
260 &excluded_senders
261 )
262 .fetch_all(self.connection().await?)
263 .await?
264 .into_iter()
265 .map(EventVerifiedRow::try_into_event)
266 .collect::<Result<Vec<_>, _>>()?;
267
268 let there_are_more = events.len() == limit as usize;
269 if there_are_more {
270 events.pop();
271 }
272
273 Ok((events, there_are_more))
274 }
275
276 #[tracing::instrument(skip(self))]
282 async fn fetch_room_messages_backwards(
283 &mut self,
284 user: &UserId,
285 room: &RoomId,
286 from_event_id: Option<&EventId>,
287 limit: UInt,
288 room_filters: &RoomEventFilter,
289 ) -> Result<(Vec<RoomEvent<Verified>>, bool), DatabaseError> {
290 let limit = i64::min(i64::from(limit), MAX_MESSAGES_I64) + 1;
292 let included_senders = room_filters
293 .clone()
294 .senders
295 .map(|senders| senders.iter().map(ToString::to_string).collect::<Vec<_>>());
296 let excluded_senders =
297 room_filters.not_senders.iter().map(ToString::to_string).collect::<Vec<_>>();
298
299 let mut events = sqlx::query_as!(
302 EventVerifiedRow,
303 "
304 SELECT
305 event_json AS \"event_json!: Box<sqlx::types::JsonRawValue>\",
306 rooms.room_version,
307 verification.attempted_at,
308 verification.signature_verification,
309 verification.soft_failed
310 FROM
311 visible_timeline_events ($2, $1) events
312 JOIN event_ordering ON events.nid = event_ordering.event_nid
313 JOIN rooms ON rooms.nid = events.room_nid
314 JOIN verification_metadata verification ON verification.event_nid = events.nid
315 WHERE
316 -- The ordering value for the event should be less than the one of from_event_id
317 -- if given
318 ($3::text IS NULL OR ordering < (
319 SELECT
320 ordering
321 FROM
322 event_ordering
323 JOIN events ON event_ordering.event_nid = events.nid
324 WHERE
325 events.event_id = $3::text))
326 -- The type should match the allowlist in the room filter if given
327 AND ($5::text[] IS NULL
328 OR events.event_json ->> 'type' = ANY ($5::text[]))
329 -- The type should not be in the denylist from the room filter
330 AND NOT (events.event_json ->> 'type' = ANY ($6::text[]))
331 -- The sender should match the allowlist given in the room filter
332 AND ($7::text[] IS NULL
333 OR events.event_json ->> 'sender' = ANY ($7::text[]))
334 -- The sender should not be in the denylist given in the room filter
335 AND NOT (events.event_json ->> 'sender' = ANY ($8::text[]))
336 AND events.stream_id <= max_safe_seq ('eventids')
337 ORDER BY
338 ordering DESC
339 LIMIT $4
340 ",
341 room.as_str(),
342 user.as_str(),
343 from_event_id.map(EventId::as_str),
344 limit,
345 room_filters.types.as_deref(),
346 &room_filters.not_types,
347 included_senders.as_deref(),
348 &excluded_senders
349 )
350 .fetch_all(self.connection().await?)
351 .await?
352 .into_iter()
353 .map(EventVerifiedRow::try_into_event)
354 .collect::<Result<Vec<_>, _>>()?;
355
356 let there_are_more = events.len() == limit as usize;
357 if there_are_more {
358 events.pop();
359 }
360
361 Ok((events, there_are_more))
362 }
363
364 #[tracing::instrument(skip(self))]
366 async fn fetch_incremental_room_state_for_user(
367 &mut self,
368 room_id: &RoomId,
369 from_event_id: StreamId,
370 to_stream_id: StreamId,
371 ) -> Result<Vec<RoomEvent<Verified>>, DatabaseError> {
372 sqlx::query_as!(
375 EventVerifiedRow,
376 r#"
377 SELECT DISTINCT ON (state_events.nid)
378 state_events.event_json AS "event_json!: Box<sqlx::types::JsonRawValue>",
379 rooms.room_version,
380 verification.attempted_at,
381 verification.signature_verification,
382 verification.soft_failed
383 FROM
384 events
385 JOIN rooms ON rooms.nid = events.room_nid
386 CROSS JOIN LATERAL (
387 SELECT
388 event_state.nid,
389 event_state.event_id,
390 event_state.depth,
391 event_state.event_json::text,
392 event_state.received_ts
393 FROM
394 state_before (events.nid) event_state
395 WHERE
396 event_state.stream_id >= $2) AS state_events
397 JOIN verification_metadata verification ON verification.event_nid = state_events.nid
398 WHERE
399 events.room_nid = (
400 SELECT
401 nid
402 FROM
403 rooms
404 WHERE
405 room_id = $1)
406 AND events.stream_id >= $2
407 AND events.stream_id <= $3
408 ORDER BY
409 state_events.nid
410 "#,
411 room_id.to_string(),
412 from_event_id,
413 to_stream_id,
414 )
415 .fetch_all(self.connection().await?)
416 .await?
417 .into_iter()
418 .map(EventVerifiedRow::try_into_event)
419 .collect()
420 }
421}
422
423impl<T> SyncQueries for T where T: Database {}