1use async_trait::async_trait;
40use chrono::{DateTime, Utc};
41use serde::{de::DeserializeOwned, Serialize};
42use sqlx::postgres::{PgPool, PgPoolOptions, PgQueryResult, PgRow};
43use sqlx::{Executor, Postgres, Row, Transaction as SqlxTransaction};
44use std::collections::HashMap;
45use std::sync::Arc;
46use tokio::sync::RwLock;
47use tracing::{debug, error, info, instrument, warn};
48
49use super::config::PostgreSQLConfig;
50use super::error::{StorageError, StorageResult};
51use super::traits::{RelationalStorage, SqlParam, SqlResult, Storage};
52use super::types::{
53 Batch, Operator, Operation, Query, SortDirection, StorageBackend, StorageEntry, StorageHealth,
54 StorageMetadata, StorageStats, Transaction, TransactionState,
55};
56
57#[derive(Debug, Clone)]
66pub struct PostgreSQLStorage {
67 pool: PgPool,
69
70 config: PostgreSQLConfig,
72
73 stats: Arc<RwLock<StorageStats>>,
75
76 transactions: Arc<RwLock<HashMap<String, TransactionContext>>>,
78
79 initialized: Arc<RwLock<bool>>,
81}
82
83#[derive(Debug)]
85struct TransactionContext {
86 transaction: Transaction,
87 operations: Vec<Operation>,
88}
89
90impl PostgreSQLStorage {
91 #[instrument(skip(config))]
105 pub async fn new(config: PostgreSQLConfig) -> StorageResult<Self> {
106 info!("Creating PostgreSQL storage backend");
107
108 let pool = PgPoolOptions::new()
110 .min_connections(2)
111 .max_connections(10)
112 .acquire_timeout(config.connect_timeout)
113 .idle_timeout(Some(std::time::Duration::from_secs(600)))
114 .max_lifetime(Some(std::time::Duration::from_secs(1800)))
115 .connect(&config.connection_url())
116 .await
117 .map_err(|e| {
118 error!("Failed to create connection pool: {}", e);
119 StorageError::ConnectionError {
120 source: Box::new(e),
121 }
122 })?;
123
124 info!("PostgreSQL connection pool created successfully");
125
126 Ok(Self {
127 pool,
128 config,
129 stats: Arc::new(RwLock::new(StorageStats::new())),
130 transactions: Arc::new(RwLock::new(HashMap::new())),
131 initialized: Arc::new(RwLock::new(false)),
132 })
133 }
134
135 pub fn pool(&self) -> &PgPool {
137 &self.pool
138 }
139
140 #[instrument(skip(self))]
142 async fn initialize_schema(&self) -> StorageResult<()> {
143 info!("Initializing PostgreSQL schema");
144
145 let start = std::time::Instant::now();
146
147 sqlx::query(
149 r#"
150 CREATE TABLE IF NOT EXISTS storage_entries (
151 table_name VARCHAR(255) NOT NULL,
152 key VARCHAR(255) NOT NULL,
153 value JSONB NOT NULL,
154 version BIGINT NOT NULL DEFAULT 1,
155 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
156 updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
157 expires_at TIMESTAMPTZ,
158 metadata JSONB,
159 PRIMARY KEY (table_name, key)
160 )
161 "#,
162 )
163 .execute(&self.pool)
164 .await
165 .map_err(|e| {
166 error!("Failed to create storage_entries table: {}", e);
167 StorageError::QueryError {
168 message: format!("Failed to create table: {}", e),
169 }
170 })?;
171
172 sqlx::query(
174 r#"
175 CREATE INDEX IF NOT EXISTS idx_expires_at
176 ON storage_entries(table_name, expires_at)
177 WHERE expires_at IS NOT NULL
178 "#,
179 )
180 .execute(&self.pool)
181 .await
182 .map_err(|e| {
183 error!("Failed to create expires_at index: {}", e);
184 StorageError::QueryError {
185 message: format!("Failed to create index: {}", e),
186 }
187 })?;
188
189 sqlx::query(
190 r#"
191 CREATE INDEX IF NOT EXISTS idx_updated_at
192 ON storage_entries(table_name, updated_at)
193 "#,
194 )
195 .execute(&self.pool)
196 .await
197 .map_err(|e| {
198 error!("Failed to create updated_at index: {}", e);
199 StorageError::QueryError {
200 message: format!("Failed to create index: {}", e),
201 }
202 })?;
203
204 sqlx::query(
206 r#"
207 CREATE INDEX IF NOT EXISTS idx_version
208 ON storage_entries(table_name, key, version)
209 "#,
210 )
211 .execute(&self.pool)
212 .await
213 .map_err(|e| {
214 error!("Failed to create version index: {}", e);
215 StorageError::QueryError {
216 message: format!("Failed to create index: {}", e),
217 }
218 })?;
219
220 let duration = start.elapsed();
221 info!("Schema initialized in {:?}", duration);
222
223 Ok(())
224 }
225
226 fn build_where_clause(&self, query: &Query) -> (String, Vec<String>) {
228 let mut conditions = Vec::new();
229 let mut params = Vec::new();
230 let mut param_index = 1;
231
232 conditions.push(format!("table_name = ${}", param_index));
234 params.push(query.table.clone());
235 param_index += 1;
236
237 for filter in &query.filters {
239 let condition = match filter.operator {
240 Operator::Equal => format!("value->>'{}' = ${}", filter.field, param_index),
241 Operator::NotEqual => format!("value->>'{}' != ${}", filter.field, param_index),
242 Operator::GreaterThan => format!("(value->>'{}')::numeric > ${}", filter.field, param_index),
243 Operator::GreaterThanOrEqual => {
244 format!("(value->>'{}')::numeric >= ${}", filter.field, param_index)
245 }
246 Operator::LessThan => format!("(value->>'{}')::numeric < ${}", filter.field, param_index),
247 Operator::LessThanOrEqual => {
248 format!("(value->>'{}')::numeric <= ${}", filter.field, param_index)
249 }
250 Operator::Like => format!("value->>'{}' LIKE ${}", filter.field, param_index),
251 Operator::NotLike => format!("value->>'{}' NOT LIKE ${}", filter.field, param_index),
252 Operator::IsNull => format!("value->>'{}' IS NULL", filter.field),
253 Operator::IsNotNull => format!("value->>'{}' IS NOT NULL", filter.field),
254 Operator::In | Operator::NotIn => {
255 continue;
257 }
258 };
259
260 conditions.push(condition);
261
262 if !matches!(filter.operator, Operator::IsNull | Operator::IsNotNull) {
264 params.push(self.value_to_string(&filter.value));
265 param_index += 1;
266 }
267 }
268
269 let where_clause = if conditions.is_empty() {
270 String::new()
271 } else {
272 format!("WHERE {}", conditions.join(" AND "))
273 };
274
275 (where_clause, params)
276 }
277
278 fn build_order_by_clause(&self, query: &Query) -> String {
280 if query.sort.is_empty() {
281 return String::new();
282 }
283
284 let order_items: Vec<String> = query
285 .sort
286 .iter()
287 .map(|sort| {
288 let direction = match sort.direction {
289 SortDirection::Ascending => "ASC",
290 SortDirection::Descending => "DESC",
291 };
292 format!("value->>'{}' {}", sort.field, direction)
293 })
294 .collect();
295
296 format!("ORDER BY {}", order_items.join(", "))
297 }
298
299 fn build_limit_offset_clause(&self, query: &Query) -> String {
301 let mut clause = String::new();
302
303 if let Some(limit) = query.limit {
304 clause.push_str(&format!(" LIMIT {}", limit));
305 }
306
307 if let Some(offset) = query.offset {
308 clause.push_str(&format!(" OFFSET {}", offset));
309 }
310
311 clause
312 }
313
314 fn value_to_string(&self, value: &super::types::Value) -> String {
316 match value {
317 super::types::Value::String(s) => s.clone(),
318 super::types::Value::Integer(i) => i.to_string(),
319 super::types::Value::Float(f) => f.to_string(),
320 super::types::Value::Boolean(b) => b.to_string(),
321 super::types::Value::DateTime(dt) => dt.to_rfc3339(),
322 super::types::Value::List(_) => "[]".to_string(),
323 super::types::Value::Null => "null".to_string(),
324 }
325 }
326
327 fn parse_row<T: DeserializeOwned>(&self, row: &PgRow) -> StorageResult<StorageEntry<T>> {
329 let key: String = row.try_get("key").map_err(|e| StorageError::QueryError {
330 message: format!("Failed to get key: {}", e),
331 })?;
332
333 let value_json: serde_json::Value = row.try_get("value").map_err(|e| StorageError::QueryError {
334 message: format!("Failed to get value: {}", e),
335 })?;
336
337 let value: T = serde_json::from_value(value_json.clone()).map_err(|e| {
338 StorageError::DeserializationError {
339 source: Box::new(e),
340 }
341 })?;
342
343 let version: i64 = row.try_get("version").map_err(|e| StorageError::QueryError {
344 message: format!("Failed to get version: {}", e),
345 })?;
346
347 let created_at: DateTime<Utc> = row.try_get("created_at").map_err(|e| StorageError::QueryError {
348 message: format!("Failed to get created_at: {}", e),
349 })?;
350
351 let updated_at: DateTime<Utc> = row.try_get("updated_at").map_err(|e| StorageError::QueryError {
352 message: format!("Failed to get updated_at: {}", e),
353 })?;
354
355 let expires_at: Option<DateTime<Utc>> = row
356 .try_get("expires_at")
357 .ok();
358
359 let metadata_json: Option<serde_json::Value> = row
360 .try_get("metadata")
361 .ok();
362
363 let metadata: HashMap<String, String> = if let Some(json) = metadata_json {
364 serde_json::from_value(json).unwrap_or_default()
365 } else {
366 HashMap::new()
367 };
368
369 Ok(StorageEntry {
370 key,
371 value,
372 version: version as u64,
373 created_at,
374 updated_at,
375 expires_at,
376 metadata,
377 })
378 }
379
380 async fn execute_with_retry<F, T>(&self, operation: F) -> StorageResult<T>
382 where
383 F: Fn() -> futures::future::BoxFuture<'static, StorageResult<T>>,
384 {
385 let max_retries = 3;
386 let mut last_error = None;
387
388 for attempt in 0..max_retries {
389 match operation().await {
390 Ok(result) => return Ok(result),
391 Err(e) if e.is_retryable() && attempt < max_retries - 1 => {
392 warn!("Retryable error on attempt {}: {}", attempt + 1, e);
393 last_error = Some(e);
394 tokio::time::sleep(std::time::Duration::from_millis(100 * (attempt + 1) as u64))
395 .await;
396 }
397 Err(e) => return Err(e),
398 }
399 }
400
401 Err(last_error.unwrap_or_else(|| StorageError::InternalError {
402 message: "Retry loop failed without error".to_string(),
403 }))
404 }
405
406 async fn record_operation(&self, success: bool, duration: std::time::Duration, bytes: u64) {
408 let mut stats = self.stats.write().await;
409
410 if success {
411 stats.record_success(duration.as_millis() as f64, bytes);
412 } else {
413 stats.record_failure();
414 }
415 }
416
417 #[instrument(skip(self))]
419 async fn cleanup_expired_entries(&self) -> StorageResult<usize> {
420 debug!("Cleaning up expired entries");
421
422 let result = sqlx::query(
423 r#"
424 DELETE FROM storage_entries
425 WHERE expires_at IS NOT NULL AND expires_at < NOW()
426 "#,
427 )
428 .execute(&self.pool)
429 .await
430 .map_err(|e| {
431 error!("Failed to cleanup expired entries: {}", e);
432 StorageError::QueryError {
433 message: format!("Failed to cleanup: {}", e),
434 }
435 })?;
436
437 let count = result.rows_affected() as usize;
438
439 if count > 0 {
440 info!("Cleaned up {} expired entries", count);
441 }
442
443 Ok(count)
444 }
445}
446
447#[async_trait]
448impl Storage for PostgreSQLStorage {
449 fn backend(&self) -> StorageBackend {
450 StorageBackend::PostgreSQL
451 }
452
453 #[instrument(skip(self))]
454 async fn metadata(&self) -> StorageResult<StorageMetadata> {
455 debug!("Fetching storage metadata");
456
457 let version_row: (String,) = sqlx::query_as("SELECT version()")
459 .fetch_one(&self.pool)
460 .await
461 .map_err(|e| StorageError::QueryError {
462 message: format!("Failed to get version: {}", e),
463 })?;
464
465 let version = version_row.0;
466
467 let capabilities = vec![
469 "transactions".to_string(),
470 "acid_compliance".to_string(),
471 "relational_queries".to_string(),
472 "jsonb_storage".to_string(),
473 "full_text_search".to_string(),
474 "indexes".to_string(),
475 "constraints".to_string(),
476 "versioning".to_string(),
477 "batch_operations".to_string(),
478 ];
479
480 let health = self.health_check().await?;
482
483 Ok(StorageMetadata {
484 backend: StorageBackend::PostgreSQL,
485 version,
486 capabilities,
487 status: health,
488 last_health_check: Utc::now(),
489 })
490 }
491
492 #[instrument(skip(self))]
493 async fn health_check(&self) -> StorageResult<StorageHealth> {
494 debug!("Performing health check");
495
496 match sqlx::query("SELECT 1").execute(&self.pool).await {
498 Ok(_) => {
499 let pool_size = self.pool.size();
501 let idle_conns = self.pool.num_idle();
502
503 if pool_size > 0 && idle_conns > 0 {
504 debug!("Health check passed: {} connections, {} idle", pool_size, idle_conns);
505 Ok(StorageHealth::Healthy)
506 } else {
507 warn!("Health check degraded: {} connections, {} idle", pool_size, idle_conns);
508 Ok(StorageHealth::Degraded)
509 }
510 }
511 Err(e) => {
512 error!("Health check failed: {}", e);
513 Ok(StorageHealth::Unhealthy)
514 }
515 }
516 }
517
518 #[instrument(skip(self))]
519 async fn stats(&self) -> StorageResult<StorageStats> {
520 debug!("Fetching storage statistics");
521
522 let stats = self.stats.read().await.clone();
523
524 let mut updated_stats = stats;
526 updated_stats.active_connections = self.pool.size() as usize;
527
528 Ok(updated_stats)
529 }
530
531 #[instrument(skip(self, value))]
532 async fn insert<T: Serialize + Send + Sync>(
533 &self,
534 table: &str,
535 key: &str,
536 value: &T,
537 ) -> StorageResult<()> {
538 debug!("Inserting entry: table={}, key={}", table, key);
539
540 let start = std::time::Instant::now();
541
542 let value_json = serde_json::to_value(value).map_err(|e| StorageError::SerializationError {
543 source: Box::new(e),
544 })?;
545
546 let result = sqlx::query(
547 r#"
548 INSERT INTO storage_entries (table_name, key, value, version, created_at, updated_at)
549 VALUES ($1, $2, $3, 1, NOW(), NOW())
550 ON CONFLICT (table_name, key) DO NOTHING
551 "#,
552 )
553 .bind(table)
554 .bind(key)
555 .bind(&value_json)
556 .execute(&self.pool)
557 .await
558 .map_err(|e| {
559 error!("Failed to insert entry: {}", e);
560 StorageError::QueryError {
561 message: format!("Insert failed: {}", e),
562 }
563 })?;
564
565 if result.rows_affected() == 0 {
566 return Err(StorageError::AlreadyExists {
567 entity: table.to_string(),
568 key: key.to_string(),
569 });
570 }
571
572 let duration = start.elapsed();
573 let bytes = value_json.to_string().len() as u64;
574 self.record_operation(true, duration, bytes).await;
575
576 debug!("Entry inserted successfully in {:?}", duration);
577 Ok(())
578 }
579
580 #[instrument(skip(self, value))]
581 async fn insert_with_ttl<T: Serialize + Send + Sync>(
582 &self,
583 table: &str,
584 key: &str,
585 value: &T,
586 ttl_seconds: i64,
587 ) -> StorageResult<()> {
588 debug!("Inserting entry with TTL: table={}, key={}, ttl={}s", table, key, ttl_seconds);
589
590 let start = std::time::Instant::now();
591
592 let value_json = serde_json::to_value(value).map_err(|e| StorageError::SerializationError {
593 source: Box::new(e),
594 })?;
595
596 let expires_at = Utc::now() + chrono::Duration::seconds(ttl_seconds);
597
598 let result = sqlx::query(
599 r#"
600 INSERT INTO storage_entries (table_name, key, value, version, created_at, updated_at, expires_at)
601 VALUES ($1, $2, $3, 1, NOW(), NOW(), $4)
602 ON CONFLICT (table_name, key) DO NOTHING
603 "#,
604 )
605 .bind(table)
606 .bind(key)
607 .bind(&value_json)
608 .bind(expires_at)
609 .execute(&self.pool)
610 .await
611 .map_err(|e| {
612 error!("Failed to insert entry with TTL: {}", e);
613 StorageError::QueryError {
614 message: format!("Insert with TTL failed: {}", e),
615 }
616 })?;
617
618 if result.rows_affected() == 0 {
619 return Err(StorageError::AlreadyExists {
620 entity: table.to_string(),
621 key: key.to_string(),
622 });
623 }
624
625 let duration = start.elapsed();
626 let bytes = value_json.to_string().len() as u64;
627 self.record_operation(true, duration, bytes).await;
628
629 debug!("Entry with TTL inserted successfully in {:?}", duration);
630 Ok(())
631 }
632
633 #[instrument(skip(self))]
634 async fn get<T: DeserializeOwned + Serialize + Send + Sync>(
635 &self,
636 table: &str,
637 key: &str,
638 ) -> StorageResult<Option<StorageEntry<T>>> {
639 debug!("Getting entry: table={}, key={}", table, key);
640
641 let start = std::time::Instant::now();
642
643 let row_result = sqlx::query(
644 r#"
645 SELECT key, value, version, created_at, updated_at, expires_at, metadata
646 FROM storage_entries
647 WHERE table_name = $1 AND key = $2
648 AND (expires_at IS NULL OR expires_at > NOW())
649 "#,
650 )
651 .bind(table)
652 .bind(key)
653 .fetch_optional(&self.pool)
654 .await
655 .map_err(|e| {
656 error!("Failed to get entry: {}", e);
657 StorageError::QueryError {
658 message: format!("Get failed: {}", e),
659 }
660 })?;
661
662 let duration = start.elapsed();
663
664 let result = if let Some(row) = row_result {
665 let entry = self.parse_row::<T>(&row)?;
666 let bytes = serde_json::to_string(&entry.value).unwrap_or_default().len() as u64;
667 self.record_operation(true, duration, bytes).await;
668 Some(entry)
669 } else {
670 self.record_operation(true, duration, 0).await;
671 None
672 };
673
674 debug!("Get operation completed in {:?}", duration);
675 Ok(result)
676 }
677
678 #[instrument(skip(self, value))]
679 async fn update<T: Serialize + Send + Sync>(
680 &self,
681 table: &str,
682 key: &str,
683 value: &T,
684 ) -> StorageResult<()> {
685 debug!("Updating entry: table={}, key={}", table, key);
686
687 let start = std::time::Instant::now();
688
689 let value_json = serde_json::to_value(value).map_err(|e| StorageError::SerializationError {
690 source: Box::new(e),
691 })?;
692
693 let result = sqlx::query(
694 r#"
695 UPDATE storage_entries
696 SET value = $3, version = version + 1, updated_at = NOW()
697 WHERE table_name = $1 AND key = $2
698 "#,
699 )
700 .bind(table)
701 .bind(key)
702 .bind(&value_json)
703 .execute(&self.pool)
704 .await
705 .map_err(|e| {
706 error!("Failed to update entry: {}", e);
707 StorageError::QueryError {
708 message: format!("Update failed: {}", e),
709 }
710 })?;
711
712 if result.rows_affected() == 0 {
713 return Err(StorageError::NotFound {
714 entity: table.to_string(),
715 key: key.to_string(),
716 });
717 }
718
719 let duration = start.elapsed();
720 let bytes = value_json.to_string().len() as u64;
721 self.record_operation(true, duration, bytes).await;
722
723 debug!("Entry updated successfully in {:?}", duration);
724 Ok(())
725 }
726
727 #[instrument(skip(self, value))]
728 async fn update_versioned<T: Serialize + Send + Sync>(
729 &self,
730 table: &str,
731 key: &str,
732 value: &T,
733 expected_version: u64,
734 ) -> StorageResult<()> {
735 debug!(
736 "Updating entry with version check: table={}, key={}, expected_version={}",
737 table, key, expected_version
738 );
739
740 let start = std::time::Instant::now();
741
742 let value_json = serde_json::to_value(value).map_err(|e| StorageError::SerializationError {
743 source: Box::new(e),
744 })?;
745
746 let result = sqlx::query(
747 r#"
748 UPDATE storage_entries
749 SET value = $3, version = version + 1, updated_at = NOW()
750 WHERE table_name = $1 AND key = $2 AND version = $4
751 "#,
752 )
753 .bind(table)
754 .bind(key)
755 .bind(&value_json)
756 .bind(expected_version as i64)
757 .execute(&self.pool)
758 .await
759 .map_err(|e| {
760 error!("Failed to update entry with version: {}", e);
761 StorageError::QueryError {
762 message: format!("Versioned update failed: {}", e),
763 }
764 })?;
765
766 if result.rows_affected() == 0 {
767 return Err(StorageError::ConstraintViolation {
768 message: format!("Version mismatch or entry not found: expected version {}", expected_version),
769 });
770 }
771
772 let duration = start.elapsed();
773 let bytes = value_json.to_string().len() as u64;
774 self.record_operation(true, duration, bytes).await;
775
776 debug!("Versioned entry updated successfully in {:?}", duration);
777 Ok(())
778 }
779
780 #[instrument(skip(self))]
781 async fn delete(&self, table: &str, key: &str) -> StorageResult<()> {
782 debug!("Deleting entry: table={}, key={}", table, key);
783
784 let start = std::time::Instant::now();
785
786 let result = sqlx::query(
787 r#"
788 DELETE FROM storage_entries
789 WHERE table_name = $1 AND key = $2
790 "#,
791 )
792 .bind(table)
793 .bind(key)
794 .execute(&self.pool)
795 .await
796 .map_err(|e| {
797 error!("Failed to delete entry: {}", e);
798 StorageError::QueryError {
799 message: format!("Delete failed: {}", e),
800 }
801 })?;
802
803 if result.rows_affected() == 0 {
804 return Err(StorageError::NotFound {
805 entity: table.to_string(),
806 key: key.to_string(),
807 });
808 }
809
810 let duration = start.elapsed();
811 self.record_operation(true, duration, 0).await;
812
813 debug!("Entry deleted successfully in {:?}", duration);
814 Ok(())
815 }
816
817 #[instrument(skip(self))]
818 async fn exists(&self, table: &str, key: &str) -> StorageResult<bool> {
819 debug!("Checking if entry exists: table={}, key={}", table, key);
820
821 let start = std::time::Instant::now();
822
823 let result: (bool,) = sqlx::query_as(
824 r#"
825 SELECT EXISTS(
826 SELECT 1 FROM storage_entries
827 WHERE table_name = $1 AND key = $2
828 AND (expires_at IS NULL OR expires_at > NOW())
829 )
830 "#,
831 )
832 .bind(table)
833 .bind(key)
834 .fetch_one(&self.pool)
835 .await
836 .map_err(|e| {
837 error!("Failed to check existence: {}", e);
838 StorageError::QueryError {
839 message: format!("Exists check failed: {}", e),
840 }
841 })?;
842
843 let duration = start.elapsed();
844 self.record_operation(true, duration, 0).await;
845
846 debug!("Exists check completed in {:?}: {}", duration, result.0);
847 Ok(result.0)
848 }
849
850 #[instrument(skip(self, batch))]
851 async fn execute_batch(&self, batch: Batch) -> StorageResult<()> {
852 debug!("Executing batch operation with {} operations", batch.operations.len());
853
854 if batch.operations.is_empty() {
855 return Ok(());
856 }
857
858 let start = std::time::Instant::now();
859
860 let mut tx = self.pool.begin().await.map_err(|e| {
862 error!("Failed to begin transaction: {}", e);
863 StorageError::TransactionError {
864 message: format!("Failed to begin transaction: {}", e),
865 }
866 })?;
867
868 for operation in &batch.operations {
869 match operation {
870 Operation::Insert { table, data } => {
871 let entry: serde_json::Value = serde_json::from_slice(data).map_err(|e| {
873 StorageError::DeserializationError {
874 source: Box::new(e),
875 }
876 })?;
877
878 let key = entry.get("key")
879 .and_then(|k| k.as_str())
880 .ok_or_else(|| StorageError::QueryError {
881 message: "Missing key in insert operation".to_string(),
882 })?;
883
884 sqlx::query(
885 r#"
886 INSERT INTO storage_entries (table_name, key, value, version, created_at, updated_at)
887 VALUES ($1, $2, $3, 1, NOW(), NOW())
888 ON CONFLICT (table_name, key) DO NOTHING
889 "#,
890 )
891 .bind(table)
892 .bind(key)
893 .bind(&entry)
894 .execute(&mut *tx)
895 .await
896 .map_err(|e| StorageError::QueryError {
897 message: format!("Batch insert failed: {}", e),
898 })?;
899 }
900 Operation::Update { table, key, data } => {
901 let value: serde_json::Value = serde_json::from_slice(data).map_err(|e| {
902 StorageError::DeserializationError {
903 source: Box::new(e),
904 }
905 })?;
906
907 sqlx::query(
908 r#"
909 UPDATE storage_entries
910 SET value = $3, version = version + 1, updated_at = NOW()
911 WHERE table_name = $1 AND key = $2
912 "#,
913 )
914 .bind(table)
915 .bind(key)
916 .bind(&value)
917 .execute(&mut *tx)
918 .await
919 .map_err(|e| StorageError::QueryError {
920 message: format!("Batch update failed: {}", e),
921 })?;
922 }
923 Operation::Delete { table, key } => {
924 sqlx::query(
925 r#"
926 DELETE FROM storage_entries
927 WHERE table_name = $1 AND key = $2
928 "#,
929 )
930 .bind(table)
931 .bind(key)
932 .execute(&mut *tx)
933 .await
934 .map_err(|e| StorageError::QueryError {
935 message: format!("Batch delete failed: {}", e),
936 })?;
937 }
938 }
939 }
940
941 tx.commit().await.map_err(|e| {
942 error!("Failed to commit batch transaction: {}", e);
943 StorageError::TransactionError {
944 message: format!("Failed to commit: {}", e),
945 }
946 })?;
947
948 let duration = start.elapsed();
949 self.record_operation(true, duration, 0).await;
950
951 info!("Batch operation completed in {:?}", duration);
952 Ok(())
953 }
954
955 #[instrument(skip(self, query))]
956 async fn query<T: DeserializeOwned + Send + Sync + 'static>(&self, query: Query) -> StorageResult<Vec<StorageEntry<T>>> {
957 debug!("Executing query on table: {}", query.table);
958
959 let start = std::time::Instant::now();
960
961 let (where_clause, _params) = self.build_where_clause(&query);
962 let order_by_clause = self.build_order_by_clause(&query);
963 let limit_offset_clause = self.build_limit_offset_clause(&query);
964
965 let sql = format!(
966 r#"
967 SELECT key, value, version, created_at, updated_at, expires_at, metadata
968 FROM storage_entries
969 {}
970 {}
971 {}
972 "#,
973 where_clause, order_by_clause, limit_offset_clause
974 );
975
976 let mut query_builder = sqlx::query(&sql);
977
978 query_builder = query_builder.bind(&query.table);
980
981 let rows = query_builder.fetch_all(&self.pool).await.map_err(|e| {
983 error!("Failed to execute query: {}", e);
984 StorageError::QueryError {
985 message: format!("Query failed: {}", e),
986 }
987 })?;
988
989 let mut results = Vec::new();
990 for row in rows {
991 let entry = self.parse_row::<T>(&row)?;
992 results.push(entry);
993 }
994
995 let duration = start.elapsed();
996 self.record_operation(true, duration, 0).await;
997
998 debug!("Query returned {} results in {:?}", results.len(), duration);
999 Ok(results)
1000 }
1001
1002 #[instrument(skip(self, query))]
1003 async fn count(&self, query: Query) -> StorageResult<usize> {
1004 debug!("Counting entries for table: {}", query.table);
1005
1006 let start = std::time::Instant::now();
1007
1008 let (where_clause, _params) = self.build_where_clause(&query);
1009
1010 let sql = format!(
1011 r#"
1012 SELECT COUNT(*) as count
1013 FROM storage_entries
1014 {}
1015 "#,
1016 where_clause
1017 );
1018
1019 let result: (i64,) = sqlx::query_as(&sql)
1020 .bind(&query.table)
1021 .fetch_one(&self.pool)
1022 .await
1023 .map_err(|e| {
1024 error!("Failed to count entries: {}", e);
1025 StorageError::QueryError {
1026 message: format!("Count failed: {}", e),
1027 }
1028 })?;
1029
1030 let duration = start.elapsed();
1031 self.record_operation(true, duration, 0).await;
1032
1033 debug!("Count completed in {:?}: {}", duration, result.0);
1034 Ok(result.0 as usize)
1035 }
1036
1037 #[instrument(skip(self))]
1038 async fn begin_transaction(&self) -> StorageResult<Transaction> {
1039 debug!("Beginning transaction");
1040
1041 let transaction = Transaction::new();
1042 let context = TransactionContext {
1043 transaction: transaction.clone(),
1044 operations: Vec::new(),
1045 };
1046
1047 let mut transactions = self.transactions.write().await;
1048 transactions.insert(transaction.id.clone(), context);
1049
1050 info!("Transaction started: {}", transaction.id);
1051 Ok(transaction)
1052 }
1053
1054 #[instrument(skip(self, transaction))]
1055 async fn commit_transaction(&self, mut transaction: Transaction) -> StorageResult<()> {
1056 debug!("Committing transaction: {}", transaction.id);
1057
1058 let mut transactions = self.transactions.write().await;
1059 let context = transactions.remove(&transaction.id).ok_or_else(|| {
1060 StorageError::TransactionError {
1061 message: format!("Transaction not found: {}", transaction.id),
1062 }
1063 })?;
1064
1065 let mut tx = self.pool.begin().await.map_err(|e| {
1067 error!("Failed to begin SQL transaction: {}", e);
1068 StorageError::TransactionError {
1069 message: format!("Failed to begin transaction: {}", e),
1070 }
1071 })?;
1072
1073 for operation in &context.operations {
1075 match operation {
1076 Operation::Insert { table, data } => {
1077 let entry: serde_json::Value = serde_json::from_slice(data).map_err(|e| {
1078 StorageError::DeserializationError {
1079 source: Box::new(e),
1080 }
1081 })?;
1082
1083 let key = entry.get("key")
1084 .and_then(|k| k.as_str())
1085 .ok_or_else(|| StorageError::QueryError {
1086 message: "Missing key in insert operation".to_string(),
1087 })?;
1088
1089 sqlx::query(
1090 r#"
1091 INSERT INTO storage_entries (table_name, key, value, version, created_at, updated_at)
1092 VALUES ($1, $2, $3, 1, NOW(), NOW())
1093 "#,
1094 )
1095 .bind(table)
1096 .bind(key)
1097 .bind(&entry)
1098 .execute(&mut *tx)
1099 .await
1100 .map_err(|e| StorageError::QueryError {
1101 message: format!("Transaction insert failed: {}", e),
1102 })?;
1103 }
1104 Operation::Update { table, key, data } => {
1105 let value: serde_json::Value = serde_json::from_slice(data).map_err(|e| {
1106 StorageError::DeserializationError {
1107 source: Box::new(e),
1108 }
1109 })?;
1110
1111 sqlx::query(
1112 r#"
1113 UPDATE storage_entries
1114 SET value = $3, version = version + 1, updated_at = NOW()
1115 WHERE table_name = $1 AND key = $2
1116 "#,
1117 )
1118 .bind(table)
1119 .bind(key)
1120 .bind(&value)
1121 .execute(&mut *tx)
1122 .await
1123 .map_err(|e| StorageError::QueryError {
1124 message: format!("Transaction update failed: {}", e),
1125 })?;
1126 }
1127 Operation::Delete { table, key } => {
1128 sqlx::query(
1129 r#"
1130 DELETE FROM storage_entries
1131 WHERE table_name = $1 AND key = $2
1132 "#,
1133 )
1134 .bind(table)
1135 .bind(key)
1136 .execute(&mut *tx)
1137 .await
1138 .map_err(|e| StorageError::QueryError {
1139 message: format!("Transaction delete failed: {}", e),
1140 })?;
1141 }
1142 }
1143 }
1144
1145 tx.commit().await.map_err(|e| {
1147 error!("Failed to commit SQL transaction: {}", e);
1148 StorageError::TransactionError {
1149 message: format!("Failed to commit: {}", e),
1150 }
1151 })?;
1152
1153 transaction.state = TransactionState::Committed;
1154 info!("Transaction committed: {}", transaction.id);
1155 Ok(())
1156 }
1157
1158 #[instrument(skip(self, transaction))]
1159 async fn rollback_transaction(&self, mut transaction: Transaction) -> StorageResult<()> {
1160 debug!("Rolling back transaction: {}", transaction.id);
1161
1162 let mut transactions = self.transactions.write().await;
1163 transactions.remove(&transaction.id);
1164
1165 transaction.state = TransactionState::RolledBack;
1166 info!("Transaction rolled back: {}", transaction.id);
1167 Ok(())
1168 }
1169
1170 #[instrument(skip(self))]
1171 async fn scan_prefix(&self, table: &str, prefix: &str) -> StorageResult<Vec<String>> {
1172 debug!("Scanning keys with prefix: table={}, prefix={}", table, prefix);
1173
1174 let start = std::time::Instant::now();
1175
1176 let pattern = format!("{}%", prefix);
1177
1178 let rows: Vec<(String,)> = sqlx::query_as(
1179 r#"
1180 SELECT key
1181 FROM storage_entries
1182 WHERE table_name = $1 AND key LIKE $2
1183 AND (expires_at IS NULL OR expires_at > NOW())
1184 ORDER BY key
1185 "#,
1186 )
1187 .bind(table)
1188 .bind(&pattern)
1189 .fetch_all(&self.pool)
1190 .await
1191 .map_err(|e| {
1192 error!("Failed to scan prefix: {}", e);
1193 StorageError::QueryError {
1194 message: format!("Prefix scan failed: {}", e),
1195 }
1196 })?;
1197
1198 let keys: Vec<String> = rows.into_iter().map(|(key,)| key).collect();
1199
1200 let duration = start.elapsed();
1201 self.record_operation(true, duration, 0).await;
1202
1203 debug!("Prefix scan returned {} keys in {:?}", keys.len(), duration);
1204 Ok(keys)
1205 }
1206
1207 #[instrument(skip(self, keys))]
1208 async fn get_multi<T: DeserializeOwned + Serialize + Send + Sync>(
1209 &self,
1210 table: &str,
1211 keys: Vec<String>,
1212 ) -> StorageResult<Vec<Option<StorageEntry<T>>>> {
1213 debug!("Getting multiple entries: table={}, count={}", table, keys.len());
1214
1215 if keys.is_empty() {
1216 return Ok(Vec::new());
1217 }
1218
1219 let start = std::time::Instant::now();
1220
1221 let mut results = Vec::new();
1222
1223 for key in keys {
1224 let entry = self.get::<T>(table, &key).await?;
1225 results.push(entry);
1226 }
1227
1228 let duration = start.elapsed();
1229 debug!("Multi-get completed in {:?}", duration);
1230 Ok(results)
1231 }
1232
1233 #[instrument(skip(self, keys))]
1234 async fn delete_multi(&self, table: &str, keys: Vec<String>) -> StorageResult<()> {
1235 debug!("Deleting multiple entries: table={}, count={}", table, keys.len());
1236
1237 if keys.is_empty() {
1238 return Ok(());
1239 }
1240
1241 let start = std::time::Instant::now();
1242
1243 let placeholders: Vec<String> = (1..=keys.len())
1245 .map(|i| format!("${}", i + 1))
1246 .collect();
1247
1248 let sql = format!(
1249 r#"
1250 DELETE FROM storage_entries
1251 WHERE table_name = $1 AND key IN ({})
1252 "#,
1253 placeholders.join(", ")
1254 );
1255
1256 let mut query = sqlx::query(&sql).bind(table);
1257
1258 for key in keys {
1259 query = query.bind(key);
1260 }
1261
1262 let result = query.execute(&self.pool).await.map_err(|e| {
1263 error!("Failed to delete multiple entries: {}", e);
1264 StorageError::QueryError {
1265 message: format!("Multi-delete failed: {}", e),
1266 }
1267 })?;
1268
1269 let duration = start.elapsed();
1270 self.record_operation(true, duration, 0).await;
1271
1272 info!("Deleted {} entries in {:?}", result.rows_affected(), duration);
1273 Ok(())
1274 }
1275
1276 #[instrument(skip(self, new_value))]
1277 async fn compare_and_swap<T: Serialize + Send + Sync>(
1278 &self,
1279 table: &str,
1280 key: &str,
1281 expected_version: u64,
1282 new_value: &T,
1283 ) -> StorageResult<bool> {
1284 debug!(
1285 "Compare-and-swap: table={}, key={}, expected_version={}",
1286 table, key, expected_version
1287 );
1288
1289 let start = std::time::Instant::now();
1290
1291 let value_json = serde_json::to_value(new_value).map_err(|e| {
1292 StorageError::SerializationError {
1293 source: Box::new(e),
1294 }
1295 })?;
1296
1297 let result = sqlx::query(
1298 r#"
1299 UPDATE storage_entries
1300 SET value = $3, version = version + 1, updated_at = NOW()
1301 WHERE table_name = $1 AND key = $2 AND version = $4
1302 "#,
1303 )
1304 .bind(table)
1305 .bind(key)
1306 .bind(&value_json)
1307 .bind(expected_version as i64)
1308 .execute(&self.pool)
1309 .await
1310 .map_err(|e| {
1311 error!("Failed to compare-and-swap: {}", e);
1312 StorageError::QueryError {
1313 message: format!("CAS failed: {}", e),
1314 }
1315 })?;
1316
1317 let success = result.rows_affected() > 0;
1318
1319 let duration = start.elapsed();
1320 self.record_operation(true, duration, 0).await;
1321
1322 debug!("Compare-and-swap completed in {:?}: {}", duration, success);
1323 Ok(success)
1324 }
1325
1326 #[instrument(skip(self))]
1327 async fn get_modified_since<T: DeserializeOwned + Send + Sync + 'static>(
1328 &self,
1329 table: &str,
1330 since: DateTime<Utc>,
1331 ) -> StorageResult<Vec<StorageEntry<T>>> {
1332 debug!("Getting entries modified since: table={}, since={}", table, since);
1333
1334 let start = std::time::Instant::now();
1335
1336 let rows = sqlx::query(
1337 r#"
1338 SELECT key, value, version, created_at, updated_at, expires_at, metadata
1339 FROM storage_entries
1340 WHERE table_name = $1 AND updated_at > $2
1341 AND (expires_at IS NULL OR expires_at > NOW())
1342 ORDER BY updated_at ASC
1343 "#,
1344 )
1345 .bind(table)
1346 .bind(since)
1347 .fetch_all(&self.pool)
1348 .await
1349 .map_err(|e| {
1350 error!("Failed to get modified entries: {}", e);
1351 StorageError::QueryError {
1352 message: format!("Get modified since failed: {}", e),
1353 }
1354 })?;
1355
1356 let mut results = Vec::new();
1357 for row in rows {
1358 let entry = self.parse_row::<T>(&row)?;
1359 results.push(entry);
1360 }
1361
1362 let duration = start.elapsed();
1363 self.record_operation(true, duration, 0).await;
1364
1365 debug!("Found {} modified entries in {:?}", results.len(), duration);
1366 Ok(results)
1367 }
1368
1369 #[instrument(skip(self))]
1370 async fn get_expiring_before<T: DeserializeOwned + Send + Sync + 'static>(
1371 &self,
1372 table: &str,
1373 before: DateTime<Utc>,
1374 ) -> StorageResult<Vec<StorageEntry<T>>> {
1375 debug!("Getting entries expiring before: table={}, before={}", table, before);
1376
1377 let start = std::time::Instant::now();
1378
1379 let rows = sqlx::query(
1380 r#"
1381 SELECT key, value, version, created_at, updated_at, expires_at, metadata
1382 FROM storage_entries
1383 WHERE table_name = $1 AND expires_at IS NOT NULL AND expires_at < $2
1384 ORDER BY expires_at ASC
1385 "#,
1386 )
1387 .bind(table)
1388 .bind(before)
1389 .fetch_all(&self.pool)
1390 .await
1391 .map_err(|e| {
1392 error!("Failed to get expiring entries: {}", e);
1393 StorageError::QueryError {
1394 message: format!("Get expiring before failed: {}", e),
1395 }
1396 })?;
1397
1398 let mut results = Vec::new();
1399 for row in rows {
1400 let entry = self.parse_row::<T>(&row)?;
1401 results.push(entry);
1402 }
1403
1404 let duration = start.elapsed();
1405 self.record_operation(true, duration, 0).await;
1406
1407 debug!("Found {} expiring entries in {:?}", results.len(), duration);
1408 Ok(results)
1409 }
1410
1411 #[instrument(skip(self))]
1412 async fn cleanup_expired(&self, table: &str) -> StorageResult<usize> {
1413 debug!("Cleaning up expired entries in table: {}", table);
1414
1415 let start = std::time::Instant::now();
1416
1417 let result = sqlx::query(
1418 r#"
1419 DELETE FROM storage_entries
1420 WHERE table_name = $1 AND expires_at IS NOT NULL AND expires_at < NOW()
1421 "#,
1422 )
1423 .bind(table)
1424 .execute(&self.pool)
1425 .await
1426 .map_err(|e| {
1427 error!("Failed to cleanup expired entries: {}", e);
1428 StorageError::QueryError {
1429 message: format!("Cleanup failed: {}", e),
1430 }
1431 })?;
1432
1433 let count = result.rows_affected() as usize;
1434
1435 let duration = start.elapsed();
1436 self.record_operation(true, duration, 0).await;
1437
1438 info!("Cleaned up {} expired entries in {:?}", count, duration);
1439 Ok(count)
1440 }
1441
1442 #[instrument(skip(self))]
1443 async fn compact(&self) -> StorageResult<()> {
1444 info!("Running VACUUM on database");
1445
1446 let start = std::time::Instant::now();
1447
1448 sqlx::query("VACUUM ANALYZE storage_entries")
1450 .execute(&self.pool)
1451 .await
1452 .map_err(|e| {
1453 error!("Failed to vacuum database: {}", e);
1454 StorageError::QueryError {
1455 message: format!("Vacuum failed: {}", e),
1456 }
1457 })?;
1458
1459 let duration = start.elapsed();
1460 info!("Database vacuumed in {:?}", duration);
1461 Ok(())
1462 }
1463
1464 #[instrument(skip(self))]
1465 async fn flush(&self) -> StorageResult<()> {
1466 debug!("Flushing pending writes (no-op for PostgreSQL)");
1467 Ok(())
1469 }
1470
1471 #[instrument(skip(self))]
1472 async fn initialize(&mut self) -> StorageResult<()> {
1473 info!("Initializing PostgreSQL storage");
1474
1475 let mut initialized = self.initialized.write().await;
1476 if *initialized {
1477 warn!("Storage already initialized");
1478 return Ok(());
1479 }
1480
1481 self.initialize_schema().await?;
1483
1484 if self.config.enable_migrations {
1486 info!("Running migrations");
1487 }
1489
1490 *initialized = true;
1491 info!("PostgreSQL storage initialized successfully");
1492 Ok(())
1493 }
1494
1495 #[instrument(skip(self))]
1496 async fn shutdown(&mut self) -> StorageResult<()> {
1497 info!("Shutting down PostgreSQL storage");
1498
1499 self.pool.close().await;
1501
1502 let mut initialized = self.initialized.write().await;
1503 *initialized = false;
1504
1505 info!("PostgreSQL storage shutdown complete");
1506 Ok(())
1507 }
1508}
1509
1510#[async_trait]
1511impl RelationalStorage for PostgreSQLStorage {
1512 #[instrument(skip(self, params))]
1513 async fn execute_sql(&self, sql: &str, params: Vec<SqlParam>) -> StorageResult<SqlResult> {
1514 debug!("Executing SQL: {}", sql);
1515
1516 let start = std::time::Instant::now();
1517
1518 let mut query = sqlx::query(sql);
1519
1520 for param in params {
1521 query = match param {
1522 SqlParam::String(s) => query.bind(s),
1523 SqlParam::Integer(i) => query.bind(i),
1524 SqlParam::Float(f) => query.bind(f),
1525 SqlParam::Boolean(b) => query.bind(b),
1526 SqlParam::DateTime(dt) => query.bind(dt),
1527 SqlParam::Null => query.bind(None::<String>),
1528 };
1529 }
1530
1531 let result = query.execute(&self.pool).await.map_err(|e| {
1532 error!("Failed to execute SQL: {}", e);
1533 StorageError::QueryError {
1534 message: format!("SQL execution failed: {}", e),
1535 }
1536 })?;
1537
1538 let duration = start.elapsed();
1539 self.record_operation(true, duration, 0).await;
1540
1541 debug!("SQL executed in {:?}, rows affected: {}", duration, result.rows_affected());
1542 Ok(SqlResult {
1543 rows_affected: result.rows_affected(),
1544 })
1545 }
1546
1547 #[instrument(skip(self, params))]
1548 async fn query_sql<T: DeserializeOwned + Send + Sync>(
1549 &self,
1550 sql: &str,
1551 params: Vec<SqlParam>,
1552 ) -> StorageResult<Vec<T>> {
1553 debug!("Querying SQL: {}", sql);
1554
1555 let start = std::time::Instant::now();
1556
1557 let mut query = sqlx::query(sql);
1558
1559 for param in params {
1560 query = match param {
1561 SqlParam::String(s) => query.bind(s),
1562 SqlParam::Integer(i) => query.bind(i),
1563 SqlParam::Float(f) => query.bind(f),
1564 SqlParam::Boolean(b) => query.bind(b),
1565 SqlParam::DateTime(dt) => query.bind(dt),
1566 SqlParam::Null => query.bind(None::<String>),
1567 };
1568 }
1569
1570 let rows = query.fetch_all(&self.pool).await.map_err(|e| {
1571 error!("Failed to query SQL: {}", e);
1572 StorageError::QueryError {
1573 message: format!("SQL query failed: {}", e),
1574 }
1575 })?;
1576
1577 let mut results = Vec::new();
1578 for row in rows {
1579 let value_json: serde_json::Value = row.try_get("value").map_err(|e| {
1581 StorageError::QueryError {
1582 message: format!("Failed to get value column: {}", e),
1583 }
1584 })?;
1585
1586 let result: T = serde_json::from_value(value_json).map_err(|e| {
1587 StorageError::DeserializationError {
1588 source: Box::new(e),
1589 }
1590 })?;
1591
1592 results.push(result);
1593 }
1594
1595 let duration = start.elapsed();
1596 self.record_operation(true, duration, 0).await;
1597
1598 debug!("SQL query returned {} rows in {:?}", results.len(), duration);
1599 Ok(results)
1600 }
1601
1602 #[instrument(skip(self, columns))]
1603 async fn create_index(
1604 &self,
1605 table: &str,
1606 index_name: &str,
1607 columns: Vec<String>,
1608 unique: bool,
1609 ) -> StorageResult<()> {
1610 info!("Creating index: {} on table: {}", index_name, table);
1611
1612 let unique_clause = if unique { "UNIQUE" } else { "" };
1613 let columns_clause = columns.join(", ");
1614
1615 let sql = format!(
1616 "CREATE {} INDEX IF NOT EXISTS {} ON {} ({})",
1617 unique_clause, index_name, table, columns_clause
1618 );
1619
1620 sqlx::query(&sql).execute(&self.pool).await.map_err(|e| {
1621 error!("Failed to create index: {}", e);
1622 StorageError::QueryError {
1623 message: format!("Index creation failed: {}", e),
1624 }
1625 })?;
1626
1627 info!("Index created successfully: {}", index_name);
1628 Ok(())
1629 }
1630
1631 #[instrument(skip(self))]
1632 async fn drop_index(&self, index_name: &str) -> StorageResult<()> {
1633 info!("Dropping index: {}", index_name);
1634
1635 let sql = format!("DROP INDEX IF EXISTS {}", index_name);
1636
1637 sqlx::query(&sql).execute(&self.pool).await.map_err(|e| {
1638 error!("Failed to drop index: {}", e);
1639 StorageError::QueryError {
1640 message: format!("Index drop failed: {}", e),
1641 }
1642 })?;
1643
1644 info!("Index dropped successfully: {}", index_name);
1645 Ok(())
1646 }
1647
1648 #[instrument(skip(self))]
1649 async fn vacuum(&self, table: &str) -> StorageResult<()> {
1650 info!("Vacuuming table: {}", table);
1651
1652 let sql = format!("VACUUM ANALYZE {}", table);
1653
1654 sqlx::query(&sql).execute(&self.pool).await.map_err(|e| {
1655 error!("Failed to vacuum table: {}", e);
1656 StorageError::QueryError {
1657 message: format!("Vacuum failed: {}", e),
1658 }
1659 })?;
1660
1661 info!("Table vacuumed successfully: {}", table);
1662 Ok(())
1663 }
1664
1665 #[instrument(skip(self))]
1666 async fn analyze(&self, table: &str) -> StorageResult<()> {
1667 info!("Analyzing table: {}", table);
1668
1669 let sql = format!("ANALYZE {}", table);
1670
1671 sqlx::query(&sql).execute(&self.pool).await.map_err(|e| {
1672 error!("Failed to analyze table: {}", e);
1673 StorageError::QueryError {
1674 message: format!("Analyze failed: {}", e),
1675 }
1676 })?;
1677
1678 info!("Table analyzed successfully: {}", table);
1679 Ok(())
1680 }
1681}
1682
1683#[cfg(test)]
1684mod tests {
1685 use super::*;
1686 use serde::{Deserialize, Serialize};
1687
1688 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1689 struct TestData {
1690 id: String,
1691 value: i32,
1692 name: String,
1693 }
1694
1695 async fn create_test_storage() -> PostgreSQLStorage {
1696 let config = PostgreSQLConfig {
1697 host: "localhost".to_string(),
1698 port: 5432,
1699 database: "test_llm_optimizer".to_string(),
1700 username: "postgres".to_string(),
1701 password: "postgres".to_string(),
1702 ..Default::default()
1703 };
1704
1705 let mut storage = PostgreSQLStorage::new(config)
1706 .await
1707 .expect("Failed to create storage");
1708
1709 storage.initialize().await.expect("Failed to initialize");
1710 storage
1711 }
1712
1713 #[tokio::test]
1714 async fn test_backend_type() {
1715 let storage = create_test_storage().await;
1716 assert_eq!(storage.backend(), StorageBackend::PostgreSQL);
1717 }
1718
1719 #[tokio::test]
1720 async fn test_health_check() {
1721 let storage = create_test_storage().await;
1722 let health = storage.health_check().await.expect("Health check failed");
1723 assert!(matches!(health, StorageHealth::Healthy | StorageHealth::Degraded));
1724 }
1725
1726 #[tokio::test]
1727 async fn test_insert_and_get() {
1728 let storage = create_test_storage().await;
1729
1730 let test_data = TestData {
1731 id: "test1".to_string(),
1732 value: 42,
1733 name: "Test Entry".to_string(),
1734 };
1735
1736 storage
1737 .insert("test_table", "test_key1", &test_data)
1738 .await
1739 .expect("Insert failed");
1740
1741 let entry: Option<StorageEntry<TestData>> = storage
1742 .get("test_table", "test_key1")
1743 .await
1744 .expect("Get failed");
1745
1746 assert!(entry.is_some());
1747 let entry = entry.unwrap();
1748 assert_eq!(entry.key, "test_key1");
1749 assert_eq!(entry.value.id, "test1");
1750 assert_eq!(entry.value.value, 42);
1751 assert_eq!(entry.version, 1);
1752 }
1753
1754 #[tokio::test]
1755 async fn test_insert_duplicate_fails() {
1756 let storage = create_test_storage().await;
1757
1758 let test_data = TestData {
1759 id: "test2".to_string(),
1760 value: 100,
1761 name: "Duplicate Test".to_string(),
1762 };
1763
1764 storage
1765 .insert("test_table", "dup_key", &test_data)
1766 .await
1767 .expect("First insert failed");
1768
1769 let result = storage.insert("test_table", "dup_key", &test_data).await;
1770 assert!(result.is_err());
1771 assert!(matches!(result.unwrap_err(), StorageError::AlreadyExists { .. }));
1772 }
1773
1774 #[tokio::test]
1775 async fn test_update() {
1776 let storage = create_test_storage().await;
1777
1778 let test_data = TestData {
1779 id: "test3".to_string(),
1780 value: 10,
1781 name: "Original".to_string(),
1782 };
1783
1784 storage
1785 .insert("test_table", "update_key", &test_data)
1786 .await
1787 .expect("Insert failed");
1788
1789 let updated_data = TestData {
1790 id: "test3".to_string(),
1791 value: 20,
1792 name: "Updated".to_string(),
1793 };
1794
1795 storage
1796 .update("test_table", "update_key", &updated_data)
1797 .await
1798 .expect("Update failed");
1799
1800 let entry: Option<StorageEntry<TestData>> = storage
1801 .get("test_table", "update_key")
1802 .await
1803 .expect("Get failed");
1804
1805 assert!(entry.is_some());
1806 let entry = entry.unwrap();
1807 assert_eq!(entry.value.value, 20);
1808 assert_eq!(entry.value.name, "Updated");
1809 assert_eq!(entry.version, 2);
1810 }
1811
1812 #[tokio::test]
1813 async fn test_update_versioned() {
1814 let storage = create_test_storage().await;
1815
1816 let test_data = TestData {
1817 id: "test4".to_string(),
1818 value: 100,
1819 name: "Versioned".to_string(),
1820 };
1821
1822 storage
1823 .insert("test_table", "version_key", &test_data)
1824 .await
1825 .expect("Insert failed");
1826
1827 let updated_data = TestData {
1828 id: "test4".to_string(),
1829 value: 200,
1830 name: "Versioned Updated".to_string(),
1831 };
1832
1833 storage
1835 .update_versioned("test_table", "version_key", &updated_data, 1)
1836 .await
1837 .expect("Versioned update failed");
1838
1839 let result = storage
1841 .update_versioned("test_table", "version_key", &updated_data, 1)
1842 .await;
1843
1844 assert!(result.is_err());
1845 assert!(matches!(result.unwrap_err(), StorageError::ConstraintViolation { .. }));
1846 }
1847
1848 #[tokio::test]
1849 async fn test_delete() {
1850 let storage = create_test_storage().await;
1851
1852 let test_data = TestData {
1853 id: "test5".to_string(),
1854 value: 50,
1855 name: "To Delete".to_string(),
1856 };
1857
1858 storage
1859 .insert("test_table", "delete_key", &test_data)
1860 .await
1861 .expect("Insert failed");
1862
1863 storage
1864 .delete("test_table", "delete_key")
1865 .await
1866 .expect("Delete failed");
1867
1868 let entry: Option<StorageEntry<TestData>> = storage
1869 .get("test_table", "delete_key")
1870 .await
1871 .expect("Get failed");
1872
1873 assert!(entry.is_none());
1874 }
1875
1876 #[tokio::test]
1877 async fn test_exists() {
1878 let storage = create_test_storage().await;
1879
1880 let test_data = TestData {
1881 id: "test6".to_string(),
1882 value: 60,
1883 name: "Exists Test".to_string(),
1884 };
1885
1886 storage
1887 .insert("test_table", "exists_key", &test_data)
1888 .await
1889 .expect("Insert failed");
1890
1891 let exists = storage
1892 .exists("test_table", "exists_key")
1893 .await
1894 .expect("Exists check failed");
1895 assert!(exists);
1896
1897 let not_exists = storage
1898 .exists("test_table", "nonexistent_key")
1899 .await
1900 .expect("Exists check failed");
1901 assert!(!not_exists);
1902 }
1903
1904 #[tokio::test]
1905 async fn test_insert_with_ttl() {
1906 let storage = create_test_storage().await;
1907
1908 let test_data = TestData {
1909 id: "test7".to_string(),
1910 value: 70,
1911 name: "TTL Test".to_string(),
1912 };
1913
1914 storage
1915 .insert_with_ttl("test_table", "ttl_key", &test_data, 2)
1916 .await
1917 .expect("Insert with TTL failed");
1918
1919 let entry: Option<StorageEntry<TestData>> = storage
1920 .get("test_table", "ttl_key")
1921 .await
1922 .expect("Get failed");
1923
1924 assert!(entry.is_some());
1925 let entry = entry.unwrap();
1926 assert!(entry.expires_at.is_some());
1927
1928 tokio::time::sleep(std::time::Duration::from_secs(3)).await;
1930
1931 let expired_entry: Option<StorageEntry<TestData>> = storage
1932 .get("test_table", "ttl_key")
1933 .await
1934 .expect("Get failed");
1935
1936 assert!(expired_entry.is_none());
1937 }
1938
1939 #[tokio::test]
1940 async fn test_scan_prefix() {
1941 let storage = create_test_storage().await;
1942
1943 for i in 0..5 {
1944 let test_data = TestData {
1945 id: format!("prefix_{}", i),
1946 value: i,
1947 name: format!("Prefix {}", i),
1948 };
1949
1950 storage
1951 .insert("test_table", &format!("prefix_{}", i), &test_data)
1952 .await
1953 .expect("Insert failed");
1954 }
1955
1956 let keys = storage
1957 .scan_prefix("test_table", "prefix_")
1958 .await
1959 .expect("Scan prefix failed");
1960
1961 assert!(keys.len() >= 5);
1962 assert!(keys.iter().all(|k| k.starts_with("prefix_")));
1963 }
1964
1965 #[tokio::test]
1966 async fn test_compare_and_swap() {
1967 let storage = create_test_storage().await;
1968
1969 let test_data = TestData {
1970 id: "test8".to_string(),
1971 value: 80,
1972 name: "CAS Test".to_string(),
1973 };
1974
1975 storage
1976 .insert("test_table", "cas_key", &test_data)
1977 .await
1978 .expect("Insert failed");
1979
1980 let new_data = TestData {
1981 id: "test8".to_string(),
1982 value: 81,
1983 name: "CAS Updated".to_string(),
1984 };
1985
1986 let success = storage
1988 .compare_and_swap("test_table", "cas_key", 1, &new_data)
1989 .await
1990 .expect("CAS failed");
1991 assert!(success);
1992
1993 let failure = storage
1995 .compare_and_swap("test_table", "cas_key", 1, &new_data)
1996 .await
1997 .expect("CAS failed");
1998 assert!(!failure);
1999 }
2000
2001 #[tokio::test]
2002 async fn test_batch_operations() {
2003 let storage = create_test_storage().await;
2004
2005 let mut batch = Batch::new(10);
2006
2007 for i in 0..5 {
2008 let test_data = TestData {
2009 id: format!("batch_{}", i),
2010 value: i,
2011 name: format!("Batch {}", i),
2012 };
2013
2014 let data = serde_json::to_vec(&test_data).unwrap();
2015 batch.add(Operation::Insert {
2016 table: "test_table".to_string(),
2017 data,
2018 }).expect("Failed to add to batch");
2019 }
2020
2021 storage
2022 .execute_batch(batch)
2023 .await
2024 .expect("Batch execution failed");
2025
2026 for i in 0..5 {
2028 let entry: Option<StorageEntry<TestData>> = storage
2029 .get("test_table", &format!("batch_{}", i))
2030 .await
2031 .expect("Get failed");
2032 assert!(entry.is_some());
2033 }
2034 }
2035
2036 #[tokio::test]
2037 async fn test_transaction_commit() {
2038 let storage = create_test_storage().await;
2039
2040 let transaction = storage
2041 .begin_transaction()
2042 .await
2043 .expect("Begin transaction failed");
2044
2045 storage
2046 .commit_transaction(transaction)
2047 .await
2048 .expect("Commit failed");
2049 }
2050
2051 #[tokio::test]
2052 async fn test_transaction_rollback() {
2053 let storage = create_test_storage().await;
2054
2055 let transaction = storage
2056 .begin_transaction()
2057 .await
2058 .expect("Begin transaction failed");
2059
2060 storage
2061 .rollback_transaction(transaction)
2062 .await
2063 .expect("Rollback failed");
2064 }
2065
2066 #[tokio::test]
2067 async fn test_get_modified_since() {
2068 let storage = create_test_storage().await;
2069
2070 let now = Utc::now();
2071
2072 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
2073
2074 let test_data = TestData {
2075 id: "test9".to_string(),
2076 value: 90,
2077 name: "Modified Test".to_string(),
2078 };
2079
2080 storage
2081 .insert("test_table", "modified_key", &test_data)
2082 .await
2083 .expect("Insert failed");
2084
2085 let entries: Vec<StorageEntry<TestData>> = storage
2086 .get_modified_since("test_table", now)
2087 .await
2088 .expect("Get modified since failed");
2089
2090 assert!(!entries.is_empty());
2091 }
2092
2093 #[tokio::test]
2094 async fn test_cleanup_expired() {
2095 let storage = create_test_storage().await;
2096
2097 let test_data = TestData {
2098 id: "test10".to_string(),
2099 value: 100,
2100 name: "Cleanup Test".to_string(),
2101 };
2102
2103 storage
2104 .insert_with_ttl("test_table", "cleanup_key", &test_data, 1)
2105 .await
2106 .expect("Insert with TTL failed");
2107
2108 tokio::time::sleep(std::time::Duration::from_secs(2)).await;
2109
2110 let count = storage
2111 .cleanup_expired("test_table")
2112 .await
2113 .expect("Cleanup failed");
2114
2115 assert!(count >= 1);
2116 }
2117
2118 #[tokio::test]
2119 async fn test_metadata() {
2120 let storage = create_test_storage().await;
2121
2122 let metadata = storage.metadata().await.expect("Metadata failed");
2123
2124 assert_eq!(metadata.backend, StorageBackend::PostgreSQL);
2125 assert!(!metadata.capabilities.is_empty());
2126 assert!(metadata.version.contains("PostgreSQL"));
2127 }
2128
2129 #[tokio::test]
2130 async fn test_stats() {
2131 let storage = create_test_storage().await;
2132
2133 let stats = storage.stats().await.expect("Stats failed");
2134
2135 assert!(stats.active_connections > 0);
2136 }
2137}