mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Speed up INSERT operations that add data to UNIQUE or PRIMARY KEY indexes by rationalizing duplicate seek operations. (CVS 6599)
FossilOrigin-Name: cac4f3d812f0a02ca5c1fa78d366f694403929a8
This commit is contained in:
34
manifest
34
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Work\stoward\scleaning\sup\sthe\sauthorizer\sinterface.\s\sWork\sis\son-going.\s\sThis\nis\san\sincremental\scheck-in.\s(CVS\s6598)
|
C Speed\sup\sINSERT\soperations\sthat\sadd\sdata\sto\sUNIQUE\sor\sPRIMARY\sKEY\sindexes\sby\srationalizing\sduplicate\sseek\soperations.\s(CVS\s6599)
|
||||||
D 2009-05-04T01:58:31
|
D 2009-05-04T11:42:30
|
||||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||||
F Makefile.in 583e87706abc3026960ed759aff6371faf84c211
|
F Makefile.in 583e87706abc3026960ed759aff6371faf84c211
|
||||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||||
@@ -106,10 +106,10 @@ F src/auth.c 3aa04c55fba2b4d5905d3a1f8f24b0c2249d1921
|
|||||||
F src/backup.c 0082d0e5a63f04e88faee0dff0a7d63d3e92a78d
|
F src/backup.c 0082d0e5a63f04e88faee0dff0a7d63d3e92a78d
|
||||||
F src/bitvec.c ef370407e03440b0852d05024fb016b14a471d3d
|
F src/bitvec.c ef370407e03440b0852d05024fb016b14a471d3d
|
||||||
F src/btmutex.c 9b899c0d8df3bd68f527b0afe03088321b696d3c
|
F src/btmutex.c 9b899c0d8df3bd68f527b0afe03088321b696d3c
|
||||||
F src/btree.c 64ad8841aefce2ba0cb3b138e5fe8669ce5fa6db
|
F src/btree.c 62f3194f5ef2cb4c1622e9600bb6257635369d8d
|
||||||
F src/btree.h 99fcc7e8c4a1e35afe271bcb38de1a698dfc904e
|
F src/btree.h 58d876d3ed944a8f4f1fd0e67024b385243fc9dd
|
||||||
F src/btreeInt.h df64030d632f8c8ac217ed52e8b6b3eacacb33a5
|
F src/btreeInt.h df64030d632f8c8ac217ed52e8b6b3eacacb33a5
|
||||||
F src/build.c 894dc1b8fba81acce54133fd69a40e609248eb38
|
F src/build.c 8e83444d31975a732be9c393cdcbe51930a35638
|
||||||
F src/callback.c c54a923b06a17a2f965e5c3a6f87a3a963209a4c
|
F src/callback.c c54a923b06a17a2f965e5c3a6f87a3a963209a4c
|
||||||
F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0
|
F src/complete.c 5ad5c6cd4548211867c204c41a126d73a9fbcea0
|
||||||
F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3
|
F src/date.c ab5f7137656652a48434d64f96bdcdc823bb23b3
|
||||||
@@ -121,7 +121,7 @@ F src/global.c 448419c44ce0701104c2121b0e06919b44514c0c
|
|||||||
F src/hash.c 7e90268f62662dc8ccb9da1e93090ea64481e4f8
|
F src/hash.c 7e90268f62662dc8ccb9da1e93090ea64481e4f8
|
||||||
F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1
|
F src/hash.h 35b216c13343d0b4f87d9f21969ac55ad72174e1
|
||||||
F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb
|
F src/hwtime.h 4a1d45f4cae1f402ea19686acf24acf4f0cb53cb
|
||||||
F src/insert.c 35d73660090a3b93ee6aa92a54d9dad912ac0197
|
F src/insert.c 050536ea91c6cf74d87a2386b5da241141943c94
|
||||||
F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0
|
F src/journal.c e00df0c0da8413ab6e1bb7d7cab5665d4a9000d0
|
||||||
F src/legacy.c 2ad5b52df322d0f132f66817095e0e79c8942611
|
F src/legacy.c 2ad5b52df322d0f132f66817095e0e79c8942611
|
||||||
F src/loadext.c 3f96631089fc4f3871a67f02f2e4fc7ea4d51edc
|
F src/loadext.c 3f96631089fc4f3871a67f02f2e4fc7ea4d51edc
|
||||||
@@ -132,7 +132,7 @@ F src/mem1.c e6d5c23941288df8191b8a98c28e3f57771e2270
|
|||||||
F src/mem2.c d02bd6a5b34f2d59012a852615621939d9c09548
|
F src/mem2.c d02bd6a5b34f2d59012a852615621939d9c09548
|
||||||
F src/mem3.c 67153ec933e08b70714055e872efb58a6b287939
|
F src/mem3.c 67153ec933e08b70714055e872efb58a6b287939
|
||||||
F src/mem5.c 838309b521c96a2a34507f74a5a739d28de4aac6
|
F src/mem5.c 838309b521c96a2a34507f74a5a739d28de4aac6
|
||||||
F src/memjournal.c 1a987d078e7446a44417a1a6a01f80b48cf65b93
|
F src/memjournal.c e68cb5f7e828b84d5bf2ea16c5d87f1ed7e9fe7f
|
||||||
F src/mutex.c 5e2ea0e0490a3567dc08a014bcee748c0cea727f
|
F src/mutex.c 5e2ea0e0490a3567dc08a014bcee748c0cea727f
|
||||||
F src/mutex.h 9e686e83a88838dac8b9c51271c651e833060f1e
|
F src/mutex.h 9e686e83a88838dac8b9c51271c651e833060f1e
|
||||||
F src/mutex_noop.c f5a07671f25a1a9bd7c10ad7107bc2585446200f
|
F src/mutex_noop.c f5a07671f25a1a9bd7c10ad7107bc2585446200f
|
||||||
@@ -162,14 +162,14 @@ F src/select.c 9587023e906afe2074a718d25d6a4326874fb791
|
|||||||
F src/shell.c 0a11f831603f17fea20ca97133c0f64e716af4a7
|
F src/shell.c 0a11f831603f17fea20ca97133c0f64e716af4a7
|
||||||
F src/sqlite.h.in 926985a312747e284c21ab32a8e8231a3bed9bd1
|
F src/sqlite.h.in 926985a312747e284c21ab32a8e8231a3bed9bd1
|
||||||
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
|
F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17
|
||||||
F src/sqliteInt.h 2edb5e7e194e8083dad299e282aa0ed5022247b8
|
F src/sqliteInt.h 1f58ab9dee0c5b752ebad9c9d7932487430d0b04
|
||||||
F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d
|
F src/sqliteLimit.h ffe93f5a0c4e7bd13e70cd7bf84cfb5c3465f45d
|
||||||
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
|
F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76
|
||||||
F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
|
F src/table.c cc86ad3d6ad54df7c63a3e807b5783c90411a08d
|
||||||
F src/tclsqlite.c d3195e0738c101a155404ecdb1cd9532a2fd34f2
|
F src/tclsqlite.c d3195e0738c101a155404ecdb1cd9532a2fd34f2
|
||||||
F src/test1.c c8f9358879876660b721369f576bf6e4ac5b9210
|
F src/test1.c c8f9358879876660b721369f576bf6e4ac5b9210
|
||||||
F src/test2.c 71c22e2974f8094fe0fd1eba8f27872dde9b2a39
|
F src/test2.c 71c22e2974f8094fe0fd1eba8f27872dde9b2a39
|
||||||
F src/test3.c d3115b301c6ee761b102f315fe24125f3d6c3a4d
|
F src/test3.c abd651f387a42696976dd5560c46b596e421cfb8
|
||||||
F src/test4.c f79ab52d27ff49b784b631a42e2ccd52cfd5c84c
|
F src/test4.c f79ab52d27ff49b784b631a42e2ccd52cfd5c84c
|
||||||
F src/test5.c 162a1cea2105a2c460a3f39fa6919617b562a288
|
F src/test5.c 162a1cea2105a2c460a3f39fa6919617b562a288
|
||||||
F src/test6.c 1a0a7a1f179469044b065b4a88aab9faee114101
|
F src/test6.c 1a0a7a1f179469044b065b4a88aab9faee114101
|
||||||
@@ -199,15 +199,15 @@ F src/test_thread.c b8a1ab7ca1a632f18e8a361880d5d65eeea08eac
|
|||||||
F src/test_wsd.c 3ae5101de6cbfda2720152ab659ea84079719241
|
F src/test_wsd.c 3ae5101de6cbfda2720152ab659ea84079719241
|
||||||
F src/tokenize.c 286ce8a4bffe5ec81ad893e6243684d8f8846ed9
|
F src/tokenize.c 286ce8a4bffe5ec81ad893e6243684d8f8846ed9
|
||||||
F src/trigger.c 448615bec40efcd6b3a9362a060f2e7067f25be5
|
F src/trigger.c 448615bec40efcd6b3a9362a060f2e7067f25be5
|
||||||
F src/update.c 5062f0f042f67a4da0aff69949f145e2bc96e3cd
|
F src/update.c e69ba30a62555cd34bca6e3f304d45fdbd98c11b
|
||||||
F src/utf.c 9541d28f40441812c0b40f00334372a0542c00ff
|
F src/utf.c 9541d28f40441812c0b40f00334372a0542c00ff
|
||||||
F src/util.c 40fb962de1b00a310de4acc87c6800173e35c25f
|
F src/util.c 40fb962de1b00a310de4acc87c6800173e35c25f
|
||||||
F src/vacuum.c 07121a727beeee88f27d704a00313ad6a7c9bef0
|
F src/vacuum.c 07121a727beeee88f27d704a00313ad6a7c9bef0
|
||||||
F src/vdbe.c 5fe07cce9f010cd052535463bd0348e8d513b448
|
F src/vdbe.c e7c3355a39dfa0bf0be69e123061a6c1503fb327
|
||||||
F src/vdbe.h 35a648bc3279a120da24f34d9a25213ec15daf8a
|
F src/vdbe.h 35a648bc3279a120da24f34d9a25213ec15daf8a
|
||||||
F src/vdbeInt.h 8726f7b4e3b55c8acf6d304a5b5f727ac1b6c5ab
|
F src/vdbeInt.h 43183a2a18654fa570219ab65e53a608057c48ae
|
||||||
F src/vdbeapi.c 86aa27a5f3493aaffb8ac051782aa3b22670d7ed
|
F src/vdbeapi.c 86aa27a5f3493aaffb8ac051782aa3b22670d7ed
|
||||||
F src/vdbeaux.c 34524d499fc6081e97771dcfdf6c2523d5877ef5
|
F src/vdbeaux.c 02cefacfa4cf652743c4507fa83646cd7f35e564
|
||||||
F src/vdbeblob.c e67757450ae8581a8b354d9d7e467e41502dfe38
|
F src/vdbeblob.c e67757450ae8581a8b354d9d7e467e41502dfe38
|
||||||
F src/vdbemem.c f5d7c0b7db32ab6939cbfa371b3b329d16a0ee21
|
F src/vdbemem.c f5d7c0b7db32ab6939cbfa371b3b329d16a0ee21
|
||||||
F src/vtab.c 53355aa2381ec3ef2eaad25672cfd5877a02fe45
|
F src/vtab.c 53355aa2381ec3ef2eaad25672cfd5877a02fe45
|
||||||
@@ -727,7 +727,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
|
|||||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||||
P a612299092a48b38c5f9cf430bbcaf41777cbcb3
|
P 694662f7860179403e0cc55b45ae8afa45aa7dfb
|
||||||
R 8d43b74d4371f916b497236f2944ae72
|
R 6576fd5d802472afe202456dad4f145e
|
||||||
U drh
|
U danielk1977
|
||||||
Z f912bd9e4ca5182f98f6a00c2cb86598
|
Z b2077480b3cdd4d5393106dee9847930
|
||||||
|
@@ -1 +1 @@
|
|||||||
694662f7860179403e0cc55b45ae8afa45aa7dfb
|
cac4f3d812f0a02ca5c1fa78d366f694403929a8
|
22
src/btree.c
22
src/btree.c
@@ -9,7 +9,7 @@
|
|||||||
** May you share freely, never taking more than you give.
|
** May you share freely, never taking more than you give.
|
||||||
**
|
**
|
||||||
*************************************************************************
|
*************************************************************************
|
||||||
** $Id: btree.c,v 1.605 2009/05/02 10:03:09 danielk1977 Exp $
|
** $Id: btree.c,v 1.606 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
**
|
**
|
||||||
** This file implements a external (disk-based) database using BTrees.
|
** This file implements a external (disk-based) database using BTrees.
|
||||||
** See the header comment on "btreeInt.h" for additional information.
|
** See the header comment on "btreeInt.h" for additional information.
|
||||||
@@ -6134,16 +6134,28 @@ static int checkForReadConflicts(
|
|||||||
**
|
**
|
||||||
** For an INTKEY table, only the nKey value of the key is used. pKey is
|
** For an INTKEY table, only the nKey value of the key is used. pKey is
|
||||||
** ignored. For a ZERODATA table, the pData and nData are both ignored.
|
** ignored. For a ZERODATA table, the pData and nData are both ignored.
|
||||||
|
**
|
||||||
|
** If the seekResult parameter is non-zero, then a successful call to
|
||||||
|
** sqlite3BtreeMoveto() to seek cursor pCur to (pKey, nKey) has already
|
||||||
|
** been performed. seekResult is the search result returned (a negative
|
||||||
|
** number if pCur points at an entry that is smaller than (pKey, nKey), or
|
||||||
|
** a positive value if pCur points at an etry that is larger than
|
||||||
|
** (pKey, nKey)).
|
||||||
|
**
|
||||||
|
** If the seekResult parameter is 0, then cursor pCur may point to any
|
||||||
|
** entry or to no entry at all. In this case this function has to seek
|
||||||
|
** the cursor before the new key can be inserted.
|
||||||
*/
|
*/
|
||||||
int sqlite3BtreeInsert(
|
int sqlite3BtreeInsert(
|
||||||
BtCursor *pCur, /* Insert data into the table of this cursor */
|
BtCursor *pCur, /* Insert data into the table of this cursor */
|
||||||
const void *pKey, i64 nKey, /* The key of the new record */
|
const void *pKey, i64 nKey, /* The key of the new record */
|
||||||
const void *pData, int nData, /* The data of the new record */
|
const void *pData, int nData, /* The data of the new record */
|
||||||
int nZero, /* Number of extra 0 bytes to append to data */
|
int nZero, /* Number of extra 0 bytes to append to data */
|
||||||
int appendBias /* True if this is likely an append */
|
int appendBias, /* True if this is likely an append */
|
||||||
|
int seekResult /* Result of prior sqlite3BtreeMoveto() call */
|
||||||
){
|
){
|
||||||
int rc;
|
int rc;
|
||||||
int loc;
|
int loc = seekResult;
|
||||||
int szNew;
|
int szNew;
|
||||||
int idx;
|
int idx;
|
||||||
MemPage *pPage;
|
MemPage *pPage;
|
||||||
@@ -6178,9 +6190,9 @@ int sqlite3BtreeInsert(
|
|||||||
** not to clear the cursor here.
|
** not to clear the cursor here.
|
||||||
*/
|
*/
|
||||||
if(
|
if(
|
||||||
SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) ||
|
SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) || (!loc &&
|
||||||
SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc))
|
SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc))
|
||||||
){
|
)){
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
** subsystem. See comments in the source code for a detailed description
|
** subsystem. See comments in the source code for a detailed description
|
||||||
** of what each interface routine does.
|
** of what each interface routine does.
|
||||||
**
|
**
|
||||||
** @(#) $Id: btree.h,v 1.113 2009/04/10 12:55:17 danielk1977 Exp $
|
** @(#) $Id: btree.h,v 1.114 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef _BTREE_H_
|
#ifndef _BTREE_H_
|
||||||
#define _BTREE_H_
|
#define _BTREE_H_
|
||||||
@@ -148,7 +148,7 @@ int sqlite3BtreeCursorHasMoved(BtCursor*, int*);
|
|||||||
int sqlite3BtreeDelete(BtCursor*);
|
int sqlite3BtreeDelete(BtCursor*);
|
||||||
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
|
||||||
const void *pData, int nData,
|
const void *pData, int nData,
|
||||||
int nZero, int bias);
|
int nZero, int bias, int seekResult);
|
||||||
int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
||||||
int sqlite3BtreeLast(BtCursor*, int *pRes);
|
int sqlite3BtreeLast(BtCursor*, int *pRes);
|
||||||
int sqlite3BtreeNext(BtCursor*, int *pRes);
|
int sqlite3BtreeNext(BtCursor*, int *pRes);
|
||||||
|
24
src/build.c
24
src/build.c
@@ -22,7 +22,7 @@
|
|||||||
** COMMIT
|
** COMMIT
|
||||||
** ROLLBACK
|
** ROLLBACK
|
||||||
**
|
**
|
||||||
** $Id: build.c,v 1.535 2009/05/03 20:23:53 drh Exp $
|
** $Id: build.c,v 1.536 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -2355,19 +2355,25 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
|
|||||||
regRecord = sqlite3GetTempReg(pParse);
|
regRecord = sqlite3GetTempReg(pParse);
|
||||||
regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
|
regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
|
||||||
if( pIndex->onError!=OE_None ){
|
if( pIndex->onError!=OE_None ){
|
||||||
int j1, j2;
|
const int regRowid = regIdxKey + pIndex->nColumn;
|
||||||
int regRowid;
|
const int j2 = sqlite3VdbeCurrentAddr(v) + 2;
|
||||||
|
void * const pRegKey = SQLITE_INT_TO_PTR(regIdxKey);
|
||||||
|
|
||||||
regRowid = regIdxKey + pIndex->nColumn;
|
/* The registers accessed by the OP_IsUnique opcode were allocated
|
||||||
j1 = sqlite3VdbeAddOp3(v, OP_IsNull, regIdxKey, 0, pIndex->nColumn);
|
** using sqlite3GetTempRange() inside of the sqlite3GenerateIndexKey()
|
||||||
j2 = sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx,
|
** call above. Just before that function was freed they were released
|
||||||
0, regRowid, SQLITE_INT_TO_PTR(regRecord), P4_INT32);
|
** (made available to the compiler for reuse) using
|
||||||
|
** sqlite3ReleaseTempRange(). So in some ways having the OP_IsUnique
|
||||||
|
** opcode use the values stored within seems dangerous. However, since
|
||||||
|
** we can be sure that no other temp registers have been allocated
|
||||||
|
** since sqlite3ReleaseTempRange() was called, it is safe to do so.
|
||||||
|
*/
|
||||||
|
sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32);
|
||||||
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort, 0,
|
sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort, 0,
|
||||||
"indexed columns are not unique", P4_STATIC);
|
"indexed columns are not unique", P4_STATIC);
|
||||||
sqlite3VdbeJumpHere(v, j1);
|
|
||||||
sqlite3VdbeJumpHere(v, j2);
|
|
||||||
}
|
}
|
||||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord);
|
||||||
|
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||||
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
|
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
|
||||||
sqlite3VdbeJumpHere(v, addr1);
|
sqlite3VdbeJumpHere(v, addr1);
|
||||||
|
52
src/insert.c
52
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.266 2009/05/03 01:01:00 drh Exp $
|
** $Id: insert.c,v 1.267 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -943,26 +943,13 @@ void sqlite3Insert(
|
|||||||
}else
|
}else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
sqlite3GenerateConstraintChecks(
|
int isReplace; /* Set to true if constraints may cause a replace */
|
||||||
pParse,
|
sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx,
|
||||||
pTab,
|
keyColumn>=0, 0, onError, endOfLoop, &isReplace
|
||||||
baseCur,
|
|
||||||
regIns,
|
|
||||||
aRegIdx,
|
|
||||||
keyColumn>=0,
|
|
||||||
0,
|
|
||||||
onError,
|
|
||||||
endOfLoop
|
|
||||||
);
|
);
|
||||||
sqlite3CompleteInsertion(
|
sqlite3CompleteInsertion(
|
||||||
pParse,
|
pParse, pTab, baseCur, regIns, aRegIdx, 0,
|
||||||
pTab,
|
(tmask&TRIGGER_AFTER) ? newIdx : -1, appendFlag, isReplace==0
|
||||||
baseCur,
|
|
||||||
regIns,
|
|
||||||
aRegIdx,
|
|
||||||
0,
|
|
||||||
(tmask&TRIGGER_AFTER) ? newIdx : -1,
|
|
||||||
appendFlag
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1113,7 +1100,8 @@ void sqlite3GenerateConstraintChecks(
|
|||||||
int rowidChng, /* True if the rowid might collide with existing entry */
|
int rowidChng, /* True if the rowid might collide with existing entry */
|
||||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||||
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 *pbMayReplace /* OUT: Set to true if constraint may cause a replace */
|
||||||
){
|
){
|
||||||
int i; /* loop counter */
|
int i; /* loop counter */
|
||||||
Vdbe *v; /* VDBE under constrution */
|
Vdbe *v; /* VDBE under constrution */
|
||||||
@@ -1266,11 +1254,13 @@ void sqlite3GenerateConstraintChecks(
|
|||||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]);
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]);
|
||||||
sqlite3IndexAffinityStr(v, pIdx);
|
sqlite3IndexAffinityStr(v, pIdx);
|
||||||
sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1);
|
sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1);
|
||||||
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
|
|
||||||
|
|
||||||
/* Find out what action to take in case there is an indexing conflict */
|
/* Find out what action to take in case there is an indexing conflict */
|
||||||
onError = pIdx->onError;
|
onError = pIdx->onError;
|
||||||
if( onError==OE_None ) continue; /* pIdx is not a UNIQUE index */
|
if( onError==OE_None ){
|
||||||
|
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
|
||||||
|
continue; /* pIdx is not a UNIQUE index */
|
||||||
|
}
|
||||||
if( overrideError!=OE_Default ){
|
if( overrideError!=OE_Default ){
|
||||||
onError = overrideError;
|
onError = overrideError;
|
||||||
}else if( onError==OE_Default ){
|
}else if( onError==OE_Default ){
|
||||||
@@ -1283,12 +1273,12 @@ void sqlite3GenerateConstraintChecks(
|
|||||||
|
|
||||||
|
|
||||||
/* Check to see if the new index entry will be unique */
|
/* Check to see if the new index entry will be unique */
|
||||||
j2 = sqlite3VdbeAddOp3(v, OP_IsNull, regIdx, 0, pIdx->nColumn);
|
|
||||||
regR = sqlite3GetTempReg(pParse);
|
regR = sqlite3GetTempReg(pParse);
|
||||||
sqlite3VdbeAddOp2(v, OP_SCopy, regRowid-hasTwoRowids, regR);
|
sqlite3VdbeAddOp2(v, OP_SCopy, regRowid-hasTwoRowids, regR);
|
||||||
j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0,
|
j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0,
|
||||||
regR, SQLITE_INT_TO_PTR(aRegIdx[iCur]),
|
regR, SQLITE_INT_TO_PTR(regIdx),
|
||||||
P4_INT32);
|
P4_INT32);
|
||||||
|
sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
|
||||||
|
|
||||||
/* Generate code that executes if the new index entry is not unique */
|
/* Generate code that executes if the new index entry is not unique */
|
||||||
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
|
||||||
@@ -1330,10 +1320,13 @@ void sqlite3GenerateConstraintChecks(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqlite3VdbeJumpHere(v, j2);
|
|
||||||
sqlite3VdbeJumpHere(v, j3);
|
sqlite3VdbeJumpHere(v, j3);
|
||||||
sqlite3ReleaseTempReg(pParse, regR);
|
sqlite3ReleaseTempReg(pParse, regR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( pbMayReplace ){
|
||||||
|
*pbMayReplace = seenReplace;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1353,7 +1346,8 @@ void sqlite3CompleteInsertion(
|
|||||||
int *aRegIdx, /* Register used by each index. 0 for unused indices */
|
int *aRegIdx, /* Register used by each index. 0 for unused indices */
|
||||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||||
int newIdx, /* Index of NEW table for triggers. -1 if none */
|
int newIdx, /* Index of NEW table for triggers. -1 if none */
|
||||||
int appendBias /* True if this is likely to be an append */
|
int appendBias, /* True if this is likely to be an append */
|
||||||
|
int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */
|
||||||
){
|
){
|
||||||
int i;
|
int i;
|
||||||
Vdbe *v;
|
Vdbe *v;
|
||||||
@@ -1370,6 +1364,9 @@ void sqlite3CompleteInsertion(
|
|||||||
for(i=nIdx-1; i>=0; i--){
|
for(i=nIdx-1; i>=0; i--){
|
||||||
if( aRegIdx[i]==0 ) continue;
|
if( aRegIdx[i]==0 ) continue;
|
||||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i]);
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i]);
|
||||||
|
if( useSeekResult ){
|
||||||
|
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
regData = regRowid + 1;
|
regData = regRowid + 1;
|
||||||
regRec = sqlite3GetTempReg(pParse);
|
regRec = sqlite3GetTempReg(pParse);
|
||||||
@@ -1390,6 +1387,9 @@ void sqlite3CompleteInsertion(
|
|||||||
if( appendBias ){
|
if( appendBias ){
|
||||||
pik_flags |= OPFLAG_APPEND;
|
pik_flags |= OPFLAG_APPEND;
|
||||||
}
|
}
|
||||||
|
if( useSeekResult ){
|
||||||
|
pik_flags |= OPFLAG_USESEEKRESULT;
|
||||||
|
}
|
||||||
sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid);
|
sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid);
|
||||||
if( !pParse->nested ){
|
if( !pParse->nested ){
|
||||||
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
|
sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC);
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
** The in-memory rollback journal is used to journal transactions for
|
** The in-memory rollback journal is used to journal transactions for
|
||||||
** ":memory:" databases and when the journal_mode=MEMORY pragma is used.
|
** ":memory:" databases and when the journal_mode=MEMORY pragma is used.
|
||||||
**
|
**
|
||||||
** @(#) $Id: memjournal.c,v 1.11 2009/04/05 12:22:09 drh Exp $
|
** @(#) $Id: memjournal.c,v 1.12 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** Internal interface definitions for SQLite.
|
** Internal interface definitions for SQLite.
|
||||||
**
|
**
|
||||||
** @(#) $Id: sqliteInt.h,v 1.867 2009/05/03 20:23:54 drh Exp $
|
** @(#) $Id: sqliteInt.h,v 1.868 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef _SQLITEINT_H_
|
#ifndef _SQLITEINT_H_
|
||||||
#define _SQLITEINT_H_
|
#define _SQLITEINT_H_
|
||||||
@@ -1282,6 +1282,7 @@ struct UnpackedRecord {
|
|||||||
KeyInfo *pKeyInfo; /* Collation and sort-order information */
|
KeyInfo *pKeyInfo; /* Collation and sort-order information */
|
||||||
u16 nField; /* Number of entries in apMem[] */
|
u16 nField; /* Number of entries in apMem[] */
|
||||||
u16 flags; /* Boolean settings. UNPACKED_... below */
|
u16 flags; /* Boolean settings. UNPACKED_... below */
|
||||||
|
i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */
|
||||||
Mem *aMem; /* Values */
|
Mem *aMem; /* Values */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1293,6 +1294,7 @@ struct UnpackedRecord {
|
|||||||
#define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */
|
#define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */
|
||||||
#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */
|
#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */
|
||||||
#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */
|
#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */
|
||||||
|
#define UNPACKED_PREFIX_SEARCH 0x0020 /* A prefix match is considered OK */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Each SQL index is represented in memory by an
|
** Each SQL index is represented in memory by an
|
||||||
@@ -1998,12 +2000,13 @@ struct AuthContext {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Bitfield flags for P2 value in OP_Insert and OP_Delete
|
** Bitfield flags for P5 value in OP_Insert and OP_Delete
|
||||||
*/
|
*/
|
||||||
#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */
|
#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */
|
||||||
#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */
|
#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */
|
||||||
#define OPFLAG_ISUPDATE 4 /* This OP_Insert is an sql UPDATE */
|
#define OPFLAG_ISUPDATE 4 /* This OP_Insert is an sql UPDATE */
|
||||||
#define OPFLAG_APPEND 8 /* This is likely to be an append */
|
#define OPFLAG_APPEND 8 /* This is likely to be an append */
|
||||||
|
#define OPFLAG_USESEEKRESULT 16 /* Try to avoid a seek in BtreeInsert() */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Each trigger present in the database schema is stored as an instance of
|
* Each trigger present in the database schema is stored as an instance of
|
||||||
@@ -2494,8 +2497,8 @@ void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int);
|
|||||||
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);
|
void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);
|
||||||
int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int);
|
int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int);
|
||||||
void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
|
void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
|
||||||
int*,int,int,int,int);
|
int*,int,int,int,int,int*);
|
||||||
void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
|
void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int,int,int);
|
||||||
int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
|
int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
|
||||||
void sqlite3BeginWriteOperation(Parse*, int, int);
|
void sqlite3BeginWriteOperation(Parse*, int, int);
|
||||||
Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
|
Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
** is not included in the SQLite library. It is used for automated
|
** is not included in the SQLite library. It is used for automated
|
||||||
** testing of the SQLite library.
|
** testing of the SQLite library.
|
||||||
**
|
**
|
||||||
** $Id: test3.c,v 1.103 2009/03/18 10:33:02 danielk1977 Exp $
|
** $Id: test3.c,v 1.104 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include "btreeInt.h"
|
#include "btreeInt.h"
|
||||||
@@ -782,7 +782,7 @@ static int btree_insert(
|
|||||||
return TCL_ERROR;
|
return TCL_ERROR;
|
||||||
}
|
}
|
||||||
pBuf = Tcl_GetByteArrayFromObj(objv[3], &len);
|
pBuf = Tcl_GetByteArrayFromObj(objv[3], &len);
|
||||||
rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, nZero, 0);
|
rc = sqlite3BtreeInsert(pCur, 0, iKey, pBuf, len, nZero, 0, 0);
|
||||||
}else{
|
}else{
|
||||||
int keylen;
|
int keylen;
|
||||||
int dlen;
|
int dlen;
|
||||||
@@ -790,7 +790,7 @@ static int btree_insert(
|
|||||||
unsigned char *pDBuf;
|
unsigned char *pDBuf;
|
||||||
pKBuf = Tcl_GetByteArrayFromObj(objv[2], &keylen);
|
pKBuf = Tcl_GetByteArrayFromObj(objv[2], &keylen);
|
||||||
pDBuf = Tcl_GetByteArrayFromObj(objv[3], &dlen);
|
pDBuf = Tcl_GetByteArrayFromObj(objv[3], &dlen);
|
||||||
rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, nZero, 0);
|
rc = sqlite3BtreeInsert(pCur, pKBuf, keylen, pDBuf, dlen, nZero, 0, 0);
|
||||||
}
|
}
|
||||||
sqlite3BtreeLeave(pCur->pBtree);
|
sqlite3BtreeLeave(pCur->pBtree);
|
||||||
if( rc ){
|
if( rc ){
|
||||||
|
@@ -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.198 2009/04/24 15:46:22 drh Exp $
|
** $Id: update.c,v 1.199 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -511,7 +511,7 @@ void sqlite3Update(
|
|||||||
*/
|
*/
|
||||||
sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
|
sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
|
||||||
aRegIdx, chngRowid, 1,
|
aRegIdx, chngRowid, 1,
|
||||||
onError, addr);
|
onError, addr, 0);
|
||||||
|
|
||||||
/* Delete the old indices for the current record.
|
/* Delete the old indices for the current record.
|
||||||
*/
|
*/
|
||||||
@@ -528,7 +528,7 @@ void sqlite3Update(
|
|||||||
/* Create the new index entries and the new record.
|
/* Create the new index entries and the new record.
|
||||||
*/
|
*/
|
||||||
sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid,
|
sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid,
|
||||||
aRegIdx, 1, -1, 0);
|
aRegIdx, 1, -1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Increment the row counter
|
/* Increment the row counter
|
||||||
|
155
src/vdbe.c
155
src/vdbe.c
@@ -43,7 +43,7 @@
|
|||||||
** in this file for details. If in doubt, do not deviate from existing
|
** in this file for details. If in doubt, do not deviate from existing
|
||||||
** commenting and indentation practices when changing or adding code.
|
** commenting and indentation practices when changing or adding code.
|
||||||
**
|
**
|
||||||
** $Id: vdbe.c,v 1.840 2009/04/30 09:10:38 danielk1977 Exp $
|
** $Id: vdbe.c,v 1.841 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include "vdbeInt.h"
|
#include "vdbeInt.h"
|
||||||
@@ -3338,104 +3338,83 @@ case OP_Found: { /* jump, in3 */
|
|||||||
|
|
||||||
/* Opcode: IsUnique P1 P2 P3 P4 *
|
/* Opcode: IsUnique P1 P2 P3 P4 *
|
||||||
**
|
**
|
||||||
** The P3 register contains an integer record number. Call this
|
** Cursor P1 is open on an index. So it has no data and its key consists
|
||||||
** record number R. The P4 register contains an index key created
|
** of a record generated by OP_MakeRecord where the last field is the
|
||||||
** using MakeRecord. Call it K.
|
|
||||||
**
|
|
||||||
** P1 is an index. So it has no data and its key consists of a
|
|
||||||
** record generated by OP_MakeRecord where the last field is the
|
|
||||||
** rowid of the entry that the index refers to.
|
** rowid of the entry that the index refers to.
|
||||||
**
|
**
|
||||||
** This instruction asks if there is an entry in P1 where the
|
** The P3 register contains an integer record number. Call this record
|
||||||
** fields matches K but the rowid is different from R.
|
** number R. Register P4 is the first in a set of N contiguous registers
|
||||||
** If there is no such entry, then there is an immediate
|
** that make up an unpacked index key that can be used with cursor P1.
|
||||||
** jump to P2. If any entry does exist where the index string
|
** The value of N can be inferred from the cursor. N includes the rowid
|
||||||
** matches K but the record number is not R, then the record
|
** value appended to the end of the index record. This rowid value may
|
||||||
** number for that entry is written into P3 and control
|
** or may not be the same as R.
|
||||||
** falls through to the next instruction.
|
**
|
||||||
|
** If any of the N registers beginning with register P4 contains a NULL
|
||||||
|
** value, jump immediately to P2.
|
||||||
|
**
|
||||||
|
** Otherwise, this instruction checks if cursor P1 contains an entry
|
||||||
|
** where the first (N-1) fields match but the rowid value at the end
|
||||||
|
** of the index entry is not R. If there is no such entry, control jumps
|
||||||
|
** to instruction P2. Otherwise, the rowid of the conflicting index
|
||||||
|
** entry is copied to register P3 and control falls through to the next
|
||||||
|
** instruction.
|
||||||
**
|
**
|
||||||
** See also: NotFound, NotExists, Found
|
** See also: NotFound, NotExists, Found
|
||||||
*/
|
*/
|
||||||
case OP_IsUnique: { /* jump, in3 */
|
case OP_IsUnique: { /* jump, in3 */
|
||||||
int i = pOp->p1;
|
int ii;
|
||||||
VdbeCursor *pCx;
|
VdbeCursor *pCx;
|
||||||
BtCursor *pCrsr;
|
BtCursor *pCrsr;
|
||||||
Mem *pK;
|
int nField;
|
||||||
i64 R;
|
Mem *aMem = &p->aMem[pOp->p4.i];
|
||||||
|
|
||||||
/* Pop the value R off the top of the stack
|
/* Assert that the values of parameters P1 and P4 are in range. */
|
||||||
*/
|
|
||||||
assert( pOp->p4type==P4_INT32 );
|
assert( pOp->p4type==P4_INT32 );
|
||||||
assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem );
|
assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem );
|
||||||
pK = &p->aMem[pOp->p4.i];
|
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||||
|
|
||||||
|
/* Find the index cursor. */
|
||||||
|
pCx = p->apCsr[pOp->p1];
|
||||||
|
assert( pCx->deferredMoveto==0 );
|
||||||
|
pCx->seekResult = 0;
|
||||||
|
pCx->cacheStatus = CACHE_STALE;
|
||||||
|
pCrsr = pCx->pCursor;
|
||||||
|
|
||||||
|
/* If any of the values are NULL, take the jump. */
|
||||||
|
nField = pCx->pKeyInfo->nField;
|
||||||
|
for(ii=0; ii<nField; ii++){
|
||||||
|
if( aMem[ii].flags & MEM_Null ){
|
||||||
|
pc = pOp->p2 - 1;
|
||||||
|
pCrsr = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert( (aMem[nField].flags & MEM_Null)==0 );
|
||||||
|
|
||||||
|
if( pCrsr!=0 ){
|
||||||
|
UnpackedRecord r; /* B-Tree index search key */
|
||||||
|
i64 R; /* Rowid stored in register P3 */
|
||||||
|
|
||||||
|
/* Populate the index search key. */
|
||||||
|
r.pKeyInfo = pCx->pKeyInfo;
|
||||||
|
r.nField = nField + 1;
|
||||||
|
r.flags = UNPACKED_PREFIX_SEARCH;
|
||||||
|
r.aMem = aMem;
|
||||||
|
|
||||||
|
/* Extract the value of R from register P3. */
|
||||||
sqlite3VdbeMemIntegerify(pIn3);
|
sqlite3VdbeMemIntegerify(pIn3);
|
||||||
R = pIn3->u.i;
|
R = pIn3->u.i;
|
||||||
assert( i>=0 && i<p->nCursor );
|
|
||||||
pCx = p->apCsr[i];
|
|
||||||
assert( pCx!=0 );
|
|
||||||
pCrsr = pCx->pCursor;
|
|
||||||
if( pCrsr!=0 ){
|
|
||||||
int res;
|
|
||||||
i64 v; /* The record number that matches K */
|
|
||||||
UnpackedRecord *pIdxKey; /* Unpacked version of P4 */
|
|
||||||
|
|
||||||
/* Make sure K is a string and make zKey point to K
|
/* Search the B-Tree index. If no conflicting record is found, jump
|
||||||
*/
|
** to P2. Otherwise, copy the rowid of the conflicting record to
|
||||||
assert( pK->flags & MEM_Blob );
|
** register P3 and fall through to the next instruction. */
|
||||||
pIdxKey = sqlite3VdbeRecordUnpack(pCx->pKeyInfo, pK->n, pK->z,
|
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &pCx->seekResult);
|
||||||
aTempRec, sizeof(aTempRec));
|
if( (r.flags & UNPACKED_PREFIX_SEARCH) || r.rowid==R ){
|
||||||
if( pIdxKey==0 ){
|
|
||||||
goto no_mem;
|
|
||||||
}
|
|
||||||
pIdxKey->flags |= UNPACKED_IGNORE_ROWID;
|
|
||||||
|
|
||||||
/* Search for an entry in P1 where all but the last rowid match K
|
|
||||||
** If there is no such entry, jump immediately to P2.
|
|
||||||
*/
|
|
||||||
assert( pCx->deferredMoveto==0 );
|
|
||||||
pCx->cacheStatus = CACHE_STALE;
|
|
||||||
rc = sqlite3BtreeMovetoUnpacked(pCrsr, pIdxKey, 0, 0, &res);
|
|
||||||
if( rc!=SQLITE_OK ){
|
|
||||||
sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
|
|
||||||
goto abort_due_to_error;
|
|
||||||
}
|
|
||||||
if( res<0 ){
|
|
||||||
rc = sqlite3BtreeNext(pCrsr, &res);
|
|
||||||
if( res ){
|
|
||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
|
}else{
|
||||||
break;
|
pIn3->u.i = r.rowid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rc = sqlite3VdbeIdxKeyCompare(pCx, pIdxKey, &res);
|
|
||||||
sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
|
|
||||||
if( rc!=SQLITE_OK ) goto abort_due_to_error;
|
|
||||||
if( res>0 ){
|
|
||||||
pc = pOp->p2 - 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* At this point, pCrsr is pointing to an entry in P1 where all but
|
|
||||||
** the final entry (the rowid) matches K. Check to see if the
|
|
||||||
** final rowid column is different from R. If it equals R then jump
|
|
||||||
** immediately to P2.
|
|
||||||
*/
|
|
||||||
rc = sqlite3VdbeIdxRowid(pCrsr, &v);
|
|
||||||
if( rc!=SQLITE_OK ){
|
|
||||||
goto abort_due_to_error;
|
|
||||||
}
|
|
||||||
if( v==R ){
|
|
||||||
pc = pOp->p2 - 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The final varint of the key is different from R. Store it back
|
|
||||||
** into register R3. (The record number of an entry that violates
|
|
||||||
** a UNIQUE constraint.)
|
|
||||||
*/
|
|
||||||
pIn3->u.i = v;
|
|
||||||
assert( pIn3->flags&MEM_Int );
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3475,6 +3454,7 @@ case OP_NotExists: { /* jump, in3 */
|
|||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
assert( pC->rowidIsValid==0 );
|
assert( pC->rowidIsValid==0 );
|
||||||
}
|
}
|
||||||
|
pC->seekResult = res;
|
||||||
}else if( !pC->pseudoTable ){
|
}else if( !pC->pseudoTable ){
|
||||||
/* This happens when an attempt to open a read cursor on the
|
/* This happens when an attempt to open a read cursor on the
|
||||||
** sqlite_master table returns SQLITE_EMPTY.
|
** sqlite_master table returns SQLITE_EMPTY.
|
||||||
@@ -3482,6 +3462,7 @@ case OP_NotExists: { /* jump, in3 */
|
|||||||
assert( pC->isTable );
|
assert( pC->isTable );
|
||||||
pc = pOp->p2 - 1;
|
pc = pOp->p2 - 1;
|
||||||
assert( pC->rowidIsValid==0 );
|
assert( pC->rowidIsValid==0 );
|
||||||
|
pC->seekResult = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3724,6 +3705,7 @@ case OP_Insert: {
|
|||||||
pC->nullRow = 0;
|
pC->nullRow = 0;
|
||||||
}else{
|
}else{
|
||||||
int nZero;
|
int nZero;
|
||||||
|
int seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
|
||||||
if( pData->flags & MEM_Zero ){
|
if( pData->flags & MEM_Zero ){
|
||||||
nZero = pData->u.nZero;
|
nZero = pData->u.nZero;
|
||||||
}else{
|
}else{
|
||||||
@@ -3732,7 +3714,8 @@ case OP_Insert: {
|
|||||||
sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
|
sqlite3BtreeSetCachedRowid(pC->pCursor, 0);
|
||||||
rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
|
rc = sqlite3BtreeInsert(pC->pCursor, 0, iKey,
|
||||||
pData->z, pData->n, nZero,
|
pData->z, pData->n, nZero,
|
||||||
pOp->p5 & OPFLAG_APPEND);
|
pOp->p5 & OPFLAG_APPEND, seekResult
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pC->rowidIsValid = 0;
|
pC->rowidIsValid = 0;
|
||||||
@@ -4103,7 +4086,7 @@ case OP_Next: { /* jump */
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Opcode: IdxInsert P1 P2 P3 * *
|
/* Opcode: IdxInsert P1 P2 P3 * P5
|
||||||
**
|
**
|
||||||
** Register P2 holds a SQL index key made using the
|
** Register P2 holds a SQL index key made using the
|
||||||
** MakeRecord instructions. This opcode writes that key
|
** MakeRecord instructions. This opcode writes that key
|
||||||
@@ -4128,7 +4111,9 @@ case OP_IdxInsert: { /* in2 */
|
|||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
int nKey = pIn2->n;
|
int nKey = pIn2->n;
|
||||||
const char *zKey = pIn2->z;
|
const char *zKey = pIn2->z;
|
||||||
rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3);
|
rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3,
|
||||||
|
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
|
||||||
|
);
|
||||||
assert( pC->deferredMoveto==0 );
|
assert( pC->deferredMoveto==0 );
|
||||||
pC->cacheStatus = CACHE_STALE;
|
pC->cacheStatus = CACHE_STALE;
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
** 6000 lines long) it was split up into several smaller files and
|
** 6000 lines long) it was split up into several smaller files and
|
||||||
** this header information was factored out.
|
** this header information was factored out.
|
||||||
**
|
**
|
||||||
** $Id: vdbeInt.h,v 1.169 2009/04/22 02:15:48 drh Exp $
|
** $Id: vdbeInt.h,v 1.170 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef _VDBEINT_H_
|
#ifndef _VDBEINT_H_
|
||||||
#define _VDBEINT_H_
|
#define _VDBEINT_H_
|
||||||
@@ -80,6 +80,10 @@ struct VdbeCursor {
|
|||||||
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
|
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
|
||||||
const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
|
const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
|
||||||
|
|
||||||
|
/* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
|
||||||
|
** OP_IsUnique opcode on this cursor. */
|
||||||
|
int seekResult;
|
||||||
|
|
||||||
/* Cached information about the header for the data record that the
|
/* Cached information about the header for the data record that the
|
||||||
** cursor is currently pointing to. Only valid if cacheValid is true.
|
** cursor is currently pointing to. Only valid if cacheValid is true.
|
||||||
** aRow might point to (ephemeral) data for the current row, or it might
|
** aRow might point to (ephemeral) data for the current row, or it might
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
** to version 2.8.7, all this code was combined into the vdbe.c source file.
|
** to version 2.8.7, all this code was combined into the vdbe.c source file.
|
||||||
** But that file was getting too big so this subroutines were split out.
|
** But that file was getting too big so this subroutines were split out.
|
||||||
**
|
**
|
||||||
** $Id: vdbeaux.c,v 1.454 2009/04/22 15:32:59 drh Exp $
|
** $Id: vdbeaux.c,v 1.455 2009/05/04 11:42:30 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include "vdbeInt.h"
|
#include "vdbeInt.h"
|
||||||
@@ -2496,6 +2496,18 @@ int sqlite3VdbeRecordCompare(
|
|||||||
}
|
}
|
||||||
if( mem1.zMalloc ) sqlite3VdbeMemRelease(&mem1);
|
if( mem1.zMalloc ) sqlite3VdbeMemRelease(&mem1);
|
||||||
|
|
||||||
|
/* If the PREFIX_SEARCH flag is set and all fields except the final
|
||||||
|
** rowid field were equal, then clear the PREFIX_SEARCH flag and set
|
||||||
|
** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1).
|
||||||
|
** This is used by the OP_IsUnique opcode.
|
||||||
|
*/
|
||||||
|
if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){
|
||||||
|
assert( idx1==szHdr1 && rc );
|
||||||
|
assert( mem1.flags & MEM_Int );
|
||||||
|
pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH;
|
||||||
|
pPKey2->rowid = mem1.u.i;
|
||||||
|
}
|
||||||
|
|
||||||
if( rc==0 ){
|
if( rc==0 ){
|
||||||
/* rc==0 here means that one of the keys ran out of fields and
|
/* rc==0 here means that one of the keys ran out of fields and
|
||||||
** all the fields up to that point were equal. If the UNPACKED_INCRKEY
|
** all the fields up to that point were equal. If the UNPACKED_INCRKEY
|
||||||
|
Reference in New Issue
Block a user