(
conn: &Connection,
updates: &[(String, String, LineFacts)],
)
| 181 | } |
| 182 | |
| 183 | async fn apply_updates( |
| 184 | conn: &Connection, |
| 185 | updates: &[(String, String, LineFacts)], |
| 186 | ) -> Option<BackfillStats> { |
| 187 | let mut stats = BackfillStats::default(); |
| 188 | for (provider, message_id, facts) in updates { |
| 189 | if let Some(timestamp) = facts.timestamp { |
| 190 | stats.dated += conn |
| 191 | .execute( |
| 192 | "UPDATE session_messages SET timestamp = ?1 |
| 193 | WHERE provider = ?2 AND message_id = ?3 AND timestamp IS NULL", |
| 194 | params![timestamp, provider.as_str(), message_id.as_str()], |
| 195 | ) |
| 196 | .await |
| 197 | .ok()?; |
| 198 | conn.execute( |
| 199 | "UPDATE lcm_raw_messages SET timestamp = ?1 |
| 200 | WHERE provider = ?2 AND message_id = ?3 AND timestamp IS NULL", |
| 201 | params![timestamp, provider.as_str(), message_id.as_str()], |
| 202 | ) |
| 203 | .await |
| 204 | .ok()?; |
| 205 | } |
| 206 | if let Some(usage) = &facts.usage { |
| 207 | let usage_json = serde_json::to_string(usage).ok()?; |
| 208 | // `json_set` preserves the other metadata keys; invalid or |
| 209 | // missing metadata degrades to a fresh `{"usage": …}` object. |
| 210 | for table in ["session_messages", "lcm_raw_messages"] { |
| 211 | let updated = conn |
| 212 | .execute( |
| 213 | &format!( |
| 214 | "UPDATE {table} SET metadata_json = json_set( |
| 215 | CASE WHEN metadata_json IS NOT NULL AND json_valid(metadata_json) |
| 216 | THEN metadata_json ELSE '{{}}' END, |
| 217 | '$.usage', json(?1)) |
| 218 | WHERE provider = ?2 AND message_id = ?3 |
| 219 | AND (metadata_json IS NULL |
| 220 | OR NOT json_valid(metadata_json) |
| 221 | OR json_extract(metadata_json, '$.usage') IS NULL)" |
| 222 | ), |
| 223 | params![usage_json.as_str(), provider.as_str(), message_id.as_str()], |
| 224 | ) |
| 225 | .await |
| 226 | .ok()?; |
| 227 | if table == "session_messages" { |
| 228 | stats.usage_added += updated; |
| 229 | } |
| 230 | } |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | // Sessions ingested while messages were undated also have NULL |
| 235 | // started_at/ended_at; derive them from the freshly dated messages. |
| 236 | let providers = JSONL_PROVIDERS |
| 237 | .map(|provider| format!("'{provider}'")) |
| 238 | .join(", "); |
| 239 | conn.execute( |
| 240 | &format!( |
no outgoing calls
no test coverage detected