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