| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | #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 |
| |
|