]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Change the interface to sqlite3GenerateConstraintChecks() for improved lucidity
authordrh <drh@noemail.net>
Fri, 1 Nov 2013 17:08:56 +0000 (17:08 +0000)
committerdrh <drh@noemail.net>
Fri, 1 Nov 2013 17:08:56 +0000 (17:08 +0000)
and to fix issues in dealing with UPDATEs for WITHOUT ROWID tables.  Make sure
iDataCur and iIdxCur are initialized when processing DELETEs of a VIEW.
UPDATE processing distinguishes between changes to ROWID and PRIMARY KEY.

FossilOrigin-Name: c525ac5630d6bcd51842cfc84f2c2c50be9cec1c

manifest
manifest.uuid
src/delete.c
src/insert.c
src/sqliteInt.h
src/update.c
test/without_rowid1.test

index 9d280e18497d7ba41f9639adbd6b30e455510d0a..6a9c5d36babea2993b8be9150dda8b1a99dd0663 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improved\sVDBE\scomments\son\sthe\sconstraint\schecker.\s\sFix\sa\smissing\swrite\nlock\sin\sthe\sUPDATE\slogic.
-D 2013-11-01T14:03:20.023
+C Change\sthe\sinterface\sto\ssqlite3GenerateConstraintChecks()\sfor\simproved\slucidity\nand\sto\sfix\sissues\sin\sdealing\swith\sUPDATEs\sfor\sWITHOUT\sROWID\stables.\s\sMake\ssure\niDataCur\sand\siIdxCur\sare\sinitialized\swhen\sprocessing\sDELETEs\sof\sa\sVIEW.\nUPDATE\sprocessing\sdistinguishes\sbetween\schanges\sto\sROWID\sand\sPRIMARY\sKEY.
+D 2013-11-01T17:08:56.112
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 0522b53cdc1fcfc18f3a98e0246add129136c654
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -173,7 +173,7 @@ F src/callback.c f99a8957ba2adf369645fac0db09ad8adcf1caa2
 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
 F src/ctime.c ea4b7f3623a0fcb1146e7f245d7410033e86859c
 F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
-F src/delete.c df1775ee33f40bc8feb37b2bfb59d792ff496359
+F src/delete.c c58e07fb07878c4b4c4b106cee7f2799a48351f8
 F src/expr.c ecc2b98eb75fe5533cfdfcca6b04cfe5f0c6001f
 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
 F src/fkey.c 628f81177299660a86e40359b3689b81f517e125
@@ -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 da74aded06680ff30a87065df70fda16af3ecda3
+F src/insert.c 4694b23067650659f822d989426116808c0c8e05
 F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
 F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
@@ -223,7 +223,7 @@ F src/shell.c d5eebdc6034014103de2b9d58e1d3f6f7de0fb50
 F src/sqlite.h.in 547a44dd4ff4d975e92a645ea2d609e543a83d0f
 F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
 F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
-F src/sqliteInt.h 69c16a6a7f45fbe5d0293b753fe8022bef6b1f09
+F src/sqliteInt.h bc4588b0d2eac8429d102609af6cfad583bfb41f
 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
 F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -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 53d6b5d50b3b23d4fcd0a36504feb5cff9aed716
-F src/update.c fff9ac57e36e54ac939e22aac077326224759372
+F src/update.c 94d63d3e06b09df3618655a841dc95d5b9466dc6
 F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
 F src/util.c 2fa6c821d28bbdbeec1b2a7b091a281c9ef8f918
 F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
@@ -1076,7 +1076,7 @@ F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
 F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
 F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
 F test/win32longpath.test e2aafc07e6990fe86c69be22a3d1a0e210cd329b
-F test/without_rowid1.test e4f023e49a57c90f6d25afd4b3e8b0305ad06e44
+F test/without_rowid1.test ad5764bbe2784a91f7106143e7d1bd8ccfb61ffd
 F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
 F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd
 F tool/build-all-msvc.bat 38623a30fd58288fda5cc7f7df2682aaab75c9d5 x
@@ -1128,7 +1128,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 5c0eaea6a26b5c3310d96b3c896ac3068a3ebad1
-R 91e4e19c708f99c7ec09052b49466395
+P 3bed599e74d354bf1513e4fb0e8665376ba44d0b
+R 67b4771869ffd7159304bf29f7d68635
 U drh
