368 {
370 return VoidResult(kcenon::common::error_info{
371 -1, "Database not initialized", "key_image_repository"});
372 }
373
374 static constexpr const char* sql = R"(
375 INSERT INTO key_images (
376 key_image_id, study_uid, sop_instance_uid, frame_number,
377 user_id, reason, document_title, created_at
378 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
379 ON CONFLICT(key_image_id) DO UPDATE SET
380 reason = excluded.reason,
381 document_title = excluded.document_title
382 )";
383
384 sqlite3_stmt* stmt = nullptr;
385 if (sqlite3_prepare_v2(
db_, sql, -1, &stmt,
nullptr) != SQLITE_OK) {
386 return VoidResult(kcenon::common::error_info{
387 -1,
388 "Failed to prepare statement: " + std::string(sqlite3_errmsg(
db_)),
389 "key_image_repository"});
390 }
391
392 auto now_str = to_timestamp_string(std::chrono::system_clock::now());
393
394 int idx = 1;
395 sqlite3_bind_text(stmt, idx++,
record.key_image_id.c_str(), -1,
396 SQLITE_TRANSIENT);
397 sqlite3_bind_text(stmt, idx++,
record.study_uid.c_str(), -1,
398 SQLITE_TRANSIENT);
399 sqlite3_bind_text(stmt, idx++,
record.sop_instance_uid.c_str(), -1,
400 SQLITE_TRANSIENT);
401 bind_optional_int(stmt, idx++,
record.frame_number);
402 sqlite3_bind_text(stmt, idx++,
record.user_id.c_str(), -1, SQLITE_TRANSIENT);
403 sqlite3_bind_text(stmt, idx++,
record.reason.c_str(), -1, SQLITE_TRANSIENT);
404 sqlite3_bind_text(stmt, idx++,
record.document_title.c_str(), -1,
405 SQLITE_TRANSIENT);
406 sqlite3_bind_text(stmt, idx++, now_str.c_str(), -1, SQLITE_TRANSIENT);
407
408 auto rc = sqlite3_step(stmt);
409 sqlite3_finalize(stmt);
410
411 if (rc != SQLITE_DONE) {
412 return VoidResult(kcenon::common::error_info{
413 -1,
414 "Failed to save key image: " + std::string(sqlite3_errmsg(
db_)),
415 "key_image_repository"});
416 }
417
418 return kcenon::common::ok();
419}