/*
** 2011 April 02
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file implements a virtual table that returns the whole numbers
** between 1 and 4294967295, inclusive.
**
** Example:
**
** CREATE VIRTUAL TABLE nums USING wholenumber;
** SELECT value FROM nums WHERE value<10;
**
** Results in:
**
** 1 2 3 4 5 6 7 8 9
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* A wholenumber cursor object */
typedef struct wholenumber_cursor wholenumber_cursor;
struct wholenumber_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3_int64 iValue; /* Current value */
sqlite3_int64 mxValue; /* Maximum value */
};
/* Methods for the wholenumber module */
static int wholenumberConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
sqlite3_vtab *pNew;
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
sqlite3_declare_vtab(db, "CREATE TABLE x(value)");
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
memset(pNew, 0, sizeof(*pNew));
return SQLITE_OK;
}
/* Note that for this virtual table, the xCreate and xConnect
** methods are identical. */
static int wholenumberDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/* The xDisconnect and xDestroy methods are also the same */
/*
** Open a new wholenumber cursor.
*/
static int wholenumberOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
wholenumber_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Close a wholenumber cursor.
*/
static int wholenumberClose(sqlite3_vtab_cursor *cur){
sqlite3_free(cur);
return SQLITE_OK;
}
/*
** Advance a cursor to its next row of output
*/
static int wholenumberNext(sqlite3_vtab_cursor *cur){
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
pCur->iValue++;
return SQLITE_OK;
}
/*
** Return the value associated with a wholenumber.
*/
static int wholenumberColumn(
sqlite3_vtab_cursor *cur,
sqlite3_context *ctx,
int i
){
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
sqlite3_result_int64(ctx, pCur->iValue);
return SQLITE_OK;
}
/*
** The rowid.
*/
static int wholenumberRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
*pRowid = pCur->iValue;
return SQLITE_OK;
}
/*
** When the wholenumber_cursor.rLimit value is 0 or less, that is a signal
** that the cursor has nothing more to output.
*/
static int wholenumberEof(sqlite3_vtab_cursor *cur){
wholenumber_cursor *pCur = (wholenumber_cursor*)cur;
return pCur->iValue>pCur->mxValue || pCur->iValue==0;
}
/*
** Called to "rewind" a cursor back to the beginning so that
** it starts its output over again. Always called at least once
** prior to any wholenumberColumn, wholenumberRowid, or wholenumberEof call.
**
** idxNum Constraints
** ------ ---------------------
** 0 (none)
** 1 value > $argv0
** 2 value >= $argv0
** 4 value < $argv0
** 8 value <= $argv0
**
** 5 value > $argv0 AND value < $argv1
** 6 value >= $argv0 AND value < $argv1
** 9 value > $argv0 AND value <= $argv1
** 10 value >= $argv0 AND value <= $argv1
*/
static int wholenumberFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
wholenumber_cursor *pCur = (wholenumber_cursor *)pVtabCursor;
sqlite3_int64 v;
int i = 0;
pCur->iValue = 1;
pCur->mxValue = 0xffffffff; /* 4294967295 */
if( idxNum & 3 ){
v = sqlite3_value_int64(argv[0]) + (idxNum&1);
if( v>pCur->iValue && v<=pCur->mxValue ) pCur->iValue = v;
i++;
}
if( idxNum & 12 ){
v = sqlite3_value_int64(argv[i]) - ((idxNum>>2)&1);
if( v>=pCur->iValue && v<pCur->mxValue ) pCur->mxValue = v;
}
return SQLITE_OK;
}
/*
** Search for terms of these forms:
**
** (1) value > $value
** (2) value >= $value
** (4) value < $value
** (8) value <= $value
**
** idxNum is an ORed combination of 1 or 2 with 4 or 8.
*/
static int wholenumberBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i;
int idxNum = 0;
int argvIdx = 1;
int ltIdx = -1;
int gtIdx = -1;
const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->usable==0 ) continue;
if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GT ){
idxNum |= 1;
ltIdx = i;
}
if( (idxNum & 3)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE ){
idxNum |= 2;
ltIdx = i;
}
if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){
idxNum |= 4;
gtIdx = i;
}
if( (idxNum & 12)==0 && pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE ){
idxNum |= 8;
gtIdx = i;
}
}
pIdxInfo->idxNum = idxNum;
if( ltIdx>=0 ){
pIdxInfo->aConstraintUsage[ltIdx].argvIndex = argvIdx++;
pIdxInfo->aConstraintUsage[ltIdx].omit = 1;
}
if( gtIdx>=0 ){
pIdxInfo->aConstraintUsage[gtIdx].argvIndex = argvIdx;
pIdxInfo->aConstraintUsage[gtIdx].omit = 1;
}
if( pIdxInfo->nOrderBy==1
&& pIdxInfo->aOrderBy[0].desc==0
){
pIdxInfo->orderByConsumed = 1;
}
if( (idxNum & 12)==0 ){
pIdxInfo->estimatedCost = 1e99;
}else if( (idxNum & 3)==0 ){
pIdxInfo->estimatedCost = (double)5;
}else{
pIdxInfo->estimatedCost = (double)1;
}
return SQLITE_OK;
}
/*
** A virtual table module that provides read-only access to a
** Tcl global variable namespace.
*/
static sqlite3_module wholenumberModule = {
0, /* iVersion */
wholenumberConnect,
wholenumberConnect,
wholenumberBestIndex,
wholenumberDisconnect,
wholenumberDisconnect,
wholenumberOpen, /* xOpen - open a cursor */
wholenumberClose, /* xClose - close a cursor */
wholenumberFilter, /* xFilter - configure scan constraints */
wholenumberNext, /* xNext - advance a cursor */
wholenumberEof, /* xEof - check for end of scan */
wholenumberColumn, /* xColumn - read data */
wholenumberRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_wholenumber_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
#ifndef SQLITE_OMIT_VIRTUALTABLE
rc = sqlite3_create_module(db, "wholenumber", &wholenumberModule, 0);
#endif
return rc;
}