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
|
||||
** 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; i<sizeof(azArgName)/sizeof(azArgName[0]); i++){
|
||||
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;
|
||||
}
|
||||
*ppVtab = sqlite3_malloc( sizeof(*pNew) );
|
||||
@ -87,6 +220,7 @@ static int lsm1Connect(
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memset(pNew, 0, sizeof(*pNew));
|
||||
pNew->keyType = 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
|
||||
memset(&sql, 0, sizeof(sql));
|
||||
lsm1VblobAppendText(&sql, "CREATE TABLE x(");
|
||||
lsm1VblobAppendText(&sql, argv[4]);
|
||||
lsm1VblobAppendText(&sql, " ");
|
||||
lsm1VblobAppendText(&sql, argv[5]);
|
||||
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:
|
||||
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);
|
||||
sqlite3_uint64 u = 0;
|
||||
n = lsm1GetVarint64(z, n, &u);
|
||||
if( u&1 ){
|
||||
*pResult = -1 - (sqlite3_int64)(u>>1);
|
||||
}else{
|
||||
eType = LSM1_TYPE_TEXT;
|
||||
pVal = (const void*)sqlite3_value_text(pValue);
|
||||
if( pVal==0 ) return SQLITE_NOMEM;
|
||||
*pResult = (sqlite3_int64)(u>>1);
|
||||
}
|
||||
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;
|
||||
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{
|
||||
uVal = iVal;
|
||||
eType = LSM1_TYPE_POSITIVE;
|
||||
pCur->aiOfst[i] = n;
|
||||
n += (pCur->aiLen[i] = (u32)(v/6));
|
||||
}
|
||||
pSpace[0] = (unsigned char)eType;
|
||||
*ppKey = pSpace;
|
||||
*pnKey = 1 + lsm1PutVarint64(&pSpace[1], uVal);
|
||||
if( n>pCur->nData ) break;
|
||||
}
|
||||
if( i<pTab->nVal ){
|
||||
pCur->zData = 0;
|
||||
return 0;
|
||||
}
|
||||
return SQLITE_OK;
|
||||
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: {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}else if( i>pTab->nVal ){
|
||||
if( i==pTab->nVal+2 ){ /* lsm1_key */
|
||||
const void *pVal;
|
||||
int nVal;
|
||||
if( lsm_csr_key(pCur->pLsmCur, &pVal, &nVal)==LSM_OK ){
|
||||
sqlite3_result_blob(ctx, pVal, nVal, SQLITE_TRANSIENT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
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: {
|
||||
}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);
|
||||
}
|
||||
}
|
||||
}else if( lsm1DecodeValues(pCur) ){
|
||||
/* The i-th value column (where leftmost is 1) */
|
||||
const u8 *zData;
|
||||
u32 nData;
|
||||
i--;
|
||||
zData = pCur->zData + 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 LSM1_COLUMN_VALUE: {
|
||||
const unsigned char *aVal;
|
||||
int nVal;
|
||||
if( lsm_csr_value(pCur->pLsmCur, (const void**)&aVal, &nVal)==LSM_OK
|
||||
&& nVal>=1
|
||||
){
|
||||
switch( aVal[0] ){
|
||||
case SQLITE_FLOAT:
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_uint64 x = 0;
|
||||
int j;
|
||||
for(j=1; j<nVal; j++){
|
||||
x = (x<<8) | aVal[j];
|
||||
}
|
||||
if( aVal[0]==SQLITE_INTEGER ){
|
||||
sqlite3_result_int64(ctx, *(sqlite3_int64*)&x);
|
||||
}else{
|
||||
double r;
|
||||
assert( sizeof(r)==sizeof(x) );
|
||||
memcpy(&r, &x, sizeof(r));
|
||||
sqlite3_result_double(ctx, r);
|
||||
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, (char*)&aVal[1], nVal-1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
sqlite3_result_text(ctx, (const char*)zData, nData, SQLITE_TRANSIENT);
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
sqlite3_result_blob(ctx, &aVal[1], nVal-1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
sqlite3_result_blob(ctx, zData, nData, SQLITE_TRANSIENT);
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
/* A NULL. Do nothing */
|
||||
}
|
||||
}
|
||||
}
|
||||
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 ){
|
||||
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;
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
rc = lsm_csr_first(pCur->pLsmCur);
|
||||
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);
|
||||
}
|
||||
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<?N (N==1 if bits 2,3 clear, or 2 if bits2,3 set)
|
||||
** 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...
|
||||
** 0 key==?1
|
||||
** 1 key>=?1 AND key<=?2
|
||||
** 2 key>?1 or key>=?1
|
||||
** 3 key<?1 or key<=?1
|
||||
** 99 Full table scan only
|
||||
*/
|
||||
static int lsm1BestIndex(
|
||||
sqlite3_vtab *tab,
|
||||
sqlite3_index_info *pIdxInfo
|
||||
){
|
||||
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 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;
|
||||
pConstraint = pIdxInfo->aConstraint;
|
||||
for(i=0; i<pIdxInfo->nConstraint && 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;
|
||||
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( idxNum==1 ){
|
||||
if( iIdx2>=0 ){
|
||||
pIdxInfo->aConstraintUsage[iIdx2].argvIndex = ++nArg;
|
||||
pIdxInfo->aConstraintUsage[iIdx2].omit = omit2;
|
||||
}
|
||||
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]);
|
||||
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{
|
||||
/* 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;
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}else{
|
||||
pValue = argv[2+LSM1_COLUMN_VALUE];
|
||||
eType = sqlite3_value_type(pValue);
|
||||
}
|
||||
memset(&val, 0, sizeof(val));
|
||||
for(i=0; i<p->nVal; 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);
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_int64 v = sqlite3_value_int64(argv[3+i]);
|
||||
if( v>=0 && v<=240/6 ){
|
||||
lsm1VblobAppendVarint(&val, v*6);
|
||||
}else{
|
||||
pVal = (unsigned char*)sqlite3_value_blob(pValue);
|
||||
}
|
||||
nVal = sqlite3_value_bytes(pValue);
|
||||
pData = sqlite3_malloc( nVal+1 );
|
||||
if( pData==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
pData[0] = (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));
|
||||
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;
|
||||
}
|
||||
for(i=8; x>0 && i>=1; i--){
|
||||
aVal[i] = x & 0xff;
|
||||
x >>= 8;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
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.
|
||||
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
|
||||
|
@ -1 +1 @@
|
||||
795eede331b832a53e886318466dedc12504f7181c1d7295af7935864a04b72c
|
||||
313df946668b943b0a9a9a91fd7bafa7212d05765c7714fa6c0de46aa9062a74
|
Reference in New Issue
Block a user