PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
kcenon::pacs::storage::instance_repository Class Reference

Repository for instance metadata persistence (legacy SQLite interface) More...

#include <instance_repository.h>

Collaboration diagram for kcenon::pacs::storage::instance_repository:
Collaboration graph

Public Member Functions

 instance_repository (sqlite3 *db)
 Construct an instance repository with a raw SQLite database handle.
 
 ~instance_repository ()
 
 instance_repository (const instance_repository &)=delete
 
auto operator= (const instance_repository &) -> instance_repository &=delete
 
 instance_repository (instance_repository &&) noexcept
 
auto operator= (instance_repository &&) noexcept -> instance_repository &
 
auto upsert_instance (int64_t series_pk, std::string_view sop_uid, std::string_view sop_class_uid, std::string_view file_path, int64_t file_size, std::string_view transfer_syntax="", std::optional< int > instance_number=std::nullopt) -> Result< int64_t >
 Insert or update an instance record by individual fields.
 
auto upsert_instance (const instance_record &record) -> Result< int64_t >
 Insert or update an instance record from an existing record struct.
 
auto find_instance (std::string_view sop_uid) const -> std::optional< instance_record >
 Find an instance record by its SOP Instance UID.
 
auto find_instance_by_pk (int64_t pk) const -> std::optional< instance_record >
 Find an instance record by its database primary key.
 
auto list_instances (std::string_view series_uid) const -> Result< std::vector< instance_record > >
 List all instance records belonging to a given series.
 
auto search_instances (const instance_query &query) const -> Result< std::vector< instance_record > >
 Search for instance records matching the given query criteria.
 
auto delete_instance (std::string_view sop_uid) -> VoidResult
 Delete an instance record by its SOP Instance UID.
 
auto instance_count () const -> Result< size_t >
 Get the total number of instance records in the repository.
 
auto instance_count (std::string_view series_uid) const -> Result< size_t >
 Get the number of instance records belonging to a given series.
 
auto get_file_path (std::string_view sop_instance_uid) const -> Result< std::optional< std::string > >
 Retrieve the file system path for a stored instance.
 
auto get_study_files (std::string_view study_instance_uid) const -> Result< std::vector< std::string > >
 Retrieve all file paths for instances belonging to a study.
 
auto get_series_files (std::string_view series_instance_uid) const -> Result< std::vector< std::string > >
 Retrieve all file paths for instances belonging to a series.
 

Private Member Functions

auto parse_instance_row (void *stmt) const -> instance_record
 

Static Private Member Functions

static auto parse_timestamp (const std::string &str) -> std::chrono::system_clock::time_point
 

Private Attributes

sqlite3 * db_ {nullptr}
 

Detailed Description

Repository for instance metadata persistence (legacy SQLite interface)

Definition at line 194 of file instance_repository.h.

Constructor & Destructor Documentation

◆ instance_repository() [1/3]

kcenon::pacs::storage::instance_repository::instance_repository ( sqlite3 * db)
explicit

Construct an instance repository with a raw SQLite database handle.

Parameters
dbPointer to the SQLite database connection

Definition at line 719 of file instance_repository.cpp.

◆ ~instance_repository()

kcenon::pacs::storage::instance_repository::~instance_repository ( )
default

◆ instance_repository() [2/3]

kcenon::pacs::storage::instance_repository::instance_repository ( const instance_repository & )
delete

◆ instance_repository() [3/3]

kcenon::pacs::storage::instance_repository::instance_repository ( instance_repository && )
defaultnoexcept

Member Function Documentation

◆ delete_instance()

auto kcenon::pacs::storage::instance_repository::delete_instance ( std::string_view sop_uid) -> VoidResult
nodiscard

Delete an instance record by its SOP Instance UID.

Parameters
sop_uidSOP Instance UID of the instance to delete
Returns
VoidResult indicating success or an error

Definition at line 1090 of file instance_repository.cpp.

