// file: kb_lib/src/db/queries/pool.rs //! Queries for `k_sol_pools`. /// Inserts or updates one normalized pool row by address. pub async fn query_pools_upsert( database: &crate::Database, dto: &crate::PoolDto, ) -> Result { match database.connection() { crate::DatabaseConnection::Sqlite(pool) => { let query_result = sqlx::query( r#" INSERT INTO k_sol_pools ( dex_id, address, pool_kind, status, first_seen_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(address) DO UPDATE SET dex_id = excluded.dex_id, pool_kind = excluded.pool_kind, status = excluded.status, updated_at = excluded.updated_at "#, ) .bind(dto.dex_id) .bind(dto.address.clone()) .bind(dto.pool_kind.to_i16()) .bind(dto.status.to_i16()) .bind(dto.first_seen_at.to_rfc3339()) .bind(dto.updated_at.to_rfc3339()) .execute(pool) .await; if let Err(error) = query_result { return Err(crate::Error::Db(format!( "cannot upsert k_sol_pools on sqlite: {}", error ))); } let id_result = sqlx::query_scalar::( r#" SELECT id FROM k_sol_pools WHERE address = ? LIMIT 1 "#, ) .bind(dto.address.clone()) .fetch_one(pool) .await; match id_result { Ok(id) => return Ok(id), Err(error) => { return Err(crate::Error::Db(format!( "cannot fetch k_sol_pools id for address '{}' on sqlite: {}", dto.address, error ))); }, } }, } } /// Reads one normalized pool row by address. pub async fn query_pools_get_by_address( database: &crate::Database, address: &str, ) -> Result, crate::Error> { match database.connection() { crate::DatabaseConnection::Sqlite(pool) => { let query_result = sqlx::query_as::( r#" SELECT id, dex_id, address, pool_kind, status, first_seen_at, updated_at FROM k_sol_pools WHERE address = ? LIMIT 1 "#, ) .bind(address) .fetch_optional(pool) .await; let entity_option = match query_result { Ok(entity_option) => entity_option, Err(error) => { return Err(crate::Error::Db(format!( "cannot read k_sol_pools '{}' on sqlite: {}", address, error ))); }, }; match entity_option { Some(entity) => { let dto_result = crate::PoolDto::try_from(entity); match dto_result { Ok(dto) => return Ok(Some(dto)), Err(error) => return Err(error), } }, None => return Ok(None), } }, } } /// Lists normalized pools ordered by id ascending. pub async fn query_pools_list( database: &crate::Database, ) -> Result, crate::Error> { match database.connection() { crate::DatabaseConnection::Sqlite(pool) => { let query_result = sqlx::query_as::( r#" SELECT id, dex_id, address, pool_kind, status, first_seen_at, updated_at FROM k_sol_pools ORDER BY id ASC "#, ) .fetch_all(pool) .await; let entities = match query_result { Ok(entities) => entities, Err(error) => { return Err(crate::Error::Db(format!( "cannot list k_sol_pools on sqlite: {}", error ))); }, }; let mut dtos = std::vec::Vec::new(); for entity in entities { let dto_result = crate::PoolDto::try_from(entity); let dto = match dto_result { Ok(dto) => dto, Err(error) => return Err(error), }; dtos.push(dto); } return Ok(dtos); }, } } #[cfg(test)] mod tests { #[tokio::test] async fn pool_roundtrip_works() { let tempdir = tempfile::tempdir().expect("tempdir must succeed"); let database_path = tempdir.path().join("pool_roundtrip.sqlite3"); let config = crate::DatabaseConfig { enabled: true, backend: crate::DatabaseBackend::Sqlite, sqlite: crate::SqliteDatabaseConfig { path: database_path.to_string_lossy().to_string(), create_if_missing: true, busy_timeout_ms: 5000, max_connections: 1, auto_initialize_schema: true, use_wal: true, }, }; let database = crate::Database::connect_and_initialize(&config) .await .expect("database init must succeed"); let dex_id = crate::query_dexs_upsert( &database, &crate::DexDto::new("raydium_amm_v4".to_string(), "Raydium AMM v4".to_string(), None, None, true), ) .await .expect("dex upsert must succeed"); let pool_id = crate::query_pools_upsert( &database, &crate::PoolDto::new( dex_id, "Pool111111111111111111111111111111111111111".to_string(), crate::PoolKind::Amm, crate::PoolStatus::Active, ), ) .await .expect("pool upsert must succeed"); assert!(pool_id > 0); let pool = crate::query_pools_get_by_address( &database, "Pool111111111111111111111111111111111111111", ) .await .expect("get pool must succeed"); assert!(pool.is_some()); assert_eq!(pool.expect("pool must exist").pool_kind, crate::PoolKind::Amm); let pools = crate::query_pools_list(&database).await.expect("list pools must succeed"); assert_eq!(pools.len(), 1); } }