#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>
-# define ALWAYS(X) 1
-# define NEVER(X) 0
- typedef unsigned char u8;
- typedef unsigned short u16;
- typedef unsigned int u32;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
#define get4byte(x) ( \
((u32)((x)[0])<<24) + \
((u32)((x)[1])<<16) + \
struct CidxCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
- sqlite3_int64 iRowid;
- sqlite3_stmt *pStmt;
+ 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;
}
/*
-** Connect to then incremental_index_check virtual table.
+** Connect to the incremental_index_check virtual table.
*/
static int cidxConnect(
sqlite3 *db,
#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, current_key TEXT,"
- " index_name HIDDEN, after_key HIDDEN"
+ " 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));
}
/*
-** xBestIndex method.
+** 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;
static int cidxClose(sqlite3_vtab_cursor *pCursor){
CidxCursor *pCsr = (CidxCursor*)pCursor;
sqlite3_finalize(pCsr->pStmt);
- pCsr->pStmt = 0;
+ sqlite3_free(pCsr->zIdxName);
+ sqlite3_free(pCsr->zAfterKey);
sqlite3_free(pCsr);
return SQLITE_OK;
}
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.
*/
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 ){
}
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); */
+ 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);
}
-
- 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 ){
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";
+ 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";
}
- }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;
}
- 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;
+ CidxCursor *pCsr = (CidxCursor*)pCursor;
+ *pRowid = pCsr->iRowid;
return SQLITE_OK;
}