mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-01 06:27:03 +03:00
The optimizer now uses only the index and ignores the table if it can get
away with doing so, thus saving a single BTree search per row of result. This could potentially double the speed of certain queries. The code passes all regression tests but new tests to exercise the new functionality are yet to be added. (CVS 2170) FossilOrigin-Name: e5aa489453bf31126da6473ef93c89ec27935cde
This commit is contained in:
20
manifest
20
manifest
@ -1,5 +1,5 @@
|
||||
C Improvements\sto\sthe\squery\soptimizer.\s\sThis\sis\sa\swork\sin\sprogress.\s(CVS\s2169)
|
||||
D 2004-12-18T18:40:27
|
||||
C The\soptimizer\snow\suses\sonly\sthe\sindex\sand\signores\sthe\stable\sif\sit\scan\sget\naway\swith\sdoing\sso,\sthus\ssaving\sa\ssingle\sBTree\ssearch\sper\srow\sof\sresult.\nThis\scould\spotentially\sdouble\sthe\sspeed\sof\scertain\squeries.\s\sThe\ncode\spasses\sall\sregression\stests\sbut\snew\stests\sto\sexercise\sthe\snew\nfunctionality\sare\syet\sto\sbe\sadded.\s(CVS\s2170)
|
||||
D 2004-12-19T00:11:35
|
||||
F Makefile.in da09f379b80c8cd78d78abaa0f32ca90a124e884
|
||||
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
|
||||
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
|
||||
@ -62,7 +62,7 @@ F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
|
||||
F src/select.c ac6610b4b2c5bd5ffc46536b760dacc420119dac
|
||||
F src/shell.c e8f4f486cbf6e60d81173146ac8a6522c930fa51
|
||||
F src/sqlite.h.in fa75850f412808afd38fddc1fd6456f4efc6fb97
|
||||
F src/sqliteInt.h 9f95e46c03ba8466e7bc4528ffe39e3ae0c955ab
|
||||
F src/sqliteInt.h a922cfd13711c68538684619fb15a4d262b12b9d
|
||||
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
|
||||
F src/tclsqlite.c 3a4044ef609565c8cc51e887d8b96933ba9f3b5c
|
||||
F src/test1.c b7d94c54e58f95452387a5cabdf98b2be8059f29
|
||||
@ -76,13 +76,13 @@ F src/update.c aa92fa2203b2233008dd75a1e97c4b441be24a7f
|
||||
F src/utf.c e45ce11be6922408cd381561721f6cca7d3b992a
|
||||
F src/util.c 4a8db4e97a3cfda12ad8dda3e77dd2d00ad1de5e
|
||||
F src/vacuum.c 705256e1111521fa04f0029de7f1667bc131d015
|
||||
F src/vdbe.c aaec606fb513102314ca351006cea6656791bdcf
|
||||
F src/vdbe.c caeb3f88d9a027d300c8fd27cf885ca964e5354e
|
||||
F src/vdbe.h 067ca8d6750ba4f69a50284765e5883dee860181
|
||||
F src/vdbeInt.h 0f74561e629af86172de7cdf0ecaea014c51696c
|
||||
F src/vdbeapi.c 0cf3bdc1072616bedc8eec7fc22e3f5a169d33fd
|
||||
F src/vdbeaux.c a7c4c90786e2633b38f2d89f3dc49aed747454e4
|
||||
F src/vdbemem.c 5876c8abf4374fef671f4fd8dc333ef3fc95a2f0
|
||||
F src/where.c 94b784521e40f1b181ac151f89150a243c88feee
|
||||
F src/where.c e4a34a8fd159d5ca59f87bb689bb513f1a2620df
|
||||
F tclinstaller.tcl 36478c3bbfc5b93ceac42d94e3c736937b808432
|
||||
F test/all.test 929bfa932b55e75c96fe2203f7650ba451c1862c
|
||||
F test/alter.test 95c57a4f461fa81293e0dccef7f83889aadb169a
|
||||
@ -109,7 +109,7 @@ F test/capi3b.test 5b6a66f9f295f79f443b5d3f33187fa5ef6cf336
|
||||
F test/collate1.test f79736d2ebf5492167ee4d1f4ab4c09dda776b03
|
||||
F test/collate2.test 12fd658d8f5106a8a5c8a77d66919d8c89394036
|
||||
F test/collate3.test 5fe8077bd82c53112974f56f51f06cbd06d71374
|
||||
F test/collate4.test a7bb41adf16e211f52b925613110af5c70ae7792
|
||||
F test/collate4.test 2d6e38e6b871073313f6d4eebfe1435c7173ebfa
|
||||
F test/collate5.test 7999fb3122386bae38acd8ccd61e0b7c5a30e289
|
||||
F test/collate6.test 6c9470d1606ee3e564675b229653e320c49ec638
|
||||
F test/conflict.test c5b849b01cfbe0a4f63a90cba6f68e2fe3a75f87
|
||||
@ -202,7 +202,7 @@ F test/utf16.test 459c2f5ab80c60092c603630a348c32d6e59c558
|
||||
F test/vacuum.test f18eccdee5b538d46298c64d6a060cfbf97bbc23
|
||||
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
|
||||
F test/view.test 3f96df86f1c61ee850b945204683773bbbb8643e
|
||||
F test/where.test e092b5c206a58d61282090acce63a9d5fbe401f3
|
||||
F test/where.test cbe22fb2e241a896fa5dcc732fda512be011ba52
|
||||
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
||||
F tool/lemon.c 250b30bcf3f1f422a2cad24b1597314777058a4b
|
||||
F tool/lempar.c 1e61d2b6cb9d8affa264a13336bc0c088498caa4
|
||||
@ -263,7 +263,7 @@ F www/tclsqlite.tcl e73f8f8e5f20e8277619433f7970060ab01088fc
|
||||
F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618
|
||||
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
|
||||
F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
|
||||
P b49b8fdd11a5a4aac15ceda58a28bbc852f6f239
|
||||
R 98ad9cc1dc8e1742c8a95031c2f5cb13
|
||||
P 9b86993ff721b577b920c7c67fb41d3d4355fe88
|
||||
R 2476c11934030eade9ca4e863deb7247
|
||||
U drh
|
||||
Z e1e7b9285b9bdd508bb0434689171c56
|
||||
Z 352407e4634c619fec9ef3761d4b80dd
|
||||
|
@ -1 +1 @@
|
||||
9b86993ff721b577b920c7c67fb41d3d4355fe88
|
||||
e5aa489453bf31126da6473ef93c89ec27935cde
|
@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** Internal interface definitions for SQLite.
|
||||
**
|
||||
** @(#) $Id: sqliteInt.h,v 1.347 2004/12/18 18:40:27 drh Exp $
|
||||
** @(#) $Id: sqliteInt.h,v 1.348 2004/12/19 00:11:35 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITEINT_H_
|
||||
#define _SQLITEINT_H_
|
||||
@ -309,6 +309,7 @@ typedef struct CollSeq CollSeq;
|
||||
typedef struct KeyInfo KeyInfo;
|
||||
typedef struct SqlCursor SqlCursor;
|
||||
typedef struct Fetch Fetch;
|
||||
typedef struct CursorSubst CursorSubst;
|
||||
|
||||
/*
|
||||
** Each database file to be accessed by the system is an instance
|
||||
@ -910,8 +911,9 @@ struct SrcList {
|
||||
*/
|
||||
struct WhereLevel {
|
||||
int iMem; /* Memory cell used by this level */
|
||||
Index *pIdx; /* Index used */
|
||||
int iCur; /* Cursor number used for this index */
|
||||
Index *pIdx; /* Index used. NULL if no index */
|
||||
int iTabCur; /* The VDBE cursor used to access the table */
|
||||
int iIdxCur; /* The VDBE cursor used to acesss pIdx */
|
||||
int score; /* How well this indexed scored */
|
||||
int brk; /* Jump here to break out of the loop */
|
||||
int cont; /* Jump here to continue with the next loop cycle */
|
||||
@ -932,6 +934,7 @@ struct WhereLevel {
|
||||
struct WhereInfo {
|
||||
Parse *pParse;
|
||||
SrcList *pTabList; /* List of tables in the join */
|
||||
int iTop; /* The very beginning of the WHERE loop */
|
||||
int iContinue; /* Jump here to continue with next record */
|
||||
int iBreak; /* Jump here to break out of the loop */
|
||||
int nLevel; /* Number of nested loop */
|
||||
@ -1067,7 +1070,6 @@ struct Parse {
|
||||
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
|
||||
TriggerStack *trigStack; /* Trigger actions being coded */
|
||||
const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
|
10
src/vdbe.c
10
src/vdbe.c
@ -43,7 +43,7 @@
|
||||
** in this file for details. If in doubt, do not deviate from existing
|
||||
** commenting and indentation practices when changing or adding code.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.433 2004/12/07 14:06:13 drh Exp $
|
||||
** $Id: vdbe.c,v 1.434 2004/12/19 00:11:35 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@ -1666,11 +1666,6 @@ case OP_SetNumColumns: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: IdxColumn P1 * *
|
||||
**
|
||||
** P1 is a cursor opened on an index. Push the first field from the
|
||||
** current index key onto the stack.
|
||||
*/
|
||||
/* Opcode: Column P1 P2 *
|
||||
**
|
||||
** Interpret the data that cursor P1 points to as a structure built using
|
||||
@ -1688,7 +1683,6 @@ case OP_SetNumColumns: {
|
||||
** stack. The column value is not copied. The number of columns in the
|
||||
** record is stored on the stack just above the record itself.
|
||||
*/
|
||||
case OP_IdxColumn:
|
||||
case OP_Column: {
|
||||
u32 payloadSize; /* Number of bytes in the record */
|
||||
int p1 = pOp->p1; /* P1 value of the opcode */
|
||||
@ -3482,7 +3476,7 @@ case OP_IdxDelete: {
|
||||
/* Opcode: IdxRecno P1 * *
|
||||
**
|
||||
** Push onto the stack an integer which is the varint located at the
|
||||
** end of the index key pointed to by cursor P1. These integer should be
|
||||
** end of the index key pointed to by cursor P1. This integer should be
|
||||
** the record number of the table entry to which this index entry points.
|
||||
**
|
||||
** See also: Recno, MakeIdxKey.
|
||||
|
351
src/where.c
351
src/where.c
@ -16,7 +16,7 @@
|
||||
** so is applicable. Because this module is responsible for selecting
|
||||
** indices, you might also think of this module as the "query optimizer".
|
||||
**
|
||||
** $Id: where.c,v 1.122 2004/12/18 18:40:27 drh Exp $
|
||||
** $Id: where.c,v 1.123 2004/12/19 00:11:35 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -296,103 +296,6 @@ static void exprAnalyze(SrcList *pSrc, ExprMaskSet *pMaskSet, ExprInfo *pInfo){
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
** pOrderBy is an ORDER BY clause from a SELECT statement. pTab is the
|
||||
** left-most table in the FROM clause of that same SELECT statement and
|
||||
** the table has a cursor number of "base".
|
||||
**
|
||||
** This routine attempts to find an index for pTab that generates the
|
||||
** correct record sequence for the given ORDER BY clause. The return value
|
||||
** is a pointer to an index that does the job. NULL is returned if the
|
||||
** table has no index that will generate the correct sort order.
|
||||
**
|
||||
** If there are two or more indices that generate the correct sort order
|
||||
** and pPreferredIdx is one of those indices, then return pPreferredIdx.
|
||||
**
|
||||
** nEqCol is the number of columns of pPreferredIdx that are used as
|
||||
** equality constraints. Any index returned must have exactly this same
|
||||
** set of columns. The ORDER BY clause only matches index columns beyond the
|
||||
** the first nEqCol columns.
|
||||
**
|
||||
** All terms of the ORDER BY clause must be either ASC or DESC. The
|
||||
** *pbRev value is set to 1 if the ORDER BY clause is all DESC and it is
|
||||
** set to 0 if the ORDER BY clause is all ASC.
|
||||
**
|
||||
** TODO: If earlier terms of an ORDER BY clause match all terms of a
|
||||
** UNIQUE index, then subsequent terms of the ORDER BY can be ignored.
|
||||
** This optimization needs to be implemented.
|
||||
*/
|
||||
static Index *findSortingIndex(
|
||||
Parse *pParse, /* Parsing context */
|
||||
Table *pTab, /* The table to be sorted */
|
||||
int base, /* Cursor number for pTab */
|
||||
ExprList *pOrderBy, /* The ORDER BY clause */
|
||||
Index *pPreferredIdx, /* Use this index, if possible and not NULL */
|
||||
int nEqCol, /* Number of index columns used with == constraints */
|
||||
int *pbRev /* Set to 1 if ORDER BY is DESC */
|
||||
){
|
||||
int i, j; /* Loop counters */
|
||||
Index *pMatch; /* Best matching index so far */
|
||||
Index *pIdx; /* Current index */
|
||||
int sortOrder; /* Which direction we are sorting */
|
||||
sqlite3 *db = pParse->db;
|
||||
|
||||
assert( pOrderBy!=0 );
|
||||
assert( pOrderBy->nExpr>0 );
|
||||
assert( pPreferredIdx!=0 || nEqCol==0 );
|
||||
sortOrder = pOrderBy->a[0].sortOrder;
|
||||
for(i=0; i<pOrderBy->nExpr; i++){
|
||||
Expr *p;
|
||||
if( pOrderBy->a[i].sortOrder!=sortOrder ){
|
||||
/* Indices can only be used if all ORDER BY terms are either
|
||||
** DESC or ASC. Indices cannot be used on a mixture. */
|
||||
return 0;
|
||||
}
|
||||
p = pOrderBy->a[i].pExpr;
|
||||
if( p->op!=TK_COLUMN || p->iTable!=base ){
|
||||
/* Can not use an index sort on anything that is not a column in the
|
||||
** left-most table of the FROM clause */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we get this far, it means the ORDER BY clause consists of columns
|
||||
** that are all either ascending or descending and which refer only to
|
||||
** the left-most table of the FROM clause. Find the index that is best
|
||||
** used for sorting.
|
||||
*/
|
||||
pMatch = 0;
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
int nExpr = pOrderBy->nExpr;
|
||||
if( pIdx->nColumn < nEqCol || pIdx->nColumn < nExpr ) continue;
|
||||
for(i=j=0; i<nEqCol; i++){
|
||||
CollSeq *pColl = sqlite3ExprCollSeq(pParse, pOrderBy->a[j].pExpr);
|
||||
if( !pColl ) pColl = db->pDfltColl;
|
||||
if( pPreferredIdx->aiColumn[i]!=pIdx->aiColumn[i] ) break;
|
||||
if( pPreferredIdx->keyInfo.aColl[i]!=pIdx->keyInfo.aColl[i] ) break;
|
||||
if( j<nExpr &&
|
||||
pOrderBy->a[j].pExpr->iColumn==pIdx->aiColumn[i] &&
|
||||
pColl==pIdx->keyInfo.aColl[i]
|
||||
){
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if( i<nEqCol ) continue;
|
||||
for(i=0; i+j<nExpr; i++){
|
||||
CollSeq *pColl = sqlite3ExprCollSeq(pParse, pOrderBy->a[i+j].pExpr);
|
||||
if( !pColl ) pColl = db->pDfltColl;
|
||||
if( pOrderBy->a[i+j].pExpr->iColumn!=pIdx->aiColumn[i+nEqCol] ||
|
||||
pColl!=pIdx->keyInfo.aColl[i+nEqCol] ) break;
|
||||
}
|
||||
if( i+j>=nExpr ){
|
||||
pMatch = pIdx;
|
||||
if( pIdx==pPreferredIdx ) break;
|
||||
}
|
||||
}
|
||||
*pbRev = sortOrder==SQLITE_SO_DESC;
|
||||
return pMatch;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine decides if pIdx can be used to satisfy the ORDER BY
|
||||
** clause. If it can, it returns 1. If pIdx cannot satisfy the
|
||||
@ -450,8 +353,9 @@ static int isSortingIndex(
|
||||
}
|
||||
pColl = sqlite3ExprCollSeq(pParse, pExpr);
|
||||
if( !pColl ) pColl = db->pDfltColl;
|
||||
if( pExpr->iColumn!=pIdx->aiColumn[i] && pColl!=pIdx->keyInfo.aColl[i] ){
|
||||
if( i<=nEqCol ){
|
||||
if( pExpr->iColumn!=pIdx->aiColumn[i] || pColl!=pIdx->keyInfo.aColl[i] ){
|
||||
/* Term j of the ORDER BY clause does not match column i of the index */
|
||||
if( i<nEqCol ){
|
||||
/* If an index column that is constrained by == fails to match an
|
||||
** ORDER BY term, that is OK. Just ignore that column of the index
|
||||
*/
|
||||
@ -577,7 +481,7 @@ static void codeEqualityTerm(
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
sqlite3VdbeAddOp(v, OP_Rewind, iTab, brk);
|
||||
sqlite3VdbeAddOp(v, OP_KeyAsData, iTab, 1);
|
||||
pLevel->inP2 = sqlite3VdbeAddOp(v, OP_IdxColumn, iTab, 0);
|
||||
pLevel->inP2 = sqlite3VdbeAddOp(v, OP_Column, iTab, 0);
|
||||
pLevel->inOp = OP_Next;
|
||||
pLevel->inP1 = iTab;
|
||||
}
|
||||
@ -681,13 +585,15 @@ WhereInfo *sqlite3WhereBegin(
|
||||
int brk, cont = 0; /* Addresses used during code generation */
|
||||
int nExpr; /* Number of subexpressions in the WHERE clause */
|
||||
Bitmask loopMask; /* One bit set for each outer loop */
|
||||
int haveKey = 0; /* True if KEY is on the stack */
|
||||
int haveRowid = 0; /* True if the ROWID is on the stack */
|
||||
ExprInfo *pTerm; /* A single term in the WHERE clause; ptr to aExpr[] */
|
||||
ExprMaskSet maskSet; /* The expression mask set */
|
||||
int iDirectEq[BMS]; /* Term of the form ROWID==X for the N-th table */
|
||||
int iDirectLt[BMS]; /* Term of the form ROWID<X or ROWID<=X */
|
||||
int iDirectGt[BMS]; /* Term of the form ROWID>X or ROWID>=X */
|
||||
ExprInfo aExpr[101]; /* The WHERE clause is divided into these terms */
|
||||
struct SrcList_item *pTabItem; /* A single entry from pTabList */
|
||||
WhereLevel *pLevel; /* A single level in the pWInfo list */
|
||||
|
||||
/* pushKey is only allowed if there is a single table (as in an INSERT or
|
||||
** UPDATE statement)
|
||||
@ -771,12 +677,13 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** to the limit of 32 bits in an integer bitmask.
|
||||
*/
|
||||
loopMask = 0;
|
||||
for(i=0; i<pTabList->nSrc && i<ARRAYSIZE(iDirectEq); i++){
|
||||
pTabItem = pTabList->a;
|
||||
pLevel = pWInfo->a;
|
||||
for(i=0; i<pTabList->nSrc && i<ARRAYSIZE(iDirectEq); i++,pTabItem++,pLevel++){
|
||||
int j;
|
||||
WhereLevel *pLevel = &pWInfo->a[i];
|
||||
int iCur = pTabList->a[i].iCursor; /* The cursor for this table */
|
||||
int iCur = pTabItem->iCursor; /* The cursor for this table */
|
||||
Bitmask mask = getMask(&maskSet, iCur); /* Cursor mask for this table */
|
||||
Table *pTab = pTabList->a[i].pTab;
|
||||
Table *pTab = pTabItem->pTab;
|
||||
Index *pIdx;
|
||||
Index *pBestIdx = 0;
|
||||
int bestScore = 0;
|
||||
@ -790,7 +697,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
**
|
||||
** (Added:) Treat ROWID IN expr like ROWID=expr.
|
||||
*/
|
||||
pLevel->iCur = -1;
|
||||
pLevel->iIdxCur = -1;
|
||||
iDirectEq[i] = -1;
|
||||
iDirectLt[i] = -1;
|
||||
iDirectGt[i] = -1;
|
||||
@ -933,13 +840,29 @@ WhereInfo *sqlite3WhereBegin(
|
||||
|
||||
/* Give bonus points if this index can be used for sorting
|
||||
*/
|
||||
if( i==0 && score>0 && ppOrderBy && *ppOrderBy ){
|
||||
if( i==0 && score!=16 && ppOrderBy && *ppOrderBy ){
|
||||
int base = pTabList->a[0].iCursor;
|
||||
if( isSortingIndex(pParse, pIdx, pTab, base, *ppOrderBy, nEq, &bRev) ){
|
||||
score += 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check to see if we can get away with using just the index without
|
||||
** ever reading the table. If that is the case, then add one bonus
|
||||
** point to the score.
|
||||
*/
|
||||
if( score && pTabItem->colUsed < (((Bitmask)1)<<(BMS-1)) ){
|
||||
for(m=0, j=0; j<pIdx->nColumn; j++){
|
||||
int x = pIdx->aiColumn[j];
|
||||
if( x<BMS-1 ){
|
||||
m |= ((Bitmask)1)<<x;
|
||||
}
|
||||
}
|
||||
if( (pTabItem->colUsed & m)==pTabItem->colUsed ){
|
||||
score++;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the score for this index is the best we have seen so far, then
|
||||
** save it
|
||||
*/
|
||||
@ -954,7 +877,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pLevel->bRev = bestRev;
|
||||
loopMask |= mask;
|
||||
if( pBestIdx ){
|
||||
pLevel->iCur = pParse->nTab++;
|
||||
pLevel->iIdxCur = pParse->nTab++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -962,7 +885,6 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** use of an index on the first table.
|
||||
*/
|
||||
if( ppOrderBy && *ppOrderBy && pTabList->nSrc>0 ){
|
||||
Index *pSortIdx = 0; /* Index that satisfies the ORDER BY clause */
|
||||
Index *pIdx; /* Index derived from the WHERE clause */
|
||||
Table *pTab; /* Left-most table in the FROM clause */
|
||||
int bRev = 0; /* True to reverse the output order */
|
||||
@ -988,47 +910,63 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** not try to sort by index. But do delete the ORDER BY clause
|
||||
** if it is redundant.
|
||||
*/
|
||||
}else{
|
||||
int nEqCol = (pLevel0->score+16)/32;
|
||||
pSortIdx = findSortingIndex(pParse, pTab, iCur,
|
||||
*ppOrderBy, pIdx, nEqCol, &bRev);
|
||||
}
|
||||
if( pSortIdx && (pIdx==0 || pIdx==pSortIdx) ){
|
||||
if( pIdx==0 ){
|
||||
pLevel0->pIdx = pSortIdx;
|
||||
pLevel0->iCur = pParse->nTab++;
|
||||
}
|
||||
pLevel0->bRev = bRev;
|
||||
}else if( (pLevel0->score&2)!=0 ){
|
||||
/* The index that was selected for searching will cause rows to
|
||||
** appear in sorted order.
|
||||
*/
|
||||
*ppOrderBy = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Open all tables in the pTabList and all indices used by those tables.
|
||||
/* Open all tables in the pTabList and any indices selected for
|
||||
** searching those tables.
|
||||
*/
|
||||
sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
|
||||
for(i=0; i<pTabList->nSrc; i++){
|
||||
pLevel = pWInfo->a;
|
||||
for(i=0, pTabItem=pTabList->a; i<pTabList->nSrc; i++, pTabItem++, pLevel++){
|
||||
Table *pTab;
|
||||
Index *pIx;
|
||||
int iIdxCur = pLevel->iIdxCur;
|
||||
|
||||
pTab = pTabList->a[i].pTab;
|
||||
pTab = pTabItem->pTab;
|
||||
if( pTab->isTransient || pTab->pSelect ) continue;
|
||||
sqlite3OpenTableForReading(v, pTabList->a[i].iCursor, pTab);
|
||||
sqlite3CodeVerifySchema(pParse, pTab->iDb);
|
||||
if( (pIx = pWInfo->a[i].pIdx)!=0 ){
|
||||
if( (pLevel->score & 1)==0 ){
|
||||
sqlite3OpenTableForReading(v, pTabItem->iCursor, pTab);
|
||||
}
|
||||
pLevel->iTabCur = pTabItem->iCursor;
|
||||
if( (pIx = pLevel->pIdx)!=0 ){
|
||||
sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0);
|
||||
sqlite3VdbeOp3(v, OP_OpenRead, pWInfo->a[i].iCur, pIx->tnum,
|
||||
sqlite3VdbeOp3(v, OP_OpenRead, iIdxCur, pIx->tnum,
|
||||
(char*)&pIx->keyInfo, P3_KEYINFO);
|
||||
}
|
||||
if( (pLevel->score & 1)!=0 ){
|
||||
sqlite3VdbeAddOp(v, OP_KeyAsData, iIdxCur, 1);
|
||||
sqlite3VdbeAddOp(v, OP_SetNumColumns, iIdxCur, pIx->nColumn+1);
|
||||
}
|
||||
sqlite3CodeVerifySchema(pParse, pTab->iDb);
|
||||
}
|
||||
pWInfo->iTop = sqlite3VdbeCurrentAddr(v);
|
||||
|
||||
/* Generate the code to do the search
|
||||
*/
|
||||
loopMask = 0;
|
||||
for(i=0; i<pTabList->nSrc; i++){
|
||||
pLevel = pWInfo->a;
|
||||
pTabItem = pTabList->a;
|
||||
for(i=0; i<pTabList->nSrc; i++, pTabItem++, pLevel++){
|
||||
int j, k;
|
||||
int iCur = pTabList->a[i].iCursor;
|
||||
Index *pIdx;
|
||||
WhereLevel *pLevel = &pWInfo->a[i];
|
||||
int iCur = pTabItem->iCursor; /* The VDBE cursor for the table */
|
||||
Index *pIdx; /* The index we will be using */
|
||||
int iIdxCur; /* The VDBE cursor for the index */
|
||||
int omitTable; /* True if we use the index only */
|
||||
|
||||
pIdx = pLevel->pIdx;
|
||||
iIdxCur = pLevel->iIdxCur;
|
||||
pLevel->inOp = OP_Noop;
|
||||
|
||||
/* Check to see if it is appropriate to omit the use of the table
|
||||
** here and use its index instead.
|
||||
*/
|
||||
omitTable = (pLevel->score&1)!=0;
|
||||
|
||||
/* If this is the right table of a LEFT OUTER JOIN, allocate and
|
||||
** initialize a memory cell that records if this table matches any
|
||||
@ -1042,8 +980,6 @@ WhereInfo *sqlite3WhereBegin(
|
||||
VdbeComment((v, "# init LEFT JOIN no-match flag"));
|
||||
}
|
||||
|
||||
pIdx = pLevel->pIdx;
|
||||
pLevel->inOp = OP_Noop;
|
||||
if( i<ARRAYSIZE(iDirectEq) && (k = iDirectEq[i])>=0 ){
|
||||
/* Case 1: We can directly reference a single row using an
|
||||
** equality comparison against the ROWID field. Or
|
||||
@ -1054,14 +990,15 @@ WhereInfo *sqlite3WhereBegin(
|
||||
pTerm = &aExpr[k];
|
||||
assert( pTerm->p!=0 );
|
||||
assert( pTerm->idxLeft==iCur );
|
||||
assert( omitTable==0 );
|
||||
brk = pLevel->brk = sqlite3VdbeMakeLabel(v);
|
||||
codeEqualityTerm(pParse, pTerm, brk, pLevel);
|
||||
cont = pLevel->cont = sqlite3VdbeMakeLabel(v);
|
||||
sqlite3VdbeAddOp(v, OP_MustBeInt, 1, brk);
|
||||
haveKey = 0;
|
||||
sqlite3VdbeAddOp(v, OP_NotExists, iCur, brk);
|
||||
haveRowid = 0;
|
||||
pLevel->op = OP_Noop;
|
||||
}else if( pIdx!=0 && pLevel->score>0 && (pLevel->score&0x0c)==0 ){
|
||||
}else if( pIdx!=0 && pLevel->score>3 && (pLevel->score&0x0c)==0 ){
|
||||
/* Case 2: There is an index and all terms of the WHERE clause that
|
||||
** refer to the index using the "==" or "IN" operators.
|
||||
*/
|
||||
@ -1100,27 +1037,26 @@ WhereInfo *sqlite3WhereBegin(
|
||||
** iteration of the scan to see if the scan has finished. */
|
||||
if( pLevel->bRev ){
|
||||
/* Scan in reverse order */
|
||||
sqlite3VdbeAddOp(v, OP_MoveLe, pLevel->iCur, brk);
|
||||
sqlite3VdbeAddOp(v, OP_MoveLe, iIdxCur, brk);
|
||||
start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
||||
sqlite3VdbeAddOp(v, OP_IdxLT, pLevel->iCur, brk);
|
||||
sqlite3VdbeAddOp(v, OP_IdxLT, iIdxCur, brk);
|
||||
pLevel->op = OP_Prev;
|
||||
}else{
|
||||
/* Scan in the forward order */
|
||||
sqlite3VdbeAddOp(v, OP_MoveGe, pLevel->iCur, brk);
|
||||
sqlite3VdbeAddOp(v, OP_MoveGe, iIdxCur, brk);
|
||||
start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
||||
sqlite3VdbeOp3(v, OP_IdxGE, pLevel->iCur, brk, "+", P3_STATIC);
|
||||
sqlite3VdbeOp3(v, OP_IdxGE, iIdxCur, brk, "+", P3_STATIC);
|
||||
pLevel->op = OP_Next;
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_RowKey, pLevel->iCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_RowKey, iIdxCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_IdxIsNull, nColumn, cont);
|
||||
sqlite3VdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
|
||||
if( i==pTabList->nSrc-1 && pushKey ){
|
||||
haveKey = 1;
|
||||
if( omitTable ){
|
||||
haveRowid = 0;
|
||||
}else{
|
||||
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
|
||||
haveKey = 0;
|
||||
sqlite3VdbeAddOp(v, OP_IdxRecno, iIdxCur, 0);
|
||||
haveRowid = 1;
|
||||
}
|
||||
pLevel->p1 = pLevel->iCur;
|
||||
pLevel->p1 = iIdxCur;
|
||||
pLevel->p2 = start;
|
||||
}else if( i<ARRAYSIZE(iDirectLt) && (iDirectLt[i]>=0 || iDirectGt[i]>=0) ){
|
||||
/* Case 3: We have an inequality comparison against the ROWID field.
|
||||
@ -1129,6 +1065,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
int start;
|
||||
int bRev = pLevel->bRev;
|
||||
|
||||
assert( omitTable==0 );
|
||||
brk = pLevel->brk = sqlite3VdbeMakeLabel(v);
|
||||
cont = pLevel->cont = sqlite3VdbeMakeLabel(v);
|
||||
if( bRev ){
|
||||
@ -1178,7 +1115,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
||||
sqlite3VdbeAddOp(v, testOp, 0, brk);
|
||||
}
|
||||
haveKey = 0;
|
||||
haveRowid = 0;
|
||||
}else if( pIdx==0 ){
|
||||
/* Case 4: There is no usable index. We must do a complete
|
||||
** scan of the entire database table.
|
||||
@ -1186,6 +1123,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
int start;
|
||||
int opRewind;
|
||||
|
||||
assert( omitTable==0 );
|
||||
brk = pLevel->brk = sqlite3VdbeMakeLabel(v);
|
||||
cont = pLevel->cont = sqlite3VdbeMakeLabel(v);
|
||||
if( pLevel->bRev ){
|
||||
@ -1199,7 +1137,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
start = sqlite3VdbeCurrentAddr(v);
|
||||
pLevel->p1 = iCur;
|
||||
pLevel->p2 = start;
|
||||
haveKey = 0;
|
||||
haveRowid = 0;
|
||||
}else{
|
||||
/* Case 5: The WHERE clause term that refers to the right-most
|
||||
** column of the index is an inequality. For example, if
|
||||
@ -1283,12 +1221,12 @@ WhereInfo *sqlite3WhereBegin(
|
||||
buildIndexProbe(v, nCol, brk, pIdx);
|
||||
if( pLevel->bRev ){
|
||||
int op = leFlag ? OP_MoveLe : OP_MoveLt;
|
||||
sqlite3VdbeAddOp(v, op, pLevel->iCur, brk);
|
||||
sqlite3VdbeAddOp(v, op, iIdxCur, brk);
|
||||
}else{
|
||||
sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
|
||||
}
|
||||
}else if( pLevel->bRev ){
|
||||
sqlite3VdbeAddOp(v, OP_Last, pLevel->iCur, brk);
|
||||
sqlite3VdbeAddOp(v, OP_Last, iIdxCur, brk);
|
||||
}
|
||||
|
||||
/* Generate the start key. This is the key that defines the lower
|
||||
@ -1327,12 +1265,12 @@ WhereInfo *sqlite3WhereBegin(
|
||||
testOp = OP_IdxLT;
|
||||
}else{
|
||||
int op = geFlag ? OP_MoveGe : OP_MoveGt;
|
||||
sqlite3VdbeAddOp(v, op, pLevel->iCur, brk);
|
||||
sqlite3VdbeAddOp(v, op, iIdxCur, brk);
|
||||
}
|
||||
}else if( pLevel->bRev ){
|
||||
testOp = OP_Noop;
|
||||
}else{
|
||||
sqlite3VdbeAddOp(v, OP_Rewind, pLevel->iCur, brk);
|
||||
sqlite3VdbeAddOp(v, OP_Rewind, iIdxCur, brk);
|
||||
}
|
||||
|
||||
/* Generate the the top of the loop. If there is a termination
|
||||
@ -1342,25 +1280,24 @@ WhereInfo *sqlite3WhereBegin(
|
||||
start = sqlite3VdbeCurrentAddr(v);
|
||||
if( testOp!=OP_Noop ){
|
||||
sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
|
||||
sqlite3VdbeAddOp(v, testOp, pLevel->iCur, brk);
|
||||
sqlite3VdbeAddOp(v, testOp, iIdxCur, brk);
|
||||
if( (leFlag && !pLevel->bRev) || (!geFlag && pLevel->bRev) ){
|
||||
sqlite3VdbeChangeP3(v, -1, "+", P3_STATIC);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_RowKey, pLevel->iCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_RowKey, iIdxCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_IdxIsNull, nEqColumn + ((score&4)!=0), cont);
|
||||
sqlite3VdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
|
||||
if( i==pTabList->nSrc-1 && pushKey ){
|
||||
haveKey = 1;
|
||||
if( omitTable ){
|
||||
haveRowid = 0;
|
||||
}else{
|
||||
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
|
||||
haveKey = 0;
|
||||
sqlite3VdbeAddOp(v, OP_IdxRecno, iIdxCur, 0);
|
||||
haveRowid = 1;
|
||||
}
|
||||
|
||||
/* Record the instruction used to terminate the loop.
|
||||
*/
|
||||
pLevel->op = pLevel->bRev ? OP_Prev : OP_Next;
|
||||
pLevel->p1 = pLevel->iCur;
|
||||
pLevel->p1 = iIdxCur;
|
||||
pLevel->p2 = start;
|
||||
}
|
||||
loopMask |= getMask(&maskSet, iCur);
|
||||
@ -1374,10 +1311,14 @@ WhereInfo *sqlite3WhereBegin(
|
||||
if( pLevel->iLeftJoin && !ExprHasProperty(pTerm->p,EP_FromJoin) ){
|
||||
continue;
|
||||
}
|
||||
if( haveKey ){
|
||||
haveKey = 0;
|
||||
if( haveRowid ){
|
||||
haveRowid = 0;
|
||||
if( omitTable ){
|
||||
sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
|
||||
}else{
|
||||
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
|
||||
}
|
||||
}
|
||||
sqlite3ExprIfFalse(pParse, pTerm->p, cont, 1);
|
||||
pTerm->p = 0;
|
||||
}
|
||||
@ -1394,21 +1335,31 @@ WhereInfo *sqlite3WhereBegin(
|
||||
for(pTerm=aExpr, j=0; j<nExpr; j++, pTerm++){
|
||||
if( pTerm->p==0 ) continue;
|
||||
if( (pTerm->prereqAll & loopMask)!=pTerm->prereqAll ) continue;
|
||||
if( haveKey ){
|
||||
/* Cannot happen. "haveKey" can only be true if pushKey is true
|
||||
if( haveRowid ){
|
||||
/* Cannot happen. "haveRowid" can only be true if pushKey is true
|
||||
** an pushKey can only be true for DELETE and UPDATE and there are
|
||||
** no outer joins with DELETE and UPDATE.
|
||||
*/
|
||||
haveKey = 0;
|
||||
assert( 0 );
|
||||
haveRowid = 0;
|
||||
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
|
||||
}
|
||||
sqlite3ExprIfFalse(pParse, pTerm->p, cont, 1);
|
||||
pTerm->p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( haveRowid && (i<pTabList->nSrc-1 || !pushKey) ){
|
||||
haveRowid = 0;
|
||||
if( omitTable ){
|
||||
sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
|
||||
}else{
|
||||
sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
pWInfo->iContinue = cont;
|
||||
if( pushKey && !haveKey ){
|
||||
if( pushKey && !haveRowid ){
|
||||
sqlite3VdbeAddOp(v, OP_Recno, pTabList->a[0].iCursor, 0);
|
||||
}
|
||||
freeMaskSet(&maskSet);
|
||||
@ -1424,7 +1375,10 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
int i;
|
||||
WhereLevel *pLevel;
|
||||
SrcList *pTabList = pWInfo->pTabList;
|
||||
struct SrcList_item *pTabItem;
|
||||
|
||||
/* Generate loop termination code.
|
||||
*/
|
||||
for(i=pTabList->nSrc-1; i>=0; i--){
|
||||
pLevel = &pWInfo->a[i];
|
||||
sqlite3VdbeResolveLabel(v, pLevel->cont);
|
||||
@ -1438,25 +1392,72 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
if( pLevel->iLeftJoin ){
|
||||
int addr;
|
||||
addr = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iLeftJoin, 0);
|
||||
sqlite3VdbeAddOp(v, OP_NotNull, 1, addr+4 + (pLevel->iCur>=0));
|
||||
sqlite3VdbeAddOp(v, OP_NotNull, 1, addr+4 + (pLevel->iIdxCur>=0));
|
||||
sqlite3VdbeAddOp(v, OP_NullRow, pTabList->a[i].iCursor, 0);
|
||||
if( pLevel->iCur>=0 ){
|
||||
sqlite3VdbeAddOp(v, OP_NullRow, pLevel->iCur, 0);
|
||||
if( pLevel->iIdxCur>=0 ){
|
||||
sqlite3VdbeAddOp(v, OP_NullRow, pLevel->iIdxCur, 0);
|
||||
}
|
||||
sqlite3VdbeAddOp(v, OP_Goto, 0, pLevel->top);
|
||||
}
|
||||
}
|
||||
|
||||
/* The "break" point is here, just past the end of the outer loop.
|
||||
** Set it.
|
||||
*/
|
||||
sqlite3VdbeResolveLabel(v, pWInfo->iBreak);
|
||||
for(i=0; i<pTabList->nSrc; i++){
|
||||
Table *pTab = pTabList->a[i].pTab;
|
||||
|
||||
/* Close all of the cursors
|
||||
*/
|
||||
pLevel = pWInfo->a;
|
||||
pTabItem = pTabList->a;
|
||||
for(i=0; i<pTabList->nSrc; i++, pTabItem++, pLevel++){
|
||||
Table *pTab = pTabItem->pTab;
|
||||
assert( pTab!=0 );
|
||||
if( pTab->isTransient || pTab->pSelect ) continue;
|
||||
pLevel = &pWInfo->a[i];
|
||||
sqlite3VdbeAddOp(v, OP_Close, pTabList->a[i].iCursor, 0);
|
||||
if( (pLevel->score & 1)==0 ){
|
||||
sqlite3VdbeAddOp(v, OP_Close, pTabItem->iCursor, 0);
|
||||
}
|
||||
if( pLevel->pIdx!=0 ){
|
||||
sqlite3VdbeAddOp(v, OP_Close, pLevel->iCur, 0);
|
||||
sqlite3VdbeAddOp(v, OP_Close, pLevel->iIdxCur, 0);
|
||||
}
|
||||
|
||||
/* Make all cursor substitutions for cases where we want to use
|
||||
** just the index and never reference the table.
|
||||
**
|
||||
** Calls to the code generator in between sqlite3WhereBegin and
|
||||
** sqlite3WhereEnd will have created code that references the table
|
||||
** directly. This loop scans all that code looking for opcodes
|
||||
** that reference the table and converts them into opcodes that
|
||||
** reference the index.
|
||||
*/
|
||||
if( pLevel->score & 1 ){
|
||||
int i, j, last;
|
||||
VdbeOp *pOp;
|
||||
Index *pIdx = pLevel->pIdx;
|
||||
|
||||
assert( pIdx!=0 );
|
||||
pOp = sqlite3VdbeGetOp(v, pWInfo->iTop);
|
||||
last = sqlite3VdbeCurrentAddr(v);
|
||||
for(i=pWInfo->iTop; i<last; i++, pOp++){
|
||||
if( pOp->p1!=pLevel->iTabCur ) continue;
|
||||
if( pOp->opcode==OP_Column ){
|
||||
pOp->p1 = pLevel->iIdxCur;
|
||||
for(j=0; j<pIdx->nColumn; j++){
|
||||
if( pOp->p2==pIdx->aiColumn[j] ){
|
||||
pOp->p2 = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else if( pOp->opcode==OP_Recno ){
|
||||
pOp->p1 = pLevel->iIdxCur;
|
||||
pOp->opcode = OP_IdxRecno;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Final cleanup
|
||||
*/
|
||||
sqliteFree(pWInfo);
|
||||
return;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this script is page cache subsystem.
|
||||
#
|
||||
# $Id: collate4.test,v 1.5 2004/11/22 19:12:21 drh Exp $
|
||||
# $Id: collate4.test,v 1.6 2004/12/19 00:11:36 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -351,7 +351,7 @@ do_test collate4-2.1.2 {
|
||||
count {
|
||||
SELECT * FROM collate4t2, collate4t1 WHERE a = b;
|
||||
}
|
||||
} {A a A A 7}
|
||||
} {A a A A 5}
|
||||
do_test collate4-2.1.3 {
|
||||
count {
|
||||
SELECT * FROM collate4t2, collate4t1 WHERE b = a;
|
||||
@ -370,7 +370,7 @@ do_test collate4-2.1.5 {
|
||||
count {
|
||||
SELECT * FROM collate4t2, collate4t1 WHERE b = a;
|
||||
}
|
||||
} {A A 5}
|
||||
} {A A 4}
|
||||
do_test collate4-2.1.6 {
|
||||
count {
|
||||
SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2);
|
||||
@ -384,12 +384,12 @@ do_test collate4-2.1.7 {
|
||||
count {
|
||||
SELECT a FROM collate4t1 WHERE a IN (SELECT * FROM collate4t2);
|
||||
}
|
||||
} {a A 8}
|
||||
} {a A 6}
|
||||
do_test collate4-2.1.8 {
|
||||
count {
|
||||
SELECT a FROM collate4t1 WHERE a IN ('z', 'a');
|
||||
}
|
||||
} {a A 7}
|
||||
} {a A 5}
|
||||
do_test collate4-2.1.9 {
|
||||
execsql {
|
||||
DROP INDEX collate4i1;
|
||||
@ -427,14 +427,14 @@ do_test collate4-2.2.1 {
|
||||
SELECT * FROM collate4t2 NATURAL JOIN collate4t1;
|
||||
}
|
||||
} {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 63}
|
||||
do_test collate4-2.2.1 {
|
||||
do_test collate4-2.2.1b {
|
||||
execsql {
|
||||
CREATE INDEX collate4i1 ON collate4t1(a, b, c);
|
||||
}
|
||||
count {
|
||||
SELECT * FROM collate4t2 NATURAL JOIN collate4t1;
|
||||
}
|
||||
} {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 45}
|
||||
} {0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 29}
|
||||
do_test collate4-2.2.2 {
|
||||
execsql {
|
||||
DROP INDEX collate4i1;
|
||||
|
@ -11,7 +11,7 @@
|
||||
# This file implements regression tests for SQLite library. The
|
||||
# focus of this file is testing the use of indices in WHERE clases.
|
||||
#
|
||||
# $Id: where.test,v 1.25 2004/12/18 18:40:28 drh Exp $
|
||||
# $Id: where.test,v 1.26 2004/12/19 00:11:36 drh Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@ -151,35 +151,35 @@ do_test where-1.29 {
|
||||
|
||||
do_test where-1.30 {
|
||||
count {SELECT w FROM t1 WHERE w>97}
|
||||
} {98 99 100 6}
|
||||
} {98 99 100 3}
|
||||
do_test where-1.31 {
|
||||
count {SELECT w FROM t1 WHERE w>=97}
|
||||
} {97 98 99 100 8}
|
||||
} {97 98 99 100 4}
|
||||
do_test where-1.33 {
|
||||
count {SELECT w FROM t1 WHERE w==97}
|
||||
} {97 3}
|
||||
} {97 2}
|
||||
do_test where-1.34 {
|
||||
count {SELECT w FROM t1 WHERE w+1==98}
|
||||
} {97 99}
|
||||
do_test where-1.35 {
|
||||
count {SELECT w FROM t1 WHERE w<3}
|
||||
} {1 2 4}
|
||||
} {1 2 2}
|
||||
do_test where-1.36 {
|
||||
count {SELECT w FROM t1 WHERE w<=3}
|
||||
} {1 2 3 6}
|
||||
} {1 2 3 3}
|
||||
do_test where-1.37 {
|
||||
count {SELECT w FROM t1 WHERE w+1<=4 ORDER BY w}
|
||||
} {1 2 3 199}
|
||||
} {1 2 3 99}
|
||||
|
||||
do_test where-1.38 {
|
||||
count {SELECT (w) FROM t1 WHERE (w)>(97)}
|
||||
} {98 99 100 6}
|
||||
} {98 99 100 3}
|
||||
do_test where-1.39 {
|
||||
count {SELECT (w) FROM t1 WHERE (w)>=(97)}
|
||||
} {97 98 99 100 8}
|
||||
} {97 98 99 100 4}
|
||||
do_test where-1.40 {
|
||||
count {SELECT (w) FROM t1 WHERE (w)==(97)}
|
||||
} {97 3}
|
||||
} {97 2}
|
||||
do_test where-1.41 {
|
||||
count {SELECT (w) FROM t1 WHERE ((w)+(1))==(98)}
|
||||
} {97 99}
|
||||
@ -237,19 +237,19 @@ do_test where-3.1 {
|
||||
SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C
|
||||
WHERE C.w=101-B.p AND B.r=10202-A.y AND A.w=11
|
||||
}
|
||||
} {11 90 11 9}
|
||||
} {11 90 11 8}
|
||||
do_test where-3.2 {
|
||||
count {
|
||||
SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C
|
||||
WHERE C.w=101-B.p AND B.r=10202-A.y AND A.w=12
|
||||
}
|
||||
} {12 89 12 9}
|
||||
} {12 89 12 8}
|
||||
do_test where-3.3 {
|
||||
count {
|
||||
SELECT A.w, B.p, C.w FROM t1 as A, t2 as B, t1 as C
|
||||
WHERE A.w=15 AND B.p=C.w AND B.r=10202-A.y
|
||||
}
|
||||
} {15 86 86 9}
|
||||
} {15 86 86 8}
|
||||
|
||||
# Test to see that the special case of a constant WHERE clause is
|
||||
# handled.
|
||||
@ -417,7 +417,7 @@ do_test where-6.8 {
|
||||
cksort {
|
||||
SELECT * FROM t3 WHERE a IN (3,5,7,1,9,4,2) ORDER BY a LIMIT 3
|
||||
}
|
||||
} {1 100 4 2 99 9 3 98 16 nosort}
|
||||
} {1 100 4 2 99 9 3 98 16 sort}
|
||||
do_test where-6.9.1 {
|
||||
cksort {
|
||||
SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a LIMIT 3
|
||||
@ -457,12 +457,12 @@ do_test where-6.9.8 {
|
||||
cksort {
|
||||
SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a DESC, c ASC LIMIT 3
|
||||
}
|
||||
} {1 100 4 sort}
|
||||
} {1 100 4 nosort}
|
||||
do_test where-6.9.9 {
|
||||
cksort {
|
||||
SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a ASC, c DESC LIMIT 3
|
||||
}
|
||||
} {1 100 4 sort}
|
||||
} {1 100 4 nosort}
|
||||
do_test where-6.10 {
|
||||
cksort {
|
||||
SELECT * FROM t3 WHERE a=1 AND c>0 ORDER BY a LIMIT 3
|
||||
|
Reference in New Issue
Block a user