-Z 7a82973f52b628c87c749d18c8301e86
+Z f06e68ba1ad92e0ce7abe5fdcd557daa
index cbe70d3dffc1961cb9f8cdb6e0b91199f73609b0..765f0dea3af117de48d55e08bae5d2b37b4d816a 100644 (file)
@@ -1 +1 @@
-3bed599e74d354bf1513e4fb0e8665376ba44d0b
\ No newline at end of file
+c525ac5630d6bcd51842cfc84f2c2c50be9cec1c
\ No newline at end of file
index 55688409b81caa3bd5089f97a1289f9887cec394..5d7078a4bbba6cae31aab492931667563f43cf83 100644 (file)
@@ -324,6 +324,7 @@ void sqlite3DeleteFrom(
 #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
   if( isView ){
     sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
+    iDataCur = iIdxCur = iTabCur;
   }
 #endif
 
index 3c9402ddd37a4f5924ac1d5000c19c4742e740a2..e4f8ead5852195d8eb10c7f06f3b5accdf639ff2 100644 (file)
@@ -1039,8 +1039,8 @@ void sqlite3Insert(
 #endif
     {
       int isReplace;    /* Set to true if constraints may cause a replace */
-      sqlite3GenerateConstraintChecks(pParse, pTab, iDataCur, iIdxCur,
-          regIns, aRegIdx, ipkColumn>=0, 0, onError, endOfLoop, &isReplace
+      sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
+          regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace
       );
       sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
       sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
@@ -1134,22 +1134,22 @@ insert_cleanup:
 ** contain the content of the first table column.  The third register will
 ** contain the content of the second table column.  And so forth.
 **
-** For an UPDATE (isUpdate!=0), if pkChng is non-zero then it contains
-** the address of a range of registers containing the rowid and table
-** data from before the change.  In other words, pkChng is like 
-** regNewData except that it describes the row before the update rather
-** than afterwards.  If pkChng is zero, that means that the rowid does 
-** not change (for a normal rowid table) or the PRIMARY KEY does not
-** change (for a WITHOUT ROWID table) in which case the old data is
-** not needed.
+** The regOldData parameter is similar to regNewData except that it contains
+** the data prior to an UPDATE rather than afterwards.  regOldData is zero
+** for an INSERT.  This routine can distinguish between UPDATE and INSERT by
+** checking regOldData for zero.
 **
-** For an INSERT (isUpdate==0), pkChng is just a boolean that indicates
-** whether or not the rowid was explicitly specified as part of the
-** INSERT statement.  If pkChng is zero, it means that the either rowid
-** is computed automatically or that the table is a WITHOUT ROWID table
-** and has no rowid.  On an INSERT, pkChng will only be true if the
-** INSERT statement provides an integer value for either the rowid
-** column or its INTEGER PRIMARY KEY alias.
+** For an UPDATE, the pkChng boolean is true if the true primary key (the
+** rowid for a normal table or the PRIMARY KEY for a WITHOUT ROWID table)
+** might be modified by the UPDATE.  If pkChng is false, then the key of
+** the iDataCur content table is guaranteed to be unchanged by the UPDATE.
+**
+** For an INSERT, the pkChng boolean indicates whether or not the rowid
+** was explicitly specified as part of the INSERT statement.  If pkChng
+** is zero, it means that the either rowid is computed automatically or
+** that the table is a WITHOUT ROWID table and has no rowid.  On an INSERT,
+** pkChng will only be true if the INSERT statement provides an integer
+** value for either the rowid column or its INTEGER PRIMARY KEY alias.
 **
 ** The code generated by this routine will store new index entries into
 ** registers identified by aRegIdx[].  No index entry is created for
@@ -1208,30 +1208,30 @@ insert_cleanup:
 void sqlite3GenerateConstraintChecks(
   Parse *pParse,       /* The parser context */
   Table *pTab,         /* The table being inserted or updated */
+  int *aRegIdx,        /* Use register aRegIdx[i] for index i.  0 for unused */
   int iDataCur,        /* Canonical data cursor (main table or PK index) */
   int iIdxCur,         /* First index cursor */
   int regNewData,      /* First register in a range holding values to insert */
-  int *aRegIdx,        /* Register used by each index.  0 for unused indices */
-  int pkChng,          /* Non-zero if the rowid or PRIMARY KEY changed */
-  int isUpdate,        /* True for UPDATE, False for INSERT */
-  int overrideError,   /* Override onError to this if not OE_Default */
+  int regOldData,      /* Previous content.  0 for INSERTs */
+  u8 pkChng,           /* Non-zero if the rowid or PRIMARY KEY changed */
+  u8 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 */
 ){
-  int i;               /* loop counter */
   Vdbe *v;             /* VDBE under constrution */
-  int nCol;            /* Number of columns */
-  int onError;         /* Conflict resolution strategy */
-  int j1;              /* Addresss of jump instruction */
-  int ix;              /* Index loop counter */
   Index *pIdx;         /* Pointer to one of the indices */
   Index *pPk = 0;      /* The PRIMARY KEY index */
   sqlite3 *db;         /* Database connection */
+  int i;               /* loop counter */
+  int ix;              /* Index loop counter */
+  int nCol;            /* Number of columns */
+  int onError;         /* Conflict resolution strategy */
+  int j1;              /* Addresss of jump instruction */
   int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
-  int regOldData;      /* Previous rowid and table data */
   int nPkField;        /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
+  u8 isUpdate;
 
-  regOldData = (pkChng && isUpdate) ? pkChng : regNewData;
+  isUpdate = regOldData!=0;
   db = pParse->db;
   v = sqlite3GetVdbe(pParse);
   assert( v!=0 );
@@ -1328,16 +1328,13 @@ void sqlite3GenerateConstraintChecks(
   }
 #endif /* !defined(SQLITE_OMIT_CHECK) */
 
-  /* If there is an INTEGER PRIMARY KEY, make sure the primary key
-  ** of the new record does not previously exist.  Except, if this
-  ** is an UPDATE and the primary key is not changing, then obviously
-  ** it is OK for the previous rowid to exist in that case.
-  **
-  ** This block only runs for tables that have a rowid.
+  /* If rowid is changing, make sure the new rowid does not previously
+  ** exist in the table.
   */
   if( pkChng && pPk==0 ){
     int addrRowidOk = sqlite3VdbeMakeLabel(v);
 
+    /* Figure out what action to take in case of a rowid collision */
     onError = pTab->keyConf;
     if( overrideError!=OE_Default ){
       onError = overrideError;
@@ -1346,9 +1343,17 @@ void sqlite3GenerateConstraintChecks(
     }
 
     if( isUpdate ){
+      /* pkChng!=0 does not mean that the rowid has change, only that
+      ** it might have changed.  Skip the conflict logic below if the rowid
+      ** is unchanged. */
       sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData);
     }
+
+    /* Check to see if the new rowid already exists in the table.  Skip
+    ** the following conflict logic if it does not. */
     sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData);
+
+    /* Generate code that deals with a rowid collision */
     switch( onError ){
       default: {
         onError = OE_Abort;
@@ -1411,6 +1416,9 @@ void sqlite3GenerateConstraintChecks(
   /* Test all UNIQUE constraints by creating entries for each UNIQUE
   ** index and making sure that duplicate entries do not already exist.
   ** Compute the revised record entries for indices as we go.
+  **
+  ** This loop also handles the case of the PRIMARY KEY index for a
+  ** WITHOUT ROWID table.
   */
   for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){
     int regIdx;          /* Range of registers hold conent for pIdx */
@@ -1422,6 +1430,7 @@ void sqlite3GenerateConstraintChecks(
     iThisCur = iIdxCur+ix;
     addrUniqueOk = sqlite3VdbeMakeLabel(v);
 
+    /* Skip partial indices for which the WHERE clause is not true */
     if( pIdx->pPartIdxWhere ){
       sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]);
       pParse->ckBase = regNewData+1;
@@ -1431,7 +1440,8 @@ void sqlite3GenerateConstraintChecks(
     }
 
     /* Create a record for this index entry as it should appear after
-    ** the insert or update. */
+    ** the insert or update.  Store that record in the aRegIdx[ix] register
+    */
     regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
     for(i=0; i<pIdx->nColumn; i++){
       int iField = pIdx->aiColumn[i];
@@ -1449,6 +1459,12 @@ void sqlite3GenerateConstraintChecks(
     VdbeComment((v, "for %s", pIdx->zName));
     sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn);
 
+    /* In an UPDATE operation, if this index is the PRIMARY KEY index 
+    ** 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;
+
     /* Find out what action to take in case there is a uniqueness conflict */
     onError = pIdx->onError;
     if( onError==OE_None ){ 
@@ -1470,11 +1486,15 @@ void sqlite3GenerateConstraintChecks(
     regR = sqlite3GetTempRange(pParse, nPkField);
     sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
                          regIdx, pIdx->nKeyCol);
+
+    /* Generate code to handle collisions */
     if( HasRowid(pTab) ){
       sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR);
       /* Conflict only if the rowid of the existing index entry
       ** is different from old-rowid */
-      sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData);
+      if( isUpdate ){
+        sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData);
+      }
     }else{
       int x;
       /* Extract the PRIMARY KEY from the end of the index entry and
index 07d0f3bdf79ef9d647d534a5af911f832a595f49..97b75f16b245f968e1a875410596378bb44b8d8b 100644 (file)
@@ -2922,8 +2922,8 @@ int sqlite3IsRowid(const char*);
 void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,int,i16,u8,u8);
 void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*);
 int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*);
-void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,int,
-                                     int*,int,int,int,int,int*);
+void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
+                                     u8,u8,int,int*);
 void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int);
 int sqlite3OpenTableAndIndices(Parse*, Table*, int, int, int*, int*);
 void sqlite3BeginWriteOperation(Parse*, int, int);
index 9efb7349d7183ee068fa62ff07cca817c03dc17e..a631101698f7a920daf40fd31fb8c8157ed82725 100644 (file)
@@ -108,7 +108,9 @@ void sqlite3Update(
   int *aXRef = 0;        /* aXRef[i] is the index in pChanges->a[] of the
                          ** an expression for the i-th column of the table.
                          ** aXRef[i]==-1 if the i-th column is not changed. */
-  int chngPk;            /* True if the rowid or PRIMARY KEY is changed */
+  u8 chngPk;             /* PRIMARY KEY changed in a WITHOUT ROWID table */
+  u8 chngRowid;          /* Rowid changed in a normal table */
+  u8 chngKey;            /* Either chngPk or chngRowid */
   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 */
@@ -201,11 +203,9 @@ void sqlite3Update(
   ** of the UPDATE statement.  Also find the column index
   ** for each column to be updated in the pChanges array.  For each
   ** column to be updated, make sure we have authorization to change
-  ** that column.  Set chngPk if the iDataCur key changes.  Note that
-  ** for WITHOUT ROWID columns, the iDataCur key contains all columns of
-  ** the table and so it will always change.
+  ** that column.
   */
-  chngPk = (pPk!=0);
+  chngRowid = chngPk = 0;
   for(i=0; i<pChanges->nExpr; i++){
     if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
       goto update_cleanup;
@@ -213,8 +213,10 @@ void sqlite3Update(
     for(j=0; j<pTab->nCol; j++){
       if( sqlite3StrICmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){
         if( j==pTab->iPKey ){
-          chngPk = 1;
+          chngRowid = 1;
           pRowidExpr = pChanges->a[i].pExpr;
+        }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){
+          chngPk = 1;
         }
         aXRef[j] = i;
         break;
@@ -223,7 +225,7 @@ void sqlite3Update(
     if( j>=pTab->nCol ){
       if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zName) ){
         j = -1;
-        chngPk = 1;
+        chngRowid = 1;
         pRowidExpr = pChanges->a[i].pExpr;
       }else{
         sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName);
@@ -245,8 +247,12 @@ void sqlite3Update(
     }
 #endif
   }
+  assert( (chngRowid & chngPk)==0 );
+  assert( chngRowid==0 || chngRowid==1 );
+  assert( chngPk==0 || chngPk==1 );
+  chngKey = chngRowid + chngPk;
 
-  hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngPk);
+  hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey);
 
   /* Allocate memory for the array aRegIdx[].  There is one entry in the
   ** array for each index associated with table being updated.  Fill in
@@ -259,11 +265,11 @@ void sqlite3Update(
   }
   for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
     int reg;
-    if( hasFK || chngPk || pIdx->pPartIdxWhere ){
+    if( chngKey || hasFK || pIdx->pPartIdxWhere || pIdx==pPk ){
       reg = ++pParse->nMem;
     }else{
       reg = 0;
-      for(i=0; i<pIdx->nColumn; i++){
+      for(i=0; i<pIdx->nKeyCol; i++){
         if( aXRef[pIdx->aiColumn[i]]>=0 ){
           reg = ++pParse->nMem;
           break;
@@ -293,11 +299,11 @@ void sqlite3Update(
   /* Allocate required registers. */
   regRowSet = ++pParse->nMem;
   regOldRowid = regNewRowid = ++pParse->nMem;
-  if( pTrigger || hasFK ){
+  if( chngPk || pTrigger || hasFK ){
     regOld = pParse->nMem + 1;
     pParse->nMem += pTab->nCol;
   }
-  if( chngPk || pTrigger || hasFK ){
+  if( chngKey || pTrigger || hasFK ){
     regNewRowid = ++pParse->nMem;
   }
   regNew = pParse->nMem + 1;
@@ -432,27 +438,30 @@ void sqlite3Update(
   ** contain the new value. If the record number is not being modified,
   ** then regNewRowid is the same register as regOldRowid, which is
   ** already populated.  */
-  assert( chngPk || pTrigger || hasFK || regOldRowid==regNewRowid );
-  if( chngPk && pPk==0 ){
+  assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid );
+  if( chngRowid ){
     sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
     sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
   }
 
-  /* If there are triggers on this table, populate an array of registers 
-  ** with the required old.* column data.  */
-  if( hasFK || pTrigger ){
+  /* Compute the old pre-UPDATE content of the row being changed, if that
+  ** information is needed */
+  if( chngPk || hasFK || pTrigger ){
     u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
     oldmask |= sqlite3TriggerColmask(pParse, 
         pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError
     );
     for(i=0; i<pTab->nCol; i++){
-      if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){
+      if( oldmask==0xffffffff
+       || (i<32 && (oldmask & (1<<i)))
+       || (chngPk && (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0)
+      ){
         sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regOld+i);
       }else{
         sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
       }
     }
-    if( chngPk==0 ){
+    if( chngRowid==0 && pPk==0 ){
       sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
     }
   }
@@ -509,7 +518,11 @@ void sqlite3Update(
     ** is deleted or renamed by a BEFORE trigger - is left undefined in the
     ** documentation.
     */
-    sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addr, regOldRowid);
+    if( pPk ){
+      sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
+    }else{
+      sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addr, regOldRowid);
+    }
 
     /* If it did not delete it, the row-trigger may still have modified 
     ** some of the columns of the row being updated. Load the values for 
@@ -527,12 +540,13 @@ void sqlite3Update(
     int j1;                       /* Address of jump instruction */
 
     /* Do constraint checks. */
-    sqlite3GenerateConstraintChecks(pParse, pTab, iDataCur, iIdxCur,
-        regNewRowid, aRegIdx, (chngPk?regOldRowid:0), 1, onError, addr, 0);
+    assert( regOldRowid>0 );
+    sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
+        regNewRowid, regOldRowid, chngKey, onError, addr, 0);
 
     /* Do FK constraint checks. */
     if( hasFK ){
-      sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngPk);
+      sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
     }
 
     /* Delete the index entries associated with the current record.  */
@@ -544,7 +558,7 @@ void sqlite3Update(
     sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx);
   
     /* If changing the record number, delete the old record.  */
-    if( hasFK || chngPk ){
+    if( hasFK || chngKey || pPk!=0 ){
       sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
     }
     if( sqlite3VdbeCurrentAddr(v)==j1+1 ){
@@ -554,7 +568,7 @@ void sqlite3Update(
     }
 
     if( hasFK ){
-      sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngPk);
+      sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
     }
   
     /* Insert the new index entries and the new record. */
@@ -565,7 +579,7 @@ void sqlite3Update(
     ** handle rows (possibly in other tables) that refer via a foreign key
     ** to the row just updated. */ 
     if( hasFK ){
-      sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngPk);
+      sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngKey);
     }
   }
 
index 2dc20ca94ac6373e730b1fe86c2b762d812f2f81..86117e768334e6eadfec9d8e8e770091a935ab03 100644 (file)
@@ -61,4 +61,15 @@ do_execsql_test without_rowid1-1.22 {
   SELECT *, '|' FROM t1 ORDER BY c, a;
 } {arctic sleep ammonia helena | journal sherman ammonia helena | dynamic phone flipper harvard | journal sherman gamma patriot |}
 
+do_execsql_test without_rowid1-1.23 {
+  SELECT *, '|' FROM t1 ORDER BY b, d;
+} {dynamic phone flipper harvard | journal sherman ammonia helena | journal sherman gamma patriot | arctic sleep ammonia helena |}
+
+# UPDATE statements.
+#
+do_execsql_test without_rowid1-1.31 {
+  UPDATE t1 SET d=3.1415926 WHERE a='journal';
+  SELECT *, '|' FROM t1 ORDER BY c, a;
+} {arctic sleep ammonia helena | journal sherman ammonia 3.1415926 | dynamic phone flipper harvard | journal sherman gamma 3.1415926 |}
+
 finish_test