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> 187585f49aSdrh #if SQLITE_OS_KV 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); 747585f49aSdrh static int kvvfsFileControl(sqlite3_file*, int op, void *pArg); 757585f49aSdrh static int kvvfsSectorSize(sqlite3_file*); 767585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file*); 777585f49aSdrh 787585f49aSdrh /* 797585f49aSdrh ** Methods for sqlite3_vfs 807585f49aSdrh */ 817585f49aSdrh static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); 827585f49aSdrh static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir); 837585f49aSdrh static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *); 847585f49aSdrh static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); 857585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename); 867585f49aSdrh static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); 877585f49aSdrh static int kvvfsSleep(sqlite3_vfs*, int microseconds); 887585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs*, double*); 897585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); 907585f49aSdrh 917585f49aSdrh static sqlite3_vfs kvvfs_vfs = { 927585f49aSdrh 1, /* iVersion */ 937585f49aSdrh sizeof(KVVfsFile), /* szOsFile */ 947585f49aSdrh 1024, /* mxPathname */ 957585f49aSdrh 0, /* pNext */ 967585f49aSdrh "kvvfs", /* zName */ 977585f49aSdrh 0, /* pAppData */ 987585f49aSdrh kvvfsOpen, /* xOpen */ 997585f49aSdrh kvvfsDelete, /* xDelete */ 1007585f49aSdrh kvvfsAccess, /* xAccess */ 1017585f49aSdrh kvvfsFullPathname, /* xFullPathname */ 1027585f49aSdrh kvvfsDlOpen, /* xDlOpen */ 1037585f49aSdrh 0, /* xDlError */ 1047585f49aSdrh 0, /* xDlSym */ 1057585f49aSdrh 0, /* xDlClose */ 1067585f49aSdrh kvvfsRandomness, /* xRandomness */ 1077585f49aSdrh kvvfsSleep, /* xSleep */ 1087585f49aSdrh kvvfsCurrentTime, /* xCurrentTime */ 1097585f49aSdrh 0, /* xGetLastError */ 1107585f49aSdrh kvvfsCurrentTimeInt64 /* xCurrentTimeInt64 */ 1117585f49aSdrh }; 1127585f49aSdrh 1137585f49aSdrh /* Methods for sqlite3_file objects referencing a database file 1147585f49aSdrh */ 1157585f49aSdrh static sqlite3_io_methods kvvfs_db_io_methods = { 1167585f49aSdrh 1, /* iVersion */ 1177585f49aSdrh kvvfsClose, /* xClose */ 1187585f49aSdrh kvvfsReadDb, /* xRead */ 1197585f49aSdrh kvvfsWriteDb, /* xWrite */ 1207585f49aSdrh kvvfsTruncateDb, /* xTruncate */ 1217585f49aSdrh kvvfsSyncDb, /* xSync */ 1227585f49aSdrh kvvfsFileSizeDb, /* xFileSize */ 1237585f49aSdrh kvvfsLock, /* xLock */ 1247585f49aSdrh kvvfsUnlock, /* xUnlock */ 1257585f49aSdrh kvvfsCheckReservedLock, /* xCheckReservedLock */ 1267585f49aSdrh kvvfsFileControl, /* xFileControl */ 1277585f49aSdrh kvvfsSectorSize, /* xSectorSize */ 1287585f49aSdrh kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ 1297585f49aSdrh 0, /* xShmMap */ 1307585f49aSdrh 0, /* xShmLock */ 1317585f49aSdrh 0, /* xShmBarrier */ 1327585f49aSdrh 0, /* xShmUnmap */ 1337585f49aSdrh 0, /* xFetch */ 1347585f49aSdrh 0 /* xUnfetch */ 1357585f49aSdrh }; 1367585f49aSdrh 1377585f49aSdrh /* Methods for sqlite3_file objects referencing a rollback journal 1387585f49aSdrh */ 1397585f49aSdrh static sqlite3_io_methods kvvfs_jrnl_io_methods = { 1407585f49aSdrh 1, /* iVersion */ 1417585f49aSdrh kvvfsClose, /* xClose */ 1427585f49aSdrh kvvfsReadJrnl, /* xRead */ 1437585f49aSdrh kvvfsWriteJrnl, /* xWrite */ 1447585f49aSdrh kvvfsTruncateJrnl, /* xTruncate */ 1457585f49aSdrh kvvfsSyncJrnl, /* xSync */ 1467585f49aSdrh kvvfsFileSizeJrnl, /* xFileSize */ 1477585f49aSdrh kvvfsLock, /* xLock */ 1487585f49aSdrh kvvfsUnlock, /* xUnlock */ 1497585f49aSdrh kvvfsCheckReservedLock, /* xCheckReservedLock */ 1507585f49aSdrh kvvfsFileControl, /* xFileControl */ 1517585f49aSdrh kvvfsSectorSize, /* xSectorSize */ 1527585f49aSdrh kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ 1537585f49aSdrh 0, /* xShmMap */ 1547585f49aSdrh 0, /* xShmLock */ 1557585f49aSdrh 0, /* xShmBarrier */ 1567585f49aSdrh 0, /* xShmUnmap */ 1577585f49aSdrh 0, /* xFetch */ 1587585f49aSdrh 0 /* xUnfetch */ 1597585f49aSdrh }; 1607585f49aSdrh 1617585f49aSdrh /****** Storage subsystem **************************************************/ 1627585f49aSdrh #include <sys/types.h> 1637585f49aSdrh #include <sys/stat.h> 1647585f49aSdrh #include <unistd.h> 1657585f49aSdrh 1667585f49aSdrh /* Forward declarations for the low-level storage engine 1677585f49aSdrh */ 168c6c9c6aaSdrh #define KVSTORAGE_KEY_SZ 32 1697585f49aSdrh 1707585f49aSdrh /* Expand the key name with an appropriate prefix and put the result 171c6c9c6aaSdrh ** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least 172c6c9c6aaSdrh ** KVSTORAGE_KEY_SZ bytes. 1737585f49aSdrh */ 1747585f49aSdrh static void kvstorageMakeKey( 1757585f49aSdrh const char *zClass, 1767585f49aSdrh const char *zKeyIn, 1777585f49aSdrh char *zKeyOut 1787585f49aSdrh ){ 179c6c9c6aaSdrh sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn); 1807585f49aSdrh } 1817585f49aSdrh 182ad5125a6Sstephan #ifdef __EMSCRIPTEN__ 183ad5125a6Sstephan /* Provide Emscripten-based impls of kvstorageWrite/Read/Delete()... */ 184ad5125a6Sstephan #include <emscripten.h> 185ad5125a6Sstephan #include <emscripten/console.h> 186ad5125a6Sstephan 187ad5125a6Sstephan /* 188ad5125a6Sstephan ** WASM_KEEP is identical to EMSCRIPTEN_KEEPALIVE but is not 189ad5125a6Sstephan ** Emscripten-specific. It explicitly includes marked functions for 190ad5125a6Sstephan ** export into the target wasm file without requiring explicit listing 191ad5125a6Sstephan ** of those functions in Emscripten's -sEXPORTED_FUNCTIONS=... list 192ad5125a6Sstephan ** (or equivalent in other build platforms). Any function with neither 193ad5125a6Sstephan ** this attribute nor which is listed as an explicit export will not 194ad5125a6Sstephan ** be exported from the wasm file (but may still be used internally 195ad5125a6Sstephan ** within the wasm file). 196ad5125a6Sstephan ** 197ad5125a6Sstephan ** The functions in this file (sqlite3-wasm.c) which require exporting 198ad5125a6Sstephan ** are marked with this flag. They may also be added to any explicit 199ad5125a6Sstephan ** build-time export list but need not be. All of these APIs are 200ad5125a6Sstephan ** intended for use only within the project's own JS/WASM code, and 201ad5125a6Sstephan ** not by client code, so an argument can be made for reducing their 202ad5125a6Sstephan ** visibility by not including them in any build-time export lists. 203ad5125a6Sstephan ** 204ad5125a6Sstephan ** 2022-09-11: it's not yet _proven_ that this approach works in 205ad5125a6Sstephan ** non-Emscripten builds. If not, such builds will need to export 206ad5125a6Sstephan ** those using the --export=... wasm-ld flag (or equivalent). As of 207ad5125a6Sstephan ** this writing we are tied to Emscripten for various reasons 208ad5125a6Sstephan ** and cannot test the library with other build environments. 209ad5125a6Sstephan */ 210ad5125a6Sstephan #define WASM_KEEP __attribute__((used,visibility("default"))) 211ad5125a6Sstephan /* 212ad5125a6Sstephan ** An internal level of indirection for accessing the static 213ad5125a6Sstephan ** kvstorageMakeKey() from EM_JS()-generated functions. This must be 214ad5125a6Sstephan ** made available for export via Emscripten but is not intended to be 215ad5125a6Sstephan ** used from client code. If called with a NULL zKeyOut it is a no-op. 216ad5125a6Sstephan ** It returns KVSTORAGE_KEY_SZ, so JS code (which cannot see that 217ad5125a6Sstephan ** constant) may call it with NULL arguments to get the size of the 218ad5125a6Sstephan ** allocation they'll need for a kvvfs key. 219ad5125a6Sstephan ** 220ad5125a6Sstephan ** Maintenance reminder: Emscripten will install this in the Module 221ad5125a6Sstephan ** init scope and will prefix its name with "_". 222ad5125a6Sstephan */ 223ad5125a6Sstephan WASM_KEEP 224ad5125a6Sstephan int sqlite3_wasm__kvvfsMakeKey(const char *zClass, const char *zKeyIn, 225ad5125a6Sstephan char *zKeyOut){ 226ad5125a6Sstephan if( 0!=zKeyOut ) kvstorageMakeKey(zClass, zKeyIn, zKeyOut); 227ad5125a6Sstephan return KVSTORAGE_KEY_SZ; 228ad5125a6Sstephan } 229ad5125a6Sstephan /* 230ad5125a6Sstephan ** Internal helper for kvstorageWrite/Read/Delete() which creates a 231ad5125a6Sstephan ** storage key for the given zClass/zKeyIn combination. Returns a 232ad5125a6Sstephan ** pointer to the key: a C string allocated on the WASM stack, or 0 if 233ad5125a6Sstephan ** allocation fails. It is up to the caller to save/restore the stack 234ad5125a6Sstephan ** before/after this operation. 235ad5125a6Sstephan */ 236ad5125a6Sstephan EM_JS(const char *, kvstorageMakeKeyOnJSStack, 237ad5125a6Sstephan (const char *zClass, const char *zKeyIn),{ 238ad5125a6Sstephan if( 0==zClass || 0==zKeyIn) return 0; 239ad5125a6Sstephan const zXKey = stackAlloc(_sqlite3_wasm__kvvfsMakeKey(0,0,0)); 240ad5125a6Sstephan if(zXKey) _sqlite3_wasm__kvvfsMakeKey(zClass, zKeyIn, zXKey); 241ad5125a6Sstephan return zXKey; 242ad5125a6Sstephan }); 243ad5125a6Sstephan 244ad5125a6Sstephan /* 245ad5125a6Sstephan ** JS impl of kvstorageWrite(). Main docs are in the C impl. This impl 246ad5125a6Sstephan ** writes zData to the global sessionStorage (if zClass starts with 247ad5125a6Sstephan ** 's') or localStorage, using a storage key derived from zClass and 248ad5125a6Sstephan ** zKey. 249ad5125a6Sstephan */ 250ad5125a6Sstephan EM_JS(int, kvstorageWrite, 251ad5125a6Sstephan (const char *zClass, const char *zKey, const char *zData),{ 252ad5125a6Sstephan const stack = stackSave(); 253ad5125a6Sstephan try { 254ad5125a6Sstephan const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey); 255ad5125a6Sstephan if(!zXKey) return 1/*OOM*/; 256ad5125a6Sstephan const jKey = UTF8ToString(zXKey); 257ad5125a6Sstephan /** 258ad5125a6Sstephan We could simplify this function and eliminate the 259ad5125a6Sstephan kvstorageMakeKey() symbol acrobatics if we'd simply hard-code 260ad5125a6Sstephan the key algo into the 3 functions which need it: 261ad5125a6Sstephan 262ad5125a6Sstephan const jKey = "kvvfs-"+UTF8ToString(zClass)+"-"+UTF8ToString(zKey); 263ad5125a6Sstephan */ 264ad5125a6Sstephan ((115/*=='s'*/===getValue(zClass)) 265ad5125a6Sstephan ? sessionStorage : localStorage).setItem(jKey, UTF8ToString(zData)); 266ad5125a6Sstephan }catch(e){ 267ad5125a6Sstephan console.error("kvstorageWrite()",e); 268ad5125a6Sstephan return 1; // Can't access SQLITE_xxx from here 269ad5125a6Sstephan }finally{ 270ad5125a6Sstephan stackRestore(stack); 271ad5125a6Sstephan } 272ad5125a6Sstephan return 0; 273ad5125a6Sstephan }); 274ad5125a6Sstephan 275ad5125a6Sstephan /* 276ad5125a6Sstephan ** JS impl of kvstorageDelete(). Main docs are in the C impl. This 277ad5125a6Sstephan ** impl generates a key derived from zClass and zKey, and removes the 278ad5125a6Sstephan ** matching entry (if any) from global sessionStorage (if zClass 279ad5125a6Sstephan ** starts with 's') or localStorage. 280ad5125a6Sstephan */ 281ad5125a6Sstephan EM_JS(int, kvstorageDelete, 282ad5125a6Sstephan (const char *zClass, const char *zKey),{ 283ad5125a6Sstephan const stack = stackSave(); 284ad5125a6Sstephan try { 285ad5125a6Sstephan const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey); 286ad5125a6Sstephan if(!zXKey) return 1/*OOM*/; 287ad5125a6Sstephan const jKey = UTF8ToString(zXKey); 288ad5125a6Sstephan ((115/*=='s'*/===getValue(zClass)) 289ad5125a6Sstephan ? sessionStorage : localStorage).removeItem(jKey); 290ad5125a6Sstephan }catch(e){ 291ad5125a6Sstephan console.error("kvstorageDelete()",e); 292ad5125a6Sstephan return 1; 293ad5125a6Sstephan }finally{ 294ad5125a6Sstephan stackRestore(stack); 295ad5125a6Sstephan } 296ad5125a6Sstephan return 0; 297ad5125a6Sstephan }); 298ad5125a6Sstephan 299ad5125a6Sstephan /* 300ad5125a6Sstephan ** JS impl of kvstorageRead(). Main docs are in the C impl. This impl 301ad5125a6Sstephan ** reads its data from the global sessionStorage (if zClass starts 302ad5125a6Sstephan ** with 's') or localStorage, using a storage key derived from zClass 303ad5125a6Sstephan ** and zKey. 304ad5125a6Sstephan */ 305ad5125a6Sstephan EM_JS(int, kvstorageRead, 306ad5125a6Sstephan (const char *zClass, const char *zKey, char *zBuf, int nBuf),{ 307ad5125a6Sstephan const stack = stackSave(); 308ad5125a6Sstephan try { 309ad5125a6Sstephan const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey); 310ad5125a6Sstephan if(!zXKey) return -3/*OOM*/; 311ad5125a6Sstephan const jKey = UTF8ToString(zXKey); 312ad5125a6Sstephan const jV = ((115/*=='s'*/===getValue(zClass)) 313ad5125a6Sstephan ? sessionStorage : localStorage).getItem(jKey); 314ad5125a6Sstephan if(!jV) return -1; 315ad5125a6Sstephan const nV = jV.length /* Note that we are relying 100% on v being 316ad5125a6Sstephan ASCII so that jV.length is equal to the 317ad5125a6Sstephan C-string's byte length. */; 318ad5125a6Sstephan if(nBuf<=0) return nV; 319ad5125a6Sstephan else if(1===nBuf){ 320ad5125a6Sstephan setValue(zBuf, 0); 321ad5125a6Sstephan return nV; 322ad5125a6Sstephan } 323ad5125a6Sstephan const zV = allocateUTF8OnStack(jV); 324ad5125a6Sstephan if(nBuf > nV + 1) nBuf = nV + 1; 325ad5125a6Sstephan HEAPU8.copyWithin(zBuf, zV, zV + nBuf - 1); 326ad5125a6Sstephan setValue( zBuf + nBuf - 1, 0 ); 327ad5125a6Sstephan return nBuf - 1; 328ad5125a6Sstephan }catch(e){ 329ad5125a6Sstephan console.error("kvstorageRead()",e); 330ad5125a6Sstephan return -2; 331ad5125a6Sstephan }finally{ 332ad5125a6Sstephan stackRestore(stack); 333ad5125a6Sstephan } 334ad5125a6Sstephan }); 335ad5125a6Sstephan 336ad5125a6Sstephan /* 337ad5125a6Sstephan ** This function exists for (1) WASM testing purposes and (2) as a 338ad5125a6Sstephan ** hook to get Emscripten to export several EM_JS()-generated 339ad5125a6Sstephan ** functions (if we don't reference them from exported C functions 340ad5125a6Sstephan ** then they get stripped away at build time). It is not part of the 341ad5125a6Sstephan ** public API and its signature and semantics may change at any time. 342ad5125a6Sstephan ** It's not even part of the private API, for that matter - it's part 343ad5125a6Sstephan ** of the Emscripten C/JS/WASM glue. 344ad5125a6Sstephan */ 345ad5125a6Sstephan WASM_KEEP 346ad5125a6Sstephan int sqlite3__wasm_emjs_kvvfs(int whichOp){ 347ad5125a6Sstephan int rc = 0; 348ad5125a6Sstephan const char * zClass = 349ad5125a6Sstephan "sezzion" /*don't collide with "session" records!*/; 350ad5125a6Sstephan const char * zKey = "hello"; 351ad5125a6Sstephan switch( whichOp ){ 352ad5125a6Sstephan case 0: break; 353ad5125a6Sstephan case 1: 354*d22cfa8cSstephan rc = kvstorageWrite(zClass, zKey, "world"); 355ad5125a6Sstephan break; 356ad5125a6Sstephan case 2: { 357ad5125a6Sstephan char buffer[128] = {0}; 358ad5125a6Sstephan char * zBuf = &buffer[0]; 359ad5125a6Sstephan rc = kvstorageRead(zClass, zKey, zBuf, (int)sizeof(buffer)); 360ad5125a6Sstephan emscripten_console_logf("kvstorageRead()=%d %s\n", rc, zBuf); 361ad5125a6Sstephan break; 362ad5125a6Sstephan } 363ad5125a6Sstephan case 3: 364ad5125a6Sstephan kvstorageDelete(zClass, zKey); 365ad5125a6Sstephan break; 366ad5125a6Sstephan case 4: 367ad5125a6Sstephan kvstorageMakeKeyOnJSStack(0,0); 368ad5125a6Sstephan break; 369ad5125a6Sstephan default: break; 370ad5125a6Sstephan } 371ad5125a6Sstephan return rc; 372ad5125a6Sstephan } 373ad5125a6Sstephan 374ad5125a6Sstephan #undef WASM_KEEP 375ad5125a6Sstephan #else /* end ifdef __EMSCRIPTEN__ */ 376ad5125a6Sstephan /* Forward declarations for the low-level storage engine 377ad5125a6Sstephan */ 378ad5125a6Sstephan static int kvstorageWrite(const char*, const char *zKey, const char *zData); 379ad5125a6Sstephan static int kvstorageDelete(const char*, const char *zKey); 380ad5125a6Sstephan static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf); 381ad5125a6Sstephan 382c6c9c6aaSdrh /* Write content into a key. zClass is the particular namespace of the 383c6c9c6aaSdrh ** underlying key/value store to use - either "local" or "session". 384c6c9c6aaSdrh ** 385c6c9c6aaSdrh ** Both zKey and zData are zero-terminated pure text strings. 3867585f49aSdrh ** 3877585f49aSdrh ** Return the number of errors. 3887585f49aSdrh */ 3897585f49aSdrh static int kvstorageWrite( 3907585f49aSdrh const char *zClass, 3917585f49aSdrh const char *zKey, 3927585f49aSdrh const char *zData 3937585f49aSdrh ){ 3947585f49aSdrh FILE *fd; 3957585f49aSdrh char zXKey[KVSTORAGE_KEY_SZ]; 3967585f49aSdrh kvstorageMakeKey(zClass, zKey, zXKey); 3977585f49aSdrh fd = fopen(zXKey, "wb"); 3987585f49aSdrh if( fd ){ 3997585f49aSdrh SQLITE_KV_TRACE(("KVVFS-WRITE %-15s (%d) %.50s%s\n", zXKey, 4007585f49aSdrh (int)strlen(zData), zData, 4017585f49aSdrh strlen(zData)>50 ? "..." : "")); 4027585f49aSdrh fputs(zData, fd); 4037585f49aSdrh fclose(fd); 4047585f49aSdrh return 0; 4057585f49aSdrh }else{ 4067585f49aSdrh return 1; 4077585f49aSdrh } 4087585f49aSdrh } 4097585f49aSdrh 410c6c9c6aaSdrh /* Delete a key (with its corresponding data) from the key/value 411c6c9c6aaSdrh ** namespace given by zClass. If the key does not previously exist, 412c6c9c6aaSdrh ** this routine is a no-op. 4137585f49aSdrh */ 4147585f49aSdrh static int kvstorageDelete(const char *zClass, const char *zKey){ 4157585f49aSdrh char zXKey[KVSTORAGE_KEY_SZ]; 4167585f49aSdrh kvstorageMakeKey(zClass, zKey, zXKey); 4177585f49aSdrh unlink(zXKey); 4187585f49aSdrh SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey)); 4197585f49aSdrh return 0; 4207585f49aSdrh } 4217585f49aSdrh 422c6c9c6aaSdrh /* Read the value associated with a zKey from the key/value namespace given 423c6c9c6aaSdrh ** by zClass and put the text data associated with that key in the first 4247585f49aSdrh ** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large 425c6c9c6aaSdrh ** enough to hold it all. The value put into zBuf must always be zero 426c6c9c6aaSdrh ** terminated, even if it gets truncated because nBuf is not large enough. 4277585f49aSdrh ** 4287585f49aSdrh ** Return the total number of bytes in the data, without truncation, and 4297585f49aSdrh ** not counting the final zero terminator. Return -1 if the key does 4307585f49aSdrh ** not exist. 4317585f49aSdrh ** 432c6c9c6aaSdrh ** If nBuf<=0 then this routine simply returns the size of the data without 4337585f49aSdrh ** actually reading it. 4347585f49aSdrh */ 4357585f49aSdrh static int kvstorageRead( 4367585f49aSdrh const char *zClass, 4377585f49aSdrh const char *zKey, 4387585f49aSdrh char *zBuf, 4397585f49aSdrh int nBuf 4407585f49aSdrh ){ 4417585f49aSdrh FILE *fd; 4427585f49aSdrh struct stat buf; 4437585f49aSdrh char zXKey[KVSTORAGE_KEY_SZ]; 4447585f49aSdrh kvstorageMakeKey(zClass, zKey, zXKey); 4457585f49aSdrh if( access(zXKey, R_OK)!=0 4467585f49aSdrh || stat(zXKey, &buf)!=0 4477585f49aSdrh || !S_ISREG(buf.st_mode) 4487585f49aSdrh ){ 4497585f49aSdrh SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); 4507585f49aSdrh return -1; 4517585f49aSdrh } 452c6c9c6aaSdrh if( nBuf<=0 ){ 4537585f49aSdrh return (int)buf.st_size; 4547585f49aSdrh }else if( nBuf==1 ){ 4557585f49aSdrh zBuf[0] = 0; 4567585f49aSdrh SQLITE_KV_TRACE(("KVVFS-READ %-15s (%d)\n", zXKey, 4577585f49aSdrh (int)buf.st_size)); 4587585f49aSdrh return (int)buf.st_size; 4597585f49aSdrh } 4607585f49aSdrh if( nBuf > buf.st_size + 1 ){ 4617585f49aSdrh nBuf = buf.st_size + 1; 4627585f49aSdrh } 4637585f49aSdrh fd = fopen(zXKey, "rb"); 4647585f49aSdrh if( fd==0 ){ 4657585f49aSdrh SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); 4667585f49aSdrh return -1; 4677585f49aSdrh }else{ 4687585f49aSdrh sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd); 4697585f49aSdrh fclose(fd); 4707585f49aSdrh zBuf[n] = 0; 4717585f49aSdrh SQLITE_KV_TRACE(("KVVFS-READ %-15s (%lld) %.50s%s\n", zXKey, 4727585f49aSdrh n, zBuf, n>50 ? "..." : "")); 4737585f49aSdrh return (int)n; 4747585f49aSdrh } 4757585f49aSdrh } 476ad5125a6Sstephan #endif /* ifdef __EMSCRIPTEN__ */ 4777585f49aSdrh 4787585f49aSdrh /****** Utility subroutines ************************************************/ 4797585f49aSdrh 4807585f49aSdrh /* 4817585f49aSdrh ** Encode binary into the text encoded used to persist on disk. 4827585f49aSdrh ** The output text is stored in aOut[], which must be at least 4837585f49aSdrh ** nData+1 bytes in length. 4847585f49aSdrh ** 4857585f49aSdrh ** Return the actual length of the encoded text, not counting the 4867585f49aSdrh ** zero terminator at the end. 4877585f49aSdrh ** 4887585f49aSdrh ** Encoding format 4897585f49aSdrh ** --------------- 4907585f49aSdrh ** 4917585f49aSdrh ** * Non-zero bytes are encoded as upper-case hexadecimal 4927585f49aSdrh ** 4937585f49aSdrh ** * A sequence of one or more zero-bytes that are not at the 4947585f49aSdrh ** beginning of the buffer are encoded as a little-endian 4957585f49aSdrh ** base-26 number using a..z. "a" means 0. "b" means 1, 4967585f49aSdrh ** "z" means 25. "ab" means 26. "ac" means 52. And so forth. 4977585f49aSdrh ** 4987585f49aSdrh ** * Because there is no overlap between the encoding characters 4997585f49aSdrh ** of hexadecimal and base-26 numbers, it is always clear where 5007585f49aSdrh ** one stops and the next begins. 5017585f49aSdrh */ 5027585f49aSdrh static int kvvfsEncode(const char *aData, int nData, char *aOut){ 5037585f49aSdrh int i, j; 5047585f49aSdrh const unsigned char *a = (const unsigned char*)aData; 5057585f49aSdrh for(i=j=0; i<nData; i++){ 5067585f49aSdrh unsigned char c = a[i]; 5077585f49aSdrh if( c!=0 ){ 5087585f49aSdrh aOut[j++] = "0123456789ABCDEF"[c>>4]; 5097585f49aSdrh aOut[j++] = "0123456789ABCDEF"[c&0xf]; 5107585f49aSdrh }else{ 5117585f49aSdrh /* A sequence of 1 or more zeros is stored as a little-endian 5127585f49aSdrh ** base-26 number using a..z as the digits. So one zero is "b". 5137585f49aSdrh ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb", 5147585f49aSdrh ** and so forth. 5157585f49aSdrh */ 5167585f49aSdrh int k; 5177585f49aSdrh for(k=1; i+k<nData && a[i+k]==0; k++){} 5187585f49aSdrh i += k-1; 5197585f49aSdrh while( k>0 ){ 5207585f49aSdrh aOut[j++] = 'a'+(k%26); 5217585f49aSdrh k /= 26; 5227585f49aSdrh } 5237585f49aSdrh } 5247585f49aSdrh } 5257585f49aSdrh aOut[j] = 0; 5267585f49aSdrh return j; 5277585f49aSdrh } 5287585f49aSdrh 5296acd7b0aSdrh static const signed char kvvfsHexValue[256] = { 5306acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 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 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 5346acd7b0aSdrh -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 5356acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -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 5396acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 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 }; 5487585f49aSdrh 5497585f49aSdrh /* 5507585f49aSdrh ** Decode the text encoding back to binary. The binary content is 5517585f49aSdrh ** written into pOut, which must be at least nOut bytes in length. 5527585f49aSdrh ** 5537585f49aSdrh ** The return value is the number of bytes actually written into aOut[]. 5547585f49aSdrh */ 5556acd7b0aSdrh static int kvvfsDecode(const char *a, char *aOut, int nOut){ 5567585f49aSdrh int i, j; 5577585f49aSdrh int c; 5586acd7b0aSdrh const unsigned char *aIn = (const unsigned char*)a; 5597585f49aSdrh i = 0; 5607585f49aSdrh j = 0; 5616acd7b0aSdrh while( 1 ){ 5626acd7b0aSdrh c = kvvfsHexValue[aIn[i]]; 5636acd7b0aSdrh if( c<0 ){ 5647585f49aSdrh int n = 0; 5657585f49aSdrh int mult = 1; 5666acd7b0aSdrh c = aIn[i]; 5676acd7b0aSdrh if( c==0 ) break; 5687585f49aSdrh while( c>='a' && c<='z' ){ 5697585f49aSdrh n += (c - 'a')*mult; 5707585f49aSdrh mult *= 26; 5717585f49aSdrh c = aIn[++i]; 5727585f49aSdrh } 5737585f49aSdrh if( j+n>nOut ) return -1; 5746acd7b0aSdrh memset(&aOut[j], 0, n); 5756acd7b0aSdrh j += n; 5766acd7b0aSdrh c = aIn[i]; 5776acd7b0aSdrh if( c==0 ) break; 5787585f49aSdrh }else{ 5796acd7b0aSdrh aOut[j] = c<<4; 5806acd7b0aSdrh c = kvvfsHexValue[aIn[++i]]; 5816acd7b0aSdrh if( c<0 ) break; 5826acd7b0aSdrh aOut[j++] += c; 5836acd7b0aSdrh i++; 5847585f49aSdrh } 5857585f49aSdrh } 5867585f49aSdrh return j; 5877585f49aSdrh } 5887585f49aSdrh 5897585f49aSdrh /* 5907585f49aSdrh ** Decode a complete journal file. Allocate space in pFile->aJrnl 5917585f49aSdrh ** and store the decoding there. Or leave pFile->aJrnl set to NULL 5927585f49aSdrh ** if an error is encountered. 5937585f49aSdrh ** 5947585f49aSdrh ** The first few characters of the text encoding will be a little-endian 5957585f49aSdrh ** base-26 number (digits a..z) that is the total number of bytes 5967585f49aSdrh ** in the decoded journal file image. This base-26 number is followed 5977585f49aSdrh ** by a single space, then the encoding of the journal. The space 5987585f49aSdrh ** separator is required to act as a terminator for the base-26 number. 5997585f49aSdrh */ 6007585f49aSdrh static void kvvfsDecodeJournal( 6017585f49aSdrh KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */ 6027585f49aSdrh const char *zTxt, /* Text encoding. Zero-terminated */ 6037585f49aSdrh int nTxt /* Bytes in zTxt, excluding zero terminator */ 6047585f49aSdrh ){ 605*d22cfa8cSstephan unsigned int n; 6067585f49aSdrh int c, i, mult; 6077585f49aSdrh i = 0; 6087585f49aSdrh mult = 1; 6097585f49aSdrh while( (c = zTxt[i++])>='a' && c<='z' ){ 6107585f49aSdrh n += (zTxt[i] - 'a')*mult; 6117585f49aSdrh mult *= 26; 6127585f49aSdrh } 6137585f49aSdrh sqlite3_free(pFile->aJrnl); 6147585f49aSdrh pFile->aJrnl = sqlite3_malloc64( n ); 6157585f49aSdrh if( pFile->aJrnl==0 ){ 6167585f49aSdrh pFile->nJrnl = 0; 6177585f49aSdrh return; 6187585f49aSdrh } 6197585f49aSdrh pFile->nJrnl = n; 6207585f49aSdrh n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl); 6217585f49aSdrh if( n<pFile->nJrnl ){ 6227585f49aSdrh sqlite3_free(pFile->aJrnl); 6237585f49aSdrh pFile->aJrnl = 0; 6247585f49aSdrh pFile->nJrnl = 0; 6257585f49aSdrh } 6267585f49aSdrh } 6277585f49aSdrh 6287585f49aSdrh /* 6297585f49aSdrh ** Read or write the "sz" element, containing the database file size. 6307585f49aSdrh */ 6317585f49aSdrh static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ 6327585f49aSdrh char zData[50]; 6337585f49aSdrh zData[0] = 0; 6347585f49aSdrh kvstorageRead(pFile->zClass, "sz", zData, sizeof(zData)-1); 6357585f49aSdrh return strtoll(zData, 0, 0); 6367585f49aSdrh } 637*d22cfa8cSstephan static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ 6387585f49aSdrh char zData[50]; 6397585f49aSdrh sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); 640*d22cfa8cSstephan return kvstorageWrite(pFile->zClass, "sz", zData); 6417585f49aSdrh } 6427585f49aSdrh 6437585f49aSdrh /****** sqlite3_io_methods methods ******************************************/ 6447585f49aSdrh 6457585f49aSdrh /* 6467585f49aSdrh ** Close an kvvfs-file. 6477585f49aSdrh */ 6487585f49aSdrh static int kvvfsClose(sqlite3_file *pProtoFile){ 6497585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6507585f49aSdrh 6517585f49aSdrh SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass, 6527585f49aSdrh pFile->isJournal ? "journal" : "db")); 6537585f49aSdrh sqlite3_free(pFile->aJrnl); 6547585f49aSdrh return SQLITE_OK; 6557585f49aSdrh } 6567585f49aSdrh 6577585f49aSdrh /* 6587585f49aSdrh ** Read from the -journal file. 6597585f49aSdrh */ 6607585f49aSdrh static int kvvfsReadJrnl( 6617585f49aSdrh sqlite3_file *pProtoFile, 6627585f49aSdrh void *zBuf, 6637585f49aSdrh int iAmt, 6647585f49aSdrh sqlite_int64 iOfst 6657585f49aSdrh ){ 6667585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 6677585f49aSdrh assert( pFile->isJournal ); 6687585f49aSdrh SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 6697585f49aSdrh if( pFile->aJrnl==0 ){ 670c6c9c6aaSdrh int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0); 6717585f49aSdrh char *aTxt; 6727585f49aSdrh if( szTxt<=4 ){ 6737585f49aSdrh return SQLITE_IOERR; 6747585f49aSdrh } 6757585f49aSdrh aTxt = sqlite3_malloc64( szTxt+1 ); 6767585f49aSdrh if( aTxt==0 ) return SQLITE_NOMEM; 6777585f49aSdrh kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1); 6787585f49aSdrh kvvfsDecodeJournal(pFile, aTxt, szTxt); 6797585f49aSdrh sqlite3_free(aTxt); 6807585f49aSdrh if( pFile->aJrnl==0 ) return SQLITE_IOERR; 6817585f49aSdrh } 6827585f49aSdrh if( iOfst+iAmt>pFile->nJrnl ){ 6837585f49aSdrh return SQLITE_IOERR_SHORT_READ; 6847585f49aSdrh } 6857585f49aSdrh memcpy(zBuf, pFile->aJrnl+iOfst, iAmt); 6867585f49aSdrh return SQLITE_OK; 6877585f49aSdrh } 6887585f49aSdrh 6897585f49aSdrh /* 6907585f49aSdrh ** Read from the database file. 6917585f49aSdrh */ 6927585f49aSdrh static int kvvfsReadDb( 6937585f49aSdrh sqlite3_file *pProtoFile, 6947585f49aSdrh void *zBuf, 6957585f49aSdrh int iAmt, 6967585f49aSdrh sqlite_int64 iOfst 6977585f49aSdrh ){ 6987585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 6997585f49aSdrh unsigned int pgno; 7007585f49aSdrh int got, n; 7017585f49aSdrh char zKey[30]; 7027585f49aSdrh char aData[131073]; 7037585f49aSdrh assert( iOfst>=0 ); 7047585f49aSdrh assert( iAmt>=0 ); 7057585f49aSdrh SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 7067585f49aSdrh if( iOfst+iAmt>=512 ){ 7077585f49aSdrh if( (iOfst % iAmt)!=0 ){ 7087585f49aSdrh return SQLITE_IOERR_READ; 7097585f49aSdrh } 7107585f49aSdrh if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ 7117585f49aSdrh return SQLITE_IOERR_READ; 7127585f49aSdrh } 7137585f49aSdrh pFile->szPage = iAmt; 7147585f49aSdrh pgno = 1 + iOfst/iAmt; 7157585f49aSdrh }else{ 7167585f49aSdrh pgno = 1; 7177585f49aSdrh } 7187585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 7197585f49aSdrh got = kvstorageRead(pFile->zClass, zKey, aData, sizeof(aData)-1); 7207585f49aSdrh if( got<0 ){ 7217585f49aSdrh n = 0; 7227585f49aSdrh }else{ 7237585f49aSdrh aData[got] = 0; 7247585f49aSdrh if( iOfst+iAmt<512 ){ 7257585f49aSdrh n = kvvfsDecode(aData, &aData[1000], 1000); 7267585f49aSdrh if( n>=iOfst+iAmt ){ 7277585f49aSdrh memcpy(zBuf, &aData[1000+iOfst], iAmt); 7287585f49aSdrh n = iAmt; 7297585f49aSdrh }else{ 7307585f49aSdrh n = 0; 7317585f49aSdrh } 7327585f49aSdrh }else{ 7337585f49aSdrh n = kvvfsDecode(aData, zBuf, iAmt); 7347585f49aSdrh } 7357585f49aSdrh } 7367585f49aSdrh if( n<iAmt ){ 7377585f49aSdrh memset(zBuf+n, 0, iAmt-n); 7387585f49aSdrh return SQLITE_IOERR_SHORT_READ; 7397585f49aSdrh } 7407585f49aSdrh return SQLITE_OK; 7417585f49aSdrh } 7427585f49aSdrh 7437585f49aSdrh 7447585f49aSdrh /* 7457585f49aSdrh ** Write into the -journal file. 7467585f49aSdrh */ 7477585f49aSdrh static int kvvfsWriteJrnl( 7487585f49aSdrh sqlite3_file *pProtoFile, 7497585f49aSdrh const void *zBuf, 7507585f49aSdrh int iAmt, 7517585f49aSdrh sqlite_int64 iOfst 7527585f49aSdrh ){ 7537585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 7547585f49aSdrh sqlite3_int64 iEnd = iOfst+iAmt; 7557585f49aSdrh SQLITE_KV_LOG(("xWrite('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 7567585f49aSdrh if( iEnd>=0x10000000 ) return SQLITE_FULL; 7577585f49aSdrh if( pFile->aJrnl==0 || pFile->nJrnl<iEnd ){ 7587585f49aSdrh char *aNew = sqlite3_realloc(pFile->aJrnl, iEnd); 7597585f49aSdrh if( aNew==0 ){ 7607585f49aSdrh return SQLITE_IOERR_NOMEM; 7617585f49aSdrh } 7627585f49aSdrh pFile->aJrnl = aNew; 7637585f49aSdrh if( pFile->nJrnl<iOfst ){ 7647585f49aSdrh memset(pFile->aJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl); 7657585f49aSdrh } 7667585f49aSdrh pFile->nJrnl = iEnd; 7677585f49aSdrh } 7687585f49aSdrh memcpy(pFile->aJrnl+iOfst, zBuf, iAmt); 7697585f49aSdrh return SQLITE_OK; 7707585f49aSdrh } 7717585f49aSdrh 7727585f49aSdrh /* 7737585f49aSdrh ** Write into the database file. 7747585f49aSdrh */ 7757585f49aSdrh static int kvvfsWriteDb( 7767585f49aSdrh sqlite3_file *pProtoFile, 7777585f49aSdrh const void *zBuf, 7787585f49aSdrh int iAmt, 7797585f49aSdrh sqlite_int64 iOfst 7807585f49aSdrh ){ 7817585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 7827585f49aSdrh unsigned int pgno; 7837585f49aSdrh char zKey[30]; 7847585f49aSdrh char aData[131073]; 7857585f49aSdrh SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 7867585f49aSdrh assert( iAmt>=512 && iAmt<=65536 ); 7877585f49aSdrh assert( (iAmt & (iAmt-1))==0 ); 7887585f49aSdrh pgno = 1 + iOfst/iAmt; 7897585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 7907585f49aSdrh kvvfsEncode(zBuf, iAmt, aData); 791*d22cfa8cSstephan if( kvstorageWrite(pFile->zClass, zKey, aData) ){ 792*d22cfa8cSstephan return SQLITE_IOERR; 793*d22cfa8cSstephan } 7947585f49aSdrh if( iOfst+iAmt > pFile->szDb ){ 7957585f49aSdrh pFile->szDb = iOfst + iAmt; 7967585f49aSdrh } 7977585f49aSdrh return SQLITE_OK; 7987585f49aSdrh } 7997585f49aSdrh 8007585f49aSdrh /* 8017585f49aSdrh ** Truncate an kvvfs-file. 8027585f49aSdrh */ 8037585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){ 8047585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8057585f49aSdrh SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size)); 8067585f49aSdrh assert( size==0 ); 8077585f49aSdrh kvstorageDelete(pFile->zClass, "jrnl"); 8087585f49aSdrh sqlite3_free(pFile->aJrnl); 8097585f49aSdrh pFile->aJrnl = 0; 8107585f49aSdrh pFile->nJrnl = 0; 8117585f49aSdrh return SQLITE_OK; 8127585f49aSdrh } 8137585f49aSdrh static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ 8147585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8157585f49aSdrh if( pFile->szDb>size 8167585f49aSdrh && pFile->szPage>0 8177585f49aSdrh && (size % pFile->szPage)==0 8187585f49aSdrh ){ 8197585f49aSdrh char zKey[50]; 8207585f49aSdrh unsigned int pgno, pgnoMax; 8217585f49aSdrh SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size)); 8227585f49aSdrh pgno = 1 + size/pFile->szPage; 8237585f49aSdrh pgnoMax = 2 + pFile->szDb/pFile->szPage; 8247585f49aSdrh while( pgno<=pgnoMax ){ 8257585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 8267585f49aSdrh kvstorageDelete(pFile->zClass, zKey); 8277585f49aSdrh pgno++; 8287585f49aSdrh } 8297585f49aSdrh pFile->szDb = size; 830*d22cfa8cSstephan return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK; 8317585f49aSdrh } 8327585f49aSdrh return SQLITE_IOERR; 8337585f49aSdrh } 8347585f49aSdrh 8357585f49aSdrh /* 8367585f49aSdrh ** Sync an kvvfs-file. 8377585f49aSdrh */ 8387585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ 8397585f49aSdrh int i, n; 8407585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8417585f49aSdrh char *zOut; 8427585f49aSdrh SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass)); 8437585f49aSdrh if( pFile->nJrnl<=0 ){ 8447585f49aSdrh return kvvfsTruncateJrnl(pProtoFile, 0); 8457585f49aSdrh } 8467585f49aSdrh zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 ); 8477585f49aSdrh if( zOut==0 ){ 8487585f49aSdrh return SQLITE_IOERR_NOMEM; 8497585f49aSdrh } 8507585f49aSdrh n = pFile->nJrnl; 8517585f49aSdrh i = 0; 8527585f49aSdrh do{ 8537585f49aSdrh zOut[i++] = 'a' + (n%26); 8547585f49aSdrh n /= 26; 8557585f49aSdrh }while( n>0 ); 8567585f49aSdrh zOut[i++] = ' '; 8577585f49aSdrh kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); 858*d22cfa8cSstephan i = kvstorageWrite(pFile->zClass, "jrnl", zOut); 8597585f49aSdrh sqlite3_free(zOut); 860*d22cfa8cSstephan return i ? SQLITE_IOERR : SQLITE_OK; 8617585f49aSdrh } 8627585f49aSdrh static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ 8637585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 864*d22cfa8cSstephan int rc = SQLITE_OK; 8657585f49aSdrh SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); 866*d22cfa8cSstephan if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){ 867*d22cfa8cSstephan rc = SQLITE_IOERR; 8687585f49aSdrh } 869*d22cfa8cSstephan return rc; 8707585f49aSdrh } 8717585f49aSdrh 8727585f49aSdrh /* 8737585f49aSdrh ** Return the current file-size of an kvvfs-file. 8747585f49aSdrh */ 8757585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ 8767585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8777585f49aSdrh SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass)); 8787585f49aSdrh *pSize = pFile->nJrnl; 8797585f49aSdrh return SQLITE_OK; 8807585f49aSdrh } 8817585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ 8827585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8837585f49aSdrh SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass)); 8847585f49aSdrh if( pFile->szDb>=0 ){ 8857585f49aSdrh *pSize = pFile->szDb; 8867585f49aSdrh }else{ 8877585f49aSdrh *pSize = kvvfsReadFileSize(pFile); 8887585f49aSdrh } 8897585f49aSdrh return SQLITE_OK; 8907585f49aSdrh } 8917585f49aSdrh 8927585f49aSdrh /* 8937585f49aSdrh ** Lock an kvvfs-file. 8947585f49aSdrh */ 8957585f49aSdrh static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){ 8967585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8977585f49aSdrh assert( !pFile->isJournal ); 8987585f49aSdrh SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock)); 8997585f49aSdrh 9007585f49aSdrh if( eLock!=SQLITE_LOCK_NONE ){ 9017585f49aSdrh pFile->szDb = kvvfsReadFileSize(pFile); 9027585f49aSdrh } 9037585f49aSdrh return SQLITE_OK; 9047585f49aSdrh } 9057585f49aSdrh 9067585f49aSdrh /* 9077585f49aSdrh ** Unlock an kvvfs-file. 9087585f49aSdrh */ 9097585f49aSdrh static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){ 9107585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 9117585f49aSdrh assert( !pFile->isJournal ); 9127585f49aSdrh SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock)); 9137585f49aSdrh if( eLock==SQLITE_LOCK_NONE ){ 9147585f49aSdrh pFile->szDb = -1; 9157585f49aSdrh } 9167585f49aSdrh return SQLITE_OK; 9177585f49aSdrh } 9187585f49aSdrh 9197585f49aSdrh /* 9207585f49aSdrh ** Check if another file-handle holds a RESERVED lock on an kvvfs-file. 9217585f49aSdrh */ 9227585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){ 9237585f49aSdrh SQLITE_KV_LOG(("xCheckReservedLock\n")); 9247585f49aSdrh *pResOut = 0; 9257585f49aSdrh return SQLITE_OK; 9267585f49aSdrh } 9277585f49aSdrh 9287585f49aSdrh /* 9297585f49aSdrh ** File control method. For custom operations on an kvvfs-file. 9307585f49aSdrh */ 9317585f49aSdrh static int kvvfsFileControl(sqlite3_file *pProtoFile, int op, void *pArg){ 9327585f49aSdrh return SQLITE_NOTFOUND; 9337585f49aSdrh } 9347585f49aSdrh 9357585f49aSdrh /* 9367585f49aSdrh ** Return the sector-size in bytes for an kvvfs-file. 9377585f49aSdrh */ 9387585f49aSdrh static int kvvfsSectorSize(sqlite3_file *pFile){ 9397585f49aSdrh return 512; 9407585f49aSdrh } 9417585f49aSdrh 9427585f49aSdrh /* 9437585f49aSdrh ** Return the device characteristic flags supported by an kvvfs-file. 9447585f49aSdrh */ 9457585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){ 9467585f49aSdrh return 0; 9477585f49aSdrh } 9487585f49aSdrh 9497585f49aSdrh /****** sqlite3_vfs methods *************************************************/ 9507585f49aSdrh 9517585f49aSdrh /* 9527585f49aSdrh ** Open an kvvfs file handle. 9537585f49aSdrh */ 9547585f49aSdrh static int kvvfsOpen( 9557585f49aSdrh sqlite3_vfs *pProtoVfs, 9567585f49aSdrh const char *zName, 9577585f49aSdrh sqlite3_file *pProtoFile, 9587585f49aSdrh int flags, 9597585f49aSdrh int *pOutFlags 9607585f49aSdrh ){ 9617585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 9627585f49aSdrh SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName)); 9637585f49aSdrh if( strcmp(zName, "local")==0 9647585f49aSdrh || strcmp(zName, "session")==0 9657585f49aSdrh ){ 9667585f49aSdrh pFile->isJournal = 0; 9677585f49aSdrh pFile->base.pMethods = &kvvfs_db_io_methods; 9687585f49aSdrh }else 9697585f49aSdrh if( strcmp(zName, "local-journal")==0 9707585f49aSdrh || strcmp(zName, "session-journal")==0 9717585f49aSdrh ){ 9727585f49aSdrh pFile->isJournal = 1; 9737585f49aSdrh pFile->base.pMethods = &kvvfs_jrnl_io_methods; 9747585f49aSdrh }else{ 9757585f49aSdrh return SQLITE_CANTOPEN; 9767585f49aSdrh } 9777585f49aSdrh if( zName[0]=='s' ){ 978c6c9c6aaSdrh pFile->zClass = "session"; 9797585f49aSdrh }else{ 980c6c9c6aaSdrh pFile->zClass = "local"; 9817585f49aSdrh } 9827585f49aSdrh pFile->aJrnl = 0; 9837585f49aSdrh pFile->nJrnl = 0; 9847585f49aSdrh pFile->szPage = -1; 9857585f49aSdrh pFile->szDb = -1; 9867585f49aSdrh return SQLITE_OK; 9877585f49aSdrh } 9887585f49aSdrh 9897585f49aSdrh /* 9907585f49aSdrh ** Delete the file located at zPath. If the dirSync argument is true, 9917585f49aSdrh ** ensure the file-system modifications are synced to disk before 9927585f49aSdrh ** returning. 9937585f49aSdrh */ 9947585f49aSdrh static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ 9957585f49aSdrh if( strcmp(zPath, "local-journal")==0 ){ 996c6c9c6aaSdrh kvstorageDelete("local", "jrnl"); 9977585f49aSdrh }else 9987585f49aSdrh if( strcmp(zPath, "session-journal")==0 ){ 999c6c9c6aaSdrh kvstorageDelete("session", "jrnl"); 10007585f49aSdrh } 10017585f49aSdrh return SQLITE_OK; 10027585f49aSdrh } 10037585f49aSdrh 10047585f49aSdrh /* 10057585f49aSdrh ** Test for access permissions. Return true if the requested permission 10067585f49aSdrh ** is available, or false otherwise. 10077585f49aSdrh */ 10087585f49aSdrh static int kvvfsAccess( 10097585f49aSdrh sqlite3_vfs *pProtoVfs, 10107585f49aSdrh const char *zPath, 10117585f49aSdrh int flags, 10127585f49aSdrh int *pResOut 10137585f49aSdrh ){ 10147585f49aSdrh SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath)); 10157585f49aSdrh if( strcmp(zPath, "local-journal")==0 ){ 1016c6c9c6aaSdrh *pResOut = kvstorageRead("local", "jrnl", 0, 0)>0; 10177585f49aSdrh }else 10187585f49aSdrh if( strcmp(zPath, "session-journal")==0 ){ 1019c6c9c6aaSdrh *pResOut = kvstorageRead("session", "jrnl", 0, 0)>0; 10207585f49aSdrh }else 10217585f49aSdrh if( strcmp(zPath, "local")==0 ){ 1022c6c9c6aaSdrh *pResOut = kvstorageRead("local", "sz", 0, 0)>0; 10237585f49aSdrh }else 10247585f49aSdrh if( strcmp(zPath, "session")==0 ){ 1025c6c9c6aaSdrh *pResOut = kvstorageRead("session", "sz", 0, 0)>0; 10267585f49aSdrh }else 10277585f49aSdrh { 10287585f49aSdrh *pResOut = 0; 10297585f49aSdrh } 10307585f49aSdrh SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut)); 10317585f49aSdrh return SQLITE_OK; 10327585f49aSdrh } 10337585f49aSdrh 10347585f49aSdrh /* 10357585f49aSdrh ** Populate buffer zOut with the full canonical pathname corresponding 10367585f49aSdrh ** to the pathname in zPath. zOut is guaranteed to point to a buffer 10377585f49aSdrh ** of at least (INST_MAX_PATHNAME+1) bytes. 10387585f49aSdrh */ 10397585f49aSdrh static int kvvfsFullPathname( 10407585f49aSdrh sqlite3_vfs *pVfs, 10417585f49aSdrh const char *zPath, 10427585f49aSdrh int nOut, 10437585f49aSdrh char *zOut 10447585f49aSdrh ){ 10456acd7b0aSdrh size_t nPath; 1046bb195124Sdrh #ifdef SQLITE_OS_KV_ALWAYS_LOCAL 10476acd7b0aSdrh zPath = "local"; 1048bb195124Sdrh #endif 10496acd7b0aSdrh nPath = strlen(zPath); 10507585f49aSdrh SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath)); 10517585f49aSdrh if( nOut<nPath+1 ) nPath = nOut - 1; 10527585f49aSdrh memcpy(zOut, zPath, nPath); 10537585f49aSdrh zOut[nPath] = 0; 10547585f49aSdrh return SQLITE_OK; 10557585f49aSdrh } 10567585f49aSdrh 10577585f49aSdrh /* 10587585f49aSdrh ** Open the dynamic library located at zPath and return a handle. 10597585f49aSdrh */ 10607585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ 10617585f49aSdrh return 0; 10627585f49aSdrh } 10637585f49aSdrh 10647585f49aSdrh /* 10657585f49aSdrh ** Populate the buffer pointed to by zBufOut with nByte bytes of 10667585f49aSdrh ** random data. 10677585f49aSdrh */ 10687585f49aSdrh static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ 10697585f49aSdrh memset(zBufOut, 0, nByte); 10707585f49aSdrh return nByte; 10717585f49aSdrh } 10727585f49aSdrh 10737585f49aSdrh /* 10747585f49aSdrh ** Sleep for nMicro microseconds. Return the number of microseconds 10757585f49aSdrh ** actually slept. 10767585f49aSdrh */ 10777585f49aSdrh static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){ 10787585f49aSdrh return SQLITE_OK; 10797585f49aSdrh } 10807585f49aSdrh 10817585f49aSdrh /* 10827585f49aSdrh ** Return the current time as a Julian Day number in *pTimeOut. 10837585f49aSdrh */ 10847585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ 1085d8bb0b5aSdrh sqlite3_int64 i = 0; 1086d8bb0b5aSdrh int rc; 1087d8bb0b5aSdrh rc = kvvfsCurrentTimeInt64(0, &i); 1088d8bb0b5aSdrh *pTimeOut = i/86400000.0; 1089d8bb0b5aSdrh return rc; 10907585f49aSdrh } 1091d8bb0b5aSdrh #include <sys/time.h> 10927585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ 1093d8bb0b5aSdrh static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; 1094d8bb0b5aSdrh struct timeval sNow; 1095d8bb0b5aSdrh (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */ 1096d8bb0b5aSdrh *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; 10977585f49aSdrh return SQLITE_OK; 10987585f49aSdrh } 10997585f49aSdrh 11007585f49aSdrh /* 11017585f49aSdrh ** This routine is called initialize the KV-vfs as the default VFS. 11027585f49aSdrh */ 11037585f49aSdrh int sqlite3_os_init(void){ 11047585f49aSdrh return sqlite3_vfs_register(&kvvfs_vfs, 1); 11057585f49aSdrh } 11067585f49aSdrh int sqlite3_os_end(void){ 11077585f49aSdrh return SQLITE_OK; 11087585f49aSdrh } 11097585f49aSdrh #endif /* SQLITE_OS_KV */ 1110