mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-14 00:22:38 +03:00
Add the ability to use indices for the first few terms of an ORDER BY clause,
then sort in batches to handle the later terms. FossilOrigin-Name: fa06a6fed9f48322d9b89721799ba12c46efa898
This commit is contained in:
41
manifest
41
manifest
@@ -1,5 +1,5 @@
|
||||
C Change\sthe\snames\sof\sSRT_DistTable\sand\sSRT_Table\sused\sby\sCTE\sto\smore\nmeaningful\sSRT_DistFifo\sand\sSRT_Fifo,\srespectively.\s\sSimplify\sthe\nIgnorableOrderby()\smacro\sin\sthe\sprocess.
|
||||
D 2014-03-21T19:56:09.443
|
||||
C Add\sthe\sability\sto\suse\sindices\sfor\sthe\sfirst\sfew\sterms\sof\san\sORDER\sBY\sclause,\nthen\ssort\sin\sbatches\sto\shandle\sthe\slater\sterms.
|
||||
D 2014-03-21T20:58:42.176
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@@ -164,8 +164,8 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c a729e63cf5cd1829507cb7b8e89f99b95141bb53
|
||||
F src/bitvec.c 19a4ba637bd85f8f63fc8c9bae5ade9fb05ec1cb
|
||||
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
|
||||
F src/btree.c 2a0e73f26c329f49e426237e71a879ffd205d778
|
||||
F src/btree.h da1b69b441ecee21f8b34ba73f0ae339540c4025
|
||||
F src/btree.c 029cec7b98fe0a985922c03f101630391044c4ad
|
||||
F src/btree.h 232836cb51753f2e96aa8ce0f052c6df850f76ba
|
||||
F src/btreeInt.h 0be66063468a520e4d66b80c7a1dc26d04ee6ea4
|
||||
F src/build.c 0d50ef95aad63f4c4fc47f3fa2670d4557c45db0
|
||||
F src/callback.c 174e3c8656bc29f91d710ab61550d16eea34be98
|
||||
@@ -173,7 +173,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 0231df905e2c4abba4483ee18ffc05adc321df2a
|
||||
F src/date.c 593c744b2623971e45affd0bde347631bdfa4625
|
||||
F src/delete.c cdd57149543bb28304d8f717c243f2a86b1fc280
|
||||
F src/expr.c 6e1e8bca468753f06758db7835a85e5408351e7a
|
||||
F src/expr.c da2b3cb41081af6b56e95e7c9e95949564ce2e21
|
||||
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
|
||||
F src/fkey.c 5269ef07b100763134f71b889327c333bd0989cf
|
||||
F src/func.c 2945bb2c4cdc0ac43733046285a4434310be1811
|
||||
@@ -211,18 +211,18 @@ F src/parse.y 22d6a074e5f5a7258947a1dc55a9bf946b765dd0
|
||||
F src/pcache.c d8eafac28290d4bb80332005435db44991d07fc2
|
||||
F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222
|
||||
F src/pcache1.c 102e6f5a2fbc646154463eb856d1fd716867b64c
|
||||
F src/pragma.c e78b4bf2a267de2c17ee09f90b6807cf8d40e6a3
|
||||
F src/pragma.c 10f169b9650f0930a7a6df67e1387a4c2c449f38
|
||||
F src/prepare.c 677521ab7132615a8a26107a1d1c3132f44ae337
|
||||
F src/printf.c e5a0005f8b3de21f85da6a709d2fbee76775bf4b
|
||||
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
||||
F src/resolve.c 273d5f47c4e2c05b2d3d2bffeda939551ab59e66
|
||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||
F src/select.c cbee644f795a5fa14fa79bd760c3c9c883a6b4d8
|
||||
F src/select.c 633c56adeb382173d01016bea9e04e4dac54dfbc
|
||||
F src/shell.c cee9f46f2688a261601b1fd3d7f4b3cddf9b5cdf
|
||||
F src/sqlite.h.in a2ef671f92747a5a1c8a47bad5c585a8dd9eca80
|
||||
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
|
||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||
F src/sqliteInt.h 76b1cf0836f0d022aec5045474f5c6c90f7b9730
|
||||
F src/sqliteInt.h 1e16bac177ce6396e59af867afb90c92949895de
|
||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
@@ -278,21 +278,21 @@ F src/update.c 5b3e74a03b3811e586b4f2b4cbd7c49f01c93115
|
||||
F src/utf.c 6dc9ec9f1b3db43ae8ba0365377f11df1ee4c01c
|
||||
F src/util.c c46c90459ef9bdc0c6c73803cf4c55425b4771cf
|
||||
F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
|
||||
F src/vdbe.c 5f0fffa9bf49a90c05dc3d46d8217603fd0ee00e
|
||||
F src/vdbe.c 37cfae03a0c40515304fb0a8a1a97ac3aa965e87
|
||||
F src/vdbe.h fb2c48c198300a7c632f09fc940011d2ad2fc2ae
|
||||
F src/vdbeInt.h e54fc4f289fce48e81b3371128446033d097733b
|
||||
F src/vdbeInt.h 2b9a6849166d0014c843ae3fd83a062be4efa325
|
||||
F src/vdbeapi.c 0ed6053f947edd0b30f64ce5aeb811872a3450a4
|
||||
F src/vdbeaux.c e45e3f9daf38c5be3fd39e9aacc1c9066af57a06
|
||||
F src/vdbeaux.c 5078ca7de4fd5ba4535bd17fe44d5b56c2d3294c
|
||||
F src/vdbeblob.c 15377abfb59251bccedd5a9c7d014a895f0c04aa
|
||||
F src/vdbemem.c 6fc77594c60f6155404f3f8d71bf36d1fdeb4447
|
||||
F src/vdbesort.c 0fef5fa890fda760f92f293a5b0f2ccfe5d2dc55
|
||||
F src/vdbesort.c 4abb7c0f8f19b7d7d82f4558d5da1a30fdf9ea38
|
||||
F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
|
||||
F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
|
||||
F src/wal.c 76e7fc6de229bea8b30bb2539110f03a494dc3a8
|
||||
F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
|
||||
F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45
|
||||
F src/where.c eed0b83bd7ffef4ac2f7a2fcc9dcfa66b18b8b9e
|
||||
F src/whereInt.h 921f935af8b684ffb49705610bda7284db1db138
|
||||
F src/where.c f394453e4960462e62671158199f060ac956f8bc
|
||||
F src/whereInt.h 2564055b440e44ebec8b47f237bbccae6719b7af
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
|
||||
@@ -431,7 +431,7 @@ F test/descidx1.test 6d03b44c8538fe0eb4924e19fba10cdd8f3c9240
|
||||
F test/descidx2.test 9f1a0c83fd57f8667c82310ca21b30a350888b5d
|
||||
F test/descidx3.test 09ddbe3f5295f482d2f8b687cf6db8bad7acd9a2
|
||||
F test/diskfull.test 106391384780753ea6896b7b4f005d10e9866b6e
|
||||
F test/distinct.test c7b194ef95dbddb32d77acbbab2e023c6eed0cb2
|
||||
F test/distinct.test 086e70c765f172e8974e9f83b9ac5ca03c154e77
|
||||
F test/distinctagg.test 1a6ef9c87a58669438fc771450d7a72577417376
|
||||
F test/e_createtable.test ee95d48664503d40f6cc9ef4a7d03216188e2ada
|
||||
F test/e_delete.test d5186e2f5478b659f16a2c8b66c09892823e542a
|
||||
@@ -722,7 +722,8 @@ F test/orderby1.test 9b524aff9147288da43a6d7ddfdcff47fa2303c6
|
||||
F test/orderby2.test bc11009f7cd99d96b1b11e57b199b00633eb5b04
|
||||
F test/orderby3.test 8619d06a3debdcd80a27c0fdea5c40b468854b99
|
||||
F test/orderby4.test 4d39bfbaaa3ae64d026ca2ff166353d2edca4ba4
|
||||
F test/orderby5.test 0eb82d5890c3f3d0563966560cfdc984ea69e30c
|
||||
F test/orderby5.test 2490183fef54417209d1df253633a605d46bd350
|
||||
F test/orderby6.test 8b38138ab0972588240b3fca0985d2e400432859
|
||||
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
|
||||
F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
|
||||
F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
|
||||
@@ -1089,7 +1090,7 @@ F test/whereC.test d6f4ecd4fa2d9429681a5b22a25d2bda8e86ab8a
|
||||
F test/whereD.test 6c2feb79ef1f68381b07f39017fe5f9b96da8d62
|
||||
F test/whereE.test b3a055eef928c992b0a33198a7b8dc10eea5ad2f
|
||||
F test/whereF.test 5b2ba0dbe8074aa13e416b37c753991f0a2492d7
|
||||
F test/whereG.test 2a3d5181decc801b36600fa1c40b0dad2ccc267f
|
||||
F test/whereG.test eb3a46b3eaf38e25e3013433b2db8a25a866c215
|
||||
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
|
||||
F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
||||
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
||||
@@ -1156,7 +1157,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
||||
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
||||
P 1f413aca00015100224273480e1ce39a76bf93ab
|
||||
R 26118dcbaf585402aab527afe8f97345
|
||||
P 45d8cc678d128f1dda6469864215a8ed9de4366a 71e9ae72c272dc86720b2bfe719f57de437c400b
|
||||
R 3371108f475a5908c384cc490e83cfb1
|
||||
U drh
|
||||
Z d7e9ffdc5a42e976a5961e8287a0561b
|
||||
Z 76333cbbd5592a854905d3a7e7bd7626
|
||||
|
||||
@@ -1 +1 @@
|
||||
45d8cc678d128f1dda6469864215a8ed9de4366a
|
||||
fa06a6fed9f48322d9b89721799ba12c46efa898
|
||||
@@ -7425,6 +7425,15 @@ int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Delete all information from the single table that pCur is open on.
|
||||
**
|
||||
** This routine only work for pCur on an ephemeral table.
|
||||
*/
|
||||
int sqlite3BtreeClearTableOfCursor(BtCursor *pCur){
|
||||
return sqlite3BtreeClearTable(pCur->pBtree, pCur->pgnoRoot, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** Erase all information in a table and add the root of the table to
|
||||
** the freelist. Except, the root of the principle table (the one on
|
||||
|
||||
@@ -115,6 +115,7 @@ int sqlite3BtreeIncrVacuum(Btree *);
|
||||
|
||||
int sqlite3BtreeDropTable(Btree*, int, int*);
|
||||
int sqlite3BtreeClearTable(Btree*, int, int*);
|
||||
int sqlite3BtreeClearTableOfCursor(BtCursor*);
|
||||
void sqlite3BtreeTripAllCursors(Btree*, int);
|
||||
|
||||
void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
|
||||
|
||||
@@ -955,7 +955,6 @@ ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
|
||||
if( p==0 ) return 0;
|
||||
pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
|
||||
if( pNew==0 ) return 0;
|
||||
pNew->iECursor = 0;
|
||||
pNew->nExpr = i = p->nExpr;
|
||||
if( (flags & EXPRDUP_REDUCE)==0 ) for(i=1; i<p->nExpr; i+=i){}
|
||||
pNew->a = pItem = sqlite3DbMallocRaw(db, i*sizeof(p->a[0]) );
|
||||
@@ -1068,7 +1067,6 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
|
||||
pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
|
||||
pNew->addrOpenEphm[0] = -1;
|
||||
pNew->addrOpenEphm[1] = -1;
|
||||
pNew->addrOpenEphm[2] = -1;
|
||||
pNew->nSelectRow = p->nSelectRow;
|
||||
pNew->pWith = withDup(db, p->pWith);
|
||||
return pNew;
|
||||
@@ -2340,7 +2338,7 @@ void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){
|
||||
int i;
|
||||
struct yColCache *p;
|
||||
assert( iFrom>=iTo+nReg || iFrom+nReg<=iTo );
|
||||
sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg-1);
|
||||
sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg);
|
||||
for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
|
||||
int x = p->iReg;
|
||||
if( x>=iFrom && x<iFrom+nReg ){
|
||||
@@ -2741,7 +2739,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
|
||||
}
|
||||
|
||||
sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */
|
||||
sqlite3ExprCodeExprList(pParse, pFarg, r1,
|
||||
sqlite3ExprCodeExprList(pParse, pFarg, r1,
|
||||
SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR);
|
||||
sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */
|
||||
}else{
|
||||
|
||||
@@ -1875,7 +1875,7 @@ void sqlite3Pragma(
|
||||
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
|
||||
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName),
|
||||
P4_DYNAMIC);
|
||||
sqlite3VdbeAddOp2(v, OP_Move, 2, 4);
|
||||
sqlite3VdbeAddOp3(v, OP_Move, 2, 4, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 2);
|
||||
sqlite3VdbeAddOp2(v, OP_ResultRow, 2, 1);
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
|
||||
254
src/select.c
254
src/select.c
@@ -14,6 +14,34 @@
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
/*
|
||||
** An instance of the following object is used to record information about
|
||||
** how to process the DISTINCT keyword, to simplify passing that information
|
||||
** into the selectInnerLoop() routine.
|
||||
*/
|
||||
typedef struct DistinctCtx DistinctCtx;
|
||||
struct DistinctCtx {
|
||||
u8 isTnct; /* True if the DISTINCT keyword is present */
|
||||
u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
|
||||
int tabTnct; /* Ephemeral table used for DISTINCT processing */
|
||||
int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
|
||||
};
|
||||
|
||||
/*
|
||||
** An instance of the following object is used to record information about
|
||||
** the ORDER BY (or GROUP BY) clause of query is being coded.
|
||||
*/
|
||||
typedef struct SortCtx SortCtx;
|
||||
struct SortCtx {
|
||||
ExprList *pOrderBy; /* The ORDER BY (or GROUP BY clause) */
|
||||
int nOBSat; /* Number of ORDER BY terms satisfied by indices */
|
||||
int iECursor; /* Cursor number for the sorter */
|
||||
int regReturn; /* Register holding block-output return address */
|
||||
int labelBkOut; /* Start label for the block-output subroutine */
|
||||
int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
|
||||
u8 sortFlags; /* Zero or more SORTFLAG_* bits */
|
||||
};
|
||||
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
|
||||
|
||||
/*
|
||||
** Delete all the content of a Select structure but do not deallocate
|
||||
@@ -87,7 +115,6 @@ Select *sqlite3SelectNew(
|
||||
assert( pOffset==0 || pLimit!=0 );
|
||||
pNew->addrOpenEphm[0] = -1;
|
||||
pNew->addrOpenEphm[1] = -1;
|
||||
pNew->addrOpenEphm[2] = -1;
|
||||
if( db->mallocFailed ) {
|
||||
clearSelect(db, pNew);
|
||||
if( pNew!=&standin ) sqlite3DbFree(db, pNew);
|
||||
@@ -419,34 +446,71 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Forward reference */
|
||||
static KeyInfo *keyInfoFromExprList(
|
||||
Parse *pParse, /* Parsing context */
|
||||
ExprList *pList, /* Form the KeyInfo object from this ExprList */
|
||||
int iStart, /* Begin with this column of pList */
|
||||
int nExtra /* Add this many extra columns to the end */
|
||||
);
|
||||
|
||||
/*
|
||||
** Insert code into "v" that will push the record on the top of the
|
||||
** stack into the sorter.
|
||||
** Insert code into "v" that will push the record in register regData
|
||||
** into the sorter.
|
||||
*/
|
||||
static void pushOntoSorter(
|
||||
Parse *pParse, /* Parser context */
|
||||
ExprList *pOrderBy, /* The ORDER BY clause */
|
||||
SortCtx *pSort, /* Information about the ORDER BY clause */
|
||||
Select *pSelect, /* The whole SELECT statement */
|
||||
int regData /* Register holding data to be sorted */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
int nExpr = pOrderBy->nExpr;
|
||||
int nExpr = pSort->pOrderBy->nExpr;
|
||||
int regBase = sqlite3GetTempRange(pParse, nExpr+2);
|
||||
int regRecord = sqlite3GetTempReg(pParse);
|
||||
int nOBSat = pSort->nOBSat;
|
||||
int op;
|
||||
sqlite3ExprCacheClear(pParse);
|
||||
sqlite3ExprCodeExprList(pParse, pOrderBy, regBase, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr);
|
||||
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
|
||||
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nExpr + 2, regRecord);
|
||||
if( pSelect->selFlags & SF_UseSorter ){
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nExpr+2-nOBSat, regRecord);
|
||||
if( nOBSat>0 ){
|
||||
int regPrevKey; /* The first nOBSat columns of the previous row */
|
||||
int addrFirst; /* Address of the OP_IfNot opcode */
|
||||
int addrJmp; /* Address of the OP_Jump opcode */
|
||||
VdbeOp *pOp; /* Opcode that opens the sorter */
|
||||
int nKey; /* Number of sorting key columns, including OP_Sequence */
|
||||
|
||||
regPrevKey = pParse->nMem+1;
|
||||
pParse->nMem += pSort->nOBSat;
|
||||
nKey = nExpr - pSort->nOBSat + 1;
|
||||
addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Compare, regPrevKey, regBase, pSort->nOBSat);
|
||||
pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
|
||||
pOp->p2 = nKey + 1;
|
||||
sqlite3VdbeChangeP4(v, -1, (char*)pOp->p4.pKeyInfo, P4_KEYINFO);
|
||||
pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat, 1);
|
||||
addrJmp = sqlite3VdbeCurrentAddr(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
|
||||
pSort->labelBkOut = sqlite3VdbeMakeLabel(v);
|
||||
pSort->regReturn = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
|
||||
sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor);
|
||||
sqlite3VdbeJumpHere(v, addrFirst);
|
||||
sqlite3VdbeAddOp3(v, OP_Move, regBase, regPrevKey, pSort->nOBSat);
|
||||
sqlite3VdbeJumpHere(v, addrJmp);
|
||||
}
|
||||
if( pSort->sortFlags & SORTFLAG_UseSorter ){
|
||||
op = OP_SorterInsert;
|
||||
}else{
|
||||
op = OP_IdxInsert;
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, op, pOrderBy->iECursor, regRecord);
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nExpr+2);
|
||||
sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
|
||||
if( nOBSat==0 ){
|
||||
sqlite3ReleaseTempReg(pParse, regRecord);
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nExpr+2);
|
||||
}
|
||||
if( pSelect->iLimit ){
|
||||
int addr1, addr2;
|
||||
int iLimit;
|
||||
@@ -459,8 +523,8 @@ static void pushOntoSorter(
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1);
|
||||
addr2 = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor);
|
||||
sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor);
|
||||
sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
|
||||
sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
|
||||
sqlite3VdbeJumpHere(v, addr2);
|
||||
}
|
||||
}
|
||||
@@ -534,19 +598,6 @@ static int checkForMultiColumnSelectError(
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** An instance of the following object is used to record information about
|
||||
** how to process the DISTINCT keyword, to simplify passing that information
|
||||
** into the selectInnerLoop() routine.
|
||||
*/
|
||||
typedef struct DistinctCtx DistinctCtx;
|
||||
struct DistinctCtx {
|
||||
u8 isTnct; /* True if the DISTINCT keyword is present */
|
||||
u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
|
||||
int tabTnct; /* Ephemeral table used for DISTINCT processing */
|
||||
int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
|
||||
};
|
||||
|
||||
/*
|
||||
** This routine generates the code for the inside of the inner loop
|
||||
** of a SELECT.
|
||||
@@ -561,7 +612,7 @@ static void selectInnerLoop(
|
||||
Select *p, /* The complete select statement being coded */
|
||||
ExprList *pEList, /* List of values being extracted */
|
||||
int srcTab, /* Pull data from this table */
|
||||
ExprList *pOrderBy, /* If not NULL, sort results using this key */
|
||||
SortCtx *pSort, /* If not NULL, info on how to process ORDER BY */
|
||||
DistinctCtx *pDistinct, /* If not NULL, info on how to process DISTINCT */
|
||||
SelectDest *pDest, /* How to dispose of the results */
|
||||
int iContinue, /* Jump here to continue with next row */
|
||||
@@ -578,7 +629,8 @@ static void selectInnerLoop(
|
||||
assert( v );
|
||||
assert( pEList!=0 );
|
||||
hasDistinct = pDistinct ? pDistinct->eTnctType : WHERE_DISTINCT_NOOP;
|
||||
if( pOrderBy==0 && !hasDistinct ){
|
||||
if( pSort && pSort->pOrderBy==0 ) pSort = 0;
|
||||
if( pSort==0 && !hasDistinct ){
|
||||
assert( iContinue!=0 );
|
||||
codeOffset(v, p->iOffset, iContinue);
|
||||
}
|
||||
@@ -669,7 +721,7 @@ static void selectInnerLoop(
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( pOrderBy==0 ){
|
||||
if( pSort==0 ){
|
||||
codeOffset(v, p->iOffset, iContinue);
|
||||
}
|
||||
}
|
||||
@@ -718,11 +770,11 @@ static void selectInnerLoop(
|
||||
int addr = sqlite3VdbeCurrentAddr(v) + 4;
|
||||
sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r1);
|
||||
assert( pOrderBy==0 );
|
||||
assert( pSort==0 );
|
||||
}
|
||||
#endif
|
||||
if( pOrderBy ){
|
||||
pushOntoSorter(pParse, pOrderBy, p, r1);
|
||||
if( pSort ){
|
||||
pushOntoSorter(pParse, pSort, p, r1);
|
||||
}else{
|
||||
int r2 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
|
||||
@@ -743,12 +795,12 @@ static void selectInnerLoop(
|
||||
assert( nResultCol==1 );
|
||||
pDest->affSdst =
|
||||
sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affSdst);
|
||||
if( pOrderBy ){
|
||||
if( pSort ){
|
||||
/* At first glance you would think we could optimize out the
|
||||
** ORDER BY in this case since the order of entries in the set
|
||||
** does not matter. But there might be a LIMIT clause, in which
|
||||
** case the order does matter */
|
||||
pushOntoSorter(pParse, pOrderBy, p, regResult);
|
||||
pushOntoSorter(pParse, pSort, p, regResult);
|
||||
}else{
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
|
||||
@@ -773,8 +825,8 @@ static void selectInnerLoop(
|
||||
*/
|
||||
case SRT_Mem: {
|
||||
assert( nResultCol==1 );
|
||||
if( pOrderBy ){
|
||||
pushOntoSorter(pParse, pOrderBy, p, regResult);
|
||||
if( pSort ){
|
||||
pushOntoSorter(pParse, pSort, p, regResult);
|
||||
}else{
|
||||
sqlite3ExprCodeMove(pParse, regResult, iParm, 1);
|
||||
/* The LIMIT clause will jump out of the loop for us */
|
||||
@@ -787,10 +839,10 @@ static void selectInnerLoop(
|
||||
case SRT_Output: { /* Return the results */
|
||||
testcase( eDest==SRT_Coroutine );
|
||||
testcase( eDest==SRT_Output );
|
||||
if( pOrderBy ){
|
||||
if( pSort ){
|
||||
int r1 = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
|
||||
pushOntoSorter(pParse, pOrderBy, p, r1);
|
||||
pushOntoSorter(pParse, pSort, p, r1);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
}else if( eDest==SRT_Coroutine ){
|
||||
sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
|
||||
@@ -867,7 +919,7 @@ static void selectInnerLoop(
|
||||
** there is a sorter, in which case the sorter has already limited
|
||||
** the output for us.
|
||||
*/
|
||||
if( pOrderBy==0 && p->iLimit ){
|
||||
if( pSort==0 && p->iLimit ){
|
||||
sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); VdbeCoverage(v);
|
||||
}
|
||||
}
|
||||
@@ -938,7 +990,12 @@ int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; }
|
||||
** function is responsible for seeing that this structure is eventually
|
||||
** freed.
|
||||
*/
|
||||
static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList, int nExtra){
|
||||
static KeyInfo *keyInfoFromExprList(
|
||||
Parse *pParse, /* Parsing context */
|
||||
ExprList *pList, /* Form the KeyInfo object from this ExprList */
|
||||
int iStart, /* Begin with this column of pList */
|
||||
int nExtra /* Add this many extra columns to the end */
|
||||
){
|
||||
int nExpr;
|
||||
KeyInfo *pInfo;
|
||||
struct ExprList_item *pItem;
|
||||
@@ -946,15 +1003,15 @@ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList, int nExtra){
|
||||
int i;
|
||||
|
||||
nExpr = pList->nExpr;
|
||||
pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra, 1);
|
||||
pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra-iStart, 1);
|
||||
if( pInfo ){
|
||||
assert( sqlite3KeyInfoIsWriteable(pInfo) );
|
||||
for(i=0, pItem=pList->a; i<nExpr; i++, pItem++){
|
||||
for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){
|
||||
CollSeq *pColl;
|
||||
pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
|
||||
if( !pColl ) pColl = db->pDfltColl;
|
||||
pInfo->aColl[i] = pColl;
|
||||
pInfo->aSortOrder[i] = pItem->sortOrder;
|
||||
pInfo->aColl[i-iStart] = pColl;
|
||||
pInfo->aSortOrder[i-iStart] = pItem->sortOrder;
|
||||
}
|
||||
}
|
||||
return pInfo;
|
||||
@@ -1056,24 +1113,31 @@ static void explainComposite(
|
||||
static void generateSortTail(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Select *p, /* The SELECT statement */
|
||||
Vdbe *v, /* Generate code into this VDBE */
|
||||
SortCtx *pSort, /* Information on the ORDER BY clause */
|
||||
int nColumn, /* Number of columns of data */
|
||||
SelectDest *pDest /* Write the sorted results here */
|
||||
){
|
||||
Vdbe *v = pParse->pVdbe; /* The prepared statement */
|
||||
int addrBreak = sqlite3VdbeMakeLabel(v); /* Jump here to exit loop */
|
||||
int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
|
||||
int addr;
|
||||
int addrOnce = 0;
|
||||
int iTab;
|
||||
int pseudoTab = 0;
|
||||
ExprList *pOrderBy = p->pOrderBy;
|
||||
|
||||
ExprList *pOrderBy = pSort->pOrderBy;
|
||||
int eDest = pDest->eDest;
|
||||
int iParm = pDest->iSDParm;
|
||||
|
||||
int regRow;
|
||||
int regRowid;
|
||||
int nKey;
|
||||
|
||||
iTab = pOrderBy->iECursor;
|
||||
if( pSort->labelBkOut ){
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBreak);
|
||||
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
|
||||
addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
|
||||
}
|
||||
iTab = pSort->iECursor;
|
||||
regRow = sqlite3GetTempReg(pParse);
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
pseudoTab = pParse->nTab++;
|
||||
@@ -1082,20 +1146,23 @@ static void generateSortTail(
|
||||
}else{
|
||||
regRowid = sqlite3GetTempReg(pParse);
|
||||
}
|
||||
if( p->selFlags & SF_UseSorter ){
|
||||
nKey = pOrderBy->nExpr - pSort->nOBSat;
|
||||
if( pSort->sortFlags & SORTFLAG_UseSorter ){
|
||||
int regSortOut = ++pParse->nMem;
|
||||
int ptab2 = pParse->nTab++;
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2);
|
||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, nKey+2);
|
||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
|
||||
VdbeCoverage(v);
|
||||
codeOffset(v, p->iOffset, addrContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, ptab2, nKey+1, regRow);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
|
||||
}else{
|
||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v);
|
||||
codeOffset(v, p->iOffset, addrContinue);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, iTab, nKey+1, regRow);
|
||||
}
|
||||
switch( eDest ){
|
||||
case SRT_Table:
|
||||
@@ -1150,11 +1217,12 @@ static void generateSortTail(
|
||||
/* The bottom of the loop
|
||||
*/
|
||||
sqlite3VdbeResolveLabel(v, addrContinue);
|
||||
if( p->selFlags & SF_UseSorter ){
|
||||
if( pSort->sortFlags & SORTFLAG_UseSorter ){
|
||||
sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr); VdbeCoverage(v);
|
||||
}else{
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v);
|
||||
}
|
||||
if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn);
|
||||
sqlite3VdbeResolveLabel(v, addrBreak);
|
||||
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
|
||||
sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0);
|
||||
@@ -4312,7 +4380,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
||||
"argument");
|
||||
pFunc->iDistinct = -1;
|
||||
}else{
|
||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0);
|
||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0, 0);
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO);
|
||||
}
|
||||
@@ -4467,12 +4535,11 @@ int sqlite3Select(
|
||||
ExprList *pEList; /* List of columns to extract. */
|
||||
SrcList *pTabList; /* List of tables to select from */
|
||||
Expr *pWhere; /* The WHERE clause. May be NULL */
|
||||
ExprList *pOrderBy; /* The ORDER BY clause. May be NULL */
|
||||
ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */
|
||||
Expr *pHaving; /* The HAVING clause. May be NULL */
|
||||
int rc = 1; /* Value to return from this function */
|
||||
int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */
|
||||
DistinctCtx sDistinct; /* Info on how to code the DISTINCT keyword */
|
||||
SortCtx sSort; /* Info on how to code the ORDER BY clause */
|
||||
AggInfo sAggInfo; /* Information used by aggregate queries */
|
||||
int iEnd; /* Address of the end of the query */
|
||||
sqlite3 *db; /* The database connection */
|
||||
@@ -4505,7 +4572,8 @@ int sqlite3Select(
|
||||
p->selFlags &= ~SF_Distinct;
|
||||
}
|
||||
sqlite3SelectPrep(pParse, p, 0);
|
||||
pOrderBy = p->pOrderBy;
|
||||
memset(&sSort, 0, sizeof(sSort));
|
||||
sSort.pOrderBy = p->pOrderBy;
|
||||
pTabList = p->pSrc;
|
||||
pEList = p->pEList;
|
||||
if( pParse->nErr || db->mallocFailed ){
|
||||
@@ -4627,7 +4695,7 @@ int sqlite3Select(
|
||||
pParse->nHeight -= sqlite3SelectExprHeight(p);
|
||||
pTabList = p->pSrc;
|
||||
if( !IgnorableOrderby(pDest) ){
|
||||
pOrderBy = p->pOrderBy;
|
||||
sSort.pOrderBy = p->pOrderBy;
|
||||
}
|
||||
}
|
||||
pEList = p->pEList;
|
||||
@@ -4654,9 +4722,9 @@ int sqlite3Select(
|
||||
** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER
|
||||
** to disable this optimization for testing purposes.
|
||||
*/
|
||||
if( sqlite3ExprListCompare(p->pGroupBy, pOrderBy, -1)==0
|
||||
if( sqlite3ExprListCompare(p->pGroupBy, sSort.pOrderBy, -1)==0
|
||||
&& OptimizationEnabled(db, SQLITE_GroupByOrder) ){
|
||||
pOrderBy = 0;
|
||||
sSort.pOrderBy = 0;
|
||||
}
|
||||
|
||||
/* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
|
||||
@@ -4675,12 +4743,12 @@ int sqlite3Select(
|
||||
** BY and DISTINCT, and an index or separate temp-table for the other.
|
||||
*/
|
||||
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
|
||||
&& sqlite3ExprListCompare(pOrderBy, p->pEList, -1)==0
|
||||
&& sqlite3ExprListCompare(sSort.pOrderBy, p->pEList, -1)==0
|
||||
){
|
||||
p->selFlags &= ~SF_Distinct;
|
||||
p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
|
||||
pGroupBy = p->pGroupBy;
|
||||
pOrderBy = 0;
|
||||
sSort.pOrderBy = 0;
|
||||
/* Notice that even thought SF_Distinct has been cleared from p->selFlags,
|
||||
** the sDistinct.isTnct is still set. Hence, isTnct represents the
|
||||
** original setting of the SF_Distinct flag, not the current setting */
|
||||
@@ -4694,16 +4762,16 @@ int sqlite3Select(
|
||||
** we figure out that the sorting index is not needed. The addrSortIndex
|
||||
** variable is used to facilitate that change.
|
||||
*/
|
||||
if( pOrderBy ){
|
||||
if( sSort.pOrderBy ){
|
||||
KeyInfo *pKeyInfo;
|
||||
pKeyInfo = keyInfoFromExprList(pParse, pOrderBy, 0);
|
||||
pOrderBy->iECursor = pParse->nTab++;
|
||||
p->addrOpenEphm[2] = addrSortIndex =
|
||||
pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, 0);
|
||||
sSort.iECursor = pParse->nTab++;
|
||||
sSort.addrSortIndex =
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||
pOrderBy->iECursor, pOrderBy->nExpr+2, 0,
|
||||
sSort.iECursor, sSort.pOrderBy->nExpr+2, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO);
|
||||
}else{
|
||||
addrSortIndex = -1;
|
||||
sSort.addrSortIndex = -1;
|
||||
}
|
||||
|
||||
/* If the output is destined for a temporary table, open that table.
|
||||
@@ -4717,9 +4785,9 @@ int sqlite3Select(
|
||||
iEnd = sqlite3VdbeMakeLabel(v);
|
||||
p->nSelectRow = LARGEST_INT64;
|
||||
computeLimitRegisters(pParse, p, iEnd);
|
||||
if( p->iLimit==0 && addrSortIndex>=0 ){
|
||||
sqlite3VdbeGetOp(v, addrSortIndex)->opcode = OP_SorterOpen;
|
||||
p->selFlags |= SF_UseSorter;
|
||||
if( p->iLimit==0 && sSort.addrSortIndex>=0 ){
|
||||
sqlite3VdbeGetOp(v, sSort.addrSortIndex)->opcode = OP_SorterOpen;
|
||||
sSort.sortFlags |= SORTFLAG_UseSorter;
|
||||
}
|
||||
|
||||
/* Open a virtual index to use for the distinct set.
|
||||
@@ -4728,7 +4796,7 @@ int sqlite3Select(
|
||||
sDistinct.tabTnct = pParse->nTab++;
|
||||
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||
sDistinct.tabTnct, 0, 0,
|
||||
(char*)keyInfoFromExprList(pParse, p->pEList, 0),
|
||||
(char*)keyInfoFromExprList(pParse, p->pEList,0,0),
|
||||
P4_KEYINFO);
|
||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
|
||||
@@ -4741,8 +4809,8 @@ int sqlite3Select(
|
||||
u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
|
||||
|
||||
/* Begin the database scan. */
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pOrderBy, p->pEList,
|
||||
wctrlFlags, 0);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
|
||||
p->pEList, wctrlFlags, 0);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){
|
||||
p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo);
|
||||
@@ -4750,19 +4818,23 @@ int sqlite3Select(
|
||||
if( sDistinct.isTnct && sqlite3WhereIsDistinct(pWInfo) ){
|
||||
sDistinct.eTnctType = sqlite3WhereIsDistinct(pWInfo);
|
||||
}
|
||||
if( pOrderBy && sqlite3WhereIsOrdered(pWInfo) ) pOrderBy = 0;
|
||||
if( sSort.pOrderBy ){
|
||||
sSort.nOBSat = sqlite3WhereIsOrdered(pWInfo);
|
||||
if( sSort.nOBSat==sSort.pOrderBy->nExpr ){
|
||||
sSort.pOrderBy = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If sorting index that was created by a prior OP_OpenEphemeral
|
||||
** instruction ended up not being needed, then change the OP_OpenEphemeral
|
||||
** into an OP_Noop.
|
||||
*/
|
||||
if( addrSortIndex>=0 && pOrderBy==0 ){
|
||||
sqlite3VdbeChangeToNoop(v, addrSortIndex);
|
||||
p->addrOpenEphm[2] = -1;
|
||||
if( sSort.addrSortIndex>=0 && sSort.pOrderBy==0 ){
|
||||
sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
|
||||
}
|
||||
|
||||
/* Use the standard inner loop. */
|
||||
selectInnerLoop(pParse, p, pEList, -1, pOrderBy, &sDistinct, pDest,
|
||||
selectInnerLoop(pParse, p, pEList, -1, &sSort, &sDistinct, pDest,
|
||||
sqlite3WhereContinueLabel(pWInfo),
|
||||
sqlite3WhereBreakLabel(pWInfo));
|
||||
|
||||
@@ -4818,7 +4890,7 @@ int sqlite3Select(
|
||||
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0;
|
||||
sAggInfo.pGroupBy = pGroupBy;
|
||||
sqlite3ExprAnalyzeAggList(&sNC, pEList);
|
||||
sqlite3ExprAnalyzeAggList(&sNC, pOrderBy);
|
||||
sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy);
|
||||
if( pHaving ){
|
||||
sqlite3ExprAnalyzeAggregates(&sNC, pHaving);
|
||||
}
|
||||
@@ -4852,7 +4924,7 @@ int sqlite3Select(
|
||||
** will be converted into a Noop.
|
||||
*/
|
||||
sAggInfo.sortingIdx = pParse->nTab++;
|
||||
pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0);
|
||||
pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, 0);
|
||||
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
|
||||
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
|
||||
0, (char*)pKeyInfo, P4_KEYINFO);
|
||||
@@ -4881,10 +4953,10 @@ int sqlite3Select(
|
||||
** in the right order to begin with.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0,
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0,
|
||||
WHERE_GROUPBY, 0);
|
||||
if( pWInfo==0 ) goto select_end;
|
||||
if( sqlite3WhereIsOrdered(pWInfo) ){
|
||||
if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){
|
||||
/* The optimizer is able to deliver rows in group by order so
|
||||
** we do not have to sort. The OP_OpenEphemeral table will be
|
||||
** cancelled later because we still need to use the pKeyInfo
|
||||
@@ -5035,7 +5107,7 @@ int sqlite3Select(
|
||||
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
|
||||
finalizeAggFunctions(pParse, &sAggInfo);
|
||||
sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL);
|
||||
selectInnerLoop(pParse, p, p->pEList, -1, pOrderBy,
|
||||
selectInnerLoop(pParse, p, p->pEList, -1, &sSort,
|
||||
&sDistinct, pDest,
|
||||
addrOutputRow+1, addrSetAbort);
|
||||
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
|
||||
@@ -5167,7 +5239,7 @@ int sqlite3Select(
|
||||
}
|
||||
updateAccumulator(pParse, &sAggInfo);
|
||||
assert( pMinMax==0 || pMinMax->nExpr==1 );
|
||||
if( sqlite3WhereIsOrdered(pWInfo) ){
|
||||
if( sqlite3WhereIsOrdered(pWInfo)>0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3WhereBreakLabel(pWInfo));
|
||||
VdbeComment((v, "%s() by index",
|
||||
(flag==WHERE_ORDERBY_MIN?"min":"max")));
|
||||
@@ -5176,7 +5248,7 @@ int sqlite3Select(
|
||||
finalizeAggFunctions(pParse, &sAggInfo);
|
||||
}
|
||||
|
||||
pOrderBy = 0;
|
||||
sSort.pOrderBy = 0;
|
||||
sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
|
||||
selectInnerLoop(pParse, p, p->pEList, -1, 0, 0,
|
||||
pDest, addrEnd, addrEnd);
|
||||
@@ -5193,9 +5265,9 @@ int sqlite3Select(
|
||||
/* If there is an ORDER BY clause, then we need to sort the results
|
||||
** and send them to the callback one by one.
|
||||
*/
|
||||
if( pOrderBy ){
|
||||
explainTempTable(pParse, "ORDER BY");
|
||||
generateSortTail(pParse, p, v, pEList->nExpr, pDest);
|
||||
if( sSort.pOrderBy ){
|
||||
explainTempTable(pParse, sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
|
||||
generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
|
||||
}
|
||||
|
||||
/* Jump here to skip this query
|
||||
|
||||
@@ -1958,7 +1958,6 @@ struct Expr {
|
||||
*/
|
||||
struct ExprList {
|
||||
int nExpr; /* Number of expressions on the list */
|
||||
int iECursor; /* VDBE Cursor associated with this ExprList */
|
||||
struct ExprList_item { /* For each expression in the list */
|
||||
Expr *pExpr; /* The list of expressions */
|
||||
char *zName; /* Token associated with this expression */
|
||||
@@ -2182,7 +2181,7 @@ struct Select {
|
||||
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
|
||||
u16 selFlags; /* Various SF_* values */
|
||||
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
|
||||
int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */
|
||||
int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */
|
||||
u64 nSelectRow; /* Estimated number of result rows */
|
||||
SrcList *pSrc; /* The FROM clause */
|
||||
Expr *pWhere; /* The WHERE clause */
|
||||
@@ -2206,9 +2205,9 @@ struct Select {
|
||||
#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
|
||||
#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
|
||||
#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
|
||||
#define SF_UseSorter 0x0040 /* Sort using a sorter */
|
||||
/* 0x0040 NOT USED */
|
||||
#define SF_Values 0x0080 /* Synthesized from VALUES clause */
|
||||
#define SF_Materialize 0x0100 /* NOT USED */
|
||||
/* 0x0100 NOT USED */
|
||||
#define SF_NestedFrom 0x0200 /* Part of a parenthesized FROM clause */
|
||||
#define SF_MaybeConvert 0x0400 /* Need convertCompoundSelectToSubquery() */
|
||||
#define SF_Recursive 0x0800 /* The recursive part of a recursive CTE */
|
||||
|
||||
38
src/vdbe.c
38
src/vdbe.c
@@ -1080,10 +1080,11 @@ case OP_Variable: { /* out2-prerelease */
|
||||
/* Opcode: Move P1 P2 P3 * *
|
||||
** Synopsis: r[P2@P3]=r[P1@P3]
|
||||
**
|
||||
** Move the values in register P1..P1+P3 over into
|
||||
** registers P2..P2+P3. Registers P1..P1+P3 are
|
||||
** Move the P3 values in register P1..P1+P3-1 over into
|
||||
** registers P2..P2+P3-1. Registers P1..P1+P3-1 are
|
||||
** left holding a NULL. It is an error for register ranges
|
||||
** P1..P1+P3 and P2..P2+P3 to overlap.
|
||||
** P1..P1+P3-1 and P2..P2+P3-1 to overlap. It is an error
|
||||
** for P3 to be less than 1.
|
||||
*/
|
||||
case OP_Move: {
|
||||
char *zMalloc; /* Holding variable for allocated memory */
|
||||
@@ -1094,7 +1095,7 @@ case OP_Move: {
|
||||
n = pOp->p3;
|
||||
p1 = pOp->p1;
|
||||
p2 = pOp->p2;
|
||||
assert( n>=0 && p1>0 && p2>0 );
|
||||
assert( n>0 && p1>0 && p2>0 );
|
||||
assert( p1+n<=p2 || p2+n<=p1 );
|
||||
|
||||
pIn1 = &aMem[p1];
|
||||
@@ -1118,7 +1119,7 @@ case OP_Move: {
|
||||
REGISTER_TRACE(p2++, pOut);
|
||||
pIn1++;
|
||||
pOut++;
|
||||
}while( n-- );
|
||||
}while( --n );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1995,6 +1996,7 @@ case OP_Permutation: {
|
||||
}
|
||||
|
||||
/* Opcode: Compare P1 P2 P3 P4 P5
|
||||
** Synopsis: r[P1@P3] <-> r[P2@P3]
|
||||
**
|
||||
** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this
|
||||
** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of
|
||||
@@ -3330,6 +3332,7 @@ case OP_OpenEphemeral: {
|
||||
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
|
||||
if( pCx==0 ) goto no_mem;
|
||||
pCx->nullRow = 1;
|
||||
pCx->isEphemeral = 1;
|
||||
rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt,
|
||||
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
|
||||
if( rc==SQLITE_OK ){
|
||||
@@ -3820,7 +3823,7 @@ case OP_NotExists: { /* jump, in3 */
|
||||
}
|
||||
|
||||
/* Opcode: Sequence P1 P2 * * *
|
||||
** Synopsis: r[P2]=rowid
|
||||
** Synopsis: r[P2]=cursor[P1].ctr++
|
||||
**
|
||||
** Find the next available sequence number for cursor P1.
|
||||
** Write the sequence number into register P2.
|
||||
@@ -4869,6 +4872,29 @@ case OP_Clear: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: ResetSorter P1 * * * *
|
||||
**
|
||||
** Delete all contents from the ephemeral table or sorter
|
||||
** that is open on cursor P1.
|
||||
**
|
||||
** This opcode only works for cursors used for sorting and
|
||||
** opened with OP_OpenEphemeral or OP_SorterOpen.
|
||||
*/
|
||||
case OP_ResetSorter: {
|
||||
VdbeCursor *pC;
|
||||
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
assert( pC!=0 );
|
||||
if( pC->pSorter ){
|
||||
sqlite3VdbeSorterReset(db, pC->pSorter);
|
||||
}else{
|
||||
assert( pC->isEphemeral );
|
||||
rc = sqlite3BtreeClearTableOfCursor(pC->pCursor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: CreateTable P1 P2 * * *
|
||||
** Synopsis: r[P2]=root iDb=P1
|
||||
**
|
||||
|
||||
@@ -72,6 +72,7 @@ struct VdbeCursor {
|
||||
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 */
|
||||
Bool isTable:1; /* True if a table requiring integer keys */
|
||||
Bool isOrdered:1; /* True if the underlying table is BTREE_UNORDERED */
|
||||
@@ -437,6 +438,7 @@ int sqlite3VdbeFrameRestore(VdbeFrame *);
|
||||
int sqlite3VdbeTransferError(Vdbe *p);
|
||||
|
||||
int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *);
|
||||
void sqlite3VdbeSorterReset(sqlite3 *, VdbeSorter *);
|
||||
void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *);
|
||||
int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *);
|
||||
int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *);
|
||||
|
||||
@@ -783,7 +783,9 @@ void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
|
||||
addr = p->nOp - 1;
|
||||
}
|
||||
pOp = &p->aOp[addr];
|
||||
assert( pOp->p4type==P4_NOTUSED || pOp->p4type==P4_INT32 );
|
||||
assert( pOp->p4type==P4_NOTUSED
|
||||
|| pOp->p4type==P4_INT32
|
||||
|| pOp->p4type==P4_KEYINFO );
|
||||
freeP4(db, pOp->p4type, pOp->p4.p);
|
||||
pOp->p4.p = 0;
|
||||
if( n==P4_INT32 ){
|
||||
|
||||
@@ -504,23 +504,40 @@ static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Reset a sorting cursor back to its original empty state.
|
||||
*/
|
||||
void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){
|
||||
if( pSorter->aIter ){
|
||||
int i;
|
||||
for(i=0; i<pSorter->nTree; i++){
|
||||
vdbeSorterIterZero(db, &pSorter->aIter[i]);
|
||||
}
|
||||
sqlite3DbFree(db, pSorter->aIter);
|
||||
pSorter->aIter = 0;
|
||||
}
|
||||
if( pSorter->pTemp1 ){
|
||||
sqlite3OsCloseFree(pSorter->pTemp1);
|
||||
pSorter->pTemp1 = 0;
|
||||
}
|
||||
vdbeSorterRecordFree(db, pSorter->pRecord);
|
||||
pSorter->pRecord = 0;
|
||||
pSorter->iWriteOff = 0;
|
||||
pSorter->iReadOff = 0;
|
||||
pSorter->nInMemory = 0;
|
||||
pSorter->nTree = 0;
|
||||
pSorter->nPMA = 0;
|
||||
pSorter->aTree = 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Free any cursor components allocated by sqlite3VdbeSorterXXX routines.
|
||||
*/
|
||||
void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
|
||||
VdbeSorter *pSorter = pCsr->pSorter;
|
||||
if( pSorter ){
|
||||
if( pSorter->aIter ){
|
||||
int i;
|
||||
for(i=0; i<pSorter->nTree; i++){
|
||||
vdbeSorterIterZero(db, &pSorter->aIter[i]);
|
||||
}
|
||||
sqlite3DbFree(db, pSorter->aIter);
|
||||
}
|
||||
if( pSorter->pTemp1 ){
|
||||
sqlite3OsCloseFree(pSorter->pTemp1);
|
||||
}
|
||||
vdbeSorterRecordFree(db, pSorter->pRecord);
|
||||
sqlite3VdbeSorterReset(db, pSorter);
|
||||
sqlite3DbFree(db, pSorter->pUnpacked);
|
||||
sqlite3DbFree(db, pSorter);
|
||||
pCsr->pSorter = 0;
|
||||
|
||||
130
src/where.c
130
src/where.c
@@ -39,7 +39,7 @@ int sqlite3WhereIsDistinct(WhereInfo *pWInfo){
|
||||
** Return FALSE if the output needs to be sorted.
|
||||
*/
|
||||
int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
|
||||
return pWInfo->bOBSat!=0;
|
||||
return pWInfo->nOBSat;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3037,8 +3037,11 @@ static Bitmask codeOneLoopStart(
|
||||
** the first one after the nEq equality constraints in the index,
|
||||
** this requires some special handling.
|
||||
*/
|
||||
assert( pWInfo->pOrderBy==0
|
||||
|| pWInfo->pOrderBy->nExpr==1
|
||||
|| (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 );
|
||||
if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0
|
||||
&& (pWInfo->bOBSat!=0)
|
||||
&& pWInfo->nOBSat>0
|
||||
&& (pIdx->nKeyCol>nEq)
|
||||
){
|
||||
assert( pLoop->u.btree.nSkip==0 );
|
||||
@@ -4508,8 +4511,8 @@ static int whereLoopAddVirtual(
|
||||
pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
|
||||
pIdxInfo->needToFreeIdxStr = 0;
|
||||
pNew->u.vtab.idxStr = pIdxInfo->idxStr;
|
||||
pNew->u.vtab.isOrdered = (u8)((pIdxInfo->nOrderBy!=0)
|
||||
&& pIdxInfo->orderByConsumed);
|
||||
pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ?
|
||||
pIdxInfo->nOrderBy : 0);
|
||||
pNew->rSetup = 0;
|
||||
pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost);
|
||||
pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows);
|
||||
@@ -4670,11 +4673,11 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
|
||||
/*
|
||||
** Examine a WherePath (with the addition of the extra WhereLoop of the 5th
|
||||
** parameters) to see if it outputs rows in the requested ORDER BY
|
||||
** (or GROUP BY) without requiring a separate sort operation. Return:
|
||||
** (or GROUP BY) without requiring a separate sort operation. Return N:
|
||||
**
|
||||
** 0: ORDER BY is not satisfied. Sorting required
|
||||
** 1: ORDER BY is satisfied. Omit sorting
|
||||
** -1: Unknown at this time
|
||||
** N>0: N terms of the ORDER BY clause are satisfied
|
||||
** N==0: No terms of the ORDER BY clause are satisfied
|
||||
** N<0: Unknown yet how many terms of ORDER BY might be satisfied.
|
||||
**
|
||||
** Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as
|
||||
** strict. With GROUP BY and DISTINCT the only requirement is that
|
||||
@@ -4684,7 +4687,7 @@ static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
|
||||
** the pOrderBy terms can be matched in any order. With ORDER BY, the
|
||||
** pOrderBy terms must be matched in strict left-to-right order.
|
||||
*/
|
||||
static int wherePathSatisfiesOrderBy(
|
||||
static i8 wherePathSatisfiesOrderBy(
|
||||
WhereInfo *pWInfo, /* The WHERE clause */
|
||||
ExprList *pOrderBy, /* ORDER BY or GROUP BY or DISTINCT clause to check */
|
||||
WherePath *pPath, /* The WherePath to check */
|
||||
@@ -4917,8 +4920,14 @@ static int wherePathSatisfiesOrderBy(
|
||||
}
|
||||
}
|
||||
} /* End the loop over all WhereLoops from outer-most down to inner-most */
|
||||
if( obSat==obDone ) return 1;
|
||||
if( !isOrderDistinct ) return 0;
|
||||
if( obSat==obDone ) return nOrderBy;
|
||||
if( !isOrderDistinct ){
|
||||
for(i=nOrderBy-1; i>0; i--){
|
||||
Bitmask m = MASKBIT(i) - 1;
|
||||
if( (obSat&m)==m ) return i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -4955,11 +4964,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
int iLoop; /* Loop counter over the terms of the join */
|
||||
int ii, jj; /* Loop counters */
|
||||
int mxI = 0; /* Index of next entry to replace */
|
||||
int nOrderBy; /* Number of ORDER BY clause terms */
|
||||
LogEst rCost; /* Cost of a path */
|
||||
LogEst nOut; /* Number of outputs */
|
||||
LogEst mxCost = 0; /* Maximum cost of a set of paths */
|
||||
LogEst mxOut = 0; /* Maximum nOut value on the set of paths */
|
||||
LogEst rSortCost; /* Cost to do a sort */
|
||||
int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */
|
||||
WherePath *aFrom; /* All nFrom paths at the previous level */
|
||||
WherePath *aTo; /* The nTo best paths at the current level */
|
||||
@@ -5001,16 +5010,12 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
|
||||
/* Precompute the cost of sorting the final result set, if the caller
|
||||
** to sqlite3WhereBegin() was concerned about sorting */
|
||||
rSortCost = 0;
|
||||
if( pWInfo->pOrderBy==0 || nRowEst==0 ){
|
||||
aFrom[0].isOrderedValid = 1;
|
||||
aFrom[0].isOrdered = 0;
|
||||
nOrderBy = 0;
|
||||
}else{
|
||||
/* TUNING: Estimated cost of sorting is 48*N*log2(N) where N is the
|
||||
** number of output rows. The 48 is the expected size of a row to sort.
|
||||
** FIXME: compute a better estimate of the 48 multiplier based on the
|
||||
** result set expressions. */
|
||||
rSortCost = nRowEst + estLog(nRowEst);
|
||||
WHERETRACE(0x002,("---- sort cost=%-3d\n", rSortCost));
|
||||
aFrom[0].isOrdered = -1;
|
||||
nOrderBy = pWInfo->pOrderBy->nExpr;
|
||||
}
|
||||
|
||||
/* Compute successively longer WherePaths using the previous generation
|
||||
@@ -5022,8 +5027,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
|
||||
Bitmask maskNew;
|
||||
Bitmask revMask = 0;
|
||||
u8 isOrderedValid = pFrom->isOrderedValid;
|
||||
u8 isOrdered = pFrom->isOrdered;
|
||||
i8 isOrdered = pFrom->isOrdered;
|
||||
if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
|
||||
if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
|
||||
/* At this point, pWLoop is a candidate to be the next loop.
|
||||
@@ -5032,21 +5036,27 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
rCost = sqlite3LogEstAdd(rCost, pFrom->rCost);
|
||||
nOut = pFrom->nRow + pWLoop->nOut;
|
||||
maskNew = pFrom->maskLoop | pWLoop->maskSelf;
|
||||
if( !isOrderedValid ){
|
||||
switch( wherePathSatisfiesOrderBy(pWInfo,
|
||||
if( isOrdered<0 ){
|
||||
isOrdered = wherePathSatisfiesOrderBy(pWInfo,
|
||||
pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags,
|
||||
iLoop, pWLoop, &revMask) ){
|
||||
case 1: /* Yes. pFrom+pWLoop does satisfy the ORDER BY clause */
|
||||
isOrdered = 1;
|
||||
isOrderedValid = 1;
|
||||
break;
|
||||
case 0: /* No. pFrom+pWLoop will require a separate sort */
|
||||
isOrdered = 0;
|
||||
isOrderedValid = 1;
|
||||
rCost = sqlite3LogEstAdd(rCost, rSortCost);
|
||||
break;
|
||||
default: /* Cannot tell yet. Try again on the next iteration */
|
||||
break;
|
||||
iLoop, pWLoop, &revMask);
|
||||
if( isOrdered>=0 && isOrdered<nOrderBy ){
|
||||
/* TUNING: Estimated cost of sorting cost as roughly N*log(N).
|
||||
** If some but not all of the columns are in sorted order, then
|
||||
** scale down the log(N) term. */
|
||||
LogEst rScale = sqlite3LogEst((nOrderBy-isOrdered)*100/nOrderBy);
|
||||
LogEst rSortCost = nRowEst + estLog(nRowEst) + rScale - 66;
|
||||
/* TUNING: The cost of implementing DISTINCT using a B-TREE is
|
||||
** also N*log(N) but it has a larger constant of proportionality.
|
||||
** Multiply by 3.0. */
|
||||
if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
|
||||
rSortCost += 16;
|
||||
}
|
||||
WHERETRACE(0x002,
|
||||
("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n",
|
||||
rSortCost, (nOrderBy-isOrdered), nOrderBy, rCost,
|
||||
sqlite3LogEstAdd(rCost,rSortCost)));
|
||||
rCost = sqlite3LogEstAdd(rCost, rSortCost);
|
||||
}
|
||||
}else{
|
||||
revMask = pFrom->revLoop;
|
||||
@@ -5054,7 +5064,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
/* Check to see if pWLoop should be added to the mxChoice best so far */
|
||||
for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){
|
||||
if( pTo->maskLoop==maskNew
|
||||
&& pTo->isOrderedValid==isOrderedValid
|
||||
&& ((pTo->isOrdered^isOrdered)&80)==0
|
||||
&& ((pTo->rCost<=rCost && pTo->nRow<=nOut) ||
|
||||
(pTo->rCost>=rCost && pTo->nRow>=nOut))
|
||||
){
|
||||
@@ -5068,7 +5078,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
if( sqlite3WhereTrace&0x4 ){
|
||||
sqlite3DebugPrintf("Skip %s cost=%-3d,%3d order=%c\n",
|
||||
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
|
||||
isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?');
|
||||
isOrdered>=0 ? isOrdered+'0' : '?');
|
||||
}
|
||||
#endif
|
||||
continue;
|
||||
@@ -5086,7 +5096,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
if( sqlite3WhereTrace&0x4 ){
|
||||
sqlite3DebugPrintf("New %s cost=%-3d,%3d order=%c\n",
|
||||
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
|
||||
isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?');
|
||||
isOrdered>=0 ? isOrdered+'0' : '?');
|
||||
}
|
||||
#endif
|
||||
}else{
|
||||
@@ -5096,10 +5106,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
sqlite3DebugPrintf(
|
||||
"Skip %s cost=%-3d,%3d order=%c",
|
||||
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
|
||||
isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?');
|
||||
isOrdered>=0 ? isOrdered+'0' : '?');
|
||||
sqlite3DebugPrintf(" vs %s cost=%-3d,%d order=%c\n",
|
||||
wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
|
||||
pTo->isOrderedValid ? (pTo->isOrdered ? 'Y' : 'N') : '?');
|
||||
pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
|
||||
}
|
||||
#endif
|
||||
testcase( pTo->rCost==rCost );
|
||||
@@ -5112,10 +5122,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
sqlite3DebugPrintf(
|
||||
"Update %s cost=%-3d,%3d order=%c",
|
||||
wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
|
||||
isOrderedValid ? (isOrdered ? 'Y' : 'N') : '?');
|
||||
isOrdered>=0 ? isOrdered+'0' : '?');
|
||||
sqlite3DebugPrintf(" was %s cost=%-3d,%3d order=%c\n",
|
||||
wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
|
||||
pTo->isOrderedValid ? (pTo->isOrdered ? 'Y' : 'N') : '?');
|
||||
pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -5124,7 +5134,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
pTo->revLoop = revMask;
|
||||
pTo->nRow = nOut;
|
||||
pTo->rCost = rCost;
|
||||
pTo->isOrderedValid = isOrderedValid;
|
||||
pTo->isOrdered = isOrdered;
|
||||
memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop);
|
||||
pTo->aLoop[iLoop] = pWLoop;
|
||||
@@ -5149,8 +5158,8 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
for(ii=0, pTo=aTo; ii<nTo; ii++, pTo++){
|
||||
sqlite3DebugPrintf(" %s cost=%-3d nrow=%-3d order=%c",
|
||||
wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
|
||||
pTo->isOrderedValid ? (pTo->isOrdered ? 'Y' : 'N') : '?');
|
||||
if( pTo->isOrderedValid && pTo->isOrdered ){
|
||||
pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?');
|
||||
if( pTo->isOrdered>0 ){
|
||||
sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop);
|
||||
}else{
|
||||
sqlite3DebugPrintf("\n");
|
||||
@@ -5193,13 +5202,18 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
|
||||
Bitmask notUsed;
|
||||
int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pResultSet, pFrom,
|
||||
WHERE_DISTINCTBY, nLoop-1, pFrom->aLoop[nLoop-1], ¬Used);
|
||||
if( rc==1 ) pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
|
||||
}
|
||||
if( pFrom->isOrdered ){
|
||||
if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){
|
||||
if( rc==pWInfo->pResultSet->nExpr ){
|
||||
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
|
||||
}
|
||||
}
|
||||
if( pWInfo->pOrderBy ){
|
||||
if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){
|
||||
if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){
|
||||
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
|
||||
}
|
||||
}else{
|
||||
pWInfo->bOBSat = 1;
|
||||
pWInfo->nOBSat = pFrom->isOrdered;
|
||||
if( pWInfo->nOBSat<0 ) pWInfo->nOBSat = 0;
|
||||
pWInfo->revMask = pFrom->revLoop;
|
||||
}
|
||||
}
|
||||
@@ -5284,7 +5298,7 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
|
||||
pLoop->maskSelf = getMask(&pWInfo->sMaskSet, iCur);
|
||||
pWInfo->a[0].iTabCur = iCur;
|
||||
pWInfo->nRowOut = 1;
|
||||
if( pWInfo->pOrderBy ) pWInfo->bOBSat = 1;
|
||||
if( pWInfo->pOrderBy ) pWInfo->nOBSat = pWInfo->pOrderBy->nExpr;
|
||||
if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
|
||||
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
|
||||
}
|
||||
@@ -5388,7 +5402,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
Parse *pParse, /* The parser context */
|
||||
SrcList *pTabList, /* FROM clause: A list of all tables to be scanned */
|
||||
Expr *pWhere, /* The WHERE clause */
|
||||
ExprList *pOrderBy, /* An ORDER BY clause, or NULL */
|
||||
ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */
|
||||
ExprList *pResultSet, /* Result set of the query */
|
||||
u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
|
||||
int iIdxCur /* If WHERE_ONETABLE_ONLY is set, index cursor number */
|
||||
@@ -5410,6 +5424,10 @@ WhereInfo *sqlite3WhereBegin(
|
||||
/* Variable initialization */
|
||||
db = pParse->db;
|
||||
memset(&sWLB, 0, sizeof(sWLB));
|
||||
|
||||
/* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
|
||||
testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
|
||||
if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0;
|
||||
sWLB.pOrderBy = pOrderBy;
|
||||
|
||||
/* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
|
||||
@@ -5488,7 +5506,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
/* Special case: No FROM clause
|
||||
*/
|
||||
if( nTabList==0 ){
|
||||
if( pOrderBy ) pWInfo->bOBSat = 1;
|
||||
if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr;
|
||||
if( wctrlFlags & WHERE_WANT_DISTINCT ){
|
||||
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
|
||||
}
|
||||
@@ -5599,8 +5617,8 @@ WhereInfo *sqlite3WhereBegin(
|
||||
if( sqlite3WhereTrace ){
|
||||
int ii;
|
||||
sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut);
|
||||
if( pWInfo->bOBSat ){
|
||||
sqlite3DebugPrintf(" ORDERBY=0x%llx", pWInfo->revMask);
|
||||
if( pWInfo->nOBSat>0 ){
|
||||
sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask);
|
||||
}
|
||||
switch( pWInfo->eDistinct ){
|
||||
case WHERE_DISTINCT_UNIQUE: {
|
||||
|
||||
@@ -121,7 +121,7 @@ struct WhereLoop {
|
||||
struct { /* Information for virtual tables */
|
||||
int idxNum; /* Index number */
|
||||
u8 needFree; /* True if sqlite3_free(idxStr) is needed */
|
||||
u8 isOrdered; /* True if satisfies ORDER BY */
|
||||
i8 isOrdered; /* True if satisfies ORDER BY */
|
||||
u16 omitMask; /* Terms that may be omitted */
|
||||
char *idxStr; /* Index identifier string */
|
||||
} vtab;
|
||||
@@ -183,8 +183,7 @@ struct WherePath {
|
||||
Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */
|
||||
LogEst nRow; /* Estimated number of rows generated by this path */
|
||||
LogEst rCost; /* Total cost of this path */
|
||||
u8 isOrdered; /* True if this path satisfies ORDER BY */
|
||||
u8 isOrderedValid; /* True if the isOrdered field is valid */
|
||||
i8 isOrdered; /* No. of ORDER BY terms satisfied. -1 for unknown */
|
||||
WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */
|
||||
};
|
||||
|
||||
@@ -398,7 +397,7 @@ struct WhereInfo {
|
||||
Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
|
||||
LogEst nRowOut; /* Estimated number of output rows */
|
||||
u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
|
||||
u8 bOBSat; /* ORDER BY satisfied by indices */
|
||||
i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */
|
||||
u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE/DELETE */
|
||||
u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
|
||||
u8 eDistinct; /* One of the WHERE_DISTINCT_* values below */
|
||||
|
||||
@@ -162,7 +162,7 @@ do_execsql_test 2.0 {
|
||||
foreach {tn sql temptables res} {
|
||||
1 "a, b FROM t1" {} {A B a b}
|
||||
2 "b, a FROM t1" {} {B A b a}
|
||||
3 "a, b, c FROM t1" {hash} {a b c A B C}
|
||||
3 "a, b, c FROM t1" {hash} {A B C a b c}
|
||||
4 "a, b, c FROM t1 ORDER BY a, b, c" {btree} {A B C a b c}
|
||||
5 "b FROM t1 WHERE a = 'a'" {} {b}
|
||||
6 "b FROM t1 ORDER BY +b COLLATE binary" {btree hash} {B b}
|
||||
|
||||
@@ -64,10 +64,28 @@ do_execsql_test 1.7 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT DISTINCT c, b, a FROM t1 WHERE +a=0;
|
||||
} {/B-TREE/}
|
||||
do_execsql_test 2.1 {
|
||||
|
||||
# In some cases, it is faster to do repeated index lookups than it is to
|
||||
# sort. But in other cases, it is faster to sort than to do repeated index
|
||||
# lookups.
|
||||
#
|
||||
do_execsql_test 2.1a {
|
||||
CREATE TABLE t2(a,b,c);
|
||||
CREATE INDEX t2bc ON t2(b,c);
|
||||
ANALYZE;
|
||||
INSERT INTO sqlite_stat1 VALUES('t1','t1bc','1000000 10 9');
|
||||
INSERT INTO sqlite_stat1 VALUES('t2','t2bc','100 10 5');
|
||||
ANALYZE sqlite_master;
|
||||
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t2 WHERE a=0 ORDER BY a, b, c;
|
||||
} {~/B-TREE/}
|
||||
do_execsql_test 2.1b {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t1 WHERE a=0 ORDER BY a, b, c;
|
||||
} {~/B-TREE/}
|
||||
} {/B-TREE/}
|
||||
|
||||
|
||||
do_execsql_test 2.2 {
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT * FROM t1 WHERE +a=0 ORDER BY a, b, c;
|
||||
|
||||
183
test/orderby6.test
Normal file
183
test/orderby6.test
Normal file
@@ -0,0 +1,183 @@
|
||||
# 2014-03-21
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing that the block-sort optimization.
|
||||
#
|
||||
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix orderby6
|
||||
|
||||
# Run all tests twice. Once with a normal table and a second time
|
||||
# with a WITHOUT ROWID table
|
||||
#
|
||||
foreach {tn rowidclause} {1 {} 2 {WITHOUT ROWID}} {
|
||||
|
||||
# Construct a table with 1000 rows and a split primary key
|
||||
#
|
||||
reset_db
|
||||
do_test $tn.1 {
|
||||
db eval "CREATE TABLE t1(a,b,c,PRIMARY KEY(b,c)) $rowidclause;"
|
||||
db eval {
|
||||
WITH RECURSIVE
|
||||
cnt(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM cnt WHERE x<1000)
|
||||
INSERT INTO t1 SELECT x, x%40, x/40 FROM cnt;
|
||||
}
|
||||
} {}
|
||||
|
||||
# Run various ORDER BY queries that can benefit from block-sort.
|
||||
# Compare the output to the same output using a full-sort enforced
|
||||
# by adding + to each term of the ORDER BY clause.
|
||||
#
|
||||
do_execsql_test $tn.2 {
|
||||
SELECT b,a,c FROM t1 ORDER BY b,a,c;
|
||||
} [db eval {SELECT b,a,c FROM t1 ORDER BY +b,+a,+c}]
|
||||
do_execsql_test $tn.3 {
|
||||
SELECT b,a,c FROM t1 ORDER BY b,c DESC,a;
|
||||
} [db eval {SELECT b,a,c FROM t1 ORDER BY +b,+c DESC,+a}]
|
||||
do_execsql_test $tn.4 {
|
||||
SELECT b,a,c FROM t1 ORDER BY b DESC,c,a;
|
||||
} [db eval {SELECT b,a,c FROM t1 ORDER BY +b DESC,+c,+a}]
|
||||
do_execsql_test $tn.5 {
|
||||
SELECT b,a,c FROM t1 ORDER BY b DESC,a,c;
|
||||
} [db eval {SELECT b,a,c FROM t1 ORDER BY +b DESC,+a,+c}]
|
||||
|
||||
# LIMIT and OFFSET clauses on block-sort queries.
|
||||
#
|
||||
do_execsql_test $tn.11 {
|
||||
SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;
|
||||
} {840 880 920 960 1000 1 41 81 121 161}
|
||||
do_execsql_test $tn.11x {
|
||||
SELECT a FROM t1 ORDER BY +b, a LIMIT 10 OFFSET 20;
|
||||
} {840 880 920 960 1000 1 41 81 121 161}
|
||||
|
||||
do_execsql_test $tn.12 {
|
||||
SELECT a FROM t1 ORDER BY b DESC, a LIMIT 10 OFFSET 20;
|
||||
} {839 879 919 959 999 38 78 118 158 198}
|
||||
do_execsql_test $tn.12 {
|
||||
SELECT a FROM t1 ORDER BY +b DESC, a LIMIT 10 OFFSET 20;
|
||||
} {839 879 919 959 999 38 78 118 158 198}
|
||||
|
||||
do_execsql_test $tn.13 {
|
||||
SELECT a FROM t1 ORDER BY b, a DESC LIMIT 10 OFFSET 45;
|
||||
} {161 121 81 41 1 962 922 882 842 802}
|
||||
do_execsql_test $tn.13x {
|
||||
SELECT a FROM t1 ORDER BY +b, a DESC LIMIT 10 OFFSET 45;
|
||||
} {161 121 81 41 1 962 922 882 842 802}
|
||||
|
||||
do_execsql_test $tn.14 {
|
||||
SELECT a FROM t1 ORDER BY b DESC, a LIMIT 10 OFFSET 45;
|
||||
} {838 878 918 958 998 37 77 117 157 197}
|
||||
do_execsql_test $tn.14x {
|
||||
SELECT a FROM t1 ORDER BY +b DESC, a LIMIT 10 OFFSET 45;
|
||||
} {838 878 918 958 998 37 77 117 157 197}
|
||||
|
||||
# Many test cases where the LIMIT+OFFSET window is in various
|
||||
# alignments with block-sort boundaries.
|
||||
#
|
||||
foreach {tx limit offset orderby} {
|
||||
1 10 24 {+b,+a}
|
||||
2 10 25 {+b,+a}
|
||||
3 10 26 {+b,+a}
|
||||
4 10 39 {+b,+a}
|
||||
5 10 40 {+b,+a}
|
||||
6 10 41 {+b,+a}
|
||||
7 27 24 {+b,+a}
|
||||
8 27 49 {+b,+a}
|
||||
11 10 24 {+b DESC,+a}
|
||||
12 10 25 {+b DESC,+a}
|
||||
13 10 26 {+b DESC,+a}
|
||||
14 10 39 {+b DESC,+a}
|
||||
15 10 40 {+b DESC,+a}
|
||||
16 10 41 {+b DESC,+a}
|
||||
17 27 24 {+b DESC,+a}
|
||||
18 27 49 {+b DESC,+a}
|
||||
21 10 24 {+b,+a DESC}
|
||||
22 10 25 {+b,+a DESC}
|
||||
23 10 26 {+b,+a DESC}
|
||||
24 10 39 {+b,+a DESC}
|
||||
25 10 40 {+b,+a DESC}
|
||||
26 10 41 {+b,+a DESC}
|
||||
27 27 24 {+b,+a DESC}
|
||||
28 27 49 {+b,+a DESC}
|
||||
31 10 24 {+b DESC,+a DESC}
|
||||
32 10 25 {+b DESC,+a DESC}
|
||||
33 10 26 {+b DESC,+a DESC}
|
||||
34 10 39 {+b DESC,+a DESC}
|
||||
35 10 40 {+b DESC,+a DESC}
|
||||
36 10 41 {+b DESC,+a DESC}
|
||||
37 27 24 {+b DESC,+a DESC}
|
||||
38 27 49 {+b DESC,+a DESC}
|
||||
} {
|
||||
set sql1 "SELECT a FROM t1 ORDER BY $orderby LIMIT $limit OFFSET $offset;"
|
||||
set sql2 [string map {+ {}} $sql1]
|
||||
# puts $sql2\n$sql1\n[db eval $sql2]
|
||||
do_test $tn.21.$tx {db eval $::sql2} [db eval $sql1]
|
||||
}
|
||||
|
||||
########################################################################
|
||||
# A second test table, t2, has many columns open to sorting.
|
||||
do_test $tn.31 {
|
||||
db eval "CREATE TABLE t2(a,b,c,d,e,f,PRIMARY KEY(b,c,d,e,f)) $rowidclause;"
|
||||
db eval {
|
||||
WITH RECURSIVE
|
||||
cnt(x) AS (VALUES(0) UNION ALL SELECT x+1 FROM cnt WHERE x<242)
|
||||
INSERT INTO t2 SELECT x, x%3, (x/3)%3, (x/9)%3, (x/27)%3, (x/81)%3
|
||||
FROM cnt;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test $tn.32 {
|
||||
SELECT a FROM t2 ORDER BY b,c,d,e,f;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f;}]
|
||||
do_execsql_test $tn.33 {
|
||||
SELECT a FROM t2 ORDER BY b,c,d,e,+f;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f;}]
|
||||
do_execsql_test $tn.34 {
|
||||
SELECT a FROM t2 ORDER BY b,c,d,+e,+f;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f;}]
|
||||
do_execsql_test $tn.35 {
|
||||
SELECT a FROM t2 ORDER BY b,c,+d,+e,+f;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f;}]
|
||||
do_execsql_test $tn.36 {
|
||||
SELECT a FROM t2 ORDER BY b,+c,+d,+e,+f;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f;}]
|
||||
|
||||
do_execsql_test $tn.37 {
|
||||
SELECT a FROM t2 ORDER BY b,c,d,e,f DESC;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f DESC;}]
|
||||
do_execsql_test $tn.38 {
|
||||
SELECT a FROM t2 ORDER BY b,c,d,e DESC,f;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e DESC,+f;}]
|
||||
do_execsql_test $tn.39 {
|
||||
SELECT a FROM t2 ORDER BY b,c,d DESC,e,f;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d DESC,+e,+f;}]
|
||||
do_execsql_test $tn.40 {
|
||||
SELECT a FROM t2 ORDER BY b,c DESC,d,e,f;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b,+c DESC,+d,+e,+f;}]
|
||||
do_execsql_test $tn.41 {
|
||||
SELECT a FROM t2 ORDER BY b DESC,c,d,e,f;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b DESC,+c,+d,+e,+f;}]
|
||||
|
||||
do_execsql_test $tn.42 {
|
||||
SELECT a FROM t2 ORDER BY b DESC,c DESC,d,e,f LIMIT 31;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b DESC,+c DESC,+d,+e,+f LIMIT 31}]
|
||||
do_execsql_test $tn.43 {
|
||||
SELECT a FROM t2 ORDER BY b,c,d,e,f DESC LIMIT 8 OFFSET 7;
|
||||
} [db eval {SELECT a FROM t2 ORDER BY +b,+c,+d,+e,+f DESC LIMIT 8 OFFSET 7}]
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
@@ -95,7 +95,7 @@ do_eqp_test whereG-1.5 {
|
||||
WHERE cname LIKE '%bach%'
|
||||
AND composer.cid=track.cid
|
||||
AND album.aid=track.aid;
|
||||
} {/.*track.*composer.*album.*/}
|
||||
} {/.*track.*(composer.*album|album.*composer).*/}
|
||||
do_execsql_test whereG-1.6 {
|
||||
SELECT DISTINCT aname
|
||||
FROM album, composer, track
|
||||
@@ -110,7 +110,7 @@ do_eqp_test whereG-1.7 {
|
||||
WHERE cname LIKE '%bach%'
|
||||
AND unlikely(composer.cid=track.cid)
|
||||
AND unlikely(album.aid=track.aid);
|
||||
} {/.*track.*composer.*album.*/}
|
||||
} {/.*track.*(composer.*album|album.*composer).*/}
|
||||
do_execsql_test whereG-1.8 {
|
||||
SELECT DISTINCT aname
|
||||
FROM album, composer, track
|
||||
|
||||
Reference in New Issue
Block a user