1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Merge recent trunk micro-optimizations and the DESC index GROUP BY ORDER BY

bug fix into the sessions branch.

FossilOrigin-Name: 83d4114f2aa404e670ced33511183baacd813a01
This commit is contained in:
drh
2014-10-14 13:41:32 +00:00
18 changed files with 291 additions and 182 deletions

View File

@ -1,5 +1,5 @@
C Merge\sall\srecent\strunk\schanges.
D 2014-10-10T12:56:35.492
C Merge\srecent\strunk\smicro-optimizations\sand\sthe\sDESC\sindex\sGROUP\sBY\sORDER\sBY\nbug\sfix\sinto\sthe\ssessions\sbranch.
D 2014-10-14T13:41:32.002
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in dd5f245aa8c741bc65845747203c8ce2f3fb6c83
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -188,10 +188,10 @@ F src/auth.c d8abcde53426275dab6243b441256fcd8ccbebb2
F src/backup.c a31809c65623cc41849b94d368917f8bb66e6a7e
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
F src/btmutex.c 49ca66250c7dfa844a4d4cb8272b87420d27d3a5
F src/btree.c fa00618117fb6bb46c243452c56997c0d22d4fc9
F src/btree.c c9fcae8145436f728c61272cba72b1469c07f30d
F src/btree.h a79aa6a71e7f1055f01052b7f821bd1c2dce95c8
F src/btreeInt.h 1bd7957161a1346a914f1f09231610e777a8e58d
F src/build.c 9e5205db9a0c8a1a4ce7379d60a2a34cb0b7339c
F src/build.c 9dc2bd94347b878c89627000c92b0c8d97ec2919
F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0
F src/complete.c 535183afb3c75628b78ce82612931ac7cdf26f14
F src/ctime.c bb434068b5308a857b181c2d204a320ff0d6c638
@ -236,19 +236,19 @@ F src/pager.h ffd5607f7b3e4590b415b007a4382f693334d428
F src/parse.y 5dfead8aed90cb0c7c1115898ee2266804daff45
F src/pcache.c 4121a0571c18581ee9f82f086d5e2030051ebd6a
F src/pcache.h 9b559127b83f84ff76d735c8262f04853be0c59a
F src/pcache1.c dab8ab930d4a73b99768d881185994f34b80ecaa
F src/pcache1.c e412cb585f777c840ddce0500eddc5c6043c2bb5
F src/pragma.c 3f3e959390a10c0131676f0e307acce372777e0f
F src/prepare.c 6ef0cf2f9274982988ed6b7cab1be23147e94196
F src/printf.c 6b79bbd063dcbadca4cf617a4cde255bcc13ea64
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
F src/resolve.c a3466128b52a86c466e47ac1a19e2174f7b5cf89
F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e
F src/select.c f11533162b57ed5ed37f549add34cbcdf51f6712
F src/shell.c 801af09adf9bc64dea59b62880bfb72b42c5f5f2
F src/select.c 428165951748151e87a15295b7357221433e311b
F src/shell.c f2b146c89967d83cca8735126ee9ff4ce7345dd1
F src/sqlite.h.in 514eeb7b0845840c132736e714ddd98ab9216e6c
F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad
F src/sqlite3ext.h 17d487c3c91b0b8c584a32fbeb393f6f795eea7d
F src/sqliteInt.h eaacd377fa2eb4761dbad3b762b0483886f76565
F src/sqliteInt.h f712b324bafdc646c7fcfe813f81e8b8eebb57b9
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 961d5926e5a8fda611d385ec22c226b8635cd1cb
F src/table.c 2e99ef7ef16187e17033d9398dc962ce22dab5cb
@ -298,18 +298,18 @@ F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb
F src/test_vfs.c f84075a388527892ff184988f43b69ce69b8083c
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/threads.c 22dded4283dc4b25422f6444cdcb8d6b1ea0b5ff
F src/threads.c 60c9d400abf17ccdc8767cdc6af90b9c5acf58bd
F src/tokenize.c cc9016e5007fc5e76789079616d2f26741bcc689
F src/trigger.c 25571661fdeae8c7f975ff40ffec205520a3f92f
F src/update.c b9e5295d3a78e96b7c2978c4f9d224d06880f031
F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c
F src/util.c 4006c01772bd8d8ac4306d523bbcee41d3e392d8
F src/vacuum.c 59f03f92bcff57faa6a8ca256eb29ccddfb0614a
F src/vdbe.c 10880af9bbd7e19eda4db1ed7a3f2dd1b645f858
F src/vdbe.c a1465183697f2a187520d13cebf6a41f3832e0c0
F src/vdbe.h d61daeffed696e21630759de9e135ee298ad9573
F src/vdbeInt.h 6d4bf3fd28699b5c81a68398e9d23720da58fcd8
F src/vdbeInt.h 7254c20b45033e1194601a02f3a386e39263c0ab
F src/vdbeapi.c 30a1c991147fdf4334900b5fed8a312ae7678707
F src/vdbeaux.c ea20bde8cad7b15198879255af849f105daa2fa0
F src/vdbeaux.c 191ea23e5adbe5b6adcb9d31860b9fb890834e24
F src/vdbeblob.c d65b01f439df63911ac3d7a9a85c15503965f2c3
F src/vdbemem.c 1731e1db2b71e62243cf825b920d14adefaba3c7
F src/vdbesort.c 5c1bacf90578d22b630fbf6ed98ccf60d83435ef
@ -318,7 +318,7 @@ F src/vtab.c 019dbfd0406a7447c990e1f7bd1dfcdb8895697f
F src/wal.c 10e7de7ce90865a68153f001a61f1d985cd17983
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
F src/where.c 6fe21e0f60a449af5d75d00e6d480370464a9a48
F src/where.c 2947912f1f3d6a7766fe087fd532a5d688d745b1
F src/whereInt.h 124d970450955a6982e174b07c320ae6d62a595c
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@ -626,7 +626,7 @@ F test/fuzzer1.test d4c52aaf3ef923da293a2653cfab33d02f718a36
F test/fuzzerfault.test 8792cd77fd5bce765b05d0c8e01b9edcf8af8536
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
F test/hexlit.test f9ecde8145bfc2341573473256c74ae37a200497
F test/hook.test 76201cfcba801d02d13600700f26ab64313d1826
F test/hook.test aa41c095d26822b8a51aa4c82904a14347961be6
F test/icu.test 70df4faca133254c042d02ae342c0a141f2663f4
F test/in.test 047c4671328e9032ab95666a67021adbbd36e98e
F test/in2.test 5d4c61d17493c832f7d2d32bef785119e87bde75
@ -647,7 +647,7 @@ F test/index.test 4d990005a67a36984e4f1a5f1bdccea8d08da4ee
F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
F test/index3.test 55a90cff99834305e8141df7afaef39674b57062
F test/index4.test ab92e736d5946840236cd61ac3191f91a7856bf6
F test/index5.test fc07c14193c0430814e7a08b5da46888ee795c33
F test/index5.test 25b0b451aceed4ac5f7d49f856f6de7257470b3e
F test/index6.test fb370966ac3cd0989053dd5385757b5c3e24ab6a
F test/index7.test 917cf1e1c7439bb155abbeabec511b28945e157b
F test/indexedby.test b2f22f3e693a53813aa3f50b812eb609ba6df1ec
@ -799,7 +799,7 @@ F test/rdonly.test dd30a4858d8e0fbad2304c2bd74a33d4df36412a
F test/regexp1.test 497ea812f264d12b6198d6e50a76be4a1973a9d8
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/releasetest.mk 2eced2f9ae701fd0a29e714a241760503ccba25a
F test/releasetest.tcl a0df0dfc5e3ee83ade87b9cc96db41b52d590b9e
F test/releasetest.tcl 4296b9adbc5992bcd0b0b2876b7651f57c1494f2
F test/resolver01.test 33abf37ff8335e6bf98f2b45a0af3e06996ccd9a
F test/rollback.test e9504a009a202c3ed711da2e6879ff60c5a4669c
F test/rowhash.test 0bc1d31415e4575d10cacf31e1a66b5cc0f8be81
@ -877,7 +877,7 @@ F test/speed3.test d32043614c08c53eafdc80f33191d5bd9b920523
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
F test/speedtest1.c 83f6b3318f7ee60e52b978b5a5e5dd7e83dfb7ee
F test/speedtest1.c e4e2aa37ff66bad9f414a50a8cb9edfaac65c9e5
F test/spellfix.test 24f676831acddd2f4056a598fd731a72c6311f49
F test/sqllimits1.test 9014524e7ab16e2a4976b13397db4c29cc29c6d9
F test/stat.test 76fd746b85459e812a0193410fb599f0531f22de
@ -950,6 +950,7 @@ F test/tkt-b1d3a2e531.test 8f7576e41ca179289ee1a8fee28386fd8e4b0550
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
F test/tkt-b72787b1.test a95e8cdad0b98af1853ac7f0afd4ab27b77bf5f3
F test/tkt-b75a9ca6b0.test 97cc2d5eeaf82799eb42138c0a1ff64370238ce4
F test/tkt-ba7cbfaedc.test e76d88e572e489ee0d64fe4caf4af18b3d1dc688
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
F test/tkt-bdc6bbbb38.test fc38bb09bdd440e3513a1f5f98fc60a075182d7d
F test/tkt-c48d99d690.test ba61977d62ab612fc515b3c488a6fbd6464a2447
@ -1220,7 +1221,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P 2695772c984c215649a16e1e3e18a8048a6a60dd 49dfee7cd1c9ab2901b8a871a6cd00b2ead76801
R 0a03cea1a4d1a941b11aa663569e1d01
P abfef254721ca29b653f24bfe80e88adaa92dc6c 005e5b388a8a97bca6d1f0e06c40d68d92aa1212
R 97eb4d72b7b8377fe0610a9bf4a72e27
U drh
Z 20a722d001f64c3e0f0d27d851d3747e
Z 615445a8ed5827e2422bf5308f655db8

