mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
878 lines
25 KiB
C
878 lines
25 KiB
C
/*
|
|
** 2016-09-07
|
|
**
|
|
** 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 implements an in-memory VFS. A database is held as a contiguous
|
|
** block of memory.
|
|
**
|
|
** This file also implements interface sqlite3_serialize() and
|
|
** sqlite3_deserialize().
|
|
*/
|
|
#include "third_party/sqlite3/sqliteInt.h"
|
|
#ifndef SQLITE_OMIT_DESERIALIZE
|
|
|
|
/*
|
|
** Forward declaration of objects used by this utility
|
|
*/
|
|
typedef struct sqlite3_vfs MemVfs;
|
|
typedef struct MemFile MemFile;
|
|
typedef struct MemStore MemStore;
|
|
|
|
/* Access to a lower-level VFS that (might) implement dynamic loading,
|
|
** access to randomness, etc.
|
|
*/
|
|
#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData))
|
|
|
|
/* Storage for a memdb file.
|
|
**
|
|
** An memdb object can be shared or separate. Shared memdb objects can be
|
|
** used by more than one database connection. Mutexes are used by shared
|
|
** memdb objects to coordinate access. Separate memdb objects are only
|
|
** connected to a single database connection and do not require additional
|
|
** mutexes.
|
|
**
|
|
** Shared memdb objects have .zFName!=0 and .pMutex!=0. They are created
|
|
** using "file:/name?vfs=memdb". The first character of the name must be
|
|
** "/" or else the object will be a separate memdb object. All shared
|
|
** memdb objects are stored in memdb_g.apMemStore[] in an arbitrary order.
|
|
**
|
|
** Separate memdb objects are created using a name that does not begin
|
|
** with "/" or using sqlite3_deserialize().
|
|
**
|
|
** Access rules for shared MemStore objects:
|
|
**
|
|
** * .zFName is initialized when the object is created and afterwards
|
|
** is unchanged until the object is destroyed. So it can be accessed
|
|
** at any time as long as we know the object is not being destroyed,
|
|
** which means while either the SQLITE_MUTEX_STATIC_VFS1 or
|
|
** .pMutex is held or the object is not part of memdb_g.apMemStore[].
|
|
**
|
|
** * Can .pMutex can only be changed while holding the
|
|
** SQLITE_MUTEX_STATIC_VFS1 mutex or while the object is not part
|
|
** of memdb_g.apMemStore[].
|
|
**
|
|
** * Other fields can only be changed while holding the .pMutex mutex
|
|
** or when the .nRef is less than zero and the object is not part of
|
|
** memdb_g.apMemStore[].
|
|
**
|
|
** * The .aData pointer has the added requirement that it can can only
|
|
** be changed (for resizing) when nMmap is zero.
|
|
**
|
|
*/
|
|
struct MemStore {
|
|
sqlite3_int64 sz; /* Size of the file */
|
|
sqlite3_int64 szAlloc; /* Space allocated to aData */
|
|
sqlite3_int64 szMax; /* Maximum allowed size of the file */
|
|
unsigned char *aData; /* content of the file */
|
|
sqlite3_mutex *pMutex; /* Used by shared stores only */
|
|
int nMmap; /* Number of memory mapped pages */
|
|
unsigned mFlags; /* Flags */
|
|
int nRdLock; /* Number of readers */
|
|
int nWrLock; /* Number of writers. (Always 0 or 1) */
|
|
int nRef; /* Number of users of this MemStore */
|
|
char *zFName; /* The filename for shared stores */
|
|
};
|
|
|
|
/* An open file */
|
|
struct MemFile {
|
|
sqlite3_file base; /* IO methods */
|
|
MemStore *pStore; /* The storage */
|
|
int eLock; /* Most recent lock against this file */
|
|
};
|
|
|
|
/*
|
|
** File-scope variables for holding the memdb files that are accessible
|
|
** to multiple database connections in separate threads.
|
|
**
|
|
** Must hold SQLITE_MUTEX_STATIC_VFS1 to access any part of this object.
|
|
*/
|
|
static struct MemFS {
|
|
int nMemStore; /* Number of shared MemStore objects */
|
|
MemStore **apMemStore; /* Array of all shared MemStore objects */
|
|
} memdb_g;
|
|
|
|
/*
|
|
** Methods for MemFile
|
|
*/
|
|
static int memdbClose(sqlite3_file*);
|
|
static int memdbRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
|
|
static int memdbWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
|
|
static int memdbTruncate(sqlite3_file*, sqlite3_int64 size);
|
|
static int memdbSync(sqlite3_file*, int flags);
|
|
static int memdbFileSize(sqlite3_file*, sqlite3_int64 *pSize);
|
|
static int memdbLock(sqlite3_file*, int);
|
|
/* static int memdbCheckReservedLock(sqlite3_file*, int *pResOut);// not used */
|
|
static int memdbFileControl(sqlite3_file*, int op, void *pArg);
|
|
/* static int memdbSectorSize(sqlite3_file*); // not used */
|
|
static int memdbDeviceCharacteristics(sqlite3_file*);
|
|
static int memdbFetch(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
|
|
static int memdbUnfetch(sqlite3_file*, sqlite3_int64 iOfst, void *p);
|
|
|
|
/*
|
|
** Methods for MemVfs
|
|
*/
|
|
static int memdbOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
|
|
/* static int memdbDelete(sqlite3_vfs*, const char *zName, int syncDir); */
|
|
static int memdbAccess(sqlite3_vfs*, const char *zName, int flags, int *);
|
|
static int memdbFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
|
|
static void *memdbDlOpen(sqlite3_vfs*, const char *zFilename);
|
|
static void memdbDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
|
|
static void (*memdbDlSym(sqlite3_vfs *pVfs, void *p, const char*zSym))(void);
|
|
static void memdbDlClose(sqlite3_vfs*, void*);
|
|
static int memdbRandomness(sqlite3_vfs*, int nByte, char *zOut);
|
|
static int memdbSleep(sqlite3_vfs*, int microseconds);
|
|
/* static int memdbCurrentTime(sqlite3_vfs*, double*); */
|
|
static int memdbGetLastError(sqlite3_vfs*, int, char *);
|
|
static int memdbCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
|
|
|
|
static sqlite3_vfs memdb_vfs = {
|
|
2, /* iVersion */
|
|
0, /* szOsFile (set when registered) */
|
|
1024, /* mxPathname */
|
|
0, /* pNext */
|
|
"memdb", /* zName */
|
|
0, /* pAppData (set when registered) */
|
|
memdbOpen, /* xOpen */
|
|
0, /* memdbDelete, */ /* xDelete */
|
|
memdbAccess, /* xAccess */
|
|
memdbFullPathname, /* xFullPathname */
|
|
memdbDlOpen, /* xDlOpen */
|
|
memdbDlError, /* xDlError */
|
|
memdbDlSym, /* xDlSym */
|
|
memdbDlClose, /* xDlClose */
|
|
memdbRandomness, /* xRandomness */
|
|
memdbSleep, /* xSleep */
|
|
0, /* memdbCurrentTime, */ /* xCurrentTime */
|
|
memdbGetLastError, /* xGetLastError */
|
|
memdbCurrentTimeInt64, /* xCurrentTimeInt64 */
|
|
0, /* xSetSystemCall */
|
|
0, /* xGetSystemCall */
|
|
0, /* xNextSystemCall */
|
|
};
|
|
|
|
static const sqlite3_io_methods memdb_io_methods = {
|
|
3, /* iVersion */
|
|
memdbClose, /* xClose */
|
|
memdbRead, /* xRead */
|
|
memdbWrite, /* xWrite */
|
|
memdbTruncate, /* xTruncate */
|
|
memdbSync, /* xSync */
|
|
memdbFileSize, /* xFileSize */
|
|
memdbLock, /* xLock */
|
|
memdbLock, /* xUnlock - same as xLock in this case */
|
|
0, /* memdbCheckReservedLock, */ /* xCheckReservedLock */
|
|
memdbFileControl, /* xFileControl */
|
|
0, /* memdbSectorSize,*/ /* xSectorSize */
|
|
memdbDeviceCharacteristics, /* xDeviceCharacteristics */
|
|
0, /* xShmMap */
|
|
0, /* xShmLock */
|
|
0, /* xShmBarrier */
|
|
0, /* xShmUnmap */
|
|
memdbFetch, /* xFetch */
|
|
memdbUnfetch /* xUnfetch */
|
|
};
|
|
|
|
/*
|
|
** Enter/leave the mutex on a MemStore
|
|
*/
|
|
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
|
|
static void memdbEnter(MemStore *p){
|
|
UNUSED_PARAMETER(p);
|
|
}
|
|
static void memdbLeave(MemStore *p){
|
|
UNUSED_PARAMETER(p);
|
|
}
|
|
#else
|
|
static void memdbEnter(MemStore *p){
|
|
sqlite3_mutex_enter(p->pMutex);
|
|
}
|
|
static void memdbLeave(MemStore *p){
|
|
sqlite3_mutex_leave(p->pMutex);
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
** Close an memdb-file.
|
|
** Free the underlying MemStore object when its refcount drops to zero
|
|
** or less.
|
|
*/
|
|
static int memdbClose(sqlite3_file *pFile){
|
|
MemStore *p = ((MemFile*)pFile)->pStore;
|
|
if( p->zFName ){
|
|
int i;
|
|
#ifndef SQLITE_MUTEX_OMIT
|
|
sqlite3_mutex *pVfsMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
|
|
#endif
|
|
sqlite3_mutex_enter(pVfsMutex);
|
|
for(i=0; ALWAYS(i<memdb_g.nMemStore); i++){
|
|
if( memdb_g.apMemStore[i]==p ){
|
|
memdbEnter(p);
|
|
if( p->nRef==1 ){
|
|
memdb_g.apMemStore[i] = memdb_g.apMemStore[--memdb_g.nMemStore];
|
|
if( memdb_g.nMemStore==0 ){
|
|
sqlite3_free(memdb_g.apMemStore);
|
|
memdb_g.apMemStore = 0;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
sqlite3_mutex_leave(pVfsMutex);
|
|
}else{
|
|
memdbEnter(p);
|
|
}
|
|
p->nRef--;
|
|
if( p->nRef<=0 ){
|
|
if( p->mFlags & SQLITE_DESERIALIZE_FREEONCLOSE ){
|
|
sqlite3_free(p->aData);
|
|
}
|
|
memdbLeave(p);
|
|
sqlite3_mutex_free(p->pMutex);
|
|
sqlite3_free(p);
|
|
}else{
|
|
memdbLeave(p);
|
|
}
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Read data from an memdb-file.
|
|
*/
|
|
static int memdbRead(
|
|
sqlite3_file *pFile,
|
|
void *zBuf,
|
|
int iAmt,
|
|
sqlite_int64 iOfst
|
|
){
|
|
MemStore *p = ((MemFile*)pFile)->pStore;
|
|
memdbEnter(p);
|
|
if( iOfst+iAmt>p->sz ){
|
|
memset(zBuf, 0, iAmt);
|
|
if( iOfst<p->sz ) memcpy(zBuf, p->aData+iOfst, p->sz - iOfst);
|
|
memdbLeave(p);
|
|
return SQLITE_IOERR_SHORT_READ;
|
|
}
|
|
memcpy(zBuf, p->aData+iOfst, iAmt);
|
|
memdbLeave(p);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Try to enlarge the memory allocation to hold at least sz bytes
|
|
*/
|
|
static int memdbEnlarge(MemStore *p, sqlite3_int64 newSz){
|
|
unsigned char *pNew;
|
|
if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || NEVER(p->nMmap>0) ){
|
|
return SQLITE_FULL;
|
|
}
|
|
if( newSz>p->szMax ){
|
|
return SQLITE_FULL;
|
|
}
|
|
newSz *= 2;
|
|
if( newSz>p->szMax ) newSz = p->szMax;
|
|
pNew = sqlite3Realloc(p->aData, newSz);
|
|
if( pNew==0 ) return SQLITE_IOERR_NOMEM;
|
|
p->aData = pNew;
|
|
p->szAlloc = newSz;
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Write data to an memdb-file.
|
|
*/
|
|
static int memdbWrite(
|
|
sqlite3_file *pFile,
|
|
const void *z,
|
|
int iAmt,
|
|
sqlite_int64 iOfst
|
|
){
|
|
MemStore *p = ((MemFile*)pFile)->pStore;
|
|
memdbEnter(p);
|
|
if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ){
|
|
/* Can't happen: memdbLock() will return SQLITE_READONLY before
|
|
** reaching this point */
|
|
memdbLeave(p);
|
|
return SQLITE_IOERR_WRITE;
|
|
}
|
|
if( iOfst+iAmt>p->sz ){
|
|
int rc;
|
|
if( iOfst+iAmt>p->szAlloc
|
|
&& (rc = memdbEnlarge(p, iOfst+iAmt))!=SQLITE_OK
|
|
){
|
|
memdbLeave(p);
|
|
return rc;
|
|
}
|
|
if( iOfst>p->sz ) memset(p->aData+p->sz, 0, iOfst-p->sz);
|
|
p->sz = iOfst+iAmt;
|
|
}
|
|
memcpy(p->aData+iOfst, z, iAmt);
|
|
memdbLeave(p);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Truncate an memdb-file.
|
|
**
|
|
** In rollback mode (which is always the case for memdb, as it does not
|
|
** support WAL mode) the truncate() method is only used to reduce
|
|
** the size of a file, never to increase the size.
|
|
*/
|
|
static int memdbTruncate(sqlite3_file *pFile, sqlite_int64 size){
|
|
MemStore *p = ((MemFile*)pFile)->pStore;
|
|
int rc = SQLITE_OK;
|
|
memdbEnter(p);
|
|
if( size>p->sz ){
|
|
/* This can only happen with a corrupt wal mode db */
|
|
rc = SQLITE_CORRUPT;
|
|
}else{
|
|
p->sz = size;
|
|
}
|
|
memdbLeave(p);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Sync an memdb-file.
|
|
*/
|
|
static int memdbSync(sqlite3_file *pFile, int flags){
|
|
UNUSED_PARAMETER(pFile);
|
|
UNUSED_PARAMETER(flags);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Return the current file-size of an memdb-file.
|
|
*/
|
|
static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
|
|
MemStore *p = ((MemFile*)pFile)->pStore;
|
|
memdbEnter(p);
|
|
*pSize = p->sz;
|
|
memdbLeave(p);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Lock an memdb-file.
|
|
*/
|
|
static int memdbLock(sqlite3_file *pFile, int eLock){
|
|
MemFile *pThis = (MemFile*)pFile;
|
|
MemStore *p = pThis->pStore;
|
|
int rc = SQLITE_OK;
|
|
if( eLock==pThis->eLock ) return SQLITE_OK;
|
|
memdbEnter(p);
|
|
if( eLock>SQLITE_LOCK_SHARED ){
|
|
if( p->mFlags & SQLITE_DESERIALIZE_READONLY ){
|
|
rc = SQLITE_READONLY;
|
|
}else if( pThis->eLock<=SQLITE_LOCK_SHARED ){
|
|
if( p->nWrLock ){
|
|
rc = SQLITE_BUSY;
|
|
}else{
|
|
p->nWrLock = 1;
|
|
}
|
|
}
|
|
}else if( eLock==SQLITE_LOCK_SHARED ){
|
|
if( pThis->eLock > SQLITE_LOCK_SHARED ){
|
|
assert( p->nWrLock==1 );
|
|
p->nWrLock = 0;
|
|
}else if( p->nWrLock ){
|
|
rc = SQLITE_BUSY;
|
|
}else{
|
|
p->nRdLock++;
|
|
}
|
|
}else{
|
|
assert( eLock==SQLITE_LOCK_NONE );
|
|
if( pThis->eLock>SQLITE_LOCK_SHARED ){
|
|
assert( p->nWrLock==1 );
|
|
p->nWrLock = 0;
|
|
}
|
|
assert( p->nRdLock>0 );
|
|
p->nRdLock--;
|
|
}
|
|
if( rc==SQLITE_OK ) pThis->eLock = eLock;
|
|
memdbLeave(p);
|
|
return rc;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
** This interface is only used for crash recovery, which does not
|
|
** occur on an in-memory database.
|
|
*/
|
|
static int memdbCheckReservedLock(sqlite3_file *pFile, int *pResOut){
|
|
*pResOut = 0;
|
|
return SQLITE_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
** File control method. For custom operations on an memdb-file.
|
|
*/
|
|
static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){
|
|
MemStore *p = ((MemFile*)pFile)->pStore;
|
|
int rc = SQLITE_NOTFOUND;
|
|
memdbEnter(p);
|
|
if( op==SQLITE_FCNTL_VFSNAME ){
|
|
*(char**)pArg = sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz);
|
|
rc = SQLITE_OK;
|
|
}
|
|
if( op==SQLITE_FCNTL_SIZE_LIMIT ){
|
|
sqlite3_int64 iLimit = *(sqlite3_int64*)pArg;
|
|
if( iLimit<p->sz ){
|
|
if( iLimit<0 ){
|
|
iLimit = p->szMax;
|
|
}else{
|
|
iLimit = p->sz;
|
|
}
|
|
}
|
|
p->szMax = iLimit;
|
|
*(sqlite3_int64*)pArg = iLimit;
|
|
rc = SQLITE_OK;
|
|
}
|
|
memdbLeave(p);
|
|
return rc;
|
|
}
|
|
|
|
#if 0 /* Not used because of SQLITE_IOCAP_POWERSAFE_OVERWRITE */
|
|
/*
|
|
** Return the sector-size in bytes for an memdb-file.
|
|
*/
|
|
static int memdbSectorSize(sqlite3_file *pFile){
|
|
return 1024;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** Return the device characteristic flags supported by an memdb-file.
|
|
*/
|
|
static int memdbDeviceCharacteristics(sqlite3_file *pFile){
|
|
UNUSED_PARAMETER(pFile);
|
|
return SQLITE_IOCAP_ATOMIC |
|
|
SQLITE_IOCAP_POWERSAFE_OVERWRITE |
|
|
SQLITE_IOCAP_SAFE_APPEND |
|
|
SQLITE_IOCAP_SEQUENTIAL;
|
|
}
|
|
|
|
/* Fetch a page of a memory-mapped file */
|
|
static int memdbFetch(
|
|
sqlite3_file *pFile,
|
|
sqlite3_int64 iOfst,
|
|
int iAmt,
|
|
void **pp
|
|
){
|
|
MemStore *p = ((MemFile*)pFile)->pStore;
|
|
memdbEnter(p);
|
|
if( iOfst+iAmt>p->sz || (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)!=0 ){
|
|
*pp = 0;
|
|
}else{
|
|
p->nMmap++;
|
|
*pp = (void*)(p->aData + iOfst);
|
|
}
|
|
memdbLeave(p);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/* Release a memory-mapped page */
|
|
static int memdbUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){
|
|
MemStore *p = ((MemFile*)pFile)->pStore;
|
|
UNUSED_PARAMETER(iOfst);
|
|
UNUSED_PARAMETER(pPage);
|
|
memdbEnter(p);
|
|
p->nMmap--;
|
|
memdbLeave(p);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Open an mem file handle.
|
|
*/
|
|
static int memdbOpen(
|
|
sqlite3_vfs *pVfs,
|
|
const char *zName,
|
|
sqlite3_file *pFd,
|
|
int flags,
|
|
int *pOutFlags
|
|
){
|
|
MemFile *pFile = (MemFile*)pFd;
|
|
MemStore *p = 0;
|
|
int szName;
|
|
UNUSED_PARAMETER(pVfs);
|
|
|
|
memset(pFile, 0, sizeof(*pFile));
|
|
szName = sqlite3Strlen30(zName);
|
|
if( szName>1 && zName[0]=='/' ){
|
|
int i;
|
|
#ifndef SQLITE_MUTEX_OMIT
|
|
sqlite3_mutex *pVfsMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
|
|
#endif
|
|
sqlite3_mutex_enter(pVfsMutex);
|
|
for(i=0; i<memdb_g.nMemStore; i++){
|
|
if( strcmp(memdb_g.apMemStore[i]->zFName,zName)==0 ){
|
|
p = memdb_g.apMemStore[i];
|
|
break;
|
|
}
|
|
}
|
|
if( p==0 ){
|
|
MemStore **apNew;
|
|
p = sqlite3Malloc( sizeof(*p) + szName + 3 );
|
|
if( p==0 ){
|
|
sqlite3_mutex_leave(pVfsMutex);
|
|
return SQLITE_NOMEM;
|
|
}
|
|
apNew = sqlite3Realloc(memdb_g.apMemStore,
|
|
sizeof(apNew[0])*(memdb_g.nMemStore+1) );
|
|
if( apNew==0 ){
|
|
sqlite3_free(p);
|
|
sqlite3_mutex_leave(pVfsMutex);
|
|
return SQLITE_NOMEM;
|
|
}
|
|
apNew[memdb_g.nMemStore++] = p;
|
|
memdb_g.apMemStore = apNew;
|
|
memset(p, 0, sizeof(*p));
|
|
p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE|SQLITE_DESERIALIZE_FREEONCLOSE;
|
|
p->szMax = sqlite3GlobalConfig.mxMemdbSize;
|
|
p->zFName = (char*)&p[1];
|
|
memcpy(p->zFName, zName, szName+1);
|
|
p->pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
|
|
if( p->pMutex==0 ){
|
|
memdb_g.nMemStore--;
|
|
sqlite3_free(p);
|
|
sqlite3_mutex_leave(pVfsMutex);
|
|
return SQLITE_NOMEM;
|
|
}
|
|
p->nRef = 1;
|
|
memdbEnter(p);
|
|
}else{
|
|
memdbEnter(p);
|
|
p->nRef++;
|
|
}
|
|
sqlite3_mutex_leave(pVfsMutex);
|
|
}else{
|
|
p = sqlite3Malloc( sizeof(*p) );
|
|
if( p==0 ){
|
|
return SQLITE_NOMEM;
|
|
}
|
|
memset(p, 0, sizeof(*p));
|
|
p->mFlags = SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE;
|
|
p->szMax = sqlite3GlobalConfig.mxMemdbSize;
|
|
}
|
|
pFile->pStore = p;
|
|
if( pOutFlags!=0 ){
|
|
*pOutFlags = flags | SQLITE_OPEN_MEMORY;
|
|
}
|
|
pFd->pMethods = &memdb_io_methods;
|
|
memdbLeave(p);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
#if 0 /* Only used to delete rollback journals, super-journals, and WAL
|
|
** files, none of which exist in memdb. So this routine is never used */
|
|
/*
|
|
** 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 memdbDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
|
|
return SQLITE_IOERR_DELETE;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** Test for access permissions. Return true if the requested permission
|
|
** is available, or false otherwise.
|
|
**
|
|
** With memdb, no files ever exist on disk. So always return false.
|
|
*/
|
|
static int memdbAccess(
|
|
sqlite3_vfs *pVfs,
|
|
const char *zPath,
|
|
int flags,
|
|
int *pResOut
|
|
){
|
|
UNUSED_PARAMETER(pVfs);
|
|
UNUSED_PARAMETER(zPath);
|
|
UNUSED_PARAMETER(flags);
|
|
*pResOut = 0;
|
|
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 memdbFullPathname(
|
|
sqlite3_vfs *pVfs,
|
|
const char *zPath,
|
|
int nOut,
|
|
char *zOut
|
|
){
|
|
UNUSED_PARAMETER(pVfs);
|
|
sqlite3_snprintf(nOut, zOut, "%s", zPath);
|
|
return SQLITE_OK;
|
|
}
|
|
|
|
/*
|
|
** Open the dynamic library located at zPath and return a handle.
|
|
*/
|
|
static void *memdbDlOpen(sqlite3_vfs *pVfs, const char *zPath){
|
|
return ORIGVFS(pVfs)->xDlOpen(ORIGVFS(pVfs), zPath);
|
|
}
|
|
|
|
/*
|
|
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
|
|
** utf-8 string describing the most recent error encountered associated
|
|
** with dynamic libraries.
|
|
*/
|
|
static void memdbDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
|
|
ORIGVFS(pVfs)->xDlError(ORIGVFS(pVfs), nByte, zErrMsg);
|
|
}
|
|
|
|
/*
|
|
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
|
*/
|
|
static void (*memdbDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
|
|
return ORIGVFS(pVfs)->xDlSym(ORIGVFS(pVfs), p, zSym);
|
|
}
|
|
|
|
/*
|
|
** Close the dynamic library handle pHandle.
|
|
*/
|
|
static void memdbDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
|
ORIGVFS(pVfs)->xDlClose(ORIGVFS(pVfs), pHandle);
|
|
}
|
|
|
|
/*
|
|
** Populate the buffer pointed to by zBufOut with nByte bytes of
|
|
** random data.
|
|
*/
|
|
static int memdbRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
|
|
return ORIGVFS(pVfs)->xRandomness(ORIGVFS(pVfs), nByte, zBufOut);
|
|
}
|
|
|
|
/*
|
|
** Sleep for nMicro microseconds. Return the number of microseconds
|
|
** actually slept.
|
|
*/
|
|
static int memdbSleep(sqlite3_vfs *pVfs, int nMicro){
|
|
return ORIGVFS(pVfs)->xSleep(ORIGVFS(pVfs), nMicro);
|
|
}
|
|
|
|
#if 0 /* Never used. Modern cores only call xCurrentTimeInt64() */
|
|
/*
|
|
** Return the current time as a Julian Day number in *pTimeOut.
|
|
*/
|
|
static int memdbCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
|
|
return ORIGVFS(pVfs)->xCurrentTime(ORIGVFS(pVfs), pTimeOut);
|
|
}
|
|
#endif
|
|
|
|
static int memdbGetLastError(sqlite3_vfs *pVfs, int a, char *b){
|
|
return ORIGVFS(pVfs)->xGetLastError(ORIGVFS(pVfs), a, b);
|
|
}
|
|
static int memdbCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p){
|
|
return ORIGVFS(pVfs)->xCurrentTimeInt64(ORIGVFS(pVfs), p);
|
|
}
|
|
|
|
/*
|
|
** Translate a database connection pointer and schema name into a
|
|
** MemFile pointer.
|
|
*/
|
|
static MemFile *memdbFromDbSchema(sqlite3 *db, const char *zSchema){
|
|
MemFile *p = 0;
|
|
MemStore *pStore;
|
|
int rc = sqlite3_file_control(db, zSchema, SQLITE_FCNTL_FILE_POINTER, &p);
|
|
if( rc ) return 0;
|
|
if( p->base.pMethods!=&memdb_io_methods ) return 0;
|
|
pStore = p->pStore;
|
|
memdbEnter(pStore);
|
|
if( pStore->zFName!=0 ) p = 0;
|
|
memdbLeave(pStore);
|
|
return p;
|
|
}
|
|
|
|
/*
|
|
** Return the serialization of a database
|
|
*/
|
|
unsigned char *sqlite3_serialize(
|
|
sqlite3 *db, /* The database connection */
|
|
const char *zSchema, /* Which database within the connection */
|
|
sqlite3_int64 *piSize, /* Write size here, if not NULL */
|
|
unsigned int mFlags /* Maybe SQLITE_SERIALIZE_NOCOPY */
|
|
){
|
|
MemFile *p;
|
|
int iDb;
|
|
Btree *pBt;
|
|
sqlite3_int64 sz;
|
|
int szPage = 0;
|
|
sqlite3_stmt *pStmt = 0;
|
|
unsigned char *pOut;
|
|
char *zSql;
|
|
int rc;
|
|
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
if( !sqlite3SafetyCheckOk(db) ){
|
|
(void)SQLITE_MISUSE_BKPT;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if( zSchema==0 ) zSchema = db->aDb[0].zDbSName;
|
|
p = memdbFromDbSchema(db, zSchema);
|
|
iDb = sqlite3FindDbName(db, zSchema);
|
|
if( piSize ) *piSize = -1;
|
|
if( iDb<0 ) return 0;
|
|
if( p ){
|
|
MemStore *pStore = p->pStore;
|
|
assert( pStore->pMutex==0 );
|
|
if( piSize ) *piSize = pStore->sz;
|
|
if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
|
|
pOut = pStore->aData;
|
|
}else{
|
|
pOut = sqlite3_malloc64( pStore->sz );
|
|
if( pOut ) memcpy(pOut, pStore->aData, pStore->sz);
|
|
}
|
|
return pOut;
|
|
}
|
|
pBt = db->aDb[iDb].pBt;
|
|
if( pBt==0 ) return 0;
|
|
szPage = sqlite3BtreeGetPageSize(pBt);
|
|
zSql = sqlite3_mprintf("PRAGMA \"%w\".page_count", zSchema);
|
|
rc = zSql ? sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) : SQLITE_NOMEM;
|
|
sqlite3_free(zSql);
|
|
if( rc ) return 0;
|
|
rc = sqlite3_step(pStmt);
|
|
if( rc!=SQLITE_ROW ){
|
|
pOut = 0;
|
|
}else{
|
|
sz = sqlite3_column_int64(pStmt, 0)*szPage;
|
|
if( piSize ) *piSize = sz;
|
|
if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
|
|
pOut = 0;
|
|
}else{
|
|
pOut = sqlite3_malloc64( sz );
|
|
if( pOut ){
|
|
int nPage = sqlite3_column_int(pStmt, 0);
|
|
Pager *pPager = sqlite3BtreePager(pBt);
|
|
int pgno;
|
|
for(pgno=1; pgno<=nPage; pgno++){
|
|
DbPage *pPage = 0;
|
|
unsigned char *pTo = pOut + szPage*(sqlite3_int64)(pgno-1);
|
|
rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pPage, 0);
|
|
if( rc==SQLITE_OK ){
|
|
memcpy(pTo, sqlite3PagerGetData(pPage), szPage);
|
|
}else{
|
|
memset(pTo, 0, szPage);
|
|
}
|
|
sqlite3PagerUnref(pPage);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sqlite3_finalize(pStmt);
|
|
return pOut;
|
|
}
|
|
|
|
/* Convert zSchema to a MemDB and initialize its content.
|
|
*/
|
|
int sqlite3_deserialize(
|
|
sqlite3 *db, /* The database connection */
|
|
const char *zSchema, /* Which DB to reopen with the deserialization */
|
|
unsigned char *pData, /* The serialized database content */
|
|
sqlite3_int64 szDb, /* Number bytes in the deserialization */
|
|
sqlite3_int64 szBuf, /* Total size of buffer pData[] */
|
|
unsigned mFlags /* Zero or more SQLITE_DESERIALIZE_* flags */
|
|
){
|
|
MemFile *p;
|
|
char *zSql;
|
|
sqlite3_stmt *pStmt = 0;
|
|
int rc;
|
|
int iDb;
|
|
|
|
#ifdef SQLITE_ENABLE_API_ARMOR
|
|
if( !sqlite3SafetyCheckOk(db) ){
|
|
return SQLITE_MISUSE_BKPT;
|
|
}
|
|
if( szDb<0 ) return SQLITE_MISUSE_BKPT;
|
|
if( szBuf<0 ) return SQLITE_MISUSE_BKPT;
|
|
#endif
|
|
|
|
sqlite3_mutex_enter(db->mutex);
|
|
if( zSchema==0 ) zSchema = db->aDb[0].zDbSName;
|
|
iDb = sqlite3FindDbName(db, zSchema);
|
|
testcase( iDb==1 );
|
|
if( iDb<2 && iDb!=0 ){
|
|
rc = SQLITE_ERROR;
|
|
goto end_deserialize;
|
|
}
|
|
zSql = sqlite3_mprintf("ATTACH x AS %Q", zSchema);
|
|
if( zSql==0 ){
|
|
rc = SQLITE_NOMEM;
|
|
}else{
|
|
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
|
sqlite3_free(zSql);
|
|
}
|
|
if( rc ) goto end_deserialize;
|
|
db->init.iDb = (u8)iDb;
|
|
db->init.reopenMemdb = 1;
|
|
rc = sqlite3_step(pStmt);
|
|
db->init.reopenMemdb = 0;
|
|
if( rc!=SQLITE_DONE ){
|
|
rc = SQLITE_ERROR;
|
|
goto end_deserialize;
|
|
}
|
|
p = memdbFromDbSchema(db, zSchema);
|
|
if( p==0 ){
|
|
rc = SQLITE_ERROR;
|
|
}else{
|
|
MemStore *pStore = p->pStore;
|
|
pStore->aData = pData;
|
|
pData = 0;
|
|
pStore->sz = szDb;
|
|
pStore->szAlloc = szBuf;
|
|
pStore->szMax = szBuf;
|
|
if( pStore->szMax<sqlite3GlobalConfig.mxMemdbSize ){
|
|
pStore->szMax = sqlite3GlobalConfig.mxMemdbSize;
|
|
}
|
|
pStore->mFlags = mFlags;
|
|
rc = SQLITE_OK;
|
|
}
|
|
|
|
end_deserialize:
|
|
sqlite3_finalize(pStmt);
|
|
if( pData && (mFlags & SQLITE_DESERIALIZE_FREEONCLOSE)!=0 ){
|
|
sqlite3_free(pData);
|
|
}
|
|
sqlite3_mutex_leave(db->mutex);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** This routine is called when the extension is loaded.
|
|
** Register the new VFS.
|
|
*/
|
|
int sqlite3MemdbInit(void){
|
|
sqlite3_vfs *pLower = sqlite3_vfs_find(0);
|
|
unsigned int sz;
|
|
if( NEVER(pLower==0) ) return SQLITE_ERROR;
|
|
sz = pLower->szOsFile;
|
|
memdb_vfs.pAppData = pLower;
|
|
/* The following conditional can only be true when compiled for
|
|
** Windows x86 and SQLITE_MAX_MMAP_SIZE=0. We always leave
|
|
** it in, to be safe, but it is marked as NO_TEST since there
|
|
** is no way to reach it under most builds. */
|
|
if( sz<sizeof(MemFile) ) sz = sizeof(MemFile); /*NO_TEST*/
|
|
memdb_vfs.szOsFile = sz;
|
|
return sqlite3_vfs_register(&memdb_vfs, 0);
|
|
}
|
|
#endif /* SQLITE_OMIT_DESERIALIZE */
|