/ Check-in [086ec2a1]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Work on the UPDATE and INSERT logic. This is an incremental check-in so that can switch over to trunk to work on an unrelated issue there.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | omit-rowid
Files: files | file ages | folders
SHA1:086ec2a177b24ad90d5d705a99d93aa0c1545217
User & Date: drh 2013-10-26 15:40:48
Context
2013-10-28
22:39
Merge recent fixes from trunk. check-in: 9f8191d1 user: drh tags: omit-rowid
2013-10-26
15:40
Work on the UPDATE and INSERT logic. This is an incremental check-in so that can switch over to trunk to work on an unrelated issue there. check-in: 086ec2a1 user: drh tags: omit-rowid
13:36
Replace the OP_IsUnique opcode with OP_NoConflict. This code simplification might be useful to move onto trunk even if this branch is never merged. check-in: e6650e16 user: drh tags: omit-rowid
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/insert.c.

1119
1120
1121
1122
1123
1124
1125




































1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
....
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



1228

1229
1230
1231
1232
1233
1234











1235
1236
1237
1238
1239
1240
1241
....
1305
1306
1307
1308
1309
1310
1311


1312
1313


1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
....
1375
1376
1377
1378
1379
1380
1381
1382
1383
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
....
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
....
1433
1434
1435
1436
1437
1438
1439
1440
1441



1442
1443





















1444
1445
1446
1447
1448
1449
1450
1451
....
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
#ifdef pTrigger
 #undef pTrigger
#endif
#ifdef tmask
 #undef tmask
#endif






































/*
** Generate code to do constraint checks prior to an INSERT or an UPDATE.
**
** The input is a range of consecutive registers as follows:
**
**    1.  The rowid of the row after the update.  (This register
**        contains a NULL for WITHOUT ROWID tables.)
**
**    2.  The data in the first column of the entry after the update.
**
**    i.  Data from middle columns...
**
**    N.  The data in the last column of the entry after the update.
**
................................................................................
*/
void sqlite3GenerateConstraintChecks(
  Parse *pParse,      /* The parser context */
  Table *pTab,        /* the table into which we are inserting */
  int baseCur,        /* A read/write cursor pointing at pTab */
  int regRowid,       /* First register in a range holding values to insert */
  int *aRegIdx,       /* Register used by each index.  0 for unused indices */
  int pkChng,         /* Non-zero if the PRIMARY KEY might collide */
  int isUpdate,       /* True for UPDATE, False for INSERT */
  int overrideError,  /* Override onError to this if not OE_Default */
  int ignoreDest,     /* Jump to this label on an OE_Ignore resolution */
  int *pbMayReplace   /* OUT: Set to true if constraint may cause a replace */
){
  int i;              /* loop counter */
  Vdbe *v;            /* VDBE under constrution */
  int nCol;           /* Number of columns */
  int onError;        /* Conflict resolution strategy */
  int j1;             /* Addresss of jump instruction */
  int j2 = 0, j3;     /* Addresses of jump instructions */
  int regData;        /* Register containing first data column */
  int iCur;           /* Table cursor number */
  Index *pIdx;         /* Pointer to one of the indices */

  sqlite3 *db;         /* Database connection */
  int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
  int regOldRowid = (pkChng && isUpdate) ? pkChng : regRowid;





  db = pParse->db;
  v = sqlite3GetVdbe(pParse);
  assert( v!=0 );
  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
  nCol = pTab->nCol;
  regData = regRowid + 1;












  /* Test all NOT NULL constraints.
  */
  for(i=0; i<nCol; i++){
    if( i==pTab->iPKey ){
      continue;
    }
................................................................................
    }
  }
