diff --git a/Makefile.in b/Makefile.in index 2c305057e6..bb22039e8f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -184,7 +184,7 @@ LIBOBJS0 = alter.lo analyze.lo attach.lo auth.lo \ main.lo malloc.lo mem0.lo mem1.lo mem2.lo mem3.lo mem5.lo \ memdb.lo memjournal.lo \ mutex.lo mutex_noop.lo mutex_unix.lo mutex_w32.lo \ - notify.lo opcodes.lo os.lo os_unix.lo os_win.lo \ + notify.lo opcodes.lo os.lo os_kv.lo os_unix.lo os_win.lo \ pager.lo parse.lo pcache.lo pcache1.lo pragma.lo prepare.lo printf.lo \ random.lo resolve.lo rowset.lo rtree.lo \ sqlite3session.lo select.lo sqlite3rbu.lo status.lo stmt.lo \ @@ -257,6 +257,7 @@ SRC = \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ $(TOP)/src/os_setup.h \ + $(TOP)/src/os_kv.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ $(TOP)/src/os_win.h \ @@ -492,6 +493,7 @@ TESTSRC2 = \ $(TOP)/src/main.c \ $(TOP)/src/mem5.c \ $(TOP)/src/os.c \ + $(TOP)/src/os_kv.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ $(TOP)/src/pager.c \ @@ -938,6 +940,9 @@ pcache1.lo: $(TOP)/src/pcache1.c $(HDR) $(TOP)/src/pcache.h os.lo: $(TOP)/src/os.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os.c +os_kv.lo: $(TOP)/src/os_kv.c $(HDR) + $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os_kv.c + os_unix.lo: $(TOP)/src/os_unix.c $(HDR) $(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/os_unix.c diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index dccbbbe5d1..fd95b013ca 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -380,6 +380,8 @@ speedtest1: $(speedtest1.js) all: $(speedtest1.js) CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm) # end speedtest1.js +######################################################################## + ######################################################################## # fiddle_remote is the remote destination for the fiddle app. It # must be a [user@]HOST:/path for rsync. @@ -402,3 +404,5 @@ push-fiddle: $(fiddle_files) rsync -va fiddle/ $(fiddle_remote) # end fiddle remote push ######################################################################## + +include kvvfs.make diff --git a/ext/wasm/api/sqlite3-api-cleanup.js b/ext/wasm/api/sqlite3-api-cleanup.js index 01aba213ed..1b57cdc5de 100644 --- a/ext/wasm/api/sqlite3-api-cleanup.js +++ b/ext/wasm/api/sqlite3-api-cleanup.js @@ -39,7 +39,7 @@ if('undefined' !== typeof Module){ // presumably an Emscripten build delete self.sqlite3ApiBootstrap; if(self.location && +self.location.port > 1024){ - console.warn("Installing sqlite3 bits as global S for dev-testing purposes."); + console.warn("Installing sqlite3 bits as global S for local dev/test purposes."); self.S = sqlite3; } diff --git a/ext/wasm/api/sqlite3-api-oo1.js b/ext/wasm/api/sqlite3-api-oo1.js index aafc04a2d5..af179d1fe1 100644 --- a/ext/wasm/api/sqlite3-api-oo1.js +++ b/ext/wasm/api/sqlite3-api-oo1.js @@ -70,6 +70,74 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ const toss3 = (...args)=>{throw new SQLite3Error(...args)}; sqlite3.SQLite3Error = SQLite3Error; + // Documented in DB.checkRc() + const checkSqlite3Rc = function(dbPtr, sqliteResultCode){ + if(sqliteResultCode){ + if(dbPtr instanceof DB) dbPtr = dbPtr.pointer; + throw new SQLite3Error( + "sqlite result code",sqliteResultCode+":", + (dbPtr + ? capi.sqlite3_errmsg(dbPtr) + : capi.sqlite3_errstr(sqliteResultCode)) + ); + } + }; + + /** + A proxy for DB class constructors. It must be called with the + being-construct DB object as its "this". + */ + const dbCtorHelper = function ctor(fn=':memory:', flags='c', vfsName){ + if(!ctor._name2vfs){ + // Map special filenames which we handle here (instead of in C) + // to some helpful metadata... + ctor._name2vfs = Object.create(null); + const isWorkerThread = (self.window===self /*===running in main window*/) + ? false + : (n)=>toss3("The VFS for",n,"is only available in the main window thread.") + ctor._name2vfs[':localStorage:'] = { + vfs: 'kvvfs', + filename: isWorkerThread || (()=>'local') + }; + ctor._name2vfs[':sessionStorage:'] = { + vfs: 'kvvfs', + filename: isWorkerThread || (()=>'session') + }; + } + if('string'!==typeof fn){ + toss3("Invalid filename for DB constructor."); + } + const vfsCheck = ctor._name2vfs[fn]; + if(vfsCheck){ + vfsName = vfsCheck.vfs; + fn = vfsCheck.filename(fn); + } + let ptr, oflags = 0; + if( flags.indexOf('c')>=0 ){ + oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; + } + if( flags.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; + if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY; + oflags |= capi.SQLITE_OPEN_EXRESCODE; + const stack = capi.wasm.scopedAllocPush(); + try { + const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */; + const pVfsName = vfsName ? capi.wasm.scopedAllocCString(vfsName) : 0; + const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, pVfsName); + ptr = capi.wasm.getPtrValue(ppDb); + checkSqlite3Rc(ptr, rc); + }catch( e ){ + if( ptr ) capi.sqlite3_close_v2(ptr); + throw e; + }finally{ + capi.wasm.scopedAllocPop(stack); + } + this.filename = fn; + __ptrMap.set(this, ptr); + __stmtMap.set(this, Object.create(null)); + __udfMap.set(this, Object.create(null)); + }; + /** The DB class provides a high-level OO wrapper around an sqlite3 db handle. @@ -102,42 +170,29 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ "c". These modes are ignored for the special ":memory:" and "" names. - The final argument is currently unimplemented but will eventually - be used to specify an optional sqlite3 VFS implementation name, - as for the final argument to sqlite3_open_v2(). + The final argument is analogous to the final argument of + sqlite3_open_v2(): the name of an sqlite3 VFS. Pass a falsy value, + or not at all, to use the default. If passed a value, it must + be the string name of a VFS For purposes of passing a DB instance to C-style sqlite3 functions, the DB object's read-only `pointer` property holds its `sqlite3*` pointer value. That property can also be used to check whether this DB instance is still open. + + + EXPERIMENTAL: in the main window thread, the filenames + ":localStorage:" and ":sessionStorage:" are special: they cause + the db to use either localStorage or sessionStorage for storing + the database. In this mode, only a single database is permitted + in each storage object. This feature is experimental and subject + to any number of changes (including outright removal). This + support requires a specific build of sqlite3, the existence of + which can be determined at runtime by checking for a non-0 return + value from sqlite3.capi.sqlite3_vfs_find("kvvfs"). */ - const DB = function ctor(fn=':memory:', flags='c', vtab="not yet implemented"){ - if('string'!==typeof fn){ - toss3("Invalid filename for DB constructor."); - } - let ptr, oflags = 0; - if( flags.indexOf('c')>=0 ){ - oflags |= capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; - } - if( flags.indexOf('w')>=0 ) oflags |= capi.SQLITE_OPEN_READWRITE; - if( 0===oflags ) oflags |= capi.SQLITE_OPEN_READONLY; - oflags |= capi.SQLITE_OPEN_EXRESCODE; - const stack = capi.wasm.scopedAllocPush(); - try { - const ppDb = capi.wasm.scopedAllocPtr() /* output (sqlite3**) arg */; - const rc = capi.sqlite3_open_v2(fn, ppDb, oflags, null); - ptr = capi.wasm.getPtrValue(ppDb); - ctor.checkRc(ptr, rc); - }catch( e ){ - if( ptr ) capi.sqlite3_close_v2(ptr); - throw e; - }finally{ - capi.wasm.scopedAllocPop(stack); - } - this.filename = fn; - __ptrMap.set(this, ptr); - __stmtMap.set(this, Object.create(null)); - __udfMap.set(this, Object.create(null)); + const DB = function ctor(fn=':memory:', flags='c', vfsName){ + dbCtorHelper.apply(this, Array.prototype.slice.call(arguments)); }; /** @@ -232,6 +287,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ }else if(args[0] && 'object'===typeof args[0]){ out.opt = args[0]; out.sql = out.opt.sql; + }else if(Array.isArray(args[0])){ + out.sql = args[0]; } break; case 2: @@ -295,17 +352,7 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ non-0 non-error codes need to be checked for in client code where they are expected. */ - DB.checkRc = function(dbPtr, sqliteResultCode){ - if(sqliteResultCode){ - if(dbPtr instanceof DB) dbPtr = dbPtr.pointer; - throw new SQLite3Error( - "sqlite result code",sqliteResultCode+":", - (dbPtr - ? capi.sqlite3_errmsg(dbPtr) - : capi.sqlite3_errstr(sqliteResultCode)) - ); - } - }; + DB.checkRc = checkSqlite3Rc; DB.prototype = { /** @@ -1532,5 +1579,19 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){ DB, Stmt }/*oo1 object*/; + + if( self.window===self && 0!==capi.sqlite3_vfs_find('kvvfs') ){ + /* In the main window thread, add a couple of convenience proxies + for localStorage and sessionStorage DBs... */ + let klass = sqlite3.oo1.LocalStorageDb = function(){ + dbCtorHelper.call(this, 'local', 'c', 'kvvfs'); + }; + klass.prototype = DB.prototype; + + klass = sqlite3.oo1.SessionStorageDb = function(){ + dbCtorHelper.call(this, 'session', 'c', 'kvvfs'); + }; + klass.prototype = DB.prototype; + } }); diff --git a/ext/wasm/api/sqlite3-api-prologue.js b/ext/wasm/api/sqlite3-api-prologue.js index 7959d047c9..17dcd42289 100644 --- a/ext/wasm/api/sqlite3-api-prologue.js +++ b/ext/wasm/api/sqlite3-api-prologue.js @@ -713,7 +713,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( return __persistentDir = ""; } try{ - if(pdir && 0===this.wasm.xCallWrapped( + if(pdir && 0===capi.wasm.xCallWrapped( 'sqlite3_wasm_init_opfs', 'i32', ['string'], pdir )){ /** OPFS does not support locking and will trigger errors if @@ -736,7 +736,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( // sqlite3_wasm_init_opfs() is not available return __persistentDir = ""; } - }.bind(capi); + }; /** Returns true if sqlite3.capi.sqlite3_web_persistent_dir() is a @@ -744,9 +744,15 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap( prefix, else returns false. */ capi.sqlite3_web_filename_is_persistent = function(name){ - const p = this.sqlite3_web_persistent_dir(); + const p = capi.sqlite3_web_persistent_dir(); return (p && name) ? name.startsWith(p) : false; - }.bind(capi); + }; + + if(0===capi.wasm.exports.sqlite3_vfs_find(0)){ + /* Assume that sqlite3_initialize() has not yet been called. + This will be the case in an SQLITE_OS_KV build. */ + capi.wasm.exports.sqlite3_initialize(); + } /* The remainder of the API will be set up in later steps. */ const sqlite3 = { diff --git a/ext/wasm/api/sqlite3-wasm.c b/ext/wasm/api/sqlite3-wasm.c index df60a4a24c..2a505f19ab 100644 --- a/ext/wasm/api/sqlite3-wasm.c +++ b/ext/wasm/api/sqlite3-wasm.c @@ -523,226 +523,5 @@ int sqlite3_wasm_init_opfs(void){ } #endif /* __EMSCRIPTEN__ && SQLITE_WASM_OPFS */ -#if defined(__EMSCRIPTEN__) // && defined(SQLITE_OS_KV) -#include -#include - -#ifndef KVSTORAGE_KEY_SZ -/* We can remove this once kvvfs and this bit is merged. */ -# define KVSTORAGE_KEY_SZ 32 -static void kvstorageMakeKey( - const char *zClass, - const char *zKeyIn, - char *zKeyOut -){ - sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn); -} -#endif - -/* -** An internal level of indirection for accessing the static -** kvstorageMakeKey() from EM_JS()-generated functions. This must be -** made available for export via Emscripten but is not intended to be -** used from client code. If called with a NULL zKeyOut it is a no-op. -** It returns KVSTORAGE_KEY_SZ, so JS code (which cannot see that -** constant) may call it with NULL arguments to get the size of the -** allocation they'll need for a kvvfs key. -** -** Maintenance reminder: Emscripten will install this in the Module -** init scope and will prefix its name with "_". -*/ -WASM_KEEP -int sqlite3_wasm__kvvfsMakeKey(const char *zClass, - const char *zKeyIn, - char *zKeyOut){ - if(zKeyOut) kvstorageMakeKey(zClass, zKeyIn, zKeyOut); - return KVSTORAGE_KEY_SZ; -} - -#if 0 -/* -** Alternately, we can implement kvstorageMakeKey() in JS in such a -** way that it's visible to kvstorageWrite/Delete/Read() but not the -** rest of the world. This impl is considerably more verbose than the -** C impl because writing directly to memory requires more code in -** JS. Though more verbose, this approach enables removal of -** sqlite3_wasm__kvvfsMakeKey(). The only catch is that the -** KVSTORAGE_KEY_SZ constant has to be hard-coded into this function. -*/ -EM_JS(void, kvstorageMakeKeyJS, - (const char *zClass, const char *zKeyIn, char *zKeyOut),{ - const max = 32; - if(!arguments.length) return max; - let n = 0, i = 0, ch = 0; - // Write key prefix to dest... - if(0){ - const prefix = "kvvfs-"; - for(i in prefix) setValue(zKeyOut+(n++), prefix.charCodeAt(i)); - }else{ - // slightly optimized but less readable... - setValue(zKeyOut + (n++), 107/*'k'*/); - setValue(zKeyOut + (n++), 118/*'v'*/); - setValue(zKeyOut + (n++), 118/*'v'*/); - setValue(zKeyOut + (n++), 102/*'f'*/); - setValue(zKeyOut + (n++), 115/*'s'*/); - setValue(zKeyOut + (n++), 45/*'-'*/); - } - // Write zClass to dest... - for(i = 0; n < max && (ch = getValue(zClass+i)); ++n, ++i){ - setValue(zKeyOut + n, ch); - } - // Write "-" separator to dest... - if(n nV + 1) nBuf = nV + 1; - HEAPU8.copyWithin(zBuf, zV, zV + nBuf - 1); - setValue( zBuf + nBuf - 1, 0 ); - return nBuf - 1; - }catch(e){ - console.error("kvstorageRead()",e); - return -2; - }finally{ - stackRestore(stack); - } -}); - -/* -** This function exists for (1) WASM testing purposes and (2) as a -** hook to get Emscripten to export several EM_JS()-generated -** functions. It is not part of the public API and its signature -** and semantics may change at any time. -*/ -WASM_KEEP -int sqlite3_wasm__emjs_keep(int whichOp){ - int rc = 0; - const char * zClass = "session"; - const char * zKey = "hello"; - switch( whichOp ){ - case 0: break; - case 1: - kvstorageWrite(zClass, zKey, "world"); - break; - case 2: { - char buffer[128] = {0}; - char * zBuf = &buffer[0]; - rc = kvstorageRead(zClass, zKey, zBuf, (int)sizeof(buffer)); - emscripten_console_logf("kvstorageRead()=%d %s\n", rc, zBuf); - break; - } - case 3: - kvstorageDelete(zClass, zKey); - break; - case 4: - kvstorageMakeKeyOnJSStack(0,0) /* force Emscripten to include this */; - break; - default: break; - } - return rc; -} -#endif /* ifdef __EMSCRIPTEN__ (kvvfs method impls) */ #undef WASM_KEEP diff --git a/ext/wasm/index.html b/ext/wasm/index.html index def70cce03..435040434f 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -39,6 +39,8 @@
  • speedtest1: a main-thread WASM build of speedtest1.
  • speedtest1-worker: an interactive Worker-thread variant of speedtest1.
  • demo-oo1: demonstration of the OO API #1.
  • +
  • kvvfs1: very basic demo of using the key-value vfs for storing + a persistent db in JS localStorage or sessionStorage.
  • diff --git a/ext/wasm/kvvfs.make b/ext/wasm/kvvfs.make new file mode 100644 index 0000000000..a65f2faf26 --- /dev/null +++ b/ext/wasm/kvvfs.make @@ -0,0 +1,84 @@ +#!/usr/bin/make +#^^^^ help emacs select makefile mode +# +# This is a sub-make for building a standalone kvvfs-based +# sqlite3.wasm. It is intended to be "include"d from the main +# GNUMakefile. +# +# Notable potential TODOs: +# +# - Trim down a custom sqlite3-api.js for this build. We can elimate +# the jaccwabyt dependency, for example, because this build won't +# make use of the VFS bits. Similarly, we can eliminate or replace +# parts of the OO1 API, or provide a related API which manages +# singletons of the localStorage/sessionStorage instances. +# +######################################################################## +MAKEFILE.kvvfs := $(lastword $(MAKEFILE_LIST)) + +kvvfs.js := sqlite3-kvvfs.js +kvvfs.wasm := sqlite3-kvvfs.wasm +kvvfs.wasm.c := $(dir.api)/sqlite3-wasm.c + +CLEAN_FILES += $(kvvfs.js) $(kvvfs.wasm) + +######################################################################## +# emcc flags for .c/.o/.wasm. +kvvfs.flags = +#kvvfs.flags += -v # _very_ loud but also informative about what it's doing + +######################################################################## +# emcc flags for .c/.o. +kvvfs.cflags := +kvvfs.cflags += -std=c99 -fPIC -g +kvvfs.cflags += -I. -I$(dir.top) +kvvfs.cflags += -DSQLITE_OS_KV=1 $(SQLITE_OPT) + +######################################################################## +# emcc flags specific to building the final .js/.wasm file... +kvvfs.jsflags := -fPIC +kvvfs.jsflags += --no-entry +kvvfs.jsflags += --minify 0 +kvvfs.jsflags += -sENVIRONMENT=web +kvvfs.jsflags += -sMODULARIZE +kvvfs.jsflags += -sSTRICT_JS +kvvfs.jsflags += -sDYNAMIC_EXECUTION=0 +kvvfs.jsflags += -sNO_POLYFILL +kvvfs.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api +kvvfs.jsflags += -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack + # wasmMemory ==> for -sIMPORTED_MEMORY + # allocateUTF8OnStack ==> kvvfs internals +kvvfs.jsflags += -sUSE_CLOSURE_COMPILER=0 +kvvfs.jsflags += -sIMPORTED_MEMORY +#kvvfs.jsflags += -sINITIAL_MEMORY=13107200 +#kvvfs.jsflags += -sTOTAL_STACK=4194304 +kvvfs.jsflags += -sEXPORT_NAME=sqlite3InitModule +kvvfs.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr. +kvvfs.jsflags += --post-js=$(post-js.js) +#kvvfs.jsflags += -sFILESYSTEM=0 # only for experimentation. sqlite3 needs the FS API +# Perhaps the kvvfs build doesn't? +#kvvfs.jsflags += -sABORTING_MALLOC +kvvfs.jsflags += -sALLOW_MEMORY_GROWTH +kvvfs.jsflags += -sALLOW_TABLE_GROWTH +kvvfs.jsflags += -Wno-limited-postlink-optimizations +# ^^^^^ it likes to warn when we have "limited optimizations" via the -g3 flag. +kvvfs.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=0 +kvvfs.jsflags += -sLLD_REPORT_UNDEFINED +#kvvfs.jsflags += --import-undefined +kvvfs.jsflags += -sMEMORY64=0 +ifneq (0,$(enable_bigint)) +kvvfs.jsflags += -sWASM_BIGINT +endif + +$(kvvfs.js): $(MAKEFILE) $(MAKEFILE.kvvfs) $(kvvfs.wasm.c) \ + EXPORTED_FUNCTIONS.api \ + $(post-js.js) + $(emcc.bin) -o $@ $(emcc_opt) $(emcc.flags) $(kvvfs.cflags) $(kvvfs.jsflags) $(kvvfs.wasm.c) + chmod -x $(kvvfs.wasm) +ifneq (,$(wasm-strip)) + $(wasm-strip) $(kvvfs.wasm) +endif + @ls -la $@ $(kvvfs.wasm) + +kvvfs: $(kvvfs.js) +all: kvvfs diff --git a/ext/wasm/kvvfs1.html b/ext/wasm/kvvfs1.html new file mode 100644 index 0000000000..773de0a603 --- /dev/null +++ b/ext/wasm/kvvfs1.html @@ -0,0 +1,43 @@ + + + + + + + + + sqlite3-kvvfs.js tests + + +
    sqlite3-kvvfs.js tests
    + +
    +
    +
    Initializing app...
    +
    + On a slow internet connection this may take a moment. If this + message displays for "a long time", intialization may have + failed and the JavaScript console may contain clues as to why. +
    +
    +
    Downloading...
    +
    + +
    +
    Everything on this page happens in the dev console. TODOs for this demo include, + but are not necessarily limited to: + +
      +
    • UI controls to switch between localStorage and sessionStorage
    • +
    • Button to clear storage.
    • +
    • Button to dump the current db contents.
    • + +
    +
    +
    +
    + + + + + diff --git a/ext/wasm/kvvfs1.js b/ext/wasm/kvvfs1.js new file mode 100644 index 0000000000..169fcc8bdf --- /dev/null +++ b/ext/wasm/kvvfs1.js @@ -0,0 +1,73 @@ +/* + 2022-09-12 + + The author disclaims copyright to this source code. In place of a + legal notice, here is a blessing: + + * May you do good and not evil. + * May you find forgiveness for yourself and forgive others. + * May you share freely, never taking more than you give. + + *********************************************************************** + + A basic test script for sqlite3-kvvfs.wasm. This file must be run in + main JS thread and sqlite3-kvvfs.js must have been loaded before it. +*/ +'use strict'; +(function(){ + const T = self.SqliteTestUtil; + const toss = function(...args){throw new Error(args.join(' '))}; + const debug = console.debug.bind(console); + const eOutput = document.querySelector('#test-output'); + const log = console.log.bind(console) + const logHtml = function(...args){ + log.apply(this, args); + const ln = document.createElement('div'); + ln.append(document.createTextNode(args.join(' '))); + eOutput.append(ln); + }; + + const runTests = function(Module){ + //log("Module",Module); + const sqlite3 = Module.sqlite3, + capi = sqlite3.capi, + oo = sqlite3.oo1, + wasm = capi.wasm; + log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); + log("Build options:",wasm.compileOptionUsed()); + T.assert( 0 !== capi.sqlite3_vfs_find(null) ); + + const dbStorage = 1 ? ':sessionStorage:' : ':localStorage:'; + /** + The names ':sessionStorage:' and ':localStorage:' are handled + via the DB class constructor, not the C level. In the C API, + the names "local" and "session" are the current (2022-09-12) + names for those keys, but that is subject to change. + */ + const db = new oo.DB( dbStorage ); + log("Storage backend:",db.filename /* note that the name was internally translated */); + try { + db.exec("create table if not exists t(a)"); + if(undefined===db.selectValue("select a from t limit 1")){ + log("New db. Populating. This DB will persist across page reloads."); + db.exec("insert into t(a) values(1),(2),(3)"); + }else{ + log("Found existing table data:"); + db.exec({ + sql: "select * from t order by a", + rowMode: 0, + callback: function(v){log(v)} + }); + } + }finally{ + db.close(); + } + log("End of demo."); + }; + + sqlite3InitModule(self.sqlite3TestModule).then(function(theModule){ + console.warn("Installing Emscripten module as global EM for dev console access."); + self.EM = theModule; + runTests(theModule); + }); +})(); diff --git a/main.mk b/main.mk index 3d8a07494d..49f4fbf90e 100644 --- a/main.mk +++ b/main.mk @@ -68,7 +68,7 @@ LIBOBJ+= vdbe.o parse.o \ main.o malloc.o mem0.o mem1.o mem2.o mem3.o mem5.o \ memdb.o memjournal.o \ mutex.o mutex_noop.o mutex_unix.o mutex_w32.o \ - notify.o opcodes.o os.o os_unix.o os_win.o \ + notify.o opcodes.o os.o os_kv.o os_unix.o os_win.o \ pager.o pcache.o pcache1.o pragma.o prepare.o printf.o \ random.o resolve.o rowset.o rtree.o \ select.o sqlite3rbu.o status.o stmt.o \ @@ -134,6 +134,7 @@ SRC = \ $(TOP)/src/os.h \ $(TOP)/src/os_common.h \ $(TOP)/src/os_setup.h \ + $(TOP)/src/os_kv.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ $(TOP)/src/os_win.h \ @@ -411,6 +412,7 @@ TESTSRC2 = \ $(TOP)/src/main.c \ $(TOP)/src/mem5.c \ $(TOP)/src/os.c \ + $(TOP)/src/os_kv.c \ $(TOP)/src/os_unix.c \ $(TOP)/src/os_win.c \ $(TOP)/src/pager.c \ diff --git a/manifest b/manifest index cdac3ba49c..c182819a97 100644 --- a/manifest +++ b/manifest @@ -1,9 +1,9 @@ -C Minor\scleanups\sand\sdocumentation\sin\sthe\swasm\spieces. -D 2022-09-11T16:59:40.674 +C Merge\skv-vfs\sbranch\sinto\sfiddle-opfs\sbranch\sto\sadd\skvvfs-based\swasm\sbuild\sand\sdemo. +D 2022-09-12T16:09:50.625 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 -F Makefile.in 918d18842ba16dac5ebd5f7913aa8fb4f39879c881deced83362ed44a14ab4c6 +F Makefile.in 50e421194df031f669667fdb238c54959ecbea5a0b97dd3ed776cffbeea926d5 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241 F Makefile.msc d547a2fdba38a1c6cd1954977d0b0cc017f5f8fbfbc65287bf8d335808938016 F README.md 8b8df9ca852aeac4864eb1e400002633ee6db84065bd01b78c33817f97d31f5e @@ -474,21 +474,21 @@ F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04 F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865f002fc80cb F ext/wasm/EXPORTED_FUNCTIONS.fiddle db7a4602f043cf4a5e4135be3609a487f9f1c83f05778bfbdf93766be4541b96 F ext/wasm/EXPORTED_RUNTIME_METHODS.fiddle a004bd5eeeda6d3b28d16779b7f1a80305bfe009dfc7f0721b042967f0d39d02 -F ext/wasm/GNUmakefile 18b80a063c684b0ca44b96221fc2c23647d0196f757c83c7a572b853562d8ac9 +F ext/wasm/GNUmakefile 6e642a0dc7ac43d9287e8f31c80ead469ddc7475a6d4ab7ac3b1feefcd4f7279 F ext/wasm/README.md e1ee1e7c321c6a250bf78a84ca6f5882890a237a450ba5a0649c7a8399194c52 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-api 1dfd067b3cbd9a49cb204097367cf2f8fe71b5a3b245d9d82a24779fd4ac2394 F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md d876597edd2b9542b6ea031adaaff1c042076fde7b670b1dc6d8a87b28a6631b F ext/wasm/api/post-js-footer.js b64319261d920211b8700004d08b956a6c285f3b0bba81456260a713ed04900c F ext/wasm/api/post-js-header.js 0e853b78db83cb1c06b01663549e0e8b4f377f12f5a2d9a4a06cb776c003880b -F ext/wasm/api/sqlite3-api-cleanup.js 1a12e64060c2cb0defd34656a76a9b1d7ed58459c290249bb31567c806fd44de +F ext/wasm/api/sqlite3-api-cleanup.js 101919ec261644e2f6f0a59952fd9612127b69ea99b493277b2789ea478f9b6b F ext/wasm/api/sqlite3-api-glue.js 2bf536a38cde324cf352bc2c575f8e22c6d204d667c0eda5a254ba45318914bc -F ext/wasm/api/sqlite3-api-oo1.js b06a1ac982c7d433396b8304550ce1493a63671a3bf653c3b5646a9075e0ca41 +F ext/wasm/api/sqlite3-api-oo1.js a9d8892be246548a9978ace506d108954aa13eb5ce25332975c8377953804ff3 F ext/wasm/api/sqlite3-api-opfs.js 011799db398157cbd254264b6ebae00d7234b93d0e9e810345f213a5774993c0 -F ext/wasm/api/sqlite3-api-prologue.js afd753be49ecba4b5bfc3e54929826a01eaf96ac2d8cec575d683e3c5bcc8464 +F ext/wasm/api/sqlite3-api-prologue.js 9e37ce4dfd74926d0df80dd7e72e33085db4bcee48e2c21236039be416a7dff2 F ext/wasm/api/sqlite3-api-worker1.js d33062afa045fd4be01ba4abc266801807472558b862b30056211b00c9c347b4 F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9 -F ext/wasm/api/sqlite3-wasm.c ab8abf26708238a17840cad224bec1511efde6e5b182068f3abf079ba1d83ef6 +F ext/wasm/api/sqlite3-wasm.c bf4637cf28463cada4b25f09651943c7ece004b253ef39b7ab68eaa60662aa09 F ext/wasm/batch-runner.html 23209ade7981acce7ecd79d6eff9f4c5a4e8b14ae867ac27cd89b230be640fa6 F ext/wasm/batch-runner.js 2abd146d3e3a66128ac0a2cc39bfd01e9811c9511fa10ec927d6649795f1ee50 F ext/wasm/common/SqliteTestUtil.js 529161a624265ba84271a52db58da022649832fa1c71309fb1e02cc037327a2b @@ -501,11 +501,14 @@ F ext/wasm/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d695 F ext/wasm/fiddle/fiddle-worker.js bccf46045be8824752876f3eec01c223be0616ccac184bffd0024cfe7a3262b8 F ext/wasm/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08 F ext/wasm/fiddle/fiddle.js 4ffcfc9a235beebaddec689a549e9e0dfad6dca5c1f0b41f03468d7e76480686 -F ext/wasm/index.html 4f635f986dbc7518280abe0ef537ba41682e35f160fac35a0745cf6c4d223b62 +F ext/wasm/index.html 5876ae0442bef5b37fec9a45ee0722798e47ef727723ada742d33554845afa6a F ext/wasm/jaccwabyt/jaccwabyt.js 0d7f32817456a0f3937fcfd934afeb32154ca33580ab264dab6c285e6dbbd215 F ext/wasm/jaccwabyt/jaccwabyt.md 447cc02b598f7792edaa8ae6853a7847b8178a18ed356afacbdbf312b2588106 F ext/wasm/jaccwabyt/jaccwabyt_test.c 39e4b865a33548f943e2eb9dd0dc8d619a80de05d5300668e9960fff30d0d36f F ext/wasm/jaccwabyt/jaccwabyt_test.exports 5ff001ef975c426ffe88d7d8a6e96ec725e568d2c2307c416902059339c06f19 +F ext/wasm/kvvfs.make dba616578bf91a76370a46494dd68a09c6dff5beb6d5561e2db65a27216e9630 +F ext/wasm/kvvfs1.html b8304cd5c7e7ec32c3b15521a95c322d6efdb8d22b3c4156123545dc54e07583 +F ext/wasm/kvvfs1.js a5075f98ffecd7d32348697db991fc61342d89aa20651034d1572af61890fb8b F ext/wasm/scratchpad-opfs-main.html 4565cf194e66188190d35f70e82553e2e2d72b9809b73c94ab67b8cfd14d2e0c F ext/wasm/scratchpad-opfs-main.js 69e960e9161f6412fd0c30f355d4112f1894d6609eb431e2d16d207d1380518e F ext/wasm/scratchpad-opfs-worker.html 66c1d15d678f3bd306373d76b61c6c8aef988f61f4a8dd40185d452f9c6d2bf5 @@ -528,7 +531,7 @@ F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 20801eed419dc58936ff9449b04041edbbbc0488a9fc683e72471dded050e0bb +F main.mk b1b02927b2e0b73d025f93e388f2f7fba07614e972453983bddc65cd2bf33bb1 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -571,7 +574,7 @@ F src/insert.c aea5361767817f917b0f0f647a1f0b1621bd858938ae6ae545c3b6b9814b798f F src/json.c 7749b98c62f691697c7ee536b570c744c0583cab4a89200fdd0fc2aa8cc8cbd6 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa F src/loadext.c 853385cc7a604157e137585097949252d5d0c731768e16b044608e5c95c3614b -F src/main.c 8983b4a316d7e09946dd731913aa41712f02e2b55cb5c6c92126ccfe2473244a +F src/main.c e11267cdd380be68d95d4292666636a7f1dff5f17a395f3d55be910d7e9350fb F src/malloc.c b7a3430cbe91d3e8e04fc10c2041b3a19794e63556ad2441a13d8dadd9b2bafc F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de @@ -590,8 +593,9 @@ F src/notify.c 89a97dc854c3aa62ad5f384ef50c5a4a11d70fcc69f86de3e991573421130ed6 F src/os.c 0eb831ba3575af5277e47f4edd14fdfc90025c67eb25ce5cda634518d308d4e9 F src/os.h 1ff5ae51d339d0e30d8a9d814f4b8f8e448169304d83a7ed9db66a65732f3e63 F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85 -F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586 -F src/os_unix.c 102f7e5c5b59c18ea3dbc929dc3be8acb3afc0e0b6ad572e032335c9c27f44f1 +F src/os_kv.c a188e92dac693b1c1b512d93b0c4dc85c1baad11e322b01121f87057996e4d11 +F src/os_setup.h 0711dbc4678f3ac52d7fe736951b6384a0615387c4ba5135a4764e4e31f4b6a6 +F src/os_unix.c d6322b78130d995160bb9cfb7850678ad6838b08c1d13915461b33326a406c04 F src/os_win.c e9454cb141908e8eef2102180bad353a36480612d5b736e4c2bd5777d9b25a34 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/pager.c 6176d9752eb580419e8fef4592dc417a6b00ddfd43ee22f818819bf8840ceee8 @@ -612,7 +616,7 @@ F src/shell.c.in e7e7c2c69ae86c5ee9e8ad66227203d46ff6dce8700a1b1dababff01c71d33d F src/sqlite.h.in b9b7fd73239d94db20332bb6e504688001e5564b655e1318a4427a1caef4b99e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d -F src/sqliteInt.h 94e7fc2a5f0fa5d1f0af84513fd2d1c70a9f6e772556b9dfef16feee63291eae +F src/sqliteInt.h 44eb45c25d379361ee1bd7fc49205471c6cb8a3727aed42372cf4716f653ebf2 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -1956,7 +1960,7 @@ F tool/mkshellc.tcl df5d249617f9cc94d5c48eb0401673eb3f31f383ecbc54e8a13ca3dd97e8 F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f -F tool/mksqlite3c.tcl eee7e9d9c58abb1045f6ed74ad95ad26e8d22be29fdc431deea5267fb3fa049c +F tool/mksqlite3c.tcl 4fc26a9bfa0c4505b203d7ca0cf086e75ebcd4d63eb719c82f37e3fecdf23d37 F tool/mksqlite3h.tcl 1f5e4a1dbbbc43c83cc6e74fe32c6c620502240b66c7c0f33a51378e78fc4edf F tool/mksqlite3internalh.tcl eb994013e833359137eb53a55acdad0b5ae1049b F tool/mkvsix.tcl b9e0777a213c23156b6542842c238479e496ebf5 @@ -2019,8 +2023,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 cdbf09fa1b0c93aeb3222a157de33a4688ae629c2b829ffff0f1f62364c5ae1c -R 9d278bdeb3b04c59e81ef94ad61886ea +P 4e6ce329872eb733ba2f7f7879747c52761ae97790fd8ed169a25a79854cc3d9 e49682c5eac91958f143e639c5656ca54560d14f5805d514bf4aa0c206e63844 +R f15733833cf5e73dce86b4365f218581 U stephan -Z 1e71f5bdda12caaeb1881e6b3d2fda00 +Z f2a01bf4c99986993a2e00b39e93be73 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 0742ec8643..bde2102af8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4e6ce329872eb733ba2f7f7879747c52761ae97790fd8ed169a25a79854cc3d9 \ No newline at end of file +a7d8b26acd3c1ae344369e4d70804c0cab45272c0983cfd32d616a0a7b28acb9 \ No newline at end of file diff --git a/src/main.c b/src/main.c index b5ef703287..efa5fc3681 100644 --- a/src/main.c +++ b/src/main.c @@ -4781,8 +4781,8 @@ int sqlite3_snapshot_open( */ int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){ int rc = SQLITE_ERROR; - int iDb; #ifndef SQLITE_OMIT_WAL + int iDb; #ifdef SQLITE_ENABLE_API_ARMOR if( !sqlite3SafetyCheckOk(db) ){ diff --git a/src/os_kv.c b/src/os_kv.c new file mode 100644 index 0000000000..a14dc5c543 --- /dev/null +++ b/src/os_kv.c @@ -0,0 +1,1077 @@ +/* +** 2022-09-06 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains an experimental VFS layer that operates on a +** Key/Value storage engine where both keys and values must be pure +** text. +*/ +#include +#if SQLITE_OS_KV + +/***************************************************************************** +** Debugging logic +*/ + +/* SQLITE_KV_TRACE() is used for tracing calls to kvstorage routines. */ +#if 0 +#define SQLITE_KV_TRACE(X) printf X; +#else +#define SQLITE_KV_TRACE(X) +#endif + +/* SQLITE_KV_LOG() is used for tracing calls to the VFS interface */ +#if 0 +#define SQLITE_KV_LOG(X) printf X; +#else +#define SQLITE_KV_LOG(X) +#endif + + +/* +** Forward declaration of objects used by this VFS implementation +*/ +typedef struct KVVfsFile KVVfsFile; + +/* 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 */ + const char *zClass; /* Storage class */ + int isJournal; /* True if this is a journal file */ + 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 */ +}; + +/* +** Methods for KVVfsFile +*/ +static int kvvfsClose(sqlite3_file*); +static int kvvfsReadDb(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); +static int kvvfsReadJrnl(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); +static int kvvfsWriteDb(sqlite3_file*,const void*,int iAmt, sqlite3_int64); +static int kvvfsWriteJrnl(sqlite3_file*,const void*,int iAmt, sqlite3_int64); +static int kvvfsTruncateDb(sqlite3_file*, sqlite3_int64 size); +static int kvvfsTruncateJrnl(sqlite3_file*, sqlite3_int64 size); +static int kvvfsSyncDb(sqlite3_file*, int flags); +static int kvvfsSyncJrnl(sqlite3_file*, int flags); +static int kvvfsFileSizeDb(sqlite3_file*, sqlite3_int64 *pSize); +static int kvvfsFileSizeJrnl(sqlite3_file*, sqlite3_int64 *pSize); +static int kvvfsLock(sqlite3_file*, int); +static int kvvfsUnlock(sqlite3_file*, int); +static int kvvfsCheckReservedLock(sqlite3_file*, int *pResOut); +static int kvvfsFileControl(sqlite3_file*, int op, void *pArg); +static int kvvfsSectorSize(sqlite3_file*); +static int kvvfsDeviceCharacteristics(sqlite3_file*); + +/* +** Methods for sqlite3_vfs +*/ +static int kvvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); +static int kvvfsDelete(sqlite3_vfs*, const char *zName, int syncDir); +static int kvvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *); +static int kvvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut); +static void *kvvfsDlOpen(sqlite3_vfs*, const char *zFilename); +static int kvvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); +static int kvvfsSleep(sqlite3_vfs*, int microseconds); +static int kvvfsCurrentTime(sqlite3_vfs*, double*); +static int kvvfsCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*); + +static sqlite3_vfs kvvfs_vfs = { + 1, /* iVersion */ + sizeof(KVVfsFile), /* szOsFile */ + 1024, /* mxPathname */ + 0, /* pNext */ + "kvvfs", /* zName */ + 0, /* pAppData */ + kvvfsOpen, /* xOpen */ + kvvfsDelete, /* xDelete */ + kvvfsAccess, /* xAccess */ + kvvfsFullPathname, /* xFullPathname */ + kvvfsDlOpen, /* xDlOpen */ + 0, /* xDlError */ + 0, /* xDlSym */ + 0, /* xDlClose */ + kvvfsRandomness, /* xRandomness */ + kvvfsSleep, /* xSleep */ + kvvfsCurrentTime, /* xCurrentTime */ + 0, /* xGetLastError */ + kvvfsCurrentTimeInt64 /* xCurrentTimeInt64 */ +}; + +/* Methods for sqlite3_file objects referencing a database file +*/ +static sqlite3_io_methods kvvfs_db_io_methods = { + 1, /* iVersion */ + kvvfsClose, /* xClose */ + kvvfsReadDb, /* xRead */ + kvvfsWriteDb, /* xWrite */ + kvvfsTruncateDb, /* xTruncate */ + kvvfsSyncDb, /* xSync */ + kvvfsFileSizeDb, /* xFileSize */ + kvvfsLock, /* xLock */ + kvvfsUnlock, /* xUnlock */ + kvvfsCheckReservedLock, /* xCheckReservedLock */ + kvvfsFileControl, /* xFileControl */ + kvvfsSectorSize, /* xSectorSize */ + kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmMap */ + 0, /* xShmLock */ + 0, /* xShmBarrier */ + 0, /* xShmUnmap */ + 0, /* xFetch */ + 0 /* xUnfetch */ +}; + +/* Methods for sqlite3_file objects referencing a rollback journal +*/ +static sqlite3_io_methods kvvfs_jrnl_io_methods = { + 1, /* iVersion */ + kvvfsClose, /* xClose */ + kvvfsReadJrnl, /* xRead */ + kvvfsWriteJrnl, /* xWrite */ + kvvfsTruncateJrnl, /* xTruncate */ + kvvfsSyncJrnl, /* xSync */ + kvvfsFileSizeJrnl, /* xFileSize */ + kvvfsLock, /* xLock */ + kvvfsUnlock, /* xUnlock */ + kvvfsCheckReservedLock, /* xCheckReservedLock */ + kvvfsFileControl, /* xFileControl */ + kvvfsSectorSize, /* xSectorSize */ + kvvfsDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmMap */ + 0, /* xShmLock */ + 0, /* xShmBarrier */ + 0, /* xShmUnmap */ + 0, /* xFetch */ + 0 /* xUnfetch */ +}; + +/****** Storage subsystem **************************************************/ +#include +#include +#include + +/* Forward declarations for the low-level storage engine +*/ +#define KVSTORAGE_KEY_SZ 32 + +/* Expand the key name with an appropriate prefix and put the result +** zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least +** KVSTORAGE_KEY_SZ bytes. +*/ +static void kvstorageMakeKey( + const char *zClass, + const char *zKeyIn, + char *zKeyOut +){ + sqlite3_snprintf(KVSTORAGE_KEY_SZ, zKeyOut, "kvvfs-%s-%s", zClass, zKeyIn); +} + +#ifdef __EMSCRIPTEN__ +/* Provide Emscripten-based impls of kvstorageWrite/Read/Delete()... */ +#include +#include + +/* +** WASM_KEEP is identical to EMSCRIPTEN_KEEPALIVE but is not +** Emscripten-specific. It explicitly includes marked functions for +** export into the target wasm file without requiring explicit listing +** of those functions in Emscripten's -sEXPORTED_FUNCTIONS=... list +** (or equivalent in other build platforms). Any function with neither +** this attribute nor which is listed as an explicit export will not +** be exported from the wasm file (but may still be used internally +** within the wasm file). +** +** The functions in this file (sqlite3-wasm.c) which require exporting +** are marked with this flag. They may also be added to any explicit +** build-time export list but need not be. All of these APIs are +** intended for use only within the project's own JS/WASM code, and +** not by client code, so an argument can be made for reducing their +** visibility by not including them in any build-time export lists. +** +** 2022-09-11: it's not yet _proven_ that this approach works in +** non-Emscripten builds. If not, such builds will need to export +** those using the --export=... wasm-ld flag (or equivalent). As of +** this writing we are tied to Emscripten for various reasons +** and cannot test the library with other build environments. +*/ +#define WASM_KEEP __attribute__((used,visibility("default"))) +/* +** An internal level of indirection for accessing the static +** kvstorageMakeKey() from EM_JS()-generated functions. This must be +** made available for export via Emscripten but is not intended to be +** used from client code. If called with a NULL zKeyOut it is a no-op. +** It returns KVSTORAGE_KEY_SZ, so JS code (which cannot see that +** constant) may call it with NULL arguments to get the size of the +** allocation they'll need for a kvvfs key. +** +** Maintenance reminder: Emscripten will install this in the Module +** init scope and will prefix its name with "_". +*/ +WASM_KEEP +int sqlite3_wasm__kvvfsMakeKey(const char *zClass, const char *zKeyIn, + char *zKeyOut){ + if( 0!=zKeyOut ) kvstorageMakeKey(zClass, zKeyIn, zKeyOut); + return KVSTORAGE_KEY_SZ; +} +/* +** Internal helper for kvstorageWrite/Read/Delete() which creates a +** storage key for the given zClass/zKeyIn combination. Returns a +** pointer to the key: a C string allocated on the WASM stack, or 0 if +** allocation fails. It is up to the caller to save/restore the stack +** before/after this operation. +*/ +EM_JS(const char *, kvstorageMakeKeyOnJSStack, + (const char *zClass, const char *zKeyIn),{ + if( 0==zClass || 0==zKeyIn) return 0; + const zXKey = stackAlloc(_sqlite3_wasm__kvvfsMakeKey(0,0,0)); + if(zXKey) _sqlite3_wasm__kvvfsMakeKey(zClass, zKeyIn, zXKey); + return zXKey; +}); + +/* +** JS impl of kvstorageWrite(). Main docs are in the C impl. This impl +** writes zData to the global sessionStorage (if zClass starts with +** 's') or localStorage, using a storage key derived from zClass and +** zKey. +*/ +EM_JS(int, kvstorageWrite, + (const char *zClass, const char *zKey, const char *zData),{ + const stack = stackSave(); + try { + const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey); + if(!zXKey) return 1/*OOM*/; + const jKey = UTF8ToString(zXKey); + /** + We could simplify this function and eliminate the + kvstorageMakeKey() symbol acrobatics if we'd simply hard-code + the key algo into the 3 functions which need it: + + const jKey = "kvvfs-"+UTF8ToString(zClass)+"-"+UTF8ToString(zKey); + */ + ((115/*=='s'*/===getValue(zClass)) + ? sessionStorage : localStorage).setItem(jKey, UTF8ToString(zData)); + }catch(e){ + console.error("kvstorageWrite()",e); + return 1; // Can't access SQLITE_xxx from here + }finally{ + stackRestore(stack); + } + return 0; +}); + +/* +** JS impl of kvstorageDelete(). Main docs are in the C impl. This +** impl generates a key derived from zClass and zKey, and removes the +** matching entry (if any) from global sessionStorage (if zClass +** starts with 's') or localStorage. +*/ +EM_JS(int, kvstorageDelete, + (const char *zClass, const char *zKey),{ + const stack = stackSave(); + try { + const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey); + if(!zXKey) return 1/*OOM*/; + _sqlite3_wasm__kvvfsMakeKey(zClass, zKey, zXKey); + const jKey = UTF8ToString(zXKey); + ((115/*=='s'*/===getValue(zClass)) + ? sessionStorage : localStorage).removeItem(jKey); + }catch(e){ + console.error("kvstorageDelete()",e); + return 1; + }finally{ + stackRestore(stack); + } + return 0; +}); + +/* +** JS impl of kvstorageRead(). Main docs are in the C impl. This impl +** reads its data from the global sessionStorage (if zClass starts +** with 's') or localStorage, using a storage key derived from zClass +** and zKey. +*/ +EM_JS(int, kvstorageRead, + (const char *zClass, const char *zKey, char *zBuf, int nBuf),{ + const stack = stackSave(); + try { + const zXKey = kvstorageMakeKeyOnJSStack(zClass,zKey); + if(!zXKey) return -3/*OOM*/; + const jKey = UTF8ToString(zXKey); + const jV = ((115/*=='s'*/===getValue(zClass)) + ? sessionStorage : localStorage).getItem(jKey); + if(!jV) return -1; + const nV = jV.length /* Note that we are relying 100% on v being + ASCII so that jV.length is equal to the + C-string's byte length. */; + if(nBuf<=0) return nV; + else if(1===nBuf){ + setValue(zBuf, 0); + return nV; + } + const zV = allocateUTF8OnStack(jV); + if(nBuf > nV + 1) nBuf = nV + 1; + HEAPU8.copyWithin(zBuf, zV, zV + nBuf - 1); + setValue( zBuf + nBuf - 1, 0 ); + return nBuf - 1; + }catch(e){ + console.error("kvstorageRead()",e); + return -2; + }finally{ + stackRestore(stack); + } +}); + +/* +** This function exists for (1) WASM testing purposes and (2) as a +** hook to get Emscripten to export several EM_JS()-generated +** functions (if we don't reference them from exported C functions +** then they get stripped away at build time). It is not part of the +** public API and its signature and semantics may change at any time. +** It's not even part of the private API, for that matter - it's part +** of the Emscripten C/JS/WASM glue. +*/ +WASM_KEEP +int sqlite3__wasm_emjs_kvvfs(int whichOp){ + int rc = 0; + const char * zClass = + "sezzion" /*don't collide with "session" records!*/; + const char * zKey = "hello"; + switch( whichOp ){ + case 0: break; + case 1: + kvstorageWrite(zClass, zKey, "world"); + break; + case 2: { + char buffer[128] = {0}; + char * zBuf = &buffer[0]; + rc = kvstorageRead(zClass, zKey, zBuf, (int)sizeof(buffer)); + emscripten_console_logf("kvstorageRead()=%d %s\n", rc, zBuf); + break; + } + case 3: + kvstorageDelete(zClass, zKey); + break; + case 4: + kvstorageMakeKeyOnJSStack(0,0); + break; + default: break; + } + return rc; +} + +#undef WASM_KEEP +#else /* end ifdef __EMSCRIPTEN__ */ +/* Forward declarations for the low-level storage engine +*/ +static int kvstorageWrite(const char*, const char *zKey, const char *zData); +static int kvstorageDelete(const char*, const char *zKey); +static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf); + +/* Write content into a key. zClass is the particular namespace of the +** underlying key/value store to use - either "local" or "session". +** +** Both zKey and zData are zero-terminated pure text strings. +** +** Return the number of errors. +*/ +static int kvstorageWrite( + const char *zClass, + const char *zKey, + const char *zData +){ + FILE *fd; + char zXKey[KVSTORAGE_KEY_SZ]; + kvstorageMakeKey(zClass, zKey, zXKey); + fd = fopen(zXKey, "wb"); + if( fd ){ + SQLITE_KV_TRACE(("KVVFS-WRITE %-15s (%d) %.50s%s\n", zXKey, + (int)strlen(zData), zData, + strlen(zData)>50 ? "..." : "")); + fputs(zData, fd); + fclose(fd); + return 0; + }else{ + return 1; + } +} + +/* Delete a key (with its corresponding data) from the key/value +** namespace given by zClass. If the key does not previously exist, +** this routine is a no-op. +*/ +static int kvstorageDelete(const char *zClass, const char *zKey){ + char zXKey[KVSTORAGE_KEY_SZ]; + kvstorageMakeKey(zClass, zKey, zXKey); + unlink(zXKey); + SQLITE_KV_TRACE(("KVVFS-DELETE %-15s\n", zXKey)); + return 0; +} + +/* Read the value associated with a zKey from the key/value namespace given +** by zClass and put the text data associated with that key in the first +** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large +** enough to hold it all. The value put into zBuf must always be zero +** terminated, even if it gets truncated because nBuf is not large enough. +** +** Return the total number of bytes in the data, without truncation, and +** not counting the final zero terminator. Return -1 if the key does +** not exist. +** +** If nBuf<=0 then this routine simply returns the size of the data without +** actually reading it. +*/ +static int kvstorageRead( + const char *zClass, + const char *zKey, + char *zBuf, + int nBuf +){ + FILE *fd; + struct stat buf; + char zXKey[KVSTORAGE_KEY_SZ]; + kvstorageMakeKey(zClass, zKey, zXKey); + if( access(zXKey, R_OK)!=0 + || stat(zXKey, &buf)!=0 + || !S_ISREG(buf.st_mode) + ){ + SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); + return -1; + } + if( nBuf<=0 ){ + return (int)buf.st_size; + }else if( nBuf==1 ){ + zBuf[0] = 0; + SQLITE_KV_TRACE(("KVVFS-READ %-15s (%d)\n", zXKey, + (int)buf.st_size)); + return (int)buf.st_size; + } + if( nBuf > buf.st_size + 1 ){ + nBuf = buf.st_size + 1; + } + fd = fopen(zXKey, "rb"); + if( fd==0 ){ + SQLITE_KV_TRACE(("KVVFS-READ %-15s (-1)\n", zXKey)); + return -1; + }else{ + sqlite3_int64 n = fread(zBuf, 1, nBuf-1, fd); + fclose(fd); + zBuf[n] = 0; + SQLITE_KV_TRACE(("KVVFS-READ %-15s (%lld) %.50s%s\n", zXKey, + n, zBuf, n>50 ? "..." : "")); + return (int)n; + } +} +#endif /* ifdef __EMSCRIPTEN__ */ + +/****** Utility subroutines ************************************************/ + +/* +** Encode binary into the text encoded used to persist on disk. +** The output text is stored in aOut[], which must be at least +** nData+1 bytes in length. +** +** Return the actual length of the encoded text, not counting the +** zero terminator at the end. +** +** Encoding format +** --------------- +** +** * Non-zero bytes are encoded as upper-case hexadecimal +** +** * A sequence of one or more zero-bytes that are not at the +** beginning of the buffer are encoded as a little-endian +** base-26 number using a..z. "a" means 0. "b" means 1, +** "z" means 25. "ab" means 26. "ac" means 52. And so forth. +** +** * Because there is no overlap between the encoding characters +** of hexadecimal and base-26 numbers, it is always clear where +** one stops and the next begins. +*/ +static int kvvfsEncode(const char *aData, int nData, char *aOut){ + int i, j; + const unsigned char *a = (const unsigned char*)aData; + for(i=j=0; i>4]; + aOut[j++] = "0123456789ABCDEF"[c&0xf]; + }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 "ab", 27 is "bb", + ** and so forth. + */ + int k; + for(k=1; i+k0 ){ + aOut[j++] = 'a'+(k%26); + k /= 26; + } + } + } + aOut[j] = 0; + return j; +} + +/* Convert hex to binary */ +static char kvvfsHexToBinary(char c){ + if( c>='0' && c<='9' ) return c - '0'; + if( c>='A' && c<='F' ) return c - 'A' + 10; + return 0; +} + +/* +** Decode the text encoding back to binary. The binary content is +** written into pOut, which must be at least nOut bytes in length. +** +** The return value is the number of bytes actually written into aOut[]. +*/ +static int kvvfsDecode(const char *aIn, char *aOut, int nOut){ + int i, j; + int c; + i = 0; + j = 0; + while( (c = aIn[i])!=0 ){ + if( c>='a' ){ + int n = 0; + int mult = 1; + while( c>='a' && c<='z' ){ + n += (c - 'a')*mult; + mult *= 26; + c = aIn[++i]; + } + if( j+n>nOut ) return -1; + while( n-->0 ){ + aOut[j++] = 0; + } + }else{ + if( j>nOut ) return -1; + aOut[j] = kvvfsHexToBinary(aIn[i++])<<4; + aOut[j++] += kvvfsHexToBinary(aIn[i++]); + } + } + 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 little-endian +** base-26 number (digits a..z) that is the total number of bytes +** in the decoded journal file image. This base-26 number is followed +** by a single space, then the encoding of the journal. The space +** separator is required to act as a terminator for the base-26 number. +*/ +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; + } + 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 or write the "sz" element, containing the database file size. +*/ +static sqlite3_int64 kvvfsReadFileSize(KVVfsFile *pFile){ + char zData[50]; + zData[0] = 0; + kvstorageRead(pFile->zClass, "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->zClass, "sz", zData); +} + +/****** sqlite3_io_methods methods ******************************************/ + +/* +** Close an kvvfs-file. +*/ +static int kvvfsClose(sqlite3_file *pProtoFile){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + + SQLITE_KV_LOG(("xClose %s %s\n", pFile->zClass, + pFile->isJournal ? "journal" : "db")); + sqlite3_free(pFile->aJrnl); + return SQLITE_OK; +} + +/* +** Read from the -journal file. +*/ +static int kvvfsReadJrnl( + sqlite3_file *pProtoFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + KVVfsFile *pFile = (KVVfsFile*)pProtoFile; + assert( pFile->isJournal ); + SQLITE_KV_LOG(("xRead('%s-journal',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); + if( pFile->aJrnl==0 ){ + int szTxt = kvstorageRead(pFile->zClass, "jrnl", 0, 0); + char *aTxt; + if( szTxt<=4 ){ + return SQLITE_IOERR; + } + aTxt = sqlite3_malloc64( szTxt+1 ); + if( aTxt==0 ) return SQLITE_NOMEM; + kvstorageRead(pFile->zClass, "jrnl", aTxt, szTxt+1); + kvvfsDecodeJournal(pFile, aTxt, szTxt); + sqlite3_free(aTxt); + if( pFile->aJrnl==0 ) return SQLITE_IOERR; + } + if( iOfst+iAmt>pFile->nJrnl ){ + return SQLITE_IOERR_SHORT_READ; + } + memcpy(zBuf, pFile->aJrnl+iOfst, iAmt); + return SQLITE_OK; +} + +/* +** Read from the database file. +*/ +static int kvvfsReadDb( + sqlite3_file *pProtoFile, + void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + KVVfsFile *pFile = (KVVfsFile*)pProtoFile; + unsigned int pgno; + int got, n; + char zKey[30]; + char aData[131073]; + assert( iOfst>=0 ); + assert( iAmt>=0 ); + SQLITE_KV_LOG(("xRead('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); + if( iOfst+iAmt>=512 ){ + if( (iOfst % iAmt)!=0 ){ + return SQLITE_IOERR_READ; + } + if( (iAmt & (iAmt-1))!=0 || iAmt<512 || iAmt>65536 ){ + return SQLITE_IOERR_READ; + } + pFile->szPage = iAmt; + pgno = 1 + iOfst/iAmt; + }else{ + pgno = 1; + } + sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); + got = kvstorageRead(pFile->zClass, zKey, aData, sizeof(aData)-1); + if( got<0 ){ + n = 0; + }else{ + aData[got] = 0; + if( iOfst+iAmt<512 ){ + n = kvvfsDecode(aData, &aData[1000], 1000); + if( n>=iOfst+iAmt ){ + memcpy(zBuf, &aData[1000+iOfst], iAmt); + n = iAmt; + }else{ + n = 0; + } + }else{ + n = kvvfsDecode(aData, zBuf, iAmt); + } + } + if( nzClass, iAmt, iOfst)); + 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; +} + +/* +** Write into the database file. +*/ +static int kvvfsWriteDb( + sqlite3_file *pProtoFile, + const void *zBuf, + int iAmt, + sqlite_int64 iOfst +){ + KVVfsFile *pFile = (KVVfsFile*)pProtoFile; + unsigned int pgno; + char zKey[30]; + char aData[131073]; + SQLITE_KV_LOG(("xWrite('%s-db',%d,%lld)\n", pFile->zClass, iAmt, iOfst)); + assert( iAmt>=512 && iAmt<=65536 ); + assert( (iAmt & (iAmt-1))==0 ); + pgno = 1 + iOfst/iAmt; + sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); + kvvfsEncode(zBuf, iAmt, aData); + kvstorageWrite(pFile->zClass, zKey, aData); + if( iOfst+iAmt > pFile->szDb ){ + pFile->szDb = iOfst + iAmt; + } + return SQLITE_OK; +} + +/* +** Truncate an kvvfs-file. +*/ +static int kvvfsTruncateJrnl(sqlite3_file *pProtoFile, sqlite_int64 size){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + SQLITE_KV_LOG(("xTruncate('%s-journal',%lld)\n", pFile->zClass, size)); + assert( size==0 ); + kvstorageDelete(pFile->zClass, "jrnl"); + sqlite3_free(pFile->aJrnl); + pFile->aJrnl = 0; + pFile->nJrnl = 0; + return SQLITE_OK; +} +static int kvvfsTruncateDb(sqlite3_file *pProtoFile, sqlite_int64 size){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + if( pFile->szDb>size + && pFile->szPage>0 + && (size % pFile->szPage)==0 + ){ + char zKey[50]; + unsigned int pgno, pgnoMax; + SQLITE_KV_LOG(("xTruncate('%s-db',%lld)\n", pFile->zClass, size)); + pgno = 1 + size/pFile->szPage; + pgnoMax = 2 + pFile->szDb/pFile->szPage; + while( pgno<=pgnoMax ){ + sqlite3_snprintf(sizeof(zKey), zKey, "%u", pgno); + kvstorageDelete(pFile->zClass, zKey); + pgno++; + } + pFile->szDb = size; + kvvfsWriteFileSize(pFile, size); + return SQLITE_OK; + } + return SQLITE_IOERR; +} + +/* +** Sync an kvvfs-file. +*/ +static int kvvfsSyncJrnl(sqlite3_file *pProtoFile, int flags){ + int i, n; + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + char *zOut; + SQLITE_KV_LOG(("xSync('%s-journal')\n", pFile->zClass)); + if( pFile->nJrnl<=0 ){ + return kvvfsTruncateJrnl(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 ); + zOut[i++] = ' '; + kvvfsEncode(pFile->aJrnl, pFile->nJrnl, &zOut[i]); + kvstorageWrite(pFile->zClass, "jrnl", zOut); + sqlite3_free(zOut); + return SQLITE_OK; +} +static int kvvfsSyncDb(sqlite3_file *pProtoFile, int flags){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + SQLITE_KV_LOG(("xSync('%s-db')\n", pFile->zClass)); + if( pFile->szDb>0 ){ + kvvfsWriteFileSize(pFile, pFile->szDb); + } + return SQLITE_OK; +} + +/* +** Return the current file-size of an kvvfs-file. +*/ +static int kvvfsFileSizeJrnl(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + SQLITE_KV_LOG(("xFileSize('%s-journal')\n", pFile->zClass)); + *pSize = pFile->nJrnl; + return SQLITE_OK; +} +static int kvvfsFileSizeDb(sqlite3_file *pProtoFile, sqlite_int64 *pSize){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + SQLITE_KV_LOG(("xFileSize('%s-db')\n", pFile->zClass)); + if( pFile->szDb>=0 ){ + *pSize = pFile->szDb; + }else{ + *pSize = kvvfsReadFileSize(pFile); + } + return SQLITE_OK; +} + +/* +** Lock an kvvfs-file. +*/ +static int kvvfsLock(sqlite3_file *pProtoFile, int eLock){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + assert( !pFile->isJournal ); + SQLITE_KV_LOG(("xLock(%s,%d)\n", pFile->zClass, eLock)); + + if( eLock!=SQLITE_LOCK_NONE ){ + pFile->szDb = kvvfsReadFileSize(pFile); + } + return SQLITE_OK; +} + +/* +** Unlock an kvvfs-file. +*/ +static int kvvfsUnlock(sqlite3_file *pProtoFile, int eLock){ + KVVfsFile *pFile = (KVVfsFile *)pProtoFile; + assert( !pFile->isJournal ); + SQLITE_KV_LOG(("xUnlock(%s,%d)\n", pFile->zClass, eLock)); + if( eLock==SQLITE_LOCK_NONE ){ + pFile->szDb = -1; + } + return SQLITE_OK; +} + +/* +** Check if another file-handle holds a RESERVED lock on an kvvfs-file. +*/ +static int kvvfsCheckReservedLock(sqlite3_file *pProtoFile, int *pResOut){ + SQLITE_KV_LOG(("xCheckReservedLock\n")); + *pResOut = 0; + return SQLITE_OK; +} + +/* +** File control method. For custom operations on an kvvfs-file. +*/ +static int kvvfsFileControl(sqlite3_file *pProtoFile, int op, void *pArg){ + return SQLITE_NOTFOUND; +} + +/* +** Return the sector-size in bytes for an kvvfs-file. +*/ +static int kvvfsSectorSize(sqlite3_file *pFile){ + return 512; +} + +/* +** Return the device characteristic flags supported by an kvvfs-file. +*/ +static int kvvfsDeviceCharacteristics(sqlite3_file *pProtoFile){ + return 0; +} + +/****** sqlite3_vfs methods *************************************************/ + +/* +** Open an kvvfs file handle. +*/ +static int kvvfsOpen( + sqlite3_vfs *pProtoVfs, + const char *zName, + sqlite3_file *pProtoFile, + int flags, + int *pOutFlags +){ + KVVfsFile *pFile = (KVVfsFile*)pProtoFile; + SQLITE_KV_LOG(("xOpen(\"%s\")\n", zName)); + if( strcmp(zName, "local")==0 + || strcmp(zName, "session")==0 + ){ + pFile->isJournal = 0; + pFile->base.pMethods = &kvvfs_db_io_methods; + }else + if( strcmp(zName, "local-journal")==0 + || strcmp(zName, "session-journal")==0 + ){ + pFile->isJournal = 1; + pFile->base.pMethods = &kvvfs_jrnl_io_methods; + }else{ + return SQLITE_CANTOPEN; + } + if( zName[0]=='s' ){ + pFile->zClass = "session"; + }else{ + pFile->zClass = "local"; + } + pFile->aJrnl = 0; + pFile->nJrnl = 0; + pFile->szPage = -1; + pFile->szDb = -1; + return SQLITE_OK; +} + +/* +** Delete the file located at zPath. If the dirSync argument is true, +** ensure the file-system modifications are synced to disk before +** returning. +*/ +static int kvvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){ + if( strcmp(zPath, "local-journal")==0 ){ + kvstorageDelete("local", "jrnl"); + }else + if( strcmp(zPath, "session-journal")==0 ){ + kvstorageDelete("session", "jrnl"); + } + return SQLITE_OK; +} + +/* +** Test for access permissions. Return true if the requested permission +** is available, or false otherwise. +*/ +static int kvvfsAccess( + sqlite3_vfs *pProtoVfs, + const char *zPath, + int flags, + int *pResOut +){ + SQLITE_KV_LOG(("xAccess(\"%s\")\n", zPath)); + if( strcmp(zPath, "local-journal")==0 ){ + *pResOut = kvstorageRead("local", "jrnl", 0, 0)>0; + }else + if( strcmp(zPath, "session-journal")==0 ){ + *pResOut = kvstorageRead("session", "jrnl", 0, 0)>0; + }else + if( strcmp(zPath, "local")==0 ){ + *pResOut = kvstorageRead("local", "sz", 0, 0)>0; + }else + if( strcmp(zPath, "session")==0 ){ + *pResOut = kvstorageRead("session", "sz", 0, 0)>0; + }else + { + *pResOut = 0; + } + SQLITE_KV_LOG(("xAccess returns %d\n",*pResOut)); + return SQLITE_OK; +} + +/* +** Populate buffer zOut with the full canonical pathname corresponding +** to the pathname in zPath. zOut is guaranteed to point to a buffer +** of at least (INST_MAX_PATHNAME+1) bytes. +*/ +static int kvvfsFullPathname( + sqlite3_vfs *pVfs, + const char *zPath, + int nOut, + char *zOut +){ + size_t nPath = strlen(zPath); + SQLITE_KV_LOG(("xFullPathname(\"%s\")\n", zPath)); + if( nOut1 +# undef SQLITE_OS_KV +# define SQLITE_OS_KV 0 +# undef SQLITE_OS_UNIX +# define SQLITE_OS_UNIX 0 +# undef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +#endif +#if SQLITE_OS_KV+1>1 +# undef SQLITE_OS_OTHER # define SQLITE_OS_OTHER 0 -# ifndef SQLITE_OS_WIN -# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \ - defined(__MINGW32__) || defined(__BORLANDC__) -# define SQLITE_OS_WIN 1 -# define SQLITE_OS_UNIX 0 -# else -# define SQLITE_OS_WIN 0 -# define SQLITE_OS_UNIX 1 -# endif -# else -# define SQLITE_OS_UNIX 0 -# endif -#else -# ifndef SQLITE_OS_WIN -# define SQLITE_OS_WIN 0 -# endif +# undef SQLITE_OS_UNIX +# define SQLITE_OS_UNIX 0 +# undef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +# define SQLITE_OMIT_LOAD_EXTENSION 1 +# define SQLITE_OMIT_WAL 1 +# define SQLITE_OMIT DEPRECATED 1 +# undef SQLITE_TEMP_STORE +# define SQLITE_TEMP_STORE 3 /* Always use memory for temporary storage */ +# define SQLITE_DQS 0 +# define SQLITE_OMIT_SHARED_CACHE 1 +# define SQLITE_OMIT_AUTOINIT 1 +#endif +#if SQLITE_OS_UNIX+1>1 +# undef SQLITE_OS_KV +# define SQLITE_OS_KV 0 +# undef SQLITE_OS_OTHER +# define SQLITE_OS_OTHER 0 +# undef SQLITE_OS_WIN +# define SQLITE_OS_WIN 0 +#endif +#if SQLITE_OS_WIN+1>1 +# undef SQLITE_OS_KV +# define SQLITE_OS_KV 0 +# undef SQLITE_OS_OTHER +# define SQLITE_OS_OTHER 0 +# undef SQLITE_OS_UNIX +# define SQLITE_OS_UNIX 0 #endif + #endif /* SQLITE_OS_SETUP_H */ diff --git a/src/os_unix.c b/src/os_unix.c index fd7a50dcdb..422fb25ed1 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -87,11 +87,11 @@ /* ** standard include files. */ -#include -#include +#include /* amalgamator: keep */ +#include /* amalgamator: keep */ #include #include -#include +#include /* amalgamator: keep */ #include #include #include diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 148ba75949..63e3583c8c 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1268,11 +1268,11 @@ typedef int VList; ** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque ** pointer types (i.e. FuncDef) defined above. */ +#include "os.h" #include "pager.h" #include "btree.h" #include "vdbe.h" #include "pcache.h" -#include "os.h" #include "mutex.h" /* The SQLITE_EXTRA_DURABLE compile-time option used to set the default diff --git a/tool/mksqlite3c.tcl b/tool/mksqlite3c.tcl index 595fc4c602..0cdf514e44 100644 --- a/tool/mksqlite3c.tcl +++ b/tool/mksqlite3c.tcl @@ -355,6 +355,7 @@ foreach file { hash.c opcodes.c + os_kv.c os_unix.c os_win.c memdb.c