use crate::TursoStorage;
use do_memory_core::{Error, Pattern, Result};
use libsql::params;
use libsql::params::IntoParams;
use tracing::{debug, info, warn};
pub struct RawPatternQuery<'a> {
storage: &'a TursoStorage,
}
impl<'a> RawPatternQuery<'a> {
pub fn new(storage: &'a TursoStorage) -> Self {
Self { storage }
}
pub async fn query(&self, sql: &str) -> Result<Vec<Pattern>> {
debug!("Executing raw pattern query: {}", sql);
let (conn, _conn_id) = self.storage.get_connection_with_id().await?;
let mut rows = conn
.query(sql, params![])
.await
.map_err(|e| Error::Storage(format!("Failed to execute pattern query: {}", e)))?;
let mut patterns = Vec::new();
while let Some(row) = rows
.next()
.await
.map_err(|e| Error::Storage(format!("Failed to fetch pattern row: {}", e)))?
{
match super::row::row_to_pattern(&row) {
Ok(pattern) => patterns.push(pattern),
Err(e) => {
warn!("Failed to parse pattern row: {}", e);
}
}
}
info!("Raw query returned {} patterns", patterns.len());
Ok(patterns)
}
pub async fn query_with_params<P: IntoParams>(
&self,
sql: &str,
params: P,
) -> Result<Vec<Pattern>> {
debug!("Executing parameterized pattern query: {}", sql);
let (conn, _conn_id) = self.storage.get_connection_with_id().await?;
let mut rows = conn
.query(sql, params)
.await
.map_err(|e| Error::Storage(format!("Failed to execute pattern query: {}", e)))?;
let mut patterns = Vec::new();
while let Some(row) = rows
.next()
.await
.map_err(|e| Error::Storage(format!("Failed to fetch pattern row: {}", e)))?
{
match super::row::row_to_pattern(&row) {
Ok(pattern) => patterns.push(pattern),
Err(e) => {
warn!("Failed to parse pattern row: {}", e);
}
}
}
info!("Parameterized query returned {} patterns", patterns.len());
Ok(patterns)
}
}
pub const PATTERN_SELECT_COLUMNS: &str = r#"
pattern_id, pattern_type, pattern_data, success_rate,
context_domain, context_language, context_tags, occurrence_count,
created_at, updated_at
"#;
impl TursoStorage {
pub async fn query_patterns_raw(&self, sql: &str) -> Result<Vec<Pattern>> {
RawPatternQuery::new(self).query(sql).await
}
pub async fn query_patterns_raw_with_params<P: IntoParams>(
&self,
sql: &str,
params: P,
) -> Result<Vec<Pattern>> {
RawPatternQuery::new(self)
.query_with_params(sql, params)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use do_memory_core::{Pattern, TaskContext};
use tempfile::TempDir;
async fn create_test_storage() -> Result<(TursoStorage, TempDir)> {
let dir = TempDir::new().unwrap();
let db_path = dir.path().join("test.db");
let db = libsql::Builder::new_local(&db_path)
.build()
.await
.map_err(|e| Error::Storage(format!("Failed to create test database: {}", e)))?;
let storage = TursoStorage::from_database(db)?;
storage.initialize_schema().await?;
Ok((storage, dir))
}
#[tokio::test]
async fn test_raw_pattern_query_empty() {
let (storage, _dir) = create_test_storage().await.unwrap();
let raw_query = RawPatternQuery::new(&storage);
let sql = format!(
"SELECT {} FROM patterns WHERE context_domain = 'nonexistent'",
PATTERN_SELECT_COLUMNS
);
let result = raw_query.query(&sql).await.unwrap();
assert!(result.is_empty());
}
#[tokio::test]
async fn test_raw_pattern_query_with_data() {
let (storage, _dir) = create_test_storage().await.unwrap();
let pattern = Pattern::DecisionPoint {
id: uuid::Uuid::new_v4(),
condition: "test condition".to_string(),
action: "test action".to_string(),
outcome_stats: do_memory_core::types::OutcomeStats {
success_count: 10,
failure_count: 2,
total_count: 12,
avg_duration_secs: 0.5,
},
context: TaskContext {
domain: "test-domain".to_string(),
..Default::default()
},
effectiveness: do_memory_core::pattern::PatternEffectiveness::default(),
};
storage.store_pattern(&pattern).await.unwrap();
let raw_query = RawPatternQuery::new(&storage);
let sql = format!(
"SELECT {} FROM patterns WHERE context_domain = ?",
PATTERN_SELECT_COLUMNS
);
let result = raw_query
.query_with_params(&sql, ["test-domain".to_string()])
.await
.unwrap();
assert!(!result.is_empty());
}
}