#endif /* !defined(SQLITE_OMIT_CHECK) */

  /* If we have an INTEGER PRIMARY KEY, make sure the primary key
  ** of the new record does not previously exist.  Except, if this
  ** is an UPDATE and the primary key is not changing, that is OK.


  */
  if( pkChng ){


    onError = pTab->keyConf;
    if( overrideError!=OE_Default ){
      onError = overrideError;
    }else if( onError==OE_Default ){
      onError = OE_Abort;
    }
    
    if( isUpdate ){
      j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, pkChng);
    }
    j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid);
    switch( onError ){
      default: {
        onError = OE_Abort;
        /* Fall thru into the next case */
      }
      case OE_Rollback:
      case OE_Abort:
................................................................................
      }
      case OE_Ignore: {
        assert( seenReplace==0 );
        sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
        break;
      }
    }
    sqlite3VdbeJumpHere(v, j3);
    if( isUpdate ){
      sqlite3VdbeJumpHere(v, j2);
    }
  }

  /* Test all UNIQUE constraints by creating entries for each UNIQUE
  ** index and making sure that duplicate entries do not already exist.
  ** Add the new records to the indices as we go.
  */
  for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
    int regIdx;
    int regR;

    int addrSkipRow = sqlite3VdbeMakeLabel(v);

    if( aRegIdx[iCur]==0 ) continue;  /* Skip unused indices */

    if( pIdx->pPartIdxWhere ){
      sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[iCur]);
      pParse->ckBase = regData;
      sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, addrSkipRow,
                         SQLITE_JUMPIFNULL);
      pParse->ckBase = 0;
    }

    /* Create a key for accessing the index entry */
    regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1);
    for(i=0; i<pIdx->nColumn; i++){
      i16 idx = pIdx->aiColumn[i];
      if( idx<0 || idx==pTab->iPKey ){
        sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
      }else{
        sqlite3VdbeAddOp2(v, OP_SCopy, regData+idx, regIdx+i);
      }
................................................................................
    sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT);
    sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn);

    /* Find out what action to take in case there is an indexing conflict */
    onError = pIdx->onError;
    if( onError==OE_None ){ 
      sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nKeyCol+1);
      sqlite3VdbeResolveLabel(v, addrSkipRow);
      continue;  /* pIdx is not a UNIQUE index */
    }
    if( overrideError!=OE_Default ){
      onError = overrideError;
    }else if( onError==OE_Default ){
      onError = OE_Abort;
    }
................................................................................
    if( seenReplace ){
      if( onError==OE_Ignore ) onError = OE_Replace;
      else if( onError==OE_Fail ) onError = OE_Abort;
    }
    
    /* Check to see if the new index entry will be unique */
    regR = sqlite3GetTempReg(pParse);
    sqlite3VdbeAddOp4Int(v, OP_NoConflict, baseCur+iCur+1, addrSkipRow,
                         regIdx, pIdx->nKeyCol);



    sqlite3VdbeAddOp2(v, OP_IdxRowid, baseCur+iCur+1, regR);
    sqlite3VdbeAddOp3(v, OP_Eq, regR, addrSkipRow, regOldRowid);





















    sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nKeyCol+1);

    /* Generate code that executes if the new index entry is not unique */
    assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
        || onError==OE_Ignore || onError==OE_Replace );
    switch( onError ){
      case OE_Rollback:
      case OE_Abort:
................................................................................
        sqlite3GenerateRowDelete(
            pParse, pTab, pTrigger, baseCur, regR, 0, 0, OE_Replace
        );
        seenReplace = 1;
        break;
      }
    }
    sqlite3VdbeResolveLabel(v, addrSkipRow);
    sqlite3ReleaseTempReg(pParse, regR);
  }
  
  if( pbMayReplace ){
    *pbMayReplace = seenReplace;
  }
}







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






|
|







 







|










<



>


<
>
>
>

>






>
>
>
>
>
>
>
>
>
>
>







 







>
>

|
>
>






|

|

|







 







|
<
<
<




|




>
|






|





|







 







|







 







|

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







 







|







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
....
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256

1257
1258
1259
1260
1261
1262

1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
....
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
....
1429
1430
1431
1432
1433
1434
1435
1436



1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
....
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
....
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
....
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
#ifdef pTrigger
 #undef pTrigger
#endif
#ifdef tmask
 #undef tmask
#endif

