SQLite

Check-in [32e2520ce9]
Login

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

Overview
Comment:Enhance the checkindex.c virtual table so that it will output the index_name and after_key parameters. Also add a new diagnostic output column named scanner_sql which shows the SQL statement used to implement the current index scan.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 32e2520ce91351acceda845d81c9567f7a634257dc2b5b90fe6fb6583d8c0f87
User & Date: drh 2017-11-07 16:23:24.445
Context
2017-11-07
16:54
Add the --trace option to the sqlite3_checker utility program. (check-in: dc217b7cfe user: drh tags: trunk)
16:23
Enhance the checkindex.c virtual table so that it will output the index_name and after_key parameters. Also add a new diagnostic output column named scanner_sql which shows the SQL statement used to implement the current index scan. (check-in: 32e2520ce9 user: drh tags: trunk)
2017-11-06
09:34
Fix a harmless compiler warning from Xcode 9.1. (check-in: 66d98310b9 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/repair/checkindex.c.
10
11
12
13
14
15
16




17
18
19
20
21
22
23
24
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
**
*************************************************************************
*/

#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1





#ifndef SQLITE_AMALGAMATION
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <assert.h>
# define ALWAYS(X)  1
# define NEVER(X)   0
  typedef unsigned char u8;
  typedef unsigned short u16;
  typedef unsigned int u32;
#define get4byte(x) (        \
    ((u32)((x)[0])<<24) +    \
    ((u32)((x)[1])<<16) +    \
    ((u32)((x)[2])<<8) +     \
    ((u32)((x)[3]))          \
)
#endif

typedef struct CidxTable CidxTable;
typedef struct CidxCursor CidxCursor;

struct CidxTable {
  sqlite3_vtab base;              /* Base class.  Must be first */
  sqlite3 *db;
};

struct CidxCursor {
  sqlite3_vtab_cursor base;       /* Base class.  Must be first */
  sqlite3_int64 iRowid;


  sqlite3_stmt *pStmt;
};

typedef struct CidxColumn CidxColumn;
struct CidxColumn {
  char *zExpr;                    /* Text for indexed expression */
  int bDesc;                      /* True for DESC columns, otherwise false */
  int bKey;                       /* Part of index, not PK */







>
>
>
>





<
<
|
|
|


















|
>
>
|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
**
*************************************************************************
*/

#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1

/*
** Stuff that is available inside the amalgamation, but which we need to
** declare ourselves if this module is compiled separately.
*/
#ifndef SQLITE_AMALGAMATION
# include <string.h>
# include <stdio.h>
# include <stdlib.h>
# include <assert.h>


typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
#define get4byte(x) (        \
    ((u32)((x)[0])<<24) +    \
    ((u32)((x)[1])<<16) +    \
    ((u32)((x)[2])<<8) +     \
    ((u32)((x)[3]))          \
)
#endif

typedef struct CidxTable CidxTable;
typedef struct CidxCursor CidxCursor;

struct CidxTable {
  sqlite3_vtab base;              /* Base class.  Must be first */
  sqlite3 *db;
};

struct CidxCursor {
  sqlite3_vtab_cursor base;       /* Base class.  Must be first */
  sqlite3_int64 iRowid;           /* Row number of the output */
  char *zIdxName;                 /* Copy of the index_name parameter */
  char *zAfterKey;                /* Copy of the after_key parameter */
  sqlite3_stmt *pStmt;            /* SQL statement that generates the output */
};

typedef struct CidxColumn CidxColumn;
struct CidxColumn {
  char *zExpr;                    /* Text for indexed expression */
  int bDesc;                      /* True for DESC columns, otherwise false */
  int bKey;                       /* Part of index, not PK */
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

101
102

103
104


105
106
107
108
109
110
111
  va_start(ap, zFmt);
  assert( pCsr->base.pVtab->zErrMsg==0 );
  pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
  va_end(ap);
}

/*
** Connect to then incremental_index_check virtual table.
*/
static int cidxConnect(
  sqlite3 *db,
  void *pAux,
  int argc, const char *const*argv,
  sqlite3_vtab **ppVtab,
  char **pzErr
){
  int rc = SQLITE_OK;
  CidxTable *pRet;

#define IIC_ERRMSG        0
#define IIC_CURRENT_KEY   1
#define IIC_INDEX_NAME    2
#define IIC_AFTER_KEY     3

  rc = sqlite3_declare_vtab(db,
      "CREATE TABLE xyz("

      " errmsg TEXT, current_key TEXT,"
      " index_name HIDDEN, after_key HIDDEN"


      ")"
  );
  pRet = cidxMalloc(&rc, sizeof(CidxTable));
  if( pRet ){
    pRet->db = db;
  }








|















>


>
|
|
>
>







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  va_start(ap, zFmt);
  assert( pCsr->base.pVtab->zErrMsg==0 );
  pCsr->base.pVtab->zErrMsg = sqlite3_vmprintf(zFmt, ap);
  va_end(ap);
}

