]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Beginning to insert the infrastructure for ON CONFLICT clauses. (CVS 355)
authordrh <drh@noemail.net>
Tue, 29 Jan 2002 18:41:24 +0000 (18:41 +0000)
committerdrh <drh@noemail.net>
Tue, 29 Jan 2002 18:41:24 +0000 (18:41 +0000)
FossilOrigin-Name: e00a9ff8f99dd58f7cb19a6195fac21f4c8b4af9

13 files changed:
VERSION
manifest
manifest.uuid
src/build.c
src/delete.c
src/insert.c
src/parse.y
src/sqliteInt.h
src/tokenize.c
src/update.c
src/vdbe.c
src/vdbe.h
www/index.tcl

diff --git a/VERSION b/VERSION
index 21bb5e156fbe2f70cdf3f02a8123b099130eeacb..276cbf9e2858c779297bb9f73b34170302949ec4 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.2.5
+2.3.0
index 909fd4ece5fecc60551569ad7b90b7a1aab22a2a..eedd784ec711ad8657afe6a9dcb271ce90bb1416 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,9 +1,9 @@
-C Version\s2.2.5\s(CVS\s448)
-D 2002-01-28T16:00:00
+C Beginning\sto\sinsert\sthe\sinfrastructure\sfor\sON\sCONFLICT\sclauses.\s(CVS\s355)
+D 2002-01-29T18:41:24
 F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af
 F Makefile.template 3e26a3b9e7aee1b811deaf673e8d8973bdb3f22d
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
-F VERSION 79f8cff9811acbf454323afd9f2fa6cd2a8b7674
+F VERSION 34f7c904a063d2d3791c38521e40ae1648cd2e7e
 F aclocal.m4 11faa843caa38fd451bc6aeb43e248d1723a269d
 F config.guess f38b1e93d1e0fa6f5a6913e9e7b12774b9232588
 F config.log 6a73d03433669b10a3f0c221198c3f26b9413914
@@ -21,36 +21,36 @@ F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
 F src/btree.c c796e387da340cb628dc1e41f684fc20253f561e
 F src/btree.h 9ead7f54c270d8a554e59352ca7318fdaf411390
-F src/build.c 3f40a6e6cea4180fb742a3d0ce71f06df8121cab
-F src/delete.c cc200609f927ee8fefdda5d11d3f3b2288493c0f
+F src/build.c c5023252c4d0ed19067f2118639b8d9f14f3f91a
+F src/delete.c 2d1abc228be80a3c41f29f3312534dd0c9cd5066
 F src/expr.c 4cae8bf44d5732182e5e8c25b4552c05ea55593e
 F src/hash.c 8f7c740ef2eaaa8decfa8751f2be30680b123e46
 F src/hash.h a5f5b3ce2d086a172c5879b0b06a27a82eac9fac
-F src/insert.c e3a3b5a1d46a02778feb3e2ccd9a6989201c03b5
+F src/insert.c 475316610462d1b6881f45565bbebf3209f1088c
 F src/main.c 0205771a6c31a9858ff131fc1e797b589afb76bf
 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
 F src/os.c c615faa4d23e742e0650e0751a6ad2a18438ad53
 F src/os.h 5405a5695bf16889d4fc6caf9d42043caa41c269
 F src/pager.c 1e80a3ba731e454df6bd2e58d32eeba7dd65121b
 F src/pager.h f78d064c780855ff70beacbeba0e2324471b26fe
-F src/parse.y f3fc4fb5766393003577bd175eb611495f6efd9f
+F src/parse.y fd79a09265b4703e37a62084db6fe67c834defb4
 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
 F src/random.c f6b36bec5ebd3edb3440224bf5bf811fe4ac9a1b
 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 3274f3039db3164ba8c31acce027ea6408f247b3
+F src/sqliteInt.h f4dc7f359549f70cd0fd6c68ae054d6986ad3afc
 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 830e9ef684334070a26583d94770bb869e2727bf
-F src/update.c 6f87a9aa0b3ec0dfec0b0758104461445e701fdb
+F src/tokenize.c 1199b96a82d5c41509b5e24fc9faa1852b7f3135
+F src/update.c 2ece9c433968f9268d93bcc2f0ece409ab4dc296
 F src/util.c 8f8973dd55a6ec63be9632fc5de86965c99d6327
-F src/vdbe.c 71c0b7d368dd4c58867d7e577df0ee5404e25722
-F src/vdbe.h 22d4df31cc16ca50b66b8125caec3495e5b407b2
+F src/vdbe.c 5e51f9dfc34ee329117f59c28410ea9d4224402a
+F src/vdbe.h 5b1bd518126fc5a30e6ea13fe11de931b32c4b59
 F src/where.c 2dda39367f193194e4c7d2e0dcab31527d9d8aba
 F test/all.test 2a51e5395ac7c2c539689b123b9782a05e3837fe
 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
@@ -111,7 +111,7 @@ F www/download.tcl 1ea61f9d89a2a5a9b2cee36b0d5cf97321bdefe0
 F www/dynload.tcl 02eb8273aa78cfa9070dd4501dca937fb22b466c
 F www/faq.tcl 32cbc134879871604070d4cc3a32e73fb22a35f9
 F www/formatchng.tcl d96e5e937dcbbebd481720aa08745ca5a906a63f
-F www/index.tcl 571c585a5c0b577db2fe7cb88cf1f12cccd96ceb
+F www/index.tcl 748614d8208c761ed3840e7958b8eed04de81822
 F www/lang.tcl 6843fd3f85cba95fd199a350533ce742c706603c
 F www/mingw.tcl f1c7c0a7f53387dd9bb4f8c7e8571b7561510ebc
 F www/opcode.tcl bdec8ef9f100dbd87bbef8976c54b88e43fd8ccc
@@ -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 dbcfe198fbaa155874ef82a96b6a4b993ccf3931
-R 8bfbd1ab9c2165331e9740c7a67704b7
+P af3bb80810c6cd302a884a106cc70591a738e102
+R 82f94ecaa281bc36c2caa4b6f3c6c838
 U drh
