1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Work toward redesigning the interface to the LSM1 virtual table.

FossilOrigin-Name: 313df946668b943b0a9a9a91fd7bafa7212d05765c7714fa6c0de46aa9062a74
This commit is contained in:
drh
2017-08-09 18:40:35 +00:00
parent ca03bdaf6e
commit 2e27d28fec
4 changed files with 511 additions and 266 deletions

View File

@ -12,6 +12,41 @@
** **
** This file implements a simple virtual table wrapper around the LSM ** This file implements a simple virtual table wrapper around the LSM
** storage engine from SQLite4. ** 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" #include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1 SQLITE_EXTENSION_INIT1
@ -22,14 +57,19 @@ SQLITE_EXTENSION_INIT1
/* Forward declaration of subclasses of virtual table objects */ /* Forward declaration of subclasses of virtual table objects */
typedef struct lsm1_vtab lsm1_vtab; typedef struct lsm1_vtab lsm1_vtab;
typedef struct lsm1_cursor lsm1_cursor; typedef struct lsm1_cursor lsm1_cursor;
typedef struct lsm1_vblob lsm1_vblob;
/* Primitive types */ /* Primitive types */
typedef unsigned char u8; typedef unsigned char u8;
typedef unsigned int u32;
typedef sqlite3_uint64 u64;
/* An open connection to an LSM table */ /* An open connection to an LSM table */
struct lsm1_vtab { struct lsm1_vtab {
sqlite3_vtab base; /* Base class - must be first */ sqlite3_vtab base; /* Base class - must be first */
lsm_db *pDb; /* Open connection to the LSM table */ 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 isDesc; /* 0: scan forward. 1: scan reverse */
u8 atEof; /* True if the scan is complete */ u8 atEof; /* True if the scan is complete */
u8 bUnique; /* True if no more than one row of output */ 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 */ /* Dequote the string */
static void lsm1Dequote(char *z){ static void lsm1Dequote(char *z){
int j; int j;
@ -76,9 +190,28 @@ static int lsm1Connect(
lsm1_vtab *pNew; lsm1_vtab *pNew;
int rc; int rc;
char *zFilename; 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 ){ for(i=0; i<sizeof(azArgName)/sizeof(azArgName[0]); i++){
*pzErr = sqlite3_mprintf("filename argument missing"); 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; return SQLITE_ERROR;
} }
*ppVtab = sqlite3_malloc( sizeof(*pNew) ); *ppVtab = sqlite3_malloc( sizeof(*pNew) );
@ -87,6 +220,7 @@ static int lsm1Connect(
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
memset(pNew, 0, sizeof(*pNew)); memset(pNew, 0, sizeof(*pNew));
pNew->keyType = keyType;
rc = lsm_new(0, &pNew->pDb); rc = lsm_new(0, &pNew->pDb);
if( rc ){ if( rc ){
*pzErr = sqlite3_mprintf("lsm_new failed with error code %d", rc); *pzErr = sqlite3_mprintf("lsm_new failed with error code %d", rc);
@ -103,22 +237,26 @@ static int lsm1Connect(
goto connect_failed; goto connect_failed;
} }
/* Column numbers */ memset(&sql, 0, sizeof(sql));
#define LSM1_COLUMN_KEY 0 lsm1VblobAppendText(&sql, "CREATE TABLE x(");
#define LSM1_COLUMN_BLOBKEY 1 lsm1VblobAppendText(&sql, argv[4]);
#define LSM1_COLUMN_VALUE 2 lsm1VblobAppendText(&sql, " ");
#define LSM1_COLUMN_BLOBVALUE 3 lsm1VblobAppendText(&sql, argv[5]);
#define LSM1_COLUMN_COMMAND 4 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)");
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);
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 */
");"
);
connect_failed: connect_failed:
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
if( pNew ){ if( pNew ){
@ -147,9 +285,13 @@ static int lsm1Open(sqlite3_vtab *pVtab, sqlite3_vtab_cursor **ppCursor){
lsm1_vtab *p = (lsm1_vtab*)pVtab; lsm1_vtab *p = (lsm1_vtab*)pVtab;
lsm1_cursor *pCur; lsm1_cursor *pCur;
int rc; 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; if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur)); 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; *ppCursor = &pCur->base;
rc = lsm_csr_open(p->pDb, &pCur->pLsmCur); rc = lsm_csr_open(p->pDb, &pCur->pLsmCur);
if( rc==LSM_OK ){ 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){ static int lsm1Close(sqlite3_vtab_cursor *cur){
lsm1_cursor *pCur = (lsm1_cursor*)cur; lsm1_cursor *pCur = (lsm1_cursor*)cur;
sqlite3_free(pCur->pKey2);
lsm_csr_close(pCur->pLsmCur); lsm_csr_close(pCur->pLsmCur);
sqlite3_free(pCur); sqlite3_free(pCur);
return SQLITE_OK; return SQLITE_OK;
@ -190,6 +333,21 @@ static int lsm1Next(sqlite3_vtab_cursor *cur){
if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)==0 ){ if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)==0 ){
pCur->atEof = 1; 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; return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
} }
@ -295,6 +453,14 @@ static int lsm1PutVarint64(unsigned char *z, sqlite3_uint64 x){
return 9; 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 ** Decode the varint in the first n bytes z[]. Write the integer value
** into *pResult and return the number of bytes in the varint. ** into *pResult and return the number of bytes in the varint.
@ -349,69 +515,74 @@ static int lsm1GetVarint64(
return 9; return 9;
} }
/* /* Encoded a signed integer as a varint. Numbers close to zero uses fewer
** Generate a key encoding for pValue such that all keys compare in ** bytes than numbers far away from zero. However, the result is not in
** lexicographical order. Return an SQLite error code or SQLITE_OK. ** lexicographical order.
** **
** The key encoding is *pnKey bytes in length written into *ppKey. ** Encoding: Non-negative integer X is encoding as an unsigned
** Space to hold the key is taken from pSpace if sufficient, or else ** varint X*2. Negative integer Y is encoding as an unsigned
** from sqlite3_malloc(). The caller is responsible for freeing malloced ** varint (1-Y)*2 + 1.
** space.
*/ */
static int lsm1EncodeKey( static int lsm1PutSignedVarint64(u8 *z, sqlite3_int64 v){
sqlite3_value *pValue, /* Value to be encoded */ sqlite3_uint64 u;
unsigned char **ppKey, /* Write the encoding here */ if( v>=0 ){
int *pnKey, /* Write the size of the encoding here */ u = (sqlite3_uint64)v;
unsigned char *pSpace, /* Use this space if it is large enough */ return lsm1PutVarint64(z, u*2);
int nSpace /* Size of pSpace[] */ }else{
){ u = (sqlite3_uint64)(1-v);
int eType = sqlite3_value_type(pValue); return lsm1PutVarint64(z, u*2+1);
*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);
}
} }
return SQLITE_OK; }
/* Decoded a signed varint. */
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;
}
/*
** 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; 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;
} }
/* /*
@ -424,83 +595,72 @@ static int lsm1Column(
int i /* Which column to return */ int i /* Which column to return */
){ ){
lsm1_cursor *pCur = (lsm1_cursor*)cur; lsm1_cursor *pCur = (lsm1_cursor*)cur;
switch( i ){ lsm1_vtab *pTab = (lsm1_vtab*)(cur->pVtab);
case LSM1_COLUMN_BLOBKEY: { 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);
}
}
}else if( i>pTab->nVal ){
if( i==pTab->nVal+2 ){ /* lsm1_key */
const void *pVal; const void *pVal;
int nVal; int nVal;
if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){ if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT); sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
} }
break; }else if( i==pTab->nVal+3 ){ /* lsm1_value */
}
case LSM1_COLUMN_KEY: {
const unsigned char *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);
}
}
break;
}
case LSM1_COLUMN_BLOBVALUE: {
const void *pVal; const void *pVal;
int nVal; 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); sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
} }
break;
} }
case LSM1_COLUMN_VALUE: { }else if( lsm1DecodeValues(pCur) ){
const unsigned char *aVal; /* The i-th value column (where leftmost is 1) */
int nVal; const u8 *zData;
if( lsm_csr_value(pCur->pLsmCur, (const void**)&aVal, &nVal)==LSM_OK u32 nData;
&& nVal>=1 i--;
){ zData = pCur->zData + pCur->aiOfst[i];
switch( aVal[0] ){ nData = pCur->aiLen[i];
case SQLITE_FLOAT: switch( pCur->aeType[i] ){
case SQLITE_INTEGER: { case 0: { /* in-line integer */
sqlite3_uint64 x = 0; sqlite3_result_int(ctx, pCur->aiOfst[i]);
int j; break;
for(j=1; j<nVal; j++){ }
x = (x<<8) | aVal[j]; case SQLITE_INTEGER: {
} sqlite3_int64 v;
if( aVal[0]==SQLITE_INTEGER ){ lsm1GetSignedVarint64(zData, nData, &v);
sqlite3_result_int64(ctx, *(sqlite3_int64*)&x); sqlite3_result_int64(ctx, v);
}else{ break;
double r; }
assert( sizeof(r)==sizeof(x) ); case SQLITE_FLOAT: {
memcpy(&r, &x, sizeof(r)); sqlite3_uint64 v1 = 0;
sqlite3_result_double(ctx, r); double v;
} lsm1GetVarint64(zData, nData, &v1);
break; memcpy(&v, &v1, sizeof(v));
} sqlite3_result_double(ctx, v);
case SQLITE_TEXT: { break;
sqlite3_result_text(ctx, (char*)&aVal[1], nVal-1, SQLITE_TRANSIENT); }
break; case SQLITE_TEXT: {
} sqlite3_result_text(ctx, (const char*)zData, nData, SQLITE_TRANSIENT);
case SQLITE_BLOB: { }
sqlite3_result_blob(ctx, &aVal[1], nVal-1, SQLITE_TRANSIENT); case SQLITE_BLOB: {
break; sqlite3_result_blob(ctx, zData, nData, SQLITE_TRANSIENT);
} }
} default: {
/* A NULL. Do nothing */
} }
break;
}
default: {
break;
} }
} }
return SQLITE_OK; return SQLITE_OK;
@ -514,21 +674,85 @@ static int lsm1Filter(
int argc, sqlite3_value **argv int argc, sqlite3_value **argv
){ ){
lsm1_cursor *pCur = (lsm1_cursor *)pVtabCursor; lsm1_cursor *pCur = (lsm1_cursor *)pVtabCursor;
lsm1_vtab *pTab = (lsm1_vtab*)(pCur->base.pVtab);
int rc = LSM_OK; int rc = LSM_OK;
int seekType = -1;
const void *pVal = 0;
int nVal;
u8 keyType = pTab->keyType;
u8 aKey1[16];
pCur->atEof = 1; pCur->atEof = 1;
if( idxNum==1 ){ sqlite3_free(pCur->pKey2);
assert( argc==1 ); pCur->pKey2 = 0;
pCur->isDesc = 0; if( idxNum<99 ){
pCur->bUnique = 1; if( keyType==SQLITE_BLOB ){
if( sqlite3_value_type(argv[0])==SQLITE_BLOB ){ pVal = sqlite3_value_blob(argv[0]);
const void *pVal = sqlite3_value_blob(argv[0]); nVal = sqlite3_value_bytes(argv[0]);
int nVal = sqlite3_value_bytes(argv[0]); }else if( keyType==SQLITE_TEXT ){
rc = lsm_csr_seek(pCur->pLsmCur, pVal, nVal, LSM_SEEK_EQ); 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{ }else{
rc = lsm_csr_first(pCur->pLsmCur); rc = lsm_csr_first(pCur->pLsmCur);
pCur->isDesc = 0;
pCur->bUnique = 0;
} }
if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)!=0 ){ if( rc==LSM_OK && lsm_csr_valid(pCur->pLsmCur)!=0 ){
pCur->atEof = 0; pCur->atEof = 0;
@ -540,59 +764,87 @@ static int lsm1Filter(
** Only comparisons against the key are allowed. The idxNum defines ** Only comparisons against the key are allowed. The idxNum defines
** which comparisons are available: ** which comparisons are available:
** **
** 0 Full table scan only ** 0 key==?1
** bit 1 key==?1 single argument for ?1 ** 1 key>=?1 AND key<=?2
** bit 2 key>?1 ** 2 key>?1 or key>=?1
** bit 3 key>=?1 ** 3 key<?1 or key<=?1
** bit 4 key<?N (N==1 if bits 2,3 clear, or 2 if bits2,3 set) ** 99 Full table scan only
** bit 5 key<=?N (N==1 if bits 2,3 clear, or 2 if bits2,3 set)
** bit 6 Use blobkey instead of key
**
** To put it another way:
**
** 0 Full table scan.
** 1 key==?1
** 2 key>?1
** 4 key>=?1
** 8 key<?1
** 10 key>?1 AND key<?2
** 12 key>=?1 AND key<?2
** 16 key<=?1
** 18 key>?1 AND key<=?2
** 20 key>=?1 AND key<=?2
** 33..52 Use blobkey in place of key...
*/ */
static int lsm1BestIndex( static int lsm1BestIndex(
sqlite3_vtab *tab, sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo sqlite3_index_info *pIdxInfo
){ ){
int i; /* Loop over constraints */ int i; /* Loop over constraints */
int idxNum = 0; /* The query plan bitmask */ int idxNum = 99; /* The query plan */
int nArg = 0; /* Number of arguments to xFilter */ int nArg = 0; /* Number of arguments to xFilter */
int eqIdx = -1; /* Index of the key== constraint, or -1 if none */ int argIdx = -1; /* Index of the key== constraint, or -1 if none */
int iIdx2 = -1; /* The index of the second key */
int omit1 = 0;
int omit2 = 0;
const struct sqlite3_index_constraint *pConstraint; const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint; pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint && idxNum<16; i++, pConstraint++){ for(i=0; i<pIdxInfo->nConstraint && idxNum<16; i++, pConstraint++){
if( pConstraint->usable==0 ) continue; if( pConstraint->usable==0 ) continue;
if( pConstraint->iColumn!=LSM1_COLUMN_KEY ) continue; if( pConstraint->iColumn!=0 ) continue;
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
switch( pConstraint->op ){ switch( pConstraint->op ){
case SQLITE_INDEX_CONSTRAINT_EQ: { case SQLITE_INDEX_CONSTRAINT_EQ: {
eqIdx = i; if( idxNum>0 ){
idxNum = 1; 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; break;
} }
} }
} }
if( eqIdx>=0 ){ if( argIdx>=0 ){
pIdxInfo->aConstraintUsage[eqIdx].argvIndex = ++nArg; pIdxInfo->aConstraintUsage[argIdx].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[eqIdx].omit = 1; pIdxInfo->aConstraintUsage[argIdx].omit = omit1;
} }
if( idxNum==1 ){ if( iIdx2>=0 ){
pIdxInfo->aConstraintUsage[iIdx2].argvIndex = ++nArg;
pIdxInfo->aConstraintUsage[iIdx2].omit = omit2;
}
if( idxNum==0 ){
pIdxInfo->estimatedCost = (double)1; pIdxInfo->estimatedCost = (double)1;
pIdxInfo->estimatedRows = 1; pIdxInfo->estimatedRows = 1;
pIdxInfo->orderByConsumed = 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{ }else{
/* Full table scan */ /* Full table scan */
pIdxInfo->estimatedCost = (double)2147483647; pIdxInfo->estimatedCost = (double)2147483647;
@ -615,16 +867,13 @@ int lsm1Update(
sqlite_int64 *pRowid sqlite_int64 *pRowid
){ ){
lsm1_vtab *p = (lsm1_vtab*)pVTab; lsm1_vtab *p = (lsm1_vtab*)pVTab;
const void *pKey;
void *pFree = 0;
int nKey; int nKey;
int eType; int i;
int rc = LSM_OK; int rc = LSM_OK;
sqlite3_value *pValue; unsigned char *pKey;
const unsigned char *pVal; unsigned char aKey[16];
unsigned char *pData; unsigned char pSpace[16];
int nVal; lsm1_vblob val;
unsigned char pSpace[100];
if( argc==1 ){ if( argc==1 ){
pVTab->zErrMsg = sqlite3_mprintf("cannot DELETE"); pVTab->zErrMsg = sqlite3_mprintf("cannot DELETE");
@ -635,78 +884,76 @@ int lsm1Update(
return SQLITE_ERROR; return SQLITE_ERROR;
} }
/* "INSERT INTO tab(command) VALUES('....')" is used to implement /* "INSERT INTO tab(lsm1_command) VALUES('....')" is used to implement
** special commands. ** 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; return SQLITE_OK;
} }
if( sqlite3_value_type(argv[2+LSM1_COLUMN_BLOBKEY])==SQLITE_BLOB ){ if( p->keyType==SQLITE_BLOB ){
/* Use the blob key exactly as supplied */ pKey = (u8*)sqlite3_value_blob(argv[2]);
pKey = sqlite3_value_blob(argv[2+LSM1_COLUMN_BLOBKEY]); nKey = sqlite3_value_bytes(argv[2]);
nKey = sqlite3_value_bytes(argv[2+LSM1_COLUMN_BLOBKEY]); }else if( p->keyType==SQLITE_TEXT ){
pKey = (u8*)sqlite3_value_text(argv[2]);
nKey = sqlite3_value_bytes(argv[2]);
}else{ }else{
/* Use a key encoding that sorts in lexicographical order */ sqlite3_int64 v = sqlite3_value_int64(argv[2]);
rc = lsm1EncodeKey(argv[2+LSM1_COLUMN_KEY], if( v>=0 ){
(unsigned char**)&pKey,&nKey, nKey = lsm1PutVarint64(aKey, (sqlite3_uint64)v);
pSpace,sizeof(pSpace)); pKey = aKey;
if( rc ) return rc; }else{
if( pKey!=(const void*)pSpace ) pFree = (void*)pKey; pVTab->zErrMsg = sqlite3_mprintf("key must be non-negative");
return SQLITE_ERROR;
}
} }
if( sqlite3_value_type(argv[2+LSM1_COLUMN_BLOBVALUE])==SQLITE_BLOB ){ memset(&val, 0, sizeof(val));
pVal = sqlite3_value_blob(argv[2+LSM1_COLUMN_BLOBVALUE]); for(i=0; i<p->nVal; i++){
nVal = sqlite3_value_bytes(argv[2+LSM1_COLUMN_BLOBVALUE]); u8 eType = sqlite3_value_type(argv[3+i]);
rc = lsm_insert(p->pDb, pKey, nKey, pVal, nVal);
}else{
pValue = argv[2+LSM1_COLUMN_VALUE];
eType = sqlite3_value_type(pValue);
switch( eType ){ switch( eType ){
case SQLITE_NULL: { case SQLITE_NULL: {
rc = lsm_delete(p->pDb, pKey, nKey); lsm1VblobAppendVarint(&val, SQLITE_NULL);
break; break;
} }
case SQLITE_BLOB: case SQLITE_INTEGER: {
case SQLITE_TEXT: { sqlite3_int64 v = sqlite3_value_int64(argv[3+i]);
if( eType==SQLITE_TEXT ){ if( v>=0 && v<=240/6 ){
pVal = sqlite3_value_text(pValue); lsm1VblobAppendVarint(&val, v*6);
}else{ }else{
pVal = (unsigned char*)sqlite3_value_blob(pValue); int n = lsm1PutSignedVarint64(pSpace, v);
} lsm1VblobAppendVarint(&val, SQLITE_INTEGER + n*6);
nVal = sqlite3_value_bytes(pValue); lsm1VblobAppend(&val, pSpace, n);
pData = sqlite3_malloc( nVal+1 );
if( pData==0 ){
rc = SQLITE_NOMEM;
}else{
pData[0] = (unsigned char)eType;
memcpy(&pData[1], pVal, nVal);
rc = lsm_insert(p->pDb, pKey, nKey, pData, nVal+1);
sqlite3_free(pData);
} }
break; break;
} }
case SQLITE_INTEGER:
case SQLITE_FLOAT: { case SQLITE_FLOAT: {
sqlite3_uint64 x; double r = sqlite3_value_double(argv[3+i]);
unsigned char aVal[9]; sqlite3_uint64 u;
int i; int n;
if( eType==SQLITE_INTEGER ){ memcpy(&u, &r, 8);
*(sqlite3_int64*)&x = sqlite3_value_int64(pValue); n = lsm1PutSignedVarint64(pSpace, u);
}else{ lsm1VblobAppendVarint(&val, SQLITE_FLOAT + n*6);
double r = sqlite3_value_double(pValue); lsm1VblobAppend(&val, pSpace, n);
assert( sizeof(r)==sizeof(x) ); break;
memcpy(&x, &r, sizeof(r)); }
} case SQLITE_BLOB: {
for(i=8; x>0 && i>=1; i--){ int n = sqlite3_value_bytes(argv[3+i]);
aVal[i] = x & 0xff; lsm1VblobAppendVarint(&val, n*6 + SQLITE_BLOB);
x >>= 8; lsm1VblobAppend(&val, sqlite3_value_blob(argv[2+i]), n);
} break;
aVal[i] = (unsigned char)eType; }
rc = lsm_insert(p->pDb, pKey, nKey, &aVal[i], 9-i); 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; 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; return rc==LSM_OK ? SQLITE_OK : SQLITE_ERROR;
} }

View File

@ -20,26 +20,26 @@ load_lsm1_vtab db
forcedelete testlsm.db forcedelete testlsm.db
do_execsql_test 1.0 { 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); PRAGMA table_info(x1);
} { } {
0 key {} 0 {} 0 0 a UINT 0 {} 0
1 blobkey {} 0 {} 0 1 b {} 0 {} 0
2 value {} 0 {} 0 2 c {} 0 {} 0
3 blobvalue {} 0 {} 0 3 d {} 0 {} 0
} }
do_execsql_test 1.1 { do_execsql_test 1.1 {
INSERT INTO x1(blobkey, blobvalue) VALUES(x'abcd', x'1234'); INSERT INTO x1(a,b,c,d) VALUES(15, 11, 22, 33);
SELECT quote(blobkey), quote(blobvalue) FROM x1; SELECT * FROM x1;
} {X'ABCD' X'1234'} } {15 11 22 33}
do_catchsql_test 1.2 { 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}} } {1 {cannot UPDATE}}
do_catchsql_test 1.3 { do_catchsql_test 1.3 {
DELETE FROM x1 WHERE blobkey = x'abcd' DELETE FROM x1 WHERE a=15;
} {1 {cannot DELETE}} } {1 {cannot DELETE}}
do_test 1.4 { do_test 1.4 {
@ -52,5 +52,3 @@ do_test 1.5 {
} {testlsm.db} } {testlsm.db}
finish_test finish_test

View File

@ -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. C Work\stoward\sredesigning\sthe\sinterface\sto\sthe\sLSM1\svirtual\stable.
D 2017-08-08T21:40:23.590 D 2017-08-09T18:40:35.775
F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8 F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 02b469e9dcd5b7ee63fc1fb05babc174260ee4cfa4e0ef2e48c3c6801567a016 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_tree.c 682679d7ef2b8b6f2fe77aeb532c8d29695bca671c220b0abac77069de5fb9fb
F ext/lsm1/lsm_unix.c 57361bcf5b1a1a028f5d66571ee490e9064d2cfb145a2cc9e5ddade467bb551b F ext/lsm1/lsm_unix.c 57361bcf5b1a1a028f5d66571ee490e9064d2cfb145a2cc9e5ddade467bb551b
F ext/lsm1/lsm_varint.c 43f954af668a66c7928b81597c14d6ad4be9fedbc276bbd80f52fa28a02fdb62 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/lsm_win32.c 0a4acbd7e8d136dd3a5753f0a9e7a9802263a9d96cef3278cf120bcaa724db7c
F ext/lsm1/test/lsm1_common.tcl 5ed4bab07c93be2e4f300ebe46007ecf4b3e20bc5fbe1dedaf04a8774a6d8d82 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/README.md 8e008c8d2b02e09096b31dfba033253ac27c6c06a18aa5826e299fa7601d90b2
F ext/misc/amatch.c 6db4607cb17c54b853a2d7c7c36046d004853f65b9b733e6f019d543d5dfae87 F ext/misc/amatch.c 6db4607cb17c54b853a2d7c7c36046d004853f65b9b733e6f019d543d5dfae87
F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb 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.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P f4a4b1497355c1b27d3d0770550fffcc3b2d2d51ab284101f19e8fc4264ee675 P 795eede331b832a53e886318466dedc12504f7181c1d7295af7935864a04b72c
R c288692e72cd4a77fa38abaf013fcc5d R 37cfc3f8919ba1594a23cb70a509962a
U drh U drh
Z 88c200cab07436f531dde2c3538548b4 Z 7334a536688a5b586851f2e43ccf1724

View File

@ -1 +1 @@
795eede331b832a53e886318466dedc12504f7181c1d7295af7935864a04b72c 313df946668b943b0a9a9a91fd7bafa7212d05765c7714fa6c0de46aa9062a74