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 _sqlite3_wasm__kvvfsMakeKey(zClass, zKey, zXKey); 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: 355ad5125a6Sstephan 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 5307585f49aSdrh /* Convert hex to binary */ 5317585f49aSdrh static char kvvfsHexToBinary(char c){ 5327585f49aSdrh if( c>='0' && c<='9' ) return c - '0'; 5337585f49aSdrh if( c>='A' && c<='F' ) return c - 'A' + 10; 5347585f49aSdrh return 0; 5357585f49aSdrh } 5367585f49aSdrh 5377585f49aSdrh /* 5387585f49aSdrh ** Decode the text encoding back to binary. The binary content is 5397585f49aSdrh ** written into pOut, which must be at least nOut bytes in length. 5407585f49aSdrh ** 5417585f49aSdrh ** The return value is the number of bytes actually written into aOut[]. 5427585f49aSdrh */ 5437585f49aSdrh static int kvvfsDecode(const char *aIn, char *aOut, int nOut){ 5447585f49aSdrh int i, j; 5457585f49aSdrh int c; 5467585f49aSdrh i = 0; 5477585f49aSdrh j = 0; 5487585f49aSdrh while( (c = aIn[i])!=0 ){ 5497585f49aSdrh if( c>='a' ){ 5507585f49aSdrh int n = 0; 5517585f49aSdrh int mult = 1; 5527585f49aSdrh while( c>='a' && c<='z' ){ 5537585f49aSdrh n += (c - 'a')*mult; 5547585f49aSdrh mult *= 26; 5557585f49aSdrh c = aIn[++i]; 5567585f49aSdrh } 5577585f49aSdrh if( j+n>nOut ) return -1; 5587585f49aSdrh while( n-->0 ){ 5597585f49aSdrh aOut[j++] = 0; 5607585f49aSdrh } 5617585f49aSdrh }else{ 5627585f49aSdrh if( j>nOut ) return -1; 5637585f49aSdrh aOut[j] = kvvfsHexToBinary(aIn[i++])<<4; 5647585f49aSdrh aOut[j++] += kvvfsHexToBinary(aIn[i++]); 5657585f49aSdrh } 5667585f49aSdrh } 5677585f49aSdrh return j; 5687585f49aSdrh } 5697585f49aSdrh 5707585f49aSdrh /* 5717585f49aSdrh ** Decode a complete journal file. Allocate space in pFile->aJrnl 5727585f49aSdrh ** and store the decoding there. Or leave pFile->aJrnl set to NULL 5737585f49aSdrh ** if an error is encountered. 5747585f49aSdrh ** 5757585f49aSdrh ** The first few characters of the text encoding will be a little-endian 5767585f49aSdrh ** base-26 number (digits a..z) that is the total number of bytes 5777585f49aSdrh ** in the decoded journal file image. This base-26 number is followed 5787585f49aSdrh ** by a single space, then the encoding of the journal. The space 5797585f49aSdrh ** separator is required to act as a terminator for the base-26 number. 5807585f49aSdrh */ 5817585f49aSdrh static void kvvfsDecodeJournal( 5827585f49aSdrh KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */ 5837585f49aSdrh const char *zTxt, /* Text encoding. Zero-terminated */ 5847585f49aSdrh int nTxt /* Bytes in zTxt, excluding zero terminator */ 5857585f49aSdrh ){ 586*1ad51ffeSdrh unsigned int n = 0; 5877585f49aSdrh int c, i, mult; 5887585f49aSdrh i = 0; 5897585f49aSdrh mult = 1; 5907585f49aSdrh while( (c = zTxt[i++])>='a' && c<='z' ){ 5917585f49aSdrh n += (zTxt[i] - 'a')*mult; 5927585f49aSdrh mult *= 26; 5937585f49aSdrh } 5947585f49aSdrh sqlite3_free(pFile->aJrnl); 5957585f49aSdrh pFile->aJrnl = sqlite3_malloc64( n ); 5967585f49aSdrh if( pFile->aJrnl==0 ){ 5977585f49aSdrh pFile->nJrnl = 0; 5987585f49aSdrh return; 5997585f49aSdrh } 6007585f49aSdrh pFile->nJrnl = n; 6017585f49aSdrh n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl); 6027585f49aSdrh if( n<pFile->nJrnl ){ 6037585f49aSdrh sqlite3_free(pFile->aJrnl); 6047585f49aSdrh pFile->aJrnl = 0; 6057585f49aSdrh pFile->nJrnl = 0; 6067585f49aSdrh } 6077585f49aSdrh } 6087585f49aSdrh 6097585f49aSdrh /* 6107585f49aSdrh ** Read or write the "sz" element, containing the database file size. 6117585f49aSdrh */ 6127585f49aSdrh static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ 6137585f49aSdrh char zData[50]; 6147585f49aSdrh zData[0] = 0; 6157585f49aSdrh kvstorageRead(pFile->zClass, "sz", zData, sizeof(zData)-1); 6167585f49aSdrh return strtoll(zData, 0, 0); 6177585f49aSdrh } 6187585f49aSdrh static void kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ 6197585f49aSdrh char zData[50]; 6207585f49aSdrh sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); 6217585f49aSdrh kvstorageWrite(pFile->zClass, "sz", zData); 6227585f49aSdrh } 6237585f49aSdrh 6247585f49aSdrh /****** sqlite3_io_methods methods ******************************************/ 6257585f49aSdrh 6267585f49aSdrh /* 6277585f49aSdrh ** Close an kvvfs-file. 6287585f49aSdrh */ 6297585f49aSdrh static int kvvfsClose(sqlite3_file *pProtoFile){ 6307585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6317585f49aSdrh 6327585f49aSdrh SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass, 6337585f49aSdrh pFile->isJournal ? "journal" : "db")); 6347585f49aSdrh sqlite3_free(pFile->aJrnl); 6357585f49aSdrh return SQLITE_OK; 6367585f49aSdrh } 6377585f49aSdrh 6387585f49aSdrh /* 6397585f49aSdrh ** Read from the -journal file. 6407585f49aSdrh */ 6417585f49aSdrh static int kvvfsReadJrnl( 6427585f49aSdrh sqlite3_file *pProtoFile, 6437585f49aSdrh void *zBuf, 6447585f49aSdrh int iAmt, 6457585f49aSdrh sqlite_int64 iOfst 6467585f49aSdrh ){ 6477585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 6487585f49aSdrh assert( pFile->isJournal ); 6497585f49aSdrh SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 6507585f49aSdrh if( pFile->aJrnl==0 ){ 651c6c9c6aaSdrh int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0); 6527585f49aSdrh char *aTxt; 6537585f49aSdrh if( szTxt<=4 ){ 6547585f49aSdrh return SQLITE_IOERR; 6557585f49aSdrh } 6567585f49aSdrh aTxt = sqlite3_malloc64( szTxt+1 ); 6577585f49aSdrh if( aTxt==0 ) return SQLITE_NOMEM; 6587585f49aSdrh kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1); 6597585f49aSdrh kvvfsDecodeJournal(pFile, aTxt, szTxt); 6607585f49aSdrh sqlite3_free(aTxt); 6617585f49aSdrh if( pFile->aJrnl==0 ) return SQLITE_IOERR; 6627585f49aSdrh } 6637585f49aSdrh if( iOfst+iAmt>pFile->nJrnl ){ 6647585f49aSdrh return SQLITE_IOERR_SHORT_READ; 6657585f49aSdrh } 6667585f49aSdrh memcpy(zBuf, pFile->aJrnl+iOfst, iAmt); 6677585f49aSdrh return SQLITE_OK; 6687585f49aSdrh } 6697585f49aSdrh 6707585f49aSdrh /* 6717585f49aSdrh ** Read from the database file. 6727585f49aSdrh */ 6737585f49aSdrh static int kvvfsReadDb( 6747585f49aSdrh sqlite3_file *pProtoFile, 6757585f49aSdrh void *zBuf, 6767585f49aSdrh int iAmt, 6777585f49aSdrh sqlite_int64 iOfst 6787585f49aSdrh ){ 6797585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 6807585f49aSdrh unsigned int pgno; 6817585f49aSdrh int got, n; 6827585f49aSdrh char zKey[30]; 6837585f49aSdrh char aData[131073]; 6847585f49aSdrh assert( iOfst>=0 ); 6857585f49aSdrh assert( iAmt>=0 ); 6867585f49aSdrh SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 6877585f49aSdrh if( iOfst+iAmt>=512 ){ 6887585f49aSdrh if( (iOfst % iAmt)!=0 ){ 6897585f49aSdrh return SQLITE_IOERR_READ; 6907585f49aSdrh } 6917585f49aSdrh if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ 6927585f49aSdrh return SQLITE_IOERR_READ; 6937585f49aSdrh } 6947585f49aSdrh pFile->szPage = iAmt; 6957585f49aSdrh pgno = 1 + iOfst/iAmt; 6967585f49aSdrh }else{ 6977585f49aSdrh pgno = 1; 6987585f49aSdrh } 6997585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 7007585f49aSdrh got = kvstorageRead(pFile->zClass, zKey, aData, sizeof(aData)-1); 7017585f49aSdrh if( got<0 ){ 7027585f49aSdrh n = 0; 7037585f49aSdrh }else{ 7047585f49aSdrh aData[got] = 0; 7057585f49aSdrh if( iOfst+iAmt<512 ){ 7067585f49aSdrh n = kvvfsDecode(aData, &aData[1000], 1000); 7077585f49aSdrh if( n>=iOfst+iAmt ){ 7087585f49aSdrh memcpy(zBuf, &aData[1000+iOfst], iAmt); 7097585f49aSdrh n = iAmt; 7107585f49aSdrh }else{ 7117585f49aSdrh n = 0; 7127585f49aSdrh } 7137585f49aSdrh }else{ 7147585f49aSdrh n = kvvfsDecode(aData, zBuf, iAmt); 7157585f49aSdrh } 7167585f49aSdrh } 7177585f49aSdrh if( n<iAmt ){ 7187585f49aSdrh memset(zBuf+n, 0, iAmt-n); 7197585f49aSdrh return SQLITE_IOERR_SHORT_READ; 7207585f49aSdrh } 7217585f49aSdrh return SQLITE_OK; 7227585f49aSdrh } 7237585f49aSdrh 7247585f49aSdrh 7257585f49aSdrh /* 7267585f49aSdrh ** Write into the -journal file. 7277585f49aSdrh */ 7287585f49aSdrh static int kvvfsWriteJrnl( 7297585f49aSdrh sqlite3_file *pProtoFile, 7307585f49aSdrh const void *zBuf, 7317585f49aSdrh int iAmt, 7327585f49aSdrh sqlite_int64 iOfst 7337585f49aSdrh ){ 7347585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 7357585f49aSdrh sqlite3_int64 iEnd = iOfst+iAmt; 7367585f49aSdrh SQLITE_KV_LOG(("xWrite('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 7377585f49aSdrh if( iEnd>=0x10000000 ) return SQLITE_FULL; 7387585f49aSdrh if( pFile->aJrnl==0 || pFile->nJrnl<iEnd ){ 7397585f49aSdrh char *aNew = sqlite3_realloc(pFile->aJrnl, iEnd); 7407585f49aSdrh if( aNew==0 ){ 7417585f49aSdrh return SQLITE_IOERR_NOMEM; 7427585f49aSdrh } 7437585f49aSdrh pFile->aJrnl = aNew; 7447585f49aSdrh if( pFile->nJrnl<iOfst ){ 7457585f49aSdrh memset(pFile->aJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl); 7467585f49aSdrh } 7477585f49aSdrh pFile->nJrnl = iEnd; 7487585f49aSdrh } 7497585f49aSdrh memcpy(pFile->aJrnl+iOfst, zBuf, iAmt); 7507585f49aSdrh return SQLITE_OK; 7517585f49aSdrh } 7527585f49aSdrh 7537585f49aSdrh /* 7547585f49aSdrh ** Write into the database file. 7557585f49aSdrh */ 7567585f49aSdrh static int kvvfsWriteDb( 7577585f49aSdrh sqlite3_file *pProtoFile, 7587585f49aSdrh const void *zBuf, 7597585f49aSdrh int iAmt, 7607585f49aSdrh sqlite_int64 iOfst 7617585f49aSdrh ){ 7627585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 7637585f49aSdrh unsigned int pgno; 7647585f49aSdrh char zKey[30]; 7657585f49aSdrh char aData[131073]; 7667585f49aSdrh SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 7677585f49aSdrh assert( iAmt>=512 && iAmt<=65536 ); 7687585f49aSdrh assert( (iAmt & (iAmt-1))==0 ); 7697585f49aSdrh pgno = 1 + iOfst/iAmt; 7707585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 7717585f49aSdrh kvvfsEncode(zBuf, iAmt, aData); 7727585f49aSdrh kvstorageWrite(pFile->zClass, zKey, aData); 7737585f49aSdrh if( iOfst+iAmt > pFile->szDb ){ 7747585f49aSdrh pFile->szDb = iOfst + iAmt; 7757585f49aSdrh } 7767585f49aSdrh return SQLITE_OK; 7777585f49aSdrh } 7787585f49aSdrh 7797585f49aSdrh /* 7807585f49aSdrh ** Truncate an kvvfs-file. 7817585f49aSdrh */ 7827585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){ 7837585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 7847585f49aSdrh SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size)); 7857585f49aSdrh assert( size==0 ); 7867585f49aSdrh kvstorageDelete(pFile->zClass, "jrnl"); 7877585f49aSdrh sqlite3_free(pFile->aJrnl); 7887585f49aSdrh pFile->aJrnl = 0; 7897585f49aSdrh pFile->nJrnl = 0; 7907585f49aSdrh return SQLITE_OK; 7917585f49aSdrh } 7927585f49aSdrh static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ 7937585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 7947585f49aSdrh if( pFile->szDb>size 7957585f49aSdrh && pFile->szPage>0 7967585f49aSdrh && (size % pFile->szPage)==0 7977585f49aSdrh ){ 7987585f49aSdrh char zKey[50]; 7997585f49aSdrh unsigned int pgno, pgnoMax; 8007585f49aSdrh SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size)); 8017585f49aSdrh pgno = 1 + size/pFile->szPage; 8027585f49aSdrh pgnoMax = 2 + pFile->szDb/pFile->szPage; 8037585f49aSdrh while( pgno<=pgnoMax ){ 8047585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 8057585f49aSdrh kvstorageDelete(pFile->zClass, zKey); 8067585f49aSdrh pgno++; 8077585f49aSdrh } 8087585f49aSdrh pFile->szDb = size; 8097585f49aSdrh kvvfsWriteFileSize(pFile, size); 8107585f49aSdrh return SQLITE_OK; 8117585f49aSdrh } 8127585f49aSdrh return SQLITE_IOERR; 8137585f49aSdrh } 8147585f49aSdrh 8157585f49aSdrh /* 8167585f49aSdrh ** Sync an kvvfs-file. 8177585f49aSdrh */ 8187585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ 8197585f49aSdrh int i, n; 8207585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8217585f49aSdrh char *zOut; 8227585f49aSdrh SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass)); 8237585f49aSdrh if( pFile->nJrnl<=0 ){ 8247585f49aSdrh return kvvfsTruncateJrnl(pProtoFile, 0); 8257585f49aSdrh } 8267585f49aSdrh zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 ); 8277585f49aSdrh if( zOut==0 ){ 8287585f49aSdrh return SQLITE_IOERR_NOMEM; 8297585f49aSdrh } 8307585f49aSdrh n = pFile->nJrnl; 8317585f49aSdrh i = 0; 8327585f49aSdrh do{ 8337585f49aSdrh zOut[i++] = 'a' + (n%26); 8347585f49aSdrh n /= 26; 8357585f49aSdrh }while( n>0 ); 8367585f49aSdrh zOut[i++] = ' '; 8377585f49aSdrh kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); 8387585f49aSdrh kvstorageWrite(pFile->zClass, "jrnl", zOut); 8397585f49aSdrh sqlite3_free(zOut); 8407585f49aSdrh return SQLITE_OK; 8417585f49aSdrh } 8427585f49aSdrh static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ 8437585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8447585f49aSdrh SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); 8457585f49aSdrh if( pFile->szDb>0 ){ 8467585f49aSdrh kvvfsWriteFileSize(pFile, pFile->szDb); 8477585f49aSdrh } 8487585f49aSdrh return SQLITE_OK; 8497585f49aSdrh } 8507585f49aSdrh 8517585f49aSdrh /* 8527585f49aSdrh ** Return the current file-size of an kvvfs-file. 8537585f49aSdrh */ 8547585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ 8557585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8567585f49aSdrh SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass)); 8577585f49aSdrh *pSize = pFile->nJrnl; 8587585f49aSdrh return SQLITE_OK; 8597585f49aSdrh } 8607585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ 8617585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8627585f49aSdrh SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass)); 8637585f49aSdrh if( pFile->szDb>=0 ){ 8647585f49aSdrh *pSize = pFile->szDb; 8657585f49aSdrh }else{ 8667585f49aSdrh *pSize = kvvfsReadFileSize(pFile); 8677585f49aSdrh } 8687585f49aSdrh return SQLITE_OK; 8697585f49aSdrh } 8707585f49aSdrh 8717585f49aSdrh /* 8727585f49aSdrh ** Lock an kvvfs-file. 8737585f49aSdrh */ 8747585f49aSdrh static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){ 8757585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8767585f49aSdrh assert( !pFile->isJournal ); 8777585f49aSdrh SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock)); 8787585f49aSdrh 8797585f49aSdrh if( eLock!=SQLITE_LOCK_NONE ){ 8807585f49aSdrh pFile->szDb = kvvfsReadFileSize(pFile); 8817585f49aSdrh } 8827585f49aSdrh return SQLITE_OK; 8837585f49aSdrh } 8847585f49aSdrh 8857585f49aSdrh /* 8867585f49aSdrh ** Unlock an kvvfs-file. 8877585f49aSdrh */ 8887585f49aSdrh static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){ 8897585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 8907585f49aSdrh assert( !pFile->isJournal ); 8917585f49aSdrh SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock)); 8927585f49aSdrh if( eLock==SQLITE_LOCK_NONE ){ 8937585f49aSdrh pFile->szDb = -1; 8947585f49aSdrh } 8957585f49aSdrh return SQLITE_OK; 8967585f49aSdrh } 8977585f49aSdrh 8987585f49aSdrh /* 8997585f49aSdrh ** Check if another file-handle holds a RESERVED lock on an kvvfs-file. 9007585f49aSdrh */ 9017585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){ 9027585f49aSdrh SQLITE_KV_LOG(("xCheckReservedLock\n")); 9037585f49aSdrh *pResOut = 0; 9047585f49aSdrh return SQLITE_OK; 9057585f49aSdrh } 9067585f49aSdrh 9077585f49aSdrh /* 9087585f49aSdrh ** File control method. For custom operations on an kvvfs-file. 9097585f49aSdrh */ 9107585f49aSdrh static int kvvfsFileControl(sqlite3_file *pProtoFile, int op, void *pArg){ 9117585f49aSdrh return SQLITE_NOTFOUND; 9127585f49aSdrh } 9137585f49aSdrh 9147585f49aSdrh /* 9157585f49aSdrh ** Return the sector-size in bytes for an kvvfs-file. 9167585f49aSdrh */ 9177585f49aSdrh static int kvvfsSectorSize(sqlite3_file *pFile){ 9187585f49aSdrh return 512; 9197585f49aSdrh } 9207585f49aSdrh 9217585f49aSdrh /* 9227585f49aSdrh ** Return the device characteristic flags supported by an kvvfs-file. 9237585f49aSdrh */ 9247585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){ 9257585f49aSdrh return 0; 9267585f49aSdrh } 9277585f49aSdrh 9287585f49aSdrh /****** sqlite3_vfs methods *************************************************/ 9297585f49aSdrh 9307585f49aSdrh /* 9317585f49aSdrh ** Open an kvvfs file handle. 9327585f49aSdrh */ 9337585f49aSdrh static int kvvfsOpen( 9347585f49aSdrh sqlite3_vfs *pProtoVfs, 9357585f49aSdrh const char *zName, 9367585f49aSdrh sqlite3_file *pProtoFile, 9377585f49aSdrh int flags, 9387585f49aSdrh int *pOutFlags 9397585f49aSdrh ){ 9407585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 9417585f49aSdrh SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName)); 9427585f49aSdrh if( strcmp(zName, "local")==0 9437585f49aSdrh || strcmp(zName, "session")==0 9447585f49aSdrh ){ 9457585f49aSdrh pFile->isJournal = 0; 9467585f49aSdrh pFile->base.pMethods = &kvvfs_db_io_methods; 9477585f49aSdrh }else 9487585f49aSdrh if( strcmp(zName, "local-journal")==0 9497585f49aSdrh || strcmp(zName, "session-journal")==0 9507585f49aSdrh ){ 9517585f49aSdrh pFile->isJournal = 1; 9527585f49aSdrh pFile->base.pMethods = &kvvfs_jrnl_io_methods; 9537585f49aSdrh }else{ 9547585f49aSdrh return SQLITE_CANTOPEN; 9557585f49aSdrh } 9567585f49aSdrh if( zName[0]=='s' ){ 957c6c9c6aaSdrh pFile->zClass = "session"; 9587585f49aSdrh }else{ 959c6c9c6aaSdrh pFile->zClass = "local"; 9607585f49aSdrh } 9617585f49aSdrh pFile->aJrnl = 0; 9627585f49aSdrh pFile->nJrnl = 0; 9637585f49aSdrh pFile->szPage = -1; 9647585f49aSdrh pFile->szDb = -1; 9657585f49aSdrh return SQLITE_OK; 9667585f49aSdrh } 9677585f49aSdrh 9687585f49aSdrh /* 9697585f49aSdrh ** Delete the file located at zPath. If the dirSync argument is true, 9707585f49aSdrh ** ensure the file-system modifications are synced to disk before 9717585f49aSdrh ** returning. 9727585f49aSdrh */ 9737585f49aSdrh static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ 9747585f49aSdrh if( strcmp(zPath, "local-journal")==0 ){ 975c6c9c6aaSdrh kvstorageDelete("local", "jrnl"); 9767585f49aSdrh }else 9777585f49aSdrh if( strcmp(zPath, "session-journal")==0 ){ 978c6c9c6aaSdrh kvstorageDelete("session", "jrnl"); 9797585f49aSdrh } 9807585f49aSdrh return SQLITE_OK; 9817585f49aSdrh } 9827585f49aSdrh 9837585f49aSdrh /* 9847585f49aSdrh ** Test for access permissions. Return true if the requested permission 9857585f49aSdrh ** is available, or false otherwise. 9867585f49aSdrh */ 9877585f49aSdrh static int kvvfsAccess( 9887585f49aSdrh sqlite3_vfs *pProtoVfs, 9897585f49aSdrh const char *zPath, 9907585f49aSdrh int flags, 9917585f49aSdrh int *pResOut 9927585f49aSdrh ){ 9937585f49aSdrh SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath)); 9947585f49aSdrh if( strcmp(zPath, "local-journal")==0 ){ 995c6c9c6aaSdrh *pResOut = kvstorageRead("local", "jrnl", 0, 0)>0; 9967585f49aSdrh }else 9977585f49aSdrh if( strcmp(zPath, "session-journal")==0 ){ 998c6c9c6aaSdrh *pResOut = kvstorageRead("session", "jrnl", 0, 0)>0; 9997585f49aSdrh }else 10007585f49aSdrh if( strcmp(zPath, "local")==0 ){ 1001c6c9c6aaSdrh *pResOut = kvstorageRead("local", "sz", 0, 0)>0; 10027585f49aSdrh }else 10037585f49aSdrh if( strcmp(zPath, "session")==0 ){ 1004c6c9c6aaSdrh *pResOut = kvstorageRead("session", "sz", 0, 0)>0; 10057585f49aSdrh }else 10067585f49aSdrh { 10077585f49aSdrh *pResOut = 0; 10087585f49aSdrh } 10097585f49aSdrh SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut)); 10107585f49aSdrh return SQLITE_OK; 10117585f49aSdrh } 10127585f49aSdrh 10137585f49aSdrh /* 10147585f49aSdrh ** Populate buffer zOut with the full canonical pathname corresponding 10157585f49aSdrh ** to the pathname in zPath. zOut is guaranteed to point to a buffer 10167585f49aSdrh ** of at least (INST_MAX_PATHNAME+1) bytes. 10177585f49aSdrh */ 10187585f49aSdrh static int kvvfsFullPathname( 10197585f49aSdrh sqlite3_vfs *pVfs, 10207585f49aSdrh const char *zPath, 10217585f49aSdrh int nOut, 10227585f49aSdrh char *zOut 10237585f49aSdrh ){ 10247585f49aSdrh size_t nPath = strlen(zPath); 10257585f49aSdrh SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath)); 10267585f49aSdrh if( nOut<nPath+1 ) nPath = nOut - 1; 10277585f49aSdrh memcpy(zOut, zPath, nPath); 10287585f49aSdrh zOut[nPath] = 0; 10297585f49aSdrh return SQLITE_OK; 10307585f49aSdrh } 10317585f49aSdrh 10327585f49aSdrh /* 10337585f49aSdrh ** Open the dynamic library located at zPath and return a handle. 10347585f49aSdrh */ 10357585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ 10367585f49aSdrh return 0; 10377585f49aSdrh } 10387585f49aSdrh 10397585f49aSdrh /* 10407585f49aSdrh ** Populate the buffer pointed to by zBufOut with nByte bytes of 10417585f49aSdrh ** random data. 10427585f49aSdrh */ 10437585f49aSdrh static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ 10447585f49aSdrh memset(zBufOut, 0, nByte); 10457585f49aSdrh return nByte; 10467585f49aSdrh } 10477585f49aSdrh 10487585f49aSdrh /* 10497585f49aSdrh ** Sleep for nMicro microseconds. Return the number of microseconds 10507585f49aSdrh ** actually slept. 10517585f49aSdrh */ 10527585f49aSdrh static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){ 10537585f49aSdrh return SQLITE_OK; 10547585f49aSdrh } 10557585f49aSdrh 10567585f49aSdrh /* 10577585f49aSdrh ** Return the current time as a Julian Day number in *pTimeOut. 10587585f49aSdrh */ 10597585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ 10607585f49aSdrh *pTimeOut = 2459829.13362986; 10617585f49aSdrh return SQLITE_OK; 10627585f49aSdrh } 10637585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ 10647585f49aSdrh *pTimeOut = (sqlite3_int64)(2459829.13362986*86400000.0); 10657585f49aSdrh return SQLITE_OK; 10667585f49aSdrh } 10677585f49aSdrh 10687585f49aSdrh /* 10697585f49aSdrh ** This routine is called initialize the KV-vfs as the default VFS. 10707585f49aSdrh */ 10717585f49aSdrh int sqlite3_os_init(void){ 10727585f49aSdrh return sqlite3_vfs_register(&kvvfs_vfs, 1); 10737585f49aSdrh } 10747585f49aSdrh int sqlite3_os_end(void){ 10757585f49aSdrh return SQLITE_OK; 10767585f49aSdrh } 10777585f49aSdrh #endif /* SQLITE_OS_KV */ 1078