use super::rows::{MemoryRow, NewMemory};
use crate::errors::AppError;
use rusqlite::{params, Connection};
pub fn find_by_name(
conn: &Connection,
namespace: &str,
name: &str,
) -> Result<Option<(i64, i64, i64)>, AppError> {
let mut stmt = conn.prepare_cached(
"SELECT m.id, m.updated_at, COALESCE(MAX(v.version), 0)
FROM memories m
LEFT JOIN memory_versions v ON v.memory_id = m.id
WHERE m.namespace = ?1 AND m.name = ?2 AND m.deleted_at IS NULL
GROUP BY m.id",
)?;
let result = stmt.query_row(params![namespace, name], |r| {
Ok((
r.get::<_, i64>(0)?,
r.get::<_, i64>(1)?,
r.get::<_, i64>(2)?,
))
});
match result {
Ok(row) => Ok(Some(row)),
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
Err(e) => Err(AppError::Database(e)),
}
}
pub fn find_by_hash(
conn: &Connection,
namespace: &str,
body_hash: &str,
) -> Result<Option<i64>, AppError> {
let mut stmt = conn.prepare_cached(
"SELECT id FROM memories WHERE namespace = ?1 AND body_hash = ?2 AND deleted_at IS NULL",
)?;
match stmt.query_row(params![namespace, body_hash], |r| r.get(0)) {
Ok(id) => Ok(Some(id)),
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
Err(e) => Err(AppError::Database(e)),
}
}
pub fn insert(conn: &Connection, m: &NewMemory) -> Result<i64, AppError> {
let validated_source = crate::memory_source::validate_source(&m.source)?;
conn.execute(
"INSERT INTO memories (namespace, name, type, description, body, body_hash, session_id, source, metadata)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
params![
m.namespace, m.name, m.memory_type, m.description, m.body,
m.body_hash, m.session_id, validated_source,
serde_json::to_string(&m.metadata)?
],
)?;
Ok(conn.last_insert_rowid())
}
pub fn update(
conn: &Connection,
id: i64,
m: &NewMemory,
expected_updated_at: Option<i64>,
) -> Result<bool, AppError> {
let validated_source = crate::memory_source::validate_source(&m.source)?;
let affected = if let Some(ts) = expected_updated_at {
conn.execute(
"UPDATE memories SET type=?2, description=?3, body=?4, body_hash=?5,
session_id=?6, source=?7, metadata=?8
WHERE id=?1 AND updated_at=?9 AND deleted_at IS NULL",
params![
id,
m.memory_type,
m.description,
m.body,
m.body_hash,
m.session_id,
validated_source,
serde_json::to_string(&m.metadata)?,
ts
],
)?
} else {
conn.execute(
"UPDATE memories SET type=?2, description=?3, body=?4, body_hash=?5,
session_id=?6, source=?7, metadata=?8
WHERE id=?1 AND deleted_at IS NULL",
params![
id,
m.memory_type,
m.description,
m.body,
m.body_hash,
m.session_id,
validated_source,
serde_json::to_string(&m.metadata)?
],
)?
};
Ok(affected == 1)
}
pub fn read_by_name(
conn: &Connection,
namespace: &str,
name: &str,
) -> Result<Option<MemoryRow>, AppError> {
let mut stmt = conn.prepare_cached(
"SELECT id, namespace, name, type, description, body, body_hash,
session_id, source, metadata, created_at, updated_at, deleted_at
FROM memories WHERE namespace=?1 AND name=?2 AND deleted_at IS NULL",
)?;
match stmt.query_row(params![namespace, name], |r| {
Ok(MemoryRow {
id: r.get(0)?,
namespace: r.get(1)?,
name: r.get(2)?,
memory_type: r.get(3)?,
description: r.get(4)?,
body: r.get(5)?,
body_hash: r.get(6)?,
session_id: r.get(7)?,
source: r.get(8)?,
metadata: r.get(9)?,
created_at: r.get(10)?,
updated_at: r.get(11)?,
deleted_at: r.get(12)?,
})
}) {
Ok(m) => Ok(Some(m)),
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
Err(e) => Err(AppError::Database(e)),
}
}
pub fn read_full(conn: &Connection, memory_id: i64) -> Result<Option<MemoryRow>, AppError> {
let mut stmt = conn.prepare_cached(
"SELECT id, namespace, name, type, description, body, body_hash,
session_id, source, metadata, created_at, updated_at, deleted_at
FROM memories WHERE id=?1 AND deleted_at IS NULL",
)?;
match stmt.query_row(params![memory_id], |r| {
Ok(MemoryRow {
id: r.get(0)?,
namespace: r.get(1)?,
name: r.get(2)?,
memory_type: r.get(3)?,
description: r.get(4)?,
body: r.get(5)?,
body_hash: r.get(6)?,
session_id: r.get(7)?,
source: r.get(8)?,
metadata: r.get(9)?,
created_at: r.get(10)?,
updated_at: r.get(11)?,
deleted_at: r.get(12)?,
})
}) {
Ok(m) => Ok(Some(m)),
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
Err(e) => Err(AppError::Database(e)),
}
}