mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
646 lines
16 KiB
C
646 lines
16 KiB
C
/*
|
|
** 2022-09-06
|
|
**
|
|
** The author disclaims copyright to this source code. In place of
|
|
** a legal notice, here is a blessing:
|
|
**
|
|
** May you do good and not evil.
|
|
** May you find forgiveness for yourself and forgive others.
|
|
** May you share freely, never taking more than you give.
|
|
**
|
|
******************************************************************************
|
|
**
|
|
** This file contains an experimental VFS layer that operates on a
|
|
** Key/Value storage engine where both keys and values must be pure
|
|
** text.
|
|
*/
|
|
#include "sqlite3.h"
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stat/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
|
|
|
|
/*****************************************************************************
|
|
** The low-level storage engine
|
|
*/
|
|
typedef struct KVStorage KVStorage;
|
|
struct KVStorage {
|
|
char *zDir;
|
|
char zKey[50];
|
|
};
|
|
|
|
static KVStorage *kvstorageOpen(void);
|
|
static void kvstorageClose(KVStorage*);
|
|
static int kvstorageWrite(KVStorage*, const char *zKey, const char *zData);
|
|
static int kvstorageDelete(KVStorage*, const char *zKey);
|
|
static int kvstorageSize(KVStorage*, const char *zKey);
|
|
static int kvstorageRead(KVStorage*, const char *zKey, char *zBuf, int nBuf);
|
|
|
|
|
|
/*
|
|
** Forward declaration of objects used by this utility
|
|
*/
|
|
typedef struct KVVfsVfs KVVfsVfs;
|
|
typedef struct KVVfsFile KVVfsFile;
|
|
|
|
struct KVVfsVfs {
|
|
sqlite3_vfs base; /* VFS methods */
|
|
KVStorage *pStore; /* Single command KV storage object */
|
|
KVVfsFile *pFiles; /* List of open KVVfsFile objects */
|
|
};
|
|
|
|
struct KVVfsFile {
|
|
sqlite3_file base; /* IO methods */
|
|
KVVfsVfs *pVfs; /* The VFS to which this file belongs */
|
|
KVVfsFile *pNext; /* Next in list of all files */
|
|
int isJournal; /* True if this is a journal file */
|
|
int nJrnl; /* Space allocated for aJrnl[] */
|
|
char *aJrnl; /* Journal content */
|
|
};
|
|
|
|
/*
|
|
** Methods for KVVfsFile
|
|
*/
|
|
static int kvvfsClose(sqlite3_file*);
|
|
static int kvvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
|
static int kvvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
|
static int kvvfsTruncate(sqlite3_file*, sqlite3_int64 size);
|
|
static int kvvfsSync(sqlite3_file*, int flags);
|
|
static int kvvfsFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
|
static int kvvfsLock(sqlite3_file*, int);
|
|
static int kvvfsUnlock(sqlite3_file*, int);
|
|
static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut);
|
|
static int kvvfsFileControl(sqlite3_file*, int op, void *pArg);
|
|
static int kvvfsSectorSize(sqlite3_file*);
|
|
static int kvvfsDeviceCharacteristics(sqlite3_file*);
|
|
|
|
/*
|
|
** Methods for KVVfsVfs
|
|
*/
|
|
static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
|
static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
|
|
static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
|
static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
|
static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename);
|
|
static void kvvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
|
static void (*kvvfsDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
|
static void kvvfsDlClose(sqlite3_vfs*, void*);
|
|
static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
|
static int kvvfsSleep(sqlite3_vfs*, int microseconds);
|
|
static int kvvfsCurrentTime(sqlite3_vfs*, double*);
|
|
static int kvvfsGetLastError(sqlite3_vfs*, int, char *);
|
|
static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
|
|
|
static KVVfsVfs kvvfs_vfs = {
|
|
{
|
|
1, /* iVersion */
|
|
sizeof(KVVfsFile), /* szOsFile */
|
|
1024, /* mxPathname */
|
|
0, /* pNext */
|
|
"kvvfs", /* zName */
|
|
0, /* pAppData */
|
|
kvvfsOpen, /* xOpen */
|
|
kvvfsDelete, /* xDelete */
|
|
kvvfsAccess, /* xAccess */
|
|
kvvfsFullPathname, /* xFullPathname */
|
|
kvvfsDlOpen, /* xDlOpen */
|
|
0, /* xDlError */
|
|
0, /* xDlSym */
|
|
0, /* xDlClose */
|
|
kvvfsRandomness, /* xRandomness */
|
|
kvvfsSleep, /* xSleep */
|
|
kvvfsCurrentTime, /* xCurrentTime */
|
|
0, /* xGetLastError */
|
|
kvvfsCurrentTimeInt64, /* xCurrentTimeInt64 */
|
|
},
|
|
0,
|
|
0
|
|
};
|
|
|
|
static sqlite3_io_methods kvvfs_io_methods = {
|
|
1, /* iVersion */
|
|
kvvfsClose, /* xClose */
|
|
kvvfsRead, /* xRead */
|
|
kvvfsWrite, /* xWrite */
|
|
kvvfsTruncate, /* xTruncate */
|
|
kvvfsSync, /* xSync */
|
|
kvvfsFileSize, /* xFileSize */
|
|
kvvfsLock, /* xLock */
|
|
kvvfsUnlock, /* xUnlock */
|
|
kvvfsCheckReservedLock, /* xCheckReservedLock */
|
|
kvvfsFileControl, /* xFileControl */
|
|
kvvfsSectorSize, /* xSectorSize */
|
|
kvvfsDeviceCharacteristics /* xDeviceCharacteristics */
|
|
0, /* xShmMap */
|
|
0, /* xShmLock */
|
|
0, /* xShmBarrier */
|
|
0, /* xShmUnmap */
|
|
0, /* xFetch */
|
|
0 /* xUnfetch */
|
|
};
|
|
|
|
/****** Storage subsystem **************************************************/
|
|
|
|
/* Allocate a new storage subsystem.
|
|
** Return NULL if OOM
|
|
*/
|
|
static KVStorage *kvstorageOpen(void){
|
|
KVStorage *pStore;
|
|
pStore = sqlite3_malloc64( sizeof(*pStore) );
|
|
if( pStore==0 ) return 0;
|
|
memset(pStore, 0, sizeof(*pStore));
|
|
return pStore;
|
|
}
|
|
|
|
/* Free all resources associated with the storage subsystem */
|
|
static void kvstorageClose(KVStorage *pStore){
|
|
sqlite3_free(pStore);
|
|
}
|
|
|
|
/* Expand the key name with an appropriate prefix and put the result
|
|
** in pStore->zKey[]
|
|
*/
|
|
static void kvstorageMakeKey(KVStorage *pStore, const char *zKey){
|
|
sqlite3_snprintf(sizeof(pStore->zKey), pStore->zKey, "kvvfs-%s", zKey);
|
|
}
|
|
|
|
/* Write content into a key. zKey is of limited size. zData should be
|
|
** pure text. In other words, zData has already been encoded.
|
|
**
|
|
** Return the number of errors.
|
|
*/
|
|
static int kvstorageWrite(
|
|
KVStorage *pStore,
|
|
const char *zKey,
|
|
const char *zData
|
|
){
|
|
FILE *fd;
|
|
kvstorageMakeKey(pStore, zKey);
|
|
fd = fopen(pStore->zKey, "wb");
|
|
if( fd==0 ) return 1;
|
|
if( fd ){
|
|
fputs(zData, fd);
|
|
fclose(fd);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Delete a key
|
|
*/
|
|
static int kvstorageDelete(KVStorage *pStore, const char *zKey){
|
|
kvstorageMakeKey(pStore, zKey);
|
|
unlink(pStore->zKey);
|
|
return 0;
|
|
}
|
|
|
|
/* Read the value associated with a key and put the result in the first
|
|
** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large
|
|
** enough to hold it all. The value put into zBuf will always be zero
|
|
** terminated.
|
|
**
|
|
** Return the total number of bytes in the data, without truncation, and
|
|
** not counting the final zero terminator. Return -1 if the key does
|
|
** not exist.
|
|
**
|
|
** If nBuf==0 then this routine simply returns the size of the data without
|
|
** actually reading it.
|
|
*/
|
|
static int kvstorageRead(
|
|
KVStorage *pStore,
|
|
const char *zKey,
|
|
char *zBuf,
|
|
int nBuf
|
|
){
|
|
FILE *fd;
|
|
struct stat buf;
|
|
kvstorageMakeKey(pStore, zKey);
|
|
if( access(pStore->zKey, R_OK)!=0
|
|
|| stat(pStore->zKey, &buf)!=0
|
|
|| !S_ISREG(buf.st_mode)
|
|
){
|
|
return -1;
|
|
}
|
|
if( nBuf<0 ){
|
|
return (int)buf.st_size;
|
|
}else if( nBuf==1 ){
|
|
zBuf[0] = 0;
|
|
return (int)buf.st_size;
|
|
}
|
|
if( nBuf-1 > buf.st_size ){
|
|
nBuf = buf.st_size + 1;
|
|
}
|
|
fd = fopen(pStore->zKey, "rb");
|
|
if( fd==0 ) return -1;
|
|
fread(zBuf, nBuf-1, 1, fd);
|
|
fclose(fd);
|
|
return nBuf-1;
|
|
}
|
|
|
|
|
|
/****** The main VFS code **************************************************/
|
|
|
|
/*
|
|
** Close an kvvfs-file.
|
|
*/
|
|
static int kvvfsClose(sqlite3_file *pProtoFile){
|
|
KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
|
|
KVVfsVfs *pVfs = pFile->pVfs;
|
|
|
|
if( pVfs->pFiles==pFile ){
|
|
pVfs->pFiles = pFile->pNext;
|
|
if( pVfs->pFiles==0 ){
|
|
kvstorageClose(pVfs->pStore);
|
|
pVfs->pStore = 0;
|
|
}
|
|
}else{
|
|
KVVfsFile *pX = pVfs->pFiles;
|
|
while( 1 ){
|
|
assert( pX );
|
|
if( pX->pNext==pFile ){
|
|
pX->pNext = pFile->pNext;
|
|
break;
|
|
}
|
|
pX = pX->pNext;
|
|
}
|
|
}
|
|
sqlite3_free(pFile->aData);
|
|
sqlite3_free(pFile);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Encode binary into the text encoded used to persist on disk.
|
|
** The output text is stored in aOut[], which must be at least
|
|
** nData+1 bytes in length.
|
|
**
|
|
** Return the actual length of the encoded text, not counting the
|
|
** zero terminator at the end.
|
|
*/
|
|
static int kvvfsEncode(const char *aData, int nData, char *aOut){
|
|
int i, j;
|
|
const unsigned *a = (const unsigned char*)aData;
|
|
for(i=j=0; i<nData; i++){
|
|
unsigned char c = a[i];
|
|
if( c!=0 ){
|
|
aOut[j++] = "0123456789ABCDEF"[c>>4];
|
|
aOut[j++] = "0123456789ABCDEF"[c&0xf];
|
|
}else{
|
|
/* A sequence of 1 or more zeros is stored as a little-endian
|
|
** base-26 number using a..z as the digits. So one zero is "b".
|
|
** Two zeros is "c". 25 zeros is "z", 26 zeros is "ba" and so forth.
|
|
*/
|
|
int k;
|
|
for(k=1; a[i+k]==0 && i+k<nData; k++){}
|
|
i += k;
|
|
while( k>0 ){
|
|
aOut[j++] = 'a'+(k%26);
|
|
k /= 26;
|
|
}
|
|
}
|
|
}
|
|
aOut[j] = 0;
|
|
return j;
|
|
}
|
|
|
|
/* Convert hex to binary */
|
|
static char kvvfsHexToBinary(char c){
|
|
if( c>='0' && c<='9' ) return c - '0';
|
|
if( c>='a' && c<='f' ) return c - 'a' + 10;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** Decode the text encoding back to binary. The binary content is
|
|
** written into pOut, which must be at least nOut bytes in length.
|
|
*/
|
|
static int kvvfsDecode(const char *aIn, char *aOut, int nOut){
|
|
char *aOut;
|
|
int i, j, k;
|
|
int c;
|
|
i = 0;
|
|
j = 0;
|
|
while( (c = aIn[i])!=0 ){
|
|
if( c>='a' ){
|
|
int n = 0;
|
|
while( c>='a' && c<='z' ){
|
|
n = n*26 + c - 'a';
|
|
c = aIn[++i];
|
|
}
|
|
if( j+n>nOut ) return -1;
|
|
while( n-->0 ){
|
|
aOut[j++] = 0;
|
|
}
|
|
}else{
|
|
if( j>nOut ) return -1;
|
|
aOut[j] = kvvfsHexToBinary(aIn[i])<<4;
|
|
i++;
|
|
aOut[j] += kvvfsHexToBinary(aIn[i]);
|
|
i++;
|
|
}
|
|
}
|
|
return j;
|
|
}
|
|
|
|
/*
|
|
** Read from the -journal file.
|
|
*/
|
|
static int kvvfsReadFromJournal(
|
|
KVVfsFile *pFile,
|
|
void *zBuf,
|
|
int iAmt,
|
|
sqlite_int64 iOfst
|
|
){
|
|
assert( pFile->isJournal );
|
|
if( pFile->aJrnl==0 ){
|
|
int szTxt = kvstorageRead(pFile->pVfs->pStore, "journal", 0, 0);
|
|
char *aTxt;
|
|
if( szTxt<=4 ){
|
|
return SQLITE_IOERR;
|
|
}
|
|
aTxt = sqlite3_malloc64( szTxt+1 );
|
|
if( aTxt==0 ) return SQLITE_NOMEM;
|
|
kvstorageRead(pFile->pVfs->pStore, "journal", aTxt, szTxt+1);
|
|
kvvfsDecodeJournal(pFile, aTxt, szTxt);
|
|
sqlite3_free(aTxt);
|
|
if( pFile->aData==0 ) return SQLITE_IOERR;
|
|
}
|
|
if( iOfst+iAmt>pFile->nJrnl ){
|
|
return SQLITE_IOERR_SHORT_READ;
|
|
}
|
|
mcmcpy(zBuf, pFile->aJrnl+iOfst, iAmt);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Read from the database file.
|
|
*/
|
|
static int kvvfsReadFromDb(
|
|
KVVfsFile *pFile,
|
|
void *zBuf,
|
|
int iAmt,
|
|
sqlite_int64 iOfst
|
|
){
|
|
return SQLITE_IOERR;
|
|
}
|
|
|
|
|
|
/*
|
|
** Read data from an kvvfs-file.
|
|
*/
|
|
static int kvvfsRead(
|
|
sqlite3_file *pProtoFile,
|
|
void *zBuf,
|
|
int iAmt,
|
|
sqlite_int64 iOfst
|
|
){
|
|
int rc;
|
|
KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
|
|
if( pFile->isJournal ){
|
|
rc = kvvfsReadFromJournal(pFile,zBuf,iAmt,iOfst);
|
|
}else{
|
|
rc = kvvfsReadFromDb(pFile,zBuf,iAmt,iOfst);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Write into the -journal file.
|
|
*/
|
|
static int kvvfsWriteToJournal(
|
|
KVVfsFile *pFile,
|
|
const void *zBuf,
|
|
int iAmt,
|
|
sqlite_int64 iOfst
|
|
){
|
|
return SQLITE_IOERR;
|
|
}
|
|
|
|
/*
|
|
** Read from the database file.
|
|
*/
|
|
static int kvvfsWriteToDb(
|
|
KVVfsFile *pFile,
|
|
const void *zBuf,
|
|
int iAmt,
|
|
sqlite_int64 iOfst
|
|
){
|
|
return SQLITE_IOERR;
|
|
}
|
|
|
|
|
|
/*
|
|
** Write data into the kvvfs-file.
|
|
*/
|
|
static int kvvfsWrite(
|
|
sqlite3_file *pProtoFile,
|
|
const void *zBuf,
|
|
int iAmt,
|
|
sqlite_int64 iOfst
|
|
){
|
|
int rc;
|
|
KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
|
|
if( pFile->isJournal ){
|
|
rc = kvvfsWriteToJournal(pFile,zBuf,iAmt,iOfst);
|
|
}else{
|
|
rc = kvvfsWriteToDb(pFile,zBuf,iAmt,iOfst);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Truncate an kvvfs-file.
|
|
*/
|
|
static int kvvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|
int rc;
|
|
KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
|
|
rc = SQLITE_IOERR;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Sync an kvvfs-file.
|
|
*/
|
|
static int kvvfsSync(sqlite3_file *pFile, int flags){
|
|
int rc;
|
|
KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
|
|
rc = SQLITE_IOERR;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Return the current file-size of an kvvfs-file.
|
|
*/
|
|
static int kvvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
|
int rc;
|
|
KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
|
|
*pSize = 0;
|
|
rc = SQLITE_IOERR;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Lock an kvvfs-file.
|
|
*/
|
|
static int kvvfsLock(sqlite3_file *pFile, int eLock){
|
|
int rc;
|
|
KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
|
|
rc = SQLITE_IOERR;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Unlock an kvvfs-file.
|
|
*/
|
|
static int kvvfsUnlock(sqlite3_file *pFile, int eLock){
|
|
int rc;
|
|
KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
|
|
rc = SQLITE_IOERR;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Check if another file-handle holds a RESERVED lock on an kvvfs-file.
|
|
*/
|
|
static int kvvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|
int rc;
|
|
KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
|
|
*pResOut = 0;
|
|
rc = SQLITE_IOERR;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** File control method. For custom operations on an kvvfs-file.
|
|
*/
|
|
static int kvvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|
int rc;
|
|
KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
|
|
rc = SQLITE_NOTFOUND;
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Return the sector-size in bytes for an kvvfs-file.
|
|
*/
|
|
static int kvvfsSectorSize(sqlite3_file *pFile){
|
|
return 4096;
|
|
}
|
|
|
|
/*
|
|
** Return the device characteristic flags supported by an kvvfs-file.
|
|
*/
|
|
static int kvvfsDeviceCharacteristics(sqlite3_file *pFile){
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
** Open an kvvfs file handle.
|
|
*/
|
|
static int kvvfsOpen(
|
|
sqlite3_vfs *pProtoVfs,
|
|
const char *zName,
|
|
sqlite3_file *pProtoFile,
|
|
int flags,
|
|
int *pOutFlags
|
|
){
|
|
int rc;
|
|
KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
|
|
KVVfsVfs *pVfs = (KVVfsVfs*)pProtoVfs;
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Delete the file located at zPath. If the dirSync argument is true,
|
|
** ensure the file-system modifications are synced to disk before
|
|
** returning.
|
|
*/
|
|
static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
|
KVVfsVfs *p = (KVVfsVfs*)pVfs;
|
|
if( sqlite3_strglob("*-journal",zPath)==0 ){
|
|
kvstorageDelete(p->pStore, "journal");
|
|
}
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Test for access permissions. Return true if the requested permission
|
|
** is available, or false otherwise.
|
|
*/
|
|
static int kvvfsAccess(
|
|
sqlite3_vfs *pVfs,
|
|
const char *zPath,
|
|
int flags,
|
|
int *pResOut
|
|
){
|
|
*pResOut = 1;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Populate buffer zOut with the full canonical pathname corresponding
|
|
** to the pathname in zPath. zOut is guaranteed to point to a buffer
|
|
** of at least (INST_MAX_PATHNAME+1) bytes.
|
|
*/
|
|
static int kvvfsFullPathname(
|
|
sqlite3_vfs *pVfs,
|
|
const char *zPath,
|
|
int nOut,
|
|
char *zOut
|
|
){
|
|
size_t nPath = strlen(zPath);
|
|
if( nOut<nPath+1 ) nPath = nOut - 1;
|
|
memcpy(zOut, zPath, nPath);
|
|
zPath[nPath] = 0;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Open the dynamic library located at zPath and return a handle.
|
|
*/
|
|
static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** Populate the buffer pointed to by zBufOut with nByte bytes of
|
|
** random data.
|
|
*/
|
|
static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
|
memset(zBufOut, 0, nByte);
|
|
return nByte;
|
|
}
|
|
|
|
/*
|
|
** Sleep for nMicro microseconds. Return the number of microseconds
|
|
** actually slept.
|
|
*/
|
|
static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Return the current time as a Julian Day number in *pTimeOut.
|
|
*/
|
|
static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
|
*pTimeOut = 2459829.13362986;
|
|
return SQLITE_OK;
|
|
}
|
|
static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
|
|
*pTimeOut = (sqlite3_int64)(2459829.13362986*86400000.0);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Register debugvfs as the default VFS for this process.
|
|
*/
|
|
int sqlite3_register_kvvfs(const char *zArg){
|
|
return sqlite3_vfs_register(&kvvfs_vfs.base, 1);
|
|
}
|