diff options
author | stephan <stephan@noemail.net> | 2022-05-24 14:36:45 +0000 |
---|---|---|
committer | stephan <stephan@noemail.net> | 2022-05-24 14:36:45 +0000 |
commit | 400ee2ecef4e56703bcbb0493947a7fa88b572fe (patch) | |
tree | ab2d02e363717fad280e9f262493702d203bfe0c /ext/fiddle/sqlite3-api.js | |
parent | 2f6a729d551826888a31b61f9b0ebd107b53ff65 (diff) | |
download | sqlite-400ee2ecef4e56703bcbb0493947a7fa88b572fe.tar.gz sqlite-400ee2ecef4e56703bcbb0493947a7fa88b572fe.zip |
fiddle: lots of generic refactoring, restructuring, and cleanup in the higher-level code. Added push-fiddle ext/fiddle/Makefile target to push the fiddle app to a remote server via rsync.
FossilOrigin-Name: ed19fef3459499abb0a4a010f368b4576d6e068d930c8480446ea677ac87c1c1
Diffstat (limited to 'ext/fiddle/sqlite3-api.js')
-rw-r--r-- | ext/fiddle/sqlite3-api.js | 196 |
1 files changed, 100 insertions, 96 deletions
diff --git a/ext/fiddle/sqlite3-api.js b/ext/fiddle/sqlite3-api.js index 90b8de798..e97e7c183 100644 --- a/ext/fiddle/sqlite3-api.js +++ b/ext/fiddle/sqlite3-api.js @@ -17,31 +17,33 @@ Note that this file is not named sqlite3.js because that file gets generated by emscripten as the JS-glue counterpart of sqlite3.wasm. - This code installs an object named self.sqlite3, where self is - expected to be either the global window or Worker object: + This code installs an object named self.Module.sqlite3, where self + is expected to be either the global window or Worker object and + Module is the object set up by the emscripten infrastructure. The + sqlite3 object looks like: - self.sqlite3 = { - api: core WASM bindings of sqlite3 APIs, + { + api: bindings for much of the core sqlite3 APIs, SQLite3: high-level OO API wrapper - }; + } The way we export this module is not _really_ modern-JS-friendly - because it exports a global symbol (which is admittedly not - ideal). Exporting it "cleanly," without introducing any global-scope - symbols, requires using a module loader in all client code. As there - are several different approaches, none of which this developer is - currently truly familiar with, the current approach will have to do - for the time being. - - Because using the low-level API properly requires some degree of - WASM-related magic, it is not recommended that that API be used - as-is in client-level code. Rather, client code should use the - higher-level OO API or write a custom wrapper on top of the - lower-level API. - - This file installs namespace.sqlite3, where namespace is `self`, - meaning either the global window or worker, depending on where this - is loaded from. + because it exports/relies on a global symbol (which is admittedly + not ideal). Exporting it "cleanly," without introducing any + global-scope symbols, requires using a module loader in all client + code. As there are several different approaches, none of which this + developer is currently truly familiar with, the current approach + will have to do for the time being. + + Because using certain parts of the low-level API properly requires + some degree of WASM-related magic, it is not recommended that that + API be used as-is in client-level code. Rather, client code should + use the higher-level OO API or write a custom wrapper on top of the + lower-level API. In short, using any C-style APIs which take + pointers-to-pointer arguments require WASM-specific interfaces + installed by emcscripten-generated code. Those which take or return + only integers, doubles, strings, or "plain" pointers to db or + statement objects can be used in a straightforward manner. # Goals and Non-goals of this API @@ -60,13 +62,16 @@ can be interacted with, but keeping the DB operations out of the UI thread is generally desirable. + - Insofar as possible, support client-side storage using JS + filesystem APIs. As of this writing, such things are still very + much TODO. Non-goals: - - As WASM is a web-based technology and UTF-8 is the King of - Encodings in that realm, there are no plans to support the - UTF16-related APIs will not be. They would add a complication to - the bindings for no appreciable benefit. + - As WASM is a web-centric technology and UTF-8 is the King of + Encodings in that realm, there are no current plans to support the + UTF16-related APIs. They would add a complication to the bindings + for no appreciable benefit. - Supporting old or niche-market platforms. WASM is built for a modern web and requires modern platforms. @@ -247,8 +252,6 @@ throw new Error(Array.prototype.join.call(arguments, ' ')); }; - const S/*convenience alias*/ = api; - /** The DB class wraps a sqlite3 db handle. */ @@ -258,7 +261,7 @@ toss("TODO: support blob image of db here."); } setValue(pPtrArg, 0, "i32"); - this.checkRc(S.sqlite3_open(name, pPtrArg)); + this.checkRc(api.sqlite3_open(name, pPtrArg)); this._pDb = getValue(pPtrArg, "i32"); this.filename = name; this._statements = {/*map of open Stmt _pointers_ to Stmt*/}; @@ -291,8 +294,8 @@ } this.db = arguments[0]; this._pStmt = arguments[1]; - this.columnCount = S.sqlite3_column_count(this._pStmt); - this.parameterCount = S.sqlite3_bind_parameter_count(this._pStmt); + this.columnCount = api.sqlite3_column_count(this._pStmt); + this.parameterCount = api.sqlite3_bind_parameter_count(this._pStmt); this._allocs = [/*list of alloc'd memory blocks for bind() values*/] }; @@ -370,7 +373,7 @@ checkRc: function(sqliteResultCode){ if(!sqliteResultCode) return this; toss("sqlite result code",sqliteResultCode+":", - S.sqlite3_errmsg(this._pDb) || "Unknown db error."); + api.sqlite3_errmsg(this._pDb) || "Unknown db error."); }, /** Finalizes all open statements and closes this database @@ -388,7 +391,7 @@ Object.values(this._udfs).forEach(Module.removeFunction); delete this._udfs; delete this._statements; - S.sqlite3_close_v2(this._pDb); + api.sqlite3_close_v2(this._pDb); delete this._pDb; } }, @@ -401,7 +404,7 @@ a name of `main`. */ fileName: function(dbName){ - return S.sqlite3_db_filename(affirmDbOpen(this)._pDb, dbName||"main"); + return api.sqlite3_db_filename(affirmDbOpen(this)._pDb, dbName||"main"); }, /** Compiles the given SQL and returns a prepared Stmt. This is @@ -410,7 +413,7 @@ prepare: function(sql){ affirmDbOpen(this); setValue(pPtrArg,0,"i32"); - this.checkRc(S.sqlite3_prepare_v2(this._pDb, sql, -1, pPtrArg, null)); + this.checkRc(api.sqlite3_prepare_v2(this._pDb, sql, -1, pPtrArg, null)); const pStmt = getValue(pPtrArg, "i32"); if(!pStmt) toss("Empty SQL is not permitted."); const stmt = new Stmt(this, pStmt, BindTypes); @@ -533,14 +536,14 @@ while(getValue(pSql, "i8")){ setValue(pPtrArg, 0, "i32"); setValue(pzTail, 0, "i32"); - this.checkRc(S.sqlite3_prepare_v2_sqlptr( + this.checkRc(api.sqlite3_prepare_v2_sqlptr( this._pDb, pSql, -1, pPtrArg, pzTail )); const pStmt = getValue(pPtrArg, "i32"); pSql = getValue(pzTail, "i32"); if(!pStmt) continue; if(opt.saveSql){ - opt.saveSql.push(S.sqlite3_sql(pStmt).trim()); + opt.saveSql.push(api.sqlite3_sql(pStmt).trim()); } stmt = new Stmt(this, pStmt, BindTypes); if(bind && stmt.parameterCount){ @@ -653,18 +656,18 @@ const tgt = []; for(i = 0; i < argc; ++i){ pVal = getValue(pArgv + (4 * i), "i32"); - valType = S.sqlite3_value_type(pVal); + valType = api.sqlite3_value_type(pVal); switch(valType){ - case S.SQLITE_INTEGER: - case S.SQLITE_FLOAT: - arg = S.sqlite3_value_double(pVal); + case api.SQLITE_INTEGER: + case api.SQLITE_FLOAT: + arg = api.sqlite3_value_double(pVal); break; case SQLITE_TEXT: - arg = S.sqlite3_value_text(pVal); + arg = api.sqlite3_value_text(pVal); break; case SQLITE_BLOB:{ - const n = S.sqlite3_value_bytes(ptr); - const pBlob = S.sqlite3_value_blob(ptr); + const n = api.sqlite3_value_bytes(ptr); + const pBlob = api.sqlite3_value_blob(ptr); arg = new Uint8Array(n); let i; for(i = 0; i < n; ++i) arg[i] = HEAP8[pBlob+i]; @@ -680,25 +683,25 @@ f._setResult = function(pCx, val){ switch(typeof val) { case 'boolean': - S.sqlite3_result_int(pCx, val ? 1 : 0); + api.sqlite3_result_int(pCx, val ? 1 : 0); break; case 'number': { (isInt32(val) - ? S.sqlite3_result_int - : S.sqlite3_result_double)(pCx, val); + ? api.sqlite3_result_int + : api.sqlite3_result_double)(pCx, val); break; } case 'string': - S.sqlite3_result_text(pCx, val, -1, + api.sqlite3_result_text(pCx, val, -1, -1/*==SQLITE_TRANSIENT*/); break; case 'object': if(null===val) { - S.sqlite3_result_null(pCx); + api.sqlite3_result_null(pCx); break; }else if(undefined!==val.length){ const pBlob = Module.allocate(val, ALLOC_NORMAL); - S.sqlite3_result_blob(pCx, pBlob, val.length, -1/*==SQLITE_TRANSIENT*/); + api.sqlite3_result_blob(pCx, pBlob, val.length, -1/*==SQLITE_TRANSIENT*/); Module._free(blobptr); break; } @@ -712,20 +715,20 @@ try{ f._setResult(pCx, callback.apply(null, f._extractArgs(argc, pArgv))); }catch(e){ - S.sqlite3_result_error(pCx, e.message, -1); + api.sqlite3_result_error(pCx, e.message, -1); } }; const pUdf = Module.addFunction(wrapper, "viii"); let fFlags = 0; - if(getOwnOption(opt, 'deterministic')) fFlags |= S.SQLITE_DETERMINISTIC; - if(getOwnOption(opt, 'directOnly')) fFlags |= S.SQLITE_DIRECTONLY; - if(getOwnOption(opt, 'innocuous')) fFlags |= S.SQLITE_INNOCUOUS; + if(getOwnOption(opt, 'deterministic')) fFlags |= api.SQLITE_DETERMINISTIC; + if(getOwnOption(opt, 'directOnly')) fFlags |= api.SQLITE_DIRECTONLY; + if(getOwnOption(opt, 'innocuous')) fFlags |= api.SQLITE_INNOCUOUS; name = name.toLowerCase(); try { - this.checkRc(S.sqlite3_create_function_v2( + this.checkRc(api.sqlite3_create_function_v2( this._pDb, name, (opt.hasOwnProperty('arity') ? +opt.arity : callback.length), - S.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf, + api.SQLITE_UTF8 | fFlags, null/*pApp*/, pUdf, null/*xStep*/, null/*xFinal*/, null/*xDestroy*/)); }catch(e){ Module.removeFunction(pUdf); @@ -802,7 +805,7 @@ */ const affirmParamIndex = function(stmt,key){ const n = ('number'===typeof key) - ? key : S.sqlite3_bind_parameter_index(stmt._pStmt, key); + ? key : api.sqlite3_bind_parameter_index(stmt._pStmt, key); if(0===n || (n===key && (n!==(n|0)/*floating point*/))){ toss("Invalid bind() parameter name: "+key); } @@ -853,7 +856,7 @@ const bytes = intArrayFromString(val,true); const pStr = Module.allocate(bytes, ALLOC_NORMAL); stmt._allocs.push(pStr); - const func = asBlob ? S.sqlite3_bind_blob : S.sqlite3_bind_text; + const func = asBlob ? api.sqlite3_bind_blob : api.sqlite3_bind_text; return func(stmt._pStmt, ndx, pStr, bytes.length, 0); } }; @@ -863,7 +866,7 @@ let rc = 0; switch((null===val || undefined===val) ? BindTypes.null : bindType){ case BindTypes.null: - rc = S.sqlite3_bind_null(stmt._pStmt, ndx); + rc = api.sqlite3_bind_null(stmt._pStmt, ndx); break; case BindTypes.string:{ rc = f._.string(stmt, ndx, val, false); @@ -871,15 +874,15 @@ } case BindTypes.number: { const m = (isInt32(val) - ? S.sqlite3_bind_int + ? api.sqlite3_bind_int /*It's illegal to bind a 64-bit int from here*/ - : S.sqlite3_bind_double); + : api.sqlite3_bind_double); rc = m(stmt._pStmt, ndx, val); break; } case BindTypes.boolean: - rc = S.sqlite3_bind_int(stmt._pStmt, ndx, val ? 1 : 0); + rc = api.sqlite3_bind_int(stmt._pStmt, ndx, val ? 1 : 0); break; case BindTypes.blob: { if('string'===typeof val){ @@ -892,7 +895,7 @@ } const pBlob = Module.allocate(val, ALLOC_NORMAL); stmt._allocs.push(pBlob); - rc = S.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0); + rc = api.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0); } } default: toss("Unsupported bind() argument type."); @@ -923,7 +926,7 @@ affirmUnlocked(this,'finalize()'); freeBindMemory(this); delete this.db._statements[this._pStmt]; - S.sqlite3_finalize(this._pStmt); + api.sqlite3_finalize(this._pStmt); delete this.columnCount; delete this.parameterCount; delete this._pStmt; @@ -937,7 +940,7 @@ freeBindMemory( affirmUnlocked(affirmStmtOpen(this), 'clearBindings()') ); - S.sqlite3_clear_bindings(this._pStmt); + api.sqlite3_clear_bindings(this._pStmt); this._mayGet = false; return this; }, @@ -953,7 +956,7 @@ reset: function(alsoClearBinds){ affirmUnlocked(this,'reset()'); if(alsoClearBinds) this.clearBindings(); - S.sqlite3_reset(affirmStmtOpen(this)._pStmt); + api.sqlite3_reset(affirmStmtOpen(this)._pStmt); this._mayGet = false; return this; }, @@ -1098,14 +1101,14 @@ */ step: function(){ affirmUnlocked(this, 'step()'); - const rc = S.sqlite3_step(affirmStmtOpen(this)._pStmt); + const rc = api.sqlite3_step(affirmStmtOpen(this)._pStmt); switch(rc){ - case S.SQLITE_DONE: return this._mayGet = false; - case S.SQLITE_ROW: return this._mayGet = true; + case api.SQLITE_DONE: return this._mayGet = false; + case api.SQLITE_ROW: return this._mayGet = true; default: this._mayGet = false; console.warn("sqlite3_step() rc=",rc,"SQL =", - S.sqlite3_sql(this._pStmt)); + api.sqlite3_sql(this._pStmt)); this.db.checkRc(rc); }; }, @@ -1155,27 +1158,27 @@ }else if(ndx && 'object'===typeof ndx){ let i = 0; while(i<this.columnCount){ - ndx[S.sqlite3_column_name(this._pStmt,i)] = this.get(i++); + ndx[api.sqlite3_column_name(this._pStmt,i)] = this.get(i++); } return ndx; } affirmColIndex(this, ndx); switch(undefined===asType - ? S.sqlite3_column_type(this._pStmt, ndx) + ? api.sqlite3_column_type(this._pStmt, ndx) : asType){ - case S.SQLITE_NULL: return null; - case S.SQLITE_INTEGER:{ - return 0 | S.sqlite3_column_double(this._pStmt, ndx); + case api.SQLITE_NULL: return null; + case api.SQLITE_INTEGER:{ + return 0 | api.sqlite3_column_double(this._pStmt, ndx); /* ^^^^^^^^ strips any fractional part and handles handles >32bits */ } - case S.SQLITE_FLOAT: - return S.sqlite3_column_double(this._pStmt, ndx); - case S.SQLITE_TEXT: - return S.sqlite3_column_text(this._pStmt, ndx); - case S.SQLITE_BLOB: { - const n = S.sqlite3_column_bytes(this._pStmt, ndx); - const ptr = S.sqlite3_column_blob(this._pStmt, ndx); + case api.SQLITE_FLOAT: + return api.sqlite3_column_double(this._pStmt, ndx); + case api.SQLITE_TEXT: + return api.sqlite3_column_text(this._pStmt, ndx); + case api.SQLITE_BLOB: { + const n = api.sqlite3_column_bytes(this._pStmt, ndx); + const ptr = api.sqlite3_column_blob(this._pStmt, ndx); const rc = new Uint8Array(n); for(let i = 0; i < n; ++i) rc[i] = HEAP8[ptr + i]; return rc; @@ -1187,16 +1190,16 @@ }, /** Equivalent to get(ndx) but coerces the result to an integer. */ - getInt: function(ndx){return this.get(ndx,S.SQLITE_INTEGER)}, + getInt: function(ndx){return this.get(ndx,api.SQLITE_INTEGER)}, /** Equivalent to get(ndx) but coerces the result to a float. */ - getFloat: function(ndx){return this.get(ndx,S.SQLITE_FLOAT)}, + getFloat: function(ndx){return this.get(ndx,api.SQLITE_FLOAT)}, /** Equivalent to get(ndx) but coerces the result to a string. */ - getString: function(ndx){return this.get(ndx,S.SQLITE_TEXT)}, + getString: function(ndx){return this.get(ndx,api.SQLITE_TEXT)}, /** Equivalent to get(ndx) but coerces the result to a Uint8Array. */ - getBlob: function(ndx){return this.get(ndx,S.SQLITE_BLOB)}, + getBlob: function(ndx){return this.get(ndx,api.SQLITE_BLOB)}, /** A convenience wrapper around get() which fetches the value as a string and then, if it is not null, passes it to @@ -1205,16 +1208,17 @@ string, on the other hand, will trigger an exception. */ getJSON: function(ndx){ - const s = this.get(ndx, S.SQLITE_STRING); + const s = this.get(ndx, api.SQLITE_STRING); return null===s ? s : JSON.parse(s); }, /** Returns the result column name of the given index, or throws if index is out of bounds or this statement has been - finalized. + finalized. This can be used without having run step() + first. */ getColumnName: function(ndx){ - return S.sqlite3_column_name( + return api.sqlite3_column_name( affirmColIndex(affirmStmtOpen(this),ndx)._pStmt, ndx ); }, @@ -1230,7 +1234,7 @@ affirmColIndex(affirmStmtOpen(this),0); if(!tgt) tgt = []; for(let i = 0; i < this.columnCount; ++i){ - tgt.push(S.sqlite3_column_name(this._pStmt, i)); + tgt.push(api.sqlite3_column_name(this._pStmt, i)); } return tgt; }, @@ -1242,7 +1246,7 @@ */ getParamIndex: function(name){ return (affirmStmtOpen(this).parameterCount - ? S.sqlite3_bind_parameter_index(this._pStmt, name) + ? api.sqlite3_bind_parameter_index(this._pStmt, name) : undefined); } }/*Stmt.prototype*/; @@ -1250,7 +1254,7 @@ /** OO binding's namespace. */ const SQLite3 = { version: { - lib: S.sqlite3_libversion(), + lib: api.sqlite3_libversion(), ooApi: "0.0.1" }, DB, @@ -1293,7 +1297,7 @@ } const rc = {}, ov = [0,0]; let i = 0, k; - while((k = S.sqlite3_compileoption_get(i++))){ + while((k = api.sqlite3_compileoption_get(i++))){ f._opt(k,ov); rc[ov[0]] = ov[1]; } @@ -1302,19 +1306,19 @@ else if(Array.isArray(optName)){ const rc = {}; optName.forEach((v)=>{ - rc[v] = S.sqlite3_compileoption_used(v); + rc[v] = api.sqlite3_compileoption_used(v); }); return rc; } else if('object' === typeof optName){ Object.keys(optName).forEach((k)=> { - optName[k] = S.sqlite3_compileoption_used(k); + optName[k] = api.sqlite3_compileoption_used(k); }); return optName; } return ( 'string'===typeof optName - ) ? !!S.sqlite3_compileoption_used(optName) : false; + ) ? !!api.sqlite3_compileoption_used(optName) : false; } }; @@ -1322,4 +1326,4 @@ api: api, SQLite3 }; -})(self/*worker or window*/); +})(self/*worker or window*/.Module); |