#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT3
#if defined(__GNUC__)
#define VSV_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER) && _MSC_VER >= 1310
#define VSV_NOINLINE __declspec(noinline)
#else
#define VSV_NOINLINE
#endif
#define VSV_MXERR 200
#define VSV_INBUFSZ 1024
typedef struct VsvReader VsvReader;
struct VsvReader {
FILE* in;
char* z;
int n;
int nAlloc;
int nLine;
int bNotFirst;
int cTerm;
int fsep;
int rsep;
int dsep;
int affinity;
int notNull;
size_t iIn;
size_t nIn;
char* zIn;
char zErr[VSV_MXERR];
};
static void vsv_reader_init(VsvReader* p) {
p->in = 0;
p->z = 0;
p->n = 0;
p->nAlloc = 0;
p->nLine = 0;
p->bNotFirst = 0;
p->nIn = 0;
p->zIn = 0;
p->notNull = 0;
p->zErr[0] = 0;
}
static void vsv_reader_reset(VsvReader* p) {
if (p->in) {
fclose(p->in);
sqlite3_free(p->zIn);
}
sqlite3_free(p->z);
vsv_reader_init(p);
}
static void vsv_errmsg(VsvReader* p, const char* zFormat, ...) {
va_list ap;
va_start(ap, zFormat);
sqlite3_vsnprintf(VSV_MXERR, p->zErr, zFormat, ap);
va_end(ap);
}
static int vsv_reader_open(VsvReader* p,
const char* zFilename,
const char* zData
) {
if (zFilename) {
p->zIn = sqlite3_malloc(VSV_INBUFSZ);
if (p->zIn == 0) {
vsv_errmsg(p, "out of memory");
return 1;
}
p->in = fopen(zFilename, "rb");
if (p->in == 0) {
sqlite3_free(p->zIn);
vsv_reader_reset(p);
vsv_errmsg(p, "cannot open '%s' for reading", zFilename);
return 1;
}
} else {
assert(p->in == 0);
p->zIn = (char*)zData;
p->nIn = strlen(zData);
}
return 0;
}
static VSV_NOINLINE int vsv_getc_refill(VsvReader* p) {
size_t got;
assert(p->iIn >= p->nIn);
assert(p->in != 0);
got = fread(p->zIn, 1, VSV_INBUFSZ, p->in);
if (got == 0) {
return EOF;
}
p->nIn = got;
p->iIn = 1;
return p->zIn[0];
}
static int vsv_getc(VsvReader* p) {
if (p->iIn >= p->nIn) {
if (p->in != 0) {
return vsv_getc_refill(p);
}
return EOF;
}
return ((unsigned char*)p->zIn)[p->iIn++];
}
static VSV_NOINLINE int vsv_resize_and_append(VsvReader* p, char c) {
char* zNew;
int nNew = p->nAlloc * 2 + 100;
zNew = sqlite3_realloc64(p->z, nNew);
if (zNew) {
p->z = zNew;
p->nAlloc = nNew;
p->z[p->n++] = c;
return 0;
} else {
vsv_errmsg(p, "out of memory");
return 1;
}
}
static int vsv_append(VsvReader* p, char c) {
if (p->n >= p->nAlloc - 1) {
return vsv_resize_and_append(p, c);
}
p->z[p->n++] = c;
return 0;
}
static char* vsv_read_one_field(VsvReader* p) {
int c;
p->notNull = 0;
p->n = 0;
c = vsv_getc(p);
if (c == EOF) {
p->cTerm = EOF;
return 0;
}
if (c == '"') {
int pc, ppc;
int startLine = p->nLine;
p->notNull = 1;
pc = ppc = 0;
while (1) {
c = vsv_getc(p);
if (c == '\n') {
p->nLine++;
}
if (c == '"' && pc == '"') {
pc = ppc;
ppc = 0;
continue;
}
if ((c == p->fsep && pc == '"') || (c == p->rsep && pc == '"') ||
(p->rsep == '\n' && c == '\n' && pc == '\r' && ppc == '"') ||
(c == EOF && pc == '"')) {
do {
p->n--;
} while (p->z[p->n] != '"');
p->cTerm = (char)c;
break;
}
if (pc == '"' && p->rsep == '\n' && c != '\r') {
vsv_errmsg(p, "line %d: unescaped %c character", p->nLine, '"');
break;
}
if (c == EOF) {
vsv_errmsg(p, "line %d: unterminated %c-quoted field\n", startLine, '"');
p->cTerm = (char)c;
break;
}
if (vsv_append(p, (char)c)) {
return 0;
}
ppc = pc;
pc = c;
}
} else {
if ((c & 0xff) == 0xef && p->bNotFirst == 0) {
vsv_append(p, (char)c);
c = vsv_getc(p);
if ((c & 0xff) == 0xbb) {
vsv_append(p, (char)c);
c = vsv_getc(p);
if ((c & 0xff) == 0xbf) {
p->bNotFirst = 1;
p->n = 0;
return vsv_read_one_field(p);
}
}
}
while (c != EOF && c != p->rsep && c != p->fsep) {
if (c == '\n')
p->nLine++;
if (!p->notNull)
p->notNull = 1;
if (vsv_append(p, (char)c))
return 0;
c = vsv_getc(p);
}
if (c == '\n') {
p->nLine++;
}
if (p->n > 0 && (p->rsep == '\n' || p->fsep == '\n') && p->z[p->n - 1] == '\r') {
p->n--;
if (p->n == 0) {
p->notNull = 0;
}
}
p->cTerm = (char)c;
}
if (p->z) {
p->z[p->n] = 0;
}
p->bNotFirst = 1;
return p->z;
}
static int vsvtabCreate(sqlite3*, void*, int, const char* const*, sqlite3_vtab**, char**);
static int vsvtabConnect(sqlite3*, void*, int, const char* const*, sqlite3_vtab**, char**);
static int vsvtabBestIndex(sqlite3_vtab*, sqlite3_index_info*);
static int vsvtabDisconnect(sqlite3_vtab*);
static int vsvtabOpen(sqlite3_vtab*, sqlite3_vtab_cursor**);
static int vsvtabClose(sqlite3_vtab_cursor*);
static int vsvtabFilter(sqlite3_vtab_cursor*,
int idxNum,
const char* idxStr,
int argc,
sqlite3_value** argv);
static int vsvtabNext(sqlite3_vtab_cursor*);
static int vsvtabEof(sqlite3_vtab_cursor*);
static int vsvtabColumn(sqlite3_vtab_cursor*, sqlite3_context*, int);
static int vsvtabRowid(sqlite3_vtab_cursor*, sqlite3_int64*);
typedef struct VsvTable {
sqlite3_vtab base;
char* zFilename;
char* zData;
long iStart;
int nCol;
int fsep;
int rsep;
int dsep;
int affinity;
int nulls;
int validateUTF8;
} VsvTable;
typedef struct VsvCursor {
sqlite3_vtab_cursor base;
VsvReader rdr;
char** azVal;
int* aLen;
int* dLen;
sqlite3_int64 iRowid;
} VsvCursor;
static void vsv_xfer_error(VsvTable* pTab, VsvReader* pRdr) {
sqlite3_free(pTab->base.zErrMsg);
pTab->base.zErrMsg = sqlite3_mprintf("%s", pRdr->zErr);
}
static int vsvtabDisconnect(sqlite3_vtab* pVtab) {
VsvTable* p = (VsvTable*)pVtab;
sqlite3_free(p->zFilename);
sqlite3_free(p->zData);
sqlite3_free(p);
return SQLITE_OK;
}
static const char* vsv_skip_whitespace(const char* z) {
while (isspace((unsigned char)z[0])) {
z++;
}
return z;
}
static void vsv_trim_whitespace(char* z) {
size_t n = strlen(z);
while (n > 0 && isspace((unsigned char)z[n])) {
n--;
}
z[n] = 0;
}
static void vsv_dequote(char* z) {
int j;
char cQuote = z[0];
size_t i, n;
if (cQuote != '\'' && cQuote != '"') {
return;
}
n = strlen(z);
if (n < 2 || z[n - 1] != z[0]) {
return;
}
for (i = 1, j = 0; i < n - 1; i++) {
if (z[i] == cQuote && z[i + 1] == cQuote) {
i++;
}
z[j++] = z[i];
}
z[j] = 0;
}
static const char* vsv_parameter(const char* zTag, int nTag, const char* z) {
z = vsv_skip_whitespace(z);
if (strncmp(zTag, z, nTag) != 0) {
return 0;
}
z = vsv_skip_whitespace(z + nTag);
if (z[0] != '=') {
return 0;
}
return vsv_skip_whitespace(z + 1);
}
static int vsv_string_parameter(VsvReader* p,
const char* zParam,
const char* zArg,
char** pzVal
) {
const char* zValue;
zValue = vsv_parameter(zParam, (int)strlen(zParam), zArg);
if (zValue == 0) {
return 0;
}
p->zErr[0] = 0;
if (*pzVal) {
vsv_errmsg(p, "more than one '%s' parameter", zParam);
return 1;
}
*pzVal = sqlite3_mprintf("%s", zValue);
if (*pzVal == 0) {
vsv_errmsg(p, "out of memory");
return 1;
}
vsv_trim_whitespace(*pzVal);
vsv_dequote(*pzVal);
return 1;
}
static int vsv_boolean(const char* z) {
if (sqlite3_stricmp("yes", z) == 0 || sqlite3_stricmp("on", z) == 0 ||
sqlite3_stricmp("true", z) == 0 || (z[0] == '1' && z[1] == 0)) {
return 1;
}
if (sqlite3_stricmp("no", z) == 0 || sqlite3_stricmp("off", z) == 0 ||
sqlite3_stricmp("false", z) == 0 || (z[0] == '0' && z[1] == 0)) {
return 0;
}
return -1;
}
static int vsv_boolean_parameter(const char* zTag,
int nTag,
const char* z,
int* pValue
) {
int b;
z = vsv_skip_whitespace(z);
if (strncmp(zTag, z, nTag) != 0) {
return 0;
}
z = vsv_skip_whitespace(z + nTag);
if (z[0] == 0) {
*pValue = 1;
return 1;
}
if (z[0] != '=') {
return 0;
}
z = vsv_skip_whitespace(z + 1);
b = vsv_boolean(z);
if (b >= 0) {
*pValue = b;
return 1;
}
return 0;
}
static int vsv_parse_sep_char(char* in, int dflt, int* out) {
if (!in) {
*out = dflt;
return 0;
}
switch (strlen(in)) {
case 0: {
*out = dflt;
return 0;
}
case 1: {
*out = in[0];
return 0;
}
case 2: {
if (in[0] != '\\') {
return 1;
}
switch (in[1]) {
case 'f': {
*out = 12;
return 0;
}
case 'n': {
*out = 10;
return 0;
}
case 't': {
*out = 9;
return 0;
}
case 'v': {
*out = 11;
return 0;
}
}
return 1;
}
case 4: {
if (sqlite3_strnicmp(in, "\\x", 2) != 0) {
return 1;
}
if (!isxdigit(in[2]) || !isxdigit(in[3])) {
return 1;
}
*out = ((in[2] > '9' ? (in[2] & 0x0f) + 9 : in[2] & 0x0f) << 4) +
(in[3] > '9' ? (in[3] & 0x0f) + 9 : in[3] & 0x0f);
return 0;
}
}
return 0;
}
static int vsvtabConnect(sqlite3* db,
void* pAux,
int argc,
const char* const* argv,
sqlite3_vtab** ppVtab,
char** pzErr) {
VsvTable* pNew = 0;
int affinity = -1;
int bHeader = -1;
int validateUTF8 = -1;
int rc = SQLITE_OK;
size_t i, j;
int b;
int nCol = -99;
int nSkip = -1;
int bNulls = -1;
VsvReader sRdr;
static const char* azParam[] = {"filename", "data", "schema", "fsep", "rsep", "dsep"};
char* azPValue[6];
#define VSV_FILENAME (azPValue[0])
#define VSV_DATA (azPValue[1])
#define VSV_SCHEMA (azPValue[2])
#define VSV_FSEP (azPValue[3])
#define VSV_RSEP (azPValue[4])
#define VSV_DSEP (azPValue[5])
assert(sizeof(azPValue) == sizeof(azParam));
memset(&sRdr, 0, sizeof(sRdr));
memset(azPValue, 0, sizeof(azPValue));
for (i = 3; i < (size_t)argc; i++) {
const char* z = argv[i];
const char* zValue;
for (j = 0; j < sizeof(azParam) / sizeof(azParam[0]); j++) {
if (vsv_string_parameter(&sRdr, azParam[j], z, &azPValue[j])) {
break;
}
}
if (j < sizeof(azParam) / sizeof(azParam[0])) {
if (sRdr.zErr[0]) {
goto vsvtab_connect_error;
}
} else if (vsv_boolean_parameter("header", 6, z, &b)) {
if (bHeader >= 0) {
vsv_errmsg(&sRdr, "more than one 'header' parameter");
goto vsvtab_connect_error;
}
bHeader = b;
} else if (vsv_boolean_parameter("validatetext", 12, z, &b)) {
if (validateUTF8 >= 0) {
vsv_errmsg(&sRdr, "more than one 'validatetext' parameter");
goto vsvtab_connect_error;
}
validateUTF8 = b;
} else if (vsv_boolean_parameter("nulls", 5, z, &b)) {
if (bNulls >= 0) {
vsv_errmsg(&sRdr, "more than one 'nulls' parameter");
goto vsvtab_connect_error;
}
bNulls = b;
} else if ((zValue = vsv_parameter("columns", 7, z)) != 0) {
if (nCol > 0) {
vsv_errmsg(&sRdr, "more than one 'columns' parameter");
goto vsvtab_connect_error;
}
nCol = atoi(zValue);
if (nCol <= 0) {
vsv_errmsg(&sRdr, "column= value must be positive");
goto vsvtab_connect_error;
}
} else if ((zValue = vsv_parameter("skip", 4, z)) != 0) {
if (nSkip > 0) {
vsv_errmsg(&sRdr, "more than one 'skip' parameter");
goto vsvtab_connect_error;
}
nSkip = atoi(zValue);
if (nSkip <= 0) {
vsv_errmsg(&sRdr, "skip= value must be positive");
goto vsvtab_connect_error;
}
} else if ((zValue = vsv_parameter("affinity", 8, z)) != 0) {
if (affinity > -1) {
vsv_errmsg(&sRdr, "more than one 'affinity' parameter");
goto vsvtab_connect_error;
}
if (sqlite3_strnicmp(zValue, "none", 4) == 0)
affinity = 0;
else if (sqlite3_strnicmp(zValue, "blob", 4) == 0)
affinity = 1;
else if (sqlite3_strnicmp(zValue, "text", 4) == 0)
affinity = 2;
else if (sqlite3_strnicmp(zValue, "integer", 7) == 0)
affinity = 3;
else if (sqlite3_strnicmp(zValue, "real", 4) == 0)
affinity = 4;
else if (sqlite3_strnicmp(zValue, "numeric", 7) == 0)
affinity = 5;
else {
vsv_errmsg(&sRdr, "unknown affinity: '%s'", zValue);
goto vsvtab_connect_error;
}
} else {
vsv_errmsg(&sRdr, "bad parameter: '%s'", z);
goto vsvtab_connect_error;
}
}
if (affinity == -1) {
affinity = 0;
}
if (bNulls == -1) {
bNulls = 0;
}
if (validateUTF8 == -1) {
validateUTF8 = 0;
}
if ((VSV_FILENAME == 0) == (VSV_DATA == 0)) {
vsv_errmsg(&sRdr, "must specify either filename= or data= but not both");
goto vsvtab_connect_error;
}
if (vsv_parse_sep_char(VSV_FSEP, ',', &(sRdr.fsep))) {
vsv_errmsg(&sRdr, "cannot parse fsep: '%s'", VSV_FSEP);
goto vsvtab_connect_error;
}
if (vsv_parse_sep_char(VSV_RSEP, '\n', &(sRdr.rsep))) {
vsv_errmsg(&sRdr, "cannot parse rsep: '%s'", VSV_RSEP);
goto vsvtab_connect_error;
}
if (vsv_parse_sep_char(VSV_DSEP, '.', &(sRdr.dsep))) {
vsv_errmsg(&sRdr, "cannot parse dsep: '%s'", VSV_DSEP);
goto vsvtab_connect_error;
}
if ((nCol <= 0 || bHeader == 1) && vsv_reader_open(&sRdr, VSV_FILENAME, VSV_DATA)) {
goto vsvtab_connect_error;
}
pNew = sqlite3_malloc(sizeof(*pNew));
*ppVtab = (sqlite3_vtab*)pNew;
if (pNew == 0) {
goto vsvtab_connect_oom;
}
memset(pNew, 0, sizeof(*pNew));
pNew->fsep = sRdr.fsep;
pNew->rsep = sRdr.rsep;
pNew->dsep = sRdr.dsep;
pNew->affinity = affinity;
pNew->validateUTF8 = validateUTF8;
pNew->nulls = bNulls;
if (VSV_SCHEMA == 0) {
sqlite3_str* pStr = sqlite3_str_new(0);
char* zSep = "";
int iCol = 0;
sqlite3_str_appendf(pStr, "CREATE TABLE x(");
if (nCol < 0 && bHeader < 1) {
nCol = 0;
do {
vsv_read_one_field(&sRdr);
nCol++;
} while (sRdr.cTerm == sRdr.fsep);
}
if (nCol > 0 && bHeader < 1) {
for (iCol = 0; iCol < nCol; iCol++) {
sqlite3_str_appendf(pStr, "%sc%d", zSep, iCol);
zSep = ",";
}
} else {
do {
char* z = vsv_read_one_field(&sRdr);
if ((nCol > 0 && iCol < nCol) || (nCol < 0 && bHeader)) {
sqlite3_str_appendf(pStr, "%s\"%w\"", zSep, z);
zSep = ",";
iCol++;
}
} while (sRdr.cTerm == sRdr.fsep);
if (nCol < 0) {
nCol = iCol;
} else {
while (iCol < nCol) {
sqlite3_str_appendf(pStr, "%sc%d", zSep, ++iCol);
zSep = ",";
}
}
}
sqlite3_str_appendf(pStr, ")");
VSV_SCHEMA = sqlite3_str_finish(pStr);
if (VSV_SCHEMA == 0) {
goto vsvtab_connect_oom;
}
} else if (nCol < 0) {
do {
vsv_read_one_field(&sRdr);
nCol++;
} while (sRdr.cTerm == sRdr.fsep);
} else if (nSkip < 1 && bHeader == 1) {
do {
vsv_read_one_field(&sRdr);
} while (sRdr.cTerm == sRdr.fsep);
}
pNew->nCol = nCol;
if (nSkip > 0) {
int tskip = nSkip + (bHeader == 1);
vsv_reader_reset(&sRdr);
if (vsv_reader_open(&sRdr, VSV_FILENAME, VSV_DATA)) {
goto vsvtab_connect_error;
}
do {
do {
if (!vsv_read_one_field(&sRdr))
goto vsvtab_connect_error;
} while (sRdr.cTerm == sRdr.fsep);
tskip--;
} while (tskip > 0 && sRdr.cTerm == sRdr.rsep);
if (tskip > 0) {
vsv_errmsg(&sRdr, "premature end of file during skip");
goto vsvtab_connect_error;
}
}
pNew->zFilename = VSV_FILENAME;
VSV_FILENAME = 0;
pNew->zData = VSV_DATA;
VSV_DATA = 0;
if (bHeader != 1 && nSkip < 1) {
pNew->iStart = 0;
} else if (pNew->zData) {
pNew->iStart = (int)sRdr.iIn;
} else {
pNew->iStart = (int)(ftell(sRdr.in) - sRdr.nIn + sRdr.iIn);
}
vsv_reader_reset(&sRdr);
rc = sqlite3_declare_vtab(db, VSV_SCHEMA);
if (rc) {
vsv_errmsg(&sRdr, "bad schema: '%s' - %s", VSV_SCHEMA, sqlite3_errmsg(db));
goto vsvtab_connect_error;
}
for (i = 0; i < sizeof(azPValue) / sizeof(azPValue[0]); i++) {
sqlite3_free(azPValue[i]);
}
sqlite3_vtab_config(db, SQLITE_VTAB_DIRECTONLY);
return SQLITE_OK;
vsvtab_connect_oom:
rc = SQLITE_NOMEM;
vsv_errmsg(&sRdr, "out of memory");
vsvtab_connect_error:
if (pNew) {
vsvtabDisconnect(&pNew->base);
}
for (i = 0; i < sizeof(azPValue) / sizeof(azPValue[0]); i++) {
sqlite3_free(azPValue[i]);
}
if (sRdr.zErr[0]) {
sqlite3_free(*pzErr);
*pzErr = sqlite3_mprintf("%s", sRdr.zErr);
}
vsv_reader_reset(&sRdr);
if (rc == SQLITE_OK) {
rc = SQLITE_ERROR;
}
return rc;
}
static void vsvtabCursorRowReset(VsvCursor* pCur) {
VsvTable* pTab = (VsvTable*)pCur->base.pVtab;
int i;
for (i = 0; i < pTab->nCol; i++) {
sqlite3_free(pCur->azVal[i]);
pCur->azVal[i] = 0;
pCur->aLen[i] = 0;
pCur->dLen[i] = -1;
}
}
static int vsvtabCreate(sqlite3* db,
void* pAux,
int argc,
const char* const* argv,
sqlite3_vtab** ppVtab,
char** pzErr) {
return vsvtabConnect(db, pAux, argc, argv, ppVtab, pzErr);
}
static int vsvtabClose(sqlite3_vtab_cursor* cur) {
VsvCursor* pCur = (VsvCursor*)cur;
vsvtabCursorRowReset(pCur);
vsv_reader_reset(&pCur->rdr);
sqlite3_free(cur);
return SQLITE_OK;
}
static int vsvtabOpen(sqlite3_vtab* p, sqlite3_vtab_cursor** ppCursor) {
VsvTable* pTab = (VsvTable*)p;
VsvCursor* pCur;
size_t nByte;
nByte = sizeof(*pCur) + (sizeof(char*) + (2 * sizeof(int))) * pTab->nCol;
pCur = sqlite3_malloc64(nByte);
if (pCur == 0)
return SQLITE_NOMEM;
memset(pCur, 0, nByte);
pCur->azVal = (char**)&pCur[1];
pCur->aLen = (int*)&pCur->azVal[pTab->nCol];
pCur->dLen = (int*)&pCur->aLen[pTab->nCol];
pCur->rdr.fsep = pTab->fsep;
pCur->rdr.rsep = pTab->rsep;
pCur->rdr.dsep = pTab->dsep;
pCur->rdr.affinity = pTab->affinity;
*ppCursor = &pCur->base;
if (vsv_reader_open(&pCur->rdr, pTab->zFilename, pTab->zData)) {
vsv_xfer_error(pTab, &pCur->rdr);
return SQLITE_ERROR;
}
return SQLITE_OK;
}
static int vsvtabNext(sqlite3_vtab_cursor* cur) {
VsvCursor* pCur = (VsvCursor*)cur;
VsvTable* pTab = (VsvTable*)cur->pVtab;
int i = 0;
char* z;
do {
z = vsv_read_one_field(&pCur->rdr);
if (z == 0) {
if (i < pTab->nCol)
pCur->dLen[i] = -1;
} else if (i < pTab->nCol) {
if (pCur->aLen[i] < pCur->rdr.n + 1) {
char* zNew = sqlite3_realloc64(pCur->azVal[i], pCur->rdr.n + 1);
if (zNew == 0) {
z = 0;
vsv_errmsg(&pCur->rdr, "out of memory");
vsv_xfer_error(pTab, &pCur->rdr);
break;
}
pCur->azVal[i] = zNew;
pCur->aLen[i] = pCur->rdr.n + 1;
}
if (!pCur->rdr.notNull && pTab->nulls) {
pCur->dLen[i] = -1;
} else {
pCur->dLen[i] = pCur->rdr.n;
memcpy(pCur->azVal[i], z, pCur->rdr.n + 1);
}
i++;
}
} while (pCur->rdr.cTerm == pCur->rdr.fsep);
if ((pCur->rdr.cTerm == EOF && i == 0)) {
pCur->iRowid = -1;
} else {
pCur->iRowid++;
while (i < pTab->nCol) {
pCur->dLen[i] = -1;
i++;
}
}
return SQLITE_OK;
}
static int vsv_isValidNumber(int dsep, char* arg) {
char* start;
char* stop;
int isValid = 0;
int hasDigit = 0;
start = arg;
stop = arg + strlen(arg) - 1;
while (start <= stop && *start == ' ') {
start++;
}
while (start <= stop && *stop == ' ') {
stop--;
}
if (start > stop) {
goto vsv_end_isValidNumber;
}
if (start <= stop && (*start == '+' || *start == '-')) {
start++;
}
if (start <= stop && isdigit(*start)) {
hasDigit = 1;
isValid = 1;
}
while (start <= stop && isdigit(*start)) {
start++;
}
if (start <= stop && *start == dsep) {
isValid = 2;
if (*start != '.') {
*start = '.';
}
start++;
}
if (start <= stop && isdigit(*start)) {
hasDigit = 1;
}
while (start <= stop && isdigit(*start)) {
start++;
}
if (!hasDigit) {
isValid = 0;
goto vsv_end_isValidNumber;
}
if (start <= stop && (*start == 'e' || *start == 'E')) {
isValid = 3;
start++;
}
if (start <= stop && isValid == 3 && (*start == '+' || *start == '-')) {
start++;
}
if (start <= stop && isValid == 3 && isdigit(*start)) {
isValid = 2;
}
while (start <= stop && isdigit(*start)) {
start++;
}
if (isValid == 3) {
isValid = 0;
}
vsv_end_isValidNumber:
if (start <= stop) {
isValid = 0;
}
return isValid;
}
static long long vsv_utf8IsValid(char* string) {
long long length = 0;
unsigned char* start;
int trailing = 0;
unsigned char c;
start = (unsigned char*)string;
while ((c = *start)) {
if (trailing) {
if ((c & 0xC0) == 0x80) {
trailing--;
start++;
length++;
continue;
} else {
length = -1;
break;
}
}
if ((c & 0x80) == 0) {
start++;
length++;
continue;
}
if ((c & 0xE0) == 0xC0) {
trailing = 1;
start++;
length++;
continue;
}
if ((c & 0xF0) == 0xE0) {
trailing = 2;
start++;
length++;
continue;
}
if ((c & 0xF8) == 0xF0) {
trailing = 3;
start++;
length++;
continue;
}
length = -1;
break;
}
return length;
}
static int vsvtabColumn(sqlite3_vtab_cursor* cur,
sqlite3_context* ctx,
int i
) {
VsvCursor* pCur = (VsvCursor*)cur;
VsvTable* pTab = (VsvTable*)cur->pVtab;
long long dLen = pCur->dLen[i];
long long length = 0;
if (i >= 0 && i < pTab->nCol && pCur->azVal[i] != 0 && dLen > -1) {
switch (pTab->affinity) {
case 0: {
if (pTab->validateUTF8) {
length = vsv_utf8IsValid(pCur->azVal[i]);
if (length == dLen) {
sqlite3_result_text(ctx, pCur->azVal[i], dLen, SQLITE_TRANSIENT);
} else {
sqlite3_result_error(ctx, "Invalid UTF8 Data", -1);
}
} else {
sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_TRANSIENT);
}
break;
}
case 1: {
sqlite3_result_blob(ctx, pCur->azVal[i], dLen, SQLITE_TRANSIENT);
break;
}
case 2: {
if (pTab->validateUTF8) {
length = vsv_utf8IsValid(pCur->azVal[i]);
if (length < dLen) {
sqlite3_result_blob(ctx, pCur->azVal[i], dLen, SQLITE_TRANSIENT);
} else {
sqlite3_result_text(ctx, pCur->azVal[i], length, SQLITE_TRANSIENT);
}
} else {
sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_TRANSIENT);
}
break;
}
case 3: {
switch (vsv_isValidNumber(pCur->rdr.dsep, pCur->azVal[i])) {
case 1: {
sqlite3_result_int64(ctx, strtoll(pCur->azVal[i], 0, 10));
break;
}
default: {
if (pTab->validateUTF8) {
length = vsv_utf8IsValid(pCur->azVal[i]);
if (length < dLen) {
sqlite3_result_blob(ctx, pCur->azVal[i], dLen, SQLITE_TRANSIENT);
} else {
sqlite3_result_text(ctx, pCur->azVal[i], length, SQLITE_TRANSIENT);
}
} else {
sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_TRANSIENT);
}
break;
}
}
break;
}
case 4: {
switch (vsv_isValidNumber(pCur->rdr.dsep, pCur->azVal[i])) {
case 1:
case 2: {
sqlite3_result_double(ctx, strtod(pCur->azVal[i], 0));
break;
}
default: {
if (pTab->validateUTF8) {
length = vsv_utf8IsValid(pCur->azVal[i]);
if (length < dLen) {
sqlite3_result_blob(ctx, pCur->azVal[i], dLen, SQLITE_TRANSIENT);
} else {
sqlite3_result_text(ctx, pCur->azVal[i], length, SQLITE_TRANSIENT);
}
} else {
sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_TRANSIENT);
}
break;
}
}
break;
}
case 5: {
switch (vsv_isValidNumber(pCur->rdr.dsep, pCur->azVal[i])) {
case 1: {
sqlite3_result_int64(ctx, strtoll(pCur->azVal[i], 0, 10));
break;
}
case 2: {
long double dv, fp, ip;
dv = strtold(pCur->azVal[i], 0);
fp = modfl(dv, &ip);
if (sizeof(long double) > sizeof(double)) {
if (fp == 0.0L && dv >= -9223372036854775808.0L &&
dv <= 9223372036854775807.0L) {
sqlite3_result_int64(ctx, (long long)dv);
} else {
sqlite3_result_double(ctx, (double)dv);
}
} else {
if (fp == 0.0L && dv >= -140737488355328.0L &&
dv <= 140737488355328.0L) {
sqlite3_result_int64(ctx, (long long)dv);
} else {
sqlite3_result_double(ctx, (double)dv);
}
}
break;
}
default: {
if (pTab->validateUTF8) {
length = vsv_utf8IsValid(pCur->azVal[i]);
if (length < dLen) {
sqlite3_result_blob(ctx, pCur->azVal[i], dLen, SQLITE_TRANSIENT);
} else {
sqlite3_result_text(ctx, pCur->azVal[i], length, SQLITE_TRANSIENT);
}
} else {
sqlite3_result_text(ctx, pCur->azVal[i], -1, SQLITE_TRANSIENT);
}
break;
}
}
}
}
}
return SQLITE_OK;
}
static int vsvtabRowid(sqlite3_vtab_cursor* cur, sqlite_int64* pRowid) {
VsvCursor* pCur = (VsvCursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
static int vsvtabEof(sqlite3_vtab_cursor* cur) {
VsvCursor* pCur = (VsvCursor*)cur;
return pCur->iRowid < 0;
}
static int vsvtabFilter(sqlite3_vtab_cursor* pVtabCursor,
int idxNum,
const char* idxStr,
int argc,
sqlite3_value** argv) {
VsvCursor* pCur = (VsvCursor*)pVtabCursor;
VsvTable* pTab = (VsvTable*)pVtabCursor->pVtab;
pCur->iRowid = 0;
if (pCur->rdr.in == 0) {
assert(pCur->rdr.zIn == pTab->zData);
assert(pTab->iStart >= 0);
assert((size_t)pTab->iStart <= pCur->rdr.nIn);
pCur->rdr.iIn = pTab->iStart;
} else {
fseek(pCur->rdr.in, pTab->iStart, SEEK_SET);
pCur->rdr.iIn = 0;
pCur->rdr.nIn = 0;
}
return vsvtabNext(pVtabCursor);
}
static int vsvtabBestIndex(sqlite3_vtab* tab, sqlite3_index_info* pIdxInfo) {
pIdxInfo->estimatedCost = 1000000;
return SQLITE_OK;
}
static sqlite3_module vsv_module = {
.xCreate = vsvtabCreate,
.xConnect = vsvtabConnect,
.xBestIndex = vsvtabBestIndex,
.xDisconnect = vsvtabDisconnect,
.xDestroy = vsvtabDisconnect,
.xOpen = vsvtabOpen,
.xClose = vsvtabClose,
.xFilter = vsvtabFilter,
.xNext = vsvtabNext,
.xEof = vsvtabEof,
.xColumn = vsvtabColumn,
.xRowid = vsvtabRowid,
};
int vsv_init(sqlite3* db) {
sqlite3_create_module(db, "vsv", &vsv_module, 0);
return SQLITE_OK;
}