| 449 | } |
| 450 | |
| 451 | int cbm_delta_patch(cbm_store_t *store, const char *project, cbm_gbuf_t *gbuf, int64_t max_db_id, |
| 452 | const cbm_delta_saved_edge_t *snapshot, int snapshot_count) { |
| 453 | sqlite3 *db = cbm_store_get_db(store); |
| 454 | if (!db) { |
| 455 | return CBM_NOT_FOUND; |
| 456 | } |
| 457 | if (cbm_store_begin(store) != CBM_STORE_OK) { |
| 458 | return CBM_NOT_FOUND; |
| 459 | } |
| 460 | delta_patch_ctx_t ctx = {.db = db, .project = project, .max_db_id = max_db_id}; |
| 461 | if (sqlite3_prepare_v2(db, |
| 462 | "SELECT id FROM nodes WHERE project = ?1" |
| 463 | " AND qualified_name = ?2", |
| 464 | CBM_NOT_FOUND, &ctx.qn_lookup, NULL) != SQLITE_OK) { |
| 465 | cbm_store_rollback(store); |
| 466 | return CBM_NOT_FOUND; |
| 467 | } |
| 468 | if (sqlite3_prepare_v2(db, |
| 469 | "INSERT INTO nodes (id, project, label, name, qualified_name," |
| 470 | " file_path, start_line, end_line, properties)" |
| 471 | " VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9)", |
| 472 | CBM_NOT_FOUND, &ctx.node_stmt, NULL) != SQLITE_OK || |
| 473 | sqlite3_prepare_v2(db, |
| 474 | "INSERT OR IGNORE INTO edges (project, source_id, target_id, type," |
| 475 | " properties) VALUES (?1,?2,?3,?4,?5)", |
| 476 | CBM_NOT_FOUND, &ctx.edge_stmt, NULL) != SQLITE_OK) { |
| 477 | sqlite3_finalize(ctx.node_stmt); |
| 478 | sqlite3_finalize(ctx.edge_stmt); |
| 479 | sqlite3_finalize(ctx.qn_lookup); |
| 480 | free(ctx.remap); |
| 481 | cbm_store_rollback(store); |
| 482 | return CBM_NOT_FOUND; |
| 483 | } |
| 484 | cbm_gbuf_foreach_node(gbuf, delta_patch_node, &ctx); |
| 485 | if (!ctx.failed) { |
| 486 | cbm_gbuf_foreach_edge(gbuf, delta_patch_edge, &ctx); |
| 487 | } |
| 488 | sqlite3_finalize(ctx.node_stmt); |
| 489 | sqlite3_finalize(ctx.edge_stmt); |
| 490 | sqlite3_finalize(ctx.qn_lookup); |
| 491 | |
| 492 | /* Re-link the snapshotted inbound edges by qualified name. A target |
| 493 | * whose QN no longer exists simply matches no row — full-reindex |
| 494 | * semantics for deleted symbols, dedup by the UNIQUE edge constraint. */ |
| 495 | if (!ctx.failed && snapshot_count > 0) { |
| 496 | sqlite3_stmt *relink = NULL; |
| 497 | if (sqlite3_prepare_v2(db, |
| 498 | "INSERT OR IGNORE INTO edges (project, source_id, target_id," |
| 499 | " type, properties)" |
| 500 | " SELECT ?1, s.id, t.id, ?2, ?3 FROM nodes s, nodes t" |
| 501 | " WHERE s.project = ?1 AND s.qualified_name = ?4" |
| 502 | " AND t.project = ?1 AND t.qualified_name = ?5", |
| 503 | CBM_NOT_FOUND, &relink, NULL) != SQLITE_OK) { |
| 504 | ctx.failed = true; |
| 505 | } else { |
| 506 | for (int i = 0; i < snapshot_count && !ctx.failed; i++) { |
| 507 | sqlite3_reset(relink); |
| 508 | sqlite3_bind_text(relink, 1, project, CBM_NOT_FOUND, SQLITE_TRANSIENT); |
no test coverage detected