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: 58a5617da3f0663f47b3ee9c07df05f1574bc9b9
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
Unified Diff Ignore Whitespace Patch
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
1069
1070
1071
1072
1073
1074
1075
1076
1077
  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;







|
<







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
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
      memcpy(&aKey[nKey], zToken, nToken);
      nKey += nToken;
      aKey[nKey++] = 0x00;
      memcpy(&aKey[nKey], pPK, nPK);
      nKey += nPK;

      if( bDel ){
        /* delete key aKey/nKey... */
        rc = sqlite4KVStoreReplace(pStore, aKey, nKey, 0, -1);
      }else{

        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){
  sqlite4 *db = pParse->db;
  Fts5Info *pInfo;                /* p4 argument for FtsUpdate opcode */













  pInfo = sqlite4DbMallocZero(db, sizeof(Fts5Info));
  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;
    }















  }

  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)) ) return;

  v = sqlite4GetVdbe(pParse);
  sqlite4VdbeAddOp3(v, OP_FtsUpdate, iRegPk, 0, iRegData);
  sqlite4VdbeChangeP4(v, -1, (const char *)pInfo, P4_FTS5INFO);
  sqlite4VdbeChangeP5(v, (u8)bDel);
}


















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)) ) return;

  v = sqlite4GetVdbe(pParse);
  sqlite4VdbeAddOp3(v, OP_FtsCksum, iCksum, 0, iReg);
  sqlite4VdbeChangeP4(v, -1, (const char *)pInfo, P4_FTS5INFO);
  sqlite4VdbeChangeP5(v, bIdx);
}








|


>













|


>

>
>
>
>
>
>
>
>
>
>
>
|











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




<











|






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


















|







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
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);







|







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
2490
2491
2492
2493
2494
2495
2496
2497
** 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 */
  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*);







|







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
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

  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 */
  i64 cksum;

  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: Noop * * * * *
**
** Do nothing.  This instruction is often useful as a jump
** destination.
*/
/*







>
>
>





|



















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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&notReady)==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&notReady)==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
4962
4963
4964
4965
4966
4967
4968
4969
      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{
        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));







|







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
32
33
34
35
36
37



















38

  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 AND 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
}




















finish_test








|





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
  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-d11f09d36e.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
}


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*    \







|













>







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*    \