Progress toward getting UPDATE to work in WITHOUT ROWID tables.

FossilOrigin-Name: e557b7d80f1ede63427a31b16757bf5d8dbfb66d
diff --git a/src/insert.c b/src/insert.c
index c3d3af7..d3d1102 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -1145,16 +1145,16 @@
 **
 ** 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 @@
 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 @@
   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 @@
   ** 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 @@
     }
     
     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 9a4f374..7724b66 100644
--- a/src/update.c
+++ b/src/update.c
@@ -99,6 +99,7 @@
   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 @@
                          ** 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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   ** 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 @@
         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 @@
       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 @@
 
   /* 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 @@
     ** 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 @@
   }
 
   /* 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 @@
   /* 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++){