-Z 6a85a2ce8e8f7c7177b049892c502454
+Z 16309c459073f336b26332a0e349efd1
index 0519c9d066bbadb0cbf42d1ebdf4a2f57a05739d..8b9c80c5d465a5b8a7e66ef1db132371c72c5f17 100644 (file)
@@ -1 +1 @@
-af3bb80810c6cd302a884a106cc70591a738e102
\ No newline at end of file
+e00a9ff8f99dd58f7cb19a6195fac21f4c8b4af9
\ No newline at end of file
index 22e68df83c2940e829f881eedde69cbf433f4cae..473b55e781b037448fdb15076277a890aa249b0b 100644 (file)
@@ -25,7 +25,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.66 2002/01/28 15:53:05 drh Exp $
+** $Id: build.c,v 1.67 2002/01/29 18:41:25 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -517,12 +517,13 @@ void sqliteAddColumn(Parse *pParse, Token *pName){
 ** been seen on a column.  This routine sets the notNull flag on
 ** the column currently under construction.
 */
-void sqliteAddNotNull(Parse *pParse){
+void sqliteAddNotNull(Parse *pParse, int onError){
   Table *p;
   int i;
   if( (p = pParse->pNewTable)==0 ) return;
   i = p->nCol-1;
-  if( i>=0 ) p->aCol[i].notNull = 1;
+  if( onError==OE_Default ) onError = OE_Abort;
+  if( i>=0 ) p->aCol[i].notNull = onError;
 }
 
 /*
@@ -599,7 +600,7 @@ void sqliteAddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){
 ** If the key is not an INTEGER PRIMARY KEY, then create a unique
 ** index for the key.  No index is created for INTEGER PRIMARY KEYs.
 */
-void sqliteAddPrimaryKey(Parse *pParse, IdList *pList){
+void sqliteAddPrimaryKey(Parse *pParse, IdList *pList, int onError){
   Table *pTab = pParse->pNewTable;
   char *zType = 0;
   int iCol = -1;
@@ -621,11 +622,13 @@ void sqliteAddPrimaryKey(Parse *pParse, IdList *pList){
   if( iCol>=0 && iCol<pTab->nCol ){
     zType = pTab->aCol[iCol].zType;
   }
+  if( onError==OE_Default ) onError = OE_Abort;
   if( pParse->db->file_format>=1 && 
            zType && sqliteStrICmp(zType, "INTEGER")==0 ){
     pTab->iPKey = iCol;
+    pTab->keyConf = onError;
   }else{
-    sqliteCreateIndex(pParse, 0, 0, pList, 1, 0, 0);
+    sqliteCreateIndex(pParse, 0, 0, pList, onError, 0, 0);
   }
 }
 
@@ -850,7 +853,7 @@ void sqliteCreateIndex(
   Token *pName,    /* Name of the index.  May be NULL */
   Token *pTable,   /* Name of the table to index.  Use pParse->pNewTable if 0 */
   IdList *pList,   /* A list of columns to be indexed */
-  int isUnique,    /* True if all entries in this index must be unique */
+  int onError,     /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
   Token *pStart,   /* The CREATE token that begins a CREATE TABLE statement */
   Token *pEnd      /* The ")" that closes the CREATE INDEX statement */
 ){
@@ -967,7 +970,7 @@ void sqliteCreateIndex(
   strcpy(pIndex->zName, zName);
   pIndex->pTable = pTab;
   pIndex->nColumn = pList->nId;
-  pIndex->isUnique = isUnique;
+  pIndex->onError = pIndex->isUnique = onError;
 
   /* Scan the names of the columns of the table to be indexed and
   ** load the column indices into the Index structure.  Report an error
@@ -1000,8 +1003,24 @@ void sqliteCreateIndex(
     }
     db->flags |= SQLITE_InternChanges;
   }
-  pIndex->pNext = pTab->pIndex;
-  pTab->pIndex = pIndex;
+
+  /* When adding an index to the list of indices for a table, make
+  ** sure all indices labeled OE_Replace come after all those labeled
+  ** OE_Ignore.  This is necessary for the correct operation of UPDATE
+  ** and INSERT.
+  */
+  if( onError!=OE_Replace || pTab->pIndex==0
+       || pTab->pIndex->onError==OE_Replace){
+    pIndex->pNext = pTab->pIndex;
+    pTab->pIndex = pIndex;
+  }else{
+    Index *pOther = pTab->pIndex;
+    while( pOther->pNext && pOther->pNext->onError!=OE_Replace ){
+      pOther = pOther->pNext;
+    }
+    pIndex->pNext = pOther->pNext;
+    pOther->pNext = pIndex;
+  }
 
   /* If the initFlag is 1 it means we are reading the SQL off the
   ** "sqlite_master" table on the disk.  So do not write to the disk
@@ -1086,7 +1105,7 @@ void sqliteCreateIndex(
         sqliteVdbeAddOp(v, OP_Column, 2, pIndex->aiColumn[i]);
       }
       sqliteVdbeAddOp(v, OP_MakeIdxKey, pIndex->nColumn, 0);
-      sqliteVdbeAddOp(v, OP_IdxPut, 1, pIndex->isUnique);
+      sqliteVdbeAddOp(v, OP_IdxPut, 1, pIndex->onError!=OE_None);
       sqliteVdbeAddOp(v, OP_Next, 2, lbl1);
       sqliteVdbeResolveLabel(v, lbl2);
       sqliteVdbeAddOp(v, OP_Close, 2, 0);
@@ -1392,7 +1411,7 @@ void sqliteCopy(
         sqliteVdbeAddOp(v, OP_FileColumn, pIdx->aiColumn[j], 0);
       }
       sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
-      sqliteVdbeAddOp(v, OP_IdxPut, i, pIdx->isUnique);
+      sqliteVdbeAddOp(v, OP_IdxPut, i, pIdx->onError!=OE_None);
     }
     sqliteVdbeAddOp(v, OP_Goto, 0, addr);
     sqliteVdbeResolveLabel(v, end);
@@ -1682,7 +1701,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
         sqliteVdbeAddOp(v, OP_Integer, i, 0);
         sqliteVdbeAddOp(v, OP_String, 0, 0);
         sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
-        sqliteVdbeAddOp(v, OP_Integer, pIdx->isUnique, 0);
+        sqliteVdbeAddOp(v, OP_Integer, pIdx->onError!=OE_None, 0);
         sqliteVdbeAddOp(v, OP_Callback, 3, 0);
        ++i;
        pIdx = pIdx->pNext;
index 70638afbaf0c5165e04f2ee13d7cada06e98da34..7d19b2b0e3357502e1204b254da821e366ecc3c5 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.23 2002/01/22 03:13:42 drh Exp $
+** $Id: delete.c,v 1.24 2002/01/29 18:41:25 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -151,24 +151,7 @@ void sqliteDeleteFrom(
     }
     end = sqliteVdbeMakeLabel(v);
     addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
-    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);
-          }
-        }
-        sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
-        sqliteVdbeAddOp(v, OP_IdxDelete, base+i, 0);
-      }
-    }
-    sqliteVdbeAddOp(v, OP_Delete, base, 0);
+    sqliteGenerateRowDelete(v, pTab, base);
     sqliteVdbeAddOp(v, OP_Goto, 0, addr);
     sqliteVdbeResolveLabel(v, end);
     sqliteVdbeAddOp(v, OP_ListReset, 0, 0);
@@ -192,3 +175,51 @@ delete_from_cleanup:
   sqliteExprDelete(pWhere);
   return;
 }
+
+/*
+** This routine generates VDBE code that causes a single row of a
+** single table to be deleted.
+**
+** 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 record number of the row to be deleted must be on the top
+**       of the stack.
+**
+** This routine pops the top of the stack to remove the record number
+** and then generates code to remove both the table record and all index
+** entries that point to that record.
+*/
+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 */
+){
+  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);
+        }
+      }
+      sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
+      sqliteVdbeAddOp(v, OP_IdxDelete, base+i, 0);
+    }
+  }
+  sqliteVdbeAddOp(v, OP_Delete, base, 0);
+}
index c24d019e535212685d403034fca7c27fcd7b8b62..f9b8b1791c4e1182f2353a56ddd521ab9a8e55f4 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.33 2002/01/28 15:53:05 drh Exp $
+** $Id: insert.c,v 1.34 2002/01/29 18:41:25 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -36,7 +36,8 @@ void sqliteInsert(
   Token *pTableName,    /* Name of table into which we are inserting */
   ExprList *pList,      /* List of values to be inserted */
   Select *pSelect,      /* A SELECT statement to use as the data source */
-  IdList *pColumn       /* Column names corresponding to IDLIST. */
+  IdList *pColumn,      /* Column names corresponding to IDLIST. */
+  int onError           /* How to handle constraint errors */
 ){
   Table *pTab;          /* The table to insert into */
   char *zTab;           /* Name of the table into which we are inserting */
@@ -329,3 +330,171 @@ insert_cleanup:
   if( pSelect ) sqliteSelectDelete(pSelect);
   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.
+**
+** This routine also generates code to check constraints.  NOT NULL,
+** CHECK, and UNIQUE constraints are all checked.  If a constraint fails,
+** then the appropriate action is performed.  The default action is to
+** execute OP_Halt to abort the transaction and cause sqlite_exec() to
+** return SQLITE_CONSTRAINT.  This is the so-called "ABORT" action.
+** Other actions are REPLACE and IGNORE.  The following table summarizes
+** what happens.
+**
+**  Constraint type  Action       What Happens
+**  ---------------  ----------   ----------------------------------------
+**  any              ABORT        The current transaction is rolled back and
+**                                sqlite_exec() returns immediately with a
+**                                return code of SQLITE_CONSTRAINT.
+**
+**  any              IGNORE       The record number and data is popped from
+**                                the stack and there is an immediate jump
+**                                to label ignoreDest.
+**
+**  NOT NULL         REPLACE      The NULL value is replace by the default
+**                                value for that column.  If the default value
+**                                is NULL, the action is the same as ABORT.
+**
+**  UNIQUE           REPLACE      The other row that conflicts with the row
+**                                being inserted is removed.
+**
+**  CHECK            REPLACE      Illegal.  The results in an exception.
+**
+** The action to take is determined by the constraint itself if
+** overrideError is OE_Default.  Otherwise, overrideError determines
+** which action to use.
+**
+** The calling routine must an open read/write cursor for pTab with
+** cursor number "base".  All indices of pTab must also have open
+** read/write cursors with cursor number base+i for the i-th cursor.
+** Except, if there is no possibility of a REPLACE action then
+** cursors do not need to be open for indices where aIdxUsed[i]==0.
+**
+** If the isUpdate flag is true, it means that the "base" cursor is
+** initially pointing to an entry that is being updated.  The isUpdate
+** flag causes extra code to be generated so that the "base" cursor
+** is still pointing at the same entry after the routine returns.
+** Without the isUpdate flag, the "base" cursor might be moved.
+*/
+void sqliteGenerateConstraintChecks(
+  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 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 */
+){
+  int i;
+  Vdbe *v;
+  int nCol;
+  int onError;
+  int addr;
+  int extra;
+  char *pToFree = 0;
+  int seenIgnore = 0;
+
+  v = sqliteGetVdbe(pParse);
+  assert( v!=0 );
+  nCol = pTab->nCol;
+
+  /* Test all NOT NULL constraints.
+  */
+  for(i=0; i<nCol; i++){
+    onError = pTab->aCol[i].notNull;
+    if( i==iPKey || onError==OE_None ) continue;
+    if( overrideError!=OE_Default ){
+      onError = overrideError;
+    }
+    if( onError==OE_Replace && pTab->aCol[i].zDflt==0 ){
+      onError = OE_Abort;
+    }
+    addr = sqliteVdbeAddOp(v, OP_Dup, nCol-i, 1);
+    sqliteVdbeAddOp(v, OP_NotNull, 0, addr+1+(onError!=OE_Abort));
+    switch( onError ){
+      case OE_Abort: {
+        sqliteVdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, 0);
+        break;
+      }
+      case OE_Ignore: {
+        sqliteVdbeAddOp(v, OP_Pop, nCol+1, 0);
+        sqliteVdbeAddOp(v, OP_GoTo, 0, ignoreDest);
+        break;
+      }
+      case OE_Replace: {
+        sqliteVdbeAddOp(v, OP_String, 0, 0);
+        sqliteVdbeChangeP3(v, -1, pTab->aCol[i].zDflt, P3_STATIC);
+        sqliteVdbeAddOp(v, OP_Push, nCol-i, 0);
+        break;
+      }
+      default: {
+        CANT_HAPPEN;
+      }
+    }
+  }
+
+  /* Test all CHECK constraints
+  */
+
+  /* Test all UNIQUE constraints.  Add index records as we go.
+  */
+  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);
+      }else{
+        sqliteVdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 0);
+      }
+    }
+    sqliteVdbeAddOp(v, OP_MakeIdxKey, pIdx->nColumn, 0);
+    onError = pIdx->onError;
+    if( onError==OE_None ) continue;
+    if( overrideError!=OE_Default ){
+      onError = overrideError;
+    }
+    jumpInst = sqliteVdbeAddOp(v, OP_IsUnique, iCur, 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);
+        sqliteVdbeAddOp(v, OP_Goto, 0, ignoreDest);
+        seenIgnore = 1;
+        break;
+      }
+      case OE_Replace: {
+        assert( seenIgnore==0 );
+        sqliteGenerateRowDelete(v, pTab, base);
+        if( isUpdate ){
+          sqliteVdbeAddOp(v, OP_Dup, nCol+extra+2, 1);
+          sqliteVdbeAddOp(v, OP_Moveto, base, 0);
+        }
+        break;
+      }
+      default: CANT_HAPPEN;
+    }
+    contAddr = sqliteVdbeCurrentAddr(v);
+    sqliteVdbeChangeP2(v, jumpInst, contAddr);
+  }
+}
+#endif
index e23104ff1507df4694e56ba9c70fc60f6585b3c8..03bd72dfdd0dd6f68639151c21ed9706414824aa 100644 (file)
@@ -14,7 +14,7 @@
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.42 2002/01/06 17:07:40 drh Exp $
+** @(#) $Id: parse.y,v 1.43 2002/01/29 18:41:25 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -103,6 +103,10 @@ id(A) ::= ID(X).         {A = X;}
 id(A) ::= TEMP(X).       {A = X;}
 id(A) ::= OFFSET(X).     {A = X;}
 id(A) ::= KEY(X).        {A = X;}
+id(A) ::= ABORT(X).      {A = X;}
+id(A) ::= IGNORE(X).     {A = X;}
+id(A) ::= REPLACE(X).    {A = X;}
+id(A) ::= CONFLICT(X).   {A = X;}
 
 // And "ids" is an identifer-or-string.
 //
@@ -138,10 +142,10 @@ carg ::= DEFAULT NULL.
 // In addition to the type name, we also care about the primary key and
 // UNIQUE constraints.
 //
-ccons ::= NOT NULL.                  {sqliteAddNotNull(pParse);}
-ccons ::= PRIMARY KEY sortorder.     {sqliteAddPrimaryKey(pParse, 0);}
-ccons ::= UNIQUE.                    {sqliteCreateIndex(pParse,0,0,0,1,0,0);}
-ccons ::= CHECK LP expr RP.
+ccons ::= NOT NULL onconf(R).               {sqliteAddNotNull(pParse, R);}
+ccons ::= PRIMARY KEY sortorder onconf(R).  {sqliteAddPrimaryKey(pParse,0,R);}
+ccons ::= UNIQUE onconf(R).            {sqliteCreateIndex(pParse,0,0,0,R,0,0);}
+ccons ::= CHECK LP expr RP onconf.
 
 // For the time being, the only constraint we care about is the primary
 // key and UNIQUE.  Both create indices.
@@ -152,9 +156,25 @@ conslist ::= conslist COMMA tcons.
 conslist ::= conslist tcons.
 conslist ::= tcons.
 tcons ::= CONSTRAINT ids.
-tcons ::= PRIMARY KEY LP idxlist(X) RP. {sqliteAddPrimaryKey(pParse,X);}
-tcons ::= UNIQUE LP idxlist(X) RP.      {sqliteCreateIndex(pParse,0,0,X,1,0,0);}
-tcons ::= CHECK expr.
+tcons ::= PRIMARY KEY LP idxlist(X) RP onconf(R).
+                                             {sqliteAddPrimaryKey(pParse,X,R);}
+tcons ::= UNIQUE LP idxlist(X) RP onconf(R).
+                                       {sqliteCreateIndex(pParse,0,0,X,R,0,0);}
+tcons ::= CHECK expr onconf.
+
+// The following is a non-standard extension that allows us to declare the
+// default behavior when there is a constraint conflict.
+//
+%type onconf {int}
+%type onconf_u {int}
+%type confresolve {int}
+onconf(A) ::= confresolve(X).                { A = X; }
+onconf(A) ::= onconf_u(X).                   { A = X; }
+onconf_u(A) ::= ON CONFLICT confresolve(X).  { A = X; }
+onconf_u(A) ::= .                            { A = OE_Default; }
+confresolve(A) ::= ABORT.                    { A = OE_Abort; }
+confresolve(A) ::= IGNORE.                   { A = OE_Ignore; }
+confresolve(A) ::= REPLACE.                  { A = OE_Replace; }
 
 ////////////////////////// The DROP TABLE /////////////////////////////////////
 //
@@ -293,8 +313,8 @@ where_opt(A) ::= WHERE expr(X).       {A = X;}
 
 ////////////////////////// The UPDATE command ////////////////////////////////
 //
-cmd ::= UPDATE ids(X) SET setlist(Y) where_opt(Z).
-    {sqliteUpdate(pParse,&X,Y,Z);}
+cmd ::= UPDATE onconf_u(R) ids(X) SET setlist(Y) where_opt(Z).
+    {sqliteUpdate(pParse,&X,Y,Z,R);}
 
 setlist(A) ::= setlist(Z) COMMA ids(X) EQ expr(Y).
     {A = sqliteExprListAppend(Z,Y,&X);}
@@ -302,10 +322,10 @@ setlist(A) ::= ids(X) EQ expr(Y).   {A = sqliteExprListAppend(0,Y,&X);}
 
 ////////////////////////// The INSERT command /////////////////////////////////
 //
-cmd ::= INSERT INTO ids(X) inscollist_opt(F) VALUES LP itemlist(Y) RP.
-               {sqliteInsert(pParse, &X, Y, 0, F);}
-cmd ::= INSERT INTO ids(X) inscollist_opt(F) select(S).
-               {sqliteInsert(pParse, &X, 0, S, F);}
+cmd ::= INSERT onconf(R) INTO ids(X) inscollist_opt(F) VALUES LP itemlist(Y) RP.
+               {sqliteInsert(pParse, &X, Y, 0, F, R);}
+cmd ::= INSERT onconf(R) INTO ids(X) inscollist_opt(F) select(S).
+               {sqliteInsert(pParse, &X, 0, S, F, R);}
 
 
 %type itemlist {ExprList*}
@@ -499,12 +519,16 @@ expritem(A) ::= .                       {A = 0;}
 
 ///////////////////////////// The CREATE INDEX command ///////////////////////
 //
-cmd ::= CREATE(S) uniqueflag(U) INDEX ids(X) ON ids(Y) LP idxlist(Z) RP(E).
-    {sqliteCreateIndex(pParse, &X, &Y, Z, U, &S, &E);}
+cmd ::= CREATE(S) uniqueflag(U) INDEX ids(X)
+        ON ids(Y) LP idxlist(Z) RP(E) onconf(R). {
+  if( U!=OE_None ) U = R;
+  if( U==OE_Default) U = OE_Abort;
+  sqliteCreateIndex(pParse, &X, &Y, Z, U, &S, &E);
+}
 
 %type uniqueflag {int}
-uniqueflag(A) ::= UNIQUE.   { A = 1; }
-uniqueflag(A) ::= .         { A = 0; }
+uniqueflag(A) ::= UNIQUE.  { A = OE_Abort; }
+uniqueflag(A) ::= .        { A = OE_None; }
 
 %type idxlist {IdList*}
 %destructor idxlist {sqliteIdListDelete($$);}
index aeeba58fc082d3e668b4bde3bdf2391dddfdce16..ca4e7616ea0f534dd50751d5209e02e2fa8da4b5 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.79 2002/01/22 14:11:29 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.80 2002/01/29 18:41:25 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -233,8 +233,24 @@ struct Table {
   u8 isCommit;     /* True if creation of this table has been committed */
   u8 isTemp;       /* True if stored in db->pBeTemp instead of db->pBe */
   u8 hasPrimKey;   /* True if there exists a primary key */
+  u8 keyConf;      /* What to do in case of uniqueness conflict on iPKey */
 };
 
+/*
+** SQLite supports three different ways to resolve a UNIQUE contraint
+** error.  (1) It can abort the transaction return SQLITE_CONSTRAINT.
+** (2) It can decide to not do the INSERT or UPDATE that was causing
+** the constraint violation. (3) It can delete existing records from
+** the table so that the pending INSERT or UPDATE will work without
+** a constraint error.  The following there symbolic values are used
+** to record which type of action to take.
+*/
+#define OE_None    0   /* There is no constraint to check */
+#define OE_Abort   1   /* Abort and rollback. */
+#define OE_Ignore  2   /* Ignore the error. Do not do the INSERT or UPDATE */
+#define OE_Replace 3   /* Delete existing record, then do INSERT or UPDATE */
+#define OE_Default 9   /* Do whatever the default action is */
+
 /*
 ** Each SQL index is represented in memory by an
 ** instance of the following structure.
@@ -260,9 +276,10 @@ struct Index {
   int *aiColumn;   /* Which columns are used by this index.  1st is 0 */
   Table *pTable;   /* The SQL table being indexed */
   int tnum;        /* Page containing root of this index in database file */
-  u8 isUnique;     /* True if keys must all be unique */
+  u8 isUnique;     /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
   u8 isCommit;     /* True if creation of this index has been committed */
   u8 isDropped;    /* True if a DROP INDEX has executed on this index */
+  u8 onError;      /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
   Index *pNext;    /* The next index associated with the same table */
 };
 