View File

@ -1 +1 @@
abfef254721ca29b653f24bfe80e88adaa92dc6c
83d4114f2aa404e670ced33511183baacd813a01

View File

@ -776,7 +776,7 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){
** back to where it ought to be if this routine returns true.
*/
int sqlite3BtreeCursorHasMoved(BtCursor *pCur){
return pCur && pCur->eState!=CURSOR_VALID;
return pCur->eState!=CURSOR_VALID;
}
/*
@ -5845,11 +5845,6 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
** in pTemp or the original pCell) and also record its index.
** Allocating a new entry in pPage->aCell[] implies that
** pPage->nOverflow is incremented.
**
** If nSkip is non-zero, then do not copy the first nSkip bytes of the
** cell. The caller will overwrite them after this function returns. If
** nSkip is non-zero, then pCell may not point to an invalid memory location
** (but pCell+nSkip is always valid).
*/
static void insertCell(
MemPage *pPage, /* Page into which we are copying */
@ -5866,7 +5861,6 @@ static void insertCell(
int ins; /* Index in data[] where new cell pointer is inserted */
int cellOffset; /* Address of first cell pointer in data[] */
u8 *data; /* The content of the whole page */
int nSkip = (iChild ? 4 : 0);
if( *pRC ) return;
@ -5884,7 +5878,7 @@ static void insertCell(
assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
memcpy(pTemp, pCell, sz);
pCell = pTemp;
}
if( iChild ){
@ -5913,7 +5907,7 @@ static void insertCell(
assert( idx+sz <= (int)pPage->pBt->usableSize );
pPage->nCell++;
pPage->nFree -= (u16)(2 + sz);
memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip);
memcpy(&data[idx], pCell, sz);
if( iChild ){
put4byte(&data[idx], iChild);
}

View File

@ -2747,7 +2747,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
}else{
addr2 = sqlite3VdbeCurrentAddr(v);
}
sqlite3VdbeAddOp2(v, OP_SorterData, iSorter, regRecord);
sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx);
sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
sqlite3ReleaseTempReg(pParse, regRecord);

View File

@ -688,7 +688,7 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
if( createFlag==1 && (
nPinned>=pGroup->mxPinned
|| nPinned>=pCache->n90pct
|| pcache1UnderMemoryPressure(pCache)
|| (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclable<nPinned)
)){
return 0;
}

View File

@ -1181,7 +1181,6 @@ static void generateSortTail(
int nKey;
int iSortTab; /* Sorter cursor to read from */
int nSortData; /* Trailing values to read from sorter */
u8 p5; /* p5 parameter for 1st OP_Column */
int i;
int bSeq; /* True if sorter record includes seq. no. */
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
@ -1215,19 +1214,16 @@ static void generateSortTail(
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
VdbeCoverage(v);
codeOffset(v, p->iOffset, addrContinue);
sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
p5 = OPFLAG_CLEARCACHE;
sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab);
bSeq = 0;
}else{
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v);
codeOffset(v, p->iOffset, addrContinue);
iSortTab = iTab;
p5 = 0;
bSeq = 1;
}
for(i=0; i<nSortData; i++){
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq+i, regRow+i);
if( i==0 ) sqlite3VdbeChangeP5(v, p5);
VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
}
switch( eDest ){
@ -5156,12 +5152,11 @@ int sqlite3Select(
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
sqlite3ExprCacheClear(pParse);
if( groupBySort ){
sqlite3VdbeAddOp2(v, OP_SorterData, sAggInfo.sortingIdx, sortOut);
sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx, sortOut,sortPTab);
}
for(j=0; j<pGroupBy->nExpr; j++){
if( groupBySort ){
sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j);
if( j==0 ) sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
}else{
sAggInfo.directMode = 1;
sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j);

View File

@ -4006,6 +4006,7 @@ static int process_input(ShellState *p, FILE *in){
if( nSql ){
if( !_all_whitespace(zSql) ){
fprintf(stderr, "Error: incomplete SQL: %s\n", zSql);
errCnt++;
}
free(zSql);
}

View File

@ -159,7 +159,7 @@
*/
#if defined(__GNUC__)
# define SQLITE_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
#elif defined(_MSC_VER) && _MSC_VER>=1310
# define SQLITE_NOINLINE __declspec(noinline)
#else
# define SQLITE_NOINLINE
@ -2677,7 +2677,6 @@ struct AuthContext {
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
#define OPFLAG_APPEND 0x08 /* This is likely to be an append */
#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */
#define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */
#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */

View File

@ -105,7 +105,7 @@ int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
/* A running thread */
struct SQLiteThread {
uintptr_t tid; /* The thread handle */
void *tid; /* The thread handle */
unsigned id; /* The thread identifier */
void *(*xTask)(void*); /* The routine to run as a thread */
void *pIn; /* Argument to xTask */
@ -153,7 +153,7 @@ int sqlite3ThreadCreate(
}else{
p->xTask = xTask;
p->pIn = pIn;
p->tid = _beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id);
p->tid = (void*)_beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id);
if( p->tid==0 ){
memset(p, 0, sizeof(*p));
}

View File

@ -224,6 +224,7 @@ static VdbeCursor *allocateCursor(
memset(pCx, 0, sizeof(VdbeCursor));
pCx->iDb = iDb;
pCx->nField = nField;
pCx->aOffset = &pCx->aType[nField];
if( isBtreeCursor ){
pCx->pCursor = (BtCursor*)
&pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
@ -2286,7 +2287,7 @@ case OP_Column: {
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( p2<pC->nField );
aOffset = pC->aType + pC->nField;
aOffset = pC->aOffset;
#ifndef SQLITE_OMIT_VIRTUALTABLE
assert( pC->pVtabCursor==0 ); /* OP_Column never called on virtual table */
#endif
@ -2297,7 +2298,7 @@ case OP_Column: {
/* If the cursor cache is stale, bring it up-to-date */
rc = sqlite3VdbeCursorMoveto(pC);
if( rc ) goto abort_due_to_error;
if( pC->cacheStatus!=p->cacheCtr || (pOp->p5&OPFLAG_CLEARCACHE)!=0 ){
if( pC->cacheStatus!=p->cacheCtr ){
if( pC->nullRow ){
if( pCrsr==0 ){
assert( pC->pseudoTableReg>0 );
@ -2342,14 +2343,6 @@ case OP_Column: {
pC->iHdrOffset = getVarint32(pC->aRow, offset);
pC->nHdrParsed = 0;
aOffset[0] = offset;
if( avail<offset ){
/* pC->aRow does not have to hold the entire row, but it does at least
** need to cover the header of the record. If pC->aRow does not contain
** the complete header, then set it to zero, forcing the header to be
** dynamically allocated. */
pC->aRow = 0;
pC->szRow = 0;
}
/* Make sure a corrupt database has not given us an oversize header.
** Do this now to avoid an oversize memory allocation.
@ -2364,6 +2357,22 @@ case OP_Column: {
rc = SQLITE_CORRUPT_BKPT;
goto op_column_error;
}
if( avail<offset ){
/* pC->aRow does not have to hold the entire row, but it does at least
** need to cover the header of the record. If pC->aRow does not contain
** the complete header, then set it to zero, forcing the header to be
** dynamically allocated. */
pC->aRow = 0;
pC->szRow = 0;
}
/* The following goto is an optimization. It can be omitted and
** everything will still work. But OP_Column is measurably faster
** by skipping the subsequent conditional, which is always true.
*/
assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */
goto op_column_read_header;
}
/* Make sure at least the first p2+1 entries of the header have been
@ -2373,6 +2382,7 @@ case OP_Column: {
/* If there is more header available for parsing in the record, try
** to extract additional fields up through the p2+1-th field
*/
op_column_read_header:
if( pC->iHdrOffset<aOffset[0] ){
/* Make sure zData points to enough of the record to cover the header. */
if( pC->aRow==0 ){
@ -2417,15 +2427,16 @@ case OP_Column: {
sMem.flags = MEM_Null;
}
/* If we have read more header data than was contained in the header,
** or if the end of the last field appears to be past the end of the
** record, or if the end of the last field appears to be before the end
** of the record (when all fields present), then we must be dealing
** with a corrupt database.
/* The record is corrupt if any of the following are true:
** (1) the bytes of the header extend past the declared header size
** (zHdr>zEndHdr)
** (2) the entire header was used but not all data was used
** (zHdr==zEndHdr && offset!=pC->payloadSize)
** (3) the end of the data extends beyond the end of the record.
** (offset > pC->payloadSize)
*/
if( (zHdr > zEndHdr)
if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset!=pC->payloadSize))
|| (offset > pC->payloadSize)
|| (zHdr==zEndHdr && offset!=pC->payloadSize)
){
rc = SQLITE_CORRUPT_BKPT;
goto op_column_error;
@ -2616,7 +2627,7 @@ case OP_MakeRecord: {
pRec = pLast;
do{
assert( memIsValid(pRec) );
serial_type = sqlite3VdbeSerialType(pRec, file_format);
pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format);
len = sqlite3VdbeSerialTypeLen(serial_type);
if( pRec->flags & MEM_Zero ){
if( nData ){
@ -2665,7 +2676,7 @@ case OP_MakeRecord: {
assert( pData0<=pLast );
pRec = pData0;
do{
serial_type = sqlite3VdbeSerialType(pRec, file_format);
serial_type = pRec->uTemp;
i += putVarint32(&zNewRecord[i], serial_type); /* serial type */
j += sqlite3VdbeSerialPut(&zNewRecord[j], pRec, serial_type); /* content */
}while( (++pRec)<=pLast );
@ -3564,7 +3575,6 @@ case OP_SeekGT: { /* jump, in3 */
applyNumericAffinity(pIn3, 0);
}
iKey = sqlite3VdbeIntValue(pIn3);
pC->rowidIsValid = 0;
/* If the P3 value could not be converted into an integer without
** loss of information, then special processing is required... */
@ -3600,13 +3610,10 @@ case OP_SeekGT: { /* jump, in3 */
}
}
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, 0, (u64)iKey, 0, &res);
pC->movetoTarget = iKey; /* Used by OP_Delete */
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
if( res==0 ){
pC->rowidIsValid = 1;
pC->lastRowid = iKey;
}
}else{
nField = pOp->p4.i;
assert( pOp->p4type==P4_INT32 );
@ -3636,7 +3643,6 @@ case OP_SeekGT: { /* jump, in3 */
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
pC->rowidIsValid = 0;
}
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
@ -3648,7 +3654,6 @@ case OP_SeekGT: { /* jump, in3 */
res = 0;
rc = sqlite3BtreeNext(pC->pCursor, &res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
pC->rowidIsValid = 0;
}else{
res = 0;
}
@ -3658,7 +3663,6 @@ case OP_SeekGT: { /* jump, in3 */
res = 0;
rc = sqlite3BtreePrevious(pC->pCursor, &res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
pC->rowidIsValid = 0;
}else{
/* res might be negative because the table is empty. Check to
** see if this is the case.
@ -3695,7 +3699,6 @@ case OP_Seek: { /* in2 */
pC->nullRow = 0;
pIn2 = &aMem[pOp->p2];
pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
pC->rowidIsValid = 0;
pC->deferredMoveto = 1;
break;
}
@ -3881,15 +3884,13 @@ case OP_NotExists: { /* jump, in3 */
res = 0;
iKey = pIn3->u.i;
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
pC->lastRowid = pIn3->u.i;
pC->rowidIsValid = res==0 ?1:0;
pC->movetoTarget = iKey; /* Used by OP_Delete */
pC->nullRow = 0;
pC->cacheStatus = CACHE_STALE;
pC->deferredMoveto = 0;
VdbeBranchTaken(res!=0,2);
if( res!=0 ){
pc = pOp->p2 - 1;
assert( pC->rowidIsValid==0 );
}
pC->seekResult = res;
break;
@ -4037,7 +4038,6 @@ case OP_NewRowid: { /* out2-prerelease */
}
assert( v>0 ); /* EV: R-40812-03570 */
}
pC->rowidIsValid = 0;
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
}
@ -4137,7 +4137,7 @@ case OP_InsertInt: {
/* Invoke the pre-update hook, if any */
if( db->xPreUpdateCallback
&& pOp->p4type==P4_TABLE
&& (!(pOp->p5 & OPFLAG_ISUPDATE) || pC->rowidIsValid==0)
&& !(pOp->p5 & OPFLAG_ISUPDATE)
&& HasRowid(pTab)
){
sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, iKey, pOp->p2);
@ -4162,7 +4162,6 @@ case OP_InsertInt: {
pData->z, pData->n, nZero,
(pOp->p5 & OPFLAG_APPEND)!=0, seekResult
);
pC->rowidIsValid = 0;
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
@ -4200,7 +4199,6 @@ case OP_InsertInt: {
** be set to by the update.
*/
case OP_Delete: {
i64 iKey;
VdbeCursor *pC;
const char *zDb;
Table *pTab;
@ -4211,29 +4209,27 @@ case OP_Delete: {
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
assert( pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
iKey = pC->lastRowid; /* Only used for the update hook */
/* The OP_Delete opcode always follows an OP_NotExists or OP_Last or
** OP_Column on the same table without any intervening operations that
** might move or invalidate the cursor. Hence cursor pC is always pointing
** to the row to be deleted and the sqlite3VdbeCursorMoveto() operation
** below is always a no-op and cannot fail. We will run it anyhow, though,
** to guard against future changes to the code generator.
**/
assert( pC->deferredMoveto==0 );
rc = sqlite3VdbeCursorMoveto(pC);
if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
#ifdef SQLITE_DEBUG
if( pOp->p4type==P4_TABLE && HasRowid(pOp->p4.pTab) ){
/* The seek operation that positioned the cursor prior to OP_Delete will
** have also set the pC->movetoTarget field to the rowid of the row that
** is being deleted */
i64 iKey = 0;
sqlite3BtreeKeySize(pC->pCursor, &iKey);
assert( pC->movetoTarget==iKey );
}
#endif
/* If the update-hook or pre-update-hook will be invoked, set iKey to
** the rowid of the row being deleted. Set zDb and zTab as well.
*/
if( pOp->p4.z && HAS_UPDATE_HOOK(db) ){
assert( pC->iDb>=0 );
assert( pC->rowidIsValid || !HasRowid(pOp->p4.pTab) );
iKey = pC->lastRowid;
zDb = db->aDb[pC->iDb].zName;
pTab = pOp->p4.pTab;
}
}
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/* Invoke the pre-update-hook if required. */
@ -4241,7 +4237,7 @@ case OP_Delete: {
assert( !(opflags & OPFLAG_ISUPDATE) || (aMem[pOp->p3].flags & MEM_Int) );
sqlite3VdbePreUpdateHook(p, pC,
(opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE,
zDb, pTab, iKey,
zDb, pTab, pC->movetoTarget,
pOp->p3
);
}
@ -4257,7 +4253,8 @@ case OP_Delete: {
p->nChange++;
assert( pOp->p4.z );
if( rc==SQLITE_OK && db->xUpdateCallback && HasRowid(pTab) ){
db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName,iKey);
db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName,
pC->movetoTarget);
}
}
break;
@ -4309,10 +4306,17 @@ case OP_SorterCompare: {
break;
};
/* Opcode: SorterData P1 P2 * * *
/* Opcode: SorterData P1 P2 P3 * *
** Synopsis: r[P2]=data
**
** Write into register P2 the current sorter data for sorter cursor P1.
** Then clear the column header cache on cursor P3.
**
** This opcode is normally use to move a record out of the sorter and into
** a register that is the source for a pseudo-table cursor created using
** OpenPseudo. That pseudo-table cursor is the one that is identified by
** parameter P3. Clearing the P3 column cache as part of this opcode saves
** us from having to issue a separate NullRow instruction to clear that cache.
*/
case OP_SorterData: {
VdbeCursor *pC;
@ -4322,6 +4326,8 @@ case OP_SorterData: {
assert( isSorter(pC) );
rc = sqlite3VdbeSorterRowkey(pC, pOut);
assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE;
break;
}
@ -4368,16 +4374,20 @@ case OP_RowData: {
assert( pC->pseudoTableReg==0 );
assert( pC->pCursor!=0 );
pCrsr = pC->pCursor;
assert( sqlite3BtreeCursorIsValid(pCrsr) );
/* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or
** OP_Rewind/Op_Next with no intervening instructions that might invalidate
** the cursor. Hence the following sqlite3VdbeCursorMoveto() call is always
** a no-op and can never fail. But we leave it in place as a safety.
** the cursor. If this where not the case, on of the following assert()s
** would fail. Should this ever change (because of changes in the code
** generator) then the fix would be to insert a call to
** sqlite3VdbeCursorMoveto().
*/
assert( pC->deferredMoveto==0 );
assert( sqlite3BtreeCursorIsValid(pCrsr) );
#if 0 /* Not required due to the previous to assert() statements */
rc = sqlite3VdbeCursorMoveto(pC);
if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
if( rc!=SQLITE_OK ) goto abort_due_to_error;
#endif
if( pC->isTable==0 ){
assert( !pC->isTable );
@ -4446,14 +4456,10 @@ case OP_Rowid: { /* out2-prerelease */
#endif /* SQLITE_OMIT_VIRTUALTABLE */
}else{
assert( pC->pCursor!=0 );
rc = sqlite3VdbeCursorMoveto(pC);
rc = sqlite3VdbeCursorRestore(pC);
if( rc ) goto abort_due_to_error;
if( pC->rowidIsValid ){
v = pC->lastRowid;
}else{
rc = sqlite3BtreeKeySize(pC->pCursor, &v);
assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */
}
rc = sqlite3BtreeKeySize(pC->pCursor, &v);
assert( rc==SQLITE_OK ); /* Always so because of CursorRestore() above */
}
pOut->u.i = v;
break;
@ -4472,7 +4478,6 @@ case OP_NullRow: {
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
pC->nullRow = 1;
pC->rowidIsValid = 0;
pC->cacheStatus = CACHE_STALE;
if( pC->pCursor ){
sqlite3BtreeClearCursor(pC->pCursor);
@ -4506,7 +4511,6 @@ case OP_Last: { /* jump */
rc = sqlite3BtreeLast(pCrsr, &res);
pC->nullRow = (u8)res;
pC->deferredMoveto = 0;
pC->rowidIsValid = 0;
pC->cacheStatus = CACHE_STALE;
#ifdef SQLITE_DEBUG
pC->seekOp = OP_Last;
@ -4573,7 +4577,6 @@ case OP_Rewind: { /* jump */
rc = sqlite3BtreeFirst(pCrsr, &res);
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
pC->rowidIsValid = 0;
}
pC->nullRow = (u8)res;
assert( pOp->p2>0 && pOp->p2<p->nOp );
@ -4699,7 +4702,6 @@ next_tail:
}else{
pC->nullRow = 1;
}
pC->rowidIsValid = 0;
goto check_for_interrupt;
}
@ -4815,10 +4817,16 @@ case OP_IdxRowid: { /* out2-prerelease */
pCrsr = pC->pCursor;
assert( pCrsr!=0 );
pOut->flags = MEM_Null;
rc = sqlite3VdbeCursorMoveto(pC);
if( NEVER(rc) ) goto abort_due_to_error;
assert( pC->deferredMoveto==0 );
assert( pC->isTable==0 );
assert( pC->deferredMoveto==0 );
/* sqlite3VbeCursorRestore() can only fail if the record has been deleted
** out from under the cursor. That will never happend for an IdxRowid
** opcode, hence the NEVER() arround the check of the return value.
*/
rc = sqlite3VdbeCursorRestore(pC);
if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
if( !pC->nullRow ){
rowid = 0; /* Not needed. Only used to silence a warning. */
rc = sqlite3VdbeIdxRowid(db, pCrsr, &rowid);

View File

@ -73,7 +73,6 @@ struct VdbeCursor {
#endif
i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */
u8 nullRow; /* True if pointing to a row with no data */
u8 rowidIsValid; /* True if lastRowid is valid */
u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
Bool isEphemeral:1; /* True for an ephemeral table */
Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */
@ -83,7 +82,6 @@ struct VdbeCursor {
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
i64 lastRowid; /* Rowid being deleted by OP_Delete */
VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
/* Cached information about the header for the data record that the
@ -100,6 +98,7 @@ struct VdbeCursor {
u32 szRow; /* Byte available in aRow */
u32 iHdrOffset; /* Offset to next unparsed byte of the header */
const u8 *aRow; /* Data for the current row, if all on one page */
u32 *aOffset; /* Pointer to aType[nField] */
u32 aType[1]; /* Type values for all entries in the record */
/* 2*nField extra array elements allocated for aType[], beyond the one
** static element declared in the structure. nField total array slots for
@ -176,7 +175,7 @@ struct Mem {
/* ShallowCopy only needs to copy the information above */
char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */
int szMalloc; /* Size of the zMalloc allocation */
int iPadding1; /* Padding for 8-byte alignment */
u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */
sqlite3 *db; /* The associated database connection */
void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */
#ifdef SQLITE_DEBUG
@ -403,6 +402,7 @@ struct PreUpdate {
void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int);
int sqlite3VdbeCursorMoveto(VdbeCursor*);
int sqlite3VdbeCursorRestore(VdbeCursor*);
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
void sqlite3VdbePrintOp(FILE*, int, Op*);
#endif

View File

@ -1746,7 +1746,7 @@ void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
sqlite3BtreeCloseCursor(pCx->pCursor);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pCx->pVtabCursor ){
else if( pCx->pVtabCursor ){
sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
const sqlite3_module *pModule = pVtabCursor->pVtab->pModule;
p->inVtabMethod = 1;
@ -1789,9 +1789,10 @@ static void closeAllCursors(Vdbe *p){
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
sqlite3VdbeFrameRestore(pFrame);
p->pFrame = 0;
p->nFrame = 0;
}
p->pFrame = 0;
p->nFrame = 0;
assert( p->nFrame==0 );
if( p->apCsr ){
int i;
@ -1813,7 +1814,7 @@ static void closeAllCursors(Vdbe *p){
}
/* Delete any auxdata allocations made by the VM */
sqlite3VdbeDeleteAuxData(p, -1, 0);
if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p, -1, 0);
assert( p->pAuxData==0 );
}
@ -2719,9 +2720,7 @@ static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){
assert( p->isTable );
rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res);
if( rc ) return rc;
p->lastRowid = p->movetoTarget;
if( res!=0 ) return SQLITE_CORRUPT_BKPT;
p->rowidIsValid = 1;
#ifdef SQLITE_TEST
sqlite3_search_count++;
#endif
@ -2747,6 +2746,17 @@ static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){
return rc;
}
/*
** Check to ensure that the cursor is valid. Restore the cursor
** if need be. Return any I/O error from the restore operation.
*/
int sqlite3VdbeCursorRestore(VdbeCursor *p){
if( sqlite3BtreeCursorHasMoved(p->pCursor) ){
return handleMovedCursor(p);
}
return SQLITE_OK;
}
/*
** Make sure the cursor p is ready to read or write the row to which it
** was last positioned. Return an error code if an OOM fault or I/O error
@ -2764,7 +2774,7 @@ int sqlite3VdbeCursorMoveto(VdbeCursor *p){
if( p->deferredMoveto ){
return handleDeferredMoveto(p);
}
if( sqlite3BtreeCursorHasMoved(p->pCursor) ){
if( p->pCursor && sqlite3BtreeCursorHasMoved(p->pCursor) ){
return handleMovedCursor(p);
}
return SQLITE_OK;

View File

@ -2209,8 +2209,8 @@ static int whereRangeScanEst(
assert( pLower==0 || (pLower->eOperator & (WO_GT|WO_GE))!=0 );
assert( pUpper==0 || (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
assert( p->pKeyInfo!=0 && p->pKeyInfo->aSortOrder!=0 );
if( p->pKeyInfo->aSortOrder[nEq] ){
assert( p->aSortOrder!=0 );
if( p->aSortOrder[nEq] ){
/* The roles of pLower and pUpper are swapped for a DESC index */
SWAP(WhereTerm*, pLower, pUpper);
}
@ -2737,9 +2737,8 @@ static void explainAppendTerm(
/*
** Argument pLevel describes a strategy for scanning table pTab. This
** function returns a pointer to a string buffer containing a description
** of the subset of table rows scanned by the strategy in the form of an
** SQL expression. Or, if all rows are scanned, NULL is returned.
** function appends text to pStr that describes the subset of table
** rows scanned by the strategy in the form of an SQL expression.
**
** For example, if the query:
**
@ -2749,49 +2748,37 @@ static void explainAppendTerm(
** string similar to:
**
** "a=? AND b>?"
**
** The returned pointer points to memory obtained from sqlite3DbMalloc().
** It is the responsibility of the caller to free the buffer when it is
** no longer required.
*/
static char *explainIndexRange(sqlite3 *db, WhereLoop *pLoop, Table *pTab){
static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop, Table *pTab){
Index *pIndex = pLoop->u.btree.pIndex;
u16 nEq = pLoop->u.btree.nEq;
u16 nSkip = pLoop->u.btree.nSkip;
int i, j;
Column *aCol = pTab->aCol;
i16 *aiColumn = pIndex->aiColumn;
StrAccum txt;
if( nEq==0 && (pLoop->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){
return 0;
}
sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH);
txt.db = db;
sqlite3StrAccumAppend(&txt, " (", 2);
if( nEq==0 && (pLoop->wsFlags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ) return;
sqlite3StrAccumAppend(pStr, " (", 2);
for(i=0; i<nEq; i++){
char *z = aiColumn[i] < 0 ? "rowid" : aCol[aiColumn[i]].zName;
if( i>=nSkip ){
explainAppendTerm(&txt, i, z, "=");
explainAppendTerm(pStr, i, z, "=");
}else{
if( i ) sqlite3StrAccumAppend(&txt, " AND ", 5);
sqlite3StrAccumAppend(&txt, "ANY(", 4);
sqlite3StrAccumAppendAll(&txt, z);
sqlite3StrAccumAppend(&txt, ")", 1);
if( i ) sqlite3StrAccumAppend(pStr, " AND ", 5);
sqlite3XPrintf(pStr, 0, "ANY(%s)", z);
}
}
j = i;
if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName;
explainAppendTerm(&txt, i++, z, ">");
explainAppendTerm(pStr, i++, z, ">");
}
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
char *z = aiColumn[j] < 0 ? "rowid" : aCol[aiColumn[j]].zName;
explainAppendTerm(&txt, i, z, "<");
explainAppendTerm(pStr, i, z, "<");
}
sqlite3StrAccumAppend(&txt, ")", 1);
return sqlite3StrAccumFinish(&txt);
sqlite3StrAccumAppend(pStr, ")", 1);
}
/*
@ -2815,11 +2802,13 @@ static void explainOneScan(
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
char *zMsg; /* Text to add to EQP output */
int iId = pParse->iSelectId; /* Select id (left-most output column) */
int isSearch; /* True for a SEARCH. False for SCAN. */
WhereLoop *pLoop; /* The controlling WhereLoop object */
u32 flags; /* Flags that describe this loop */
char *zMsg; /* Text to add to EQP output */
StrAccum str; /* EQP output string */
char zBuf[100]; /* Initial space for EQP output string */
pLoop = pLevel->pWLoop;
flags = pLoop->wsFlags;
@ -2829,54 +2818,70 @@ static void explainOneScan(
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN");
sqlite3StrAccumInit(&str, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
str.db = db;
sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
if( pItem->pSelect ){
zMsg = sqlite3MAppendf(db, zMsg, "%s SUBQUERY %d", zMsg,pItem->iSelectId);
sqlite3XPrintf(&str, 0, " SUBQUERY %d", pItem->iSelectId);
}else{
zMsg = sqlite3MAppendf(db, zMsg, "%s TABLE %s", zMsg, pItem->zName);
sqlite3XPrintf(&str, 0, " TABLE %s", pItem->zName);
}
if( pItem->zAlias ){
zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias);
sqlite3XPrintf(&str, 0, " AS %s", pItem->zAlias);
}
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0
&& ALWAYS(pLoop->u.btree.pIndex!=0)
){
const char *zFmt;
Index *pIdx = pLoop->u.btree.pIndex;
char *zWhere = explainIndexRange(db, pLoop, pItem->pTab);
if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
const char *zFmt = 0;
Index *pIdx;
assert( pLoop->u.btree.pIndex!=0 );
pIdx = pLoop->u.btree.pIndex;
assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) );
if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){
zFmt = zWhere ? "%s USING PRIMARY KEY%.0s%s" : "%s%.0s%s";
if( isSearch ){
zFmt = "PRIMARY KEY";
}
}else if( flags & WHERE_AUTO_INDEX ){
zFmt = "%s USING AUTOMATIC COVERING INDEX%.0s%s";
zFmt = "AUTOMATIC COVERING INDEX";
}else if( flags & WHERE_IDX_ONLY ){
zFmt = "%s USING COVERING INDEX %s%s";
zFmt = "COVERING INDEX %s";
}else{
zFmt = "%s USING INDEX %s%s";
zFmt = "INDEX %s";
}
if( zFmt ){
sqlite3StrAccumAppend(&str, " USING ", 7);
sqlite3XPrintf(&str, 0, zFmt, pIdx->zName);
explainIndexRange(&str, pLoop, pItem->pTab);
}
zMsg = sqlite3MAppendf(db, zMsg, zFmt, zMsg, pIdx->zName, zWhere);
sqlite3DbFree(db, zWhere);
}else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg);
const char *zRange;
if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg);
zRange = "(rowid=?)";
}else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid<?)", zMsg);
zRange = "(rowid>? AND rowid<?)";
}else if( flags&WHERE_BTM_LIMIT ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>?)", zMsg);
}else if( ALWAYS(flags&WHERE_TOP_LIMIT) ){
zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid<?)", zMsg);
zRange = "(rowid>?)";
}else{
assert( flags&WHERE_TOP_LIMIT);
zRange = "(rowid<?)";
}
sqlite3StrAccumAppendAll(&str, " USING INTEGER PRIMARY KEY ");
sqlite3StrAccumAppendAll(&str, zRange);
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg,
sqlite3XPrintf(&str, 0, " VIRTUAL TABLE INDEX %d:%s",
pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
}
#endif
zMsg = sqlite3MAppendf(db, zMsg, "%s", zMsg);
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
if( pLoop->nOut>=10 ){
sqlite3XPrintf(&str, 0, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
}else{
sqlite3StrAccumAppend(&str, " (~1 row)", 9);
}
#endif
zMsg = sqlite3StrAccumFinish(&str);
sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC);
}
}
@ -5355,7 +5360,7 @@ static i8 wherePathSatisfiesOrderBy(
isMatch = 1;
break;
}
if( isMatch && (pWInfo->wctrlFlags & WHERE_GROUPBY)==0 ){
if( isMatch && (wctrlFlags & WHERE_GROUPBY)==0 ){
/* Make sure the sort order is compatible in an ORDER BY clause.
** Sort order is irrelevant for a GROUP BY clause. */
if( revSet ){
@ -5820,12 +5825,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)
&& pWInfo->nOBSat==pWInfo->pOrderBy->nExpr
){
Bitmask notUsed = 0;
Bitmask revMask = 0;
int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy,
pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], &notUsed
pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], &revMask
);
assert( pWInfo->sorted==0 );
pWInfo->sorted = (nOrder==pWInfo->pOrderBy->nExpr);
if( nOrder==pWInfo->pOrderBy->nExpr ){
pWInfo->sorted = 1;
pWInfo->revMask = revMask;
}
}
}

View File

@ -202,6 +202,8 @@ do_test hook-4.1.2w {
set ::update_hook {}
execsql {
INSERT INTO t1w VALUES(4, 'four');
PRAGMA vdbe_debug=on;
PRAGMA vdbe_addoptrace=on;
DELETE FROM t1w WHERE b = 'two';
UPDATE t1w SET b = '' WHERE a = 1 OR a = 3;
DELETE FROM t1w WHERE 1; -- Avoid the truncate optimization (for now)

View File

@ -16,6 +16,9 @@ source $testdir/tester.tcl
set ::testprefix index5
do_test 1.1 {
if {[permutation]=="memsubsys1"} {
execsql { PRAGMA auto_vacuum = 0; }
}
execsql {
PRAGMA page_size = 1024;
CREATE TABLE t1(x);
@ -38,7 +41,7 @@ tvfs filter xWrite
tvfs script write_cb
proc write_cb {xCall file handle iOfst args} {
if {[file tail $file]=="test.db"} {
lappend ::write_list [expr $iOfst/1024]
lappend ::write_list [expr $iOfst/1024 + 1]
}
}

View File

@ -13,6 +13,7 @@ optional) are:
-makefile PATH-TO-MAKEFILE (default "releasetest.mk")
-platform PLATFORM (see below)
-quick BOOLEAN (default "0")
-config CONFIGNAME (Run only CONFIGNAME)
The default value for -makefile is "./releasetest.mk".
@ -292,6 +293,7 @@ proc run_test_suite {name testtarget config} {
proc process_options {argv} {
set ::MAKEFILE releasetest.mk ;# Default value
set ::QUICK 0 ;# Default value
set config {}
set platform $::tcl_platform(os)-$::tcl_platform(machine)
for {set i 0} {$i < [llength $argv]} {incr i} {
@ -311,6 +313,11 @@ proc process_options {argv} {
set ::QUICK [lindex $argv $i]
}
-config {
incr i
set config [lindex $argv $i]
}
default {
puts stderr ""
puts stderr [string trim $::USAGE_MESSAGE]
@ -333,7 +340,12 @@ proc process_options {argv} {
exit
}
set ::CONFIGLIST $::Platforms($platform)
if {$config!=""} {
if {[llength $config]==1} {lappend config fulltest}
set ::CONFIGLIST $config
} else {
set ::CONFIGLIST $::Platforms($platform)
}
puts "Running the following configurations for $platform:"
puts " [string trim $::CONFIGLIST]"
}

View File

@ -934,6 +934,7 @@ void testset_cte(void){
}
#ifdef SQLITE_ENABLE_RTREE
/* Generate two numbers between 1 and mx. The first number is less than
** the second. Usually the numbers are near each other but can sometimes
** be far apart.
@ -954,7 +955,9 @@ static void twoCoords(
*pX0 = x0;
*pX1 = x1;
}
#endif
#ifdef SQLITE_ENABLE_RTREE
/* The following routine is an R-Tree geometry callback. It returns
** true if the object overlaps a slice on the Y coordinate between the
** two values given as arguments. In other words
@ -974,7 +977,9 @@ static int xsliceGeometryCallback(
*pRes = aCoord[3]>=p->aParam[0] && aCoord[2]<=p->aParam[1];
return SQLITE_OK;
}
#endif /* SQLITE_ENABLE_RTREE */
#ifdef SQLITE_ENABLE_RTREE
/*
** A testset for the R-Tree virtual table
*/
@ -1110,6 +1115,7 @@ void testset_rtree(int p1, int p2){
}
speedtest1_end_test();
}
#endif /* SQLITE_ENABLE_RTREE */
/*
** A testset used for debugging speedtest1 itself.
@ -1329,7 +1335,12 @@ int main(int argc, char **argv){
}else if( strcmp(zTSet,"cte")==0 ){
testset_cte();
}else if( strcmp(zTSet,"rtree")==0 ){
#ifdef SQLITE_ENABLE_RTREE
testset_rtree(6, 147);
#else
fatal_error("compile with -DSQLITE_ENABLE_RTREE to enable "
"the R-Tree tests\n");
#endif
}else{
fatal_error("unknown testset: \"%s\"\nChoices: main debug1 cte rtree\n",
zTSet);

65
test/tkt-ba7cbfaedc.test Normal file
View File

@ -0,0 +1,65 @@
# 2014-10-11
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#*************************************************************************
#
# Test that ticket [ba7cbfaedc] has been fixed.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix tkt-ba7cbfaedc
do_execsql_test 1 {
CREATE TABLE t1 (x, y);
INSERT INTO t1 VALUES (3, 'a');
INSERT INTO t1 VALUES (1, 'a');
INSERT INTO t1 VALUES (2, 'b');
INSERT INTO t1 VALUES (2, 'a');
INSERT INTO t1 VALUES (3, 'b');
INSERT INTO t1 VALUES (1, 'b');
}
do_execsql_test 1.1 {
CREATE INDEX i1 ON t1(x, y);
}
foreach {n idx} {
1 { CREATE INDEX i1 ON t1(x, y) }
2 { CREATE INDEX i1 ON t1(x DESC, y) }
3 { CREATE INDEX i1 ON t1(x, y DESC) }
4 { CREATE INDEX i1 ON t1(x DESC, y DESC) }
} {
catchsql { DROP INDEX i1 }
execsql $idx
foreach {tn q res} {
1 "GROUP BY x, y ORDER BY x, y" {1 a 1 b 2 a 2 b 3 a 3 b}
2 "GROUP BY x, y ORDER BY x DESC, y" {3 a 3 b 2 a 2 b 1 a 1 b}
3 "GROUP BY x, y ORDER BY x, y DESC" {1 b 1 a 2 b 2 a 3 b 3 a}
4 "GROUP BY x, y ORDER BY x DESC, y DESC" {3 b 3 a 2 b 2 a 1 b 1 a}
} {
do_execsql_test 1.$n.$tn "SELECT * FROM t1 $q" $res
}
}
do_execsql_test 2.0 {
drop table if exists t1;
create table t1(id int);
insert into t1(id) values(1),(2),(3),(4),(5);
create index t1_idx_id on t1(id asc);
select * from t1 group by id order by id;
select * from t1 group by id order by id asc;
select * from t1 group by id order by id desc;
} {
1 2 3 4 5 1 2 3 4 5 5 4 3 2 1
}
finish_test