mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-10 01:02:56 +03:00
Progress toward getting UPDATE to work in WITHOUT ROWID tables.
FossilOrigin-Name: e557b7d80f1ede63427a31b16757bf5d8dbfb66d
This commit is contained in:
102
src/update.c
102
src/update.c
@@ -99,6 +99,7 @@ void sqlite3Update(
|
||||
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 */
|
||||
@@ -107,6 +108,7 @@ void sqlite3Update(
|
||||
** 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 */
|
||||
@@ -114,6 +116,8 @@ void sqlite3Update(
|
||||
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) */
|
||||
@@ -121,6 +125,7 @@ void sqlite3Update(
|
||||
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 */
|
||||
@@ -129,6 +134,7 @@ void sqlite3Update(
|
||||
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;
|
||||
@@ -179,6 +185,7 @@ void sqlite3Update(
|
||||
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));
|
||||
@@ -191,7 +198,7 @@ void sqlite3Update(
|
||||
** 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;
|
||||
@@ -201,13 +208,15 @@ void sqlite3Update(
|
||||
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;
|
||||
@@ -250,7 +259,7 @@ void sqlite3Update(
|
||||
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;
|
||||
@@ -313,24 +322,49 @@ void sqlite3Update(
|
||||
|
||||
/* 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);
|
||||
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 */
|
||||
|
||||
/* Remember the rowid of every item to be updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
|
||||
if( !okOnePass ){
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
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 ){
|
||||
@@ -345,7 +379,9 @@ void sqlite3Update(
|
||||
** 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{
|
||||
@@ -369,19 +405,22 @@ void sqlite3Update(
|
||||
}
|
||||
|
||||
/* 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
|
||||
@@ -528,8 +567,13 @@ void sqlite3Update(
|
||||
/* 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++){
|
||||
|
Reference in New Issue
Block a user