/*
** If regFirst is a set of value for a table row in table order and pPk
** is the PRIMARY KEY index for that table, then return the index of the
** first register in a contiguous array of registers that are the primary
** key values for the table row.
**
** For the common cases where the PRIMARY KEY has only a single value or
** where a multi-value PRIMARY KEY is contiguous in table order, this
** routine simply returns a pointer into the regFirst array.  But if there
** is a multi-value PRIMARY KEY with the values out-of-order, this routine
** has to generate code that will copy PRIMARY KEY values into newly
** allocated contiguous registers.
*/
static int sqlite3PrimaryKeyRegisters(Parse *pParse, Index *pPk, int regFirst){
  int i;
  int nKeyCol = pPk->nKeyCol;
  int regPk;
  assert( pParse->pVdbe!=0 );
  if( nKeyCol==1 ){
    return regFirst + pPk->aiColumn[0];
  }
  for(i=1; i<nKeyCol; i++){
    if( pPk->aiColumn[i-1]+1!=pPk->aiColumn[i] ) break;
  }
  if( i==nKeyCol ){
    return regFirst + pPk->aiColumn[0];
  }
  regPk = pParse->nMem+1;
  pParse->nMem += nKeyCol;
  for(i=0; i<nKeyCol; i++){
    int x = pPk->aiColumn[i];
    sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, regFirst+x, regPk+i);
  }
  return regPk;
}


/*
** Generate code to do constraint checks prior to an INSERT or an UPDATE.
**
** The input is a range of consecutive registers as follows:
**
**    1.  The rowid of the row after the update, or NULL
**        for WITHOUT ROWID tables.
**
**    2.  The data in the first column of the entry after the update.
**
**    i.  Data from middle columns...
**
**    N.  The data in the last column of the entry after the update.
**
................................................................................
*/
void sqlite3GenerateConstraintChecks(
  Parse *pParse,      /* The parser context */
  Table *pTab,        /* the table into which we are inserting */
  int baseCur,        /* A read/write cursor pointing at pTab */
  int regRowid,       /* First register in a range holding values to insert */
  int *aRegIdx,       /* Register used by each index.  0 for unused indices */
  int pkChng,         /* Non-zero if the rowid or PRIMARY KEY changed */
  int isUpdate,       /* True for UPDATE, False for INSERT */
  int overrideError,  /* Override onError to this if not OE_Default */
  int ignoreDest,     /* Jump to this label on an OE_Ignore resolution */
  int *pbMayReplace   /* OUT: Set to true if constraint may cause a replace */
){
  int i;              /* loop counter */
  Vdbe *v;            /* VDBE under constrution */
  int nCol;           /* Number of columns */
  int onError;        /* Conflict resolution strategy */
  int j1;             /* Addresss of jump instruction */

  int regData;        /* Register containing first data column */
  int iCur;           /* Table cursor number */
  Index *pIdx;         /* Pointer to one of the indices */
  Index *pPk = 0;      /* The PRIMARY KEY index */
  sqlite3 *db;         /* Database connection */
  int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */

  int regOldPk;        /* Previous rowid or PRIMARY KEY value */
  int regNewPk = 0;    /* New PRIMARY KEY value */
  int pkCur = 0;       /* Cursor used by the PRIMARY KEY */

  regOldPk = (pkChng && isUpdate) ? pkChng : regRowid;
  db = pParse->db;
  v = sqlite3GetVdbe(pParse);
  assert( v!=0 );
  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
  nCol = pTab->nCol;
  regData = regRowid + 1;

  /* For WITHOUT ROWID tables, we'll need to know the Index and the cursor
  ** number for the PRIMARY KEY index */
  if( !HasRowid(pTab) ){
    pkCur = baseCur+1;
    pPk = pTab->pIndex;
    while( ALWAYS(pPk) && pPk->autoIndex!=2 ){
      pPk=pPk->pNext;
      pkCur++;
    }
  }

  /* Test all NOT NULL constraints.
  */
  for(i=0; i<nCol; i++){
    if( i==pTab->iPKey ){
      continue;
    }
................................................................................
    }
  }
