Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix a couple of bugs in the schemalint code. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | schemalint |
Files: | files | file ages | folders |
SHA1: |
02fbf699c07286f842d9617755f071b0 |
User & Date: | dan 2016-02-19 07:53:43.883 |
Context
2016-02-22
| ||
19:51 | Add test script shell6.test, containing tests for schemalint. (check-in: 0b73406595 user: dan tags: schemalint) | |
2016-02-19
| ||
07:53 | Fix a couple of bugs in the schemalint code. (check-in: 02fbf699c0 user: dan tags: schemalint) | |
2016-02-18
| ||
19:10 | Have the schemalint output distinguish between existing and recommended indexes. (check-in: 4ab3df25f1 user: dan tags: schemalint) | |
Changes
Changes to src/shell_indexes.c.
︙ | ︙ | |||
91 92 93 94 95 96 97 | i64 covering; /* Mask of columns required for cov. index */ IdxConstraint *pOrder; /* ORDER BY columns */ IdxWhere where; /* WHERE Constraints */ IdxScan *pNextScan; /* Next IdxScan object for same query */ }; /* | | | 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | i64 covering; /* Mask of columns required for cov. index */ IdxConstraint *pOrder; /* ORDER BY columns */ IdxWhere where; /* WHERE Constraints */ IdxScan *pNextScan; /* Next IdxScan object for same query */ }; /* ** Context object passed to idxWhereInfo() and other functions. */ struct IdxContext { char **pzErrmsg; IdxWhere *pCurrent; /* Current where clause */ int rc; /* Error code (if error has occurred) */ IdxScan *pScan; /* List of scan objects */ sqlite3 *dbm; /* In-memory db for this analysis */ |
︙ | ︙ | |||
450 451 452 453 454 455 456 | int nIn = zIn ? strlen(zIn) : 0; int nAppend = 0; va_start(ap, zFmt); if( *pRc==SQLITE_OK ){ zAppend = sqlite3_vmprintf(zFmt, ap); if( zAppend ){ nAppend = strlen(zAppend); | | | 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 | int nIn = zIn ? strlen(zIn) : 0; int nAppend = 0; va_start(ap, zFmt); if( *pRc==SQLITE_OK ){ zAppend = sqlite3_vmprintf(zFmt, ap); if( zAppend ){ nAppend = strlen(zAppend); zRet = (char*)sqlite3_malloc(nIn + nAppend + 1); } if( zAppend && zRet ){ memcpy(zRet, zIn, nIn); memcpy(&zRet[nIn], zAppend, nAppend+1); }else{ sqlite3_free(zRet); zRet = 0; |
︙ | ︙ | |||
527 528 529 530 531 532 533 534 535 | IdxConstraint *pTail /* List of range constraints */ ){ const char *zTbl = pScan->zTable; sqlite3_stmt *pIdxList = 0; IdxConstraint *pIter; int nEq = 0; /* Number of elements in pEq */ int rc, rc2; /* Count the elements in list pEq */ | > | | | | 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 | IdxConstraint *pTail /* List of range constraints */ ){ const char *zTbl = pScan->zTable; sqlite3_stmt *pIdxList = 0; IdxConstraint *pIter; int nEq = 0; /* Number of elements in pEq */ int rc, rc2; /* Count the elements in list pEq */ for(pIter=pEq; pIter; pIter=pIter->pLink) nEq++; rc = idxPrintfPrepareStmt(dbm, &pIdxList, 0, "PRAGMA index_list=%Q", zTbl); while( rc==SQLITE_OK && sqlite3_step(pIdxList)==SQLITE_ROW ){ int bMatch = 1; IdxConstraint *pT = pTail; sqlite3_stmt *pInfo = 0; const char *zIdx = (const char*)sqlite3_column_text(pIdxList, 1); /* Zero the IdxConstraint.bFlag values in the pEq list */ for(pIter=pEq; pIter; pIter=pIter->pLink) pIter->bFlag = 0; rc = idxPrintfPrepareStmt(dbm, &pInfo, 0, "PRAGMA index_xInfo=%Q", zIdx); while( rc==SQLITE_OK && sqlite3_step(pInfo)==SQLITE_ROW ){ int iIdx = sqlite3_column_int(pInfo, 0); int iCol = sqlite3_column_int(pInfo, 1); const char *zColl = (const char*)sqlite3_column_text(pInfo, 4); if( iIdx<nEq ){ for(pIter=pEq; pIter; pIter=pIter->pLink){ if( pIter->bFlag ) continue; if( pIter->iCol!=iCol ) continue; if( sqlite3_stricmp(pIter->zColl, zColl) ) continue; pIter->bFlag = 1; break; } if( pIter==0 ){ |
︙ | ︙ | |||
623 624 625 626 627 628 629 | }else{ zFmt = "CREATE INDEX %s_idx_%08x ON %s(%s)"; } zIdx = sqlite3_mprintf(zFmt, pScan->zTable, h, pScan->zTable, zCols); if( !zIdx ){ rc = SQLITE_NOMEM; }else{ | | < | < | 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 | }else{ zFmt = "CREATE INDEX %s_idx_%08x ON %s(%s)"; } zIdx = sqlite3_mprintf(zFmt, pScan->zTable, h, pScan->zTable, zCols); if( !zIdx ){ rc = SQLITE_NOMEM; }else{ rc = sqlite3_exec(dbm, zIdx, 0, 0, pCtx->pzErrmsg); /* printf("%s\n", zIdx); */ } } if( rc==SQLITE_OK && pCtx->iIdxRowid==0 ){ int rc2; sqlite3_stmt *pLast = 0; rc = idxPrepareStmt(dbm, &pLast, pCtx->pzErrmsg, "SELECT max(rowid) FROM sqlite_master" |
︙ | ︙ | |||
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 | rc = idxCreateFromWhere(pCtx, mask, pScan, p1, pEq, pTail); for(p2=p1->pSibling; p2 && rc==SQLITE_OK; p2=p2->pSibling){ rc = idxCreateFromWhere(pCtx, mask, pScan, p2, pEq, pTail); } } return rc; } static int idxCreateFromWhere( IdxContext *pCtx, i64 mask, /* Consider only these constraints */ IdxScan *pScan, /* Create indexes for this scan */ IdxWhere *pWhere, /* Read constraints from here */ IdxConstraint *pEq, /* == constraints for inclusion */ IdxConstraint *pTail /* range/ORDER BY constraints for inclusion */ ){ sqlite3 *dbm = pCtx->dbm; IdxConstraint *p1 = pEq; IdxConstraint *pCon; int rc; /* Gather up all the == constraints that match the mask. */ for(pCon=pWhere->pEq; pCon; pCon=pCon->pNext){ | > > > > > > > > > > > > | > > > | > > > | 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 | rc = idxCreateFromWhere(pCtx, mask, pScan, p1, pEq, pTail); for(p2=p1->pSibling; p2 && rc==SQLITE_OK; p2=p2->pSibling){ rc = idxCreateFromWhere(pCtx, mask, pScan, p2, pEq, pTail); } } return rc; } /* ** Return true if list pList (linked by IdxConstraint.pLink) contains ** a constraint compatible with *p. Otherwise return false. */ static int idxFindConstraint(IdxConstraint *pList, IdxConstraint *p){ IdxConstraint *pCmp; for(pCmp=pList; pCmp; pCmp=pCmp->pLink){ if( p->iCol==pCmp->iCol ) return 1; } return 0; } static int idxCreateFromWhere( IdxContext *pCtx, i64 mask, /* Consider only these constraints */ IdxScan *pScan, /* Create indexes for this scan */ IdxWhere *pWhere, /* Read constraints from here */ IdxConstraint *pEq, /* == constraints for inclusion */ IdxConstraint *pTail /* range/ORDER BY constraints for inclusion */ ){ sqlite3 *dbm = pCtx->dbm; IdxConstraint *p1 = pEq; IdxConstraint *pCon; int rc; /* Gather up all the == constraints that match the mask. */ for(pCon=pWhere->pEq; pCon; pCon=pCon->pNext){ if( (mask & pCon->depmask)==pCon->depmask && idxFindConstraint(p1, pCon)==0 && idxFindConstraint(pTail, pCon)==0 ){ pCon->pLink = p1; p1 = pCon; } } /* Create an index using the == constraints collected above. And the ** range constraint/ORDER BY terms passed in by the caller, if any. */ rc = idxCreateFromCons(pCtx, pScan, p1, pTail); if( rc==SQLITE_OK ){ rc = idxCreateForeachOr(pCtx, mask, pScan, pWhere, p1, pTail); } /* If no range/ORDER BY passed by the caller, create a version of the ** index for each range constraint that matches the mask. */ if( pTail==0 ){ for(pCon=pWhere->pRange; rc==SQLITE_OK && pCon; pCon=pCon->pNext){ assert( pCon->pLink==0 ); if( (mask & pCon->depmask)==pCon->depmask && idxFindConstraint(pEq, pCon)==0 && idxFindConstraint(pTail, pCon)==0 ){ rc = idxCreateFromCons(pCtx, pScan, p1, pCon); if( rc==SQLITE_OK ){ rc = idxCreateForeachOr(pCtx, mask, pScan, pWhere, p1, pCon); } } } } |
︙ | ︙ | |||
783 784 785 786 787 788 789 790 791 | while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){ int i; const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3); int nDetail = strlen(zDetail); for(i=0; i<nDetail; i++){ if( memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){ int nIdx = 0; | > > > > > > < | 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 | while( rc==SQLITE_OK && sqlite3_step(pExplain)==SQLITE_ROW ){ int i; const char *zDetail = (const char*)sqlite3_column_text(pExplain, 3); int nDetail = strlen(zDetail); for(i=0; i<nDetail; i++){ const char *zIdx = 0; if( memcmp(&zDetail[i], " USING INDEX ", 13)==0 ){ zIdx = &zDetail[i+13]; }else if( memcmp(&zDetail[i], " USING COVERING INDEX ", 22)==0 ){ zIdx = &zDetail[i+22]; } if( zIdx ){ int nIdx = 0; while( zIdx[nIdx]!='\0' && zIdx[nIdx]!=' ' ) nIdx++; sqlite3_bind_text(pSelect, 1, zIdx, nIdx, SQLITE_STATIC); if( SQLITE_ROW==sqlite3_step(pSelect) ){ i64 iRowid = sqlite3_column_int64(pSelect, 0); const char *zSql = (const char*)sqlite3_column_text(pSelect, 1); if( iRowid>=pCtx->iIdxRowid ){ xOut(pOutCtx, zSql); |
︙ | ︙ | |||
852 853 854 855 856 857 858 859 860 861 862 863 864 865 | ){ int rc = SQLITE_OK; sqlite3 *dbm = 0; IdxContext ctx; sqlite3_stmt *pStmt = 0; /* Statement compiled from zSql */ memset(&ctx, 0, sizeof(IdxContext)); /* Open an in-memory database to work with. The main in-memory ** database schema contains tables similar to those in the users ** database (handle db). The attached in-memory db (aux) contains ** application tables used by the code in this file. */ rc = sqlite3_open(":memory:", &dbm); if( rc==SQLITE_OK ){ | > | 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 | ){ int rc = SQLITE_OK; sqlite3 *dbm = 0; IdxContext ctx; sqlite3_stmt *pStmt = 0; /* Statement compiled from zSql */ memset(&ctx, 0, sizeof(IdxContext)); ctx.pzErrmsg = pzErrmsg; /* Open an in-memory database to work with. The main in-memory ** database schema contains tables similar to those in the users ** database (handle db). The attached in-memory db (aux) contains ** application tables used by the code in this file. */ rc = sqlite3_open(":memory:", &dbm); if( rc==SQLITE_OK ){ |
︙ | ︙ |