**
** The regRowid parameter is the index of the register containing (1).
**
-** If isUpdate is true and rowidChng is non-zero, then rowidChng contains
+** If isUpdate is true and pkChng is non-zero, then pkChng contains
** the address of a register containing the rowid before the update takes
** place. isUpdate is true for UPDATEs and false for INSERTs. If isUpdate
-** is false, indicating an INSERT statement, then a non-zero rowidChng
+** is false, indicating an INSERT statement, then a non-zero pkChng
** indicates that the rowid was explicitly specified as part of the
-** INSERT statement. If rowidChng is false, it means that the rowid is
+** INSERT statement. If pkChng is false, it means that the rowid is
** computed automatically in an insert or that the rowid value is not
** modified by an update.
**
-** The code generated by this routine store new index entries into
+** The code generated by this routine should store new index entries into
** registers identified by aRegIdx[]. No index entry is created for
** indices where aRegIdx[i]==0. The order of indices in aRegIdx[] is
** the same as the order of indices on the linked list of indices
void sqlite3GenerateConstraintChecks(
Parse *pParse, /* The parser context */
Table *pTab, /* the table into which we are inserting */
- int baseCur, /* Index of a read/write cursor pointing at pTab */
- int regRowid, /* Index of the range of input registers */
+ int baseCur, /* A read/write cursor pointing at pTab */
+ int regRowid, /* First register in a range holding values to insert */
int *aRegIdx, /* Register used by each index. 0 for unused indices */
- int rowidChng, /* True if the rowid might collide with existing entry */
+ int pkChng, /* Non-zero if the PRIMARY KEY might collide */
int isUpdate, /* True for UPDATE, False for INSERT */
int overrideError, /* Override onError to this if not OE_Default */
int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
Index *pIdx; /* Pointer to one of the indices */
sqlite3 *db; /* Database connection */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
- int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid;
+ int regOldRowid = (pkChng && isUpdate) ? pkChng : regRowid;
db = pParse->db;
v = sqlite3GetVdbe(pParse);
** of the new record does not previously exist. Except, if this
** is an UPDATE and the primary key is not changing, that is OK.
*/
- if( rowidChng ){
+ if( pkChng ){
onError = pTab->keyConf;
if( overrideError!=OE_Default ){
onError = overrideError;
}
if( isUpdate ){
- j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, rowidChng);
+ j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, pkChng);
}
j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid);
switch( onError ){
WhereInfo *pWInfo; /* Information about the WHERE clause */
Vdbe *v; /* The virtual database engine */
Index *pIdx; /* For looping over indices */
+ Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */
int nIdx; /* Number of indices that need updating */
int iCur; /* VDBE Cursor number of pTab */
sqlite3 *db; /* The database structure */
** an expression for the i-th column of the table.
** aXRef[i]==-1 if the i-th column is not changed. */
int chngRowid; /* True if the record number is being changed */
+ int chngPk; /* The PRIMARY KEY of a WITHOUT ROWID table changed */
Expr *pRowidExpr = 0; /* Expression defining the new record number */
int openAll = 0; /* True if all indices need to be opened */
AuthContext sContext; /* The authorization context */
int iDb; /* Database containing the table being updated */
int okOnePass; /* True for one-pass algorithm without the FIFO */
int hasFK; /* True if foreign key processing is required */
+ int labelBreak; /* Jump here to break out of UPDATE loop */
+ int labelContinue; /* Jump here to continue next step of UPDATE loop */
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True when updating a view (INSTEAD OF trigger) */
int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
#endif
int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */
+ int iEph = 0; /* Ephemeral table holding all primary key values */
/* Register Allocations */
int regRowCount = 0; /* A count of rows changed */
int regNew; /* Content of the NEW.* table in triggers */
int regOld = 0; /* Content of OLD.* table in triggers */
int regRowSet = 0; /* Rowset of rows to be updated */
+ int regKey = 0; /* composite PRIMARY KEY value */
memset(&sContext, 0, sizeof(sContext));
db = pParse->db;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
pParse->nTab++;
}
+ pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
/* Initialize the name-context */
memset(&sNC, 0, sizeof(sNC));
** column to be updated, make sure we have authorization to change
** that column.
*/
- chngRowid = 0;
+ chngPk = chngRowid = 0;
for(i=0; i<pChanges->nExpr; i++){
if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
goto update_cleanup;
if( j==pTab->iPKey ){
chngRowid = 1;
pRowidExpr = pChanges->a[i].pExpr;
+ }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){
+ chngPk = 1;
}
aXRef[j] = i;
break;
}
}
if( j>=pTab->nCol ){
- if( sqlite3IsRowid(pChanges->a[i].zName) ){
+ if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zName) ){
j = -1;
chngRowid = 1;
pRowidExpr = pChanges->a[i].pExpr;
reg = ++pParse->nMem;
}else{
reg = 0;
- for(i=0; i<pIdx->nKeyCol; i++){
+ for(i=0; i<pIdx->nColumn; i++){
if( aXRef[pIdx->aiColumn[i]]>=0 ){
reg = ++pParse->nMem;
break;
/* Begin the database scan
*/
- sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
- pWInfo = sqlite3WhereBegin(
- pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0
- );
- if( pWInfo==0 ) goto update_cleanup;
- okOnePass = sqlite3WhereOkOnePass(pWInfo);
-
- /* Remember the rowid of every item to be updated.
- */
- sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
- if( !okOnePass ){
- sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
+ pWInfo = sqlite3WhereBegin(
+ pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED, 0
+ );
+ if( pWInfo==0 ) goto update_cleanup;
+ okOnePass = sqlite3WhereOkOnePass(pWInfo);
+
+ /* Remember the rowid of every item to be updated.
+ */
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
+ if( !okOnePass ){
+ sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
+ }
+
+ /* End the database scan loop.
+ */
+ sqlite3WhereEnd(pWInfo);
+ }else{
+ int iPk; /* First of nPk memory cells holding PRIMARY KEY value */
+ i16 nPk; /* Number of components of the PRIMARY KEY */
+
+ assert( pPk!=0 );
+ nPk = pPk->nKeyCol;
+ iPk = pParse->nMem+1;
+ pParse->nMem += nPk;
+ regKey = ++pParse->nMem;
+ iEph = pParse->nTab++;
+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iEph, nPk, 0,
+ (char*)sqlite3IndexKeyinfo(pParse, pPk),
+ P4_KEYINFO_HANDOFF);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, 0, 0);
+ if( pWInfo==0 ) goto update_cleanup;
+ for(i=0; i<nPk; i++){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, pPk->aiColumn[i], iPk+i);
+ }
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
+ sqlite3IndexAffinityStr(v, pPk), P4_TRANSIENT);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
+ sqlite3WhereEnd(pWInfo);
+ okOnePass = 0;
}
- /* End the database scan loop.
- */
- sqlite3WhereEnd(pWInfo);
-
/* Initialize the count of updated rows
*/
if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
** action, then we need to open all indices because we might need
** to be deleting some records.
*/
- if( !okOnePass ) sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
+ if( !okOnePass && HasRowid(pTab) ){
+ sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
+ }
if( onError==OE_Replace ){
openAll = 1;
}else{
}
/* Top of the update loop */
- if( okOnePass ){
+ labelBreak = sqlite3VdbeMakeLabel(v);
+ labelContinue = sqlite3VdbeMakeLabel(v);
+ if( pPk ){
+ sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak);
+ addr = sqlite3VdbeAddOp2(v, OP_RowKey, iEph, regKey);
+ sqlite3VdbeAddOp3(v, OP_NotFound, iEph, labelContinue, regKey);
+ }else if( okOnePass ){
int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid);
- addr = sqlite3VdbeAddOp0(v, OP_Goto);
+ addr = sqlite3VdbeAddOp2(v, OP_Goto, 0, labelBreak);
sqlite3VdbeJumpHere(v, a1);
}else{
- addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
+ addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak,
+ regOldRowid);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
}
- /* Make cursor iCur point to the record that is being updated. If
- ** this record does not exist for some reason (deleted by a trigger,
- ** for example, then jump to the next iteration of the RowSet loop. */
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
-
/* If the record number will change, set register regNewRowid to
** contain the new value. If the record number is not being modified,
** then regNewRowid is the same register as regOldRowid, which is
/* Repeat the above with the next record to be updated, until
** all record selected by the WHERE clause have been updated.
*/
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
- sqlite3VdbeJumpHere(v, addr);
+ if( pPk ){
+ sqlite3VdbeResolveLabel(v, labelContinue);
+ sqlite3VdbeAddOp2(v, OP_Next, iEph, addr);
+ }else if( !okOnePass ){
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
+ }
+ sqlite3VdbeResolveLabel(v, labelBreak);
/* Close all tables */
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){