1091 {
1092 const char* sql = "DELETE FROM instances WHERE sop_uid = ?;";
1093
1094 sqlite3_stmt* stmt = nullptr;
1095 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1096 if (rc != SQLITE_OK) {
1097 return make_error<std::monostate>(
1098 rc,
1099 kcenon::pacs::compat::format("Failed to prepare delete: {}",
1100 sqlite3_errmsg(db_)),
1101 "storage");
1102 }
1103
1104 sqlite3_bind_text(stmt, 1, sop_uid.data(),
1105 static_cast<int>(sop_uid.size()), SQLITE_TRANSIENT);
1106
1107 rc = sqlite3_step(stmt);
1108 sqlite3_finalize(stmt);
1109
1110 if (rc != SQLITE_DONE) {
1111 return make_error<std::monostate>(
1112 rc,
1113 kcenon::pacs::compat::format("Failed to delete instance: {}",
1114 sqlite3_errmsg(db_)),
1115 "storage");
1116 }
1117
1118 return ok();
1119}

◆ find_instance()

auto kcenon::pacs::storage::instance_repository::find_instance ( std::string_view sop_uid) const -> std::optional<instance_record>
nodiscard

Find an instance record by its SOP Instance UID.

Parameters
sop_uidSOP Instance UID to search for
Returns
The matching instance record, or std::nullopt if not found

Definition at line 910 of file instance_repository.cpp.

911 {
912 const char* sql = R"(
913 SELECT instance_pk, series_pk, sop_uid, sop_class_uid, instance_number,
914 transfer_syntax, content_date, content_time,
915 rows, columns, bits_allocated, number_of_frames,
916 file_path, file_size, file_hash, created_at
917 FROM instances
918 WHERE sop_uid = ?;
919 )";
920
921 sqlite3_stmt* stmt = nullptr;
922 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
923 if (rc != SQLITE_OK) {
924 return std::nullopt;
925 }
926
927 sqlite3_bind_text(stmt, 1, sop_uid.data(),
928 static_cast<int>(sop_uid.size()), SQLITE_TRANSIENT);
929
930 rc = sqlite3_step(stmt);
931 if (rc != SQLITE_ROW) {
932 sqlite3_finalize(stmt);
933 return std::nullopt;
934 }
935
936 auto record = parse_instance_row(stmt);
937 sqlite3_finalize(stmt);
938 return record;
939}
auto parse_instance_row(void *stmt) const -> instance_record

◆ find_instance_by_pk()

auto kcenon::pacs::storage::instance_repository::find_instance_by_pk ( int64_t pk) const -> std::optional<instance_record>
nodiscard

Find an instance record by its database primary key.

Parameters
pkPrimary key of the instance record
Returns
The matching instance record, or std::nullopt if not found

Definition at line 941 of file instance_repository.cpp.

942 {
943 const char* sql = R"(
944 SELECT instance_pk, series_pk, sop_uid, sop_class_uid, instance_number,
945 transfer_syntax, content_date, content_time,
946 rows, columns, bits_allocated, number_of_frames,
947 file_path, file_size, file_hash, created_at
948 FROM instances
949 WHERE instance_pk = ?;
950 )";
951
952 sqlite3_stmt* stmt = nullptr;
953 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
954 if (rc != SQLITE_OK) {
955 return std::nullopt;
956 }
957
958 sqlite3_bind_int64(stmt, 1, pk);
959
960 rc = sqlite3_step(stmt);
961 if (rc != SQLITE_ROW) {
962 sqlite3_finalize(stmt);
963 return std::nullopt;
964 }
965
966 auto record = parse_instance_row(stmt);
967 sqlite3_finalize(stmt);
968 return record;
969}

◆ get_file_path()

auto kcenon::pacs::storage::instance_repository::get_file_path ( std::string_view sop_instance_uid) const -> Result<std::optional<std::string>>
nodiscard

Retrieve the file system path for a stored instance.

Parameters
sop_instance_uidSOP Instance UID to look up
Returns
Result containing the file path if found, std::nullopt if the instance exists but has no file, or an error

Definition at line 1173 of file instance_repository.cpp.

