]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Continue working to get UPDATE operational for WITHOUT ROWID tables.
authordrh <drh@noemail.net>
Wed, 30 Oct 2013 20:22:55 +0000 (20:22 +0000)
committerdrh <drh@noemail.net>
Wed, 30 Oct 2013 20:22:55 +0000 (20:22 +0000)
Fix PRAGMA integrity_check so that it works on WITHOUT ROWID tables.

FossilOrigin-Name: 0d4fea7462c0f61cd1c736cbcd7bea5ec2034d54

manifest
manifest.uuid
src/delete.c
src/insert.c
src/pragma.c
src/sqliteInt.h
src/update.c
src/vdbe.c
test/e_reindex.test
test/pragma.test
test/without_rowid1.test [new file with mode: 0644]

index 7e83d2e7ba10e3082223f6fea84ae9f25d75f5f9..966cce8d776287e49fd2f6dd50fa10132e0200d8 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Make\ssure\sKeyInfo\sobjects\son\smulti-column\sindices\sof\sWITHOUT\sROWID\stables\nhave\sthe\scorrect\snField\sand\snXField\svalues.\s\sAlso,\sadd\sthe\nSQLITE_ENABLE_MODULE_COMMENT\scompile-time\soption\sand\sthe\sVdbeModuleComment()\nmacro\sand\suse\sit\sto\slabel\sentry\sand\sexit\spoints\sof\ssome\skey\sroutines.
-D 2013-10-30T15:52:32.018
+C Continue\sworking\sto\sget\sUPDATE\soperational\sfor\sWITHOUT\sROWID\stables.\nFix\sPRAGMA\sintegrity_check\sso\sthat\sit\sworks\son\sWITHOUT\sROWID\stables.
+D 2013-10-30T20:22:55.102
 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 d4917b7b977636f38b9c03040d8bb5f1457f9457
+F src/delete.c 7b56fcc7e290f52e1825692cce37e2f001c7286b
 F src/expr.c 3180b6332072b263f845592e72e92971af562ab0
 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 f8eea9f2303c90a4bfc62bb298dac45b7cc7cd11
+F src/insert.c 4d832cca6d235ca0fc7f2c4acff23ca3246c8548
 F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
 F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
@@ -212,7 +212,7 @@ F src/parse.y 073a8294e1826f1b1656e84806b77e4199f4bb57
 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
 F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222
 F src/pcache1.c a467393909a4ed7ca9de066d85ba5c5b04a5be63
-F src/pragma.c 21f9e61ca8a3ccf699d28d4a5ec04ef33824624a
+F src/pragma.c c6612470d2db2cc30d226c1990faeaff7320a296
 F src/prepare.c f47ba2bba7ac5650881ab6c41f6d33a6de1a8d52
 F src/printf.c da9119eb31a187a4b99f60aa4a225141c0ebb74b
 F src/random.c 0b2dbc37fdfbfa6bd455b091dfcef5bdb32dba68
@@ -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 03e6459326d16123441ada0a053163fd1e2e341d
+F src/sqliteInt.h f8fc23b598245de9460af2de163c91fc31d4fb27
 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
 F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -275,11 +275,11 @@ 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 e39378bc5ed0c42e80624229703e59b5c7a4d50a
+F src/update.c 103ab76ccd4211b4c298ad4fe4c7a16fbd274818
 F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269
 F src/util.c 2fa6c821d28bbdbeec1b2a7b091a281c9ef8f918
 F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
-F src/vdbe.c 3c98b77343f8a1f4624b86795bf29cfd008b0e4a
+F src/vdbe.c 05857502186641716e8d9204de138b642208628f
 F src/vdbe.h c18a2dd91c838601b867a214e43c5f66d5d001ba
 F src/vdbeInt.h 42dcff74dbeb2b071e569b53f885fc9c2e4b4cb0
 F src/vdbeapi.c 93a22a9ba2abe292d5c2cf304d7eb2e894dde0ed
