Skip to content

First steps towards PostgreSQL integration - #651

Open
gstavrinos wants to merge 1 commit into
selfpatch:mainfrom
aperion-robotics:postgres-fault-storage
Open

First steps towards PostgreSQL integration#651
gstavrinos wants to merge 1 commit into
selfpatch:mainfrom
aperion-robotics:postgres-fault-storage

Conversation

@gstavrinos

@gstavrinos gstavrinos commented Sep 4, 2026

Copy link
Copy Markdown

Pull Request

Summary

As discussed in #649, this is an early implementation of the PostgreSQL integration. Keep in mind that currently the code does not compile because the testing suite is not included in this PR.


Issue

Link the related issue (required):


Type

  • Bug fix
  • New feature or tests
  • Breaking change
  • Documentation only

Testing

Tests are yet to be implemented, so the code remains in an early stage, completely untested even for basic functionality


Checklist

  • Breaking changes are clearly described (and announced in docs / changelog if needed)
  • Tests were added or updated if needed
  • Docs were updated if behavior or public API changed

TODOs (based on your checklist, will tick the list as improvements come along)

  • Testing suite following the SQLite paradigm (Currently WIP, ETA next week)
  • Documentation update on how to use the new PostgreSQL fault storage
  • For now, no breaking changes have been added, and the goal is to not introduce any.

As this is still a WIP, feel free to offer suggestions, recommendations or problems you might think of.

Tests are yet to be implemented, so the code remains in an early stage, completely untested even for basic functionality
Copilot AI lite review requested due to automatic review settings September 4, 2026 11:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are multiple confirmed correctness/build issues (schema DDL syntax error, incorrect SELECT result handling via affected_rows(), missing test source file in CMake, and credential logging risk) that must be fixed before it can be safely validated.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Introduces an initial PostgreSQL-backed implementation of the FaultStorage backend for ros2_medkit_fault_manager, wiring it into FaultManagerNode via a new storage_type=postgres option and a database_url parameter.

Changes:

  • Add PgFaultStorage (libpqxx-based) with schema initialization and implementations for fault/events, snapshots, near-misses, and rosbag retention APIs.
  • Extend FaultManagerNode to select PostgreSQL storage via parameters.
  • Update build/package dependencies to include PostgreSQL/libpqxx and add a placeholder GTest target for PostgreSQL storage.
File summaries
File Description
src/ros2_medkit_fault_manager/src/postgres_fault_storage.cpp New PostgreSQL FaultStorage implementation and schema creation logic.
src/ros2_medkit_fault_manager/include/ros2_medkit_fault_manager/postgres_fault_storage.hpp Public header for PgFaultStorage implementing the FaultStorage interface.
src/ros2_medkit_fault_manager/src/fault_manager_node.cpp Adds database_url param and selects PostgreSQL storage when storage_type=postgres.
src/ros2_medkit_fault_manager/include/ros2_medkit_fault_manager/fault_manager_node.hpp Stores new database_url_ member.
src/ros2_medkit_fault_manager/CMakeLists.txt Adds PostgreSQL dependency linkage and a PostgreSQL test target (currently missing source).
src/ros2_medkit_fault_manager/package.xml Declares libpqxx dependency.
Review details

Suppressed comments (4)

src/ros2_medkit_fault_manager/src/postgres_fault_storage.cpp:547

  • get_fault() uses affected_rows() on a SELECT result; this can incorrectly return nullopt even when a fault exists. Use res.empty() to test whether the query returned rows.
    auto res = tx.exec_params(
        "SELECT fault_code, severity, description, first_occurred_ns, last_occurred_ns, occurrence_count, status, "
        "reporting_sources, last_passed_ns FROM faults WHERE fault_code = $1",
        fault_code);
    if (res.affected_rows() == 0) {

src/ros2_medkit_fault_manager/src/postgres_fault_storage.cpp:653

  • contains() uses affected_rows() on a SELECT result; that can incorrectly report false even when the row exists. For SELECT queries, check res.empty() instead.
    auto res = tx.exec_params("SELECT 1 FROM faults WHERE fault_code = $1 LIMIT 1", fault_code);
    tx.commit();
    return res.affected_rows() > 0;

src/ros2_medkit_fault_manager/src/postgres_fault_storage.cpp:748

  • The newest-capture query aliases the column as max_capture_id but then reads max_capture, and also uses affected_rows() on a SELECT. This can throw at runtime and/or disable trimming. Prefer COALESCE + res.empty() and read the correct alias.
      auto res =
          tx.exec_params("SELECT MAX(capture_id) AS max_capture_id FROM snapshots WHERE fault_code = $1", fault_code);
      int64_t newest_capture = 0;
      if (res.affected_rows() > 0) {
        newest_capture = res[0]["max_capture"].as<int64_t>();

src/ros2_medkit_fault_manager/src/postgres_fault_storage.cpp:753

  • The snapshot-trimming loop uses count_res.affected_rows() == 0 on a SELECT COUNT(*) query; for SELECTs this is not a valid emptiness check and can short-circuit trimming unexpectedly. Use count_res.empty() (or just read the first row) instead.
        auto count_res = tx.exec_params("SELECT COUNT(*) AS sz FROM snapshots WHERE fault_code = $1", fault_code);
        if (count_res.affected_rows() == 0 || count_res[0]["sz"].as<size_t>() <= max_snapshots_per_fault_) {
          break;
  • Files reviewed: 6/6 changed files
  • Comments generated: 6
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +142 to 146
# PostgreSQL storage tests
medkit_add_gtest(test_postgres_storage test/test_postgres_storage.cpp)
target_link_libraries(test_postgres_storage fault_manager_lib)
medkit_target_dependencies(test_postgres_storage rclcpp ros2_medkit_msgs)

}

if (storage_type_ == "postgres") {
RCLCPP_INFO(get_logger(), "Using PostgreSQL fault storage: %s", database_url_.c_str());
Comment on lines +99 to +100
CREATE INDEX IF NOT EXISTS idx_snapshots_fault_code ON snapshots(fault_code);
CREATE INDEX IF NOT EXISTS idx_snapshots_fault_topic ON snapshots(fault_code, topic))");
"first_occurred_ns FROM faults WHERE fault_code = $1",
fault_code);

if (res.affected_rows() > 0) {
Comment on lines +1291 to +1293
auto res = tx.exec_params("SELECT COUNT(*) FROM rosbag_files WHERE file_path = $1", file_path);
tx.commit();
return res.affected_rows();
for (const auto & r : res) {
paths.insert(r["file_path"].as<std::string>());
}
removed = res.affected_rows() > 0;
@gstavrinos

Copy link
Copy Markdown
Author

Oh, wow, Copilot came in aggressively! I will consider the LLM's remarks as I am implementing the tests.

@bburda
bburda self-requested a review September 4, 2026 17:52
@bburda

bburda commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Oh, wow, Copilot came in aggressively! I will consider the LLM's remarks as I am implementing the tests.

No worries, he is always like that. Use your own judgement, some of his comments are not worth fixing.
Also please ping me any time if you need guidance or get stuck, or once the PR is ready for review.

Happy to take a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants