#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include "lsm.h"
#include <assert.h>
#include <string.h>
typedef struct lsm1_vtab lsm1_vtab;
typedef struct lsm1_cursor lsm1_cursor;
typedef struct lsm1_vblob lsm1_vblob;
typedef unsigned char u8;
typedef unsigned int u32;
typedef sqlite3_uint64 u64;
struct lsm1_vtab {
sqlite3_vtab base;
lsm_db *pDb;
u8 keyType;
u32 nVal;
};
struct lsm1_cursor {
sqlite3_vtab_cursor base;
lsm_cursor *pLsmCur;
u8 isDesc;
u8 atEof;
u8 bUnique;
u8 *zData;
u32 nData;
u8 *aeType;
u32 *aiOfst;
u32 *aiLen;
u8 *pKey2;
u32 nKey2;
};
struct lsm1_vblob {
u8 *a;
u64 n;
u64 nAlloc;
u8 errNoMem;
};
#if defined(__GNUC__)
# define LSM1_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER) && _MSC_VER>=1310
# define LSM1_NOINLINE __declspec(noinline)
#else
# define LSM1_NOINLINE
#endif
static int lsm1VblobEnlarge(lsm1_vblob *p, u32 N){
if( p->n+N>p->nAlloc ){
if( p->errNoMem ) return 1;
p->nAlloc += N + (p->nAlloc ? p->nAlloc : N);
p->a = sqlite3_realloc64(p->a, p->nAlloc);
if( p->a==0 ){
p->n = 0;
p->nAlloc = 0;
p->errNoMem = 1;
return 1;
}
p->nAlloc = sqlite3_msize(p->a);
}
return 0;
}
static LSM1_NOINLINE void lsm1VblobEnlargeAndAppend(
lsm1_vblob *p,
const u8 *pData,
u32 N
){
if( p->n+N>p->nAlloc && lsm1VblobEnlarge(p, N) ) return;
memcpy(p->a+p->n, pData, N);
p->n += N;
}
static void lsm1VblobAppend(lsm1_vblob *p, const u8 *pData, u32 N){
sqlite3_int64 n = p->n;
if( n+N>p->nAlloc ){
lsm1VblobEnlargeAndAppend(p, pData, N);
}else{
p->n += N;
memcpy(p->a+n, pData, N);
}
}
static void lsm1VblobAppendText(lsm1_vblob *p, const char *z){
lsm1VblobAppend(p, (u8*)z, (u32)strlen(z));
}
static void lsm1Dequote(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 int lsm1Connect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
lsm1_vtab *pNew;
int rc;
char *zFilename;
u8 keyType = 0;
int i;
lsm1_vblob sql;
static const char *azTypes[] = { "UINT", "TEXT", "BLOB" };
static const u8 aeTypes[] = { SQLITE_INTEGER, SQLITE_TEXT, SQLITE_BLOB };
static const char *azArgName[] = {"filename", "key", "key type", "value1" };
for(i=0; i<sizeof(azArgName)/sizeof(azArgName[0]); i++){
if( argc<i+4 || argv[i+3]==0 || argv[i+3][0]==0 ){
*pzErr = sqlite3_mprintf("%s (%r) argument missing",
azArgName[i], i+1);
return SQLITE_ERROR;
}
}
for(i=0; i<sizeof(azTypes)/sizeof(azTypes[0]); i++){
if( sqlite3_stricmp(azTypes[i],argv[5])==0 ){
keyType = aeTypes[i];
break;
}
}
if( keyType==0 ){
*pzErr = sqlite3_mprintf("key type should be INT, TEXT, or BLOB");
return SQLITE_ERROR;
}
*ppVtab = sqlite3_malloc( sizeof(*pNew) );
pNew = (lsm1_vtab*)*ppVtab;
if( pNew==0 ){
return SQLITE_NOMEM;
}
memset(pNew, 0, sizeof(*pNew));
pNew->keyType = keyType;
rc = lsm_new(0, &pNew->pDb);
if( rc ){
*pzErr = sqlite3_mprintf("lsm_new failed with error code %d", rc);
rc = SQLITE_ERROR;
goto connect_failed;
}
zFilename = sqlite3_mprintf("%s", argv[3]);
lsm1Dequote(zFilename);
rc = lsm_open(pNew->pDb, zFilename);
sqlite3_free(zFilename);
if( rc ){
*pzErr = sqlite3_mprintf("lsm_open failed with %d", rc);
rc = SQLITE_ERROR;
goto connect_failed;
}
memset(&sql, 0, sizeof(sql));
lsm1VblobAppendText(&sql, "CREATE TABLE x(");
lsm1VblobAppendText(&sql, argv[4]);
lsm1VblobAppendText(&sql, " ");
lsm1VblobAppendText(&sql, argv[5]);
lsm1VblobAppendText(&sql, " PRIMARY KEY");
for(i=6; i<argc; i++){
lsm1VblobAppendText(&sql, ", ");
lsm1VblobAppendText(&sql, argv[i]);
pNew->nVal++;
}
lsm1VblobAppendText(&sql,
", lsm1_command HIDDEN"
", lsm1_key HIDDEN"
", lsm1_value HIDDEN) WITHOUT ROWID");
lsm1VblobAppend(&sql, (u8*)"", 1);
if( sql.errNoMem ){
rc = SQLITE_NOMEM;
goto connect_failed;
}
rc = sqlite3_declare_vtab(db, (const char*)sql.a);
sqlite3_free(sql.a);
connect_failed:
if( rc!=SQLITE_OK ){
if( pNew ){
if( pNew->pDb ) lsm_close(pNew->pDb);
sqlite3_free(pNew);
}
*ppVtab = 0;
}
return rc;
}
static int lsm1Disconnect(sqlite3_vtab *pVtab){
lsm1_vtab *p = (lsm1_vtab*)pVtab;
lsm_close(p->pDb);
sqlite3_free(p);
return SQLITE_OK;
}
static int lsm1Open(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){
lsm1_vtab *p = (lsm1_vtab*)pVtab;
lsm1_cursor *pCur;
int rc;
pCur = sqlite3_malloc64( sizeof(*pCur)
+ p->nVal*(sizeof(pCur->aiOfst)+sizeof(pCur->aiLen)+1) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
pCur->aiOfst = (u32*)&pCur[1];
pCur->aiLen = &pCur->aiOfst[p->nVal];
pCur->aeType = (u8*)&pCur->aiLen[p->nVal];
*ppCursor = &pCur->base;
rc = lsm_csr_open(p->pDb, &pCur->pLsmCur);
if( rc==LSM_OK ){
rc = SQLITE_OK;
}else{
sqlite3_free(pCur);
*ppCursor = 0;
rc = SQLITE_ERROR;
}
return rc;
}
static int lsm1Close(sqlite3_vtab_cursor *cur){
lsm1_cursor *pCur = (lsm1_cursor*)cur;
sqlite3_free(pCur->pKey2);
lsm_csr_close(pCur->pLsmCur);
sqlite3_free(pCur);
return SQLITE_OK;
}
static int lsm1Next(sqlite3_vtab_cursor *cur){
lsm1_cursor *pCur = (lsm1_cursor*)cur;
int rc = LSM_OK;
if( pCur->bUnique ){
pCur->atEof = 1;
}else{
if( pCur->isDesc ){
rc = lsm_csr_prev(pCur->pLsmCur);
}else{
rc = lsm_csr_next(pCur->pLsmCur);
}
if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)==0 ){
pCur->atEof = 1;
}
if( pCur->pKey2 && pCur->atEof==0 ){
const u8 *pVal;
u32 nVal;
assert( pCur->isDesc==0 );
rc = lsm_csr_key(pCur->pLsmCur, (const void**)&pVal, (int*)&nVal);
if( rc==LSM_OK ){
u32 len = pCur->nKey2;
int c;
if( len>nVal ) len = nVal;
c = memcmp(pVal, pCur->pKey2, len);
if( c==0 ) c = nVal - pCur->nKey2;
if( c>0 ) pCur->atEof = 1;
}
}
pCur->zData = 0;
}
return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
}
static int lsm1Eof(sqlite3_vtab_cursor *cur){
lsm1_cursor *pCur = (lsm1_cursor*)cur;
return pCur->atEof;
}
static int lsm1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
*pRowid = 0;
return SQLITE_OK;
}
#define LSM1_TYPE_NEGATIVE 0
#define LSM1_TYPE_POSITIVE 1
#define LSM1_TYPE_TEXT 2
#define LSM1_TYPE_BLOB 3
static void varintWrite32(unsigned char *z, unsigned int y){
z[0] = (unsigned char)(y>>24);
z[1] = (unsigned char)(y>>16);
z[2] = (unsigned char)(y>>8);
z[3] = (unsigned char)(y);
}
static int lsm1PutVarint64(unsigned char *z, sqlite3_uint64 x){
unsigned int w, y;
if( x<=240 ){
z[0] = (unsigned char)x;
return 1;
}
if( x<=2287 ){
y = (unsigned int)(x - 240);
z[0] = (unsigned char)(y/256 + 241);
z[1] = (unsigned char)(y%256);
return 2;
}
if( x<=67823 ){
y = (unsigned int)(x - 2288);
z[0] = 249;
z[1] = (unsigned char)(y/256);
z[2] = (unsigned char)(y%256);
return 3;
}
y = (unsigned int)x;
w = (unsigned int)(x>>32);
if( w==0 ){
if( y<=16777215 ){
z[0] = 250;
z[1] = (unsigned char)(y>>16);
z[2] = (unsigned char)(y>>8);
z[3] = (unsigned char)(y);
return 4;
}
z[0] = 251;
varintWrite32(z+1, y);
return 5;
}
if( w<=255 ){
z[0] = 252;
z[1] = (unsigned char)w;
varintWrite32(z+2, y);
return 6;
}
if( w<=65535 ){
z[0] = 253;
z[1] = (unsigned char)(w>>8);
z[2] = (unsigned char)w;
varintWrite32(z+3, y);
return 7;
}
if( w<=16777215 ){
z[0] = 254;
z[1] = (unsigned char)(w>>16);
z[2] = (unsigned char)(w>>8);
z[3] = (unsigned char)w;
varintWrite32(z+4, y);
return 8;
}
z[0] = 255;
varintWrite32(z+1, w);
varintWrite32(z+5, y);
return 9;
}
static void lsm1VblobAppendVarint(lsm1_vblob *p, sqlite3_uint64 x){
sqlite3_int64 n = p->n;
if( n+9>p->nAlloc && lsm1VblobEnlarge(p, 9) ) return;
p->n += lsm1PutVarint64(p->a+p->n, x);
}
static int lsm1GetVarint64(
const unsigned char *z,
int n,
sqlite3_uint64 *pResult
){
unsigned int x;
if( n<1 ) return 0;
if( z[0]<=240 ){
*pResult = z[0];
return 1;
}
if( z[0]<=248 ){
if( n<2 ) return 0;
*pResult = (z[0]-241)*256 + z[1] + 240;
return 2;
}
if( n<z[0]-246 ) return 0;
if( z[0]==249 ){
*pResult = 2288 + 256*z[1] + z[2];
return 3;
}
if( z[0]==250 ){
*pResult = (z[1]<<16) + (z[2]<<8) + z[3];
return 4;
}
x = (z[1]<<24) + (z[2]<<16) + (z[3]<<8) + z[4];
if( z[0]==251 ){
*pResult = x;
return 5;
}
if( z[0]==252 ){
*pResult = (((sqlite3_uint64)x)<<8) + z[5];
return 6;
}
if( z[0]==253 ){
*pResult = (((sqlite3_uint64)x)<<16) + (z[5]<<8) + z[6];
return 7;
}
if( z[0]==254 ){
*pResult = (((sqlite3_uint64)x)<<24) + (z[5]<<16) + (z[6]<<8) + z[7];
return 8;
}
*pResult = (((sqlite3_uint64)x)<<32) +
(0xffffffff & ((z[5]<<24) + (z[6]<<16) + (z[7]<<8) + z[8]));
return 9;
}
static int lsm1PutSignedVarint64(u8 *z, sqlite3_int64 v){
sqlite3_uint64 u;
if( v>=0 ){
u = (sqlite3_uint64)v;
return lsm1PutVarint64(z, u*2);
}else{
u = (sqlite3_uint64)(-1-v);
return lsm1PutVarint64(z, u*2+1);
}
}
static int lsm1GetSignedVarint64(
const unsigned char *z,
int n,
sqlite3_int64 *pResult
){
sqlite3_uint64 u = 0;
n = lsm1GetVarint64(z, n, &u);
if( u&1 ){
*pResult = -1 - (sqlite3_int64)(u>>1);
}else{
*pResult = (sqlite3_int64)(u>>1);
}
return n;
}
static int lsm1DecodeValues(lsm1_cursor *pCur){
lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab);
int i, n;
int rc;
u8 eType;
sqlite3_uint64 v;
if( pCur->zData ) return 1;
rc = lsm_csr_value(pCur->pLsmCur, (const void**)&pCur->zData,
(int*)&pCur->nData);
if( rc ) return 0;
for(i=n=0; i<pTab->nVal; i++){
v = 0;
n += lsm1GetVarint64(pCur->zData+n, pCur->nData-n, &v);
pCur->aeType[i] = eType = (u8)(v%6);
if( eType==0 ){
pCur->aiOfst[i] = (u32)(v/6);
pCur->aiLen[i] = 0;
}else{
pCur->aiOfst[i] = n;
n += (pCur->aiLen[i] = (u32)(v/6));
}
if( n>pCur->nData ) break;
}
if( i<pTab->nVal ){
pCur->zData = 0;
return 0;
}
return 1;
}
static int lsm1Column(
sqlite3_vtab_cursor *cur,
sqlite3_context *ctx,
int i
){
lsm1_cursor *pCur = (lsm1_cursor*)cur;
lsm1_vtab *pTab = (lsm1_vtab*)(cur->pVtab);
if( i==0 ){
const void *pVal;
int nVal;
if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
if( pTab->keyType==SQLITE_BLOB ){
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
}else if( pTab->keyType==SQLITE_TEXT ){
sqlite3_result_text(ctx,(const char*)pVal, nVal, SQLITE_TRANSIENT);
}else{
const unsigned char *z = (const unsigned char*)pVal;
sqlite3_uint64 v1;
lsm1GetVarint64(z, nVal, &v1);
sqlite3_result_int64(ctx, (sqlite3_int64)v1);
}
}
}else if( i>pTab->nVal ){
if( i==pTab->nVal+2 ){
const void *pVal;
int nVal;
if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
}
}else if( i==pTab->nVal+3 ){
const void *pVal;
int nVal;
if( lsm_csr_value(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
}
}
}else if( lsm1DecodeValues(pCur) ){
const u8 *zData;
u32 nData;
i--;
zData = pCur->zData + pCur->aiOfst[i];
nData = pCur->aiLen[i];
switch( pCur->aeType[i] ){
case 0: {
sqlite3_result_int(ctx, pCur->aiOfst[i]);
break;
}
case SQLITE_INTEGER: {
sqlite3_int64 v;
lsm1GetSignedVarint64(zData, nData, &v);
sqlite3_result_int64(ctx, v);
break;
}
case SQLITE_FLOAT: {
double v;
if( nData==sizeof(v) ){
memcpy(&v, zData, sizeof(v));
sqlite3_result_double(ctx, v);
}
break;
}
case SQLITE_TEXT: {
sqlite3_result_text(ctx, (const char*)zData, nData, SQLITE_TRANSIENT);
break;
}
case SQLITE_BLOB: {
sqlite3_result_blob(ctx, zData, nData, SQLITE_TRANSIENT);
break;
}
default: {
}
}
}
return SQLITE_OK;
}
static void lsm1KeyFromValue(
int keyType,
sqlite3_value *pValue,
u8 *pBuf,
const u8 **ppKey,
int *pnKey
){
if( keyType==SQLITE_BLOB ){
*ppKey = (const u8*)sqlite3_value_blob(pValue);
*pnKey = sqlite3_value_bytes(pValue);
}else if( keyType==SQLITE_TEXT ){
*ppKey = (const u8*)sqlite3_value_text(pValue);
*pnKey = sqlite3_value_bytes(pValue);
}else{
sqlite3_int64 v = sqlite3_value_int64(pValue);
if( v<0 ) v = 0;
*pnKey = lsm1PutVarint64(pBuf, v);
*ppKey = pBuf;
}
}
static int lsm1Filter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
lsm1_cursor *pCur = (lsm1_cursor *)pVtabCursor;
lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab);
int rc = LSM_OK;
int seekType = -1;
const u8 *pVal = 0;
int nVal;
u8 keyType = pTab->keyType;
u8 aKey1[16];
pCur->atEof = 1;
sqlite3_free(pCur->pKey2);
pCur->pKey2 = 0;
if( idxNum<99 ){
lsm1KeyFromValue(keyType, argv[0], aKey1, &pVal, &nVal);
}
switch( idxNum ){
case 0: {
assert( argc==1 );
seekType = LSM_SEEK_EQ;
pCur->isDesc = 0;
pCur->bUnique = 1;
break;
}
case 1: {
u8 aKey[12];
seekType = LSM_SEEK_GE;
pCur->isDesc = 0;
pCur->bUnique = 0;
if( keyType==SQLITE_INTEGER ){
sqlite3_int64 v = sqlite3_value_int64(argv[1]);
if( v<0 ) v = 0;
pCur->nKey2 = lsm1PutVarint64(aKey, (sqlite3_uint64)v);
pCur->pKey2 = sqlite3_malloc( pCur->nKey2 );
if( pCur->pKey2==0 ) return SQLITE_NOMEM;
memcpy(pCur->pKey2, aKey, pCur->nKey2);
}else{
pCur->nKey2 = sqlite3_value_bytes(argv[1]);
pCur->pKey2 = sqlite3_malloc( pCur->nKey2 );
if( pCur->pKey2==0 ) return SQLITE_NOMEM;
if( keyType==SQLITE_BLOB ){
memcpy(pCur->pKey2, sqlite3_value_blob(argv[1]), pCur->nKey2);
}else{
memcpy(pCur->pKey2, sqlite3_value_text(argv[1]), pCur->nKey2);
}
}
break;
}
case 2: {
seekType = LSM_SEEK_GE;
pCur->isDesc = 0;
pCur->bUnique = 0;
break;
}
case 3: {
seekType = LSM_SEEK_LE;
pCur->isDesc = 1;
pCur->bUnique = 0;
break;
}
default: {
pCur->isDesc = 0;
pCur->bUnique = 0;
break;
}
}
if( pVal ){
rc = lsm_csr_seek(pCur->pLsmCur, pVal, nVal, seekType);
}else{
rc = lsm_csr_first(pCur->pLsmCur);
}
if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)!=0 ){
pCur->atEof = 0;
}
return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
}
static int lsm1BestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i;
int idxNum = 99;
int nArg = 0;
int argIdx = -1;
int iIdx2 = -1;
int omit1 = 0;
int omit2 = 0;
const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->usable==0 ) continue;
if( pConstraint->iColumn!=0 ) continue;
switch( pConstraint->op ){
case SQLITE_INDEX_CONSTRAINT_EQ: {
if( idxNum>0 ){
argIdx = i;
iIdx2 = -1;
idxNum = 0;
omit1 = 1;
}
break;
}
case SQLITE_INDEX_CONSTRAINT_GE:
case SQLITE_INDEX_CONSTRAINT_GT: {
if( idxNum==99 ){
argIdx = i;
idxNum = 2;
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE;
}else if( idxNum==3 ){
iIdx2 = idxNum;
omit2 = omit1;
argIdx = i;
idxNum = 1;
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_GE;
}
break;
}
case SQLITE_INDEX_CONSTRAINT_LE:
case SQLITE_INDEX_CONSTRAINT_LT: {
if( idxNum==99 ){
argIdx = i;
idxNum = 3;
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE;
}else if( idxNum==2 ){
iIdx2 = i;
idxNum = 1;
omit1 = pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE;
}
break;
}
}
}
if( argIdx>=0 ){
pIdxInfo->aConstraintUsage[argIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[argIdx].omit = omit1;
}
if( iIdx2>=0 ){
pIdxInfo->aConstraintUsage[iIdx2].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[iIdx2].omit = omit2;
}
if( idxNum==0 ){
pIdxInfo->estimatedCost = (double)1;
pIdxInfo->estimatedRows = 1;
pIdxInfo->orderByConsumed = 1;
}else if( idxNum==1 ){
pIdxInfo->estimatedCost = (double)100;
pIdxInfo->estimatedRows = 100;
}else if( idxNum<99 ){
pIdxInfo->estimatedCost = (double)5000;
pIdxInfo->estimatedRows = 5000;
}else{
pIdxInfo->estimatedCost = (double)2147483647;
pIdxInfo->estimatedRows = 2147483647;
}
pIdxInfo->idxNum = idxNum;
return SQLITE_OK;
}
int lsm1Update(
sqlite3_vtab *pVTab,
int argc,
sqlite3_value **argv,
sqlite_int64 *pRowid
){
lsm1_vtab *p = (lsm1_vtab*)pVTab;
int nKey, nKey2;
int i;
int rc = LSM_OK;
const u8 *pKey, *pKey2;
unsigned char aKey[16];
unsigned char pSpace[16];
lsm1_vblob val;
if( argc==1 ){
lsm1KeyFromValue(p->keyType, argv[0], aKey, &pKey, &nKey);
lsm_delete(p->pDb, pKey, nKey);
return SQLITE_OK;
}
if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){
lsm1KeyFromValue(p->keyType, argv[0], aKey, &pKey, &nKey);
lsm1KeyFromValue(p->keyType, argv[1], pSpace, &pKey2, &nKey2);
if( nKey!=nKey2 || memcmp(pKey, pKey2, nKey)!=0 ){
lsm_delete(p->pDb, pKey, nKey);
}
}
if( sqlite3_value_type(argv[3+p->nVal])!=SQLITE_NULL ){
return SQLITE_OK;
}
lsm1KeyFromValue(p->keyType, argv[2], aKey, &pKey, &nKey);
memset(&val, 0, sizeof(val));
for(i=0; i<p->nVal; i++){
sqlite3_value *pArg = argv[3+i];
u8 eType = sqlite3_value_type(pArg);
switch( eType ){
case SQLITE_NULL: {
lsm1VblobAppendVarint(&val, SQLITE_NULL);
break;
}
case SQLITE_INTEGER: {
sqlite3_int64 v = sqlite3_value_int64(pArg);
if( v>=0 && v<=240/6 ){
lsm1VblobAppendVarint(&val, v*6);
}else{
int n = lsm1PutSignedVarint64(pSpace, v);
lsm1VblobAppendVarint(&val, SQLITE_INTEGER + n*6);
lsm1VblobAppend(&val, pSpace, n);
}
break;
}
case SQLITE_FLOAT: {
double r = sqlite3_value_double(pArg);
lsm1VblobAppendVarint(&val, SQLITE_FLOAT + 8*6);
lsm1VblobAppend(&val, (u8*)&r, sizeof(r));
break;
}
case SQLITE_BLOB: {
int n = sqlite3_value_bytes(pArg);
lsm1VblobAppendVarint(&val, n*6 + SQLITE_BLOB);
lsm1VblobAppend(&val, sqlite3_value_blob(pArg), n);
break;
}
case SQLITE_TEXT: {
int n = sqlite3_value_bytes(pArg);
lsm1VblobAppendVarint(&val, n*6 + SQLITE_TEXT);
lsm1VblobAppend(&val, sqlite3_value_text(pArg), n);
break;
}
}
}
if( val.errNoMem ){
return SQLITE_NOMEM;
}
rc = lsm_insert(p->pDb, pKey, nKey, val.a, val.n);
sqlite3_free(val.a);
return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
}
static int lsm1Begin(sqlite3_vtab *pVtab){
lsm1_vtab *p = (lsm1_vtab*)pVtab;
int rc = lsm_begin(p->pDb, 1);
return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
}
static int lsm1Sync(sqlite3_vtab *pVtab){
return SQLITE_OK;
}
static int lsm1Commit(sqlite3_vtab *pVtab){
lsm1_vtab *p = (lsm1_vtab*)pVtab;
int rc = lsm_commit(p->pDb, 0);
return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
}
static int lsm1Rollback(sqlite3_vtab *pVtab){
lsm1_vtab *p = (lsm1_vtab*)pVtab;
int rc = lsm_rollback(p->pDb, 0);
return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
}
static sqlite3_module lsm1Module = {
0,
lsm1Connect,
lsm1Connect,
lsm1BestIndex,
lsm1Disconnect,
lsm1Disconnect,
lsm1Open,
lsm1Close,
lsm1Filter,
lsm1Next,
lsm1Eof,
lsm1Column,
lsm1Rowid,
lsm1Update,
lsm1Begin,
lsm1Sync,
lsm1Commit,
lsm1Rollback,
0,
0,
};
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_lsm_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
SQLITE_EXTENSION_INIT2(pApi);
rc = sqlite3_create_module(db, "lsm1", &lsm1Module, 0);
return rc;
}