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:
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
14
manifest
14
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.
|
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
|
||||||
|
@ -1 +1 @@
|
|||||||
795eede331b832a53e886318466dedc12504f7181c1d7295af7935864a04b72c
|
313df946668b943b0a9a9a91fd7bafa7212d05765c7714fa6c0de46aa9062a74
|
Reference in New Issue
Block a user