| 1502 | } |
| 1503 | |
| 1504 | bool toolboxRegisterObjectTopic( |
| 1505 | void* ctx, DataSourceHandle source, PJ_string_view_t topic_name, PJ_string_view_t metadata_json, |
| 1506 | PJ_object_topic_handle_t* out_handle, PJ_error_t* out_error) noexcept { |
| 1507 | auto* impl = static_cast<DatastoreToolboxHostState*>(ctx); |
| 1508 | if (out_handle == nullptr) { |
| 1509 | propagateError(out_error, "out_handle must not be null"); |
| 1510 | return false; |
| 1511 | } |
| 1512 | // Validate the source handle against the engine — same check used by scalar |
| 1513 | // ensureTopic so the toolbox can't register a topic against a dataset that |
| 1514 | // doesn't exist. Scope the engine lock to JUST this read — do NOT hold it |
| 1515 | // across object_store.registerTopic below: the engine and ObjectStore locks |
| 1516 | // must never nest (see pj_datastore/CLAUDE.md). |
| 1517 | { |
| 1518 | auto lock = impl->core.engine.lockEngine(); |
| 1519 | if (impl->core.engine.getDataset(source.id) == nullptr) { |
| 1520 | impl->setObjectError(fmt::format("data source {} not found", source.id)); |
| 1521 | propagateError(out_error, impl->object_last_error.c_str()); |
| 1522 | return false; |
| 1523 | } |
| 1524 | } |
| 1525 | try { |
| 1526 | ObjectTopicDescriptor desc{}; |
| 1527 | desc.dataset_id = source.id; |
| 1528 | desc.topic_name = std::string(toStringView(topic_name)); |
| 1529 | desc.metadata_json = std::string(toStringView(metadata_json)); |
| 1530 | auto result = impl->object_store.registerTopic(desc); |
| 1531 | if (!result) { |
| 1532 | impl->setObjectError(result.error()); |
| 1533 | propagateError(out_error, impl->object_last_error.c_str()); |
| 1534 | return false; |
| 1535 | } |
| 1536 | out_handle->id = result->id; |
| 1537 | impl->object_last_error.clear(); |
| 1538 | return true; |
| 1539 | } catch (const std::exception& e) { |
| 1540 | impl->setObjectError(e.what()); |
| 1541 | propagateError(out_error, impl->object_last_error.c_str()); |
| 1542 | return false; |
| 1543 | } catch (...) { |
| 1544 | impl->setObjectError("registerObjectTopic: unknown exception"); |
| 1545 | propagateError(out_error, impl->object_last_error.c_str()); |
| 1546 | return false; |
| 1547 | } |
| 1548 | } |
| 1549 | |
| 1550 | bool toolboxPushOwnedObject( |
| 1551 | void* ctx, PJ_object_topic_handle_t topic, int64_t timestamp_ns, const uint8_t* data, uint64_t size, |
nothing calls this directly
no test coverage detected