From da475b8dbc6c2a74e3e489d07493a50fce0f54bc Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 4 Nov 2013 21:44:54 +0000 Subject: [PATCH] Bug fixes in the INSERT constraint checker. Refactor the Rowid handling logic for ANALYZE with STAT3/4. FossilOrigin-Name: 1ea43c0f236792a3bc13e1cb330f5ff3402c2851 --- manifest | 16 ++++----- manifest.uuid | 2 +- src/analyze.c | 96 +++++++++++++++++++++++++++++++-------------------- src/insert.c | 60 ++++++++++++++++++-------------- src/pragma.c | 2 +- 5 files changed, 102 insertions(+), 74 deletions(-) diff --git a/manifest b/manifest index ad2634b5b1..fadb82468d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sanother\stest\scase\sfile\sfor\sWITHOUT\sROWID\sand\sfix\sthe\sbugs\sthat\sthe\snew\s\ntest\sfile\suncovered. -D 2013-11-04T18:34:46.599 +C Bug\sfixes\sin\sthe\sINSERT\sconstraint\schecker.\s\sRefactor\sthe\sRowid\shandling\slogic\nfor\sANALYZE\swith\sSTAT3/4. +D 2013-11-04T21:44:54.263 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 0522b53cdc1fcfc18f3a98e0246add129136c654 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -159,7 +159,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a F src/alter.c 2af0330bb1b601af7a7789bf7229675fd772a083 -F src/analyze.c b583d62c9b74d887864eaa594d629c0b33bea4a5 +F src/analyze.c 0727d84692bdad3bada987950fd714445cca225c F src/attach.c 0a17c9364895316ca4f52d06a97a72c0af1ae8b3 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 2f1987981139bd2f6d8c728d64bf09fb387443c3 @@ -182,7 +182,7 @@ F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759 F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4 F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08 -F src/insert.c faa58c9978001f67b3fcb4e0fb588c7852c05483 +F src/insert.c b89a81a5c4cfc5cf83f29751e07b45527af27dc2 F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b @@ -212,7 +212,7 @@ F src/parse.y 073a8294e1826f1b1656e84806b77e4199f4bb57 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c a467393909a4ed7ca9de066d85ba5c5b04a5be63 -F src/pragma.c 8d67f2e7a73b8260cfed328eb024fb16c20e0f16 +F src/pragma.c ac43f31b04afd586f7d4a7535f6c4b07b329a1de F src/prepare.c fa6988589f39af8504a61731614cd4f6ae71554f F src/printf.c da9119eb31a187a4b99f60aa4a225141c0ebb74b F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68 @@ -1131,7 +1131,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 89098e6d18dacd1554cf4471b5f035db85d1f327 -R 4fbdd86f5ad03355f6f11feb2ddb3d95 +P bc2a06eb8e57573d08e77800a7937eee5af3f035 +R c6a00a38efed0dd468c88c1afd0d6407 U drh -Z 1acf97ed6e77633885d90ac086f2b0ff +Z 16670a36629db5e8da9906d68f8a996e diff --git a/manifest.uuid b/manifest.uuid index 479d71aef0..3475724835 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -bc2a06eb8e57573d08e77800a7937eee5af3f035 \ No newline at end of file +1ea43c0f236792a3bc13e1cb330f5ff3402c2851 \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index a71231eb5e..0e5869427e 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -268,9 +268,11 @@ struct Stat4Sample { tRowcnt *anDLt; /* sqlite_stat4.nDLt */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 tRowcnt *anLt; /* sqlite_stat4.nLt */ - i64 iRowid; /* Rowid in main table of the key */ + union { + i64 iRowid; /* Rowid in main table of the key */ + u8 *aRowid; /* Key for WITHOUT ROWID tables */ + } u; u32 nRowid; /* Sizeof aRowid[] */ - u8 *aRowid; /* Key for WITHOUT ROWID tables */ u8 isPSample; /* True if a periodic sample */ int iCol; /* If !isPSample, the reason for inclusion */ u32 iHash; /* Tiebreaker hash */ @@ -295,22 +297,57 @@ struct Stat4Accum { */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 static void sampleClear(sqlite3 *db, Stat4Sample *p){ - sqlite3DbFree(db, p->aRowid); - p->aRowid = 0; - p->nRowid = 0; + assert( db!=0 ); + if( p->nRowid ){ + sqlite3DbFree(db, p->u.aRowid); + p->nRowid = 0; + } } #endif -/* Make a copy of the Stat4Sample.aRowid field. +/* Initialize the BLOB value of a ROWID */ #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 -static void sampleDup(sqlite3 *db, Stat4Sample *p){ - if( p->aRowid ){ - u8 *aCopy = sqlite3DbMallocRaw(db, p->nRowid); - if( aCopy ){ - memcpy(aCopy, p->aRowid, p->nRowid); - p->aRowid = aCopy; - } +static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){ + assert( db!=0 ); + if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid); + p->u.aRowid = sqlite3DbMallocRaw(db, n); + if( p->u.aRowid ){ + p->nRowid = n; + memcpy(p->u.aRowid, pData, n); + }else{ + p->nRowid = 0; + } +} +#endif + +/* Initialize the INTEGER value of a ROWID. +*/ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 +static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){ + assert( db!=0 ); + if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid); + p->nRowid = 0; + p->u.iRowid = iRowid; +} +#endif + + +/* +** Copy the contents of object (*pFrom) into (*pTo). +*/ +#ifdef SQLITE_ENABLE_STAT3_OR_STAT4 +static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){ + pTo->isPSample = pFrom->isPSample; + pTo->iCol = pFrom->iCol; + pTo->iHash = pFrom->iHash; + memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol); + memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol); + memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol); + if( pFrom->nRowid ){ + sampleSetRowid(p->db, pTo, pFrom->nRowid, pFrom->u.aRowid); + }else{ + sampleSetRowidInt64(p->db, pTo, pFrom->u.iRowid); } } #endif @@ -324,6 +361,7 @@ static void stat4Destructor(void *pOld){ int i; for(i=0; inCol; i++) sampleClear(p->db, p->aBest+i); for(i=0; imxSample; i++) sampleClear(p->db, p->a+i); + sampleClear(p->db, &p->current); #endif sqlite3DbFree(p->db, p); } @@ -483,23 +521,6 @@ static int sampleIsBetter( #endif } -/* -** Copy the contents of object (*pFrom) into (*pTo). -*/ -static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){ - pTo->iRowid = pFrom->iRowid; - pTo->isPSample = pFrom->isPSample; - pTo->iCol = pFrom->iCol; - pTo->iHash = pFrom->iHash; - sampleClear(p->db, pTo); - pTo->nRowid = pFrom->nRowid; - pTo->aRowid = pFrom->aRowid; - sampleDup(p->db, pTo); - memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol); - memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol); - memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol); -} - /* ** Copy the contents of sample *pNew into the p->a[] array. If necessary, ** remove the least desirable sample from p->a[] to make room. @@ -545,6 +566,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ tRowcnt *anEq = pMin->anEq; tRowcnt *anLt = pMin->anLt; tRowcnt *anDLt = pMin->anDLt; + sampleClear(p->db, pMin); memmove(pMin, &pMin[1], sizeof(p->a[0])*(p->nSample-p->iMin-1)); pSample = &p->a[p->nSample-1]; pSample->anEq = anEq; @@ -694,13 +716,10 @@ static void statPush( p->nRow++; #ifdef SQLITE_ENABLE_STAT3_OR_STAT4 if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){ - p->current.iRowid = sqlite3_value_int64(argv[2]); - p->current.aRowid = 0; - p->current.nRowid = 0; + sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2])); }else{ - p->current.iRowid = 0; - p->current.nRowid = sqlite3_value_bytes(argv[2]); - p->current.aRowid = (u8*)sqlite3_value_blob(argv[2]); + sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]), + sqlite3_value_blob(argv[2])); } p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; #endif @@ -827,9 +846,10 @@ static void statGet( if( p->iGetnSample ){ Stat4Sample *pS = p->a + p->iGet; if( pS->nRowid==0 ){ - sqlite3_result_int64(context, pS->iRowid); + sqlite3_result_int64(context, pS->u.iRowid); }else{ - sqlite3_result_blob(context, pS->aRowid, pS->nRowid, SQLITE_STATIC); + sqlite3_result_blob(context, pS->u.aRowid, pS->nRowid, + SQLITE_TRANSIENT); } } }else{ diff --git a/src/insert.c b/src/insert.c index 3cc88f96f6..a6cab3e899 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1463,7 +1463,10 @@ void sqlite3GenerateConstraintChecks( ** of a WITHOUT ROWID table and there has been no change the ** primary key, then no collision is possible. The collision detection ** logic below can all be skipped. */ - if( isUpdate && pPk && pkChng==0 ) continue; + if( isUpdate && pPk && pkChng==0 ){ + sqlite3VdbeResolveLabel(v, addrUniqueOk); + continue; + } /* Find out what action to take in case there is a uniqueness conflict */ onError = pIdx->onError; @@ -1498,38 +1501,40 @@ void sqlite3GenerateConstraintChecks( }else{ int x; /* Extract the PRIMARY KEY from the end of the index entry and - ** store it in register regR..regR+nPk-1 */ - for(i=0; inKeyCol; i++){ - x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); - sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); - VdbeComment((v, "%s.%s", pTab->zName, - pTab->aCol[pPk->aiColumn[i]].zName)); + ** store it in registers regR..regR+nPk-1 */ + if( isUpdate || onError==OE_Replace ){ + for(i=0; inKeyCol; i++){ + x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]); + sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i); + VdbeComment((v, "%s.%s", pTab->zName, + pTab->aCol[pPk->aiColumn[i]].zName)); + } } - if( pIdx->autoIndex==2 ){ - /* For a PRIMARY KEY index on a WITHOUT ROWID table, always conflict - ** on an INSERT. On an UPDATE, only conflict if the PRIMARY KEY - ** has changed. */ - if( isUpdate ){ + if( isUpdate ){ + if( pIdx->autoIndex==2 ){ + /* For a PRIMARY KEY index on a WITHOUT ROWID table, always conflict + ** on an INSERT. On an UPDATE, only conflict if the PRIMARY KEY + ** has changed. */ int addrPkConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; for(i=0; inKeyCol-1; i++){ x = pPk->aiColumn[i]; sqlite3VdbeAddOp3(v, OP_Ne, regOldData+1+x, - addrPkConflict, regIdx+x); + addrPkConflict, regIdx+i); } x = pPk->aiColumn[i]; sqlite3VdbeAddOp3(v, OP_Eq, regOldData+1+x, addrUniqueOk, regIdx+i); + }else{ + /* For a UNIQUE index on a WITHOUT ROWID table, conflict only if the + ** PRIMARY KEY value of the match is different from the old + ** PRIMARY KEY value from before the update. */ + int addrConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; + for(i=0; inKeyCol-1; i++){ + sqlite3VdbeAddOp3(v, OP_Ne, regOldData+pPk->aiColumn[i]+1, + addrConflict, regR+i); + } + sqlite3VdbeAddOp3(v, OP_Eq, regOldData+pPk->aiColumn[i]+1, + addrUniqueOk, regR+i); } - }else{ - /* For a UNIQUE index on a WITHOUT ROWID table, conflict only if the - ** PRIMARY KEY value of the match is different from the old PRIMARY KEY - ** value from before the update. */ - int addrConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol; - for(i=0; inKeyCol-1; i++){ - sqlite3VdbeAddOp3(v, OP_Ne, - regOldData+pPk->aiColumn[i]+1, addrConflict, regR+i); - } - sqlite3VdbeAddOp3(v, OP_Eq, - regOldData+pPk->aiColumn[i]+1, addrUniqueOk, regR+i); } } sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn); @@ -1827,8 +1832,8 @@ static int xferOptimization( int iDbSrc; /* The database of pSrc */ int iSrc, iDest; /* Cursors from source and destination */ int addr1, addr2; /* Loop addresses */ - int emptyDestTest; /* Address of test for empty pDest */ - int emptySrcTest; /* Address of test for empty pSrc */ + int emptyDestTest = 0; /* Address of test for empty pDest */ + int emptySrcTest = 0; /* Address of test for empty pSrc */ Vdbe *v; /* The VDBE we are building */ KeyInfo *pKey; /* Key information for an index */ int regAutoinc; /* Memory register used by AUTOINC */ @@ -2026,6 +2031,9 @@ static int xferOptimization( sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0); sqlite3VdbeAddOp2(v, OP_Close, iDest, 0); + }else{ + sqlite3TableLock(pParse, iDbDest, pDest->tnum, 1, pDest->zName); + sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName); } for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){ for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){ diff --git a/src/pragma.c b/src/pragma.c index 3871ad02a6..948d96056c 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -1903,7 +1903,7 @@ void sqlite3Pragma( r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3); sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1); /* increment entry count */ jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, 0, r1, - pIdx->nKeyCol+1); + pIdx->nColumn); sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, "row ", P4_STATIC); sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);