| 1024 | } |
| 1025 | |
| 1026 | void cbm_store_close(cbm_store_t *s) { |
| 1027 | if (!s) { |
| 1028 | return; |
| 1029 | } |
| 1030 | |
| 1031 | /* Checkpoint WAL before close to prevent orphan WAL accumulation. |
| 1032 | * Best-effort — silently skips if concurrent reader holds a lock. */ |
| 1033 | if (s->db && s->db_path) { |
| 1034 | (void)sqlite3_wal_checkpoint_v2(s->db, NULL, SQLITE_CHECKPOINT_PASSIVE, NULL, NULL); |
| 1035 | } |
| 1036 | |
| 1037 | /* Finalize all cached statements */ |
| 1038 | finalize_stmt(&s->stmt_upsert_node); |
| 1039 | finalize_stmt(&s->stmt_find_node_by_id); |
| 1040 | finalize_stmt(&s->stmt_find_node_by_qn); |
| 1041 | finalize_stmt(&s->stmt_find_node_by_qn_any); |
| 1042 | finalize_stmt(&s->stmt_find_nodes_by_name); |
| 1043 | finalize_stmt(&s->stmt_find_nodes_by_name_any); |
| 1044 | finalize_stmt(&s->stmt_find_nodes_by_label); |
| 1045 | finalize_stmt(&s->stmt_find_nodes_by_file); |
| 1046 | finalize_stmt(&s->stmt_count_nodes); |
| 1047 | finalize_stmt(&s->stmt_delete_nodes_by_project); |
| 1048 | finalize_stmt(&s->stmt_delete_nodes_by_file); |
| 1049 | finalize_stmt(&s->stmt_delete_nodes_by_label); |
| 1050 | |
| 1051 | finalize_stmt(&s->stmt_insert_edge); |
| 1052 | finalize_stmt(&s->stmt_find_edges_by_source); |
| 1053 | finalize_stmt(&s->stmt_find_edges_by_target); |
| 1054 | finalize_stmt(&s->stmt_find_edges_by_source_type); |
| 1055 | finalize_stmt(&s->stmt_find_edges_by_target_type); |
| 1056 | finalize_stmt(&s->stmt_find_edges_by_type); |
| 1057 | finalize_stmt(&s->stmt_count_edges); |
| 1058 | finalize_stmt(&s->stmt_count_edges_by_type); |
| 1059 | finalize_stmt(&s->stmt_delete_edges_by_project); |
| 1060 | finalize_stmt(&s->stmt_delete_edges_by_type); |
| 1061 | |
| 1062 | finalize_stmt(&s->stmt_upsert_project); |
| 1063 | finalize_stmt(&s->stmt_get_project); |
| 1064 | finalize_stmt(&s->stmt_list_projects); |
| 1065 | finalize_stmt(&s->stmt_delete_project); |
| 1066 | |
| 1067 | finalize_stmt(&s->stmt_upsert_file_hash); |
| 1068 | finalize_stmt(&s->stmt_get_file_hashes); |
| 1069 | finalize_stmt(&s->stmt_delete_file_hash); |
| 1070 | finalize_stmt(&s->stmt_delete_file_hashes); |
| 1071 | |
| 1072 | /* Use sqlite3_close_v2 — auto-deallocates when last statement finalizes. |
| 1073 | * Prevents ASan false-positive leaks from sqlite3 internal state. */ |
| 1074 | sqlite3_close_v2(s->db); |
| 1075 | safe_str_free(&s->db_path); |
| 1076 | free(s); |
| 1077 | } |
| 1078 | |
| 1079 | sqlite3 *cbm_store_get_db(cbm_store_t *s) { |
| 1080 | return s ? s->db : NULL; |