#endif /* !defined(SQLITE_OMIT_CHECK) */

  /* If we have an INTEGER PRIMARY KEY, make sure the primary key
  ** of the new record does not previously exist.  Except, if this
  ** is an UPDATE and the primary key is not changing, that is OK.
  **
  ** This block only runs for tables that have a rowid.
  */
  if( pkChng && pkCur==0 ){
    int addrRowidOk = sqlite3VdbeMakeLabel(v);

    onError = pTab->keyConf;
    if( overrideError!=OE_Default ){
      onError = overrideError;
    }else if( onError==OE_Default ){
      onError = OE_Abort;
    }

    if( isUpdate ){
      sqlite3VdbeAddOp3(v, OP_Eq, regRowid, addrRowidOk, pkChng);
    }
    sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, addrRowidOk, regRowid);
    switch( onError ){
      default: {
        onError = OE_Abort;
        /* Fall thru into the next case */
      }
      case OE_Rollback:
      case OE_Abort:
................................................................................
      }
      case OE_Ignore: {
        assert( seenReplace==0 );
        sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
        break;
      }
    }
    sqlite3VdbeResolveLabel(v, addrRowidOk);



  }

  /* Test all UNIQUE constraints by creating entries for each UNIQUE
  ** index and making sure that duplicate entries do not already exist.
  ** Compute the revised record entries for indices as we go.
  */
  for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
    int regIdx;
    int regR;
    int idxCur = baseCur+iCur+1;
    int addrUniqueOk = sqlite3VdbeMakeLabel(v);

    if( aRegIdx[iCur]==0 ) continue;  /* Skip unused indices */

    if( pIdx->pPartIdxWhere ){
      sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[iCur]);
      pParse->ckBase = regData;
      sqlite3ExprIfFalse(pParse, pIdx->pPartIdxWhere, addrUniqueOk,
                         SQLITE_JUMPIFNULL);
      pParse->ckBase = 0;
    }

    /* Create a key for accessing the index entry */
    regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
    for(i=0; i<pIdx->nColumn; i++){
      i16 idx = pIdx->aiColumn[i];
      if( idx<0 || idx==pTab->iPKey ){
        sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
      }else{
        sqlite3VdbeAddOp2(v, OP_SCopy, regData+idx, regIdx+i);
      }
................................................................................
    sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT);
    sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn);

    /* Find out what action to take in case there is an indexing conflict */
    onError = pIdx->onError;
    if( onError==OE_None ){ 
      sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nKeyCol+1);
      sqlite3VdbeResolveLabel(v, addrUniqueOk);
      continue;  /* pIdx is not a UNIQUE index */
    }
    if( overrideError!=OE_Default ){
      onError = overrideError;
    }else if( onError==OE_Default ){
      onError = OE_Abort;
    }
................................................................................
    if( seenReplace ){
      if( onError==OE_Ignore ) onError = OE_Replace;
      else if( onError==OE_Fail ) onError = OE_Abort;
    }
    
    /* Check to see if the new index entry will be unique */
    regR = sqlite3GetTempReg(pParse);
    sqlite3VdbeAddOp4Int(v, OP_NoConflict, idxCur, addrUniqueOk,
                         regIdx, pIdx->nKeyCol);
    if( HasRowid(pTab) ){
      /* Conflict only if the rowid of the existing entry with the matching
      ** key is different from old-rowid */
      sqlite3VdbeAddOp2(v, OP_IdxRowid, idxCur, regR);
      sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldPk);
    }else if( pIdx->autoIndex==2 ){
      /* If there is a matching entry on the PRIMARY KEY index ... */
      int addrPkConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol;
      for(i=0; i<pPk->nKeyCol-1; i++){
        sqlite3VdbeAddOp3(v, OP_Ne,
                          regOldPk+pPk->aiColumn[i], addrPkConflict, regIdx+i);
      }
      sqlite3VdbeAddOp3(v, OP_Eq,
                        regOldPk+pPk->aiColumn[i], addrUniqueOk, regIdx+i);
    }else{
      int addrConflict = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol*2;
      assert( pIdx->nKeyCol + pPk->nKeyCol == pIdx->nColumn );
      for(i=0; i<pPk->nKeyCol-1; i++){
        sqlite3VdbeAddOp3(v, OP_Column, idxCur, pIdx->nKeyCol+i, regR);
        sqlite3VdbeAddOp3(v, OP_Ne,
                          regOldPk+pPk->aiColumn[i], addrConflict, regR);
      }
      sqlite3VdbeAddOp3(v, OP_Column, idxCur, pIdx->nKeyCol+i, regR);
      sqlite3VdbeAddOp3(v, OP_Eq,
                        regOldPk+pPk->aiColumn[i], addrUniqueOk, regR);
    }
    sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);

    /* Generate code that executes if the new index entry is not unique */
    assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
        || onError==OE_Ignore || onError==OE_Replace );
    switch( onError ){
      case OE_Rollback:
      case OE_Abort:
................................................................................
        sqlite3GenerateRowDelete(
            pParse, pTab, pTrigger, baseCur, regR, 0, 0, OE_Replace
        );
        seenReplace = 1;
        break;
      }
    }
    sqlite3VdbeResolveLabel(v, addrUniqueOk);
    sqlite3ReleaseTempReg(pParse, regR);
  }
  
  if( pbMayReplace ){
    *pbMayReplace = seenReplace;
  }
}

