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:
41
manifest
41
manifest
@ -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
|
||||
|
@ -1 +1 @@
|
||||
abfef254721ca29b653f24bfe80e88adaa92dc6c
|
||||
83d4114f2aa404e670ced33511183baacd813a01
|
12
src/btree.c
12
src/btree.c
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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() */
|
||||
|
@ -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));
|
||||
}
|
||||
|
148
src/vdbe.c
148
src/vdbe.c
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
120
src/where.c
120
src/where.c
@ -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], ¬Used
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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]"
|
||||
}
|
||||
|
@ -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
65
test/tkt-ba7cbfaedc.test
Normal 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
|
||||
|
||||
|
Reference in New Issue
Block a user