Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Improvements to register allocation, especially in the ANALYZE command. New assert() statements added to help verify that memory allocation is correct, and to help fuzzer find lingering errors. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
6f8b97f31a4c8552312b4c98432ea356 |
User & Date: | drh 2023-03-26 16:36:27 |
References
2023-03-27
| ||
13:24 | Do not allow constant factoring during PRAGMA integrity_check, since the constants might be stored in registers that are later reused for other purposes. dbsqlfuzz dc9ab26037cf5ef797d28cd1ae0855ade584216d. Problem discovered by a new assert() statement added in [6f8b97f31a4c8552]. (check-in: 0bba27b7 user: drh tags: trunk) | |
Context
2023-03-27
| ||
13:24 | Do not allow constant factoring during PRAGMA integrity_check, since the constants might be stored in registers that are later reused for other purposes. dbsqlfuzz dc9ab26037cf5ef797d28cd1ae0855ade584216d. Problem discovered by a new assert() statement added in [6f8b97f31a4c8552]. (check-in: 0bba27b7 user: drh tags: trunk) | |
13:10 | Improvements to register allocation, especially in the ANALYZE command. New assert() statements added to help verify that memory allocation is correct, and to help fuzzer find lingering errors. (check-in: 636f6fad user: drh tags: branch-3.41) | |
2023-03-26
| ||
16:36 | Improvements to register allocation, especially in the ANALYZE command. New assert() statements added to help verify that memory allocation is correct, and to help fuzzer find lingering errors. (check-in: 6f8b97f3 user: drh tags: trunk) | |
11:54 | Disable factoring of constant values during ANALYZE. This is a temporary fix for forum post 07de5f6216. The register allocation logic in ANALYZE needs to be completely refactored, but that will take longer. This check-in will serve to resolve the issue until a better fix can be devised. (check-in: c3967d12 user: drh tags: trunk) | |
Changes
Changes to src/analyze.c.
︙ | ︙ | |||
194 195 196 197 198 199 200 | const int nToOpen = 1; #endif if( v==0 ) return; assert( sqlite3BtreeHoldsAllMutexes(db) ); assert( sqlite3VdbeDb(v)==db ); pDb = &db->aDb[iDb]; | < | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | const int nToOpen = 1; #endif if( v==0 ) return; assert( sqlite3BtreeHoldsAllMutexes(db) ); assert( sqlite3VdbeDb(v)==db ); pDb = &db->aDb[iDb]; /* Create new statistic tables if they do not exist, or clear them ** if they do already exist. */ for(i=0; i<ArraySize(aTable); i++){ const char *zTab = aTable[i].zName; Table *pStat; |
︙ | ︙ | |||
998 999 1000 1001 1002 1003 1004 | #ifdef SQLITE_ENABLE_STAT4 int doOnce = 1; /* Flag for a one-time computation */ #endif #ifdef SQLITE_ENABLE_PREUPDATE_HOOK Table *pStat1 = 0; #endif | | > | 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 | #ifdef SQLITE_ENABLE_STAT4 int doOnce = 1; /* Flag for a one-time computation */ #endif #ifdef SQLITE_ENABLE_PREUPDATE_HOOK Table *pStat1 = 0; #endif sqlite3TouchRegister(pParse, iMem); assert( sqlite3NoTempsInRange(pParse, regNewRowid, iMem) ); v = sqlite3GetVdbe(pParse); if( v==0 || NEVER(pTab==0) ){ return; } if( !IsOrdinaryTable(pTab) ){ /* Do not gather statistics on views or virtual tables */ return; |
︙ | ︙ | |||
1104 1105 1106 1107 1108 1109 1110 | ** end_of_scan: */ /* Make sure there are enough memory cells allocated to accommodate ** the regPrev array and a trailing rowid (the rowid slot is required ** when building a record to insert into the sample column of ** the sqlite_stat4 table. */ | | | 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 | ** end_of_scan: */ /* Make sure there are enough memory cells allocated to accommodate ** the regPrev array and a trailing rowid (the rowid slot is required ** when building a record to insert into the sample column of ** the sqlite_stat4 table. */ sqlite3TouchRegister(pParse, regPrev+nColTest); /* Open a read-only cursor on the index being analyzed. */ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb); sqlite3VdbeSetP4KeyInfo(pParse, pIdx); VdbeComment((v, "%s", pIdx->zName)); |
︙ | ︙ | |||
1292 1293 1294 1295 1296 1297 1298 | }else{ nColX = pX->nColumn; } if( nColX>mxCol ) mxCol = nColX; } /* Allocate space to compute results for the largest index */ | | | | < < < < < | < < | > > | 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 | }else{ nColX = pX->nColumn; } if( nColX>mxCol ) mxCol = nColX; } /* Allocate space to compute results for the largest index */ sqlite3TouchRegister(pParse, regCol+mxCol); doOnce = 0; #ifdef SQLITE_DEBUG /* Verify that the call to sqlite3ClearTempRegCache() below ** really is needed. ** https://sqlite.org/forum/forumpost/83cb4a95a0 (2023-03-25) */ testcase( !sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) ); #endif sqlite3ClearTempRegCache(pParse); /* tag-20230325-1 */ assert( sqlite3NoTempsInRange(pParse, regEq, regCol+mxCol) ); } assert( sqlite3NoTempsInRange(pParse, regEq, regCol+nCol) ); addrNext = sqlite3VdbeCurrentAddr(v); callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid); addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid); VdbeCoverage(v); callStatGet(pParse, regStat, STAT_GET_NEQ, regEq); callStatGet(pParse, regStat, STAT_GET_NLT, regLt); |
︙ | ︙ | |||
1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 | openStatTable(pParse, iDb, iStatCur, 0, 0); iMem = pParse->nMem+1; iTab = pParse->nTab; assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab); } loadAnalysis(pParse, iDb); } /* ** Generate code that will do an analysis of a single table in ** a database. If pOnlyIdx is not NULL then it is a single index | > | 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 | openStatTable(pParse, iDb, iStatCur, 0, 0); iMem = pParse->nMem+1; iTab = pParse->nTab; assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab); iMem = sqlite3FirstAvailableRegister(pParse, iMem); } loadAnalysis(pParse, iDb); } /* ** Generate code that will do an analysis of a single table in ** a database. If pOnlyIdx is not NULL then it is a single index |
︙ | ︙ |
Changes to src/expr.c.
︙ | ︙ | |||
6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 | ** the sub/co-routine does not use registers in common with the code that ** invokes the sub/co-routine. */ void sqlite3ClearTempRegCache(Parse *pParse){ pParse->nTempReg = 0; pParse->nRangeReg = 0; } /* ** Validate that no temporary register falls within the range of ** iFirst..iLast, inclusive. This routine is only call from within assert() ** statements. */ #ifdef SQLITE_DEBUG int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ int i; if( pParse->nRangeReg>0 && pParse->iRangeReg+pParse->nRangeReg > iFirst && pParse->iRangeReg <= iLast ){ return 0; } for(i=0; i<pParse->nTempReg; i++){ if( pParse->aTempReg[i]>=iFirst && pParse->aTempReg[i]<=iLast ){ return 0; } } return 1; } #endif /* SQLITE_DEBUG */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 6642 6643 6644 6645 6646 6647 6648 6649 6650 6651 6652 6653 6654 6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688 6689 6690 6691 6692 6693 6694 6695 6696 | ** the sub/co-routine does not use registers in common with the code that ** invokes the sub/co-routine. */ void sqlite3ClearTempRegCache(Parse *pParse){ pParse->nTempReg = 0; pParse->nRangeReg = 0; } /* ** Make sure sufficient registers have been allocated so that ** iReg is a valid register number. */ void sqlite3TouchRegister(Parse *pParse, int iReg){ if( pParse->nMem<iReg ) pParse->nMem = iReg; } /* ** Return the latest reusable register in the set of all registers. ** The value returned is no less than iMin. If any register iMin or ** greater is in permanent use, then return one more than that last ** permanent register. */ int sqlite3FirstAvailableRegister(Parse *pParse, int iMin){ const ExprList *pList = pParse->pConstExpr; if( pList ){ int i; for(i=0; i<pList->nExpr; i++){ if( pList->a[i].u.iConstExprReg>=iMin ){ iMin = pList->a[i].u.iConstExprReg + 1; } } } pParse->nTempReg = 0; pParse->nRangeReg = 0; return iMin; } /* ** Validate that no temporary register falls within the range of ** iFirst..iLast, inclusive. This routine is only call from within assert() ** statements. */ #ifdef SQLITE_DEBUG int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ int i; if( pParse->nRangeReg>0 && pParse->iRangeReg+pParse->nRangeReg > iFirst && pParse->iRangeReg <= iLast ){ return 0; } for(i=0; i<pParse->nTempReg; i++){ if( pParse->aTempReg[i]>=iFirst && pParse->aTempReg[i]<=iLast ){ return 0; } } if( pParse->pConstExpr ){ ExprList *pList = pParse->pConstExpr; for(i=0; i<pList->nExpr; i++){ int iReg = pList->a[i].u.iConstExprReg; if( iReg==0 ) continue; if( iReg>=iFirst && iReg<=iLast ) return 0; } } return 1; } #endif /* SQLITE_DEBUG */ |
Changes to src/pragma.c.
︙ | ︙ | |||
1520 1521 1522 1523 1524 1525 1526 | k = sqliteHashNext(k); } if( pTab==0 || !IsOrdinaryTable(pTab) || pTab->u.tab.pFKey==0 ) continue; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); zDb = db->aDb[iDb].zDbSName; sqlite3CodeVerifySchema(pParse, iDb); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); | | | 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 | k = sqliteHashNext(k); } if( pTab==0 || !IsOrdinaryTable(pTab) || pTab->u.tab.pFKey==0 ) continue; iDb = sqlite3SchemaToIndex(db, pTab->pSchema); zDb = db->aDb[iDb].zDbSName; sqlite3CodeVerifySchema(pParse, iDb); sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName); sqlite3TouchRegister(pParse, pTab->nCol+regRow); sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead); sqlite3VdbeLoadString(v, regResult, pTab->zName); assert( IsOrdinaryTable(pTab) ); for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){ pParent = sqlite3FindTable(db, pFK->zTo, zDb); if( pParent==0 ) continue; pIdx = 0; |
︙ | ︙ | |||
1561 1562 1563 1564 1565 1566 1567 | } addrOk = sqlite3VdbeMakeLabel(pParse); /* Generate code to read the child key values into registers ** regRow..regRow+n. If any of the child key values are NULL, this ** row cannot cause an FK violation. Jump directly to addrOk in ** this case. */ | | | 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 | } addrOk = sqlite3VdbeMakeLabel(pParse); /* Generate code to read the child key values into registers ** regRow..regRow+n. If any of the child key values are NULL, this ** row cannot cause an FK violation. Jump directly to addrOk in ** this case. */ sqlite3TouchRegister(pParse, regRow + pFK->nCol); for(j=0; j<pFK->nCol; j++){ int iCol = aiCols ? aiCols[j] : pFK->aCol[j].iFrom; sqlite3ExprCodeGetColumnOfTable(v, pTab, 0, iCol, regRow+j); sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); VdbeCoverage(v); } /* Generate code to query the parent index for a matching parent |
︙ | ︙ | |||
1725 1726 1727 1728 1729 1730 1731 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ aRoot[++cnt] = pIdx->tnum; } } aRoot[0] = cnt; /* Make sure sufficient number of registers have been allocated */ | | | 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 | for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ aRoot[++cnt] = pIdx->tnum; } } aRoot[0] = cnt; /* Make sure sufficient number of registers have been allocated */ sqlite3TouchRegister(pParse, 8+mxIdx); sqlite3ClearTempRegCache(pParse); /* Do the b-tree integrity checks */ sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY); sqlite3VdbeChangeP5(v, (u8)i); addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 | int sqlite3RunParser(Parse*, const char*); void sqlite3FinishCoding(Parse*); int sqlite3GetTempReg(Parse*); void sqlite3ReleaseTempReg(Parse*,int); int sqlite3GetTempRange(Parse*,int); void sqlite3ReleaseTempRange(Parse*,int,int); void sqlite3ClearTempRegCache(Parse*); #ifdef SQLITE_DEBUG int sqlite3NoTempsInRange(Parse*,int,int); #endif Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int); Expr *sqlite3Expr(sqlite3*,int,const char*); void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*); Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*); | > > | 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 | int sqlite3RunParser(Parse*, const char*); void sqlite3FinishCoding(Parse*); int sqlite3GetTempReg(Parse*); void sqlite3ReleaseTempReg(Parse*,int); int sqlite3GetTempRange(Parse*,int); void sqlite3ReleaseTempRange(Parse*,int,int); void sqlite3ClearTempRegCache(Parse*); void sqlite3TouchRegister(Parse*,int); int sqlite3FirstAvailableRegister(Parse*,int); #ifdef SQLITE_DEBUG int sqlite3NoTempsInRange(Parse*,int,int); #endif Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int); Expr *sqlite3Expr(sqlite3*,int,const char*); void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*); Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*); |
︙ | ︙ |