Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Modify where.c and so on to handle fts scans. Opcodes do not work yet. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
58a5617da3f0663f47b3ee9c07df05f1 |
User & Date: | dan 2012-12-26 19:40:46.706 |
Context
2012-12-27
| ||
18:01 | Fill in some functions so that a tiny subset of fts5 queries work. check-in: fb07003744 user: dan tags: trunk | |
2012-12-26
| ||
19:40 | Modify where.c and so on to handle fts scans. Opcodes do not work yet. check-in: 58a5617da3 user: dan tags: trunk | |
2012-12-24
| ||
15:32 | Fixes for updates and deletes on tables with fts5 indexes. check-in: 8161b13910 user: dan tags: trunk | |
Changes
Changes to src/expr.c.
︙ | ︙ | |||
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 | switch( pExpr->op ){ /* Consider functions to be constant if all their arguments are constant ** and pWalker->u.i==2 */ case TK_FUNCTION: if( pWalker->u.i==2 ) return 0; /* Fall through */ case TK_ID: case TK_COLUMN: case TK_AGG_FUNCTION: case TK_AGG_COLUMN: testcase( pExpr->op==TK_ID ); testcase( pExpr->op==TK_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); testcase( pExpr->op==TK_AGG_COLUMN ); | > | 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 | switch( pExpr->op ){ /* Consider functions to be constant if all their arguments are constant ** and pWalker->u.i==2 */ case TK_FUNCTION: if( pWalker->u.i==2 ) return 0; /* Fall through */ case TK_ID: case TK_MATCH: case TK_COLUMN: case TK_AGG_FUNCTION: case TK_AGG_COLUMN: testcase( pExpr->op==TK_ID ); testcase( pExpr->op==TK_COLUMN ); testcase( pExpr->op==TK_AGG_FUNCTION ); testcase( pExpr->op==TK_AGG_COLUMN ); |
︙ | ︙ | |||
2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 | ){ sqlite4VdbeAddOp1(v, OP_RealAffinity, target); } #endif break; } /* ** Form A: ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END ** ** Form B: ** CASE WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END | > > > > > | 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 | ){ sqlite4VdbeAddOp1(v, OP_RealAffinity, target); } #endif break; } case TK_MATCH: { sqlite4ErrorMsg(pParse, "no index to process MATCH operator"); return 0; } /* ** Form A: ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END ** ** Form B: ** CASE WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END |
︙ | ︙ | |||
3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 | ** later. We might as well just use the original instruction and ** avoid the OP_SCopy. */ static int isAppropriateForFactoring(Expr *p){ if( !sqlite4ExprIsConstantNotJoin(p) ){ return 0; /* Only constant expressions are appropriate for factoring */ } if( (p->flags & EP_FixedDest)==0 ){ return 1; /* Any constant without a fixed destination is appropriate */ } while( p->op==TK_UPLUS ) p = p->pLeft; switch( p->op ){ #ifndef SQLITE4_OMIT_BLOB_LITERAL case TK_BLOB: | > | 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 | ** later. We might as well just use the original instruction and ** avoid the OP_SCopy. */ static int isAppropriateForFactoring(Expr *p){ if( !sqlite4ExprIsConstantNotJoin(p) ){ return 0; /* Only constant expressions are appropriate for factoring */ } if( p->op==TK_MATCH || p->op==TK_TABLE ) return 0; if( (p->flags & EP_FixedDest)==0 ){ return 1; /* Any constant without a fixed destination is appropriate */ } while( p->op==TK_UPLUS ) p = p->pLeft; switch( p->op ){ #ifndef SQLITE4_OMIT_BLOB_LITERAL case TK_BLOB: |
︙ | ︙ |
Changes to src/fts5.c.
︙ | ︙ | |||
142 143 144 145 146 147 148 149 150 151 152 153 154 155 | Fts5ExprNode *pRight; }; struct Fts5Expr { Fts5ExprNode *pRoot; }; /* ** Return true if argument c is one of the special non-whitespace ** characters that ends an unquoted expression token. */ static int fts5IsSpecial(char c){ return (c==':' || c=='(' || c==')' || c=='+' || c=='"'); } | > > > > > > > > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | Fts5ExprNode *pRight; }; struct Fts5Expr { Fts5ExprNode *pRoot; }; /* ** FTS5 specific cursor data. */ struct Fts5Cursor { Fts5Expr *pExpr; /* MATCH expression for this cursor */ }; /* ** Return true if argument c is one of the special non-whitespace ** characters that ends an unquoted expression token. */ static int fts5IsSpecial(char c){ return (c==':' || c=='(' || c==')' || c=='+' || c=='"'); } |
︙ | ︙ | |||
1062 1063 1064 1065 1066 1067 1068 | for(i=0; rc==SQLITE4_OK && i<pInfo->nCol; i++){ sqlite4_value *pArg = (sqlite4_value *)(&aArg[i]); if( pArg->flags & MEM_Str ){ const char *zText; int nText; zText = (const char *)sqlite4_value_text(pArg); | | < | 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 | for(i=0; rc==SQLITE4_OK && i<pInfo->nCol; i++){ sqlite4_value *pArg = (sqlite4_value *)(&aArg[i]); if( pArg->flags & MEM_Str ){ const char *zText; int nText; zText = (const char *)sqlite4_value_text(pArg); nText = sqlite4_value_bytes(pArg); sCtx.iCol = i; rc = pInfo->pTokenizer->xTokenize( &sCtx, pInfo->p, zText, nText, fts5TokenizeCb ); } } nKey = sqlite4VarintLen(pInfo->iRoot) + 2 + sCtx.nMax + nPK; |
︙ | ︙ | |||
1089 1090 1091 1092 1093 1094 1095 | memcpy(&aKey[nKey], zToken, nToken); nKey += nToken; aKey[nKey++] = 0x00; memcpy(&aKey[nKey], pPK, nPK); nKey += nPK; if( bDel ){ | | > | > > > > > > > > > > > > | > > > > > > > > > > > > > > > < | > > > > > > > > > > > > > > > > > | | 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 | memcpy(&aKey[nKey], zToken, nToken); nKey += nToken; aKey[nKey++] = 0x00; memcpy(&aKey[nKey], pPK, nPK); nKey += nPK; if( bDel ){ /* delete key aKey/nKey from the index */ rc = sqlite4KVStoreReplace(pStore, aKey, nKey, 0, -1); }else{ /* Insert a new entry for aKey/nKey into the fts index */ const KVByteArray *aData = (const KVByteArray *)&pTerm[1]; aData += pTerm->nToken; rc = sqlite4KVStoreReplace(pStore, aKey, nKey, aData, pTerm->nData); } } sqlite4DbFree(db, pTerm); } sqlite4DbFree(db, aKey); sqlite4HashClear(&sCtx.hash); return rc; } static Fts5Info *fts5InfoCreate(Parse *pParse, Index *pIdx, int bCol){ sqlite4 *db = pParse->db; Fts5Info *pInfo; /* p4 argument for FtsUpdate opcode */ int nByte; nByte = sizeof(Fts5Info); if( bCol ){ int i; int nCol = pIdx->pTable->nCol; for(i=0; i<nCol; i++){ const char *zCol = pIdx->pTable->aCol[i].zName; nByte += sqlite4Strlen30(zCol) + 1; } nByte += nCol * sizeof(char *); } pInfo = sqlite4DbMallocZero(db, nByte); if( pInfo ){ pInfo->iDb = sqlite4SchemaToIndex(db, pIdx->pSchema); pInfo->iRoot = pIdx->tnum; pInfo->nCol = pIdx->pTable->nCol; fts5TokenizerCreate(pParse, pIdx->pFts, &pInfo->pTokenizer, &pInfo->p); if( pInfo->p==0 ){ assert( pParse->nErr ); sqlite4DbFree(db, pInfo); pInfo = 0; } else if( bCol ){ int i; char *p; int nCol = pIdx->pTable->nCol; pInfo->azCol = (char **)&pInfo[1]; p = (char *)(&pInfo->azCol[nCol]); for(i=0; i<nCol; i++){ const char *zCol = pIdx->pTable->aCol[i].zName; int n = sqlite4Strlen30(zCol) + 1; pInfo->azCol[i] = p; memcpy(p, zCol, n); p += n; } } } return pInfo; } void sqlite4Fts5CodeUpdate( Parse *pParse, Index *pIdx, int iRegPk, int iRegData, int bDel ){ Vdbe *v; Fts5Info *pInfo; /* p4 argument for FtsUpdate opcode */ if( 0==(pInfo = fts5InfoCreate(pParse, pIdx, 0)) ) return; v = sqlite4GetVdbe(pParse); sqlite4VdbeAddOp3(v, OP_FtsUpdate, iRegPk, 0, iRegData); sqlite4VdbeChangeP4(v, -1, (const char *)pInfo, P4_FTS5INFO); sqlite4VdbeChangeP5(v, (u8)bDel); } void sqlite4Fts5CodeQuery( Parse *pParse, Index *pIdx, int iCsr, int iJump, int iRegMatch ){ Vdbe *v; Fts5Info *pInfo; /* p4 argument for FtsOpen opcode */ if( 0==(pInfo = fts5InfoCreate(pParse, pIdx, 1)) ) return; v = sqlite4GetVdbe(pParse); sqlite4VdbeAddOp3(v, OP_FtsOpen, iCsr, iJump, iRegMatch); sqlite4VdbeChangeP4(v, -1, (const char *)pInfo, P4_FTS5INFO); } void sqlite4Fts5FreeInfo(sqlite4 *db, Fts5Info *p){ if( db->pnBytesFreed==0 ){ if( p->p ) p->pTokenizer->xDestroy(p->p); sqlite4DbFree(db, p); } } void sqlite4Fts5CodeCksum( Parse *pParse, Index *pIdx, int iCksum, int iReg, int bIdx /* True for fts index, false for table */ ){ Vdbe *v; Fts5Info *pInfo; /* p4 argument for FtsCksum opcode */ if( 0==(pInfo = fts5InfoCreate(pParse, pIdx, 0)) ) return; v = sqlite4GetVdbe(pParse); sqlite4VdbeAddOp3(v, OP_FtsCksum, iCksum, 0, iReg); sqlite4VdbeChangeP4(v, -1, (const char *)pInfo, P4_FTS5INFO); sqlite4VdbeChangeP5(v, bIdx); } |
︙ | ︙ | |||
1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 | } } *piCksum = sCtx.cksum; return rc; } /************************************************************************** *************************************************************************** ** Below this point is test code. */ #ifdef SQLITE4_TEST static int fts5PrintExprNode(sqlite4 *, const char **, Fts5ExprNode *, char **); | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 | } } *piCksum = sCtx.cksum; return rc; } void sqlite4Fts5Close(sqlite4 *db, Fts5Cursor *pCsr){ if( pCsr ){ fts5ExpressionFree(db, pCsr->pExpr); sqlite4DbFree(db, pCsr); } } int sqlite4Fts5Open( sqlite4 *db, /* Database handle */ Fts5Info *pInfo, /* Index description */ const char *zMatch, /* Match expression */ int bDesc, /* True to iterate in desc. order of PK */ Fts5Cursor **ppCsr, /* OUT: New FTS cursor object */ char **pzErr /* OUT: Error message */ ){ int rc = SQLITE4_OK; Fts5Cursor *pCsr; pCsr = sqlite4DbMallocZero(db, sizeof(Fts5Cursor)); if( !pCsr ){ rc = SQLITE4_NOMEM; }else{ rc = fts5ParseExpression(db, pInfo->pTokenizer, pInfo->p, pInfo->azCol, pInfo->nCol, zMatch, &pCsr->pExpr, pzErr ); } if( rc!=SQLITE4_OK ){ sqlite4Fts5Close(db, pCsr); } return rc; } int sqlite4Fts5Next(sqlite4 *db, Fts5Cursor *pCsr){ return SQLITE4_OK; } /* ** Return true if the cursor passed as the second argument currently points ** to a valid entry, or false otherwise. */ int sqlite4Fts5Valid(Fts5Cursor *pCsr){ return 0; } /************************************************************************** *************************************************************************** ** Below this point is test code. */ #ifdef SQLITE4_TEST static int fts5PrintExprNode(sqlite4 *, const char **, Fts5ExprNode *, char **); |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
843 844 845 846 847 848 849 | {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} %type likeop {struct LikeOp} likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.not = 0;} likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.not = 1;} | | | 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 | {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} expr(A) ::= expr(X) CONCAT(OP) expr(Y). {spanBinaryExpr(&A,pParse,@OP,&X,&Y);} %type likeop {struct LikeOp} likeop(A) ::= LIKE_KW(X). {A.eOperator = X; A.not = 0;} likeop(A) ::= NOT LIKE_KW(X). {A.eOperator = X; A.not = 1;} /* likeop(A) ::= MATCH(X). {A.eOperator = X; A.not = 0;} */ likeop(A) ::= NOT MATCH(X). {A.eOperator = X; A.not = 1;} expr(A) ::= expr(X) likeop(OP) expr(Y). [LIKE_KW] { ExprList *pList; pList = sqlite4ExprListAppend(pParse,0, Y.pExpr); pList = sqlite4ExprListAppend(pParse,pList, X.pExpr); A.pExpr = sqlite4ExprFunction(pParse, pList, &OP.eOperator); if( OP.not ) A.pExpr = sqlite4PExpr(pParse, TK_NOT, A.pExpr, 0, 0); |
︙ | ︙ | |||
866 867 868 869 870 871 872 873 874 875 876 877 878 879 | pList = sqlite4ExprListAppend(pParse,pList, E.pExpr); A.pExpr = sqlite4ExprFunction(pParse, pList, &OP.eOperator); if( OP.not ) A.pExpr = sqlite4PExpr(pParse, TK_NOT, A.pExpr, 0, 0); A.zStart = X.zStart; A.zEnd = E.zEnd; if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc; } %include { /* Construct an expression node for a unary postfix operator */ static void spanUnaryPostfix( ExprSpan *pOut, /* Write the new expression node here */ Parse *pParse, /* Parsing context to record errors */ | > > > > | 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 | pList = sqlite4ExprListAppend(pParse,pList, E.pExpr); A.pExpr = sqlite4ExprFunction(pParse, pList, &OP.eOperator); if( OP.not ) A.pExpr = sqlite4PExpr(pParse, TK_NOT, A.pExpr, 0, 0); A.zStart = X.zStart; A.zEnd = E.zEnd; if( A.pExpr ) A.pExpr->flags |= EP_InfixFunc; } expr(A) ::= expr(L) MATCH expr(R). { spanBinaryExpr(&A, pParse, TK_MATCH, &L, &R); } %include { /* Construct an expression node for a unary postfix operator */ static void spanUnaryPostfix( ExprSpan *pOut, /* Write the new expression node here */ Parse *pParse, /* Parsing context to record errors */ |
︙ | ︙ |
Changes to src/resolve.c.
︙ | ︙ | |||
431 432 433 434 435 436 437 438 439 440 441 442 443 444 | testcase( iCol==BMS ); testcase( iCol==BMS-1 ); pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); ExprSetProperty(p, EP_Resolved); } return p; } /* ** This routine is callback for sqlite4WalkExpr(). ** ** Resolve symbolic names into TK_COLUMN operators for the current ** node in the expression tree. Return 0 to continue the search down ** the tree or 2 to abort the tree walk. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | testcase( iCol==BMS ); testcase( iCol==BMS-1 ); pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); ExprSetProperty(p, EP_Resolved); } return p; } static void resolveMatch(Parse *pParse, NameContext *pNC, Expr *pExpr){ Expr *pLeft = pExpr->pLeft; SrcList *pSrc = pNC->pSrcList; SrcListItem *pItem; char *zLhs; int i; Index *pIdx; if( pLeft->op!=TK_ID || pSrc==0 ){ sqlite4ErrorMsg(pParse, "lhs of MATCH operator must be a table name"); return; } zLhs = pLeft->u.zToken; for(i=0; i<pSrc->nSrc; i++){ pItem = &pSrc->a[i]; if( pItem->zAlias && sqlite4StrICmp(zLhs, pItem->zAlias)==0 ) break; if( pItem->zAlias==0 && sqlite4StrICmp(zLhs, pItem->zName)==0 ) break; } if( i==pSrc->nSrc ){ sqlite4ErrorMsg(pParse, "no such table: %s", zLhs); return; } for(pIdx=pItem->pTab->pIndex; pIdx; pIdx=pIdx->pNext){ if( pIdx->eIndexType==SQLITE4_INDEX_FTS5 ) break; } if( !pIdx ){ sqlite4ErrorMsg(pParse, "no index to process MATCH operator"); return; } pExpr->pLeft = 0; pExpr->pIdx = pIdx; sqlite4ExprDelete(pParse->db, pLeft); } /* ** This routine is callback for sqlite4WalkExpr(). ** ** Resolve symbolic names into TK_COLUMN operators for the current ** node in the expression tree. Return 0 to continue the search down ** the tree or 2 to abort the tree walk. |
︙ | ︙ | |||
611 612 613 614 615 616 617 618 619 620 621 622 623 624 | case TK_VARIABLE: { if( pNC->isCheck ){ sqlite4ErrorMsg(pParse,"parameters prohibited in CHECK constraints"); } break; } #endif } return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; } /* ** pEList is a list of expressions which are really the result set of the ** a SELECT statement. pE is a term in an ORDER BY or GROUP BY clause. | > > > > | 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 | case TK_VARIABLE: { if( pNC->isCheck ){ sqlite4ErrorMsg(pParse,"parameters prohibited in CHECK constraints"); } break; } #endif case TK_MATCH: { resolveMatch(pParse, pNC, pExpr); break; } } return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue; } /* ** pEList is a list of expressions which are really the result set of the ** a SELECT statement. pE is a term in an ORDER BY or GROUP BY clause. |
︙ | ︙ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
561 562 563 564 565 566 567 568 569 570 571 572 573 574 | typedef struct FKey FKey; typedef struct FuncDestructor FuncDestructor; typedef struct FuncDef FuncDef; typedef struct FuncDefTable FuncDefTable; typedef struct Fts5Tokenizer Fts5Tokenizer; typedef struct Fts5Index Fts5Index; typedef struct Fts5Info Fts5Info; typedef struct IdList IdList; typedef struct IdListItem IdListItem; typedef struct Index Index; typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; typedef struct Lookaside Lookaside; | > | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 | typedef struct FKey FKey; typedef struct FuncDestructor FuncDestructor; typedef struct FuncDef FuncDef; typedef struct FuncDefTable FuncDefTable; typedef struct Fts5Tokenizer Fts5Tokenizer; typedef struct Fts5Index Fts5Index; typedef struct Fts5Info Fts5Info; typedef struct Fts5Cursor Fts5Cursor; typedef struct IdList IdList; typedef struct IdListItem IdListItem; typedef struct Index Index; typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; typedef struct Lookaside Lookaside; |
︙ | ︙ | |||
1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 | ** TK_VARIABLE: variable number (always >= 1). */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ u8 flags2; /* Second set of flags. EP2_... */ u8 op2; /* If a TK_REGISTER, the original value of Expr.op */ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ Table *pTab; /* Table for TK_COLUMN expressions. */ #if SQLITE4_MAX_EXPR_DEPTH>0 int nHeight; /* Height of the tree headed by this node */ #endif }; /* ** The following are the meanings of bits in the Expr.flags field. | > | 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 | ** TK_VARIABLE: variable number (always >= 1). */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ u8 flags2; /* Second set of flags. EP2_... */ u8 op2; /* If a TK_REGISTER, the original value of Expr.op */ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ Table *pTab; /* Table for TK_COLUMN expressions. */ Index *pIdx; /* Fts index used by MATCH expressions */ #if SQLITE4_MAX_EXPR_DEPTH>0 int nHeight; /* Height of the tree headed by this node */ #endif }; /* ** The following are the meanings of bits in the Expr.flags field. |
︙ | ︙ | |||
2483 2484 2485 2486 2487 2488 2489 | ** An instance of this structure is used as the p4 argument to some fts5 ** related vdbe opcodes. */ struct Fts5Info { int iDb; /* Database containing this index */ int iRoot; /* Root page number of index */ int nCol; /* Number of columns in indexed table */ | | | 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 | ** An instance of this structure is used as the p4 argument to some fts5 ** related vdbe opcodes. */ struct Fts5Info { int iDb; /* Database containing this index */ int iRoot; /* Root page number of index */ int nCol; /* Number of columns in indexed table */ const char **azCol; /* Column names for table */ Fts5Tokenizer *pTokenizer; /* Tokenizer module */ sqlite4_tokenizer *p; /* Tokenizer instance */ }; int sqlite4WalkExpr(Walker*, Expr*); int sqlite4WalkExprList(Walker*, ExprList*); int sqlite4WalkSelect(Walker*, Select*); |
︙ | ︙ | |||
3269 3270 3271 3272 3273 3274 3275 3276 3277 | void sqlite4Fts5IndexInit(Parse *, Index *, ExprList *); void sqlite4Fts5IndexFree(sqlite4 *, Index *); int sqlite4Fts5Update(sqlite4 *, Fts5Info *, Mem *pPk, Mem *aArg, int, char **); void sqlite4Fts5FreeInfo(sqlite4 *db, Fts5Info *); void sqlite4Fts5CodeUpdate(Parse *, Index *pIdx, int iRegPk, int iRegData, int); void sqlite4Fts5CodeCksum(Parse *, Index *, int, int, int); #endif /* _SQLITEINT_H_ */ | > | 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 | void sqlite4Fts5IndexInit(Parse *, Index *, ExprList *); void sqlite4Fts5IndexFree(sqlite4 *, Index *); int sqlite4Fts5Update(sqlite4 *, Fts5Info *, Mem *pPk, Mem *aArg, int, char **); void sqlite4Fts5FreeInfo(sqlite4 *db, Fts5Info *); void sqlite4Fts5CodeUpdate(Parse *, Index *pIdx, int iRegPk, int iRegData, int); void sqlite4Fts5CodeCksum(Parse *, Index *, int, int, int); void sqlite4Fts5CodeQuery(Parse *, Index *, int, int, int); #endif /* _SQLITEINT_H_ */ |
Changes to src/vdbe.c.
︙ | ︙ | |||
4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 | rc = sqlite4Fts5Update(db, pInfo, pKey, aArg, pOp->p5, &p->zErrMsg); break; } /* ** Opcode: FtsCksum P1 * P3 P4 P5 */ case OP_FtsCksum: { Fts5Info *pInfo; /* Description of fts5 index to update */ Mem *pKey; /* Primary key of row */ Mem *aArg; /* Pointer to array of N values */ | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 | rc = sqlite4Fts5Update(db, pInfo, pKey, aArg, pOp->p5, &p->zErrMsg); break; } /* ** Opcode: FtsCksum P1 * P3 P4 P5 ** ** This opcode is used by the integrity-check procedure that verifies that ** the contents of an fts5 index and its corresponding table match. */ case OP_FtsCksum: { Fts5Info *pInfo; /* Description of fts5 index to update */ Mem *pKey; /* Primary key of row */ Mem *aArg; /* Pointer to array of N values */ i64 cksum; /* Checksum for this row or index entry */ assert( pOp->p4type==P4_FTS5INFO ); pInfo = pOp->p4.pFtsInfo; pOut = &aMem[pOp->p1]; pKey = &aMem[pOp->p3]; aArg = &aMem[pOp->p3+1]; cksum = 0; if( pOp->p5 ){ sqlite4Fts5EntryCksum(db, pInfo, pKey, aArg, &cksum); pOut->u.i = pOut->u.i ^ cksum; }else{ sqlite4Fts5RowCksum(db, pInfo, pKey, aArg, &cksum); pOut->u.i = pOut->u.i ^ cksum; } break; } /* Opcode: FtsOpen P1 P2 P3 P4 P5 ** ** Open an FTS cursor named P1. P4 points to an Fts5Info object. ** ** Register P3 contains the MATCH expression that this cursor will iterate ** through the matches for. P5 is set to 0 to iterate through the results ** in ascending PK order, or 1 for descending PK order. ** ** If the expression matches zero rows, jump to instruction P2. Otherwise, ** leave the cursor pointing at the first match and fall through to the ** next instruction. */ case OP_FtsOpen: { /* jump */ Fts5Info *pInfo; /* Description of fts5 index to update */ char *zErr; VdbeCursor *pCur; char *zMatch; Mem *pMatch; pMatch = &aMem[pOp->p3]; Stringify(pMatch, encoding); zMatch = pMatch->z; assert( pOp->p4type==P4_FTS5INFO ); pInfo = pOp->p4.pFtsInfo; pCur = allocateCursor(p, pOp->p1, 0, pInfo->iDb, 0); if( pCur ){ rc = sqlite4Fts5Open(db, pInfo, zMatch, pOp->p5, &pCur->pFts, &p->zErrMsg); } pc = pOp->p2-1; break; } /* Opcode: FtsNext P1 P2 * * * ** ** Advance FTS cursor P1 to the next entry and jump to instruction P2. Or, ** if there is no next entry, set the cursor to point to EOF and fall through ** to the next instruction. */ case OP_FtsNext: { assert( 0 ); break; } /* Opcode: FtsPk P1 P2 * * * ** ** P1 is an FTS cursor that points to a valid entry (not EOF). Copy the PK ** blob for the current entry to register P2. */ case OP_FtsPk: { assert( 0 ); break; } /* Opcode: Noop * * * * * ** ** Do nothing. This instruction is often useful as a jump ** destination. */ /* |
︙ | ︙ |
Changes to src/vdbeInt.h.
︙ | ︙ | |||
66 67 68 69 70 71 72 73 74 75 76 77 78 79 | Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ sqlite4_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ const sqlite4_module *pModule; /* Module for cursor pVtabCursor */ i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred move-to */ i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */ /* Result of last sqlite4-Moveto() done by an OP_NotExists or ** OP_IsUnique opcode on this cursor. */ int seekResult; }; /* | > | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */ sqlite4_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ const sqlite4_module *pModule; /* Module for cursor pVtabCursor */ i64 seqCount; /* Sequence counter */ i64 movetoTarget; /* Argument to the deferred move-to */ i64 lastRowid; /* Last rowid from a Next or NextIdx operation */ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */ Fts5Cursor *pFts; /* Fts5 cursor object (or NULL) */ /* Result of last sqlite4-Moveto() done by an OP_NotExists or ** OP_IsUnique opcode on this cursor. */ int seekResult; }; /* |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 | if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; *pnRow = nRowEst; WHERETRACE(("IN row estimate: est=%g\n", nRowEst)); } return rc; } #endif /* defined(SQLITE4_ENABLE_STAT3) */ /* ** Find the best query plan for accessing a particular table. Write the ** best query plan and its cost into the WhereCost object supplied as the ** last parameter. ** ** The lowest cost plan wins. The cost is an estimate of the amount of | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 | if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; *pnRow = nRowEst; WHERETRACE(("IN row estimate: est=%g\n", nRowEst)); } return rc; } #endif /* defined(SQLITE4_ENABLE_STAT3) */ /* ** Try to find a MATCH expression that constrains the pTabItem table in the ** WHERE clause. If one exists, set *piTerm to the index in the pWC->a[] array ** and return non-zero. If no such expression exists, return 0. */ static int findMatchExpr( Parse *pParse, WhereClause *pWC, SrcListItem *pTabItem, int *piTerm ){ int i; int iCsr = pTabItem->iCursor; for(i=0; i<pWC->nTerm; i++){ Expr *pMatch = pWC->a[i].pExpr; if( pMatch->iTable==iCsr && pMatch->op==TK_MATCH ) break; } if( i==pWC->nTerm ) return 0; *piTerm = i; return 1; } static int bestMatchIdx( Parse *pParse, WhereClause *pWC, SrcListItem *pTabItem, Bitmask notReady, WhereCost *pCost ){ int iTerm; if( 0==findMatchExpr(pParse, pWC, pTabItem, &iTerm) ) return 0; /* Check that the MATCH expression is not composed using values from any ** tables that are not ready. If it does, return 0. */ if( notReady & pWC->a[iTerm].prereqAll ) return 0; pCost->used = pWC->a[iTerm].prereqAll; pCost->rCost = 1.0; pCost->plan.wsFlags = WHERE_INDEXED; pCost->plan.nEq = 0; pCost->plan.nRow = 10; pCost->plan.u.pIdx = pWC->a[iTerm].pExpr->pIdx; return 1; } /* ** Find the best query plan for accessing a particular table. Write the ** best query plan and its cost into the WhereCost object supplied as the ** last parameter. ** ** The lowest cost plan wins. The cost is an estimate of the amount of |
︙ | ︙ | |||
3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 | */ if( pLevel->iFrom>0 && (pTabItem[0].jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite4VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); VdbeComment((v, "init LEFT JOIN no-match flag")); } #ifndef SQLITE4_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ /* Case 0: The table is a virtual-table. Use the VFilter and VNext ** to access the data. */ int iReg; /* P3 Value for OP_VFilter */ sqlite4_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx; | > > > > > > > > > > > > > > > > > > > > > | 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 | */ if( pLevel->iFrom>0 && (pTabItem[0].jointype & JT_LEFT)!=0 ){ pLevel->iLeftJoin = ++pParse->nMem; sqlite4VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); VdbeComment((v, "init LEFT JOIN no-match flag")); } if( (pLevel->plan.wsFlags & WHERE_INDEXED) && (pLevel->plan.u.pIdx->eIndexType==SQLITE4_INDEX_FTS5) ){ /* Case -1: An FTS query */ int iTerm; int rMatch; int rFree; findMatchExpr(pParse, pWC, pTabItem, &iTerm); rMatch = sqlite4ExprCodeTemp(pParse, pWC->a[iTerm].pExpr->pRight, &rFree); pWC->a[iTerm].wtFlags |= TERM_CODED; sqlite4Fts5CodeQuery(pParse, pLevel->plan.u.pIdx, pLevel->iIdxCur, addrBrk, rMatch ); sqlite4ReleaseTempReg(pParse, rFree); pLevel->p2 = sqlite4VdbeCurrentAddr(v); sqlite4VdbeAddOp3(v, OP_SeekPk, iCur, 0, pLevel->iIdxCur); pLevel->op = OP_FtsNext; pLevel->p1 = pLevel->iIdxCur; }else #ifndef SQLITE4_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ /* Case 0: The table is a virtual-table. Use the VFilter and VNext ** to access the data. */ int iReg; /* P3 Value for OP_VFilter */ sqlite4_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx; |
︙ | ︙ | |||
4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 | pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); pDist = (i==0 ? pDistinct : 0); if( pTabItem->pIndex==0 ) nUnconstrained++; WHERETRACE(("=== trying table %d with isOptimal=%d ===\n", j, isOptimal)); assert( pTabItem->pTab ); #ifndef SQLITE4_OMIT_VIRTUALTABLE if( IsVirtual(pTabItem->pTab) ){ sqlite4_index_info **pp = &pWInfo->a[j].pIdxInfo; bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, &sCost, pp); }else #endif { bestKVIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, pDist, &sCost); } assert( isOptimal || (sCost.used¬Ready)==0 ); /* If an INDEXED BY clause is present, then the plan must use that ** index if it uses any index at all */ assert( pTabItem->pIndex==0 || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 || sCost.plan.u.pIdx==pTabItem->pIndex ); | > > > > > | 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 | pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); pDist = (i==0 ? pDistinct : 0); if( pTabItem->pIndex==0 ) nUnconstrained++; WHERETRACE(("=== trying table %d with isOptimal=%d ===\n", j, isOptimal)); assert( pTabItem->pTab ); if( bestMatchIdx(pParse, pWC, pTabItem, notReady, &sCost) ){ /* no-op */ }else #ifndef SQLITE4_OMIT_VIRTUALTABLE if( IsVirtual(pTabItem->pTab) ){ sqlite4_index_info **pp = &pWInfo->a[j].pIdxInfo; bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, &sCost, pp); }else #endif { bestKVIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy, pDist, &sCost); } assert( isOptimal || (sCost.used¬Ready)==0 ); /* If an INDEXED BY clause is present, then the plan must use that ** index if it uses any index at all */ assert( pTabItem->pIndex==0 || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 || sCost.plan.u.pIdx==pTabItem->pIndex ); |
︙ | ︙ | |||
4955 4956 4957 4958 4959 4960 4961 | constructAutomaticIndex(pParse, pWC, pTabItem, notReady, pLevel); }else #endif if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ Index *pIx = pLevel->plan.u.pIdx; if( pIx->eIndexType==SQLITE4_INDEX_PRIMARYKEY ){ pLevel->iIdxCur = pTabItem->iCursor; | | | 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 | constructAutomaticIndex(pParse, pWC, pTabItem, notReady, pLevel); }else #endif if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){ Index *pIx = pLevel->plan.u.pIdx; if( pIx->eIndexType==SQLITE4_INDEX_PRIMARYKEY ){ pLevel->iIdxCur = pTabItem->iCursor; }else if( pIx->eIndexType!=SQLITE4_INDEX_FTS5 ){ KeyInfo *pKey = sqlite4IndexKeyinfo(pParse, pIx); int iIdxCur = pLevel->iIdxCur; assert( pIx->pSchema==pTab->pSchema ); assert( iIdxCur>=0 ); sqlite4VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb, (char*)pKey, P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIx->zName)); |
︙ | ︙ |
Changes to test/fts5query1.test.
︙ | ︙ | |||
25 26 27 28 29 30 31 | 2 "INSERT INTO t1 VALUES(2, 'b c e', 'A A a')" 3 "INSERT INTO t1 VALUES(3, 'd A A', 'e c a')" 4 "DELETE FROM t1 WHERE a=1" 5 "DELETE FROM t1" 6 "INSERT INTO t1 VALUES(1, 'May you do', 'good and not evil')" 7 "INSERT INTO t1 VALUES(2, 'May you find', 'forgiveness for yourself')" 8 "UPDATE t1 SET b = 'and forgive others' WHERE a = 2" | | > > > > > > > > > > > > > > > > > > > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | 2 "INSERT INTO t1 VALUES(2, 'b c e', 'A A a')" 3 "INSERT INTO t1 VALUES(3, 'd A A', 'e c a')" 4 "DELETE FROM t1 WHERE a=1" 5 "DELETE FROM t1" 6 "INSERT INTO t1 VALUES(1, 'May you do', 'good and not evil')" 7 "INSERT INTO t1 VALUES(2, 'May you find', 'forgiveness for yourself')" 8 "UPDATE t1 SET b = 'and forgive others' WHERE a = 2" 9 "UPDATE t1 SET a = 4, c = 'a b c d' WHERE a = 2" } { do_execsql_test 1.$tn.1 $stmt do_execsql_test 1.$tn.2 {PRAGMA fts_check(i1)} ok } do_execsql_test 2.0 { DROP TABLE t1; CREATE TABLE t1(x PRIMARY KEY, y); CREATE INDEX i1 ON t1 USING fts5(); INSERT INTO t1 VALUES(1, 'o n e'); INSERT INTO t1 VALUES(2, 't w o'); INSERT INTO t1 VALUES(3, 't h r e e'); INSERT INTO t1 VALUES(4, 'f o u r'); } breakpoint explain {SELECT x FROM t1 WHERE t1 MATCH 't'} foreach {tn stmt res} { 1 {SELECT x FROM t1 WHERE t1 MATCH 't'} {2 3} } { do_execsql_test 2.$tn $stmt $res } finish_test |
Changes to test/permutations.test.
︙ | ︙ | |||
210 211 212 213 214 215 216 | tkt-02a8e81d44.test tkt-26ff0c2d1e.test tkt-2d1a5c67d.test tkt-2ea2425d34.test tkt-31338dca7e.test tkt-38cb5df375.test tkt-3998683a16.test tkt-3a77c9714e.test tkt-3fe897352e.test tkt-4a03edc4c8.test tkt-54844eea3f.test tkt-5e10420e8d.test tkt-752e1646fc.test tkt-80ba201079.test tkt-80e031a00f.test tkt-91e2e8ba6f.test tkt-9d68c883.test tkt-b1d3a2e531.test tkt-b351d95f9.test tkt-b72787b1.test | | > | 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | tkt-02a8e81d44.test tkt-26ff0c2d1e.test tkt-2d1a5c67d.test tkt-2ea2425d34.test tkt-31338dca7e.test tkt-38cb5df375.test tkt-3998683a16.test tkt-3a77c9714e.test tkt-3fe897352e.test tkt-4a03edc4c8.test tkt-54844eea3f.test tkt-5e10420e8d.test tkt-752e1646fc.test tkt-80ba201079.test tkt-80e031a00f.test tkt-91e2e8ba6f.test tkt-9d68c883.test tkt-b1d3a2e531.test tkt-b351d95f9.test tkt-b72787b1.test tkt-bd484a090c.test tkt-cbd054fa6b.test tkt-d635236375.test tkt-f973c7ac31.test tkt-fa7bf5ec.test tkt1443.test tkt1444.test tkt1449.test tkt1473.test tkt1501.test tkt1514.test tkt1537.test tkt1873.test tkt2141.test tkt2192.test tkt2213.test tkt2285.test tkt2339.test tkt2391.test tkt2450.test tkt2640.test tkt2767.test tkt2817.test tkt2822.test tkt2832.test tkt2927.test tkt2942.test tkt3121.test tkt3201.test tkt3292.test tkt3298.test tkt3334.test tkt3346.test tkt3419.test tkt3424.test tkt3442.test tkt3461.test tkt3493.test tkt3508.test tkt3522.test tkt3527.test tkt3541.test tkt3554.test tkt3581.test tkt35xx.test tkt3630.test tkt3718.test tkt3761.test tkt3773.test tkt3841.test tkt3871.test tkt3879.test tkt3911.test tkt3918.test tkt3922.test tkt3929.test tkt3935.test tkt3997.test } # tkt-d11f09d36e.test test_suite "veryquick" -prefix "" -description { "Very" quick test suite. Runs in less than 5 minutes on a workstation. This test suite is the same as the "quick" tests, except that some files that test malloc and IO errors are omitted. } -files [ test_set $allquicktests -exclude *malloc* *ioerr* *fault* \ |
︙ | ︙ |