From: drh Date: Wed, 9 Aug 2017 18:40:35 +0000 (+0000) Subject: Work toward redesigning the interface to the LSM1 virtual table. X-Git-Tag: version-3.21.0~178 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2e27d28fec2ff07b7169ecce4939b0d2351edaa7;p=thirdparty%2Fsqlite.git Work toward redesigning the interface to the LSM1 virtual table. FossilOrigin-Name: 313df946668b943b0a9a9a91fd7bafa7212d05765c7714fa6c0de46aa9062a74 --- diff --git a/ext/lsm1/lsm_vtab.c b/ext/lsm1/lsm_vtab.c index 4caa5ba4f5..0e43d1432c 100644 --- a/ext/lsm1/lsm_vtab.c +++ b/ext/lsm1/lsm_vtab.c @@ -12,6 +12,41 @@ ** ** This file implements a simple virtual table wrapper around the LSM ** storage engine from SQLite4. +** +** USAGE +** +** CREATE VIRTUAL TABLE demo USING lsm1(filename,key,keytype,value1,...); +** +** The keytype must be one of: UINT, TEXT, BLOB. All keys must be of that +** one type. "UINT" means unsigned integer. The values may be any +** SQLite datatype. +** +** INTERNALS +** +** The key encoding for BLOB and TEXT is just a copy of the blob or text. +** UTF-8 is used for text. The key encoding for UINT is the variable-length +** integer format at https://sqlite.org/src4/doc/trunk/www/varint.wiki. +** +** The values are encoded as a single blob (since that is what lsm stores as +** its content). There is a "type integer" followed by "content" for each +** value, alternating back and forth. The content might be empty. +** +** TYPE1 CONTENT1 TYPE2 CONTENT2 TYPE3 CONTENT3 .... +** +** Each "type integer" is encoded as a variable-length integer in the +** format of the link above. Let the type integer be T. The actual +** datatype is an integer 0-5 equal to T%6. Values 1 through 5 correspond +** to SQLITE_INTEGER through SQLITE_NULL. The size of the content in bytes +** is T/6. Type value 0 means that the value is an integer whose actual +** values is T/6 and there is no content. The type-value-0 integer format +** only works for integers in the range of 0 through 40. +** +** There is no content for NULL or type-0 integers. For BLOB and TEXT +** values, the content is the blob data or the UTF-8 text data. For +** non-negative integers X, the content is a variable-length integer X*2. +** For negative integers Y, the content is varaible-length integer (1-Y)*2+1. +** For FLOAT values, the content is the variable length encoding of the +** integer with the same bit pattern as the IEEE754 floating point value. */ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 @@ -22,14 +57,19 @@ SQLITE_EXTENSION_INIT1 /* Forward declaration of subclasses of virtual table objects */ typedef struct lsm1_vtab lsm1_vtab; typedef struct lsm1_cursor lsm1_cursor; +typedef struct lsm1_vblob lsm1_vblob; /* Primitive types */ typedef unsigned char u8; +typedef unsigned int u32; +typedef sqlite3_uint64 u64; /* 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 */ + u8 keyType; /* SQLITE_BLOB, _TEXT, or _INTEGER */ + u32 nVal; /* Number of value columns */ }; @@ -43,8 +83,82 @@ struct lsm1_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 */ + u8 *zData; /* Content of the current row */ + u32 nData; /* Number of bytes in the current row */ + u8 *aeType; /* Types for all column values */ + u32 *aiOfst; /* Offsets to the various fields */ + u32 *aiLen; /* Length of each field */ + u8 *pKey2; /* Loop termination key, or NULL */ + u32 nKey2; /* Length of the loop termination key */ }; +/* An extensible buffer object. +** +** Content can be appended. Space to hold new content is automatically +** allocated. +*/ +struct lsm1_vblob { + u8 *a; /* Space to hold content, from sqlite3_malloc64() */ + u64 n; /* Bytes of space used */ + u64 nAlloc; /* Bytes of space allocated */ + u8 errNoMem; /* True if a memory allocation error has been seen */ +}; + +#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 + + +/* Increase the available space in the vblob object so that it can hold +** at least N more bytes. Return the number of errors. +*/ +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; +} + +/* Append N bytes to a vblob after first enlarging it */ +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; +} + +/* Append N bytes to a vblob */ +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); + } +} + +/* append text to a vblob */ +static void lsm1VblobAppendText(lsm1_vblob *p, const char *z){ + lsm1VblobAppend(p, (u8*)z, (u32)strlen(z)); +} + /* Dequote the string */ static void lsm1Dequote(char *z){ int j; @@ -76,9 +190,28 @@ static int lsm1Connect( 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" }; - if( argc!=4 || argv[3]==0 || argv[3][0]==0 ){ - *pzErr = sqlite3_mprintf("filename argument missing"); + for(i=0; ikeyType = keyType; rc = lsm_new(0, &pNew->pDb); if( rc ){ *pzErr = sqlite3_mprintf("lsm_new failed with error code %d", rc); @@ -103,22 +237,26 @@ static int lsm1Connect( goto connect_failed; } -/* Column numbers */ -#define LSM1_COLUMN_KEY 0 -#define LSM1_COLUMN_BLOBKEY 1 -#define LSM1_COLUMN_VALUE 2 -#define LSM1_COLUMN_BLOBVALUE 3 -#define LSM1_COLUMN_COMMAND 4 - - rc = sqlite3_declare_vtab(db, - "CREATE TABLE x(" - " key," /* The primary key. Any non-NULL */ - " blobkey," /* Pure BLOB primary key */ - " value," /* The value associated with key. Any non-NULL */ - " blobvalue," /* Pure BLOB value */ - " command hidden" /* Insert here for control operations */ - ");" - ); + memset(&sql, 0, sizeof(sql)); + lsm1VblobAppendText(&sql, "CREATE TABLE x("); + lsm1VblobAppendText(&sql, argv[4]); + lsm1VblobAppendText(&sql, " "); + lsm1VblobAppendText(&sql, argv[5]); + for(i=6; inVal++; + } + lsm1VblobAppendText(&sql, + ", lsm1_command HIDDEN, lsm1_key HIDDEN, lsm1_value HIDDEN)"); + 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 ){ @@ -147,9 +285,13 @@ 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) ); + 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 ){ @@ -167,6 +309,7 @@ static int lsm1Open(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){ */ 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; @@ -190,6 +333,21 @@ static int lsm1Next(sqlite3_vtab_cursor *cur){ 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; } @@ -295,6 +453,14 @@ static int lsm1PutVarint64(unsigned char *z, sqlite3_uint64 x){ return 9; } +/* Append non-negative integer x as a variable-length integer. +*/ +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); +} + /* ** Decode the varint in the first n bytes z[]. Write the integer value ** into *pResult and return the number of bytes in the varint. @@ -349,69 +515,74 @@ static int lsm1GetVarint64( return 9; } -/* -** Generate a key encoding for pValue such that all keys compare in -** lexicographical order. Return an SQLite error code or SQLITE_OK. +/* Encoded a signed integer as a varint. Numbers close to zero uses fewer +** bytes than numbers far away from zero. However, the result is not in +** lexicographical order. ** -** The key encoding is *pnKey bytes in length written into *ppKey. -** Space to hold the key is taken from pSpace if sufficient, or else -** from sqlite3_malloc(). The caller is responsible for freeing malloced -** space. +** Encoding: Non-negative integer X is encoding as an unsigned +** varint X*2. Negative integer Y is encoding as an unsigned +** varint (1-Y)*2 + 1. */ -static int lsm1EncodeKey( - sqlite3_value *pValue, /* Value to be encoded */ - unsigned char **ppKey, /* Write the encoding here */ - int *pnKey, /* Write the size of the encoding here */ - unsigned char *pSpace, /* Use this space if it is large enough */ - int nSpace /* Size of pSpace[] */ +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); + } +} + +/* Decoded a signed varint. */ +static int lsm1GetSignedVarint64( + const unsigned char *z, + int n, + sqlite3_int64 *pResult ){ - int eType = sqlite3_value_type(pValue); - *ppKey = 0; - *pnKey = 0; - assert( nSpace>=32 ); - switch( eType ){ - default: { - return SQLITE_ERROR; /* We cannot handle NULL keys */ - } - case SQLITE_BLOB: - case SQLITE_TEXT: { - int nVal = sqlite3_value_bytes(pValue); - const void *pVal; - if( eType==SQLITE_BLOB ){ - eType = LSM1_TYPE_BLOB; - pVal = sqlite3_value_blob(pValue); - }else{ - eType = LSM1_TYPE_TEXT; - pVal = (const void*)sqlite3_value_text(pValue); - if( pVal==0 ) return SQLITE_NOMEM; - } - if( nVal+1>nSpace ){ - pSpace = sqlite3_malloc( nVal+1 ); - if( pSpace==0 ) return SQLITE_NOMEM; - } - pSpace[0] = (unsigned char)eType; - memcpy(&pSpace[1], pVal, nVal); - *ppKey = pSpace; - *pnKey = nVal+1; - break; - } - case SQLITE_INTEGER: { - sqlite3_int64 iVal = sqlite3_value_int64(pValue); - sqlite3_uint64 uVal; - if( iVal<0 ){ - if( iVal==0xffffffffffffffffLL ) return SQLITE_ERROR; - uVal = *(sqlite3_uint64*)&iVal; - eType = LSM1_TYPE_NEGATIVE; - }else{ - uVal = iVal; - eType = LSM1_TYPE_POSITIVE; - } - pSpace[0] = (unsigned char)eType; - *ppKey = pSpace; - *pnKey = 1 + lsm1PutVarint64(&pSpace[1], uVal); + 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; +} + + +/* +** Read the value part of the key-value pair and decode it into columns. +*/ +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; inVal; 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; } - return SQLITE_OK; + if( inVal ){ + pCur->zData = 0; + return 0; + } + return 1; } /* @@ -424,83 +595,72 @@ static int lsm1Column( int i /* Which column to return */ ){ lsm1_cursor *pCur = (lsm1_cursor*)cur; - switch( i ){ - case LSM1_COLUMN_BLOBKEY: { - const void *pVal; - int nVal; - if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){ + lsm1_vtab *pTab = (lsm1_vtab*)(cur->pVtab); + if( i==0 ){ + /* The key column */ + 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); } - break; } - case LSM1_COLUMN_KEY: { - const unsigned char *pVal; + }else if( i>pTab->nVal ){ + if( i==pTab->nVal+2 ){ /* lsm1_key */ + const void *pVal; int nVal; - if( lsm_csr_key(pCur->pLsmCur, (const void**)&pVal, &nVal)==LSM_OK - && nVal>=1 - ){ - if( pVal[0]==LSM1_TYPE_BLOB ){ - sqlite3_result_blob(ctx, (const void*)&pVal[1],nVal-1, - SQLITE_TRANSIENT); - }else if( pVal[0]==LSM1_TYPE_TEXT ){ - sqlite3_result_text(ctx, (const char*)&pVal[1],nVal-1, - SQLITE_TRANSIENT); - }else if( nVal>=2 && nVal<=10 && - (pVal[0]==LSM1_TYPE_POSITIVE || pVal[0]==LSM1_TYPE_NEGATIVE) - ){ - sqlite3_int64 iVal; - lsm1GetVarint64(pVal+1, nVal-1, (sqlite3_uint64*)&iVal); - sqlite3_result_int64(ctx, iVal); - } + if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){ + sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT); } - break; - } - case LSM1_COLUMN_BLOBVALUE: { + }else if( i==pTab->nVal+3 ){ /* lsm1_value */ const void *pVal; int nVal; - if( lsm_csr_value(pCur->pLsmCur, (const void**)&pVal, &nVal)==LSM_OK ){ + if( lsm_csr_value(pCur->pLsmCur, &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; jzData + pCur->aiOfst[i]; + nData = pCur->aiLen[i]; + switch( pCur->aeType[i] ){ + case 0: { /* in-line integer */ + 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: { + sqlite3_uint64 v1 = 0; + double v; + lsm1GetVarint64(zData, nData, &v1); + memcpy(&v, &v1, sizeof(v)); + sqlite3_result_double(ctx, v); + break; + } + case SQLITE_TEXT: { + sqlite3_result_text(ctx, (const char*)zData, nData, SQLITE_TRANSIENT); + } + case SQLITE_BLOB: { + sqlite3_result_blob(ctx, zData, nData, SQLITE_TRANSIENT); + } + default: { + /* A NULL. Do nothing */ } - break; - } - default: { - break; } } return SQLITE_OK; @@ -514,21 +674,85 @@ static int lsm1Filter( 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 void *pVal = 0; + int nVal; + u8 keyType = pTab->keyType; + u8 aKey1[16]; + pCur->atEof = 1; - if( idxNum==1 ){ - assert( argc==1 ); - pCur->isDesc = 0; - pCur->bUnique = 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); + sqlite3_free(pCur->pKey2); + pCur->pKey2 = 0; + if( idxNum<99 ){ + if( keyType==SQLITE_BLOB ){ + pVal = sqlite3_value_blob(argv[0]); + nVal = sqlite3_value_bytes(argv[0]); + }else if( keyType==SQLITE_TEXT ){ + pVal = sqlite3_value_text(argv[0]); + nVal = sqlite3_value_bytes(argv[0]); + }else{ + sqlite3_int64 v = sqlite3_value_int64(argv[0]); + if( v<0 ) v = 0; + nVal = lsm1PutVarint64(aKey1, v); + pVal = aKey1; + } + } + switch( idxNum ){ + case 0: { /* key==argv[0] */ + assert( argc==1 ); + seekType = LSM_SEEK_EQ; + pCur->isDesc = 0; + pCur->bUnique = 1; + break; + } + case 1: { /* key>=argv[0] AND key<=argv[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: { /* key>=argv[0] */ + seekType = LSM_SEEK_GE; + pCur->isDesc = 0; + pCur->bUnique = 0; + break; + } + case 3: { /* key<=argv[0] */ + seekType = LSM_SEEK_LE; + pCur->isDesc = 1; + pCur->bUnique = 0; + break; } + default: { /* full table scan */ + 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); - pCur->isDesc = 0; - pCur->bUnique = 0; } if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)!=0 ){ pCur->atEof = 0; @@ -540,59 +764,87 @@ static int lsm1Filter( ** Only comparisons against the key are allowed. The idxNum defines ** which comparisons are available: ** -** 0 Full table scan only -** bit 1 key==?1 single argument for ?1 -** bit 2 key>?1 -** bit 3 key>=?1 -** bit 4 key?1 -** 4 key>=?1 -** 8 key?1 AND key=?1 AND key?1 AND key<=?2 -** 20 key>=?1 AND key<=?2 -** 33..52 Use blobkey in place of key... +** 0 key==?1 +** 1 key>=?1 AND key<=?2 +** 2 key>?1 or key>=?1 +** 3 keyaConstraint; 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; + if( pConstraint->iColumn!=0 ) continue; switch( pConstraint->op ){ case SQLITE_INDEX_CONSTRAINT_EQ: { - eqIdx = i; - idxNum = 1; + 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( eqIdx>=0 ){ - pIdxInfo->aConstraintUsage[eqIdx].argvIndex = ++nArg; - pIdxInfo->aConstraintUsage[eqIdx].omit = 1; + 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==1 ){ + 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{ /* Full table scan */ pIdxInfo->estimatedCost = (double)2147483647; @@ -615,16 +867,13 @@ int lsm1Update( sqlite_int64 *pRowid ){ lsm1_vtab *p = (lsm1_vtab*)pVTab; - const void *pKey; - void *pFree = 0; int nKey; - int eType; + int i; int rc = LSM_OK; - sqlite3_value *pValue; - const unsigned char *pVal; - unsigned char *pData; - int nVal; - unsigned char pSpace[100]; + unsigned char *pKey; + unsigned char aKey[16]; + unsigned char pSpace[16]; + lsm1_vblob val; if( argc==1 ){ pVTab->zErrMsg = sqlite3_mprintf("cannot DELETE"); @@ -635,78 +884,76 @@ int lsm1Update( return SQLITE_ERROR; } - /* "INSERT INTO tab(command) VALUES('....')" is used to implement + /* "INSERT INTO tab(lsm1_command) VALUES('....')" is used to implement ** special commands. */ - if( sqlite3_value_type(argv[2+LSM1_COLUMN_COMMAND])!=SQLITE_NULL ){ + if( sqlite3_value_type(argv[3+p->nVal])!=SQLITE_NULL ){ return SQLITE_OK; } - if( sqlite3_value_type(argv[2+LSM1_COLUMN_BLOBKEY])==SQLITE_BLOB ){ - /* Use the blob key exactly as supplied */ - pKey = sqlite3_value_blob(argv[2+LSM1_COLUMN_BLOBKEY]); - nKey = sqlite3_value_bytes(argv[2+LSM1_COLUMN_BLOBKEY]); - }else{ - /* Use a key encoding that sorts in lexicographical order */ - rc = lsm1EncodeKey(argv[2+LSM1_COLUMN_KEY], - (unsigned char**)&pKey,&nKey, - pSpace,sizeof(pSpace)); - if( rc ) return rc; - if( pKey!=(const void*)pSpace ) pFree = (void*)pKey; - } - if( sqlite3_value_type(argv[2+LSM1_COLUMN_BLOBVALUE])==SQLITE_BLOB ){ - pVal = sqlite3_value_blob(argv[2+LSM1_COLUMN_BLOBVALUE]); - nVal = sqlite3_value_bytes(argv[2+LSM1_COLUMN_BLOBVALUE]); - rc = lsm_insert(p->pDb, pKey, nKey, pVal, nVal); + if( p->keyType==SQLITE_BLOB ){ + pKey = (u8*)sqlite3_value_blob(argv[2]); + nKey = sqlite3_value_bytes(argv[2]); + }else if( p->keyType==SQLITE_TEXT ){ + pKey = (u8*)sqlite3_value_text(argv[2]); + nKey = sqlite3_value_bytes(argv[2]); }else{ - pValue = argv[2+LSM1_COLUMN_VALUE]; - eType = sqlite3_value_type(pValue); + sqlite3_int64 v = sqlite3_value_int64(argv[2]); + if( v>=0 ){ + nKey = lsm1PutVarint64(aKey, (sqlite3_uint64)v); + pKey = aKey; + }else{ + pVTab->zErrMsg = sqlite3_mprintf("key must be non-negative"); + return SQLITE_ERROR; + } + } + memset(&val, 0, sizeof(val)); + for(i=0; inVal; i++){ + u8 eType = sqlite3_value_type(argv[3+i]); switch( eType ){ case SQLITE_NULL: { - rc = lsm_delete(p->pDb, pKey, nKey); + lsm1VblobAppendVarint(&val, SQLITE_NULL); break; } - case SQLITE_BLOB: - case SQLITE_TEXT: { - 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; + case SQLITE_INTEGER: { + sqlite3_int64 v = sqlite3_value_int64(argv[3+i]); + if( v>=0 && v<=240/6 ){ + lsm1VblobAppendVarint(&val, v*6); }else{ - pData[0] = (unsigned char)eType; - memcpy(&pData[1], pVal, nVal); - rc = lsm_insert(p->pDb, pKey, nKey, pData, nVal+1); - sqlite3_free(pData); + int n = lsm1PutSignedVarint64(pSpace, v); + lsm1VblobAppendVarint(&val, SQLITE_INTEGER + n*6); + lsm1VblobAppend(&val, pSpace, n); } 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 r = sqlite3_value_double(pValue); - assert( sizeof(r)==sizeof(x) ); - memcpy(&x, &r, sizeof(r)); - } - for(i=8; x>0 && i>=1; i--){ - aVal[i] = x & 0xff; - x >>= 8; - } - aVal[i] = (unsigned char)eType; - rc = lsm_insert(p->pDb, pKey, nKey, &aVal[i], 9-i); + double r = sqlite3_value_double(argv[3+i]); + sqlite3_uint64 u; + int n; + memcpy(&u, &r, 8); + n = lsm1PutSignedVarint64(pSpace, u); + lsm1VblobAppendVarint(&val, SQLITE_FLOAT + n*6); + lsm1VblobAppend(&val, pSpace, n); + break; + } + case SQLITE_BLOB: { + int n = sqlite3_value_bytes(argv[3+i]); + lsm1VblobAppendVarint(&val, n*6 + SQLITE_BLOB); + lsm1VblobAppend(&val, sqlite3_value_blob(argv[2+i]), n); + break; + } + case SQLITE_TEXT: { + int n = sqlite3_value_bytes(argv[3+i]); + lsm1VblobAppendVarint(&val, n*6 + SQLITE_TEXT); + lsm1VblobAppend(&val, sqlite3_value_text(argv[2+i]), n); break; } } } - sqlite3_free(pFree); + 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; } diff --git a/ext/lsm1/test/lsm1_simple.test b/ext/lsm1/test/lsm1_simple.test index c2f711df2b..fb2c544986 100644 --- a/ext/lsm1/test/lsm1_simple.test +++ b/ext/lsm1/test/lsm1_simple.test @@ -20,26 +20,26 @@ load_lsm1_vtab db forcedelete testlsm.db do_execsql_test 1.0 { - CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db); + CREATE VIRTUAL TABLE x1 USING lsm1(testlsm.db,a,UINT,b,c,d); PRAGMA table_info(x1); } { - 0 key {} 0 {} 0 - 1 blobkey {} 0 {} 0 - 2 value {} 0 {} 0 - 3 blobvalue {} 0 {} 0 + 0 a UINT 0 {} 0 + 1 b {} 0 {} 0 + 2 c {} 0 {} 0 + 3 d {} 0 {} 0 } do_execsql_test 1.1 { - INSERT INTO x1(blobkey, blobvalue) VALUES(x'abcd', x'1234'); - SELECT quote(blobkey), quote(blobvalue) FROM x1; -} {X'ABCD' X'1234'} + INSERT INTO x1(a,b,c,d) VALUES(15, 11, 22, 33); + SELECT * FROM x1; +} {15 11 22 33} do_catchsql_test 1.2 { - UPDATE x1 SET blobvalue = x'7890' WHERE blobkey = x'abcd'; + UPDATE x1 SET d = d+1.0 WHERE a=15; } {1 {cannot UPDATE}} do_catchsql_test 1.3 { - DELETE FROM x1 WHERE blobkey = x'abcd' + DELETE FROM x1 WHERE a=15; } {1 {cannot DELETE}} do_test 1.4 { @@ -52,5 +52,3 @@ do_test 1.5 { } {testlsm.db} finish_test - - diff --git a/manifest b/manifest index 299eb7899b..a80962d784 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C The\sidentifier\s"vsnprintf"\sappears\sto\sbe\sa\sreserved\sword\sin\srecent\sversions\nof\sXCode,\sso\savoid\susing\sit\sas\sa\sstructure\sfield. -D 2017-08-08T21:40:23.590 +C Work\stoward\sredesigning\sthe\sinterface\sto\sthe\sLSM1\svirtual\stable. +D 2017-08-09T18:40:35.775 F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 02b469e9dcd5b7ee63fc1fb05babc174260ee4cfa4e0ef2e48c3c6801567a016 @@ -249,10 +249,10 @@ F ext/lsm1/lsm_str.c 65e361b488c87b10bf3e5c0070b14ffc602cf84f094880bece77bbf6678 F ext/lsm1/lsm_tree.c 682679d7ef2b8b6f2fe77aeb532c8d29695bca671c220b0abac77069de5fb9fb F ext/lsm1/lsm_unix.c 57361bcf5b1a1a028f5d66571ee490e9064d2cfb145a2cc9e5ddade467bb551b F ext/lsm1/lsm_varint.c 43f954af668a66c7928b81597c14d6ad4be9fedbc276bbd80f52fa28a02fdb62 -F ext/lsm1/lsm_vtab.c d5af32abe32601b3f2618b9488225db9ca06af803ddee1aaaf1653e08e9d112b +F ext/lsm1/lsm_vtab.c 356e1206c45c09b7cb267b2518557ac6b30524719731760ffc39de3050b3f220 F ext/lsm1/lsm_win32.c 0a4acbd7e8d136dd3a5753f0a9e7a9802263a9d96cef3278cf120bcaa724db7c F ext/lsm1/test/lsm1_common.tcl 5ed4bab07c93be2e4f300ebe46007ecf4b3e20bc5fbe1dedaf04a8774a6d8d82 -F ext/lsm1/test/lsm1_simple.test 3bb38951450cd1f12a6c294949334d6fbb109a3da38c48eaf0877a37c43a0fab +F ext/lsm1/test/lsm1_simple.test e4541832cf542e3c01a718e176e82978f593caf0d735d668d17368c31a11da27 F ext/misc/README.md 8e008c8d2b02e09096b31dfba033253ac27c6c06a18aa5826e299fa7601d90b2 F ext/misc/amatch.c 6db4607cb17c54b853a2d7c7c36046d004853f65b9b733e6f019d543d5dfae87 F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb @@ -1644,7 +1644,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f4a4b1497355c1b27d3d0770550fffcc3b2d2d51ab284101f19e8fc4264ee675 -R c288692e72cd4a77fa38abaf013fcc5d +P 795eede331b832a53e886318466dedc12504f7181c1d7295af7935864a04b72c +R 37cfc3f8919ba1594a23cb70a509962a U drh -Z 88c200cab07436f531dde2c3538548b4 +Z 7334a536688a5b586851f2e43ccf1724 diff --git a/manifest.uuid b/manifest.uuid index 17bf4a066d..d69c5e4367 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -795eede331b832a53e886318466dedc12504f7181c1d7295af7935864a04b72c \ No newline at end of file +313df946668b943b0a9a9a91fd7bafa7212d05765c7714fa6c0de46aa9062a74 \ No newline at end of file