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

Repository for remote node persistence (legacy SQLite interface) More...

#include <node_repository.h>

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

Public Member Functions

 node_repository (sqlite3 *db)
 
 ~node_repository ()
 
 node_repository (const node_repository &)=delete
 
auto operator= (const node_repository &) -> node_repository &=delete
 
 node_repository (node_repository &&) noexcept
 
auto operator= (node_repository &&) noexcept -> node_repository &
 
auto upsert (const client::remote_node &node) -> Result< int64_t >
 
auto find_by_id (std::string_view node_id) const -> std::optional< client::remote_node >
 
auto find_by_pk (int64_t pk) const -> std::optional< client::remote_node >
 
auto find_all () const -> std::vector< client::remote_node >
 
auto find_by_status (client::node_status status) const -> std::vector< client::remote_node >
 
auto remove (std::string_view node_id) -> VoidResult
 
auto exists (std::string_view node_id) const -> bool
 
auto count () const -> size_t
 
auto update_status (std::string_view node_id, client::node_status status, std::string_view error_message="") -> VoidResult
 
auto update_last_verified (std::string_view node_id) -> VoidResult
 
auto is_valid () const noexcept -> bool
 

Private Member Functions

auto parse_row (void *stmt) const -> client::remote_node
 

Private Attributes

sqlite3 * db_ {nullptr}
 

Detailed Description

Repository for remote node persistence (legacy SQLite interface)

This is the legacy interface maintained for builds without database_system. New code should use the base_repository version when PACS_WITH_DATABASE_SYSTEM is defined.

Definition at line 177 of file node_repository.h.

Constructor & Destructor Documentation

◆ node_repository() [1/3]

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

Definition at line 449 of file node_repository.cpp.

◆ ~node_repository()

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

◆ node_repository() [2/3]

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

◆ node_repository() [3/3]

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

Member Function Documentation

◆ count()

size_t kcenon::pacs::storage::node_repository::count ( ) const -> size_t
nodiscard

Definition at line 703 of file node_repository.cpp.

703 {
704 if (!db_) return 0;
705
706 static constexpr const char* sql = "SELECT COUNT(*) FROM remote_nodes";
707
708 sqlite3_stmt* stmt = nullptr;
709 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
710 return 0;
711 }
712
713 size_t result = 0;
714 if (sqlite3_step(stmt) == SQLITE_ROW) {
715 result = static_cast<size_t>(sqlite3_column_int64(stmt, 0));
716 }
717
718 sqlite3_finalize(stmt);
719 return result;
720}

References db_.

◆ exists()

bool kcenon::pacs::storage::node_repository::exists ( std::string_view node_id) const -> bool
nodiscard

Definition at line 686 of file node_repository.cpp.

686 {
687 if (!db_) return false;
688
689 static constexpr const char* sql = "SELECT 1 FROM remote_nodes WHERE node_id = ?";
690
691 sqlite3_stmt* stmt = nullptr;
692 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
693 return false;
694 }
695
696 sqlite3_bind_text(stmt, 1, node_id.data(), static_cast<int>(node_id.size()), SQLITE_TRANSIENT);
697
698 bool found = (sqlite3_step(stmt) == SQLITE_ROW);
699 sqlite3_finalize(stmt);
700 return found;
701}

References db_.

◆ find_all()

std::vector< client::remote_node > kcenon::pacs::storage::node_repository::find_all ( ) const -> std::vector<client::remote_node>
nodiscard

Definition at line 602 of file node_repository.cpp.

602 {
603 std::vector<client::remote_node> result;
604 if (!db_) return result;
605
606 static constexpr const char* sql = R"(
607 SELECT pk, node_id, name, ae_title, host, port,
608 supports_find, supports_move, supports_get, supports_store, supports_worklist,
609 connection_timeout_sec, dimse_timeout_sec, max_associations,
610 status, last_verified, last_error, last_error_message,
611 created_at, updated_at
612 FROM remote_nodes ORDER BY name
613 )";
614
615 sqlite3_stmt* stmt = nullptr;
616 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
617 return result;
618 }
619
620 while (sqlite3_step(stmt) == SQLITE_ROW) {
621 result.push_back(parse_row(stmt));
622 }
623
624 sqlite3_finalize(stmt);
625 return result;
626}
auto parse_row(void *stmt) const -> client::remote_node