1174 {
1175 const char* sql = "SELECT file_path FROM instances WHERE sop_uid = ?;";
1176
1177 sqlite3_stmt* stmt = nullptr;
1178 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1179 if (rc != SQLITE_OK) {
1180 return make_error<std::optional<std::string>>(
1182 kcenon::pacs::compat::format("Failed to prepare query: {}",
1183 sqlite3_errmsg(db_)),
1184 "storage");
1185 }
1186
1187 sqlite3_bind_text(stmt, 1, sop_instance_uid.data(),
1188 static_cast<int>(sop_instance_uid.size()),
1189 SQLITE_TRANSIENT);
1190
1191 rc = sqlite3_step(stmt);
1192 if (rc != SQLITE_ROW) {
1193 sqlite3_finalize(stmt);
1194 return ok(std::optional<std::string>(std::nullopt));
1195 }
1196
1197 auto path = get_text(stmt, 0);
1198 sqlite3_finalize(stmt);
1199 return ok(std::optional<std::string>(path));
1200}
constexpr dicom_tag sop_instance_uid
SOP Instance UID.
constexpr int database_query_error
Definition result.h:122

References kcenon::pacs::error_codes::database_query_error.

◆ get_series_files()

auto kcenon::pacs::storage::instance_repository::get_series_files ( std::string_view series_instance_uid) const -> Result<std::vector<std::string>>
nodiscard

Retrieve all file paths for instances belonging to a series.

Parameters
series_instance_uidSeries Instance UID to look up
Returns
Result containing a vector of file paths, or an error

Definition at line 1237 of file instance_repository.cpp.

1238 {
1239 std::vector<std::string> results;
1240
1241 const char* sql = R"(
1242 SELECT i.file_path
1243 FROM instances i
1244 JOIN series se ON i.series_pk = se.series_pk
1245 WHERE se.series_uid = ?
1246 ORDER BY i.instance_number;
1247 )";
1248
1249 sqlite3_stmt* stmt = nullptr;
1250 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1251 if (rc != SQLITE_OK) {
1252 return make_error<std::vector<std::string>>(
1254 kcenon::pacs::compat::format("Failed to prepare query: {}",
1255 sqlite3_errmsg(db_)),
1256 "storage");
1257 }
1258
1259 sqlite3_bind_text(stmt, 1, series_instance_uid.data(),
1260 static_cast<int>(series_instance_uid.size()),
1261 SQLITE_TRANSIENT);
1262
1263 while (sqlite3_step(stmt) == SQLITE_ROW) {
1264 results.push_back(get_text(stmt, 0));
1265 }
1266
1267 sqlite3_finalize(stmt);
1268 return ok(std::move(results));
1269}
constexpr dicom_tag series_instance_uid
Series Instance UID.

References kcenon::pacs::error_codes::database_query_error.

◆ get_study_files()

auto kcenon::pacs::storage::instance_repository::get_study_files ( std::string_view study_instance_uid) const -> Result<std::vector<std::string>>
nodiscard

Retrieve all file paths for instances belonging to a study.

Parameters
study_instance_uidStudy Instance UID to look up
Returns
Result containing a vector of file paths, or an error

Definition at line 1202 of file instance_repository.cpp.

1203 {
1204 std::vector<std::string> results;
1205
1206 const char* sql = R"(
1207 SELECT i.file_path
1208 FROM instances i
1209 JOIN series se ON i.series_pk = se.series_pk
1210 JOIN studies st ON se.study_pk = st.study_pk
1211 WHERE st.study_uid = ?
1212 ORDER BY se.series_number, i.instance_number;
1213 )";
1214
1215 sqlite3_stmt* stmt = nullptr;
1216 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1217 if (rc != SQLITE_OK) {
1218 return make_error<std::vector<std::string>>(
1220 kcenon::pacs::compat::format("Failed to prepare query: {}",
1221 sqlite3_errmsg(db_)),
1222 "storage");
1223 }
1224
1225 sqlite3_bind_text(stmt, 1, study_instance_uid.data(),
1226 static_cast<int>(study_instance_uid.size()),
1227 SQLITE_TRANSIENT);
1228
1229 while (sqlite3_step(stmt) == SQLITE_ROW) {
1230 results.push_back(get_text(stmt, 0));
1231 }
1232
1233 sqlite3_finalize(stmt);
1234 return ok(std::move(results));
1235}
constexpr dicom_tag study_instance_uid
Study Instance UID.

References kcenon::pacs::error_codes::database_query_error.

◆ instance_count() [1/2]

auto kcenon::pacs::storage::instance_repository::instance_count ( ) const -> Result<size_t>
nodiscard

Get the total number of instance records in the repository.

Returns
Result containing the total instance count, or an error

