1use std::ops::{Deref, DerefMut};
4
5#[cfg(feature = "mysql")]
6use sea_query::MysqlQueryBuilder;
7#[cfg(feature = "postgres")]
8use sea_query::PostgresQueryBuilder;
9#[cfg(feature = "sqlite")]
10use sea_query::SqliteQueryBuilder;
11use sea_query::{
12 Cond, Expr, ExprTrait, Func, Iden, IntoColumnRef, OnConflict, Query, SelectStatement,
13};
14use sea_query_sqlx::SqlxBinder;
15use sqlx::{Database, Pool};
16use ulid::Ulid;
17
18use evento_core::{
19 cursor::{self, Args, Cursor, Edge, PageInfo, ReadResult, Value},
20 EventFilter, Executor, WriteError,
21};
22
23#[derive(Iden, Clone)]
40pub enum Event {
41 Table,
43 Id,
45 Name,
47 AggregatorType,
49 AggregatorId,
51 Version,
53 Data,
55 Metadata,
57 RoutingKey,
59 Timestamp,
61 TimestampSubsec,
63}
64
65#[derive(Iden)]
71pub enum Snapshot {
72 Table,
74 Id,
76 Type,
78 Cursor,
80 Revision,
82 Data,
84 CreatedAt,
86 UpdatedAt,
88}
89
90#[derive(Iden)]
103pub enum Subscriber {
104 Table,
106 Key,
108 WorkerId,
110 Cursor,
112 Lag,
114 Enabled,
116 CreatedAt,
118 UpdatedAt,
120}
121
122#[cfg(feature = "mysql")]
126pub type MySql = Sql<sqlx::MySql>;
127
128#[cfg(feature = "mysql")]
132pub type RwMySql = evento_core::Rw<MySql, MySql>;
133
134#[cfg(feature = "postgres")]
138pub type Postgres = Sql<sqlx::Postgres>;
139
140#[cfg(feature = "postgres")]
144pub type RwPostgres = evento_core::Rw<Postgres, Postgres>;
145
146#[cfg(feature = "sqlite")]
150pub type Sqlite = Sql<sqlx::Sqlite>;
151
152#[cfg(feature = "sqlite")]
156pub type RwSqlite = evento_core::Rw<Sqlite, Sqlite>;
157
158pub struct Sql<DB: Database>(Pool<DB>, tokio::sync::watch::Sender<u64>);
196
197impl<DB: Database> Sql<DB> {
198 fn build_sqlx<S: SqlxBinder>(statement: S) -> (String, sea_query_sqlx::SqlxValues) {
199 match DB::NAME {
200 #[cfg(feature = "sqlite")]
201 "SQLite" => statement.build_sqlx(SqliteQueryBuilder),
202 #[cfg(feature = "mysql")]
203 "MySQL" => statement.build_sqlx(MysqlQueryBuilder),
204 #[cfg(feature = "postgres")]
205 "PostgreSQL" => statement.build_sqlx(PostgresQueryBuilder),
206 name => panic!("'{name}' not supported, consider using SQLite, PostgreSQL or MySQL"),
207 }
208 }
209}
210
211#[async_trait::async_trait]
212impl<DB> Executor for Sql<DB>
213where
214 DB: Database,
215 for<'c> &'c mut DB::Connection: sqlx::Executor<'c, Database = DB>,
216 sea_query_sqlx::SqlxValues: sqlx::IntoArguments<DB>,
217 String: for<'r> sqlx::Decode<'r, DB> + sqlx::Type<DB>,
218 bool: for<'r> sqlx::Decode<'r, DB> + sqlx::Type<DB>,
219 Vec<u8>: for<'r> sqlx::Decode<'r, DB> + sqlx::Type<DB>,
220 i64: for<'r> sqlx::Decode<'r, DB> + sqlx::Type<DB>,
221 usize: sqlx::ColumnIndex<DB::Row>,
222 SqlEvent: for<'r> sqlx::FromRow<'r, DB::Row>,
223{
224 async fn read(
225 &self,
226 aggregators: Option<Vec<EventFilter>>,
227 routing_key: Option<evento_core::RoutingKey>,
228 args: Args,
229 ) -> anyhow::Result<ReadResult<evento_core::Event>> {
230 let statement = Query::select()
231 .columns([
232 Event::Id,
233 Event::Name,
234 Event::AggregatorType,
235 Event::AggregatorId,
236 Event::Version,
237 Event::Data,
238 Event::Metadata,
239 Event::RoutingKey,
240 Event::Timestamp,
241 Event::TimestampSubsec,
242 ])
243 .from(Event::Table)
244 .conditions(
245 aggregators.is_some(),
246 |q| {
247 let Some(aggregators) = aggregators else {
248 return;
249 };
250
251 let mut cond = Cond::any();
252
253 for aggregator in aggregators {
254 let mut aggregator_cond = Cond::all()
255 .add(Expr::col(Event::AggregatorType).eq(aggregator.aggregate_type));
256
257 if let Some(id) = aggregator.aggregate_id {
258 aggregator_cond =
259 aggregator_cond.add(Expr::col(Event::AggregatorId).eq(id));
260 }
261
262 if let Some(name) = aggregator.name {
263 aggregator_cond = aggregator_cond.add(Expr::col(Event::Name).eq(name));
264 }
265
266 cond = cond.add(aggregator_cond);
267 }
268
269 q.and_where(cond.into());
270 },
271 |_| {},
272 )
273 .conditions(
274 matches!(routing_key, Some(evento_core::RoutingKey::Value(_))),
275 |q| {
276 if let Some(evento_core::RoutingKey::Value(Some(ref routing_key))) = routing_key
277 {
278 q.and_where(Expr::col(Event::RoutingKey).eq(routing_key));
279 }
280
281 if let Some(evento_core::RoutingKey::Value(None)) = routing_key {
282 q.and_where(Expr::col(Event::RoutingKey).is_null());
283 }
284 },
285 |_q| {},
286 )
287 .to_owned();
288
289 Ok(Reader::new(statement)
290 .args(args)
291 .execute::<_, SqlEvent, _>(&self.0)
292 .await?
293 .map(|e| e.0))
294 }
295
296 async fn latest_timestamp(
297 &self,
298 aggregators: Option<Vec<EventFilter>>,
299 routing_key: Option<evento_core::RoutingKey>,
300 ) -> anyhow::Result<u64> {
301 let statement = Query::select()
302 .expr(Func::max(Expr::col(Event::Timestamp)))
303 .from(Event::Table)
304 .conditions(
305 aggregators.is_some(),
306 |q| {
307 let Some(aggregators) = aggregators else {
308 return;
309 };
310
311 let mut cond = Cond::any();
312
313 for aggregator in aggregators {
314 let mut aggregator_cond = Cond::all()
315 .add(Expr::col(Event::AggregatorType).eq(aggregator.aggregate_type));
316
317 if let Some(id) = aggregator.aggregate_id {
318 aggregator_cond =
319 aggregator_cond.add(Expr::col(Event::AggregatorId).eq(id));
320 }
321
322 if let Some(name) = aggregator.name {
323 aggregator_cond = aggregator_cond.add(Expr::col(Event::Name).eq(name));
324 }
325
326 cond = cond.add(aggregator_cond);
327 }
328
329 q.and_where(cond.into());
330 },
331 |_| {},
332 )
333 .conditions(
334 matches!(routing_key, Some(evento_core::RoutingKey::Value(_))),
335 |q| {
336 if let Some(evento_core::RoutingKey::Value(Some(ref routing_key))) = routing_key
337 {
338 q.and_where(Expr::col(Event::RoutingKey).eq(routing_key));
339 }
340
341 if let Some(evento_core::RoutingKey::Value(None)) = routing_key {
342 q.and_where(Expr::col(Event::RoutingKey).is_null());
343 }
344 },
345 |_q| {},
346 )
347 .to_owned();
348
349 let (sql, values) = Self::build_sqlx(statement);
350
351 let (ts,): (Option<i64>,) =
352 sqlx::query_as_with::<DB, (Option<i64>,), _>(sqlx::AssertSqlSafe(sql.as_str()), values)
353 .fetch_one(&self.0)
354 .await?;
355
356 Ok(ts.map(|v| if v < 0 { 0 } else { v as u64 }).unwrap_or(0))
357 }
358
359 async fn get_subscriber_cursor(&self, key: String) -> anyhow::Result<Option<Value>> {
360 let statement = Query::select()
361 .columns([Subscriber::Cursor])
362 .from(Subscriber::Table)
363 .and_where(Expr::col(Subscriber::Key).eq(Expr::value(key)))
364 .limit(1)
365 .to_owned();
366
367 let (sql, values) = Self::build_sqlx(statement);
368
369 let Some((cursor,)) = sqlx::query_as_with::<DB, (Option<String>,), _>(
370 sqlx::AssertSqlSafe(sql.as_str()),
371 values,
372 )
373 .fetch_optional(&self.0)
374 .await?
375 else {
376 return Ok(None);
377 };
378
379 Ok(cursor.map(|c| c.into()))
380 }
381
382 async fn is_subscriber_running(&self, key: String, worker_id: Ulid) -> anyhow::Result<bool> {
383 let statement = Query::select()
384 .columns([Subscriber::WorkerId, Subscriber::Enabled])
385 .from(Subscriber::Table)
386 .and_where(Expr::col(Subscriber::Key).eq(Expr::value(key)))
387 .limit(1)
388 .to_owned();
389
390 let (sql, values) = Self::build_sqlx(statement);
391
392 let (id, enabled) =
393 sqlx::query_as_with::<DB, (String, bool), _>(sqlx::AssertSqlSafe(sql.as_str()), values)
394 .fetch_one(&self.0)
395 .await?;
396
397 Ok(worker_id.to_string() == id && enabled)
398 }
399
400 async fn upsert_subscriber(&self, key: String, worker_id: Ulid) -> anyhow::Result<()> {
401 let statement = Query::insert()
402 .into_table(Subscriber::Table)
403 .columns([Subscriber::Key, Subscriber::WorkerId, Subscriber::Lag])
404 .values_panic([key.into(), worker_id.to_string().into(), 0.into()])
405 .on_conflict(
406 OnConflict::column(Subscriber::Key)
407 .update_columns([Subscriber::WorkerId])
408 .value(Subscriber::UpdatedAt, Expr::current_timestamp())
409 .to_owned(),
410 )
411 .to_owned();
412
413 let (sql, values) = Self::build_sqlx(statement);
414
415 sqlx::query_with::<DB, _>(sqlx::AssertSqlSafe(sql.as_str()), values)
416 .execute(&self.0)
417 .await?;
418
419 Ok(())
420 }
421
422 async fn write(&self, events: Vec<evento_core::Event>) -> Result<(), WriteError> {
423 let mut statement = Query::insert()
424 .into_table(Event::Table)
425 .columns([
426 Event::Id,
427 Event::Name,
428 Event::Data,
429 Event::Metadata,
430 Event::AggregatorType,
431 Event::AggregatorId,
432 Event::Version,
433 Event::RoutingKey,
434 Event::Timestamp,
435 Event::TimestampSubsec,
436 ])
437 .to_owned();
438
439 for event in events {
440 let metadata = bitcode::encode(&event.metadata);
441 statement.values_panic([
442 event.id.to_string().into(),
443 event.name.into(),
444 event.data.into(),
445 metadata.into(),
446 event.aggregate_type.into(),
447 event.aggregate_id.into(),
448 event.version.into(),
449 event.routing_key.into(),
450 event.timestamp.into(),
451 event.timestamp_subsec.into(),
452 ]);
453 }
454
455 let (sql, values) = Self::build_sqlx(statement);
456
457 sqlx::query_with::<DB, _>(sqlx::AssertSqlSafe(sql.as_str()), values)
458 .execute(&self.0)
459 .await
460 .map_err(|err| {
461 let err_str = err.to_string();
462 if err_str.contains("(code: 2067)") {
463 return WriteError::InvalidOriginalVersion;
464 }
465 if err_str.contains("1062 (23000): Duplicate entry") {
466 return WriteError::InvalidOriginalVersion;
467 }
468 if err_str.contains("duplicate key value violates unique constraint") {
469 return WriteError::InvalidOriginalVersion;
470 }
471 WriteError::Unknown(err.into())
472 })?;
473
474 self.1.send_modify(|v| *v += 1);
477
478 Ok(())
479 }
480
481 fn write_watch(&self) -> Option<tokio::sync::watch::Receiver<u64>> {
482 Some(self.1.subscribe())
483 }
484
485 async fn acknowledge(&self, key: String, cursor: Value, lag: u64) -> anyhow::Result<()> {
486 let statement = Query::update()
487 .table(Subscriber::Table)
488 .values([
489 (Subscriber::Cursor, cursor.0.into()),
490 (Subscriber::Lag, lag.into()),
491 (Subscriber::UpdatedAt, Expr::current_timestamp()),
492 ])
493 .and_where(Expr::col(Subscriber::Key).eq(key))
494 .to_owned();
495
496 let (sql, values) = Self::build_sqlx(statement);
497
498 sqlx::query_with::<DB, _>(sqlx::AssertSqlSafe(sql.as_str()), values)
499 .execute(&self.0)
500 .await?;
501
502 Ok(())
503 }
504
505 async fn get_snapshot(
506 &self,
507 aggregate_type: String,
508 aggregate_revision: String,
509 id: String,
510 ) -> anyhow::Result<Option<(Vec<u8>, Value)>> {
511 let statement = Query::select()
512 .columns([Snapshot::Data, Snapshot::Cursor])
513 .from(Snapshot::Table)
514 .and_where(Expr::col(Snapshot::Type).eq(Expr::value(aggregate_type)))
515 .and_where(Expr::col(Snapshot::Id).eq(Expr::value(id)))
516 .and_where(Expr::col(Snapshot::Revision).eq(Expr::value(aggregate_revision)))
517 .limit(1)
518 .to_owned();
519
520 let (sql, values) = Self::build_sqlx(statement);
521
522 Ok(sqlx::query_as_with::<DB, (Vec<u8>, String), _>(
523 sqlx::AssertSqlSafe(sql.as_str()),
524 values,
525 )
526 .fetch_optional(&self.0)
527 .await
528 .map(|res| res.map(|(data, cursor)| (data, cursor.into())))?)
529 }
530
531 async fn save_snapshot(
532 &self,
533 aggregate_type: String,
534 aggregate_revision: String,
535 id: String,
536 data: Vec<u8>,
537 cursor: Value,
538 ) -> anyhow::Result<()> {
539 let statement = Query::insert()
540 .into_table(Snapshot::Table)
541 .columns([
542 Snapshot::Type,
543 Snapshot::Id,
544 Snapshot::Cursor,
545 Snapshot::Revision,
546 Snapshot::Data,
547 ])
548 .values_panic([
549 aggregate_type.into(),
550 id.to_string().into(),
551 cursor.to_string().into(),
552 aggregate_revision.into(),
553 data.into(),
554 ])
555 .on_conflict(
556 OnConflict::columns([Snapshot::Type, Snapshot::Id])
557 .update_columns([Snapshot::Data, Snapshot::Cursor, Snapshot::Revision])
558 .value(Snapshot::UpdatedAt, Expr::current_timestamp())
559 .to_owned(),
560 )
561 .to_owned();
562
563 let (sql, values) = Self::build_sqlx(statement);
564
565 sqlx::query_with::<DB, _>(sqlx::AssertSqlSafe(sql.as_str()), values)
566 .execute(&self.0)
567 .await?;
568
569 Ok(())
570 }
571
572 async fn delete_snapshot(&self, aggregate_type: String, id: String) -> anyhow::Result<()> {
573 let statement = Query::delete()
574 .from_table(Snapshot::Table)
575 .and_where(Expr::col(Snapshot::Type).eq(Expr::value(aggregate_type)))
576 .and_where(Expr::col(Snapshot::Id).eq(Expr::value(id)))
577 .to_owned();
578
579 let (sql, values) = Self::build_sqlx(statement);
580
581 sqlx::query_with::<DB, _>(sqlx::AssertSqlSafe(sql.as_str()), values)
582 .execute(&self.0)
583 .await?;
584
585 Ok(())
586 }
587}
588
589impl<D: Database> Clone for Sql<D> {
590 fn clone(&self) -> Self {
591 Self(self.0.clone(), self.1.clone())
594 }
595}
596
597impl<D: Database> From<Pool<D>> for Sql<D> {
598 fn from(value: Pool<D>) -> Self {
599 Self(value, tokio::sync::watch::channel(0).0)
600 }
601}
602
603pub struct Reader {
644 statement: SelectStatement,
645 args: Args,
646 order: cursor::Order,
647}
648
649impl Reader {
650 pub fn new(statement: SelectStatement) -> Self {
652 Self {
653 statement,
654 args: Args::default(),
655 order: cursor::Order::Asc,
656 }
657 }
658
659 pub fn order(&mut self, order: cursor::Order) -> &mut Self {
661 self.order = order;
662
663 self
664 }
665
666 pub fn desc(&mut self) -> &mut Self {
668 self.order(cursor::Order::Desc)
669 }
670
671 pub fn args(&mut self, args: Args) -> &mut Self {
673 self.args = args;
674
675 self
676 }
677
678 pub fn backward(&mut self, last: u16, before: Option<Value>) -> &mut Self {
685 self.args(Args {
686 last: Some(last),
687 before,
688 ..Default::default()
689 })
690 }
691
692 pub fn forward(&mut self, first: u16, after: Option<Value>) -> &mut Self {
699 self.args(Args {
700 first: Some(first),
701 after,
702 ..Default::default()
703 })
704 }
705
706 pub async fn execute<'e, 'c: 'e, DB, O, E>(
719 &mut self,
720 executor: E,
721 ) -> anyhow::Result<ReadResult<O>>
722 where
723 DB: Database,
724 E: 'e + sqlx::Executor<'c, Database = DB>,
725 O: for<'r> sqlx::FromRow<'r, DB::Row>,
726 O: Cursor,
727 O: Send + Unpin,
728 O: Bind<Cursor = O>,
729 <<O as Bind>::I as IntoIterator>::IntoIter: DoubleEndedIterator,
730 <<O as Bind>::V as IntoIterator>::IntoIter: DoubleEndedIterator,
731 sea_query_sqlx::SqlxValues: sqlx::IntoArguments<DB>,
732 {
733 let limit = self.build_reader::<O, O>()?;
734
735 let (sql, values) = match DB::NAME {
736 #[cfg(feature = "sqlite")]
737 "SQLite" => self.statement.build_sqlx(SqliteQueryBuilder),
738 #[cfg(feature = "mysql")]
739 "MySQL" => self.build_sqlx(MysqlQueryBuilder),
740 #[cfg(feature = "postgres")]
741 "PostgreSQL" => self.build_sqlx(PostgresQueryBuilder),
742 name => panic!("'{name}' not supported, consider using SQLite, PostgreSQL or MySQL"),
743 };
744
745 let mut rows = sqlx::query_as_with::<DB, O, _>(sqlx::AssertSqlSafe(sql.as_str()), values)
746 .fetch_all(executor)
747 .await?;
748
749 let has_more = rows.len() > limit as usize;
750 if has_more {
751 rows.pop();
752 }
753
754 let mut edges = vec![];
755 for node in rows.into_iter() {
756 edges.push(Edge {
757 cursor: node.serialize_cursor()?,
758 node,
759 });
760 }
761
762 if self.args.is_backward() {
763 edges = edges.into_iter().rev().collect();
764 }
765
766 let page_info = if self.args.is_backward() {
767 let start_cursor = edges.first().map(|e| e.cursor.clone());
768
769 PageInfo {
770 has_previous_page: has_more,
771 has_next_page: false,
772 start_cursor,
773 end_cursor: None,
774 }
775 } else {
776 let end_cursor = edges.last().map(|e| e.cursor.clone());
777 PageInfo {
778 has_previous_page: false,
779 has_next_page: has_more,
780 start_cursor: None,
781 end_cursor,
782 }
783 };
784
785 Ok(ReadResult { edges, page_info })
786 }
787
788 fn build_reader<O: Cursor, B: Bind<Cursor = O>>(&mut self) -> Result<u16, cursor::CursorError>
789 where
790 B::T: Clone,
791 <<B as Bind>::I as IntoIterator>::IntoIter: DoubleEndedIterator,
792 <<B as Bind>::V as IntoIterator>::IntoIter: DoubleEndedIterator,
793 {
794 let (limit, cursor) = self.args.get_info();
795
796 if let Some(cursor) = cursor.as_ref() {
797 self.build_reader_where::<O, B>(cursor)?;
798 }
799
800 self.build_reader_order::<B>();
801 self.limit((limit + 1).into());
802
803 Ok(limit)
804 }
805
806 fn build_reader_where<O, B>(&mut self, cursor: &Value) -> Result<(), cursor::CursorError>
807 where
808 O: Cursor,
809 B: Bind<Cursor = O>,
810 B::T: Clone,
811 <<B as Bind>::I as IntoIterator>::IntoIter: DoubleEndedIterator,
812 <<B as Bind>::V as IntoIterator>::IntoIter: DoubleEndedIterator,
813 {
814 let is_order_desc = self.is_order_desc();
815 let cursor = O::deserialize_cursor(cursor)?;
816 let columns = B::columns().into_iter().rev();
817 let values = B::values(cursor).into_iter().rev();
818
819 let mut expr = None::<Expr>;
820 for (col, value) in columns.zip(values) {
821 let current_expr = if is_order_desc {
822 Expr::col(col.clone()).lt(value.clone())
823 } else {
824 Expr::col(col.clone()).gt(value.clone())
825 };
826
827 let Some(ref prev_expr) = expr else {
828 expr = Some(current_expr.clone());
829 continue;
830 };
831
832 expr = Some(current_expr.or(Expr::col(col).eq(value).and(prev_expr.clone())));
833 }
834
835 self.and_where(expr.unwrap());
836
837 Ok(())
838 }
839
840 fn build_reader_order<O: Bind>(&mut self) {
841 let order = if self.is_order_desc() {
842 sea_query::Order::Desc
843 } else {
844 sea_query::Order::Asc
845 };
846
847 let columns = O::columns();
848 for col in columns {
849 self.order_by(col, order.clone());
850 }
851 }
852
853 fn is_order_desc(&self) -> bool {
854 matches!(
855 (&self.order, self.args.is_backward()),
856 (cursor::Order::Asc, true) | (cursor::Order::Desc, false)
857 )
858 }
859}
860
861impl Deref for Reader {
862 type Target = SelectStatement;
863
864 fn deref(&self) -> &Self::Target {
865 &self.statement
866 }
867}
868
869impl DerefMut for Reader {
870 fn deref_mut(&mut self) -> &mut Self::Target {
871 &mut self.statement
872 }
873}
874
875pub trait Bind {
893 type T: IntoColumnRef + Clone;
895 type I: IntoIterator<Item = Self::T>;
897 type V: IntoIterator<Item = Expr>;
899 type Cursor: Cursor;
901
902 fn columns() -> Self::I;
904 fn values(cursor: <<Self as Bind>::Cursor as Cursor>::T) -> Self::V;
906}
907
908impl evento_core::cursor::Cursor for SqlEvent {
909 type T = evento_core::EventCursor;
910
911 fn serialize(&self) -> Self::T {
912 evento_core::EventCursor {
913 i: self.0.id.to_string(),
914 v: self.0.version,
915 t: self.0.timestamp,
916 s: self.0.timestamp_subsec,
917 }
918 }
919}
920
921impl Bind for SqlEvent {
922 type T = Event;
923 type I = [Self::T; 4];
924 type V = [Expr; 4];
925 type Cursor = Self;
926
927 fn columns() -> Self::I {
928 [
929 Event::Timestamp,
930 Event::TimestampSubsec,
931 Event::Version,
932 Event::Id,
933 ]
934 }
935
936 fn values(cursor: <<Self as Bind>::Cursor as Cursor>::T) -> Self::V {
937 [
938 cursor.t.into(),
939 cursor.s.into(),
940 cursor.v.into(),
941 cursor.i.into(),
942 ]
943 }
944}
945
946#[cfg(feature = "sqlite")]
947impl From<Sqlite> for evento_core::Evento {
948 fn from(value: Sqlite) -> Self {
949 evento_core::Evento::new(value)
950 }
951}
952
953#[cfg(feature = "sqlite")]
954impl From<&Sqlite> for evento_core::Evento {
955 fn from(value: &Sqlite) -> Self {
956 evento_core::Evento::new(value.clone())
957 }
958}
959
960#[cfg(feature = "mysql")]
961impl From<MySql> for evento_core::Evento {
962 fn from(value: MySql) -> Self {
963 evento_core::Evento::new(value)
964 }
965}
966
967#[cfg(feature = "mysql")]
968impl From<&MySql> for evento_core::Evento {
969 fn from(value: &MySql) -> Self {
970 evento_core::Evento::new(value.clone())
971 }
972}
973
974#[cfg(feature = "postgres")]
975impl From<Postgres> for evento_core::Evento {
976 fn from(value: Postgres) -> Self {
977 evento_core::Evento::new(value)
978 }
979}
980
981#[cfg(feature = "postgres")]
982impl From<&Postgres> for evento_core::Evento {
983 fn from(value: &Postgres) -> Self {
984 evento_core::Evento::new(value.clone())
985 }
986}
987
988#[derive(Debug, Clone, PartialEq, Default)]
989pub struct SqlEvent(pub evento_core::Event);
990
991impl<R: sqlx::Row> sqlx::FromRow<'_, R> for SqlEvent
992where
993 i32: sqlx::Type<R::Database> + for<'r> sqlx::Decode<'r, R::Database>,
994 Vec<u8>: sqlx::Type<R::Database> + for<'r> sqlx::Decode<'r, R::Database>,
995 String: sqlx::Type<R::Database> + for<'r> sqlx::Decode<'r, R::Database>,
996 i64: sqlx::Type<R::Database> + for<'r> sqlx::Decode<'r, R::Database>,
997 for<'r> &'r str: sqlx::Type<R::Database> + sqlx::Decode<'r, R::Database>,
998 for<'r> &'r str: sqlx::ColumnIndex<R>,
999{
1000 fn from_row(row: &R) -> Result<Self, sqlx::Error> {
1001 let timestamp: i64 = sqlx::Row::try_get(row, "timestamp")?;
1002 let timestamp_subsec: i64 = sqlx::Row::try_get(row, "timestamp_subsec")?;
1003 let version: i32 = sqlx::Row::try_get(row, "version")?;
1004 let metadata: Vec<u8> = sqlx::Row::try_get(row, "metadata")?;
1005 let metadata: evento_core::metadata::Metadata =
1006 bitcode::decode(&metadata).map_err(|e| sqlx::Error::Decode(e.into()))?;
1007
1008 Ok(SqlEvent(evento_core::Event {
1009 id: Ulid::from_string(sqlx::Row::try_get(row, "id")?)
1010 .map_err(|err| sqlx::Error::InvalidArgument(err.to_string()))?,
1011 aggregate_id: sqlx::Row::try_get(row, "aggregator_id")?,
1012 aggregate_type: sqlx::Row::try_get(row, "aggregator_type")?,
1013 version: version as u16,
1014 name: sqlx::Row::try_get(row, "name")?,
1015 routing_key: sqlx::Row::try_get(row, "routing_key")?,
1016 data: sqlx::Row::try_get(row, "data")?,
1017 timestamp: timestamp as u64,
1018 timestamp_subsec: timestamp_subsec as u32,
1019 metadata,
1020 }))
1021 }
1022}