@@ -483,13 +500,14 @@ void sqliteCommitInternalChanges(sqlite*);
 void sqliteRollbackInternalChanges(sqlite*);
 void sqliteStartTable(Parse*,Token*,Token*,int);
 void sqliteAddColumn(Parse*,Token*);
-void sqliteAddNotNull(Parse*);
+void sqliteAddNotNull(Parse*, int);
+void sqliteAddPrimaryKey(Parse*, IdList*, int);
 void sqliteAddColumnType(Parse*,Token*,Token*);
 void sqliteAddDefaultValue(Parse*,Token*,int);
 void sqliteEndTable(Parse*,Token*);
 void sqliteDropTable(Parse*, Token*);
 void sqliteDeleteTable(sqlite*, Table*);
-void sqliteInsert(Parse*, Token*, ExprList*, Select*, IdList*);
+void sqliteInsert(Parse*, Token*, ExprList*, Select*, IdList*, int);
 IdList *sqliteIdListAppend(IdList*, Token*);
 void sqliteIdListAddAlias(IdList*, Token*);
 void sqliteIdListDelete(IdList*);
@@ -500,7 +518,7 @@ Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*,
                         int,int,int);
 void sqliteSelectDelete(Select*);
 void sqliteDeleteFrom(Parse*, Token*, Expr*);
-void sqliteUpdate(Parse*, Token*, ExprList*, Expr*);
+void sqliteUpdate(Parse*, Token*, ExprList*, Expr*, int);
 WhereInfo *sqliteWhereBegin(Parse*, IdList*, Expr*, int);
 void sqliteWhereEnd(WhereInfo*);
 void sqliteExprCode(Parse*, Expr*);
index 1917c652dcc436fb3ccdc0845d6b4a637007cf26..0a8d0b6913b4aa193faa8f70a2f0b15fb75295f6 100644 (file)
@@ -15,7 +15,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.32 2001/11/06 04:00:19 drh Exp $
+** $Id: tokenize.c,v 1.33 2002/01/29 18:41:25 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -38,6 +38,7 @@ struct Keyword {
 ** These are the keywords
 */
 static Keyword aKeywordTable[] = {
+  { "ABORT",             0, TK_ABORT,            0 },
   { "ALL",               0, TK_ALL,              0 },
   { "AND",               0, TK_AND,              0 },
   { "AS",                0, TK_AS,               0 },
@@ -48,6 +49,7 @@ static Keyword aKeywordTable[] = {
   { "CHECK",             0, TK_CHECK,            0 },
   { "CLUSTER",           0, TK_CLUSTER,          0 },
   { "COMMIT",            0, TK_COMMIT,           0 },
+  { "CONFLICT",          0, TK_CONFLICT,         0 },
   { "CONSTRAINT",        0, TK_CONSTRAINT,       0 },
   { "COPY",              0, TK_COPY,             0 },
   { "CREATE",            0, TK_CREATE,           0 },
@@ -64,6 +66,7 @@ static Keyword aKeywordTable[] = {
   { "GLOB",              0, TK_GLOB,             0 },
   { "GROUP",             0, TK_GROUP,            0 },
   { "HAVING",            0, TK_HAVING,           0 },
+  { "IGNORE",            0, TK_IGNORE,           0 },
   { "IN",                0, TK_IN,               0 },
   { "INDEX",             0, TK_INDEX,            0 },
   { "INSERT",            0, TK_INSERT,           0 },
@@ -83,6 +86,7 @@ static Keyword aKeywordTable[] = {
   { "ORDER",             0, TK_ORDER,            0 },
   { "PRAGMA",            0, TK_PRAGMA,           0 },
   { "PRIMARY",           0, TK_PRIMARY,          0 },
+  { "REPLACE",           0, TK_REPLACE,          0 },
   { "ROLLBACK",          0, TK_ROLLBACK,         0 },
   { "SELECT",            0, TK_SELECT,           0 },
   { "SET",               0, TK_SET,              0 },
index 88df296423c4cdb3e7b9f6f9ddf078ceadc03fe2..e4d170456ad224d3f4b4f447cf83ac0ba5c763f1 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.28 2002/01/28 15:53:05 drh Exp $
+** $Id: update.c,v 1.29 2002/01/29 18:41:25 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -23,7 +23,8 @@ void sqliteUpdate(
   Parse *pParse,         /* The parser context */
   Token *pTableName,     /* The table in which we should change things */
   ExprList *pChanges,    /* Things to be changed */
-  Expr *pWhere           /* The WHERE clause.  May be null */
+  Expr *pWhere,          /* The WHERE clause.  May be null */
+  int onError            /* How to handle constraint errors */
 ){
   int i, j;              /* Loop counters */
   Table *pTab;           /* The table to be updated */
index ce025f4624bc9a5fc3b3fa77fec3e139d15628e0..2e5251284d419e5ddf38bf3575b90fe0afea7964 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.108 2002/01/28 15:53:05 drh Exp $
+** $Id: vdbe.c,v 1.109 2002/01/29 18:41:25 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -382,6 +382,17 @@ void sqliteVdbeChangeP1(Vdbe *p, int addr, int val){
   }
 }
 
+/*
+** Change the value of the P2 operand for a specific instruction.
+** This routine is useful for setting a jump destination.
+*/
+void sqliteVdbeChangeP2(Vdbe *p, int addr, int val){
+  assert( val>=0 );
+  if( p && addr>=0 && p->nOp>addr && p->aOp ){
+    p->aOp[addr].p2 = val;
+  }
+}
+
 /*
 ** Change the value of the P3 operand for a specific instruction.
 ** This routine is useful when a large program is loaded from a
@@ -853,31 +864,31 @@ static char *zOpName[] = { 0,
   "SetCookie",         "VerifyCookie",      "Open",              "OpenTemp",
   "OpenWrite",         "OpenAux",           "OpenWrAux",         "Close",
   "MoveTo",            "NewRecno",          "PutIntKey",         "PutStrKey",
-  "Distinct",          "Found",             "NotFound",          "NotExists",
-  "Delete",            "Column",            "KeyAsData",         "Recno",
-  "FullKey",           "Rewind",            "Next",              "Destroy",
-  "Clear",             "CreateIndex",       "CreateTable",       "Reorganize",
-  "IdxPut",            "IdxDelete",         "IdxRecno",          "IdxGT",
-  "IdxGE",             "MemLoad",           "MemStore",          "ListWrite",
-  "ListRewind",        "ListRead",          "ListReset",         "SortPut",
-  "SortMakeRec",       "SortMakeKey",       "Sort",              "SortNext",
-  "SortCallback",      "SortReset",         "FileOpen",          "FileRead",
-  "FileColumn",        "AggReset",          "AggFocus",          "AggIncr",
-  "AggNext",           "AggSet",            "AggGet",            "SetInsert",
-  "SetFound",          "SetNotFound",       "MakeRecord",        "MakeKey",
-  "MakeIdxKey",        "IncrKey",           "Goto",              "If",
-  "Halt",              "ColumnCount",       "ColumnName",        "Callback",
-  "NullCallback",      "Integer",           "String",            "Pop",
-  "Dup",               "Pull",              "MustBeInt",         "Add",
-  "AddImm",            "Subtract",          "Multiply",          "Divide",
-  "Remainder",         "BitAnd",            "BitOr",             "BitNot",
-  "ShiftLeft",         "ShiftRight",        "AbsValue",          "Precision",
-  "Min",               "Max",               "Like",              "Glob",
-  "Eq",                "Ne",                "Lt",                "Le",
-  "Gt",                "Ge",                "IsNull",            "NotNull",
-  "Negative",          "And",               "Or",                "Not",
-  "Concat",            "Noop",              "Strlen",            "Substr",
-  "Limit",           
+  "Distinct",          "Found",             "NotFound",          "IsUnique",
+  "NotExists",         "Delete",            "Column",            "KeyAsData",
+  "Recno",             "FullKey",           "Rewind",            "Next",
+  "Destroy",           "Clear",             "CreateIndex",       "CreateTable",
+  "Reorganize",        "IdxPut",            "IdxDelete",         "IdxRecno",
+  "IdxGT",             "IdxGE",             "MemLoad",           "MemStore",
+  "ListWrite",         "ListRewind",        "ListRead",          "ListReset",
+  "SortPut",           "SortMakeRec",       "SortMakeKey",       "Sort",
+  "SortNext",          "SortCallback",      "SortReset",         "FileOpen",
+  "FileRead",          "FileColumn",        "AggReset",          "AggFocus",
+  "AggIncr",           "AggNext",           "AggSet",            "AggGet",
+  "SetInsert",         "SetFound",          "SetNotFound",       "MakeRecord",
+  "MakeKey",           "MakeIdxKey",        "IncrKey",           "Goto",
+  "If",                "Halt",              "ColumnCount",       "ColumnName",
+  "Callback",          "NullCallback",      "Integer",           "String",
+  "Pop",               "Dup",               "Pull",              "Push",
+  "MustBeInt",         "Add",               "AddImm",            "Subtract",
+  "Multiply",          "Divide",            "Remainder",         "BitAnd",
+  "BitOr",             "BitNot",            "ShiftLeft",         "ShiftRight",
+  "AbsValue",          "Precision",         "Min",               "Max",
+  "Like",              "Glob",              "Eq",                "Ne",
+  "Lt",                "Le",                "Gt",                "Ge",
+  "IsNull",            "NotNull",           "Negative",          "And",
+  "Or",                "Not",               "Concat",            "Noop",
+  "Strlen",            "Substr",            "Limit",           
 };
 
 /*
@@ -1138,17 +1149,26 @@ case OP_Goto: {
   break;
 }
 
-/* Opcode:  Halt * * *
+/* Opcode:  Halt P1 * *
 **
 ** Exit immediately.  All open cursors, Lists, Sorts, etc are closed
 ** automatically.
 **
-** There is an implied Halt instruction inserted at the very end of
+** P1 is the result code returned by sqlite_exec().  For a normal
+** halt, this should be SQLITE_OK (0).  For errors, it can be some
+** other value.
+**
+** There is an implied "Halt 0 0 0" instruction inserted at the very end of
 ** every program.  So a jump past the last instruction of the program
 ** is the same as executing Halt.
 */
 case OP_Halt: {
-  pc = p->nOp-1;
+  if( pOp->p1!=SQLITE_OK ){
+    rc = pOp->p1;
+    goto abort_due_to_error;
+  }else{
+    pc = p->nOp-1;
+  }
   break;
 }
 
@@ -1195,7 +1215,7 @@ case OP_Pop: {
   break;
 }
 
-/* Opcode: Dup P1 * *
+/* Opcode: Dup P1 P2 *
 **
 ** A copy of the P1-th element of the stack 
 ** is made and pushed onto the top of the stack.
@@ -1203,6 +1223,11 @@ case OP_Pop: {
 ** instruction "Dup 0 0 0" will make a copy of the
 ** top of the stack.
 **
+** If the content of the P1-th element is a dynamically
+** allocated string, then a new copy of that string
+** is made if P2==0.  If P2!=0, then just a pointer
+** to the string is copied.
+**
 ** Also see the Pull instruction.
 */
 case OP_Dup: {
@@ -1212,7 +1237,7 @@ case OP_Dup: {
   VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; )
   memcpy(&aStack[j], &aStack[i], sizeof(aStack[i])-NBFS);
   if( aStack[j].flags & STK_Str ){
-    if( aStack[j].flags & STK_Static ){
+    if( pOp->p2 || (aStack[j].flags & STK_Static)!=0 ){
       zStack[j] = zStack[i];
       aStack[j].flags = STK_Str | STK_Static;
     }else if( aStack[i].n<=NBFS ){
@@ -1265,6 +1290,33 @@ case OP_Pull: {
   break;
 }
 
+/* Opcode: Push P1 * *
+**
+** Overwrite the value of the P1-th element down on the
+** stack (P1==0 is the top of the stack) with the value
+** of the top of the stack.  The pop the top of the stack.
+*/
+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]);
+  }
+  aStack[to] = aStack[from];
+  if( aStack[to].flags & (STK_Dyn|STK_Static) ){
+    zStack[to] = zStack[from];
+  }else{
+    zStack[to] = aStack[to].z;
+  }
+  aStack[from].flags &= ~STK_Dyn;
+  p->tos--;
+  break;
+}
+
 /* Opcode: ColumnCount P1 * *
 **
 ** Specify the number of column values that will appear in the
@@ -2010,8 +2062,8 @@ case OP_IsNull: {
 
 /* Opcode: NotNull * P2 *
 **
-** Pop a single value from the stack.  If the value popped is not an
-** empty string, then jump to p2.  Otherwise continue to the next 
+** Pop a single value from the stack.  If the value popped is not
+** NULL, then jump to p2.  Otherwise continue to the next 
 ** instruction.
 */
 case OP_NotNull: {
@@ -2675,7 +2727,7 @@ case OP_MoveTo: {
 ** The difference between this operation and Distinct is that
 ** Distinct does not pop the key from the stack.
 **
-** See also: Distinct, Found, MoveTo, NotExists
+** See also: Distinct, Found, MoveTo, NotExists, IsUnique
 */
 case OP_Distinct:
 case OP_NotFound:
@@ -2702,6 +2754,66 @@ case OP_Found: {
   break;
 }
 
+/* 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.
+**
+** 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.
+**
+** See also: Distinct, NotFound, NotExists
+*/
+case OP_IsUnique: {
+  int i = pOp->p1;
+  int tos = p->tos;
+  BtCursor *pCrsr;
+
+  VERIFY( if( tos<0 ) goto not_enough_stack; )
+  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 );
+    rc = sqliteBtreeMoveto(pCrsr, zKey, nKey-4, &res);
+    if( rc!=SQLITE_OK ) goto abort_due_to_error;
+    if( res<0 ){
+      rc = sqliteBtreeNext(pCrsr, &res);
+      if( res ){
+        pc = pOp->p2 - 1;
+        break;
+      }
+    }
+    rc = sqliteBtreeKeyCompare(pCrsr, zKey, nKey-4, 4, &res);
+    if( rc!=SQLITE_OK ) goto abort_due_to_error;
+    if( res>0 ){
+      pc = pOp->p2 - 1;
+      break;
+    }
+    sqliteBtreeKey(pCrsr, nKey - 4, 4, (char*)&v1);
+    memcpy((char*)&v2, &zKey[nKey-4], 4);
+    if( v1==v2 ){
+      pc = pOp->p2 - 1;
+      break;
+    }
+    tos = ++p->tos;
+    VERIFY( if( NeedStack(p, p->tos) ) goto no_mem; )
+    aStack[tos].i = keyToInt(v1);
+    aStack[tos].flags = STK_Int;
+  }
+  break;
+}
+
 /* Opcode: NotExists P1 P2 *
 **
 ** Use the top of the stack as a integer key.  If a record with that key
index 0f270b92fdaa4808a7c8d330dbb7b3fd892d58dc..2fc688042a391eb661b522a460ab38a0a55e3017 100644 (file)
@@ -15,7 +15,7 @@
 ** or VDBE.  The VDBE implements an abstract machine that runs a
 ** simple program to access and modify the underlying database.
 **
-** $Id: vdbe.h,v 1.38 2002/01/28 15:53:05 drh Exp $
+** $Id: vdbe.h,v 1.39 2002/01/29 18:41:25 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -89,117 +89,119 @@ typedef struct VdbeOp VdbeOp;
 #define OP_Distinct           17
 #define OP_Found              18
 #define OP_NotFound           19
-#define OP_NotExists          20
-#define OP_Delete             21
-#define OP_Column             22
-#define OP_KeyAsData          23
-#define OP_Recno              24
-#define OP_FullKey            25
-#define OP_Rewind             26
-#define OP_Next               27
-
-#define OP_Destroy            28
-#define OP_Clear              29
-#define OP_CreateIndex        30
-#define OP_CreateTable        31
-#define OP_Reorganize         32
-
-#define OP_IdxPut             33
-#define OP_IdxDelete          34
-#define OP_IdxRecno           35
-#define OP_IdxGT              36
-#define OP_IdxGE              37
-
-#define OP_MemLoad            38
-#define OP_MemStore           39
-
-#define OP_ListWrite          40
-#define OP_ListRewind         41
-#define OP_ListRead           42
-#define OP_ListReset          43
-
-#define OP_SortPut            44
-#define OP_SortMakeRec        45
-#define OP_SortMakeKey        46
-#define OP_Sort               47
-#define OP_SortNext           48
-#define OP_SortCallback       49
-#define OP_SortReset          50
-
-#define OP_FileOpen           51
-#define OP_FileRead           52
-#define OP_FileColumn         53
-
-#define OP_AggReset           54
-#define OP_AggFocus           55
-#define OP_AggIncr            56
-#define OP_AggNext            57
-#define OP_AggSet             58
-#define OP_AggGet             59
-
-#define OP_SetInsert          60
-#define OP_SetFound           61
-#define OP_SetNotFound        62
-
-#define OP_MakeRecord         63
-#define OP_MakeKey            64
-#define OP_MakeIdxKey         65
-#define OP_IncrKey            66
-
-#define OP_Goto               67
-#define OP_If                 68
-#define OP_Halt               69
-
-#define OP_ColumnCount        70
-#define OP_ColumnName         71
-#define OP_Callback           72
-#define OP_NullCallback       73
-
-#define OP_Integer            74
-#define OP_String             75
-#define OP_Pop                76
-#define OP_Dup                77
-#define OP_Pull               78
-#define OP_MustBeInt          79
-
-#define OP_Add                80
-#define OP_AddImm             81
-#define OP_Subtract           82
-#define OP_Multiply           83
-#define OP_Divide             84
-#define OP_Remainder          85
-#define OP_BitAnd             86
-#define OP_BitOr              87
-#define OP_BitNot             88
-#define OP_ShiftLeft          89
-#define OP_ShiftRight         90
-#define OP_AbsValue           91
-#define OP_Precision          92
-#define OP_Min                93
-#define OP_Max                94
-#define OP_Like               95
-#define OP_Glob               96
-#define OP_Eq                 97
-#define OP_Ne                 98
-#define OP_Lt                 99
-#define OP_Le                100
-#define OP_Gt                101
-#define OP_Ge                102
-#define OP_IsNull            103
-#define OP_NotNull           104
-#define OP_Negative          105
-#define OP_And               106
-#define OP_Or                107
-#define OP_Not               108
-#define OP_Concat            109
-#define OP_Noop              110
-
-#define OP_Strlen            111
-#define OP_Substr            112
-
-#define OP_Limit             113
-
-#define OP_MAX               113
+#define OP_IsUnique           20
+#define OP_NotExists          21
+#define OP_Delete             22
+#define OP_Column             23
+#define OP_KeyAsData          24
+#define OP_Recno              25
+#define OP_FullKey            26
+#define OP_Rewind             27
+#define OP_Next               28
+
+#define OP_Destroy            29
+#define OP_Clear              30
+#define OP_CreateIndex        31
+#define OP_CreateTable        32
+#define OP_Reorganize         33
+
+#define OP_IdxPut             34
+#define OP_IdxDelete          35
+#define OP_IdxRecno           36
+#define OP_IdxGT              37
+#define OP_IdxGE              38
+
+#define OP_MemLoad            39
+#define OP_MemStore           40
+
+#define OP_ListWrite          41
+#define OP_ListRewind         42
+#define OP_ListRead           43
+#define OP_ListReset          44
+
+#define OP_SortPut            45
+#define OP_SortMakeRec        46
+#define OP_SortMakeKey        47
+#define OP_Sort               48
+#define OP_SortNext           49
+#define OP_SortCallback       50
+#define OP_SortReset          51
+
+#define OP_FileOpen           52
+#define OP_FileRead           53
+#define OP_FileColumn         54
+
+#define OP_AggReset           55
+#define OP_AggFocus           56
+#define OP_AggIncr            57
+#define OP_AggNext            58
+#define OP_AggSet             59
+#define OP_AggGet             60
+
+#define OP_SetInsert          61
+#define OP_SetFound           62
+#define OP_SetNotFound        63
+
+#define OP_MakeRecord         64
+#define OP_MakeKey            65
+#define OP_MakeIdxKey         66
+#define OP_IncrKey            67
+
+#define OP_Goto               68
+#define OP_If                 69
+#define OP_Halt               70
+
+#define OP_ColumnCount        71
+#define OP_ColumnName         72
+#define OP_Callback           73
+#define OP_NullCallback       74
+
+#define OP_Integer            75
+#define OP_String             76
+#define OP_Pop                77
+#define OP_Dup                78
+#define OP_Pull               79
+#define OP_Push               80
+#define OP_MustBeInt          81
+
+#define OP_Add                82
+#define OP_AddImm             83
+#define OP_Subtract           84
+#define OP_Multiply           85
+#define OP_Divide             86
+#define OP_Remainder          87
+#define OP_BitAnd             88
+#define OP_BitOr              89
+#define OP_BitNot             90
+#define OP_ShiftLeft          91
+#define OP_ShiftRight         92
+#define OP_AbsValue           93
+#define OP_Precision          94
+#define OP_Min                95
+#define OP_Max                96
+#define OP_Like               97
+#define OP_Glob               98
+#define OP_Eq                 99
+#define OP_Ne                100
+#define OP_Lt                101
+#define OP_Le                102
+#define OP_Gt                103
+#define OP_Ge                104
+#define OP_IsNull            105
+#define OP_NotNull           106
+#define OP_Negative          107
+#define OP_And               108
+#define OP_Or                109
+#define OP_Not               110
+#define OP_Concat            111
+#define OP_Noop              112
+
+#define OP_Strlen            113
+#define OP_Substr            114
+
+#define OP_Limit             115
+
+#define OP_MAX               115
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation
@@ -210,6 +212,7 @@ void sqliteVdbeCreateCallback(Vdbe*, int*);
 int sqliteVdbeAddOp(Vdbe*,int,int,int);
 int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOp const *aOp);
 void sqliteVdbeChangeP1(Vdbe*, int addr, int P1);
+void sqliteVdbeChangeP2(Vdbe*, int addr, int P2);
 void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
 void sqliteVdbeDequoteP3(Vdbe*, int addr);
 int sqliteVdbeMakeLabel(Vdbe*);
index 825ce5e43b225edb949247b263470279424febfe..3ae7a97b2b681c9bc31a3decc833e815a90c10dc 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Run this TCL script to generate HTML for the index.html file.
 #
-set rcsid {$Id: index.tcl,v 1.51 2002/01/09 13:35:11 drh Exp $}
+set rcsid {$Id: index.tcl,v 1.52 2002/01/29 18:41:26 drh Exp $}
 
 puts {<html>
 <head><title>SQLite: An SQL Database Engine In A C Library</title></head>
@@ -65,7 +65,7 @@ puts {<h2>Current Status</h2>
 The latest source code is
 <a href="download.html">available for download</a>.
 There are currently no known memory leaks or bugs
-in version 2.2.1 of the library.
+in version 2.2.5 of the library.
 </p>
 
 <p>