Definition at line 1121 of file instance_repository.cpp.

1121 {
1122 const char* sql = "SELECT COUNT(*) FROM instances;";
1123
1124 sqlite3_stmt* stmt = nullptr;
1125 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1126 if (rc != SQLITE_OK) {
1127 return make_error<size_t>(
1129 kcenon::pacs::compat::format("Failed to prepare query: {}",
1130 sqlite3_errmsg(db_)),
1131 "storage");
1132 }
1133
1134 size_t count = 0;
1135 if (sqlite3_step(stmt) == SQLITE_ROW) {
1136 count = static_cast<size_t>(sqlite3_column_int64(stmt, 0));
1137 }
1138
1139 sqlite3_finalize(stmt);
1140 return ok(count);
1141}

References kcenon::pacs::error_codes::database_query_error, and db_.

◆ instance_count() [2/2]

auto kcenon::pacs::storage::instance_repository::instance_count ( std::string_view series_uid) const -> Result<size_t>
nodiscard

Get the number of instance records belonging to a given series.

Parameters
series_uidSeries Instance UID to count instances for
Returns
Result containing the instance count for the series, or an error

Definition at line 1143 of file instance_repository.cpp.

1144 {
1145 const char* sql = R"(
1146 SELECT COUNT(*) FROM instances i
1147 JOIN series s ON i.series_pk = s.series_pk
1148 WHERE s.series_uid = ?;
1149 )";
1150
1151 sqlite3_stmt* stmt = nullptr;
1152 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
1153 if (rc != SQLITE_OK) {
1154 return make_error<size_t>(
1156 kcenon::pacs::compat::format("Failed to prepare query: {}",
1157 sqlite3_errmsg(db_)),
1158 "storage");
1159 }
1160
1161 sqlite3_bind_text(stmt, 1, series_uid.data(),
1162 static_cast<int>(series_uid.size()), SQLITE_TRANSIENT);
1163
1164 size_t count = 0;
1165 if (sqlite3_step(stmt) == SQLITE_ROW) {
1166 count = static_cast<size_t>(sqlite3_column_int64(stmt, 0));
1167 }
1168
1169 sqlite3_finalize(stmt);
1170 return ok(count);
1171}

References kcenon::pacs::error_codes::database_query_error.

◆ list_instances()

auto kcenon::pacs::storage::instance_repository::list_instances ( std::string_view series_uid) const -> Result<std::vector<instance_record>>
nodiscard

List all instance records belonging to a given series.

Parameters
series_uidSeries Instance UID to filter by
Returns
Result containing a vector of matching instance records, or an error

Definition at line 971 of file instance_repository.cpp.

972 {
973 std::vector<instance_record> results;
974
975 const char* sql = R"(
976 SELECT i.instance_pk, i.series_pk, i.sop_uid, i.sop_class_uid,
977 i.instance_number, i.transfer_syntax, i.content_date,
978 i.content_time, i.rows, i.columns, i.bits_allocated,
979 i.number_of_frames, i.file_path, i.file_size, i.file_hash,
980 i.created_at
981 FROM instances i
982 JOIN series s ON i.series_pk = s.series_pk
983 WHERE s.series_uid = ?
984 ORDER BY i.instance_number ASC, i.sop_uid ASC;
985 )";
986
987 sqlite3_stmt* stmt = nullptr;
988 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
989 if (rc != SQLITE_OK) {
990 return make_error<std::vector<instance_record>>(
992 kcenon::pacs::compat::format("Failed to prepare query: {}",
993 sqlite3_errmsg(db_)),
994 "storage");
995 }
996
997 sqlite3_bind_text(stmt, 1, series_uid.data(),
998 static_cast<int>(series_uid.size()), SQLITE_TRANSIENT);
999
1000 while (sqlite3_step(stmt) == SQLITE_ROW) {
1001 results.push_back(parse_instance_row(stmt));
1002 }
1003
1004 sqlite3_finalize(stmt);
1005 return ok(std::move(results));
1006}

References kcenon::pacs::error_codes::database_query_error.

◆ operator=() [1/2]

auto kcenon::pacs::storage::instance_repository::operator= ( const instance_repository & ) -> instance_repository &=delete
delete

◆ operator=() [2/2]