@@ -437,7 +437,7 @@ F test/e_expr.test d5cdda0e4ffb17760858ed4c7c4ece07efc40f71
 F test/e_fkey.test 17cfb40002d165299681f39aac0cb5890c359935
 F test/e_fts3.test 5c02288842e4f941896fd44afdef564dd5fc1459
 F test/e_insert.test 291e056e1a442a5e5166a989a8a03a46e38225ca
-F test/e_reindex.test e175794fc41f8e8aef34772e87a7d7b7a9251dd3
+F test/e_reindex.test 396b7b4f0a66863b4e95116a67d93b227193e589
 F test/e_resolve.test dcce9308fb13b934ce29591105d031d3e14fbba6
 F test/e_select.test d3226cb94fae4af3f198e68e71f655e106d0be47
 F test/e_select2.test 22c660a7becf0712c95e1ca1b2d9e716a1261460
@@ -728,7 +728,7 @@ F test/pcache.test b09104b03160aca0d968d99e8cd2c5b1921a993d
 F test/pcache2.test a83efe2dec0d392f814bfc998def1d1833942025
 F test/percentile.test b98fc868d71eb5619d42a1702e9ab91718cbed54
 F test/permutations.test e154f5ed66d4d4913a99a110e870c9407f75b055
-F test/pragma.test 5e7de6c32a5d764f09437d2025f07e4917b9e178
+F test/pragma.test e882183ecd21d064cec5c7aaea174fbd36293429
 F test/pragma2.test aea7b3d82c76034a2df2b38a13745172ddc0bc13
 F test/printf.test ec9870c4dce8686a37818e0bf1aba6e6a1863552
 F test/progress.test a282973d1d17f08071bc58a77d6b80f2a81c354d
@@ -1076,6 +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 1c3d5a5df986d931a3c5c19ffef5804bd324d9e5
 F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
 F test/zerodamage.test 209d7ed441f44cc5299e4ebffbef06fd5aabfefd
 F tool/build-all-msvc.bat 38623a30fd58288fda5cc7f7df2682aaab75c9d5 x
@@ -1127,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 72d45eb79b5d0505050cff57a00d725948b2a0c0
-R 52fcbe874e2522b4dc7ef0c1b4c145a0
+P 6d9af6065fc0da8337aee2297a8da7511eecccf1
+R 00013e769d264c62972e72b68f63eb56
 U drh