/*
** Connect to the incremental_index_check virtual table.
*/
static int cidxConnect(
  sqlite3 *db,
  void *pAux,
  int argc, const char *const*argv,
  sqlite3_vtab **ppVtab,
  char **pzErr
){
  int rc = SQLITE_OK;
  CidxTable *pRet;

#define IIC_ERRMSG        0
#define IIC_CURRENT_KEY   1
#define IIC_INDEX_NAME    2
#define IIC_AFTER_KEY     3
#define IIC_SCANNER_SQL   4
  rc = sqlite3_declare_vtab(db,
      "CREATE TABLE xyz("
      " errmsg TEXT,"            /* Error message or NULL if everything is ok */
      " current_key TEXT,"       /* SQLite quote() text of key values */
      " index_name HIDDEN,"      /* IN: name of the index being scanned */
      " after_key HIDDEN,"       /* IN: Start scanning after this key */
      " scanner_sql HIDDEN"      /* debuggingn info: SQL used for scanner */
      ")"
  );
  pRet = cidxMalloc(&rc, sizeof(CidxTable));
  if( pRet ){
    pRet->db = db;
  }

119
120
121
122
123
124
125


126






127
128
129
130
131
132
133
static int cidxDisconnect(sqlite3_vtab *pVtab){
  CidxTable *pTab = (CidxTable*)pVtab;
  sqlite3_free(pTab);
  return SQLITE_OK;
}

/*


** xBestIndex method.






*/
static int cidxBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pInfo){
  int iIdxName = -1;
  int iAfterKey = -1;
  int i;

  for(i=0; i<pInfo->nConstraint; i++){







>
>
|
>
>
>
>
>
>







127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
static int cidxDisconnect(sqlite3_vtab *pVtab){
  CidxTable *pTab = (CidxTable*)pVtab;
  sqlite3_free(pTab);
  return SQLITE_OK;
}

/*
** idxNum and idxStr are not used.  There are only three possible plans,
** which are all distinguished by the number of parameters.
**
**   No parameters:         A degenerate plan.  The result is zero rows.
**   1 Parameter:           Scan all of the index starting with first entry
**   2 parameters:          Scan the index starting after the "after_key".    
**
** Provide successively smaller costs for each of these plans to encourage
** the query planner to select the one with the most parameters.
*/
static int cidxBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pInfo){
  int iIdxName = -1;
  int iAfterKey = -1;
  int i;

  for(i=0; i<pInfo->nConstraint; i++){
175
176
177
178
179
180
181
182

183
184
185
186
187
188
189

/*
** Close a btreeinfo cursor.
*/
static int cidxClose(sqlite3_vtab_cursor *pCursor){
  CidxCursor *pCsr = (CidxCursor*)pCursor;
  sqlite3_finalize(pCsr->pStmt);
  pCsr->pStmt = 0;

  sqlite3_free(pCsr);
  return SQLITE_OK;
}

/*
** Move a btreeinfo cursor to the next entry in the file.
*/







|
>







191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

/*
** Close a btreeinfo cursor.
*/
static int cidxClose(sqlite3_vtab_cursor *pCursor){
  CidxCursor *pCsr = (CidxCursor*)pCursor;
  sqlite3_finalize(pCsr->pStmt);
  sqlite3_free(pCsr->zIdxName);
  sqlite3_free(pCsr->zAfterKey);
  sqlite3_free(pCsr);
  return SQLITE_OK;
}

/*
** Move a btreeinfo cursor to the next entry in the file.
*/
645
646
647
648
649
650
651













































































652
653
654
655
656
657
658
659
660
661
662
663
664
665







666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759

760
761
762
763
764
765
766
767
768
769


770
771

772















773
774
775
776
777

778
779
780
781
782
783
784
785
          break;
      }
    }
  }

  return zRet;
}














































