auto kcenon::pacs::storage::instance_repository::operator= ( instance_repository && ) -> instance_repository &
defaultnoexcept

◆ parse_instance_row()

auto kcenon::pacs::storage::instance_repository::parse_instance_row ( void * stmt) const -> instance_record
nodiscardprivate

Definition at line 733 of file instance_repository.cpp.

734 {
735 auto* stmt = static_cast<sqlite3_stmt*>(stmt_ptr);
736 instance_record record;
737
738 record.pk = sqlite3_column_int64(stmt, 0);
739 record.series_pk = sqlite3_column_int64(stmt, 1);
740 record.sop_uid = get_text(stmt, 2);
741 record.sop_class_uid = get_text(stmt, 3);
742
743 if (sqlite3_column_type(stmt, 4) != SQLITE_NULL) {
744 record.instance_number = sqlite3_column_int(stmt, 4);
745 }
746
747 record.transfer_syntax = get_text(stmt, 5);
748 record.content_date = get_text(stmt, 6);
749 record.content_time = get_text(stmt, 7);
750
751 if (sqlite3_column_type(stmt, 8) != SQLITE_NULL) {
752 record.rows = sqlite3_column_int(stmt, 8);
753 }
754 if (sqlite3_column_type(stmt, 9) != SQLITE_NULL) {
755 record.columns = sqlite3_column_int(stmt, 9);
756 }
757 if (sqlite3_column_type(stmt, 10) != SQLITE_NULL) {
758 record.bits_allocated = sqlite3_column_int(stmt, 10);
759 }
760 if (sqlite3_column_type(stmt, 11) != SQLITE_NULL) {
761 record.number_of_frames = sqlite3_column_int(stmt, 11);
762 }
763
764 record.file_path = get_text(stmt, 12);
765 record.file_size = sqlite3_column_int64(stmt, 13);
766 record.file_hash = get_text(stmt, 14);
767 record.created_at = parse_datetime(get_text(stmt, 15).c_str());
768
769 return record;
770}

◆ parse_timestamp()

auto kcenon::pacs::storage::instance_repository::parse_timestamp ( const std::string & str) -> std::chrono::system_clock::time_point
staticnodiscardprivate

Definition at line 728 of file instance_repository.cpp.

729 {
730 return parse_datetime(str.c_str());
731}

◆ search_instances()

auto kcenon::pacs::storage::instance_repository::search_instances ( const instance_query & query) const -> Result<std::vector<instance_record>>
nodiscard

Search for instance records matching the given query criteria.

Parameters
queryQuery parameters including optional filters and pagination
Returns
Result containing a vector of matching instance records, or an error

Definition at line 1008 of file instance_repository.cpp.

1009 {
1010 std::vector<instance_record> results;
1011
1012 std::string sql = R"(
1013 SELECT i.instance_pk, i.series_pk, i.sop_uid, i.sop_class_uid,
1014 i.instance_number, i.transfer_syntax, i.content_date,
1015 i.content_time, i.rows, i.columns, i.bits_allocated,
1016 i.number_of_frames, i.file_path, i.file_size, i.file_hash,
1017 i.created_at
1018 FROM instances i
1019 JOIN series s ON i.series_pk = s.series_pk
1020 WHERE 1=1
1021 )";
1022
1023 std::vector<std::string> params;
1024
1025 if (query.series_uid.has_value()) {
1026 sql += " AND s.series_uid = ?";
1027 params.push_back(*query.series_uid);
1028 }
1029 if (query.sop_uid.has_value()) {
1030 sql += " AND i.sop_uid = ?";
1031 params.push_back(*query.sop_uid);
1032 }
1033 if (query.sop_class_uid.has_value()) {
1034 sql += " AND i.sop_class_uid = ?";
1035 params.push_back(*query.sop_class_uid);
1036 }
1037 if (query.content_date.has_value()) {
1038 sql += " AND i.content_date = ?";
1039 params.push_back(*query.content_date);
1040 }
1041 if (query.content_date_from.has_value()) {
1042 sql += " AND i.content_date >= ?";
1043 params.push_back(*query.content_date_from);
1044 }
1045 if (query.content_date_to.has_value()) {
1046 sql += " AND i.content_date <= ?";
1047 params.push_back(*query.content_date_to);
1048 }
1049
1050 sql += " ORDER BY i.instance_number ASC, i.sop_uid ASC";
1051
1052 if (query.limit > 0) {
1053 sql += kcenon::pacs::compat::format(" LIMIT {}", query.limit);
1054 }
1055 if (query.offset > 0) {
1056 sql += kcenon::pacs::compat::format(" OFFSET {}", query.offset);
1057 }
1058
1059 sqlite3_stmt* stmt = nullptr;
1060 auto rc = sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr);
1061 if (rc != SQLITE_OK) {
1062 return make_error<std::vector<instance_record>>(
1064 kcenon::pacs::compat::format("Failed to prepare query: {}",
1065 sqlite3_errmsg(db_)),
1066 "storage");
1067 }
1068
1069 int bind_index = 1;
1070 for (const auto& param : params) {
1071 sqlite3_bind_text(stmt, bind_index++, param.c_str(), -1,
1072 SQLITE_TRANSIENT);
1073 }
1074
1075 while (sqlite3_step(stmt) == SQLITE_ROW) {
1076 auto record = parse_instance_row(stmt);
1077 if (query.instance_number.has_value()) {
1078 if (!record.instance_number.has_value() ||
1079 *record.instance_number != *query.instance_number) {
1080 continue;
1081 }
1082 }
1083 results.push_back(std::move(record));
1084 }
1085
1086 sqlite3_finalize(stmt);
1087 return ok(std::move(results));
1088}
const atna_coded_value query
Query (110112)

