#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT3
typedef struct series_cursor series_cursor;
struct series_cursor {
sqlite3_vtab_cursor base;
int isDesc;
sqlite3_int64 iRowid;
sqlite3_int64 iValue;
sqlite3_int64 mnValue;
sqlite3_int64 mxValue;
sqlite3_int64 iStep;
};
static int seriesConnect(sqlite3* db,
void* pUnused,
int argcUnused,
const char* const* argvUnused,
sqlite3_vtab** ppVtab,
char** pzErrUnused) {
sqlite3_vtab* pNew;
int rc;
#define SERIES_COLUMN_VALUE 0
#define SERIES_COLUMN_START 1
#define SERIES_COLUMN_STOP 2
#define SERIES_COLUMN_STEP 3
(void)pUnused;
(void)argcUnused;
(void)argvUnused;
(void)pzErrUnused;
rc = sqlite3_declare_vtab(db, "CREATE TABLE x(value,start hidden,stop hidden,step hidden)");
if (rc == SQLITE_OK) {
pNew = *ppVtab = sqlite3_malloc(sizeof(*pNew));
if (pNew == 0)
return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
return rc;
}
static int seriesDisconnect(sqlite3_vtab* pVtab) {
sqlite3_free(pVtab);
return SQLITE_OK;
}
static int seriesOpen(sqlite3_vtab* pUnused, sqlite3_vtab_cursor** ppCursor) {
series_cursor* pCur;
(void)pUnused;
pCur = sqlite3_malloc(sizeof(*pCur));
if (pCur == 0)
return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
static int seriesClose(sqlite3_vtab_cursor* cur) {
sqlite3_free(cur);
return SQLITE_OK;
}
static int seriesNext(sqlite3_vtab_cursor* cur) {
series_cursor* pCur = (series_cursor*)cur;
if (pCur->isDesc) {
pCur->iValue -= pCur->iStep;
} else {
pCur->iValue += pCur->iStep;
}
pCur->iRowid++;
return SQLITE_OK;
}
static int seriesColumn(sqlite3_vtab_cursor* cur,
sqlite3_context* ctx,
int i
) {
series_cursor* pCur = (series_cursor*)cur;
sqlite3_int64 x = 0;
switch (i) {
case SERIES_COLUMN_START:
x = pCur->mnValue;
break;
case SERIES_COLUMN_STOP:
x = pCur->mxValue;
break;
case SERIES_COLUMN_STEP:
x = pCur->iStep;
break;
default:
x = pCur->iValue;
break;
}
sqlite3_result_int64(ctx, x);
return SQLITE_OK;
}
static int seriesRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid) {
series_cursor* pCur = (series_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
static int seriesEof(sqlite3_vtab_cursor* cur) {
series_cursor* pCur = (series_cursor*)cur;
if (pCur->isDesc) {
return pCur->iValue < pCur->mnValue;
} else {
return pCur->iValue > pCur->mxValue;
}
}
#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
#define SQLITE_SERIES_CONSTRAINT_VERIFY 0
#endif
static int seriesFilter(sqlite3_vtab_cursor* pVtabCursor,
int idxNum,
const char* idxStrUnused,
int argc,
sqlite3_value** argv) {
series_cursor* pCur = (series_cursor*)pVtabCursor;
int i = 0;
(void)idxStrUnused;
if (idxNum & 1) {
pCur->mnValue = sqlite3_value_int64(argv[i++]);
} else {
pCur->mnValue = 0;
}
if (idxNum & 2) {
pCur->mxValue = sqlite3_value_int64(argv[i++]);
} else {
pCur->mxValue = 0xffffffff;
}
if (idxNum & 4) {
pCur->iStep = sqlite3_value_int64(argv[i++]);
if (pCur->iStep == 0) {
pCur->iStep = 1;
} else if (pCur->iStep < 0) {
pCur->iStep = -pCur->iStep;
if ((idxNum & 16) == 0)
idxNum |= 8;
}
} else {
pCur->iStep = 1;
}
for (i = 0; i < argc; i++) {
if (sqlite3_value_type(argv[i]) == SQLITE_NULL) {
pCur->mnValue = 1;
pCur->mxValue = 0;
break;
}
}
if (idxNum & 8) {
pCur->isDesc = 1;
pCur->iValue = pCur->mxValue;
if (pCur->iStep > 0) {
pCur->iValue -= (pCur->mxValue - pCur->mnValue) % pCur->iStep;
}
} else {
pCur->isDesc = 0;
pCur->iValue = pCur->mnValue;
}
pCur->iRowid = 1;
return SQLITE_OK;
}
static int seriesBestIndex(sqlite3_vtab* pVTab, sqlite3_index_info* pIdxInfo) {
int i, j;
int idxNum = 0;
int bStartSeen = 0;
int unusableMask = 0;
int nArg = 0;
int aIdx[3];
const struct sqlite3_index_constraint* pConstraint;
assert(SERIES_COLUMN_STOP == SERIES_COLUMN_START + 1);
assert(SERIES_COLUMN_STEP == SERIES_COLUMN_START + 2);
aIdx[0] = aIdx[1] = aIdx[2] = -1;
pConstraint = pIdxInfo->aConstraint;
for (i = 0; i < pIdxInfo->nConstraint; i++, pConstraint++) {
int iCol;
int iMask;
if (pConstraint->iColumn < SERIES_COLUMN_START)
continue;
iCol = pConstraint->iColumn - SERIES_COLUMN_START;
assert(iCol >= 0 && iCol <= 2);
iMask = 1 << iCol;
if (iCol == 0)
bStartSeen = 1;
if (pConstraint->usable == 0) {
unusableMask |= iMask;
continue;
} else if (pConstraint->op == SQLITE_INDEX_CONSTRAINT_EQ) {
idxNum |= iMask;
aIdx[iCol] = i;
}
}
for (i = 0; i < 3; i++) {
if ((j = aIdx[i]) >= 0) {
pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY;
}
}
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
if (!bStartSeen) {
sqlite3_free(pVTab->zErrMsg);
pVTab->zErrMsg =
sqlite3_mprintf("first argument to \"generate_series()\" missing or unusable");
return SQLITE_ERROR;
}
#endif
if ((unusableMask & ~idxNum) != 0) {
return SQLITE_CONSTRAINT;
}
if ((idxNum & 3) == 3) {
pIdxInfo->estimatedCost = (double)(2 - ((idxNum & 4) != 0));
pIdxInfo->estimatedRows = 1000;
if (pIdxInfo->nOrderBy == 1) {
if (pIdxInfo->aOrderBy[0].desc) {
idxNum |= 8;
} else {
idxNum |= 16;
}
pIdxInfo->orderByConsumed = 1;
}
} else {
pIdxInfo->estimatedRows = 2147483647;
}
pIdxInfo->idxNum = idxNum;
return SQLITE_OK;
}
static sqlite3_module series_module = {
.xConnect = seriesConnect,
.xBestIndex = seriesBestIndex,
.xDisconnect = seriesDisconnect,
.xOpen = seriesOpen,
.xClose = seriesClose,
.xFilter = seriesFilter,
.xNext = seriesNext,
.xEof = seriesEof,
.xColumn = seriesColumn,
.xRowid = seriesRowid,
};
int stats_series_init(sqlite3* db) {
sqlite3_create_module(db, "stats_seq", &series_module, 0);
sqlite3_create_module(db, "generate_series", &series_module, 0);
return SQLITE_OK;
}