Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -1959,11 +1959,13 @@ ** erasing iTable (this can happen with an auto-vacuum database). */ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ Vdbe *v = sqlite4GetVdbe(pParse); sqlite4VdbeAddOp2(v, OP_Clear, iTable, iDb); +#if 0 sqlite4MayAbort(pParse); +#endif } /* ** Write VDBE code to erase table pTab and all associated indices on disk. ** Code to update the sqlite_master tables and internal schema definitions Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -1213,10 +1213,38 @@ iPk++; } if( piPk ) *piPk = iPk; return p; } + +/* +** Index pIdx is a UNIQUE index. This function returns a pointer to a buffer +** containing an error message to tell the user that the UNIQUE constraint +** has failed. +** +** The returned buffer should be freed by the caller using sqlite4DbFree(). +*/ +static char *notUniqueMessage( + Parse *pParse, /* Parse context */ + Index *pIdx /* Index to generate error message for */ +){ + const int nCol = pIdx->nColumn; /* Number of columns indexed by pIdx */ + StrAccum errMsg; /* Buffer to build error message within */ + int iCol; /* Used to iterate through indexed columns */ + + sqlite4StrAccumInit(&errMsg, 0, 0, 200); + errMsg.db = pParse->db; + sqlite4StrAccumAppend(&errMsg, (nCol>1 ? "columns " : "column "), -1); + for(iCol=0; iColnColumn; iCol++){ + const char *zCol = indexColumnName(pIdx, iCol); + sqlite4StrAccumAppend(&errMsg, (iCol==0 ? "" : ", "), -1); + sqlite4StrAccumAppend(&errMsg, zCol, -1); + } + sqlite4StrAccumAppend(&errMsg, (nCol>1 ? " are" : " is"), -1); + sqlite4StrAccumAppend(&errMsg, " not unique", -1); + return sqlite4StrAccumFinish(&errMsg); +} /* ** This function generates code used as part of both INSERT and UPDATE ** statements. The generated code performs two tasks: ** @@ -1250,12 +1278,12 @@ ** PK index) attached to the database table. Entries are in the same order ** as the linked list of Index structures attached to the table. ** ** If an array entry is non-zero, it contains the register that the ** corresponding index key should be written to. If an entry is zero, then -** the corresponding index key is not required by the caller and that any -** UNIQUE enforced by the index does not need to be checked. +** the corresponding index key is not required by the caller. In this case +** any UNIQUE constraint enforced by the index does not need to be checked. ** ** ** ** Generate code to do constraint checks prior to an INSERT or an UPDATE. ** @@ -1343,10 +1371,12 @@ int overrideError, /* Override onError to this if not OE_Default */ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */ int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */ ){ + u8 aPkRoot[10]; /* Root page number for pPk as a varint */ + int nPkRoot; /* Size of aPkRoot in bytes */ Index *pPk; /* Primary key index for table pTab */ int i; /* loop counter */ Vdbe *v; /* VDBE under constrution */ int nCol; /* Number of columns */ int onError; /* Conflict resolution strategy */ @@ -1357,10 +1387,11 @@ v = sqlite4GetVdbe(pParse); assert( v!=0 ); assert( pTab->pSelect==0 ); /* This table is not a VIEW */ nCol = pTab->nCol; pPk = sqlite4FindPrimaryKey(pTab, 0); + nPkRoot = sqlite4PutVarint64(aPkRoot, pPk->tnum); assert( pPk->eIndexType==SQLITE_INDEX_PRIMARYKEY ); /* Test all NOT NULL constraints. */ generateNotNullChecks(pParse, pTab, regContent, overrideError, ignoreDest); @@ -1373,20 +1404,21 @@ ** Add the new records to the indices as we go. */ for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ int nTmpReg; /* Number of temp registers required */ int regTmp; /* First temp register allocated */ - int regShort; /* Reg. for number of bytes in short key */ + int regPk; /* PK of conflicting row (for REPLACE) */ if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */ /* Create an index key. Primary key indexes consists of just the primary ** key values. Other indexes consists of the indexed columns followed by ** the primary key values. */ nTmpReg = 1 + pIdx->nColumn + (pIdx==pPk ? 0 : pPk->nColumn); regTmp = sqlite4GetTempRange(pParse, nTmpReg); - regShort = regTmp + nTmpReg - 1; + regPk = regTmp + nTmpReg - 1; + for(i=0; inColumn; i++){ int idx = pIdx->aiColumn[i]; sqlite4VdbeAddOp2(v, OP_SCopy, regContent+idx, regTmp+i); } if( pIdx!=pPk ){ @@ -1394,23 +1426,20 @@ int idx = pPk->aiColumn[i]; sqlite4VdbeAddOp2(v, OP_SCopy, regContent+idx, regTmp+i+pIdx->nColumn); } } sqlite4VdbeAddOp3(v, OP_MakeIdxKey, baseCur+iCur, regTmp, aRegIdx[iCur]); - sqlite4VdbeChangeP4(v, -1, (const char *)regShort, P4_INT32); VdbeComment((v, "key for %s", pIdx->zName)); /* If Index.onError==OE_None, then pIdx is not a UNIQUE or PRIMARY KEY ** index. In this case there is no need to test the index for uniqueness ** - all that is required is to populate the aRegIdx[iCur] register. Jump ** to the next iteration of the loop if this is the case. */ onError = pIdx->onError; if( onError!=OE_None ){ int iTest; /* Address of OP_IsUnique instruction */ - - iTest = sqlite4VdbeAddOp3(v, OP_IsUnique, baseCur+iCur, 0, aRegIdx[iCur]); - sqlite4VdbeChangeP4(v, -1, (const char *)regShort, P4_INT32); + int regOut = 0; /* Figure out what to do if a UNIQUE constraint is encountered. ** ** TODO: If a previous constraint is a REPLACE, why change IGNORE to ** REPLACE and FAIL to ABORT here? */ @@ -1421,55 +1450,43 @@ } if( seenReplace ){ if( onError==OE_Ignore ) onError = OE_Replace; else if( onError==OE_Fail ) onError = OE_Abort; } + + if( onError==OE_Replace ){ + sqlite4VdbeAddOp3(v, OP_Blob, nPkRoot, regPk, 0); + sqlite4VdbeChangeP4(v, -1, aPkRoot, nPkRoot); + regOut = regPk; + } + iTest = sqlite4VdbeAddOp3(v, OP_IsUnique, baseCur+iCur, 0, aRegIdx[iCur]); + sqlite4VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(regOut), P4_INT32); switch( onError ){ case OE_Rollback: case OE_Abort: case OE_Fail: { - int j; - StrAccum errMsg; - const char *zSep; - char *zErr; - - sqlite4StrAccumInit(&errMsg, 0, 0, 200); - errMsg.db = pParse->db; - zSep = pIdx->nColumn>1 ? "columns " : "column "; - for(j=0; jnColumn; j++){ - const char *zCol = indexColumnName(pIdx, j); - sqlite4StrAccumAppend(&errMsg, zSep, -1); - zSep = ", "; - sqlite4StrAccumAppend(&errMsg, zCol, -1); - } - sqlite4StrAccumAppend(&errMsg, - pIdx->nColumn>1 ? " are not unique" : " is not unique", -1); - zErr = sqlite4StrAccumFinish(&errMsg); + char *zErr = notUniqueMessage(pParse, pIdx); sqlite4HaltConstraint(pParse, onError, zErr, 0); - sqlite4DbFree(errMsg.db, zErr); + sqlite4DbFree(pParse->db, zErr); break; } + case OE_Ignore: { assert( seenReplace==0 ); sqlite4VdbeAddOp2(v, OP_Goto, 0, ignoreDest); break; } default: { - assert( 0 ); -#if 0 - Trigger *pTrigger = 0; + Trigger *pTrigger; assert( onError==OE_Replace ); sqlite4MultiWrite(pParse); - if( pParse->db->flags&SQLITE_RecTriggers ){ - pTrigger = sqlite4TriggersExist(pParse, pTab, TK_DELETE, 0, 0); - } + pTrigger = sqlite4TriggersExist(pParse, pTab, TK_DELETE, 0, 0); sqlite4GenerateRowDelete( - pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace + pParse, pTab, baseCur, regOut, 0, pTrigger, OE_Replace ); seenReplace = 1; -#endif break; } } /* If the OP_IsUnique passes (no constraint violation) jump here */ Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -2984,32 +2984,45 @@ /* Opcode: IsUnique P1 P2 P3 P4 * ** ** Cursor P1 is open on an index that enforces a UNIQUE constraint. ** Register P3 contains an encoded key suitable to be inserted into the -** index. -** -** Jump to instruction P2 if the encoded key can be inserted into the -** index without violating a unique constraint. Otherwise, fall through +** index. If the key can be inserted into the index without violating +** a UNIQUE constraint, jump to instruction P2. Otherwise, fall through ** to the next instruction. +** +** If P4 is a non-zero integer and the jump is not taken, then it is +** a register that currently contains a blob. At the start of the blob +** is a varint that contains the index number for the PRIMARY KEY index +** of the table. The contents of P4 are overwritten with an index key +** composed of the varint from the start of the initial blob content +** and the PRIMARY KEY values from the index entry causing the UNIQUE +** constraint to fail. */ case OP_IsUnique: { /* jump, in3 */ VdbeCursor *pC; Mem *pShort; Mem *pProbe; + Mem *pOut; + int iOut; int nShort; int dir; + u64 dummy; KVByteArray *aKey; /* Key read from cursor */ KVSize nKey; /* Size of aKey in bytes */ assert( pOp->p4type==P4_INT32 ); pProbe = &aMem[pOp->p3]; - pShort = &aMem[pOp->p4.i]; - nShort = pShort->u.i; pC = p->apCsr[pOp->p1]; + pOut = (pOp->p4.i==0 ? 0 : &aMem[pOp->p4.i]); + assert( pOut==0 || (pOut->flags & MEM_Blob) ); + + nShort = sqlite4VdbeShortKey(pProbe->z, pProbe->n, + pC->pKeyInfo->nField - pC->pKeyInfo->nPK + ); assert( nShort<=pProbe->n ); assert( (nShort==pProbe->n)==(pC->pKeyInfo->nPK==0) ); dir = (nShort < pProbe->n); rc = sqlite4KVCursorSeek(pC->pKVCur, pProbe->z, nShort, dir); @@ -3024,10 +3037,17 @@ if( nKeyz, aKey, nShort) || (nKey==pProbe->n && 0==memcmp(pProbe->z, aKey, nKey)) ){ pc = pOp->p2-1; + }else if( pOut ){ + iOut = sqlite4GetVarint64(pOut->z, pOut->n, &dummy); + rc = sqlite4VdbeMemGrow(pOut, iOut+(pProbe->n - nShort), 1); + if( rc==SQLITE_OK ){ + memcpy(&pOut->z[iOut], &aKey[nShort], (pProbe->n - nShort)); + pOut->n = iOut + (pProbe->n - nShort); + } } } } #if 0 Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -388,10 +388,11 @@ int *pnOut, /* Number of bytes in the key */ int *pnShort /* Number of bytes omitting primary key */ ); int sqlite4VdbeEncodeIntKey(u8 *aBuf,sqlite4_int64 v); int sqlite4VdbeDecodeIntKey(const KVByteArray*, KVSize, sqlite4_int64*); +int sqlite4VdbeShortKey(u8 *, int, int); int sqlite4MemCompare(const Mem*, const Mem*, const CollSeq*); int sqlite4VdbeExec(Vdbe*); int sqlite4VdbeList(Vdbe*); int sqlite4VdbeHalt(Vdbe*); int sqlite4VdbeChangeEncoding(Mem *, int); Index: src/vdbecodec.c ================================================================== --- src/vdbecodec.c +++ src/vdbecodec.c @@ -528,11 +528,11 @@ if( e<=10 ) p->aOut[i] = 0x17+e; } }else if( flags & MEM_Str ){ if( enlargeEncoderAllocation(p, pMem->n*4 + 2) ) return SQLITE_NOMEM; - p->aOut[p->nOut++] = 0x0e; /* Text */ + p->aOut[p->nOut++] = 0x24; /* Text */ if( pColl==0 || pColl->xMkKey==0 ){ memcpy(p->aOut+p->nOut, pMem->z, pMem->n); p->nOut += pMem->n; }else{ n = pColl->xMkKey(pColl->pUser, pMem->z, pMem->n, p->aOut+p->nOut, @@ -552,11 +552,11 @@ n = pMem->n; a = (u8*)pMem->z; s = 1; t = 0; if( enlargeEncoderAllocation(p, (n*8+6)/7 + 2) ) return SQLITE_NOMEM; - p->aOut[p->nOut++] = 0x0f; /* Blob */ + p->aOut[p->nOut++] = 0x25; /* Blob */ for(i=0; iaOut[p->nOut++] = 0x80 | t | (x>>s); if( s<7 ){ t = x<<(7-s); @@ -573,10 +573,59 @@ if( sortOrder==SQLITE_SO_DESC ){ for(i=iStart; inOut; i++) p->aOut[i] ^= 0xff; } return SQLITE_OK; } + +/* +** Variables aKey/nKey contain an encoded index key. This function returns +** the length (in bytes) of the key with all but the first nField fields +** removed. +*/ +int sqlite4VdbeShortKey( + u8 *aKey, /* Buffer containing encoded key */ + int nKey, /* Size of buffer aKey[] in bytes */ + int nField /* Number of fields */ +){ + u8 *p = aKey; + u8 *pEnd = &aKey[nKey]; + u64 dummy; + int i; + + p = aKey; + p += sqlite4GetVarint64(p, pEnd-p, &dummy); + + for(i=0; i