Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -935,10 +935,26 @@ }else if( pPk && iIdxCol<(pIdx->nColumn + pPk->nColumn) ){ iRet = pPk->aiColumn[iIdxCol - pIdx->nColumn]; } return iRet; } + +/* +** Return a pointer to a buffer containing the name of the collation +** sequence used with the iIdxCol'th field in index pIdx, including any +** appended PRIMARY KEY fields. +*/ +static char *idxColumnCollation(Index *pIdx, Index *pPk, int iIdxCol){ + char *zColl; + assert( iIdxCol<(pIdx->nColumn + pPk->nColumn) ); + if( iIdxColnColumn ){ + zColl = pIdx->azColl[iIdxCol]; + }else if( pPk && iIdxCol<(pIdx->nColumn + pPk->nColumn) ){ + zColl = pPk->azColl[iIdxCol - pIdx->nColumn]; + } + return zColl; +} /* ** Return the total number of fields in the index pIdx, including any ** trailing primary key fields. */ @@ -979,11 +995,11 @@ Index *pPk = sqlite4FindPrimaryKey(pIdx->pTable, 0); pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity; for(j=0; idxColumnNumber(pIdx, pPk, j)!=iColumn; j++){ if( NEVER(j>=idxColumnCount(pIdx, pPk)) ) return 0; } - pScan->zCollName = pIdx->azColl[j]; + pScan->zCollName = idxColumnCollation(pIdx, pPk, j); }else{ pScan->idxaff = 0; pScan->zCollName = 0; } pScan->opMask = opMask; @@ -3021,11 +3037,11 @@ pParse->db->mallocFailed = 1; } /* Evaluate the equality constraints */ - assert( pIdx->nColumn>=nEq ); + assert( idxColumnCount(pIdx, sqlite4FindPrimaryKey(pIdx->pTable, 0))>=nEq ); for(j=0; jaLTerm[j]; assert( pTerm!=0 ); /* The following true for indices with redundant columns. @@ -3752,12 +3768,12 @@ SrcList *pOrTab; /* Shortened table list or OR-clause generation */ Index *pCov = 0; /* Potential covering index (or NULL) */ int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */ int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ - int regRowset = 0; /* Register for RowSet object */ - int regRowid = 0; /* Register holding rowid */ + int regKeyset = 0; /* Register for RowSet object */ + int regKey = 0; /* Register holding key */ int iLoopBody = sqlite4VdbeMakeLabel(v); /* Start of loop body */ int iRetInit; /* Address of regReturn init */ int untestedTerms = 0; /* Some terms not completely tested */ int ii; /* Loop counter */ Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ @@ -3790,12 +3806,12 @@ } }else{ pOrTab = pWInfo->pTabList; } - /* Initialize the rowset register to contain NULL. An SQL NULL is - ** equivalent to an empty rowset. + /* Initialize the keyset register to contain NULL. An SQL NULL is + ** equivalent to an empty keyset. ** ** Also initialize regReturn to contain the address of the instruction ** immediately following the OP_Return at the bottom of the loop. This ** is required in a few obscure LEFT JOIN cases where control jumps ** over the top of the loop into the body of it. In this case the @@ -3802,13 +3818,13 @@ ** correct response for the end-of-loop code (the OP_Return) is to ** fall through to the next instruction, just as an OP_Next does if ** called on an uninitialized cursor. */ if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ - regRowset = ++pParse->nMem; - regRowid = ++pParse->nMem; - sqlite4VdbeAddOp2(v, OP_Null, 0, regRowset); + regKeyset = ++pParse->nMem; + regKey = ++pParse->nMem; + sqlite4VdbeAddOp2(v, OP_Null, 0, regKeyset); } iRetInit = sqlite4VdbeAddOp2(v, OP_Integer, 0, regReturn); /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y ** Then for every term xN, evaluate as the subexpression: xN AND z @@ -3858,15 +3874,13 @@ explainOneScan( pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0 ); if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii); - int r; - r = sqlite4ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur, - regRowid); - sqlite4VdbeAddOp4Int(v, OP_RowSetTest, regRowset, - sqlite4VdbeCurrentAddr(v)+2, r, iSet); + sqlite4VdbeAddOp2(v, OP_RowKey, iCur, regKey); + sqlite4VdbeAddOp4Int(v, OP_RowSetTest, regKeyset, + sqlite4VdbeCurrentAddr(v)+2, regKey, iSet); } sqlite4VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody); /* The pSubWInfo->untestedTerms flag means that this OR term ** contained one or more AND term from a notReady table. The @@ -3885,10 +3899,11 @@ ** processed or the index is the same as that used by all previous ** terms, set pCov to the candidate covering index. Otherwise, set ** pCov to NULL to indicate that no candidate covering index will ** be available. */ +#if 0 pSubLoop = pSubWInfo->a[0].pWLoop; assert( (pSubLoop->wsFlags & WHERE_AUTO_INDEX)==0 ); if( (pSubLoop->wsFlags & WHERE_INDEXED)!=0 && (ii==0 || pSubLoop->u.btree.pIndex==pCov) ){ @@ -3895,10 +3910,11 @@ assert( pSubWInfo->a[0].iIdxCur==iCovCur ); pCov = pSubLoop->u.btree.pIndex; }else{ pCov = 0; } +#endif /* Finish the loop through table entries that match term pOrTerm. */ sqlite4WhereEnd(pSubWInfo); } } @@ -4181,10 +4197,12 @@ */ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ WhereLoop **ppPrev, *p, *pNext = 0; WhereInfo *pWInfo = pBuilder->pWInfo; sqlite4 *db = pWInfo->pParse->db; + + assert( pTemplate->u.btree.pIndex || !(pTemplate->wsFlags & WHERE_INDEXED) ); /* If pBuilder->pBest is defined, then only keep track of the single ** best WhereLoop. pBuilder->pBest->maskSelf==0 indicates that no ** prior WhereLoops have been evaluated and that the current pTemplate ** is therefore the first and hence the best and should be retained. @@ -4613,10 +4631,28 @@ rc = whereLoopInsert(pBuilder, pNew); } } } #endif /* ifndef SQLITE4_OMIT_AUTOMATIC_INDEX */ + + /* If this table has no primary key, then it is either a materialized + ** view or ephemeral table. Either way, add an entry for a full-scan + ** of it. */ + if( pPk==0 ){ + assert( pSrc->pTab->pSelect || (pSrc->pTab->tabFlags & TF_Ephemeral) ); + pNew->u.btree.nEq = 0; + pNew->nLTerm = 0; + pNew->iSortIdx = 0; + pNew->rSetup = 0; + pNew->prereq = mExtra; + pNew->nOut = rSize; + pNew->u.btree.pIndex = 0; + pNew->iSortIdx = 0; + pNew->wsFlags = 0; + pNew->rRun = whereCostAdd(rSize,rLogSize) + 16; + rc = whereLoopInsert(pBuilder, pNew); + } /* Loop through the set of indices being considered. */ for(; rc==SQLITE4_OK && pProbe; pProbe=pProbe->pNext, iSortIdx++){ if( pProbe->eIndexType==SQLITE4_INDEX_FTS5 ) continue;