From: dan Date: Tue, 25 Feb 2014 21:01:25 +0000 (+0000) Subject: Attempt to speed up sqlite3VdbeRecordCompare() by various means. This code is in... X-Git-Tag: version-3.8.4~34^2~2^2~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1fed5dab0dedceca104c51670cf74114587b536f;p=thirdparty%2Fsqlite.git Attempt to speed up sqlite3VdbeRecordCompare() by various means. This code is in an interim state. FossilOrigin-Name: 85206e0bbac29adab52bef795f6d1479f2ae2c0e --- diff --git a/manifest b/manifest index f25d3185ff..2d70d7d908 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Also\sadjust\sthe\sorder\sof\sfiles\sin\sthe\samalgamation\sto\sensure\sthat\n_FILE_OFFSET_BITS\sis\sdefined\sbefore\sany\s#include,\sfor\sQNX. -D 2014-02-25T18:12:58.878 +C Attempt\sto\sspeed\sup\ssqlite3VdbeRecordCompare()\sby\svarious\smeans.\sThis\scode\sis\sin\san\sinterim\sstate. +D 2014-02-25T21:01:25.824 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -163,7 +163,7 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53 F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 -F src/btree.c b945df4f0114b4cc71006acc2fbb1333fb33a200 +F src/btree.c f4d85c3e2e189a219965d6d4525330333735fd1d F src/btree.h 9e0f97c01b972f779eb7655cfb4f8727fd6dc26f F src/btreeInt.h 0be66063468a520e4d66b80c7a1dc26d04ee6ea4 F src/build.c 00ce613bc2256e525c9195cb10d0df7bcc48d1f0 @@ -221,7 +221,7 @@ F src/shell.c 3dd86bf73ccd079f0e32ef5069600586085e8239 F src/sqlite.h.in a2ef671f92747a5a1c8a47bad5c585a8dd9eca80 F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h 6725dc99b3985a97bad315910e59c3f7f5058916 +F src/sqliteInt.h 6d868994f476b616ddb1795a51aa83c331ef5a62 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -277,14 +277,14 @@ F src/update.c 5b3e74a03b3811e586b4f2b4cbd7c49f01c93115 F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269 F src/util.c c46c90459ef9bdc0c6c73803cf4c55425b4771cf F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179 -F src/vdbe.c 6c8f28911e702151c8ef03c568db5a066d3a85d4 -F src/vdbe.h 147027d6e8e667a63e87177a38e2b42c71fdacf8 +F src/vdbe.c ab910206dd8c9c5c1455f82953934bdbfe0bcc2a +F src/vdbe.h 0758eff7f1bf939bcafa377b2fafba4f5be63007 F src/vdbeInt.h 5286af9067cabdb8ba57b87c0c988a931be6c6c8 F src/vdbeapi.c 5bc41aaea448a7fc250902c418f1795859be3820 -F src/vdbeaux.c 8b8eeb3cd89e4b3d4f40186344915b49b7c1c0f7 +F src/vdbeaux.c 988269c675ebb75a3610479840b65bcb8ea92647 F src/vdbeblob.c d939997de046b8fcc607cfee4248f3d33dbcca50 -F src/vdbemem.c 06603e8e9d2f3247b68c6bbe4bd37fb6721b5bda -F src/vdbesort.c 9d83601f9d6243fe70dd0169a2820c5ddfd48147 +F src/vdbemem.c 25cc487244bf6ad647105c5adbc3052403dfd143 +F src/vdbesort.c 5144d84bd7d0d2545af1c6322edbbf07f97e3892 F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767 F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd F src/wal.c 76e7fc6de229bea8b30bb2539110f03a494dc3a8 @@ -735,7 +735,7 @@ F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025 F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54 F test/permutations.test 40add071ba71aefe1c04f5845308cf46f7de8d04 -F test/pragma.test e882183ecd21d064cec5c7aaea174fbd36293429 +F test/pragma.test c1bc3a29f664b57d0ae40b498ad31f370de8daa6 F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13 F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552 F test/printf2.test bed79b4c3e5da08ba88ad637c0bf62586843cfb1 @@ -1151,7 +1151,10 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 953cdd6adadfd46b51ad61d7939cecca154a02cb -R d169e5164f16cb0355a3972eba04326c -U drh -Z 6c6f89e1dd41d2a146386cfa06ae53d9 +P 23001a85cd334090cf6c70d4d7e722a01f4f6899 +R 04ade79c9b23add6f2d6adf0c34db1e4 +T *branch * experimental +T *sym-experimental * +T -sym-trunk * +U dan +Z d3f147085a7b8a8eca4578239439871b diff --git a/manifest.uuid b/manifest.uuid index a069c374c5..dd611d410f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -23001a85cd334090cf6c70d4d7e722a01f4f6899 \ No newline at end of file +85206e0bbac29adab52bef795f6d1479f2ae2c0e \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 4fd8e27bf4..6c5f02403e 100644 --- a/src/btree.c +++ b/src/btree.c @@ -4547,6 +4547,7 @@ int sqlite3BtreeMovetoUnpacked( int *pRes /* Write search results here */ ){ int rc; + int (*xRecordCompare)(int, const void*, UnpackedRecord*); assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); @@ -4568,6 +4569,11 @@ int sqlite3BtreeMovetoUnpacked( } } + if( pIdxKey ){ + xRecordCompare = sqlite3VdbeFindCompare(pIdxKey); + assert( pIdxKey->default_rc==1 || pIdxKey->default_rc==0 || pIdxKey->default_rc==-1); + } + rc = moveToRoot(pCur); if( rc ){ return rc; @@ -4652,14 +4658,14 @@ int sqlite3BtreeMovetoUnpacked( ** single byte varint and the record fits entirely on the main ** b-tree page. */ testcase( pCell+nCell+1==pPage->aDataEnd ); - c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey); + c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey); }else if( !(pCell[1] & 0x80) && (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal ){ /* The record-size field is a 2 byte varint and the record ** fits entirely on the main b-tree page. */ testcase( pCell+nCell+2==pPage->aDataEnd ); - c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey); + c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey); }else{ /* The record flows over onto one or more overflow pages. In ** this case the whole cell needs to be parsed, a buffer allocated @@ -4680,7 +4686,7 @@ int sqlite3BtreeMovetoUnpacked( sqlite3_free(pCellKey); goto moveto_finish; } - c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); + c = xRecordCompare(nCell, pCellKey, pIdxKey); sqlite3_free(pCellKey); } if( c<0 ){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 72fe47be5a..76dea413e0 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1590,15 +1590,10 @@ struct KeyInfo { struct UnpackedRecord { KeyInfo *pKeyInfo; /* Collation and sort-order information */ u16 nField; /* Number of entries in apMem[] */ - u8 flags; /* Boolean settings. UNPACKED_... below */ + char default_rc; /* Comparison result if keys are equal */ Mem *aMem; /* Values */ }; -/* -** Allowed values of UnpackedRecord.flags -*/ -#define UNPACKED_INCRKEY 0x01 /* Make this key an epsilon larger */ -#define UNPACKED_PREFIX_MATCH 0x02 /* A prefix match is considered OK */ /* ** Each SQL index is represented in memory by an diff --git a/src/vdbe.c b/src/vdbe.c index 5ab0127658..097f182a23 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -3555,16 +3555,16 @@ case OP_SeekGT: { /* jump, in3 */ /* The next line of code computes as follows, only faster: ** if( oc==OP_SeekGT || oc==OP_SeekLE ){ - ** r.flags = UNPACKED_INCRKEY; + ** r.default_rc = -1; ** }else{ - ** r.flags = 0; + ** r.default_rc = +1; ** } */ - r.flags = (u8)(UNPACKED_INCRKEY * (1 & (oc - OP_SeekLT))); - assert( oc!=OP_SeekGT || r.flags==UNPACKED_INCRKEY ); - assert( oc!=OP_SeekLE || r.flags==UNPACKED_INCRKEY ); - assert( oc!=OP_SeekGE || r.flags==0 ); - assert( oc!=OP_SeekLT || r.flags==0 ); + r.default_rc = ((1 & (oc - OP_SeekLT)) ? -1 : +1); + assert( oc!=OP_SeekGT || r.default_rc==-1 ); + assert( oc!=OP_SeekLE || r.default_rc==-1 ); + assert( oc!=OP_SeekGE || r.default_rc==+1 ); + assert( oc!=OP_SeekLT || r.default_rc==+1 ); r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG @@ -3722,7 +3722,6 @@ case OP_Found: { /* jump, in3 */ if( ii ) REGISTER_TRACE(pOp->p3+ii, &r.aMem[ii]); #endif } - r.flags = UNPACKED_PREFIX_MATCH; pIdxKey = &r; }else{ pIdxKey = sqlite3VdbeAllocUnpackedRecord( @@ -3732,8 +3731,8 @@ case OP_Found: { /* jump, in3 */ assert( pIn3->flags & MEM_Blob ); assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */ sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey); - pIdxKey->flags |= UNPACKED_PREFIX_MATCH; } + pIdxKey->default_rc = 0; if( pOp->opcode==OP_NoConflict ){ /* For the OP_NoConflict opcode, take the jump if any of the ** input fields are NULL, since any key with a NULL will not @@ -4622,7 +4621,7 @@ case OP_IdxDelete: { assert( pOp->p5==0 ); r.pKeyInfo = pC->pKeyInfo; r.nField = (u16)pOp->p3; - r.flags = UNPACKED_PREFIX_MATCH; + r.default_rc = 0; r.aMem = &aMem[pOp->p2]; #ifdef SQLITE_DEBUG { int i; for(i=0; ip4.i; if( pOp->opcodeopcode==OP_IdxLE || pOp->opcode==OP_IdxGT ); - r.flags = UNPACKED_INCRKEY | UNPACKED_PREFIX_MATCH; + r.default_rc = -1; }else{ assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxLT ); - r.flags = UNPACKED_PREFIX_MATCH; + r.default_rc = 0; } r.aMem = &aMem[pOp->p3]; #ifdef SQLITE_DEBUG diff --git a/src/vdbe.h b/src/vdbe.h index 66c69eec23..af07831faa 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -214,6 +214,9 @@ void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*); int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **); +typedef int (*RecordCompare)(int,const void*, UnpackedRecord*); +RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *); + #ifndef SQLITE_OMIT_TRIGGER void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *); #endif diff --git a/src/vdbeaux.c b/src/vdbeaux.c index ef70f8ae3f..e817ccb23f 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -3102,7 +3102,7 @@ void sqlite3VdbeRecordUnpack( u32 szHdr; Mem *pMem = p->aMem; - p->flags = 0; + p->default_rc = 0; assert( EIGHT_BYTE_ALIGNMENT(pMem) ); idx = getVarint32(aKey, szHdr); d = szHdr; @@ -3123,6 +3123,215 @@ void sqlite3VdbeRecordUnpack( p->nField = u; } +static int vdbeRecordCompareString( + int nKey1, const void *pKey1, /* Left key */ + UnpackedRecord *pPKey2 /* Right key */ +){ + const u8 *aKey1 = (const u8*)pKey1; + int szHdr; + int serial_type; + int res; + + szHdr = aKey1[0]; + getVarint32(&aKey1[1], serial_type); + + if( serial_type<12 ){ + res = -1; /* (pKey1/nKey1) is a number or a null */ + }else if( !(serial_type & 0x01) ){ + res = +1; /* (pKey1/nKey1) is a blob */ + }else{ + int nCmp; + int nStr; + aKey1 = &aKey1[szHdr]; + + nStr = (serial_type-12) / 2; + if( (szHdr + nStr) > nKey1 ) return 0; /* Corruption */ + nCmp = MIN( pPKey2->aMem[0].n, nStr ); + res = memcmp(aKey1, pPKey2->aMem[0].z, nCmp); + + if( res==0 ){ + res = nStr - pPKey2->aMem[0].n; + if( res==0 ) res = pPKey2->default_rc; + } + } + + assert( (res==0 && sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2)==0) + || (res<0 && sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2)<0) + || (res>0 && sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2)>0) + ); + return res; +} + +static int vdbeRecordCompareInt( + int nKey1, const void *pKey1, /* Left key */ + UnpackedRecord *pPKey2 /* Right key */ +){ + const u8 *aKey1 = (const u8*)pKey1; + int szHdr; + int serial_type; + int res; + + szHdr = aKey1[0]; + getVarint32(&aKey1[1], serial_type); + + if( serial_type==0 ){ + res = -1; /* NULL values are smaller than integers */ + }else if( serial_type>=12 ){ + res = +1; /* text/blob values are greater */ + }else{ + Mem mem; + sqlite3VdbeSerialGet(&aKey1[szHdr], serial_type, &mem); + if( mem.flags & MEM_Int ){ + i64 v = pPKey2->aMem[0].u.i; + if( v>mem.u.i ){ + res = -1; + }else if( vdefault_rc; + } + }else{ + double v = (double)pPKey2->aMem[0].u.i; + if( v>mem.r ){ + res = -1; + }else if( vdefault_rc; + } + } + } + + assert( (res==0 && sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2)==0) + || (res<0 && sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2)<0) + || (res>0 && sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2)>0) + ); + return res; +} + +static int vdbeCompareMemString( + const Mem *pMem1, + const Mem *pMem2, + const CollSeq *pColl +){ + if( pMem1->enc==pColl->enc ){ + /* The strings are already in the correct encoding. Call the + ** comparison function directly */ + return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); + }else{ + int rc; + const void *v1, *v2; + int n1, n2; + Mem c1; + Mem c2; + memset(&c1, 0, sizeof(c1)); + memset(&c2, 0, sizeof(c2)); + sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem); + sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem); + v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc); + n1 = v1==0 ? 0 : c1.n; + v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc); + n2 = v2==0 ? 0 : c2.n; + rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2); + sqlite3VdbeMemRelease(&c1); + sqlite3VdbeMemRelease(&c2); + return rc; + } +} + +/* +** Compare the values contained by the two memory cells, returning +** negative, zero or positive if pMem1 is less than, equal to, or greater +** than pMem2. Sorting order is NULL's first, followed by numbers (integers +** and reals) sorted numerically, followed by text ordered by the collating +** sequence pColl and finally blob's ordered by memcmp(). +** +** Two NULL values are considered equal by this function. +*/ +int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){ + int rc; + int f1, f2; + int combined_flags; + + f1 = pMem1->flags; + f2 = pMem2->flags; + combined_flags = f1|f2; + assert( (combined_flags & MEM_RowSet)==0 ); + + /* If one value is NULL, it is less than the other. If both values + ** are NULL, return 0. + */ + if( combined_flags&MEM_Null ){ + return (f2&MEM_Null) - (f1&MEM_Null); + } + + /* If one value is a number and the other is not, the number is less. + ** If both are numbers, compare as reals if one is a real, or as integers + ** if both values are integers. + */ + if( combined_flags&(MEM_Int|MEM_Real) ){ + double r1, r2; + if( (f1 & f2 & MEM_Int)!=0 ){ + if( pMem1->u.i < pMem2->u.i ) return -1; + if( pMem1->u.i > pMem2->u.i ) return 1; + return 0; + } + if( (f1&MEM_Real)!=0 ){ + r1 = pMem1->r; + }else if( (f1&MEM_Int)!=0 ){ + r1 = (double)pMem1->u.i; + }else{ + return 1; + } + if( (f2&MEM_Real)!=0 ){ + r2 = pMem2->r; + }else if( (f2&MEM_Int)!=0 ){ + r2 = (double)pMem2->u.i; + }else{ + return -1; + } + if( r1r2 ) return 1; + return 0; + } + + /* If one value is a string and the other is a blob, the string is less. + ** If both are strings, compare using the collating functions. + */ + if( combined_flags&MEM_Str ){ + if( (f1 & MEM_Str)==0 ){ + return 1; + } + if( (f2 & MEM_Str)==0 ){ + return -1; + } + + assert( pMem1->enc==pMem2->enc ); + assert( pMem1->enc==SQLITE_UTF8 || + pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE ); + + /* The collation sequence must be defined at this point, even if + ** the user deletes the collation sequence after the vdbe program is + ** compiled (this was not always the case). + */ + assert( !pColl || pColl->xCmp ); + + if( pColl ){ + return vdbeCompareMemString(pMem1, pMem2, pColl); + } + /* If a NULL pointer was passed as the collate function, fall through + ** to the blob case and use memcmp(). */ + } + + /* Both values must be blobs. Compare using memcmp(). */ + rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n); + if( rc==0 ){ + rc = pMem1->n - pMem2->n; + } + return rc; +} + + /* ** This function compares the two table rows or index records ** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero @@ -3140,7 +3349,7 @@ void sqlite3VdbeRecordUnpack( ** equal, then the keys are considered to be equal and ** the parts beyond the common prefix are ignored. */ -int sqlite3VdbeRecordCompare( +static int vdbeRecordComparePrev( int nKey1, const void *pKey1, /* Left key */ UnpackedRecord *pPKey2 /* Right key */ ){ @@ -3216,24 +3425,191 @@ int sqlite3VdbeRecordCompare( assert( mem1.zMalloc==0 ); /* rc==0 here means that one of the keys ran out of fields and - ** all the fields up to that point were equal. If the UNPACKED_INCRKEY - ** flag is set, then break the tie by treating key2 as larger. - ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes - ** are considered to be equal. Otherwise, the longer key is the - ** larger. As it happens, the pPKey2 will always be the longer - ** if there is a difference. + ** all the fields up to that point were equal. Return the the default_rc + ** value. */ + return pPKey2->default_rc; +} + + +int sqlite3VdbeRecordCompare( + int nKey1, const void *pKey1, /* Left key */ + UnpackedRecord *pPKey2 /* Right key */ +){ + u32 d1; /* Offset into aKey[] of next data element */ + u32 idx1; /* Offset into aKey[] of next header element */ + u32 szHdr1; /* Number of bytes in header */ + int i = 0; + int rc = 0; + KeyInfo *pKeyInfo = pPKey2->pKeyInfo; + const unsigned char *aKey1 = (const unsigned char *)pKey1; + Mem mem1; + +#ifdef SQLITE_DEBUG + int expected = vdbeRecordComparePrev(nKey1, pKey1, pPKey2); + static int nCall = 0; + nCall++; +#endif + + VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */ + + idx1 = getVarint32(aKey1, szHdr1); + d1 = szHdr1; + assert( pPKey2->pKeyInfo->nField+pPKey2->pKeyInfo->nXField>=pPKey2->nField + || CORRUPT_DB ); + assert( pPKey2->pKeyInfo->aSortOrder!=0 ); + assert( pPKey2->pKeyInfo->nField>0 ); + assert( idx1<=szHdr1 || CORRUPT_DB ); + do{ + Mem *pRhs = &pPKey2->aMem[i]; + u32 serial_type; + + /* RHS is an integer */ + if( pRhs->flags & MEM_Int ){ + serial_type = aKey1[idx1]; + if( serial_type>=12 ){ + rc = +1; + }else if( serial_type==0 ){ + rc = -1; + }else{ + sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); + if( serial_type==7 ){ + double rhs = (double)pRhs->u.i; + if( mem1.rrhs ){ + rc = +1; + } + }else{ + i64 rhs = pRhs->u.i; + if( mem1.u.irhs ){ + rc = +1; + } + } + } + } + + /* RHS is real */ + else if( pRhs->flags & MEM_Real ){ + serial_type = aKey1[idx1]; + if( serial_type>=12 ){ + rc = +1; + }else if( serial_type==0 ){ + rc = -1; + }else{ + double rhs = pRhs->r; + double lhs; + sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1); + if( serial_type==7 ){ + lhs = mem1.r; + }else{ + lhs = mem1.u.i; + } + if( lhsrhs ){ + rc = +1; + } + } + } + + /* RHS is a string */ + else if( pRhs->flags & MEM_Str ){ + getVarint32(&aKey1[idx1], serial_type); + if( serial_type<12 ){ + rc = -1; + }else if( !(serial_type & 0x01) ){ + rc = +1; + }else{ + mem1.n = (serial_type - 12) / 2; + if( (d1+mem1.n) > nKey1 ){ + rc = 1; /* Corruption */ + }else if( pKeyInfo->aColl[i] ){ + mem1.enc = pKeyInfo->enc; + mem1.db = pKeyInfo->db; + mem1.flags = MEM_Str; + mem1.z = &aKey1[d1]; + rc = vdbeCompareMemString(&mem1, pRhs, pKeyInfo->aColl[i]); + }else{ + int nCmp = MIN(mem1.n, pRhs->n); + rc = memcmp(&aKey1[d1], pRhs->z, nCmp); + if( rc==0 ) rc = mem1.n - pRhs->n; + } + } + } + + /* RHS is a blob */ + else if( pRhs->flags & MEM_Blob ){ + getVarint32(&aKey1[idx1], serial_type); + if( serial_type<12 || (serial_type & 0x01) ){ + rc = -1; + }else{ + int nStr = (serial_type - 12) / 2; + if( (d1+nStr) > nKey1 ){ + rc = 1; /* Corruption */ + }else{ + int nCmp = MIN(nStr, pRhs->n); + rc = memcmp(&aKey1[d1], pRhs->z, nCmp); + if( rc==0 ) rc = nStr - pRhs->n; + } + } + } + + /* RHS is null */ + else{ + serial_type = aKey1[idx1]; + rc = (serial_type!=0); + } + + if( rc!=0 ){ + assert( mem1.zMalloc==0 ); /* See comment below */ + if( pKeyInfo->aSortOrder[i] ){ + rc = -rc; +#if 0 + assert( (rc>0 && (rc^(int)0x80000000)<0) + || (rc<0 && (rc^(int)0x80000000)>0) ); + assert( sizeof(int)==4 ); + rc ^= (int)0x80000000; /* similar in spirit to: "rc = -rc;" */ + assert( rc!=0 ); +#endif + } + assert( (rc<0 && expected<0) || (rc>0 && expected>0) || CORRUPT_DB ); + return rc; + } + + i++; + d1 += sqlite3VdbeSerialTypeLen(serial_type); + idx1 += sqlite3VarintLen(serial_type); + }while( idx1nField && d1<=nKey1 ); + + /* No memory allocation is ever used on mem1. Prove this using + ** the following assert(). If the assert() fails, it indicates a + ** memory leak and a need to call sqlite3VdbeMemRelease(&mem1). */ - assert( rc==0 ); - if( pPKey2->flags & UNPACKED_INCRKEY ){ - rc = -1; - }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){ - /* Leave rc==0 */ - }else if( idx1default_rc==expected ); + return pPKey2->default_rc; +} + +RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){ + if( p->nField==1 && p->pKeyInfo->aSortOrder[0]==0 ){ + int flags = p->aMem[0].flags; + if( (flags & MEM_Int) ){ + return vdbeRecordCompareInt; + }else if( (p->aMem[0].flags&(MEM_Int|MEM_Real|MEM_Null|MEM_Blob))==0 + && p->pKeyInfo->aColl[0]==0 + ){ + return vdbeRecordCompareString; + } } - return rc; + return sqlite3VdbeRecordCompare; } - + /* ** pCur points at an index entry created using the OP_MakeRecord opcode. @@ -3347,7 +3723,6 @@ int sqlite3VdbeIdxKeyCompare( if( rc ){ return rc; } - assert( pUnpacked->flags & UNPACKED_PREFIX_MATCH ); *res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked); sqlite3VdbeMemRelease(&m); return SQLITE_OK; diff --git a/src/vdbemem.c b/src/vdbemem.c index bcf9586b3f..4936f40016 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -754,119 +754,6 @@ int sqlite3VdbeMemSetStr( return SQLITE_OK; } -/* -** Compare the values contained by the two memory cells, returning -** negative, zero or positive if pMem1 is less than, equal to, or greater -** than pMem2. Sorting order is NULL's first, followed by numbers (integers -** and reals) sorted numerically, followed by text ordered by the collating -** sequence pColl and finally blob's ordered by memcmp(). -** -** Two NULL values are considered equal by this function. -*/ -int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){ - int rc; - int f1, f2; - int combined_flags; - - f1 = pMem1->flags; - f2 = pMem2->flags; - combined_flags = f1|f2; - assert( (combined_flags & MEM_RowSet)==0 ); - - /* If one value is NULL, it is less than the other. If both values - ** are NULL, return 0. - */ - if( combined_flags&MEM_Null ){ - return (f2&MEM_Null) - (f1&MEM_Null); - } - - /* If one value is a number and the other is not, the number is less. - ** If both are numbers, compare as reals if one is a real, or as integers - ** if both values are integers. - */ - if( combined_flags&(MEM_Int|MEM_Real) ){ - double r1, r2; - if( (f1 & f2 & MEM_Int)!=0 ){ - if( pMem1->u.i < pMem2->u.i ) return -1; - if( pMem1->u.i > pMem2->u.i ) return 1; - return 0; - } - if( (f1&MEM_Real)!=0 ){ - r1 = pMem1->r; - }else if( (f1&MEM_Int)!=0 ){ - r1 = (double)pMem1->u.i; - }else{ - return 1; - } - if( (f2&MEM_Real)!=0 ){ - r2 = pMem2->r; - }else if( (f2&MEM_Int)!=0 ){ - r2 = (double)pMem2->u.i; - }else{ - return -1; - } - if( r1r2 ) return 1; - return 0; - } - - /* If one value is a string and the other is a blob, the string is less. - ** If both are strings, compare using the collating functions. - */ - if( combined_flags&MEM_Str ){ - if( (f1 & MEM_Str)==0 ){ - return 1; - } - if( (f2 & MEM_Str)==0 ){ - return -1; - } - - assert( pMem1->enc==pMem2->enc ); - assert( pMem1->enc==SQLITE_UTF8 || - pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE ); - - /* The collation sequence must be defined at this point, even if - ** the user deletes the collation sequence after the vdbe program is - ** compiled (this was not always the case). - */ - assert( !pColl || pColl->xCmp ); - - if( pColl ){ - if( pMem1->enc==pColl->enc ){ - /* The strings are already in the correct encoding. Call the - ** comparison function directly */ - return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z); - }else{ - const void *v1, *v2; - int n1, n2; - Mem c1; - Mem c2; - memset(&c1, 0, sizeof(c1)); - memset(&c2, 0, sizeof(c2)); - sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem); - sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem); - v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc); - n1 = v1==0 ? 0 : c1.n; - v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc); - n2 = v2==0 ? 0 : c2.n; - rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2); - sqlite3VdbeMemRelease(&c1); - sqlite3VdbeMemRelease(&c2); - return rc; - } - } - /* If a NULL pointer was passed as the collate function, fall through - ** to the blob case and use memcmp(). */ - } - - /* Both values must be blobs. Compare using memcmp(). */ - rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n); - if( rc==0 ){ - rc = pMem1->n - pMem2->n; - } - return rc; -} - /* ** Move data out of a btree key or data field and into a Mem structure. ** The data or key is taken from the entry that pCur is currently pointing diff --git a/src/vdbesort.c b/src/vdbesort.c index 3e4cad5b4a..c4d15d6872 100644 --- a/src/vdbesort.c +++ b/src/vdbesort.c @@ -409,7 +409,7 @@ static void vdbeSorterCompare( return; } } - r2->flags |= UNPACKED_PREFIX_MATCH; + assert( r2->default_rc==0 ); } *pRes = sqlite3VdbeRecordCompare(nKey1, pKey1, r2); diff --git a/test/pragma.test b/test/pragma.test index 1043170378..fcbb1808fa 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -1575,6 +1575,8 @@ do_test pragma-20.8 { forcedelete data_dir } ;# endif windows +database_may_be_corrupt + do_test 21.1 { # Create a corrupt database in testerr.db. And a non-corrupt at test.db. # @@ -1680,4 +1682,5 @@ do_test 23.5 { } } {0 0 t1 y {} {NO ACTION} {NO ACTION} NONE} +database_never_corrupt finish_test