-Z 3d4390b53d26d2b8ae9a915e21b79a8b
+Z 05e092e5dbaf4f4f4e2497c5914c1170
index 88593d7affa248e4c2345ae86d53fc73a19fd1e7..5b6052dd47c8886419816a2a106e2770055ebe97 100644 (file)
@@ -1 +1 @@
-6d9af6065fc0da8337aee2297a8da7511eecccf1
\ No newline at end of file
+0d4fea7462c0f61cd1c736cbcd7bea5ec2034d54
\ No newline at end of file
index b8e3d256ca1301ee49b7db7b3aab3283cd6f3e46..b7601f8af4ed1343b8bd389c6059e8edb308f3c5 100644 (file)
@@ -604,8 +604,11 @@ void sqlite3GenerateRowDelete(
   ** a view (in which case the only effect of the DELETE statement is to
   ** fire the INSTEAD OF triggers).  */ 
   if( pTab->pSelect==0 ){
-    sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0);
-    sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
+    Index *pPk;
+    int iMainCur;
+    sqlite3PrincipleBtree(pTab, iCur, &pPk, &iMainCur);
+    sqlite3GenerateRowIndexDelete(pParse, pTab, iMainCur, 0);
+    sqlite3VdbeAddOp2(v, OP_Delete, iMainCur, (count?OPFLAG_NCHANGE:0));
     if( count ){
       sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
     }
index b79a729d8367bb7b9b9537624f681919c222323d..752d40167989475ed39b9985c044af77db235de4 100644 (file)
@@ -1159,6 +1159,40 @@ static int sqlite3PrimaryKeyRegisters(Parse *pParse, Index *pPk, int regFirst){
   return regPk;
 }
 
+/*
+** Locate the "principle btree" for a table.  This is the table itself for
+** ordinary tables, but for WITHOUT ROWID tables, the principle btree is the
+** PRIMARY KEY index.
+**
+** Inputs are pTab and baseCur.  The *ppPk is written with a pointer to the
+** PRIMARY KEY index for WITHOUT ROWID tables or with NULL for ordinary
+** tables.  The *piPkCur is written with the cursor to use, assuming that the
+** table cursor is baseCur and that index cursors are consecutively numbered
+** thereafter.
+*/
+void sqlite3PrincipleBtree(
+  Table *pTab,        /* The main Table object */
+  int baseCur,        /* VDBE cursor for main table. */
+  Index **ppPk,       /* Write PRIMARY KEY index of WITHOUT ROWID tables here */
+  int *piPkCur        /* Either baseCur or the cursor for *ppPk */
+){
+  int pkCur;
+  Index *pPk;
+  if( !HasRowid(pTab) ){
+    pkCur = baseCur+1;
+    pPk = pTab->pIndex;
+    while( ALWAYS(pPk) && pPk->autoIndex!=2 ){
+      pPk=pPk->pNext;
+      pkCur++;
+    }
+  }else{
+    pkCur = baseCur;
+    pPk = 0;
+  }
+  *ppPk = pPk;
+  *piPkCur = pkCur;
+}
+
 
 /*
 ** Generate code to do constraint checks prior to an INSERT or an UPDATE.
@@ -1263,6 +1297,7 @@ void sqlite3GenerateConstraintChecks(
   int regOldPk;        /* Previous rowid or PRIMARY KEY value */
   int regNewPk = 0;    /* New PRIMARY KEY value */
   int pkCur = 0;       /* Cursor used by the PRIMARY KEY */
+  int nPkField;        /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
 
   regOldPk = (pkChng && isUpdate) ? pkChng : regRowid;
   db = pParse->db;
@@ -1271,20 +1306,15 @@ void sqlite3GenerateConstraintChecks(
   assert( pTab->pSelect==0 );  /* This table is not a VIEW */
   nCol = pTab->nCol;
   regData = regRowid + 1;
-  VdbeModuleComment((v, "BEGIN: GenerateConstraintChecks(%d,%d,%d)",
-                     baseCur, regRowid, pkChng));
 
   /* For WITHOUT ROWID tables, we'll need to know the Index and the cursor
   ** number for the PRIMARY KEY index */
-  if( !HasRowid(pTab) ){
-    assert( pkChng==0 || isUpdate!=0 );
-    pkCur = baseCur+1;
-    pPk = pTab->pIndex;
-    while( ALWAYS(pPk) && pPk->autoIndex!=2 ){
-      pPk=pPk->pNext;
-      pkCur++;
-    }
-  }
+  sqlite3PrincipleBtree(pTab, baseCur, &pPk, &pkCur);
+  nPkField = pPk ? pPk->nKeyCol : 1;
+
+  /* Record that this module has started */
+  VdbeModuleComment((v, "BEGIN: GenCnstCks(%d,%d,%d,%d,%d)",
+                     baseCur, regRowid, pkChng, regOldPk, pkCur));
 
   /* Test all NOT NULL constraints.
   */
@@ -1366,7 +1396,7 @@ void sqlite3GenerateConstraintChecks(
   **
   ** This block only runs for tables that have a rowid.
   */
-  if( pkChng && pkCur==0 ){
+  if( pkChng && pPk==0 ){
     int addrRowidOk = sqlite3VdbeMakeLabel(v);
 
     onError = pTab->keyConf;
@@ -1492,7 +1522,7 @@ void sqlite3GenerateConstraintChecks(
     }
     
     /* Check to see if the new index entry will be unique */
-    regR = sqlite3GetTempReg(pParse);
+    regR = sqlite3GetTempRange(pParse, nPkField);
     sqlite3VdbeAddOp4Int(v, OP_NoConflict, idxCur, addrUniqueOk,
                          regIdx, pIdx->nKeyCol);
 #if 0
@@ -1510,13 +1540,15 @@ void sqlite3GenerateConstraintChecks(
       ** if the PRIMARY KEY has changed.  If the PRIMARY KEY is unchanged,
       ** then the matching entry is just the original row that is being
       ** modified. */
-      int addrPkConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol;
-      for(i=0; i<pPk->nKeyCol-1; i++){
-        sqlite3VdbeAddOp3(v, OP_Ne,
-                          regOldPk+pPk->aiColumn[i], addrPkConflict, regIdx+i);
+      if( onError!=OE_Replace ){
+        int addrPkConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol;
+        for(i=0; i<pPk->nKeyCol-1; i++){
+          sqlite3VdbeAddOp3(v, OP_Ne, regOldPk+pPk->aiColumn[i]+1,
+                            addrPkConflict, regIdx+i);
+        }
+        sqlite3VdbeAddOp3(v, OP_Eq, regOldPk+pPk->aiColumn[i]+1,
+                          addrUniqueOk, regIdx+i);
       }
-      sqlite3VdbeAddOp3(v, OP_Eq,
-                        regOldPk+pPk->aiColumn[i], addrUniqueOk, regIdx+i);
     }else{
       /* For a UNIQUE index on a WITHOUT ROWID table, conflict only if the
       ** PRIMARY KEY value of the match is different from the old PRIMARY KEY
@@ -1524,13 +1556,13 @@ void sqlite3GenerateConstraintChecks(
       int addrConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol*2;
       assert( pIdx->nKeyCol + pPk->nKeyCol == pIdx->nColumn );
       for(i=0; i<pPk->nKeyCol-1; i++){
-        sqlite3VdbeAddOp3(v, OP_Column, idxCur, pIdx->nKeyCol+i, regR);
+        sqlite3VdbeAddOp3(v, OP_Column, idxCur, pIdx->nKeyCol+i, regR+i);
         sqlite3VdbeAddOp3(v, OP_Ne,
-                          regOldPk+pPk->aiColumn[i], addrConflict, regR);
+                          regOldPk+pPk->aiColumn[i], addrConflict, regR+i);
       }
-      sqlite3VdbeAddOp3(v, OP_Column, idxCur, pIdx->nKeyCol+i, regR);
+      sqlite3VdbeAddOp3(v, OP_Column, idxCur, pIdx->nKeyCol+i, regR+i);
       sqlite3VdbeAddOp3(v, OP_Eq,
-                        regOldPk+pPk->aiColumn[i], addrUniqueOk, regR);
+                        regOldPk+pPk->aiColumn[i], addrUniqueOk, regR+i);
     }
     sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);
 
@@ -1575,9 +1607,13 @@ void sqlite3GenerateConstraintChecks(
         if( db->flags&SQLITE_RecTriggers ){
           pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
         }
-        sqlite3GenerateRowDelete(
-            pParse, pTab, pTrigger, baseCur, regR, 0, 0, OE_Replace
-        );
+        if( pIdx==pPk ){
+          /*sqlite3VdbeAddOp3(v, OP_IdxDelete, pkCur, regIdx, pIdx->nColumn);*/
+          sqlite3VdbeAddOp1(v, OP_Delete, pkCur);
+        }else{
+          sqlite3GenerateRowDelete(pParse, pTab, pTrigger, baseCur, 
+                                   regR, nPkField, 0, OE_Replace);
+        }
         seenReplace = 1;
         break;
       }
@@ -1589,7 +1625,7 @@ void sqlite3GenerateConstraintChecks(
   if( pbMayReplace ){
     *pbMayReplace = seenReplace;
   }
-  VdbeModuleComment((v, "END: GenerateConstraintChecks()"));
+  VdbeModuleComment((v, "END: GenCnstCks()"));
 }
 
 /*
@@ -1679,7 +1715,11 @@ int sqlite3OpenTableAndIndices(
   iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
   v = sqlite3GetVdbe(pParse);
   assert( v!=0 );
-  sqlite3OpenTable(pParse, baseCur, iDb, pTab, op);
+  if( pkCur<0 ){
+    sqlite3OpenTable(pParse, baseCur, iDb, pTab, op);
+  }else{
+    sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
+  }
   for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
     KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
     int iCur = (pkCur>=0 && pIdx->autoIndex==2) ? pkCur : i+baseCur;
index a8df215e4f1050ce5ec6f60624a9b58f7cbc77c9..02fbc0335351f74e27e9402552bec13d019d062e 100644 (file)
@@ -1846,8 +1846,10 @@ void sqlite3Pragma(
       for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
         Table *pTab = sqliteHashData(x);
         Index *pIdx;
-        sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt);
-        cnt++;
+        if( HasRowid(pTab) ){
+          sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt);
+          cnt++;
+        }
         for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
           sqlite3VdbeAddOp2(v, OP_Integer, pIdx->tnum, 2+cnt);
           cnt++;
@@ -1855,7 +1857,7 @@ void sqlite3Pragma(
       }
 
       /* Make sure sufficient number of registers have been allocated */
-      pParse->nMem = MAX( pParse->nMem, cnt+7 );
+      pParse->nMem = MAX( pParse->nMem, cnt+8 );
 
       /* Do the b-tree integrity checks */
       sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1);
@@ -1873,44 +1875,44 @@ void sqlite3Pragma(
       */
       for(x=sqliteHashFirst(pTbls); x && !isQuick; x=sqliteHashNext(x)){
         Table *pTab = sqliteHashData(x);
-        Index *pIdx;
+        Index *pIdx, *pPk;
         int loopTop;
+        int pkCur;
 
         if( pTab->pIndex==0 ) continue;
+        sqlite3PrincipleBtree(pTab, 1, &pPk, &pkCur);
+        pkCur = (pPk==0) ? -1 : 1;
         addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1);  /* Stop if out of errors */
         sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
         sqlite3VdbeJumpHere(v, addr);
         sqlite3ExprCacheClear(pParse);
-        sqlite3OpenTableAndIndices(pParse, pTab, 1, -1, OP_OpenRead);
+        sqlite3OpenTableAndIndices(pParse, pTab, 1, pkCur, OP_OpenRead);
+        sqlite3VdbeAddOp2(v, OP_Integer, 0, 7);
         for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
-          sqlite3VdbeAddOp2(v, OP_Integer, 0, 7+j); /* index entries counter */
+          sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */
         }
-        pParse->nMem = MAX(pParse->nMem, 7+j);
-        loopTop = sqlite3VdbeAddOp2(v, OP_Rewind, 1, 0) + 1;
+        pParse->nMem = MAX(pParse->nMem, 8+j);
+        sqlite3VdbeAddOp2(v, OP_Rewind, 1, 0);
+        loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
         for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
-          int jmp2, jmp3;
+          int jmp2, jmp3, jmp4;
           int r1;
-          static const VdbeOpList idxErr[] = {
-            { OP_AddImm,      1, -1,  0},
-            { OP_String8,     0,  3,  0},    /* 1 */
-            { OP_Rowid,       1,  4,  0},
-            { OP_String8,     0,  5,  0},    /* 3 */
-            { OP_String8,     0,  6,  0},    /* 4 */
-            { OP_Concat,      4,  3,  3},
-            { OP_Concat,      5,  3,  3},
-            { OP_Concat,      6,  3,  3},
-            { OP_ResultRow,   3,  1,  0},
-            { OP_IfPos,       1,  0,  0},    /* 9 */
-            { OP_Halt,        0,  0,  0},
-          };
+          if( pPk==pIdx ) continue;
           r1 = sqlite3GenerateIndexKey(pParse, pIdx, 1, 0, 0, &jmp3);
-          sqlite3VdbeAddOp2(v, OP_AddImm, 7+j, 1);  /* increment entry count */
+          sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1);  /* increment entry count */
           jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, j+2, 0, r1, pIdx->nKeyCol+1);
-          addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr);
-          sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC);
-          sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC);
-          sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT);
-          sqlite3VdbeJumpHere(v, addr+9);
+          sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */
+          sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, "row ", P4_STATIC);
+          sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
+          sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, " missing from index ",
+                            P4_STATIC);
+          sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
+          sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0, pIdx->zName, P4_TRANSIENT);
+          sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
+          sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1);
+          jmp4 = sqlite3VdbeAddOp1(v, OP_IfPos, 1);
+          sqlite3VdbeAddOp0(v, OP_Halt);
+          sqlite3VdbeJumpHere(v, jmp4);
           sqlite3VdbeJumpHere(v, jmp2);
           sqlite3VdbeResolveLabel(v, jmp3);
         }
