| 1125 | } |
| 1126 | |
| 1127 | void cbm_store_close(cbm_store_t *s) { |
| 1128 | if (!s) { |
| 1129 | return; |
| 1130 | } |
| 1131 | |
| 1132 | /* Checkpoint WAL before close to prevent orphan WAL accumulation. |
| 1133 | * Best-effort — silently skips if concurrent reader holds a lock. */ |
| 1134 | if (s->db && s->db_path) { |
| 1135 | (void)sqlite3_wal_checkpoint_v2(s->db, NULL, SQLITE_CHECKPOINT_PASSIVE, NULL, NULL); |
| 1136 | } |
| 1137 | |
| 1138 | /* Finalize all cached statements */ |
| 1139 | finalize_stmt(&s->stmt_upsert_node); |
| 1140 | finalize_stmt(&s->stmt_find_node_by_id); |
| 1141 | finalize_stmt(&s->stmt_find_node_by_qn); |
| 1142 | finalize_stmt(&s->stmt_find_node_by_qn_any); |
| 1143 | finalize_stmt(&s->stmt_find_nodes_by_name); |
| 1144 | finalize_stmt(&s->stmt_find_nodes_by_name_any); |
| 1145 | finalize_stmt(&s->stmt_find_nodes_by_label); |
| 1146 | finalize_stmt(&s->stmt_find_nodes_by_file); |
| 1147 | finalize_stmt(&s->stmt_count_nodes); |
| 1148 | finalize_stmt(&s->stmt_delete_nodes_by_project); |
| 1149 | finalize_stmt(&s->stmt_delete_nodes_by_file); |
| 1150 | finalize_stmt(&s->stmt_delete_nodes_by_label); |
| 1151 | |
| 1152 | finalize_stmt(&s->stmt_insert_edge); |
| 1153 | finalize_stmt(&s->stmt_find_edges_by_source); |
| 1154 | finalize_stmt(&s->stmt_find_edges_by_target); |
| 1155 | finalize_stmt(&s->stmt_find_edges_by_source_type); |
| 1156 | finalize_stmt(&s->stmt_find_edges_by_target_type); |
| 1157 | finalize_stmt(&s->stmt_find_edges_by_type); |
| 1158 | finalize_stmt(&s->stmt_count_edges); |
| 1159 | finalize_stmt(&s->stmt_count_edges_by_type); |
| 1160 | finalize_stmt(&s->stmt_delete_edges_by_project); |
| 1161 | finalize_stmt(&s->stmt_delete_edges_by_type); |
| 1162 | |
| 1163 | finalize_stmt(&s->stmt_upsert_project); |
| 1164 | finalize_stmt(&s->stmt_get_project); |
| 1165 | finalize_stmt(&s->stmt_list_projects); |
| 1166 | finalize_stmt(&s->stmt_delete_project); |
| 1167 | |
| 1168 | finalize_stmt(&s->stmt_upsert_file_hash); |
| 1169 | finalize_stmt(&s->stmt_get_file_hashes); |
| 1170 | finalize_stmt(&s->stmt_delete_file_hash); |
| 1171 | finalize_stmt(&s->stmt_delete_file_hashes); |
| 1172 | |
| 1173 | /* Use sqlite3_close_v2 — auto-deallocates when last statement finalizes. |
| 1174 | * Prevents ASan false-positive leaks from sqlite3 internal state. */ |
| 1175 | sqlite3_close_v2(s->db); |
| 1176 | safe_str_free(&s->db_path); |
| 1177 | free(s); |
| 1178 | } |
| 1179 | |
| 1180 | sqlite3 *cbm_store_get_db(cbm_store_t *s) { |
| 1181 | return s ? s->db : NULL; |