From: drh Date: Fri, 25 Oct 2013 19:17:17 +0000 (+0000) Subject: Progress toward getting UPDATE to work in WITHOUT ROWID tables. X-Git-Tag: version-3.8.2~137^2~56 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ea22abe3d68f6d93fdf7aa5bfd52892b46ab6aef;p=thirdparty%2Fsqlite.git Progress toward getting UPDATE to work in WITHOUT ROWID tables. FossilOrigin-Name: e557b7d80f1ede63427a31b16757bf5d8dbfb66d --- diff --git a/manifest b/manifest index 13d1feecb8..a6af22eaab 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Basic\sDELETE\soperations\snow\sworking\son\sWITHOUT\sROWID\stables. -D 2013-10-25T14:46:15.958 +C Progress\stoward\sgetting\sUPDATE\sto\swork\sin\sWITHOUT\sROWID\stables. +D 2013-10-25T19:17:17.511 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 0522b53cdc1fcfc18f3a98e0246add129136c654 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -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 71b3d1f551ab8e0ba59fdbaff3feef8d931a424e +F src/insert.c 976fc5c9b3ab32995b22f87ba3be52d11c9e50b9 F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b @@ -275,7 +275,7 @@ F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/tokenize.c 70061085a51f2f4fc15ece94f32c03bcb78e63b2 F src/trigger.c ba0a883cd536b7dfdd4df3733001f5372a4299da -F src/update.c 2bb5a267796e6d0177ef5689487c3688de5c309e +F src/update.c e39378bc5ed0c42e80624229703e59b5c7a4d50a F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269 F src/util.c 2fa6c821d28bbdbeec1b2a7b091a281c9ef8f918 F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179 @@ -1127,7 +1127,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 85daf5174679630474b3bccf3d662d5d56bf00d5 -R 98e4cedace27c103667612897f7b7404 +P 9eafafa31c4a2bbcf48418743e0fcb17c374e9a6 +R bcff8223a4c38fdc4478f7b14868c2a5 U drh -Z b296ee4a843fc7eab50d30ebcbf09f32 +Z 45e2a3e25d194ae58eddf07aeb33a8d9 diff --git a/manifest.uuid b/manifest.uuid index ddaf3cea50..e746b047bf 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9eafafa31c4a2bbcf48418743e0fcb17c374e9a6 \ No newline at end of file +e557b7d80f1ede63427a31b16757bf5d8dbfb66d \ No newline at end of file diff --git a/src/insert.c b/src/insert.c index c3d3af7235..d3d1102b85 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1145,16 +1145,16 @@ insert_cleanup: ** ** 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 @@ -1208,10 +1208,10 @@ insert_cleanup: 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 */ @@ -1228,7 +1228,7 @@ void sqlite3GenerateConstraintChecks( 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); @@ -1314,7 +1314,7 @@ void sqlite3GenerateConstraintChecks( ** 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; @@ -1323,7 +1323,7 @@ void sqlite3GenerateConstraintChecks( } 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 ){ diff --git a/src/update.c b/src/update.c index 9a4f374fa9..7724b666a8 100644 --- a/src/update.c +++ b/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; inExpr; 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; inKeyCol; i++){ + for(i=0; inColumn; 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); - - /* 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; iaiColumn[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++){