/* 
** Position a cursor back to the beginning.
*/
static int cidxFilter(
  sqlite3_vtab_cursor *pCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  int rc = SQLITE_OK;
  CidxCursor *pCsr = (CidxCursor*)pCursor;
  const char *zIdxName = 0;
  const char *zAfterKey = 0;








  if( argc>0 ){
    zIdxName = (const char*)sqlite3_value_text(argv[0]);
    if( argc>1 ){
      zAfterKey = (const char*)sqlite3_value_text(argv[1]);
    }
  }

  if( zIdxName ){
    char *zTab = 0;
    char *zCurrentKey = 0;
    char *zOrderBy = 0;
    char *zSubWhere = 0;
    char *zSubExpr = 0;
    char *zSrcList = 0;

    char **azAfter = 0;
    CidxIndex *pIdx = 0;

    rc = cidxLookupIndex(pCsr, zIdxName, &pIdx, &zTab);

    zOrderBy = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ORDERBY);
    zCurrentKey = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_CURRENT_KEY);
    zSubWhere = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBWHERE);
    zSubExpr = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBEXPR);
    zSrcList = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ALL);

    if( rc==SQLITE_OK && zAfterKey ){
      rc = cidxDecodeAfter(pCsr, pIdx->nCol, zAfterKey, &azAfter);
    }

    if( rc || zAfterKey==0 ){
      pCsr->pStmt = cidxPrepare(&rc, pCsr, 
          "SELECT (SELECT %s FROM %Q AS t WHERE %s), %s "
          "FROM (SELECT %s FROM %Q ORDER BY %s) AS i",
          zSubExpr, zTab, zSubWhere, zCurrentKey, 
          zSrcList, zTab, zOrderBy
      );
      /* printf("SQL: %s\n", sqlite3_sql(pCsr->pStmt)); */
    }else{
      const char *zSep = "";
      char *zSql;
      int i;

      zSql = cidxMprintf(&rc, 
          "SELECT (SELECT %s FROM %Q WHERE %s), %s FROM (",
          zSubExpr, zTab, zSubWhere, zCurrentKey
      );
      for(i=pIdx->nCol-1; i>=0; i--){
        int j;
        if( pIdx->aCol[i].bDesc && azAfter[i]==0 ) continue;
        for(j=0; j<2; j++){
          char *zWhere = cidxWhere(&rc, pIdx->aCol, azAfter, i, j);
          zSql = cidxMprintf(&rc, "%z"
              "%sSELECT * FROM (SELECT %s FROM %Q WHERE %z ORDER BY %s)",
              zSql, zSep, zSrcList, zTab, zWhere, zOrderBy
          );
          zSep = " UNION ALL ";
          if( pIdx->aCol[i].bDesc==0 ) break;
        }
      }
      zSql = cidxMprintf(&rc, "%z) AS i", zSql);

      /* printf("SQL: %s\n", zSql); */
      pCsr->pStmt = cidxPrepare(&rc, pCsr, "%z", zSql);
    }

    sqlite3_free(zTab);
    sqlite3_free(zCurrentKey);
    sqlite3_free(zOrderBy);
    sqlite3_free(zSubWhere);
    sqlite3_free(zSubExpr);
    sqlite3_free(zSrcList);
    cidxFreeIndex(pIdx);
    sqlite3_free(azAfter);
  }

  if( pCsr->pStmt ){
    assert( rc==SQLITE_OK );
    rc = cidxNext(pCursor);
  }
  pCsr->iRowid = 1;
  return rc;
}

/* 
** Return a column value.
*/
static int cidxColumn(
  sqlite3_vtab_cursor *pCursor, 
  sqlite3_context *ctx, 
  int iCol
){
  CidxCursor *pCsr = (CidxCursor*)pCursor;
  assert( iCol>=IIC_ERRMSG && iCol<=IIC_AFTER_KEY );

  if( iCol==IIC_ERRMSG ){
    const char *zVal = 0;
    if( sqlite3_column_type(pCsr->pStmt, 0)==SQLITE_INTEGER ){
      if( sqlite3_column_int(pCsr->pStmt, 0)==0 ){
        zVal = "row data mismatch";
      }
    }else{
      zVal = "row missing";
    }
    sqlite3_result_text(ctx, zVal, -1, SQLITE_STATIC);


  }else if( iCol==IIC_CURRENT_KEY ){
    sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, 1));

  }















  return SQLITE_OK;
}

/* Return the ROWID for the sqlite_btreeinfo table */
static int cidxRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){

  *pRowid = 0;
  return SQLITE_OK;
}

