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