References kcenon::pacs::error_codes::database_query_error.

◆ upsert_instance() [1/2]

auto kcenon::pacs::storage::instance_repository::upsert_instance ( const instance_record & record) -> Result<int64_t>
nodiscard

Insert or update an instance record from an existing record struct.

Parameters
recordThe instance record to upsert
Returns
Result containing the primary key of the upserted record, or an error

Definition at line 791 of file instance_repository.cpp.

792 {
793 if (record.sop_uid.empty()) {
794 return make_error<int64_t>(-1, "SOP Instance UID is required",
795 "storage");
796 }
797
798 if (record.sop_uid.length() > 64) {
799 return make_error<int64_t>(
800 -1, "SOP Instance UID exceeds maximum length of 64 characters",
801 "storage");
802 }
803
804 if (record.series_pk <= 0) {
805 return make_error<int64_t>(-1, "Valid series_pk is required",
806 "storage");
807 }
808
809 if (record.file_path.empty()) {
810 return make_error<int64_t>(-1, "File path is required", "storage");
811 }
812
813 if (record.file_size < 0) {
814 return make_error<int64_t>(-1, "File size must be non-negative",
815 "storage");
816 }
817
818 const char* sql = R"(
819 INSERT INTO instances (
820 series_pk, sop_uid, sop_class_uid, instance_number,
821 transfer_syntax, content_date, content_time,
822 rows, columns, bits_allocated, number_of_frames,
823 file_path, file_size, file_hash
824 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
825 ON CONFLICT(sop_uid) DO UPDATE SET
826 series_pk = excluded.series_pk,
827 sop_class_uid = excluded.sop_class_uid,
828 instance_number = excluded.instance_number,
829 transfer_syntax = excluded.transfer_syntax,
830 content_date = excluded.content_date,
831 content_time = excluded.content_time,
832 rows = excluded.rows,
833 columns = excluded.columns,
834 bits_allocated = excluded.bits_allocated,
835 number_of_frames = excluded.number_of_frames,
836 file_path = excluded.file_path,
837 file_size = excluded.file_size,
838 file_hash = excluded.file_hash
839 RETURNING instance_pk;
840 )";
841
842 sqlite3_stmt* stmt = nullptr;
843 auto rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
844 if (rc != SQLITE_OK) {
845 return make_error<int64_t>(
846 rc,
847 kcenon::pacs::compat::format("Failed to prepare statement: {}",
848 sqlite3_errmsg(db_)),
849 "storage");
850 }
851
852 sqlite3_bind_int64(stmt, 1, record.series_pk);
853 sqlite3_bind_text(stmt, 2, record.sop_uid.c_str(), -1, SQLITE_TRANSIENT);
854 sqlite3_bind_text(stmt, 3, record.sop_class_uid.c_str(), -1,
855 SQLITE_TRANSIENT);
856
857 if (record.instance_number.has_value()) {
858 sqlite3_bind_int(stmt, 4, *record.instance_number);
859 } else {
860 sqlite3_bind_null(stmt, 4);
861 }
862
863 sqlite3_bind_text(stmt, 5, record.transfer_syntax.c_str(), -1,
864 SQLITE_TRANSIENT);
865 sqlite3_bind_text(stmt, 6, record.content_date.c_str(), -1,
866 SQLITE_TRANSIENT);
867 sqlite3_bind_text(stmt, 7, record.content_time.c_str(), -1,
868 SQLITE_TRANSIENT);
869
870 if (record.rows.has_value()) {
871 sqlite3_bind_int(stmt, 8, *record.rows);
872 } else {
873 sqlite3_bind_null(stmt, 8);
874 }
875 if (record.columns.has_value()) {
876 sqlite3_bind_int(stmt, 9, *record.columns);
877 } else {
878 sqlite3_bind_null(stmt, 9);
879 }
880 if (record.bits_allocated.has_value()) {
881 sqlite3_bind_int(stmt, 10, *record.bits_allocated);
882 } else {
883 sqlite3_bind_null(stmt, 10);
884 }
885 if (record.number_of_frames.has_value()) {
886 sqlite3_bind_int(stmt, 11, *record.number_of_frames);
887 } else {
888 sqlite3_bind_null(stmt, 11);
889 }
890
891 sqlite3_bind_text(stmt, 12, record.file_path.c_str(), -1, SQLITE_TRANSIENT);
892 sqlite3_bind_int64(stmt, 13, record.file_size);
893 sqlite3_bind_text(stmt, 14, record.file_hash.c_str(), -1, SQLITE_TRANSIENT);
894
895 rc = sqlite3_step(stmt);
896 if (rc != SQLITE_ROW) {
897 auto error_msg = sqlite3_errmsg(db_);
898 sqlite3_finalize(stmt);
899 return make_error<int64_t>(
900 rc,
901 kcenon::pacs::compat::format("Failed to upsert instance: {}", error_msg),
902 "storage");
903 }
904
905 auto pk = sqlite3_column_int64(stmt, 0);
906 sqlite3_finalize(stmt);
907 return pk;
908}