/*
** Register the virtual table modules with the database handle passed
** as the only argument.
*/







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














>
>
>
>
>
>
>








<
<
<
|
<
<
|
<
<
|
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


<
<
<
<
<
<
<
<
<



















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





>
|







662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774



775


776


777

778




















779






















780
781









782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
          break;
      }
    }
  }

  return zRet;
}

/*
** Generate SQL (in memory obtained from sqlite3_malloc()) that will
** continue the index scan for zIdxName starting after zAfterKey.
*/
int cidxGenerateScanSql(
  CidxCursor *pCsr,           /* The cursor which needs the new statement */
  const char *zIdxName,       /* index to be scanned */
  const char *zAfterKey,      /* start after this key, if not NULL */
  char **pzSqlOut             /* OUT: Write the generated SQL here */
){
  int rc;
  char *zTab = 0;
  char *zCurrentKey = 0;
  char *zOrderBy = 0;
  char *zSubWhere = 0;
  char *zSubExpr = 0;
  char *zSrcList = 0;
  char **azAfter = 0;
  CidxIndex *pIdx = 0;

  *pzSqlOut = 0;
  rc = cidxLookupIndex(pCsr, zIdxName, &pIdx, &zTab);

  zOrderBy = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ORDERBY);
  zCurrentKey = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_CURRENT_KEY);
  zSubWhere = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBWHERE);
  zSubExpr = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_SUBEXPR);
  zSrcList = cidxColumnList(&rc, zIdxName, pIdx, CIDX_CLIST_ALL);

  if( rc==SQLITE_OK && zAfterKey ){
    rc = cidxDecodeAfter(pCsr, pIdx->nCol, zAfterKey, &azAfter);
  }

  if( rc || zAfterKey==0 ){
    *pzSqlOut = cidxMprintf(&rc,
        "SELECT (SELECT %s FROM %Q AS t WHERE %s), %s "
        "FROM (SELECT %s FROM %Q ORDER BY %s) AS i",
        zSubExpr, zTab, zSubWhere, zCurrentKey, 
        zSrcList, zTab, zOrderBy
    );
  }else{
    const char *zSep = "";
    char *zSql;
    int i;

    zSql = cidxMprintf(&rc, 
        "SELECT (SELECT %s FROM %Q WHERE %s), %s FROM (",
        zSubExpr, zTab, zSubWhere, zCurrentKey
    );
    for(i=pIdx->nCol-1; i>=0; i--){
      int j;
      if( pIdx->aCol[i].bDesc && azAfter[i]==0 ) continue;
      for(j=0; j<2; j++){
        char *zWhere = cidxWhere(&rc, pIdx->aCol, azAfter, i, j);
        zSql = cidxMprintf(&rc, "%z"
            "%sSELECT * FROM (SELECT %s FROM %Q WHERE %z ORDER BY %s)",
            zSql, zSep, zSrcList, zTab, zWhere, zOrderBy
        );
        zSep = " UNION ALL ";
        if( pIdx->aCol[i].bDesc==0 ) break;
      }
    }
    *pzSqlOut = cidxMprintf(&rc, "%z) AS i", zSql);
  }

  sqlite3_free(zTab);
  sqlite3_free(zCurrentKey);
  sqlite3_free(zOrderBy);
  sqlite3_free(zSubWhere);
  sqlite3_free(zSubExpr);
  sqlite3_free(zSrcList);
  cidxFreeIndex(pIdx);
  sqlite3_free(azAfter);
  return rc;
}


/* 
** Position a cursor back to the beginning.
*/
static int cidxFilter(
  sqlite3_vtab_cursor *pCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  int rc = SQLITE_OK;
  CidxCursor *pCsr = (CidxCursor*)pCursor;
  const char *zIdxName = 0;
  const char *zAfterKey = 0;

  sqlite3_free(pCsr->zIdxName);
  pCsr->zIdxName = 0;
  sqlite3_free(pCsr->zAfterKey);
  pCsr->zAfterKey = 0;
  sqlite3_finalize(pCsr->pStmt);
  pCsr->pStmt = 0;

  if( argc>0 ){
    zIdxName = (const char*)sqlite3_value_text(argv[0]);
    if( argc>1 ){
      zAfterKey = (const char*)sqlite3_value_text(argv[1]);
    }
  }

  if( zIdxName ){



    char *zSql = 0;


    pCsr->zIdxName = sqlite3_mprintf("%s", zIdxName);


    pCsr->zAfterKey = zAfterKey ? sqlite3_mprintf("%s", zAfterKey) : 0;

    rc = cidxGenerateScanSql(pCsr, zIdxName, zAfterKey, &zSql);




















    if( zSql ){






















      pCsr->pStmt = cidxPrepare(&rc, pCsr, "%z", zSql);
    }









  }

  if( pCsr->pStmt ){
    assert( rc==SQLITE_OK );
    rc = cidxNext(pCursor);
  }
  pCsr->iRowid = 1;
  return rc;
}

