diff --git a/ext/misc/vfskv.c b/ext/misc/vfskv.c index 4d8d797a4f..0773f70945 100644 --- a/ext/misc/vfskv.c +++ b/ext/misc/vfskv.c @@ -47,19 +47,26 @@ static int kvstorageRead(KVStorage*, const char *zKey, char *zBuf, int nBuf); typedef struct KVVfsVfs KVVfsVfs; typedef struct KVVfsFile KVVfsFile; + +/* All information about the database */ struct KVVfsVfs { sqlite3_vfs base; /* VFS methods */ KVStorage *pStore; /* Single command KV storage object */ KVVfsFile *pFiles; /* List of open KVVfsFile objects */ }; +/* A single open file. There are only two files represented by this +** VFS - the database and the rollback journal. +*/ struct KVVfsFile { sqlite3_file base; /* IO methods */ KVVfsVfs *pVfs; /* The VFS to which this file belongs */ KVVfsFile *pNext; /* Next in list of all files */ int isJournal; /* True if this is a journal file */ - int nJrnl; /* Space allocated for aJrnl[] */ + unsigned int nJrnl; /* Space allocated for aJrnl[] */ char *aJrnl; /* Journal content */ + int szPage; /* Last known page size */ + sqlite3_int64 szDb; /* Database file size. -1 means unknown */ }; /* @@ -291,7 +298,8 @@ static int kvvfsEncode(const char *aData, int nData, char *aOut){ }else{ /* A sequence of 1 or more zeros is stored as a little-endian ** base-26 number using a..z as the digits. So one zero is "b". - ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ba" and so forth. + ** Two zeros is "c". 25 zeros is "z", 26 zeros is "ab", 27 is "bb", + ** and so forth. */ int k; for(k=1; a[i+k]==0 && i+k='a' ){ int n = 0; + int mult = 1; while( c>='a' && c<='z' ){ - n = n*26 + c - 'a'; + n += (c - 'a')*mult; + mult *= 26; c = aIn[++i]; } if( j+n>nOut ) return -1; @@ -345,6 +357,44 @@ static int kvvfsDecode(const char *aIn, char *aOut, int nOut){ return j; } +/* +** Decode a complete journal file. Allocate space in pFile->aJrnl +** and store the decoding there. Or leave pFile->aJrnl set to NULL +** if an error is encountered. +** +** The first few characters of the text encoding will be a +** base-26 number (digits a..z) that is the total number of bytes +** in the decoding. Use this number to size the initial allocation. +*/ +static void kvvfsDecodeJournal( + KVVfsFile *pFile, /* Store decoding in pFile->aJrnl */ + const char *zTxt, /* Text encoding. Zero-terminated */ + int nTxt /* Bytes in zTxt, excluding zero terminator */ +){ + unsigned int n; + int c, i, mult; + i = 0; + mult = 1; + while( (c = zTxt[i])>='a' && c<='z' ){ + n += (zTxt[i] - 'a')*mult; + mult *= 26; + i++; + } + sqlite3_free(pFile->aJrnl); + pFile->aJrnl = sqlite3_malloc64( n ); + if( pFile->aJrnl==0 ){ + pFile->nJrnl = 0; + return; + } + pFile->nJrnl = n; + n = kvvfsDecode(zTxt+i, pFile->aJrnl, pFile->nJrnl); + if( nnJrnl ){ + sqlite3_free(pFile->aJrnl); + pFile->aJrnl = 0; + pFile->nJrnl = 0; + } +} + /* ** Read from the -journal file. */ @@ -384,7 +434,36 @@ static int kvvfsReadFromDb( int iAmt, sqlite_int64 iOfst ){ - return SQLITE_IOERR; + unsigned int pgno; + KVVfsPage *pPage; + int got; + char zKey[30]; + char aData[131073]; + assert( pFile->szDb>=0 ); + assert( iOfst>=0 ); + assert( iAmt>=0 ); + if( (iOfst % iAmt)!=0 ){ + return SQLITE_IOERR_READ; + } + if( iAmt!=100 || iOfst!=0 ){ + if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ + return SQLITE_IOERR_READ; + } + pFile->szPage = iAmt; + } + pgno = 1 + iOfst/iAmt; + sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno) + got = kvstorageRead(pFile->pVfs->pStore, zKey, aData, sizeof(aData)-1); + if( got<0 ){ + return SQLITE_IOERR_READ; + } + aData[got] = 0; + n = kvvfsDecode(aData, zBuf, iAmt); + if( npVfs->pStore, "sz", zData, sizeof(zData)-1); + return strtoll(zData, 0, 0); +} +static void kvvfsWriteFileSize(KVVfsFile *pFile, sqlite3_int64 sz){ + char zData[50]; + sqlite3_snprintf(sizeof(zData), zData, "%lld", sz); + kvstorageWrite(pFile->pVfs->pStore, "sz", zData); +} + /* ** Write into the -journal file. */ @@ -416,11 +510,25 @@ static int kvvfsWriteToJournal( int iAmt, sqlite_int64 iOfst ){ - return SQLITE_IOERR; + sqlite3_int64 iEnd = iOfst+iAmt; + if( iEnd>=0x10000000 ) return SQLITE_FULL; + if( pFile->aJrnl==0 || pFile->nJrnlaJrnl, iEnd); + if( aNew==0 ){ + return SQLITE_IOERR_NOMEM; + } + pFile->aJrnl = aNew; + if( pFile->nJrnlaJrnl+pFile->nJrnl, 0, iOfst-pFile->nJrnl); + } + pFile->nJrnl = iEnd; + } + memcpy(pFile->aJrnl+iOfst, zBuf, iAmt); + return SQLITE_OK; } /* -** Read from the database file. +** Write into the database file. */ static int kvvfsWriteToDb( KVVfsFile *pFile, @@ -428,7 +536,19 @@ static int kvvfsWriteToDb( int iAmt, sqlite_int64 iOfst ){ - return SQLITE_IOERR; + unsigned int pgno; + char zKey[30]; + char aData[131073]; + assert( iAmt>=512 && iAmt<=65536 ); + assert( (iAmt & (iAmt-1))==0 ); + pgno = 1 + iOfst/iAmt; + sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno) + nData = kvvfsEncode(zBuf, iAmt, aData); + kvstorageWrite(pFile->pVfs->pStore, zKey, aData); + if( iOfst+iAmt > pFile->szDb ){ + pFile->szDb = iOfst + iAmt; + } + return SQLITE_OK; } @@ -454,20 +574,65 @@ static int kvvfsWrite( /* ** Truncate an kvvfs-file. */ -static int kvvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ - int rc; +static int kvvfsTruncate(sqlite3_file *pProtoFile, sqlite_int64 size){ KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - rc = SQLITE_IOERR; - return rc; + if( pFile->isJournal ){ + assert( size==0 ); + kvstorageDelete(pFile->pVfs->pStore, "journal"); + sqlite3_free(pFile->aData); + pFile->aData = 0; + pFile->nData = 0; + return SQLITE_OK; + } + if( pFile->szDb>size + && pFile->szPage>0 + && (size % pFile->szPage)==0 + ){ + char zKey[50]; + unsigned int pgno, pgnoMax; + pgno = 1 + size/pFile->szPage; + pgnoMax = 2 + pFile->szDb/pFile->szPage; + while( pgno<=pgnoMax ){ + sqlite3_snprintf(sizeof(zKey), zKey, "pg-%u", pgno); + kvstorageDelete(pFile->pVfs->pStore, zKey); + pgno++; + } + pFile->szDb = size; + kvvfsWriteFileSize(pFile->pVfs->pStore, size); + return SQLITE_OK; + } + return SQLITE_IOERR; } /* ** Sync an kvvfs-file. */ -static int kvvfsSync(sqlite3_file *pFile, int flags){ - int rc; +static int kvvfsSync(sqlite3_file *pProtoFile, int flags){ + int i, k, n; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - rc = SQLITE_IOERR; + char *zOut; + if( !pFile->isJournal ){ + if( pFile->szDb>0 ){ + kvvfsWriteFileSize(pFile, pFile->szDb); + } + return SQLITE_OK; + } + if( pFile->nJrnl<=0 ){ + return kvvfsTruncate(pProtoFile, 0); + } + zOut = sqlite3_malloc64( pFile->nJrnl*2 + 50 ); + if( zOut==0 ){ + return SQLITE_IOERR_NOMEM; + } + n = pFile->nJrnl; + i = 0; + do{ + zOut[i++] = 'a' + (n%26); + n /= 26; + }while( n>0 ); + kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); + kvstorageWrite(pFile->pVfs->pStore, "journal", zOut); + sqlite3_free(zOut); return rc; } @@ -475,31 +640,37 @@ static int kvvfsSync(sqlite3_file *pFile, int flags){ ** Return the current file-size of an kvvfs-file. */ static int kvvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ - int rc; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - *pSize = 0; - rc = SQLITE_IOERR; - return rc; + if( pFile->isJournal ){ + *pSize = pFile->nJrnl; + }else{ + *pSize = pFile->szDb; + } + return SQLITE_OK; } /* ** Lock an kvvfs-file. */ static int kvvfsLock(sqlite3_file *pFile, int eLock){ - int rc; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - rc = SQLITE_IOERR; - return rc; + assert( !pFile->isJournal ); + if( eLock!=SQLITE_LOCK_NONE ){ + pFile->szDb = kvvfsReadFileSize(pFile); + } + return SQLITE_OK; } /* ** Unlock an kvvfs-file. */ static int kvvfsUnlock(sqlite3_file *pFile, int eLock){ - int rc; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; - rc = SQLITE_IOERR; - return rc; + assert( !pFile->isJournal ); + if( eLock==SQLITE_LOCK_NONE ){ + pFile->szDb = -1; + } + return SQLITE_OK; } /* @@ -509,7 +680,7 @@ static int kvvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){ int rc; KVVfsFile *pFile = (KVVfsFile *)pProtoFile; *pResOut = 0; - rc = SQLITE_IOERR; + rc = SQLITE_OK; return rc; } @@ -527,7 +698,7 @@ static int kvvfsFileControl(sqlite3_file *pFile, int op, void *pArg){ ** Return the sector-size in bytes for an kvvfs-file. */ static int kvvfsSectorSize(sqlite3_file *pFile){ - return 4096; + return 512; } /* @@ -548,12 +719,15 @@ static int kvvfsOpen( int flags, int *pOutFlags ){ - int rc; KVVfsFile *pFile = (KVVfsFile*)pProtoFile; KVVfsVfs *pVfs = (KVVfsVfs*)pProtoVfs; - + pFile->aJrnl = 0; + pFile->nJrnl = 0; + pFile->isJournal = sqlite3_strglob("*-journal", zName)==0; + pFile->szPage = -1; + pFile->szDb = -1; - return rc; + return SQLITE_OK; } /* @@ -574,12 +748,17 @@ static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ ** is available, or false otherwise. */ static int kvvfsAccess( - sqlite3_vfs *pVfs, + sqlite3_vfs *pProtoVfs, const char *zPath, int flags, int *pResOut ){ - *pResOut = 1; + KVVfsVfs *pVfs = (KVVfsVfs*)pProtoVfs; + if( sqlite3_strglob("*-journal", zPath)==0 ){ + *pResOut = kvstorageRead(pVfs->pStore, "journal", 0, 0)>0; + }else{ + *pResOut = 1; + } return SQLITE_OK; } diff --git a/manifest b/manifest index dfddb1fb37..f872ac3349 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Non-working\scode\stowards\sa\sVFS\sfor\stext\skey/value\sstorage. -D 2022-09-07T17:29:22.881 +C Code\ssnapshot.\s\sCompletely\suntested.\s\sProbably\sdoes\snot\scompile. +D 2022-09-08T15:48:01.413 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -335,7 +335,7 @@ F ext/misc/uint.c 053fed3bce2e89583afcd4bf804d75d659879bbcedac74d0fa9ed548839a03 F ext/misc/unionvtab.c 36237f0607ca954ac13a4a0e2d2ac40c33bc6e032a5f55f431713061ef1625f9 F ext/misc/urifuncs.c f71360d14fa9e7626b563f1f781c6148109462741c5235ac63ae0f8917b9c751 F ext/misc/uuid.c 5bb2264c1b64d163efa46509544fd7500cb8769cb7c16dd52052da8d961505cf -F ext/misc/vfskv.c aebaf8b59b70a066c2c0ba9344db0494ff50452ff6b4e79c9133e7a2ea112a99 +F ext/misc/vfskv.c cb82423dcc37af07953d71f15518e8a75d81b2dbab45dc237e181c5b03373703 F ext/misc/vfslog.c 3932ab932eeb2601dbc4447cb14d445aaa9fbe43b863ef5f014401c3420afd20 F ext/misc/vfsstat.c 474d08efc697b8eba300082cb1eb74a5f0f3df31ed257db1cb07e72ab0e53dfb F ext/misc/vtablog.c 5538acd0c8ddaae372331bee11608d76973436b77d6a91e8635cfc9432fba5ae @@ -2001,11 +2001,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 6b00ecb59fd303f7985902c35a46db9e729201d4beaedea46596b728d9e4b1c8 -R 26f26a2d9ce78c288f974e48234fd3a7 -T *branch * kv-vfs -T *sym-kv-vfs * -T -sym-trunk * +P f9c89ee8d5ef46342bea78fa8d4da058d9ea628183ec985642bbda1cdfeacd5f +R fed5a09fb1667ca82dde0796f5fd6c9d U drh -Z 68d678ebfc334c4be8e66c0fb69610ed +Z ee96129288f9f67591a72cc787991652 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index d9d59da7ca..8e900ee9c5 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f9c89ee8d5ef46342bea78fa8d4da058d9ea628183ec985642bbda1cdfeacd5f \ No newline at end of file +51e267696d25f2f83b5bb847d5254ab15573ef1a16ea3d7dd8279c73c0bee539 \ No newline at end of file