◆ upsert_instance() [2/2]

auto kcenon::pacs::storage::instance_repository::upsert_instance ( int64_t series_pk,
std::string_view sop_uid,
std::string_view sop_class_uid,
std::string_view file_path,
int64_t file_size,
std::string_view transfer_syntax = "",
std::optional< int > instance_number = std::nullopt ) -> Result<int64_t>
nodiscard

Insert or update an instance record by individual fields.

Parameters
series_pkPrimary key of the parent series
sop_uidSOP Instance UID (DICOM tag 0008,0018)
sop_class_uidSOP Class UID (DICOM tag 0008,0016)
file_pathFile system path where the instance is stored
file_sizeSize of the stored file in bytes
transfer_syntaxTransfer Syntax UID (DICOM tag 0002,0010), empty if unknown
instance_numberInstance Number (DICOM tag 0020,0013), nullopt if absent
Returns
Result containing the primary key of the upserted record, or an error

Definition at line 772 of file instance_repository.cpp.

779 {
780 instance_record record;
781 record.series_pk = series_pk;
782 record.sop_uid = std::string(sop_uid);
783 record.sop_class_uid = std::string(sop_class_uid);
784 record.file_path = std::string(file_path);
785 record.file_size = file_size;
786 record.transfer_syntax = std::string(transfer_syntax);
787 record.instance_number = instance_number;
788 return upsert_instance(record);
789}
auto upsert_instance(int64_t series_pk, std::string_view sop_uid, std::string_view sop_class_uid, std::string_view file_path, int64_t file_size, std::string_view transfer_syntax="", std::optional< int > instance_number=std::nullopt) -> Result< int64_t >
Insert or update an instance record by individual fields.
constexpr dicom_tag instance_number
Instance Number.

Member Data Documentation

◆ db_

sqlite3* kcenon::pacs::storage::instance_repository::db_ {nullptr}
private

Definition at line 320 of file instance_repository.h.

320{nullptr};

Referenced by instance_count().


The documentation for this class was generated from the following files: