]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
The new ON CONFLICT logic is in and passes the legacy tests. But the
authordrh <drh@noemail.net>
Tue, 29 Jan 2002 23:07:02 +0000 (23:07 +0000)
committerdrh <drh@noemail.net>
Tue, 29 Jan 2002 23:07:02 +0000 (23:07 +0000)
new capabilities have not been tested and are likely broken. (CVS 356)

FossilOrigin-Name: ac8a4189e2a0c41161ee359db25de94435420368

manifest
manifest.uuid
src/build.c
src/delete.c
src/insert.c
src/sqliteInt.h
src/update.c
src/vdbe.c
test/intpkey.test

index eedd784ec711ad8657afe6a9dcb271ce90bb1416..ea170d3457a1a21958e20eb7534df8dee6317b12 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Beginning\sto\sinsert\sthe\sinfrastructure\sfor\sON\sCONFLICT\sclauses.\s(CVS\s355)
-D 2002-01-29T18:41:24
+C The\snew\sON\sCONFLICT\slogic\sis\sin\sand\spasses\sthe\slegacy\stests.\s\sBut\sthe\nnew\scapabilities\shave\snot\sbeen\stested\sand\sare\slikely\sbroken.\s(CVS\s356)
+D 2002-01-29T23:07:02
 F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af
 F Makefile.template 3e26a3b9e7aee1b811deaf673e8d8973bdb3f22d
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -21,12 +21,12 @@ F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
 F src/btree.c c796e387da340cb628dc1e41f684fc20253f561e
 F src/btree.h 9ead7f54c270d8a554e59352ca7318fdaf411390
-F src/build.c c5023252c4d0ed19067f2118639b8d9f14f3f91a
-F src/delete.c 2d1abc228be80a3c41f29f3312534dd0c9cd5066
+F src/build.c c55881f270b1a77d1025dcfba7c87db46d43a9d0
+F src/delete.c 4cdb6d2e94e2eb1b1aa79eefafd4669d43c249d6
 F src/expr.c 4cae8bf44d5732182e5e8c25b4552c05ea55593e
 F src/hash.c 8f7c740ef2eaaa8decfa8751f2be30680b123e46
 F src/hash.h a5f5b3ce2d086a172c5879b0b06a27a82eac9fac
-F src/insert.c 475316610462d1b6881f45565bbebf3209f1088c
+F src/insert.c 35c3e17bf5f8ef3a9cdd95f7cfdbf3d94cd598c2
 F src/main.c 0205771a6c31a9858ff131fc1e797b589afb76bf
 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
 F src/os.c c615faa4d23e742e0650e0751a6ad2a18438ad53
@@ -40,16 +40,16 @@ F src/select.c fc11d5a8c2bae1b62d8028ffb111c773ad6bf161
 F src/shell.c c102dfe388c7618a668c944ff157c49cb48f28e3
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in f57074c84a2c112a5093ba7a9d9636aa9cacc87c
-F src/sqliteInt.h f4dc7f359549f70cd0fd6c68ae054d6986ad3afc
+F src/sqliteInt.h 60c0945eb4159c44adec9aadadb61dfd931d29e9
 F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a
 F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
 F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
 F src/test2.c e9f99aa5ee73872819259d6612c11e55e1644321
 F src/test3.c d6775f95fd91f5b3cf0e2382a28e5aaeb68f745b
 F src/tokenize.c 1199b96a82d5c41509b5e24fc9faa1852b7f3135
-F src/update.c 2ece9c433968f9268d93bcc2f0ece409ab4dc296
+F src/update.c 5ffd4bbd380f1fa99da184f28416e6dcf8b5508e
 F src/util.c 8f8973dd55a6ec63be9632fc5de86965c99d6327
-F src/vdbe.c 5e51f9dfc34ee329117f59c28410ea9d4224402a
+F src/vdbe.c abd60d37361eaaa3b94d016cd2a9f31bd8d57620
 F src/vdbe.h 5b1bd518126fc5a30e6ea13fe11de931b32c4b59
 F src/where.c 2dda39367f193194e4c7d2e0dcab31527d9d8aba
 F test/all.test 2a51e5395ac7c2c539689b123b9782a05e3837fe
@@ -65,7 +65,7 @@ F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa
 F test/index.test c8a471243bbf878974b99baf5badd59407237cf3
 F test/insert.test a5c122aa726f1cef6f07d6767e8fd6f220994c11
 F test/insert2.test d6901ca931e308fea7fca8c95ebe7dc957cc9fc2
-F test/intpkey.test d6c7f42679b3f87da3933f5dddac92c822dd89c5
+F test/intpkey.test ce3de8326082929667cf356855426519cfe2f5c7
 F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
 F test/limit.test a930f3eba2a7691c8397ccab33710b931589566a
 F test/lock.test 19593689260c419efe7ced55b1418653a4b7bcd1