References db_, and parse_row().

Here is the call graph for this function:

◆ find_by_id()

std::optional< client::remote_node > kcenon::pacs::storage::node_repository::find_by_id ( std::string_view node_id) const -> std::optional<client::remote_node>
nodiscard

Definition at line 546 of file node_repository.cpp.

546 {
547 if (!db_) return std::nullopt;
548
549 static constexpr const char* sql = R"(
550 SELECT pk, node_id, name, ae_title, host, port,
551 supports_find, supports_move, supports_get, supports_store, supports_worklist,
552 connection_timeout_sec, dimse_timeout_sec, max_associations,
553 status, last_verified, last_error, last_error_message,
554 created_at, updated_at
555 FROM remote_nodes WHERE node_id = ?
556 )";
557
558 sqlite3_stmt* stmt = nullptr;
559 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
560 return std::nullopt;
561 }
562
563 sqlite3_bind_text(stmt, 1, node_id.data(), static_cast<int>(node_id.size()), SQLITE_TRANSIENT);
564
565 std::optional<client::remote_node> result;
566 if (sqlite3_step(stmt) == SQLITE_ROW) {
567 result = parse_row(stmt);
568 }
569
570 sqlite3_finalize(stmt);
571 return result;
572}

References db_, and parse_row().

Here is the call graph for this function:

◆ find_by_pk()

std::optional< client::remote_node > kcenon::pacs::storage::node_repository::find_by_pk ( int64_t pk) const -> std::optional<client::remote_node>
nodiscard

Definition at line 574 of file node_repository.cpp.

574 {
575 if (!db_) return std::nullopt;
576
577 static constexpr const char* sql = R"(
578 SELECT pk, node_id, name, ae_title, host, port,
579 supports_find, supports_move, supports_get, supports_store, supports_worklist,
580 connection_timeout_sec, dimse_timeout_sec, max_associations,
581 status, last_verified, last_error, last_error_message,
582 created_at, updated_at
583 FROM remote_nodes WHERE pk = ?
584 )";
585
586 sqlite3_stmt* stmt = nullptr;
587 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
588 return std::nullopt;
589 }
590
591 sqlite3_bind_int64(stmt, 1, pk);
592
593 std::optional<client::remote_node> result;
594 if (sqlite3_step(stmt) == SQLITE_ROW) {
595 result = parse_row(stmt);
596 }
597
598 sqlite3_finalize(stmt);
599 return result;
600}

References db_, and parse_row().

Here is the call graph for this function:

◆ find_by_status()

std::vector< client::remote_node > kcenon::pacs::storage::node_repository::find_by_status ( client::node_status status) const -> std::vector<client::remote_node>
nodiscard

Definition at line 628 of file node_repository.cpp.

629 {
630 std::vector<client::remote_node> result;
631 if (!db_) return result;
632
633 static constexpr const char* sql = R"(
634 SELECT pk, node_id, name, ae_title, host, port,
635 supports_find, supports_move, supports_get, supports_store, supports_worklist,
636 connection_timeout_sec, dimse_timeout_sec, max_associations,
637 status, last_verified, last_error, last_error_message,
638 created_at, updated_at
639 FROM remote_nodes WHERE status = ? ORDER BY name
640 )";
641
642 sqlite3_stmt* stmt = nullptr;
643 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
644 return result;
645 }
646
647 auto status_str = std::string(client::to_string(status));
648 sqlite3_bind_text(stmt, 1, status_str.c_str(), -1, SQLITE_TRANSIENT);
649
650 while (sqlite3_step(stmt) == SQLITE_ROW) {
651 result.push_back(parse_row(stmt));
652 }
653
654 sqlite3_finalize(stmt);
655 return result;
656}
constexpr const char * to_string(job_type type) noexcept
Convert job_type to string representation.
Definition job_types.h:54

References db_, parse_row(), and kcenon::pacs::client::to_string().

Here is the call graph for this function:

◆ is_valid()

bool kcenon::pacs::storage::node_repository::is_valid ( ) const -> bool
nodiscardnoexcept

Definition at line 821 of file node_repository.cpp.

821 {
822 return db_ != nullptr;
823}

References db_.

◆ operator=() [1/2]

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

◆ operator=() [2/2]

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

◆ parse_row()

client::remote_node kcenon::pacs::storage::node_repository::parse_row ( void * stmt) const -> client::remote_node
nodiscardprivate

Definition at line 829 of file node_repository.cpp.

829 {
830 auto* stmt = static_cast<sqlite3_stmt*>(stmt_ptr);
831 client::remote_node node;
832
833 int col = 0;
834 node.pk = get_int64_column(stmt, col++);
835 node.node_id = get_text_column(stmt, col++);
836 node.name = get_text_column(stmt, col++);
837 node.ae_title = get_text_column(stmt, col++);
838 node.host = get_text_column(stmt, col++);
839 node.port = static_cast<uint16_t>(get_int_column(stmt, col++, 104));
840
841 node.supports_find = get_int_column(stmt, col++) != 0;
842 node.supports_move = get_int_column(stmt, col++) != 0;
843 node.supports_get = get_int_column(stmt, col++) != 0;
844 node.supports_store = get_int_column(stmt, col++) != 0;
845 node.supports_worklist = get_int_column(stmt, col++) != 0;
846
847 node.connection_timeout = std::chrono::seconds(get_int_column(stmt, col++, 30));
848 node.dimse_timeout = std::chrono::seconds(get_int_column(stmt, col++, 60));
849 node.max_associations = static_cast<size_t>(get_int_column(stmt, col++, 4));
850
851 auto status_str = get_text_column(stmt, col++);
852 node.status = client::node_status_from_string(status_str);
853
854 auto last_verified_str = get_text_column(stmt, col++);
855 node.last_verified = from_timestamp_string(last_verified_str.c_str());
856
857 auto last_error_str = get_text_column(stmt, col++);
858 node.last_error = from_timestamp_string(last_error_str.c_str());
859
860 node.last_error_message = get_text_column(stmt, col++);
861
862 auto created_at_str = get_text_column(stmt, col++);
863 node.created_at = from_timestamp_string(created_at_str.c_str());
864
865 auto updated_at_str = get_text_column(stmt, col++);
866 node.updated_at = from_timestamp_string(updated_at_str.c_str());
867
868 return node;
869}
node_status node_status_from_string(std::string_view str) noexcept
Parse node_status from string.
Definition remote_node.h:80

References kcenon::pacs::client::remote_node::ae_title, kcenon::pacs::client::remote_node::connection_timeout, kcenon::pacs::client::remote_node::created_at, kcenon::pacs::client::remote_node::dimse_timeout, kcenon::pacs::client::remote_node::host, kcenon::pacs::client::remote_node::last_error, kcenon::pacs::client::remote_node::last_error_message, kcenon::pacs::client::remote_node::last_verified, kcenon::pacs::client::remote_node::max_associations, kcenon::pacs::client::remote_node::name, kcenon::pacs::client::remote_node::node_id, kcenon::pacs::client::node_status_from_string(), kcenon::pacs::client::remote_node::pk, kcenon::pacs::client::remote_node::port, kcenon::pacs::client::remote_node::status, kcenon::pacs::client::remote_node::supports_find, kcenon::pacs::client::remote_node::supports_get, kcenon::pacs::client::remote_node::supports_move, kcenon::pacs::client::remote_node::supports_store, kcenon::pacs::client::remote_node::supports_worklist, and kcenon::pacs::client::remote_node::updated_at.

Referenced by find_all(), find_by_id(), find_by_pk(), and find_by_status().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ remove()

VoidResult kcenon::pacs::storage::node_repository::remove ( std::string_view node_id) -> VoidResult
nodiscard

Definition at line 658 of file node_repository.cpp.

658 {
659 if (!db_) {
660 return VoidResult(kcenon::common::error_info{-1, "Database not initialized", "node_repository"});
661 }
662
663 static constexpr const char* sql = "DELETE FROM remote_nodes WHERE node_id = ?";
664
665 sqlite3_stmt* stmt = nullptr;
666 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
667 return VoidResult(kcenon::common::error_info{
668 -1, "Failed to prepare statement: " + std::string(sqlite3_errmsg(db_)),
669 "node_repository"});
670 }
671
672 sqlite3_bind_text(stmt, 1, node_id.data(), static_cast<int>(node_id.size()), SQLITE_TRANSIENT);
673
674 auto rc = sqlite3_step(stmt);
675 sqlite3_finalize(stmt);
676
677 if (rc != SQLITE_DONE) {
678 return VoidResult(kcenon::common::error_info{
679 -1, "Failed to delete: " + std::string(sqlite3_errmsg(db_)),
680 "node_repository"});
681 }
682
683 return kcenon::common::ok();
684}

References db_.

◆ update_last_verified()

VoidResult kcenon::pacs::storage::node_repository::update_last_verified ( std::string_view node_id) -> VoidResult
nodiscard

Definition at line 784 of file node_repository.cpp.

784 {
785 if (!db_) {
786 return VoidResult(kcenon::common::error_info{-1, "Database not initialized", "node_repository"});
787 }
788
789 static constexpr const char* sql = R"(
790 UPDATE remote_nodes SET
791 last_verified = CURRENT_TIMESTAMP,
792 updated_at = CURRENT_TIMESTAMP
793 WHERE node_id = ?
794 )";
795
796 sqlite3_stmt* stmt = nullptr;
797 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
798 return VoidResult(kcenon::common::error_info{
799 -1, "Failed to prepare statement: " + std::string(sqlite3_errmsg(db_)),
800 "node_repository"});
801 }
802
803 sqlite3_bind_text(stmt, 1, node_id.data(), static_cast<int>(node_id.size()), SQLITE_TRANSIENT);
804
805 auto rc = sqlite3_step(stmt);
806 sqlite3_finalize(stmt);
807
808 if (rc != SQLITE_DONE) {
809 return VoidResult(kcenon::common::error_info{
810 -1, "Failed to update last_verified: " + std::string(sqlite3_errmsg(db_)),
811 "node_repository"});
812 }
813
814 return kcenon::common::ok();
815}

References db_.

◆ update_status()

VoidResult kcenon::pacs::storage::node_repository::update_status ( std::string_view node_id,
client::node_status status,
std::string_view error_message = "" ) -> VoidResult
nodiscard

Definition at line 726 of file node_repository.cpp.

729 {
730
731 if (!db_) {
732 return VoidResult(kcenon::common::error_info{-1, "Database not initialized", "node_repository"});
733 }
734
735 const char* sql = nullptr;
736 if (status == client::node_status::error || status == client::node_status::offline) {
737 sql = R"(
738 UPDATE remote_nodes SET
739 status = ?,
740 last_error = CURRENT_TIMESTAMP,
741 last_error_message = ?,
742 updated_at = CURRENT_TIMESTAMP
743 WHERE node_id = ?
744 )";
745 } else {
746 sql = R"(
747 UPDATE remote_nodes SET
748 status = ?,
749 updated_at = CURRENT_TIMESTAMP
750 WHERE node_id = ?
751 )";
752 }
753
754 sqlite3_stmt* stmt = nullptr;
755 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
756 return VoidResult(kcenon::common::error_info{
757 -1, "Failed to prepare statement: " + std::string(sqlite3_errmsg(db_)),
758 "node_repository"});
759 }
760
761 auto status_str = std::string(client::to_string(status));
762 int idx = 1;
763 sqlite3_bind_text(stmt, idx++, status_str.c_str(), -1, SQLITE_TRANSIENT);
764
765 if (status == client::node_status::error || status == client::node_status::offline) {
766 sqlite3_bind_text(stmt, idx++, error_message.data(),
767 static_cast<int>(error_message.size()), SQLITE_TRANSIENT);
768 }
769
770 sqlite3_bind_text(stmt, idx++, node_id.data(), static_cast<int>(node_id.size()), SQLITE_TRANSIENT);
771
772 auto rc = sqlite3_step(stmt);
773 sqlite3_finalize(stmt);
774
775 if (rc != SQLITE_DONE) {
776 return VoidResult(kcenon::common::error_info{
777 -1, "Failed to update status: " + std::string(sqlite3_errmsg(db_)),
778 "node_repository"});
779 }
780
781 return kcenon::common::ok();
782}
@ offline
Node is not responding.
@ error
Node returned an error.

References db_, kcenon::pacs::client::error, kcenon::pacs::client::offline, and kcenon::pacs::client::to_string().

Here is the call graph for this function:

◆ upsert()

Result< int64_t > kcenon::pacs::storage::node_repository::upsert ( const client::remote_node & node) -> Result<int64_t>
nodiscard

Definition at line 461 of file node_repository.cpp.

