#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include <ctype.h>
#include <stdio.h>
#include <math.h>
#include <limits.h>
#ifdef SQLITE_HAVE_ZLIB
#include <zlib.h>
#define fopen gzopen
#define fclose gzclose
#define fread gzfread
#define fseek gzseek
#define ftell gztell
#endif
#undef LONGDOUBLE_CONSTANT
#undef LONGDOUBLE_TYPE
#if defined(SQLITE_USE_QUADMATH) && defined(__GNUC__) && defined(_WIN64)
#include <quadmath.h>
#define LONGDOUBLE_TYPE __float128
#define LONGDOUBLE_CONSTANT(x) x##Q
#define modfl modfq
#define strtold strtoflt128
#else
#define LONGDOUBLE_TYPE long double
#define LONGDOUBLE_CONSTANT(x) x##L
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
#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
{
#ifdef SQLITE_HAVE_ZLIB
gzFile in;
#else
FILE *in;
#endif
char *z;
int n;
int nAlloc;
int nLine;
int bNotFirst;
int cTerm;
int fsep;
int rsep;
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;
}
assert( p->z==0 || p->n<p->nAlloc );
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 affinity;
int nulls;
int validateUTF8;
unsigned int tstFlags;
} VsvTable;
#define VSVTEST_FIDX 0x0001
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;
int i, j;
#ifdef SQLITE_TEST
int tstFlags = 0;
#endif
int b;
int nCol = -99;
int nSkip = -1;
int bNulls = -1;
VsvReader sRdr;
static const char *azParam[] = {
"filename", "data", "schema", "fsep", "rsep"
};
char *azPValue[5];
#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])
assert( sizeof(azPValue)==sizeof(azParam) );
memset(&sRdr, 0, sizeof(sRdr));
memset(azPValue, 0, sizeof(azPValue));
for (i=3; i<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
#ifdef SQLITE_TEST
if ((zValue = vsv_parameter("testflags",9,z))!=0)
{
tstFlags = (unsigned int)atoi(zValue);
}
else
#endif
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 ((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->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)
{
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;
#ifdef SQLITE_TEST
pNew->tstFlags = tstFlags;
#endif
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.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(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=='.') {
isValid = 2;
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;
}
#if 0#endif
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;
static int hasExtended = 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->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->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->azVal[i]))
{
case 1:
{
sqlite_int64 ival;
ival = strtoll(pCur->azVal[i], 0, 10);
if (ival > LLONG_MIN && ival < LLONG_MAX) {
sqlite3_result_int64(ctx, ival);
break;
}
}
case 2:
{
LONGDOUBLE_TYPE dv, fp, ip;
#if defined(SQLITE_USE_QUADMATH) && defined(__GNUC__) && defined(_WIN64)
if (!hasExtended) hasExtended = 1;
#else
if (!hasExtended) {
if (sizeof(long double) > sizeof(double)) {
volatile unsigned long long i = ULLONG_MAX;
volatile long double l;
volatile double d;
l = i;
d = i;
hasExtended = (d == l) ? -1 : 1;
} else {
hasExtended = -1;
}
}
#endif
dv = strtold(pCur->azVal[i], 0);
fp = modfl(dv, &ip);
if (hasExtended<0)
{
if (fp==0.0L && ip >= -9007199254740991LL && dv <= 9007199254740991LL)
{
sqlite3_result_int64(ctx, (long long)ip);
}
else
{
sqlite3_result_double(ctx, dv);
}
}
else
{
if (fp==0.0L && ip >= LLONG_MIN && ip <= LLONG_MAX)
{
sqlite3_result_int64(ctx, (long long)ip);
}
else
{
sqlite3_result_double(ctx, 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( vsv_append(&pCur->rdr, 0) ) return SQLITE_NOMEM;
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;
#ifdef SQLITE_TEST
if ((((VsvTable*)tab)->tstFlags & VSVTEST_FIDX)!=0)
{
int i;
int nConst = 0;
for (i=0; i<pIdxInfo->nConstraint; i++)
{
unsigned char op;
if (pIdxInfo->aConstraint[i].usable==0) continue;
op = pIdxInfo->aConstraint[i].op;
if (op==SQLITE_INDEX_CONSTRAINT_EQ
|| op==SQLITE_INDEX_CONSTRAINT_LIKE
|| op==SQLITE_INDEX_CONSTRAINT_GLOB
)
{
pIdxInfo->estimatedCost = 10;
pIdxInfo->aConstraintUsage[nConst].argvIndex = nConst+1;
nConst++;
}
}
}
#endif
return SQLITE_OK;
}
static sqlite3_module VsvModule = {
0,
vsvtabCreate,
vsvtabConnect,
vsvtabBestIndex,
vsvtabDisconnect,
vsvtabDisconnect,
vsvtabOpen,
vsvtabClose,
vsvtabFilter,
vsvtabNext,
vsvtabEof,
vsvtabColumn,
vsvtabRowid,
0,
0,
0,
0,
0,
0,
0,
};
#ifdef SQLITE_TEST
static int vsvtabUpdate(sqlite3_vtab *p,int n,sqlite3_value**v,sqlite3_int64*x)
{
return SQLITE_READONLY;
}
static sqlite3_module VsvModuleFauxWrite = {
0,
vsvtabCreate,
vsvtabConnect,
vsvtabBestIndex,
vsvtabDisconnect,
vsvtabDisconnect,
vsvtabOpen,
vsvtabClose,
vsvtabFilter,
vsvtabNext,
vsvtabEof,
vsvtabColumn,
vsvtabRowid,
vsvtabUpdate,
0,
0,
0,
0,
0,
0,
};
#endif
#endif
#ifndef SQLITE_CORE
#ifdef _WIN32
__declspec(dllexport)
#endif
#endif
int sqlite3_vsv_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
)
{
#ifndef SQLITE_OMIT_VIRTUALTABLE
int rc;
SQLITE_EXTENSION_INIT2(pApi);
rc = sqlite3_create_module(db, "vsv", &VsvModule, 0);
#ifdef SQLITE_TEST
if (rc==SQLITE_OK)
{
rc = sqlite3_create_module(db, "vsv_wr", &VsvModuleFauxWrite, 0);
}
#endif
return rc;
#else
return SQLITE_OK;
#endif
}
#undef modfl
#undef strtold