/* 
** Return a column value.
*/
static int cidxColumn(
  sqlite3_vtab_cursor *pCursor, 
  sqlite3_context *ctx, 
  int iCol
){
  CidxCursor *pCsr = (CidxCursor*)pCursor;
  assert( iCol>=IIC_ERRMSG && iCol<=IIC_SCANNER_SQL );
  switch( iCol ){
    case IIC_ERRMSG: {
      const char *zVal = 0;
      if( sqlite3_column_type(pCsr->pStmt, 0)==SQLITE_INTEGER ){
        if( sqlite3_column_int(pCsr->pStmt, 0)==0 ){
          zVal = "row data mismatch";
        }
      }else{
        zVal = "row missing";
      }
      sqlite3_result_text(ctx, zVal, -1, SQLITE_STATIC);
      break;
    }
    case IIC_CURRENT_KEY: {
      sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, 1));
      break;
    }
    case IIC_INDEX_NAME: {
      sqlite3_result_text(ctx, pCsr->zIdxName, -1, SQLITE_TRANSIENT);
      break;
    }
    case IIC_AFTER_KEY: {
      sqlite3_result_text(ctx, pCsr->zAfterKey, -1, SQLITE_TRANSIENT);
      break;
    }
    case IIC_SCANNER_SQL: {
      char *zSql = 0;
      cidxGenerateScanSql(pCsr, pCsr->zIdxName, pCsr->zAfterKey, &zSql);
      sqlite3_result_text(ctx, zSql, -1, sqlite3_free);
      break;
    }
  }
  return SQLITE_OK;
}

/* Return the ROWID for the sqlite_btreeinfo table */
static int cidxRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
  CidxCursor *pCsr = (CidxCursor*)pCursor;
  *pRowid = pCsr->iRowid;
  return SQLITE_OK;
}

/*
** Register the virtual table modules with the database handle passed
** as the only argument.
*/
Changes to ext/repair/test/checkindex01.test.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53










54
55
56
57
58
59
60

  uplevel [list do_test $tn.2 "incr_index_check $idx 1" [list {*}$res]]
  uplevel [list do_test $tn.3 "incr_index_check $idx 2" [list {*}$res]]
  uplevel [list do_test $tn.4 "incr_index_check $idx 5" [list {*}$res]]
}


do_execsql_test 1.2 {
  SELECT errmsg IS NULL, current_key FROM incremental_index_check('i1');
} {
  1 'five',5
  1 'four',4
  1 'one',1
  1 'three',3
  1 'two',2










}

do_index_check_test 1.3 i1 {
  {} 'five',5
  {} 'four',4
  {} 'one',1
  {} 'three',3







|
|

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







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

  uplevel [list do_test $tn.2 "incr_index_check $idx 1" [list {*}$res]]
  uplevel [list do_test $tn.3 "incr_index_check $idx 2" [list {*}$res]]
  uplevel [list do_test $tn.4 "incr_index_check $idx 5" [list {*}$res]]
}


do_execsql_test 1.2.1 {
  SELECT rowid, errmsg IS NULL, current_key FROM incremental_index_check('i1');
} {
  1 1 'five',5
  2 1 'four',4
  3 1 'one',1
  4 1 'three',3
  5 1 'two',2
}
do_execsql_test 1.2.2 {
  SELECT errmsg IS NULL, current_key, index_name, after_key, scanner_sql
    FROM incremental_index_check('i1') LIMIT 1;
} {
  1
  'five',5
  i1
  {}
  {SELECT (SELECT a IS i.i0 FROM 't1' AS t WHERE "rowid" COLLATE BINARY IS i.i1), quote(i0)||','||quote(i1) FROM (SELECT (a) AS i0, ("rowid" COLLATE BINARY) AS i1 FROM 't1' ORDER BY 1,2) AS i}
}

do_index_check_test 1.3 i1 {
  {} 'five',5
  {} 'four',4
  {} 'one',1
  {} 'three',3