4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 *************************************************************************
14 #include "sqlite3ext.h"
15 SQLITE_EXTENSION_INIT1
18 ** Stuff that is available inside the amalgamation, but which we need to
19 ** declare ourselves if this module is compiled separately.
21 #ifndef SQLITE_AMALGAMATION
26 typedef unsigned char u8
;
27 typedef unsigned short u16
;
28 typedef unsigned int u32
;
29 #define get4byte(x) ( \
30 ((u32)((x)[0])<<24) + \
31 ((u32)((x)[1])<<16) + \
32 ((u32)((x)[2])<<8) + \
37 typedef struct CidxTable CidxTable
;
38 typedef struct CidxCursor CidxCursor
;
41 sqlite3_vtab base
; /* Base class. Must be first */
46 sqlite3_vtab_cursor base
; /* Base class. Must be first */
47 sqlite3_int64 iRowid
; /* Row number of the output */
48 char *zIdxName
; /* Copy of the index_name parameter */
49 char *zAfterKey
; /* Copy of the after_key parameter */
50 sqlite3_stmt
*pStmt
; /* SQL statement that generates the output */
53 typedef struct CidxColumn CidxColumn
;
55 char *zExpr
; /* Text for indexed expression */
56 int bDesc
; /* True for DESC columns, otherwise false */
57 int bKey
; /* Part of index, not PK */
60 typedef struct CidxIndex CidxIndex
;
62 char *zWhere
; /* WHERE clause, if any */
63 int nCol
; /* Elements in aCol[] array */
64 CidxColumn aCol
[1]; /* Array of indexed columns */
67 static void *cidxMalloc(int *pRc
, int n
){
70 if( *pRc
==SQLITE_OK
){
71 pRet
= sqlite3_malloc(n
);
81 static void cidxCursorError(CidxCursor
*pCsr
, const char *zFmt
, ...){
84 assert( pCsr
->base
.pVtab
->zErrMsg
==0 );
85 pCsr
->base
.pVtab
->zErrMsg
= sqlite3_vmprintf(zFmt
, ap
);
90 ** Connect to the incremental_index_check virtual table.
92 static int cidxConnect(
95 int argc
, const char *const*argv
,
96 sqlite3_vtab
**ppVtab
,
103 #define IIC_CURRENT_KEY 1
104 #define IIC_INDEX_NAME 2
105 #define IIC_AFTER_KEY 3
106 #define IIC_SCANNER_SQL 4
107 rc
= sqlite3_declare_vtab(db
,
109 " errmsg TEXT," /* Error message or NULL if everything is ok */
110 " current_key TEXT," /* SQLite quote() text of key values */
111 " index_name HIDDEN," /* IN: name of the index being scanned */
112 " after_key HIDDEN," /* IN: Start scanning after this key */
113 " scanner_sql HIDDEN" /* debuggingn info: SQL used for scanner */
116 pRet
= cidxMalloc(&rc
, sizeof(CidxTable
));
121 *ppVtab
= (sqlite3_vtab
*)pRet
;
126 ** Disconnect from or destroy an incremental_index_check virtual table.
128 static int cidxDisconnect(sqlite3_vtab
*pVtab
){
129 CidxTable
*pTab
= (CidxTable
*)pVtab
;
135 ** idxNum and idxStr are not used. There are only three possible plans,
136 ** which are all distinguished by the number of parameters.
138 ** No parameters: A degenerate plan. The result is zero rows.
139 ** 1 Parameter: Scan all of the index starting with first entry
140 ** 2 parameters: Scan the index starting after the "after_key".
142 ** Provide successively smaller costs for each of these plans to encourage
143 ** the query planner to select the one with the most parameters.
145 static int cidxBestIndex(sqlite3_vtab
*tab
, sqlite3_index_info
*pInfo
){
150 for(i
=0; i
<pInfo
->nConstraint
; i
++){
151 struct sqlite3_index_constraint
*p
= &pInfo
->aConstraint
[i
];
152 if( p
->usable
==0 ) continue;
153 if( p
->op
!=SQLITE_INDEX_CONSTRAINT_EQ
) continue;
155 if( p
->iColumn
==IIC_INDEX_NAME
){
158 if( p
->iColumn
==IIC_AFTER_KEY
){
164 pInfo
->estimatedCost
= 1000000000.0;
166 pInfo
->aConstraintUsage
[iIdxName
].argvIndex
= 1;
167 pInfo
->aConstraintUsage
[iIdxName
].omit
= 1;
169 pInfo
->estimatedCost
= 1000000.0;
171 pInfo
->aConstraintUsage
[iAfterKey
].argvIndex
= 2;
172 pInfo
->aConstraintUsage
[iAfterKey
].omit
= 1;
173 pInfo
->estimatedCost
= 1000.0;
181 ** Open a new btreeinfo cursor.
183 static int cidxOpen(sqlite3_vtab
*pVTab
, sqlite3_vtab_cursor
**ppCursor
){
187 pRet
= cidxMalloc(&rc
, sizeof(CidxCursor
));
189 *ppCursor
= (sqlite3_vtab_cursor
*)pRet
;
194 ** Close a btreeinfo cursor.
196 static int cidxClose(sqlite3_vtab_cursor
*pCursor
){
197 CidxCursor
*pCsr
= (CidxCursor
*)pCursor
;
198 sqlite3_finalize(pCsr
->pStmt
);
199 sqlite3_free(pCsr
->zIdxName
);
200 sqlite3_free(pCsr
->zAfterKey
);
206 ** Move a btreeinfo cursor to the next entry in the file.
208 static int cidxNext(sqlite3_vtab_cursor
*pCursor
){
209 CidxCursor
*pCsr
= (CidxCursor
*)pCursor
;
210 int rc
= sqlite3_step(pCsr
->pStmt
);
211 if( rc
!=SQLITE_ROW
){
212 rc
= sqlite3_finalize(pCsr
->pStmt
);
215 sqlite3
*db
= ((CidxTable
*)pCsr
->base
.pVtab
)->db
;
216 cidxCursorError(pCsr
, "Cursor error: %s", sqlite3_errmsg(db
));
225 /* We have reached EOF if previous sqlite3_step() returned
226 ** anything other than SQLITE_ROW;
228 static int cidxEof(sqlite3_vtab_cursor
*pCursor
){
229 CidxCursor
*pCsr
= (CidxCursor
*)pCursor
;
230 return pCsr
->pStmt
==0;
233 static char *cidxMprintf(int *pRc
, const char *zFmt
, ...){
237 zRet
= sqlite3_vmprintf(zFmt
, ap
);
238 if( *pRc
==SQLITE_OK
){
250 static sqlite3_stmt
*cidxPrepare(
251 int *pRc
, CidxCursor
*pCsr
, const char *zFmt
, ...
253 sqlite3_stmt
*pRet
= 0;
255 va_list ap
; /* ... printf arguments */
258 zSql
= sqlite3_vmprintf(zFmt
, ap
);
259 if( *pRc
==SQLITE_OK
){
263 sqlite3
*db
= ((CidxTable
*)pCsr
->base
.pVtab
)->db
;
264 *pRc
= sqlite3_prepare_v2(db
, zSql
, -1, &pRet
, 0);
265 if( *pRc
!=SQLITE_OK
){
266 cidxCursorError(pCsr
, "SQL error: %s", sqlite3_errmsg(db
));
276 static void cidxFinalize(int *pRc
, sqlite3_stmt
*pStmt
){
277 int rc
= sqlite3_finalize(pStmt
);
278 if( *pRc
==SQLITE_OK
) *pRc
= rc
;
281 char *cidxStrdup(int *pRc
, const char *zStr
){
283 if( *pRc
==SQLITE_OK
){
284 int n
= (int)strlen(zStr
);
285 zRet
= cidxMalloc(pRc
, n
+1);
286 if( zRet
) memcpy(zRet
, zStr
, n
+1);
291 static void cidxFreeIndex(CidxIndex
*pIdx
){
294 for(i
=0; i
<pIdx
->nCol
; i
++){
295 sqlite3_free(pIdx
->aCol
[i
].zExpr
);
297 sqlite3_free(pIdx
->zWhere
);
302 static int cidx_isspace(char c
){
303 return c
==' ' || c
=='\t' || c
=='\r' || c
=='\n';
306 static int cidx_isident(char c
){
308 || (c
>='0' && c
<='9') || (c
>='a' && c
<='z')
309 || (c
>='A' && c
<='Z') || c
=='_';
312 #define CIDX_PARSE_EOF 0
313 #define CIDX_PARSE_COMMA 1 /* "," */
314 #define CIDX_PARSE_OPEN 2 /* "(" */
315 #define CIDX_PARSE_CLOSE 3 /* ")" */
318 ** Argument zIn points into the start, middle or end of a CREATE INDEX
319 ** statement. If argument pbDoNotTrim is non-NULL, then this function
320 ** scans the input until it finds EOF, a comma (",") or an open or
321 ** close parenthesis character. It then sets (*pzOut) to point to said
322 ** character and returns a CIDX_PARSE_XXX constant as appropriate. The
323 ** parser is smart enough that special characters inside SQL strings
324 ** or comments are not returned for.
326 ** Or, if argument pbDoNotTrim is NULL, then this function sets *pzOut
327 ** to point to the first character of the string that is not whitespace
328 ** or part of an SQL comment and returns CIDX_PARSE_EOF.
330 ** Additionally, if pbDoNotTrim is not NULL and the element immediately
331 ** before (*pzOut) is an SQL comment of the form "-- comment", then
332 ** (*pbDoNotTrim) is set before returning. In all other cases it is
335 static int cidxFindNext(
338 int *pbDoNotTrim
/* OUT: True if prev is -- comment */
343 while( cidx_isspace(*z
) ) z
++;
344 if( z
[0]=='-' && z
[1]=='-' ){
347 if( z
[0]=='\0' ) return CIDX_PARSE_EOF
;
350 while( cidx_isspace(*z
) ) z
++;
351 if( pbDoNotTrim
) *pbDoNotTrim
= 1;
353 if( z
[0]=='/' && z
[1]=='*' ){
355 while( z
[0]!='*' || z
[1]!='/' ){
356 if( z
[1]=='\0' ) return CIDX_PARSE_EOF
;
362 if( pbDoNotTrim
==0 ) return CIDX_PARSE_EOF
;
365 return CIDX_PARSE_EOF
;
367 return CIDX_PARSE_OPEN
;
369 return CIDX_PARSE_CLOSE
;
371 return CIDX_PARSE_COMMA
;
404 static int cidxParseSQL(CidxCursor
*pCsr
, CidxIndex
*pIdx
, const char *zSql
){
405 const char *z
= zSql
;
411 CidxColumn
*pCol
= pIdx
->aCol
;
413 e
= cidxFindNext(z
, &z
, &bDoNotTrim
);
414 if( e
!=CIDX_PARSE_OPEN
) goto parse_error
;
418 e
= cidxFindNext(z
, &z
, &bDoNotTrim
);
419 if( e
==CIDX_PARSE_EOF
) goto parse_error
;
420 if( (e
==CIDX_PARSE_COMMA
|| e
==CIDX_PARSE_CLOSE
) && nParen
==1 ){
422 if( pCol
->zExpr
) goto parse_error
;
425 while( cidx_isspace(z
[-1]) ) z
--;
426 if( !sqlite3_strnicmp(&z
[-3], "asc", 3) && 0==cidx_isident(z
[-4]) ){
428 while( cidx_isspace(z
[-1]) ) z
--;
430 if( !sqlite3_strnicmp(&z
[-4], "desc", 4) && 0==cidx_isident(z
[-5]) ){
432 while( cidx_isspace(z
[-1]) ) z
--;
434 while( cidx_isspace(z1
[0]) ) z1
++;
437 pCol
->zExpr
= cidxMprintf(&rc
, "%.*s", z
-z1
, z1
);
441 if( e
==CIDX_PARSE_OPEN
) nParen
++;
442 if( e
==CIDX_PARSE_CLOSE
) nParen
--;
446 /* Search for a WHERE clause */
447 cidxFindNext(z
, &z
, 0);
448 if( 0==sqlite3_strnicmp(z
, "where", 5) ){
449 pIdx
->zWhere
= cidxMprintf(&rc
, "%s\n", &z
[5]);
450 }else if( z
[0]!='\0' ){
457 cidxCursorError(pCsr
, "Parse error in: %s", zSql
);
461 static int cidxLookupIndex(
462 CidxCursor
*pCsr
, /* Cursor object */
463 const char *zIdx
, /* Name of index to look up */
464 CidxIndex
**ppIdx
, /* OUT: Description of columns */
465 char **pzTab
/* OUT: Table name */
471 sqlite3_stmt
*pFindTab
= 0;
472 sqlite3_stmt
*pInfo
= 0;
474 /* Find the table for this index. */
475 pFindTab
= cidxPrepare(&rc
, pCsr
,
476 "SELECT tbl_name, sql FROM sqlite_schema WHERE name=%Q AND type='index'",
479 if( rc
==SQLITE_OK
&& sqlite3_step(pFindTab
)==SQLITE_ROW
){
480 const char *zSql
= (const char*)sqlite3_column_text(pFindTab
, 1);
481 zTab
= cidxStrdup(&rc
, (const char*)sqlite3_column_text(pFindTab
, 0));
483 pInfo
= cidxPrepare(&rc
, pCsr
, "PRAGMA index_xinfo(%Q)", zIdx
);
488 while( sqlite3_step(pInfo
)==SQLITE_ROW
){
489 const char *zName
= (const char*)sqlite3_column_text(pInfo
, 2);
490 const char *zColl
= (const char*)sqlite3_column_text(pInfo
, 4);
492 if( zName
==0 ) zName
= "rowid";
494 int nByte
= sizeof(CidxIndex
) + sizeof(CidxColumn
)*(nAlloc
+8);
495 pIdx
= (CidxIndex
*)sqlite3_realloc(pIdx
, nByte
);
498 p
= &pIdx
->aCol
[iCol
++];
499 p
->bDesc
= sqlite3_column_int(pInfo
, 3);
500 p
->bKey
= sqlite3_column_int(pInfo
, 5);
501 if( zSql
==0 || p
->bKey
==0 ){
502 p
->zExpr
= cidxMprintf(&rc
, "\"%w\" COLLATE %s",zName
,zColl
);
509 cidxFinalize(&rc
, pInfo
);
512 if( rc
==SQLITE_OK
&& zSql
){
513 rc
= cidxParseSQL(pCsr
, pIdx
, zSql
);
517 cidxFinalize(&rc
, pFindTab
);
518 if( rc
==SQLITE_OK
&& zTab
==0 ){
533 static int cidxDecodeAfter(
536 const char *zAfterKey
,
541 int nAfterKey
= (int)strlen(zAfterKey
);
543 azAfter
= cidxMalloc(&rc
, sizeof(char*)*nCol
+ nAfterKey
+1);
546 char *zCopy
= (char*)&azAfter
[nCol
];
548 memcpy(zCopy
, zAfterKey
, nAfterKey
+1);
549 for(i
=0; i
<nCol
; i
++){
550 while( *p
==' ' ) p
++;
552 /* Check NULL values */
554 if( memcmp(p
, "NULL", 4) ) goto parse_error
;
558 /* Check strings and blob literals */
559 else if( *p
=='X' || *p
=='\'' ){
562 if( *p
!='\'' ) goto parse_error
;
565 if( *p
=='\0' ) goto parse_error
;
568 if( *p
!='\'' ) break;
577 while( (*p
>='0' && *p
<='9')
578 || *p
=='.' || *p
=='+' || *p
=='-' || *p
=='e' || *p
=='E'
584 while( *p
==' ' ) p
++;
585 if( *p
!=(i
==(nCol
-1) ? '\0' : ',') ){
596 sqlite3_free(azAfter
);
598 cidxCursorError(pCsr
, "%s", "error parsing after value");
602 static char *cidxWhere(
603 int *pRc
, CidxColumn
*aCol
, char **azAfter
, int iGt
, int bLastIsNull
606 const char *zSep
= "";
609 for(i
=0; i
<iGt
; i
++){
610 zRet
= cidxMprintf(pRc
, "%z%s(%s) IS %s", zRet
,
611 zSep
, aCol
[i
].zExpr
, (azAfter
[i
] ? azAfter
[i
] : "NULL")
617 zRet
= cidxMprintf(pRc
, "%z%s(%s) IS NULL", zRet
, zSep
, aCol
[iGt
].zExpr
);
619 else if( azAfter
[iGt
] ){
620 zRet
= cidxMprintf(pRc
, "%z%s(%s) %s %s", zRet
,
621 zSep
, aCol
[iGt
].zExpr
, (aCol
[iGt
].bDesc
? "<" : ">"),
625 zRet
= cidxMprintf(pRc
, "%z%s(%s) IS NOT NULL", zRet
, zSep
,aCol
[iGt
].zExpr
);
631 #define CIDX_CLIST_ALL 0
632 #define CIDX_CLIST_ORDERBY 1
633 #define CIDX_CLIST_CURRENT_KEY 2
634 #define CIDX_CLIST_SUBWHERE 3
635 #define CIDX_CLIST_SUBEXPR 4
638 ** This function returns various strings based on the contents of the
639 ** CidxIndex structure and the eType parameter.
641 static char *cidxColumnList(
642 int *pRc
, /* IN/OUT: Error code */
644 CidxIndex
*pIdx
, /* Indexed columns */
645 int eType
/* True to include ASC/DESC */
648 if( *pRc
==SQLITE_OK
){
649 const char *aDir
[2] = {"", " DESC"};
651 const char *zSep
= "";
653 for(i
=0; i
<pIdx
->nCol
; i
++){
654 CidxColumn
*p
= &pIdx
->aCol
[i
];
655 assert( pIdx
->aCol
[i
].bDesc
==0 || pIdx
->aCol
[i
].bDesc
==1 );
658 case CIDX_CLIST_ORDERBY
:
659 zRet
= cidxMprintf(pRc
, "%z%s%d%s", zRet
, zSep
, i
+1, aDir
[p
->bDesc
]);
663 case CIDX_CLIST_CURRENT_KEY
:
664 zRet
= cidxMprintf(pRc
, "%z%squote(i%d)", zRet
, zSep
, i
);
668 case CIDX_CLIST_SUBWHERE
:
670 zRet
= cidxMprintf(pRc
, "%z%s%s IS i.i%d", zRet
,
677 case CIDX_CLIST_SUBEXPR
:
679 zRet
= cidxMprintf(pRc
, "%z%s%s IS i.i%d", zRet
,
687 assert( eType
==CIDX_CLIST_ALL
);
688 zRet
= cidxMprintf(pRc
, "%z%s(%s) AS i%d", zRet
, zSep
, p
->zExpr
, i
);
699 ** Generate SQL (in memory obtained from sqlite3_malloc()) that will
700 ** continue the index scan for zIdxName starting after zAfterKey.
702 int cidxGenerateScanSql(
703 CidxCursor
*pCsr
, /* The cursor which needs the new statement */
704 const char *zIdxName
, /* index to be scanned */
705 const char *zAfterKey
, /* start after this key, if not NULL */
706 char **pzSqlOut
/* OUT: Write the generated SQL here */
710 char *zCurrentKey
= 0;
719 rc
= cidxLookupIndex(pCsr
, zIdxName
, &pIdx
, &zTab
);
721 zOrderBy
= cidxColumnList(&rc
, zIdxName
, pIdx
, CIDX_CLIST_ORDERBY
);
722 zCurrentKey
= cidxColumnList(&rc
, zIdxName
, pIdx
, CIDX_CLIST_CURRENT_KEY
);
723 zSubWhere
= cidxColumnList(&rc
, zIdxName
, pIdx
, CIDX_CLIST_SUBWHERE
);
724 zSubExpr
= cidxColumnList(&rc
, zIdxName
, pIdx
, CIDX_CLIST_SUBEXPR
);
725 zSrcList
= cidxColumnList(&rc
, zIdxName
, pIdx
, CIDX_CLIST_ALL
);
727 if( rc
==SQLITE_OK
&& zAfterKey
){
728 rc
= cidxDecodeAfter(pCsr
, pIdx
->nCol
, zAfterKey
, &azAfter
);
733 *pzSqlOut
= cidxMprintf(&rc
,
734 "SELECT (SELECT %s FROM %Q AS t WHERE %s), %s "
735 "FROM (SELECT %s FROM %Q INDEXED BY %Q %s%sORDER BY %s) AS i",
736 zSubExpr
, zTab
, zSubWhere
, zCurrentKey
,
737 zSrcList
, zTab
, zIdxName
,
738 (pIdx
->zWhere
? "WHERE " : ""), (pIdx
->zWhere
? pIdx
->zWhere
: ""),
742 const char *zSep
= "";
746 zSql
= cidxMprintf(&rc
,
747 "SELECT (SELECT %s FROM %Q WHERE %s), %s FROM (",
748 zSubExpr
, zTab
, zSubWhere
, zCurrentKey
750 for(i
=pIdx
->nCol
-1; i
>=0; i
--){
752 if( pIdx
->aCol
[i
].bDesc
&& azAfter
[i
]==0 ) continue;
754 char *zWhere
= cidxWhere(&rc
, pIdx
->aCol
, azAfter
, i
, j
);
755 zSql
= cidxMprintf(&rc
, "%z"
757 "SELECT %s FROM %Q INDEXED BY %Q WHERE %s%s%z ORDER BY %s"
759 zSql
, zSep
, zSrcList
, zTab
, zIdxName
,
760 pIdx
->zWhere
? pIdx
->zWhere
: "",
761 pIdx
->zWhere
? " AND " : "",
764 zSep
= " UNION ALL ";
765 if( pIdx
->aCol
[i
].bDesc
==0 ) break;
768 *pzSqlOut
= cidxMprintf(&rc
, "%z) AS i", zSql
);
773 sqlite3_free(zCurrentKey
);
774 sqlite3_free(zOrderBy
);
775 sqlite3_free(zSubWhere
);
776 sqlite3_free(zSubExpr
);
777 sqlite3_free(zSrcList
);
779 sqlite3_free(azAfter
);
785 ** Position a cursor back to the beginning.
787 static int cidxFilter(
788 sqlite3_vtab_cursor
*pCursor
,
789 int idxNum
, const char *idxStr
,
790 int argc
, sqlite3_value
**argv
793 CidxCursor
*pCsr
= (CidxCursor
*)pCursor
;
794 const char *zIdxName
= 0;
795 const char *zAfterKey
= 0;
797 sqlite3_free(pCsr
->zIdxName
);
799 sqlite3_free(pCsr
->zAfterKey
);
801 sqlite3_finalize(pCsr
->pStmt
);
805 zIdxName
= (const char*)sqlite3_value_text(argv
[0]);
807 zAfterKey
= (const char*)sqlite3_value_text(argv
[1]);
813 pCsr
->zIdxName
= sqlite3_mprintf("%s", zIdxName
);
814 pCsr
->zAfterKey
= zAfterKey
? sqlite3_mprintf("%s", zAfterKey
) : 0;
815 rc
= cidxGenerateScanSql(pCsr
, zIdxName
, zAfterKey
, &zSql
);
817 pCsr
->pStmt
= cidxPrepare(&rc
, pCsr
, "%z", zSql
);
822 assert( rc
==SQLITE_OK
);
823 rc
= cidxNext(pCursor
);
830 ** Return a column value.
832 static int cidxColumn(
833 sqlite3_vtab_cursor
*pCursor
,
834 sqlite3_context
*ctx
,
837 CidxCursor
*pCsr
= (CidxCursor
*)pCursor
;
838 assert( iCol
>=IIC_ERRMSG
&& iCol
<=IIC_SCANNER_SQL
);
841 const char *zVal
= 0;
842 if( sqlite3_column_type(pCsr
->pStmt
, 0)==SQLITE_INTEGER
){
843 if( sqlite3_column_int(pCsr
->pStmt
, 0)==0 ){
844 zVal
= "row data mismatch";
847 zVal
= "row missing";
849 sqlite3_result_text(ctx
, zVal
, -1, SQLITE_STATIC
);
852 case IIC_CURRENT_KEY
: {
853 sqlite3_result_value(ctx
, sqlite3_column_value(pCsr
->pStmt
, 1));
856 case IIC_INDEX_NAME
: {
857 sqlite3_result_text(ctx
, pCsr
->zIdxName
, -1, SQLITE_TRANSIENT
);
860 case IIC_AFTER_KEY
: {
861 sqlite3_result_text(ctx
, pCsr
->zAfterKey
, -1, SQLITE_TRANSIENT
);
864 case IIC_SCANNER_SQL
: {
866 cidxGenerateScanSql(pCsr
, pCsr
->zIdxName
, pCsr
->zAfterKey
, &zSql
);
867 sqlite3_result_text(ctx
, zSql
, -1, sqlite3_free
);
874 /* Return the ROWID for the sqlite_btreeinfo table */
875 static int cidxRowid(sqlite3_vtab_cursor
*pCursor
, sqlite_int64
*pRowid
){
876 CidxCursor
*pCsr
= (CidxCursor
*)pCursor
;
877 *pRowid
= pCsr
->iRowid
;
882 ** Register the virtual table modules with the database handle passed
883 ** as the only argument.
885 static int ciInit(sqlite3
*db
){
886 static sqlite3_module cidx_module
= {
889 cidxConnect
, /* xConnect */
890 cidxBestIndex
, /* xBestIndex */
891 cidxDisconnect
, /* xDisconnect */
893 cidxOpen
, /* xOpen - open a cursor */
894 cidxClose
, /* xClose - close a cursor */
895 cidxFilter
, /* xFilter - configure scan constraints */
896 cidxNext
, /* xNext - advance a cursor */
897 cidxEof
, /* xEof - check for end of scan */
898 cidxColumn
, /* xColumn - read data */
899 cidxRowid
, /* xRowid - read data */
913 return sqlite3_create_module(db
, "incremental_index_check", &cidx_module
, 0);
917 ** Extension load function.
920 __declspec(dllexport
)
922 int sqlite3_checkindex_init(
925 const sqlite3_api_routines
*pApi
927 SQLITE_EXTENSION_INIT2(pApi
);