Beginning to insert the infrastructure for ON CONFLICT clauses. (CVS 355)
FossilOrigin-Name: e00a9ff8f99dd58f7cb19a6195fac21f4c8b4af9
This commit is contained in:
parent
881a99e493
commit
9cfcf5d4f6
32
manifest
32
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
|
||||
|
@ -1 +1 @@
|
||||
af3bb80810c6cd302a884a106cc70591a738e102
|
||||
e00a9ff8f99dd58f7cb19a6195fac21f4c8b4af9
|
43
src/build.c
43
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 <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;
|
||||
|
69
src/delete.c
69
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; 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);
|
||||
}
|
||||
|
173
src/insert.c
173
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; 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
|
||||
|
60
src/parse.y
60
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($$);}
|
||||
|
@ -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*);
|
||||
|
@ -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 },
|
||||
|
@ -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 */
|
||||
|
180
src/vdbe.c
180
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 <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
|
||||
|
195
src/vdbe.h
195
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*);
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user