@@ -119,7 +119,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P af3bb80810c6cd302a884a106cc70591a738e102
-R 82f94ecaa281bc36c2caa4b6f3c6c838
+P e00a9ff8f99dd58f7cb19a6195fac21f4c8b4af9
+R 1ca39242f5cf0f094b73bd233e3fcc93
 U drh
-Z 16309c459073f336b26332a0e349efd1
+Z 0835a3278f559e60402c3a41f932d0d4
index 8b9c80c5d465a5b8a7e66ef1db132371c72c5f17..ba959099f7fdffaabf5191c3e98f1d54843d5f68 100644 (file)
@@ -1 +1 @@
-e00a9ff8f99dd58f7cb19a6195fac21f4c8b4af9
\ No newline at end of file
+ac8a4189e2a0c41161ee359db25de94435420368
\ No newline at end of file
index 473b55e781b037448fdb15076277a890aa249b0b..7eabe18a18f38c2cd40a405bdefdc353e7d5d504 100644 (file)
@@ -25,7 +25,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.67 2002/01/29 18:41:25 drh Exp $
+** $Id: build.c,v 1.68 2002/01/29 23:07:02 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -866,6 +866,7 @@ void sqliteCreateIndex(
   int hideName = 0;         /* Do not put table name in the hash table */
 
   if( pParse->nErr || sqlite_malloc_failed ) goto exit_create_index;
+  if( onError==OE_Default ) onError = OE_Abort;
 
   /*
   ** Find the table that is to be indexed.  Return early if not found.
index 7d19b2b0e3357502e1204b254da821e366ecc3c5..15e2ea3f9d475b399982c4589ce978cbb33dac83 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle DELETE FROM statements.
 **
-** $Id: delete.c,v 1.24 2002/01/29 18:41:25 drh Exp $
+** $Id: delete.c,v 1.25 2002/01/29 23:07:02 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -200,26 +200,50 @@ void sqliteGenerateRowDelete(
   Vdbe *v,           /* Generate code into this VDBE */
   Table *pTab,       /* Table containing the row to be deleted */
   int base           /* Cursor number for the table */
+){
+  sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
+  sqliteGenerateRowIndexDelete(v, pTab, base, 0);
+  sqliteVdbeAddOp(v, OP_Delete, base, 0);
+}
+
+/*
+** This routine generates VDBE code that causes the deletion of all
+** index entries associated with a single row of a single table.
+**
+** The VDBE must be in a particular state when this routine is called.
+** These are the requirements:
+**
+**   1.  A read/write cursor pointing to pTab, the table containing the row
+**       to be deleted, must be opened as cursor number "base".
+**
+**   2.  Read/write cursors for all indices of pTab must be open as
+**       cursor number base+i for the i-th index.
+**
+**   3.  The "base" cursor must be pointing to the row that is to be
+**       deleted.
+*/
+void sqliteGenerateRowIndexDelete(
+  Vdbe *v,           /* Generate code into this VDBE */
+  Table *pTab,       /* Table containing the row to be deleted */
+  int base,          /* Cursor number for the table */
+  char *aIdxUsed     /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */
 ){
   int i;
   Index *pIdx;
 
-  sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
-  if( pTab->pIndex ){
-    for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
-      int j;
-      sqliteVdbeAddOp(v, OP_Recno, base, 0);
-      for(j=0; j<pIdx->nColumn; j++){
-        int idx = pIdx->aiColumn[j];
-        if( idx==pTab->iPKey ){
-          sqliteVdbeAddOp(v, OP_Dup, j, 0);
-        }else{
-          sqliteVdbeAddOp(v, OP_Column, base, idx);
-        }
+  for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+    int j;
+    if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue;
+    sqliteVdbeAddOp(v, OP_Recno, base, 0);
+    for(j=0; j<pIdx->nColumn; j++){
+      int idx = pIdx->aiColumn[j];
+      if( idx==pTab->iPKey ){
+        sqliteVdbeAddOp(v, OP_Dup, j, 0);
+      }else{
+        sqliteVdbeAddOp(v, OP_Column, base, idx);
       }
-      sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
-      sqliteVdbeAddOp(v, OP_IdxDelete, base+i, 0);
     }
+    sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
+    sqliteVdbeAddOp(v, OP_IdxDelete, base+i, 0);
   }
-  sqliteVdbeAddOp(v, OP_Delete, base, 0);
 }
index f9b8b1791c4e1182f2353a56ddd521ab9a8e55f4..1a01fafd95d33b92ab1d6fc852f9593d76df40e6 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements in SQLite.
 **
-** $Id: insert.c,v 1.34 2002/01/29 18:41:25 drh Exp $
+** $Id: insert.c,v 1.35 2002/01/29 23:07:02 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -51,6 +51,7 @@ void sqliteInsert(
   sqlite *db;           /* The main database structure */
   int openOp;           /* Opcode used to open cursors */
   int keyColumn = -1;   /* Column that is the INTEGER PRIMARY KEY */
+  int endOfLoop;        /* Label for the end of the insertion loop */
 
   if( pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup;
   db = pParse->db;
@@ -201,7 +202,9 @@ void sqliteInsert(
   /* Push the record number for the new entry onto the stack.  The
   ** record number is a randomly generate integer created by NewRecno
   ** except when the table has an INTEGER PRIMARY KEY column, in which
-  ** case the record number is the same as that column.
+  ** case the record number is the same as that column.  May a copy
+  ** because sqliteGenerateConstraintChecks() requires two copies of
+  ** the record number.
   */
   if( keyColumn>=0 ){
     if( srcTab>=0 ){
@@ -213,13 +216,7 @@ void sqliteInsert(
   }else{
     sqliteVdbeAddOp(v, OP_NewRecno, base, 0);
   }
-
-  /* If there are indices, we'll need the new record number again, so make
-  ** a copy.
-  */
-  if( pTab->pIndex ){
-    sqliteVdbeAddOp(v, OP_Dup, 0, 0);
-  }
+  sqliteVdbeAddOp(v, OP_Dup, 0, 0);
 
   /* Push onto the stack, data for all columns of the new entry, beginning
   ** with the first column.
@@ -250,45 +247,12 @@ void sqliteInsert(
     }
   }
 
-  /* Create the new record and put it into the database.
-  */
-  sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
-  sqliteVdbeAddOp(v, OP_PutIntKey, base, keyColumn>=0);
-  
-  /* Create appropriate entries for the new data row in all indices
-  ** of the table.
+  /* Generate code to check constraints and generate index keys and
+  ** do the insertion.
   */
-  for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
-    if( pIdx->pNext ){
-      sqliteVdbeAddOp(v, OP_Dup, 0, 0);
-    }
-    for(i=0; i<pIdx->nColumn; i++){
-      int idx = pIdx->aiColumn[i];
-      if( idx==pTab->iPKey ){
-        /* Copy the record number in place of the INTEGER PRIMARY KEY column */
-        sqliteVdbeAddOp(v, OP_Dup, i, 0);
-        continue;
-      }
-      if( pColumn==0 ){
-        j = idx;
-      }else{
-        for(j=0; j<pColumn->nId; j++){
-          if( pColumn->a[j].idx==idx ) break;
-        }
-      }
-      if( pColumn && j>=pColumn->nId ){
-        sqliteVdbeAddOp(v, OP_String, 0, 0);
-        sqliteVdbeChangeP3(v, -1, pTab->aCol[idx].zDflt, P3_STATIC);
-      }else if( srcTab>=0 ){
-        sqliteVdbeAddOp(v, OP_Column, srcTab, idx); 
-      }else{
-        sqliteExprCode(pParse, pList->a[j].pExpr);
-      }
-    }
-    sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
-    sqliteVdbeAddOp(v, OP_IdxPut, idx+base, pIdx->isUnique);
-  }
-
+  endOfLoop = sqliteVdbeMakeLabel(v);
+  sqliteGenerateConstraintChecks(pParse, pTab, base, 0,1,onError,endOfLoop,0);
+  sqliteCompleteInsertion(pParse, pTab, base, 0, 1);
 
   /* If inserting from a SELECT, keep a count of the number of
   ** rows inserted.
@@ -299,6 +263,7 @@ void sqliteInsert(
 
   /* The bottom of the loop, if the data source is a SELECT statement
   */
+  sqliteVdbeResolveLabel(v, endOfLoop);
   if( srcTab>=0 ){
     sqliteVdbeAddOp(v, OP_Next, srcTab, iCont);
     sqliteVdbeResolveLabel(v, iBreak);
@@ -331,17 +296,33 @@ insert_cleanup:
   sqliteIdListDelete(pColumn);
 }
 
-#if 0
 /*
 ** Generate code to do a constraint check prior to an INSERT or an UPDATE.
 **
 ** When this routine is called, the stack contains (from bottom to top)
-** the recno of the row to be updated and each column of new data beginning
-** with the first column.  The code generated by this routine pushes addition
-** entries onto the stack which are the keys for new index entries for
-** the new record.  The order of index keys is the same as the order of
-** the indices on the pTable->pIndex list.  A key is only created for
-** index i if aIdxUsed!=0 and aIdxUsed[i]!=0.
+** the following values:
+**
+**    1.  The recno of the row to be updated before it is updated.
+**
+**    2.  The recno of the row after the update.  (This is usually the
+**        same as (1) but can be different if an UPDATE changes an
+**        INTEGER PRIMARY KEY column.)
+**
+**    3.  The data in the first column of the entry after the update.
+**
+**    i.  Data from middle columns...
+**
+**    N.  The data in the last column of the entry after the update.
+**
+** The old recno shown as entry (1) above is omitted if the recnoChng
+** parameter is 0.  recnoChange is true if the record number is changing
+** and false if not.
+**
+** The code generated by this routine pushes additional entries onto
+** the stack which are the keys for new index entries for the new record.
+** The order of index keys is the same as the order of the indices on
+** the pTable->pIndex list.  A key is only created for index i if 
+** aIdxUsed!=0 and aIdxUsed[i]!=0.
 **
 ** This routine also generates code to check constraints.  NOT NULL,
 ** CHECK, and UNIQUE constraints are all checked.  If a constraint fails,
@@ -391,6 +372,7 @@ void sqliteGenerateConstraintChecks(
   Table *pTab,        /* the table into which we are inserting */
   int base,           /* Index of a read/write cursor pointing at pTab */
   char *aIdxUsed,     /* Which indices are used.  NULL means all are used */
+  int recnoChng,      /* True if the record number will change */
   int overrideError,  /* Override onError to this if not OE_Default */
   int ignoreDest,     /* Jump to this label on an OE_Ignore resolution */
   int isUpdate        /* True for UPDATE, False for INSERT */
@@ -401,18 +383,26 @@ void sqliteGenerateConstraintChecks(
   int onError;
   int addr;
   int extra;
-  char *pToFree = 0;
-  int seenIgnore = 0;
+  int iCur;
+  Index *pIdx;
+  int seenReplace = 0;
+  int jumpInst;
+  int contAddr;
 
   v = sqliteGetVdbe(pParse);
   assert( v!=0 );
   nCol = pTab->nCol;
+  recnoChng = (recnoChng!=0);  /* Must be either 1 or 0 */
 
   /* Test all NOT NULL constraints.
   */
   for(i=0; i<nCol; i++){
+    if( i==pTab->iPKey ){
+      /* Fix me: Make sure the INTEGER PRIMARY KEY is not NULL. */
+      continue;
+    }
     onError = pTab->aCol[i].notNull;
-    if( i==iPKey || onError==OE_None ) continue;
+    if( onError==OE_None ) continue;
     if( overrideError!=OE_Default ){
       onError = overrideError;
     }
@@ -427,8 +417,8 @@ void sqliteGenerateConstraintChecks(
         break;
       }
       case OE_Ignore: {
-        sqliteVdbeAddOp(v, OP_Pop, nCol+1, 0);
-        sqliteVdbeAddOp(v, OP_GoTo, 0, ignoreDest);
+        sqliteVdbeAddOp(v, OP_Pop, nCol+1+recnoChng, 0);
+        sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
         break;
       }
       case OE_Replace: {
@@ -437,9 +427,7 @@ void sqliteGenerateConstraintChecks(
         sqliteVdbeAddOp(v, OP_Push, nCol-i, 0);
         break;
       }
-      default: {
-        CANT_HAPPEN;
-      }
+      default: assert(0);
     }
   }
 
@@ -448,20 +436,44 @@ void sqliteGenerateConstraintChecks(
 
   /* Test all UNIQUE constraints.  Add index records as we go.
   */
+  if( recnoChng && pTab->iPKey>=0 && pTab->keyConf!=OE_Replace 
+      && overrideError!=OE_Replace ){
+    sqliteVdbeAddOp(v, OP_Dup, nCol, 1);
+    jumpInst = sqliteVdbeAddOp(v, OP_NotExists, base, 0);
+    onError = pTab->keyConf;
+    if( overrideError!=OE_Default ){
+      onError = overrideError;
+    }
+    switch( onError ){
+      case OE_Abort: {
+        sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, 0);
+        break;
+      }
+      case OE_Ignore: {
+        sqliteVdbeAddOp(v, OP_Pop, nCol+2, 0);
+        sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
+        break;
+      }
+      default: assert(0);
+    }
+    contAddr = sqliteVdbeCurrentAddr(v);
+    sqliteVdbeChangeP2(v, jumpInst, contAddr);
+    if( isUpdate ){
+      sqliteVdbeAddOp(v, OP_Dup, nCol+1, 1);
+      sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
+    }
+  }
   extra = 0;
   for(extra=(-1), iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
-    int jumpInst;
-    int contAddr;
-
     if( aIdxUsed && aIdxUsed[iCur]==0 ) continue;
     extra++;    
     sqliteVdbeAddOp(v, OP_Dup, nCol+extra, 1);
     for(i=0; i<pIdx->nColumn; i++){
       int idx = pIdx->aiColumn[i];
       if( idx==pTab->iPKey ){
-        sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol+1, 0);
+        sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol+1, 1);
       }else{
-        sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 0);
+        sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1);
       }
     }
     sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
@@ -470,31 +482,68 @@ void sqliteGenerateConstraintChecks(
     if( overrideError!=OE_Default ){
       onError = overrideError;
     }
-    jumpInst = sqliteVdbeAddOp(v, OP_IsUnique, iCur, 0);
+    sqliteVdbeAddOp(v, OP_Dup, extra+nCol+2, 1);
+    jumpInst = sqliteVdbeAddOp(v, OP_IsUnique, base+iCur+1, 0);
     switch( onError ){
       case OE_Abort: {
         sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, 0);
         break;
       }
       case OE_Ignore: {
-        sqliteVdbeAddOp(v, OP_Pop, nCol+extra+2, 0);
+        assert( seenReplace==0 );
+        sqliteVdbeAddOp(v, OP_Pop, nCol+extra+2+recnoChng, 0);
         sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
-        seenIgnore = 1;
         break;
       }
       case OE_Replace: {
-        assert( seenIgnore==0 );
+        sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
         sqliteGenerateRowDelete(v, pTab, base);
         if( isUpdate ){
-          sqliteVdbeAddOp(v, OP_Dup, nCol+extra+2, 1);
-          sqliteVdbeAddOp(v, OP_Moveto, base, 0);
+          sqliteVdbeAddOp(v, OP_Dup, nCol+extra+recnoChng, 1);
+          sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
         }
+        seenReplace = 1;
         break;
       }
-      default: CANT_HAPPEN;
+      default: assert(0);
     }
     contAddr = sqliteVdbeCurrentAddr(v);
     sqliteVdbeChangeP2(v, jumpInst, contAddr);
   }
 }
-#endif
+
+/*
+** This routine generates code to finish the INSERT or UPDATE operation
+** that was started by a prior call to sqliteGenerateConstraintChecks.
+** The stack must contain keys for all active indices followed by data
+** and the recno for the new entry.  This routine creates the new
+** entries in all indices and in the main table.
+**
+** The arguments to this routine should be the same as the first five
+** arguments to sqliteGenerateConstraintChecks.
+*/
+void sqliteCompleteInsertion(
+  Parse *pParse,      /* The parser context */
+  Table *pTab,        /* the table into which we are inserting */
+  int base,           /* Index of a read/write cursor pointing at pTab */
+  char *aIdxUsed,     /* Which indices are used.  NULL means all are used */
+  int recnoChng       /* True if the record number changed */
+){
+  int i;
+  Vdbe *v;
+  int nIdx;
+  Index *pIdx;
+
+  v = sqliteGetVdbe(pParse);
+  assert( v!=0 );
+  for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
+  for(i=nIdx-1; i>=0; i--){
+    if( aIdxUsed && aIdxUsed[i]==0 ) continue;
+    sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, 0);
+  }
+  sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
+  sqliteVdbeAddOp(v, OP_PutIntKey, base, 0);
+  if( recnoChng ){
+    sqliteVdbeAddOp(v, OP_Pop, 1, 0);
+  }
+}
index ca4e7616ea0f534dd50751d5209e02e2fa8da4b5..c6e11d64a383baf578d6c63d713cf6b8a71569ee 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.80 2002/01/29 18:41:25 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.81 2002/01/29 23:07:02 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -547,3 +547,7 @@ void sqliteCommitTransaction(Parse*);
 void sqliteRollbackTransaction(Parse*);
 char *sqlite_mprintf(const char *, ...);
 int sqliteExprIsConstant(Expr*);
+void sqliteGenerateRowDelete(Vdbe*, Table*, int);
+void sqliteGenerateRowIndexDelete(Vdbe*, Table*, int, char*);
+void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
+void sqliteCompleteInsertion(Parse*, Table*, int, char*, int);
index e4d170456ad224d3f4b4f447cf83ac0ba5c763f1..6dff1fdb74e41cff31b10eefbbdd32a414dd3033 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle UPDATE statements.
 **
-** $Id: update.c,v 1.29 2002/01/29 18:41:25 drh Exp $
+** $Id: update.c,v 1.30 2002/01/29 23:07:02 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -29,20 +29,23 @@ void sqliteUpdate(
   int i, j;              /* Loop counters */
   Table *pTab;           /* The table to be updated */
   IdList *pTabList = 0;  /* List containing only pTab */
-  int end, addr;         /* A couple of addresses in the generated code */
+  int addr;              /* VDBE instruction address of the start of the loop */
   WhereInfo *pWInfo;     /* Information about the WHERE clause */
   Vdbe *v;               /* The virtual database engine */
   Index *pIdx;           /* For looping over indices */
   int nIdx;              /* Number of indices that need updating */
+  int nIdxTotal;         /* Total number of indices */
   int base;              /* Index of first available table cursor */
   sqlite *db;            /* The database structure */
   Index **apIdx = 0;     /* An array of indices that need updating too */
+  char *aIdxUsed = 0;    /* aIdxUsed[i] if the i-th index is used */
   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 openOp;            /* Opcode used to open tables */
   int chngRecno;         /* True if the record number is being changed */
   Expr *pRecnoExpr;      /* Expression defining the new record number */
+  int openAll;           /* True if all indices need to be opened */
 
   if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup;
   db = pParse->db;
@@ -123,7 +126,7 @@ void sqliteUpdate(
   ** key includes one of the columns named in pChanges or if the record
   ** number of the original table entry is changing.
   */
-  for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+  for(nIdx=nIdxTotal=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdxTotal++){
     if( chngRecno ){
       i = 0;
     }else {
@@ -133,11 +136,12 @@ void sqliteUpdate(
     }
     if( i<pIdx->nColumn ) nIdx++;
   }
-  if( nIdx>0 ){
-    apIdx = sqliteMalloc( sizeof(Index*) * nIdx );
+  if( nIdxTotal>0 ){
+    apIdx = sqliteMalloc( sizeof(Index*) * nIdx + nIdxTotal );
     if( apIdx==0 ) goto update_cleanup;
+    aIdxUsed = (char*)&apIdx[nIdx];
   }
-  for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+  for(nIdx=j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
     if( chngRecno ){
       i = 0;
     }else{
@@ -145,7 +149,12 @@ void sqliteUpdate(
         if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
       }
     }
-    if( i<pIdx->nColumn ) apIdx[nIdx++] = pIdx;
+    if( i<pIdx->nColumn ){
+      apIdx[nIdx++] = pIdx;
+      aIdxUsed[j] = 1;
+    }else{
+      aIdxUsed[j] = 0;
+    }
   }
 
   /* Begin generating code.
@@ -178,14 +187,30 @@ void sqliteUpdate(
   }
 
   /* Rewind the list of records that need to be updated and
-  ** open every index that needs updating.
+  ** open every index that needs updating.  Note that if any
+  ** index could potentially invoke a REPLACE conflict resolution 
+  ** action, then we need to open all indices because we might need
+  ** to be deleting some records.
   */
   sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
   base = pParse->nTab;
   openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite;
   sqliteVdbeAddOp(v, openOp, base, pTab->tnum);
-  for(i=0; i<nIdx; i++){
-    sqliteVdbeAddOp(v, openOp, base+i+1, apIdx[i]->tnum);
+  if( onError==OE_Replace ){
+    openAll = 1;
+  }else{
+    openAll = 0;
+    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+      if( pIdx->onError==OE_Replace ){
+        openAll = 1;
+        break;
+      }
+    }
+  }
+  for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+    if( openAll || aIdxUsed[i] ){
+      sqliteVdbeAddOp(v, openOp, base+i+1, pIdx->tnum);
+    }
   }
 
   /* Loop over every record that needs updating.  We have to load
@@ -194,42 +219,28 @@ void sqliteUpdate(
   ** Also, the old data is needed to delete the old index entires.
   ** So make the cursor point at the old record.
   */
-  end = sqliteVdbeMakeLabel(v);
-  addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
+  addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
   sqliteVdbeAddOp(v, OP_Dup, 0, 0);
   sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
 
-  /* Delete the old indices for the current record.
-  */
-  for(i=0; i<nIdx; i++){
-    sqliteVdbeAddOp(v, OP_Dup, 0, 0);
-    pIdx = apIdx[i];
-    for(j=0; j<pIdx->nColumn; j++){
-      int x = pIdx->aiColumn[j];
-      if( x==pTab->iPKey ){
-        sqliteVdbeAddOp(v, OP_Dup, j, 0);
-      }else{
-        sqliteVdbeAddOp(v, OP_Column, base, x);
-      }
-    }
-    sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
-    sqliteVdbeAddOp(v, OP_IdxDelete, base+i+1, 0);
-  }
-
-  /* If changing the record number, remove the old record number
-  ** from the top of the stack and replace it with the new one.
+  /* If the record number will change, push the record number as it
+  ** will be after the update. (The old record number is currently
+  ** on top of the stack.)
   */
   if( chngRecno ){
-    sqliteVdbeAddOp(v, OP_Pop, 1, 0);
-    sqliteExprCode(pParse, pRecnoExpr);
-    sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
+    if( pTab->iPKey<0 || (j = aXRef[pTab->iPKey])<0 ){
+      sqliteVdbeAddOp(v, OP_Dup, 0, 0);
+    }else{
+      sqliteExprCode(pParse, pChanges->a[j].pExpr);
+      sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
+    }
   }
 
   /* Compute new data for this record.  
   */
   for(i=0; i<pTab->nCol; i++){
     if( i==pTab->iPKey ){
-      sqliteVdbeAddOp(v, OP_Dup, i, 0);
+      sqliteVdbeAddOp(v, OP_String, 0, 0);
       continue;
     }
     j = aXRef[i];
@@ -240,35 +251,26 @@ void sqliteUpdate(
     }
   }
 
+  /* Do constraint checks
+  */
+  sqliteGenerateConstraintChecks(pParse, pTab, base, aIdxUsed, chngRecno,
+                                 onError, addr,1);
+
+  /* Delete the old indices for the current record.
+  */
+  sqliteGenerateRowIndexDelete(v, pTab, base, aIdxUsed);
+
   /* If changing the record number, delete the old record.
   */
   if( chngRecno ){
     sqliteVdbeAddOp(v, OP_Delete, 0, 0);
   }
 
-  /* Insert new index entries that correspond to the new data
-  */
-  for(i=0; i<nIdx; i++){
-    sqliteVdbeAddOp(v, OP_Dup, pTab->nCol, 0); /* The KEY */
-    pIdx = apIdx[i];
-    for(j=0; j<pIdx->nColumn; j++){
-      int idx = pIdx->aiColumn[j];
-      if( idx==pTab->iPKey ){
-        sqliteVdbeAddOp(v, OP_Dup, j, 0);
-      }else{
-        sqliteVdbeAddOp(v, OP_Dup, j+pTab->nCol-idx, 0);
-      }
-    }
-    sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
-    sqliteVdbeAddOp(v, OP_IdxPut, base+i+1, pIdx->isUnique);
-  }
-
-  /* Write the new data back into the database.
+  /* Create the new index entries and the new record.
   */
-  sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
-  sqliteVdbeAddOp(v, OP_PutIntKey, base, 0);
+  sqliteCompleteInsertion(pParse, pTab, base, aIdxUsed, chngRecno);
 
-  /* Increment the count of rows affected by the update
+  /* Increment the row counter 
   */
   if( db->flags & SQLITE_CountRows ){
     sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
@@ -278,7 +280,7 @@ void sqliteUpdate(
   ** all record selected by the WHERE clause have been updated.
   */
   sqliteVdbeAddOp(v, OP_Goto, 0, addr);
-  sqliteVdbeResolveLabel(v, end);
+  sqliteVdbeChangeP2(v, addr, sqliteVdbeCurrentAddr(v));
   sqliteVdbeAddOp(v, OP_ListReset, 0, 0);
   if( (db->flags & SQLITE_InTrans)==0 ){
     sqliteVdbeAddOp(v, OP_Commit, 0, 0);
index 2e5251284d419e5ddf38bf3575b90fe0afea7964..40b5e5d02ca753ca10d4f98110252ee9c9120758 100644 (file)
@@ -30,7 +30,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.109 2002/01/29 18:41:25 drh Exp $
+** $Id: vdbe.c,v 1.110 2002/01/29 23:07:02 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -1299,9 +1299,7 @@ case OP_Pull: {
 case OP_Push: {
   int from = p->tos;
   int to = p->tos - pOp->p1;
-  int i;
-  Stack ts;
-  char *tz;
+
   VERIFY( if( to<0 ) goto not_enough_stack; )
   if( aStack[to].flags & STK_Dyn ){
     sqliteFree(zStack[to]);
@@ -2756,35 +2754,53 @@ case OP_Found: {
 
 /* Opcode: IsUnique P1 P2 *
 **
-** The top of the stack is an index key created using MakeIdxKey.  If
-** there does not exist an entry in P1 that exactly matches the top of
-** the stack, then jump immediately to P2.  If there are no entries
-** in P1 that match all but the last four bytes of the top of the stack
-** then also jump to P2.  The index key on the top of the stack is
-** unchanged.
+** The top of the stack is an integer record number.  Call this
+** record number R.  The next on the stack is an index key created
+** using MakeIdxKey.  Call it K.  This instruction pops R from the
+** stack but it leaves K unchanged.
 **
-** If there is an entry in P1 which differs from the index key on the
-** top of the stack only in the last four bytes, then do not jump. 
-** Instead, push the last four bytes of the existing P1 entry onto the
-** stack and fall through.  This new stack element is the record number
-** of an existing entry this preventing the index key on the stack from
-** being a unique key.
+** P1 is an index.  So all but the last four bytes of K are an
+** index string.  The last four bytes of K are a record number.
+**
+** This instruction asks if there is an entry in P1 where the
+** index string matches K but the record number is different
+** from R.  If there is no such entry, then there is an immediate
+** jump to P2.  If any entry does exist where the index string
+** matches K but the record number is not R, then the record
+** number for that entry is pushed onto the stack and control
+** falls through to the next instruction.
 **
 ** See also: Distinct, NotFound, NotExists
 */
 case OP_IsUnique: {
   int i = pOp->p1;
   int tos = p->tos;
+  int nos = tos-1;
   BtCursor *pCrsr;
+  int R;
 
-  VERIFY( if( tos<0 ) goto not_enough_stack; )
+  /* Pop the value R off the top of the stack
+  */
+  VERIFY( if( nos<0 ) goto not_enough_stack; )
+  Integerify(p, tos);
+  R = aStack[tos].i;   
+  POPSTACK;
   if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
     int res, rc;
-    int v1, v2;
-    char *zKey = zStack[tos];
-    int nKey = aStack[tos].n;
-    if( Stringify(p, tos) ) goto no_mem;
-    assert( aStack[tos].n >= 4 );
+    int v;         /* The record number on the P1 entry that matches K */
+    char *zKey;    /* The value of K */
+    int nKey;      /* Number of bytes in K */
+
+    /* Make sure K is a string and make zKey point to K
+    */
+    if( Stringify(p, nos) ) goto no_mem;
+    zKey = zStack[nos];
+    nKey = aStack[nos].n;
+    assert( nKey >= 4 );
+
+    /* Search for an entry in P1 where all but the last four bytes match K.
+    ** If there is no such entry, jump immediately to P2.
+    */
     rc = sqliteBtreeMoveto(pCrsr, zKey, nKey-4, &res);
     if( rc!=SQLITE_OK ) goto abort_due_to_error;
     if( res<0 ){
@@ -2800,15 +2816,27 @@ case OP_IsUnique: {
       pc = pOp->p2 - 1;
       break;
     }
-    sqliteBtreeKey(pCrsr, nKey - 4, 4, (char*)&v1);
-    memcpy((char*)&v2, &zKey[nKey-4], 4);
-    if( v1==v2 ){
+
+    /* At this point, pCrsr is pointing to an entry in P1 where all but
+    ** the last for bytes of the key match K.  Check to see if the last
+    ** four bytes of the key are different from R.  If the last four
+    ** bytes equal R then jump immediately to P2.
+    */
+    sqliteBtreeKey(pCrsr, nKey - 4, 4, (char*)&v);
+    v = keyToInt(v);
+    if( v==R ){
       pc = pOp->p2 - 1;
       break;
     }
-    tos = ++p->tos;
+
+    /* The last four bytes of the key are different from R.  Convert the
+    ** last four bytes of the key into an integer and push it onto the
+    ** stack.  (These bytes are the record number of an entry that
+    ** violates a UNIQUE constraint.)
+    */
+    p->tos++;
     VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; )
-    aStack[tos].i = keyToInt(v1);
+    aStack[tos].i = v;
     aStack[tos].flags = STK_Int;
   }
   break;
@@ -2830,14 +2858,13 @@ case OP_IsUnique: {
 case OP_NotExists: {
   int i = pOp->p1;
   int tos = p->tos;
-  int alreadyExists = 0;
-  Cursor *pC;
+  BtCursor *pCrsr;
   VERIFY( if( tos<0 ) goto not_enough_stack; )
-  if( VERIFY( i>=0 && i<p->nCursor && ) (pC = &p->aCsr[i])->pCursor!=0 ){
+  if( VERIFY( i>=0 && i<p->nCursor && ) (pCrsr = p->aCsr[i].pCursor)!=0 ){
     int res, rx, iKey;
     assert( aStack[tos].flags & STK_Int );
     iKey = intToKey(aStack[tos].i);
-    rx = sqliteBtreeMoveto(pC->pCursor, (char*)&iKey, sizeof(int), &res);
+    rx = sqliteBtreeMoveto(pCrsr, (char*)&iKey, sizeof(int), &res);
     if( rx!=SQLITE_OK || res!=0 ){
        pc = pOp->p2 - 1;
     }
@@ -2910,7 +2937,7 @@ case OP_NewRecno: {
   break;
 }
 
-/* Opcode: PutIK P1 P2 *
+/* Opcode: PutIntKey P1 P2 *
 **
 ** Write an entry into the database file P1.  A new entry is
 ** created if it doesn't already exist or the data for an existing
@@ -2921,7 +2948,7 @@ case OP_NewRecno: {
 ** If P2==1 then overwriting is prohibited.  If a prior entry with
 ** the same key exists, an SQLITE_CONSTRAINT exception is raised.
 */
-/* Opcode: PutSK P1 P2 *
+/* Opcode: PutStrKey P1 P2 *
 **
 ** Write an entry into the database file P1.  A new entry is
 ** created if it doesn't already exist or the data for an existing
index c3adfd0cf76933d6497978855b17b4d55ac8d8a5..d28efd4726fa033501cb8f7548b254e67ba03c6f 100644 (file)
@@ -13,7 +13,7 @@
 # This file implements tests for the special processing associated
 # with INTEGER PRIMARY KEY columns.
 #
-# $Id: intpkey.test,v 1.6 2002/01/16 21:00:28 drh Exp $
+# $Id: intpkey.test,v 1.7 2002/01/29 23:07:02 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -179,6 +179,7 @@ do_test intpkey-2.1.4 {
     SELECT * FROM t1 WHERE b>='y' AND rowid<10
   }
 } {-3 y z}
+
 do_test intpkey-2.2 {
   execsql {
     UPDATE t1 SET a=8 WHERE b=='y';