@@ -1920,11 +1922,12 @@ void sqlite3Pragma(
         sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, 
                      "wrong # of entries in index ", P4_STATIC);
         for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
+          if( pPk==pIdx ) continue;
           addr = sqlite3VdbeCurrentAddr(v);
           sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr+2);
           sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
           sqlite3VdbeAddOp2(v, OP_Count, j+2, 3);
-          sqlite3VdbeAddOp3(v, OP_Eq, 7+j, addr+8, 3);
+          sqlite3VdbeAddOp3(v, OP_Eq, 8+j, addr+8, 3);
           sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1);
           sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pIdx->zName, P4_TRANSIENT);
           sqlite3VdbeAddOp3(v, OP_Concat, 3, 2, 7);
index ec4aaa14db9e528709cfc80fda18f3e80286775d..d730bf9b7c3a93304024f7f9ed1795430285ae88 100644 (file)
@@ -1526,7 +1526,7 @@ struct KeyInfo {
   sqlite3 *db;        /* The database connection */
   u8 enc;             /* Text encoding - one of the SQLITE_UTF* values */
   u16 nField;         /* Number of key columns in the index */
-  u16 nXField;         /* Number of columns beyond the key columns */
+  u16 nXField;        /* Number of columns beyond the key columns */
   u8 *aSortOrder;     /* Sort order for each column. */
   CollSeq *aColl[1];  /* Collating sequence for each term of the key */
 };
@@ -2922,6 +2922,7 @@ int sqlite3IsRowid(const char*);
 void sqlite3GenerateRowDelete(Parse*,Table*,Trigger*,int,int,i16,u8,u8);
 void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);
 int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*);
+void sqlite3PrincipleBtree(Table*,int,Index**,int*);
 void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
                                      int*,int,int,int,int,int*);
 void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
index 7724b666a8e95419813772b103b47deffa430683..9863456496db8c0e9646c4181b8545f4fcf8059c 100644 (file)
@@ -102,6 +102,7 @@ void sqlite3Update(
   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 */
+  int pkCur;             /* VDBE Cursor for the pPk index */
   sqlite3 *db;           /* The database structure */
   int *aRegIdx = 0;      /* One register assigned to each index to be updated */
   int *aXRef = 0;        /* aXRef[i] is the index in pChanges->a[] of the
@@ -185,7 +186,10 @@ void sqlite3Update(
   for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
     pParse->nTab++;
   }
-  pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
+
+  /* For WITHOUT ROWID tables, we'll need to know the Index and the cursor
+  ** number for the PRIMARY KEY index */
+  sqlite3PrincipleBtree(pTab, iCur, &pPk, &pkCur);
 
   /* Initialize the name-context */
   memset(&sNC, 0, sizeof(sNC));
@@ -531,8 +535,12 @@ void sqlite3Update(
     }
 
     /* Delete the index entries associated with the current record.  */
-    j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
-    sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);
+    if( pPk ){
+      /*j1 = sqlite3VdbeAddOp3(v, OP_NotFound, pkCur, */
+    }else{
+      j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
+      sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);
+    }
   
     /* If changing the record number, delete the old record.  */
     if( hasFK || chngRowid ){
index 1dd8757f4ba279c18cfa939a7afe1bcd047370ab..2423754072e483c2d40d9fb4485b0fdb81fa3dba 100644 (file)
@@ -3734,7 +3734,13 @@ case OP_Found: {        /* jump, in3 */
       r.nField = (u16)pOp->p4.i;
       r.aMem = pIn3;
 #ifdef SQLITE_DEBUG
-      { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
+      {
+        int i;
+        for(i=0; i<r.nField; i++){
+          assert( memIsValid(&r.aMem[i]) );
+          if( i ) REGISTER_TRACE(pOp->p3+i, &r.aMem[i]);
+        }
+      }
 #endif
       r.flags = UNPACKED_PREFIX_MATCH;
       pIdxKey = &r;
index c1eae0646cfc28ef939db0321461e140f8133751..4b86787a05feac9af305a5aff2f833cc18771ac1 100644 (file)
@@ -67,10 +67,10 @@ sqlite3 db test.db
 do_execsql_test e_reindex-1.3 {
   PRAGMA integrity_check;
 } [list \
-  {rowid 4 missing from index i2} \
-  {rowid 4 missing from index i1} \
-  {rowid 5 missing from index i2} \
-  {rowid 5 missing from index i1} \
+  {row 3 missing from index i2} \
+  {row 3 missing from index i1} \
+  {row 4 missing from index i2} \
+  {row 4 missing from index i1} \
   {wrong # of entries in index i2} \
   {wrong # of entries in index i1}
 ]
index a6d198eb692ae422ec84bebf255fd682fdaa09d7..10431703780d32f6ca3879fc3ed2f4be96139137 100644 (file)
@@ -285,31 +285,31 @@ ifcapable attach {
       db close
       sqlite3 db test.db
       execsql {PRAGMA integrity_check}
-    } {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+    } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
     do_test pragma-3.3 {
       execsql {PRAGMA integrity_check=1}
-    } {{rowid 1 missing from index i2}}
+    } {{row 1 missing from index i2}}
     do_test pragma-3.4 {
       execsql {
         ATTACH DATABASE 'test.db' AS t2;
         PRAGMA integrity_check
       }
-    } {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+    } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
     do_test pragma-3.5 {
       execsql {
         PRAGMA integrity_check=4
       }
-    } {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2}}
+    } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2}}
     do_test pragma-3.6 {
       execsql {
         PRAGMA integrity_check=xyz
       }
-    } {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+    } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
     do_test pragma-3.7 {
       execsql {
         PRAGMA integrity_check=0
       }
-    } {{rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+    } {{row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
   
     # Add additional corruption by appending unused pages to the end of
     # the database file testerr.db
@@ -344,7 +344,7 @@ ifcapable attach {
     } {{*** in database t2 ***
 Page 4 is never used
 Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
     do_test pragma-3.10 {
       execsql {
         PRAGMA integrity_check=1
@@ -358,7 +358,7 @@ Page 4 is never used}}
     } {{*** in database t2 ***
 Page 4 is never used
 Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2}}
+Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2}}
     do_test pragma-3.12 {
       execsql {
         PRAGMA integrity_check=4
@@ -366,7 +366,7 @@ Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from inde
     } {{*** in database t2 ***
 Page 4 is never used
 Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2}}
+Page 6 is never used} {row 1 missing from index i2}}
     do_test pragma-3.13 {
       execsql {
         PRAGMA integrity_check=3
@@ -390,10 +390,10 @@ Page 5 is never used}}
     } {{*** in database t2 ***
 Page 4 is never used
 Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
+Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
 Page 4 is never used
 Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2}}
+Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2}}
     do_test pragma-3.16 {
       execsql {
         PRAGMA integrity_check(10)
@@ -401,10 +401,10 @@ Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from inde
     } {{*** in database t2 ***
 Page 4 is never used
 Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
+Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
 Page 4 is never used
 Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2}}
+Page 6 is never used} {row 1 missing from index i2}}
     do_test pragma-3.17 {
       execsql {
         PRAGMA integrity_check=8
@@ -412,7 +412,7 @@ Page 6 is never used} {rowid 1 missing from index i2}}
     } {{*** in database t2 ***
 Page 4 is never used
 Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2} {rowid 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
+Page 6 is never used} {row 1 missing from index i2} {row 2 missing from index i2} {wrong # of entries in index i2} {*** in database t3 ***
 Page 4 is never used
 Page 5 is never used}}
     do_test pragma-3.18 {
@@ -422,7 +422,7 @@ Page 5 is never used}}
     } {{*** in database t2 ***
 Page 4 is never used
 Page 5 is never used
-Page 6 is never used} {rowid 1 missing from index i2}}
+Page 6 is never used} {row 1 missing from index i2}}
   }
   do_test pragma-3.19 {
     catch {db close}
diff --git a/test/without_rowid1.test b/test/without_rowid1.test
new file mode 100644 (file)
index 0000000..4c8665a
--- /dev/null
@@ -0,0 +1,66 @@
+# 2013-10-30
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# This file implements regression tests for SQLite library.  The
+# focus of this file is testing WITHOUT ROWID tables.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Create and query a WITHOUT ROWID table.
+#
+do_execsql_test without_rowid1-1.0 {
+  CREATE TABLE t1(a,b,c,d, PRIMARY KEY(c,a)) WITHOUT ROWID;
+  CREATE INDEX t1bd ON t1(b, d);
+  INSERT INTO t1 VALUES('journal','sherman','ammonia','helena');
+  INSERT INTO t1 VALUES('dynamic','juliet','flipper','command');
+  INSERT INTO t1 VALUES('journal','sherman','gamma','patriot');
+  INSERT INTO t1 VALUES('arctic','sleep','ammonia','helena');
+  SELECT *, '|' FROM t1 ORDER BY c, a;
+} {arctic sleep ammonia helena | journal sherman ammonia helena | dynamic juliet flipper command | journal sherman gamma patriot |}
+
+integrity_check without_rowid1-1.0ic
+
+do_execsql_test without_rowid1-1.1 {
+  SELECT *, '|' FROM t1 ORDER BY +c, a;
+} {arctic sleep ammonia helena | journal sherman ammonia helena | dynamic juliet flipper command | journal sherman gamma patriot |}
+
+do_execsql_test without_rowid1-1.2 {
+  SELECT *, '|' FROM t1 ORDER BY c DESC, a DESC;
+} {journal sherman gamma patriot | dynamic juliet flipper command | journal sherman ammonia helena | arctic sleep ammonia helena |}
+
+do_execsql_test without_rowid1-1.11 {
+  SELECT *, '|' FROM t1 ORDER BY b, d;
+} {dynamic juliet flipper command | journal sherman ammonia helena | journal sherman gamma patriot | arctic sleep ammonia helena |}
+
+do_execsql_test without_rowid1-1.12 {
+  SELECT *, '|' FROM t1 ORDER BY +b, d;
+} {dynamic juliet flipper command | journal sherman ammonia helena | journal sherman gamma patriot | arctic sleep ammonia helena |}
+
+if 0 {
+# Trying to insert a duplicate PRIMARY KEY fails.
+#
+do_test without_rowid1-1.21 {
+  catchsql {
+    INSERT INTO t1 VALUES('dynamic','phone','flipper','harvard');
+  }
+} {1 {columns c, a are not unique}}
+
+# REPLACE INTO works, however.
+#
+do_execsql_test without_rowid1-1.22 {
+  REPLACE INTO t1 VALUES('dynamic','phone','flipper','harvard');
+  SELECT *, '|' FROM t1 ORDER BY c, a;
+} {}
+}
+
+finish_test