#include "sqliteInt.h"
typedef struct ExprInfo ExprInfo;
struct ExprInfo {
Expr *p;
u8 indexable;
short int idxLeft;
short int idxRight;
unsigned prereqLeft;
unsigned prereqRight;
unsigned prereqAll;
};
typedef struct ExprMaskSet ExprMaskSet;
struct ExprMaskSet {
int n;
int ix[31];
};
#define ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))
static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){
int cnt = 0;
if( pExpr==0 || nSlot<1 ) return 0;
if( nSlot==1 || pExpr->op!=TK_AND ){
aSlot[0].p = pExpr;
return 1;
}
if( pExpr->pLeft->op!=TK_AND ){
aSlot[0].p = pExpr->pLeft;
cnt = 1 + exprSplit(nSlot-1, &aSlot[1], pExpr->pRight);
}else{
cnt = exprSplit(nSlot, aSlot, pExpr->pLeft);
cnt += exprSplit(nSlot-cnt, &aSlot[cnt], pExpr->pRight);
}
return cnt;
}
#define initMaskSet(P) memset(P, 0, sizeof(*P))
static int getMask(ExprMaskSet *pMaskSet, int iCursor){
int i;
for(i=0; i<pMaskSet->n; i++){
if( pMaskSet->ix[i]==iCursor ) return 1<<i;
}
if( i==pMaskSet->n && i<ARRAYSIZE(pMaskSet->ix) ){
pMaskSet->n++;
pMaskSet->ix[i] = iCursor;
return 1<<i;
}
return 0;
}
#define freeMaskSet(P)
static int exprTableUsage(ExprMaskSet *pMaskSet, Expr *p){
unsigned int mask = 0;
if( p==0 ) return 0;
if( p->op==TK_COLUMN ){
mask = getMask(pMaskSet, p->iTable);
if( mask==0 ) mask = -1;
return mask;
}
if( p->pRight ){
mask = exprTableUsage(pMaskSet, p->pRight);
}
if( p->pLeft ){
mask |= exprTableUsage(pMaskSet, p->pLeft);
}
if( p->pList ){
int i;
for(i=0; i<p->pList->nExpr; i++){
mask |= exprTableUsage(pMaskSet, p->pList->a[i].pExpr);
}
}
return mask;
}
static int allowedOp(int op){
switch( op ){
case TK_LT:
case TK_LE:
case TK_GT:
case TK_GE:
case TK_EQ:
case TK_IN:
return 1;
default:
return 0;
}
}
static void exprAnalyze(ExprMaskSet *pMaskSet, ExprInfo *pInfo){
Expr *pExpr = pInfo->p;
pInfo->prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
pInfo->prereqRight = exprTableUsage(pMaskSet, pExpr->pRight);
pInfo->prereqAll = exprTableUsage(pMaskSet, pExpr);
pInfo->indexable = 0;
pInfo->idxLeft = -1;
pInfo->idxRight = -1;
if( allowedOp(pExpr->op) && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
if( pExpr->pRight && pExpr->pRight->op==TK_COLUMN ){
pInfo->idxRight = pExpr->pRight->iTable;
pInfo->indexable = 1;
}
if( pExpr->pLeft->op==TK_COLUMN ){
pInfo->idxLeft = pExpr->pLeft->iTable;
pInfo->indexable = 1;
}
}
}
static Index *findSortingIndex(
Table *pTab,
int base,
ExprList *pOrderBy,
Index *pPreferredIdx,
int nEqCol,
int *pbRev
){
int i, j;
Index *pMatch;
Index *pIdx;
int sortOrder;
assert( pOrderBy!=0 );
assert( pOrderBy->nExpr>0 );
sortOrder = pOrderBy->a[0].sortOrder & SQLITE_SO_DIRMASK;
for(i=0; i<pOrderBy->nExpr; i++){
Expr *p;
if( (pOrderBy->a[i].sortOrder & SQLITE_SO_DIRMASK)!=sortOrder ){
return 0;
}
if( (pOrderBy->a[i].sortOrder & SQLITE_SO_TYPEMASK)!=SQLITE_SO_UNK ){
return 0;
}
p = pOrderBy->a[i].pExpr;
if( p->op!=TK_COLUMN || p->iTable!=base ){
return 0;
}
}
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++){
if( pPreferredIdx->aiColumn[i]!=pIdx->aiColumn[i] ) break;
if( j<nExpr && pOrderBy->a[j].pExpr->iColumn==pIdx->aiColumn[i] ){ j++; }
}
if( i<nEqCol ) continue;
for(i=0; i+j<nExpr; i++){
if( pOrderBy->a[i+j].pExpr->iColumn!=pIdx->aiColumn[i+nEqCol] ) break;
}
if( i+j>=nExpr ){
pMatch = pIdx;
if( pIdx==pPreferredIdx ) break;
}
}
if( pMatch && pbRev ){
*pbRev = sortOrder==SQLITE_SO_DESC;
}
return pMatch;
}
static void disableTerm(WhereLevel *pLevel, Expr **ppExpr){
Expr *pExpr = *ppExpr;
if( pLevel->iLeftJoin==0 || ExprHasProperty(pExpr, EP_FromJoin) ){
*ppExpr = 0;
}
}
WhereInfo *sqliteWhereBegin(
Parse *pParse,
SrcList *pTabList,
Expr *pWhere,
int pushKey,
ExprList **ppOrderBy
){
int i;
WhereInfo *pWInfo;
Vdbe *v = pParse->pVdbe;
int brk, cont = 0;
int nExpr;
int loopMask;
int haveKey;
ExprMaskSet maskSet;
int iDirectEq[32];
int iDirectLt[32];
int iDirectGt[32];
ExprInfo aExpr[101];
assert( pushKey==0 || pTabList->nSrc==1 );
initMaskSet(&maskSet);
memset(aExpr, 0, sizeof(aExpr));
nExpr = exprSplit(ARRAYSIZE(aExpr), aExpr, pWhere);
if( nExpr==ARRAYSIZE(aExpr) ){
sqliteErrorMsg(pParse, "WHERE clause too complex - no more "
"than %d terms allowed", (int)ARRAYSIZE(aExpr)-1);
return 0;
}
pWInfo = sqliteMalloc( sizeof(WhereInfo) + pTabList->nSrc*sizeof(WhereLevel));
if( sqlite_malloc_failed ){
sqliteFree(pWInfo);
return 0;
}
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
pWInfo->peakNTab = pWInfo->savedNTab = pParse->nTab;
pWInfo->iBreak = sqliteVdbeMakeLabel(v);
if( pWhere && (pTabList->nSrc==0 || sqliteExprIsConstant(pWhere)) ){
sqliteExprIfFalse(pParse, pWhere, pWInfo->iBreak, 1);
pWhere = 0;
}
for(i=0; i<nExpr; i++){
exprAnalyze(&maskSet, &aExpr[i]);
if( pParse->trigStack ){
int x;
if( (x = pParse->trigStack->newIdx) >= 0 ){
int mask = ~getMask(&maskSet, x);
aExpr[i].prereqRight &= mask;
aExpr[i].prereqLeft &= mask;
aExpr[i].prereqAll &= mask;
}
if( (x = pParse->trigStack->oldIdx) >= 0 ){
int mask = ~getMask(&maskSet, x);
aExpr[i].prereqRight &= mask;
aExpr[i].prereqLeft &= mask;
aExpr[i].prereqAll &= mask;
}
}
}
loopMask = 0;
for(i=0; i<pTabList->nSrc && i<ARRAYSIZE(iDirectEq); i++){
int j;
int iCur = pTabList->a[i].iCursor;
int mask = getMask(&maskSet, iCur);
Table *pTab = pTabList->a[i].pTab;
Index *pIdx;
Index *pBestIdx = 0;
int bestScore = 0;
pWInfo->a[i].iCur = -1;
iDirectEq[i] = -1;
iDirectLt[i] = -1;
iDirectGt[i] = -1;
for(j=0; j<nExpr; j++){
if( aExpr[j].idxLeft==iCur && aExpr[j].p->pLeft->iColumn<0
&& (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){
switch( aExpr[j].p->op ){
case TK_IN:
case TK_EQ: iDirectEq[i] = j; break;
case TK_LE:
case TK_LT: iDirectLt[i] = j; break;
case TK_GE:
case TK_GT: iDirectGt[i] = j; break;
}
}
if( aExpr[j].idxRight==iCur && aExpr[j].p->pRight->iColumn<0
&& (aExpr[j].prereqLeft & loopMask)==aExpr[j].prereqLeft ){
switch( aExpr[j].p->op ){
case TK_EQ: iDirectEq[i] = j; break;
case TK_LE:
case TK_LT: iDirectGt[i] = j; break;
case TK_GE:
case TK_GT: iDirectLt[i] = j; break;
}
}
}
if( iDirectEq[i]>=0 ){
loopMask |= mask;
pWInfo->a[i].pIdx = 0;
continue;
}
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int eqMask = 0;
int ltMask = 0;
int gtMask = 0;
int inMask = 0;
int nEq, m, score;
if( pIdx->nColumn>32 ) continue;
for(j=0; j<nExpr; j++){
if( aExpr[j].idxLeft==iCur
&& (aExpr[j].prereqRight & loopMask)==aExpr[j].prereqRight ){
int iColumn = aExpr[j].p->pLeft->iColumn;
int k;
for(k=0; k<pIdx->nColumn; k++){
if( pIdx->aiColumn[k]==iColumn ){
switch( aExpr[j].p->op ){
case TK_IN: {
if( k==0 ) inMask |= 1;
break;
}
case TK_EQ: {
eqMask |= 1<<k;
break;
}
case TK_LE:
case TK_LT: {
ltMask |= 1<<k;
break;
}
case TK_GE:
case TK_GT: {
gtMask |= 1<<k;
break;
}
default: {
assert( 0 );
break;
}
}
break;
}
}
}
if( aExpr[j].idxRight==iCur
&& (aExpr[j].prereqLeft & loopMask)==aExpr[j].prereqLeft ){
int iColumn = aExpr[j].p->pRight->iColumn;
int k;
for(k=0; k<pIdx->nColumn; k++){
if( pIdx->aiColumn[k]==iColumn ){
switch( aExpr[j].p->op ){
case TK_EQ: {
eqMask |= 1<<k;
break;
}
case TK_LE:
case TK_LT: {
gtMask |= 1<<k;
break;
}
case TK_GE:
case TK_GT: {
ltMask |= 1<<k;
break;
}
default: {
assert( 0 );
break;
}
}
break;
}
}
}
}
for(nEq=0; nEq<pIdx->nColumn; nEq++){
m = (1<<(nEq+1))-1;
if( (m & eqMask)!=m ) break;
}
score = nEq*8;
m = 1<<nEq;
if( m & ltMask ) score++;
if( m & gtMask ) score+=2;
if( score==0 && inMask ) score = 4;
if( score>bestScore ){
pBestIdx = pIdx;
bestScore = score;
}
}
pWInfo->a[i].pIdx = pBestIdx;
pWInfo->a[i].score = bestScore;
pWInfo->a[i].bRev = 0;
loopMask |= mask;
if( pBestIdx ){
pWInfo->a[i].iCur = pParse->nTab++;
pWInfo->peakNTab = pParse->nTab;
}
}
if( ppOrderBy && *ppOrderBy && pTabList->nSrc>0 ){
Index *pSortIdx;
Index *pIdx;
Table *pTab;
int bRev = 0;
pTab = pTabList->a[0].pTab;
pIdx = pWInfo->a[0].pIdx;
if( pIdx && pWInfo->a[0].score==4 ){
pSortIdx = 0;
}else if( iDirectEq[0]>=0 || iDirectLt[0]>=0 || iDirectGt[0]>=0 ){
pSortIdx = 0;
}else{
int nEqCol = (pWInfo->a[0].score+4)/8;
pSortIdx = findSortingIndex(pTab, pTabList->a[0].iCursor,
*ppOrderBy, pIdx, nEqCol, &bRev);
}
if( pSortIdx && (pIdx==0 || pIdx==pSortIdx) ){
if( pIdx==0 ){
pWInfo->a[0].pIdx = pSortIdx;
pWInfo->a[0].iCur = pParse->nTab++;
pWInfo->peakNTab = pParse->nTab;
}
pWInfo->a[0].bRev = bRev;
*ppOrderBy = 0;
}
}
for(i=0; i<pTabList->nSrc; i++){
Table *pTab;
Index *pIx;
pTab = pTabList->a[i].pTab;
if( pTab->isTransient || pTab->pSelect ) continue;
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
sqliteVdbeOp3(v, OP_OpenRead, pTabList->a[i].iCursor, pTab->tnum,
pTab->zName, P3_STATIC);
sqliteCodeVerifySchema(pParse, pTab->iDb);
if( (pIx = pWInfo->a[i].pIdx)!=0 ){
sqliteVdbeAddOp(v, OP_Integer, pIx->iDb, 0);
sqliteVdbeOp3(v, OP_OpenRead, pWInfo->a[i].iCur, pIx->tnum, pIx->zName,0);
}
}
loopMask = 0;
for(i=0; i<pTabList->nSrc; i++){
int j, k;
int iCur = pTabList->a[i].iCursor;
Index *pIdx;
WhereLevel *pLevel = &pWInfo->a[i];
if( i>0 && (pTabList->a[i-1].jointype & JT_LEFT)!=0 ){
if( !pParse->nMem ) pParse->nMem++;
pLevel->iLeftJoin = pParse->nMem++;
sqliteVdbeAddOp(v, OP_String, 0, 0);
sqliteVdbeAddOp(v, OP_MemStore, pLevel->iLeftJoin, 1);
}
pIdx = pLevel->pIdx;
pLevel->inOp = OP_Noop;
if( i<ARRAYSIZE(iDirectEq) && iDirectEq[i]>=0 ){
k = iDirectEq[i];
assert( k<nExpr );
assert( aExpr[k].p!=0 );
assert( aExpr[k].idxLeft==iCur || aExpr[k].idxRight==iCur );
brk = pLevel->brk = sqliteVdbeMakeLabel(v);
if( aExpr[k].idxLeft==iCur ){
Expr *pX = aExpr[k].p;
if( pX->op!=TK_IN ){
sqliteExprCode(pParse, aExpr[k].p->pRight);
}else if( pX->pList ){
sqliteVdbeAddOp(v, OP_SetFirst, pX->iTable, brk);
pLevel->inOp = OP_SetNext;
pLevel->inP1 = pX->iTable;
pLevel->inP2 = sqliteVdbeCurrentAddr(v);
}else{
assert( pX->pSelect );
sqliteVdbeAddOp(v, OP_Rewind, pX->iTable, brk);
sqliteVdbeAddOp(v, OP_KeyAsData, pX->iTable, 1);
pLevel->inP2 = sqliteVdbeAddOp(v, OP_FullKey, pX->iTable, 0);
pLevel->inOp = OP_Next;
pLevel->inP1 = pX->iTable;
}
}else{
sqliteExprCode(pParse, aExpr[k].p->pLeft);
}
disableTerm(pLevel, &aExpr[k].p);
cont = pLevel->cont = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_MustBeInt, 1, brk);
haveKey = 0;
sqliteVdbeAddOp(v, OP_NotExists, iCur, brk);
pLevel->op = OP_Noop;
}else if( pIdx!=0 && pLevel->score>0 && pLevel->score%4==0 ){
int start;
int testOp;
int nColumn = (pLevel->score+4)/8;
brk = pLevel->brk = sqliteVdbeMakeLabel(v);
for(j=0; j<nColumn; j++){
for(k=0; k<nExpr; k++){
Expr *pX = aExpr[k].p;
if( pX==0 ) continue;
if( aExpr[k].idxLeft==iCur
&& (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight
&& pX->pLeft->iColumn==pIdx->aiColumn[j]
){
if( pX->op==TK_EQ ){
sqliteExprCode(pParse, pX->pRight);
disableTerm(pLevel, &aExpr[k].p);
break;
}
if( pX->op==TK_IN && nColumn==1 ){
if( pX->pList ){
sqliteVdbeAddOp(v, OP_SetFirst, pX->iTable, brk);
pLevel->inOp = OP_SetNext;
pLevel->inP1 = pX->iTable;
pLevel->inP2 = sqliteVdbeCurrentAddr(v);
}else{
assert( pX->pSelect );
sqliteVdbeAddOp(v, OP_Rewind, pX->iTable, brk);
sqliteVdbeAddOp(v, OP_KeyAsData, pX->iTable, 1);
pLevel->inP2 = sqliteVdbeAddOp(v, OP_FullKey, pX->iTable, 0);
pLevel->inOp = OP_Next;
pLevel->inP1 = pX->iTable;
}
disableTerm(pLevel, &aExpr[k].p);
break;
}
}
if( aExpr[k].idxRight==iCur
&& aExpr[k].p->op==TK_EQ
&& (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
&& aExpr[k].p->pRight->iColumn==pIdx->aiColumn[j]
){
sqliteExprCode(pParse, aExpr[k].p->pLeft);
disableTerm(pLevel, &aExpr[k].p);
break;
}
}
}
pLevel->iMem = pParse->nMem++;
cont = pLevel->cont = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_NotNull, -nColumn, sqliteVdbeCurrentAddr(v)+3);
sqliteVdbeAddOp(v, OP_Pop, nColumn, 0);
sqliteVdbeAddOp(v, OP_Goto, 0, brk);
sqliteVdbeAddOp(v, OP_MakeKey, nColumn, 0);
sqliteAddIdxKeyType(v, pIdx);
if( nColumn==pIdx->nColumn || pLevel->bRev ){
sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 0);
testOp = OP_IdxGT;
}else{
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
sqliteVdbeAddOp(v, OP_IncrKey, 0, 0);
sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
testOp = OP_IdxGE;
}
if( pLevel->bRev ){
sqliteVdbeAddOp(v, OP_IncrKey, 0, 0);
sqliteVdbeAddOp(v, OP_MoveLt, pLevel->iCur, brk);
start = sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
sqliteVdbeAddOp(v, OP_IdxLT, pLevel->iCur, brk);
pLevel->op = OP_Prev;
}else{
sqliteVdbeAddOp(v, OP_MoveTo, pLevel->iCur, brk);
start = sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
sqliteVdbeAddOp(v, testOp, pLevel->iCur, brk);
pLevel->op = OP_Next;
}
sqliteVdbeAddOp(v, OP_RowKey, pLevel->iCur, 0);
sqliteVdbeAddOp(v, OP_IdxIsNull, nColumn, cont);
sqliteVdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
if( i==pTabList->nSrc-1 && pushKey ){
haveKey = 1;
}else{
sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0);
haveKey = 0;
}
pLevel->p1 = pLevel->iCur;
pLevel->p2 = start;
}else if( i<ARRAYSIZE(iDirectLt) && (iDirectLt[i]>=0 || iDirectGt[i]>=0) ){
int testOp = OP_Noop;
int start;
brk = pLevel->brk = sqliteVdbeMakeLabel(v);
cont = pLevel->cont = sqliteVdbeMakeLabel(v);
if( iDirectGt[i]>=0 ){
k = iDirectGt[i];
assert( k<nExpr );
assert( aExpr[k].p!=0 );
assert( aExpr[k].idxLeft==iCur || aExpr[k].idxRight==iCur );
if( aExpr[k].idxLeft==iCur ){
sqliteExprCode(pParse, aExpr[k].p->pRight);
}else{
sqliteExprCode(pParse, aExpr[k].p->pLeft);
}
sqliteVdbeAddOp(v, OP_ForceInt,
aExpr[k].p->op==TK_LT || aExpr[k].p->op==TK_GT, brk);
sqliteVdbeAddOp(v, OP_MoveTo, iCur, brk);
disableTerm(pLevel, &aExpr[k].p);
}else{
sqliteVdbeAddOp(v, OP_Rewind, iCur, brk);
}
if( iDirectLt[i]>=0 ){
k = iDirectLt[i];
assert( k<nExpr );
assert( aExpr[k].p!=0 );
assert( aExpr[k].idxLeft==iCur || aExpr[k].idxRight==iCur );
if( aExpr[k].idxLeft==iCur ){
sqliteExprCode(pParse, aExpr[k].p->pRight);
}else{
sqliteExprCode(pParse, aExpr[k].p->pLeft);
}
pLevel->iMem = pParse->nMem++;
sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
if( aExpr[k].p->op==TK_LT || aExpr[k].p->op==TK_GT ){
testOp = OP_Ge;
}else{
testOp = OP_Gt;
}
disableTerm(pLevel, &aExpr[k].p);
}
start = sqliteVdbeCurrentAddr(v);
pLevel->op = OP_Next;
pLevel->p1 = iCur;
pLevel->p2 = start;
if( testOp!=OP_Noop ){
sqliteVdbeAddOp(v, OP_Recno, iCur, 0);
sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
sqliteVdbeAddOp(v, testOp, 0, brk);
}
haveKey = 0;
}else if( pIdx==0 ){
int start;
brk = pLevel->brk = sqliteVdbeMakeLabel(v);
cont = pLevel->cont = sqliteVdbeMakeLabel(v);
sqliteVdbeAddOp(v, OP_Rewind, iCur, brk);
start = sqliteVdbeCurrentAddr(v);
pLevel->op = OP_Next;
pLevel->p1 = iCur;
pLevel->p2 = start;
haveKey = 0;
}else{
int score = pLevel->score;
int nEqColumn = score/8;
int start;
int leFlag, geFlag;
int testOp;
for(j=0; j<nEqColumn; j++){
for(k=0; k<nExpr; k++){
if( aExpr[k].p==0 ) continue;
if( aExpr[k].idxLeft==iCur
&& aExpr[k].p->op==TK_EQ
&& (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight
&& aExpr[k].p->pLeft->iColumn==pIdx->aiColumn[j]
){
sqliteExprCode(pParse, aExpr[k].p->pRight);
disableTerm(pLevel, &aExpr[k].p);
break;
}
if( aExpr[k].idxRight==iCur
&& aExpr[k].p->op==TK_EQ
&& (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
&& aExpr[k].p->pRight->iColumn==pIdx->aiColumn[j]
){
sqliteExprCode(pParse, aExpr[k].p->pLeft);
disableTerm(pLevel, &aExpr[k].p);
break;
}
}
}
for(j=0; j<nEqColumn; j++){
sqliteVdbeAddOp(v, OP_Dup, nEqColumn-1, 0);
}
cont = pLevel->cont = sqliteVdbeMakeLabel(v);
brk = pLevel->brk = sqliteVdbeMakeLabel(v);
if( (score & 1)!=0 ){
for(k=0; k<nExpr; k++){
Expr *pExpr = aExpr[k].p;
if( pExpr==0 ) continue;
if( aExpr[k].idxLeft==iCur
&& (pExpr->op==TK_LT || pExpr->op==TK_LE)
&& (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight
&& pExpr->pLeft->iColumn==pIdx->aiColumn[j]
){
sqliteExprCode(pParse, pExpr->pRight);
leFlag = pExpr->op==TK_LE;
disableTerm(pLevel, &aExpr[k].p);
break;
}
if( aExpr[k].idxRight==iCur
&& (pExpr->op==TK_GT || pExpr->op==TK_GE)
&& (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
&& pExpr->pRight->iColumn==pIdx->aiColumn[j]
){
sqliteExprCode(pParse, pExpr->pLeft);
leFlag = pExpr->op==TK_GE;
disableTerm(pLevel, &aExpr[k].p);
break;
}
}
testOp = OP_IdxGE;
}else{
testOp = nEqColumn>0 ? OP_IdxGE : OP_Noop;
leFlag = 1;
}
if( testOp!=OP_Noop ){
int nCol = nEqColumn + (score & 1);
pLevel->iMem = pParse->nMem++;
sqliteVdbeAddOp(v, OP_NotNull, -nCol, sqliteVdbeCurrentAddr(v)+3);
sqliteVdbeAddOp(v, OP_Pop, nCol, 0);
sqliteVdbeAddOp(v, OP_Goto, 0, brk);
sqliteVdbeAddOp(v, OP_MakeKey, nCol, 0);
sqliteAddIdxKeyType(v, pIdx);
if( leFlag ){
sqliteVdbeAddOp(v, OP_IncrKey, 0, 0);
}
if( pLevel->bRev ){
sqliteVdbeAddOp(v, OP_MoveLt, pLevel->iCur, brk);
}else{
sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
}
}else if( pLevel->bRev ){
sqliteVdbeAddOp(v, OP_Last, pLevel->iCur, brk);
}
if( (score & 2)!=0 ){
for(k=0; k<nExpr; k++){
Expr *pExpr = aExpr[k].p;
if( pExpr==0 ) continue;
if( aExpr[k].idxLeft==iCur
&& (pExpr->op==TK_GT || pExpr->op==TK_GE)
&& (aExpr[k].prereqRight & loopMask)==aExpr[k].prereqRight
&& pExpr->pLeft->iColumn==pIdx->aiColumn[j]
){
sqliteExprCode(pParse, pExpr->pRight);
geFlag = pExpr->op==TK_GE;
disableTerm(pLevel, &aExpr[k].p);
break;
}
if( aExpr[k].idxRight==iCur
&& (pExpr->op==TK_LT || pExpr->op==TK_LE)
&& (aExpr[k].prereqLeft & loopMask)==aExpr[k].prereqLeft
&& pExpr->pRight->iColumn==pIdx->aiColumn[j]
){
sqliteExprCode(pParse, pExpr->pLeft);
geFlag = pExpr->op==TK_LE;
disableTerm(pLevel, &aExpr[k].p);
break;
}
}
}else{
geFlag = 1;
}
if( nEqColumn>0 || (score&2)!=0 ){
int nCol = nEqColumn + ((score&2)!=0);
sqliteVdbeAddOp(v, OP_NotNull, -nCol, sqliteVdbeCurrentAddr(v)+3);
sqliteVdbeAddOp(v, OP_Pop, nCol, 0);
sqliteVdbeAddOp(v, OP_Goto, 0, brk);
sqliteVdbeAddOp(v, OP_MakeKey, nCol, 0);
sqliteAddIdxKeyType(v, pIdx);
if( !geFlag ){
sqliteVdbeAddOp(v, OP_IncrKey, 0, 0);
}
if( pLevel->bRev ){
pLevel->iMem = pParse->nMem++;
sqliteVdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
testOp = OP_IdxLT;
}else{
sqliteVdbeAddOp(v, OP_MoveTo, pLevel->iCur, brk);
}
}else if( pLevel->bRev ){
testOp = OP_Noop;
}else{
sqliteVdbeAddOp(v, OP_Rewind, pLevel->iCur, brk);
}
start = sqliteVdbeCurrentAddr(v);
if( testOp!=OP_Noop ){
sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
sqliteVdbeAddOp(v, testOp, pLevel->iCur, brk);
}
sqliteVdbeAddOp(v, OP_RowKey, pLevel->iCur, 0);
sqliteVdbeAddOp(v, OP_IdxIsNull, nEqColumn + (score & 1), cont);
sqliteVdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
if( i==pTabList->nSrc-1 && pushKey ){
haveKey = 1;
}else{
sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0);
haveKey = 0;
}
pLevel->op = pLevel->bRev ? OP_Prev : OP_Next;
pLevel->p1 = pLevel->iCur;
pLevel->p2 = start;
}
loopMask |= getMask(&maskSet, iCur);
for(j=0; j<nExpr; j++){
if( aExpr[j].p==0 ) continue;
if( (aExpr[j].prereqAll & loopMask)!=aExpr[j].prereqAll ) continue;
if( pLevel->iLeftJoin && !ExprHasProperty(aExpr[j].p,EP_FromJoin) ){
continue;
}
if( haveKey ){
haveKey = 0;
sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0);
}
sqliteExprIfFalse(pParse, aExpr[j].p, cont, 1);
aExpr[j].p = 0;
}
brk = cont;
if( pLevel->iLeftJoin ){
pLevel->top = sqliteVdbeCurrentAddr(v);
sqliteVdbeAddOp(v, OP_Integer, 1, 0);
sqliteVdbeAddOp(v, OP_MemStore, pLevel->iLeftJoin, 1);
for(j=0; j<nExpr; j++){
if( aExpr[j].p==0 ) continue;
if( (aExpr[j].prereqAll & loopMask)!=aExpr[j].prereqAll ) continue;
if( haveKey ){
haveKey = 0;
sqliteVdbeAddOp(v, OP_MoveTo, iCur, 0);
}
sqliteExprIfFalse(pParse, aExpr[j].p, cont, 1);
aExpr[j].p = 0;
}
}
}
pWInfo->iContinue = cont;
if( pushKey && !haveKey ){
sqliteVdbeAddOp(v, OP_Recno, pTabList->a[0].iCursor, 0);
}
freeMaskSet(&maskSet);
return pWInfo;
}
void sqliteWhereEnd(WhereInfo *pWInfo){
Vdbe *v = pWInfo->pParse->pVdbe;
int i;
WhereLevel *pLevel;
SrcList *pTabList = pWInfo->pTabList;
for(i=pTabList->nSrc-1; i>=0; i--){
pLevel = &pWInfo->a[i];
sqliteVdbeResolveLabel(v, pLevel->cont);
if( pLevel->op!=OP_Noop ){
sqliteVdbeAddOp(v, pLevel->op, pLevel->p1, pLevel->p2);
}
sqliteVdbeResolveLabel(v, pLevel->brk);
if( pLevel->inOp!=OP_Noop ){
sqliteVdbeAddOp(v, pLevel->inOp, pLevel->inP1, pLevel->inP2);
}
if( pLevel->iLeftJoin ){
int addr;
addr = sqliteVdbeAddOp(v, OP_MemLoad, pLevel->iLeftJoin, 0);
sqliteVdbeAddOp(v, OP_NotNull, 1, addr+4 + (pLevel->iCur>=0));
sqliteVdbeAddOp(v, OP_NullRow, pTabList->a[i].iCursor, 0);
if( pLevel->iCur>=0 ){
sqliteVdbeAddOp(v, OP_NullRow, pLevel->iCur, 0);
}
sqliteVdbeAddOp(v, OP_Goto, 0, pLevel->top);
}
}
sqliteVdbeResolveLabel(v, pWInfo->iBreak);
for(i=0; i<pTabList->nSrc; i++){
Table *pTab = pTabList->a[i].pTab;
assert( pTab!=0 );
if( pTab->isTransient || pTab->pSelect ) continue;
pLevel = &pWInfo->a[i];
sqliteVdbeAddOp(v, OP_Close, pTabList->a[i].iCursor, 0);
if( pLevel->pIdx!=0 ){
sqliteVdbeAddOp(v, OP_Close, pLevel->iCur, 0);
}
}
#if 0
if( pWInfo->pParse->nTab==pWInfo->peakNTab ){
pWInfo->pParse->nTab = pWInfo->savedNTab;
}
#endif
sqliteFree(pWInfo);
return;
}