]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Progress toward getting UPDATE to work in WITHOUT ROWID tables.
authordrh <drh@noemail.net>
Fri, 25 Oct 2013 19:17:17 +0000 (19:17 +0000)
committerdrh <drh@noemail.net>
Fri, 25 Oct 2013 19:17:17 +0000 (19:17 +0000)
FossilOrigin-Name: e557b7d80f1ede63427a31b16757bf5d8dbfb66d

manifest
manifest.uuid
src/insert.c
src/update.c

index 13d1feecb881d6e82744500afc19d892ed9f9890..a6af22eaab2ff1228a07906ce4a3a082966e0f6b 100644 (file)
--- 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
index ddaf3cea50a51cc5aeb575eaf2695c647be619cd..e746b047bf2bc579555bcfd71b849a5de866a83a 100644 (file)
@@ -1 +1 @@
-9eafafa31c4a2bbcf48418743e0fcb17c374e9a6
\ No newline at end of file
+e557b7d80f1ede63427a31b16757bf5d8dbfb66d
\ No newline at end of file
index c3d3af72358f44ab27e7a61730e5dacebb8eba50..d3d1102b85a035362a4175fcae5ef9c54f953af7 100644 (file)
@@ -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 ){
index 9a4f374fa9e9707170cbcbccd4f9fc69c12c7a95..7724b666a8e95419813772b103b47deffa430683 100644 (file)
@@ -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);
-
-  /* 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 ){
@@ -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++){