Fossil SCM
Add the test-clusters command to verify that all artifacts are reachable through cluster chains. Fix the cluster creator so that it does not create gaps if the number of unclustered entries exceeds 800.
Commit
64a9c81a222733aebcfb4ec6630280b41361e4fb
Parent
6b5c797cc3c9268…
2 files changed
+68
+10
-2
+68
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -427,10 +427,78 @@ | ||
| 427 | 427 | } |
| 428 | 428 | db_begin_transaction(); |
| 429 | 429 | create_cluster(); |
| 430 | 430 | db_end_transaction(0); |
| 431 | 431 | } |
| 432 | + | |
| 433 | +/* | |
| 434 | +** COMMAND: test-clusters | |
| 435 | +** | |
| 436 | +** Verify that all non-private and non-shunned artifacts are accessible | |
| 437 | +** through the cluster chain. | |
| 438 | +*/ | |
| 439 | +void test_clusters_cmd(void){ | |
| 440 | + Bag pending; | |
| 441 | + Stmt q; | |
| 442 | + int n; | |
| 443 | + | |
| 444 | + db_find_and_open_repository(0, 2); | |
| 445 | + bag_init(&pending); | |
| 446 | + db_multi_exec( | |
| 447 | + "CREATE TEMP TABLE xdone(x INTEGER PRIMARY KEY);" | |
| 448 | + "INSERT INTO xdone SELECT rid FROM unclustered;" | |
| 449 | + "INSERT OR IGNORE INTO xdone SELECT rid FROM private;" | |
| 450 | + "INSERT OR IGNORE INTO xdone" | |
| 451 | + " SELECT blob.rid FROM shun JOIN blob USING(uuid);" | |
| 452 | + ); | |
| 453 | + db_prepare(&q, | |
| 454 | + "SELECT rid FROM unclustered WHERE rid IN" | |
| 455 | + " (SELECT rid FROM tagxref WHERE tagid=%d)", TAG_CLUSTER | |
| 456 | + ); | |
| 457 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 458 | + bag_insert(&pending, db_column_int(&q, 0)); | |
| 459 | + } | |
| 460 | + db_finalize(&q); | |
| 461 | + while( bag_count(&pending)>0 ){ | |
| 462 | + Manifest *p; | |
| 463 | + int rid = bag_first(&pending); | |
| 464 | + int i; | |
| 465 | + | |
| 466 | + bag_remove(&pending, rid); | |
| 467 | + p = manifest_get(rid, CFTYPE_CLUSTER); | |
| 468 | + if( p==0 ){ | |
| 469 | + fossil_fatal("bad cluster: rid=%d", rid); | |
| 470 | + } | |
| 471 | + for(i=0; i<p->nCChild; i++){ | |
| 472 | + const char *zUuid = p->azCChild[i]; | |
| 473 | + int crid = name_to_rid(zUuid); | |
| 474 | + if( crid==0 ){ | |
| 475 | + fossil_warning("cluster (rid=%d) references unknown artifact %s", | |
| 476 | + rid, zUuid); | |
| 477 | + continue; | |
| 478 | + } | |
| 479 | + db_multi_exec("INSERT OR IGNORE INTO xdone VALUES(%d)", crid); | |
| 480 | + if( db_exists("SELECT 1 FROM tagxref WHERE tagid=%d AND rid=%d", | |
| 481 | + TAG_CLUSTER, crid) ){ | |
| 482 | + bag_insert(&pending, crid); | |
| 483 | + } | |
| 484 | + } | |
| 485 | + manifest_destroy(p); | |
| 486 | + } | |
| 487 | + n = db_int(0, "SELECT count(*) FROM /*scan*/" | |
| 488 | + " (SELECT rid FROM blob EXCEPT SELECT x FROM xdone)"); | |
| 489 | + if( n==0 ){ | |
| 490 | + printf("all artifacts reachable through clusters\n"); | |
| 491 | + }else{ | |
| 492 | + printf("%d unreachable artifacts:\n", n); | |
| 493 | + db_prepare(&q, "SELECT rid, uuid FROM blob WHERE rid NOT IN xdone"); | |
| 494 | + while( db_step(&q)==SQLITE_ROW ){ | |
| 495 | + printf(" %3d %s\n", db_column_int(&q,0), db_column_text(&q,1)); | |
| 496 | + } | |
| 497 | + db_finalize(&q); | |
| 498 | + } | |
| 499 | +} | |
| 432 | 500 | |
| 433 | 501 | /* |
| 434 | 502 | ** COMMAND: scrub |
| 435 | 503 | ** %fossil scrub [--verily] [--force] [REPOSITORY] |
| 436 | 504 | ** |
| 437 | 505 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -427,10 +427,78 @@ | |
| 427 | } |
| 428 | db_begin_transaction(); |
| 429 | create_cluster(); |
| 430 | db_end_transaction(0); |
| 431 | } |
| 432 | |
| 433 | /* |
| 434 | ** COMMAND: scrub |
| 435 | ** %fossil scrub [--verily] [--force] [REPOSITORY] |
| 436 | ** |
| 437 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -427,10 +427,78 @@ | |
| 427 | } |
| 428 | db_begin_transaction(); |
| 429 | create_cluster(); |
| 430 | db_end_transaction(0); |
| 431 | } |
| 432 | |
| 433 | /* |
| 434 | ** COMMAND: test-clusters |
| 435 | ** |
| 436 | ** Verify that all non-private and non-shunned artifacts are accessible |
| 437 | ** through the cluster chain. |
| 438 | */ |
| 439 | void test_clusters_cmd(void){ |
| 440 | Bag pending; |
| 441 | Stmt q; |
| 442 | int n; |
| 443 | |
| 444 | db_find_and_open_repository(0, 2); |
| 445 | bag_init(&pending); |
| 446 | db_multi_exec( |
| 447 | "CREATE TEMP TABLE xdone(x INTEGER PRIMARY KEY);" |
| 448 | "INSERT INTO xdone SELECT rid FROM unclustered;" |
| 449 | "INSERT OR IGNORE INTO xdone SELECT rid FROM private;" |
| 450 | "INSERT OR IGNORE INTO xdone" |
| 451 | " SELECT blob.rid FROM shun JOIN blob USING(uuid);" |
| 452 | ); |
| 453 | db_prepare(&q, |
| 454 | "SELECT rid FROM unclustered WHERE rid IN" |
| 455 | " (SELECT rid FROM tagxref WHERE tagid=%d)", TAG_CLUSTER |
| 456 | ); |
| 457 | while( db_step(&q)==SQLITE_ROW ){ |
| 458 | bag_insert(&pending, db_column_int(&q, 0)); |
| 459 | } |
| 460 | db_finalize(&q); |
| 461 | while( bag_count(&pending)>0 ){ |
| 462 | Manifest *p; |
| 463 | int rid = bag_first(&pending); |
| 464 | int i; |
| 465 | |
| 466 | bag_remove(&pending, rid); |
| 467 | p = manifest_get(rid, CFTYPE_CLUSTER); |
| 468 | if( p==0 ){ |
| 469 | fossil_fatal("bad cluster: rid=%d", rid); |
| 470 | } |
| 471 | for(i=0; i<p->nCChild; i++){ |
| 472 | const char *zUuid = p->azCChild[i]; |
| 473 | int crid = name_to_rid(zUuid); |
| 474 | if( crid==0 ){ |
| 475 | fossil_warning("cluster (rid=%d) references unknown artifact %s", |
| 476 | rid, zUuid); |
| 477 | continue; |
| 478 | } |
| 479 | db_multi_exec("INSERT OR IGNORE INTO xdone VALUES(%d)", crid); |
| 480 | if( db_exists("SELECT 1 FROM tagxref WHERE tagid=%d AND rid=%d", |
| 481 | TAG_CLUSTER, crid) ){ |
| 482 | bag_insert(&pending, crid); |
| 483 | } |
| 484 | } |
| 485 | manifest_destroy(p); |
| 486 | } |
| 487 | n = db_int(0, "SELECT count(*) FROM /*scan*/" |
| 488 | " (SELECT rid FROM blob EXCEPT SELECT x FROM xdone)"); |
| 489 | if( n==0 ){ |
| 490 | printf("all artifacts reachable through clusters\n"); |
| 491 | }else{ |
| 492 | printf("%d unreachable artifacts:\n", n); |
| 493 | db_prepare(&q, "SELECT rid, uuid FROM blob WHERE rid NOT IN xdone"); |
| 494 | while( db_step(&q)==SQLITE_ROW ){ |
| 495 | printf(" %3d %s\n", db_column_int(&q,0), db_column_text(&q,1)); |
| 496 | } |
| 497 | db_finalize(&q); |
| 498 | } |
| 499 | } |
| 500 | |
| 501 | /* |
| 502 | ** COMMAND: scrub |
| 503 | ** %fossil scrub [--verily] [--force] [REPOSITORY] |
| 504 | ** |
| 505 |
+10
-2
| --- src/xfer.c | ||
| +++ src/xfer.c | ||
| @@ -584,13 +584,15 @@ | ||
| 584 | 584 | ** count toward the 100 total. And phantoms are never added to a new |
| 585 | 585 | ** cluster. |
| 586 | 586 | */ |
| 587 | 587 | void create_cluster(void){ |
| 588 | 588 | Blob cluster, cksum; |
| 589 | + Blob deleteWhere; | |
| 589 | 590 | Stmt q; |
| 590 | 591 | int nUncl; |
| 591 | 592 | int nRow = 0; |
| 593 | + int rid; | |
| 592 | 594 | |
| 593 | 595 | /* We should not ever get any private artifacts in the unclustered table. |
| 594 | 596 | ** But if we do (because of a bug) now is a good time to delete them. */ |
| 595 | 597 | db_multi_exec( |
| 596 | 598 | "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" |
| @@ -599,10 +601,11 @@ | ||
| 599 | 601 | nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" |
| 600 | 602 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 601 | 603 | " WHERE rid=unclustered.rid)"); |
| 602 | 604 | if( nUncl>=100 ){ |
| 603 | 605 | blob_zero(&cluster); |
| 606 | + blob_zero(&deleteWhere); | |
| 604 | 607 | db_prepare(&q, "SELECT uuid FROM unclustered, blob" |
| 605 | 608 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 606 | 609 | " WHERE rid=unclustered.rid)" |
| 607 | 610 | " AND unclustered.rid=blob.rid" |
| 608 | 611 | " AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| @@ -612,18 +615,23 @@ | ||
| 612 | 615 | nRow++; |
| 613 | 616 | if( nRow>=800 && nUncl>nRow+100 ){ |
| 614 | 617 | md5sum_blob(&cluster, &cksum); |
| 615 | 618 | blob_appendf(&cluster, "Z %b\n", &cksum); |
| 616 | 619 | blob_reset(&cksum); |
| 617 | - content_put(&cluster, 0, 0, 0); | |
| 620 | + rid = content_put(&cluster, 0, 0, 0); | |
| 618 | 621 | blob_reset(&cluster); |
| 619 | 622 | nUncl -= nRow; |
| 620 | 623 | nRow = 0; |
| 624 | + blob_appendf(&deleteWhere, ",%d", rid); | |
| 621 | 625 | } |
| 622 | 626 | } |
| 623 | 627 | db_finalize(&q); |
| 624 | - db_multi_exec("DELETE FROM unclustered"); | |
| 628 | + db_multi_exec( | |
| 629 | + "DELETE FROM unclustered WHERE rid NOT IN (0 %s)", | |
| 630 | + blob_str(&deleteWhere) | |
| 631 | + ); | |
| 632 | + blob_reset(&deleteWhere); | |
| 625 | 633 | if( nRow>0 ){ |
| 626 | 634 | md5sum_blob(&cluster, &cksum); |
| 627 | 635 | blob_appendf(&cluster, "Z %b\n", &cksum); |
| 628 | 636 | blob_reset(&cksum); |
| 629 | 637 | content_put(&cluster, 0, 0, 0); |
| 630 | 638 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -584,13 +584,15 @@ | |
| 584 | ** count toward the 100 total. And phantoms are never added to a new |
| 585 | ** cluster. |
| 586 | */ |
| 587 | void create_cluster(void){ |
| 588 | Blob cluster, cksum; |
| 589 | Stmt q; |
| 590 | int nUncl; |
| 591 | int nRow = 0; |
| 592 | |
| 593 | /* We should not ever get any private artifacts in the unclustered table. |
| 594 | ** But if we do (because of a bug) now is a good time to delete them. */ |
| 595 | db_multi_exec( |
| 596 | "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" |
| @@ -599,10 +601,11 @@ | |
| 599 | nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" |
| 600 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 601 | " WHERE rid=unclustered.rid)"); |
| 602 | if( nUncl>=100 ){ |
| 603 | blob_zero(&cluster); |
| 604 | db_prepare(&q, "SELECT uuid FROM unclustered, blob" |
| 605 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 606 | " WHERE rid=unclustered.rid)" |
| 607 | " AND unclustered.rid=blob.rid" |
| 608 | " AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| @@ -612,18 +615,23 @@ | |
| 612 | nRow++; |
| 613 | if( nRow>=800 && nUncl>nRow+100 ){ |
| 614 | md5sum_blob(&cluster, &cksum); |
| 615 | blob_appendf(&cluster, "Z %b\n", &cksum); |
| 616 | blob_reset(&cksum); |
| 617 | content_put(&cluster, 0, 0, 0); |
| 618 | blob_reset(&cluster); |
| 619 | nUncl -= nRow; |
| 620 | nRow = 0; |
| 621 | } |
| 622 | } |
| 623 | db_finalize(&q); |
| 624 | db_multi_exec("DELETE FROM unclustered"); |
| 625 | if( nRow>0 ){ |
| 626 | md5sum_blob(&cluster, &cksum); |
| 627 | blob_appendf(&cluster, "Z %b\n", &cksum); |
| 628 | blob_reset(&cksum); |
| 629 | content_put(&cluster, 0, 0, 0); |
| 630 |
| --- src/xfer.c | |
| +++ src/xfer.c | |
| @@ -584,13 +584,15 @@ | |
| 584 | ** count toward the 100 total. And phantoms are never added to a new |
| 585 | ** cluster. |
| 586 | */ |
| 587 | void create_cluster(void){ |
| 588 | Blob cluster, cksum; |
| 589 | Blob deleteWhere; |
| 590 | Stmt q; |
| 591 | int nUncl; |
| 592 | int nRow = 0; |
| 593 | int rid; |
| 594 | |
| 595 | /* We should not ever get any private artifacts in the unclustered table. |
| 596 | ** But if we do (because of a bug) now is a good time to delete them. */ |
| 597 | db_multi_exec( |
| 598 | "DELETE FROM unclustered WHERE rid IN (SELECT rid FROM private)" |
| @@ -599,10 +601,11 @@ | |
| 601 | nUncl = db_int(0, "SELECT count(*) FROM unclustered /*scan*/" |
| 602 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 603 | " WHERE rid=unclustered.rid)"); |
| 604 | if( nUncl>=100 ){ |
| 605 | blob_zero(&cluster); |
| 606 | blob_zero(&deleteWhere); |
| 607 | db_prepare(&q, "SELECT uuid FROM unclustered, blob" |
| 608 | " WHERE NOT EXISTS(SELECT 1 FROM phantom" |
| 609 | " WHERE rid=unclustered.rid)" |
| 610 | " AND unclustered.rid=blob.rid" |
| 611 | " AND NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" |
| @@ -612,18 +615,23 @@ | |
| 615 | nRow++; |
| 616 | if( nRow>=800 && nUncl>nRow+100 ){ |
| 617 | md5sum_blob(&cluster, &cksum); |
| 618 | blob_appendf(&cluster, "Z %b\n", &cksum); |
| 619 | blob_reset(&cksum); |
| 620 | rid = content_put(&cluster, 0, 0, 0); |
| 621 | blob_reset(&cluster); |
| 622 | nUncl -= nRow; |
| 623 | nRow = 0; |
| 624 | blob_appendf(&deleteWhere, ",%d", rid); |
| 625 | } |
| 626 | } |
| 627 | db_finalize(&q); |
| 628 | db_multi_exec( |
| 629 | "DELETE FROM unclustered WHERE rid NOT IN (0 %s)", |
| 630 | blob_str(&deleteWhere) |
| 631 | ); |
| 632 | blob_reset(&deleteWhere); |
| 633 | if( nRow>0 ){ |
| 634 | md5sum_blob(&cluster, &cksum); |
| 635 | blob_appendf(&cluster, "Z %b\n", &cksum); |
| 636 | blob_reset(&cksum); |
| 637 | content_put(&cluster, 0, 0, 0); |
| 638 |