diff --git a/VERSION b/VERSION
index 21bb5e156f..276cbf9e28 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.2.5
+2.3.0
diff --git a/manifest b/manifest
index 909fd4ece5..eedd784ec7 100644
--- 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
diff --git a/manifest.uuid b/manifest.uuid
index 0519c9d066..8b9c80c5d4 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-af3bb80810c6cd302a884a106cc70591a738e102
\ No newline at end of file
+e00a9ff8f99dd58f7cb19a6195fac21f4c8b4af9
\ No newline at end of file
diff --git a/src/build.c b/src/build.c
index 22e68df83c..473b55e781 100644
--- a/src/build.c
+++ b/src/build.c
@@ -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
@@ -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 && iColnCol ){
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;
diff --git a/src/delete.c b/src/delete.c
index 70638afbaf..7d19b2b0e3 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -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; jnColumn; 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; jnColumn; 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);
+}
diff --git a/src/insert.c b/src/insert.c
index c24d019e53..f9b8b1791c 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -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; iaCol[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; inColumn; 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
diff --git a/src/parse.y b/src/parse.y
index e23104ff15..03bd72dfdd 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -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($$);}
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index aeeba58fc0..ca4e7616ea 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -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*);
diff --git a/src/tokenize.c b/src/tokenize.c
index 1917c652dc..0a8d0b6913 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -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 },
diff --git a/src/update.c b/src/update.c
index 88df296423..e4d170456a 100644
--- a/src/update.c
+++ b/src/update.c
@@ -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 */
diff --git a/src/vdbe.c b/src/vdbe.c
index ce025f4624..2e5251284d 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -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
@@ -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 && inCursor && ) (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
diff --git a/src/vdbe.h b/src/vdbe.h
index 0f270b92fd..2fc688042a 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -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_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 28
-#define OP_Clear 29
-#define OP_CreateIndex 30
-#define OP_CreateTable 31
-#define OP_Reorganize 32
+#define OP_Destroy 29
+#define OP_Clear 30
+#define OP_CreateIndex 31
+#define OP_CreateTable 32
+#define OP_Reorganize 33
-#define OP_IdxPut 33
-#define OP_IdxDelete 34
-#define OP_IdxRecno 35
-#define OP_IdxGT 36
-#define OP_IdxGE 37
+#define OP_IdxPut 34
+#define OP_IdxDelete 35
+#define OP_IdxRecno 36
+#define OP_IdxGT 37
+#define OP_IdxGE 38
-#define OP_MemLoad 38
-#define OP_MemStore 39
+#define OP_MemLoad 39
+#define OP_MemStore 40
-#define OP_ListWrite 40
-#define OP_ListRewind 41
-#define OP_ListRead 42
-#define OP_ListReset 43
+#define OP_ListWrite 41
+#define OP_ListRewind 42
+#define OP_ListRead 43
+#define OP_ListReset 44
-#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_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 51
-#define OP_FileRead 52
-#define OP_FileColumn 53
+#define OP_FileOpen 52
+#define OP_FileRead 53
+#define OP_FileColumn 54
-#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_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 60
-#define OP_SetFound 61
-#define OP_SetNotFound 62
+#define OP_SetInsert 61
+#define OP_SetFound 62
+#define OP_SetNotFound 63
-#define OP_MakeRecord 63
-#define OP_MakeKey 64
-#define OP_MakeIdxKey 65
-#define OP_IncrKey 66
+#define OP_MakeRecord 64
+#define OP_MakeKey 65
+#define OP_MakeIdxKey 66
+#define OP_IncrKey 67
-#define OP_Goto 67
-#define OP_If 68
-#define OP_Halt 69
+#define OP_Goto 68
+#define OP_If 69
+#define OP_Halt 70
-#define OP_ColumnCount 70
-#define OP_ColumnName 71
-#define OP_Callback 72
-#define OP_NullCallback 73
+#define OP_ColumnCount 71
+#define OP_ColumnName 72
+#define OP_Callback 73
+#define OP_NullCallback 74
-#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_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 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_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 111
-#define OP_Substr 112
+#define OP_Strlen 113
+#define OP_Substr 114
-#define OP_Limit 113
+#define OP_Limit 115
-#define OP_MAX 113
+#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*);
diff --git a/www/index.tcl b/www/index.tcl
index 825ce5e43b..3ae7a97b2b 100644
--- a/www/index.tcl
+++ b/www/index.tcl
@@ -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 {
SQLite: An SQL Database Engine In A C Library
@@ -65,7 +65,7 @@ puts {Current Status
The latest source code is
available for download.
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.