diff --git a/ext/lsm1/Makefile b/ext/lsm1/Makefile index 46ef9e2534..2ce8831074 100644 --- a/ext/lsm1/Makefile +++ b/ext/lsm1/Makefile @@ -18,7 +18,8 @@ LSMOBJ = \ lsm_str.o \ lsm_tree.o \ lsm_unix.o \ - lsm_varint.o + lsm_varint.o \ + lsm_vtab.o LSMHDR = \ lsm.h \ diff --git a/ext/lsm1/lsm.h b/ext/lsm1/lsm.h index ee0f8f2ac7..48701c4c5e 100644 --- a/ext/lsm1/lsm.h +++ b/ext/lsm1/lsm.h @@ -620,10 +620,10 @@ int lsm_csr_last(lsm_cursor *pCsr); **
  • At least one seek function must have been called on the cursor. **
  • To call lsm_csr_next(), the most recent call to a seek function must ** have been either lsm_csr_first() or a call to lsm_csr_seek() specifying -** LSM_SEEK_GE. +** LSM_SEEK_GE. **
  • To call lsm_csr_prev(), the most recent call to a seek function must -** have been either lsm_csr_first() or a call to lsm_csr_seek() specifying -** LSM_SEEK_GE. +** have been either lsm_csr_last() or a call to lsm_csr_seek() specifying +** LSM_SEEK_LE. ** ** ** Otherwise, if the above conditions are not met when lsm_csr_next or diff --git a/ext/lsm1/lsm_sorted.c b/ext/lsm1/lsm_sorted.c index 1b38e5b505..c7b03c8452 100644 --- a/ext/lsm1/lsm_sorted.c +++ b/ext/lsm1/lsm_sorted.c @@ -70,7 +70,6 @@ #ifndef _LSM_INT_H # include "lsmInt.h" #endif -#include "sqlite3.h" /* only for sqlite3_snprintf() */ #define LSM_LOG_STRUCTURE 0 #define LSM_LOG_DATA 0 @@ -5470,16 +5469,17 @@ static int fileToString( char *zSeg; zSeg = segToString(pDb->pEnv, pSeg, nMin); - sqlite3_snprintf(nBuf-i, &aBuf[i], "%s", zSeg); + snprintf(&aBuf[i], nBuf-i, "%s", zSeg); i += strlen(&aBuf[i]); lsmFree(pDb->pEnv, zSeg); #ifdef LSM_LOG_FREELIST lsmInfoArrayStructure(pDb, 1, pSeg->iFirst, &zSeg); - sqlite3_snprintf(nBuf-i, &aBuf[i], " (%s)", zSeg); + snprintf(&aBuf[i], nBuf-1, " (%s)", zSeg); i += strlen(&aBuf[i]); lsmFree(pDb->pEnv, zSeg); #endif + aBuf[nBuf] = 0; }else{ aBuf[0] = '\0'; } @@ -5880,7 +5880,7 @@ void lsmSortedDumpStructure( } if( i==0 ){ - sqlite3_snprintf(sizeof(zLevel), zLevel, "L%d: (age=%d) (flags=%.4x)", + snprintf(zLevel, sizeof(zLevel), "L%d: (age=%d) (flags=%.4x)", iLevel, (int)pLevel->iAge, (int)pLevel->flags ); }else{ diff --git a/ext/lsm1/lsm_vtab.c b/ext/lsm1/lsm_vtab.c new file mode 100644 index 0000000000..88312a6538 --- /dev/null +++ b/ext/lsm1/lsm_vtab.c @@ -0,0 +1,483 @@ +/* +** 2015-11-16 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file implements a simple virtual table wrapper around the LSM +** storage engine from SQLite4. +*/ +#include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#include "lsm.h" +#include +#include + +/* Forward declaration of subclasses of virtual table objects */ +typedef struct lsm1_vtab lsm1_vtab; +typedef struct lsm1_cursor lsm1_cursor; + +/* Primitive types */ +typedef unsigned char u8; + +/* An open connection to an LSM table */ +struct lsm1_vtab { + sqlite3_vtab base; /* Base class - must be first */ + lsm_db *pDb; /* Open connection to the LSM table */ +}; + + +/* lsm1_cursor is a subclass of sqlite3_vtab_cursor which will +** serve as the underlying representation of a cursor that scans +** over rows of the result +*/ +struct lsm1_cursor { + sqlite3_vtab_cursor base; /* Base class - must be first */ + lsm_cursor *pLsmCur; /* The LSM cursor */ + u8 isDesc; /* 0: scan forward. 1: scan reverse */ + u8 atEof; /* True if the scan is complete */ + u8 bUnique; /* True if no more than one row of output */ +}; + +/* +** The lsm1Connect() method is invoked to create a new +** lsm1_vtab that describes the virtual table. +*/ +static int lsm1Connect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + lsm1_vtab *pNew; + int rc; + + if( argc!=4 || argv[3]==0 || argv[3][0]==0 ){ + *pzErr = sqlite3_mprintf("filename argument missing"); + return SQLITE_ERROR; + } + *ppVtab = sqlite3_malloc( sizeof(*pNew) ); + pNew = (lsm1_vtab*)*ppVtab; + if( pNew==0 ){ + return SQLITE_NOMEM; + } + memset(pNew, 0, sizeof(*pNew)); + 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; + } + rc = lsm_open(pNew->pDb, argv[0]); + if( rc ){ + *pzErr = sqlite3_mprintf("lsm_open failed with %d", rc); + rc = SQLITE_ERROR; + goto connect_failed; + } + +/* Column numbers */ +#define LSM1_COLUMN_KEY 0 +#define LSM1_COLUMN_VALUE 1 +#define LSM1_COLUMN_COMMAND 2 + + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(key,value,command hidden)"); +connect_failed: + if( rc!=SQLITE_OK ){ + if( pNew ){ + if( pNew->pDb ) lsm_close(pNew->pDb); + sqlite3_free(pNew); + } + *ppVtab = 0; + } + return rc; +} + +/* +** This method is the destructor for lsm1_cursor objects. +*/ +static int lsm1Disconnect(sqlite3_vtab *pVtab){ + lsm1_vtab *p = (lsm1_vtab*)pVtab; + lsm_close(p->pDb); + sqlite3_free(p); + return SQLITE_OK; +} + +/* +** Constructor for a new lsm1_cursor object. +*/ +static int lsm1Open(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){ + lsm1_vtab *p = (lsm1_vtab*)pVtab; + lsm1_cursor *pCur; + int rc; + pCur = sqlite3_malloc( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + *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; +} + +/* +** Destructor for a lsm1_cursor. +*/ +static int lsm1Close(sqlite3_vtab_cursor *cur){ + lsm1_cursor *pCur = (lsm1_cursor*)cur; + lsm_csr_close(pCur->pLsmCur); + sqlite3_free(pCur); + return SQLITE_OK; +} + + +/* +** Advance a lsm1_cursor to its next row of output. +*/ +static int lsm1Next(sqlite3_vtab_cursor *cur){ + lsm1_cursor *pCur = (lsm1_cursor*)cur; + int rc; + 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; + } + } + return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; +} + +/* +** Return TRUE if the cursor has been moved off of the last +** row of output. +*/ +static int lsm1Eof(sqlite3_vtab_cursor *cur){ + lsm1_cursor *pCur = (lsm1_cursor*)cur; + return pCur->atEof; +} + +/* +** Return values of columns for the row at which the lsm1_cursor +** is currently pointing. +*/ +static int lsm1Column( + sqlite3_vtab_cursor *cur, /* The cursor */ + sqlite3_context *ctx, /* First argument to sqlite3_result_...() */ + int i /* Which column to return */ +){ + lsm1_cursor *pCur = (lsm1_cursor*)cur; + switch( i ){ + case LSM1_COLUMN_KEY: { + const void *pVal; + int nVal; + if( lsm_csr_value(pCur->pLsmCur, (const void**)&pVal, &nVal)==LSM_OK ){ + sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT); + } + break; + } + case LSM1_COLUMN_VALUE: { + const unsigned char *aVal; + int nVal; + if( lsm_csr_value(pCur->pLsmCur, (const void**)&aVal, &nVal)==LSM_OK + && nVal>=1 + ){ + switch( aVal[0] ){ + case SQLITE_FLOAT: + case SQLITE_INTEGER: { + sqlite3_uint64 x = 0; + int j; + for(j=1; j<=8; j++){ + x = (x<<8) | aVal[j]; + } + if( aVal[0]==SQLITE_INTEGER ){ + sqlite3_result_int64(ctx, *(sqlite3_int64*)&x); + }else{ + sqlite3_result_double(ctx, *(double*)&x); + } + break; + } + case SQLITE_TEXT: { + sqlite3_result_text(ctx, (char*)&aVal[1], nVal-1, SQLITE_TRANSIENT); + break; + } + case SQLITE_BLOB: { + sqlite3_result_blob(ctx, &aVal[1], nVal-1, SQLITE_TRANSIENT); + break; + } + } + } + break; + } + default: { + break; + } + } + return SQLITE_OK; +} + +/* +** Rowids are not supported by the underlying virtual table. So always +** return 0 for the rowid. +*/ +static int lsm1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + *pRowid = 0; + return SQLITE_OK; +} + +/* Move to the first row to return. +*/ +static int lsm1Filter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + lsm1_cursor *pCur = (lsm1_cursor *)pVtabCursor; + int rc; + if( idxNum==1 ){ + assert( argc==1 ); + pCur->isDesc = 0; + pCur->bUnique = 1; + pCur->atEof = 1; + if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ + const void *pVal = sqlite3_value_blob(argv[0]); + int nVal = sqlite3_value_bytes(argv[0]); + rc = lsm_csr_seek(pCur->pLsmCur, pVal, nVal, LSM_SEEK_EQ); + if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)!=0 ){ + pCur->atEof = 0; + } + } + }else{ + rc = lsm_csr_first(pCur->pLsmCur); + pCur->atEof = 0; + pCur->isDesc = 0; + pCur->bUnique = 0; + } + return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; +} + +/* +** Only comparisons against the key are allowed. The idxNum defines +** which comparisons are available: +** +** 0 Full table scan only +** 1 key==? single argument for ? +** +*/ +static int lsm1BestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + int i; /* Loop over constraints */ + int idxNum = 0; /* The query plan bitmask */ + int nArg = 0; /* Number of arguments to xFilter */ + int eqIdx = -1; /* Index of the key== constraint, or -1 if none */ + + const struct sqlite3_index_constraint *pConstraint; + pConstraint = pIdxInfo->aConstraint; + for(i=0; inConstraint && idxNum<16; i++, pConstraint++){ + if( pConstraint->usable==0 ) continue; + if( pConstraint->iColumn!=LSM1_COLUMN_KEY ) continue; + if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue; + switch( pConstraint->op ){ + case SQLITE_INDEX_CONSTRAINT_EQ: { + eqIdx = i; + idxNum = 1; + break; + } + } + } + if( eqIdx>=0 ){ + pIdxInfo->aConstraintUsage[eqIdx].argvIndex = ++nArg; + pIdxInfo->aConstraintUsage[eqIdx].omit = 1; + } + if( idxNum==1 ){ + pIdxInfo->estimatedCost = (double)1; + pIdxInfo->estimatedRows = 1; + pIdxInfo->orderByConsumed = 1; + }else{ + /* Full table scan */ + pIdxInfo->estimatedCost = (double)2147483647; + pIdxInfo->estimatedRows = 2147483647; + } + pIdxInfo->idxNum = idxNum; + return SQLITE_OK; +} + +/* +** The xUpdate method is normally used for INSERT, REPLACE, UPDATE, and +** DELETE. But this virtual table only supports INSERT and REPLACE. +** DELETE is accomplished by inserting a record with a value of NULL. +** UPDATE is achieved by using REPLACE. +*/ +int lsm1Update( + sqlite3_vtab *pVTab, + int argc, + sqlite3_value **argv, + sqlite_int64 *pRowid +){ + lsm1_vtab *p = (lsm1_vtab*)pVTab; + const void *pKey; + int nKey; + int eType; + int rc; + sqlite3_value *pValue; + if( argc==1 ){ + pVTab->zErrMsg = sqlite3_mprintf("cannot DELETE"); + return SQLITE_ERROR; + } + if( sqlite3_value_type(argv[0])!=SQLITE_NULL ){ + pVTab->zErrMsg = sqlite3_mprintf("cannot UPDATE"); + return SQLITE_ERROR; + } + + /* "INSERT INTO tab(command) VALUES('....')" is used to implement + ** special commands. + */ + if( sqlite3_value_type(argv[2+LSM1_COLUMN_COMMAND])!=SQLITE_NULL ){ + return SQLITE_OK; + } + if( sqlite3_value_type(argv[2+LSM1_COLUMN_KEY])!=SQLITE_BLOB ){ + pVTab->zErrMsg = sqlite3_mprintf("BLOB keys only"); + return SQLITE_ERROR; + } + pKey = sqlite3_value_blob(argv[2+LSM1_COLUMN_KEY]); + nKey = sqlite3_value_bytes(argv[2+LSM1_COLUMN_KEY]); + pValue = argv[2+LSM1_COLUMN_VALUE]; + eType = sqlite3_value_type(pValue); + switch( eType ){ + case SQLITE_NULL: { + rc = lsm_delete(p->pDb, pKey, nKey); + break; + } + case SQLITE_BLOB: + case SQLITE_TEXT: { + const unsigned char *pVal; + unsigned char *pData; + int nVal; + if( eType==SQLITE_TEXT ){ + pVal = sqlite3_value_text(pValue); + }else{ + pVal = (unsigned char*)sqlite3_value_blob(pValue); + } + nVal = sqlite3_value_bytes(pValue); + pData = sqlite3_malloc( nVal+1 ); + if( pData==0 ){ + rc = SQLITE_NOMEM; + }else{ + pData[0] = eType; + memcpy(&pData[1], pVal, nVal); + rc = lsm_insert(p->pDb, pKey, nKey, pData, nVal+1); + sqlite3_free(pData); + } + break; + } + case SQLITE_INTEGER: + case SQLITE_FLOAT: { + sqlite3_uint64 x; + unsigned char aVal[9]; + int i; + if( eType==SQLITE_INTEGER ){ + *(sqlite3_int64*)&x = sqlite3_value_int64(pValue); + }else{ + *(double*)&x = sqlite3_value_double(pValue); + } + for(i=9; i>=1; i--){ + aVal[i] = x & 0xff; + x >>= 8; + } + aVal[0] = eType; + rc = lsm_insert(p->pDb, pKey, nKey, aVal, 9); + break; + } + } + return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR; +} + +/* Begin a transaction +*/ +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; +} + +/* Phase 1 of a transaction commit. +*/ +static int lsm1Sync(sqlite3_vtab *pVtab){ + return SQLITE_OK; +} + +/* Commit a transaction +*/ +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; +} + +/* Rollback a transaction +*/ +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; +} + +/* +** This following structure defines all the methods for the +** generate_lsm1 virtual table. +*/ +static sqlite3_module lsm1Module = { + 0, /* iVersion */ + lsm1Connect, /* xCreate */ + lsm1Connect, /* xConnect */ + lsm1BestIndex, /* xBestIndex */ + lsm1Disconnect, /* xDisconnect */ + lsm1Disconnect, /* xDestroy */ + lsm1Open, /* xOpen - open a cursor */ + lsm1Close, /* xClose - close a cursor */ + lsm1Filter, /* xFilter - configure scan constraints */ + lsm1Next, /* xNext - advance a cursor */ + lsm1Eof, /* xEof - check for end of scan */ + lsm1Column, /* xColumn - read data */ + lsm1Rowid, /* xRowid - read data */ + lsm1Update, /* xUpdate */ + lsm1Begin, /* xBegin */ + lsm1Sync, /* xSync */ + lsm1Commit, /* xCommit */ + lsm1Rollback, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ +}; + + +#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; +} diff --git a/manifest b/manifest index bc7d46e361..24460438df 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Import\sthe\sLSM\scode\sfrom\sSQLite4\sfor\suse\sin\san\sexperimental\svirtual\stable.\nNB:\sThis\sis\sa\sspeculative\sexperiment\sand\scould\seasily\sresult\sin\sa\sdead-end\nbranch. -D 2015-11-16T16:00:22.063 +C The\svirtual\stable\scompiles\sbut\sdoes\snot\swork\sand\sis\smissing\smany\sfeatures.\nThis\sis\san\sincremental\scheck-in. +D 2015-11-17T00:15:21.503 F Makefile.in d828db6afa6c1fa060d01e33e4674408df1942a1 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc e928e68168df69b353300ac87c10105206653a03 @@ -190,8 +190,8 @@ F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43 F ext/icu/icu.c b2732aef0b076e4276d9b39b5a33cec7a05e1413 F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37 -F ext/lsm1/Makefile a921a54e7364814897a6ea74638741c672f42f07 -F ext/lsm1/lsm.h d88fce3ea06730f7967159b7459b037e01d82df5 +F ext/lsm1/Makefile f8d8f229fbeafa5352edab5be7e722ed71233b12 +F ext/lsm1/lsm.h 0f6f64ff071471cb87bf98beb8386566f30ea001 F ext/lsm1/lsmInt.h bc270dd81b3355c7410b06a6d54dd3eb9493a3e8 F ext/lsm1/lsm_ckpt.c e7907e782fe2e95de0833675e35e726e487cc4cd F ext/lsm1/lsm_file.c 63796f5741422064b146cfbffbaeaeda14b70175 @@ -200,11 +200,12 @@ F ext/lsm1/lsm_main.c f52eada2910f8a57bd4cafcee39c6c375f6b7ed8 F ext/lsm1/lsm_mem.c 4c51ea9fa285ee6e35301b33491642d071740a0a F ext/lsm1/lsm_mutex.c 378edf0a2b142b4f7640ee982df06d50b98788ea F ext/lsm1/lsm_shared.c 54cc3a5157c6abd77f7d3ae60708b9f7bf022b3c -F ext/lsm1/lsm_sorted.c aca377f4091263a1103cf409340130bcafd87939 +F ext/lsm1/lsm_sorted.c 4a9e3ffecda87b379ed757b59c9cbcd84a80b55c F ext/lsm1/lsm_str.c 77ebdd5040ddf267a6f724d4c83132d2dce8a226 F ext/lsm1/lsm_tree.c 5d9fb2bc58a1a70c75126bd8d7198f7b627e165b F ext/lsm1/lsm_unix.c fcaf5b6738713f1229dc0e1a90393ecf24f787f2 F ext/lsm1/lsm_varint.c b19ae9bd26b5a1e8402fb8a564b25d9542338a41 +F ext/lsm1/lsm_vtab.c a0c0b73c5ba4208282fe37acc4da3f490e823d4d F ext/misc/amatch.c a1a8f66c29d40bd71b075546ddeddb477b17a2bb F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704 F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83 @@ -1418,10 +1419,7 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P ad5fcaa583ef743d143b6c030e0d78019709fe71 -R c1b42294f3f880839d2371f275775b59 -T *branch * lsm-vtab -T *sym-lsm-vtab * -T -sym-trunk * +P 3d930501a2acb7f20932cfeb4e3fe308b4569cd6 +R a12c72fec91515b78f9b7786ff4fed28 U drh -Z b1fe55788e9f370521e84a28fea017e9 +Z 0ef63f102801971372945e050120c775 diff --git a/manifest.uuid b/manifest.uuid index 5135d99cfa..01c9a19c8a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3d930501a2acb7f20932cfeb4e3fe308b4569cd6 \ No newline at end of file +a32849d6bf66462d1f511714a00f24519d7b7079 \ No newline at end of file