1use super::rows::{MemoryRow, NewMemory};
6use crate::errors::AppError;
7use rusqlite::{params, Connection};
8
9pub fn find_by_name(
26 conn: &Connection,
27 namespace: &str,
28 name: &str,
29) -> Result<Option<(i64, i64, i64)>, AppError> {
30 let mut stmt = conn.prepare_cached(
31 "SELECT m.id, m.updated_at, COALESCE(MAX(v.version), 0)
32 FROM memories m
33 LEFT JOIN memory_versions v ON v.memory_id = m.id
34 WHERE m.namespace = ?1 AND m.name = ?2 AND m.deleted_at IS NULL
35 GROUP BY m.id",
36 )?;
37 let result = stmt.query_row(params![namespace, name], |r| {
38 Ok((
39 r.get::<_, i64>(0)?,
40 r.get::<_, i64>(1)?,
41 r.get::<_, i64>(2)?,
42 ))
43 });
44 match result {
45 Ok(row) => Ok(Some(row)),
46 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
47 Err(e) => Err(AppError::Database(e)),
48 }
49}
50
51pub fn find_by_hash(
65 conn: &Connection,
66 namespace: &str,
67 body_hash: &str,
68) -> Result<Option<i64>, AppError> {
69 let mut stmt = conn.prepare_cached(
70 "SELECT id FROM memories WHERE namespace = ?1 AND body_hash = ?2 AND deleted_at IS NULL",
71 )?;
72 match stmt.query_row(params![namespace, body_hash], |r| r.get(0)) {
73 Ok(id) => Ok(Some(id)),
74 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
75 Err(e) => Err(AppError::Database(e)),
76 }
77}
78
79pub fn insert(conn: &Connection, m: &NewMemory) -> Result<i64, AppError> {
95 let validated_source = crate::memory_source::validate_source(&m.source)?;
101 conn.execute(
102 "INSERT INTO memories (namespace, name, type, description, body, body_hash, session_id, source, metadata)
103 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
104 params![
105 m.namespace, m.name, m.memory_type, m.description, m.body,
106 m.body_hash, m.session_id, validated_source,
107 serde_json::to_string(&m.metadata)?
108 ],
109 )?;
110 Ok(conn.last_insert_rowid())
111}
112
113pub fn update(
128 conn: &Connection,
129 id: i64,
130 m: &NewMemory,
131 expected_updated_at: Option<i64>,
132) -> Result<bool, AppError> {
133 let validated_source = crate::memory_source::validate_source(&m.source)?;
138 let affected = if let Some(ts) = expected_updated_at {
139 conn.execute(
140 "UPDATE memories SET type=?2, description=?3, body=?4, body_hash=?5,
141 session_id=?6, source=?7, metadata=?8
142 WHERE id=?1 AND updated_at=?9 AND deleted_at IS NULL",
143 params![
144 id,
145 m.memory_type,
146 m.description,
147 m.body,
148 m.body_hash,
149 m.session_id,
150 validated_source,
151 serde_json::to_string(&m.metadata)?,
152 ts
153 ],
154 )?
155 } else {
156 conn.execute(
157 "UPDATE memories SET type=?2, description=?3, body=?4, body_hash=?5,
158 session_id=?6, source=?7, metadata=?8
159 WHERE id=?1 AND deleted_at IS NULL",
160 params![
161 id,
162 m.memory_type,
163 m.description,
164 m.body,
165 m.body_hash,
166 m.session_id,
167 validated_source,
168 serde_json::to_string(&m.metadata)?
169 ],
170 )?
171 };
172 Ok(affected == 1)
173}
174
175pub fn read_by_name(
185 conn: &Connection,
186 namespace: &str,
187 name: &str,
188) -> Result<Option<MemoryRow>, AppError> {
189 let mut stmt = conn.prepare_cached(
190 "SELECT id, namespace, name, type, description, body, body_hash,
191 session_id, source, metadata, created_at, updated_at, deleted_at
192 FROM memories WHERE namespace=?1 AND name=?2 AND deleted_at IS NULL",
193 )?;
194 match stmt.query_row(params![namespace, name], |r| {
195 Ok(MemoryRow {
196 id: r.get(0)?,
197 namespace: r.get(1)?,
198 name: r.get(2)?,
199 memory_type: r.get(3)?,
200 description: r.get(4)?,
201 body: r.get(5)?,
202 body_hash: r.get(6)?,
203 session_id: r.get(7)?,
204 source: r.get(8)?,
205 metadata: r.get(9)?,
206 created_at: r.get(10)?,
207 updated_at: r.get(11)?,
208 deleted_at: r.get(12)?,
209 })
210 }) {
211 Ok(m) => Ok(Some(m)),
212 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
213 Err(e) => Err(AppError::Database(e)),
214 }
215}
216
217pub fn read_full(conn: &Connection, memory_id: i64) -> Result<Option<MemoryRow>, AppError> {
226 let mut stmt = conn.prepare_cached(
227 "SELECT id, namespace, name, type, description, body, body_hash,
228 session_id, source, metadata, created_at, updated_at, deleted_at
229 FROM memories WHERE id=?1 AND deleted_at IS NULL",
230 )?;
231 match stmt.query_row(params![memory_id], |r| {
232 Ok(MemoryRow {
233 id: r.get(0)?,
234 namespace: r.get(1)?,
235 name: r.get(2)?,
236 memory_type: r.get(3)?,
237 description: r.get(4)?,
238 body: r.get(5)?,
239 body_hash: r.get(6)?,
240 session_id: r.get(7)?,
241 source: r.get(8)?,
242 metadata: r.get(9)?,
243 created_at: r.get(10)?,
244 updated_at: r.get(11)?,
245 deleted_at: r.get(12)?,
246 })
247 }) {
248 Ok(m) => Ok(Some(m)),
249 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
250 Err(e) => Err(AppError::Database(e)),
251 }
252}