Changes to src/vdbe.c.

3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
** rowid P3 then jump immediately to P2.  If P1 does contain a record
** with rowid P3 then leave the cursor pointing at that record and fall
** through to the next instruction.
**
** The OP_NotFound opcode performs the same operation on index btrees
** (with arbitrary multi-value keys).
**
** See also: Found, NotFound, IsUnique
*/
case OP_NotExists: {        /* jump, in3 */
  VdbeCursor *pC;
  BtCursor *pCrsr;
  int res;
  u64 iKey;








|







3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
** rowid P3 then jump immediately to P2.  If P1 does contain a record
** with rowid P3 then leave the cursor pointing at that record and fall
** through to the next instruction.
**
** The OP_NotFound opcode performs the same operation on index btrees
** (with arbitrary multi-value keys).
**
** See also: Found, NotFound, NoConflict
*/
case OP_NotExists: {        /* jump, in3 */
  VdbeCursor *pC;
  BtCursor *pCrsr;
  int res;
  u64 iKey;

Changes to src/vdbeInt.h.

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  sqlite3_vtab_cursor *pVtabCursor;  /* The cursor for a virtual table */
  const sqlite3_module *pModule;     /* Module for cursor pVtabCursor */
  i64 seqCount;         /* Sequence counter */
  i64 movetoTarget;     /* Argument to the deferred sqlite3BtreeMoveto() */
  i64 lastRowid;        /* Last rowid from a Next or NextIdx operation */
  VdbeSorter *pSorter;  /* Sorter object for OP_SorterOpen cursors */

  /* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or 
  ** OP_IsUnique opcode on this cursor. */
  int seekResult;

  /* Cached information about the header for the data record that the
  ** cursor is currently pointing to.  Only valid if cacheStatus matches
  ** Vdbe.cacheCtr.  Vdbe.cacheCtr will never take on the value of
  ** CACHE_STALE and so setting cacheStatus=CACHE_STALE guarantees that
  ** the cache is out of date.







|
<







78
79
80
81
82
83
84
85

86
87
88
89
90
91
92
  sqlite3_vtab_cursor *pVtabCursor;  /* The cursor for a virtual table */
  const sqlite3_module *pModule;     /* Module for cursor pVtabCursor */
  i64 seqCount;         /* Sequence counter */
  i64 movetoTarget;     /* Argument to the deferred sqlite3BtreeMoveto() */
  i64 lastRowid;        /* Last rowid from a Next or NextIdx operation */
  VdbeSorter *pSorter;  /* Sorter object for OP_SorterOpen cursors */

  /* Result of last sqlite3BtreeMoveto() done by an OP_NotExists */

  int seekResult;

  /* Cached information about the header for the data record that the
  ** cursor is currently pointing to.  Only valid if cacheStatus matches
  ** Vdbe.cacheCtr.  Vdbe.cacheCtr will never take on the value of
  ** CACHE_STALE and so setting cacheStatus=CACHE_STALE guarantees that
  ** the cache is out of date.