From: drh Date: Wed, 20 Nov 2013 17:25:55 +0000 (+0000) Subject: Refactoring the OP_Column opcode for improved performance and X-Git-Tag: version-3.8.2~69^2~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=399af1d2c26f383c6069b30e2ca966de31689aba;p=thirdparty%2Fsqlite.git Refactoring the OP_Column opcode for improved performance and maintainability. FossilOrigin-Name: 7c914e3997d2b28164a2fa7eb4398262b6ddb4b2 --- diff --git a/manifest b/manifest index 0b494c9b3b..91be839db0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Simplifications\sto\sthe\sVdbeCursor\sobject. -D 2013-11-20T02:53:58.741 +C Refactoring\sthe\sOP_Column\sopcode\sfor\simproved\sperformance\sand\nmaintainability. +D 2013-11-20T17:25:55.912 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 8a07bebafbfda0eb67728f4bd15a36201662d1a1 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -280,11 +280,11 @@ F src/update.c c05a0ee658f1a149e0960dfd110f3b8bd846bcb0 F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269 F src/util.c 2fa6c821d28bbdbeec1b2a7b091a281c9ef8f918 F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179 -F src/vdbe.c da810a2b71f26e23d597e3b966166db8f1117fad +F src/vdbe.c d4b8a9067a163f18535324c76bafdddff6b93491 F src/vdbe.h c06f0813f853566457ce9cfb1a4a4bc39a5da644 -F src/vdbeInt.h 4044cd2e9dc1aef3e15fc68f80cbdf2446eff369 +F src/vdbeInt.h fbae1c449049a1a26ebbdf44e1beb08344072b72 F src/vdbeapi.c 93a22a9ba2abe292d5c2cf304d7eb2e894dde0ed -F src/vdbeaux.c 91f9e1fb59561fa7ba312b518b6123982c912d6c +F src/vdbeaux.c c592609996435944837b8906fbbcfcfac0714c90 F src/vdbeblob.c d883398f7260725147dbf5b40c2b61332aee47f9 F src/vdbemem.c cc529bbf4f13e4e181bdb446bf6e6962ab030b4b F src/vdbesort.c 9d83601f9d6243fe70dd0169a2820c5ddfd48147 @@ -1140,7 +1140,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 96a65388e75fed96e2e73ef65726f6db88cc5ccd -R ca796b6fcdb020d784b4cd61fedf5893 +P 5562cd343d8f69242e06a51a7f1aef7ee7d78eec +R 71521f10a62f5e2d15e34378a9a872cf +T *branch * OP_Column-refactor +T *sym-OP_Column-refactor * +T -sym-trunk * U drh -Z 97e722e14aa88f975fca1769e7e16ee6 +Z 41c0df82986aff89d8eb065b326b79ba diff --git a/manifest.uuid b/manifest.uuid index 27fbbf3bcd..6e355397de 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5562cd343d8f69242e06a51a7f1aef7ee7d78eec \ No newline at end of file +7c914e3997d2b28164a2fa7eb4398262b6ddb4b2 \ No newline at end of file diff --git a/src/vdbe.c b/src/vdbe.c index bee9a4efe3..874645f189 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -214,7 +214,7 @@ static VdbeCursor *allocateCursor( nByte = ROUND8(sizeof(VdbeCursor)) + (isBtreeCursor?sqlite3BtreeCursorSize():0) + - 2*nField*sizeof(u32); + (2*nField+2)*sizeof(u32); assert( iCurnCursor ); if( p->apCsr[iCur] ){ @@ -228,10 +228,11 @@ static VdbeCursor *allocateCursor( pCx->nField = nField; if( nField ){ pCx->aType = (u32 *)&pMem->z[ROUND8(sizeof(VdbeCursor))]; + pCx->aOffset = pCx->aType + nField; } if( isBtreeCursor ){ pCx->pCursor = (BtCursor*) - &pMem->z[ROUND8(sizeof(VdbeCursor))+2*nField*sizeof(u32)]; + &pMem->z[ROUND8(sizeof(VdbeCursor))+(2*nField+2)*sizeof(u32)]; sqlite3BtreeCursorZero(pCx->pCursor); } } @@ -2253,151 +2254,101 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ ** skipped for length() and all content loading can be skipped for typeof(). */ case OP_Column: { - u32 payloadSize; /* Number of bytes in the record */ i64 payloadSize64; /* Number of bytes in the record */ int p1; /* P1 value of the opcode */ int p2; /* column number to retrieve */ VdbeCursor *pC; /* The VDBE cursor */ - char *zRec; /* Pointer to complete record-data */ BtCursor *pCrsr; /* The BTree cursor */ u32 *aType; /* aType[i] holds the numeric type of the i-th column */ u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */ int nField; /* number of fields in the record */ int len; /* The length of the serialized data for the column */ int i; /* Loop counter */ - char *zData; /* Part of the record being decoded */ Mem *pDest; /* Where to write the extracted value */ Mem sMem; /* For storing the record being decoded */ - u8 *zIdx; /* Index into header */ - u8 *zEndHdr; /* Pointer to first byte after the header */ + const u8 *zData; /* Part of the record being decoded */ + const u8 *zHdr; /* Next unparsed byte of the header */ + const u8 *zEndHdr; /* Pointer to first byte after the header */ u32 offset; /* Offset into the data */ u32 szField; /* Number of bytes in the content of a field */ - int szHdr; /* Size of the header size field at start of record */ int avail; /* Number of bytes of available data */ u32 t; /* A type code from the record header */ Mem *pReg; /* PseudoTable input register */ - p1 = pOp->p1; - p2 = pOp->p2; - pC = 0; - memset(&sMem, 0, sizeof(sMem)); assert( p1nCursor ); + p2 = pOp->p2; + sMem.zMalloc = 0; assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); - zRec = 0; - - /* This block sets the variable payloadSize to be the total number of - ** bytes in the record. - ** - ** zRec is set to be the complete text of the record if it is available. - ** The complete record text is always available for pseudo-tables - ** If the record is stored in a cursor, the complete record text - ** might be available in the pC->aRow cache. Or it might not be. - ** If the data is unavailable, zRec is set to NULL. - ** - ** We also compute the number of columns in the record. For cursors, - ** the number of columns is stored in the VdbeCursor.nField element. - */ pC = p->apCsr[p1]; assert( pC!=0 ); + nField = pC->nField; + assert( p2aType; + aOffset = pC->aOffset; #ifndef SQLITE_OMIT_VIRTUALTABLE assert( pC->pVtabCursor==0 ); #endif pCrsr = pC->pCursor; - if( pCrsr!=0 ){ - /* The record is stored in a B-Tree */ - rc = sqlite3VdbeCursorMoveto(pC); - if( rc ) goto abort_due_to_error; - if( pC->nullRow ){ - payloadSize = 0; - }else if( pC->cacheStatus==p->cacheCtr ){ - payloadSize = pC->payloadSize; - zRec = (char*)pC->aRow; - }else if( pC->isIndex ){ - assert( sqlite3BtreeCursorIsValid(pCrsr) ); - VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64); - assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ - /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the - ** payload size, so it is impossible for payloadSize64 to be - ** larger than 32 bits. */ - assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 ); - payloadSize = (u32)payloadSize64; - }else{ - assert( sqlite3BtreeCursorIsValid(pCrsr) ); - VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &payloadSize); - assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ - } - }else{ - assert( pC->pseudoTableReg>0 ); - pReg = &aMem[pC->pseudoTableReg]; - if( pC->multiPseudo ){ - sqlite3VdbeMemShallowCopy(pDest, pReg+p2, MEM_Ephem); - Deephemeralize(pDest); - goto op_column_out; - } - assert( pReg->flags & MEM_Blob ); - assert( memIsValid(pReg) ); - payloadSize = pReg->n; - zRec = pReg->z; - pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; - assert( payloadSize==0 || zRec!=0 ); - } - - /* If payloadSize is 0, then just store a NULL. This can happen because of - ** nullRow or because of a corrupt database. */ - if( payloadSize==0 ){ - MemSetTypeFlag(pDest, MEM_Null); - goto op_column_out; - } - assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 ); - if( payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ - goto too_big; - } - - nField = pC->nField; - assert( p2aType; - if( pC->cacheStatus==p->cacheCtr ){ - aOffset = pC->aOffset; - }else{ - assert(aType); - avail = 0; - pC->aOffset = aOffset = &aType[nField]; - pC->payloadSize = payloadSize; - pC->cacheStatus = p->cacheCtr; + assert( pCrsr!=0 || pC->pseudoTableReg>0 ); - /* Figure out how many bytes are in the header */ - if( zRec ){ - zData = zRec; + /* If the cursor cache is stale, bring it up-to-date */ + rc = sqlite3VdbeCursorMoveto(pC); + if( rc ) goto abort_due_to_error; + if( pC->cacheStatus!=p->cacheCtr || (pOp->p5&OPFLAG_CLEARCACHE)!=0 ){ + if( pCrsr==0 ){ + assert( pC->pseudoTableReg>0 ); + pReg = &aMem[pC->pseudoTableReg]; + if( pC->multiPseudo ){ + sqlite3VdbeMemShallowCopy(pDest, pReg+p2, MEM_Ephem); + Deephemeralize(pDest); + goto op_column_out; + } + assert( pReg->flags & MEM_Blob ); + assert( memIsValid(pReg) ); + pC->payloadSize = pC->szRow = avail = pReg->n; + pC->aRow = (u8*)pReg->z; + }else if( pC->nullRow ){ + MemSetTypeFlag(pDest, MEM_Null); + goto op_column_out; }else{ if( pC->isIndex ){ - zData = (char*)sqlite3BtreeKeyFetch(pCrsr, &avail); + assert( sqlite3BtreeCursorIsValid(pCrsr) ); + VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64); + assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ + /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the + ** payload size, so it is impossible for payloadSize64 to be + ** larger than 32 bits. */ + assert( (payloadSize64 & SQLITE_MAX_U32)==(u64)payloadSize64 ); + pC->aRow = sqlite3BtreeKeyFetch(pCrsr, &avail); + pC->payloadSize = (u32)payloadSize64; }else{ - zData = (char*)sqlite3BtreeDataFetch(pCrsr, &avail); + assert( sqlite3BtreeCursorIsValid(pCrsr) ); + VVA_ONLY(rc =) sqlite3BtreeDataSize(pCrsr, &pC->payloadSize); + assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ + pC->aRow = sqlite3BtreeDataFetch(pCrsr, &avail); } - /* If KeyFetch()/DataFetch() managed to get the entire payload, - ** save the payload in the pC->aRow cache. That will save us from - ** having to make additional calls to fetch the content portion of - ** the record. - */ - assert( avail>=0 ); - if( payloadSize <= (u32)avail ){ - zRec = zData; - pC->aRow = (u8*)zData; + assert( avail<=65536 ); /* Maximum page size is 64KiB */ + if( pC->payloadSize <= (u32)avail ){ + pC->szRow = pC->payloadSize; }else{ - pC->aRow = 0; + pC->szRow = avail; + } + if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ + goto too_big; } } - /* The following assert is true in all cases except when - ** the database file has been corrupted externally. - ** assert( zRec!=0 || avail>=payloadSize || avail>=9 ); */ - szHdr = getVarint32((u8*)zData, offset); + + pC->cacheStatus = p->cacheCtr; + pC->iHdrOffset = getVarint32(pC->aRow, offset); + pC->nHdrParsed = 0; + aOffset[0] = offset; + if( availaRow = 0; + pC->szRow = 0; + } /* Make sure a corrupt database has not given us an oversize header. ** Do this now to avoid an oversize memory allocation. @@ -2408,78 +2359,55 @@ case OP_Column: { ** 3-byte type for each of the maximum of 32768 columns plus three ** extra bytes for the header length itself. 32768*3 + 3 = 98307. */ - if( offset > 98307 ){ + if( offset > 98307 || offset > pC->payloadSize ){ rc = SQLITE_CORRUPT_BKPT; goto op_column_out; } + } - /* Compute in len the number of bytes of data we need to read in order - ** to get nField type values. offset is an upper bound on this. But - ** nField might be significantly less than the true number of columns - ** in the table, and in that case, 5*nField+3 might be smaller than offset. - ** We want to minimize len in order to limit the size of the memory - ** allocation, especially if a corrupt database file has caused offset - ** to be oversized. Offset is limited to 98307 above. But 98307 might - ** still exceed Robson memory allocation limits on some configurations. - ** On systems that cannot tolerate large memory allocations, nField*5+3 - ** will likely be much smaller since nField will likely be less than - ** 20 or so. This insures that Robson memory allocation limits are - ** not exceeded even for corrupt database files. - */ - len = nField*5 + 3; - if( len > (int)offset ) len = (int)offset; - - /* The KeyFetch() or DataFetch() above are fast and will get the entire - ** record header in most cases. But they will fail to get the complete - ** record header if the record header does not fit on a single page - ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to - ** acquire the complete header text. - */ - if( !zRec && availisIndex, &sMem); + /* Make sure at least the first p2+1 entries of the header have been + ** parsed and valid information is in aOffset[] and aType[]. + */ + if( pC->nHdrParsed<=p2 && pC->iHdrOffsetaRow==0 ){ + memset(&sMem, 0, sizeof(sMem)); + rc = sqlite3VdbeMemFromBtree(pCrsr, 0, pC->aOffset[0], pC->isIndex,&sMem); if( rc!=SQLITE_OK ){ goto op_column_out; } - zData = sMem.z; + zData = (u8*)sMem.z; + }else{ + zData = pC->aRow; } - zEndHdr = (u8 *)&zData[len]; - zIdx = (u8 *)&zData[szHdr]; - /* Scan the header and use it to fill in the aType[] and aOffset[] - ** arrays. aType[i] will contain the type integer for the i-th - ** column and aOffset[i] will contain the offset from the beginning - ** of the record to the start of the data for the i-th column - */ - for(i=0; inHdrParsed; + offset = aOffset[i]; + zHdr = zData + pC->iHdrOffset; + zEndHdr = zData + pC->aOffset[0]; + for(; i<=p2 && zHdrnHdrParsed = i; + pC->iHdrOffset = (u32)(zHdr - zData); + if( pC->aRow==0 ){ + sqlite3VdbeMemRelease(&sMem); + sMem.flags = MEM_Null; } - sqlite3VdbeMemRelease(&sMem); - sMem.flags = MEM_Null; /* If we have read more header data than was contained in the header, ** or if the end of the last field appears to be past the end of the @@ -2487,8 +2415,10 @@ case OP_Column: { ** of the record (when all fields present), then we must be dealing ** with a corrupt database. */ - if( (zIdx > zEndHdr) || (offset > payloadSize) - || (zIdx==zEndHdr && offset!=payloadSize) ){ + if( (zHdr > zEndHdr) + || (offset > pC->payloadSize) + || (zHdr==zEndHdr && offset!=pC->payloadSize) + ){ rc = SQLITE_CORRUPT_BKPT; goto op_column_out; } @@ -2500,12 +2430,12 @@ case OP_Column: { ** request. In this case, set the value NULL or to P4 if P4 is ** a pointer to a Mem object. */ - if( aOffset[p2] ){ + if( p2nHdrParsed ){ assert( rc==SQLITE_OK ); - if( zRec ){ + if( pC->szRow>=aOffset[p2+1] ){ /* This is the common case where the whole row fits on a single page */ VdbeMemRelease(pDest); - sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest); + sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], aType[p2], pDest); }else{ /* This branch happens only when the row overflows onto multiple pages */ t = aType[p2]; @@ -2517,18 +2447,19 @@ case OP_Column: { ** bogus content rather than reading content from disk. NULL works ** for text and blob and whatever is in the payloadSize64 variable ** will work for everything else. */ - zData = t<12 ? (char*)&payloadSize64 : 0; + zData = t<12 ? (u8*)&payloadSize64 : 0; }else{ len = sqlite3VdbeSerialTypeLen(t); + memset(&sMem, 0, sizeof(sMem)); sqlite3VdbeMemMove(&sMem, pDest); rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->isIndex, &sMem); if( rc!=SQLITE_OK ){ goto op_column_out; } - zData = sMem.z; + zData = (u8*)sMem.z; } - sqlite3VdbeSerialGet((u8*)zData, t, pDest); + sqlite3VdbeSerialGet(zData, t, pDest); } pDest->enc = encoding; }else{ @@ -3312,6 +3243,8 @@ case OP_OpenWrite: { nField = pOp->p4.i; } assert( pOp->p1>=0 ); + assert( nField>=0 ); + testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */ pCur = allocateCursor(p, pOp->p1, nField, iDb, 1); if( pCur==0 ) goto no_mem; pCur->nullRow = 1; @@ -3372,6 +3305,7 @@ case OP_OpenEphemeral: { SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); + assert( pOp->p2>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; @@ -3417,6 +3351,8 @@ case OP_OpenEphemeral: { case OP_SorterOpen: { VdbeCursor *pCx; + assert( pOp->p1>=0 ); + assert( pOp->p2>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); if( pCx==0 ) goto no_mem; pCx->pKeyInfo = pOp->p4.pKeyInfo; @@ -3448,6 +3384,7 @@ case OP_OpenPseudo: { VdbeCursor *pCx; assert( pOp->p1>=0 ); + assert( pOp->p3>=0 ); pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); if( pCx==0 ) goto no_mem; pCx->nullRow = 1; @@ -4421,6 +4358,7 @@ case OP_NullRow: { assert( pC!=0 ); pC->nullRow = 1; pC->rowidIsValid = 0; + pC->cacheStatus = CACHE_STALE; assert( pC->pCursor || pC->pVtabCursor ); if( pC->pCursor ){ sqlite3BtreeClearCursor(pC->pCursor); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index d5cad65ecd..fdd1f46744 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -93,10 +93,14 @@ struct VdbeCursor { ** be NULL. */ u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */ - int payloadSize; /* Total number of bytes in the record */ + u32 payloadSize; /* Total number of bytes in the record */ + u16 nHdrParsed; /* Number of header fields parsed so far */ + u16 nFieldPresent; /* Number of fields in the record */ + u32 szRow; /* Byte available in aRow */ + u32 iHdrOffset; /* Offset to next unparsed byte of the header */ u32 *aType; /* Type values for all entries in the record */ u32 *aOffset; /* Cached offsets to the start of each columns data */ - u8 *aRow; /* Data for the current row, if all on one page */ + const u8 *aRow; /* Data for the current row, if all on one page */ }; typedef struct VdbeCursor VdbeCursor; diff --git a/src/vdbeaux.c b/src/vdbeaux.c index dc284b54ae..2e3cdfb711 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -2658,7 +2658,7 @@ int sqlite3VdbeCursorMoveto(VdbeCursor *p){ #endif p->deferredMoveto = 0; p->cacheStatus = CACHE_STALE; - }else if( ALWAYS(p->pCursor) ){ + }else if( p->pCursor ){ int hasMoved; int rc = sqlite3BtreeCursorHasMoved(p->pCursor, &hasMoved); if( rc ) return rc;