modernc.org/cc@v1.0.1/v2/testdata/_sqlite/ext/fts3/tool/fts3view.c (about) 1 /* 2 ** This program is a debugging and analysis utility that displays 3 ** information about an FTS3 or FTS4 index. 4 ** 5 ** Link this program against the SQLite3 amalgamation with the 6 ** SQLITE_ENABLE_FTS4 compile-time option. Then run it as: 7 ** 8 ** fts3view DATABASE 9 ** 10 ** to get a list of all FTS3/4 tables in DATABASE, or do 11 ** 12 ** fts3view DATABASE TABLE COMMAND .... 13 ** 14 ** to see various aspects of the TABLE table. Type fts3view with no 15 ** arguments for a list of available COMMANDs. 16 */ 17 #include <stdio.h> 18 #include <stdarg.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include <ctype.h> 22 #include "sqlite3.h" 23 24 /* 25 ** Extra command-line arguments: 26 */ 27 int nExtra; 28 char **azExtra; 29 30 /* 31 ** Look for a command-line argument. 32 */ 33 const char *findOption(const char *zName, int hasArg, const char *zDefault){ 34 int i; 35 const char *zResult = zDefault; 36 for(i=0; i<nExtra; i++){ 37 const char *z = azExtra[i]; 38 while( z[0]=='-' ) z++; 39 if( strcmp(z, zName)==0 ){ 40 int j = 1; 41 if( hasArg==0 || i==nExtra-1 ) j = 0; 42 zResult = azExtra[i+j]; 43 while( i+j<nExtra ){ 44 azExtra[i] = azExtra[i+j+1]; 45 i++; 46 } 47 break; 48 } 49 } 50 return zResult; 51 } 52 53 54 /* 55 ** Prepare an SQL query 56 */ 57 static sqlite3_stmt *prepare(sqlite3 *db, const char *zFormat, ...){ 58 va_list ap; 59 char *zSql; 60 sqlite3_stmt *pStmt; 61 int rc; 62 63 va_start(ap, zFormat); 64 zSql = sqlite3_vmprintf(zFormat, ap); 65 va_end(ap); 66 rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); 67 if( rc ){ 68 fprintf(stderr, "Error: %s\nSQL: %s\n", sqlite3_errmsg(db), zSql); 69 exit(1); 70 } 71 sqlite3_free(zSql); 72 return pStmt; 73 } 74 75 /* 76 ** Run an SQL statement 77 */ 78 static int runSql(sqlite3 *db, const char *zFormat, ...){ 79 va_list ap; 80 char *zSql; 81 int rc; 82 83 va_start(ap, zFormat); 84 zSql = sqlite3_vmprintf(zFormat, ap); 85 rc = sqlite3_exec(db, zSql, 0, 0, 0); 86 va_end(ap); 87 return rc; 88 } 89 90 /* 91 ** Show the table schema 92 */ 93 static void showSchema(sqlite3 *db, const char *zTab){ 94 sqlite3_stmt *pStmt; 95 pStmt = prepare(db, 96 "SELECT sql FROM sqlite_master" 97 " WHERE name LIKE '%q%%'" 98 " ORDER BY 1", 99 zTab); 100 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 101 printf("%s;\n", sqlite3_column_text(pStmt, 0)); 102 } 103 sqlite3_finalize(pStmt); 104 pStmt = prepare(db, "PRAGMA page_size"); 105 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 106 printf("PRAGMA page_size=%s;\n", sqlite3_column_text(pStmt, 0)); 107 } 108 sqlite3_finalize(pStmt); 109 pStmt = prepare(db, "PRAGMA journal_mode"); 110 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 111 printf("PRAGMA journal_mode=%s;\n", sqlite3_column_text(pStmt, 0)); 112 } 113 sqlite3_finalize(pStmt); 114 pStmt = prepare(db, "PRAGMA auto_vacuum"); 115 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 116 const char *zType = "???"; 117 switch( sqlite3_column_int(pStmt, 0) ){ 118 case 0: zType = "OFF"; break; 119 case 1: zType = "FULL"; break; 120 case 2: zType = "INCREMENTAL"; break; 121 } 122 printf("PRAGMA auto_vacuum=%s;\n", zType); 123 } 124 sqlite3_finalize(pStmt); 125 pStmt = prepare(db, "PRAGMA encoding"); 126 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 127 printf("PRAGMA encoding=%s;\n", sqlite3_column_text(pStmt, 0)); 128 } 129 sqlite3_finalize(pStmt); 130 } 131 132 /* 133 ** Read a 64-bit variable-length integer from memory starting at p[0]. 134 ** Return the number of bytes read, or 0 on error. 135 ** The value is stored in *v. 136 */ 137 int getVarint(const unsigned char *p, sqlite_int64 *v){ 138 const unsigned char *q = p; 139 sqlite_uint64 x = 0, y = 1; 140 while( (*q&0x80)==0x80 && q-(unsigned char *)p<9 ){ 141 x += y * (*q++ & 0x7f); 142 y <<= 7; 143 } 144 x += y * (*q++); 145 *v = (sqlite_int64) x; 146 return (int) (q - (unsigned char *)p); 147 } 148 149 150 /* Show the content of the %_stat table 151 */ 152 static void showStat(sqlite3 *db, const char *zTab){ 153 sqlite3_stmt *pStmt; 154 pStmt = prepare(db, "SELECT id, value FROM '%q_stat'", zTab); 155 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 156 printf("stat[%d] =", sqlite3_column_int(pStmt, 0)); 157 switch( sqlite3_column_type(pStmt, 1) ){ 158 case SQLITE_INTEGER: { 159 printf(" %d\n", sqlite3_column_int(pStmt, 1)); 160 break; 161 } 162 case SQLITE_BLOB: { 163 unsigned char *x = (unsigned char*)sqlite3_column_blob(pStmt, 1); 164 int len = sqlite3_column_bytes(pStmt, 1); 165 int i = 0; 166 sqlite3_int64 v; 167 while( i<len ){ 168 i += getVarint(x, &v); 169 printf(" %lld", v); 170 } 171 printf("\n"); 172 break; 173 } 174 } 175 } 176 sqlite3_finalize(pStmt); 177 } 178 179 /* 180 ** Report on the vocabulary. This creates an fts4aux table with a random 181 ** name, but deletes it in the end. 182 */ 183 static void showVocabulary(sqlite3 *db, const char *zTab){ 184 char *zAux; 185 sqlite3_uint64 r; 186 sqlite3_stmt *pStmt; 187 int nDoc = 0; 188 int nToken = 0; 189 int nOccurrence = 0; 190 int nTop; 191 int n, i; 192 193 sqlite3_randomness(sizeof(r), &r); 194 zAux = sqlite3_mprintf("viewer_%llx", zTab, r); 195 runSql(db, "BEGIN"); 196 pStmt = prepare(db, "SELECT count(*) FROM %Q", zTab); 197 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 198 nDoc = sqlite3_column_int(pStmt, 0); 199 } 200 sqlite3_finalize(pStmt); 201 printf("Number of documents...................... %9d\n", nDoc); 202 203 runSql(db, "CREATE VIRTUAL TABLE %s USING fts4aux(%Q)", zAux, zTab); 204 pStmt = prepare(db, 205 "SELECT count(*), sum(occurrences) FROM %s WHERE col='*'", 206 zAux); 207 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 208 nToken = sqlite3_column_int(pStmt, 0); 209 nOccurrence = sqlite3_column_int(pStmt, 1); 210 } 211 sqlite3_finalize(pStmt); 212 printf("Total tokens in all documents............ %9d\n", nOccurrence); 213 printf("Total number of distinct tokens.......... %9d\n", nToken); 214 if( nToken==0 ) goto end_vocab; 215 216 n = 0; 217 pStmt = prepare(db, "SELECT count(*) FROM %s" 218 " WHERE col='*' AND occurrences==1", zAux); 219 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 220 n = sqlite3_column_int(pStmt, 0); 221 } 222 sqlite3_finalize(pStmt); 223 printf("Tokens used exactly once................. %9d %5.2f%%\n", 224 n, n*100.0/nToken); 225 226 n = 0; 227 pStmt = prepare(db, "SELECT count(*) FROM %s" 228 " WHERE col='*' AND documents==1", zAux); 229 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 230 n = sqlite3_column_int(pStmt, 0); 231 } 232 sqlite3_finalize(pStmt); 233 printf("Tokens used in only one document......... %9d %5.2f%%\n", 234 n, n*100.0/nToken); 235 236 if( nDoc>=2000 ){ 237 n = 0; 238 pStmt = prepare(db, "SELECT count(*) FROM %s" 239 " WHERE col='*' AND occurrences<=%d", zAux, nDoc/1000); 240 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 241 n = sqlite3_column_int(pStmt, 0); 242 } 243 sqlite3_finalize(pStmt); 244 printf("Tokens used in 0.1%% or less of docs...... %9d %5.2f%%\n", 245 n, n*100.0/nToken); 246 } 247 248 if( nDoc>=200 ){ 249 n = 0; 250 pStmt = prepare(db, "SELECT count(*) FROM %s" 251 " WHERE col='*' AND occurrences<=%d", zAux, nDoc/100); 252 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 253 n = sqlite3_column_int(pStmt, 0); 254 } 255 sqlite3_finalize(pStmt); 256 printf("Tokens used in 1%% or less of docs........ %9d %5.2f%%\n", 257 n, n*100.0/nToken); 258 } 259 260 nTop = atoi(findOption("top", 1, "25")); 261 printf("The %d most common tokens:\n", nTop); 262 pStmt = prepare(db, 263 "SELECT term, documents FROM %s" 264 " WHERE col='*'" 265 " ORDER BY documents DESC, term" 266 " LIMIT %d", zAux, nTop); 267 i = 0; 268 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 269 i++; 270 n = sqlite3_column_int(pStmt, 1); 271 printf(" %2d. %-30s %9d docs %5.2f%%\n", i, 272 sqlite3_column_text(pStmt, 0), n, n*100.0/nDoc); 273 } 274 sqlite3_finalize(pStmt); 275 276 end_vocab: 277 runSql(db, "ROLLBACK"); 278 sqlite3_free(zAux); 279 } 280 281 /* 282 ** Report on the number and sizes of segments 283 */ 284 static void showSegmentStats(sqlite3 *db, const char *zTab){ 285 sqlite3_stmt *pStmt; 286 int nSeg = 0; 287 sqlite3_int64 szSeg = 0, mxSeg = 0; 288 int nIdx = 0; 289 sqlite3_int64 szIdx = 0, mxIdx = 0; 290 int nRoot = 0; 291 sqlite3_int64 szRoot = 0, mxRoot = 0; 292 sqlite3_int64 mx; 293 int nLeaf; 294 int n; 295 int pgsz; 296 int mxLevel; 297 int i; 298 299 pStmt = prepare(db, 300 "SELECT count(*), sum(length(block)), max(length(block))" 301 " FROM '%q_segments'", 302 zTab); 303 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 304 nSeg = sqlite3_column_int(pStmt, 0); 305 szSeg = sqlite3_column_int64(pStmt, 1); 306 mxSeg = sqlite3_column_int64(pStmt, 2); 307 } 308 sqlite3_finalize(pStmt); 309 pStmt = prepare(db, 310 "SELECT count(*), sum(length(block)), max(length(block))" 311 " FROM '%q_segments' a JOIN '%q_segdir' b" 312 " WHERE a.blockid BETWEEN b.leaves_end_block+1 AND b.end_block", 313 zTab, zTab); 314 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 315 nIdx = sqlite3_column_int(pStmt, 0); 316 szIdx = sqlite3_column_int64(pStmt, 1); 317 mxIdx = sqlite3_column_int64(pStmt, 2); 318 } 319 sqlite3_finalize(pStmt); 320 pStmt = prepare(db, 321 "SELECT count(*), sum(length(root)), max(length(root))" 322 " FROM '%q_segdir'", 323 zTab); 324 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 325 nRoot = sqlite3_column_int(pStmt, 0); 326 szRoot = sqlite3_column_int64(pStmt, 1); 327 mxRoot = sqlite3_column_int64(pStmt, 2); 328 } 329 sqlite3_finalize(pStmt); 330 331 printf("Number of segments....................... %9d\n", nSeg+nRoot); 332 printf("Number of leaf segments.................. %9d\n", nSeg-nIdx); 333 printf("Number of index segments................. %9d\n", nIdx); 334 printf("Number of root segments.................. %9d\n", nRoot); 335 printf("Total size of all segments............... %9lld\n", szSeg+szRoot); 336 printf("Total size of all leaf segments.......... %9lld\n", szSeg-szIdx); 337 printf("Total size of all index segments......... %9lld\n", szIdx); 338 printf("Total size of all root segments.......... %9lld\n", szRoot); 339 if( nSeg>0 ){ 340 printf("Average size of all segments............. %11.1f\n", 341 (double)(szSeg+szRoot)/(double)(nSeg+nRoot)); 342 printf("Average size of leaf segments............ %11.1f\n", 343 (double)(szSeg-szIdx)/(double)(nSeg-nIdx)); 344 } 345 if( nIdx>0 ){ 346 printf("Average size of index segments........... %11.1f\n", 347 (double)szIdx/(double)nIdx); 348 } 349 if( nRoot>0 ){ 350 printf("Average size of root segments............ %11.1f\n", 351 (double)szRoot/(double)nRoot); 352 } 353 mx = mxSeg; 354 if( mx<mxRoot ) mx = mxRoot; 355 printf("Maximum segment size..................... %9lld\n", mx); 356 printf("Maximum index segment size............... %9lld\n", mxIdx); 357 printf("Maximum root segment size................ %9lld\n", mxRoot); 358 359 pStmt = prepare(db, "PRAGMA page_size"); 360 pgsz = 1024; 361 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 362 pgsz = sqlite3_column_int(pStmt, 0); 363 } 364 sqlite3_finalize(pStmt); 365 printf("Database page size....................... %9d\n", pgsz); 366 pStmt = prepare(db, 367 "SELECT count(*)" 368 " FROM '%q_segments' a JOIN '%q_segdir' b" 369 " WHERE a.blockid BETWEEN b.start_block AND b.leaves_end_block" 370 " AND length(a.block)>%d", 371 zTab, zTab, pgsz-45); 372 n = 0; 373 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 374 n = sqlite3_column_int(pStmt, 0); 375 } 376 sqlite3_finalize(pStmt); 377 nLeaf = nSeg - nIdx; 378 printf("Leaf segments larger than %5d bytes.... %9d %5.2f%%\n", 379 pgsz-45, n, nLeaf>0 ? n*100.0/nLeaf : 0.0); 380 381 pStmt = prepare(db, "SELECT max(level%%1024) FROM '%q_segdir'", zTab); 382 mxLevel = 0; 383 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 384 mxLevel = sqlite3_column_int(pStmt, 0); 385 } 386 sqlite3_finalize(pStmt); 387 388 for(i=0; i<=mxLevel; i++){ 389 pStmt = prepare(db, 390 "SELECT count(*), sum(len), avg(len), max(len), sum(len>%d)," 391 " count(distinct idx)" 392 " FROM (SELECT length(a.block) AS len, idx" 393 " FROM '%q_segments' a JOIN '%q_segdir' b" 394 " WHERE (a.blockid BETWEEN b.start_block" 395 " AND b.leaves_end_block)" 396 " AND (b.level%%1024)==%d)", 397 pgsz-45, zTab, zTab, i); 398 if( sqlite3_step(pStmt)==SQLITE_ROW 399 && (nLeaf = sqlite3_column_int(pStmt, 0))>0 400 ){ 401 sqlite3_int64 sz; 402 nIdx = sqlite3_column_int(pStmt, 5); 403 printf("For level %d:\n", i); 404 printf(" Number of indexes...................... %9d\n", nIdx); 405 printf(" Number of leaf segments................ %9d\n", nLeaf); 406 if( nIdx>1 ){ 407 printf(" Average leaf segments per index........ %11.1f\n", 408 (double)nLeaf/(double)nIdx); 409 } 410 printf(" Total size of all leaf segments........ %9lld\n", 411 (sz = sqlite3_column_int64(pStmt, 1))); 412 printf(" Average size of leaf segments.......... %11.1f\n", 413 sqlite3_column_double(pStmt, 2)); 414 if( nIdx>1 ){ 415 printf(" Average leaf segment size per index.... %11.1f\n", 416 (double)sz/(double)nIdx); 417 } 418 printf(" Maximum leaf segment size.............. %9lld\n", 419 sqlite3_column_int64(pStmt, 3)); 420 n = sqlite3_column_int(pStmt, 4); 421 printf(" Leaf segments larger than %5d bytes.. %9d %5.2f%%\n", 422 pgsz-45, n, n*100.0/nLeaf); 423 } 424 sqlite3_finalize(pStmt); 425 } 426 } 427 428 /* 429 ** Print a single "tree" line of the segdir map output. 430 */ 431 static void printTreeLine(sqlite3_int64 iLower, sqlite3_int64 iUpper){ 432 printf(" tree %9lld", iLower); 433 if( iUpper>iLower ){ 434 printf(" thru %9lld (%lld blocks)", iUpper, iUpper-iLower+1); 435 } 436 printf("\n"); 437 } 438 439 /* 440 ** Check to see if the block of a %_segments entry is NULL. 441 */ 442 static int isNullSegment(sqlite3 *db, const char *zTab, sqlite3_int64 iBlockId){ 443 sqlite3_stmt *pStmt; 444 int rc = 1; 445 446 pStmt = prepare(db, "SELECT block IS NULL FROM '%q_segments'" 447 " WHERE blockid=%lld", zTab, iBlockId); 448 if( sqlite3_step(pStmt)==SQLITE_ROW ){ 449 rc = sqlite3_column_int(pStmt, 0); 450 } 451 sqlite3_finalize(pStmt); 452 return rc; 453 } 454 455 /* 456 ** Show a map of segments derived from the %_segdir table. 457 */ 458 static void showSegdirMap(sqlite3 *db, const char *zTab){ 459 int mxIndex, iIndex; 460 sqlite3_stmt *pStmt = 0; 461 sqlite3_stmt *pStmt2 = 0; 462 int prevLevel; 463 464 pStmt = prepare(db, "SELECT max(level/1024) FROM '%q_segdir'", zTab); 465 if( sqlite3_step(pStmt)==SQLITE_ROW ){ 466 mxIndex = sqlite3_column_int(pStmt, 0); 467 }else{ 468 mxIndex = 0; 469 } 470 sqlite3_finalize(pStmt); 471 472 printf("Number of inverted indices............... %3d\n", mxIndex+1); 473 pStmt = prepare(db, 474 "SELECT level, idx, start_block, leaves_end_block, end_block, rowid" 475 " FROM '%q_segdir'" 476 " WHERE level/1024==?" 477 " ORDER BY level DESC, idx", 478 zTab); 479 pStmt2 = prepare(db, 480 "SELECT blockid FROM '%q_segments'" 481 " WHERE blockid BETWEEN ? AND ? ORDER BY blockid", 482 zTab); 483 for(iIndex=0; iIndex<=mxIndex; iIndex++){ 484 if( mxIndex>0 ){ 485 printf("**************************** Index %d " 486 "****************************\n", iIndex); 487 } 488 sqlite3_bind_int(pStmt, 1, iIndex); 489 prevLevel = -1; 490 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 491 int iLevel = sqlite3_column_int(pStmt, 0)%1024; 492 int iIdx = sqlite3_column_int(pStmt, 1); 493 sqlite3_int64 iStart = sqlite3_column_int64(pStmt, 2); 494 sqlite3_int64 iLEnd = sqlite3_column_int64(pStmt, 3); 495 sqlite3_int64 iEnd = sqlite3_column_int64(pStmt, 4); 496 char rtag[20]; 497 if( iLevel!=prevLevel ){ 498 printf("level %2d idx %2d", iLevel, iIdx); 499 prevLevel = iLevel; 500 }else{ 501 printf(" idx %2d", iIdx); 502 } 503 sqlite3_snprintf(sizeof(rtag), rtag, "r%lld", 504 sqlite3_column_int64(pStmt,5)); 505 printf(" root %9s\n", rtag); 506 if( iLEnd>iStart ){ 507 sqlite3_int64 iLower, iPrev = 0, iX; 508 if( iLEnd+1<=iEnd ){ 509 sqlite3_bind_int64(pStmt2, 1, iLEnd+1); 510 sqlite3_bind_int64(pStmt2, 2, iEnd); 511 iLower = -1; 512 while( sqlite3_step(pStmt2)==SQLITE_ROW ){ 513 iX = sqlite3_column_int64(pStmt2, 0); 514 if( iLower<0 ){ 515 iLower = iPrev = iX; 516 }else if( iX==iPrev+1 ){ 517 iPrev = iX; 518 }else{ 519 printTreeLine(iLower, iPrev); 520 iLower = iPrev = iX; 521 } 522 } 523 sqlite3_reset(pStmt2); 524 if( iLower>=0 ){ 525 if( iLower==iPrev && iLower==iEnd 526 && isNullSegment(db,zTab,iLower) 527 ){ 528 printf(" null %9lld\n", iLower); 529 }else{ 530 printTreeLine(iLower, iPrev); 531 } 532 } 533 } 534 printf(" leaves %9lld thru %9lld (%lld blocks)\n", 535 iStart, iLEnd, iLEnd - iStart + 1); 536 } 537 } 538 sqlite3_reset(pStmt); 539 } 540 sqlite3_finalize(pStmt); 541 sqlite3_finalize(pStmt2); 542 } 543 544 /* 545 ** Decode a single segment block and display the results on stdout. 546 */ 547 static void decodeSegment( 548 const unsigned char *aData, /* Content to print */ 549 int nData /* Number of bytes of content */ 550 ){ 551 sqlite3_int64 iChild = 0; 552 sqlite3_int64 iPrefix; 553 sqlite3_int64 nTerm; 554 sqlite3_int64 n; 555 sqlite3_int64 iDocsz; 556 int iHeight; 557 sqlite3_int64 i = 0; 558 int cnt = 0; 559 char zTerm[1000]; 560 561 i += getVarint(aData, &n); 562 iHeight = (int)n; 563 printf("height: %d\n", iHeight); 564 if( iHeight>0 ){ 565 i += getVarint(aData+i, &iChild); 566 printf("left-child: %lld\n", iChild); 567 } 568 while( i<nData ){ 569 if( (cnt++)>0 ){ 570 i += getVarint(aData+i, &iPrefix); 571 }else{ 572 iPrefix = 0; 573 } 574 i += getVarint(aData+i, &nTerm); 575 if( iPrefix+nTerm+1 >= sizeof(zTerm) ){ 576 fprintf(stderr, "term to long\n"); 577 exit(1); 578 } 579 memcpy(zTerm+iPrefix, aData+i, (size_t)nTerm); 580 zTerm[iPrefix+nTerm] = 0; 581 i += nTerm; 582 if( iHeight==0 ){ 583 i += getVarint(aData+i, &iDocsz); 584 printf("term: %-25s doclist %7lld bytes offset %lld\n", zTerm, iDocsz, i); 585 i += iDocsz; 586 }else{ 587 printf("term: %-25s child %lld\n", zTerm, ++iChild); 588 } 589 } 590 } 591 592 593 /* 594 ** Print a a blob as hex and ascii. 595 */ 596 static void printBlob( 597 const unsigned char *aData, /* Content to print */ 598 int nData /* Number of bytes of content */ 599 ){ 600 int i, j; 601 const char *zOfstFmt; 602 const int perLine = 16; 603 604 if( (nData&~0xfff)==0 ){ 605 zOfstFmt = " %03x: "; 606 }else if( (nData&~0xffff)==0 ){ 607 zOfstFmt = " %04x: "; 608 }else if( (nData&~0xfffff)==0 ){ 609 zOfstFmt = " %05x: "; 610 }else if( (nData&~0xffffff)==0 ){ 611 zOfstFmt = " %06x: "; 612 }else{ 613 zOfstFmt = " %08x: "; 614 } 615 616 for(i=0; i<nData; i += perLine){ 617 fprintf(stdout, zOfstFmt, i); 618 for(j=0; j<perLine; j++){ 619 if( i+j>nData ){ 620 fprintf(stdout, " "); 621 }else{ 622 fprintf(stdout,"%02x ", aData[i+j]); 623 } 624 } 625 for(j=0; j<perLine; j++){ 626 if( i+j>nData ){ 627 fprintf(stdout, " "); 628 }else{ 629 fprintf(stdout,"%c", isprint(aData[i+j]) ? aData[i+j] : '.'); 630 } 631 } 632 fprintf(stdout,"\n"); 633 } 634 } 635 636 /* 637 ** Convert text to a 64-bit integer 638 */ 639 static sqlite3_int64 atoi64(const char *z){ 640 sqlite3_int64 v = 0; 641 while( z[0]>='0' && z[0]<='9' ){ 642 v = v*10 + z[0] - '0'; 643 z++; 644 } 645 return v; 646 } 647 648 /* 649 ** Return a prepared statement which, when stepped, will return in its 650 ** first column the blob associated with segment zId. If zId begins with 651 ** 'r' then it is a rowid of a %_segdir entry. Otherwise it is a 652 ** %_segment entry. 653 */ 654 static sqlite3_stmt *prepareToGetSegment( 655 sqlite3 *db, /* The database */ 656 const char *zTab, /* The FTS3/4 table name */ 657 const char *zId /* ID of the segment to open */ 658 ){ 659 sqlite3_stmt *pStmt; 660 if( zId[0]=='r' ){ 661 pStmt = prepare(db, "SELECT root FROM '%q_segdir' WHERE rowid=%lld", 662 zTab, atoi64(zId+1)); 663 }else{ 664 pStmt = prepare(db, "SELECT block FROM '%q_segments' WHERE blockid=%lld", 665 zTab, atoi64(zId)); 666 } 667 return pStmt; 668 } 669 670 /* 671 ** Print the content of a segment or of the root of a segdir. The segment 672 ** or root is identified by azExtra[0]. If the first character of azExtra[0] 673 ** is 'r' then the remainder is the integer rowid of the %_segdir entry. 674 ** If the first character of azExtra[0] is not 'r' then, then all of 675 ** azExtra[0] is an integer which is the block number. 676 ** 677 ** If the --raw option is present in azExtra, then a hex dump is provided. 678 ** Otherwise a decoding is shown. 679 */ 680 static void showSegment(sqlite3 *db, const char *zTab){ 681 const unsigned char *aData; 682 int nData; 683 sqlite3_stmt *pStmt; 684 685 pStmt = prepareToGetSegment(db, zTab, azExtra[0]); 686 if( sqlite3_step(pStmt)!=SQLITE_ROW ){ 687 sqlite3_finalize(pStmt); 688 return; 689 } 690 nData = sqlite3_column_bytes(pStmt, 0); 691 aData = sqlite3_column_blob(pStmt, 0); 692 printf("Segment %s of size %d bytes:\n", azExtra[0], nData); 693 if( findOption("raw", 0, 0)!=0 ){ 694 printBlob(aData, nData); 695 }else{ 696 decodeSegment(aData, nData); 697 } 698 sqlite3_finalize(pStmt); 699 } 700 701 /* 702 ** Decode a single doclist and display the results on stdout. 703 */ 704 static void decodeDoclist( 705 const unsigned char *aData, /* Content to print */ 706 int nData /* Number of bytes of content */ 707 ){ 708 sqlite3_int64 iPrevDocid = 0; 709 sqlite3_int64 iDocid; 710 sqlite3_int64 iPos; 711 sqlite3_int64 iPrevPos = 0; 712 sqlite3_int64 iCol; 713 int i = 0; 714 715 while( i<nData ){ 716 i += getVarint(aData+i, &iDocid); 717 printf("docid %lld col0", iDocid+iPrevDocid); 718 iPrevDocid += iDocid; 719 iPrevPos = 0; 720 while( 1 ){ 721 i += getVarint(aData+i, &iPos); 722 if( iPos==1 ){ 723 i += getVarint(aData+i, &iCol); 724 printf(" col%lld", iCol); 725 iPrevPos = 0; 726 }else if( iPos==0 ){ 727 printf("\n"); 728 break; 729 }else{ 730 iPrevPos += iPos - 2; 731 printf(" %lld", iPrevPos); 732 } 733 } 734 } 735 } 736 737 738 /* 739 ** Print the content of a doclist. The segment or segdir-root is 740 ** identified by azExtra[0]. If the first character of azExtra[0] 741 ** is 'r' then the remainder is the integer rowid of the %_segdir entry. 742 ** If the first character of azExtra[0] is not 'r' then, then all of 743 ** azExtra[0] is an integer which is the block number. The offset 744 ** into the segment is identified by azExtra[1]. The size of the doclist 745 ** is azExtra[2]. 746 ** 747 ** If the --raw option is present in azExtra, then a hex dump is provided. 748 ** Otherwise a decoding is shown. 749 */ 750 static void showDoclist(sqlite3 *db, const char *zTab){ 751 const unsigned char *aData; 752 sqlite3_int64 offset; 753 int nData; 754 sqlite3_stmt *pStmt; 755 756 offset = atoi64(azExtra[1]); 757 nData = atoi(azExtra[2]); 758 pStmt = prepareToGetSegment(db, zTab, azExtra[0]); 759 if( sqlite3_step(pStmt)!=SQLITE_ROW ){ 760 sqlite3_finalize(pStmt); 761 return; 762 } 763 aData = sqlite3_column_blob(pStmt, 0); 764 printf("Doclist at %s offset %lld of size %d bytes:\n", 765 azExtra[0], offset, nData); 766 if( findOption("raw", 0, 0)!=0 ){ 767 printBlob(aData+offset, nData); 768 }else{ 769 decodeDoclist(aData+offset, nData); 770 } 771 sqlite3_finalize(pStmt); 772 } 773 774 /* 775 ** Show the top N largest segments 776 */ 777 static void listBigSegments(sqlite3 *db, const char *zTab){ 778 int nTop, i; 779 sqlite3_stmt *pStmt; 780 sqlite3_int64 sz; 781 sqlite3_int64 id; 782 783 nTop = atoi(findOption("top", 1, "25")); 784 printf("The %d largest segments:\n", nTop); 785 pStmt = prepare(db, 786 "SELECT blockid, length(block) AS len FROM '%q_segments'" 787 " ORDER BY 2 DESC, 1" 788 " LIMIT %d", zTab, nTop); 789 i = 0; 790 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 791 i++; 792 id = sqlite3_column_int64(pStmt, 0); 793 sz = sqlite3_column_int64(pStmt, 1); 794 printf(" %2d. %9lld size %lld\n", i, id, sz); 795 } 796 sqlite3_finalize(pStmt); 797 } 798 799 800 801 static void usage(const char *argv0){ 802 fprintf(stderr, "Usage: %s DATABASE\n" 803 " or: %s DATABASE FTS3TABLE ARGS...\n", argv0, argv0); 804 fprintf(stderr, 805 "ARGS:\n" 806 " big-segments [--top N] show the largest segments\n" 807 " doclist BLOCKID OFFSET SIZE [--raw] Decode a doclist\n" 808 " schema FTS table schema\n" 809 " segdir directory of segments\n" 810 " segment BLOCKID [--raw] content of a segment\n" 811 " segment-stats info on segment sizes\n" 812 " stat the %%_stat table\n" 813 " vocabulary [--top N] document vocabulary\n" 814 ); 815 exit(1); 816 } 817 818 int main(int argc, char **argv){ 819 sqlite3 *db; 820 int rc; 821 const char *zTab; 822 const char *zCmd; 823 824 if( argc<2 ) usage(argv[0]); 825 rc = sqlite3_open(argv[1], &db); 826 if( rc ){ 827 fprintf(stderr, "Cannot open %s\n", argv[1]); 828 exit(1); 829 } 830 if( argc==2 ){ 831 sqlite3_stmt *pStmt; 832 int cnt = 0; 833 pStmt = prepare(db, "SELECT b.sql" 834 " FROM sqlite_master a, sqlite_master b" 835 " WHERE a.name GLOB '*_segdir'" 836 " AND b.name=substr(a.name,1,length(a.name)-7)" 837 " ORDER BY 1"); 838 while( sqlite3_step(pStmt)==SQLITE_ROW ){ 839 cnt++; 840 printf("%s;\n", sqlite3_column_text(pStmt, 0)); 841 } 842 sqlite3_finalize(pStmt); 843 if( cnt==0 ){ 844 printf("/* No FTS3/4 tables found in database %s */\n", argv[1]); 845 } 846 return 0; 847 } 848 if( argc<4 ) usage(argv[0]); 849 zTab = argv[2]; 850 zCmd = argv[3]; 851 nExtra = argc-4; 852 azExtra = argv+4; 853 if( strcmp(zCmd,"big-segments")==0 ){ 854 listBigSegments(db, zTab); 855 }else if( strcmp(zCmd,"doclist")==0 ){ 856 if( argc<7 ) usage(argv[0]); 857 showDoclist(db, zTab); 858 }else if( strcmp(zCmd,"schema")==0 ){ 859 showSchema(db, zTab); 860 }else if( strcmp(zCmd,"segdir")==0 ){ 861 showSegdirMap(db, zTab); 862 }else if( strcmp(zCmd,"segment")==0 ){ 863 if( argc<5 ) usage(argv[0]); 864 showSegment(db, zTab); 865 }else if( strcmp(zCmd,"segment-stats")==0 ){ 866 showSegmentStats(db, zTab); 867 }else if( strcmp(zCmd,"stat")==0 ){ 868 showStat(db, zTab); 869 }else if( strcmp(zCmd,"vocabulary")==0 ){ 870 showVocabulary(db, zTab); 871 }else{ 872 usage(argv[0]); 873 } 874 return 0; 875 }