17585f49aSdrh /* 27585f49aSdrh ** 2022-09-06 37585f49aSdrh ** 47585f49aSdrh ** The author disclaims copyright to this source code. In place of 57585f49aSdrh ** a legal notice, here is a blessing: 67585f49aSdrh ** 77585f49aSdrh ** May you do good and not evil. 87585f49aSdrh ** May you find forgiveness for yourself and forgive others. 97585f49aSdrh ** May you share freely, never taking more than you give. 107585f49aSdrh ** 117585f49aSdrh ****************************************************************************** 127585f49aSdrh ** 137585f49aSdrh ** This file contains an experimental VFS layer that operates on a 147585f49aSdrh ** Key/Value storage engine where both keys and values must be pure 157585f49aSdrh ** text. 167585f49aSdrh */ 177585f49aSdrh #include <sqliteInt.h> 1820a9ed1dSdrh #if SQLITE_OS_KV || (SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL)) 197585f49aSdrh 207585f49aSdrh /***************************************************************************** 217585f49aSdrh ** Debugging logic 227585f49aSdrh */ 237585f49aSdrh 247585f49aSdrh /* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */ 257585f49aSdrh #if 0 26d35b5587Sstephan #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 33d35b5587Sstephan #define SQLITE_KV_LOG(X) printf X 347585f49aSdrh #else 357585f49aSdrh #define SQLITE_KV_LOG(X) 367585f49aSdrh #endif 377585f49aSdrh 387585f49aSdrh 397585f49aSdrh /* 407585f49aSdrh ** Forward declaration of objects used by this VFS implementation 417585f49aSdrh */ 427585f49aSdrh typedef struct KVVfsFile KVVfsFile; 437585f49aSdrh 447585f49aSdrh /* A single open file. There are only two files represented by this 457585f49aSdrh ** VFS - the database and the rollback journal. 467585f49aSdrh */ 477585f49aSdrh struct KVVfsFile { 487585f49aSdrh sqlite3_file base; /* IO methods */ 497585f49aSdrh const char *zClass; /* Storage class */ 507585f49aSdrh int isJournal; /* True if this is a journal file */ 517585f49aSdrh unsigned int nJrnl; /* Space allocated for aJrnl[] */ 527585f49aSdrh char *aJrnl; /* Journal content */ 537585f49aSdrh int szPage; /* Last known page size */ 547585f49aSdrh sqlite3_int64 szDb; /* Database file size. -1 means unknown */ 557585f49aSdrh }; 567585f49aSdrh 577585f49aSdrh /* 587585f49aSdrh ** Methods for KVVfsFile 597585f49aSdrh */ 607585f49aSdrh static int kvvfsClose(sqlite3_file*); 617585f49aSdrh static int kvvfsReadDb(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); 627585f49aSdrh static int kvvfsReadJrnl(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); 637585f49aSdrh static int kvvfsWriteDb(sqlite3_file*,const void*,int iAmt, sqlite3_int64); 647585f49aSdrh static int kvvfsWriteJrnl(sqlite3_file*,const void*,int iAmt, sqlite3_int64); 657585f49aSdrh static int kvvfsTruncateDb(sqlite3_file*, sqlite3_int64 size); 667585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file*, sqlite3_int64 size); 677585f49aSdrh static int kvvfsSyncDb(sqlite3_file*, int flags); 687585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file*, int flags); 697585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file*, sqlite3_int64 *pSize); 707585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize); 717585f49aSdrh static int kvvfsLock(sqlite3_file*, int); 727585f49aSdrh static int kvvfsUnlock(sqlite3_file*, int); 737585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut); 7484e50767Sdrh static int kvvfsFileControlDb(sqlite3_file*, int op, void *pArg); 7584e50767Sdrh static int kvvfsFileControlJrnl(sqlite3_file*, int op, void *pArg); 767585f49aSdrh static int kvvfsSectorSize(sqlite3_file*); 777585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file*); 787585f49aSdrh 797585f49aSdrh /* 807585f49aSdrh ** Methods for sqlite3_vfs 817585f49aSdrh */ 827585f49aSdrh static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); 837585f49aSdrh static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir); 847585f49aSdrh static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *); 857585f49aSdrh static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); 867585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename); 877585f49aSdrh static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); 887585f49aSdrh static int kvvfsSleep(sqlite3_vfs*, int microseconds); 897585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs*, double*); 907585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); 917585f49aSdrh 9220a9ed1dSdrh static sqlite3_vfs sqlite3OsKvvfsObject = { 937585f49aSdrh 1, /* iVersion */ 947585f49aSdrh sizeof(KVVfsFile), /* szOsFile */ 957585f49aSdrh 1024, /* mxPathname */ 967585f49aSdrh 0, /* pNext */ 977585f49aSdrh "kvvfs", /* zName */ 987585f49aSdrh 0, /* pAppData */ 997585f49aSdrh kvvfsOpen, /* xOpen */ 1007585f49aSdrh kvvfsDelete, /* xDelete */ 1017585f49aSdrh kvvfsAccess, /* xAccess */ 1027585f49aSdrh kvvfsFullPathname, /* xFullPathname */ 1037585f49aSdrh kvvfsDlOpen, /* xDlOpen */ 1047585f49aSdrh 0, /* xDlError */ 1057585f49aSdrh 0, /* xDlSym */ 1067585f49aSdrh 0, /* xDlClose */ 1077585f49aSdrh kvvfsRandomness, /* xRandomness */ 1087585f49aSdrh kvvfsSleep, /* xSleep */ 1097585f49aSdrh kvvfsCurrentTime, /* xCurrentTime */ 1107585f49aSdrh 0, /* xGetLastError */ 1117585f49aSdrh kvvfsCurrentTimeInt64 /* xCurrentTimeInt64 */ 1127585f49aSdrh }; 1137585f49aSdrh 1147585f49aSdrh /* Methods for sqlite3_file objects referencing a database file 1157585f49aSdrh */ 1167585f49aSdrh static sqlite3_io_methods kvvfs_db_io_methods = { 1177585f49aSdrh 1, /* iVersion */ 1187585f49aSdrh kvvfsClose, /* xClose */ 1197585f49aSdrh kvvfsReadDb, /* xRead */ 1207585f49aSdrh kvvfsWriteDb, /* xWrite */ 1217585f49aSdrh kvvfsTruncateDb, /* xTruncate */ 1227585f49aSdrh kvvfsSyncDb, /* xSync */ 1237585f49aSdrh kvvfsFileSizeDb, /* xFileSize */ 1247585f49aSdrh kvvfsLock, /* xLock */ 1257585f49aSdrh kvvfsUnlock, /* xUnlock */ 1267585f49aSdrh kvvfsCheckReservedLock, /* xCheckReservedLock */ 12784e50767Sdrh kvvfsFileControlDb, /* xFileControl */ 1287585f49aSdrh kvvfsSectorSize, /* xSectorSize */ 1297585f49aSdrh kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ 1307585f49aSdrh 0, /* xShmMap */ 1317585f49aSdrh 0, /* xShmLock */ 1327585f49aSdrh 0, /* xShmBarrier */ 1337585f49aSdrh 0, /* xShmUnmap */ 1347585f49aSdrh 0, /* xFetch */ 1357585f49aSdrh 0 /* xUnfetch */ 1367585f49aSdrh }; 1377585f49aSdrh 1387585f49aSdrh /* Methods for sqlite3_file objects referencing a rollback journal 1397585f49aSdrh */ 1407585f49aSdrh static sqlite3_io_methods kvvfs_jrnl_io_methods = { 1417585f49aSdrh 1, /* iVersion */ 1427585f49aSdrh kvvfsClose, /* xClose */ 1437585f49aSdrh kvvfsReadJrnl, /* xRead */ 1447585f49aSdrh kvvfsWriteJrnl, /* xWrite */ 1457585f49aSdrh kvvfsTruncateJrnl, /* xTruncate */ 1467585f49aSdrh kvvfsSyncJrnl, /* xSync */ 1477585f49aSdrh kvvfsFileSizeJrnl, /* xFileSize */ 1487585f49aSdrh kvvfsLock, /* xLock */ 1497585f49aSdrh kvvfsUnlock, /* xUnlock */ 1507585f49aSdrh kvvfsCheckReservedLock, /* xCheckReservedLock */ 15184e50767Sdrh kvvfsFileControlJrnl, /* xFileControl */ 1527585f49aSdrh kvvfsSectorSize, /* xSectorSize */ 1537585f49aSdrh kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ 1547585f49aSdrh 0, /* xShmMap */ 1557585f49aSdrh 0, /* xShmLock */ 1567585f49aSdrh 0, /* xShmBarrier */ 1577585f49aSdrh 0, /* xShmUnmap */ 1587585f49aSdrh 0, /* xFetch */ 1597585f49aSdrh 0 /* xUnfetch */ 1607585f49aSdrh }; 1617585f49aSdrh 1627585f49aSdrh /****** Storage subsystem **************************************************/ 1637585f49aSdrh #include <sys/types.h> 1647585f49aSdrh #include <sys/stat.h> 1657585f49aSdrh #include <unistd.h> 1667585f49aSdrh 1677585f49aSdrh /* Forward declarations for the low-level storage engine 1687585f49aSdrh */ 169d35b5587Sstephan static int kvstorageWrite(const char*, const char *zKey, const char *zData); 170d35b5587Sstephan static int kvstorageDelete(const char*, const char *zKey); 171d35b5587Sstephan static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf); 172c6c9c6aaSdrh #define KVSTORAGE_KEY_SZ 32 1737585f49aSdrh 1747585f49aSdrh /* Expand the key name with an appropriate prefix and put the result 175c6c9c6aaSdrh ** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least 176c6c9c6aaSdrh ** KVSTORAGE_KEY_SZ bytes. 1777585f49aSdrh */ 1787585f49aSdrh static void kvstorageMakeKey( 1797585f49aSdrh const char *zClass, 1807585f49aSdrh const char *zKeyIn, 1817585f49aSdrh char *zKeyOut 1827585f49aSdrh ){ 183c6c9c6aaSdrh sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn); 1847585f49aSdrh } 1857585f49aSdrh 186c6c9c6aaSdrh /* Write content into a key. zClass is the particular namespace of the 187c6c9c6aaSdrh ** underlying key/value store to use - either "local" or "session". 188c6c9c6aaSdrh ** 189c6c9c6aaSdrh ** Both zKey and zData are zero-terminated pure text strings. 1907585f49aSdrh ** 1917585f49aSdrh ** Return the number of errors. 1927585f49aSdrh */ 1937585f49aSdrh static int kvstorageWrite( 1947585f49aSdrh const char *zClass, 1957585f49aSdrh const char *zKey, 1967585f49aSdrh const char *zData 1977585f49aSdrh ){ 1987585f49aSdrh FILE *fd; 1997585f49aSdrh char zXKey[KVSTORAGE_KEY_SZ]; 2007585f49aSdrh kvstorageMakeKey(zClass, zKey, zXKey); 2017585f49aSdrh fd = fopen(zXKey, "wb"); 2027585f49aSdrh if( fd ){ 2037585f49aSdrh SQLITE_KV_TRACE(("KVVFS-WRITE %-15s (%d) %.50s%s\n", zXKey, 2047585f49aSdrh (int)strlen(zData), zData, 2057585f49aSdrh strlen(zData)>50 ? "..." : "")); 2067585f49aSdrh fputs(zData, fd); 2077585f49aSdrh fclose(fd); 2087585f49aSdrh return 0; 2097585f49aSdrh }else{ 2107585f49aSdrh return 1; 2117585f49aSdrh } 2127585f49aSdrh } 2137585f49aSdrh 214c6c9c6aaSdrh /* Delete a key (with its corresponding data) from the key/value 215c6c9c6aaSdrh ** namespace given by zClass. If the key does not previously exist, 216c6c9c6aaSdrh ** this routine is a no-op. 2177585f49aSdrh */ 2187585f49aSdrh static int kvstorageDelete(const char *zClass, const char *zKey){ 2197585f49aSdrh char zXKey[KVSTORAGE_KEY_SZ]; 2207585f49aSdrh kvstorageMakeKey(zClass, zKey, zXKey); 2217585f49aSdrh unlink(zXKey); 2227585f49aSdrh SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey)); 2237585f49aSdrh return 0; 2247585f49aSdrh } 2257585f49aSdrh 226c6c9c6aaSdrh /* Read the value associated with a zKey from the key/value namespace given 227c6c9c6aaSdrh ** by zClass and put the text data associated with that key in the first 2287585f49aSdrh ** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large 229c6c9c6aaSdrh ** enough to hold it all. The value put into zBuf must always be zero 230c6c9c6aaSdrh ** terminated, even if it gets truncated because nBuf is not large enough. 2317585f49aSdrh ** 2327585f49aSdrh ** Return the total number of bytes in the data, without truncation, and 2337585f49aSdrh ** not counting the final zero terminator. Return -1 if the key does 2347585f49aSdrh ** not exist. 2357585f49aSdrh ** 236c6c9c6aaSdrh ** If nBuf<=0 then this routine simply returns the size of the data without 2377585f49aSdrh ** actually reading it. 2387585f49aSdrh */ 2397585f49aSdrh static int kvstorageRead( 2407585f49aSdrh const char *zClass, 2417585f49aSdrh const char *zKey, 2427585f49aSdrh char *zBuf, 2437585f49aSdrh int nBuf 2447585f49aSdrh ){ 2457585f49aSdrh FILE *fd; 2467585f49aSdrh struct stat buf; 2477585f49aSdrh char zXKey[KVSTORAGE_KEY_SZ]; 2487585f49aSdrh kvstorageMakeKey(zClass, zKey, zXKey); 2497585f49aSdrh if( access(zXKey, R_OK)!=0 2507585f49aSdrh || stat(zXKey, &buf)!=0 2517585f49aSdrh || !S_ISREG(buf.st_mode) 2527585f49aSdrh ){ 2537585f49aSdrh SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); 2547585f49aSdrh return -1; 2557585f49aSdrh } 256c6c9c6aaSdrh if( nBuf<=0 ){ 2577585f49aSdrh return (int)buf.st_size; 2587585f49aSdrh }else if( nBuf==1 ){ 2597585f49aSdrh zBuf[0] = 0; 2607585f49aSdrh SQLITE_KV_TRACE(("KVVFS-READ %-15s (%d)\n", zXKey, 2617585f49aSdrh (int)buf.st_size)); 2627585f49aSdrh return (int)buf.st_size; 2637585f49aSdrh } 2647585f49aSdrh if( nBuf > buf.st_size + 1 ){ 2657585f49aSdrh nBuf = buf.st_size + 1; 2667585f49aSdrh } 2677585f49aSdrh fd = fopen(zXKey, "rb"); 2687585f49aSdrh if( fd==0 ){ 2697585f49aSdrh SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); 2707585f49aSdrh return -1; 2717585f49aSdrh }else{ 2727585f49aSdrh sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd); 2737585f49aSdrh fclose(fd); 2747585f49aSdrh zBuf[n] = 0; 2757585f49aSdrh SQLITE_KV_TRACE(("KVVFS-READ %-15s (%lld) %.50s%s\n", zXKey, 2767585f49aSdrh n, zBuf, n>50 ? "..." : "")); 2777585f49aSdrh return (int)n; 2787585f49aSdrh } 2797585f49aSdrh } 280d35b5587Sstephan 281d35b5587Sstephan /* 282d35b5587Sstephan ** An internal level of indirection which enables us to replace the 283d35b5587Sstephan ** kvvfs i/o methods with JavaScript implementations in WASM builds. 284d35b5587Sstephan ** Maintenance reminder: if this struct changes in any way, the JSON 285d35b5587Sstephan ** rendering of its structure must be updated in 286d35b5587Sstephan ** sqlite3_wasm_enum_json(). There are no binary compatibility 287d35b5587Sstephan ** concerns, so it does not need an iVersion member. This file is 288d35b5587Sstephan ** necessarily always compiled together with sqlite3_wasm_enum_json(), 289d35b5587Sstephan ** and JS code dynamically creates the mapping of members based on 290d35b5587Sstephan ** that JSON description. 291d35b5587Sstephan */ 292d35b5587Sstephan typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods; 293d35b5587Sstephan struct sqlite3_kvvfs_methods { 294d35b5587Sstephan int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf); 295d35b5587Sstephan int (*xWrite)(const char *zClass, const char *zKey, const char *zData); 296d35b5587Sstephan int (*xDelete)(const char *zClass, const char *zKey); 297d35b5587Sstephan const int nKeySize; 298d35b5587Sstephan }; 299d35b5587Sstephan 300d35b5587Sstephan /* 301d35b5587Sstephan ** This object holds the kvvfs I/O methods which may be swapped out 302d35b5587Sstephan ** for JavaScript-side implementations in WASM builds. In such builds 303d35b5587Sstephan ** it cannot be const, but in native builds it should be so that 304d35b5587Sstephan ** the compiler can hopefully optimize this level of indirection out. 305d35b5587Sstephan ** That said, kvvfs is intended primarily for use in WASM builds. 306d35b5587Sstephan ** 307d35b5587Sstephan ** Note that this is not explicitly flagged as static because the 308d35b5587Sstephan ** amalgamation build will tag it with SQLITE_PRIVATE. 309d35b5587Sstephan */ 310d35b5587Sstephan #ifndef SQLITE_WASM 311d35b5587Sstephan const 312d35b5587Sstephan #endif 313d35b5587Sstephan sqlite3_kvvfs_methods sqlite3KvvfsMethods = { 314d35b5587Sstephan kvstorageRead, 315d35b5587Sstephan kvstorageWrite, 316d35b5587Sstephan kvstorageDelete, 317d35b5587Sstephan KVSTORAGE_KEY_SZ 318d35b5587Sstephan }; 3197585f49aSdrh 3207585f49aSdrh /****** Utility subroutines ************************************************/ 3217585f49aSdrh 3227585f49aSdrh /* 3237585f49aSdrh ** Encode binary into the text encoded used to persist on disk. 3247585f49aSdrh ** The output text is stored in aOut[], which must be at least 3257585f49aSdrh ** nData+1 bytes in length. 3267585f49aSdrh ** 3277585f49aSdrh ** Return the actual length of the encoded text, not counting the 3287585f49aSdrh ** zero terminator at the end. 3297585f49aSdrh ** 3307585f49aSdrh ** Encoding format 3317585f49aSdrh ** --------------- 3327585f49aSdrh ** 3337585f49aSdrh ** * Non-zero bytes are encoded as upper-case hexadecimal 3347585f49aSdrh ** 3357585f49aSdrh ** * A sequence of one or more zero-bytes that are not at the 3367585f49aSdrh ** beginning of the buffer are encoded as a little-endian 3377585f49aSdrh ** base-26 number using a..z. "a" means 0. "b" means 1, 3387585f49aSdrh ** "z" means 25. "ab" means 26. "ac" means 52. And so forth. 3397585f49aSdrh ** 3407585f49aSdrh ** * Because there is no overlap between the encoding characters 3417585f49aSdrh ** of hexadecimal and base-26 numbers, it is always clear where 3427585f49aSdrh ** one stops and the next begins. 3437585f49aSdrh */ 3447585f49aSdrh static int kvvfsEncode(const char *aData, int nData, char *aOut){ 3457585f49aSdrh int i, j; 3467585f49aSdrh const unsigned char *a = (const unsigned char*)aData; 3477585f49aSdrh for(i=j=0; i<nData; i++){ 3487585f49aSdrh unsigned char c = a[i]; 3497585f49aSdrh if( c!=0 ){ 3507585f49aSdrh aOut[j++] = "0123456789ABCDEF"[c>>4]; 3517585f49aSdrh aOut[j++] = "0123456789ABCDEF"[c&0xf]; 3527585f49aSdrh }else{ 3537585f49aSdrh /* A sequence of 1 or more zeros is stored as a little-endian 3547585f49aSdrh ** base-26 number using a..z as the digits. So one zero is "b". 3557585f49aSdrh ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb", 3567585f49aSdrh ** and so forth. 3577585f49aSdrh */ 3587585f49aSdrh int k; 3597585f49aSdrh for(k=1; i+k<nData && a[i+k]==0; k++){} 3607585f49aSdrh i += k-1; 3617585f49aSdrh while( k>0 ){ 3627585f49aSdrh aOut[j++] = 'a'+(k%26); 3637585f49aSdrh k /= 26; 3647585f49aSdrh } 3657585f49aSdrh } 3667585f49aSdrh } 3677585f49aSdrh aOut[j] = 0; 3687585f49aSdrh return j; 3697585f49aSdrh } 3707585f49aSdrh 3716acd7b0aSdrh static const signed char kvvfsHexValue[256] = { 3726acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3736acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3746acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3756acd7b0aSdrh 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 3766acd7b0aSdrh -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3776acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3786acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3796acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3806acd7b0aSdrh 3816acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3826acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3836acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3846acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3856acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3866acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3876acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3886acd7b0aSdrh -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 3896acd7b0aSdrh }; 3907585f49aSdrh 3917585f49aSdrh /* 3927585f49aSdrh ** Decode the text encoding back to binary. The binary content is 3937585f49aSdrh ** written into pOut, which must be at least nOut bytes in length. 3947585f49aSdrh ** 3957585f49aSdrh ** The return value is the number of bytes actually written into aOut[]. 3967585f49aSdrh */ 3976acd7b0aSdrh static int kvvfsDecode(const char *a, char *aOut, int nOut){ 3987585f49aSdrh int i, j; 3997585f49aSdrh int c; 4006acd7b0aSdrh const unsigned char *aIn = (const unsigned char*)a; 4017585f49aSdrh i = 0; 4027585f49aSdrh j = 0; 4036acd7b0aSdrh while( 1 ){ 4046acd7b0aSdrh c = kvvfsHexValue[aIn[i]]; 4056acd7b0aSdrh if( c<0 ){ 4067585f49aSdrh int n = 0; 4077585f49aSdrh int mult = 1; 4086acd7b0aSdrh c = aIn[i]; 4096acd7b0aSdrh if( c==0 ) break; 4107585f49aSdrh while( c>='a' && c<='z' ){ 4117585f49aSdrh n += (c - 'a')*mult; 4127585f49aSdrh mult *= 26; 4137585f49aSdrh c = aIn[++i]; 4147585f49aSdrh } 4157585f49aSdrh if( j+n>nOut ) return -1; 4166acd7b0aSdrh memset(&aOut[j], 0, n); 4176acd7b0aSdrh j += n; 4186acd7b0aSdrh c = aIn[i]; 4196acd7b0aSdrh if( c==0 ) break; 4207585f49aSdrh }else{ 4216acd7b0aSdrh aOut[j] = c<<4; 4226acd7b0aSdrh c = kvvfsHexValue[aIn[++i]]; 4236acd7b0aSdrh if( c<0 ) break; 4246acd7b0aSdrh aOut[j++] += c; 4256acd7b0aSdrh i++; 4267585f49aSdrh } 4277585f49aSdrh } 4287585f49aSdrh return j; 4297585f49aSdrh } 4307585f49aSdrh 4317585f49aSdrh /* 4327585f49aSdrh ** Decode a complete journal file. Allocate space in pFile->aJrnl 4337585f49aSdrh ** and store the decoding there. Or leave pFile->aJrnl set to NULL 4347585f49aSdrh ** if an error is encountered. 4357585f49aSdrh ** 4367585f49aSdrh ** The first few characters of the text encoding will be a little-endian 4377585f49aSdrh ** base-26 number (digits a..z) that is the total number of bytes 4387585f49aSdrh ** in the decoded journal file image. This base-26 number is followed 4397585f49aSdrh ** by a single space, then the encoding of the journal. The space 4407585f49aSdrh ** separator is required to act as a terminator for the base-26 number. 4417585f49aSdrh */ 4427585f49aSdrh static void kvvfsDecodeJournal( 4437585f49aSdrh KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */ 4447585f49aSdrh const char *zTxt, /* Text encoding. Zero-terminated */ 4457585f49aSdrh int nTxt /* Bytes in zTxt, excluding zero terminator */ 4467585f49aSdrh ){ 447a50d3b7aSdrh unsigned int n = 0; 4487585f49aSdrh int c, i, mult; 4497585f49aSdrh i = 0; 4507585f49aSdrh mult = 1; 4517585f49aSdrh while( (c = zTxt[i++])>='a' && c<='z' ){ 4527585f49aSdrh n += (zTxt[i] - 'a')*mult; 4537585f49aSdrh mult *= 26; 4547585f49aSdrh } 4557585f49aSdrh sqlite3_free(pFile->aJrnl); 4567585f49aSdrh pFile->aJrnl = sqlite3_malloc64( n ); 4577585f49aSdrh if( pFile->aJrnl==0 ){ 4587585f49aSdrh pFile->nJrnl = 0; 4597585f49aSdrh return; 4607585f49aSdrh } 4617585f49aSdrh pFile->nJrnl = n; 4627585f49aSdrh n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl); 4637585f49aSdrh if( n<pFile->nJrnl ){ 4647585f49aSdrh sqlite3_free(pFile->aJrnl); 4657585f49aSdrh pFile->aJrnl = 0; 4667585f49aSdrh pFile->nJrnl = 0; 4677585f49aSdrh } 4687585f49aSdrh } 4697585f49aSdrh 4707585f49aSdrh /* 4717585f49aSdrh ** Read or write the "sz" element, containing the database file size. 4727585f49aSdrh */ 4737585f49aSdrh static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ 4747585f49aSdrh char zData[50]; 4757585f49aSdrh zData[0] = 0; 476d35b5587Sstephan sqlite3KvvfsMethods.xRead(pFile->zClass, "sz", zData, sizeof(zData)-1); 4777585f49aSdrh return strtoll(zData, 0, 0); 4787585f49aSdrh } 479d22cfa8cSstephan static int kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ 4807585f49aSdrh char zData[50]; 4817585f49aSdrh sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); 482d35b5587Sstephan return sqlite3KvvfsMethods.xWrite(pFile->zClass, "sz", zData); 4837585f49aSdrh } 4847585f49aSdrh 4857585f49aSdrh /****** sqlite3_io_methods methods ******************************************/ 4867585f49aSdrh 4877585f49aSdrh /* 4887585f49aSdrh ** Close an kvvfs-file. 4897585f49aSdrh */ 4907585f49aSdrh static int kvvfsClose(sqlite3_file *pProtoFile){ 4917585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 4927585f49aSdrh 4937585f49aSdrh SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass, 4947585f49aSdrh pFile->isJournal ? "journal" : "db")); 4957585f49aSdrh sqlite3_free(pFile->aJrnl); 4967585f49aSdrh return SQLITE_OK; 4977585f49aSdrh } 4987585f49aSdrh 4997585f49aSdrh /* 5007585f49aSdrh ** Read from the -journal file. 5017585f49aSdrh */ 5027585f49aSdrh static int kvvfsReadJrnl( 5037585f49aSdrh sqlite3_file *pProtoFile, 5047585f49aSdrh void *zBuf, 5057585f49aSdrh int iAmt, 5067585f49aSdrh sqlite_int64 iOfst 5077585f49aSdrh ){ 5087585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 5097585f49aSdrh assert( pFile->isJournal ); 5107585f49aSdrh SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 5117585f49aSdrh if( pFile->aJrnl==0 ){ 512c6c9c6aaSdrh int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0); 5137585f49aSdrh char *aTxt; 5147585f49aSdrh if( szTxt<=4 ){ 5157585f49aSdrh return SQLITE_IOERR; 5167585f49aSdrh } 5177585f49aSdrh aTxt = sqlite3_malloc64( szTxt+1 ); 5187585f49aSdrh if( aTxt==0 ) return SQLITE_NOMEM; 5197585f49aSdrh kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1); 5207585f49aSdrh kvvfsDecodeJournal(pFile, aTxt, szTxt); 5217585f49aSdrh sqlite3_free(aTxt); 5227585f49aSdrh if( pFile->aJrnl==0 ) return SQLITE_IOERR; 5237585f49aSdrh } 5247585f49aSdrh if( iOfst+iAmt>pFile->nJrnl ){ 5257585f49aSdrh return SQLITE_IOERR_SHORT_READ; 5267585f49aSdrh } 5277585f49aSdrh memcpy(zBuf, pFile->aJrnl+iOfst, iAmt); 5287585f49aSdrh return SQLITE_OK; 5297585f49aSdrh } 5307585f49aSdrh 5317585f49aSdrh /* 5327585f49aSdrh ** Read from the database file. 5337585f49aSdrh */ 5347585f49aSdrh static int kvvfsReadDb( 5357585f49aSdrh sqlite3_file *pProtoFile, 5367585f49aSdrh void *zBuf, 5377585f49aSdrh int iAmt, 5387585f49aSdrh sqlite_int64 iOfst 5397585f49aSdrh ){ 5407585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 5417585f49aSdrh unsigned int pgno; 5427585f49aSdrh int got, n; 5437585f49aSdrh char zKey[30]; 544756440feSdrh char aData[133073]; 5457585f49aSdrh assert( iOfst>=0 ); 5467585f49aSdrh assert( iAmt>=0 ); 5477585f49aSdrh SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 5487585f49aSdrh if( iOfst+iAmt>=512 ){ 5497585f49aSdrh if( (iOfst % iAmt)!=0 ){ 5507585f49aSdrh return SQLITE_IOERR_READ; 5517585f49aSdrh } 5527585f49aSdrh if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ 5537585f49aSdrh return SQLITE_IOERR_READ; 5547585f49aSdrh } 5557585f49aSdrh pFile->szPage = iAmt; 5567585f49aSdrh pgno = 1 + iOfst/iAmt; 5577585f49aSdrh }else{ 5587585f49aSdrh pgno = 1; 5597585f49aSdrh } 5607585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 561d35b5587Sstephan got = sqlite3KvvfsMethods.xRead(pFile->zClass, zKey, aData, sizeof(aData)-1); 5627585f49aSdrh if( got<0 ){ 5637585f49aSdrh n = 0; 5647585f49aSdrh }else{ 5657585f49aSdrh aData[got] = 0; 5667585f49aSdrh if( iOfst+iAmt<512 ){ 567756440feSdrh int k = iOfst+iAmt; 568756440feSdrh aData[k*2] = 0; 569756440feSdrh n = kvvfsDecode(aData, &aData[2000], sizeof(aData)-2000); 5707585f49aSdrh if( n>=iOfst+iAmt ){ 571756440feSdrh memcpy(zBuf, &aData[2000+iOfst], iAmt); 5727585f49aSdrh n = iAmt; 5737585f49aSdrh }else{ 5747585f49aSdrh n = 0; 5757585f49aSdrh } 5767585f49aSdrh }else{ 5777585f49aSdrh n = kvvfsDecode(aData, zBuf, iAmt); 5787585f49aSdrh } 5797585f49aSdrh } 5807585f49aSdrh if( n<iAmt ){ 5817585f49aSdrh memset(zBuf+n, 0, iAmt-n); 5827585f49aSdrh return SQLITE_IOERR_SHORT_READ; 5837585f49aSdrh } 5847585f49aSdrh return SQLITE_OK; 5857585f49aSdrh } 5867585f49aSdrh 5877585f49aSdrh 5887585f49aSdrh /* 5897585f49aSdrh ** Write into the -journal file. 5907585f49aSdrh */ 5917585f49aSdrh static int kvvfsWriteJrnl( 5927585f49aSdrh sqlite3_file *pProtoFile, 5937585f49aSdrh const void *zBuf, 5947585f49aSdrh int iAmt, 5957585f49aSdrh sqlite_int64 iOfst 5967585f49aSdrh ){ 5977585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 5987585f49aSdrh sqlite3_int64 iEnd = iOfst+iAmt; 5997585f49aSdrh SQLITE_KV_LOG(("xWrite('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 6007585f49aSdrh if( iEnd>=0x10000000 ) return SQLITE_FULL; 6017585f49aSdrh if( pFile->aJrnl==0 || pFile->nJrnl<iEnd ){ 6027585f49aSdrh char *aNew = sqlite3_realloc(pFile->aJrnl, iEnd); 6037585f49aSdrh if( aNew==0 ){ 6047585f49aSdrh return SQLITE_IOERR_NOMEM; 6057585f49aSdrh } 6067585f49aSdrh pFile->aJrnl = aNew; 6077585f49aSdrh if( pFile->nJrnl<iOfst ){ 6087585f49aSdrh memset(pFile->aJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl); 6097585f49aSdrh } 6107585f49aSdrh pFile->nJrnl = iEnd; 6117585f49aSdrh } 6127585f49aSdrh memcpy(pFile->aJrnl+iOfst, zBuf, iAmt); 6137585f49aSdrh return SQLITE_OK; 6147585f49aSdrh } 6157585f49aSdrh 6167585f49aSdrh /* 6177585f49aSdrh ** Write into the database file. 6187585f49aSdrh */ 6197585f49aSdrh static int kvvfsWriteDb( 6207585f49aSdrh sqlite3_file *pProtoFile, 6217585f49aSdrh const void *zBuf, 6227585f49aSdrh int iAmt, 6237585f49aSdrh sqlite_int64 iOfst 6247585f49aSdrh ){ 6257585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 6267585f49aSdrh unsigned int pgno; 6277585f49aSdrh char zKey[30]; 6287585f49aSdrh char aData[131073]; 6297585f49aSdrh SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); 6307585f49aSdrh assert( iAmt>=512 && iAmt<=65536 ); 6317585f49aSdrh assert( (iAmt & (iAmt-1))==0 ); 632*f7b58da8Sdrh assert( pFile->szPage<0 || pFile->szPage==iAmt ); 633*f7b58da8Sdrh pFile->szPage = iAmt; 6347585f49aSdrh pgno = 1 + iOfst/iAmt; 6357585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 6367585f49aSdrh kvvfsEncode(zBuf, iAmt, aData); 637d35b5587Sstephan if( sqlite3KvvfsMethods.xWrite(pFile->zClass, zKey, aData) ){ 638d22cfa8cSstephan return SQLITE_IOERR; 639d22cfa8cSstephan } 6407585f49aSdrh if( iOfst+iAmt > pFile->szDb ){ 6417585f49aSdrh pFile->szDb = iOfst + iAmt; 6427585f49aSdrh } 6437585f49aSdrh return SQLITE_OK; 6447585f49aSdrh } 6457585f49aSdrh 6467585f49aSdrh /* 6477585f49aSdrh ** Truncate an kvvfs-file. 6487585f49aSdrh */ 6497585f49aSdrh static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){ 6507585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6517585f49aSdrh SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size)); 6527585f49aSdrh assert( size==0 ); 653d35b5587Sstephan sqlite3KvvfsMethods.xDelete(pFile->zClass, "jrnl"); 6547585f49aSdrh sqlite3_free(pFile->aJrnl); 6557585f49aSdrh pFile->aJrnl = 0; 6567585f49aSdrh pFile->nJrnl = 0; 6577585f49aSdrh return SQLITE_OK; 6587585f49aSdrh } 6597585f49aSdrh static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ 6607585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6617585f49aSdrh if( pFile->szDb>size 6627585f49aSdrh && pFile->szPage>0 6637585f49aSdrh && (size % pFile->szPage)==0 6647585f49aSdrh ){ 6657585f49aSdrh char zKey[50]; 6667585f49aSdrh unsigned int pgno, pgnoMax; 6677585f49aSdrh SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size)); 6687585f49aSdrh pgno = 1 + size/pFile->szPage; 6697585f49aSdrh pgnoMax = 2 + pFile->szDb/pFile->szPage; 6707585f49aSdrh while( pgno<=pgnoMax ){ 6717585f49aSdrh sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); 672d35b5587Sstephan sqlite3KvvfsMethods.xDelete(pFile->zClass, zKey); 6737585f49aSdrh pgno++; 6747585f49aSdrh } 6757585f49aSdrh pFile->szDb = size; 676d22cfa8cSstephan return kvvfsWriteFileSize(pFile, size) ? SQLITE_IOERR : SQLITE_OK; 6777585f49aSdrh } 6787585f49aSdrh return SQLITE_IOERR; 6797585f49aSdrh } 6807585f49aSdrh 6817585f49aSdrh /* 6827585f49aSdrh ** Sync an kvvfs-file. 6837585f49aSdrh */ 6847585f49aSdrh static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ 6857585f49aSdrh int i, n; 6867585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 6877585f49aSdrh char *zOut; 6887585f49aSdrh SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass)); 6897585f49aSdrh if( pFile->nJrnl<=0 ){ 6907585f49aSdrh return kvvfsTruncateJrnl(pProtoFile, 0); 6917585f49aSdrh } 6927585f49aSdrh zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 ); 6937585f49aSdrh if( zOut==0 ){ 6947585f49aSdrh return SQLITE_IOERR_NOMEM; 6957585f49aSdrh } 6967585f49aSdrh n = pFile->nJrnl; 6977585f49aSdrh i = 0; 6987585f49aSdrh do{ 6997585f49aSdrh zOut[i++] = 'a' + (n%26); 7007585f49aSdrh n /= 26; 7017585f49aSdrh }while( n>0 ); 7027585f49aSdrh zOut[i++] = ' '; 7037585f49aSdrh kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); 704d35b5587Sstephan i = sqlite3KvvfsMethods.xWrite(pFile->zClass, "jrnl", zOut); 7057585f49aSdrh sqlite3_free(zOut); 706d22cfa8cSstephan return i ? SQLITE_IOERR : SQLITE_OK; 7077585f49aSdrh } 7087585f49aSdrh static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ 70984e50767Sdrh return SQLITE_OK; 7107585f49aSdrh } 7117585f49aSdrh 7127585f49aSdrh /* 7137585f49aSdrh ** Return the current file-size of an kvvfs-file. 7147585f49aSdrh */ 7157585f49aSdrh static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ 7167585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 7177585f49aSdrh SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass)); 7187585f49aSdrh *pSize = pFile->nJrnl; 7197585f49aSdrh return SQLITE_OK; 7207585f49aSdrh } 7217585f49aSdrh static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ 7227585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 7237585f49aSdrh SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass)); 7247585f49aSdrh if( pFile->szDb>=0 ){ 7257585f49aSdrh *pSize = pFile->szDb; 7267585f49aSdrh }else{ 7277585f49aSdrh *pSize = kvvfsReadFileSize(pFile); 7287585f49aSdrh } 7297585f49aSdrh return SQLITE_OK; 7307585f49aSdrh } 7317585f49aSdrh 7327585f49aSdrh /* 7337585f49aSdrh ** Lock an kvvfs-file. 7347585f49aSdrh */ 7357585f49aSdrh static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){ 7367585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 7377585f49aSdrh assert( !pFile->isJournal ); 7387585f49aSdrh SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock)); 7397585f49aSdrh 7407585f49aSdrh if( eLock!=SQLITE_LOCK_NONE ){ 7417585f49aSdrh pFile->szDb = kvvfsReadFileSize(pFile); 7427585f49aSdrh } 7437585f49aSdrh return SQLITE_OK; 7447585f49aSdrh } 7457585f49aSdrh 7467585f49aSdrh /* 7477585f49aSdrh ** Unlock an kvvfs-file. 7487585f49aSdrh */ 7497585f49aSdrh static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){ 7507585f49aSdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 7517585f49aSdrh assert( !pFile->isJournal ); 7527585f49aSdrh SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock)); 7537585f49aSdrh if( eLock==SQLITE_LOCK_NONE ){ 7547585f49aSdrh pFile->szDb = -1; 7557585f49aSdrh } 7567585f49aSdrh return SQLITE_OK; 7577585f49aSdrh } 7587585f49aSdrh 7597585f49aSdrh /* 7607585f49aSdrh ** Check if another file-handle holds a RESERVED lock on an kvvfs-file. 7617585f49aSdrh */ 7627585f49aSdrh static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){ 7637585f49aSdrh SQLITE_KV_LOG(("xCheckReservedLock\n")); 7647585f49aSdrh *pResOut = 0; 7657585f49aSdrh return SQLITE_OK; 7667585f49aSdrh } 7677585f49aSdrh 7687585f49aSdrh /* 7697585f49aSdrh ** File control method. For custom operations on an kvvfs-file. 7707585f49aSdrh */ 77184e50767Sdrh static int kvvfsFileControlJrnl(sqlite3_file *pProtoFile, int op, void *pArg){ 77284e50767Sdrh SQLITE_KV_LOG(("xFileControl(%d) on journal\n", op)); 77384e50767Sdrh return SQLITE_NOTFOUND; 77484e50767Sdrh } 77584e50767Sdrh static int kvvfsFileControlDb(sqlite3_file *pProtoFile, int op, void *pArg){ 77684e50767Sdrh SQLITE_KV_LOG(("xFileControl(%d) on database\n", op)); 77784e50767Sdrh if( op==SQLITE_FCNTL_SYNC ){ 77884e50767Sdrh KVVfsFile *pFile = (KVVfsFile *)pProtoFile; 77984e50767Sdrh int rc = SQLITE_OK; 78084e50767Sdrh SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); 78184e50767Sdrh if( pFile->szDb>0 && 0!=kvvfsWriteFileSize(pFile, pFile->szDb) ){ 78284e50767Sdrh rc = SQLITE_IOERR; 78384e50767Sdrh } 78484e50767Sdrh return rc; 78584e50767Sdrh } 7867585f49aSdrh return SQLITE_NOTFOUND; 7877585f49aSdrh } 7887585f49aSdrh 7897585f49aSdrh /* 7907585f49aSdrh ** Return the sector-size in bytes for an kvvfs-file. 7917585f49aSdrh */ 7927585f49aSdrh static int kvvfsSectorSize(sqlite3_file *pFile){ 7937585f49aSdrh return 512; 7947585f49aSdrh } 7957585f49aSdrh 7967585f49aSdrh /* 7977585f49aSdrh ** Return the device characteristic flags supported by an kvvfs-file. 7987585f49aSdrh */ 7997585f49aSdrh static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){ 8007585f49aSdrh return 0; 8017585f49aSdrh } 8027585f49aSdrh 8037585f49aSdrh /****** sqlite3_vfs methods *************************************************/ 8047585f49aSdrh 8057585f49aSdrh /* 8067585f49aSdrh ** Open an kvvfs file handle. 8077585f49aSdrh */ 8087585f49aSdrh static int kvvfsOpen( 8097585f49aSdrh sqlite3_vfs *pProtoVfs, 8107585f49aSdrh const char *zName, 8117585f49aSdrh sqlite3_file *pProtoFile, 8127585f49aSdrh int flags, 8137585f49aSdrh int *pOutFlags 8147585f49aSdrh ){ 8157585f49aSdrh KVVfsFile *pFile = (KVVfsFile*)pProtoFile; 8167585f49aSdrh SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName)); 8177585f49aSdrh if( strcmp(zName, "local")==0 8187585f49aSdrh || strcmp(zName, "session")==0 8197585f49aSdrh ){ 8207585f49aSdrh pFile->isJournal = 0; 8217585f49aSdrh pFile->base.pMethods = &kvvfs_db_io_methods; 8227585f49aSdrh }else 8237585f49aSdrh if( strcmp(zName, "local-journal")==0 8247585f49aSdrh || strcmp(zName, "session-journal")==0 8257585f49aSdrh ){ 8267585f49aSdrh pFile->isJournal = 1; 8277585f49aSdrh pFile->base.pMethods = &kvvfs_jrnl_io_methods; 8287585f49aSdrh }else{ 8297585f49aSdrh return SQLITE_CANTOPEN; 8307585f49aSdrh } 8317585f49aSdrh if( zName[0]=='s' ){ 832c6c9c6aaSdrh pFile->zClass = "session"; 8337585f49aSdrh }else{ 834c6c9c6aaSdrh pFile->zClass = "local"; 8357585f49aSdrh } 8367585f49aSdrh pFile->aJrnl = 0; 8377585f49aSdrh pFile->nJrnl = 0; 8387585f49aSdrh pFile->szPage = -1; 8397585f49aSdrh pFile->szDb = -1; 8407585f49aSdrh return SQLITE_OK; 8417585f49aSdrh } 8427585f49aSdrh 8437585f49aSdrh /* 8447585f49aSdrh ** Delete the file located at zPath. If the dirSync argument is true, 8457585f49aSdrh ** ensure the file-system modifications are synced to disk before 8467585f49aSdrh ** returning. 8477585f49aSdrh */ 8487585f49aSdrh static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ 8497585f49aSdrh if( strcmp(zPath, "local-journal")==0 ){ 850d35b5587Sstephan sqlite3KvvfsMethods.xDelete("local", "jrnl"); 8517585f49aSdrh }else 8527585f49aSdrh if( strcmp(zPath, "session-journal")==0 ){ 853d35b5587Sstephan sqlite3KvvfsMethods.xDelete("session", "jrnl"); 8547585f49aSdrh } 8557585f49aSdrh return SQLITE_OK; 8567585f49aSdrh } 8577585f49aSdrh 8587585f49aSdrh /* 8597585f49aSdrh ** Test for access permissions. Return true if the requested permission 8607585f49aSdrh ** is available, or false otherwise. 8617585f49aSdrh */ 8627585f49aSdrh static int kvvfsAccess( 8637585f49aSdrh sqlite3_vfs *pProtoVfs, 8647585f49aSdrh const char *zPath, 8657585f49aSdrh int flags, 8667585f49aSdrh int *pResOut 8677585f49aSdrh ){ 8687585f49aSdrh SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath)); 8697585f49aSdrh if( strcmp(zPath, "local-journal")==0 ){ 870d35b5587Sstephan *pResOut = sqlite3KvvfsMethods.xRead("local", "jrnl", 0, 0)>0; 8717585f49aSdrh }else 8727585f49aSdrh if( strcmp(zPath, "session-journal")==0 ){ 873d35b5587Sstephan *pResOut = sqlite3KvvfsMethods.xRead("session", "jrnl", 0, 0)>0; 8747585f49aSdrh }else 8757585f49aSdrh if( strcmp(zPath, "local")==0 ){ 876d35b5587Sstephan *pResOut = sqlite3KvvfsMethods.xRead("local", "sz", 0, 0)>0; 8777585f49aSdrh }else 8787585f49aSdrh if( strcmp(zPath, "session")==0 ){ 879d35b5587Sstephan *pResOut = sqlite3KvvfsMethods.xRead("session", "sz", 0, 0)>0; 8807585f49aSdrh }else 8817585f49aSdrh { 8827585f49aSdrh *pResOut = 0; 8837585f49aSdrh } 8847585f49aSdrh SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut)); 8857585f49aSdrh return SQLITE_OK; 8867585f49aSdrh } 8877585f49aSdrh 8887585f49aSdrh /* 8897585f49aSdrh ** Populate buffer zOut with the full canonical pathname corresponding 8907585f49aSdrh ** to the pathname in zPath. zOut is guaranteed to point to a buffer 8917585f49aSdrh ** of at least (INST_MAX_PATHNAME+1) bytes. 8927585f49aSdrh */ 8937585f49aSdrh static int kvvfsFullPathname( 8947585f49aSdrh sqlite3_vfs *pVfs, 8957585f49aSdrh const char *zPath, 8967585f49aSdrh int nOut, 8977585f49aSdrh char *zOut 8987585f49aSdrh ){ 8996acd7b0aSdrh size_t nPath; 900bb195124Sdrh #ifdef SQLITE_OS_KV_ALWAYS_LOCAL 9016acd7b0aSdrh zPath = "local"; 902bb195124Sdrh #endif 9036acd7b0aSdrh nPath = strlen(zPath); 9047585f49aSdrh SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath)); 9057585f49aSdrh if( nOut<nPath+1 ) nPath = nOut - 1; 9067585f49aSdrh memcpy(zOut, zPath, nPath); 9077585f49aSdrh zOut[nPath] = 0; 9087585f49aSdrh return SQLITE_OK; 9097585f49aSdrh } 9107585f49aSdrh 9117585f49aSdrh /* 9127585f49aSdrh ** Open the dynamic library located at zPath and return a handle. 9137585f49aSdrh */ 9147585f49aSdrh static void *kvvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){ 9157585f49aSdrh return 0; 9167585f49aSdrh } 9177585f49aSdrh 9187585f49aSdrh /* 9197585f49aSdrh ** Populate the buffer pointed to by zBufOut with nByte bytes of 9207585f49aSdrh ** random data. 9217585f49aSdrh */ 9227585f49aSdrh static int kvvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){ 9237585f49aSdrh memset(zBufOut, 0, nByte); 9247585f49aSdrh return nByte; 9257585f49aSdrh } 9267585f49aSdrh 9277585f49aSdrh /* 9287585f49aSdrh ** Sleep for nMicro microseconds. Return the number of microseconds 9297585f49aSdrh ** actually slept. 9307585f49aSdrh */ 9317585f49aSdrh static int kvvfsSleep(sqlite3_vfs *pVfs, int nMicro){ 9327585f49aSdrh return SQLITE_OK; 9337585f49aSdrh } 9347585f49aSdrh 9357585f49aSdrh /* 9367585f49aSdrh ** Return the current time as a Julian Day number in *pTimeOut. 9377585f49aSdrh */ 9387585f49aSdrh static int kvvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ 939d8bb0b5aSdrh sqlite3_int64 i = 0; 940d8bb0b5aSdrh int rc; 941d8bb0b5aSdrh rc = kvvfsCurrentTimeInt64(0, &i); 942d8bb0b5aSdrh *pTimeOut = i/86400000.0; 943d8bb0b5aSdrh return rc; 9447585f49aSdrh } 945d8bb0b5aSdrh #include <sys/time.h> 9467585f49aSdrh static int kvvfsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){ 947d8bb0b5aSdrh static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; 948d8bb0b5aSdrh struct timeval sNow; 949d8bb0b5aSdrh (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */ 950d8bb0b5aSdrh *pTimeOut = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; 9517585f49aSdrh return SQLITE_OK; 9527585f49aSdrh } 95320a9ed1dSdrh #endif /* SQLITE_OS_KV || SQLITE_OS_UNIX */ 9547585f49aSdrh 95520a9ed1dSdrh #if SQLITE_OS_KV 9567585f49aSdrh /* 9577585f49aSdrh ** This routine is called initialize the KV-vfs as the default VFS. 9587585f49aSdrh */ 9597585f49aSdrh int sqlite3_os_init(void){ 96020a9ed1dSdrh return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 1); 9617585f49aSdrh } 9627585f49aSdrh int sqlite3_os_end(void){ 9637585f49aSdrh return SQLITE_OK; 9647585f49aSdrh } 9657585f49aSdrh #endif /* SQLITE_OS_KV */ 96620a9ed1dSdrh 96720a9ed1dSdrh #if SQLITE_OS_UNIX && defined(SQLITE_OS_KV_OPTIONAL) 96820a9ed1dSdrh int sqlite3KvvfsInit(void){ 96920a9ed1dSdrh return sqlite3_vfs_register(&sqlite3OsKvvfsObject, 0); 97020a9ed1dSdrh } 97120a9ed1dSdrh #endif 972