xref: /sqlite-3.40.0/src/os_kv.c (revision a50d3b7a)
17585f49aSdrh /*
27585f49aSdrh ** 2022-09-06
37585f49aSdrh **
47585f49aSdrh ** The author disclaims copyright to this source code.  In place of
57585f49aSdrh ** a legal notice, here is a blessing:
67585f49aSdrh **
77585f49aSdrh **    May you do good and not evil.
87585f49aSdrh **    May you find forgiveness for yourself and forgive others.
97585f49aSdrh **    May you share freely, never taking more than you give.
107585f49aSdrh **
117585f49aSdrh ******************************************************************************
127585f49aSdrh **
137585f49aSdrh ** This file contains an experimental VFS layer that operates on a
147585f49aSdrh ** Key/Value storage engine where both keys and values must be pure
157585f49aSdrh ** text.
167585f49aSdrh */
177585f49aSdrh #include <sqliteInt.h>
1820a9ed1dSdrh #if SQLITE_OS_KV || (SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL))
197585f49aSdrh 
207585f49aSdrh /*****************************************************************************
217585f49aSdrh ** Debugging logic
227585f49aSdrh */
237585f49aSdrh 
247585f49aSdrh /* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */
257585f49aSdrh #if 0
267585f49aSdrh #define SQLITE_KV_TRACE(X)  printf X;
277585f49aSdrh #else
287585f49aSdrh #define SQLITE_KV_TRACE(X)
297585f49aSdrh #endif
307585f49aSdrh 
317585f49aSdrh /* SQLITE_KV_LOG() is used for tracing calls to the VFS interface */
327585f49aSdrh #if 0
337585f49aSdrh #define SQLITE_KV_LOG(X)  printf X;
347585f49aSdrh #else
357585f49aSdrh #define SQLITE_KV_LOG(X)
367585f49aSdrh #endif
377585f49aSdrh 
387585f49aSdrh 
397585f49aSdrh /*
407585f49aSdrh ** Forward declaration of objects used by this VFS implementation
417585f49aSdrh */
427585f49aSdrh typedef struct KVVfsFile KVVfsFile;
437585f49aSdrh 
447585f49aSdrh /* A single open file.  There are only two files represented by this
457585f49aSdrh ** VFS - the database and the rollback journal.
467585f49aSdrh */
477585f49aSdrh struct KVVfsFile {
487585f49aSdrh   sqlite3_file base;              /* IO methods */
497585f49aSdrh   const char *zClass;             /* Storage class */
507585f49aSdrh   int isJournal;                  /* True if this is a journal file */
517585f49aSdrh   unsigned int nJrnl;             /* Space allocated for aJrnl[] */
527585f49aSdrh   char *aJrnl;                    /* Journal content */
537585f49aSdrh   int szPage;                     /* Last known page size */
547585f49aSdrh   sqlite3_int64 szDb;             /* Database file size.  -1 means unknown */
557585f49aSdrh };
567585f49aSdrh 
577585f49aSdrh /*
587585f49aSdrh ** Methods for KVVfsFile
597585f49aSdrh */
607585f49aSdrh static int kvvfsClose(sqlite3_file*);
617585f49aSdrh static int kvvfsReadDb(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
627585f49aSdrh static int kvvfsReadJrnl(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
637585f49aSdrh static int kvvfsWriteDb(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
647585f49aSdrh static int kvvfsWriteJrnl(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
657585f49aSdrh static int kvvfsTruncateDb(sqlite3_file*, sqlite3_int64 size);
667585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file*, sqlite3_int64 size);
677585f49aSdrh static int kvvfsSyncDb(sqlite3_file*, int flags);
687585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file*, int flags);
697585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file*, sqlite3_int64 *pSize);
707585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize);
717585f49aSdrh static int kvvfsLock(sqlite3_file*, int);
727585f49aSdrh static int kvvfsUnlock(sqlite3_file*, int);
737585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut);
7484e50767Sdrh static int kvvfsFileControlDb(sqlite3_file*, int op, void *pArg);
7584e50767Sdrh static int kvvfsFileControlJrnl(sqlite3_file*, int op, void *pArg);
767585f49aSdrh static int kvvfsSectorSize(sqlite3_file*);
777585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file*);
787585f49aSdrh 
797585f49aSdrh /*
807585f49aSdrh ** Methods for sqlite3_vfs
817585f49aSdrh */
827585f49aSdrh static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
837585f49aSdrh static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
847585f49aSdrh static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
857585f49aSdrh static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
867585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename);
877585f49aSdrh static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
887585f49aSdrh static int kvvfsSleep(sqlite3_vfs*, int microseconds);
897585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs*, double*);
907585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
917585f49aSdrh 
9220a9ed1dSdrh static sqlite3_vfs sqlite3OsKvvfsObject = {
937585f49aSdrh   1,                              /* iVersion */
947585f49aSdrh   sizeof(KVVfsFile),              /* szOsFile */
957585f49aSdrh   1024,                           /* mxPathname */
967585f49aSdrh   0,                              /* pNext */
977585f49aSdrh   "kvvfs",                        /* zName */
987585f49aSdrh   0,                              /* pAppData */
997585f49aSdrh   kvvfsOpen,                      /* xOpen */
1007585f49aSdrh   kvvfsDelete,                    /* xDelete */
1017585f49aSdrh   kvvfsAccess,                    /* xAccess */
1027585f49aSdrh   kvvfsFullPathname,              /* xFullPathname */
1037585f49aSdrh   kvvfsDlOpen,                    /* xDlOpen */
1047585f49aSdrh   0,                              /* xDlError */
1057585f49aSdrh   0,                              /* xDlSym */
1067585f49aSdrh   0,                              /* xDlClose */
1077585f49aSdrh   kvvfsRandomness,                /* xRandomness */
1087585f49aSdrh   kvvfsSleep,                     /* xSleep */
1097585f49aSdrh   kvvfsCurrentTime,               /* xCurrentTime */
1107585f49aSdrh   0,                              /* xGetLastError */
1117585f49aSdrh   kvvfsCurrentTimeInt64           /* xCurrentTimeInt64 */
1127585f49aSdrh };
1137585f49aSdrh 
1147585f49aSdrh /* Methods for sqlite3_file objects referencing a database file
1157585f49aSdrh */
1167585f49aSdrh static sqlite3_io_methods kvvfs_db_io_methods = {
1177585f49aSdrh   1,                              /* iVersion */
1187585f49aSdrh   kvvfsClose,                     /* xClose */
1197585f49aSdrh   kvvfsReadDb,                    /* xRead */
1207585f49aSdrh   kvvfsWriteDb,                   /* xWrite */
1217585f49aSdrh   kvvfsTruncateDb,                /* xTruncate */
1227585f49aSdrh   kvvfsSyncDb,                    /* xSync */
1237585f49aSdrh   kvvfsFileSizeDb,                /* xFileSize */
1247585f49aSdrh   kvvfsLock,                      /* xLock */
1257585f49aSdrh   kvvfsUnlock,                    /* xUnlock */
1267585f49aSdrh   kvvfsCheckReservedLock,         /* xCheckReservedLock */
12784e50767Sdrh   kvvfsFileControlDb,             /* xFileControl */
1287585f49aSdrh   kvvfsSectorSize,                /* xSectorSize */
1297585f49aSdrh   kvvfsDeviceCharacteristics,     /* xDeviceCharacteristics */
1307585f49aSdrh   0,                              /* xShmMap */
1317585f49aSdrh   0,                              /* xShmLock */
1327585f49aSdrh   0,                              /* xShmBarrier */
1337585f49aSdrh   0,                              /* xShmUnmap */
1347585f49aSdrh   0,                              /* xFetch */
1357585f49aSdrh   0                               /* xUnfetch */
1367585f49aSdrh };
1377585f49aSdrh 
1387585f49aSdrh /* Methods for sqlite3_file objects referencing a rollback journal
1397585f49aSdrh */
1407585f49aSdrh static sqlite3_io_methods kvvfs_jrnl_io_methods = {
1417585f49aSdrh   1,                              /* iVersion */
1427585f49aSdrh   kvvfsClose,                     /* xClose */
1437585f49aSdrh   kvvfsReadJrnl,                  /* xRead */
1447585f49aSdrh   kvvfsWriteJrnl,                 /* xWrite */
1457585f49aSdrh   kvvfsTruncateJrnl,              /* xTruncate */
1467585f49aSdrh   kvvfsSyncJrnl,                  /* xSync */
1477585f49aSdrh   kvvfsFileSizeJrnl,              /* xFileSize */
1487585f49aSdrh   kvvfsLock,                      /* xLock */
1497585f49aSdrh   kvvfsUnlock,                    /* xUnlock */
1507585f49aSdrh   kvvfsCheckReservedLock,         /* xCheckReservedLock */
15184e50767Sdrh   kvvfsFileControlJrnl,           /* xFileControl */
1527585f49aSdrh   kvvfsSectorSize,                /* xSectorSize */
1537585f49aSdrh   kvvfsDeviceCharacteristics,     /* xDeviceCharacteristics */
1547585f49aSdrh   0,                              /* xShmMap */
1557585f49aSdrh   0,                              /* xShmLock */
1567585f49aSdrh   0,                              /* xShmBarrier */
1577585f49aSdrh   0,                              /* xShmUnmap */
1587585f49aSdrh   0,                              /* xFetch */
1597585f49aSdrh   0                               /* xUnfetch */
1607585f49aSdrh };
1617585f49aSdrh 
1627585f49aSdrh /****** Storage subsystem **************************************************/
1637585f49aSdrh #include <sys/types.h>
1647585f49aSdrh #include <sys/stat.h>
1657585f49aSdrh #include <unistd.h>
1667585f49aSdrh 
1677585f49aSdrh /* Forward declarations for the low-level storage engine
1687585f49aSdrh */
169c6c9c6aaSdrh #define KVSTORAGE_KEY_SZ  32
1707585f49aSdrh 
1717585f49aSdrh /* Expand the key name with an appropriate prefix and put the result
172c6c9c6aaSdrh ** zKeyOut[].  The zKeyOut[] buffer is assumed to hold at least
173c6c9c6aaSdrh ** KVSTORAGE_KEY_SZ bytes.
1747585f49aSdrh */
1757585f49aSdrh static void kvstorageMakeKey(
1767585f49aSdrh   const char *zClass,
1777585f49aSdrh   const char *zKeyIn,
1787585f49aSdrh   char *zKeyOut
1797585f49aSdrh ){
180c6c9c6aaSdrh   sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn);
1817585f49aSdrh }
1827585f49aSdrh 
183ad5125a6Sstephan #ifdef __EMSCRIPTEN__
184ad5125a6Sstephan /* Provide Emscripten-based impls of kvstorageWrite/Read/Delete()... */
185ad5125a6Sstephan #include <emscripten.h>
186ad5125a6Sstephan #include <emscripten/console.h>
187ad5125a6Sstephan 
188ad5125a6Sstephan /*
189ad5125a6Sstephan ** WASM_KEEP is identical to EMSCRIPTEN_KEEPALIVE but is not
190ad5125a6Sstephan ** Emscripten-specific. It explicitly includes marked functions for
191ad5125a6Sstephan ** export into the target wasm file without requiring explicit listing
192ad5125a6Sstephan ** of those functions in Emscripten's -sEXPORTED_FUNCTIONS=... list
193ad5125a6Sstephan ** (or equivalent in other build platforms). Any function with neither
194ad5125a6Sstephan ** this attribute nor which is listed as an explicit export will not
195ad5125a6Sstephan ** be exported from the wasm file (but may still be used internally
196ad5125a6Sstephan ** within the wasm file).
197ad5125a6Sstephan **
198ad5125a6Sstephan ** The functions in this file (sqlite3-wasm.c) which require exporting
199ad5125a6Sstephan ** are marked with this flag. They may also be added to any explicit
200ad5125a6Sstephan ** build-time export list but need not be. All of these APIs are
201ad5125a6Sstephan ** intended for use only within the project's own JS/WASM code, and
202ad5125a6Sstephan ** not by client code, so an argument can be made for reducing their
203ad5125a6Sstephan ** visibility by not including them in any build-time export lists.
204ad5125a6Sstephan **
205ad5125a6Sstephan ** 2022-09-11: it's not yet _proven_ that this approach works in
206ad5125a6Sstephan ** non-Emscripten builds. If not, such builds will need to export
207ad5125a6Sstephan ** those using the --export=... wasm-ld flag (or equivalent). As of
208ad5125a6Sstephan ** this writing we are tied to Emscripten for various reasons
209ad5125a6Sstephan ** and cannot test the library with other build environments.
210ad5125a6Sstephan */
211ad5125a6Sstephan #define WASM_KEEP __attribute__((used,visibility("default")))
212ad5125a6Sstephan /*
213ad5125a6Sstephan ** An internal level of indirection for accessing the static
214ad5125a6Sstephan ** kvstorageMakeKey() from EM_JS()-generated functions. This must be
215ad5125a6Sstephan ** made available for export via Emscripten but is not intended to be
216ad5125a6Sstephan ** used from client code. If called with a NULL zKeyOut it is a no-op.
217ad5125a6Sstephan ** It returns KVSTORAGE_KEY_SZ, so JS code (which cannot see that
218ad5125a6Sstephan ** constant) may call it with NULL arguments to get the size of the
219ad5125a6Sstephan ** allocation they'll need for a kvvfs key.
220ad5125a6Sstephan **
221ad5125a6Sstephan ** Maintenance reminder: Emscripten will install this in the Module
222ad5125a6Sstephan ** init scope and will prefix its name with "_".
223ad5125a6Sstephan */
224ad5125a6Sstephan WASM_KEEP
225ad5125a6Sstephan int sqlite3_wasm__kvvfsMakeKey(const char *zClass, const char *zKeyIn,
226ad5125a6Sstephan                                char *zKeyOut){
227ad5125a6Sstephan   if( 0!=zKeyOut ) kvstorageMakeKey(zClass, zKeyIn, zKeyOut);
228ad5125a6Sstephan   return KVSTORAGE_KEY_SZ;
229ad5125a6Sstephan }
230ad5125a6Sstephan /*
231ad5125a6Sstephan ** Internal helper for kvstorageWrite/Read/Delete() which creates a
232ad5125a6Sstephan ** storage key for the given zClass/zKeyIn combination. Returns a
233ad5125a6Sstephan ** pointer to the key: a C string allocated on the WASM stack, or 0 if
234ad5125a6Sstephan ** allocation fails. It is up to the caller to save/restore the stack
235ad5125a6Sstephan ** before/after this operation.
236ad5125a6Sstephan */
237ad5125a6Sstephan EM_JS(const char *, kvstorageMakeKeyOnJSStack,
238ad5125a6Sstephan       (const char *zClass, const char *zKeyIn),{
239ad5125a6Sstephan   if( 0==zClass || 0==zKeyIn) return 0;
240ad5125a6Sstephan   const zXKey = stackAlloc(_sqlite3_wasm__kvvfsMakeKey(0,0,0));
241ad5125a6Sstephan   if(zXKey) _sqlite3_wasm__kvvfsMakeKey(zClass, zKeyIn, zXKey);
242ad5125a6Sstephan   return zXKey;
243ad5125a6Sstephan });
244ad5125a6Sstephan 
245ad5125a6Sstephan /*
246ad5125a6Sstephan ** JS impl of kvstorageWrite(). Main docs are in the C impl. This impl
247ad5125a6Sstephan ** writes zData to the global sessionStorage (if zClass starts with
248ad5125a6Sstephan ** 's') or localStorage, using a storage key derived from zClass and
249ad5125a6Sstephan ** zKey.
250ad5125a6Sstephan */
251ad5125a6Sstephan EM_JS(int, kvstorageWrite,
252ad5125a6Sstephan       (const char *zClass, const char *zKey, const char *zData),{
253ad5125a6Sstephan   const stack = stackSave();
254ad5125a6Sstephan   try {
255ad5125a6Sstephan     const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey);
256ad5125a6Sstephan     if(!zXKey) return 1/*OOM*/;
257ad5125a6Sstephan     const jKey = UTF8ToString(zXKey);
258ad5125a6Sstephan     /**
259ad5125a6Sstephan        We could simplify this function and eliminate the
260ad5125a6Sstephan        kvstorageMakeKey() symbol acrobatics if we'd simply hard-code
261ad5125a6Sstephan        the key algo into the 3 functions which need it:
262ad5125a6Sstephan 
263ad5125a6Sstephan        const jKey = "kvvfs-"+UTF8ToString(zClass)+"-"+UTF8ToString(zKey);
264ad5125a6Sstephan     */
265ad5125a6Sstephan     ((115/*=='s'*/===getValue(zClass))
266ad5125a6Sstephan      ? sessionStorage : localStorage).setItem(jKey, UTF8ToString(zData));
267ad5125a6Sstephan   }catch(e){
268ad5125a6Sstephan     console.error("kvstorageWrite()",e);
269ad5125a6Sstephan     return 1; // Can't access SQLITE_xxx from here
270ad5125a6Sstephan   }finally{
271ad5125a6Sstephan     stackRestore(stack);
272ad5125a6Sstephan   }
273ad5125a6Sstephan   return 0;
274ad5125a6Sstephan });
275ad5125a6Sstephan 
276ad5125a6Sstephan /*
277ad5125a6Sstephan ** JS impl of kvstorageDelete(). Main docs are in the C impl. This
278ad5125a6Sstephan ** impl generates a key derived from zClass and zKey, and removes the
279ad5125a6Sstephan ** matching entry (if any) from global sessionStorage (if zClass
280ad5125a6Sstephan ** starts with 's') or localStorage.
281ad5125a6Sstephan */
282ad5125a6Sstephan EM_JS(int, kvstorageDelete,
283ad5125a6Sstephan       (const char *zClass, const char *zKey),{
284ad5125a6Sstephan   const stack = stackSave();
285ad5125a6Sstephan   try {
286ad5125a6Sstephan     const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey);
287ad5125a6Sstephan     if(!zXKey) return 1/*OOM*/;
288ad5125a6Sstephan     const jKey = UTF8ToString(zXKey);
289ad5125a6Sstephan     ((115/*=='s'*/===getValue(zClass))
290ad5125a6Sstephan      ? sessionStorage : localStorage).removeItem(jKey);
291ad5125a6Sstephan   }catch(e){
292ad5125a6Sstephan     console.error("kvstorageDelete()",e);
293ad5125a6Sstephan     return 1;
294ad5125a6Sstephan   }finally{
295ad5125a6Sstephan     stackRestore(stack);
296ad5125a6Sstephan   }
297ad5125a6Sstephan   return 0;
298ad5125a6Sstephan });
299ad5125a6Sstephan 
300ad5125a6Sstephan /*
301ad5125a6Sstephan ** JS impl of kvstorageRead(). Main docs are in the C impl. This impl
302ad5125a6Sstephan ** reads its data from the global sessionStorage (if zClass starts
303ad5125a6Sstephan ** with 's') or localStorage, using a storage key derived from zClass
304ad5125a6Sstephan ** and zKey.
305ad5125a6Sstephan */
306ad5125a6Sstephan EM_JS(int, kvstorageRead,
307ad5125a6Sstephan       (const char *zClass, const char *zKey, char *zBuf, int nBuf),{
308ad5125a6Sstephan   const stack = stackSave();
309ad5125a6Sstephan   try {
310ad5125a6Sstephan     const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey);
311ad5125a6Sstephan     if(!zXKey) return -3/*OOM*/;
312ad5125a6Sstephan     const jKey = UTF8ToString(zXKey);
313ad5125a6Sstephan     const jV = ((115/*=='s'*/===getValue(zClass))
314ad5125a6Sstephan                 ? sessionStorage : localStorage).getItem(jKey);
315ad5125a6Sstephan     if(!jV) return -1;
316ad5125a6Sstephan     const nV = jV.length /* Note that we are relying 100% on v being
317ad5125a6Sstephan                             ASCII so that jV.length is equal to the
318ad5125a6Sstephan                             C-string's byte length. */;
319ad5125a6Sstephan     if(nBuf<=0) return nV;
320ad5125a6Sstephan     else if(1===nBuf){
321ad5125a6Sstephan       setValue(zBuf, 0);
322ad5125a6Sstephan       return nV;
323ad5125a6Sstephan     }
324ad5125a6Sstephan     const zV = allocateUTF8OnStack(jV);
325ad5125a6Sstephan     if(nBuf > nV + 1) nBuf = nV + 1;
326ad5125a6Sstephan     HEAPU8.copyWithin(zBuf, zV, zV + nBuf - 1);
327ad5125a6Sstephan     setValue( zBuf + nBuf - 1, 0 );
328ad5125a6Sstephan     return nBuf - 1;
329ad5125a6Sstephan   }catch(e){
330ad5125a6Sstephan     console.error("kvstorageRead()",e);
331ad5125a6Sstephan     return -2;
332ad5125a6Sstephan   }finally{
333ad5125a6Sstephan     stackRestore(stack);
334ad5125a6Sstephan   }
335ad5125a6Sstephan });
336ad5125a6Sstephan 
337ad5125a6Sstephan /*
338ad5125a6Sstephan ** This function exists for (1) WASM testing purposes and (2) as a
339ad5125a6Sstephan ** hook to get Emscripten to export several EM_JS()-generated
340ad5125a6Sstephan ** functions (if we don't reference them from exported C functions
341ad5125a6Sstephan ** then they get stripped away at build time). It is not part of the
342ad5125a6Sstephan ** public API and its signature and semantics may change at any time.
343ad5125a6Sstephan ** It's not even part of the private API, for that matter - it's part
344ad5125a6Sstephan ** of the Emscripten C/JS/WASM glue.
345ad5125a6Sstephan */
346ad5125a6Sstephan WASM_KEEP
347ad5125a6Sstephan int sqlite3__wasm_emjs_kvvfs(int whichOp){
348ad5125a6Sstephan   int rc = 0;
349ad5125a6Sstephan   const char * zClass =
350ad5125a6Sstephan     "sezzion" /*don't collide with "session" records!*/;
351ad5125a6Sstephan   const char * zKey = "hello";
352ad5125a6Sstephan   switch( whichOp ){
353ad5125a6Sstephan     case 0: break;
354ad5125a6Sstephan     case 1:
355d22cfa8cSstephan       rc = kvstorageWrite(zClass, zKey, "world");
356ad5125a6Sstephan       break;
357ad5125a6Sstephan     case 2: {
358ad5125a6Sstephan       char buffer[128] = {0};
359ad5125a6Sstephan       char * zBuf = &buffer[0];
360ad5125a6Sstephan       rc = kvstorageRead(zClass, zKey, zBuf, (int)sizeof(buffer));
361ad5125a6Sstephan       emscripten_console_logf("kvstorageRead()=%d %s\n", rc, zBuf);
362ad5125a6Sstephan       break;
363ad5125a6Sstephan     }
364ad5125a6Sstephan     case 3:
365ad5125a6Sstephan       kvstorageDelete(zClass, zKey);
366ad5125a6Sstephan       break;
367ad5125a6Sstephan     case 4:
368ad5125a6Sstephan       kvstorageMakeKeyOnJSStack(0,0);
369ad5125a6Sstephan       break;
370ad5125a6Sstephan     default: break;
371ad5125a6Sstephan   }
372ad5125a6Sstephan   return rc;
373ad5125a6Sstephan }
374ad5125a6Sstephan 
375ad5125a6Sstephan #undef WASM_KEEP
376ad5125a6Sstephan #else /* end ifdef __EMSCRIPTEN__ */
377ad5125a6Sstephan /* Forward declarations for the low-level storage engine
378ad5125a6Sstephan */
379ad5125a6Sstephan static int kvstorageWrite(const char*, const char *zKey, const char *zData);
380ad5125a6Sstephan static int kvstorageDelete(const char*, const char *zKey);
381ad5125a6Sstephan static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf);
382ad5125a6Sstephan 
383c6c9c6aaSdrh /* Write content into a key.  zClass is the particular namespace of the
384c6c9c6aaSdrh ** underlying key/value store to use - either "local" or "session".
385c6c9c6aaSdrh **
386c6c9c6aaSdrh ** Both zKey and zData are zero-terminated pure text strings.
3877585f49aSdrh **
3887585f49aSdrh ** Return the number of errors.
3897585f49aSdrh */
3907585f49aSdrh static int kvstorageWrite(
3917585f49aSdrh   const char *zClass,
3927585f49aSdrh   const char *zKey,
3937585f49aSdrh   const char *zData
3947585f49aSdrh ){
3957585f49aSdrh   FILE *fd;
3967585f49aSdrh   char zXKey[KVSTORAGE_KEY_SZ];
3977585f49aSdrh   kvstorageMakeKey(zClass, zKey, zXKey);
3987585f49aSdrh   fd = fopen(zXKey, "wb");
3997585f49aSdrh   if( fd ){
4007585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-WRITE  %-15s (%d) %.50s%s\n", zXKey,
4017585f49aSdrh                  (int)strlen(zData), zData,
4027585f49aSdrh                  strlen(zData)>50 ? "..." : ""));
4037585f49aSdrh     fputs(zData, fd);
4047585f49aSdrh     fclose(fd);
4057585f49aSdrh     return 0;
4067585f49aSdrh   }else{
4077585f49aSdrh     return 1;
4087585f49aSdrh   }
4097585f49aSdrh }
4107585f49aSdrh 
411c6c9c6aaSdrh /* Delete a key (with its corresponding data) from the key/value
412c6c9c6aaSdrh ** namespace given by zClass.  If the key does not previously exist,
413c6c9c6aaSdrh ** this routine is a no-op.
4147585f49aSdrh */
4157585f49aSdrh static int kvstorageDelete(const char *zClass, const char *zKey){
4167585f49aSdrh   char zXKey[KVSTORAGE_KEY_SZ];
4177585f49aSdrh   kvstorageMakeKey(zClass, zKey, zXKey);
4187585f49aSdrh   unlink(zXKey);
4197585f49aSdrh   SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey));
4207585f49aSdrh   return 0;
4217585f49aSdrh }
4227585f49aSdrh 
423c6c9c6aaSdrh /* Read the value associated with a zKey from the key/value namespace given
424c6c9c6aaSdrh ** by zClass and put the text data associated with that key in the first
4257585f49aSdrh ** nBuf bytes of zBuf[].  The value might be truncated if zBuf is not large
426c6c9c6aaSdrh ** enough to hold it all.  The value put into zBuf must always be zero
427c6c9c6aaSdrh ** terminated, even if it gets truncated because nBuf is not large enough.
4287585f49aSdrh **
4297585f49aSdrh ** Return the total number of bytes in the data, without truncation, and
4307585f49aSdrh ** not counting the final zero terminator.   Return -1 if the key does
4317585f49aSdrh ** not exist.
4327585f49aSdrh **
433c6c9c6aaSdrh ** If nBuf<=0 then this routine simply returns the size of the data without
4347585f49aSdrh ** actually reading it.
4357585f49aSdrh */
4367585f49aSdrh static int kvstorageRead(
4377585f49aSdrh   const char *zClass,
4387585f49aSdrh   const char *zKey,
4397585f49aSdrh   char *zBuf,
4407585f49aSdrh   int nBuf
4417585f49aSdrh ){
4427585f49aSdrh   FILE *fd;
4437585f49aSdrh   struct stat buf;
4447585f49aSdrh   char zXKey[KVSTORAGE_KEY_SZ];
4457585f49aSdrh   kvstorageMakeKey(zClass, zKey, zXKey);
4467585f49aSdrh   if( access(zXKey, R_OK)!=0
4477585f49aSdrh    || stat(zXKey, &buf)!=0
4487585f49aSdrh    || !S_ISREG(buf.st_mode)
4497585f49aSdrh   ){
4507585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (-1)\n", zXKey));
4517585f49aSdrh     return -1;
4527585f49aSdrh   }
453c6c9c6aaSdrh   if( nBuf<=0 ){
4547585f49aSdrh     return (int)buf.st_size;
4557585f49aSdrh   }else if( nBuf==1 ){
4567585f49aSdrh     zBuf[0] = 0;
4577585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (%d)\n", zXKey,
4587585f49aSdrh                  (int)buf.st_size));
4597585f49aSdrh     return (int)buf.st_size;
4607585f49aSdrh   }
4617585f49aSdrh   if( nBuf > buf.st_size + 1 ){
4627585f49aSdrh     nBuf = buf.st_size + 1;
4637585f49aSdrh   }
4647585f49aSdrh   fd = fopen(zXKey, "rb");
4657585f49aSdrh   if( fd==0 ){
4667585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (-1)\n", zXKey));
4677585f49aSdrh     return -1;
4687585f49aSdrh   }else{
4697585f49aSdrh     sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd);
4707585f49aSdrh     fclose(fd);
4717585f49aSdrh     zBuf[n] = 0;
4727585f49aSdrh     SQLITE_KV_TRACE(("KVVFS-READ   %-15s (%lld) %.50s%s\n", zXKey,
4737585f49aSdrh                  n, zBuf, n>50 ? "..." : ""));
4747585f49aSdrh     return (int)n;
4757585f49aSdrh   }
4767585f49aSdrh }
477ad5125a6Sstephan #endif /* ifdef __EMSCRIPTEN__ */
4787585f49aSdrh 
4797585f49aSdrh /****** Utility subroutines ************************************************/
4807585f49aSdrh 
4817585f49aSdrh /*
4827585f49aSdrh ** Encode binary into the text encoded used to persist on disk.
4837585f49aSdrh ** The output text is stored in aOut[], which must be at least
4847585f49aSdrh ** nData+1 bytes in length.
4857585f49aSdrh **
4867585f49aSdrh ** Return the actual length of the encoded text, not counting the
4877585f49aSdrh ** zero terminator at the end.
4887585f49aSdrh **
4897585f49aSdrh ** Encoding format
4907585f49aSdrh ** ---------------
4917585f49aSdrh **
4927585f49aSdrh **   *  Non-zero bytes are encoded as upper-case hexadecimal
4937585f49aSdrh **
4947585f49aSdrh **   *  A sequence of one or more zero-bytes that are not at the
4957585f49aSdrh **      beginning of the buffer are encoded as a little-endian
4967585f49aSdrh **      base-26 number using a..z.  "a" means 0.  "b" means 1,
4977585f49aSdrh **      "z" means 25.  "ab" means 26.  "ac" means 52.  And so forth.
4987585f49aSdrh **
4997585f49aSdrh **   *  Because there is no overlap between the encoding characters
5007585f49aSdrh **      of hexadecimal and base-26 numbers, it is always clear where
5017585f49aSdrh **      one stops and the next begins.
5027585f49aSdrh */
5037585f49aSdrh static int kvvfsEncode(const char *aData, int nData, char *aOut){
5047585f49aSdrh   int i, j;
5057585f49aSdrh   const unsigned char *a = (const unsigned char*)aData;
5067585f49aSdrh   for(i=j=0; i<nData; i++){
5077585f49aSdrh     unsigned char c = a[i];
5087585f49aSdrh     if( c!=0 ){
5097585f49aSdrh       aOut[j++] = "0123456789ABCDEF"[c>>4];
5107585f49aSdrh       aOut[j++] = "0123456789ABCDEF"[c&0xf];
5117585f49aSdrh     }else{
5127585f49aSdrh       /* A sequence of 1 or more zeros is stored as a little-endian
5137585f49aSdrh       ** base-26 number using a..z as the digits. So one zero is "b".
5147585f49aSdrh       ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb",
5157585f49aSdrh       ** and so forth.
5167585f49aSdrh       */
5177585f49aSdrh       int k;
5187585f49aSdrh       for(k=1; i+k<nData && a[i+k]==0; k++){}
5197585f49aSdrh       i += k-1;
5207585f49aSdrh       while( k>0 ){
5217585f49aSdrh         aOut[j++] = 'a'+(k%26);
5227585f49aSdrh         k /= 26;
5237585f49aSdrh       }
5247585f49aSdrh     }
5257585f49aSdrh   }
5267585f49aSdrh   aOut[j] = 0;
5277585f49aSdrh   return j;
5287585f49aSdrh }
5297585f49aSdrh 
5306acd7b0aSdrh static const signed char kvvfsHexValue[256] = {
5316acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5326acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5336acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5346acd7b0aSdrh    0,  1,  2,  3,  4,  5,  6,  7,    8,  9, -1, -1, -1, -1, -1, -1,
5356acd7b0aSdrh   -1, 10, 11, 12, 13, 14, 15, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5366acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5376acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5386acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5396acd7b0aSdrh 
5406acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5416acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5426acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5436acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5446acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5456acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5466acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1,
5476acd7b0aSdrh   -1, -1, -1, -1, -1, -1, -1, -1,   -1, -1, -1, -1, -1, -1, -1, -1
5486acd7b0aSdrh };
5497585f49aSdrh 
5507585f49aSdrh /*
5517585f49aSdrh ** Decode the text encoding back to binary.  The binary content is
5527585f49aSdrh ** written into pOut, which must be at least nOut bytes in length.
5537585f49aSdrh **
5547585f49aSdrh ** The return value is the number of bytes actually written into aOut[].
5557585f49aSdrh */
5566acd7b0aSdrh static int kvvfsDecode(const char *a, char *aOut, int nOut){
5577585f49aSdrh   int i, j;
5587585f49aSdrh   int c;
5596acd7b0aSdrh   const unsigned char *aIn = (const unsigned char*)a;
5607585f49aSdrh   i = 0;
5617585f49aSdrh   j = 0;
5626acd7b0aSdrh   while( 1 ){
5636acd7b0aSdrh     c = kvvfsHexValue[aIn[i]];
5646acd7b0aSdrh     if( c<0 ){
5657585f49aSdrh       int n = 0;
5667585f49aSdrh       int mult = 1;
5676acd7b0aSdrh       c = aIn[i];
5686acd7b0aSdrh       if( c==0 ) break;
5697585f49aSdrh       while( c>='a' && c<='z' ){
5707585f49aSdrh         n += (c - 'a')*mult;
5717585f49aSdrh         mult *= 26;
5727585f49aSdrh         c = aIn[++i];
5737585f49aSdrh       }
5747585f49aSdrh       if( j+n>nOut ) return -1;
5756acd7b0aSdrh       memset(&aOut[j], 0, n);
5766acd7b0aSdrh       j += n;
5776acd7b0aSdrh       c = aIn[i];
5786acd7b0aSdrh       if( c==0 ) break;
5797585f49aSdrh     }else{
5806acd7b0aSdrh       aOut[j] = c<<4;
5816acd7b0aSdrh       c = kvvfsHexValue[aIn[++i]];
5826acd7b0aSdrh       if( c<0 ) break;
5836acd7b0aSdrh       aOut[j++] += c;
5846acd7b0aSdrh       i++;
5857585f49aSdrh     }
5867585f49aSdrh   }
5877585f49aSdrh   return j;
5887585f49aSdrh }
5897585f49aSdrh 
5907585f49aSdrh /*
5917585f49aSdrh ** Decode a complete journal file.  Allocate space in pFile->aJrnl
5927585f49aSdrh ** and store the decoding there.  Or leave pFile->aJrnl set to NULL
5937585f49aSdrh ** if an error is encountered.
5947585f49aSdrh **
5957585f49aSdrh ** The first few characters of the text encoding will be a little-endian
5967585f49aSdrh ** base-26 number (digits a..z) that is the total number of bytes
5977585f49aSdrh ** in the decoded journal file image.  This base-26 number is followed
5987585f49aSdrh ** by a single space, then the encoding of the journal.  The space
5997585f49aSdrh ** separator is required to act as a terminator for the base-26 number.
6007585f49aSdrh */
6017585f49aSdrh static void kvvfsDecodeJournal(
6027585f49aSdrh   KVVfsFile *pFile,      /* Store decoding in pFile->aJrnl */
6037585f49aSdrh   const char *zTxt,      /* Text encoding.  Zero-terminated */
6047585f49aSdrh   int nTxt               /* Bytes in zTxt, excluding zero terminator */
6057585f49aSdrh ){
606*a50d3b7aSdrh   unsigned int n = 0;
6077585f49aSdrh   int c, i, mult;
6087585f49aSdrh   i = 0;
6097585f49aSdrh   mult = 1;
6107585f49aSdrh   while( (c = zTxt[i++])>='a' && c<='z' ){
6117585f49aSdrh     n += (zTxt[i] - 'a')*mult;
6127585f49aSdrh     mult *= 26;
6137585f49aSdrh   }
6147585f49aSdrh   sqlite3_free(pFile->aJrnl);
6157585f49aSdrh   pFile->aJrnl = sqlite3_malloc64( n );
6167585f49aSdrh   if( pFile->aJrnl==0 ){
6177585f49aSdrh     pFile->nJrnl = 0;
6187585f49aSdrh     return;
6197585f49aSdrh   }
6207585f49aSdrh   pFile->nJrnl = n;
6217585f49aSdrh   n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl);
6227585f49aSdrh   if( n<pFile->nJrnl ){
6237585f49aSdrh     sqlite3_free(pFile->aJrnl);
6247585f49aSdrh     pFile->aJrnl = 0;
6257585f49aSdrh     pFile->nJrnl = 0;
6267585f49aSdrh   }
6277585f49aSdrh }
6287585f49aSdrh 
6297585f49aSdrh /*
6307585f49aSdrh ** Read or write the "sz" element, containing the database file size.
6317585f49aSdrh */
6327585f49aSdrh static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){
6337585f49aSdrh   char zData[50];
6347585f49aSdrh   zData[0] = 0;
6357585f49aSdrh   kvstorageRead(pFile->zClass, "sz", zData, sizeof(zData)-1);
6367585f49aSdrh   return strtoll(zData, 0, 0);
6377585f49aSdrh }
638d22cfa8cSstephan static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){
6397585f49aSdrh   char zData[50];
6407585f49aSdrh   sqlite3_snprintf(sizeof(zData), zData, "%lld", sz);
641d22cfa8cSstephan   return kvstorageWrite(pFile->zClass, "sz", zData);
6427585f49aSdrh }
6437585f49aSdrh 
6447585f49aSdrh /****** sqlite3_io_methods methods ******************************************/
6457585f49aSdrh 
6467585f49aSdrh /*
6477585f49aSdrh ** Close an kvvfs-file.
6487585f49aSdrh */
6497585f49aSdrh static int kvvfsClose(sqlite3_file *pProtoFile){
6507585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
6517585f49aSdrh 
6527585f49aSdrh   SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass,
6537585f49aSdrh              pFile->isJournal ? "journal" : "db"));
6547585f49aSdrh   sqlite3_free(pFile->aJrnl);
6557585f49aSdrh   return SQLITE_OK;
6567585f49aSdrh }
6577585f49aSdrh 
6587585f49aSdrh /*
6597585f49aSdrh ** Read from the -journal file.
6607585f49aSdrh */
6617585f49aSdrh static int kvvfsReadJrnl(
6627585f49aSdrh   sqlite3_file *pProtoFile,
6637585f49aSdrh   void *zBuf,
6647585f49aSdrh   int iAmt,
6657585f49aSdrh   sqlite_int64 iOfst
6667585f49aSdrh ){
6677585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
6687585f49aSdrh   assert( pFile->isJournal );
6697585f49aSdrh   SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
6707585f49aSdrh   if( pFile->aJrnl==0 ){
671c6c9c6aaSdrh     int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0);
6727585f49aSdrh     char *aTxt;
6737585f49aSdrh     if( szTxt<=4 ){
6747585f49aSdrh       return SQLITE_IOERR;
6757585f49aSdrh     }
6767585f49aSdrh     aTxt = sqlite3_malloc64( szTxt+1 );
6777585f49aSdrh     if( aTxt==0 ) return SQLITE_NOMEM;
6787585f49aSdrh     kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1);
6797585f49aSdrh     kvvfsDecodeJournal(pFile, aTxt, szTxt);
6807585f49aSdrh     sqlite3_free(aTxt);
6817585f49aSdrh     if( pFile->aJrnl==0 ) return SQLITE_IOERR;
6827585f49aSdrh   }
6837585f49aSdrh   if( iOfst+iAmt>pFile->nJrnl ){
6847585f49aSdrh     return SQLITE_IOERR_SHORT_READ;
6857585f49aSdrh   }
6867585f49aSdrh   memcpy(zBuf, pFile->aJrnl+iOfst, iAmt);
6877585f49aSdrh   return SQLITE_OK;
6887585f49aSdrh }
6897585f49aSdrh 
6907585f49aSdrh /*
6917585f49aSdrh ** Read from the database file.
6927585f49aSdrh */
6937585f49aSdrh static int kvvfsReadDb(
6947585f49aSdrh   sqlite3_file *pProtoFile,
6957585f49aSdrh   void *zBuf,
6967585f49aSdrh   int iAmt,
6977585f49aSdrh   sqlite_int64 iOfst
6987585f49aSdrh ){
6997585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
7007585f49aSdrh   unsigned int pgno;
7017585f49aSdrh   int got, n;
7027585f49aSdrh   char zKey[30];
703756440feSdrh   char aData[133073];
7047585f49aSdrh   assert( iOfst>=0 );
7057585f49aSdrh   assert( iAmt>=0 );
7067585f49aSdrh   SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
7077585f49aSdrh   if( iOfst+iAmt>=512 ){
7087585f49aSdrh     if( (iOfst % iAmt)!=0 ){
7097585f49aSdrh       return SQLITE_IOERR_READ;
7107585f49aSdrh     }
7117585f49aSdrh     if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){
7127585f49aSdrh       return SQLITE_IOERR_READ;
7137585f49aSdrh     }
7147585f49aSdrh     pFile->szPage = iAmt;
7157585f49aSdrh     pgno = 1 + iOfst/iAmt;
7167585f49aSdrh   }else{
7177585f49aSdrh     pgno = 1;
7187585f49aSdrh   }
7197585f49aSdrh   sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
7207585f49aSdrh   got = kvstorageRead(pFile->zClass, zKey, aData, sizeof(aData)-1);
7217585f49aSdrh   if( got<0 ){
7227585f49aSdrh     n = 0;
7237585f49aSdrh   }else{
7247585f49aSdrh     aData[got] = 0;
7257585f49aSdrh     if( iOfst+iAmt<512 ){
726756440feSdrh       int k = iOfst+iAmt;
727756440feSdrh       aData[k*2] = 0;
728756440feSdrh       n = kvvfsDecode(aData, &aData[2000], sizeof(aData)-2000);
7297585f49aSdrh       if( n>=iOfst+iAmt ){
730756440feSdrh         memcpy(zBuf, &aData[2000+iOfst], iAmt);
7317585f49aSdrh         n = iAmt;
7327585f49aSdrh       }else{
7337585f49aSdrh         n = 0;
7347585f49aSdrh       }
7357585f49aSdrh     }else{
7367585f49aSdrh       n = kvvfsDecode(aData, zBuf, iAmt);
7377585f49aSdrh     }
7387585f49aSdrh   }
7397585f49aSdrh   if( n<iAmt ){
7407585f49aSdrh     memset(zBuf+n, 0, iAmt-n);
7417585f49aSdrh     return SQLITE_IOERR_SHORT_READ;
7427585f49aSdrh   }
7437585f49aSdrh   return SQLITE_OK;
7447585f49aSdrh }
7457585f49aSdrh 
7467585f49aSdrh 
7477585f49aSdrh /*
7487585f49aSdrh ** Write into the -journal file.
7497585f49aSdrh */
7507585f49aSdrh static int kvvfsWriteJrnl(
7517585f49aSdrh   sqlite3_file *pProtoFile,
7527585f49aSdrh   const void *zBuf,
7537585f49aSdrh   int iAmt,
7547585f49aSdrh   sqlite_int64 iOfst
7557585f49aSdrh ){
7567585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
7577585f49aSdrh   sqlite3_int64 iEnd = iOfst+iAmt;
7587585f49aSdrh   SQLITE_KV_LOG(("xWrite('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
7597585f49aSdrh   if( iEnd>=0x10000000 ) return SQLITE_FULL;
7607585f49aSdrh   if( pFile->aJrnl==0 || pFile->nJrnl<iEnd ){
7617585f49aSdrh     char *aNew = sqlite3_realloc(pFile->aJrnl, iEnd);
7627585f49aSdrh     if( aNew==0 ){
7637585f49aSdrh       return SQLITE_IOERR_NOMEM;
7647585f49aSdrh     }
7657585f49aSdrh     pFile->aJrnl = aNew;
7667585f49aSdrh     if( pFile->nJrnl<iOfst ){
7677585f49aSdrh       memset(pFile->aJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl);
7687585f49aSdrh     }
7697585f49aSdrh     pFile->nJrnl = iEnd;
7707585f49aSdrh   }
7717585f49aSdrh   memcpy(pFile->aJrnl+iOfst, zBuf, iAmt);
7727585f49aSdrh   return SQLITE_OK;
7737585f49aSdrh }
7747585f49aSdrh 
7757585f49aSdrh /*
7767585f49aSdrh ** Write into the database file.
7777585f49aSdrh */
7787585f49aSdrh static int kvvfsWriteDb(
7797585f49aSdrh   sqlite3_file *pProtoFile,
7807585f49aSdrh   const void *zBuf,
7817585f49aSdrh   int iAmt,
7827585f49aSdrh   sqlite_int64 iOfst
7837585f49aSdrh ){
7847585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
7857585f49aSdrh   unsigned int pgno;
7867585f49aSdrh   char zKey[30];
7877585f49aSdrh   char aData[131073];
7887585f49aSdrh   SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst));
7897585f49aSdrh   assert( iAmt>=512 && iAmt<=65536 );
7907585f49aSdrh   assert( (iAmt & (iAmt-1))==0 );
7917585f49aSdrh   pgno = 1 + iOfst/iAmt;
7927585f49aSdrh   sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
7937585f49aSdrh   kvvfsEncode(zBuf, iAmt, aData);
794d22cfa8cSstephan   if( kvstorageWrite(pFile->zClass, zKey, aData) ){
795d22cfa8cSstephan     return SQLITE_IOERR;
796d22cfa8cSstephan   }
7977585f49aSdrh   if( iOfst+iAmt > pFile->szDb ){
7987585f49aSdrh     pFile->szDb = iOfst + iAmt;
7997585f49aSdrh   }
8007585f49aSdrh   return SQLITE_OK;
8017585f49aSdrh }
8027585f49aSdrh 
8037585f49aSdrh /*
8047585f49aSdrh ** Truncate an kvvfs-file.
8057585f49aSdrh */
8067585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){
8077585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
8087585f49aSdrh   SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size));
8097585f49aSdrh   assert( size==0 );
8107585f49aSdrh   kvstorageDelete(pFile->zClass, "jrnl");
8117585f49aSdrh   sqlite3_free(pFile->aJrnl);
8127585f49aSdrh   pFile->aJrnl = 0;
8137585f49aSdrh   pFile->nJrnl = 0;
8147585f49aSdrh   return SQLITE_OK;
8157585f49aSdrh }
8167585f49aSdrh static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){
8177585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
8187585f49aSdrh   if( pFile->szDb>size
8197585f49aSdrh    && pFile->szPage>0
8207585f49aSdrh    && (size % pFile->szPage)==0
8217585f49aSdrh   ){
8227585f49aSdrh     char zKey[50];
8237585f49aSdrh     unsigned int pgno, pgnoMax;
8247585f49aSdrh     SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size));
8257585f49aSdrh     pgno = 1 + size/pFile->szPage;
8267585f49aSdrh     pgnoMax = 2 + pFile->szDb/pFile->szPage;
8277585f49aSdrh     while( pgno<=pgnoMax ){
8287585f49aSdrh       sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno);
8297585f49aSdrh       kvstorageDelete(pFile->zClass, zKey);
8307585f49aSdrh       pgno++;
8317585f49aSdrh     }
8327585f49aSdrh     pFile->szDb = size;
833d22cfa8cSstephan     return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK;
8347585f49aSdrh   }
8357585f49aSdrh   return SQLITE_IOERR;
8367585f49aSdrh }
8377585f49aSdrh 
8387585f49aSdrh /*
8397585f49aSdrh ** Sync an kvvfs-file.
8407585f49aSdrh */
8417585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){
8427585f49aSdrh   int i, n;
8437585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
8447585f49aSdrh   char *zOut;
8457585f49aSdrh   SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass));
8467585f49aSdrh   if( pFile->nJrnl<=0 ){
8477585f49aSdrh     return kvvfsTruncateJrnl(pProtoFile, 0);
8487585f49aSdrh   }
8497585f49aSdrh   zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 );
8507585f49aSdrh   if( zOut==0 ){
8517585f49aSdrh     return SQLITE_IOERR_NOMEM;
8527585f49aSdrh   }
8537585f49aSdrh   n = pFile->nJrnl;
8547585f49aSdrh   i = 0;
8557585f49aSdrh   do{
8567585f49aSdrh     zOut[i++] = 'a' + (n%26);
8577585f49aSdrh     n /= 26;
8587585f49aSdrh   }while( n>0 );
8597585f49aSdrh   zOut[i++] = ' ';
8607585f49aSdrh   kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]);
861d22cfa8cSstephan   i = kvstorageWrite(pFile->zClass, "jrnl", zOut);
8627585f49aSdrh   sqlite3_free(zOut);
863d22cfa8cSstephan   return i ? SQLITE_IOERR : SQLITE_OK;
8647585f49aSdrh }
8657585f49aSdrh static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){
86684e50767Sdrh   return SQLITE_OK;
8677585f49aSdrh }
8687585f49aSdrh 
8697585f49aSdrh /*
8707585f49aSdrh ** Return the current file-size of an kvvfs-file.
8717585f49aSdrh */
8727585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){
8737585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
8747585f49aSdrh   SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass));
8757585f49aSdrh   *pSize = pFile->nJrnl;
8767585f49aSdrh   return SQLITE_OK;
8777585f49aSdrh }
8787585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){
8797585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
8807585f49aSdrh   SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass));
8817585f49aSdrh   if( pFile->szDb>=0 ){
8827585f49aSdrh     *pSize = pFile->szDb;
8837585f49aSdrh   }else{
8847585f49aSdrh     *pSize = kvvfsReadFileSize(pFile);
8857585f49aSdrh   }
8867585f49aSdrh   return SQLITE_OK;
8877585f49aSdrh }
8887585f49aSdrh 
8897585f49aSdrh /*
8907585f49aSdrh ** Lock an kvvfs-file.
8917585f49aSdrh */
8927585f49aSdrh static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){
8937585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
8947585f49aSdrh   assert( !pFile->isJournal );
8957585f49aSdrh   SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock));
8967585f49aSdrh 
8977585f49aSdrh   if( eLock!=SQLITE_LOCK_NONE ){
8987585f49aSdrh     pFile->szDb = kvvfsReadFileSize(pFile);
8997585f49aSdrh   }
9007585f49aSdrh   return SQLITE_OK;
9017585f49aSdrh }
9027585f49aSdrh 
9037585f49aSdrh /*
9047585f49aSdrh ** Unlock an kvvfs-file.
9057585f49aSdrh */
9067585f49aSdrh static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){
9077585f49aSdrh   KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
9087585f49aSdrh   assert( !pFile->isJournal );
9097585f49aSdrh   SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock));
9107585f49aSdrh   if( eLock==SQLITE_LOCK_NONE ){
9117585f49aSdrh     pFile->szDb = -1;
9127585f49aSdrh   }
9137585f49aSdrh   return SQLITE_OK;
9147585f49aSdrh }
9157585f49aSdrh 
9167585f49aSdrh /*
9177585f49aSdrh ** Check if another file-handle holds a RESERVED lock on an kvvfs-file.
9187585f49aSdrh */
9197585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){
9207585f49aSdrh   SQLITE_KV_LOG(("xCheckReservedLock\n"));
9217585f49aSdrh   *pResOut = 0;
9227585f49aSdrh   return SQLITE_OK;
9237585f49aSdrh }
9247585f49aSdrh 
9257585f49aSdrh /*
9267585f49aSdrh ** File control method. For custom operations on an kvvfs-file.
9277585f49aSdrh */
92884e50767Sdrh static int kvvfsFileControlJrnl(sqlite3_file *pProtoFile, int op, void *pArg){
92984e50767Sdrh   SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op));
93084e50767Sdrh   return SQLITE_NOTFOUND;
93184e50767Sdrh }
93284e50767Sdrh static int kvvfsFileControlDb(sqlite3_file *pProtoFile, int op, void *pArg){
93384e50767Sdrh   SQLITE_KV_LOG(("xFileControl(%d) on database\n", op));
93484e50767Sdrh   if( op==SQLITE_FCNTL_SYNC ){
93584e50767Sdrh     KVVfsFile *pFile = (KVVfsFile *)pProtoFile;
93684e50767Sdrh     int rc = SQLITE_OK;
93784e50767Sdrh     SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass));
93884e50767Sdrh     if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){
93984e50767Sdrh       rc = SQLITE_IOERR;
94084e50767Sdrh     }
94184e50767Sdrh     return rc;
94284e50767Sdrh   }
9437585f49aSdrh   return SQLITE_NOTFOUND;
9447585f49aSdrh }
9457585f49aSdrh 
9467585f49aSdrh /*
9477585f49aSdrh ** Return the sector-size in bytes for an kvvfs-file.
9487585f49aSdrh */
9497585f49aSdrh static int kvvfsSectorSize(sqlite3_file *pFile){
9507585f49aSdrh   return 512;
9517585f49aSdrh }
9527585f49aSdrh 
9537585f49aSdrh /*
9547585f49aSdrh ** Return the device characteristic flags supported by an kvvfs-file.
9557585f49aSdrh */
9567585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){
9577585f49aSdrh   return 0;
9587585f49aSdrh }
9597585f49aSdrh 
9607585f49aSdrh /****** sqlite3_vfs methods *************************************************/
9617585f49aSdrh 
9627585f49aSdrh /*
9637585f49aSdrh ** Open an kvvfs file handle.
9647585f49aSdrh */
9657585f49aSdrh static int kvvfsOpen(
9667585f49aSdrh   sqlite3_vfs *pProtoVfs,
9677585f49aSdrh   const char *zName,
9687585f49aSdrh   sqlite3_file *pProtoFile,
9697585f49aSdrh   int flags,
9707585f49aSdrh   int *pOutFlags
9717585f49aSdrh ){
9727585f49aSdrh   KVVfsFile *pFile = (KVVfsFile*)pProtoFile;
9737585f49aSdrh   SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName));
9747585f49aSdrh   if( strcmp(zName, "local")==0
9757585f49aSdrh    || strcmp(zName, "session")==0
9767585f49aSdrh   ){
9777585f49aSdrh     pFile->isJournal = 0;
9787585f49aSdrh     pFile->base.pMethods = &kvvfs_db_io_methods;
9797585f49aSdrh   }else
9807585f49aSdrh   if( strcmp(zName, "local-journal")==0
9817585f49aSdrh    || strcmp(zName, "session-journal")==0
9827585f49aSdrh   ){
9837585f49aSdrh     pFile->isJournal = 1;
9847585f49aSdrh     pFile->base.pMethods = &kvvfs_jrnl_io_methods;
9857585f49aSdrh   }else{
9867585f49aSdrh     return SQLITE_CANTOPEN;
9877585f49aSdrh   }
9887585f49aSdrh   if( zName[0]=='s' ){
989c6c9c6aaSdrh     pFile->zClass = "session";
9907585f49aSdrh   }else{
991c6c9c6aaSdrh     pFile->zClass = "local";
9927585f49aSdrh   }
9937585f49aSdrh   pFile->aJrnl = 0;
9947585f49aSdrh   pFile->nJrnl = 0;
9957585f49aSdrh   pFile->szPage = -1;
9967585f49aSdrh   pFile->szDb = -1;
9977585f49aSdrh   return SQLITE_OK;
9987585f49aSdrh }
9997585f49aSdrh 
10007585f49aSdrh /*
10017585f49aSdrh ** Delete the file located at zPath. If the dirSync argument is true,
10027585f49aSdrh ** ensure the file-system modifications are synced to disk before
10037585f49aSdrh ** returning.
10047585f49aSdrh */
10057585f49aSdrh static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
10067585f49aSdrh   if( strcmp(zPath, "local-journal")==0 ){
1007c6c9c6aaSdrh     kvstorageDelete("local", "jrnl");
10087585f49aSdrh   }else
10097585f49aSdrh   if( strcmp(zPath, "session-journal")==0 ){
1010c6c9c6aaSdrh     kvstorageDelete("session", "jrnl");
10117585f49aSdrh   }
10127585f49aSdrh   return SQLITE_OK;
10137585f49aSdrh }
10147585f49aSdrh 
10157585f49aSdrh /*
10167585f49aSdrh ** Test for access permissions. Return true if the requested permission
10177585f49aSdrh ** is available, or false otherwise.
10187585f49aSdrh */
10197585f49aSdrh static int kvvfsAccess(
10207585f49aSdrh   sqlite3_vfs *pProtoVfs,
10217585f49aSdrh   const char *zPath,
10227585f49aSdrh   int flags,
10237585f49aSdrh   int *pResOut
10247585f49aSdrh ){
10257585f49aSdrh   SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath));
10267585f49aSdrh   if( strcmp(zPath, "local-journal")==0 ){
1027c6c9c6aaSdrh     *pResOut = kvstorageRead("local", "jrnl", 0, 0)>0;
10287585f49aSdrh   }else
10297585f49aSdrh   if( strcmp(zPath, "session-journal")==0 ){
1030c6c9c6aaSdrh     *pResOut = kvstorageRead("session", "jrnl", 0, 0)>0;
10317585f49aSdrh   }else
10327585f49aSdrh   if( strcmp(zPath, "local")==0 ){
1033c6c9c6aaSdrh     *pResOut = kvstorageRead("local", "sz", 0, 0)>0;
10347585f49aSdrh   }else
10357585f49aSdrh   if( strcmp(zPath, "session")==0 ){
1036c6c9c6aaSdrh     *pResOut = kvstorageRead("session", "sz", 0, 0)>0;
10377585f49aSdrh   }else
10387585f49aSdrh   {
10397585f49aSdrh     *pResOut = 0;
10407585f49aSdrh   }
10417585f49aSdrh   SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut));
10427585f49aSdrh   return SQLITE_OK;
10437585f49aSdrh }
10447585f49aSdrh 
10457585f49aSdrh /*
10467585f49aSdrh ** Populate buffer zOut with the full canonical pathname corresponding
10477585f49aSdrh ** to the pathname in zPath. zOut is guaranteed to point to a buffer
10487585f49aSdrh ** of at least (INST_MAX_PATHNAME+1) bytes.
10497585f49aSdrh */
10507585f49aSdrh static int kvvfsFullPathname(
10517585f49aSdrh   sqlite3_vfs *pVfs,
10527585f49aSdrh   const char *zPath,
10537585f49aSdrh   int nOut,
10547585f49aSdrh   char *zOut
10557585f49aSdrh ){
10566acd7b0aSdrh   size_t nPath;
1057bb195124Sdrh #ifdef SQLITE_OS_KV_ALWAYS_LOCAL
10586acd7b0aSdrh   zPath = "local";
1059bb195124Sdrh #endif
10606acd7b0aSdrh   nPath = strlen(zPath);
10617585f49aSdrh   SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath));
10627585f49aSdrh   if( nOut<nPath+1 ) nPath = nOut - 1;
10637585f49aSdrh   memcpy(zOut, zPath, nPath);
10647585f49aSdrh   zOut[nPath] = 0;
10657585f49aSdrh   return SQLITE_OK;
10667585f49aSdrh }
10677585f49aSdrh 
10687585f49aSdrh /*
10697585f49aSdrh ** Open the dynamic library located at zPath and return a handle.
10707585f49aSdrh */
10717585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
10727585f49aSdrh   return 0;
10737585f49aSdrh }
10747585f49aSdrh 
10757585f49aSdrh /*
10767585f49aSdrh ** Populate the buffer pointed to by zBufOut with nByte bytes of
10777585f49aSdrh ** random data.
10787585f49aSdrh */
10797585f49aSdrh static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
10807585f49aSdrh   memset(zBufOut, 0, nByte);
10817585f49aSdrh   return nByte;
10827585f49aSdrh }
10837585f49aSdrh 
10847585f49aSdrh /*
10857585f49aSdrh ** Sleep for nMicro microseconds. Return the number of microseconds
10867585f49aSdrh ** actually slept.
10877585f49aSdrh */
10887585f49aSdrh static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){
10897585f49aSdrh   return SQLITE_OK;
10907585f49aSdrh }
10917585f49aSdrh 
10927585f49aSdrh /*
10937585f49aSdrh ** Return the current time as a Julian Day number in *pTimeOut.
10947585f49aSdrh */
10957585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
1096d8bb0b5aSdrh   sqlite3_int64 i = 0;
1097d8bb0b5aSdrh   int rc;
1098d8bb0b5aSdrh   rc = kvvfsCurrentTimeInt64(0, &i);
1099d8bb0b5aSdrh   *pTimeOut = i/86400000.0;
1100d8bb0b5aSdrh   return rc;
11017585f49aSdrh }
1102d8bb0b5aSdrh #include <sys/time.h>
11037585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
1104d8bb0b5aSdrh   static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
1105d8bb0b5aSdrh   struct timeval sNow;
1106d8bb0b5aSdrh   (void)gettimeofday(&sNow, 0);  /* Cannot fail given valid arguments */
1107d8bb0b5aSdrh   *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
11087585f49aSdrh   return SQLITE_OK;
11097585f49aSdrh }
111020a9ed1dSdrh #endif /* SQLITE_OS_KV || SQLITE_OS_UNIX */
11117585f49aSdrh 
111220a9ed1dSdrh #if SQLITE_OS_KV
11137585f49aSdrh /*
11147585f49aSdrh ** This routine is called initialize the KV-vfs as the default VFS.
11157585f49aSdrh */
11167585f49aSdrh int sqlite3_os_init(void){
111720a9ed1dSdrh   return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1);
11187585f49aSdrh }
11197585f49aSdrh int sqlite3_os_end(void){
11207585f49aSdrh   return SQLITE_OK;
11217585f49aSdrh }
11227585f49aSdrh #endif /* SQLITE_OS_KV */
112320a9ed1dSdrh 
112420a9ed1dSdrh #if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)
112520a9ed1dSdrh int sqlite3KvvfsInit(void){
112620a9ed1dSdrh   return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0);
112720a9ed1dSdrh }
112820a9ed1dSdrh #endif
1129