|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_CARRAY) |
|
|
#include "sqliteInt.h" |
|
|
#if defined(_WIN32) || defined(__RTP__) || defined(_WRS_KERNEL) |
|
|
struct iovec { |
|
|
void *iov_base; |
|
|
size_t iov_len; |
|
|
}; |
|
|
#else |
|
|
# include <sys/uio.h> |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const char *azCarrayType[] = { |
|
|
"int32", "int64", "double", "char*", "struct iovec" |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct carray_bind carray_bind; |
|
|
struct carray_bind { |
|
|
void *aData; |
|
|
int nData; |
|
|
int mFlags; |
|
|
void (*xDel)(void*); |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct carray_cursor carray_cursor; |
|
|
struct carray_cursor { |
|
|
sqlite3_vtab_cursor base; |
|
|
sqlite3_int64 iRowid; |
|
|
void *pPtr; |
|
|
sqlite3_int64 iCnt; |
|
|
unsigned char eType; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int carrayConnect( |
|
|
sqlite3 *db, |
|
|
void *pAux, |
|
|
int argc, const char *const*argv, |
|
|
sqlite3_vtab **ppVtab, |
|
|
char **pzErr |
|
|
){ |
|
|
sqlite3_vtab *pNew; |
|
|
int rc; |
|
|
|
|
|
|
|
|
#define CARRAY_COLUMN_VALUE 0 |
|
|
#define CARRAY_COLUMN_POINTER 1 |
|
|
#define CARRAY_COLUMN_COUNT 2 |
|
|
#define CARRAY_COLUMN_CTYPE 3 |
|
|
|
|
|
rc = sqlite3_declare_vtab(db, |
|
|
"CREATE TABLE x(value,pointer hidden,count hidden,ctype hidden)"); |
|
|
if( rc==SQLITE_OK ){ |
|
|
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) ); |
|
|
if( pNew==0 ) return SQLITE_NOMEM; |
|
|
memset(pNew, 0, sizeof(*pNew)); |
|
|
} |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int carrayDisconnect(sqlite3_vtab *pVtab){ |
|
|
sqlite3_free(pVtab); |
|
|
return SQLITE_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int carrayOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ |
|
|
carray_cursor *pCur; |
|
|
pCur = sqlite3_malloc( sizeof(*pCur) ); |
|
|
if( pCur==0 ) return SQLITE_NOMEM; |
|
|
memset(pCur, 0, sizeof(*pCur)); |
|
|
*ppCursor = &pCur->base; |
|
|
return SQLITE_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int carrayClose(sqlite3_vtab_cursor *cur){ |
|
|
sqlite3_free(cur); |
|
|
return SQLITE_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int carrayNext(sqlite3_vtab_cursor *cur){ |
|
|
carray_cursor *pCur = (carray_cursor*)cur; |
|
|
pCur->iRowid++; |
|
|
return SQLITE_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int carrayColumn( |
|
|
sqlite3_vtab_cursor *cur, |
|
|
sqlite3_context *ctx, |
|
|
int i |
|
|
){ |
|
|
carray_cursor *pCur = (carray_cursor*)cur; |
|
|
sqlite3_int64 x = 0; |
|
|
switch( i ){ |
|
|
case CARRAY_COLUMN_POINTER: return SQLITE_OK; |
|
|
case CARRAY_COLUMN_COUNT: x = pCur->iCnt; break; |
|
|
case CARRAY_COLUMN_CTYPE: { |
|
|
sqlite3_result_text(ctx, azCarrayType[pCur->eType], -1, SQLITE_STATIC); |
|
|
return SQLITE_OK; |
|
|
} |
|
|
default: { |
|
|
switch( pCur->eType ){ |
|
|
case CARRAY_INT32: { |
|
|
int *p = (int*)pCur->pPtr; |
|
|
sqlite3_result_int(ctx, p[pCur->iRowid-1]); |
|
|
return SQLITE_OK; |
|
|
} |
|
|
case CARRAY_INT64: { |
|
|
sqlite3_int64 *p = (sqlite3_int64*)pCur->pPtr; |
|
|
sqlite3_result_int64(ctx, p[pCur->iRowid-1]); |
|
|
return SQLITE_OK; |
|
|
} |
|
|
case CARRAY_DOUBLE: { |
|
|
double *p = (double*)pCur->pPtr; |
|
|
sqlite3_result_double(ctx, p[pCur->iRowid-1]); |
|
|
return SQLITE_OK; |
|
|
} |
|
|
case CARRAY_TEXT: { |
|
|
const char **p = (const char**)pCur->pPtr; |
|
|
sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT); |
|
|
return SQLITE_OK; |
|
|
} |
|
|
default: { |
|
|
const struct iovec *p = (struct iovec*)pCur->pPtr; |
|
|
assert( pCur->eType==CARRAY_BLOB ); |
|
|
sqlite3_result_blob(ctx, p[pCur->iRowid-1].iov_base, |
|
|
(int)p[pCur->iRowid-1].iov_len, SQLITE_TRANSIENT); |
|
|
return SQLITE_OK; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
sqlite3_result_int64(ctx, x); |
|
|
return SQLITE_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int carrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ |
|
|
carray_cursor *pCur = (carray_cursor*)cur; |
|
|
*pRowid = pCur->iRowid; |
|
|
return SQLITE_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int carrayEof(sqlite3_vtab_cursor *cur){ |
|
|
carray_cursor *pCur = (carray_cursor*)cur; |
|
|
return pCur->iRowid>pCur->iCnt; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int carrayFilter( |
|
|
sqlite3_vtab_cursor *pVtabCursor, |
|
|
int idxNum, const char *idxStr, |
|
|
int argc, sqlite3_value **argv |
|
|
){ |
|
|
carray_cursor *pCur = (carray_cursor *)pVtabCursor; |
|
|
pCur->pPtr = 0; |
|
|
pCur->iCnt = 0; |
|
|
switch( idxNum ){ |
|
|
case 1: { |
|
|
carray_bind *pBind = sqlite3_value_pointer(argv[0], "carray-bind"); |
|
|
if( pBind==0 ) break; |
|
|
pCur->pPtr = pBind->aData; |
|
|
pCur->iCnt = pBind->nData; |
|
|
pCur->eType = pBind->mFlags & 0x07; |
|
|
break; |
|
|
} |
|
|
case 2: |
|
|
case 3: { |
|
|
pCur->pPtr = sqlite3_value_pointer(argv[0], "carray"); |
|
|
pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0; |
|
|
if( idxNum<3 ){ |
|
|
pCur->eType = CARRAY_INT32; |
|
|
}else{ |
|
|
unsigned char i; |
|
|
const char *zType = (const char*)sqlite3_value_text(argv[2]); |
|
|
for(i=0; i<sizeof(azCarrayType)/sizeof(azCarrayType[0]); i++){ |
|
|
if( sqlite3_stricmp(zType, azCarrayType[i])==0 ) break; |
|
|
} |
|
|
if( i>=sizeof(azCarrayType)/sizeof(azCarrayType[0]) ){ |
|
|
pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf( |
|
|
"unknown datatype: %Q", zType); |
|
|
return SQLITE_ERROR; |
|
|
}else{ |
|
|
pCur->eType = i; |
|
|
} |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
pCur->iRowid = 1; |
|
|
return SQLITE_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int carrayBestIndex( |
|
|
sqlite3_vtab *tab, |
|
|
sqlite3_index_info *pIdxInfo |
|
|
){ |
|
|
int i; |
|
|
int ptrIdx = -1; |
|
|
int cntIdx = -1; |
|
|
int ctypeIdx = -1; |
|
|
unsigned seen = 0; |
|
|
|
|
|
const struct sqlite3_index_constraint *pConstraint; |
|
|
pConstraint = pIdxInfo->aConstraint; |
|
|
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){ |
|
|
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; |
|
|
if( pConstraint->iColumn>=0 ) seen |= 1 << pConstraint->iColumn; |
|
|
if( pConstraint->usable==0 ) continue; |
|
|
switch( pConstraint->iColumn ){ |
|
|
case CARRAY_COLUMN_POINTER: |
|
|
ptrIdx = i; |
|
|
break; |
|
|
case CARRAY_COLUMN_COUNT: |
|
|
cntIdx = i; |
|
|
break; |
|
|
case CARRAY_COLUMN_CTYPE: |
|
|
ctypeIdx = i; |
|
|
break; |
|
|
} |
|
|
} |
|
|
if( ptrIdx>=0 ){ |
|
|
pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1; |
|
|
pIdxInfo->aConstraintUsage[ptrIdx].omit = 1; |
|
|
pIdxInfo->estimatedCost = (double)1; |
|
|
pIdxInfo->estimatedRows = 100; |
|
|
pIdxInfo->idxNum = 1; |
|
|
if( cntIdx>=0 ){ |
|
|
pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2; |
|
|
pIdxInfo->aConstraintUsage[cntIdx].omit = 1; |
|
|
pIdxInfo->idxNum = 2; |
|
|
if( ctypeIdx>=0 ){ |
|
|
pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3; |
|
|
pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1; |
|
|
pIdxInfo->idxNum = 3; |
|
|
}else if( seen & (1<<CARRAY_COLUMN_CTYPE) ){ |
|
|
|
|
|
|
|
|
return SQLITE_CONSTRAINT; |
|
|
} |
|
|
}else if( seen & (1<<CARRAY_COLUMN_COUNT) ){ |
|
|
|
|
|
|
|
|
return SQLITE_CONSTRAINT; |
|
|
} |
|
|
}else{ |
|
|
pIdxInfo->estimatedCost = (double)2147483647; |
|
|
pIdxInfo->estimatedRows = 2147483647; |
|
|
pIdxInfo->idxNum = 0; |
|
|
} |
|
|
return SQLITE_OK; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static sqlite3_module carrayModule = { |
|
|
0, |
|
|
0, |
|
|
carrayConnect, |
|
|
carrayBestIndex, |
|
|
carrayDisconnect, |
|
|
0, |
|
|
carrayOpen, |
|
|
carrayClose, |
|
|
carrayFilter, |
|
|
carrayNext, |
|
|
carrayEof, |
|
|
carrayColumn, |
|
|
carrayRowid, |
|
|
0, |
|
|
0, |
|
|
0, |
|
|
0, |
|
|
0, |
|
|
0, |
|
|
0, |
|
|
0, |
|
|
0, |
|
|
0, |
|
|
0, |
|
|
0 |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void carrayBindDel(void *pPtr){ |
|
|
carray_bind *p = (carray_bind*)pPtr; |
|
|
if( p->xDel!=SQLITE_STATIC ){ |
|
|
p->xDel(p->aData); |
|
|
} |
|
|
sqlite3_free(p); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SQLITE_API int sqlite3_carray_bind( |
|
|
sqlite3_stmt *pStmt, |
|
|
int idx, |
|
|
void *aData, |
|
|
int nData, |
|
|
int mFlags, |
|
|
void (*xDestroy)(void*) |
|
|
){ |
|
|
carray_bind *pNew = 0; |
|
|
int i; |
|
|
int rc = SQLITE_OK; |
|
|
|
|
|
|
|
|
assert( CARRAY_INT32==0 && CARRAY_INT64==1 && CARRAY_DOUBLE==2 ); |
|
|
assert( CARRAY_TEXT==3 && CARRAY_BLOB==4 ); |
|
|
if( mFlags<CARRAY_INT32 || mFlags>CARRAY_BLOB ){ |
|
|
rc = SQLITE_ERROR; |
|
|
goto carray_bind_error; |
|
|
} |
|
|
|
|
|
pNew = sqlite3_malloc64(sizeof(*pNew)); |
|
|
if( pNew==0 ){ |
|
|
rc = SQLITE_NOMEM; |
|
|
goto carray_bind_error; |
|
|
} |
|
|
|
|
|
pNew->nData = nData; |
|
|
pNew->mFlags = mFlags; |
|
|
if( xDestroy==SQLITE_TRANSIENT ){ |
|
|
sqlite3_int64 sz = nData; |
|
|
switch( mFlags ){ |
|
|
case CARRAY_INT32: sz *= 4; break; |
|
|
case CARRAY_INT64: sz *= 8; break; |
|
|
case CARRAY_DOUBLE: sz *= 8; break; |
|
|
case CARRAY_TEXT: sz *= sizeof(char*); break; |
|
|
default: sz *= sizeof(struct iovec); break; |
|
|
} |
|
|
if( mFlags==CARRAY_TEXT ){ |
|
|
for(i=0; i<nData; i++){ |
|
|
const char *z = ((char**)aData)[i]; |
|
|
if( z ) sz += strlen(z) + 1; |
|
|
} |
|
|
}else if( mFlags==CARRAY_BLOB ){ |
|
|
for(i=0; i<nData; i++){ |
|
|
sz += ((struct iovec*)aData)[i].iov_len; |
|
|
} |
|
|
} |
|
|
|
|
|
pNew->aData = sqlite3_malloc64( sz ); |
|
|
if( pNew->aData==0 ){ |
|
|
rc = SQLITE_NOMEM; |
|
|
goto carray_bind_error; |
|
|
} |
|
|
|
|
|
if( mFlags==CARRAY_TEXT ){ |
|
|
char **az = (char**)pNew->aData; |
|
|
char *z = (char*)&az[nData]; |
|
|
for(i=0; i<nData; i++){ |
|
|
const char *zData = ((char**)aData)[i]; |
|
|
sqlite3_int64 n; |
|
|
if( zData==0 ){ |
|
|
az[i] = 0; |
|
|
continue; |
|
|
} |
|
|
az[i] = z; |
|
|
n = strlen(zData); |
|
|
memcpy(z, zData, n+1); |
|
|
z += n+1; |
|
|
} |
|
|
}else if( mFlags==CARRAY_BLOB ){ |
|
|
struct iovec *p = (struct iovec*)pNew->aData; |
|
|
unsigned char *z = (unsigned char*)&p[nData]; |
|
|
for(i=0; i<nData; i++){ |
|
|
size_t n = ((struct iovec*)aData)[i].iov_len; |
|
|
p[i].iov_len = n; |
|
|
p[i].iov_base = z; |
|
|
z += n; |
|
|
memcpy(p[i].iov_base, ((struct iovec*)aData)[i].iov_base, n); |
|
|
} |
|
|
}else{ |
|
|
memcpy(pNew->aData, aData, sz); |
|
|
} |
|
|
pNew->xDel = sqlite3_free; |
|
|
}else{ |
|
|
pNew->aData = aData; |
|
|
pNew->xDel = xDestroy; |
|
|
} |
|
|
return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel); |
|
|
|
|
|
carray_bind_error: |
|
|
if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){ |
|
|
xDestroy(aData); |
|
|
} |
|
|
sqlite3_free(pNew); |
|
|
return rc; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Module *sqlite3CarrayRegister(sqlite3 *db){ |
|
|
return sqlite3VtabCreateModule(db, "carray", &carrayModule, 0, 0); |
|
|
} |
|
|
|
|
|
#endif |
|
|
|