@@ -1227,14 +1227,52 @@ typedef struct {
12271227 bool has_row ;
12281228} graph_edge_cursor_t ;
12291229
1230+ #ifdef CBM_ENABLE_TEST_SEAMS
1231+ static atomic_int graph_compare_test_bind_countdown = ATOMIC_VAR_INIT (CBM_NOT_FOUND );
1232+ static atomic_int graph_compare_test_cancel_countdown = ATOMIC_VAR_INIT (CBM_NOT_FOUND );
1233+
1234+ void cbm_store_compare_test_fail_bind_after (int successful_binds ) {
1235+ atomic_store (& graph_compare_test_bind_countdown , successful_binds );
1236+ }
1237+
1238+ void cbm_store_compare_test_cancel_after (int successful_checks ) {
1239+ atomic_store (& graph_compare_test_cancel_countdown , successful_checks );
1240+ }
1241+
1242+ static bool graph_compare_test_countdown_fires (atomic_int * countdown ) {
1243+ int remaining = atomic_load (countdown );
1244+ while (remaining >= 0 ) {
1245+ int next = remaining == 0 ? CBM_NOT_FOUND : remaining - 1 ;
1246+ if (atomic_compare_exchange_weak (countdown , & remaining , next )) {
1247+ return remaining == 0 ;
1248+ }
1249+ }
1250+ return false;
1251+ }
1252+ #endif
1253+
12301254static bool graph_compare_is_cancelled (const graph_compare_progress_t * progress ) {
1255+ #ifdef CBM_ENABLE_TEST_SEAMS
1256+ if (graph_compare_test_countdown_fires (& graph_compare_test_cancel_countdown )) {
1257+ return true;
1258+ }
1259+ #endif
12311260 return progress && progress -> cancel && progress -> cancel (progress -> context );
12321261}
12331262
12341263static int graph_compare_progress (void * context ) {
12351264 return graph_compare_is_cancelled ((const graph_compare_progress_t * )context ) ? 1 : 0 ;
12361265}
12371266
1267+ static int graph_compare_bind_text (sqlite3_stmt * stmt , int column , const char * value ) {
1268+ #ifdef CBM_ENABLE_TEST_SEAMS
1269+ if (graph_compare_test_countdown_fires (& graph_compare_test_bind_countdown )) {
1270+ return SQLITE_NOMEM ;
1271+ }
1272+ #endif
1273+ return bind_text (stmt , column , value );
1274+ }
1275+
12381276/* SQLite's BINARY collation compares the unsigned bytes of the UTF-8 text.
12391277 * Keep the merge comparator byte-for-byte equivalent so neither cursor can
12401278 * be advanced past a row that SQLite considers distinct. */
@@ -1282,7 +1320,11 @@ static int graph_count_rows(cbm_store_t *store, const char *sql, const char *pro
12821320 store_set_error_sqlite (store , "compare count prepare" );
12831321 return CBM_STORE_ERR ;
12841322 }
1285- bind_text (stmt , SKIP_ONE , project );
1323+ if (graph_compare_bind_text (stmt , SKIP_ONE , project ) != SQLITE_OK ) {
1324+ store_set_error (store , "compare count bind failed" );
1325+ sqlite3_finalize (stmt );
1326+ return CBM_STORE_ERR ;
1327+ }
12861328 int step_rc = sqlite3_step (stmt );
12871329 if (step_rc != SQLITE_ROW ) {
12881330 store_set_error_sqlite (store , "compare count" );
@@ -1388,35 +1430,46 @@ static int graph_edge_cursor_step(cbm_store_t *store, graph_edge_cursor_t *curso
13881430static int graph_prepare_node_cursor (cbm_store_t * store , const char * project ,
13891431 graph_node_cursor_t * cursor ) {
13901432 static const char sql [] =
1391- "SELECT qualified_name,label,coalesce(file_path,'') FROM nodes WHERE project=?1 "
1433+ "SELECT qualified_name,label,replace(coalesce(file_path,''),'\\','/') "
1434+ "FROM nodes WHERE project=?1 "
13921435 "ORDER BY qualified_name COLLATE BINARY,label COLLATE BINARY,"
1393- "coalesce(file_path,'') COLLATE BINARY;" ;
1436+ "replace( coalesce(file_path,''),'\\','/ ') COLLATE BINARY;" ;
13941437 if (sqlite3_prepare_v2 (store -> db , sql , CBM_NOT_FOUND , & cursor -> stmt , NULL ) != SQLITE_OK ) {
13951438 store_set_error_sqlite (store , "compare node prepare" );
13961439 return CBM_STORE_ERR ;
13971440 }
1398- bind_text (cursor -> stmt , SKIP_ONE , project );
1441+ if (graph_compare_bind_text (cursor -> stmt , SKIP_ONE , project ) != SQLITE_OK ) {
1442+ store_set_error (store , "compare node bind failed" );
1443+ sqlite3_finalize (cursor -> stmt );
1444+ cursor -> stmt = NULL ;
1445+ return CBM_STORE_ERR ;
1446+ }
13991447 return graph_node_cursor_step (store , cursor );
14001448}
14011449
14021450static int graph_prepare_edge_cursor (cbm_store_t * store , const char * project ,
14031451 graph_edge_cursor_t * cursor ) {
14041452 static const char sql [] =
1405- "SELECT sn.qualified_name,sn.label,coalesce(sn.file_path,''),"
1406- "tn.qualified_name,tn.label,coalesce(tn.file_path,''),e.type,"
1453+ "SELECT sn.qualified_name,sn.label,replace( coalesce(sn.file_path,''),'\\','/ '),"
1454+ "tn.qualified_name,tn.label,replace( coalesce(tn.file_path,''),'\\','/ '),e.type,"
14071455 "coalesce(e.local_name_gen,'') FROM edges e "
14081456 "JOIN nodes sn ON sn.id=e.source_id JOIN nodes tn ON tn.id=e.target_id "
14091457 "WHERE e.project=?1 ORDER BY "
14101458 "sn.qualified_name COLLATE BINARY,sn.label COLLATE BINARY,"
1411- "coalesce(sn.file_path,'') COLLATE BINARY,"
1459+ "replace( coalesce(sn.file_path,''),'\\','/ ') COLLATE BINARY,"
14121460 "tn.qualified_name COLLATE BINARY,tn.label COLLATE BINARY,"
1413- "coalesce(tn.file_path,'') COLLATE BINARY,"
1461+ "replace( coalesce(tn.file_path,''),'\\','/ ') COLLATE BINARY,"
14141462 "e.type COLLATE BINARY,coalesce(e.local_name_gen,'') COLLATE BINARY;" ;
14151463 if (sqlite3_prepare_v2 (store -> db , sql , CBM_NOT_FOUND , & cursor -> stmt , NULL ) != SQLITE_OK ) {
14161464 store_set_error_sqlite (store , "compare edge prepare" );
14171465 return CBM_STORE_ERR ;
14181466 }
1419- bind_text (cursor -> stmt , SKIP_ONE , project );
1467+ if (graph_compare_bind_text (cursor -> stmt , SKIP_ONE , project ) != SQLITE_OK ) {
1468+ store_set_error (store , "compare edge bind failed" );
1469+ sqlite3_finalize (cursor -> stmt );
1470+ cursor -> stmt = NULL ;
1471+ return CBM_STORE_ERR ;
1472+ }
14201473 return graph_edge_cursor_step (store , cursor );
14211474}
14221475
0 commit comments