summaryrefslogtreecommitdiff
path: root/src/storage/store.c
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-07-04 16:22:35 +0900
committertanaka takashi <isazaka@gmail.com>2026-07-04 16:22:35 +0900
commiteb5e9d9f37bda7d78f69614da70402357794168a (patch)
tree608dd61d8b4ca989df505e9777a8b95f4f17b06d /src/storage/store.c
parent1a6a2e7f8572415d435c056dc9947d6d08eb1862 (diff)
feat(P1-002): add offline JSONL import via import_messages MCP tool
- implement `import_messages` MCP tool parsing manifest + PUT/DEL JSONL - atomic store replacement under lock with overwrite/reject conflict policies - add `include_deleted` to read_messages export view for full round-trip - register new tool in MCP registry and update protocol test expectations - update docs/development-plan.md and docs/implementation-status.md Debug/Release/ASan 15/15 CTest pass
Diffstat (limited to 'src/storage/store.c')
-rw-r--r--src/storage/store.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/src/storage/store.c b/src/storage/store.c
index a964f60..45621d1 100644
--- a/src/storage/store.c
+++ b/src/storage/store.c
@@ -1921,6 +1921,105 @@ tiny_store_health(const tiny_store_t *store, tiny_store_health_t *health)
snprintf(health->last_error, sizeof(health->last_error), "%s", store->last_error);
}
+tiny_store_status_t
+tiny_store_import(tiny_store_t *store,
+ const tiny_import_record_t *records,
+ size_t record_count,
+ tiny_import_policy_t policy,
+ size_t *imported_count,
+ size_t *rejected_count)
+{
+ tiny_store_entry_t *saved_entries;
+ size_t saved_count, saved_capacity, index;
+ tiny_rebuild_report_t report;
+ tiny_store_status_t status = TINY_STORE_OK;
+ if (!store || !records || !imported_count || !rejected_count)
+ {
+ return TINY_STORE_IO_ERROR;
+ }
+ *imported_count = 0;
+ *rejected_count = 0;
+ saved_entries = store->entries;
+ saved_count = store->entry_count;
+ saved_capacity = store->entry_capacity;
+ store->entries = NULL;
+ store->entry_count = 0;
+ store->entry_capacity = 0;
+ for (index = 0; index < record_count && status == TINY_STORE_OK; ++index)
+ {
+ const tiny_import_record_t *record = &records[index];
+ tiny_store_entry_t *existing = find_entry(store, record->message.message_id);
+ if (existing != NULL && policy == TINY_IMPORT_REJECT && !record->deleted
+ && !existing->deleted)
+ {
+ (*rejected_count)++;
+ continue;
+ }
+ if (existing != NULL)
+ {
+ tiny_message_free(&existing->message);
+ existing->deleted = record->deleted;
+ existing->sequence = (uint64_t)record->message.timestamp_ms;
+ existing->message = record->message;
+ (*imported_count)++;
+ }
+ else
+ {
+ if (!ensure_entries(store))
+ {
+ status = TINY_STORE_NO_MEMORY;
+ break;
+ }
+ memset(
+ &store->entries[store->entry_count], 0, sizeof(store->entries[store->entry_count]));
+ store->entries[store->entry_count].deleted = record->deleted;
+ store->entries[store->entry_count].sequence = (uint64_t)record->message.timestamp_ms;
+ store->entries[store->entry_count].message = record->message;
+ store->entry_count++;
+ (*imported_count)++;
+ }
+ }
+ if (status == TINY_STORE_OK)
+ {
+ store->update_records = 0;
+ store->dead_bytes = 0;
+ store->snapshot_valid = false;
+ store->recovered_tail = false;
+ status = tiny_store_rebuild(store, &report);
+ if (status != TINY_STORE_OK)
+ {
+ for (index = 0; index < store->entry_count; ++index)
+ {
+ tiny_message_free(&store->entries[index].message);
+ }
+ free(store->entries);
+ store->entries = saved_entries;
+ store->entry_count = saved_count;
+ store->entry_capacity = saved_capacity;
+ }
+ else
+ {
+ for (index = 0; index < saved_count; ++index)
+ {
+ tiny_message_free(&saved_entries[index].message);
+ }
+ free(saved_entries);
+ }
+ }
+ else
+ {
+ for (index = 0; index < store->entry_count; ++index)
+ {
+ tiny_message_free(&store->entries[index].message);
+ }
+ free(store->entries);
+ store->entries = saved_entries;
+ store->entry_count = saved_count;
+ store->entry_capacity = saved_capacity;
+ }
+ return status;
+}
+
size_t
tiny_store_entry_count(const tiny_store_t *store)
{