From 9cfcf5d4f66d3683b78961b6b082657a9f7a22dc Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 29 Jan 2002 18:41:24 +0000 Subject: [PATCH] Beginning to insert the infrastructure for ON CONFLICT clauses. (CVS 355) FossilOrigin-Name: e00a9ff8f99dd58f7cb19a6195fac21f4c8b4af9 --- VERSION | 2 +- manifest | 32 ++++---- manifest.uuid | 2 +- src/build.c | 43 ++++++++--- src/delete.c | 69 ++++++++++++----- src/insert.c | 173 +++++++++++++++++++++++++++++++++++++++++- src/parse.y | 60 ++++++++++----- src/sqliteInt.h | 28 +++++-- src/tokenize.c | 6 +- src/update.c | 5 +- src/vdbe.c | 180 +++++++++++++++++++++++++++++++++++--------- src/vdbe.h | 195 ++++++++++++++++++++++++------------------------ www/index.tcl | 4 +- 13 files changed, 590 insertions(+), 209 deletions(-) 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.