461 {
462 if (!db_) {
463 return kcenon::common::make_error<int64_t>(
464 -1, "Database not initialized", "node_repository");
465 }
466
467 static constexpr const char* sql = R"(
468 INSERT INTO remote_nodes (
469 node_id, name, ae_title, host, port,
470 supports_find, supports_move, supports_get, supports_store, supports_worklist,
471 connection_timeout_sec, dimse_timeout_sec, max_associations,
472 status, last_verified, last_error, last_error_message,
473 created_at, updated_at
474 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
475 ON CONFLICT(node_id) DO UPDATE SET
476 name = excluded.name,
477 ae_title = excluded.ae_title,
478 host = excluded.host,
479 port = excluded.port,
480 supports_find = excluded.supports_find,
481 supports_move = excluded.supports_move,
482 supports_get = excluded.supports_get,
483 supports_store = excluded.supports_store,
484 supports_worklist = excluded.supports_worklist,
485 connection_timeout_sec = excluded.connection_timeout_sec,
486 dimse_timeout_sec = excluded.dimse_timeout_sec,
487 max_associations = excluded.max_associations,
488 updated_at = CURRENT_TIMESTAMP
489 RETURNING pk
490 )";
491
492 sqlite3_stmt* stmt = nullptr;
493 if (sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr) != SQLITE_OK) {
494 return kcenon::common::make_error<int64_t>(
495 -1, "Failed to prepare statement: " + std::string(sqlite3_errmsg(db_)),
496 "node_repository");
497 }
498
499 int idx = 1;
500 sqlite3_bind_text(stmt, idx++, node.node_id.c_str(), -1, SQLITE_TRANSIENT);
501 sqlite3_bind_text(stmt, idx++, node.name.c_str(), -1, SQLITE_TRANSIENT);
502 sqlite3_bind_text(stmt, idx++, node.ae_title.c_str(), -1, SQLITE_TRANSIENT);
503 sqlite3_bind_text(stmt, idx++, node.host.c_str(), -1, SQLITE_TRANSIENT);
504 sqlite3_bind_int(stmt, idx++, node.port);
505 sqlite3_bind_int(stmt, idx++, node.supports_find ? 1 : 0);
506 sqlite3_bind_int(stmt, idx++, node.supports_move ? 1 : 0);
507 sqlite3_bind_int(stmt, idx++, node.supports_get ? 1 : 0);
508 sqlite3_bind_int(stmt, idx++, node.supports_store ? 1 : 0);
509 sqlite3_bind_int(stmt, idx++, node.supports_worklist ? 1 : 0);
510 sqlite3_bind_int(stmt, idx++, static_cast<int>(node.connection_timeout.count()));
511 sqlite3_bind_int(stmt, idx++, static_cast<int>(node.dimse_timeout.count()));
512 sqlite3_bind_int(stmt, idx++, static_cast<int>(node.max_associations));
513
514 auto status_str = std::string(client::to_string(node.status));
515 sqlite3_bind_text(stmt, idx++, status_str.c_str(), -1, SQLITE_TRANSIENT);
516
517 auto last_verified_str = to_timestamp_string(node.last_verified);
518 if (last_verified_str.empty()) {
519 sqlite3_bind_null(stmt, idx++);
520 } else {
521 sqlite3_bind_text(stmt, idx++, last_verified_str.c_str(), -1, SQLITE_TRANSIENT);
522 }
523
524 auto last_error_str = to_timestamp_string(node.last_error);
525 if (last_error_str.empty()) {
526 sqlite3_bind_null(stmt, idx++);
527 } else {
528 sqlite3_bind_text(stmt, idx++, last_error_str.c_str(), -1, SQLITE_TRANSIENT);
529 }
530
531 sqlite3_bind_text(stmt, idx++, node.last_error_message.c_str(), -1, SQLITE_TRANSIENT);
532
533 int64_t pk = 0;
534 if (sqlite3_step(stmt) == SQLITE_ROW) {
535 pk = sqlite3_column_int64(stmt, 0);
536 } else {
537 auto err = std::string(sqlite3_errmsg(db_));
538 sqlite3_finalize(stmt);
539 return kcenon::common::make_error<int64_t>(-1, "Failed to upsert: " + err, "node_repository");
540 }
541
542 sqlite3_finalize(stmt);
543 return kcenon::common::ok(pk);
544}

References kcenon::pacs::client::to_string().

Here is the call graph for this function:

Member Data Documentation

◆ db_

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

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